sently 0.9.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +46 -0
  3. package/README.md +18 -16
  4. package/dist/adapters/bun.js +1 -1
  5. package/dist/adapters/cf.js +2 -2
  6. package/dist/adapters/cf.js.map +1 -1
  7. package/dist/adapters/deno.js +1 -1
  8. package/dist/adapters/node.js +1 -1
  9. package/dist/auth/oauth2.js +2 -2
  10. package/dist/auth/oauth2.js.map +1 -1
  11. package/dist/channel-result.d.ts +6 -0
  12. package/dist/channel-result.js +1 -0
  13. package/dist/{chunk-skqhj0rm.js → chunk-1ke3nmyg.js} +3 -3
  14. package/dist/{chunk-skqhj0rm.js.map → chunk-1ke3nmyg.js.map} +1 -1
  15. package/dist/{chunk-7gy2q9sh.js → chunk-5es0v1j7.js} +2 -2
  16. package/dist/{chunk-7gy2q9sh.js.map → chunk-5es0v1j7.js.map} +1 -1
  17. package/dist/{chunk-0qxws3kj.js → chunk-7wtfjx0d.js} +2 -2
  18. package/dist/{chunk-0qxws3kj.js.map → chunk-7wtfjx0d.js.map} +1 -1
  19. package/dist/chunk-8kpgbrba.js +4 -0
  20. package/dist/{chunk-93vqxxj2.js.map → chunk-8kpgbrba.js.map} +2 -2
  21. package/dist/{chunk-vjpds2pf.js → chunk-a057jzsy.js} +2 -2
  22. package/dist/{chunk-vjpds2pf.js.map → chunk-a057jzsy.js.map} +1 -1
  23. package/dist/{chunk-8njyga62.js → chunk-bhr7w3gg.js} +2 -2
  24. package/dist/{chunk-8njyga62.js.map → chunk-bhr7w3gg.js.map} +1 -1
  25. package/dist/chunk-cs1tvehn.js +4 -0
  26. package/dist/chunk-cs1tvehn.js.map +10 -0
  27. package/dist/{chunk-29z7fkzy.js → chunk-d5aw5vag.js} +3 -3
  28. package/dist/{chunk-29z7fkzy.js.map → chunk-d5aw5vag.js.map} +1 -1
  29. package/dist/chunk-f5x8vesr.js +4 -0
  30. package/dist/{chunk-b602dhck.js.map → chunk-f5x8vesr.js.map} +2 -2
  31. package/dist/chunk-fv0bemgf.js +4 -0
  32. package/dist/{chunk-yp311efr.js.map → chunk-fv0bemgf.js.map} +3 -3
  33. package/dist/{chunk-ywq6s10g.js → chunk-hnzmn4s4.js} +2 -2
  34. package/dist/{chunk-ywq6s10g.js.map → chunk-hnzmn4s4.js.map} +1 -1
  35. package/dist/chunk-htdg075t.js +4 -0
  36. package/dist/{chunk-qtkd5bak.js.map → chunk-htdg075t.js.map} +4 -3
  37. package/dist/{chunk-0gqy32pe.js → chunk-q2mahnb7.js} +3 -3
  38. package/dist/chunk-q2mahnb7.js.map +10 -0
  39. package/dist/chunk-sbgt4165.js +4 -0
  40. package/dist/chunk-sbgt4165.js.map +10 -0
  41. package/dist/{chunk-6npp3x3c.js → chunk-th4cwrpb.js} +2 -2
  42. package/dist/{chunk-6npp3x3c.js.map → chunk-th4cwrpb.js.map} +1 -1
  43. package/dist/{chunk-tamww15j.js → chunk-ttbwyxmh.js} +2 -2
  44. package/dist/{chunk-tamww15j.js.map → chunk-ttbwyxmh.js.map} +1 -1
  45. package/dist/chunk-w4c9kj70.js +13 -0
  46. package/dist/{chunk-n0qeyzqm.js.map → chunk-w4c9kj70.js.map} +2 -2
  47. package/dist/chunk-w9sabmb0.js +4 -0
  48. package/dist/chunk-w9sabmb0.js.map +10 -0
  49. package/dist/{chunk-8dty8c4p.js → chunk-yg3kj723.js} +3 -3
  50. package/dist/{chunk-8dty8c4p.js.map → chunk-yg3kj723.js.map} +2 -2
  51. package/dist/chunk-z1589fjk.js +4 -0
  52. package/dist/chunk-z1589fjk.js.map +10 -0
  53. package/dist/core/channel-result.d.ts +36 -0
  54. package/dist/core/channel-result.js +3 -0
  55. package/dist/core/channel-result.js.map +10 -0
  56. package/dist/core/decorated-transport.d.ts +30 -0
  57. package/dist/core/decorator-hooks.d.ts +17 -0
  58. package/dist/core/errors.js +2 -2
  59. package/dist/core/errors.js.map +1 -1
  60. package/dist/core/provider-label.d.ts +7 -3
  61. package/dist/core/push-endpoint.d.ts +5 -0
  62. package/dist/core/push-types.d.ts +48 -14
  63. package/dist/core/sms-types.d.ts +12 -0
  64. package/dist/core/smtp.js +2 -2
  65. package/dist/core/smtp.js.map +1 -1
  66. package/dist/core/whatsapp-types.d.ts +12 -0
  67. package/dist/detect.js +2 -2
  68. package/dist/detect.js.map +1 -1
  69. package/dist/dkim.js +2 -2
  70. package/dist/dkim.js.map +2 -2
  71. package/dist/idempotency.js +2 -2
  72. package/dist/idempotency.js.map +1 -1
  73. package/dist/index.d.ts +4 -0
  74. package/dist/index.js +5 -0
  75. package/dist/mailer.js +2 -2
  76. package/dist/mailer.js.map +1 -1
  77. package/dist/observability/console.js +1 -1
  78. package/dist/plugins/react.js +2 -2
  79. package/dist/plugins/react.js.map +1 -1
  80. package/dist/plugins/template.js +1 -1
  81. package/dist/pool/pool.js +2 -2
  82. package/dist/pool/pool.js.map +1 -1
  83. package/dist/push.d.ts +1 -0
  84. package/dist/push.js +2 -2
  85. package/dist/push.js.map +3 -3
  86. package/dist/sms.d.ts +1 -0
  87. package/dist/sms.js +2 -2
  88. package/dist/sms.js.map +3 -3
  89. package/dist/smtp-mailer.js +2 -2
  90. package/dist/smtp-mailer.js.map +1 -1
  91. package/dist/transports/brevo.js +2 -2
  92. package/dist/transports/brevo.js.map +2 -2
  93. package/dist/transports/cloudflare-email.js +2 -2
  94. package/dist/transports/cloudflare-email.js.map +2 -2
  95. package/dist/transports/fallback.d.ts +32 -6
  96. package/dist/transports/fallback.js +2 -2
  97. package/dist/transports/fallback.js.map +1 -1
  98. package/dist/transports/fcm.d.ts +55 -0
  99. package/dist/transports/fcm.js +4 -0
  100. package/dist/transports/fcm.js.map +10 -0
  101. package/dist/transports/loops.js +2 -2
  102. package/dist/transports/loops.js.map +1 -1
  103. package/dist/transports/mailersend.js +2 -2
  104. package/dist/transports/mailersend.js.map +2 -2
  105. package/dist/transports/mailgun.js +2 -2
  106. package/dist/transports/mailgun.js.map +2 -2
  107. package/dist/transports/mailtrap.js +2 -2
  108. package/dist/transports/mailtrap.js.map +2 -2
  109. package/dist/transports/msegat.js +2 -2
  110. package/dist/transports/msegat.js.map +1 -1
  111. package/dist/transports/plunk.js +2 -2
  112. package/dist/transports/plunk.js.map +1 -1
  113. package/dist/transports/postmark.js +2 -2
  114. package/dist/transports/postmark.js.map +2 -2
  115. package/dist/transports/preview.js +2 -2
  116. package/dist/transports/preview.js.map +1 -1
  117. package/dist/transports/resend.js +2 -2
  118. package/dist/transports/resend.js.map +2 -2
  119. package/dist/transports/retry.d.ts +28 -9
  120. package/dist/transports/retry.js +2 -2
  121. package/dist/transports/retry.js.map +3 -3
  122. package/dist/transports/sendgrid.js +2 -2
  123. package/dist/transports/sendgrid.js.map +2 -2
  124. package/dist/transports/ses.js +2 -2
  125. package/dist/transports/ses.js.map +2 -2
  126. package/dist/transports/smtp.js +2 -2
  127. package/dist/transports/smtp.js.map +1 -1
  128. package/dist/transports/sndr.js +2 -2
  129. package/dist/transports/sndr.js.map +1 -1
  130. package/dist/transports/sparkpost.js +2 -2
  131. package/dist/transports/sparkpost.js.map +2 -2
  132. package/dist/transports/taqnyat-mail.js +2 -2
  133. package/dist/transports/taqnyat-mail.js.map +1 -1
  134. package/dist/transports/taqnyat-sms.js +2 -2
  135. package/dist/transports/taqnyat-sms.js.map +1 -1
  136. package/dist/transports/taqnyat-whatsapp.js +2 -2
  137. package/dist/transports/taqnyat-whatsapp.js.map +1 -1
  138. package/dist/transports/twilio-sms.js +2 -2
  139. package/dist/transports/twilio-sms.js.map +2 -2
  140. package/dist/transports/unifonic.d.ts +81 -0
  141. package/dist/transports/unifonic.js +3 -0
  142. package/dist/transports/unifonic.js.map +10 -0
  143. package/dist/transports/webpush.js +2 -2
  144. package/dist/transports/webpush.js.map +3 -3
  145. package/dist/transports/weighted-fallback.d.ts +11 -9
  146. package/dist/transports/weighted-fallback.js +2 -2
  147. package/dist/transports/weighted-fallback.js.map +3 -3
  148. package/dist/transports/whatsapp-cloud.js +2 -2
  149. package/dist/transports/whatsapp-cloud.js.map +1 -1
  150. package/dist/webhooks/brevo.js +2 -2
  151. package/dist/webhooks/brevo.js.map +1 -1
  152. package/dist/webhooks/mailgun.js +2 -2
  153. package/dist/webhooks/mailgun.js.map +1 -1
  154. package/dist/webhooks/postmark.js +2 -2
  155. package/dist/webhooks/postmark.js.map +1 -1
  156. package/dist/webhooks/resend.js +2 -2
  157. package/dist/webhooks/resend.js.map +1 -1
  158. package/dist/webhooks/sendgrid.js +2 -2
  159. package/dist/webhooks/sendgrid.js.map +1 -1
  160. package/dist/webhooks/ses.js +2 -2
  161. package/dist/webhooks/ses.js.map +1 -1
  162. package/dist/webhooks/sndr.js +2 -2
  163. package/dist/webhooks/sndr.js.map +1 -1
  164. package/dist/webhooks/timing-safe-equal.js +1 -1
  165. package/dist/webhooks/twilio-sms.d.ts +20 -0
  166. package/dist/webhooks/twilio-sms.js +3 -0
  167. package/dist/webhooks/twilio-sms.js.map +10 -0
  168. package/dist/webhooks/types.d.ts +37 -3
  169. package/dist/webhooks/types.js +3 -0
  170. package/dist/webhooks/types.js.map +9 -0
  171. package/dist/webhooks/unifonic.d.ts +13 -0
  172. package/dist/webhooks/unifonic.js +3 -0
  173. package/dist/webhooks/unifonic.js.map +10 -0
  174. package/dist/webhooks/whatsapp-cloud.d.ts +14 -0
  175. package/dist/webhooks/whatsapp-cloud.js +3 -0
  176. package/dist/webhooks/whatsapp-cloud.js.map +10 -0
  177. package/dist/webhooks.d.ts +10 -5
  178. package/dist/webhooks.js +11 -0
  179. package/dist/whatsapp.d.ts +1 -0
  180. package/dist/whatsapp.js +2 -2
  181. package/dist/whatsapp.js.map +3 -3
  182. package/package.json +25 -1
  183. package/site/content/docs/channels/hooks.mdx +6 -5
  184. package/site/content/docs/channels/index.mdx +2 -2
  185. package/site/content/docs/channels/push.mdx +27 -21
  186. package/site/content/docs/channels/sms.mdx +4 -1
  187. package/site/content/docs/decorators/fallback.mdx +41 -14
  188. package/site/content/docs/decorators/index.mdx +29 -12
  189. package/site/content/docs/decorators/retry.mdx +32 -9
  190. package/site/content/docs/decorators/weighted-fallback.mdx +27 -6
  191. package/site/content/docs/get-started/entrypoints.mdx +6 -5
  192. package/site/content/docs/get-started/installation.mdx +3 -2
  193. package/site/content/docs/get-started/introduction.mdx +1 -1
  194. package/site/content/docs/guides/webhooks.mdx +35 -20
  195. package/site/content/docs/index.mdx +1 -1
  196. package/site/content/docs/quick-start/push.mdx +22 -5
  197. package/site/content/docs/reference/channel-result.mdx +82 -0
  198. package/site/content/docs/reference/exports.mdx +5 -3
  199. package/site/content/docs/reference/meta.json +1 -0
  200. package/site/content/docs/reference/push-options.mdx +44 -3
  201. package/site/content/docs/reference/sms-options.mdx +18 -2
  202. package/site/content/docs/reference/webhook-events.mdx +38 -14
  203. package/site/content/docs/reference/whatsapp-options.mdx +19 -1
  204. package/site/content/docs/transports/fcm.mdx +80 -0
  205. package/site/content/docs/transports/index.mdx +3 -1
  206. package/site/content/docs/transports/meta.json +3 -1
  207. package/site/content/docs/transports/unifonic.mdx +85 -0
  208. package/dist/chunk-0gqy32pe.js.map +0 -10
  209. package/dist/chunk-5915vbcj.js +0 -4
  210. package/dist/chunk-5915vbcj.js.map +0 -10
  211. package/dist/chunk-93vqxxj2.js +0 -4
  212. package/dist/chunk-b602dhck.js +0 -4
  213. package/dist/chunk-gp4fwrs8.js +0 -4
  214. package/dist/chunk-gp4fwrs8.js.map +0 -10
  215. package/dist/chunk-n0qeyzqm.js +0 -13
  216. package/dist/chunk-q8k3m6qe.js +0 -4
  217. package/dist/chunk-q8k3m6qe.js.map +0 -10
  218. package/dist/chunk-qtkd5bak.js +0 -4
  219. package/dist/chunk-yp311efr.js +0 -4
@@ -5,6 +5,6 @@
5
5
  "/**\n * @module\n * SMTP-capable mailer factory — host/port config, pooling, and runtime adapters.\n *\n * Import from `sently/smtp` or use {@link createSMTPMailer} from the main package.\n * For HTTP transports, use `sently/mailer` instead (~10 KB smaller in app bundles).\n *\n * @example\n * ```ts\n * import { createSMTPMailer } from \"sently/smtp\";\n *\n * const mailer = await createSMTPMailer({\n * host: \"smtp.example.com\",\n * port: 587,\n * auth: { user: \"you@example.com\", pass: \"secret\" },\n * });\n * ```\n */\nimport type { Mailer, SMTPMailerOptions } from \"./core/types.js\";\nimport { createDefaultAdapter } from \"./detect.js\";\nimport { MailerImpl } from \"./mailer.js\";\n\n/**\n * Create a mailer from SMTP relay options (host, port, auth, pool, adapter).\n */\nexport async function createSMTPMailer(options: SMTPMailerOptions): Promise<Mailer> {\n const adapterOptions = {\n ...(options.secure !== undefined ? { secure: options.secure } : {}),\n ...(options.connectionTimeout !== undefined\n ? { connectionTimeout: options.connectionTimeout }\n : {}),\n ...(options.tls !== undefined ? { tls: options.tls } : {}),\n };\n\n if (options.pool) {\n const { SMTPPool } = await import(\"./pool/pool.js\");\n return new MailerImpl(\n new SMTPPool(options, {\n createAdapter: async () => options.adapter ?? (await createDefaultAdapter(adapterOptions)),\n }),\n options.plugins,\n options.hooks,\n );\n }\n\n const adapter = options.adapter ?? (await createDefaultAdapter(adapterOptions));\n const { SMTPTransport } = await import(\"./transports/smtp.js\");\n\n return new MailerImpl(new SMTPTransport({ ...options, adapter }), options.plugins, options.hooks);\n}\n"
6
6
  ],
7
7
  "mappings": "iVAyBA,SAAsB,LAAgB,LAAC,JAA6C,LAClF,FAAM,JAAiB,FACjB,EAAQ,SAAW,OAAY,CAAE,OAAQ,EAAQ,MAAO,EAAI,CAAC,KAC7D,EAAQ,oBAAsB,OAC9B,CAAE,kBAAmB,EAAQ,iBAAkB,EAC/C,CAAC,KACD,EAAQ,MAAQ,OAAY,CAAE,IAAK,EAAQ,GAAI,EAAI,CAAC,CAC1D,EAEA,GAAI,EAAQ,KAAM,CAChB,IAAQ,YAAa,KAAa,0BAClC,OAAO,IAAI,EACT,IAAI,EAAS,EAAS,CACpB,cAAe,SAAY,EAAQ,SAAY,MAAM,EAAqB,CAAc,CAC1F,CAAC,EACD,EAAQ,QACR,EAAQ,KACV,EAGF,IAAM,EAAU,EAAQ,SAAY,MAAM,EAAqB,CAAc,GACrE,iBAAkB,KAAa,gCAEvC,OAAO,IAAI,EAAW,IAAI,EAAc,IAAK,EAAS,SAAQ,CAAC,EAAG,EAAQ,QAAS,EAAQ,KAAK",
8
- "debugId": "DE7394EDE2CB853864756E2164756E21",
8
+ "debugId": "A74F4D0E0003389764756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,3 @@
1
- import{I as R}from"../chunk-skqhj0rm.js";import{Q as G,S as M}from"../chunk-93vqxxj2.js";import{Y as L}from"../chunk-ywq6s10g.js";import{ca as N,da as Q}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class U extends N{statusCode;constructor(q,w,D){super(q,Q(w),{statusCode:w,provider:"brevo"});this.statusCode=w;this.name="BrevoError",this.code=D}}function J(q){return G(q).map((w)=>({email:w.address,...w.name?{name:w.name}:{}}))}class X{provider="brevo";apiKey;constructor(q){this.apiKey=q.apiKey}async send(q){let w=await R(q.attachments),D=G(q.from)[0],W={sender:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},to:J(q.to),subject:q.subject,...q.cc?{cc:J(q.cc)}:{},...q.bcc?{bcc:J(q.bcc)}:{},...q.replyTo?{replyTo:(()=>{let z=G(q.replyTo)[0];return z?{email:z.address,...z.name?{name:z.name}:{}}:void 0})()}:{},...q.html?{htmlContent:q.html}:{},...q.text?{textContent:q.text}:{},...w.length>0?{attachment:w.map((z)=>({name:z.filename,content:z.content instanceof Uint8Array?L(z.content).replace(/\r\n/g,""):z.content}))}:{}},H=await fetch("https://api.brevo.com/v3/smtp/email",{method:"POST",headers:{"api-key":this.apiKey,"Content-Type":"application/json"},body:JSON.stringify(W)}),F=await H.json();if(!H.ok)throw new U(F.message??"Brevo API error",H.status,F.code??"");let K=M(q.to);return{messageId:F.messageId??"",accepted:K,rejected:[],response:F.messageId??"sent",envelope:{from:D?.address??"",to:K}}}async verify(){try{let q=await fetch("https://api.brevo.com/v3/account",{headers:{"api-key":this.apiKey}}),w=await q.json();if(!q.ok)return{ok:!1,provider:"brevo",message:w.message??`HTTP ${q.status}`};return{ok:!0,provider:"brevo",...w.companyName?{message:w.companyName}:{}}}catch(q){return{ok:!1,provider:"brevo",message:q instanceof Error?q.message:String(q)}}}}export{X as BrevoTransport,U as BrevoError};
1
+ import{K as R}from"../chunk-1ke3nmyg.js";import{S as G,U as M}from"../chunk-8kpgbrba.js";import{da as L}from"../chunk-hnzmn4s4.js";import{ja as N,ka as Q}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class U extends N{statusCode;constructor(q,w,D){super(q,Q(w),{statusCode:w,provider:"brevo"});this.statusCode=w;this.name="BrevoError",this.code=D}}function J(q){return G(q).map((w)=>({email:w.address,...w.name?{name:w.name}:{}}))}class X{provider="brevo";apiKey;constructor(q){this.apiKey=q.apiKey}async send(q){let w=await R(q.attachments),D=G(q.from)[0],W={sender:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},to:J(q.to),subject:q.subject,...q.cc?{cc:J(q.cc)}:{},...q.bcc?{bcc:J(q.bcc)}:{},...q.replyTo?{replyTo:(()=>{let z=G(q.replyTo)[0];return z?{email:z.address,...z.name?{name:z.name}:{}}:void 0})()}:{},...q.html?{htmlContent:q.html}:{},...q.text?{textContent:q.text}:{},...w.length>0?{attachment:w.map((z)=>({name:z.filename,content:z.content instanceof Uint8Array?L(z.content).replace(/\r\n/g,""):z.content}))}:{}},H=await fetch("https://api.brevo.com/v3/smtp/email",{method:"POST",headers:{"api-key":this.apiKey,"Content-Type":"application/json"},body:JSON.stringify(W)}),F=await H.json();if(!H.ok)throw new U(F.message??"Brevo API error",H.status,F.code??"");let K=M(q.to);return{messageId:F.messageId??"",accepted:K,rejected:[],response:F.messageId??"sent",envelope:{from:D?.address??"",to:K}}}async verify(){try{let q=await fetch("https://api.brevo.com/v3/account",{headers:{"api-key":this.apiKey}}),w=await q.json();if(!q.ok)return{ok:!1,provider:"brevo",message:w.message??`HTTP ${q.status}`};return{ok:!0,provider:"brevo",...w.companyName?{message:w.companyName}:{}}}catch(q){return{ok:!1,provider:"brevo",message:q instanceof Error?q.message:String(q)}}}}export{X as BrevoTransport,U as BrevoError};
2
2
 
3
- //# debugId=E4625E2F9F65354364756E2164756E21
3
+ //# debugId=04DAEDC142021E4864756E2164756E21
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * @module\n * Brevo (formerly Sendinblue) HTTP transport for sently.\n *\n * @example\n * ```ts\n * import { BrevoTransport } from \"sently/transports/brevo\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new BrevoTransport({ apiKey: \"xkeysib-...\" }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type {\n BrevoConfig,\n MailOptions,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Error thrown when the Brevo API returns a non-success response. */\nexport class BrevoError extends SentlyError {\n /** Creates a Brevo API error with status code and error code. */\n constructor(\n message: string,\n public readonly statusCode: number,\n apiCode: string,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"brevo\",\n });\n this.name = \"BrevoError\";\n this.code = apiCode;\n }\n}\n\nfunction toAddressObjects(input: MailOptions[\"to\"]): Array<{ email: string; name?: string }> {\n return parseAddresses(input).map((addr) => ({\n email: addr.address,\n ...(addr.name ? { name: addr.name } : {}),\n }));\n}\n\n/**\n * Brevo HTTP API transport.\n */\nexport class BrevoTransport implements Transport {\n readonly provider = \"brevo\";\n\n /** Brevo API key used for Authorization. */\n private readonly apiKey: string;\n\n /** Creates a Brevo transport with the given API key. */\n constructor(config: BrevoConfig) {\n this.apiKey = config.apiKey;\n }\n\n /** Sends an email via the Brevo 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 body: Record<string, unknown> = {\n sender: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n to: toAddressObjects(options.to),\n subject: options.subject,\n ...(options.cc ? { cc: toAddressObjects(options.cc) } : {}),\n ...(options.bcc ? { bcc: toAddressObjects(options.bcc) } : {}),\n ...(options.replyTo\n ? {\n replyTo: (() => {\n const reply = parseAddresses(options.replyTo as MailOptions[\"to\"])[0];\n return reply\n ? { email: reply.address, ...(reply.name ? { name: reply.name } : {}) }\n : undefined;\n })(),\n }\n : {}),\n ...(options.html ? { htmlContent: options.html } : {}),\n ...(options.text ? { textContent: options.text } : {}),\n ...(attachments.length > 0\n ? {\n attachment: attachments.map((att) => ({\n name: att.filename,\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n })),\n }\n : {}),\n };\n\n const response = await fetch(\"https://api.brevo.com/v3/smtp/email\", {\n method: \"POST\",\n headers: {\n \"api-key\": this.apiKey,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(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 BrevoError(\n payload.message ?? \"Brevo API error\",\n response.status,\n payload.code ?? \"\",\n );\n }\n\n const toEmails = extractEmails(options.to);\n return {\n messageId: payload.messageId ?? \"\",\n accepted: toEmails,\n rejected: [],\n response: payload.messageId ?? \"sent\",\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies the Brevo API key by fetching account info. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(\"https://api.brevo.com/v3/account\", {\n headers: {\n \"api-key\": this.apiKey,\n },\n });\n\n const payload = (await response.json()) as { companyName?: string; message?: string };\n\n if (!response.ok) {\n return {\n ok: false,\n provider: \"brevo\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return {\n ok: true,\n provider: \"brevo\",\n ...(payload.companyName ? { message: payload.companyName } : {}),\n };\n } catch (err) {\n return {\n ok: false,\n provider: \"brevo\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
6
6
  ],
7
- "mappings": "qOA2BO,CAAM,KAAmB,JAAY,JAIxB,WAFlB,WAAW,CACT,EACgB,EAChB,EACA,CACA,MAAM,EAAS,EAAuB,CAAU,EAAG,CACjD,aACA,SAAU,OACZ,CAAC,EANe,kBAOhB,KAAK,KAAO,aACZ,KAAK,KAAO,EAEhB,CAEA,SAAS,CAAgB,CAAC,EAAmE,CAC3F,OAAO,EAAe,CAAK,EAAE,IAAI,CAAC,KAAU,CAC1C,MAAO,EAAK,WACR,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CACzC,EAAE,EAMG,MAAM,CAAoC,CACtC,SAAW,QAGH,OAGjB,WAAW,CAAC,EAAqB,CAC/B,KAAK,OAAS,EAAO,YAIjB,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAO,EAAe,EAAQ,IAAI,EAAE,GAEpC,EAAgC,CACpC,OAAQ,EACJ,CAAE,MAAO,EAAK,WAAa,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CAAG,EACjE,CAAE,MAAO,EAAG,EAChB,GAAI,EAAiB,EAAQ,EAAE,EAC/B,QAAS,EAAQ,WACb,EAAQ,GAAK,CAAE,GAAI,EAAiB,EAAQ,EAAE,CAAE,EAAI,CAAC,KACrD,EAAQ,IAAM,CAAE,IAAK,EAAiB,EAAQ,GAAG,CAAE,EAAI,CAAC,KACxD,EAAQ,QACR,CACE,SAAU,IAAM,CACd,IAAM,EAAQ,EAAe,EAAQ,OAA4B,EAAE,GACnE,OAAO,EACH,CAAE,MAAO,EAAM,WAAa,EAAM,KAAO,CAAE,KAAM,EAAM,IAAK,EAAI,CAAC,CAAG,EACpE,SACH,CACL,EACA,CAAC,KACD,EAAQ,KAAO,CAAE,YAAa,EAAQ,IAAK,EAAI,CAAC,KAChD,EAAQ,KAAO,CAAE,YAAa,EAAQ,IAAK,EAAI,CAAC,KAChD,EAAY,OAAS,EACrB,CACE,WAAY,EAAY,IAAI,CAAC,KAAS,CACpC,KAAM,EAAI,SACV,QACE,EAAI,mBAAmB,WACnB,EAAa,EAAI,OAAO,EAAE,QAAQ,QAAS,EAAE,EAC7C,EAAI,OACZ,EAAE,CACJ,EACA,CAAC,CACP,EAEM,EAAW,MAAM,MAAM,sCAAuC,CAClE,OAAQ,OACR,QAAS,CACP,UAAW,KAAK,OAChB,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAMrC,GAAI,CAAC,EAAS,GACZ,MAAM,IAAI,EACR,EAAQ,SAAW,kBACnB,EAAS,OACT,EAAQ,MAAQ,EAClB,EAGF,IAAM,EAAW,EAAc,EAAQ,EAAE,EACzC,MAAO,CACL,UAAW,EAAQ,WAAa,GAChC,SAAU,EACV,SAAU,CAAC,EACX,SAAU,EAAQ,WAAa,OAC/B,SAAU,CACR,KAAM,GAAM,SAAW,GACvB,GAAI,CACN,CACF,OAII,OAAM,EAA0B,CACpC,GAAI,CACF,IAAM,EAAW,MAAM,MAAM,mCAAoC,CAC/D,QAAS,CACP,UAAW,KAAK,MAClB,CACF,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAErC,GAAI,CAAC,EAAS,GACZ,MAAO,CACL,GAAI,GACJ,SAAU,QACV,QAAS,EAAQ,SAAW,QAAQ,EAAS,QAC/C,EAGF,MAAO,CACL,GAAI,GACJ,SAAU,WACN,EAAQ,YAAc,CAAE,QAAS,EAAQ,WAAY,EAAI,CAAC,CAChE,EACA,MAAO,EAAK,CACZ,MAAO,CACL,GAAI,GACJ,SAAU,QACV,QAAS,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,CAC1D,GAGN",
8
- "debugId": "E4625E2F9F65354364756E2164756E21",
7
+ "mappings": "sOA2BO,CAAM,KAAmB,JAAY,JAIxB,WAFlB,WAAW,CACT,EACgB,EAChB,EACA,CACA,MAAM,EAAS,EAAuB,CAAU,EAAG,CACjD,aACA,SAAU,OACZ,CAAC,EANe,kBAOhB,KAAK,KAAO,aACZ,KAAK,KAAO,EAEhB,CAEA,SAAS,CAAgB,CAAC,EAAmE,CAC3F,OAAO,EAAe,CAAK,EAAE,IAAI,CAAC,KAAU,CAC1C,MAAO,EAAK,WACR,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CACzC,EAAE,EAMG,MAAM,CAAoC,CACtC,SAAW,QAGH,OAGjB,WAAW,CAAC,EAAqB,CAC/B,KAAK,OAAS,EAAO,YAIjB,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAO,EAAe,EAAQ,IAAI,EAAE,GAEpC,EAAgC,CACpC,OAAQ,EACJ,CAAE,MAAO,EAAK,WAAa,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CAAG,EACjE,CAAE,MAAO,EAAG,EAChB,GAAI,EAAiB,EAAQ,EAAE,EAC/B,QAAS,EAAQ,WACb,EAAQ,GAAK,CAAE,GAAI,EAAiB,EAAQ,EAAE,CAAE,EAAI,CAAC,KACrD,EAAQ,IAAM,CAAE,IAAK,EAAiB,EAAQ,GAAG,CAAE,EAAI,CAAC,KACxD,EAAQ,QACR,CACE,SAAU,IAAM,CACd,IAAM,EAAQ,EAAe,EAAQ,OAA4B,EAAE,GACnE,OAAO,EACH,CAAE,MAAO,EAAM,WAAa,EAAM,KAAO,CAAE,KAAM,EAAM,IAAK,EAAI,CAAC,CAAG,EACpE,SACH,CACL,EACA,CAAC,KACD,EAAQ,KAAO,CAAE,YAAa,EAAQ,IAAK,EAAI,CAAC,KAChD,EAAQ,KAAO,CAAE,YAAa,EAAQ,IAAK,EAAI,CAAC,KAChD,EAAY,OAAS,EACrB,CACE,WAAY,EAAY,IAAI,CAAC,KAAS,CACpC,KAAM,EAAI,SACV,QACE,EAAI,mBAAmB,WACnB,EAAa,EAAI,OAAO,EAAE,QAAQ,QAAS,EAAE,EAC7C,EAAI,OACZ,EAAE,CACJ,EACA,CAAC,CACP,EAEM,EAAW,MAAM,MAAM,sCAAuC,CAClE,OAAQ,OACR,QAAS,CACP,UAAW,KAAK,OAChB,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAMrC,GAAI,CAAC,EAAS,GACZ,MAAM,IAAI,EACR,EAAQ,SAAW,kBACnB,EAAS,OACT,EAAQ,MAAQ,EAClB,EAGF,IAAM,EAAW,EAAc,EAAQ,EAAE,EACzC,MAAO,CACL,UAAW,EAAQ,WAAa,GAChC,SAAU,EACV,SAAU,CAAC,EACX,SAAU,EAAQ,WAAa,OAC/B,SAAU,CACR,KAAM,GAAM,SAAW,GACvB,GAAI,CACN,CACF,OAII,OAAM,EAA0B,CACpC,GAAI,CACF,IAAM,EAAW,MAAM,MAAM,mCAAoC,CAC/D,QAAS,CACP,UAAW,KAAK,MAClB,CACF,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAErC,GAAI,CAAC,EAAS,GACZ,MAAO,CACL,GAAI,GACJ,SAAU,QACV,QAAS,EAAQ,SAAW,QAAQ,EAAS,QAC/C,EAGF,MAAO,CACL,GAAI,GACJ,SAAU,WACN,EAAQ,YAAc,CAAE,QAAS,EAAQ,WAAY,EAAI,CAAC,CAChE,EACA,MAAO,EAAK,CACZ,MAAO,CACL,GAAI,GACJ,SAAU,QACV,QAAS,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,CAC1D,GAGN",
8
+ "debugId": "04DAEDC142021E4864756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,3 @@
1
- import{I as O}from"../chunk-skqhj0rm.js";import{Q as M,S as G}from"../chunk-93vqxxj2.js";import{Y as L}from"../chunk-ywq6s10g.js";import{ca as N}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class I extends N{constructor(q,D){super(q,"PROVIDER_ERROR",{provider:"cloudflare-email",cause:D});this.name="CloudflareEmailError"}}class Q{provider="cloudflare-email";sendEmail;constructor(q){this.sendEmail=q.sendEmail}async send(q){let D=M(q.from)[0],H=G(q.to),J=H[0];if(!D||!J)throw new I("Cloudflare Email requires from and to addresses");let F=[];if(q.text!==void 0)F.push({type:"text/plain",value:q.text});if(q.html!==void 0)F.push({type:"text/html",value:q.html});if(F.length===0)F.push({type:"text/plain",value:""});let K=await O(q.attachments),P={from:D.address,to:J,subject:q.subject,content:F,...q.replyTo?{reply_to:G(q.replyTo)[0]}:{},...q.cc?{cc:G(q.cc)}:{},...q.bcc?{bcc:G(q.bcc)}:{},...q.headers?{headers:q.headers}:{},...K.length>0?{attachments:K.map((z)=>({disposition:z.inline?"inline":"attachment",filename:z.filename,type:z.contentType??"application/octet-stream",content:z.content instanceof Uint8Array?L(z.content).replace(/\r\n/g,""):String(z.content??"")}))}:{}};try{await this.sendEmail(P)}catch(z){throw new I(z instanceof Error?z.message:"Cloudflare Email send failed",z)}return{messageId:q.messageId??"",accepted:H,rejected:[],response:"sent via Cloudflare Email binding",envelope:{from:D.address,to:H}}}async verify(){return{ok:!0,provider:"cloudflare-email",message:"send_email binding configured (not validated at runtime)"}}}export{Q as CloudflareEmailTransport,I as CloudflareEmailError};
1
+ import{K as O}from"../chunk-1ke3nmyg.js";import{S as M,U as G}from"../chunk-8kpgbrba.js";import{da as L}from"../chunk-hnzmn4s4.js";import{ja as N}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class I extends N{constructor(q,D){super(q,"PROVIDER_ERROR",{provider:"cloudflare-email",cause:D});this.name="CloudflareEmailError"}}class Q{provider="cloudflare-email";sendEmail;constructor(q){this.sendEmail=q.sendEmail}async send(q){let D=M(q.from)[0],H=G(q.to),J=H[0];if(!D||!J)throw new I("Cloudflare Email requires from and to addresses");let F=[];if(q.text!==void 0)F.push({type:"text/plain",value:q.text});if(q.html!==void 0)F.push({type:"text/html",value:q.html});if(F.length===0)F.push({type:"text/plain",value:""});let K=await O(q.attachments),P={from:D.address,to:J,subject:q.subject,content:F,...q.replyTo?{reply_to:G(q.replyTo)[0]}:{},...q.cc?{cc:G(q.cc)}:{},...q.bcc?{bcc:G(q.bcc)}:{},...q.headers?{headers:q.headers}:{},...K.length>0?{attachments:K.map((z)=>({disposition:z.inline?"inline":"attachment",filename:z.filename,type:z.contentType??"application/octet-stream",content:z.content instanceof Uint8Array?L(z.content).replace(/\r\n/g,""):String(z.content??"")}))}:{}};try{await this.sendEmail(P)}catch(z){throw new I(z instanceof Error?z.message:"Cloudflare Email send failed",z)}return{messageId:q.messageId??"",accepted:H,rejected:[],response:"sent via Cloudflare Email binding",envelope:{from:D.address,to:H}}}async verify(){return{ok:!0,provider:"cloudflare-email",message:"send_email binding configured (not validated at runtime)"}}}export{Q as CloudflareEmailTransport,I as CloudflareEmailError};
2
2
 
3
- //# debugId=12AACAC7726534CE64756E2164756E21
3
+ //# debugId=219D40A6BE4108D664756E2164756E21
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * @module\n * Cloudflare Workers Email transport — wraps the `send_email` binding\n * configured in `wrangler.toml`, not a fetch-based HTTP API.\n *\n * @example\n * ```ts\n * // wrangler.toml: [[send_email]]\n * import { CloudflareEmailTransport } from \"sently/transports/cloudflare-email\";\n * import { createMailer } from \"sently/mailer\";\n *\n * export default {\n * async fetch(request, env) {\n * const mailer = await createMailer({\n * transport: new CloudflareEmailTransport({ sendEmail: env.SEND_EMAIL }),\n * });\n * await mailer.send({ from: \"...\", to: \"...\", subject: \"...\", text: \"...\" });\n * },\n * };\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { SentlyError } from \"../core/errors.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Message shape accepted by the Cloudflare Workers `send_email` binding. */\nexport interface CloudflareEmailMessage {\n /** Envelope sender. */\n from: string;\n /** Primary recipient. */\n to: string;\n /** Message subject. */\n subject?: string;\n /** Reply-To address. */\n reply_to?: string;\n /** Carbon-copy recipients. */\n cc?: string[];\n /** Blind carbon-copy recipients. */\n bcc?: string[];\n /** Additional MIME headers. */\n headers?: Record<string, string>;\n /** Message body parts. */\n content: Array<{ type: string; value: string }>;\n /** Optional attachments (base64 content). */\n attachments?: Array<{\n disposition: \"attachment\" | \"inline\";\n filename: string;\n type: string;\n content: string;\n }>;\n}\n\n/** Callable Workers `send_email` binding. */\nexport type CloudflareSendEmailFn = (message: CloudflareEmailMessage) => Promise<void>;\n\n/** Cloudflare Workers Email binding configuration. */\nexport interface CloudflareEmailConfig {\n /** The Workers `send_email` binding (e.g. `env.SEND_EMAIL`). */\n sendEmail: CloudflareSendEmailFn;\n}\n\n/** Error thrown when the Cloudflare Email binding rejects a send. */\nexport class CloudflareEmailError extends SentlyError {\n /** Creates a Cloudflare Email binding error. */\n constructor(message: string, cause?: unknown) {\n super(message, \"PROVIDER_ERROR\", { provider: \"cloudflare-email\", cause });\n this.name = \"CloudflareEmailError\";\n }\n}\n\n/**\n * Cloudflare Workers Email binding transport.\n */\nexport class CloudflareEmailTransport implements Transport {\n readonly provider = \"cloudflare-email\";\n\n private readonly sendEmail: CloudflareSendEmailFn;\n\n /** Creates a transport backed by the Workers `send_email` binding. */\n constructor(config: CloudflareEmailConfig) {\n this.sendEmail = config.sendEmail;\n }\n\n /** Sends an email via the Workers `send_email` binding. */\n async send(options: MailOptions): Promise<SendResult> {\n const from = parseAddresses(options.from)[0];\n const toEmails = extractEmails(options.to);\n const primaryTo = toEmails[0];\n\n if (!from || !primaryTo) {\n throw new CloudflareEmailError(\"Cloudflare Email requires from and to addresses\");\n }\n\n const content: CloudflareEmailMessage[\"content\"] = [];\n if (options.text !== undefined) {\n content.push({ type: \"text/plain\", value: options.text });\n }\n if (options.html !== undefined) {\n content.push({ type: \"text/html\", value: options.html });\n }\n if (content.length === 0) {\n content.push({ type: \"text/plain\", value: \"\" });\n }\n\n const attachments = await resolveAttachments(options.attachments);\n const message: CloudflareEmailMessage = {\n from: from.address,\n to: primaryTo,\n subject: options.subject,\n content,\n ...(options.replyTo ? { reply_to: extractEmails(options.replyTo)[0] } : {}),\n ...(options.cc ? { cc: extractEmails(options.cc) } : {}),\n ...(options.bcc ? { bcc: extractEmails(options.bcc) } : {}),\n ...(options.headers ? { headers: options.headers } : {}),\n ...(attachments.length > 0\n ? {\n attachments: attachments.map((att) => ({\n disposition: att.inline ? (\"inline\" as const) : (\"attachment\" as const),\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 : String(att.content ?? \"\"),\n })),\n }\n : {}),\n };\n\n try {\n await this.sendEmail(message);\n } catch (err) {\n throw new CloudflareEmailError(\n err instanceof Error ? err.message : \"Cloudflare Email send failed\",\n err,\n );\n }\n\n return {\n messageId: options.messageId ?? \"\",\n accepted: toEmails,\n rejected: [],\n response: \"sent via Cloudflare Email binding\",\n envelope: {\n from: from.address,\n to: toEmails,\n },\n };\n }\n\n /**\n * Confirms the binding is configured — no network validation is available.\n */\n async verify(): Promise<VerifyResult> {\n return {\n ok: true,\n provider: \"cloudflare-email\",\n message: \"send_email binding configured (not validated at runtime)\",\n };\n }\n}\n"
6
6
  ],
7
- "mappings": "6NAgEO,CAAM,KAA6B,JAAY,JAEpD,WAAW,CAAC,EAAiB,EAAiB,CAC5C,MAAM,EAAS,iBAAkB,CAAE,SAAU,mBAAoB,OAAM,CAAC,EACxE,KAAK,KAAO,uBAEhB,CAKO,MAAM,CAA8C,CAChD,SAAW,mBAEH,UAGjB,WAAW,CAAC,EAA+B,CACzC,KAAK,UAAY,EAAO,eAIpB,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAO,EAAe,EAAQ,IAAI,EAAE,GACpC,EAAW,EAAc,EAAQ,EAAE,EACnC,EAAY,EAAS,GAE3B,GAAI,CAAC,GAAQ,CAAC,EACZ,MAAM,IAAI,EAAqB,iDAAiD,EAGlF,IAAM,EAA6C,CAAC,EACpD,GAAI,EAAQ,OAAS,OACnB,EAAQ,KAAK,CAAE,KAAM,aAAc,MAAO,EAAQ,IAAK,CAAC,EAE1D,GAAI,EAAQ,OAAS,OACnB,EAAQ,KAAK,CAAE,KAAM,YAAa,MAAO,EAAQ,IAAK,CAAC,EAEzD,GAAI,EAAQ,SAAW,EACrB,EAAQ,KAAK,CAAE,KAAM,aAAc,MAAO,EAAG,CAAC,EAGhD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAkC,CACtC,KAAM,EAAK,QACX,GAAI,EACJ,QAAS,EAAQ,QACjB,aACI,EAAQ,QAAU,CAAE,SAAU,EAAc,EAAQ,OAAO,EAAE,EAAG,EAAI,CAAC,KACrE,EAAQ,GAAK,CAAE,GAAI,EAAc,EAAQ,EAAE,CAAE,EAAI,CAAC,KAClD,EAAQ,IAAM,CAAE,IAAK,EAAc,EAAQ,GAAG,CAAE,EAAI,CAAC,KACrD,EAAQ,QAAU,CAAE,QAAS,EAAQ,OAAQ,EAAI,CAAC,KAClD,EAAY,OAAS,EACrB,CACE,YAAa,EAAY,IAAI,CAAC,KAAS,CACrC,YAAa,EAAI,OAAU,SAAsB,aACjD,SAAU,EAAI,SACd,KAAM,EAAI,aAAe,2BACzB,QACE,EAAI,mBAAmB,WACnB,EAAa,EAAI,OAAO,EAAE,QAAQ,QAAS,EAAE,EAC7C,OAAO,EAAI,SAAW,EAAE,CAChC,EAAE,CACJ,EACA,CAAC,CACP,EAEA,GAAI,CACF,MAAM,KAAK,UAAU,CAAO,EAC5B,MAAO,EAAK,CACZ,MAAM,IAAI,EACR,aAAe,MAAQ,EAAI,QAAU,+BACrC,CACF,EAGF,MAAO,CACL,UAAW,EAAQ,WAAa,GAChC,SAAU,EACV,SAAU,CAAC,EACX,SAAU,oCACV,SAAU,CACR,KAAM,EAAK,QACX,GAAI,CACN,CACF,OAMI,OAAM,EAA0B,CACpC,MAAO,CACL,GAAI,GACJ,SAAU,mBACV,QAAS,0DACX,EAEJ",
8
- "debugId": "12AACAC7726534CE64756E2164756E21",
7
+ "mappings": "8NAgEO,CAAM,KAA6B,JAAY,JAEpD,WAAW,CAAC,EAAiB,EAAiB,CAC5C,MAAM,EAAS,iBAAkB,CAAE,SAAU,mBAAoB,OAAM,CAAC,EACxE,KAAK,KAAO,uBAEhB,CAKO,MAAM,CAA8C,CAChD,SAAW,mBAEH,UAGjB,WAAW,CAAC,EAA+B,CACzC,KAAK,UAAY,EAAO,eAIpB,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAO,EAAe,EAAQ,IAAI,EAAE,GACpC,EAAW,EAAc,EAAQ,EAAE,EACnC,EAAY,EAAS,GAE3B,GAAI,CAAC,GAAQ,CAAC,EACZ,MAAM,IAAI,EAAqB,iDAAiD,EAGlF,IAAM,EAA6C,CAAC,EACpD,GAAI,EAAQ,OAAS,OACnB,EAAQ,KAAK,CAAE,KAAM,aAAc,MAAO,EAAQ,IAAK,CAAC,EAE1D,GAAI,EAAQ,OAAS,OACnB,EAAQ,KAAK,CAAE,KAAM,YAAa,MAAO,EAAQ,IAAK,CAAC,EAEzD,GAAI,EAAQ,SAAW,EACrB,EAAQ,KAAK,CAAE,KAAM,aAAc,MAAO,EAAG,CAAC,EAGhD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAkC,CACtC,KAAM,EAAK,QACX,GAAI,EACJ,QAAS,EAAQ,QACjB,aACI,EAAQ,QAAU,CAAE,SAAU,EAAc,EAAQ,OAAO,EAAE,EAAG,EAAI,CAAC,KACrE,EAAQ,GAAK,CAAE,GAAI,EAAc,EAAQ,EAAE,CAAE,EAAI,CAAC,KAClD,EAAQ,IAAM,CAAE,IAAK,EAAc,EAAQ,GAAG,CAAE,EAAI,CAAC,KACrD,EAAQ,QAAU,CAAE,QAAS,EAAQ,OAAQ,EAAI,CAAC,KAClD,EAAY,OAAS,EACrB,CACE,YAAa,EAAY,IAAI,CAAC,KAAS,CACrC,YAAa,EAAI,OAAU,SAAsB,aACjD,SAAU,EAAI,SACd,KAAM,EAAI,aAAe,2BACzB,QACE,EAAI,mBAAmB,WACnB,EAAa,EAAI,OAAO,EAAE,QAAQ,QAAS,EAAE,EAC7C,OAAO,EAAI,SAAW,EAAE,CAChC,EAAE,CACJ,EACA,CAAC,CACP,EAEA,GAAI,CACF,MAAM,KAAK,UAAU,CAAO,EAC5B,MAAO,EAAK,CACZ,MAAM,IAAI,EACR,aAAe,MAAQ,EAAI,QAAU,+BACrC,CACF,EAGF,MAAO,CACL,UAAW,EAAQ,WAAa,GAChC,SAAU,EACV,SAAU,CAAC,EACX,SAAU,oCACV,SAAU,CACR,KAAM,EAAK,QACX,GAAI,CACN,CACF,OAMI,OAAM,EAA0B,CACpC,MAAO,CACL,GAAI,GACJ,SAAU,mBACV,QAAS,0DACX,EAEJ",
8
+ "debugId": "219D40A6BE4108D664756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,5 +1,30 @@
1
+ /**
2
+ * @module
3
+ * FallbackTransport — routes through an ordered list of transports,
4
+ * advancing to the next when the current one fails. Use for provider
5
+ * failover across email, SMS, WhatsApp, or push. Composes with
6
+ * RetryTransport — wrap each entry in RetryTransport to retry within a
7
+ * provider before failing over to the next.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { FallbackTransport } from "sently/transports/fallback";
12
+ * import { RetryTransport } from "sently/transports/retry";
13
+ * import { ResendTransport } from "sently/transports/resend";
14
+ * import { SESTransport } from "sently/transports/ses";
15
+ *
16
+ * const transport = new FallbackTransport(
17
+ * [
18
+ * new RetryTransport(new ResendTransport({ apiKey })),
19
+ * new RetryTransport(new SESTransport({ accessKeyId, secretAccessKey })),
20
+ * ],
21
+ * { cooldownMs: 300_000 },
22
+ * );
23
+ * ```
24
+ */
25
+ import type { DecoratedTransport, FallbackAugmentedResult } from "../core/decorated-transport.js";
1
26
  import { getProviderLabel } from "../core/provider-label.js";
2
- import type { MailOptions, SendResult, Transport, VerifyResult } from "../core/types.js";
27
+ import type { MailOptions, SendResult, VerifyResult } from "../core/types.js";
3
28
  /** One failed provider attempt collected during failover. */
4
29
  export interface FallbackAttempt {
5
30
  /** Provider label (e.g. `"resend"`, `"ses"`). */
@@ -62,8 +87,9 @@ export declare const inferProviderLabel: typeof getProviderLabel;
62
87
  type MailerOnFallback = (failedProvider: string, nextProvider: string, error: unknown) => void | Promise<void>;
63
88
  /**
64
89
  * Decorator transport that fails over through an ordered list of transports.
90
+ * Defaults preserve email {@link Transport} compatibility.
65
91
  */
66
- export declare class FallbackTransport implements Transport {
92
+ export declare class FallbackTransport<TOptions = MailOptions, TResult = SendResult> implements DecoratedTransport<TOptions, FallbackAugmentedResult<TResult>> {
67
93
  private readonly transports;
68
94
  readonly provider = "fallback";
69
95
  private readonly shouldFallback;
@@ -76,16 +102,16 @@ export declare class FallbackTransport implements Transport {
76
102
  * Creates a failover transport over an ordered list of backends.
77
103
  * @throws {Error} When the transports array is empty.
78
104
  */
79
- constructor(transports: Transport[], options?: FallbackOptions);
105
+ constructor(transports: DecoratedTransport<TOptions, TResult>[], options?: FallbackOptions);
80
106
  /**
81
- * Register a per-send onFallback callback from mailer lifecycle hooks.
82
- * @internal Used by {@link MailerImpl}; cleared after each send.
107
+ * Register a per-send onFallback callback from sender lifecycle hooks.
108
+ * @internal Cleared after each send.
83
109
  */
84
110
  setMailerOnFallback(callback: MailerOnFallback | undefined): void;
85
111
  private isCoolingDown;
86
112
  private markUnhealthy;
87
113
  /** Sends through transports in order until one succeeds. */
88
- send(message: MailOptions): Promise<SendResult>;
114
+ send(message: TOptions): Promise<FallbackAugmentedResult<TResult>>;
89
115
  private findNextTryIndex;
90
116
  /**
91
117
  * Returns the result of the first transport whose verify() returns `ok: true`.
@@ -1,3 +1,3 @@
1
- import{i as a,j as b,k as c}from"../chunk-0gqy32pe.js";import"../chunk-gp4fwrs8.js";import"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";export{b as inferProviderLabel,c as FallbackTransport,a as FallbackError};
1
+ import{k as a,l as b,m as c}from"../chunk-q2mahnb7.js";import"../chunk-cs1tvehn.js";import"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";export{b as inferProviderLabel,c as FallbackTransport,a as FallbackError};
2
2
 
3
- //# debugId=2A42FFD9B78F28F364756E2164756E21
3
+ //# debugId=B2A432F75D9CF77B64756E2164756E21
@@ -4,6 +4,6 @@
4
4
  "sourcesContent": [
5
5
  ],
6
6
  "mappings": "",
7
- "debugId": "2A42FFD9B78F28F364756E2164756E21",
7
+ "debugId": "B2A432F75D9CF77B64756E2164756E21",
8
8
  "names": []
9
9
  }
@@ -0,0 +1,55 @@
1
+ import { SentlyError } from "../core/errors.js";
2
+ import type { PushOptions, PushSendResult, PushTransport } from "../core/push-types.js";
3
+ import type { VerifyResult } from "../core/types.js";
4
+ /** Firebase service-account credentials for FCM HTTP v1. */
5
+ export interface FcmConfig {
6
+ /** Firebase / GCP project ID. */
7
+ projectId: string;
8
+ /**
9
+ * Service account `client_email`.
10
+ * Store in environment variables — never hardcode.
11
+ */
12
+ clientEmail: string;
13
+ /**
14
+ * Service account PEM private key (`-----BEGIN PRIVATE KEY-----`…).
15
+ * Treat as a tier-1 secret — inject from env / secrets manager only.
16
+ * Literal `\n` sequences in env strings are normalized to newlines.
17
+ */
18
+ privateKey: string;
19
+ /**
20
+ * Optional injectable access-token provider (testing / custom auth).
21
+ * When set, service-account JWT exchange is skipped.
22
+ */
23
+ getAccessToken?: () => Promise<string>;
24
+ }
25
+ /** Error thrown when the FCM HTTP v1 API rejects a request. */
26
+ export declare class FcmError extends SentlyError {
27
+ readonly statusCode: number;
28
+ readonly apiError: unknown;
29
+ /** Creates an FCM API error with status code and response payload. */
30
+ constructor(message: string, statusCode: number, apiError: unknown);
31
+ }
32
+ /**
33
+ * FCM HTTP v1 transport — device-token push for Android / iOS / web clients.
34
+ */
35
+ export declare class FcmTransport implements PushTransport {
36
+ readonly provider = "fcm";
37
+ private readonly projectId;
38
+ private readonly clientEmail;
39
+ private readonly privateKeyPem;
40
+ private readonly getAccessTokenOverride;
41
+ private cached;
42
+ private cryptoKey;
43
+ /** Creates an FCM transport from service-account credentials. */
44
+ constructor(config: FcmConfig);
45
+ /**
46
+ * Sends via `POST https://fcm.googleapis.com/v1/projects/{projectId}/messages:send`.
47
+ * Requires {@link FcmPushOptions} (`token`).
48
+ */
49
+ send(options: PushOptions): Promise<PushSendResult>;
50
+ /** Lightweight credential check (does not call Google). */
51
+ verify(): Promise<VerifyResult>;
52
+ private getAccessToken;
53
+ private signServiceAccountJwt;
54
+ private getCryptoKey;
55
+ }
@@ -0,0 +1,4 @@
1
+ import{Z as M}from"../chunk-z1589fjk.js";import{ga as z,ia as G}from"../chunk-hnzmn4s4.js";import{ja as D,ka as K}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";var N="https://www.googleapis.com/auth/firebase.messaging",H="https://oauth2.googleapis.com/token";class Z extends D{statusCode;apiError;constructor(q,V,J){super(q,K(V),{statusCode:V,provider:"fcm",cause:J});this.statusCode=V;this.apiError=J;this.name="FcmError"}}class R{provider="fcm";projectId;clientEmail;privateKeyPem;getAccessTokenOverride;cached;cryptoKey;constructor(q){this.projectId=q.projectId,this.clientEmail=q.clientEmail,this.privateKeyPem=L(q.privateKey),this.getAccessTokenOverride=q.getAccessToken}async send(q){if(!M(q))throw new Z("FcmTransport requires PushOptions.token; use WebPushTransport for browser subscriptions",400,{hint:"webpush"});if(q.token.trim().length===0)throw new Z("FCM device token must not be empty",400,{field:"token"});let V=await this.getAccessToken(),J=v(q),W=`https://fcm.googleapis.com/v1/projects/${encodeURIComponent(this.projectId)}/messages:send`,Q=await fetch(W,{method:"POST",headers:{Authorization:`Bearer ${V}`,"Content-Type":"application/json"},body:JSON.stringify({message:J})}),X=await Q.text(),Y;try{Y=X.length>0?JSON.parse(X):{}}catch{throw new Z("FCM returned non-JSON response",Q.status,X)}if(!Q.ok)throw new Z(Y.error?.message??`FCM API error (${Q.status})`,Q.status,Y);let $=typeof Y.name==="string"?Y.name:"";return{messageId:$.split("/").pop()||q.messageId||$||crypto.randomUUID(),status:"accepted",response:$||String(Q.status),provider:"fcm"}}async verify(){if(this.getAccessTokenOverride)return{ok:!0,provider:"fcm",message:"Custom getAccessToken configured"};let q=Boolean(this.projectId&&this.clientEmail&&this.privateKeyPem);return{ok:q,provider:"fcm",message:q?"Credentials present":"Missing projectId, clientEmail, or privateKey"}}async getAccessToken(){if(this.getAccessTokenOverride)return this.getAccessTokenOverride();let q=Date.now();if(this.cached&&q<this.cached.expiresAtMs-60000)return this.cached.accessToken;let V=await this.signServiceAccountJwt(),J=new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:jwt-bearer",assertion:V}),W=await fetch(H,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:J.toString()}),Q=await W.text(),X;try{X=Q.length>0?JSON.parse(Q):{}}catch{throw new Z("OAuth token endpoint returned non-JSON",W.status,Q)}if(!W.ok||typeof X.access_token!=="string")throw new Z(X.error??"Failed to obtain FCM access token",W.status,X);let Y=typeof X.expires_in==="number"?X.expires_in:3600;return this.cached={accessToken:X.access_token,expiresAtMs:q+Y*1000},X.access_token}async signServiceAccountJwt(){let q=Math.floor(Date.now()/1000),V={alg:"RS256",typ:"JWT"},J={iss:this.clientEmail,scope:N,aud:H,iat:q,exp:q+3600},W=z(G(JSON.stringify(V))),Q=z(G(JSON.stringify(J))),X=`${W}.${Q}`,Y=await this.getCryptoKey(),$=await crypto.subtle.sign({name:"RSASSA-PKCS1-v1_5"},Y,A(G(X)));return`${X}.${z(new Uint8Array($))}`}async getCryptoKey(){if(this.cryptoKey)return this.cryptoKey;let q=P(this.privateKeyPem);return this.cryptoKey=await crypto.subtle.importKey("pkcs8",A(q),{name:"RSASSA-PKCS1-v1_5",hash:"SHA-256"},!1,["sign"]),this.cryptoKey}}function v(q){let V={title:q.title,body:q.body};if(q.image!==void 0)V.image=q.image;let J={token:q.token,notification:V};if(q.data!==void 0)J.data=I(q.data);if(q.ttl!==void 0)J.android={ttl:`${q.ttl}s`};return J}function I(q){let V={};for(let[J,W]of Object.entries(q))V[J]=typeof W==="string"?W:JSON.stringify(W);return V}function L(q){return q.includes("\\n")?q.replace(/\\n/g,`
2
+ `):q}function P(q){let V=q.replace(/-----BEGIN[^-]+-----/g,"").replace(/-----END[^-]+-----/g,"").replace(/\s/g,""),J=atob(V),W=new Uint8Array(J.length);for(let Q=0;Q<J.length;Q++)W[Q]=J.charCodeAt(Q);return W}function A(q){return q.buffer.slice(q.byteOffset,q.byteOffset+q.byteLength)}export{R as FcmTransport,Z as FcmError};
3
+
4
+ //# debugId=888F02F364B1BF2564756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/transports/fcm.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * @module\n * Firebase Cloud Messaging HTTP v1 transport for mobile push.\n *\n * Authenticates with a Google service-account JWT (RS256) exchanged for an\n * OAuth2 access token — zero runtime dependencies.\n *\n * Sently-first: wire into {@link createPushSender} with {@link FcmPushOptions}\n * (`token`, not a Web Push subscription).\n *\n * @example\n * ```ts\n * import { createPushSender } from \"sently/push\";\n * import { FcmTransport } from \"sently/transports/fcm\";\n *\n * const push = createPushSender({\n * transport: new FcmTransport({\n * projectId: process.env.FCM_PROJECT_ID!,\n * clientEmail: process.env.FCM_CLIENT_EMAIL!,\n * privateKey: process.env.FCM_PRIVATE_KEY!,\n * }),\n * });\n *\n * await push.send({\n * token: deviceToken,\n * title: \"Hello\",\n * body: \"World\",\n * });\n * ```\n */\nimport { encodeBase64Url, encodeUtf8 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type {\n FcmPushOptions,\n PushOptions,\n PushSendResult,\n PushTransport,\n} from \"../core/push-types.js\";\nimport { isFcmPushOptions } from \"../core/push-types.js\";\nimport type { VerifyResult } from \"../core/types.js\";\n\nconst FCM_SCOPE = \"https://www.googleapis.com/auth/firebase.messaging\";\nconst TOKEN_URL = \"https://oauth2.googleapis.com/token\";\n\n/** Firebase service-account credentials for FCM HTTP v1. */\nexport interface FcmConfig {\n /** Firebase / GCP project ID. */\n projectId: string;\n /**\n * Service account `client_email`.\n * Store in environment variables — never hardcode.\n */\n clientEmail: string;\n /**\n * Service account PEM private key (`-----BEGIN PRIVATE KEY-----`…).\n * Treat as a tier-1 secret — inject from env / secrets manager only.\n * Literal `\\n` sequences in env strings are normalized to newlines.\n */\n privateKey: string;\n /**\n * Optional injectable access-token provider (testing / custom auth).\n * When set, service-account JWT exchange is skipped.\n */\n getAccessToken?: () => Promise<string>;\n}\n\n/** Error thrown when the FCM HTTP v1 API rejects a request. */\nexport class FcmError extends SentlyError {\n /** Creates an FCM 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, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"fcm\",\n cause: apiError,\n });\n this.name = \"FcmError\";\n }\n}\n\ninterface CachedToken {\n accessToken: string;\n expiresAtMs: number;\n}\n\n/**\n * FCM HTTP v1 transport — device-token push for Android / iOS / web clients.\n */\nexport class FcmTransport implements PushTransport {\n readonly provider = \"fcm\";\n\n private readonly projectId: string;\n private readonly clientEmail: string;\n private readonly privateKeyPem: string;\n private readonly getAccessTokenOverride: (() => Promise<string>) | undefined;\n private cached: CachedToken | undefined;\n private cryptoKey: CryptoKey | undefined;\n\n /** Creates an FCM transport from service-account credentials. */\n constructor(config: FcmConfig) {\n this.projectId = config.projectId;\n this.clientEmail = config.clientEmail;\n this.privateKeyPem = normalizePem(config.privateKey);\n this.getAccessTokenOverride = config.getAccessToken;\n }\n\n /**\n * Sends via `POST https://fcm.googleapis.com/v1/projects/{projectId}/messages:send`.\n * Requires {@link FcmPushOptions} (`token`).\n */\n async send(options: PushOptions): Promise<PushSendResult> {\n if (!isFcmPushOptions(options)) {\n throw new FcmError(\n \"FcmTransport requires PushOptions.token; use WebPushTransport for browser subscriptions\",\n 400,\n { hint: \"webpush\" },\n );\n }\n if (options.token.trim().length === 0) {\n throw new FcmError(\"FCM device token must not be empty\", 400, { field: \"token\" });\n }\n\n const accessToken = await this.getAccessToken();\n const message = buildFcmMessage(options);\n const url = `https://fcm.googleapis.com/v1/projects/${encodeURIComponent(this.projectId)}/messages:send`;\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ message }),\n });\n\n const text = await response.text();\n let payload: { name?: string; error?: { message?: string; status?: string; code?: number } };\n try {\n payload = text.length > 0 ? (JSON.parse(text) as typeof payload) : {};\n } catch {\n throw new FcmError(\"FCM returned non-JSON response\", response.status, text);\n }\n\n if (!response.ok) {\n throw new FcmError(\n payload.error?.message ?? `FCM API error (${response.status})`,\n response.status,\n payload,\n );\n }\n\n // Response name looks like projects/.../messages/{id}\n const name = typeof payload.name === \"string\" ? payload.name : \"\";\n const messageId = name.split(\"/\").pop() || options.messageId || name || crypto.randomUUID();\n\n return {\n messageId,\n status: \"accepted\",\n response: name || String(response.status),\n provider: \"fcm\",\n };\n }\n\n /** Lightweight credential check (does not call Google). */\n async verify(): Promise<VerifyResult> {\n if (this.getAccessTokenOverride) {\n return { ok: true, provider: \"fcm\", message: \"Custom getAccessToken configured\" };\n }\n const ok = Boolean(this.projectId && this.clientEmail && this.privateKeyPem);\n return {\n ok,\n provider: \"fcm\",\n message: ok ? \"Credentials present\" : \"Missing projectId, clientEmail, or privateKey\",\n };\n }\n\n private async getAccessToken(): Promise<string> {\n if (this.getAccessTokenOverride) {\n return this.getAccessTokenOverride();\n }\n\n const now = Date.now();\n if (this.cached && now < this.cached.expiresAtMs - 60_000) {\n return this.cached.accessToken;\n }\n\n const jwt = await this.signServiceAccountJwt();\n const body = new URLSearchParams({\n grant_type: \"urn:ietf:params:oauth:grant-type:jwt-bearer\",\n assertion: jwt,\n });\n\n const response = await fetch(TOKEN_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: body.toString(),\n });\n\n const text = await response.text();\n let payload: { access_token?: string; expires_in?: number; error?: string };\n try {\n payload = text.length > 0 ? (JSON.parse(text) as typeof payload) : {};\n } catch {\n throw new FcmError(\"OAuth token endpoint returned non-JSON\", response.status, text);\n }\n\n if (!response.ok || typeof payload.access_token !== \"string\") {\n throw new FcmError(\n payload.error ?? \"Failed to obtain FCM access token\",\n response.status,\n payload,\n );\n }\n\n const expiresInSec = typeof payload.expires_in === \"number\" ? payload.expires_in : 3600;\n this.cached = {\n accessToken: payload.access_token,\n expiresAtMs: now + expiresInSec * 1000,\n };\n return payload.access_token;\n }\n\n private async signServiceAccountJwt(): Promise<string> {\n const nowSec = Math.floor(Date.now() / 1000);\n const header = { alg: \"RS256\", typ: \"JWT\" };\n const claims = {\n iss: this.clientEmail,\n scope: FCM_SCOPE,\n aud: TOKEN_URL,\n iat: nowSec,\n exp: nowSec + 3600,\n };\n\n const encodedHeader = encodeBase64Url(encodeUtf8(JSON.stringify(header)));\n const encodedClaims = encodeBase64Url(encodeUtf8(JSON.stringify(claims)));\n const signingInput = `${encodedHeader}.${encodedClaims}`;\n\n const key = await this.getCryptoKey();\n const signature = await crypto.subtle.sign(\n { name: \"RSASSA-PKCS1-v1_5\" },\n key,\n toArrayBuffer(encodeUtf8(signingInput)),\n );\n\n return `${signingInput}.${encodeBase64Url(new Uint8Array(signature))}`;\n }\n\n private async getCryptoKey(): Promise<CryptoKey> {\n if (this.cryptoKey) {\n return this.cryptoKey;\n }\n const der = pemToDer(this.privateKeyPem);\n this.cryptoKey = await crypto.subtle.importKey(\n \"pkcs8\",\n toArrayBuffer(der),\n { name: \"RSASSA-PKCS1-v1_5\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n return this.cryptoKey;\n }\n}\n\nfunction buildFcmMessage(options: FcmPushOptions): Record<string, unknown> {\n const notification: Record<string, string> = {\n title: options.title,\n body: options.body,\n };\n if (options.image !== undefined) {\n notification.image = options.image;\n }\n\n const message: Record<string, unknown> = {\n token: options.token,\n notification,\n };\n\n if (options.data !== undefined) {\n message.data = stringifyData(options.data);\n }\n\n if (options.ttl !== undefined) {\n message.android = { ttl: `${options.ttl}s` };\n }\n\n return message;\n}\n\nfunction stringifyData(data: Record<string, unknown>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(data)) {\n out[key] = typeof value === \"string\" ? value : JSON.stringify(value);\n }\n return out;\n}\n\nfunction normalizePem(pem: string): string {\n return pem.includes(\"\\\\n\") ? pem.replace(/\\\\n/g, \"\\n\") : pem;\n}\n\nfunction pemToDer(pem: string): Uint8Array {\n const lines = pem\n .replace(/-----BEGIN[^-]+-----/g, \"\")\n .replace(/-----END[^-]+-----/g, \"\")\n .replace(/\\s/g, \"\");\n const binary = atob(lines);\n const der = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n der[i] = binary.charCodeAt(i);\n }\n return der;\n}\n\nfunction toArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "888F02F364B1BF2564756E2164756E21",
9
+ "names": []
10
+ }
@@ -1,3 +1,3 @@
1
- import{S as K}from"../chunk-93vqxxj2.js";import"../chunk-ywq6s10g.js";import{ca as M,da as P}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class G extends M{statusCode;apiError;constructor(q,B,z){super(q,P(B),{statusCode:B,provider:"loops",cause:z});this.statusCode=B;this.apiError=z;this.name="LoopsError"}}var U="x-loops-transactional-id";class V{provider="loops";apiKey;defaultTransactionalId;constructor(q){if(this.apiKey=q.apiKey,q.defaultTransactionalId!==void 0)this.defaultTransactionalId=q.defaultTransactionalId}async send(q){let B=q.headers?.[U]??this.defaultTransactionalId;if(!B)throw new G("Loops requires a transactional template ID. Set options.headers['x-loops-transactional-id'] or LoopsConfig.defaultTransactionalId. Loops cannot send raw subject/html/text.",400,{code:"MISSING_TRANSACTIONAL_ID"});let z=K(q.to)[0];if(!z)throw new G("Loops requires at least one recipient in options.to",400,{code:"MISSING_RECIPIENT"});let Q={transactionalId:B,email:z,dataVariables:q.data??{}},J=await fetch("https://app.loops.so/api/v1/transactional",{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(Q)}),F=await J.json();if(!J.ok||F.success===!1)throw new G(F.message??"Loops API error",J.status,F);return{messageId:F.id??"",accepted:[z],rejected:[],response:F.id??"sent",envelope:{from:K(q.from)[0]??"",to:[z]}}}async verify(){return{ok:!0,provider:"loops",message:"key not validated (no verify endpoint)"}}}export{V as LoopsTransport,G as LoopsError};
1
+ import{U as K}from"../chunk-8kpgbrba.js";import"../chunk-hnzmn4s4.js";import{ja as M,ka as P}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class G extends M{statusCode;apiError;constructor(q,B,z){super(q,P(B),{statusCode:B,provider:"loops",cause:z});this.statusCode=B;this.apiError=z;this.name="LoopsError"}}var U="x-loops-transactional-id";class V{provider="loops";apiKey;defaultTransactionalId;constructor(q){if(this.apiKey=q.apiKey,q.defaultTransactionalId!==void 0)this.defaultTransactionalId=q.defaultTransactionalId}async send(q){let B=q.headers?.[U]??this.defaultTransactionalId;if(!B)throw new G("Loops requires a transactional template ID. Set options.headers['x-loops-transactional-id'] or LoopsConfig.defaultTransactionalId. Loops cannot send raw subject/html/text.",400,{code:"MISSING_TRANSACTIONAL_ID"});let z=K(q.to)[0];if(!z)throw new G("Loops requires at least one recipient in options.to",400,{code:"MISSING_RECIPIENT"});let Q={transactionalId:B,email:z,dataVariables:q.data??{}},J=await fetch("https://app.loops.so/api/v1/transactional",{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(Q)}),F=await J.json();if(!J.ok||F.success===!1)throw new G(F.message??"Loops API error",J.status,F);return{messageId:F.id??"",accepted:[z],rejected:[],response:F.id??"sent",envelope:{from:K(q.from)[0]??"",to:[z]}}}async verify(){return{ok:!0,provider:"loops",message:"key not validated (no verify endpoint)"}}}export{V as LoopsTransport,G as LoopsError};
2
2
 
3
- //# debugId=155A09B7C69A1BDB64756E2164756E21
3
+ //# debugId=A883935EF398881864756E2164756E21
@@ -5,6 +5,6 @@
5
5
  "/**\n * @module\n * Loops transactional HTTP transport for sently.\n *\n * Loops is template-first. `subject`, `html`, and `text` are ignored — provide a\n * transactional template ID and data variables instead.\n *\n * Set the template ID per message via the `x-loops-transactional-id` header, or\n * configure a default with `defaultTransactionalId` on the transport constructor.\n * Data variables are read from `options.data`.\n *\n * @example\n * ```ts\n * import { LoopsTransport } from \"sently/transports/loops\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new LoopsTransport({ apiKey: process.env.LOOPS_API_KEY! }),\n * });\n *\n * await mailer.send({\n * from: \"noreply@example.com\",\n * to: \"user@example.com\",\n * subject: \"ignored by Loops\",\n * headers: { \"x-loops-transactional-id\": \"clxxxxxxxx\" },\n * data: { firstName: \"Ada\" },\n * });\n * ```\n */\nimport { extractEmails } from \"../core/address.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\n\n/** Loops HTTP API configuration. */\nexport interface LoopsConfig {\n /** Loops API key for Bearer authentication. */\n apiKey: string;\n /** Default transactional template ID when not set per message via header. */\n defaultTransactionalId?: string;\n}\n\n/** Error thrown when the Loops API returns a non-success response. */\nexport class LoopsError extends SentlyError {\n /** Creates a Loops API error with status code. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"loops\",\n cause: apiError,\n });\n this.name = \"LoopsError\";\n }\n}\n\nconst TRANSACTIONAL_ID_HEADER = \"x-loops-transactional-id\";\n\n/**\n * Loops transactional HTTP API transport.\n */\nexport class LoopsTransport implements Transport {\n readonly provider = \"loops\";\n\n private readonly apiKey: string;\n private readonly defaultTransactionalId?: string;\n\n /** Creates a Loops transport with the given API key. */\n constructor(config: LoopsConfig) {\n this.apiKey = config.apiKey;\n if (config.defaultTransactionalId !== undefined) {\n this.defaultTransactionalId = config.defaultTransactionalId;\n }\n }\n\n /** Sends a transactional email via the Loops HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const transactionalId =\n options.headers?.[TRANSACTIONAL_ID_HEADER] ?? this.defaultTransactionalId;\n\n if (!transactionalId) {\n throw new LoopsError(\n \"Loops requires a transactional template ID. Set options.headers['x-loops-transactional-id'] or LoopsConfig.defaultTransactionalId. Loops cannot send raw subject/html/text.\",\n 400,\n { code: \"MISSING_TRANSACTIONAL_ID\" },\n );\n }\n\n const recipient = extractEmails(options.to)[0];\n if (!recipient) {\n throw new LoopsError(\"Loops requires at least one recipient in options.to\", 400, {\n code: \"MISSING_RECIPIENT\",\n });\n }\n\n const body = {\n transactionalId,\n email: recipient,\n dataVariables: options.data ?? {},\n };\n\n const response = await fetch(\"https://app.loops.so/api/v1/transactional\", {\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 {\n success?: boolean;\n id?: string;\n message?: string;\n };\n\n if (!response.ok || payload.success === false) {\n throw new LoopsError(payload.message ?? \"Loops API error\", response.status, payload);\n }\n\n return {\n messageId: payload.id ?? \"\",\n accepted: [recipient],\n rejected: [],\n response: payload.id ?? \"sent\",\n envelope: {\n from: extractEmails(options.from)[0] ?? \"\",\n to: [recipient],\n },\n };\n }\n\n /**\n * Returns success without a network call — Loops has no simple verify endpoint.\n */\n async verify(): Promise<VerifyResult> {\n return {\n ok: true,\n provider: \"loops\",\n message: \"key not validated (no verify endpoint)\",\n };\n }\n}\n"
6
6
  ],
7
7
  "mappings": "oKA0CO,CAAM,KAAmB,JAAY,CAIxB,WACA,SAHlB,WAAW,CACT,EACgB,EACA,EAChB,CACA,MAAM,EAAS,EAAuB,CAAU,EAAG,CACjD,aACA,SAAU,QACV,MAAO,CACT,CAAC,EAPe,kBACA,gBAOhB,KAAK,KAAO,aAEhB,CAEA,IAAM,EAA0B,2BAKzB,MAAM,CAAoC,CACtC,SAAW,QAEH,OACA,uBAGjB,WAAW,CAAC,EAAqB,CAE/B,GADA,KAAK,OAAS,EAAO,OACjB,EAAO,yBAA2B,OACpC,KAAK,uBAAyB,EAAO,4BAKnC,KAAI,CAAC,EAA2C,CACpD,IAAM,EACJ,EAAQ,UAAU,IAA4B,KAAK,uBAErD,GAAI,CAAC,EACH,MAAM,IAAI,EACR,8KACA,IACA,CAAE,KAAM,0BAA2B,CACrC,EAGF,IAAM,EAAY,EAAc,EAAQ,EAAE,EAAE,GAC5C,GAAI,CAAC,EACH,MAAM,IAAI,EAAW,sDAAuD,IAAK,CAC/E,KAAM,mBACR,CAAC,EAGH,IAAM,EAAO,CACX,kBACA,MAAO,EACP,cAAe,EAAQ,MAAQ,CAAC,CAClC,EAEM,EAAW,MAAM,MAAM,4CAA6C,CACxE,OAAQ,OACR,QAAS,CACP,cAAe,UAAU,KAAK,SAC9B,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAMrC,GAAI,CAAC,EAAS,IAAM,EAAQ,UAAY,GACtC,MAAM,IAAI,EAAW,EAAQ,SAAW,kBAAmB,EAAS,OAAQ,CAAO,EAGrF,MAAO,CACL,UAAW,EAAQ,IAAM,GACzB,SAAU,CAAC,CAAS,EACpB,SAAU,CAAC,EACX,SAAU,EAAQ,IAAM,OACxB,SAAU,CACR,KAAM,EAAc,EAAQ,IAAI,EAAE,IAAM,GACxC,GAAI,CAAC,CAAS,CAChB,CACF,OAMI,OAAM,EAA0B,CACpC,MAAO,CACL,GAAI,GACJ,SAAU,QACV,QAAS,wCACX,EAEJ",
8
- "debugId": "155A09B7C69A1BDB64756E2164756E21",
8
+ "debugId": "A883935EF398881864756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,3 @@
1
- import{I as U}from"../chunk-skqhj0rm.js";import{Q as G,S as N}from"../chunk-93vqxxj2.js";import{Y as L}from"../chunk-ywq6s10g.js";import{ca as Q,da as R}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class W extends Q{statusCode;apiError;constructor(q,z,D){super(q,R(z),{statusCode:z,provider:"mailersend",cause:D});this.statusCode=z;this.apiError=D;this.name="MailerSendError"}}function H(q){return G(q).map((z)=>({email:z.address,...z.name?{name:z.name}:{}}))}class Y{provider="mailersend";apiToken;constructor(q){this.apiToken=q.apiToken}async send(q){let z=await U(q.attachments),D=G(q.from)[0],X={from:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},to:H(q.to),subject:q.subject,...q.cc?{cc:H(q.cc)}:{},...q.bcc?{bcc:H(q.bcc)}:{},...q.replyTo?{reply_to:(()=>{let w=G(q.replyTo)[0];return w?{email:w.address,...w.name?{name:w.name}:{}}:void 0})()}:{},...q.text?{text:q.text}:{},...q.html?{html:q.html}:{},...z.length>0?{attachments:z.map((w)=>({filename:w.filename,content:w.content instanceof Uint8Array?L(w.content).replace(/\r\n/g,""):w.content,disposition:"attachment"}))}:{}},F=await fetch("https://api.mailersend.com/v1/email",{method:"POST",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify(X)});if(!F.ok){let w=await F.json().catch(()=>({}));throw new W(w.message??"MailerSend API error",F.status,w)}let J=F.headers.get("x-message-id")??"",K=N(q.to);return{messageId:J,accepted:K,rejected:[],response:J||"accepted",envelope:{from:D?.address??"",to:K}}}async verify(){try{let q=await fetch("https://api.mailersend.com/v1/domains",{headers:{Authorization:`Bearer ${this.apiToken}`}});if(!q.ok)return{ok:!1,provider:"mailersend",message:(await q.json().catch(()=>({}))).message??`HTTP ${q.status}`};return{ok:!0,provider:"mailersend",message:"API token is valid"}}catch(q){return{ok:!1,provider:"mailersend",message:q instanceof Error?q.message:String(q)}}}}export{Y as MailerSendTransport,W as MailerSendError};
1
+ import{K as U}from"../chunk-1ke3nmyg.js";import{S as G,U as N}from"../chunk-8kpgbrba.js";import{da as L}from"../chunk-hnzmn4s4.js";import{ja as Q,ka as R}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class W extends Q{statusCode;apiError;constructor(q,z,D){super(q,R(z),{statusCode:z,provider:"mailersend",cause:D});this.statusCode=z;this.apiError=D;this.name="MailerSendError"}}function H(q){return G(q).map((z)=>({email:z.address,...z.name?{name:z.name}:{}}))}class Y{provider="mailersend";apiToken;constructor(q){this.apiToken=q.apiToken}async send(q){let z=await U(q.attachments),D=G(q.from)[0],X={from:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},to:H(q.to),subject:q.subject,...q.cc?{cc:H(q.cc)}:{},...q.bcc?{bcc:H(q.bcc)}:{},...q.replyTo?{reply_to:(()=>{let w=G(q.replyTo)[0];return w?{email:w.address,...w.name?{name:w.name}:{}}:void 0})()}:{},...q.text?{text:q.text}:{},...q.html?{html:q.html}:{},...z.length>0?{attachments:z.map((w)=>({filename:w.filename,content:w.content instanceof Uint8Array?L(w.content).replace(/\r\n/g,""):w.content,disposition:"attachment"}))}:{}},F=await fetch("https://api.mailersend.com/v1/email",{method:"POST",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify(X)});if(!F.ok){let w=await F.json().catch(()=>({}));throw new W(w.message??"MailerSend API error",F.status,w)}let J=F.headers.get("x-message-id")??"",K=N(q.to);return{messageId:J,accepted:K,rejected:[],response:J||"accepted",envelope:{from:D?.address??"",to:K}}}async verify(){try{let q=await fetch("https://api.mailersend.com/v1/domains",{headers:{Authorization:`Bearer ${this.apiToken}`}});if(!q.ok)return{ok:!1,provider:"mailersend",message:(await q.json().catch(()=>({}))).message??`HTTP ${q.status}`};return{ok:!0,provider:"mailersend",message:"API token is valid"}}catch(q){return{ok:!1,provider:"mailersend",message:q instanceof Error?q.message:String(q)}}}}export{Y as MailerSendTransport,W as MailerSendError};
2
2
 
3
- //# debugId=01F773171442CE1F64756E2164756E21
3
+ //# debugId=7681F6A24C70E14264756E2164756E21
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * @module\n * MailerSend HTTP transport for sently.\n *\n * @example\n * ```ts\n * import { MailerSendTransport } from \"sently/transports/mailersend\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new MailerSendTransport({ apiToken: process.env.MAILERSEND_API_TOKEN! }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** MailerSend HTTP API configuration. */\nexport interface MailerSendConfig {\n /** MailerSend API token for Bearer authentication. */\n apiToken: string;\n}\n\n/** Error thrown when the MailerSend API returns a non-success response. */\nexport class MailerSendError extends SentlyError {\n /** Creates a MailerSend API error with status code. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"mailersend\",\n cause: apiError,\n });\n this.name = \"MailerSendError\";\n }\n}\n\nfunction toAddressObjects(input: MailOptions[\"to\"]): Array<{ email: string; name?: string }> {\n return parseAddresses(input).map((addr) => ({\n email: addr.address,\n ...(addr.name ? { name: addr.name } : {}),\n }));\n}\n\n/**\n * MailerSend HTTP API transport.\n */\nexport class MailerSendTransport implements Transport {\n readonly provider = \"mailersend\";\n\n private readonly apiToken: string;\n\n /** Creates a MailerSend transport with the given API token. */\n constructor(config: MailerSendConfig) {\n this.apiToken = config.apiToken;\n }\n\n /** Sends an email via the MailerSend 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 body: Record<string, unknown> = {\n from: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n to: toAddressObjects(options.to),\n subject: options.subject,\n ...(options.cc ? { cc: toAddressObjects(options.cc) } : {}),\n ...(options.bcc ? { bcc: toAddressObjects(options.bcc) } : {}),\n ...(options.replyTo\n ? {\n reply_to: (() => {\n const reply = parseAddresses(options.replyTo as MailOptions[\"to\"])[0];\n return reply\n ? { email: reply.address, ...(reply.name ? { name: reply.name } : {}) }\n : undefined;\n })(),\n }\n : {}),\n ...(options.text ? { text: options.text } : {}),\n ...(options.html ? { html: options.html } : {}),\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 disposition: \"attachment\",\n })),\n }\n : {}),\n };\n\n const response = await fetch(\"https://api.mailersend.com/v1/email\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const payload = (await response.json().catch(() => ({}))) as {\n message?: string;\n errors?: unknown;\n };\n throw new MailerSendError(\n payload.message ?? \"MailerSend API error\",\n response.status,\n payload,\n );\n }\n\n const messageId = response.headers.get(\"x-message-id\") ?? \"\";\n const toEmails = extractEmails(options.to);\n\n return {\n messageId,\n accepted: toEmails,\n rejected: [],\n response: messageId || \"accepted\",\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies the MailerSend API token by listing domains. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(\"https://api.mailersend.com/v1/domains\", {\n headers: {\n Authorization: `Bearer ${this.apiToken}`,\n },\n });\n\n if (!response.ok) {\n const payload = (await response.json().catch(() => ({}))) as { message?: string };\n return {\n ok: false,\n provider: \"mailersend\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return { ok: true, provider: \"mailersend\", message: \"API token is valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"mailersend\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
6
6
  ],
7
- "mappings": "qOA2BO,CAAM,KAAwB,JAAY,JAI7B,WACA,SAHlB,WAAW,CACT,EACgB,EACA,EAChB,CACA,MAAM,EAAS,EAAuB,CAAU,EAAG,CACjD,aACA,SAAU,aACV,MAAO,CACT,CAAC,EAPe,kBACA,gBAOhB,KAAK,KAAO,kBAEhB,CAEA,SAAS,CAAgB,CAAC,EAAmE,CAC3F,OAAO,EAAe,CAAK,EAAE,IAAI,CAAC,KAAU,CAC1C,MAAO,EAAK,WACR,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CACzC,EAAE,EAMG,MAAM,CAAyC,CAC3C,SAAW,aAEH,SAGjB,WAAW,CAAC,EAA0B,CACpC,KAAK,SAAW,EAAO,cAInB,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAO,EAAe,EAAQ,IAAI,EAAE,GAEpC,EAAgC,CACpC,KAAM,EACF,CAAE,MAAO,EAAK,WAAa,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CAAG,EACjE,CAAE,MAAO,EAAG,EAChB,GAAI,EAAiB,EAAQ,EAAE,EAC/B,QAAS,EAAQ,WACb,EAAQ,GAAK,CAAE,GAAI,EAAiB,EAAQ,EAAE,CAAE,EAAI,CAAC,KACrD,EAAQ,IAAM,CAAE,IAAK,EAAiB,EAAQ,GAAG,CAAE,EAAI,CAAC,KACxD,EAAQ,QACR,CACE,UAAW,IAAM,CACf,IAAM,EAAQ,EAAe,EAAQ,OAA4B,EAAE,GACnE,OAAO,EACH,CAAE,MAAO,EAAM,WAAa,EAAM,KAAO,CAAE,KAAM,EAAM,IAAK,EAAI,CAAC,CAAG,EACpE,SACH,CACL,EACA,CAAC,KACD,EAAQ,KAAO,CAAE,KAAM,EAAQ,IAAK,EAAI,CAAC,KACzC,EAAQ,KAAO,CAAE,KAAM,EAAQ,IAAK,EAAI,CAAC,KACzC,EAAY,OAAS,EACrB,CACE,YAAa,EAAY,IAAI,CAAC,KAAS,CACrC,SAAU,EAAI,SACd,QACE,EAAI,mBAAmB,WACnB,EAAa,EAAI,OAAO,EAAE,QAAQ,QAAS,EAAE,EAC7C,EAAI,QACV,YAAa,YACf,EAAE,CACJ,EACA,CAAC,CACP,EAEM,EAAW,MAAM,MAAM,sCAAuC,CAClE,OAAQ,OACR,QAAS,CACP,cAAe,UAAU,KAAK,WAC9B,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAED,GAAI,CAAC,EAAS,GAAI,CAChB,IAAM,EAAW,MAAM,EAAS,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,EAIvD,MAAM,IAAI,EACR,EAAQ,SAAW,uBACnB,EAAS,OACT,CACF,EAGF,IAAM,EAAY,EAAS,QAAQ,IAAI,cAAc,GAAK,GACpD,EAAW,EAAc,EAAQ,EAAE,EAEzC,MAAO,CACL,YACA,SAAU,EACV,SAAU,CAAC,EACX,SAAU,GAAa,WACvB,SAAU,CACR,KAAM,GAAM,SAAW,GACvB,GAAI,CACN,CACF,OAII,OAAM,EAA0B,CACpC,GAAI,CACF,IAAM,EAAW,MAAM,MAAM,wCAAyC,CACpE,QAAS,CACP,cAAe,UAAU,KAAK,UAChC,CACF,CAAC,EAED,GAAI,CAAC,EAAS,GAEZ,MAAO,CACL,GAAI,GACJ,SAAU,aACV,SAJe,MAAM,EAAS,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,GAIpC,SAAW,QAAQ,EAAS,QAC/C,EAGF,MAAO,CAAE,GAAI,GAAM,SAAU,aAAc,QAAS,oBAAqB,EACzE,MAAO,EAAK,CACZ,MAAO,CACL,GAAI,GACJ,SAAU,aACV,QAAS,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,CAC1D,GAGN",
8
- "debugId": "01F773171442CE1F64756E2164756E21",
7
+ "mappings": "sOA2BO,CAAM,KAAwB,JAAY,JAI7B,WACA,SAHlB,WAAW,CACT,EACgB,EACA,EAChB,CACA,MAAM,EAAS,EAAuB,CAAU,EAAG,CACjD,aACA,SAAU,aACV,MAAO,CACT,CAAC,EAPe,kBACA,gBAOhB,KAAK,KAAO,kBAEhB,CAEA,SAAS,CAAgB,CAAC,EAAmE,CAC3F,OAAO,EAAe,CAAK,EAAE,IAAI,CAAC,KAAU,CAC1C,MAAO,EAAK,WACR,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CACzC,EAAE,EAMG,MAAM,CAAyC,CAC3C,SAAW,aAEH,SAGjB,WAAW,CAAC,EAA0B,CACpC,KAAK,SAAW,EAAO,cAInB,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAO,EAAe,EAAQ,IAAI,EAAE,GAEpC,EAAgC,CACpC,KAAM,EACF,CAAE,MAAO,EAAK,WAAa,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CAAG,EACjE,CAAE,MAAO,EAAG,EAChB,GAAI,EAAiB,EAAQ,EAAE,EAC/B,QAAS,EAAQ,WACb,EAAQ,GAAK,CAAE,GAAI,EAAiB,EAAQ,EAAE,CAAE,EAAI,CAAC,KACrD,EAAQ,IAAM,CAAE,IAAK,EAAiB,EAAQ,GAAG,CAAE,EAAI,CAAC,KACxD,EAAQ,QACR,CACE,UAAW,IAAM,CACf,IAAM,EAAQ,EAAe,EAAQ,OAA4B,EAAE,GACnE,OAAO,EACH,CAAE,MAAO,EAAM,WAAa,EAAM,KAAO,CAAE,KAAM,EAAM,IAAK,EAAI,CAAC,CAAG,EACpE,SACH,CACL,EACA,CAAC,KACD,EAAQ,KAAO,CAAE,KAAM,EAAQ,IAAK,EAAI,CAAC,KACzC,EAAQ,KAAO,CAAE,KAAM,EAAQ,IAAK,EAAI,CAAC,KACzC,EAAY,OAAS,EACrB,CACE,YAAa,EAAY,IAAI,CAAC,KAAS,CACrC,SAAU,EAAI,SACd,QACE,EAAI,mBAAmB,WACnB,EAAa,EAAI,OAAO,EAAE,QAAQ,QAAS,EAAE,EAC7C,EAAI,QACV,YAAa,YACf,EAAE,CACJ,EACA,CAAC,CACP,EAEM,EAAW,MAAM,MAAM,sCAAuC,CAClE,OAAQ,OACR,QAAS,CACP,cAAe,UAAU,KAAK,WAC9B,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAED,GAAI,CAAC,EAAS,GAAI,CAChB,IAAM,EAAW,MAAM,EAAS,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,EAIvD,MAAM,IAAI,EACR,EAAQ,SAAW,uBACnB,EAAS,OACT,CACF,EAGF,IAAM,EAAY,EAAS,QAAQ,IAAI,cAAc,GAAK,GACpD,EAAW,EAAc,EAAQ,EAAE,EAEzC,MAAO,CACL,YACA,SAAU,EACV,SAAU,CAAC,EACX,SAAU,GAAa,WACvB,SAAU,CACR,KAAM,GAAM,SAAW,GACvB,GAAI,CACN,CACF,OAII,OAAM,EAA0B,CACpC,GAAI,CACF,IAAM,EAAW,MAAM,MAAM,wCAAyC,CACpE,QAAS,CACP,cAAe,UAAU,KAAK,UAChC,CACF,CAAC,EAED,GAAI,CAAC,EAAS,GAEZ,MAAO,CACL,GAAI,GACJ,SAAU,aACV,SAJe,MAAM,EAAS,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,GAIpC,SAAW,QAAQ,EAAS,QAC/C,EAGF,MAAO,CAAE,GAAI,GAAM,SAAU,aAAc,QAAS,oBAAqB,EACzE,MAAO,EAAK,CACZ,MAAO,CACL,GAAI,GACJ,SAAU,aACV,QAAS,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,CAC1D,GAGN",
8
+ "debugId": "7681F6A24C70E14264756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,3 @@
1
- import{I as W}from"../chunk-skqhj0rm.js";import{Q as G,R as J,S as Q}from"../chunk-93vqxxj2.js";import{Y as N}from"../chunk-ywq6s10g.js";import{ca as R,da as U}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class X extends R{statusCode;apiError;constructor(q,z,F){super(q,U(z),{statusCode:z,provider:"mailgun",cause:F});this.statusCode=z;this.apiError=F;this.name="MailgunError"}}class ${provider="mailgun";apiKey;domain;baseUrl;constructor(q){this.apiKey=q.apiKey,this.domain=q.domain,this.baseUrl=q.region==="eu"?"https://api.eu.mailgun.net":"https://api.mailgun.net"}async send(q){let z=await W(q.attachments),F=G(q.from)[0],w=new FormData;if(w.append("from",F?J(F):""),w.append("to",G(q.to).map(J).join(", ")),q.cc)w.append("cc",G(q.cc).map(J).join(", "));if(q.bcc)w.append("bcc",G(q.bcc).map(J).join(", "));if(w.append("subject",q.subject),q.text)w.append("text",q.text);if(q.html)w.append("html",q.html);if(q.replyTo){let D=G(q.replyTo)[0];if(D)w.append("h:Reply-To",J(D))}for(let D of z){let Z=D.content instanceof Uint8Array?D.content:new TextEncoder().encode(String(D.content??"")),_=new Blob([new Uint8Array(Z)],{type:D.contentType??"application/octet-stream"});w.append("attachment",_,D.filename)}let Y=N(`api:${this.apiKey}`).replace(/\r\n/g,""),L=await fetch(`${this.baseUrl}/v3/${this.domain}/messages`,{method:"POST",headers:{Authorization:`Basic ${Y}`},body:w}),K=await L.json();if(!L.ok)throw new X(K.message??"Mailgun API error",L.status,K);let O=Q(q.to);return{messageId:K.id??"",accepted:O,rejected:[],response:K.message??"queued",envelope:{from:F?.address??"",to:O}}}async verify(){try{let q=N(`api:${this.apiKey}`).replace(/\r\n/g,""),z=await fetch(`${this.baseUrl}/v3/domains`,{headers:{Authorization:`Basic ${q}`}});if(!z.ok)return{ok:!1,provider:"mailgun",message:(await z.json().catch(()=>({}))).message??`HTTP ${z.status}`};return{ok:!0,provider:"mailgun",message:"API key is valid"}}catch(q){return{ok:!1,provider:"mailgun",message:q instanceof Error?q.message:String(q)}}}}export{$ as MailgunTransport,X as MailgunError};
1
+ import{K as W}from"../chunk-1ke3nmyg.js";import{S as G,T as J,U as Q}from"../chunk-8kpgbrba.js";import{da as N}from"../chunk-hnzmn4s4.js";import{ja as R,ka as U}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class X extends R{statusCode;apiError;constructor(q,z,F){super(q,U(z),{statusCode:z,provider:"mailgun",cause:F});this.statusCode=z;this.apiError=F;this.name="MailgunError"}}class ${provider="mailgun";apiKey;domain;baseUrl;constructor(q){this.apiKey=q.apiKey,this.domain=q.domain,this.baseUrl=q.region==="eu"?"https://api.eu.mailgun.net":"https://api.mailgun.net"}async send(q){let z=await W(q.attachments),F=G(q.from)[0],w=new FormData;if(w.append("from",F?J(F):""),w.append("to",G(q.to).map(J).join(", ")),q.cc)w.append("cc",G(q.cc).map(J).join(", "));if(q.bcc)w.append("bcc",G(q.bcc).map(J).join(", "));if(w.append("subject",q.subject),q.text)w.append("text",q.text);if(q.html)w.append("html",q.html);if(q.replyTo){let D=G(q.replyTo)[0];if(D)w.append("h:Reply-To",J(D))}for(let D of z){let Z=D.content instanceof Uint8Array?D.content:new TextEncoder().encode(String(D.content??"")),_=new Blob([new Uint8Array(Z)],{type:D.contentType??"application/octet-stream"});w.append("attachment",_,D.filename)}let Y=N(`api:${this.apiKey}`).replace(/\r\n/g,""),L=await fetch(`${this.baseUrl}/v3/${this.domain}/messages`,{method:"POST",headers:{Authorization:`Basic ${Y}`},body:w}),K=await L.json();if(!L.ok)throw new X(K.message??"Mailgun API error",L.status,K);let O=Q(q.to);return{messageId:K.id??"",accepted:O,rejected:[],response:K.message??"queued",envelope:{from:F?.address??"",to:O}}}async verify(){try{let q=N(`api:${this.apiKey}`).replace(/\r\n/g,""),z=await fetch(`${this.baseUrl}/v3/domains`,{headers:{Authorization:`Basic ${q}`}});if(!z.ok)return{ok:!1,provider:"mailgun",message:(await z.json().catch(()=>({}))).message??`HTTP ${z.status}`};return{ok:!0,provider:"mailgun",message:"API key is valid"}}catch(q){return{ok:!1,provider:"mailgun",message:q instanceof Error?q.message:String(q)}}}}export{$ as MailgunTransport,X as MailgunError};
2
2
 
3
- //# debugId=3693D707D234EDC064756E2164756E21
3
+ //# debugId=232B4DDF733D87B464756E2164756E21
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * @module\n * Mailgun HTTP transport for sently.\n * Uses the Mailgun Messages API v3 with multipart/form-data.\n *\n * @example\n * ```ts\n * import { MailgunTransport } from \"sently/transports/mailgun\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new MailgunTransport({\n * apiKey: \"key-...\",\n * domain: \"mg.example.com\",\n * }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses, toMIMEHeader } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type {\n MailgunConfig,\n MailOptions,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Error thrown when the Mailgun API returns a non-success response. */\nexport class MailgunError extends SentlyError {\n /** Creates a Mailgun 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, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"mailgun\",\n cause: apiError,\n });\n this.name = \"MailgunError\";\n }\n}\n\n/**\n * Mailgun HTTP API transport (multipart/form-data).\n */\nexport class MailgunTransport implements Transport {\n readonly provider = \"mailgun\";\n\n /** Mailgun API key for basic authentication. */\n private readonly apiKey: string;\n /** Mailgun sending domain. */\n private readonly domain: string;\n /** Mailgun API base URL (US or EU region). */\n private readonly baseUrl: string;\n\n /** Creates a Mailgun transport with the given API key and domain. */\n constructor(config: MailgunConfig) {\n this.apiKey = config.apiKey;\n this.domain = config.domain;\n this.baseUrl =\n config.region === \"eu\" ? \"https://api.eu.mailgun.net\" : \"https://api.mailgun.net\";\n }\n\n /** Sends an email via the Mailgun Messages API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n const form = new FormData();\n\n form.append(\"from\", from ? toMIMEHeader(from) : \"\");\n form.append(\"to\", parseAddresses(options.to).map(toMIMEHeader).join(\", \"));\n\n if (options.cc) {\n form.append(\"cc\", parseAddresses(options.cc).map(toMIMEHeader).join(\", \"));\n }\n if (options.bcc) {\n form.append(\"bcc\", parseAddresses(options.bcc).map(toMIMEHeader).join(\", \"));\n }\n\n form.append(\"subject\", options.subject);\n\n if (options.text) {\n form.append(\"text\", options.text);\n }\n if (options.html) {\n form.append(\"html\", options.html);\n }\n if (options.replyTo) {\n const replyTo = parseAddresses(options.replyTo)[0];\n if (replyTo) {\n form.append(\"h:Reply-To\", toMIMEHeader(replyTo));\n }\n }\n\n for (const attachment of attachments) {\n const content =\n attachment.content instanceof Uint8Array\n ? attachment.content\n : new TextEncoder().encode(String(attachment.content ?? \"\"));\n const blob = new Blob([new Uint8Array(content)], {\n type: attachment.contentType ?? \"application/octet-stream\",\n });\n form.append(\"attachment\", blob, attachment.filename);\n }\n\n const auth = encodeBase64(`api:${this.apiKey}`).replace(/\\r\\n/g, \"\");\n const response = await fetch(`${this.baseUrl}/v3/${this.domain}/messages`, {\n method: \"POST\",\n headers: {\n Authorization: `Basic ${auth}`,\n },\n body: form,\n });\n\n const payload = (await response.json()) as { id?: string; message?: string };\n\n if (!response.ok) {\n throw new MailgunError(payload.message ?? \"Mailgun API error\", response.status, payload);\n }\n\n const toEmails = extractEmails(options.to);\n return {\n messageId: payload.id ?? \"\",\n accepted: toEmails,\n rejected: [],\n response: payload.message ?? \"queued\",\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies the Mailgun API key by listing domains. */\n async verify(): Promise<VerifyResult> {\n try {\n const auth = encodeBase64(`api:${this.apiKey}`).replace(/\\r\\n/g, \"\");\n const response = await fetch(`${this.baseUrl}/v3/domains`, {\n headers: {\n Authorization: `Basic ${auth}`,\n },\n });\n\n if (!response.ok) {\n const payload = (await response.json().catch(() => ({}))) as { message?: string };\n return {\n ok: false,\n provider: \"mailgun\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return { ok: true, provider: \"mailgun\", message: \"API key is valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"mailgun\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "3693D707D234EDC064756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "232B4DDF733D87B464756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,3 @@
1
- import{I as X}from"../chunk-skqhj0rm.js";import{Q as L,S as U}from"../chunk-93vqxxj2.js";import{Y as Q}from"../chunk-ywq6s10g.js";import{ca as V,da as W}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class K extends V{statusCode;apiError;constructor(q,z,D){super(q,W(z),{statusCode:z,provider:"mailtrap",cause:D});this.statusCode=z;this.apiError=D;this.name="MailtrapError"}}function J(q){return L(q).map((z)=>({email:z.address,...z.name?{name:z.name}:{}}))}class Z{provider="mailtrap";apiToken;sandbox;inboxId;constructor(q){if(this.apiToken=q.apiToken,this.sandbox=q.sandbox??!1,q.inboxId!==void 0)this.inboxId=q.inboxId}sendUrl(){if(this.sandbox){if(!this.inboxId)throw new K("Mailtrap sandbox mode requires inboxId",400,{code:"INVALID_CONFIG"});return`https://sandbox.api.mailtrap.io/api/send/${this.inboxId}`}return"https://send.api.mailtrap.io/api/send"}async send(q){let z=await X(q.attachments),D=L(q.from)[0],Y={from:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},to:J(q.to),subject:q.subject,...q.cc?{cc:J(q.cc)}:{},...q.bcc?{bcc:J(q.bcc)}:{},...q.text?{text:q.text}:{},...q.html?{html:q.html}:{},...z.length>0?{attachments:z.map((F)=>({filename:F.filename,content:F.content instanceof Uint8Array?Q(F.content).replace(/\r\n/g,""):F.content,type:F.contentType??"application/octet-stream"}))}:{}},H=await fetch(this.sendUrl(),{method:"POST",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify(Y)}),G=await H.json();if(!H.ok||G.success!==!0)throw new K(G.message??"Mailtrap API error",H.status,G);let N=G.message_ids??[],P=U(q.to);return{messageId:N.join(","),accepted:P,rejected:[],response:N.join(",")||"sent",envelope:{from:D?.address??"",to:P}}}async verify(){return{ok:!0,provider:"mailtrap",message:"key not validated (no verify endpoint)"}}}export{Z as MailtrapTransport,K as MailtrapError};
1
+ import{K as X}from"../chunk-1ke3nmyg.js";import{S as L,U}from"../chunk-8kpgbrba.js";import{da as Q}from"../chunk-hnzmn4s4.js";import{ja as V,ka as W}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class K extends V{statusCode;apiError;constructor(q,z,D){super(q,W(z),{statusCode:z,provider:"mailtrap",cause:D});this.statusCode=z;this.apiError=D;this.name="MailtrapError"}}function J(q){return L(q).map((z)=>({email:z.address,...z.name?{name:z.name}:{}}))}class Z{provider="mailtrap";apiToken;sandbox;inboxId;constructor(q){if(this.apiToken=q.apiToken,this.sandbox=q.sandbox??!1,q.inboxId!==void 0)this.inboxId=q.inboxId}sendUrl(){if(this.sandbox){if(!this.inboxId)throw new K("Mailtrap sandbox mode requires inboxId",400,{code:"INVALID_CONFIG"});return`https://sandbox.api.mailtrap.io/api/send/${this.inboxId}`}return"https://send.api.mailtrap.io/api/send"}async send(q){let z=await X(q.attachments),D=L(q.from)[0],Y={from:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},to:J(q.to),subject:q.subject,...q.cc?{cc:J(q.cc)}:{},...q.bcc?{bcc:J(q.bcc)}:{},...q.text?{text:q.text}:{},...q.html?{html:q.html}:{},...z.length>0?{attachments:z.map((F)=>({filename:F.filename,content:F.content instanceof Uint8Array?Q(F.content).replace(/\r\n/g,""):F.content,type:F.contentType??"application/octet-stream"}))}:{}},H=await fetch(this.sendUrl(),{method:"POST",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify(Y)}),G=await H.json();if(!H.ok||G.success!==!0)throw new K(G.message??"Mailtrap API error",H.status,G);let N=G.message_ids??[],P=U(q.to);return{messageId:N.join(","),accepted:P,rejected:[],response:N.join(",")||"sent",envelope:{from:D?.address??"",to:P}}}async verify(){return{ok:!0,provider:"mailtrap",message:"key not validated (no verify endpoint)"}}}export{Z as MailtrapTransport,K as MailtrapError};
2
2
 
3
- //# debugId=332E52E7FE5672B864756E2164756E21
3
+ //# debugId=F90C10510978157964756E2164756E21
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * @module\n * Mailtrap HTTP transport for sently.\n *\n * @example\n * ```ts\n * import { MailtrapTransport } from \"sently/transports/mailtrap\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new MailtrapTransport({ apiToken: process.env.MAILTRAP_API_TOKEN! }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Mailtrap HTTP API configuration. */\nexport interface MailtrapConfig {\n /** Mailtrap API token for Bearer authentication. */\n apiToken: string;\n /** When true, use the sandbox send endpoint (requires `inboxId`). */\n sandbox?: boolean;\n /** Sandbox inbox ID — required when `sandbox` is true. */\n inboxId?: string;\n}\n\n/** Error thrown when the Mailtrap API returns a non-success response. */\nexport class MailtrapError extends SentlyError {\n /** Creates a Mailtrap API error with status code. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"mailtrap\",\n cause: apiError,\n });\n this.name = \"MailtrapError\";\n }\n}\n\nfunction toAddressObjects(input: MailOptions[\"to\"]): Array<{ email: string; name?: string }> {\n return parseAddresses(input).map((addr) => ({\n email: addr.address,\n ...(addr.name ? { name: addr.name } : {}),\n }));\n}\n\n/**\n * Mailtrap HTTP API transport.\n */\nexport class MailtrapTransport implements Transport {\n readonly provider = \"mailtrap\";\n\n private readonly apiToken: string;\n private readonly sandbox: boolean;\n private readonly inboxId?: string;\n\n /** Creates a Mailtrap transport with the given API token. */\n constructor(config: MailtrapConfig) {\n this.apiToken = config.apiToken;\n this.sandbox = config.sandbox ?? false;\n if (config.inboxId !== undefined) {\n this.inboxId = config.inboxId;\n }\n }\n\n private sendUrl(): string {\n if (this.sandbox) {\n if (!this.inboxId) {\n throw new MailtrapError(\"Mailtrap sandbox mode requires inboxId\", 400, {\n code: \"INVALID_CONFIG\",\n });\n }\n return `https://sandbox.api.mailtrap.io/api/send/${this.inboxId}`;\n }\n return \"https://send.api.mailtrap.io/api/send\";\n }\n\n /** Sends an email via the Mailtrap 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 body: Record<string, unknown> = {\n from: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n to: toAddressObjects(options.to),\n subject: options.subject,\n ...(options.cc ? { cc: toAddressObjects(options.cc) } : {}),\n ...(options.bcc ? { bcc: toAddressObjects(options.bcc) } : {}),\n ...(options.text ? { text: options.text } : {}),\n ...(options.html ? { html: options.html } : {}),\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 type: att.contentType ?? \"application/octet-stream\",\n })),\n }\n : {}),\n };\n\n const response = await fetch(this.sendUrl(), {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as {\n success?: boolean;\n message_ids?: string[];\n message?: string;\n };\n\n if (!response.ok || payload.success !== true) {\n throw new MailtrapError(payload.message ?? \"Mailtrap API error\", response.status, payload);\n }\n\n const messageIds = payload.message_ids ?? [];\n const toEmails = extractEmails(options.to);\n\n return {\n messageId: messageIds.join(\",\"),\n accepted: toEmails,\n rejected: [],\n response: messageIds.join(\",\") || \"sent\",\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /**\n * Returns success without a network call — Mailtrap has no simple verify endpoint.\n */\n async verify(): Promise<VerifyResult> {\n return {\n ok: true,\n provider: \"mailtrap\",\n message: \"key not validated (no verify endpoint)\",\n };\n }\n}\n"
6
6
  ],
7
- "mappings": "qOA+BO,CAAM,KAAsB,JAAY,JAI3B,WACA,SAHlB,WAAW,CACT,EACgB,EACA,EAChB,CACA,MAAM,EAAS,EAAuB,CAAU,EAAG,CACjD,aACA,SAAU,WACV,MAAO,CACT,CAAC,EAPe,kBACA,gBAOhB,KAAK,KAAO,gBAEhB,CAEA,SAAS,CAAgB,CAAC,EAAmE,CAC3F,OAAO,EAAe,CAAK,EAAE,IAAI,CAAC,KAAU,CAC1C,MAAO,EAAK,WACR,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CACzC,EAAE,EAMG,MAAM,CAAuC,CACzC,SAAW,WAEH,SACA,QACA,QAGjB,WAAW,CAAC,EAAwB,CAGlC,GAFA,KAAK,SAAW,EAAO,SACvB,KAAK,QAAU,EAAO,SAAW,GAC7B,EAAO,UAAY,OACrB,KAAK,QAAU,EAAO,QAIlB,OAAO,EAAW,CACxB,GAAI,KAAK,QAAS,CAChB,GAAI,CAAC,KAAK,QACR,MAAM,IAAI,EAAc,yCAA0C,IAAK,CACrE,KAAM,gBACR,CAAC,EAEH,MAAO,4CAA4C,KAAK,UAE1D,MAAO,6CAIH,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAO,EAAe,EAAQ,IAAI,EAAE,GAEpC,EAAgC,CACpC,KAAM,EACF,CAAE,MAAO,EAAK,WAAa,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CAAG,EACjE,CAAE,MAAO,EAAG,EAChB,GAAI,EAAiB,EAAQ,EAAE,EAC/B,QAAS,EAAQ,WACb,EAAQ,GAAK,CAAE,GAAI,EAAiB,EAAQ,EAAE,CAAE,EAAI,CAAC,KACrD,EAAQ,IAAM,CAAE,IAAK,EAAiB,EAAQ,GAAG,CAAE,EAAI,CAAC,KACxD,EAAQ,KAAO,CAAE,KAAM,EAAQ,IAAK,EAAI,CAAC,KACzC,EAAQ,KAAO,CAAE,KAAM,EAAQ,IAAK,EAAI,CAAC,KACzC,EAAY,OAAS,EACrB,CACE,YAAa,EAAY,IAAI,CAAC,KAAS,CACrC,SAAU,EAAI,SACd,QACE,EAAI,mBAAmB,WACnB,EAAa,EAAI,OAAO,EAAE,QAAQ,QAAS,EAAE,EAC7C,EAAI,QACV,KAAM,EAAI,aAAe,0BAC3B,EAAE,CACJ,EACA,CAAC,CACP,EAEM,EAAW,MAAM,MAAM,KAAK,QAAQ,EAAG,CAC3C,OAAQ,OACR,QAAS,CACP,cAAe,UAAU,KAAK,WAC9B,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAMrC,GAAI,CAAC,EAAS,IAAM,EAAQ,UAAY,GACtC,MAAM,IAAI,EAAc,EAAQ,SAAW,qBAAsB,EAAS,OAAQ,CAAO,EAG3F,IAAM,EAAa,EAAQ,aAAe,CAAC,EACrC,EAAW,EAAc,EAAQ,EAAE,EAEzC,MAAO,CACL,UAAW,EAAW,KAAK,GAAG,EAC9B,SAAU,EACV,SAAU,CAAC,EACX,SAAU,EAAW,KAAK,GAAG,GAAK,OAClC,SAAU,CACR,KAAM,GAAM,SAAW,GACvB,GAAI,CACN,CACF,OAMI,OAAM,EAA0B,CACpC,MAAO,CACL,GAAI,GACJ,SAAU,WACV,QAAS,wCACX,EAEJ",
8
- "debugId": "332E52E7FE5672B864756E2164756E21",
7
+ "mappings": "iOA+BO,CAAM,KAAsB,JAAY,JAI3B,WACA,SAHlB,WAAW,CACT,EACgB,EACA,EAChB,CACA,MAAM,EAAS,EAAuB,CAAU,EAAG,CACjD,aACA,SAAU,WACV,MAAO,CACT,CAAC,EAPe,kBACA,gBAOhB,KAAK,KAAO,gBAEhB,CAEA,SAAS,CAAgB,CAAC,EAAmE,CAC3F,OAAO,EAAe,CAAK,EAAE,IAAI,CAAC,KAAU,CAC1C,MAAO,EAAK,WACR,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CACzC,EAAE,EAMG,MAAM,CAAuC,CACzC,SAAW,WAEH,SACA,QACA,QAGjB,WAAW,CAAC,EAAwB,CAGlC,GAFA,KAAK,SAAW,EAAO,SACvB,KAAK,QAAU,EAAO,SAAW,GAC7B,EAAO,UAAY,OACrB,KAAK,QAAU,EAAO,QAIlB,OAAO,EAAW,CACxB,GAAI,KAAK,QAAS,CAChB,GAAI,CAAC,KAAK,QACR,MAAM,IAAI,EAAc,yCAA0C,IAAK,CACrE,KAAM,gBACR,CAAC,EAEH,MAAO,4CAA4C,KAAK,UAE1D,MAAO,6CAIH,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAO,EAAe,EAAQ,IAAI,EAAE,GAEpC,EAAgC,CACpC,KAAM,EACF,CAAE,MAAO,EAAK,WAAa,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CAAG,EACjE,CAAE,MAAO,EAAG,EAChB,GAAI,EAAiB,EAAQ,EAAE,EAC/B,QAAS,EAAQ,WACb,EAAQ,GAAK,CAAE,GAAI,EAAiB,EAAQ,EAAE,CAAE,EAAI,CAAC,KACrD,EAAQ,IAAM,CAAE,IAAK,EAAiB,EAAQ,GAAG,CAAE,EAAI,CAAC,KACxD,EAAQ,KAAO,CAAE,KAAM,EAAQ,IAAK,EAAI,CAAC,KACzC,EAAQ,KAAO,CAAE,KAAM,EAAQ,IAAK,EAAI,CAAC,KACzC,EAAY,OAAS,EACrB,CACE,YAAa,EAAY,IAAI,CAAC,KAAS,CACrC,SAAU,EAAI,SACd,QACE,EAAI,mBAAmB,WACnB,EAAa,EAAI,OAAO,EAAE,QAAQ,QAAS,EAAE,EAC7C,EAAI,QACV,KAAM,EAAI,aAAe,0BAC3B,EAAE,CACJ,EACA,CAAC,CACP,EAEM,EAAW,MAAM,MAAM,KAAK,QAAQ,EAAG,CAC3C,OAAQ,OACR,QAAS,CACP,cAAe,UAAU,KAAK,WAC9B,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAMrC,GAAI,CAAC,EAAS,IAAM,EAAQ,UAAY,GACtC,MAAM,IAAI,EAAc,EAAQ,SAAW,qBAAsB,EAAS,OAAQ,CAAO,EAG3F,IAAM,EAAa,EAAQ,aAAe,CAAC,EACrC,EAAW,EAAc,EAAQ,EAAE,EAEzC,MAAO,CACL,UAAW,EAAW,KAAK,GAAG,EAC9B,SAAU,EACV,SAAU,CAAC,EACX,SAAU,EAAW,KAAK,GAAG,GAAK,OAClC,SAAU,CACR,KAAM,GAAM,SAAW,GACvB,GAAI,CACN,CACF,OAMI,OAAM,EAA0B,CACpC,MAAO,CACL,GAAI,GACJ,SAAU,WACV,QAAS,wCACX,EAEJ",
8
+ "debugId": "F90C10510978157964756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,3 @@
1
- import{ca as Z,da as V}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class L extends Z{statusCode;apiError;constructor(q,A,F,D,B){super(q,D??(A>=400?V(A):"PROVIDER_ERROR"),{statusCode:A,provider:"msegat",cause:F});this.statusCode=A;this.apiError=F;if(this.name="MsegatError",B!==void 0)this.code=B}}function X(q){let A=q.trim();if(A.startsWith("+"))A=A.slice(1);if(A.startsWith("00"))A=A.slice(2);return A}function W(q){let A=q.code??q.Code;if(typeof A==="string"||typeof A==="number")return String(A);return}function Y(q){return q==="1"||q==="M0000"}function _(q){let A=W(q);if(Y(A))return!0;return q.success===!0&&(A===void 0||Y(A))}function K(q){if(typeof q.message==="string")return q.message;if(typeof q.Message==="string")return q.Message;return}function $(q){switch(q){case"1060":return"PROVIDER_ERROR";case"400":case"404":return"BAD_REQUEST";default:return"BAD_REQUEST"}}async function Q(q,A,F){let D=await fetch(q,{method:"POST",headers:{"Content-Type":"application/json",...F},body:JSON.stringify(A)}),B=await D.text(),G;try{G=JSON.parse(B)}catch{throw new L("Msegat returned non-JSON response",D.status,B)}return{response:D,text:B,payload:G}}function U(q,A,F){let D=W(A);if(!q.ok||!_(A)){let B=K(A)??`${F} (code ${D??"unknown"})`,G=q.status>=400?V(q.status):$(D);throw new L(B,q.status,A,G,D)}return D??"1"}class j{provider="msegat";userName;apiKey;sender;constructor(q){this.userName=q.userName,this.apiKey=q.apiKey,this.sender=q.sender}async send(q){let A=X(q.to),{response:F,text:D,payload:B}=await Q("https://www.msegat.com/gw/sendsms.php",{userName:this.userName,apiKey:this.apiKey,numbers:A,userSender:q.from??this.sender,msg:q.body,msgEncoding:"UTF8",reqBulkId:"true"});U(F,B,"Msegat SMS API error");let G=B.bulk_id??B.messageId??B.id??B.Id;return{messageId:typeof G==="string"||typeof G==="number"?String(G):q.messageId??crypto.randomUUID(),to:q.to,status:"accepted",response:D,provider:"msegat"}}async sendOtp(q){let A=q.lang??"En",F=X(q.to),{response:D,text:B,payload:G}=await Q("https://www.msegat.com/gw/sendOTPCode.php",{userName:this.userName,apiKey:this.apiKey,number:F,userSender:q.from??this.sender,lang:A},{lang:A});U(D,G,"Msegat OTP send error");let I=G.id??G.Id;if(typeof I!=="string"&&typeof I!=="number")throw new L("Msegat OTP send succeeded but returned no id",D.status,G);return{id:String(I),to:q.to,status:"accepted",response:B,provider:"msegat"}}async verifyOtp(q){let A=q.lang??"En",{response:F,text:D,payload:B}=await Q("https://www.msegat.com/gw/verifyOTPCode.php",{userName:this.userName,apiKey:this.apiKey,code:q.code,id:q.id,userSender:q.from??this.sender,lang:A},{lang:A});return U(F,B,"Msegat OTP verify error"),{ok:!0,message:K(B)??"Success",response:D,provider:"msegat"}}async verify(){if(!this.userName||!this.apiKey||!this.sender)return{ok:!1,provider:"msegat",message:"Missing credentials"};let q=new URL("https://www.msegat.com/gw/Credits.php");q.searchParams.set("userName",this.userName),q.searchParams.set("apiKey",this.apiKey);try{let A=await fetch(q,{method:"GET",headers:{"Content-Type":"application/json"}}),F=(await A.text()).trim();if(!A.ok)return{ok:!1,provider:"msegat",message:`Credits inquiry failed (HTTP ${A.status}): ${F||"empty body"}`};let D=Number(F);if(Number.isFinite(D))return{ok:!0,provider:"msegat",message:`Balance: ${F}`};try{let B=JSON.parse(F);if(_(B))return{ok:!0,provider:"msegat",message:K(B)??"OK"};let G=W(B);return{ok:!1,provider:"msegat",message:K(B)??`Credits inquiry failed (code ${G??"unknown"})`}}catch{return{ok:!1,provider:"msegat",message:`Credits inquiry returned unexpected body: ${F}`}}}catch(A){return{ok:!1,provider:"msegat",message:A instanceof Error?A.message:"Credits inquiry failed"}}}}export{j as MsegatTransport,L as MsegatError};
1
+ import{ja as Z,ka as V}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class L extends Z{statusCode;apiError;constructor(q,A,F,D,B){super(q,D??(A>=400?V(A):"PROVIDER_ERROR"),{statusCode:A,provider:"msegat",cause:F});this.statusCode=A;this.apiError=F;if(this.name="MsegatError",B!==void 0)this.code=B}}function X(q){let A=q.trim();if(A.startsWith("+"))A=A.slice(1);if(A.startsWith("00"))A=A.slice(2);return A}function W(q){let A=q.code??q.Code;if(typeof A==="string"||typeof A==="number")return String(A);return}function Y(q){return q==="1"||q==="M0000"}function _(q){let A=W(q);if(Y(A))return!0;return q.success===!0&&(A===void 0||Y(A))}function K(q){if(typeof q.message==="string")return q.message;if(typeof q.Message==="string")return q.Message;return}function $(q){switch(q){case"1060":return"PROVIDER_ERROR";case"400":case"404":return"BAD_REQUEST";default:return"BAD_REQUEST"}}async function Q(q,A,F){let D=await fetch(q,{method:"POST",headers:{"Content-Type":"application/json",...F},body:JSON.stringify(A)}),B=await D.text(),G;try{G=JSON.parse(B)}catch{throw new L("Msegat returned non-JSON response",D.status,B)}return{response:D,text:B,payload:G}}function U(q,A,F){let D=W(A);if(!q.ok||!_(A)){let B=K(A)??`${F} (code ${D??"unknown"})`,G=q.status>=400?V(q.status):$(D);throw new L(B,q.status,A,G,D)}return D??"1"}class j{provider="msegat";userName;apiKey;sender;constructor(q){this.userName=q.userName,this.apiKey=q.apiKey,this.sender=q.sender}async send(q){let A=X(q.to),{response:F,text:D,payload:B}=await Q("https://www.msegat.com/gw/sendsms.php",{userName:this.userName,apiKey:this.apiKey,numbers:A,userSender:q.from??this.sender,msg:q.body,msgEncoding:"UTF8",reqBulkId:"true"});U(F,B,"Msegat SMS API error");let G=B.bulk_id??B.messageId??B.id??B.Id;return{messageId:typeof G==="string"||typeof G==="number"?String(G):q.messageId??crypto.randomUUID(),to:q.to,status:"accepted",response:D,provider:"msegat"}}async sendOtp(q){let A=q.lang??"En",F=X(q.to),{response:D,text:B,payload:G}=await Q("https://www.msegat.com/gw/sendOTPCode.php",{userName:this.userName,apiKey:this.apiKey,number:F,userSender:q.from??this.sender,lang:A},{lang:A});U(D,G,"Msegat OTP send error");let I=G.id??G.Id;if(typeof I!=="string"&&typeof I!=="number")throw new L("Msegat OTP send succeeded but returned no id",D.status,G);return{id:String(I),to:q.to,status:"accepted",response:B,provider:"msegat"}}async verifyOtp(q){let A=q.lang??"En",{response:F,text:D,payload:B}=await Q("https://www.msegat.com/gw/verifyOTPCode.php",{userName:this.userName,apiKey:this.apiKey,code:q.code,id:q.id,userSender:q.from??this.sender,lang:A},{lang:A});return U(F,B,"Msegat OTP verify error"),{ok:!0,message:K(B)??"Success",response:D,provider:"msegat"}}async verify(){if(!this.userName||!this.apiKey||!this.sender)return{ok:!1,provider:"msegat",message:"Missing credentials"};let q=new URL("https://www.msegat.com/gw/Credits.php");q.searchParams.set("userName",this.userName),q.searchParams.set("apiKey",this.apiKey);try{let A=await fetch(q,{method:"GET",headers:{"Content-Type":"application/json"}}),F=(await A.text()).trim();if(!A.ok)return{ok:!1,provider:"msegat",message:`Credits inquiry failed (HTTP ${A.status}): ${F||"empty body"}`};let D=Number(F);if(Number.isFinite(D))return{ok:!0,provider:"msegat",message:`Balance: ${F}`};try{let B=JSON.parse(F);if(_(B))return{ok:!0,provider:"msegat",message:K(B)??"OK"};let G=W(B);return{ok:!1,provider:"msegat",message:K(B)??`Credits inquiry failed (code ${G??"unknown"})`}}catch{return{ok:!1,provider:"msegat",message:`Credits inquiry returned unexpected body: ${F}`}}}catch(A){return{ok:!1,provider:"msegat",message:A instanceof Error?A.message:"Credits inquiry failed"}}}}export{j as MsegatTransport,L as MsegatError};
2
2
 
3
- //# debugId=C8170D426FAB9BF364756E2164756E21
3
+ //# debugId=E58D04225405310864756E2164756E21
@@ -5,6 +5,6 @@
5
5
  "/**\n * @module\n * Msegat SMS gateway transport (Saudi Arabia).\n *\n * Shapes follow the official Postman collection\n * ([MSEGAT Communication Platform API](https://documenter.getpostman.com/view/39158411/2sBY4LT3EY)):\n * - `POST /gw/sendsms.php` — JSON send (`code` `\"1\"` / `\"M0000\"`)\n * - `GET /gw/Credits.php` — balance / credential check\n * - `POST /gw/sendOTPCode.php` / `verifyOTPCode.php` — vendor OTP helpers\n *\n * Sently-first: wire into {@link createSmsSender}; OTP stays on this class.\n *\n * @example\n * ```ts\n * import { createSmsSender } from \"sently/sms\";\n * import { MsegatTransport } from \"sently/transports/msegat\";\n *\n * const msegat = new MsegatTransport({\n * userName: process.env.MSEGAT_USERNAME!,\n * apiKey: process.env.MSEGAT_API_KEY!,\n * sender: \"MyBrand\",\n * });\n * const sms = createSmsSender({ transport: msegat });\n *\n * await sms.send({ to: \"+9665xxxxxxxx\", body: \"Hello\" });\n * const otp = await msegat.sendOtp({ to: \"+9665xxxxxxxx\", lang: \"En\" });\n * await msegat.verifyOtp({ id: otp.id, code: \"1234\", lang: \"En\" });\n * ```\n */\nimport { httpStatusToSentlyCode, SentlyError, type SentlyErrorCode } from \"../core/errors.js\";\nimport type { SmsOptions, SmsSendResult, SmsTransport } from \"../core/sms-types.js\";\nimport type { VerifyResult } from \"../core/types.js\";\n\n/** Msegat SMS API configuration. */\nexport interface MsegatConfig {\n /** Msegat account username. */\n userName: string;\n /** Msegat API key. */\n apiKey: string;\n /** Pre-approved sender name (use `auth-mseg` for free OTP tests). */\n sender: string;\n}\n\n/** Options for {@link MsegatTransport.sendOtp}. */\nexport interface MsegatSendOtpOptions {\n /** Recipient phone number (E.164 or international digits). */\n to: string;\n /** Sender ID override; defaults to transport `sender`. */\n from?: string;\n /**\n * Message language for header + body (`Ar` or `En`).\n * Postman: \"lang : Ar or En in header\". Defaults to `\"En\"`.\n */\n lang?: \"Ar\" | \"En\";\n}\n\n/** Result of a successful {@link MsegatTransport.sendOtp} call. */\nexport interface MsegatOtpSendResult {\n /** OTP session id required by {@link MsegatTransport.verifyOtp}. */\n id: string;\n /** Recipient phone number as passed in. */\n to: string;\n /** Delivery status (`\"accepted\"` on success). */\n status: string;\n /** Raw response body text. */\n response: string;\n /** Provider identifier. */\n provider: \"msegat\";\n}\n\n/** Options for {@link MsegatTransport.verifyOtp}. */\nexport interface MsegatVerifyOtpOptions {\n /** OTP session id from {@link MsegatOtpSendResult.id}. */\n id: string | number;\n /** Code the user entered. */\n code: string;\n /** Sender ID override; defaults to transport `sender`. */\n from?: string;\n /** Message language (`Ar` or `En`). Defaults to `\"En\"`. */\n lang?: \"Ar\" | \"En\";\n}\n\n/** Result of a successful {@link MsegatTransport.verifyOtp} call. */\nexport interface MsegatOtpVerifyResult {\n /** Always `true` when the call resolves (failures throw). */\n ok: true;\n /** Provider message when present. */\n message: string;\n /** Raw response body text. */\n response: string;\n /** Provider identifier. */\n provider: \"msegat\";\n}\n\n/** Error thrown when the Msegat SMS API reports a failure. */\nexport class MsegatError extends SentlyError {\n /** Creates a Msegat SMS API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n sentlyCode?: SentlyErrorCode,\n /** Msegat response `code` when present (e.g. `\"1020\"`, `\"M0002\"`). */\n providerCode?: string,\n ) {\n super(\n message,\n sentlyCode ?? (statusCode >= 400 ? httpStatusToSentlyCode(statusCode) : \"PROVIDER_ERROR\"),\n {\n statusCode,\n provider: \"msegat\",\n cause: apiError,\n },\n );\n this.name = \"MsegatError\";\n if (providerCode !== undefined) {\n this.code = providerCode;\n }\n }\n}\n\n/**\n * International format without `+` or leading `00`\n * (Postman: \"international format without zeros\").\n */\nfunction normalizeMsegatPhone(phone: string): string {\n let normalized = phone.trim();\n if (normalized.startsWith(\"+\")) {\n normalized = normalized.slice(1);\n }\n if (normalized.startsWith(\"00\")) {\n normalized = normalized.slice(2);\n }\n return normalized;\n}\n\nfunction responseCode(payload: Record<string, unknown>): string | undefined {\n const raw = payload.code ?? payload.Code;\n if (typeof raw === \"string\" || typeof raw === \"number\") {\n return String(raw);\n }\n return undefined;\n}\n\n/** Documented success codes: `1` / `M0000` (and OTP `success: true`). */\nfunction isSuccessCode(code: string | undefined): boolean {\n return code === \"1\" || code === \"M0000\";\n}\n\nfunction isSuccessPayload(payload: Record<string, unknown>): boolean {\n const code = responseCode(payload);\n if (isSuccessCode(code)) return true;\n // sendOTPCode success example includes `\"success\": true` alongside `code: \"1\"`.\n return payload.success === true && (code === undefined || isSuccessCode(code));\n}\n\nfunction responseMessage(payload: Record<string, unknown>): string | undefined {\n if (typeof payload.message === \"string\") return payload.message;\n if (typeof payload.Message === \"string\") return payload.Message;\n return undefined;\n}\n\n/** Map documented Msegat codes onto stable sently codes. */\nfunction msegatCodeToSently(code: string | undefined): SentlyErrorCode {\n switch (code) {\n case \"1060\":\n // Balance is not enough — provider-side account state.\n return \"PROVIDER_ERROR\";\n case \"400\":\n case \"404\":\n // OTP expired / not found.\n return \"BAD_REQUEST\";\n default:\n return \"BAD_REQUEST\";\n }\n}\n\nasync function postJson(\n url: string,\n body: Record<string, unknown>,\n headers?: Record<string, string>,\n): Promise<{ response: Response; text: string; payload: Record<string, unknown> }> {\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...headers,\n },\n body: JSON.stringify(body),\n });\n\n const text = await response.text();\n let payload: Record<string, unknown>;\n try {\n payload = JSON.parse(text) as Record<string, unknown>;\n } catch {\n throw new MsegatError(\"Msegat returned non-JSON response\", response.status, text);\n }\n\n return { response, text, payload };\n}\n\nfunction assertSuccess(\n response: Response,\n payload: Record<string, unknown>,\n fallbackMessage: string,\n): string {\n const code = responseCode(payload);\n if (!response.ok || !isSuccessPayload(payload)) {\n const message = responseMessage(payload) ?? `${fallbackMessage} (code ${code ?? \"unknown\"})`;\n const sentlyCode =\n response.status >= 400 ? httpStatusToSentlyCode(response.status) : msegatCodeToSently(code);\n throw new MsegatError(message, response.status, payload, sentlyCode, code);\n }\n return code ?? \"1\";\n}\n\n/**\n * Msegat SMS transport via JSON `sendsms.php` + Credits-based `verify()`,\n * with provider-specific OTP helpers.\n */\nexport class MsegatTransport implements SmsTransport {\n readonly provider = \"msegat\";\n\n private readonly userName: string;\n private readonly apiKey: string;\n private readonly sender: string;\n\n /** Creates an Msegat SMS transport. */\n constructor(config: MsegatConfig) {\n this.userName = config.userName;\n this.apiKey = config.apiKey;\n this.sender = config.sender;\n }\n\n /**\n * Sends an SMS via `POST /gw/sendsms.php`\n * (JSON: `userName`, `apiKey`, `numbers`, `userSender`, `msg`).\n */\n async send(options: SmsOptions): Promise<SmsSendResult> {\n const numbers = normalizeMsegatPhone(options.to);\n const { response, text, payload } = await postJson(\"https://www.msegat.com/gw/sendsms.php\", {\n userName: this.userName,\n apiKey: this.apiKey,\n numbers,\n userSender: options.from ?? this.sender,\n msg: options.body,\n // Optional per Postman; UTF8 is the documented default.\n msgEncoding: \"UTF8\",\n // Optional: request bulk id when the account returns one.\n reqBulkId: \"true\",\n });\n\n assertSuccess(response, payload, \"Msegat SMS API error\");\n\n const idCandidate = payload.bulk_id ?? payload.messageId ?? payload.id ?? payload.Id;\n const messageId =\n typeof idCandidate === \"string\" || typeof idCandidate === \"number\"\n ? String(idCandidate)\n : (options.messageId ?? crypto.randomUUID());\n\n return {\n messageId,\n to: options.to,\n status: \"accepted\",\n response: text,\n provider: \"msegat\",\n };\n }\n\n /**\n * Vendor extra: send a provider-generated OTP via `sendOTPCode.php`.\n * Not part of {@link SmsTransport} — call on {@link MsegatTransport} directly.\n * Keep the returned {@link MsegatOtpSendResult.id} for {@link verifyOtp}.\n */\n async sendOtp(options: MsegatSendOtpOptions): Promise<MsegatOtpSendResult> {\n const lang = options.lang ?? \"En\";\n const number = normalizeMsegatPhone(options.to);\n const { response, text, payload } = await postJson(\n \"https://www.msegat.com/gw/sendOTPCode.php\",\n {\n userName: this.userName,\n apiKey: this.apiKey,\n number,\n userSender: options.from ?? this.sender,\n lang,\n },\n // Postman: \"lang : Ar or En in header\"\n { lang },\n );\n\n assertSuccess(response, payload, \"Msegat OTP send error\");\n\n const idRaw = payload.id ?? payload.Id;\n if (typeof idRaw !== \"string\" && typeof idRaw !== \"number\") {\n throw new MsegatError(\n \"Msegat OTP send succeeded but returned no id\",\n response.status,\n payload,\n );\n }\n\n return {\n id: String(idRaw),\n to: options.to,\n status: \"accepted\",\n response: text,\n provider: \"msegat\",\n };\n }\n\n /**\n * Vendor extra: verify a code previously sent with {@link sendOtp} via\n * `verifyOTPCode.php`. Not part of {@link SmsTransport} — call on\n * {@link MsegatTransport} directly. Throws {@link MsegatError} when the code\n * is invalid, expired (`400`), or not found (`404`).\n */\n async verifyOtp(options: MsegatVerifyOtpOptions): Promise<MsegatOtpVerifyResult> {\n const lang = options.lang ?? \"En\";\n const { response, text, payload } = await postJson(\n \"https://www.msegat.com/gw/verifyOTPCode.php\",\n {\n userName: this.userName,\n apiKey: this.apiKey,\n code: options.code,\n id: options.id,\n userSender: options.from ?? this.sender,\n lang,\n },\n { lang },\n );\n\n assertSuccess(response, payload, \"Msegat OTP verify error\");\n\n return {\n ok: true,\n message: responseMessage(payload) ?? \"Success\",\n response: text,\n provider: \"msegat\",\n };\n }\n\n /**\n * Credential check via `GET /gw/Credits.php` (no SMS sent).\n * Postman success body is a bare balance number (e.g. `\"272.6\"`).\n */\n async verify(): Promise<VerifyResult> {\n if (!this.userName || !this.apiKey || !this.sender) {\n return { ok: false, provider: \"msegat\", message: \"Missing credentials\" };\n }\n\n const url = new URL(\"https://www.msegat.com/gw/Credits.php\");\n url.searchParams.set(\"userName\", this.userName);\n url.searchParams.set(\"apiKey\", this.apiKey);\n\n try {\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n // Matches Postman Credits request headers.\n \"Content-Type\": \"application/json\",\n },\n });\n const text = (await response.text()).trim();\n\n if (!response.ok) {\n return {\n ok: false,\n provider: \"msegat\",\n message: `Credits inquiry failed (HTTP ${response.status}): ${text || \"empty body\"}`,\n };\n }\n\n const balance = Number(text);\n if (Number.isFinite(balance)) {\n return {\n ok: true,\n provider: \"msegat\",\n message: `Balance: ${text}`,\n };\n }\n\n // Some failures still return HTTP 200 with a code/message JSON body.\n try {\n const payload = JSON.parse(text) as Record<string, unknown>;\n if (isSuccessPayload(payload)) {\n return { ok: true, provider: \"msegat\", message: responseMessage(payload) ?? \"OK\" };\n }\n const code = responseCode(payload);\n return {\n ok: false,\n provider: \"msegat\",\n message: responseMessage(payload) ?? `Credits inquiry failed (code ${code ?? \"unknown\"})`,\n };\n } catch {\n return {\n ok: false,\n provider: \"msegat\",\n message: `Credits inquiry returned unexpected body: ${text}`,\n };\n }\n } catch (error) {\n return {\n ok: false,\n provider: \"msegat\",\n message: error instanceof Error ? error.message : \"Credits inquiry failed\",\n };\n }\n }\n}\n"
6
6
  ],
7
7
  "mappings": "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",
8
- "debugId": "C8170D426FAB9BF364756E2164756E21",
8
+ "debugId": "E58D04225405310864756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,3 @@
1
- import{Q as K,S as L}from"../chunk-93vqxxj2.js";import"../chunk-ywq6s10g.js";import{ca as M,da as N}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class O extends M{statusCode;apiError;constructor(q,B,z){super(q,N(B),{statusCode:B,provider:"plunk",cause:z});this.statusCode=B;this.apiError=z;this.name="PlunkError"}}class V{provider="plunk";apiKey;constructor(q){this.apiKey=q.apiKey}async send(q){let B=K(q.from)[0],z=L(q.to),Q=q.html??q.text??"",H=[],J=[];for(let U of z){let F=await fetch("https://api.useplunk.com/v1/send",{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({to:U,subject:q.subject,body:Q,subscribed:!1})}),D=await F.json();if(!F.ok||D.success!==!0)throw new O(D.message??"Plunk API error",F.status,D);let G=D.emails?.[0]?.id??"";if(G)H.push(G);J.push(G||"sent")}return{messageId:H.join(","),accepted:z,rejected:[],response:J.join("; "),envelope:{from:B?.address??"",to:z}}}async verify(){return{ok:!0,provider:"plunk",message:"no verify endpoint; key not validated"}}}export{V as PlunkTransport,O as PlunkError};
1
+ import{S as K,U as L}from"../chunk-8kpgbrba.js";import"../chunk-hnzmn4s4.js";import{ja as M,ka as N}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class O extends M{statusCode;apiError;constructor(q,B,z){super(q,N(B),{statusCode:B,provider:"plunk",cause:z});this.statusCode=B;this.apiError=z;this.name="PlunkError"}}class V{provider="plunk";apiKey;constructor(q){this.apiKey=q.apiKey}async send(q){let B=K(q.from)[0],z=L(q.to),Q=q.html??q.text??"",H=[],J=[];for(let U of z){let F=await fetch("https://api.useplunk.com/v1/send",{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({to:U,subject:q.subject,body:Q,subscribed:!1})}),D=await F.json();if(!F.ok||D.success!==!0)throw new O(D.message??"Plunk API error",F.status,D);let G=D.emails?.[0]?.id??"";if(G)H.push(G);J.push(G||"sent")}return{messageId:H.join(","),accepted:z,rejected:[],response:J.join("; "),envelope:{from:B?.address??"",to:z}}}async verify(){return{ok:!0,provider:"plunk",message:"no verify endpoint; key not validated"}}}export{V as PlunkTransport,O as PlunkError};
2
2
 
3
- //# debugId=8B468EAA2B0EF13864756E2164756E21
3
+ //# debugId=C48A47B07B5EFDD564756E2164756E21