sently 0.9.2 → 1.0.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 (245) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +86 -0
  3. package/README.md +39 -34
  4. package/SECURITY.md +59 -0
  5. package/dist/adapters/bun.js +1 -1
  6. package/dist/adapters/cf.js +2 -2
  7. package/dist/adapters/cf.js.map +1 -1
  8. package/dist/adapters/deno.js +1 -1
  9. package/dist/adapters/node.js +1 -1
  10. package/dist/auth/oauth2.js +2 -2
  11. package/dist/auth/oauth2.js.map +1 -1
  12. package/dist/channel-result.d.ts +6 -0
  13. package/dist/channel-result.js +1 -0
  14. package/dist/{chunk-skqhj0rm.js → chunk-1ke3nmyg.js} +3 -3
  15. package/dist/{chunk-skqhj0rm.js.map → chunk-1ke3nmyg.js.map} +1 -1
  16. package/dist/{chunk-7gy2q9sh.js → chunk-5es0v1j7.js} +2 -2
  17. package/dist/{chunk-7gy2q9sh.js.map → chunk-5es0v1j7.js.map} +1 -1
  18. package/dist/{chunk-0qxws3kj.js → chunk-7wtfjx0d.js} +2 -2
  19. package/dist/{chunk-0qxws3kj.js.map → chunk-7wtfjx0d.js.map} +1 -1
  20. package/dist/chunk-8kpgbrba.js +4 -0
  21. package/dist/{chunk-93vqxxj2.js.map → chunk-8kpgbrba.js.map} +2 -2
  22. package/dist/{chunk-vjpds2pf.js → chunk-a057jzsy.js} +2 -2
  23. package/dist/{chunk-vjpds2pf.js.map → chunk-a057jzsy.js.map} +1 -1
  24. package/dist/{chunk-8njyga62.js → chunk-bhr7w3gg.js} +2 -2
  25. package/dist/{chunk-8njyga62.js.map → chunk-bhr7w3gg.js.map} +1 -1
  26. package/dist/chunk-cs1tvehn.js +4 -0
  27. package/dist/chunk-cs1tvehn.js.map +10 -0
  28. package/dist/{chunk-29z7fkzy.js → chunk-d5aw5vag.js} +3 -3
  29. package/dist/{chunk-29z7fkzy.js.map → chunk-d5aw5vag.js.map} +1 -1
  30. package/dist/chunk-f5x8vesr.js +4 -0
  31. package/dist/{chunk-b602dhck.js.map → chunk-f5x8vesr.js.map} +2 -2
  32. package/dist/chunk-fv0bemgf.js +4 -0
  33. package/dist/{chunk-yp311efr.js.map → chunk-fv0bemgf.js.map} +3 -3
  34. package/dist/{chunk-ywq6s10g.js → chunk-hnzmn4s4.js} +2 -2
  35. package/dist/{chunk-ywq6s10g.js.map → chunk-hnzmn4s4.js.map} +1 -1
  36. package/dist/chunk-htdg075t.js +4 -0
  37. package/dist/{chunk-qtkd5bak.js.map → chunk-htdg075t.js.map} +4 -3
  38. package/dist/{chunk-0gqy32pe.js → chunk-q2mahnb7.js} +3 -3
  39. package/dist/chunk-q2mahnb7.js.map +10 -0
  40. package/dist/chunk-sbgt4165.js +4 -0
  41. package/dist/chunk-sbgt4165.js.map +10 -0
  42. package/dist/{chunk-6npp3x3c.js → chunk-th4cwrpb.js} +2 -2
  43. package/dist/{chunk-6npp3x3c.js.map → chunk-th4cwrpb.js.map} +1 -1
  44. package/dist/{chunk-tamww15j.js → chunk-ttbwyxmh.js} +2 -2
  45. package/dist/{chunk-tamww15j.js.map → chunk-ttbwyxmh.js.map} +1 -1
  46. package/dist/chunk-w4c9kj70.js +13 -0
  47. package/dist/{chunk-n0qeyzqm.js.map → chunk-w4c9kj70.js.map} +2 -2
  48. package/dist/chunk-w9sabmb0.js +4 -0
  49. package/dist/chunk-w9sabmb0.js.map +10 -0
  50. package/dist/{chunk-8dty8c4p.js → chunk-yg3kj723.js} +3 -3
  51. package/dist/{chunk-8dty8c4p.js.map → chunk-yg3kj723.js.map} +2 -2
  52. package/dist/chunk-z1589fjk.js +4 -0
  53. package/dist/chunk-z1589fjk.js.map +10 -0
  54. package/dist/core/channel-result.d.ts +36 -0
  55. package/dist/core/channel-result.js +3 -0
  56. package/dist/core/channel-result.js.map +10 -0
  57. package/dist/core/decorated-transport.d.ts +30 -0
  58. package/dist/core/decorator-hooks.d.ts +17 -0
  59. package/dist/core/errors.js +2 -2
  60. package/dist/core/errors.js.map +1 -1
  61. package/dist/core/provider-label.d.ts +7 -3
  62. package/dist/core/push-endpoint.d.ts +5 -0
  63. package/dist/core/push-types.d.ts +48 -14
  64. package/dist/core/sms-types.d.ts +12 -0
  65. package/dist/core/smtp.js +2 -2
  66. package/dist/core/smtp.js.map +1 -1
  67. package/dist/core/whatsapp-types.d.ts +12 -0
  68. package/dist/detect.js +2 -2
  69. package/dist/detect.js.map +1 -1
  70. package/dist/dkim.js +2 -2
  71. package/dist/dkim.js.map +2 -2
  72. package/dist/idempotency.js +2 -2
  73. package/dist/idempotency.js.map +1 -1
  74. package/dist/index.d.ts +4 -0
  75. package/dist/index.js +5 -0
  76. package/dist/mailer.js +2 -2
  77. package/dist/mailer.js.map +1 -1
  78. package/dist/observability/console.js +1 -1
  79. package/dist/plugins/react.js +2 -2
  80. package/dist/plugins/react.js.map +1 -1
  81. package/dist/plugins/template.js +1 -1
  82. package/dist/pool/pool.js +2 -2
  83. package/dist/pool/pool.js.map +1 -1
  84. package/dist/push.d.ts +1 -0
  85. package/dist/push.js +2 -2
  86. package/dist/push.js.map +3 -3
  87. package/dist/sms.d.ts +1 -0
  88. package/dist/sms.js +2 -2
  89. package/dist/sms.js.map +3 -3
  90. package/dist/smtp-mailer.js +2 -2
  91. package/dist/smtp-mailer.js.map +1 -1
  92. package/dist/transports/brevo.js +2 -2
  93. package/dist/transports/brevo.js.map +2 -2
  94. package/dist/transports/cloudflare-email.js +2 -2
  95. package/dist/transports/cloudflare-email.js.map +2 -2
  96. package/dist/transports/fallback.d.ts +32 -6
  97. package/dist/transports/fallback.js +2 -2
  98. package/dist/transports/fallback.js.map +1 -1
  99. package/dist/transports/fcm.d.ts +55 -0
  100. package/dist/transports/fcm.js +4 -0
  101. package/dist/transports/fcm.js.map +10 -0
  102. package/dist/transports/loops.js +2 -2
  103. package/dist/transports/loops.js.map +1 -1
  104. package/dist/transports/mailersend.js +2 -2
  105. package/dist/transports/mailersend.js.map +2 -2
  106. package/dist/transports/mailgun.js +2 -2
  107. package/dist/transports/mailgun.js.map +2 -2
  108. package/dist/transports/mailpit.d.ts +166 -0
  109. package/dist/transports/mailpit.js +3 -0
  110. package/dist/transports/mailpit.js.map +10 -0
  111. package/dist/transports/mailtrap.js +2 -2
  112. package/dist/transports/mailtrap.js.map +2 -2
  113. package/dist/transports/msegat.js +2 -2
  114. package/dist/transports/msegat.js.map +1 -1
  115. package/dist/transports/plunk.js +2 -2
  116. package/dist/transports/plunk.js.map +1 -1
  117. package/dist/transports/postmark.js +2 -2
  118. package/dist/transports/postmark.js.map +2 -2
  119. package/dist/transports/preview.js +2 -2
  120. package/dist/transports/preview.js.map +1 -1
  121. package/dist/transports/resend.js +2 -2
  122. package/dist/transports/resend.js.map +2 -2
  123. package/dist/transports/retry.d.ts +28 -9
  124. package/dist/transports/retry.js +2 -2
  125. package/dist/transports/retry.js.map +3 -3
  126. package/dist/transports/sendgrid.js +2 -2
  127. package/dist/transports/sendgrid.js.map +2 -2
  128. package/dist/transports/ses.js +2 -2
  129. package/dist/transports/ses.js.map +2 -2
  130. package/dist/transports/smtp.js +2 -2
  131. package/dist/transports/smtp.js.map +1 -1
  132. package/dist/transports/sndr.js +2 -2
  133. package/dist/transports/sndr.js.map +1 -1
  134. package/dist/transports/sparkpost.js +2 -2
  135. package/dist/transports/sparkpost.js.map +2 -2
  136. package/dist/transports/taqnyat-mail.js +2 -2
  137. package/dist/transports/taqnyat-mail.js.map +1 -1
  138. package/dist/transports/taqnyat-sms.js +2 -2
  139. package/dist/transports/taqnyat-sms.js.map +1 -1
  140. package/dist/transports/taqnyat-whatsapp.js +2 -2
  141. package/dist/transports/taqnyat-whatsapp.js.map +1 -1
  142. package/dist/transports/twilio-sms.js +2 -2
  143. package/dist/transports/twilio-sms.js.map +2 -2
  144. package/dist/transports/unifonic.d.ts +81 -0
  145. package/dist/transports/unifonic.js +3 -0
  146. package/dist/transports/unifonic.js.map +10 -0
  147. package/dist/transports/webpush.js +2 -2
  148. package/dist/transports/webpush.js.map +3 -3
  149. package/dist/transports/weighted-fallback.d.ts +11 -9
  150. package/dist/transports/weighted-fallback.js +2 -2
  151. package/dist/transports/weighted-fallback.js.map +3 -3
  152. package/dist/transports/whatsapp-cloud.js +2 -2
  153. package/dist/transports/whatsapp-cloud.js.map +1 -1
  154. package/dist/webhooks/brevo.js +2 -2
  155. package/dist/webhooks/brevo.js.map +1 -1
  156. package/dist/webhooks/mailgun.js +2 -2
  157. package/dist/webhooks/mailgun.js.map +1 -1
  158. package/dist/webhooks/postmark.js +2 -2
  159. package/dist/webhooks/postmark.js.map +1 -1
  160. package/dist/webhooks/resend.js +2 -2
  161. package/dist/webhooks/resend.js.map +1 -1
  162. package/dist/webhooks/sendgrid.js +2 -2
  163. package/dist/webhooks/sendgrid.js.map +1 -1
  164. package/dist/webhooks/ses.js +2 -2
  165. package/dist/webhooks/ses.js.map +1 -1
  166. package/dist/webhooks/sndr.js +2 -2
  167. package/dist/webhooks/sndr.js.map +1 -1
  168. package/dist/webhooks/timing-safe-equal.js +1 -1
  169. package/dist/webhooks/twilio-sms.d.ts +20 -0
  170. package/dist/webhooks/twilio-sms.js +3 -0
  171. package/dist/webhooks/twilio-sms.js.map +10 -0
  172. package/dist/webhooks/types.d.ts +37 -3
  173. package/dist/webhooks/types.js +3 -0
  174. package/dist/webhooks/types.js.map +9 -0
  175. package/dist/webhooks/unifonic.d.ts +13 -0
  176. package/dist/webhooks/unifonic.js +3 -0
  177. package/dist/webhooks/unifonic.js.map +10 -0
  178. package/dist/webhooks/whatsapp-cloud.d.ts +14 -0
  179. package/dist/webhooks/whatsapp-cloud.js +3 -0
  180. package/dist/webhooks/whatsapp-cloud.js.map +10 -0
  181. package/dist/webhooks.d.ts +10 -5
  182. package/dist/webhooks.js +11 -0
  183. package/dist/whatsapp.d.ts +1 -0
  184. package/dist/whatsapp.js +2 -2
  185. package/dist/whatsapp.js.map +3 -3
  186. package/package.json +39 -2
  187. package/site/content/docs/ai/llms-txt.mdx +47 -2
  188. package/site/content/docs/channels/email.mdx +3 -0
  189. package/site/content/docs/channels/hooks.mdx +6 -5
  190. package/site/content/docs/channels/index.mdx +5 -3
  191. package/site/content/docs/channels/push.mdx +31 -21
  192. package/site/content/docs/channels/sms.mdx +4 -1
  193. package/site/content/docs/decorators/fallback.mdx +45 -13
  194. package/site/content/docs/decorators/index.mdx +31 -13
  195. package/site/content/docs/decorators/preview.mdx +52 -9
  196. package/site/content/docs/decorators/retry.mdx +32 -9
  197. package/site/content/docs/decorators/weighted-fallback.mdx +27 -6
  198. package/site/content/docs/get-started/entrypoints.mdx +11 -8
  199. package/site/content/docs/get-started/index.mdx +8 -2
  200. package/site/content/docs/get-started/installation.mdx +3 -2
  201. package/site/content/docs/get-started/introduction.mdx +7 -3
  202. package/site/content/docs/get-started/meta.json +4 -1
  203. package/site/content/docs/get-started/migrate-nodemailer.mdx +132 -5
  204. package/site/content/docs/get-started/non-goals.mdx +57 -0
  205. package/site/content/docs/get-started/stability.mdx +69 -0
  206. package/site/content/docs/get-started/support-matrix.mdx +62 -0
  207. package/site/content/docs/guides/compare.mdx +87 -0
  208. package/site/content/docs/guides/failover.mdx +121 -0
  209. package/site/content/docs/guides/index.mdx +2 -0
  210. package/site/content/docs/guides/meta.json +2 -0
  211. package/site/content/docs/guides/security.mdx +38 -8
  212. package/site/content/docs/guides/webhooks.mdx +35 -20
  213. package/site/content/docs/index.mdx +78 -4
  214. package/site/content/docs/meta.json +0 -1
  215. package/site/content/docs/reference/channel-result.mdx +82 -0
  216. package/site/content/docs/reference/exports.mdx +9 -4
  217. package/site/content/docs/reference/meta.json +1 -0
  218. package/site/content/docs/reference/push-options.mdx +58 -3
  219. package/site/content/docs/reference/sms-options.mdx +18 -2
  220. package/site/content/docs/reference/webhook-events.mdx +38 -14
  221. package/site/content/docs/reference/whatsapp-options.mdx +19 -1
  222. package/site/content/docs/transports/fcm.mdx +84 -0
  223. package/site/content/docs/transports/index.mdx +11 -3
  224. package/site/content/docs/transports/mailpit.mdx +123 -0
  225. package/site/content/docs/transports/meta.json +4 -1
  226. package/site/content/docs/transports/smtp.mdx +61 -22
  227. package/site/content/docs/transports/unifonic.mdx +85 -0
  228. package/dist/chunk-0gqy32pe.js.map +0 -10
  229. package/dist/chunk-5915vbcj.js +0 -4
  230. package/dist/chunk-5915vbcj.js.map +0 -10
  231. package/dist/chunk-93vqxxj2.js +0 -4
  232. package/dist/chunk-b602dhck.js +0 -4
  233. package/dist/chunk-gp4fwrs8.js +0 -4
  234. package/dist/chunk-gp4fwrs8.js.map +0 -10
  235. package/dist/chunk-n0qeyzqm.js +0 -13
  236. package/dist/chunk-q8k3m6qe.js +0 -4
  237. package/dist/chunk-q8k3m6qe.js.map +0 -10
  238. package/dist/chunk-qtkd5bak.js +0 -4
  239. package/dist/chunk-yp311efr.js +0 -4
  240. package/site/content/docs/quick-start/email.mdx +0 -40
  241. package/site/content/docs/quick-start/index.mdx +0 -15
  242. package/site/content/docs/quick-start/meta.json +0 -5
  243. package/site/content/docs/quick-start/push.mdx +0 -36
  244. package/site/content/docs/quick-start/sms.mdx +0 -36
  245. package/site/content/docs/quick-start/whatsapp.mdx +0 -38
@@ -1,8 +1,27 @@
1
- import type { MailOptions, RetryConfig, SendResult, Transport, VerifyResult } from "../core/types.js";
1
+ /**
2
+ * @module
3
+ * RetryTransport — decorator that wraps any sently channel transport
4
+ * and retries failed sends with configurable backoff.
5
+ * Works with email, SMS, WhatsApp, and push transports.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { RetryTransport } from 'sently/transports/retry'
10
+ * import { ResendTransport } from 'sently/transports/resend'
11
+ *
12
+ * const transport = new RetryTransport(
13
+ * new ResendTransport({ apiKey }),
14
+ * { maxAttempts: 3, backoff: 'exponential', retryOn: [429, 503] }
15
+ * )
16
+ * ```
17
+ */
18
+ import type { DecoratedTransport } from "../core/decorated-transport.js";
19
+ import type { MailOptions, RetryConfig, SendResult, VerifyResult } from "../core/types.js";
2
20
  /**
3
21
  * Decorator transport that retries failed sends with configurable backoff.
22
+ * Defaults preserve email {@link Transport} compatibility.
4
23
  */
5
- export declare class RetryTransport implements Transport {
24
+ export declare class RetryTransport<TOptions = MailOptions, TResult = SendResult> implements DecoratedTransport<TOptions, TResult> {
6
25
  /** Transport that performs the actual send on each attempt. */
7
26
  private readonly inner;
8
27
  /** Injectable sleep function for testing backoff timing. */
@@ -18,27 +37,27 @@ export declare class RetryTransport implements Transport {
18
37
  private readonly retryOn;
19
38
  /** Optional callback invoked before each retry attempt. */
20
39
  private readonly onRetry;
21
- /** Additional onRetry callback wired by {@link MailerImpl} hooks per send. */
40
+ /** Additional onRetry callback wired by sender lifecycle hooks per send. */
22
41
  private mailerOnRetry;
23
42
  /** Wraps an inner transport with retry logic and optional backoff configuration. */
24
43
  constructor(
25
44
  /** Transport that performs the actual send on each attempt. */
26
- inner: Transport, config?: RetryConfig,
45
+ inner: DecoratedTransport<TOptions, TResult>, config?: RetryConfig,
27
46
  /** Injectable sleep function for testing backoff timing. */
28
47
  _sleep?: (ms: number) => Promise<void>);
29
48
  /**
30
- * Register a per-send onRetry callback from mailer lifecycle hooks.
31
- * Do not share one {@link RetryTransport} across multiple mailers — the last
49
+ * Register a per-send onRetry callback from sender lifecycle hooks.
50
+ * Do not share one {@link RetryTransport} across multiple senders — the last
32
51
  * `setMailerOnRetry` wins; overwriting an active callback logs a dev warning.
33
- * @internal Used by {@link MailerImpl}; cleared after each send.
52
+ * @internal Cleared after each send.
34
53
  */
35
54
  setMailerOnRetry(callback: RetryConfig["onRetry"] | undefined): void;
36
55
  /** Sends with retries according to configured backoff and retry rules. */
37
- send(options: MailOptions): Promise<SendResult>;
56
+ send(options: TOptions): Promise<TResult>;
38
57
  /** Delegates to the inner transport verify or returns a default success result. */
39
58
  verify(): Promise<VerifyResult>;
40
59
  /** Delegates close to the inner transport if available. */
41
60
  close(): Promise<void>;
42
61
  /** Delegates batch sends to the inner transport when available. */
43
- sendBatch(messages: MailOptions[]): Promise<SendResult[]>;
62
+ sendBatch(messages: TOptions[]): Promise<TResult[]>;
44
63
  }
@@ -1,3 +1,3 @@
1
- import{ca as G}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";var H=[429,500,502,503,504];function I(q,x,z){if(x==="exponential")return z*2**(q-1);if(x==="linear")return z*q;return z}function J(q,x){if(q instanceof G&&q.sentlyCode==="SMTP_AUTH_FAILED")return!1;if(typeof q==="object"&&q!==null&&"statusCode"in q&&typeof q.statusCode==="number")return x.includes(q.statusCode);return!0}class K{inner;_sleep;provider="retry";maxAttempts;backoff;baseDelay;retryOn;onRetry;mailerOnRetry;constructor(q,x,z=(B)=>new Promise((C)=>setTimeout(C,B))){this.inner=q;this._sleep=z;this.maxAttempts=x?.maxAttempts??3,this.backoff=x?.backoff??"exponential",this.baseDelay=x?.baseDelay??1000,this.retryOn=x?.retryOn??H,this.onRetry=x?.onRetry,this.mailerOnRetry=void 0}setMailerOnRetry(q){if(q!==void 0&&this.mailerOnRetry!==void 0&&this.mailerOnRetry!==q){if(!(typeof process<"u"&&process.env?.NODE_ENV==="production"))console.warn("[sently] RetryTransport.setMailerOnRetry: overwriting an active mailer onRetry callback. Do not share one RetryTransport instance across multiple mailers.")}this.mailerOnRetry=q}async send(q){let x;for(let z=1;z<=this.maxAttempts;z++)try{return await this.inner.send(q)}catch(B){if(x=B,z===this.maxAttempts)throw B;if(!J(B,this.retryOn))throw B;let C=I(z,this.backoff,this.baseDelay);this.onRetry?.(z,B),this.mailerOnRetry?.(z,B),await this._sleep(C)}throw x}async verify(){if(this.inner.verify)return this.inner.verify();return{ok:!0,provider:"retry"}}async close(){await this.inner.close?.()}async sendBatch(q){if(this.inner.sendBatch)return this.inner.sendBatch(q);return Promise.all(q.map((x)=>this.send(x)))}}export{K as RetryTransport};
1
+ import{ja as G}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";var H=[429,500,502,503,504];function I(q,x,z){if(x==="exponential")return z*2**(q-1);if(x==="linear")return z*q;return z}function J(q,x){if(q instanceof G&&q.sentlyCode==="SMTP_AUTH_FAILED")return!1;if(typeof q==="object"&&q!==null&&"statusCode"in q&&typeof q.statusCode==="number")return x.includes(q.statusCode);return!0}class K{inner;_sleep;provider="retry";maxAttempts;backoff;baseDelay;retryOn;onRetry;mailerOnRetry;constructor(q,x,z=(B)=>new Promise((C)=>setTimeout(C,B))){this.inner=q;this._sleep=z;this.maxAttempts=x?.maxAttempts??3,this.backoff=x?.backoff??"exponential",this.baseDelay=x?.baseDelay??1000,this.retryOn=x?.retryOn??H,this.onRetry=x?.onRetry,this.mailerOnRetry=void 0}setMailerOnRetry(q){if(q!==void 0&&this.mailerOnRetry!==void 0&&this.mailerOnRetry!==q){if(!(typeof process<"u"&&process.env?.NODE_ENV==="production"))console.warn("[sently] RetryTransport.setMailerOnRetry: overwriting an active mailer onRetry callback. Do not share one RetryTransport instance across multiple senders.")}this.mailerOnRetry=q}async send(q){let x;for(let z=1;z<=this.maxAttempts;z++)try{return await this.inner.send(q)}catch(B){if(x=B,z===this.maxAttempts)throw B;if(!J(B,this.retryOn))throw B;let C=I(z,this.backoff,this.baseDelay);this.onRetry?.(z,B),this.mailerOnRetry?.(z,B),await this._sleep(C)}throw x}async verify(){if(this.inner.verify)return this.inner.verify();return{ok:!0,provider:"retry"}}async close(){await this.inner.close?.()}async sendBatch(q){if(this.inner.sendBatch)return this.inner.sendBatch(q);return Promise.all(q.map((x)=>this.send(x)))}}export{K as RetryTransport};
2
2
 
3
- //# debugId=85CFC69CB504195764756E2164756E21
3
+ //# debugId=F115F560369E227A64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/transports/retry.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * @module\n * RetryTransport — decorator that wraps any sently transport\n * and retries failed sends with configurable backoff.\n * Works with SMTP, Resend, SendGrid, SES, and any custom transport.\n *\n * @example\n * ```ts\n * import { RetryTransport } from 'sently/transports/retry'\n * import { ResendTransport } from 'sently/transports/resend'\n *\n * const transport = new RetryTransport(\n * new ResendTransport({ apiKey }),\n * { maxAttempts: 3, backoff: 'exponential', retryOn: [429, 503] }\n * )\n * ```\n */\nimport { SentlyError } from \"../core/errors.js\";\nimport type {\n MailOptions,\n RetryConfig,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\n\nconst DEFAULT_RETRY_ON = [429, 500, 502, 503, 504];\n\nfunction computeDelay(attempt: number, backoff: string, base: number): number {\n if (backoff === \"exponential\") {\n return base * 2 ** (attempt - 1);\n }\n if (backoff === \"linear\") {\n return base * attempt;\n }\n return base;\n}\n\nfunction shouldRetry(err: unknown, retryOn: number[]): boolean {\n if (err instanceof SentlyError && err.sentlyCode === \"SMTP_AUTH_FAILED\") {\n return false;\n }\n\n if (\n typeof err === \"object\" &&\n err !== null &&\n \"statusCode\" in err &&\n typeof (err as { statusCode: unknown }).statusCode === \"number\"\n ) {\n return retryOn.includes((err as { statusCode: number }).statusCode);\n }\n\n return true;\n}\n\n/**\n * Decorator transport that retries failed sends with configurable backoff.\n */\nexport class RetryTransport implements Transport {\n readonly provider = \"retry\";\n\n /** Maximum send attempts including the initial try. */\n private readonly maxAttempts: number;\n /** Backoff strategy between retries. */\n private readonly backoff: \"exponential\" | \"linear\" | \"fixed\";\n /** Base delay in milliseconds before the first retry. */\n private readonly baseDelay: number;\n /** HTTP status codes that trigger a retry. */\n private readonly retryOn: number[];\n /** Optional callback invoked before each retry attempt. */\n private readonly onRetry: RetryConfig[\"onRetry\"];\n /** Additional onRetry callback wired by {@link MailerImpl} hooks per send. */\n private mailerOnRetry: RetryConfig[\"onRetry\"];\n\n /** Wraps an inner transport with retry logic and optional backoff configuration. */\n constructor(\n /** Transport that performs the actual send on each attempt. */\n private readonly inner: Transport,\n config?: RetryConfig,\n /** Injectable sleep function for testing backoff timing. */\n private readonly _sleep: (ms: number) => Promise<void> = (ms) =>\n new Promise((resolve) => setTimeout(resolve, ms)),\n ) {\n this.maxAttempts = config?.maxAttempts ?? 3;\n this.backoff = config?.backoff ?? \"exponential\";\n this.baseDelay = config?.baseDelay ?? 1000;\n this.retryOn = config?.retryOn ?? DEFAULT_RETRY_ON;\n this.onRetry = config?.onRetry;\n this.mailerOnRetry = undefined;\n }\n\n /**\n * Register a per-send onRetry callback from mailer lifecycle hooks.\n * Do not share one {@link RetryTransport} across multiple mailers — the last\n * `setMailerOnRetry` wins; overwriting an active callback logs a dev warning.\n * @internal Used by {@link MailerImpl}; cleared after each send.\n */\n setMailerOnRetry(callback: RetryConfig[\"onRetry\"] | undefined): void {\n if (\n callback !== undefined &&\n this.mailerOnRetry !== undefined &&\n this.mailerOnRetry !== callback\n ) {\n const isProduction = typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\";\n if (!isProduction) {\n console.warn(\n \"[sently] RetryTransport.setMailerOnRetry: overwriting an active mailer onRetry callback. Do not share one RetryTransport instance across multiple mailers.\",\n );\n }\n }\n this.mailerOnRetry = callback;\n }\n\n /** Sends with retries according to configured backoff and retry rules. */\n async send(options: MailOptions): Promise<SendResult> {\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= this.maxAttempts; attempt++) {\n try {\n return await this.inner.send(options);\n } catch (err) {\n lastError = err;\n if (attempt === this.maxAttempts) {\n throw err;\n }\n if (!shouldRetry(err, this.retryOn)) {\n throw err;\n }\n const delay = computeDelay(attempt, this.backoff, this.baseDelay);\n this.onRetry?.(attempt, err);\n this.mailerOnRetry?.(attempt, err);\n await this._sleep(delay);\n }\n }\n\n throw lastError;\n }\n\n /** Delegates to the inner transport verify or returns a default success result. */\n async verify(): Promise<VerifyResult> {\n if (this.inner.verify) {\n return this.inner.verify();\n }\n return { ok: true, provider: \"retry\" };\n }\n\n /** Delegates close to the inner transport if available. */\n async close(): Promise<void> {\n await this.inner.close?.();\n }\n\n /** Delegates batch sends to the inner transport when available. */\n async sendBatch(messages: MailOptions[]): Promise<SendResult[]> {\n if (this.inner.sendBatch) {\n return this.inner.sendBatch(messages);\n }\n return Promise.all(messages.map((message) => this.send(message)));\n }\n}\n"
5
+ "/**\n * @module\n * RetryTransport — decorator that wraps any sently channel transport\n * and retries failed sends with configurable backoff.\n * Works with email, SMS, WhatsApp, and push transports.\n *\n * @example\n * ```ts\n * import { RetryTransport } from 'sently/transports/retry'\n * import { ResendTransport } from 'sently/transports/resend'\n *\n * const transport = new RetryTransport(\n * new ResendTransport({ apiKey }),\n * { maxAttempts: 3, backoff: 'exponential', retryOn: [429, 503] }\n * )\n * ```\n */\nimport type { DecoratedTransport } from \"../core/decorated-transport.js\";\nimport { SentlyError } from \"../core/errors.js\";\nimport type { MailOptions, RetryConfig, SendResult, VerifyResult } from \"../core/types.js\";\n\nconst DEFAULT_RETRY_ON = [429, 500, 502, 503, 504];\n\nfunction computeDelay(attempt: number, backoff: string, base: number): number {\n if (backoff === \"exponential\") {\n return base * 2 ** (attempt - 1);\n }\n if (backoff === \"linear\") {\n return base * attempt;\n }\n return base;\n}\n\nfunction shouldRetry(err: unknown, retryOn: number[]): boolean {\n if (err instanceof SentlyError && err.sentlyCode === \"SMTP_AUTH_FAILED\") {\n return false;\n }\n\n if (\n typeof err === \"object\" &&\n err !== null &&\n \"statusCode\" in err &&\n typeof (err as { statusCode: unknown }).statusCode === \"number\"\n ) {\n return retryOn.includes((err as { statusCode: number }).statusCode);\n }\n\n return true;\n}\n\n/**\n * Decorator transport that retries failed sends with configurable backoff.\n * Defaults preserve email {@link Transport} compatibility.\n */\nexport class RetryTransport<TOptions = MailOptions, TResult = SendResult>\n implements DecoratedTransport<TOptions, TResult>\n{\n readonly provider = \"retry\";\n\n /** Maximum send attempts including the initial try. */\n private readonly maxAttempts: number;\n /** Backoff strategy between retries. */\n private readonly backoff: \"exponential\" | \"linear\" | \"fixed\";\n /** Base delay in milliseconds before the first retry. */\n private readonly baseDelay: number;\n /** HTTP status codes that trigger a retry. */\n private readonly retryOn: number[];\n /** Optional callback invoked before each retry attempt. */\n private readonly onRetry: RetryConfig[\"onRetry\"];\n /** Additional onRetry callback wired by sender lifecycle hooks per send. */\n private mailerOnRetry: RetryConfig[\"onRetry\"];\n\n /** Wraps an inner transport with retry logic and optional backoff configuration. */\n constructor(\n /** Transport that performs the actual send on each attempt. */\n private readonly inner: DecoratedTransport<TOptions, TResult>,\n config?: RetryConfig,\n /** Injectable sleep function for testing backoff timing. */\n private readonly _sleep: (ms: number) => Promise<void> = (ms) =>\n new Promise((resolve) => setTimeout(resolve, ms)),\n ) {\n this.maxAttempts = config?.maxAttempts ?? 3;\n this.backoff = config?.backoff ?? \"exponential\";\n this.baseDelay = config?.baseDelay ?? 1000;\n this.retryOn = config?.retryOn ?? DEFAULT_RETRY_ON;\n this.onRetry = config?.onRetry;\n this.mailerOnRetry = undefined;\n }\n\n /**\n * Register a per-send onRetry callback from sender lifecycle hooks.\n * Do not share one {@link RetryTransport} across multiple senders — the last\n * `setMailerOnRetry` wins; overwriting an active callback logs a dev warning.\n * @internal Cleared after each send.\n */\n setMailerOnRetry(callback: RetryConfig[\"onRetry\"] | undefined): void {\n if (\n callback !== undefined &&\n this.mailerOnRetry !== undefined &&\n this.mailerOnRetry !== callback\n ) {\n const isProduction = typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\";\n if (!isProduction) {\n console.warn(\n \"[sently] RetryTransport.setMailerOnRetry: overwriting an active mailer onRetry callback. Do not share one RetryTransport instance across multiple senders.\",\n );\n }\n }\n this.mailerOnRetry = callback;\n }\n\n /** Sends with retries according to configured backoff and retry rules. */\n async send(options: TOptions): Promise<TResult> {\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= this.maxAttempts; attempt++) {\n try {\n return await this.inner.send(options);\n } catch (err) {\n lastError = err;\n if (attempt === this.maxAttempts) {\n throw err;\n }\n if (!shouldRetry(err, this.retryOn)) {\n throw err;\n }\n const delay = computeDelay(attempt, this.backoff, this.baseDelay);\n this.onRetry?.(attempt, err);\n this.mailerOnRetry?.(attempt, err);\n await this._sleep(delay);\n }\n }\n\n throw lastError;\n }\n\n /** Delegates to the inner transport verify or returns a default success result. */\n async verify(): Promise<VerifyResult> {\n if (this.inner.verify) {\n return this.inner.verify();\n }\n return { ok: true, provider: \"retry\" };\n }\n\n /** Delegates close to the inner transport if available. */\n async close(): Promise<void> {\n await this.inner.close?.();\n }\n\n /** Delegates batch sends to the inner transport when available. */\n async sendBatch(messages: TOptions[]): Promise<TResult[]> {\n if (this.inner.sendBatch) {\n return this.inner.sendBatch(messages);\n }\n return Promise.all(messages.map((message) => this.send(message)));\n }\n}\n"
6
6
  ],
7
- "mappings": "4EA0BA,DAAM,EAAmB,CAAC,IAAK,IAAK,IAAK,IAAK,GAAG,EAEjD,SAAS,CAAY,CAAC,EAAiB,EAAiB,EAAsB,CAC5E,GAAI,IAAY,cACd,OAAO,EAAO,IAAM,EAAU,GAEhC,GAAI,IAAY,SACd,OAAO,EAAO,EAEhB,OAAO,EAGT,SAAS,CAAW,CAAC,EAAc,EAA4B,CAC7D,GAAI,aAAe,GAAe,EAAI,aAAe,mBACnD,MAAO,GAGT,GACE,OAAO,IAAQ,UACf,IAAQ,MACR,eAAgB,GAChB,OAAQ,EAAgC,aAAe,SAEvD,OAAO,EAAQ,SAAU,EAA+B,UAAU,EAGpE,MAAO,GAMF,MAAM,CAAoC,CAmB5B,MAGA,OArBV,SAAW,QAGH,YAEA,QAEA,UAEA,QAEA,QAET,cAGR,WAAW,CAEQ,EACjB,EAEiB,EAAwC,CAAC,IACxD,IAAI,QAAQ,CAAC,IAAY,WAAW,EAAS,CAAE,CAAC,EAClD,CALiB,aAGA,cAGjB,KAAK,YAAc,GAAQ,aAAe,EAC1C,KAAK,QAAU,GAAQ,SAAW,cAClC,KAAK,UAAY,GAAQ,WAAa,KACtC,KAAK,QAAU,GAAQ,SAAW,EAClC,KAAK,QAAU,GAAQ,QACvB,KAAK,cAAgB,OASvB,gBAAgB,CAAC,EAAoD,CACnE,GACE,IAAa,QACb,KAAK,gBAAkB,QACvB,KAAK,gBAAkB,GAGvB,GAAI,EADiB,OAAO,QAAY,KAAe,QAAQ,KAAK,WAAa,cAE/E,QAAQ,KACN,4JACF,EAGJ,KAAK,cAAgB,OAIjB,KAAI,CAAC,EAA2C,CACpD,IAAI,EAEJ,QAAS,EAAU,EAAG,GAAW,KAAK,YAAa,IACjD,GAAI,CACF,OAAO,MAAM,KAAK,MAAM,KAAK,CAAO,EACpC,MAAO,EAAK,CAEZ,GADA,EAAY,EACR,IAAY,KAAK,YACnB,MAAM,EAER,GAAI,CAAC,EAAY,EAAK,KAAK,OAAO,EAChC,MAAM,EAER,IAAM,EAAQ,EAAa,EAAS,KAAK,QAAS,KAAK,SAAS,EAChE,KAAK,UAAU,EAAS,CAAG,EAC3B,KAAK,gBAAgB,EAAS,CAAG,EACjC,MAAM,KAAK,OAAO,CAAK,EAI3B,MAAM,OAIF,OAAM,EAA0B,CACpC,GAAI,KAAK,MAAM,OACb,OAAO,KAAK,MAAM,OAAO,EAE3B,MAAO,CAAE,GAAI,GAAM,SAAU,OAAQ,OAIjC,MAAK,EAAkB,CAC3B,MAAM,KAAK,MAAM,QAAQ,OAIrB,UAAS,CAAC,EAAgD,CAC9D,GAAI,KAAK,MAAM,UACb,OAAO,KAAK,MAAM,UAAU,CAAQ,EAEtC,OAAO,QAAQ,IAAI,EAAS,IAAI,CAAC,IAAY,KAAK,KAAK,CAAO,CAAC,CAAC,EAEpE",
8
- "debugId": "85CFC69CB504195764756E2164756E21",
7
+ "mappings": "4EAqBA,DAAM,EAAmB,CAAC,IAAK,IAAK,IAAK,IAAK,GAAG,EAEjD,SAAS,CAAY,CAAC,EAAiB,EAAiB,EAAsB,CAC5E,GAAI,IAAY,cACd,OAAO,EAAO,IAAM,EAAU,GAEhC,GAAI,IAAY,SACd,OAAO,EAAO,EAEhB,OAAO,EAGT,SAAS,CAAW,CAAC,EAAc,EAA4B,CAC7D,GAAI,aAAe,GAAe,EAAI,aAAe,mBACnD,MAAO,GAGT,GACE,OAAO,IAAQ,UACf,IAAQ,MACR,eAAgB,GAChB,OAAQ,EAAgC,aAAe,SAEvD,OAAO,EAAQ,SAAU,EAA+B,UAAU,EAGpE,MAAO,GAOF,MAAM,CAEb,CAmBqB,MAGA,OArBV,SAAW,QAGH,YAEA,QAEA,UAEA,QAEA,QAET,cAGR,WAAW,CAEQ,EACjB,EAEiB,EAAwC,CAAC,IACxD,IAAI,QAAQ,CAAC,IAAY,WAAW,EAAS,CAAE,CAAC,EAClD,CALiB,aAGA,cAGjB,KAAK,YAAc,GAAQ,aAAe,EAC1C,KAAK,QAAU,GAAQ,SAAW,cAClC,KAAK,UAAY,GAAQ,WAAa,KACtC,KAAK,QAAU,GAAQ,SAAW,EAClC,KAAK,QAAU,GAAQ,QACvB,KAAK,cAAgB,OASvB,gBAAgB,CAAC,EAAoD,CACnE,GACE,IAAa,QACb,KAAK,gBAAkB,QACvB,KAAK,gBAAkB,GAGvB,GAAI,EADiB,OAAO,QAAY,KAAe,QAAQ,KAAK,WAAa,cAE/E,QAAQ,KACN,4JACF,EAGJ,KAAK,cAAgB,OAIjB,KAAI,CAAC,EAAqC,CAC9C,IAAI,EAEJ,QAAS,EAAU,EAAG,GAAW,KAAK,YAAa,IACjD,GAAI,CACF,OAAO,MAAM,KAAK,MAAM,KAAK,CAAO,EACpC,MAAO,EAAK,CAEZ,GADA,EAAY,EACR,IAAY,KAAK,YACnB,MAAM,EAER,GAAI,CAAC,EAAY,EAAK,KAAK,OAAO,EAChC,MAAM,EAER,IAAM,EAAQ,EAAa,EAAS,KAAK,QAAS,KAAK,SAAS,EAChE,KAAK,UAAU,EAAS,CAAG,EAC3B,KAAK,gBAAgB,EAAS,CAAG,EACjC,MAAM,KAAK,OAAO,CAAK,EAI3B,MAAM,OAIF,OAAM,EAA0B,CACpC,GAAI,KAAK,MAAM,OACb,OAAO,KAAK,MAAM,OAAO,EAE3B,MAAO,CAAE,GAAI,GAAM,SAAU,OAAQ,OAIjC,MAAK,EAAkB,CAC3B,MAAM,KAAK,MAAM,QAAQ,OAIrB,UAAS,CAAC,EAA0C,CACxD,GAAI,KAAK,MAAM,UACb,OAAO,KAAK,MAAM,UAAU,CAAQ,EAEtC,OAAO,QAAQ,IAAI,EAAS,IAAI,CAAC,IAAY,KAAK,KAAK,CAAO,CAAC,CAAC,EAEpE",
8
+ "debugId": "F115F560369E227A64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,3 @@
1
- import{I as R}from"../chunk-skqhj0rm.js";import{Q as H,S as K}from"../chunk-93vqxxj2.js";import{Y as N}from"../chunk-ywq6s10g.js";import{ca as O,da as Q}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class M extends O{statusCode;apiError;constructor(q,w,D){super(q,Q(w),{statusCode:w,provider:"sendgrid",cause:D});this.statusCode=w;this.apiError=D;this.name="SendGridError"}}class U{provider="sendgrid";apiKey;constructor(q){this.apiKey=q.apiKey}buildPersonalization(q){return{to:H(q.to).map((w)=>({email:w.address,name:w.name})),...q.cc?{cc:H(q.cc).map((w)=>({email:w.address,name:w.name}))}:{},...q.bcc?{bcc:H(q.bcc).map((w)=>({email:w.address,name:w.name}))}:{},subject:q.subject,...q.headers?{headers:q.headers}:{}}}buildContent(q){return[...q.text?[{type:"text/plain",value:q.text}]:[],...q.html?[{type:"text/html",value:q.html}]:[]]}toSendResult(q,w,D="Accepted"){let J=H(q.from)[0];return{messageId:w,accepted:K(q.to),rejected:[],response:D,envelope:{from:J?.address??"",to:[...K(q.to),...q.cc?K(q.cc):[],...q.bcc?K(q.bcc):[]]}}}async send(q){let w=await R(q.attachments),D=H(q.from)[0],J={personalizations:[this.buildPersonalization(q)],from:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},...q.replyTo?{reply_to:H(q.replyTo).map((z)=>({email:z.address,name:z.name}))[0]}:{},content:this.buildContent(q),...w.length>0?{attachments:w.map((z)=>({filename:z.filename,type:z.contentType??"application/octet-stream",content:z.content instanceof Uint8Array?N(z.content).replace(/\r\n/g,""):z.content,...z.contentId?{content_id:z.contentId}:{},disposition:z.inline?"inline":"attachment"}))}:{}},F=await fetch("https://api.sendgrid.com/v3/mail/send",{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(J)});if(!F.ok){let z=await F.text();throw new M("SendGrid API error",F.status,z)}let L=F.headers.get("x-message-id")??q.messageId??"";return this.toSendResult(q,L)}async sendBatch(q){if(q.length===0)return[];let w=q[0],D=H(w.from)[0],J={personalizations:q.map((z)=>this.buildPersonalization(z)),from:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},...w.replyTo?{reply_to:H(w.replyTo).map((z)=>({email:z.address,name:z.name}))[0]}:{},content:this.buildContent(w)},F=await fetch("https://api.sendgrid.com/v3/mail/send",{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(J)});if(!F.ok){let z=await F.text();throw new M("SendGrid API error",F.status,z)}let L=F.headers.get("x-message-id")??"";return q.map((z)=>this.toSendResult(z,L||z.messageId||""))}async verify(){try{let q=await fetch("https://api.sendgrid.com/v3/user/profile",{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!q.ok)return{ok:!1,provider:"sendgrid",message:await q.text().catch(()=>"")||`HTTP ${q.status}`};let w=await q.json();return{ok:!0,provider:"sendgrid",...w.username?{message:w.username}:{}}}catch(q){return{ok:!1,provider:"sendgrid",message:q instanceof Error?q.message:String(q)}}}}export{U as SendGridTransport,M as SendGridError};
1
+ import{K as R}from"../chunk-1ke3nmyg.js";import{S as H,U as K}from"../chunk-8kpgbrba.js";import{da as N}from"../chunk-hnzmn4s4.js";import{ja as O,ka as Q}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class M extends O{statusCode;apiError;constructor(q,w,D){super(q,Q(w),{statusCode:w,provider:"sendgrid",cause:D});this.statusCode=w;this.apiError=D;this.name="SendGridError"}}class U{provider="sendgrid";apiKey;constructor(q){this.apiKey=q.apiKey}buildPersonalization(q){return{to:H(q.to).map((w)=>({email:w.address,name:w.name})),...q.cc?{cc:H(q.cc).map((w)=>({email:w.address,name:w.name}))}:{},...q.bcc?{bcc:H(q.bcc).map((w)=>({email:w.address,name:w.name}))}:{},subject:q.subject,...q.headers?{headers:q.headers}:{}}}buildContent(q){return[...q.text?[{type:"text/plain",value:q.text}]:[],...q.html?[{type:"text/html",value:q.html}]:[]]}toSendResult(q,w,D="Accepted"){let J=H(q.from)[0];return{messageId:w,accepted:K(q.to),rejected:[],response:D,envelope:{from:J?.address??"",to:[...K(q.to),...q.cc?K(q.cc):[],...q.bcc?K(q.bcc):[]]}}}async send(q){let w=await R(q.attachments),D=H(q.from)[0],J={personalizations:[this.buildPersonalization(q)],from:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},...q.replyTo?{reply_to:H(q.replyTo).map((z)=>({email:z.address,name:z.name}))[0]}:{},content:this.buildContent(q),...w.length>0?{attachments:w.map((z)=>({filename:z.filename,type:z.contentType??"application/octet-stream",content:z.content instanceof Uint8Array?N(z.content).replace(/\r\n/g,""):z.content,...z.contentId?{content_id:z.contentId}:{},disposition:z.inline?"inline":"attachment"}))}:{}},F=await fetch("https://api.sendgrid.com/v3/mail/send",{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(J)});if(!F.ok){let z=await F.text();throw new M("SendGrid API error",F.status,z)}let L=F.headers.get("x-message-id")??q.messageId??"";return this.toSendResult(q,L)}async sendBatch(q){if(q.length===0)return[];let w=q[0],D=H(w.from)[0],J={personalizations:q.map((z)=>this.buildPersonalization(z)),from:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},...w.replyTo?{reply_to:H(w.replyTo).map((z)=>({email:z.address,name:z.name}))[0]}:{},content:this.buildContent(w)},F=await fetch("https://api.sendgrid.com/v3/mail/send",{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(J)});if(!F.ok){let z=await F.text();throw new M("SendGrid API error",F.status,z)}let L=F.headers.get("x-message-id")??"";return q.map((z)=>this.toSendResult(z,L||z.messageId||""))}async verify(){try{let q=await fetch("https://api.sendgrid.com/v3/user/profile",{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!q.ok)return{ok:!1,provider:"sendgrid",message:await q.text().catch(()=>"")||`HTTP ${q.status}`};let w=await q.json();return{ok:!0,provider:"sendgrid",...w.username?{message:w.username}:{}}}catch(q){return{ok:!1,provider:"sendgrid",message:q instanceof Error?q.message:String(q)}}}}export{U as SendGridTransport,M as SendGridError};
2
2
 
3
- //# debugId=D36AE2D50C91F89564756E2164756E21
3
+ //# debugId=603F05A2076CBCA364756E2164756E21
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * @module\n * SendGrid v3 HTTP API transport for sending email via api.sendgrid.com.\n *\n * @example\n * ```ts\n * import { SendGridTransport } from \"sently/transports/sendgrid\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new SendGridTransport({ apiKey: process.env.SENDGRID_API_KEY! }),\n * });\n *\n * await mailer.send({\n * from: \"sender@example.com\",\n * to: \"recipient@example.com\",\n * subject: \"Hello\",\n * text: \"Plain text body\",\n * });\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** SendGrid API configuration. */\nexport interface SendGridConfig {\n /** SendGrid API key (Bearer token). */\n apiKey: string;\n}\n\n/** Error thrown when the SendGrid API returns a non-success response. */\nexport class SendGridError extends SentlyError {\n /** Creates a SendGrid API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"sendgrid\",\n cause: apiError,\n });\n this.name = \"SendGridError\";\n }\n}\n\n/**\n * SendGrid v3 HTTP API transport.\n */\nexport class SendGridTransport implements Transport {\n readonly provider = \"sendgrid\";\n\n /** SendGrid API key for Bearer authentication. */\n private readonly apiKey: string;\n\n /** Creates a SendGrid transport with the given API key. */\n constructor(config: SendGridConfig) {\n this.apiKey = config.apiKey;\n }\n\n /** Build a SendGrid personalization for one message. */\n private buildPersonalization(options: MailOptions): Record<string, unknown> {\n return {\n to: parseAddresses(options.to).map((addr) => ({ email: addr.address, name: addr.name })),\n ...(options.cc\n ? {\n cc: parseAddresses(options.cc).map((addr) => ({\n email: addr.address,\n name: addr.name,\n })),\n }\n : {}),\n ...(options.bcc\n ? {\n bcc: parseAddresses(options.bcc).map((addr) => ({\n email: addr.address,\n name: addr.name,\n })),\n }\n : {}),\n subject: options.subject,\n ...(options.headers ? { headers: options.headers } : {}),\n };\n }\n\n /** Build SendGrid content parts from text/html options. */\n private buildContent(options: MailOptions): Array<{ type: string; value: string }> {\n return [\n ...(options.text ? [{ type: \"text/plain\", value: options.text }] : []),\n ...(options.html ? [{ type: \"text/html\", value: options.html }] : []),\n ];\n }\n\n /** Map a SendGrid response to a normalized SendResult for one message. */\n private toSendResult(\n options: MailOptions,\n messageId: string,\n responseText = \"Accepted\",\n ): SendResult {\n const from = parseAddresses(options.from)[0];\n return {\n messageId,\n accepted: extractEmails(options.to),\n rejected: [],\n response: responseText,\n envelope: {\n from: from?.address ?? \"\",\n to: [\n ...extractEmails(options.to),\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ],\n },\n };\n }\n\n /** Sends an email via the SendGrid v3 HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n\n const body = {\n personalizations: [this.buildPersonalization(options)],\n from: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n ...(options.replyTo\n ? {\n reply_to: parseAddresses(options.replyTo).map((addr) => ({\n email: addr.address,\n name: addr.name,\n }))[0],\n }\n : {}),\n content: this.buildContent(options),\n ...(attachments.length > 0\n ? {\n attachments: attachments.map((att) => ({\n filename: att.filename,\n type: att.contentType ?? \"application/octet-stream\",\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n ...(att.contentId ? { content_id: att.contentId } : {}),\n disposition: att.inline ? \"inline\" : \"attachment\",\n })),\n }\n : {}),\n };\n\n const response = await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const apiError = await response.text();\n throw new SendGridError(\"SendGrid API error\", response.status, apiError);\n }\n\n const messageId = response.headers.get(\"x-message-id\") ?? options.messageId ?? \"\";\n\n return this.toSendResult(options, messageId);\n }\n\n /** Send multiple messages in one request using SendGrid personalizations. */\n async sendBatch(messages: MailOptions[]): Promise<SendResult[]> {\n if (messages.length === 0) {\n return [];\n }\n\n const first = messages[0] as MailOptions;\n const from = parseAddresses(first.from)[0];\n\n const body = {\n personalizations: messages.map((message) => this.buildPersonalization(message)),\n from: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n ...(first.replyTo\n ? {\n reply_to: parseAddresses(first.replyTo).map((addr) => ({\n email: addr.address,\n name: addr.name,\n }))[0],\n }\n : {}),\n content: this.buildContent(first),\n };\n\n const response = await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const apiError = await response.text();\n throw new SendGridError(\"SendGrid API error\", response.status, apiError);\n }\n\n const messageId = response.headers.get(\"x-message-id\") ?? \"\";\n return messages.map((message) =>\n this.toSendResult(message, messageId || message.messageId || \"\"),\n );\n }\n\n /** Verifies the SendGrid API key by fetching the user profile. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(\"https://api.sendgrid.com/v3/user/profile\", {\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n },\n });\n\n if (!response.ok) {\n const apiError = await response.text().catch(() => \"\");\n return {\n ok: false,\n provider: \"sendgrid\",\n message: apiError || `HTTP ${response.status}`,\n };\n }\n\n const payload = (await response.json()) as { username?: string };\n return {\n ok: true,\n provider: \"sendgrid\",\n ...(payload.username ? { message: payload.username } : {}),\n };\n } catch (err) {\n return {\n ok: false,\n provider: \"sendgrid\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "D36AE2D50C91F89564756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "603F05A2076CBCA364756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,7 +1,7 @@
1
- import{u as v}from"../chunk-8dty8c4p.js";import{I as b}from"../chunk-skqhj0rm.js";import{Q as C,R,S as D}from"../chunk-93vqxxj2.js";import{Y as x}from"../chunk-ywq6s10g.js";import{ca as A,da as k}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";var z=new TextEncoder;async function K(F){let G=await crypto.subtle.digest("SHA-256",z.encode(F));return T(new Uint8Array(G))}async function w(F,G){let J=typeof F==="string"?z.encode(F):new Uint8Array(F),X=await crypto.subtle.importKey("raw",J,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),L=await crypto.subtle.sign("HMAC",X,z.encode(G));return new Uint8Array(L)}async function B(F){let{method:G,url:J,body:X,credentials:L}=F,Y=new URL(J),Z=F._date??`${new Date().toISOString().replace(/[-:]/g,"").slice(0,15)}Z`,Q=Z.slice(0,8),$={...Object.fromEntries(Object.entries(F.headers).map(([N,S])=>[N.toLowerCase(),S.trim()])),host:Y.host,"x-amz-date":Z};if(L.sessionToken)$["x-amz-security-token"]=L.sessionToken;let O=Object.keys($).map((N)=>N.toLowerCase()).sort(),V=O.join(";"),P=`${O.map((N)=>`${N}:${$[N]}`).join(`
1
+ import{w as v}from"../chunk-yg3kj723.js";import{K as b}from"../chunk-1ke3nmyg.js";import{S as C,T as R,U as D}from"../chunk-8kpgbrba.js";import{da as x}from"../chunk-hnzmn4s4.js";import{ja as A,ka as k}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";var z=new TextEncoder;async function K(F){let G=await crypto.subtle.digest("SHA-256",z.encode(F));return T(new Uint8Array(G))}async function w(F,G){let J=typeof F==="string"?z.encode(F):new Uint8Array(F),X=await crypto.subtle.importKey("raw",J,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),L=await crypto.subtle.sign("HMAC",X,z.encode(G));return new Uint8Array(L)}async function B(F){let{method:G,url:J,body:X,credentials:L}=F,Y=new URL(J),Z=F._date??`${new Date().toISOString().replace(/[-:]/g,"").slice(0,15)}Z`,Q=Z.slice(0,8),$={...Object.fromEntries(Object.entries(F.headers).map(([N,S])=>[N.toLowerCase(),S.trim()])),host:Y.host,"x-amz-date":Z};if(L.sessionToken)$["x-amz-security-token"]=L.sessionToken;let O=Object.keys($).map((N)=>N.toLowerCase()).sort(),V=O.join(";"),P=`${O.map((N)=>`${N}:${$[N]}`).join(`
2
2
  `)}
3
3
  `,I=y(Y.searchParams),U=await K(X),W=[G.toUpperCase(),u(Y.pathname),I,P,V,U].join(`
4
4
  `),_=`${Q}/${L.region}/${L.service}/aws4_request`,M=["AWS4-HMAC-SHA256",Z,_,await K(W)].join(`
5
5
  `),H=await h(L.secretAccessKey,Q,L.region,L.service),f=T(await w(H,M)),E=[`AWS4-HMAC-SHA256 Credential=${L.accessKeyId}/${_}`,`SignedHeaders=${V}`,`Signature=${f}`].join(", ");return{headers:{...$,Authorization:E}}}async function h(F,G,J,X){let L=await w(`AWS4${F}`,G),Y=await w(L,J),Z=await w(Y,X);return w(Z,"aws4_request")}function u(F){if(!F||F==="/")return"/";return F.split("/").map((G)=>encodeURIComponent(decodeURIComponent(G))).join("/")}function y(F){let G=[];for(let[J,X]of F.entries())G.push(`${j(J)}=${j(X)}`);return G.sort(),G.join("&")}function j(F){return encodeURIComponent(F).replace(/[!'()*]/g,(G)=>`%${G.charCodeAt(0).toString(16).toUpperCase()}`)}function T(F){return Array.from(F,(G)=>G.toString(16).padStart(2,"0")).join("")}class q extends A{statusCode;requestId;constructor(F,G,J,X){super(F,k(G),{statusCode:G,provider:"ses"});this.statusCode=G;this.requestId=X;this.name="SESError",this.code=J}}class g{provider="ses";accessKeyId;secretAccessKey;region;sessionToken;dkim;constructor(F){this.accessKeyId=F.accessKeyId,this.secretAccessKey=F.secretAccessKey,this.region=F.region??"us-east-1",this.sessionToken=F.sessionToken,this.dkim=F.dkim}async send(F){let G=await b(F.attachments),J={...F,attachments:G},X=C(F.from)[0],L=X?R(X):"",Y=D(F.to),Z=F.cc?D(F.cc):[],Q=F.bcc?D(F.bcc):[],$={ToAddresses:Y,CcAddresses:Z,BccAddresses:Q},O;if(G.length>0){let M=await v(J,this.dkim);O={FromEmailAddress:L,Destination:$,Content:{Raw:{Data:x(M.raw).replace(/\r\n/g,"")}}}}else O={FromEmailAddress:L,Destination:$,Content:{Simple:{Subject:{Data:F.subject,Charset:"UTF-8"},Body:{...F.text?{Text:{Data:F.text,Charset:"UTF-8"}}:{},...F.html?{Html:{Data:F.html,Charset:"UTF-8"}}:{}}}}};let V=JSON.stringify(O),P=`https://email.${this.region}.amazonaws.com/v2/email/outbound-emails`,I=await B({method:"POST",url:P,headers:{"content-type":"application/json"},body:V,credentials:{accessKeyId:this.accessKeyId,secretAccessKey:this.secretAccessKey,region:this.region,service:"ses",...this.sessionToken?{sessionToken:this.sessionToken}:{}}}),U=await fetch(P,{method:"POST",headers:I.headers,body:V}),W=await U.json();if(!U.ok)throw new q(W.message??"SES API error",U.status,W.Code??"",U.headers.get("x-amzn-requestid")??"");let _=W.MessageId??"";return{messageId:_,accepted:[...Y,...Z,...Q],rejected:[],response:`MessageId: ${_}`,envelope:{from:X?.address??"",to:Y}}}async verify(){try{let F=`https://email.${this.region}.amazonaws.com/v2/email/configuration-sets`,G=await B({method:"GET",url:F,headers:{},body:"",credentials:{accessKeyId:this.accessKeyId,secretAccessKey:this.secretAccessKey,region:this.region,service:"ses",...this.sessionToken?{sessionToken:this.sessionToken}:{}}}),J=await fetch(F,{method:"GET",headers:G.headers}),X=await J.json().catch(()=>({}));if(!J.ok)return{ok:!1,provider:"ses",message:X.message??`HTTP ${J.status}`};return{ok:!0,provider:"ses",message:"Credentials are valid"}}catch(F){return{ok:!1,provider:"ses",message:F instanceof Error?F.message:String(F)}}}}export{g as SESTransport,q as SESError};
6
6
 
7
- //# debugId=2D3D31F22A2D609A64756E2164756E21
7
+ //# debugId=64EB4A6F1F66261A64756E2164756E21
@@ -5,7 +5,7 @@
5
5
  "/**\n * @module\n * AWS Signature Version 4 signing using Web Crypto (HMAC-SHA256).\n * Works on Node.js, Bun, Deno, and Cloudflare Workers.\n * No external dependencies.\n *\n * @example\n * ```ts\n * import { signRequest } from \"sently/core/sigv4\";\n * const signed = await signRequest({\n * method: \"POST\",\n * url: \"https://email.us-east-1.amazonaws.com/v2/email/outbound-emails\",\n * headers: { \"content-type\": \"application/json\" },\n * body: '{\"...\"}',\n * credentials: { accessKeyId, secretAccessKey, region: \"us-east-1\", service: \"ses\" },\n * });\n * ```\n */\n\n/** AWS credentials and signing scope for SigV4. */\nexport interface SigV4Credentials {\n /** AWS access key ID. */\n accessKeyId: string;\n /** AWS secret access key. */\n secretAccessKey: string;\n /** AWS region (e.g. `us-east-1`). */\n region: string;\n /** AWS service name (e.g. `ses`, `s3`). */\n service: string;\n /** Optional STS session token for temporary credentials. */\n sessionToken?: string;\n}\n\n/** HTTP request to sign with AWS Signature Version 4. */\nexport interface SigV4Request {\n /** HTTP method (e.g. `POST`). */\n method: string;\n /** Full request URL including path and query. */\n url: string;\n /** Request headers to include in the signature. */\n headers: Record<string, string>;\n /** Request body as a string (empty for GET). */\n body: string;\n /** AWS credentials and signing scope. */\n credentials: SigV4Credentials;\n /** Override datetime for testing. Full 'YYYYMMDDTHHMMSSZ' when provided. */\n _date?: string;\n}\n\n/** Signed request headers including Authorization. */\nexport interface SigV4Result {\n /** All headers including Authorization, x-amz-date, and x-amz-security-token */\n headers: Record<string, string>;\n}\n\nconst encoder = new TextEncoder();\n\n/**\n * Compute SHA-256 hash of a string using Web Crypto.\n * Returns lowercase hex string.\n * @internal\n */\nexport async function sha256Hex(data: string): Promise<string> {\n const hash = await crypto.subtle.digest(\"SHA-256\", encoder.encode(data));\n return bytesToHex(new Uint8Array(hash));\n}\n\n/**\n * Compute HMAC-SHA256 using Web Crypto.\n * @internal\n */\nexport async function hmacSHA256(key: Uint8Array | string, data: string): Promise<Uint8Array> {\n const keyBytes = typeof key === \"string\" ? encoder.encode(key) : new Uint8Array(key);\n const cryptoKey = await crypto.subtle.importKey(\n \"raw\",\n keyBytes,\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const signature = await crypto.subtle.sign(\"HMAC\", cryptoKey, encoder.encode(data));\n return new Uint8Array(signature);\n}\n\n/**\n * Sign an HTTP request with AWS Signature Version 4.\n * Returns the complete set of headers to include in the request.\n */\nexport async function signRequest(request: SigV4Request): Promise<SigV4Result> {\n const { method, url, body, credentials } = request;\n const parsed = new URL(url);\n const amzDate = request._date ?? `${new Date().toISOString().replace(/[-:]/g, \"\").slice(0, 15)}Z`;\n const dateStamp = amzDate.slice(0, 8);\n\n const headers: Record<string, string> = {\n ...Object.fromEntries(\n Object.entries(request.headers).map(([key, value]) => [key.toLowerCase(), value.trim()]),\n ),\n host: parsed.host,\n \"x-amz-date\": amzDate,\n };\n\n if (credentials.sessionToken) {\n headers[\"x-amz-security-token\"] = credentials.sessionToken;\n }\n\n const signedHeaderNames = Object.keys(headers)\n .map((name) => name.toLowerCase())\n .sort();\n const signedHeaders = signedHeaderNames.join(\";\");\n const canonicalHeaders = `${signedHeaderNames.map((name) => `${name}:${headers[name]}`).join(\"\\n\")}\\n`;\n const canonicalQuery = normalizeQuery(parsed.searchParams);\n const payloadHash = await sha256Hex(body);\n const canonicalRequest = [\n method.toUpperCase(),\n canonicalUri(parsed.pathname),\n canonicalQuery,\n canonicalHeaders,\n signedHeaders,\n payloadHash,\n ].join(\"\\n\");\n\n const credentialScope = `${dateStamp}/${credentials.region}/${credentials.service}/aws4_request`;\n const stringToSign = [\n \"AWS4-HMAC-SHA256\",\n amzDate,\n credentialScope,\n await sha256Hex(canonicalRequest),\n ].join(\"\\n\");\n\n const signingKey = await deriveSigningKey(\n credentials.secretAccessKey,\n dateStamp,\n credentials.region,\n credentials.service,\n );\n const signature = bytesToHex(await hmacSHA256(signingKey, stringToSign));\n const authorization = [\n `AWS4-HMAC-SHA256 Credential=${credentials.accessKeyId}/${credentialScope}`,\n `SignedHeaders=${signedHeaders}`,\n `Signature=${signature}`,\n ].join(\", \");\n\n return {\n headers: {\n ...headers,\n Authorization: authorization,\n },\n };\n}\n\nasync function deriveSigningKey(\n secretAccessKey: string,\n dateStamp: string,\n region: string,\n service: string,\n): Promise<Uint8Array> {\n const kDate = await hmacSHA256(`AWS4${secretAccessKey}`, dateStamp);\n const kRegion = await hmacSHA256(kDate, region);\n const kService = await hmacSHA256(kRegion, service);\n return hmacSHA256(kService, \"aws4_request\");\n}\n\nfunction canonicalUri(pathname: string): string {\n if (!pathname || pathname === \"/\") {\n return \"/\";\n }\n return pathname\n .split(\"/\")\n .map((segment) => encodeURIComponent(decodeURIComponent(segment)))\n .join(\"/\");\n}\n\nfunction normalizeQuery(searchParams: URLSearchParams): string {\n const pairs: string[] = [];\n for (const [key, value] of searchParams.entries()) {\n pairs.push(`${encodeRfc3986(key)}=${encodeRfc3986(value)}`);\n }\n pairs.sort();\n return pairs.join(\"&\");\n}\n\nfunction encodeRfc3986(value: string): string {\n return encodeURIComponent(value).replace(\n /[!'()*]/g,\n (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n",
6
6
  "/**\n * @module\n * AWS SES v2 HTTP transport for sently.\n * Signs requests with AWS Signature Version 4 using Web Crypto.\n * Works on Node.js, Bun, Deno, and Cloudflare Workers.\n *\n * @example\n * ```ts\n * import { SESTransport } from \"sently/transports/ses\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new SESTransport({\n * accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n * region: \"us-east-1\",\n * }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses, toMIMEHeader } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport { buildMIME } from \"../core/mime.js\";\nimport { signRequest } from \"../core/sigv4.js\";\nimport type { MailOptions, SESConfig, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Error thrown when the AWS SES API returns a non-success response. */\nexport class SESError extends SentlyError {\n /** Creates an AWS SES API error with status code, error code, and request ID. */\n constructor(\n message: string,\n public readonly statusCode: number,\n awsCode: string,\n public readonly requestId: string,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"ses\",\n });\n this.name = \"SESError\";\n this.code = awsCode;\n }\n}\n\n/**\n * AWS SES v2 HTTP API transport.\n */\nexport class SESTransport implements Transport {\n readonly provider = \"ses\";\n\n /** AWS access key ID for SigV4 signing. */\n private readonly accessKeyId: string;\n /** AWS secret access key for SigV4 signing. */\n private readonly secretAccessKey: string;\n /** AWS region for the SES endpoint. */\n private readonly region: string;\n /** Optional STS session token for temporary credentials. */\n private readonly sessionToken: string | undefined;\n /** Optional DKIM config for raw MIME attachment sends. */\n private readonly dkim: SESConfig[\"dkim\"];\n\n /** Creates an SES transport with AWS credentials. */\n constructor(config: SESConfig) {\n this.accessKeyId = config.accessKeyId;\n this.secretAccessKey = config.secretAccessKey;\n this.region = config.region ?? \"us-east-1\";\n this.sessionToken = config.sessionToken;\n this.dkim = config.dkim;\n }\n\n /** Sends an email via the AWS SES v2 HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const resolvedOptions = { ...options, attachments };\n const from = parseAddresses(options.from)[0];\n const fromEmail = from ? toMIMEHeader(from) : \"\";\n const toEmails = extractEmails(options.to);\n const ccEmails = options.cc ? extractEmails(options.cc) : [];\n const bccEmails = options.bcc ? extractEmails(options.bcc) : [];\n\n const destination = {\n ToAddresses: toEmails,\n CcAddresses: ccEmails,\n BccAddresses: bccEmails,\n };\n\n let requestBody: Record<string, unknown>;\n\n if (attachments.length > 0) {\n const mime = await buildMIME(resolvedOptions, this.dkim);\n requestBody = {\n FromEmailAddress: fromEmail,\n Destination: destination,\n Content: {\n Raw: {\n Data: encodeBase64(mime.raw).replace(/\\r\\n/g, \"\"),\n },\n },\n };\n } else {\n requestBody = {\n FromEmailAddress: fromEmail,\n Destination: destination,\n Content: {\n Simple: {\n Subject: { Data: options.subject, Charset: \"UTF-8\" },\n Body: {\n ...(options.text ? { Text: { Data: options.text, Charset: \"UTF-8\" } } : {}),\n ...(options.html ? { Html: { Data: options.html, Charset: \"UTF-8\" } } : {}),\n },\n },\n },\n };\n }\n\n const body = JSON.stringify(requestBody);\n const url = `https://email.${this.region}.amazonaws.com/v2/email/outbound-emails`;\n const signed = await signRequest({\n method: \"POST\",\n url,\n headers: {\n \"content-type\": \"application/json\",\n },\n body,\n credentials: {\n accessKeyId: this.accessKeyId,\n secretAccessKey: this.secretAccessKey,\n region: this.region,\n service: \"ses\",\n ...(this.sessionToken ? { sessionToken: this.sessionToken } : {}),\n },\n });\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: signed.headers,\n body,\n });\n\n const payload = (await response.json()) as {\n MessageId?: string;\n message?: string;\n Code?: string;\n };\n\n if (!response.ok) {\n throw new SESError(\n payload.message ?? \"SES API error\",\n response.status,\n payload.Code ?? \"\",\n response.headers.get(\"x-amzn-requestid\") ?? \"\",\n );\n }\n\n const messageId = payload.MessageId ?? \"\";\n return {\n messageId,\n accepted: [...toEmails, ...ccEmails, ...bccEmails],\n rejected: [],\n response: `MessageId: ${messageId}`,\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies AWS credentials by listing SES configuration sets. */\n async verify(): Promise<VerifyResult> {\n try {\n const url = `https://email.${this.region}.amazonaws.com/v2/email/configuration-sets`;\n const signed = await signRequest({\n method: \"GET\",\n url,\n headers: {},\n body: \"\",\n credentials: {\n accessKeyId: this.accessKeyId,\n secretAccessKey: this.secretAccessKey,\n region: this.region,\n service: \"ses\",\n ...(this.sessionToken ? { sessionToken: this.sessionToken } : {}),\n },\n });\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: signed.headers,\n });\n\n const payload = (await response.json().catch(() => ({}))) as { message?: string };\n\n if (!response.ok) {\n return {\n ok: false,\n provider: \"ses\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return { ok: true, provider: \"ses\", message: \"Credentials are valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"ses\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
7
7
  ],
8
- "mappings": "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",
9
- "debugId": "2D3D31F22A2D609A64756E2164756E21",
8
+ "mappings": "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",
9
+ "debugId": "64EB4A6F1F66261A64756E2164756E21",
10
10
  "names": []
11
11
  }
@@ -1,3 +1,3 @@
1
- import{l as c,m as d,n as e,o as f,p as g,q as h}from"../chunk-vjpds2pf.js";import"../chunk-b602dhck.js";import"../chunk-8dty8c4p.js";import{G as b,w as a}from"../chunk-n0qeyzqm.js";import"../chunk-skqhj0rm.js";import"../chunk-93vqxxj2.js";import"../chunk-ywq6s10g.js";import"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";export{d as resolveSMTPConfig,h as readSMTPResponse,e as openSMTPSession,b as encodeLine,f as deliverSMTPMessage,g as closeSMTPSession,c as SMTPTransport,a as SMTPError};
1
+ import{n as c,o as d,p as e,q as f,r as g,s as h}from"../chunk-a057jzsy.js";import"../chunk-f5x8vesr.js";import"../chunk-yg3kj723.js";import{I as b,y as a}from"../chunk-w4c9kj70.js";import"../chunk-1ke3nmyg.js";import"../chunk-8kpgbrba.js";import"../chunk-hnzmn4s4.js";import"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";export{d as resolveSMTPConfig,h as readSMTPResponse,e as openSMTPSession,b as encodeLine,f as deliverSMTPMessage,g as closeSMTPSession,c as SMTPTransport,a as SMTPError};
2
2
 
3
- //# debugId=4D50658004631B9764756E2164756E21
3
+ //# debugId=8DFA86AED4A5A20E64756E2164756E21
@@ -4,6 +4,6 @@
4
4
  "sourcesContent": [
5
5
  ],
6
6
  "mappings": "",
7
- "debugId": "4D50658004631B9764756E2164756E21",
7
+ "debugId": "8DFA86AED4A5A20E64756E2164756E21",
8
8
  "names": []
9
9
  }
@@ -1,3 +1,3 @@
1
- import{H as V}from"../chunk-8njyga62.js";import{Q as W,R as Z,S as B}from"../chunk-93vqxxj2.js";import"../chunk-ywq6s10g.js";import{ca as $,da as G}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";var Y="x-sndr-template-id";class v extends ${statusCode;apiError;constructor(q,w,F){super(q,G(w),{statusCode:w,provider:"sndr",cause:F});this.statusCode=w;this.apiError=F;this.name="SndrError"}}class j{provider="sndr";apiKey;baseUrl;defaultTemplateId;constructor(q){if(this.apiKey=q.apiKey,this.baseUrl=(q.baseUrl??"https://api.sndr.sh").replace(/\/+$/,""),q.defaultTemplateId!==void 0)this.defaultTemplateId=q.defaultTemplateId}async send(q){let w=W(q.from)[0],F=B(q.to),O=q.headers?.[Y]??this.defaultTemplateId,J={from:w?Z(w):"",to:F,subject:q.subject,...q.cc?{cc:B(q.cc)}:{},...q.bcc?{bcc:B(q.bcc)}:{},...q.replyTo?{reply_to:(()=>{return W(q.replyTo)[0]?.address})()}:{},...q.html?{html:q.html}:{},...q.text?{text:q.text}:{},...O?{template_id:O}:{},...O&&q.data?{variables:q.data}:{}};if(q.headers){let U=Object.fromEntries(Object.entries(q.headers).filter(([N])=>N!==Y));if(Object.keys(U).length>0)J.headers=U}if(J.reply_to===void 0)delete J.reply_to;let X=V(q),Q=await fetch(`${this.baseUrl}/v1/send`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json",Accept:"application/json",...X?{"Idempotency-Key":X}:{}},body:JSON.stringify(J)}),z=await Q.json().catch(()=>({}));if(!Q.ok)throw new v(z.error?.message??z.message??"SNDR API error",Q.status,z);let L=w?.address??"";return{messageId:z.id??q.messageId??"",accepted:F,rejected:[],response:z.status??z.id??"queued",envelope:{from:L,to:[...F,...q.cc?B(q.cc):[],...q.bcc?B(q.bcc):[]]}}}async verify(){try{let q=await fetch(`${this.baseUrl}/v1/domains`,{headers:{Authorization:`Bearer ${this.apiKey}`,Accept:"application/json"}});if(!q.ok){let w=await q.json().catch(()=>({}));return{ok:!1,provider:"sndr",message:w.error?.message??w.message??`HTTP ${q.status}`}}return{ok:!0,provider:"sndr",message:"API key is valid"}}catch(q){return{ok:!1,provider:"sndr",message:q instanceof Error?q.message:String(q)}}}}export{j as SndrTransport,v as SndrError,Y as SNDR_TEMPLATE_ID_HEADER};
1
+ import{J as V}from"../chunk-bhr7w3gg.js";import{S as W,T as Z,U as B}from"../chunk-8kpgbrba.js";import"../chunk-hnzmn4s4.js";import{ja as $,ka as G}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";var Y="x-sndr-template-id";class v extends ${statusCode;apiError;constructor(q,w,F){super(q,G(w),{statusCode:w,provider:"sndr",cause:F});this.statusCode=w;this.apiError=F;this.name="SndrError"}}class j{provider="sndr";apiKey;baseUrl;defaultTemplateId;constructor(q){if(this.apiKey=q.apiKey,this.baseUrl=(q.baseUrl??"https://api.sndr.sh").replace(/\/+$/,""),q.defaultTemplateId!==void 0)this.defaultTemplateId=q.defaultTemplateId}async send(q){let w=W(q.from)[0],F=B(q.to),O=q.headers?.[Y]??this.defaultTemplateId,J={from:w?Z(w):"",to:F,subject:q.subject,...q.cc?{cc:B(q.cc)}:{},...q.bcc?{bcc:B(q.bcc)}:{},...q.replyTo?{reply_to:(()=>{return W(q.replyTo)[0]?.address})()}:{},...q.html?{html:q.html}:{},...q.text?{text:q.text}:{},...O?{template_id:O}:{},...O&&q.data?{variables:q.data}:{}};if(q.headers){let U=Object.fromEntries(Object.entries(q.headers).filter(([N])=>N!==Y));if(Object.keys(U).length>0)J.headers=U}if(J.reply_to===void 0)delete J.reply_to;let X=V(q),Q=await fetch(`${this.baseUrl}/v1/send`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json",Accept:"application/json",...X?{"Idempotency-Key":X}:{}},body:JSON.stringify(J)}),z=await Q.json().catch(()=>({}));if(!Q.ok)throw new v(z.error?.message??z.message??"SNDR API error",Q.status,z);let L=w?.address??"";return{messageId:z.id??q.messageId??"",accepted:F,rejected:[],response:z.status??z.id??"queued",envelope:{from:L,to:[...F,...q.cc?B(q.cc):[],...q.bcc?B(q.bcc):[]]}}}async verify(){try{let q=await fetch(`${this.baseUrl}/v1/domains`,{headers:{Authorization:`Bearer ${this.apiKey}`,Accept:"application/json"}});if(!q.ok){let w=await q.json().catch(()=>({}));return{ok:!1,provider:"sndr",message:w.error?.message??w.message??`HTTP ${q.status}`}}return{ok:!0,provider:"sndr",message:"API key is valid"}}catch(q){return{ok:!1,provider:"sndr",message:q instanceof Error?q.message:String(q)}}}}export{j as SndrTransport,v as SndrError,Y as SNDR_TEMPLATE_ID_HEADER};
2
2
 
3
- //# debugId=6475E854D5CE8CE864756E2164756E21
3
+ //# debugId=0CA0AE7BD8D22B3F64756E2164756E21
@@ -5,6 +5,6 @@
5
5
  "/**\n * @module\n * SNDR HTTP API transport for sending email via api.sndr.sh.\n *\n * Aligned with the official SNDR API / `@rkiza/sndr` SDK:\n * `POST /v1/send` with Bearer auth and optional `Idempotency-Key`.\n *\n * @see https://www.sndr.sh/docs/api-reference\n *\n * @example\n * ```ts\n * import { SndrTransport } from \"sently/transports/sndr\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new SndrTransport({ apiKey: process.env.SNDR_API_KEY! }),\n * });\n *\n * await mailer.send({\n * from: \"hello@yourdomain.com\",\n * to: \"customer@example.com\",\n * subject: \"Welcome aboard\",\n * html: \"<p>Thanks for joining us.</p>\",\n * });\n * ```\n */\nimport { extractEmails, parseAddresses, toMIMEHeader } from \"../core/address.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport { resolveIdempotencyKey } from \"../core/idempotency-key.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\n\n/** Header for a per-message SNDR template ID (`template_id`). */\nexport const SNDR_TEMPLATE_ID_HEADER = \"x-sndr-template-id\";\n\n/** SNDR API configuration. */\nexport interface SndrConfig {\n /**\n * SNDR API key (starts with `sndr_live_` or `sndr_test_`).\n * Store in environment variables — never hardcode.\n */\n apiKey: string;\n /** API base URL. Default: `https://api.sndr.sh`. */\n baseUrl?: string;\n /**\n * Default SNDR template ID when not set per message via\n * {@link SNDR_TEMPLATE_ID_HEADER}.\n */\n defaultTemplateId?: string;\n}\n\n/** Error thrown when the SNDR API returns a non-success response. */\nexport class SndrError extends SentlyError {\n /** Creates an SNDR 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: \"sndr\",\n cause: apiError,\n });\n this.name = \"SndrError\";\n }\n}\n\n/**\n * SNDR HTTP API transport.\n */\nexport class SndrTransport implements Transport {\n readonly provider = \"sndr\";\n\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly defaultTemplateId?: string;\n\n /** Creates an SNDR transport with the given API key. */\n constructor(config: SndrConfig) {\n this.apiKey = config.apiKey;\n this.baseUrl = (config.baseUrl ?? \"https://api.sndr.sh\").replace(/\\/+$/, \"\");\n if (config.defaultTemplateId !== undefined) {\n this.defaultTemplateId = config.defaultTemplateId;\n }\n }\n\n /** Sends an email via `POST /v1/send`. */\n async send(options: MailOptions): Promise<SendResult> {\n const from = parseAddresses(options.from)[0];\n const to = extractEmails(options.to);\n const templateId = options.headers?.[SNDR_TEMPLATE_ID_HEADER] ?? this.defaultTemplateId;\n\n const body: Record<string, unknown> = {\n from: from ? toMIMEHeader(from) : \"\",\n to,\n subject: options.subject,\n ...(options.cc ? { cc: extractEmails(options.cc) } : {}),\n ...(options.bcc ? { bcc: extractEmails(options.bcc) } : {}),\n ...(options.replyTo\n ? {\n reply_to: (() => {\n const reply = parseAddresses(options.replyTo as MailOptions[\"to\"])[0];\n return reply?.address;\n })(),\n }\n : {}),\n ...(options.html ? { html: options.html } : {}),\n ...(options.text ? { text: options.text } : {}),\n ...(templateId ? { template_id: templateId } : {}),\n ...(templateId && options.data ? { variables: options.data } : {}),\n };\n\n if (options.headers) {\n const headers = Object.fromEntries(\n Object.entries(options.headers).filter(([key]) => key !== SNDR_TEMPLATE_ID_HEADER),\n );\n if (Object.keys(headers).length > 0) {\n body.headers = headers;\n }\n }\n\n // Drop undefined reply_to when parse failed.\n if (body.reply_to === undefined) {\n delete body.reply_to;\n }\n\n const idempotencyKey = resolveIdempotencyKey(options);\n\n const response = await fetch(`${this.baseUrl}/v1/send`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n ...(idempotencyKey ? { \"Idempotency-Key\": idempotencyKey } : {}),\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json().catch(() => ({}))) as {\n id?: string;\n status?: string;\n error?: { code?: string; message?: string; request_id?: string };\n message?: string;\n };\n\n if (!response.ok) {\n throw new SndrError(\n payload.error?.message ?? payload.message ?? \"SNDR API error\",\n response.status,\n payload,\n );\n }\n\n const fromAddress = from?.address ?? \"\";\n return {\n messageId: payload.id ?? options.messageId ?? \"\",\n accepted: to,\n rejected: [],\n response: payload.status ?? payload.id ?? \"queued\",\n envelope: {\n from: fromAddress,\n to: [\n ...to,\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ],\n },\n };\n }\n\n /** Verifies the API key by listing domains (`GET /v1/domains`). */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(`${this.baseUrl}/v1/domains`, {\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n const payload = (await response.json().catch(() => ({}))) as {\n error?: { message?: string };\n message?: string;\n };\n return {\n ok: false,\n provider: \"sndr\",\n message: payload.error?.message ?? payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return { ok: true, provider: \"sndr\", message: \"API key is valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"sndr\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
6
6
  ],
7
7
  "mappings": "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",
8
- "debugId": "6475E854D5CE8CE864756E2164756E21",
8
+ "debugId": "0CA0AE7BD8D22B3F64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,3 @@
1
- import{I as X}from"../chunk-skqhj0rm.js";import{Q as G,R as Q,S as F}from"../chunk-93vqxxj2.js";import{Y as O}from"../chunk-ywq6s10g.js";import{ca as U,da as W}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class Y extends U{statusCode;errors;constructor(q,z,w){super(q,W(z),{statusCode:z,provider:"sparkpost",cause:w});this.statusCode=z;this.errors=w;this.name="SparkPostError"}}function _(q){return[...G(q.to),...q.cc?G(q.cc):[],...q.bcc?G(q.bcc):[]].map((w)=>({address:{email:w.address,...w.name?{name:w.name}:{}}}))}class ${provider="sparkpost";apiKey;baseUrl;constructor(q){this.apiKey=q.apiKey,this.baseUrl=q.euRegion?"https://api.eu.sparkpost.com":"https://api.sparkpost.com"}async send(q){let z=await X(q.attachments),w=G(q.from)[0],Z={content:{from:w?Q(w):"",subject:q.subject,...q.html?{html:q.html}:{},...q.text?{text:q.text}:{},...q.replyTo?{reply_to:F(q.replyTo)[0]}:{},...z.length>0?{attachments:z.map((D)=>({type:D.contentType??"application/octet-stream",name:D.filename,data:D.content instanceof Uint8Array?O(D.content).replace(/\r\n/g,""):D.content}))}:{}},recipients:_(q)},K=await fetch(`${this.baseUrl}/api/v1/transmissions`,{method:"POST",headers:{Authorization:this.apiKey,"Content-Type":"application/json"},body:JSON.stringify(Z)}),J=await K.json();if(!K.ok)throw new Y(J.message??"SparkPost API error",K.status,J.errors??J);let L=J.results?.id??"",N=[...F(q.to),...q.cc?F(q.cc):[],...q.bcc?F(q.bcc):[]];return{messageId:L,accepted:N,rejected:[],response:L||"sent",envelope:{from:w?.address??"",to:N}}}async verify(){try{let q=await fetch(`${this.baseUrl}/api/v1/account`,{headers:{Authorization:this.apiKey}});if(!q.ok){let z=await q.json().catch(()=>({}));return{ok:!1,provider:"sparkpost",message:z.errors?.[0]?.message??z.message??`HTTP ${q.status}`}}return{ok:!0,provider:"sparkpost",message:"API key is valid"}}catch(q){return{ok:!1,provider:"sparkpost",message:q instanceof Error?q.message:String(q)}}}}export{$ as SparkPostTransport,Y as SparkPostError};
1
+ import{K as X}from"../chunk-1ke3nmyg.js";import{S as G,T as Q,U as F}from"../chunk-8kpgbrba.js";import{da as O}from"../chunk-hnzmn4s4.js";import{ja as U,ka as W}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class Y extends U{statusCode;errors;constructor(q,z,w){super(q,W(z),{statusCode:z,provider:"sparkpost",cause:w});this.statusCode=z;this.errors=w;this.name="SparkPostError"}}function _(q){return[...G(q.to),...q.cc?G(q.cc):[],...q.bcc?G(q.bcc):[]].map((w)=>({address:{email:w.address,...w.name?{name:w.name}:{}}}))}class ${provider="sparkpost";apiKey;baseUrl;constructor(q){this.apiKey=q.apiKey,this.baseUrl=q.euRegion?"https://api.eu.sparkpost.com":"https://api.sparkpost.com"}async send(q){let z=await X(q.attachments),w=G(q.from)[0],Z={content:{from:w?Q(w):"",subject:q.subject,...q.html?{html:q.html}:{},...q.text?{text:q.text}:{},...q.replyTo?{reply_to:F(q.replyTo)[0]}:{},...z.length>0?{attachments:z.map((D)=>({type:D.contentType??"application/octet-stream",name:D.filename,data:D.content instanceof Uint8Array?O(D.content).replace(/\r\n/g,""):D.content}))}:{}},recipients:_(q)},K=await fetch(`${this.baseUrl}/api/v1/transmissions`,{method:"POST",headers:{Authorization:this.apiKey,"Content-Type":"application/json"},body:JSON.stringify(Z)}),J=await K.json();if(!K.ok)throw new Y(J.message??"SparkPost API error",K.status,J.errors??J);let L=J.results?.id??"",N=[...F(q.to),...q.cc?F(q.cc):[],...q.bcc?F(q.bcc):[]];return{messageId:L,accepted:N,rejected:[],response:L||"sent",envelope:{from:w?.address??"",to:N}}}async verify(){try{let q=await fetch(`${this.baseUrl}/api/v1/account`,{headers:{Authorization:this.apiKey}});if(!q.ok){let z=await q.json().catch(()=>({}));return{ok:!1,provider:"sparkpost",message:z.errors?.[0]?.message??z.message??`HTTP ${q.status}`}}return{ok:!0,provider:"sparkpost",message:"API key is valid"}}catch(q){return{ok:!1,provider:"sparkpost",message:q instanceof Error?q.message:String(q)}}}}export{$ as SparkPostTransport,Y as SparkPostError};
2
2
 
3
- //# debugId=45DD34E196570CD864756E2164756E21
3
+ //# debugId=304B700875CFBA0D64756E2164756E21
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * @module\n * SparkPost HTTP transport for sently.\n *\n * v1 simplification: all `to`, `cc`, and `bcc` addresses are placed in\n * `recipients[]` without SparkPost `header_to` handling for cc/bcc routing.\n *\n * @example\n * ```ts\n * import { SparkPostTransport } from \"sently/transports/sparkpost\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new SparkPostTransport({ apiKey: process.env.SPARKPOST_API_KEY! }),\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 { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** SparkPost HTTP API configuration. */\nexport interface SparkPostConfig {\n /** SparkPost API key (raw key — no Bearer prefix). */\n apiKey: string;\n /** When true, use the EU API host (`api.eu.sparkpost.com`). */\n euRegion?: boolean;\n}\n\n/** Error thrown when the SparkPost API returns a non-success response. */\nexport class SparkPostError extends SentlyError {\n /** Creates a SparkPost API error with status code and error details. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly errors: unknown,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"sparkpost\",\n cause: errors,\n });\n this.name = \"SparkPostError\";\n }\n}\n\nfunction toRecipients(options: MailOptions): Array<{ address: { email: string; name?: string } }> {\n const addresses = [\n ...parseAddresses(options.to),\n ...(options.cc ? parseAddresses(options.cc) : []),\n ...(options.bcc ? parseAddresses(options.bcc) : []),\n ];\n\n return addresses.map((addr) => ({\n address: {\n email: addr.address,\n ...(addr.name ? { name: addr.name } : {}),\n },\n }));\n}\n\n/**\n * SparkPost HTTP API transport.\n */\nexport class SparkPostTransport implements Transport {\n readonly provider = \"sparkpost\";\n\n private readonly apiKey: string;\n private readonly baseUrl: string;\n\n /** Creates a SparkPost transport with the given API key. */\n constructor(config: SparkPostConfig) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.euRegion ? \"https://api.eu.sparkpost.com\" : \"https://api.sparkpost.com\";\n }\n\n /** Sends an email via the SparkPost transmissions 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 content: Record<string, unknown> = {\n from: from ? toMIMEHeader(from) : \"\",\n subject: options.subject,\n ...(options.html ? { html: options.html } : {}),\n ...(options.text ? { text: options.text } : {}),\n ...(options.replyTo ? { reply_to: extractEmails(options.replyTo)[0] } : {}),\n ...(attachments.length > 0\n ? {\n attachments: attachments.map((att) => ({\n type: att.contentType ?? \"application/octet-stream\",\n name: att.filename,\n data:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n })),\n }\n : {}),\n };\n\n const body = {\n content,\n recipients: toRecipients(options),\n };\n\n const response = await fetch(`${this.baseUrl}/api/v1/transmissions`, {\n method: \"POST\",\n headers: {\n Authorization: this.apiKey,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as {\n results?: { id?: string; total_accepted_recipients?: number };\n errors?: unknown;\n message?: string;\n };\n\n if (!response.ok) {\n throw new SparkPostError(\n payload.message ?? \"SparkPost API error\",\n response.status,\n payload.errors ?? payload,\n );\n }\n\n const messageId = payload.results?.id ?? \"\";\n const toEmails = [\n ...extractEmails(options.to),\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ];\n\n return {\n messageId,\n accepted: toEmails,\n rejected: [],\n response: messageId || \"sent\",\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies the SparkPost API key by fetching account info. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(`${this.baseUrl}/api/v1/account`, {\n headers: {\n Authorization: this.apiKey,\n },\n });\n\n if (!response.ok) {\n const payload = (await response.json().catch(() => ({}))) as {\n errors?: Array<{ message?: string }>;\n message?: string;\n };\n const errMsg = payload.errors?.[0]?.message ?? payload.message ?? `HTTP ${response.status}`;\n return {\n ok: false,\n provider: \"sparkpost\",\n message: errMsg,\n };\n }\n\n return { ok: true, provider: \"sparkpost\", message: \"API key is valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"sparkpost\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "45DD34E196570CD864756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "304B700875CFBA0D64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,3 @@
1
- import{Q as H,S as I}from"../chunk-93vqxxj2.js";import"../chunk-ywq6s10g.js";import{ca as J,da as K}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class D extends J{statusCode;apiError;constructor(b,z,B){super(b,K(z>=100&&z<600?z:400),{statusCode:z,provider:"taqnyat-mail",cause:B});this.statusCode=z;this.apiError=B;this.name="TaqnyatMailError"}}class P{provider="taqnyat-mail";bearerToken;campaignName;constructor(b){this.bearerToken=b.bearerToken,this.campaignName=b.campaignName}async send(b){let z=H(b.from)[0],B=b.html??b.text;if(!B)throw new D("Taqnyat mail requires html or text body",400,{message:"Missing body"});if(!z?.address)throw new D("Taqnyat mail requires a from address",400,{message:"Missing from"});let F=I(b.to),N=new URLSearchParams({bearerTokens:this.bearerToken,campaignName:this.campaignName,subject:b.subject,from:z.address,to:F.join(","),msg:B}),G=await fetch("https://api.taqnyat.sa/mailSend.php",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/x-www-form-urlencoded"},body:N}),v=await G.json();if(!(v.ResponseStatus==="success"&&v.Data!=null&&v.Error==null)){let O=v.Error?.MessageEn?.trim()||v.Error?.MessageAr?.trim()||"Taqnyat Email API error";throw new D(O,v.Error?.ErrorCode??(G.ok?400:G.status),v)}return{messageId:String(v.Data?.msgId??b.messageId??""),accepted:F,rejected:[],response:v.Data?.MessageEn??"sent successfully",envelope:{from:z.address,to:F},provider:"taqnyat-mail"}}async verify(){return{ok:Boolean(this.bearerToken&&this.campaignName),provider:"taqnyat-mail",message:this.bearerToken&&this.campaignName?"Credentials present":"Missing credentials"}}}export{P as TaqnyatMailTransport,D as TaqnyatMailError};
1
+ import{S as H,U as I}from"../chunk-8kpgbrba.js";import"../chunk-hnzmn4s4.js";import{ja as J,ka as K}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class D extends J{statusCode;apiError;constructor(b,z,B){super(b,K(z>=100&&z<600?z:400),{statusCode:z,provider:"taqnyat-mail",cause:B});this.statusCode=z;this.apiError=B;this.name="TaqnyatMailError"}}class P{provider="taqnyat-mail";bearerToken;campaignName;constructor(b){this.bearerToken=b.bearerToken,this.campaignName=b.campaignName}async send(b){let z=H(b.from)[0],B=b.html??b.text;if(!B)throw new D("Taqnyat mail requires html or text body",400,{message:"Missing body"});if(!z?.address)throw new D("Taqnyat mail requires a from address",400,{message:"Missing from"});let F=I(b.to),N=new URLSearchParams({bearerTokens:this.bearerToken,campaignName:this.campaignName,subject:b.subject,from:z.address,to:F.join(","),msg:B}),G=await fetch("https://api.taqnyat.sa/mailSend.php",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/x-www-form-urlencoded"},body:N}),v=await G.json();if(!(v.ResponseStatus==="success"&&v.Data!=null&&v.Error==null)){let O=v.Error?.MessageEn?.trim()||v.Error?.MessageAr?.trim()||"Taqnyat Email API error";throw new D(O,v.Error?.ErrorCode??(G.ok?400:G.status),v)}return{messageId:String(v.Data?.msgId??b.messageId??""),accepted:F,rejected:[],response:v.Data?.MessageEn??"sent successfully",envelope:{from:z.address,to:F},provider:"taqnyat-mail"}}async verify(){return{ok:Boolean(this.bearerToken&&this.campaignName),provider:"taqnyat-mail",message:this.bearerToken&&this.campaignName?"Credentials present":"Missing credentials"}}}export{P as TaqnyatMailTransport,D as TaqnyatMailError};
2
2
 
3
- //# debugId=BC80D2F97553209A64756E2164756E21
3
+ //# debugId=9C656A01E86E9E8164756E2164756E21
@@ -5,6 +5,6 @@
5
5
  "/**\n * @module\n * Taqnyat Email API transport (`mailSend.php`).\n *\n * Follows [Taqnyat Email](https://dev.taqnyat.sa/ar/doc/mail/):\n * `POST https://api.taqnyat.sa/mailSend.php` with `bearerTokens`, `campaignName`,\n * `subject`, `from`, `to`, `msg`. Success: HTTP 201 + `ResponseStatus: \"success\"`\n * and `Data.msgId`.\n *\n * Sently-first: wire into {@link createMailer}.\n *\n * @example\n * ```ts\n * import { createMailer } from \"sently/mailer\";\n * import { TaqnyatMailTransport } from \"sently/transports/taqnyat-mail\";\n *\n * const mailer = await createMailer({\n * transport: new TaqnyatMailTransport({\n * bearerToken: process.env.TAQNYAT_MAIL_TOKEN!,\n * campaignName: \"transactional\",\n * }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\n\n/** Taqnyat Email API configuration. */\nexport interface TaqnyatMailConfig {\n /** Bearer token from a Taqnyat application enabled for Email (`bearerTokens`). */\n bearerToken: string;\n /**\n * Campaign name required by `mailSend.php`.\n * Must be consistent across channels when using Taqnyat failover features.\n */\n campaignName: string;\n}\n\n/** Error thrown when the Taqnyat Email API reports a failure. */\nexport class TaqnyatMailError extends SentlyError {\n /** Creates a Taqnyat Email 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(\n message,\n httpStatusToSentlyCode(statusCode >= 100 && statusCode < 600 ? statusCode : 400),\n {\n statusCode,\n provider: \"taqnyat-mail\",\n cause: apiError,\n },\n );\n this.name = \"TaqnyatMailError\";\n }\n}\n\n/**\n * Taqnyat Email transport via `mailSend.php`.\n */\nexport class TaqnyatMailTransport implements Transport {\n readonly provider = \"taqnyat-mail\";\n\n private readonly bearerToken: string;\n private readonly campaignName: string;\n\n /** Creates a Taqnyat Email transport. */\n constructor(config: TaqnyatMailConfig) {\n this.bearerToken = config.bearerToken;\n this.campaignName = config.campaignName;\n }\n\n /** Sends an email via Taqnyat `mailSend.php`. */\n async send(options: MailOptions): Promise<SendResult> {\n const from = parseAddresses(options.from)[0];\n const msg = options.html ?? options.text;\n if (!msg) {\n throw new TaqnyatMailError(\"Taqnyat mail requires html or text body\", 400, {\n message: \"Missing body\",\n });\n }\n if (!from?.address) {\n throw new TaqnyatMailError(\"Taqnyat mail requires a from address\", 400, {\n message: \"Missing from\",\n });\n }\n\n const toList = extractEmails(options.to);\n // Docs examples: plain email addresses + comma-separated `to`, `bearerTokens` param.\n const params = new URLSearchParams({\n bearerTokens: this.bearerToken,\n campaignName: this.campaignName,\n subject: options.subject,\n from: from.address,\n to: toList.join(\",\"),\n msg,\n });\n\n const response = await fetch(\"https://api.taqnyat.sa/mailSend.php\", {\n method: \"POST\",\n headers: {\n // Auth section: Bearer for REST; examples also pass bearerTokens in the body.\n Authorization: `Bearer ${this.bearerToken}`,\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: params,\n });\n\n const payload = (await response.json()) as {\n status?: number;\n ResponseStatus?: string;\n Data?: {\n msgId?: number | string;\n MessageEn?: string;\n MessageAr?: string;\n price?: string;\n } | null;\n Error?: {\n ErrorCode?: number;\n MessageEn?: string;\n MessageAr?: string;\n } | null;\n };\n\n const succeeded =\n payload.ResponseStatus === \"success\" && payload.Data != null && payload.Error == null;\n\n if (!succeeded) {\n const message =\n payload.Error?.MessageEn?.trim() ||\n payload.Error?.MessageAr?.trim() ||\n \"Taqnyat Email API error\";\n throw new TaqnyatMailError(\n message,\n payload.Error?.ErrorCode ?? (response.ok ? 400 : response.status),\n payload,\n );\n }\n\n return {\n messageId: String(payload.Data?.msgId ?? options.messageId ?? \"\"),\n accepted: toList,\n rejected: [],\n response: payload.Data?.MessageEn ?? \"sent successfully\",\n envelope: {\n from: from.address,\n to: toList,\n },\n provider: \"taqnyat-mail\",\n };\n }\n\n /** Lightweight credential check. */\n async verify(): Promise<VerifyResult> {\n return {\n ok: Boolean(this.bearerToken && this.campaignName),\n provider: \"taqnyat-mail\",\n message:\n this.bearerToken && this.campaignName ? \"Credentials present\" : \"Missing credentials\",\n };\n }\n}\n"
6
6
  ],
7
7
  "mappings": "2KAwCO,CAAM,KAAyB,JAAY,CAI9B,WACA,SAHlB,WAAW,CACT,EACgB,EACA,EAChB,CACA,MACE,EACA,EAAuB,GAAc,KAAO,EAAa,IAAM,EAAa,GAAG,EAC/E,CACE,aACA,SAAU,eACV,MAAO,CACT,CACF,EAXgB,kBACA,gBAWhB,KAAK,KAAO,mBAEhB,CAKO,MAAM,CAA0C,CAC5C,SAAW,eAEH,YACA,aAGjB,WAAW,CAAC,EAA2B,CACrC,KAAK,YAAc,EAAO,YAC1B,KAAK,aAAe,EAAO,kBAIvB,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAO,EAAe,EAAQ,IAAI,EAAE,GACpC,EAAM,EAAQ,MAAQ,EAAQ,KACpC,GAAI,CAAC,EACH,MAAM,IAAI,EAAiB,0CAA2C,IAAK,CACzE,QAAS,cACX,CAAC,EAEH,GAAI,CAAC,GAAM,QACT,MAAM,IAAI,EAAiB,uCAAwC,IAAK,CACtE,QAAS,cACX,CAAC,EAGH,IAAM,EAAS,EAAc,EAAQ,EAAE,EAEjC,EAAS,IAAI,gBAAgB,CACjC,aAAc,KAAK,YACnB,aAAc,KAAK,aACnB,QAAS,EAAQ,QACjB,KAAM,EAAK,QACX,GAAI,EAAO,KAAK,GAAG,EACnB,KACF,CAAC,EAEK,EAAW,MAAM,MAAM,sCAAuC,CAClE,OAAQ,OACR,QAAS,CAEP,cAAe,UAAU,KAAK,cAC9B,eAAgB,mCAClB,EACA,KAAM,CACR,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAmBrC,GAAI,EAFF,EAAQ,iBAAmB,WAAa,EAAQ,MAAQ,MAAQ,EAAQ,OAAS,MAEnE,CACd,IAAM,EACJ,EAAQ,OAAO,WAAW,KAAK,GAC/B,EAAQ,OAAO,WAAW,KAAK,GAC/B,0BACF,MAAM,IAAI,EACR,EACA,EAAQ,OAAO,YAAc,EAAS,GAAK,IAAM,EAAS,QAC1D,CACF,EAGF,MAAO,CACL,UAAW,OAAO,EAAQ,MAAM,OAAS,EAAQ,WAAa,EAAE,EAChE,SAAU,EACV,SAAU,CAAC,EACX,SAAU,EAAQ,MAAM,WAAa,oBACrC,SAAU,CACR,KAAM,EAAK,QACX,GAAI,CACN,EACA,SAAU,cACZ,OAII,OAAM,EAA0B,CACpC,MAAO,CACL,GAAI,QAAQ,KAAK,aAAe,KAAK,YAAY,EACjD,SAAU,eACV,QACE,KAAK,aAAe,KAAK,aAAe,sBAAwB,qBACpE,EAEJ",
8
- "debugId": "BC80D2F97553209A64756E2164756E21",
8
+ "debugId": "9C656A01E86E9E8164756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,3 @@
1
- import{h as O}from"../chunk-7gy2q9sh.js";import{ca as W,da as X}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class K extends W{statusCode;apiError;constructor(A,B,G){super(A,X(B>=100&&B<600?B:400),{statusCode:B,provider:"taqnyat-sms",cause:G});this.statusCode=B;this.apiError=G;this.name="TaqnyatSmsError"}}function R(A){let B=A.code??A.statusCode??A.status;if(typeof B==="number")return B;if(typeof B==="string"&&/^-?\d+$/.test(B))return Number(B);return}function Q(A){if(typeof A.message==="string")return A.message;if(typeof A.MessageEn==="string")return A.MessageEn;return}function U(A){if(Array.isArray(A)){let B=A[0];return B&&typeof B==="object"?B:{}}if(A&&typeof A==="object")return A;return{}}class Z{provider="taqnyat-sms";bearerToken;sender;constructor(A){this.bearerToken=A.bearerToken,this.sender=A.sender}async send(A){let G={recipients:[O(A.to)],body:A.body,sender:A.from??this.sender};if(A.messageId!==void 0)G.smsId=A.messageId;let I=await fetch("https://api.taqnyat.sa/v1/messages",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/json"},body:JSON.stringify(G)}),D=await I.json();if(I.status!==201)throw new K(D.message??"Taqnyat SMS API error",D.statusCode??I.status,D);return{messageId:String(D.messageId??A.messageId??""),to:A.to,status:"accepted",response:`cost: ${D.cost??"?"} ${D.currency??"SAR"}`,provider:"taqnyat-sms"}}async sendOtp(A){let B=A.lang??"ar",G=O(A.to),I=[{apiKey:this.bearerToken,numbers:[G],method:"sms",sender:A.from??this.sender,lang:B,requestId:A.requestId,returnJson:1,...A.note!==void 0?{note:A.note}:{}}],D=await fetch("https://api.taqnyat.sa/verify.php/",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/json"},body:JSON.stringify(I)}),J=await D.text(),N;try{N=JSON.parse(J)}catch{throw new K("Taqnyat Verify returned non-JSON response",D.status,J)}let H=U(N),F=R(H);if(!D.ok||F!==5)throw new K(Q(H)??`Taqnyat OTP send failed (code ${F??"unknown"})`,F??D.status,H);return{requestId:A.requestId,to:A.to,code:F,response:J,provider:"taqnyat-sms"}}async verifyOtp(A){let B=A.lang??"ar",G=O(A.to),I=[{apiKey:this.bearerToken,numbers:[G],method:"sms",sender:A.from??this.sender,lang:B,requestId:A.requestId,activeKey:A.code,returnJson:1,...A.note!==void 0?{note:A.note}:{}}],D=await fetch("https://api.taqnyat.sa/verify.php/",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/json"},body:JSON.stringify(I)}),J=await D.text(),N;try{N=JSON.parse(J)}catch{throw new K("Taqnyat Verify returned non-JSON response",D.status,J)}let H=U(N),F=R(H),Y=new Set([10,13,19]);if(!D.ok||F===void 0||!Y.has(F))throw new K(Q(H)??`Taqnyat OTP verify failed (code ${F??"unknown"})`,F??D.status,H);return{ok:!0,code:F,message:Q(H)??"Activation process completed successfully",response:J,provider:"taqnyat-sms"}}async verify(){return{ok:Boolean(this.bearerToken&&this.sender),provider:"taqnyat-sms",message:this.bearerToken&&this.sender?"Credentials present":"Missing credentials"}}}export{Z as TaqnyatSmsTransport,K as TaqnyatSmsError};
1
+ import{j as O}from"../chunk-5es0v1j7.js";import{ja as W,ka as X}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class K extends W{statusCode;apiError;constructor(A,B,G){super(A,X(B>=100&&B<600?B:400),{statusCode:B,provider:"taqnyat-sms",cause:G});this.statusCode=B;this.apiError=G;this.name="TaqnyatSmsError"}}function R(A){let B=A.code??A.statusCode??A.status;if(typeof B==="number")return B;if(typeof B==="string"&&/^-?\d+$/.test(B))return Number(B);return}function Q(A){if(typeof A.message==="string")return A.message;if(typeof A.MessageEn==="string")return A.MessageEn;return}function U(A){if(Array.isArray(A)){let B=A[0];return B&&typeof B==="object"?B:{}}if(A&&typeof A==="object")return A;return{}}class Z{provider="taqnyat-sms";bearerToken;sender;constructor(A){this.bearerToken=A.bearerToken,this.sender=A.sender}async send(A){let G={recipients:[O(A.to)],body:A.body,sender:A.from??this.sender};if(A.messageId!==void 0)G.smsId=A.messageId;let I=await fetch("https://api.taqnyat.sa/v1/messages",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/json"},body:JSON.stringify(G)}),D=await I.json();if(I.status!==201)throw new K(D.message??"Taqnyat SMS API error",D.statusCode??I.status,D);return{messageId:String(D.messageId??A.messageId??""),to:A.to,status:"accepted",response:`cost: ${D.cost??"?"} ${D.currency??"SAR"}`,provider:"taqnyat-sms"}}async sendOtp(A){let B=A.lang??"ar",G=O(A.to),I=[{apiKey:this.bearerToken,numbers:[G],method:"sms",sender:A.from??this.sender,lang:B,requestId:A.requestId,returnJson:1,...A.note!==void 0?{note:A.note}:{}}],D=await fetch("https://api.taqnyat.sa/verify.php/",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/json"},body:JSON.stringify(I)}),J=await D.text(),N;try{N=JSON.parse(J)}catch{throw new K("Taqnyat Verify returned non-JSON response",D.status,J)}let H=U(N),F=R(H);if(!D.ok||F!==5)throw new K(Q(H)??`Taqnyat OTP send failed (code ${F??"unknown"})`,F??D.status,H);return{requestId:A.requestId,to:A.to,code:F,response:J,provider:"taqnyat-sms"}}async verifyOtp(A){let B=A.lang??"ar",G=O(A.to),I=[{apiKey:this.bearerToken,numbers:[G],method:"sms",sender:A.from??this.sender,lang:B,requestId:A.requestId,activeKey:A.code,returnJson:1,...A.note!==void 0?{note:A.note}:{}}],D=await fetch("https://api.taqnyat.sa/verify.php/",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/json"},body:JSON.stringify(I)}),J=await D.text(),N;try{N=JSON.parse(J)}catch{throw new K("Taqnyat Verify returned non-JSON response",D.status,J)}let H=U(N),F=R(H),Y=new Set([10,13,19]);if(!D.ok||F===void 0||!Y.has(F))throw new K(Q(H)??`Taqnyat OTP verify failed (code ${F??"unknown"})`,F??D.status,H);return{ok:!0,code:F,message:Q(H)??"Activation process completed successfully",response:J,provider:"taqnyat-sms"}}async verify(){return{ok:Boolean(this.bearerToken&&this.sender),provider:"taqnyat-sms",message:this.bearerToken&&this.sender?"Credentials present":"Missing credentials"}}}export{Z as TaqnyatSmsTransport,K as TaqnyatSmsError};
2
2
 
3
- //# debugId=18DB31EDCA321AE964756E2164756E21
3
+ //# debugId=8ED03B5D386F317B64756E2164756E21
@@ -5,6 +5,6 @@
5
5
  "/**\n * @module\n * Taqnyat SMS API transport (Saudi Arabia).\n *\n * Request/response shapes follow\n * [Taqnyat SMS](https://dev.taqnyat.sa/en/doc/sms/) and\n * [Verify](https://dev.taqnyat.sa/en/doc/verify/):\n * - SMS: `POST /v1/messages` with Bearer + JSON (`recipients`, `body`, `sender`)\n * - OTP: `POST /verify.php/` with JSON array (`apiKey`, `numbers`, `method`, …)\n *\n * Sently-first: wire SMS into {@link createSmsSender}; OTP helpers stay on this class.\n *\n * @example\n * ```ts\n * import { createSmsSender } from \"sently/sms\";\n * import { TaqnyatSmsTransport } from \"sently/transports/taqnyat-sms\";\n *\n * const taqnyat = new TaqnyatSmsTransport({\n * bearerToken: process.env.TAQNYAT_TOKEN!,\n * sender: \"MyBrand\",\n * });\n * const sms = createSmsSender({ transport: taqnyat });\n *\n * await sms.send({ to: \"+9665xxxxxxxx\", body: \"Hello\" });\n * await taqnyat.sendOtp({ to: \"+9665xxxxxxxx\", requestId: \"login-1\", lang: \"en\" });\n * await taqnyat.verifyOtp({\n * to: \"+9665xxxxxxxx\",\n * requestId: \"login-1\",\n * code: \"6240\",\n * lang: \"en\",\n * });\n * ```\n */\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type { SmsOptions, SmsSendResult, SmsTransport } from \"../core/sms-types.js\";\nimport type { VerifyResult } from \"../core/types.js\";\nimport { normalizeTaqnyatPhone } from \"./taqnyat-phone.js\";\n\n/** Taqnyat SMS API configuration. */\nexport interface TaqnyatSmsConfig {\n /** Bearer token from the Taqnyat dashboard. */\n bearerToken: string;\n /** Pre-approved sender name. */\n sender: string;\n}\n\n/** Options for {@link TaqnyatSmsTransport.sendOtp}. */\nexport interface TaqnyatSendOtpOptions {\n /** Recipient phone number (E.164 or international digits). */\n to: string;\n /** Unique id for this verification flow (required again on {@link verifyOtp}). */\n requestId: string;\n /** Message language (`en` or `ar`). Defaults to `\"ar\"` per Taqnyat docs. */\n lang?: \"en\" | \"ar\";\n /** Optional note appended to the OTP SMS. */\n note?: string;\n /** Sender ID override; defaults to transport `sender`. */\n from?: string;\n}\n\n/** Result of a successful {@link TaqnyatSmsTransport.sendOtp} call. */\nexport interface TaqnyatOtpSendResult {\n /** Echo of {@link TaqnyatSendOtpOptions.requestId}. */\n requestId: string;\n /** Recipient as passed in. */\n to: string;\n /** Provider status code (`5` = code sent). */\n code: number;\n /** Raw response body text. */\n response: string;\n /** Provider identifier. */\n provider: \"taqnyat-sms\";\n}\n\n/** Options for {@link TaqnyatSmsTransport.verifyOtp}. */\nexport interface TaqnyatVerifyOtpOptions {\n /** Recipient phone number (same as send). */\n to: string;\n /** Same {@link TaqnyatSendOtpOptions.requestId} used when sending. */\n requestId: string;\n /** OTP code the user entered (`activeKey` in Taqnyat docs). */\n code: string;\n /** Message language (`en` or `ar`). Defaults to `\"ar\"`. */\n lang?: \"en\" | \"ar\";\n /** Sender ID override; defaults to transport `sender`. */\n from?: string;\n /** Optional note (documented on check OTP). */\n note?: string;\n}\n\n/** Result of a successful {@link TaqnyatSmsTransport.verifyOtp} call. */\nexport interface TaqnyatOtpVerifyResult {\n /** Always `true` when the call resolves (failures throw). */\n ok: true;\n /** Provider status code (`10` = completed, `13`/`19` = already verified). */\n code: number;\n /** Provider message when present. */\n message: string;\n /** Raw response body text. */\n response: string;\n /** Provider identifier. */\n provider: \"taqnyat-sms\";\n}\n\n/** Error thrown when the Taqnyat SMS / Verify API returns a non-success response. */\nexport class TaqnyatSmsError extends SentlyError {\n /** Creates a Taqnyat 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 ) {\n super(\n message,\n httpStatusToSentlyCode(statusCode >= 100 && statusCode < 600 ? statusCode : 400),\n {\n statusCode,\n provider: \"taqnyat-sms\",\n cause: apiError,\n },\n );\n this.name = \"TaqnyatSmsError\";\n }\n}\n\nfunction verifyPayloadCode(payload: Record<string, unknown>): number | undefined {\n const raw = payload.code ?? payload.statusCode ?? payload.status;\n if (typeof raw === \"number\") return raw;\n if (typeof raw === \"string\" && /^-?\\d+$/.test(raw)) return Number(raw);\n return undefined;\n}\n\nfunction verifyPayloadMessage(payload: Record<string, unknown>): string | undefined {\n if (typeof payload.message === \"string\") return payload.message;\n if (typeof payload.MessageEn === \"string\") return payload.MessageEn;\n return undefined;\n}\n\nfunction unwrapVerifyPayload(payload: unknown): Record<string, unknown> {\n if (Array.isArray(payload)) {\n const first = payload[0];\n return first && typeof first === \"object\" ? (first as Record<string, unknown>) : {};\n }\n if (payload && typeof payload === \"object\") {\n return payload as Record<string, unknown>;\n }\n return {};\n}\n\n/**\n * Taqnyat SMS transport via JSON Bearer API, plus Verify OTP helpers.\n */\nexport class TaqnyatSmsTransport implements SmsTransport {\n readonly provider = \"taqnyat-sms\";\n\n private readonly bearerToken: string;\n private readonly sender: string;\n\n /** Creates a Taqnyat SMS transport. */\n constructor(config: TaqnyatSmsConfig) {\n this.bearerToken = config.bearerToken;\n this.sender = config.sender;\n }\n\n /**\n * Sends an SMS via `POST https://api.taqnyat.sa/v1/messages`\n * (Bearer + JSON: `recipients`, `body`, `sender`; optional `smsId`).\n */\n async send(options: SmsOptions): Promise<SmsSendResult> {\n const recipients = [normalizeTaqnyatPhone(options.to)];\n const body: Record<string, unknown> = {\n recipients,\n body: options.body,\n sender: options.from ?? this.sender,\n };\n // Official PHP SDK maps a client id to `smsId`.\n if (options.messageId !== undefined) {\n body.smsId = options.messageId;\n }\n\n const response = await fetch(\"https://api.taqnyat.sa/v1/messages\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.bearerToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as {\n statusCode?: number;\n messageId?: number;\n cost?: number;\n currency?: string;\n message?: string;\n accepted?: string;\n rejected?: string;\n };\n\n // Docs: successful send returns HTTP 201 with statusCode 201.\n if (response.status !== 201) {\n throw new TaqnyatSmsError(\n payload.message ?? \"Taqnyat SMS API error\",\n payload.statusCode ?? response.status,\n payload,\n );\n }\n\n return {\n messageId: String(payload.messageId ?? options.messageId ?? \"\"),\n to: options.to,\n status: \"accepted\",\n response: `cost: ${payload.cost ?? \"?\"} ${payload.currency ?? \"SAR\"}`,\n provider: \"taqnyat-sms\",\n };\n }\n\n /**\n * Vendor extra: generate/send OTP via `POST /verify.php/` (success code `5`).\n * Not part of {@link SmsTransport} — call on {@link TaqnyatSmsTransport} directly.\n */\n async sendOtp(options: TaqnyatSendOtpOptions): Promise<TaqnyatOtpSendResult> {\n const lang = options.lang ?? \"ar\";\n const number = normalizeTaqnyatPhone(options.to);\n const body = [\n {\n apiKey: this.bearerToken,\n numbers: [number],\n method: \"sms\",\n sender: options.from ?? this.sender,\n lang,\n requestId: options.requestId,\n returnJson: 1,\n ...(options.note !== undefined ? { note: options.note } : {}),\n },\n ];\n\n const response = await fetch(\"https://api.taqnyat.sa/verify.php/\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.bearerToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const text = await response.text();\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n throw new TaqnyatSmsError(\"Taqnyat Verify returned non-JSON response\", response.status, text);\n }\n\n const result = unwrapVerifyPayload(parsed);\n const code = verifyPayloadCode(result);\n\n // Docs: 5 = Activation code sent successfully.\n if (!response.ok || code !== 5) {\n throw new TaqnyatSmsError(\n verifyPayloadMessage(result) ?? `Taqnyat OTP send failed (code ${code ?? \"unknown\"})`,\n code ?? response.status,\n result,\n );\n }\n\n return {\n requestId: options.requestId,\n to: options.to,\n code,\n response: text,\n provider: \"taqnyat-sms\",\n };\n }\n\n /**\n * Vendor extra: check OTP via `POST /verify.php/` with `activeKey`\n * (`10` = completed; `13` / `19` = already verified).\n */\n async verifyOtp(options: TaqnyatVerifyOtpOptions): Promise<TaqnyatOtpVerifyResult> {\n const lang = options.lang ?? \"ar\";\n const number = normalizeTaqnyatPhone(options.to);\n const body = [\n {\n apiKey: this.bearerToken,\n numbers: [number],\n method: \"sms\",\n sender: options.from ?? this.sender,\n lang,\n requestId: options.requestId,\n activeKey: options.code,\n returnJson: 1,\n ...(options.note !== undefined ? { note: options.note } : {}),\n },\n ];\n\n const response = await fetch(\"https://api.taqnyat.sa/verify.php/\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.bearerToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const text = await response.text();\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n throw new TaqnyatSmsError(\"Taqnyat Verify returned non-JSON response\", response.status, text);\n }\n\n const result = unwrapVerifyPayload(parsed);\n const code = verifyPayloadCode(result);\n const okCodes = new Set([10, 13, 19]);\n\n if (!response.ok || code === undefined || !okCodes.has(code)) {\n throw new TaqnyatSmsError(\n verifyPayloadMessage(result) ?? `Taqnyat OTP verify failed (code ${code ?? \"unknown\"})`,\n code ?? response.status,\n result,\n );\n }\n\n return {\n ok: true,\n code,\n message: verifyPayloadMessage(result) ?? \"Activation process completed successfully\",\n response: text,\n provider: \"taqnyat-sms\",\n };\n }\n\n /** Lightweight credential check. */\n async verify(): Promise<VerifyResult> {\n return {\n ok: Boolean(this.bearerToken && this.sender),\n provider: \"taqnyat-sms\",\n message: this.bearerToken && this.sender ? \"Credentials present\" : \"Missing credentials\",\n };\n }\n}\n"
6
6
  ],
7
7
  "mappings": "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",
8
- "debugId": "18DB31EDCA321AE964756E2164756E21",
8
+ "debugId": "8ED03B5D386F317B64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,3 @@
1
- import{h as O}from"../chunk-7gy2q9sh.js";import{ca as K,da as N}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class Q extends K{statusCode;apiError;constructor(B,D,G){super(B,N(D>=100&&D<600?D:400),{statusCode:D,provider:"taqnyat-whatsapp",cause:G});this.statusCode=D;this.apiError=G;this.name="TaqnyatWhatsAppError"}}function V(B){return"template"in B}function X(B){let D=B.statuses;if(Array.isArray(D)){let G=D[0];if(typeof G?.message_id==="string")return G.message_id}else if(D&&typeof D==="object"){for(let G of Object.values(D))if(G&&typeof G==="object"){let H=G.message_id;if(typeof H==="string")return H}}return}class Y{provider="taqnyat-whatsapp";bearerToken;constructor(B){this.bearerToken=B.bearerToken}async send(B){let D=O(B.to),G=V(B)?{to:D,type:"template",template:{name:B.template.name,language:{code:B.template.language},...B.template.components?{components:B.template.components}:{}}}:{to:D,type:"text",text:{body:B.text}},H=await fetch("https://api.taqnyat.sa/wa/v2/messages/",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/json"},body:JSON.stringify(G)}),F=await H.json(),J=X(F);if(!H.ok||F.message!==void 0&&J===void 0){let R=typeof F.reason==="string"?F.reason:typeof F.message==="string"?`Taqnyat WhatsApp error ${F.message}`:"Taqnyat WhatsApp API error",U=typeof F.message==="string"&&/^\d+$/.test(F.message)?Number(F.message):H.status;throw new Q(R,U||H.status,F)}return{messageId:J??B.messageId??"",to:B.to,status:"accepted",response:typeof F.type==="string"?F.type:"Message sent",provider:"taqnyat-whatsapp"}}async verify(){return{ok:Boolean(this.bearerToken),provider:"taqnyat-whatsapp",message:this.bearerToken?"Credentials present":"Missing credentials"}}}export{Y as TaqnyatWhatsAppTransport,Q as TaqnyatWhatsAppError};
1
+ import{j as O}from"../chunk-5es0v1j7.js";import{ja as K,ka as N}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class Q extends K{statusCode;apiError;constructor(B,D,G){super(B,N(D>=100&&D<600?D:400),{statusCode:D,provider:"taqnyat-whatsapp",cause:G});this.statusCode=D;this.apiError=G;this.name="TaqnyatWhatsAppError"}}function V(B){return"template"in B}function X(B){let D=B.statuses;if(Array.isArray(D)){let G=D[0];if(typeof G?.message_id==="string")return G.message_id}else if(D&&typeof D==="object"){for(let G of Object.values(D))if(G&&typeof G==="object"){let H=G.message_id;if(typeof H==="string")return H}}return}class Y{provider="taqnyat-whatsapp";bearerToken;constructor(B){this.bearerToken=B.bearerToken}async send(B){let D=O(B.to),G=V(B)?{to:D,type:"template",template:{name:B.template.name,language:{code:B.template.language},...B.template.components?{components:B.template.components}:{}}}:{to:D,type:"text",text:{body:B.text}},H=await fetch("https://api.taqnyat.sa/wa/v2/messages/",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/json"},body:JSON.stringify(G)}),F=await H.json(),J=X(F);if(!H.ok||F.message!==void 0&&J===void 0){let R=typeof F.reason==="string"?F.reason:typeof F.message==="string"?`Taqnyat WhatsApp error ${F.message}`:"Taqnyat WhatsApp API error",U=typeof F.message==="string"&&/^\d+$/.test(F.message)?Number(F.message):H.status;throw new Q(R,U||H.status,F)}return{messageId:J??B.messageId??"",to:B.to,status:"accepted",response:typeof F.type==="string"?F.type:"Message sent",provider:"taqnyat-whatsapp"}}async verify(){return{ok:Boolean(this.bearerToken),provider:"taqnyat-whatsapp",message:this.bearerToken?"Credentials present":"Missing credentials"}}}export{Y as TaqnyatWhatsAppTransport,Q as TaqnyatWhatsAppError};
2
2
 
3
- //# debugId=5657D7720E9F16CB64756E2164756E21
3
+ //# debugId=E4D853E15FDA606B64756E2164756E21
@@ -5,6 +5,6 @@
5
5
  "/**\n * @module\n * Taqnyat WhatsApp Business API transport.\n *\n * Follows [Taqnyat WhatsApp](https://dev.taqnyat.sa/en/doc/whatsapp/):\n * `POST https://api.taqnyat.sa/wa/v2/messages/` with Bearer + JSON.\n * Template: `{ to, type: \"template\", template: { name, language: { code } } }`.\n * Session text: `{ to, type: \"text\", text: { body } }`.\n *\n * Sently-first: wire into {@link createWhatsAppSender}.\n *\n * @example\n * ```ts\n * import { createWhatsAppSender } from \"sently/whatsapp\";\n * import { TaqnyatWhatsAppTransport } from \"sently/transports/taqnyat-whatsapp\";\n *\n * const wa = createWhatsAppSender({\n * transport: new TaqnyatWhatsAppTransport({\n * bearerToken: process.env.TAQNYAT_WHATSAPP_TOKEN!,\n * }),\n * });\n * ```\n */\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type { VerifyResult } from \"../core/types.js\";\nimport type {\n WhatsAppOptions,\n WhatsAppSendResult,\n WhatsAppTransport,\n} from \"../core/whatsapp-types.js\";\nimport { normalizeTaqnyatPhone } from \"./taqnyat-phone.js\";\n\n/** Taqnyat WhatsApp API configuration. */\nexport interface TaqnyatWhatsAppConfig {\n /** Bearer token from a Taqnyat application enabled for WhatsApp. */\n bearerToken: string;\n}\n\n/** Error thrown when the Taqnyat WhatsApp API reports a failure. */\nexport class TaqnyatWhatsAppError extends SentlyError {\n /** Creates a Taqnyat WhatsApp 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(\n message,\n httpStatusToSentlyCode(statusCode >= 100 && statusCode < 600 ? statusCode : 400),\n {\n statusCode,\n provider: \"taqnyat-whatsapp\",\n cause: apiError,\n },\n );\n this.name = \"TaqnyatWhatsAppError\";\n }\n}\n\nfunction isTemplateMessage(\n options: WhatsAppOptions,\n): options is Extract<WhatsAppOptions, { template: unknown }> {\n return \"template\" in options;\n}\n\nfunction extractMessageId(payload: Record<string, unknown>): string | undefined {\n const statuses = payload.statuses;\n if (Array.isArray(statuses)) {\n const first = statuses[0] as Record<string, unknown> | undefined;\n if (typeof first?.message_id === \"string\") return first.message_id;\n } else if (statuses && typeof statuses === \"object\") {\n for (const value of Object.values(statuses as Record<string, unknown>)) {\n if (value && typeof value === \"object\") {\n const id = (value as Record<string, unknown>).message_id;\n if (typeof id === \"string\") return id;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Taqnyat WhatsApp transport via JSON Bearer `wa/v2/messages/`.\n */\nexport class TaqnyatWhatsAppTransport implements WhatsAppTransport {\n readonly provider = \"taqnyat-whatsapp\";\n\n private readonly bearerToken: string;\n\n /** Creates a Taqnyat WhatsApp transport. */\n constructor(config: TaqnyatWhatsAppConfig) {\n this.bearerToken = config.bearerToken;\n }\n\n /** Sends a WhatsApp template or session text message. */\n async send(options: WhatsAppOptions): Promise<WhatsAppSendResult> {\n const to = normalizeTaqnyatPhone(options.to);\n const body = isTemplateMessage(options)\n ? {\n to,\n type: \"template\" as const,\n template: {\n name: options.template.name,\n language: { code: options.template.language },\n ...(options.template.components ? { components: options.template.components } : {}),\n },\n }\n : {\n to,\n type: \"text\" as const,\n text: { body: options.text },\n };\n\n // Docs: base URL must end with `/`.\n const response = await fetch(\"https://api.taqnyat.sa/wa/v2/messages/\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.bearerToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as Record<string, unknown>;\n const messageId = extractMessageId(payload);\n\n // Docs return `{ message, reason }` on failure (often still HTTP 200).\n if (!response.ok || (payload.message !== undefined && messageId === undefined)) {\n const reason =\n typeof payload.reason === \"string\"\n ? payload.reason\n : typeof payload.message === \"string\"\n ? `Taqnyat WhatsApp error ${payload.message}`\n : \"Taqnyat WhatsApp API error\";\n const status =\n typeof payload.message === \"string\" && /^\\d+$/.test(payload.message)\n ? Number(payload.message)\n : response.status;\n throw new TaqnyatWhatsAppError(reason, status || response.status, payload);\n }\n\n return {\n messageId: messageId ?? options.messageId ?? \"\",\n to: options.to,\n status: \"accepted\",\n response: typeof payload.type === \"string\" ? payload.type : \"Message sent\",\n provider: \"taqnyat-whatsapp\",\n };\n }\n\n /** Lightweight credential check. */\n async verify(): Promise<VerifyResult> {\n return {\n ok: Boolean(this.bearerToken),\n provider: \"taqnyat-whatsapp\",\n message: this.bearerToken ? \"Credentials present\" : \"Missing credentials\",\n };\n }\n}\n"
6
6
  ],
7
7
  "mappings": "kIAuCO,CAAM,KAA6B,CAAY,CAIlC,WACA,SAHlB,WAAW,CACT,EACgB,EACA,EAChB,CACA,MACE,EACA,EAAuB,GAAc,KAAO,EAAa,IAAM,EAAa,GAAG,EAC/E,CACE,aACA,SAAU,mBACV,MAAO,CACT,CACF,EAXgB,kBACA,gBAWhB,KAAK,KAAO,uBAEhB,CAEA,SAAS,CAAiB,CACxB,EAC4D,CAC5D,MAAO,aAAc,EAGvB,SAAS,CAAgB,CAAC,EAAsD,CAC9E,IAAM,EAAW,EAAQ,SACzB,GAAI,MAAM,QAAQ,CAAQ,EAAG,CAC3B,IAAM,EAAQ,EAAS,GACvB,GAAI,OAAO,GAAO,aAAe,SAAU,OAAO,EAAM,WACnD,QAAI,GAAY,OAAO,IAAa,UACzC,QAAW,KAAS,OAAO,OAAO,CAAmC,EACnE,GAAI,GAAS,OAAO,IAAU,SAAU,CACtC,IAAM,EAAM,EAAkC,WAC9C,GAAI,OAAO,IAAO,SAAU,OAAO,GAIzC,OAMK,MAAM,CAAsD,CACxD,SAAW,mBAEH,YAGjB,WAAW,CAAC,EAA+B,CACzC,KAAK,YAAc,EAAO,iBAItB,KAAI,CAAC,EAAuD,CAChE,IAAM,EAAK,EAAsB,EAAQ,EAAE,EACrC,EAAO,EAAkB,CAAO,EAClC,CACE,KACA,KAAM,WACN,SAAU,CACR,KAAM,EAAQ,SAAS,KACvB,SAAU,CAAE,KAAM,EAAQ,SAAS,QAAS,KACxC,EAAQ,SAAS,WAAa,CAAE,WAAY,EAAQ,SAAS,UAAW,EAAI,CAAC,CACnF,CACF,EACA,CACE,KACA,KAAM,OACN,KAAM,CAAE,KAAM,EAAQ,IAAK,CAC7B,EAGE,EAAW,MAAM,MAAM,yCAA0C,CACrE,OAAQ,OACR,QAAS,CACP,cAAe,UAAU,KAAK,cAC9B,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAC/B,EAAY,EAAiB,CAAO,EAG1C,GAAI,CAAC,EAAS,IAAO,EAAQ,UAAY,QAAa,IAAc,OAAY,CAC9E,IAAM,EACJ,OAAO,EAAQ,SAAW,SACtB,EAAQ,OACR,OAAO,EAAQ,UAAY,SACzB,0BAA0B,EAAQ,UAClC,6BACF,EACJ,OAAO,EAAQ,UAAY,UAAY,QAAQ,KAAK,EAAQ,OAAO,EAC/D,OAAO,EAAQ,OAAO,EACtB,EAAS,OACf,MAAM,IAAI,EAAqB,EAAQ,GAAU,EAAS,OAAQ,CAAO,EAG3E,MAAO,CACL,UAAW,GAAa,EAAQ,WAAa,GAC7C,GAAI,EAAQ,GACZ,OAAQ,WACR,SAAU,OAAO,EAAQ,OAAS,SAAW,EAAQ,KAAO,eAC5D,SAAU,kBACZ,OAII,OAAM,EAA0B,CACpC,MAAO,CACL,GAAI,QAAQ,KAAK,WAAW,EAC5B,SAAU,mBACV,QAAS,KAAK,YAAc,sBAAwB,qBACtD,EAEJ",
8
- "debugId": "5657D7720E9F16CB64756E2164756E21",
8
+ "debugId": "E4D853E15FDA606B64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,3 @@
1
- import{Y as J}from"../chunk-ywq6s10g.js";import{ca as K,da as L}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class H extends K{statusCode;apiError;constructor(k,q,x){super(k,L(q),{statusCode:q,provider:"twilio-sms",cause:x});this.statusCode=q;this.apiError=x;this.name="TwilioSmsError"}}class N{provider="twilio-sms";accountSid;authToken;apiKey;from;messagingServiceSid;statusCallback;constructor(k){if(this.accountSid=k.accountSid,this.authToken=k.authToken,k.apiKey!==void 0)this.apiKey=k.apiKey;if(k.from!==void 0)this.from=k.from;if(k.messagingServiceSid!==void 0)this.messagingServiceSid=k.messagingServiceSid;if(k.statusCallback!==void 0)this.statusCallback=k.statusCallback}async send(k){let q=k.from??this.from;if(!q&&!this.messagingServiceSid)throw new H("Twilio SMS requires From or MessagingServiceSid",400,{message:"Missing from / messagingServiceSid"});let x=new URLSearchParams({To:k.to,Body:k.body});if(q)x.set("From",q);if(this.messagingServiceSid)x.set("MessagingServiceSid",this.messagingServiceSid);if(this.statusCallback)x.set("StatusCallback",this.statusCallback);let I=`https://api.twilio.com/2010-04-01/Accounts/${this.accountSid}/Messages.json`,z=await fetch(I,{method:"POST",headers:{...this.authHeaders(),"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:x}),D=await this.readJson(z);if(!z.ok){let M=typeof D==="object"&&D!==null&&"message"in D&&typeof D.message==="string"?D.message:"Twilio SMS API error";throw new H(M,z.status,D)}let F=D;return{messageId:F.sid??k.messageId??"",to:k.to,status:F.status??"queued",response:F.status??"queued",provider:"twilio-sms"}}async verify(){if(!this.accountSid||!this.authToken)return{ok:!1,provider:"twilio-sms",message:"Missing credentials"};try{let k=`https://api.twilio.com/2010-04-01/Accounts/${this.accountSid}.json`,q=await fetch(k,{method:"GET",headers:{...this.authHeaders(),Accept:"application/json"}});if(!q.ok){let z=await this.readJson(q);return{ok:!1,provider:"twilio-sms",message:typeof z==="object"&&z!==null&&"message"in z&&typeof z.message==="string"?z.message:`HTTP ${q.status}`}}let x=await this.readJson(q);return{ok:!0,provider:"twilio-sms",message:`Account ${x.friendly_name??x.status??"ok"}`}}catch(k){return{ok:!1,provider:"twilio-sms",message:k instanceof Error?k.message:String(k)}}}authHeaders(){let k=this.apiKey??this.accountSid;return{Authorization:`Basic ${J(`${k}:${this.authToken}`).replace(/\r\n/g,"")}`}}async readJson(k){return k.json().catch(()=>({}))}}export{N as TwilioSmsTransport,H as TwilioSmsError};
1
+ import{da as J}from"../chunk-hnzmn4s4.js";import{ja as K,ka as L}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class H extends K{statusCode;apiError;constructor(k,q,x){super(k,L(q),{statusCode:q,provider:"twilio-sms",cause:x});this.statusCode=q;this.apiError=x;this.name="TwilioSmsError"}}class N{provider="twilio-sms";accountSid;authToken;apiKey;from;messagingServiceSid;statusCallback;constructor(k){if(this.accountSid=k.accountSid,this.authToken=k.authToken,k.apiKey!==void 0)this.apiKey=k.apiKey;if(k.from!==void 0)this.from=k.from;if(k.messagingServiceSid!==void 0)this.messagingServiceSid=k.messagingServiceSid;if(k.statusCallback!==void 0)this.statusCallback=k.statusCallback}async send(k){let q=k.from??this.from;if(!q&&!this.messagingServiceSid)throw new H("Twilio SMS requires From or MessagingServiceSid",400,{message:"Missing from / messagingServiceSid"});let x=new URLSearchParams({To:k.to,Body:k.body});if(q)x.set("From",q);if(this.messagingServiceSid)x.set("MessagingServiceSid",this.messagingServiceSid);if(this.statusCallback)x.set("StatusCallback",this.statusCallback);let I=`https://api.twilio.com/2010-04-01/Accounts/${this.accountSid}/Messages.json`,z=await fetch(I,{method:"POST",headers:{...this.authHeaders(),"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:x}),D=await this.readJson(z);if(!z.ok){let M=typeof D==="object"&&D!==null&&"message"in D&&typeof D.message==="string"?D.message:"Twilio SMS API error";throw new H(M,z.status,D)}let F=D;return{messageId:F.sid??k.messageId??"",to:k.to,status:F.status??"queued",response:F.status??"queued",provider:"twilio-sms"}}async verify(){if(!this.accountSid||!this.authToken)return{ok:!1,provider:"twilio-sms",message:"Missing credentials"};try{let k=`https://api.twilio.com/2010-04-01/Accounts/${this.accountSid}.json`,q=await fetch(k,{method:"GET",headers:{...this.authHeaders(),Accept:"application/json"}});if(!q.ok){let z=await this.readJson(q);return{ok:!1,provider:"twilio-sms",message:typeof z==="object"&&z!==null&&"message"in z&&typeof z.message==="string"?z.message:`HTTP ${q.status}`}}let x=await this.readJson(q);return{ok:!0,provider:"twilio-sms",message:`Account ${x.friendly_name??x.status??"ok"}`}}catch(k){return{ok:!1,provider:"twilio-sms",message:k instanceof Error?k.message:String(k)}}}authHeaders(){let k=this.apiKey??this.accountSid;return{Authorization:`Basic ${J(`${k}:${this.authToken}`).replace(/\r\n/g,"")}`}}async readJson(k){return k.json().catch(()=>({}))}}export{N as TwilioSmsTransport,H as TwilioSmsError};
2
2
 
3
- //# debugId=54563D7C95D0712D64756E2164756E21
3
+ //# debugId=68862E4E64033D4164756E2164756E21