sently 0.5.1 → 0.6.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 (110) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +61 -30
  3. package/dist/adapters/bun.js +1 -1
  4. package/dist/adapters/cf.js +2 -2
  5. package/dist/adapters/cf.js.map +1 -1
  6. package/dist/adapters/deno.js +1 -1
  7. package/dist/adapters/node.js +1 -1
  8. package/dist/auth/oauth2.js +2 -2
  9. package/dist/auth/oauth2.js.map +1 -1
  10. package/dist/chunk-1cmggzw0.js +4 -0
  11. package/dist/chunk-1cmggzw0.js.map +10 -0
  12. package/dist/{chunk-32dj0v9g.js → chunk-50ycrgx6.js} +2 -2
  13. package/dist/{chunk-32dj0v9g.js.map → chunk-50ycrgx6.js.map} +1 -1
  14. package/dist/chunk-9ckbc9mk.js +4 -0
  15. package/dist/chunk-9ckbc9mk.js.map +10 -0
  16. package/dist/{chunk-nj3wmg69.js → chunk-bzw0fdmy.js} +2 -2
  17. package/dist/{chunk-nj3wmg69.js.map → chunk-bzw0fdmy.js.map} +1 -1
  18. package/dist/chunk-gkec86xg.js +13 -0
  19. package/dist/chunk-gkec86xg.js.map +11 -0
  20. package/dist/chunk-pqp843mh.js +4 -0
  21. package/dist/chunk-pqp843mh.js.map +10 -0
  22. package/dist/chunk-q8k3m6qe.js +4 -0
  23. package/dist/chunk-q8k3m6qe.js.map +10 -0
  24. package/dist/{chunk-zwm5bfa4.js → chunk-qp4y0kp2.js} +2 -2
  25. package/dist/{chunk-zwm5bfa4.js.map → chunk-qp4y0kp2.js.map} +1 -1
  26. package/dist/{chunk-rhade81v.js → chunk-rszb1n1d.js} +2 -2
  27. package/dist/{chunk-rhade81v.js.map → chunk-rszb1n1d.js.map} +1 -1
  28. package/dist/{chunk-ntqmn6pe.js → chunk-tfxje74k.js} +2 -2
  29. package/dist/{chunk-ntqmn6pe.js.map → chunk-tfxje74k.js.map} +1 -1
  30. package/dist/{chunk-nhec1319.js → chunk-v8eh0n8d.js} +3 -3
  31. package/dist/{chunk-nhec1319.js.map → chunk-v8eh0n8d.js.map} +1 -1
  32. package/dist/chunk-wp4qxk1j.js +4 -0
  33. package/dist/chunk-wp4qxk1j.js.map +11 -0
  34. package/dist/{chunk-kdqxhybf.js → chunk-xdx6ypr5.js} +3 -3
  35. package/dist/{chunk-kdqxhybf.js.map → chunk-xdx6ypr5.js.map} +1 -1
  36. package/dist/{chunk-cvmk9h1r.js → chunk-z7j2n9k8.js} +3 -3
  37. package/dist/{chunk-cvmk9h1r.js.map → chunk-z7j2n9k8.js.map} +1 -1
  38. package/dist/chunk-zm7fe0wb.js +4 -0
  39. package/dist/{chunk-j5m37k1c.js.map → chunk-zm7fe0wb.js.map} +2 -2
  40. package/dist/core/errors.d.ts +55 -0
  41. package/dist/core/smtp.d.ts +3 -3
  42. package/dist/core/smtp.js +2 -2
  43. package/dist/core/smtp.js.map +1 -1
  44. package/dist/core/types.d.ts +28 -1
  45. package/dist/detect.js +2 -2
  46. package/dist/detect.js.map +3 -3
  47. package/dist/dkim.js +2 -2
  48. package/dist/dkim.js.map +2 -2
  49. package/dist/errors.d.ts +29 -0
  50. package/dist/errors.js +8 -0
  51. package/dist/{react.js.map → errors.js.map} +1 -1
  52. package/dist/idempotency.js +2 -2
  53. package/dist/idempotency.js.map +1 -1
  54. package/dist/index.d.ts +9 -1
  55. package/dist/index.js +9 -0
  56. package/dist/mailer.d.ts +4 -3
  57. package/dist/mailer.js +2 -2
  58. package/dist/mailer.js.map +1 -1
  59. package/dist/plugins/react.js +2 -2
  60. package/dist/plugins/react.js.map +1 -1
  61. package/dist/plugins/template.js +1 -1
  62. package/dist/pool/pool.js +2 -2
  63. package/dist/pool/pool.js.map +2 -2
  64. package/dist/react.js +4 -3
  65. package/dist/transports/brevo.d.ts +3 -3
  66. package/dist/transports/brevo.js +2 -2
  67. package/dist/transports/brevo.js.map +3 -3
  68. package/dist/transports/mailgun.d.ts +2 -1
  69. package/dist/transports/mailgun.js +2 -2
  70. package/dist/transports/mailgun.js.map +3 -3
  71. package/dist/transports/postmark.d.ts +2 -1
  72. package/dist/transports/postmark.js +2 -2
  73. package/dist/transports/postmark.js.map +3 -3
  74. package/dist/transports/preview.js +2 -2
  75. package/dist/transports/preview.js.map +1 -1
  76. package/dist/transports/resend.d.ts +2 -1
  77. package/dist/transports/resend.js +2 -2
  78. package/dist/transports/resend.js.map +3 -3
  79. package/dist/transports/retry.d.ts +9 -0
  80. package/dist/transports/retry.js +2 -2
  81. package/dist/transports/retry.js.map +3 -4
  82. package/dist/transports/sendgrid.d.ts +2 -1
  83. package/dist/transports/sendgrid.js +2 -2
  84. package/dist/transports/sendgrid.js.map +3 -3
  85. package/dist/transports/ses.d.ts +3 -3
  86. package/dist/transports/ses.js +5 -5
  87. package/dist/transports/ses.js.map +3 -3
  88. package/dist/transports/smtp.js +2 -2
  89. package/dist/transports/smtp.js.map +1 -1
  90. package/dist/webhooks/brevo.js +3 -0
  91. package/dist/webhooks/brevo.js.map +10 -0
  92. package/dist/webhooks/mailgun.js +3 -0
  93. package/dist/webhooks/mailgun.js.map +10 -0
  94. package/dist/webhooks/postmark.js +3 -0
  95. package/dist/webhooks/postmark.js.map +10 -0
  96. package/dist/webhooks/resend.js +3 -0
  97. package/dist/webhooks/resend.js.map +10 -0
  98. package/dist/webhooks/sendgrid.js +3 -0
  99. package/dist/webhooks/sendgrid.js.map +10 -0
  100. package/dist/webhooks/ses.js +3 -0
  101. package/dist/webhooks/ses.js.map +10 -0
  102. package/dist/webhooks/timing-safe-equal.js +3 -0
  103. package/dist/{webhooks.js.map → webhooks/timing-safe-equal.js.map} +1 -1
  104. package/dist/webhooks.js +13 -3
  105. package/package.json +5 -1
  106. package/dist/chunk-j5m37k1c.js +0 -4
  107. package/dist/chunk-m2njnxre.js +0 -13
  108. package/dist/chunk-m2njnxre.js.map +0 -11
  109. package/dist/chunk-pjzsvsm6.js +0 -4
  110. package/dist/chunk-pjzsvsm6.js.map +0 -11
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @module
3
+ * Unified sently error types — import from `sently/errors`.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { SentlyError } from "sently/errors";
8
+ * import { ResendError } from "sently/transports/resend";
9
+ *
10
+ * try {
11
+ * await mailer.send({ ... });
12
+ * } catch (err) {
13
+ * if (err instanceof SentlyError) {
14
+ * console.error(err.sentlyCode, err.statusCode);
15
+ * }
16
+ * if (err instanceof ResendError) {
17
+ * console.error(err.statusCode, err.apiError);
18
+ * }
19
+ * }
20
+ * ```
21
+ */
22
+ export type { SentlyErrorCode, SentlyErrorOptions } from "./core/errors.js";
23
+ export {
24
+ /** Map an HTTP status code to a stable sently error code. */
25
+ httpStatusToSentlyCode,
26
+ /** Base error class for all sently transport and protocol failures. */
27
+ SentlyError,
28
+ /** Map an SMTP response code to a stable sently error code. */
29
+ smtpCodeToSentlyCode, } from "./core/errors.js";
package/dist/errors.js ADDED
@@ -0,0 +1,8 @@
1
+ export {
2
+
3
+ httpStatusToSentlyCode,
4
+
5
+ SentlyError,
6
+
7
+ smtpCodeToSentlyCode,
8
+ } from "./core/errors.js";
@@ -4,6 +4,6 @@
4
4
  "sourcesContent": [
5
5
  ],
6
6
  "mappings": "",
7
- "debugId": "AB3248C3DD5B9EFC64756E2164756E21",
7
+ "debugId": "2BC101DB496C40DA64756E2164756E21",
8
8
  "names": []
9
9
  }
@@ -1,3 +1,3 @@
1
- import{j as F}from"./chunk-zwm5bfa4.js";import{n as C,p as B}from"./chunk-kdqxhybf.js";import"./chunk-32dj0v9g.js";import"./chunk-rhade81v.js";class G{entries=new Map;async has(q){let w=this.entries.get(q);if(w===void 0)return!1;if(w<=Date.now())return this.entries.delete(q),!1;return!0}async set(q,w){let z=w!==void 0?Date.now()+w:Number.POSITIVE_INFINITY;this.entries.set(q,z)}}function H(q){let w=C(q.from)[0],z=[...B(q.to),...q.cc?B(q.cc):[],...q.bcc?B(q.bcc):[]];return{messageId:q.messageId??"",accepted:B(q.to),rejected:[],response:"Deduped — idempotency key already recorded",envelope:{from:w?.address??"",to:z},deduped:!0}}class J{inner;store;ttlMs;constructor(q,w){this.inner=q;this.store=w?.store??new G,this.ttlMs=w?.ttlMs??86400000}async send(q){let w=F(q);if(w===void 0)return this.inner.send(q);if(await this.store.has(w))return H(q);let z=await this.inner.send(q);return await this.store.set(w,this.ttlMs),z}async sendBatch(q){if(this.inner.sendBatch)return this.inner.sendBatch(q);return Promise.all(q.map((w)=>this.send(w)))}async verify(){if(this.inner.verify)return this.inner.verify();return{ok:!0,provider:"idempotency"}}async close(){await this.inner.close?.()}}export{G as MemoryIdempotencyStore,J as IdempotencyTransport};
1
+ import{q as F}from"./chunk-qp4y0kp2.js";import{A as B,y as C}from"./chunk-xdx6ypr5.js";import"./chunk-50ycrgx6.js";import"./chunk-rszb1n1d.js";class G{entries=new Map;async has(q){let w=this.entries.get(q);if(w===void 0)return!1;if(w<=Date.now())return this.entries.delete(q),!1;return!0}async set(q,w){let z=w!==void 0?Date.now()+w:Number.POSITIVE_INFINITY;this.entries.set(q,z)}}function H(q){let w=C(q.from)[0],z=[...B(q.to),...q.cc?B(q.cc):[],...q.bcc?B(q.bcc):[]];return{messageId:q.messageId??"",accepted:B(q.to),rejected:[],response:"Deduped — idempotency key already recorded",envelope:{from:w?.address??"",to:z},deduped:!0}}class J{inner;store;ttlMs;constructor(q,w){this.inner=q;this.store=w?.store??new G,this.ttlMs=w?.ttlMs??86400000}async send(q){let w=F(q);if(w===void 0)return this.inner.send(q);if(await this.store.has(w))return H(q);let z=await this.inner.send(q);return await this.store.set(w,this.ttlMs),z}async sendBatch(q){if(this.inner.sendBatch)return this.inner.sendBatch(q);return Promise.all(q.map((w)=>this.send(w)))}async verify(){if(this.inner.verify)return this.inner.verify();return{ok:!0,provider:"idempotency"}}async close(){await this.inner.close?.()}}export{G as MemoryIdempotencyStore,J as IdempotencyTransport};
2
2
 
3
- //# debugId=C9F463170D0BFD9764756E2164756E21
3
+ //# debugId=D8BBD409B64A4DE564756E2164756E21
@@ -5,6 +5,6 @@
5
5
  "/**\n * @module\n * IdempotencyTransport — decorator that deduplicates sends on retry or replay.\n *\n * Wrap **outside** RetryTransport so all retry attempts share one key:\n *\n * ```ts\n * new IdempotencyTransport(new RetryTransport(inner))\n * ```\n *\n * For production, supply a shared store (Redis, Dragonfly, etc.).\n *\n * @example\n * ```ts\n * import { IdempotencyTransport, MemoryIdempotencyStore } from \"sently/idempotency\";\n * import { RetryTransport } from \"sently/transports/retry\";\n * import { ResendTransport } from \"sently/transports/resend\";\n *\n * const transport = new IdempotencyTransport(\n * new RetryTransport(new ResendTransport({ apiKey })),\n * { store: new MemoryIdempotencyStore(), ttlMs: 86_400_000 },\n * );\n * ```\n */\nimport { extractEmails, parseAddresses } from \"./core/address.js\";\nimport { resolveIdempotencyKey } from \"./core/idempotency-key.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"./core/types.js\";\n\n/** Key-value store for idempotency deduplication. */\nexport interface IdempotencyStore {\n /** Returns true when the key was already recorded. */\n has(key: string): Promise<boolean>;\n /** Record a key, optionally expiring after `ttlMs` milliseconds. */\n set(key: string, ttlMs?: number): Promise<void>;\n}\n\n/** In-memory idempotency store with optional TTL expiry. Suitable for single-process use. */\nexport class MemoryIdempotencyStore implements IdempotencyStore {\n private readonly entries = new Map<string, number>();\n\n /** Returns true when the key exists and has not expired. */\n async has(key: string): Promise<boolean> {\n const expiresAt = this.entries.get(key);\n if (expiresAt === undefined) {\n return false;\n }\n if (expiresAt <= Date.now()) {\n this.entries.delete(key);\n return false;\n }\n return true;\n }\n\n /** Record a key with optional TTL in milliseconds. */\n async set(key: string, ttlMs?: number): Promise<void> {\n const expiresAt = ttlMs !== undefined ? Date.now() + ttlMs : Number.POSITIVE_INFINITY;\n this.entries.set(key, expiresAt);\n }\n}\n\n/** Options for {@link IdempotencyTransport}. */\nexport interface IdempotencyTransportOptions {\n /** Store used to track sent keys. Defaults to {@link MemoryIdempotencyStore}. */\n store?: IdempotencyStore;\n /** TTL in milliseconds for recorded keys. Default: 24 hours. */\n ttlMs?: number;\n}\n\nfunction syntheticDedupedResult(options: MailOptions): SendResult {\n const from = parseAddresses(options.from)[0];\n const to = [\n ...extractEmails(options.to),\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ];\n\n return {\n messageId: options.messageId ?? \"\",\n accepted: extractEmails(options.to),\n rejected: [],\n response: \"Deduped — idempotency key already recorded\",\n envelope: {\n from: from?.address ?? \"\",\n to,\n },\n deduped: true,\n };\n}\n\n/**\n * Transport decorator that skips duplicate sends when an idempotency key\n * was already recorded. Checks the store once before delegating to the inner\n * transport, so retries inside RetryTransport reuse the same key automatically.\n */\nexport class IdempotencyTransport implements Transport {\n private readonly store: IdempotencyStore;\n private readonly ttlMs: number;\n\n /** Wraps an inner transport with idempotency deduplication. */\n constructor(\n /** Transport that performs the actual send when the key is new. */\n private readonly inner: Transport,\n options?: IdempotencyTransportOptions,\n ) {\n this.store = options?.store ?? new MemoryIdempotencyStore();\n this.ttlMs = options?.ttlMs ?? 86_400_000;\n }\n\n /** Sends once per idempotency key; returns a synthetic result on duplicate keys. */\n async send(options: MailOptions): Promise<SendResult> {\n const key = resolveIdempotencyKey(options);\n if (key === undefined) {\n return this.inner.send(options);\n }\n\n if (await this.store.has(key)) {\n return syntheticDedupedResult(options);\n }\n\n const result = await this.inner.send(options);\n await this.store.set(key, this.ttlMs);\n return result;\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 /** 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: \"idempotency\" };\n }\n\n /** Delegates close to the inner transport if available. */\n async close(): Promise<void> {\n await this.inner.close?.();\n }\n}\n"
6
6
  ],
7
7
  "mappings": "iKAqCO,AAAM,LAAmD,LAC7C,QAAU,IAAI,SAGzB,IAAG,CAAC,EAA+B,CACvC,IAAM,EAAY,KAAK,QAAQ,IAAI,CAAG,EACtC,GAAI,IAAc,OAChB,MAAO,GAET,GAAI,GAAa,KAAK,IAAI,EAExB,OADA,KAAK,QAAQ,OAAO,CAAG,EAChB,GAET,MAAO,QAIH,IAAG,CAAC,EAAa,EAA+B,CACpD,IAAM,EAAY,IAAU,OAAY,KAAK,IAAI,EAAI,EAAQ,OAAO,kBACpE,KAAK,QAAQ,IAAI,EAAK,CAAS,EAEnC,CAUA,SAAS,CAAsB,CAAC,EAAkC,CAChE,IAAM,EAAO,EAAe,EAAQ,IAAI,EAAE,GACpC,EAAK,CACT,GAAG,EAAc,EAAQ,EAAE,EAC3B,GAAI,EAAQ,GAAK,EAAc,EAAQ,EAAE,EAAI,CAAC,EAC9C,GAAI,EAAQ,IAAM,EAAc,EAAQ,GAAG,EAAI,CAAC,CAClD,EAEA,MAAO,CACL,UAAW,EAAQ,WAAa,GAChC,SAAU,EAAc,EAAQ,EAAE,EAClC,SAAU,CAAC,EACX,SAAU,6CACV,SAAU,CACR,KAAM,GAAM,SAAW,GACvB,IACF,EACA,QAAS,EACX,EAQK,MAAM,CAA0C,CAOlC,MANF,MACA,MAGjB,WAAW,CAEQ,EACjB,EACA,CAFiB,aAGjB,KAAK,MAAQ,GAAS,OAAS,IAAI,EACnC,KAAK,MAAQ,GAAS,OAAS,cAI3B,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAM,EAAsB,CAAO,EACzC,GAAI,IAAQ,OACV,OAAO,KAAK,MAAM,KAAK,CAAO,EAGhC,GAAI,MAAM,KAAK,MAAM,IAAI,CAAG,EAC1B,OAAO,EAAuB,CAAO,EAGvC,IAAM,EAAS,MAAM,KAAK,MAAM,KAAK,CAAO,EAE5C,OADA,MAAM,KAAK,MAAM,IAAI,EAAK,KAAK,KAAK,EAC7B,OAIH,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,OAI5D,OAAM,EAA0B,CACpC,GAAI,KAAK,MAAM,OACb,OAAO,KAAK,MAAM,OAAO,EAE3B,MAAO,CAAE,GAAI,GAAM,SAAU,aAAc,OAIvC,MAAK,EAAkB,CAC3B,MAAM,KAAK,MAAM,QAAQ,EAE7B",
8
- "debugId": "C9F463170D0BFD9764756E2164756E21",
8
+ "debugId": "D8BBD409B64A4DE564756E2164756E21",
9
9
  "names": []
10
10
  }
package/dist/index.d.ts CHANGED
@@ -26,10 +26,18 @@ GOOGLE_TOKEN_URL,
26
26
  MICROSOFT_TOKEN_URL,
27
27
  /** OAuth2 client with in-memory token cache and automatic refresh. */
28
28
  OAuth2Client, } from "./auth/oauth2.js";
29
+ export type { SentlyErrorCode, SentlyErrorOptions } from "./core/errors.js";
30
+ export {
31
+ /** Map an HTTP status code to a stable sently error code. */
32
+ httpStatusToSentlyCode,
33
+ /** Base error class for all sently transport and protocol failures. */
34
+ SentlyError,
35
+ /** Map an SMTP response code to a stable sently error code. */
36
+ smtpCodeToSentlyCode, } from "./core/errors.js";
29
37
  export {
30
38
  /** SMTP protocol error with server response details. */
31
39
  SMTPError, } from "./core/smtp.js";
32
- export type { Address, AddressInput, Attachment, BrevoConfig, BulkSendOptions, BulkSendResult, CreateMailerOptions, DKIMConfig, Envelope, Mailer, MailgunConfig, MailOptions, MailPlugin, OAuth2Config, PoolConfig, PreviewConfig, RetryConfig, Runtime, SESConfig, SendResult, SMTPAuth, SMTPConfig, SocketAdapter, TLSOptions, Transport, TransportMailerOptions, VerifyResult, } from "./core/types.js";
40
+ export type { Address, AddressInput, Attachment, BrevoConfig, BulkSendOptions, BulkSendResult, CreateMailerOptions, DKIMConfig, Envelope, Mailer, MailerHookContext, MailerHooks, MailgunConfig, MailOptions, MailPlugin, OAuth2Config, PoolConfig, PreviewConfig, RetryConfig, Runtime, SESConfig, SendResult, SMTPAuth, SMTPConfig, SocketAdapter, TLSOptions, Transport, TransportMailerOptions, VerifyResult, } from "./core/types.js";
33
41
  export {
34
42
  /** Create a ready-to-use Mailer instance. */
35
43
  createMailer,
package/dist/index.js CHANGED
@@ -6,6 +6,15 @@ export {
6
6
 
7
7
  OAuth2Client,
8
8
  } from "./auth/oauth2.js";
9
+
10
+ export {
11
+
12
+ httpStatusToSentlyCode,
13
+
14
+ SentlyError,
15
+
16
+ smtpCodeToSentlyCode,
17
+ } from "./core/errors.js";
9
18
  export {
10
19
 
11
20
  SMTPError,
package/dist/mailer.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { BulkSendOptions, BulkSendResult, Mailer, MailOptions, MailPlugin, SendResult, Transport, TransportMailerOptions, VerifyResult } from "./core/types.js";
2
- export type { TransportMailerOptions };
1
+ import type { BulkSendOptions, BulkSendResult, Mailer, MailerHookContext, MailerHooks, MailOptions, MailPlugin, SendResult, Transport, TransportMailerOptions, VerifyResult } from "./core/types.js";
2
+ export type { MailerHookContext, MailerHooks, TransportMailerOptions };
3
3
  /**
4
4
  * Create a mailer that wraps a custom {@link Transport} (HTTP API, preview, retry, etc.).
5
5
  */
@@ -8,7 +8,8 @@ export declare function createMailer(options: TransportMailerOptions): Promise<M
8
8
  export declare class MailerImpl implements Mailer {
9
9
  private readonly transport;
10
10
  private readonly plugins;
11
- constructor(transport: Transport, plugins?: MailPlugin[]);
11
+ private readonly hooks?;
12
+ constructor(transport: Transport, plugins?: MailPlugin[], hooks?: MailerHooks | undefined);
12
13
  send(options: MailOptions): Promise<SendResult>;
13
14
  private processMessage;
14
15
  sendBulk(messages: MailOptions[], options?: BulkSendOptions): Promise<BulkSendResult>;
package/dist/mailer.js CHANGED
@@ -1,3 +1,3 @@
1
- import{r as a,s as b}from"./chunk-pjzsvsm6.js";import"./chunk-ntqmn6pe.js";import"./chunk-rhade81v.js";export{a as createMailer,b as MailerImpl};
1
+ import{t as a,u as b}from"./chunk-wp4qxk1j.js";import"./chunk-1cmggzw0.js";import"./chunk-tfxje74k.js";import"./chunk-xdx6ypr5.js";import"./chunk-gkec86xg.js";import"./chunk-9ckbc9mk.js";import"./chunk-50ycrgx6.js";import"./chunk-rszb1n1d.js";export{a as createMailer,b as MailerImpl};
2
2
 
3
- //# debugId=87404C28F00AC40D64756E2164756E21
3
+ //# debugId=1771C28406C2AFAA64756E2164756E21
@@ -4,6 +4,6 @@
4
4
  "sourcesContent": [
5
5
  ],
6
6
  "mappings": "",
7
- "debugId": "87404C28F00AC40D64756E2164756E21",
7
+ "debugId": "1771C28406C2AFAA64756E2164756E21",
8
8
  "names": []
9
9
  }
@@ -1,3 +1,3 @@
1
- import{K as B}from"../chunk-rhade81v.js";function F(){return async(j)=>{if(j.react===void 0||j.react===null)return j;let{render:k}=await import("@react-email/render"),q=j.html??await k(j.react,{pretty:!1}),z=j.text??await k(j.react,{plainText:!0}),{react:D,...A}=j;return{...A,html:q,text:z}}}export{F as reactPlugin};
1
+ import{V as B}from"../chunk-rszb1n1d.js";function F(){return async(j)=>{if(j.react===void 0||j.react===null)return j;let{render:k}=await import("@react-email/render"),q=j.html??await k(j.react,{pretty:!1}),z=j.text??await k(j.react,{plainText:!0}),{react:D,...A}=j;return{...A,html:q,text:z}}}export{F as reactPlugin};
2
2
 
3
- //# debugId=06063038813DDE2664756E2164756E21
3
+ //# debugId=9D511B0951A46E1B64756E2164756E21
@@ -5,6 +5,6 @@
5
5
  "/**\n * @module\n * React Email plugin for sently.\n * Renders a React element to HTML and plain text via `@react-email/render`.\n *\n * @example\n * ```ts\n * import { reactPlugin } from \"sently/react\";\n * import { WelcomeEmail } from \"./emails/welcome\";\n *\n * const mailer = await createMailer({\n * transport: new ResendTransport({ apiKey }),\n * plugins: [reactPlugin()],\n * });\n *\n * await mailer.send({\n * from: \"...\",\n * to: \"...\",\n * subject: \"Welcome!\",\n * react: WelcomeEmail({ name: \"Ali\" }),\n * });\n * ```\n */\nimport type { MailOptions, MailPlugin } from \"../core/types.js\";\n\n/** Mail options with a React element body (requires `react` peer). */\nexport interface ReactMailOptions extends Omit<MailOptions, \"react\"> {\n /** React element rendered to html + text by the react plugin. */\n react?: import(\"react\").ReactElement;\n}\n\n/**\n * Create a plugin that renders `options.react` to HTML and plain text.\n * Explicit `html` / `text` values always win. The `react` field is stripped\n * before options reach the transport.\n */\nexport function reactPlugin(): MailPlugin {\n return async (options: MailOptions): Promise<MailOptions> => {\n if (options.react === undefined || options.react === null) {\n return options;\n }\n\n const { render } = await import(\"@react-email/render\");\n\n const html =\n options.html ??\n (await render(options.react as Parameters<typeof render>[0], { pretty: false }));\n\n const text =\n options.text ??\n (await render(options.react as Parameters<typeof render>[0], { plainText: true }));\n\n const { react: _react, ...rest } = options;\n return { ...rest, html, text };\n };\n}\n"
6
6
  ],
7
7
  "mappings": "yCAoCO,SAAS,CAAW,EAAe,CACxC,MAAO,OAAO,IAA+C,CAC3D,GAAI,EAAQ,QAAU,QAAa,EAAQ,QAAU,KACnD,OAAO,EAGT,IAAQ,UAAW,KAAa,+BAE1B,EACJ,EAAQ,MACP,MAAM,EAAO,EAAQ,MAAuC,CAAE,OAAQ,EAAM,CAAC,EAE1E,EACJ,EAAQ,MACP,MAAM,EAAO,EAAQ,MAAuC,CAAE,UAAW,EAAK,CAAC,GAE1E,MAAO,KAAW,GAAS,EACnC,MAAO,IAAK,EAAM,OAAM,MAAK",
8
- "debugId": "06063038813DDE2664756E2164756E21",
8
+ "debugId": "9D511B0951A46E1B64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,3 @@
1
- import"../chunk-rhade81v.js";function E(q,j){return q.replace(/\{\{(\w+)\}\}/g,(z,A)=>{let w=j[A];return w!==void 0&&w!==null?String(w):""})}function F(q){return(j)=>{if(!j.template)return j;let z=q.templates[j.template];if(!z)throw Error(`sently: template "${j.template}" not found`);let A=q.engine(z,j.data??{}),{template:w,data:D,...B}=j;return{...B,html:A}}}export{F as templatePlugin,E as simpleEngine};
1
+ import"../chunk-rszb1n1d.js";function E(q,j){return q.replace(/\{\{(\w+)\}\}/g,(z,A)=>{let w=j[A];return w!==void 0&&w!==null?String(w):""})}function F(q){return(j)=>{if(!j.template)return j;let z=q.templates[j.template];if(!z)throw Error(`sently: template "${j.template}" not found`);let A=q.engine(z,j.data??{}),{template:w,data:D,...B}=j;return{...B,html:A}}}export{F as templatePlugin,E as simpleEngine};
2
2
 
3
3
  //# debugId=094B3D1AC9B011AB64756E2164756E21
package/dist/pool/pool.js CHANGED
@@ -1,3 +1,3 @@
1
- import{b as B,c as Q,d as U,e as V}from"../chunk-nj3wmg69.js";import"../chunk-j5m37k1c.js";import{k as K}from"../chunk-nhec1319.js";import{l as N}from"../chunk-cvmk9h1r.js";import"../chunk-kdqxhybf.js";import{t as G}from"../chunk-ntqmn6pe.js";import"../chunk-m2njnxre.js";import"../chunk-32dj0v9g.js";import"../chunk-rhade81v.js";async function W(k){let q=B(k.config),w=await k.createAdapter();await w.connect(k.connectHost,q.port),await Q(w,q);let z=0,D=!0,F=Promise.resolve(),X=k.maxMessages;return{get idle(){return D},get messageCount(){return z},get usable(){return z<X},async send(H){let Y=async()=>{D=!1;try{let Z={...H,attachments:await N(H.attachments)},_=await K(Z,q.dkim),$=await U(w,_);return z+=1,$}finally{D=!0}},J=F.then(Y);return F=J.then(()=>{return},()=>{return}),J},async close(){await F,await V(w)}}}class b{config;maxConnections;maxMessages;createAdapterFn;rateLimiter;connections=[];queue=[];draining=!1;closed=!1;processChain=Promise.resolve();constructor(k,q){if(this.config=k,this.maxConnections=k.maxConnections??5,this.maxMessages=k.maxMessages??100,q?.createAdapter){let w=q.createAdapter;this.createAdapterFn=async()=>w()}else if(k.adapter)this.createAdapterFn=async()=>k.adapter;else throw Error("SMTPPool requires config.adapter or options.createAdapter");if(k.rateDelta!==void 0&&k.rateDelta>0)this.rateLimiter=new G(k.rateDelta,k.rateLimit??1000,q?.now);else this.rateLimiter=null}async send(k){if(this.draining)throw Error("SMTPPool is closing — no new messages accepted");if(this.closed)throw Error("SMTPPool is closed");if(this.rateLimiter)await this.rateLimiter.acquire();return new Promise((q,w)=>{this.queue.push({options:k,resolve:q,reject:w}),this.scheduleProcess()})}scheduleProcess(){this.processChain=this.processChain.then(()=>this.processQueue()).catch(()=>{return})}async verify(){try{let k=await this.spawnConnection();try{return{ok:!0,provider:"smtp-pool"}}finally{await k.close(),this.removeConnection(k)}}catch(k){return{ok:!1,provider:"smtp-pool",message:k instanceof Error?k.message:String(k)}}}async close(){this.draining=!0,await this.drainQueue(),await Promise.allSettled(this.connections.map((k)=>k.close())),this.connections.length=0,this.closed=!0}get connectionCount(){return this.connections.length}get queueSize(){return this.queue.length}async processQueue(){if(this.draining)return;while(this.queue.length>0){let k=this.connections.find((q)=>q.idle&&q.usable);if(k){let q=this.queue.shift();if(!q)break;try{let w=await k.send(q.options);if(q.resolve(w),!k.usable)await k.close(),this.removeConnection(k)}catch(w){q.reject(w),await k.close().catch(()=>{return}),this.removeConnection(k)}continue}if(this.connections.length<this.maxConnections){let q=this.queue.shift();if(!q)break;let w=await this.spawnConnection();try{let z=await w.send(q.options);if(q.resolve(z),!w.usable)await w.close(),this.removeConnection(w)}catch(z){q.reject(z),await w.close().catch(()=>{return}),this.removeConnection(w)}continue}break}}async spawnConnection(){let k=B(this.config),q=await W({config:this.config,maxMessages:this.maxMessages,connectHost:k.host,createAdapter:this.createAdapterFn});return this.connections.push(q),q}removeConnection(k){let q=this.connections.indexOf(k);if(q>=0)this.connections.splice(q,1)}async drainQueue(){while(this.queue.length>0||this.connections.some((k)=>!k.idle))if(await this.processQueue(),this.queue.length>0)await new Promise((k)=>setTimeout(k,10))}}export{b as SMTPPool,G as RateLimiter};
1
+ import{i as B,j as Q,k as U,l as V}from"../chunk-bzw0fdmy.js";import"../chunk-zm7fe0wb.js";import{r as K}from"../chunk-v8eh0n8d.js";import{s as N}from"../chunk-z7j2n9k8.js";import{w as G}from"../chunk-tfxje74k.js";import"../chunk-xdx6ypr5.js";import"../chunk-gkec86xg.js";import"../chunk-9ckbc9mk.js";import"../chunk-50ycrgx6.js";import"../chunk-rszb1n1d.js";async function W(k){let q=B(k.config),w=await k.createAdapter();await w.connect(k.connectHost,q.port),await Q(w,q);let z=0,D=!0,F=Promise.resolve(),X=k.maxMessages;return{get idle(){return D},get messageCount(){return z},get usable(){return z<X},async send(H){let Y=async()=>{D=!1;try{let Z={...H,attachments:await N(H.attachments)},_=await K(Z,q.dkim),$=await U(w,_);return z+=1,$}finally{D=!0}},J=F.then(Y);return F=J.then(()=>{return},()=>{return}),J},async close(){await F,await V(w)}}}class b{config;maxConnections;maxMessages;createAdapterFn;rateLimiter;connections=[];queue=[];draining=!1;closed=!1;processChain=Promise.resolve();constructor(k,q){if(this.config=k,this.maxConnections=k.maxConnections??5,this.maxMessages=k.maxMessages??100,q?.createAdapter){let w=q.createAdapter;this.createAdapterFn=async()=>w()}else if(k.adapter)this.createAdapterFn=async()=>k.adapter;else throw Error("SMTPPool requires config.adapter or options.createAdapter");if(k.rateDelta!==void 0&&k.rateDelta>0)this.rateLimiter=new G(k.rateDelta,k.rateLimit??1000,q?.now);else this.rateLimiter=null}async send(k){if(this.draining)throw Error("SMTPPool is closing — no new messages accepted");if(this.closed)throw Error("SMTPPool is closed");if(this.rateLimiter)await this.rateLimiter.acquire();return new Promise((q,w)=>{this.queue.push({options:k,resolve:q,reject:w}),this.scheduleProcess()})}scheduleProcess(){this.processChain=this.processChain.then(()=>this.processQueue()).catch(()=>{return})}async verify(){try{let k=await this.spawnConnection();try{return{ok:!0,provider:"smtp-pool"}}finally{await k.close(),this.removeConnection(k)}}catch(k){return{ok:!1,provider:"smtp-pool",message:k instanceof Error?k.message:String(k)}}}async close(){this.draining=!0,await this.drainQueue(),await Promise.allSettled(this.connections.map((k)=>k.close())),this.connections.length=0,this.closed=!0}get connectionCount(){return this.connections.length}get queueSize(){return this.queue.length}async processQueue(){if(this.draining)return;while(this.queue.length>0){let k=this.connections.find((q)=>q.idle&&q.usable);if(k){let q=this.queue.shift();if(!q)break;try{let w=await k.send(q.options);if(q.resolve(w),!k.usable)await k.close(),this.removeConnection(k)}catch(w){q.reject(w),await k.close().catch(()=>{return}),this.removeConnection(k)}continue}if(this.connections.length<this.maxConnections){let q=this.queue.shift();if(!q)break;let w=await this.spawnConnection();try{let z=await w.send(q.options);if(q.resolve(z),!w.usable)await w.close(),this.removeConnection(w)}catch(z){q.reject(z),await w.close().catch(()=>{return}),this.removeConnection(w)}continue}break}}async spawnConnection(){let k=B(this.config),q=await W({config:this.config,maxMessages:this.maxMessages,connectHost:k.host,createAdapter:this.createAdapterFn});return this.connections.push(q),q}removeConnection(k){let q=this.connections.indexOf(k);if(q>=0)this.connections.splice(q,1)}async drainQueue(){while(this.queue.length>0||this.connections.some((k)=>!k.idle))if(await this.processQueue(),this.queue.length>0)await new Promise((k)=>setTimeout(k,10))}}export{b as SMTPPool,G as RateLimiter};
2
2
 
3
- //# debugId=1FB3D48CB667401164756E2164756E21
3
+ //# debugId=F95E0F4F4A8DD72A64756E2164756E21
@@ -5,7 +5,7 @@
5
5
  "import { buildMIME } from \"../core/mime.js\";\nimport type { MailOptions, SendResult, SMTPConfig } from \"../core/types.js\";\nimport { resolveAttachments } from \"../transports/resolve-attachments.js\";\nimport {\n closeSMTPSession,\n deliverSMTPMessage,\n openSMTPSession,\n resolveSMTPConfig,\n} from \"../transports/smtp.js\";\n\n/** A single pooled SMTP connection with a persistent session. */\nexport interface PooledConnection {\n /** Send one message over this connection. */\n send(options: MailOptions): Promise<SendResult>;\n /** Whether this connection is idle and available for work. */\n readonly idle: boolean;\n /** Number of messages sent on this connection. */\n readonly messageCount: number;\n /** Whether this connection can accept more messages before recycle. */\n readonly usable: boolean;\n /** Close the connection and end the SMTP session. */\n close(): Promise<void>;\n}\n\n/** Options for creating a pooled connection. */\nexport interface PooledConnectionOptions {\n /** SMTP configuration for the pooled session. */\n config: SMTPConfig;\n /** Maximum messages before this connection is recycled. */\n maxMessages: number;\n /** Hostname to connect to (may differ from config.host for direct MX). */\n connectHost: string;\n /** Factory that creates the socket adapter for this connection. */\n createAdapter: () => Promise<import(\"../core/types.js\").SocketAdapter>;\n}\n\n/**\n * Create a pooled SMTP connection with an open authenticated session.\n */\nexport async function createPooledConnection(\n options: PooledConnectionOptions,\n): Promise<PooledConnection> {\n const config = resolveSMTPConfig(options.config);\n const adapter = await options.createAdapter();\n await adapter.connect(options.connectHost, config.port);\n await openSMTPSession(adapter, config);\n\n let messageCount = 0;\n let idle = true;\n let sendChain: Promise<void> = Promise.resolve();\n\n const maxMessages = options.maxMessages;\n\n return {\n get idle(): boolean {\n return idle;\n },\n get messageCount(): number {\n return messageCount;\n },\n get usable(): boolean {\n return messageCount < maxMessages;\n },\n\n async send(mailOptions: MailOptions): Promise<SendResult> {\n const run = async (): Promise<SendResult> => {\n idle = false;\n try {\n const resolvedOptions = {\n ...mailOptions,\n attachments: await resolveAttachments(mailOptions.attachments),\n };\n const mime = await buildMIME(resolvedOptions, config.dkim);\n const result = await deliverSMTPMessage(adapter, mime);\n messageCount += 1;\n return result;\n } finally {\n idle = true;\n }\n };\n\n const resultPromise = sendChain.then(run);\n sendChain = resultPromise.then(\n () => undefined,\n () => undefined,\n );\n return resultPromise;\n },\n\n async close(): Promise<void> {\n await sendChain;\n await closeSMTPSession(adapter);\n },\n };\n}\n",
6
6
  "/**\n * @module\n * SMTP connection pool with optional rate limiting.\n *\n * @example\n * ```ts\n * import { SMTPPool } from \"sently/pool\";\n * import { NodeAdapter } from \"sently/adapters/node\";\n *\n * const pool = new SMTPPool({\n * host: \"smtp.example.com\",\n * auth: { user: \"you@example.com\", pass: \"secret\" },\n * adapter: new NodeAdapter(),\n * pool: true,\n * maxConnections: 5,\n * });\n *\n * await pool.send({\n * from: \"you@example.com\",\n * to: \"recipient@example.com\",\n * subject: \"Hello\",\n * text: \"Pooled send\",\n * });\n * ```\n */\n\nimport { RateLimiter } from \"../core/rate-limiter.js\";\nimport type {\n MailOptions,\n PoolConfig,\n SendResult,\n SMTPConfig,\n SocketAdapter,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveSMTPConfig } from \"../transports/smtp.js\";\nimport { createPooledConnection, type PooledConnection } from \"./connection.js\";\n\n/** Options for {@link SMTPPool}. */\nexport interface SMTPPoolOptions {\n /** Factory for a new socket adapter per pooled connection. */\n createAdapter?: () => Promise<SocketAdapter> | SocketAdapter;\n /** Injectable clock for rate limiting (testing). */\n now?: () => number;\n}\n\ninterface QueueEntry {\n options: MailOptions;\n resolve: (result: SendResult) => void;\n reject: (error: unknown) => void;\n}\n\n/**\n * SMTP connection pool with optional rate limiting.\n */\nexport class SMTPPool implements Transport {\n /** Resolved SMTP and pool configuration. */\n private readonly config: SMTPConfig & PoolConfig;\n /** Maximum simultaneous pooled connections. */\n private readonly maxConnections: number;\n /** Maximum messages per connection before recycle. */\n private readonly maxMessages: number;\n /** Factory that creates a socket adapter for each new connection. */\n private readonly createAdapterFn: () => Promise<SocketAdapter>;\n /** Optional token-bucket rate limiter, or null when disabled. */\n private readonly rateLimiter: RateLimiter | null;\n /** Active pooled SMTP connections. */\n private readonly connections: PooledConnection[] = [];\n /** Pending send operations waiting for a connection. */\n private readonly queue: QueueEntry[] = [];\n /** True while {@link close} is draining; rejects new sends. */\n private draining = false;\n /** True after the pool has fully closed. */\n private closed = false;\n /** Serializes queue processing to avoid concurrent drain races. */\n private processChain: Promise<void> = Promise.resolve();\n\n /** Creates an SMTP connection pool. */\n constructor(config: SMTPConfig & PoolConfig, options?: SMTPPoolOptions) {\n this.config = config;\n this.maxConnections = config.maxConnections ?? 5;\n this.maxMessages = config.maxMessages ?? 100;\n\n if (options?.createAdapter) {\n const factory = options.createAdapter;\n this.createAdapterFn = async () => factory();\n } else if (config.adapter) {\n this.createAdapterFn = async () => config.adapter as SocketAdapter;\n } else {\n throw new Error(\"SMTPPool requires config.adapter or options.createAdapter\");\n }\n\n if (config.rateDelta !== undefined && config.rateDelta > 0) {\n this.rateLimiter = new RateLimiter(config.rateDelta, config.rateLimit ?? 1000, options?.now);\n } else {\n this.rateLimiter = null;\n }\n }\n\n /** Sends a message through the pool. */\n async send(options: MailOptions): Promise<SendResult> {\n if (this.draining) {\n throw new Error(\"SMTPPool is closing — no new messages accepted\");\n }\n if (this.closed) {\n throw new Error(\"SMTPPool is closed\");\n }\n if (this.rateLimiter) {\n await this.rateLimiter.acquire();\n }\n return new Promise<SendResult>((resolve, reject) => {\n this.queue.push({ options, resolve, reject });\n this.scheduleProcess();\n });\n }\n\n /** Schedules asynchronous processing of the send queue. */\n private scheduleProcess(): void {\n this.processChain = this.processChain.then(() => this.processQueue()).catch(() => undefined);\n }\n\n /** Verifies connectivity using a temporary connection. */\n async verify(): Promise<VerifyResult> {\n try {\n const conn = await this.spawnConnection();\n try {\n return { ok: true, provider: \"smtp-pool\" };\n } finally {\n await conn.close();\n this.removeConnection(conn);\n }\n } catch (err) {\n return {\n ok: false,\n provider: \"smtp-pool\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n /** Drains the queue and closes all connections. */\n async close(): Promise<void> {\n this.draining = true;\n await this.drainQueue();\n await Promise.allSettled(this.connections.map((c) => c.close()));\n this.connections.length = 0;\n this.closed = true;\n }\n\n /** Current number of open pooled connections. */\n get connectionCount(): number {\n return this.connections.length;\n }\n\n /** Number of messages waiting in the send queue. */\n get queueSize(): number {\n return this.queue.length;\n }\n\n /** Dispatches queued messages to idle or newly spawned connections. */\n private async processQueue(): Promise<void> {\n if (this.draining) {\n return;\n }\n\n while (this.queue.length > 0) {\n const idleConn = this.connections.find((c) => c.idle && c.usable);\n if (idleConn) {\n const entry = this.queue.shift();\n if (!entry) {\n break;\n }\n try {\n const result = await idleConn.send(entry.options);\n entry.resolve(result);\n if (!idleConn.usable) {\n await idleConn.close();\n this.removeConnection(idleConn);\n }\n } catch (err) {\n entry.reject(err);\n await idleConn.close().catch(() => undefined);\n this.removeConnection(idleConn);\n }\n continue;\n }\n\n if (this.connections.length < this.maxConnections) {\n const entry = this.queue.shift();\n if (!entry) {\n break;\n }\n const conn = await this.spawnConnection();\n try {\n const result = await conn.send(entry.options);\n entry.resolve(result);\n if (!conn.usable) {\n await conn.close();\n this.removeConnection(conn);\n }\n } catch (err) {\n entry.reject(err);\n await conn.close().catch(() => undefined);\n this.removeConnection(conn);\n }\n continue;\n }\n\n break;\n }\n }\n\n /** Opens a new authenticated pooled SMTP connection. */\n private async spawnConnection(): Promise<PooledConnection> {\n const resolved = resolveSMTPConfig(this.config);\n const conn = await createPooledConnection({\n config: this.config,\n maxMessages: this.maxMessages,\n connectHost: resolved.host,\n createAdapter: this.createAdapterFn,\n });\n this.connections.push(conn);\n return conn;\n }\n\n /** Removes a connection from the active pool list. */\n private removeConnection(conn: PooledConnection): void {\n const index = this.connections.indexOf(conn);\n if (index >= 0) {\n this.connections.splice(index, 1);\n }\n }\n\n /** Waits until the send queue and in-flight work are fully drained. */\n private async drainQueue(): Promise<void> {\n while (this.queue.length > 0 || this.connections.some((c) => !c.idle)) {\n await this.processQueue();\n if (this.queue.length > 0) {\n await new Promise((r) => setTimeout(r, 10));\n }\n }\n }\n}\n\n/** @internal Exposed for deterministic rate limiter tests. */\nexport { RateLimiter } from \"../core/rate-limiter.js\";\n"
7
7
  ],
8
- "mappings": "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",
9
- "debugId": "1FB3D48CB667401164756E2164756E21",
8
+ "mappings": "oZAuCA,UAAsB,JAAsB,JAC1C,HAC2B,JAC3B,DAAM,HAAS,HAAkB,HAAQ,MAAM,EACzC,EAAU,MAAM,EAAQ,cAAc,EAC5C,MAAM,EAAQ,QAAQ,EAAQ,YAAa,EAAO,IAAI,EACtD,MAAM,EAAgB,EAAS,CAAM,EAErC,IAAI,EAAe,EACf,EAAO,GACP,EAA2B,QAAQ,QAAQ,EAEzC,EAAc,EAAQ,YAE5B,MAAO,IACD,KAAI,EAAY,CAClB,OAAO,MAEL,aAAY,EAAW,CACzB,OAAO,MAEL,OAAM,EAAY,CACpB,OAAO,EAAe,QAGlB,KAAI,CAAC,EAA+C,CACxD,IAAM,EAAM,SAAiC,CAC3C,EAAO,GACP,GAAI,CACF,IAAM,EAAkB,IACnB,EACH,YAAa,MAAM,EAAmB,EAAY,WAAW,CAC/D,EACM,EAAO,MAAM,EAAU,EAAiB,EAAO,IAAI,EACnD,EAAS,MAAM,EAAmB,EAAS,CAAI,EAErD,OADA,GAAgB,EACT,SACP,CACA,EAAO,KAIL,EAAgB,EAAU,KAAK,CAAG,EAKxC,OAJA,EAAY,EAAc,KACxB,IAAG,CAAG,QACN,IAAG,CAAG,OACR,EACO,QAGH,MAAK,EAAkB,CAC3B,MAAM,EACN,MAAM,EAAiB,CAAO,EAElC,ECrCK,MAAM,CAA8B,CAExB,OAEA,eAEA,YAEA,gBAEA,YAEA,YAAkC,CAAC,EAEnC,MAAsB,CAAC,EAEhC,SAAW,GAEX,OAAS,GAET,aAA8B,QAAQ,QAAQ,EAGtD,WAAW,CAAC,EAAiC,EAA2B,CAKtE,GAJA,KAAK,OAAS,EACd,KAAK,eAAiB,EAAO,gBAAkB,EAC/C,KAAK,YAAc,EAAO,aAAe,IAErC,GAAS,cAAe,CAC1B,IAAM,EAAU,EAAQ,cACxB,KAAK,gBAAkB,SAAY,EAAQ,EACtC,QAAI,EAAO,QAChB,KAAK,gBAAkB,SAAY,EAAO,QAE1C,WAAU,MAAM,2DAA2D,EAG7E,GAAI,EAAO,YAAc,QAAa,EAAO,UAAY,EACvD,KAAK,YAAc,IAAI,EAAY,EAAO,UAAW,EAAO,WAAa,KAAM,GAAS,GAAG,EAE3F,UAAK,YAAc,UAKjB,KAAI,CAAC,EAA2C,CACpD,GAAI,KAAK,SACP,MAAU,MAAM,gDAA+C,EAEjE,GAAI,KAAK,OACP,MAAU,MAAM,oBAAoB,EAEtC,GAAI,KAAK,YACP,MAAM,KAAK,YAAY,QAAQ,EAEjC,OAAO,IAAI,QAAoB,CAAC,EAAS,IAAW,CAClD,KAAK,MAAM,KAAK,CAAE,UAAS,UAAS,QAAO,CAAC,EAC5C,KAAK,gBAAgB,EACtB,EAIK,eAAe,EAAS,CAC9B,KAAK,aAAe,KAAK,aAAa,KAAK,IAAM,KAAK,aAAa,CAAC,EAAE,MAAM,IAAG,CAAG,OAAS,OAIvF,OAAM,EAA0B,CACpC,GAAI,CACF,IAAM,EAAO,MAAM,KAAK,gBAAgB,EACxC,GAAI,CACF,MAAO,CAAE,GAAI,GAAM,SAAU,WAAY,SACzC,CACA,MAAM,EAAK,MAAM,EACjB,KAAK,iBAAiB,CAAI,GAE5B,MAAO,EAAK,CACZ,MAAO,CACL,GAAI,GACJ,SAAU,YACV,QAAS,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,CAC1D,QAKE,MAAK,EAAkB,CAC3B,KAAK,SAAW,GAChB,MAAM,KAAK,WAAW,EACtB,MAAM,QAAQ,WAAW,KAAK,YAAY,IAAI,CAAC,IAAM,EAAE,MAAM,CAAC,CAAC,EAC/D,KAAK,YAAY,OAAS,EAC1B,KAAK,OAAS,MAIZ,gBAAe,EAAW,CAC5B,OAAO,KAAK,YAAY,UAItB,UAAS,EAAW,CACtB,OAAO,KAAK,MAAM,YAIN,aAAY,EAAkB,CAC1C,GAAI,KAAK,SACP,OAGF,MAAO,KAAK,MAAM,OAAS,EAAG,CAC5B,IAAM,EAAW,KAAK,YAAY,KAAK,CAAC,IAAM,EAAE,MAAQ,EAAE,MAAM,EAChE,GAAI,EAAU,CACZ,IAAM,EAAQ,KAAK,MAAM,MAAM,EAC/B,GAAI,CAAC,EACH,MAEF,GAAI,CACF,IAAM,EAAS,MAAM,EAAS,KAAK,EAAM,OAAO,EAEhD,GADA,EAAM,QAAQ,CAAM,EAChB,CAAC,EAAS,OACZ,MAAM,EAAS,MAAM,EACrB,KAAK,iBAAiB,CAAQ,EAEhC,MAAO,EAAK,CACZ,EAAM,OAAO,CAAG,EAChB,MAAM,EAAS,MAAM,EAAE,MAAM,IAAG,CAAG,OAAS,EAC5C,KAAK,iBAAiB,CAAQ,EAEhC,SAGF,GAAI,KAAK,YAAY,OAAS,KAAK,eAAgB,CACjD,IAAM,EAAQ,KAAK,MAAM,MAAM,EAC/B,GAAI,CAAC,EACH,MAEF,IAAM,EAAO,MAAM,KAAK,gBAAgB,EACxC,GAAI,CACF,IAAM,EAAS,MAAM,EAAK,KAAK,EAAM,OAAO,EAE5C,GADA,EAAM,QAAQ,CAAM,EAChB,CAAC,EAAK,OACR,MAAM,EAAK,MAAM,EACjB,KAAK,iBAAiB,CAAI,EAE5B,MAAO,EAAK,CACZ,EAAM,OAAO,CAAG,EAChB,MAAM,EAAK,MAAM,EAAE,MAAM,IAAG,CAAG,OAAS,EACxC,KAAK,iBAAiB,CAAI,EAE5B,SAGF,YAKU,gBAAe,EAA8B,CACzD,IAAM,EAAW,EAAkB,KAAK,MAAM,EACxC,EAAO,MAAM,EAAuB,CACxC,OAAQ,KAAK,OACb,YAAa,KAAK,YAClB,YAAa,EAAS,KACtB,cAAe,KAAK,eACtB,CAAC,EAED,OADA,KAAK,YAAY,KAAK,CAAI,EACnB,EAID,gBAAgB,CAAC,EAA8B,CACrD,IAAM,EAAQ,KAAK,YAAY,QAAQ,CAAI,EAC3C,GAAI,GAAS,EACX,KAAK,YAAY,OAAO,EAAO,CAAC,OAKtB,WAAU,EAAkB,CACxC,MAAO,KAAK,MAAM,OAAS,GAAK,KAAK,YAAY,KAAK,CAAC,IAAM,CAAC,EAAE,IAAI,EAElE,GADA,MAAM,KAAK,aAAa,EACpB,KAAK,MAAM,OAAS,EACtB,MAAM,IAAI,QAAQ,CAAC,IAAM,WAAW,EAAG,EAAE,CAAC,EAIlD",
9
+ "debugId": "F95E0F4F4A8DD72A64756E2164756E21",
10
10
  "names": []
11
11
  }
package/dist/react.js CHANGED
@@ -1,3 +1,4 @@
1
- import"./chunk-rhade81v.js";export{k as reactPlugin};
2
-
3
- //# debugId=AB3248C3DD5B9EFC64756E2164756E21
1
+ export {
2
+
3
+ reactPlugin,
4
+ } from "./plugins/react.js";
@@ -1,10 +1,10 @@
1
+ import { SentlyError } from "../core/errors.js";
1
2
  import type { BrevoConfig, MailOptions, SendResult, Transport, VerifyResult } from "../core/types.js";
2
3
  /** Error thrown when the Brevo API returns a non-success response. */
3
- export declare class BrevoError extends Error {
4
+ export declare class BrevoError extends SentlyError {
4
5
  readonly statusCode: number;
5
- readonly code: string;
6
6
  /** Creates a Brevo API error with status code and error code. */
7
- constructor(message: string, statusCode: number, code: string);
7
+ constructor(message: string, statusCode: number, apiCode: string);
8
8
  }
9
9
  /**
10
10
  * Brevo HTTP API transport.
@@ -1,3 +1,3 @@
1
- import{l as M}from"../chunk-cvmk9h1r.js";import{n as F,p as L}from"../chunk-kdqxhybf.js";import{G as K}from"../chunk-32dj0v9g.js";import"../chunk-rhade81v.js";class N extends Error{statusCode;code;constructor(q,w,C){super(q);this.statusCode=w;this.code=C;this.name="BrevoError"}}function H(q){return F(q).map((w)=>({email:w.address,...w.name?{name:w.name}:{}}))}class R{apiKey;constructor(q){this.apiKey=q.apiKey}async send(q){let w=await M(q.attachments),C=F(q.from)[0],Q={sender:C?{email:C.address,...C.name?{name:C.name}:{}}:{email:""},to:H(q.to),subject:q.subject,...q.cc?{cc:H(q.cc)}:{},...q.bcc?{bcc:H(q.bcc)}:{},...q.replyTo?{replyTo:(()=>{let z=F(q.replyTo)[0];return z?{email:z.address,...z.name?{name:z.name}:{}}:void 0})()}:{},...q.html?{htmlContent:q.html}:{},...q.text?{textContent:q.text}:{},...w.length>0?{attachment:w.map((z)=>({name:z.filename,content:z.content instanceof Uint8Array?K(z.content).replace(/\r\n/g,""):z.content}))}:{}},G=await fetch("https://api.brevo.com/v3/smtp/email",{method:"POST",headers:{"api-key":this.apiKey,"Content-Type":"application/json"},body:JSON.stringify(Q)}),D=await G.json();if(!G.ok)throw new N(D.message??"Brevo API error",G.status,D.code??"");let J=L(q.to);return{messageId:D.messageId??"",accepted:J,rejected:[],response:D.messageId??"sent",envelope:{from:C?.address??"",to:J}}}async verify(){try{let q=await fetch("https://api.brevo.com/v3/account",{headers:{"api-key":this.apiKey}}),w=await q.json();if(!q.ok)return{ok:!1,provider:"brevo",message:w.message??`HTTP ${q.status}`};return{ok:!0,provider:"brevo",...w.companyName?{message:w.companyName}:{}}}catch(q){return{ok:!1,provider:"brevo",message:q instanceof Error?q.message:String(q)}}}}export{R as BrevoTransport,N as BrevoError};
1
+ import{s as R}from"../chunk-z7j2n9k8.js";import{A as M,y as G}from"../chunk-xdx6ypr5.js";import{O as N,P as Q}from"../chunk-9ckbc9mk.js";import{R as L}from"../chunk-50ycrgx6.js";import"../chunk-rszb1n1d.js";class U extends N{statusCode;constructor(q,w,D){super(q,Q(w),{statusCode:w,provider:"brevo"});this.statusCode=w;this.name="BrevoError",this.code=D}}function J(q){return G(q).map((w)=>({email:w.address,...w.name?{name:w.name}:{}}))}class X{apiKey;constructor(q){this.apiKey=q.apiKey}async send(q){let w=await R(q.attachments),D=G(q.from)[0],W={sender:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},to:J(q.to),subject:q.subject,...q.cc?{cc:J(q.cc)}:{},...q.bcc?{bcc:J(q.bcc)}:{},...q.replyTo?{replyTo:(()=>{let z=G(q.replyTo)[0];return z?{email:z.address,...z.name?{name:z.name}:{}}:void 0})()}:{},...q.html?{htmlContent:q.html}:{},...q.text?{textContent:q.text}:{},...w.length>0?{attachment:w.map((z)=>({name:z.filename,content:z.content instanceof Uint8Array?L(z.content).replace(/\r\n/g,""):z.content}))}:{}},H=await fetch("https://api.brevo.com/v3/smtp/email",{method:"POST",headers:{"api-key":this.apiKey,"Content-Type":"application/json"},body:JSON.stringify(W)}),F=await H.json();if(!H.ok)throw new U(F.message??"Brevo API error",H.status,F.code??"");let K=M(q.to);return{messageId:F.messageId??"",accepted:K,rejected:[],response:F.messageId??"sent",envelope:{from:D?.address??"",to:K}}}async verify(){try{let q=await fetch("https://api.brevo.com/v3/account",{headers:{"api-key":this.apiKey}}),w=await q.json();if(!q.ok)return{ok:!1,provider:"brevo",message:w.message??`HTTP ${q.status}`};return{ok:!0,provider:"brevo",...w.companyName?{message:w.companyName}:{}}}catch(q){return{ok:!1,provider:"brevo",message:q instanceof Error?q.message:String(q)}}}}export{X as BrevoTransport,U as BrevoError};
2
2
 
3
- //# debugId=3E1FA1A055C8575B64756E2164756E21
3
+ //# debugId=E8FDCFC46678915B64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/transports/brevo.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * @module\n * Brevo (formerly Sendinblue) HTTP transport for sently.\n *\n * @example\n * ```ts\n * import { BrevoTransport } from \"sently/transports/brevo\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new BrevoTransport({ apiKey: \"xkeysib-...\" }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport type {\n BrevoConfig,\n MailOptions,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Error thrown when the Brevo API returns a non-success response. */\nexport class BrevoError extends Error {\n /** Creates a Brevo API error with status code and error code. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly code: string,\n ) {\n super(message);\n this.name = \"BrevoError\";\n }\n}\n\nfunction toAddressObjects(input: MailOptions[\"to\"]): Array<{ email: string; name?: string }> {\n return parseAddresses(input).map((addr) => ({\n email: addr.address,\n ...(addr.name ? { name: addr.name } : {}),\n }));\n}\n\n/**\n * Brevo HTTP API transport.\n */\nexport class BrevoTransport implements Transport {\n /** Brevo API key used for Authorization. */\n private readonly apiKey: string;\n\n /** Creates a Brevo transport with the given API key. */\n constructor(config: BrevoConfig) {\n this.apiKey = config.apiKey;\n }\n\n /** Sends an email via the Brevo HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n\n const body: Record<string, unknown> = {\n sender: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n to: toAddressObjects(options.to),\n subject: options.subject,\n ...(options.cc ? { cc: toAddressObjects(options.cc) } : {}),\n ...(options.bcc ? { bcc: toAddressObjects(options.bcc) } : {}),\n ...(options.replyTo\n ? {\n replyTo: (() => {\n const reply = parseAddresses(options.replyTo as MailOptions[\"to\"])[0];\n return reply\n ? { email: reply.address, ...(reply.name ? { name: reply.name } : {}) }\n : undefined;\n })(),\n }\n : {}),\n ...(options.html ? { htmlContent: options.html } : {}),\n ...(options.text ? { textContent: options.text } : {}),\n ...(attachments.length > 0\n ? {\n attachment: attachments.map((att) => ({\n name: att.filename,\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n })),\n }\n : {}),\n };\n\n const response = await fetch(\"https://api.brevo.com/v3/smtp/email\", {\n method: \"POST\",\n headers: {\n \"api-key\": this.apiKey,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as {\n messageId?: string;\n message?: string;\n code?: string;\n };\n\n if (!response.ok) {\n throw new BrevoError(\n payload.message ?? \"Brevo API error\",\n response.status,\n payload.code ?? \"\",\n );\n }\n\n const toEmails = extractEmails(options.to);\n return {\n messageId: payload.messageId ?? \"\",\n accepted: toEmails,\n rejected: [],\n response: payload.messageId ?? \"sent\",\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies the Brevo API key by fetching account info. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(\"https://api.brevo.com/v3/account\", {\n headers: {\n \"api-key\": this.apiKey,\n },\n });\n\n const payload = (await response.json()) as { companyName?: string; message?: string };\n\n if (!response.ok) {\n return {\n ok: false,\n provider: \"brevo\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return {\n ok: true,\n provider: \"brevo\",\n ...(payload.companyName ? { message: payload.companyName } : {}),\n };\n } catch (err) {\n return {\n ok: false,\n provider: \"brevo\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
5
+ "/**\n * @module\n * Brevo (formerly Sendinblue) HTTP transport for sently.\n *\n * @example\n * ```ts\n * import { BrevoTransport } from \"sently/transports/brevo\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new BrevoTransport({ apiKey: \"xkeysib-...\" }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type {\n BrevoConfig,\n MailOptions,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Error thrown when the Brevo API returns a non-success response. */\nexport class BrevoError extends SentlyError {\n /** Creates a Brevo API error with status code and error code. */\n constructor(\n message: string,\n public readonly statusCode: number,\n apiCode: string,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"brevo\",\n });\n this.name = \"BrevoError\";\n this.code = apiCode;\n }\n}\n\nfunction toAddressObjects(input: MailOptions[\"to\"]): Array<{ email: string; name?: string }> {\n return parseAddresses(input).map((addr) => ({\n email: addr.address,\n ...(addr.name ? { name: addr.name } : {}),\n }));\n}\n\n/**\n * Brevo HTTP API transport.\n */\nexport class BrevoTransport implements Transport {\n /** Brevo API key used for Authorization. */\n private readonly apiKey: string;\n\n /** Creates a Brevo transport with the given API key. */\n constructor(config: BrevoConfig) {\n this.apiKey = config.apiKey;\n }\n\n /** Sends an email via the Brevo HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n\n const body: Record<string, unknown> = {\n sender: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n to: toAddressObjects(options.to),\n subject: options.subject,\n ...(options.cc ? { cc: toAddressObjects(options.cc) } : {}),\n ...(options.bcc ? { bcc: toAddressObjects(options.bcc) } : {}),\n ...(options.replyTo\n ? {\n replyTo: (() => {\n const reply = parseAddresses(options.replyTo as MailOptions[\"to\"])[0];\n return reply\n ? { email: reply.address, ...(reply.name ? { name: reply.name } : {}) }\n : undefined;\n })(),\n }\n : {}),\n ...(options.html ? { htmlContent: options.html } : {}),\n ...(options.text ? { textContent: options.text } : {}),\n ...(attachments.length > 0\n ? {\n attachment: attachments.map((att) => ({\n name: att.filename,\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n })),\n }\n : {}),\n };\n\n const response = await fetch(\"https://api.brevo.com/v3/smtp/email\", {\n method: \"POST\",\n headers: {\n \"api-key\": this.apiKey,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as {\n messageId?: string;\n message?: string;\n code?: string;\n };\n\n if (!response.ok) {\n throw new BrevoError(\n payload.message ?? \"Brevo API error\",\n response.status,\n payload.code ?? \"\",\n );\n }\n\n const toEmails = extractEmails(options.to);\n return {\n messageId: payload.messageId ?? \"\",\n accepted: toEmails,\n rejected: [],\n response: payload.messageId ?? \"sent\",\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies the Brevo API key by fetching account info. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(\"https://api.brevo.com/v3/account\", {\n headers: {\n \"api-key\": this.apiKey,\n },\n });\n\n const payload = (await response.json()) as { companyName?: string; message?: string };\n\n if (!response.ok) {\n return {\n ok: false,\n provider: \"brevo\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return {\n ok: true,\n provider: \"brevo\",\n ...(payload.companyName ? { message: payload.companyName } : {}),\n };\n } catch (err) {\n return {\n ok: false,\n provider: \"brevo\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
6
6
  ],
7
- "mappings": "8KA0BO,CAAM,KAAmB,AAAM,CAIlB,WACA,KAHlB,WAAW,CACT,EACgB,EACA,EAChB,CACA,MAAM,CAAO,EAHG,kBACA,YAGhB,KAAK,KAAO,aAEhB,CAEA,SAAS,CAAgB,CAAC,EAAmE,CAC3F,OAAO,EAAe,CAAK,EAAE,IAAI,CAAC,KAAU,CAC1C,MAAO,EAAK,WACR,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CACzC,EAAE,EAMG,MAAM,CAAoC,CAE9B,OAGjB,WAAW,CAAC,EAAqB,CAC/B,KAAK,OAAS,EAAO,YAIjB,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAO,EAAe,EAAQ,IAAI,EAAE,GAEpC,EAAgC,CACpC,OAAQ,EACJ,CAAE,MAAO,EAAK,WAAa,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CAAG,EACjE,CAAE,MAAO,EAAG,EAChB,GAAI,EAAiB,EAAQ,EAAE,EAC/B,QAAS,EAAQ,WACb,EAAQ,GAAK,CAAE,GAAI,EAAiB,EAAQ,EAAE,CAAE,EAAI,CAAC,KACrD,EAAQ,IAAM,CAAE,IAAK,EAAiB,EAAQ,GAAG,CAAE,EAAI,CAAC,KACxD,EAAQ,QACR,CACE,SAAU,IAAM,CACd,IAAM,EAAQ,EAAe,EAAQ,OAA4B,EAAE,GACnE,OAAO,EACH,CAAE,MAAO,EAAM,WAAa,EAAM,KAAO,CAAE,KAAM,EAAM,IAAK,EAAI,CAAC,CAAG,EACpE,SACH,CACL,EACA,CAAC,KACD,EAAQ,KAAO,CAAE,YAAa,EAAQ,IAAK,EAAI,CAAC,KAChD,EAAQ,KAAO,CAAE,YAAa,EAAQ,IAAK,EAAI,CAAC,KAChD,EAAY,OAAS,EACrB,CACE,WAAY,EAAY,IAAI,CAAC,KAAS,CACpC,KAAM,EAAI,SACV,QACE,EAAI,mBAAmB,WACnB,EAAa,EAAI,OAAO,EAAE,QAAQ,QAAS,EAAE,EAC7C,EAAI,OACZ,EAAE,CACJ,EACA,CAAC,CACP,EAEM,EAAW,MAAM,MAAM,sCAAuC,CAClE,OAAQ,OACR,QAAS,CACP,UAAW,KAAK,OAChB,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAMrC,GAAI,CAAC,EAAS,GACZ,MAAM,IAAI,EACR,EAAQ,SAAW,kBACnB,EAAS,OACT,EAAQ,MAAQ,EAClB,EAGF,IAAM,EAAW,EAAc,EAAQ,EAAE,EACzC,MAAO,CACL,UAAW,EAAQ,WAAa,GAChC,SAAU,EACV,SAAU,CAAC,EACX,SAAU,EAAQ,WAAa,OAC/B,SAAU,CACR,KAAM,GAAM,SAAW,GACvB,GAAI,CACN,CACF,OAII,OAAM,EAA0B,CACpC,GAAI,CACF,IAAM,EAAW,MAAM,MAAM,mCAAoC,CAC/D,QAAS,CACP,UAAW,KAAK,MAClB,CACF,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAErC,GAAI,CAAC,EAAS,GACZ,MAAO,CACL,GAAI,GACJ,SAAU,QACV,QAAS,EAAQ,SAAW,QAAQ,EAAS,QAC/C,EAGF,MAAO,CACL,GAAI,GACJ,SAAU,WACN,EAAQ,YAAc,CAAE,QAAS,EAAQ,WAAY,EAAI,CAAC,CAChE,EACA,MAAO,EAAK,CACZ,MAAO,CACL,GAAI,GACJ,SAAU,QACV,QAAS,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,CAC1D,GAGN",
8
- "debugId": "3E1FA1A055C8575B64756E2164756E21",
7
+ "mappings": "mOA2BO,CAAM,KAAmB,JAAY,JAIxB,WAFlB,WAAW,CACT,EACgB,EAChB,EACA,CACA,MAAM,EAAS,EAAuB,CAAU,EAAG,CACjD,aACA,SAAU,OACZ,CAAC,EANe,kBAOhB,KAAK,KAAO,aACZ,KAAK,KAAO,EAEhB,CAEA,SAAS,CAAgB,CAAC,EAAmE,CAC3F,OAAO,EAAe,CAAK,EAAE,IAAI,CAAC,KAAU,CAC1C,MAAO,EAAK,WACR,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CACzC,EAAE,EAMG,MAAM,CAAoC,CAE9B,OAGjB,WAAW,CAAC,EAAqB,CAC/B,KAAK,OAAS,EAAO,YAIjB,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAO,EAAe,EAAQ,IAAI,EAAE,GAEpC,EAAgC,CACpC,OAAQ,EACJ,CAAE,MAAO,EAAK,WAAa,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CAAG,EACjE,CAAE,MAAO,EAAG,EAChB,GAAI,EAAiB,EAAQ,EAAE,EAC/B,QAAS,EAAQ,WACb,EAAQ,GAAK,CAAE,GAAI,EAAiB,EAAQ,EAAE,CAAE,EAAI,CAAC,KACrD,EAAQ,IAAM,CAAE,IAAK,EAAiB,EAAQ,GAAG,CAAE,EAAI,CAAC,KACxD,EAAQ,QACR,CACE,SAAU,IAAM,CACd,IAAM,EAAQ,EAAe,EAAQ,OAA4B,EAAE,GACnE,OAAO,EACH,CAAE,MAAO,EAAM,WAAa,EAAM,KAAO,CAAE,KAAM,EAAM,IAAK,EAAI,CAAC,CAAG,EACpE,SACH,CACL,EACA,CAAC,KACD,EAAQ,KAAO,CAAE,YAAa,EAAQ,IAAK,EAAI,CAAC,KAChD,EAAQ,KAAO,CAAE,YAAa,EAAQ,IAAK,EAAI,CAAC,KAChD,EAAY,OAAS,EACrB,CACE,WAAY,EAAY,IAAI,CAAC,KAAS,CACpC,KAAM,EAAI,SACV,QACE,EAAI,mBAAmB,WACnB,EAAa,EAAI,OAAO,EAAE,QAAQ,QAAS,EAAE,EAC7C,EAAI,OACZ,EAAE,CACJ,EACA,CAAC,CACP,EAEM,EAAW,MAAM,MAAM,sCAAuC,CAClE,OAAQ,OACR,QAAS,CACP,UAAW,KAAK,OAChB,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAMrC,GAAI,CAAC,EAAS,GACZ,MAAM,IAAI,EACR,EAAQ,SAAW,kBACnB,EAAS,OACT,EAAQ,MAAQ,EAClB,EAGF,IAAM,EAAW,EAAc,EAAQ,EAAE,EACzC,MAAO,CACL,UAAW,EAAQ,WAAa,GAChC,SAAU,EACV,SAAU,CAAC,EACX,SAAU,EAAQ,WAAa,OAC/B,SAAU,CACR,KAAM,GAAM,SAAW,GACvB,GAAI,CACN,CACF,OAII,OAAM,EAA0B,CACpC,GAAI,CACF,IAAM,EAAW,MAAM,MAAM,mCAAoC,CAC/D,QAAS,CACP,UAAW,KAAK,MAClB,CACF,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAErC,GAAI,CAAC,EAAS,GACZ,MAAO,CACL,GAAI,GACJ,SAAU,QACV,QAAS,EAAQ,SAAW,QAAQ,EAAS,QAC/C,EAGF,MAAO,CACL,GAAI,GACJ,SAAU,WACN,EAAQ,YAAc,CAAE,QAAS,EAAQ,WAAY,EAAI,CAAC,CAChE,EACA,MAAO,EAAK,CACZ,MAAO,CACL,GAAI,GACJ,SAAU,QACV,QAAS,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,CAC1D,GAGN",
8
+ "debugId": "E8FDCFC46678915B64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,6 +1,7 @@
1
+ import { SentlyError } from "../core/errors.js";
1
2
  import type { MailgunConfig, MailOptions, SendResult, Transport, VerifyResult } from "../core/types.js";
2
3
  /** Error thrown when the Mailgun API returns a non-success response. */
3
- export declare class MailgunError extends Error {
4
+ export declare class MailgunError extends SentlyError {
4
5
  readonly statusCode: number;
5
6
  readonly apiError: unknown;
6
7
  /** Creates a Mailgun API error with status code and response payload. */
@@ -1,3 +1,3 @@
1
- import{l as Q}from"../chunk-cvmk9h1r.js";import{n as D,o as F,p as O}from"../chunk-kdqxhybf.js";import{G as L}from"../chunk-32dj0v9g.js";import"../chunk-rhade81v.js";class R extends Error{statusCode;apiError;constructor(q,C,G){super(q);this.statusCode=C;this.apiError=G;this.name="MailgunError"}}class X{apiKey;domain;baseUrl;constructor(q){this.apiKey=q.apiKey,this.domain=q.domain,this.baseUrl=q.region==="eu"?"https://api.eu.mailgun.net":"https://api.mailgun.net"}async send(q){let C=await Q(q.attachments),G=D(q.from)[0],w=new FormData;if(w.append("from",G?F(G):""),w.append("to",D(q.to).map(F).join(", ")),q.cc)w.append("cc",D(q.cc).map(F).join(", "));if(q.bcc)w.append("bcc",D(q.bcc).map(F).join(", "));if(w.append("subject",q.subject),q.text)w.append("text",q.text);if(q.html)w.append("html",q.html);if(q.replyTo){let z=D(q.replyTo)[0];if(z)w.append("h:Reply-To",F(z))}for(let z of C){let U=z.content instanceof Uint8Array?z.content:new TextEncoder().encode(String(z.content??"")),W=new Blob([new Uint8Array(U)],{type:z.contentType??"application/octet-stream"});w.append("attachment",W,z.filename)}let S=L(`api:${this.apiKey}`).replace(/\r\n/g,""),K=await fetch(`${this.baseUrl}/v3/${this.domain}/messages`,{method:"POST",headers:{Authorization:`Basic ${S}`},body:w}),J=await K.json();if(!K.ok)throw new R(J.message??"Mailgun API error",K.status,J);let N=O(q.to);return{messageId:J.id??"",accepted:N,rejected:[],response:J.message??"queued",envelope:{from:G?.address??"",to:N}}}async verify(){try{let q=L(`api:${this.apiKey}`).replace(/\r\n/g,""),C=await fetch(`${this.baseUrl}/v3/domains`,{headers:{Authorization:`Basic ${q}`}});if(!C.ok)return{ok:!1,provider:"mailgun",message:(await C.json().catch(()=>({}))).message??`HTTP ${C.status}`};return{ok:!0,provider:"mailgun",message:"API key is valid"}}catch(q){return{ok:!1,provider:"mailgun",message:q instanceof Error?q.message:String(q)}}}}export{X as MailgunTransport,R as MailgunError};
1
+ import{s as W}from"../chunk-z7j2n9k8.js";import{A as Q,y as G,z as J}from"../chunk-xdx6ypr5.js";import{O as R,P as U}from"../chunk-9ckbc9mk.js";import{R as N}from"../chunk-50ycrgx6.js";import"../chunk-rszb1n1d.js";class X extends R{statusCode;apiError;constructor(q,z,F){super(q,U(z),{statusCode:z,provider:"mailgun",cause:F});this.statusCode=z;this.apiError=F;this.name="MailgunError"}}class ${apiKey;domain;baseUrl;constructor(q){this.apiKey=q.apiKey,this.domain=q.domain,this.baseUrl=q.region==="eu"?"https://api.eu.mailgun.net":"https://api.mailgun.net"}async send(q){let z=await W(q.attachments),F=G(q.from)[0],w=new FormData;if(w.append("from",F?J(F):""),w.append("to",G(q.to).map(J).join(", ")),q.cc)w.append("cc",G(q.cc).map(J).join(", "));if(q.bcc)w.append("bcc",G(q.bcc).map(J).join(", "));if(w.append("subject",q.subject),q.text)w.append("text",q.text);if(q.html)w.append("html",q.html);if(q.replyTo){let D=G(q.replyTo)[0];if(D)w.append("h:Reply-To",J(D))}for(let D of z){let Z=D.content instanceof Uint8Array?D.content:new TextEncoder().encode(String(D.content??"")),_=new Blob([new Uint8Array(Z)],{type:D.contentType??"application/octet-stream"});w.append("attachment",_,D.filename)}let Y=N(`api:${this.apiKey}`).replace(/\r\n/g,""),L=await fetch(`${this.baseUrl}/v3/${this.domain}/messages`,{method:"POST",headers:{Authorization:`Basic ${Y}`},body:w}),K=await L.json();if(!L.ok)throw new X(K.message??"Mailgun API error",L.status,K);let O=Q(q.to);return{messageId:K.id??"",accepted:O,rejected:[],response:K.message??"queued",envelope:{from:F?.address??"",to:O}}}async verify(){try{let q=N(`api:${this.apiKey}`).replace(/\r\n/g,""),z=await fetch(`${this.baseUrl}/v3/domains`,{headers:{Authorization:`Basic ${q}`}});if(!z.ok)return{ok:!1,provider:"mailgun",message:(await z.json().catch(()=>({}))).message??`HTTP ${z.status}`};return{ok:!0,provider:"mailgun",message:"API key is valid"}}catch(q){return{ok:!1,provider:"mailgun",message:q instanceof Error?q.message:String(q)}}}}export{$ as MailgunTransport,X as MailgunError};
2
2
 
3
- //# debugId=58D5A6ECB5B1262364756E2164756E21
3
+ //# debugId=152C6D6A1205828E64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/transports/mailgun.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * @module\n * Mailgun HTTP transport for sently.\n * Uses the Mailgun Messages API v3 with multipart/form-data.\n *\n * @example\n * ```ts\n * import { MailgunTransport } from \"sently/transports/mailgun\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new MailgunTransport({\n * apiKey: \"key-...\",\n * domain: \"mg.example.com\",\n * }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses, toMIMEHeader } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport type {\n MailgunConfig,\n MailOptions,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Error thrown when the Mailgun API returns a non-success response. */\nexport class MailgunError extends Error {\n /** Creates a Mailgun API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message);\n this.name = \"MailgunError\";\n }\n}\n\n/**\n * Mailgun HTTP API transport (multipart/form-data).\n */\nexport class MailgunTransport implements Transport {\n /** Mailgun API key for basic authentication. */\n private readonly apiKey: string;\n /** Mailgun sending domain. */\n private readonly domain: string;\n /** Mailgun API base URL (US or EU region). */\n private readonly baseUrl: string;\n\n /** Creates a Mailgun transport with the given API key and domain. */\n constructor(config: MailgunConfig) {\n this.apiKey = config.apiKey;\n this.domain = config.domain;\n this.baseUrl =\n config.region === \"eu\" ? \"https://api.eu.mailgun.net\" : \"https://api.mailgun.net\";\n }\n\n /** Sends an email via the Mailgun Messages API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n const form = new FormData();\n\n form.append(\"from\", from ? toMIMEHeader(from) : \"\");\n form.append(\"to\", parseAddresses(options.to).map(toMIMEHeader).join(\", \"));\n\n if (options.cc) {\n form.append(\"cc\", parseAddresses(options.cc).map(toMIMEHeader).join(\", \"));\n }\n if (options.bcc) {\n form.append(\"bcc\", parseAddresses(options.bcc).map(toMIMEHeader).join(\", \"));\n }\n\n form.append(\"subject\", options.subject);\n\n if (options.text) {\n form.append(\"text\", options.text);\n }\n if (options.html) {\n form.append(\"html\", options.html);\n }\n if (options.replyTo) {\n const replyTo = parseAddresses(options.replyTo)[0];\n if (replyTo) {\n form.append(\"h:Reply-To\", toMIMEHeader(replyTo));\n }\n }\n\n for (const attachment of attachments) {\n const content =\n attachment.content instanceof Uint8Array\n ? attachment.content\n : new TextEncoder().encode(String(attachment.content ?? \"\"));\n const blob = new Blob([new Uint8Array(content)], {\n type: attachment.contentType ?? \"application/octet-stream\",\n });\n form.append(\"attachment\", blob, attachment.filename);\n }\n\n const auth = encodeBase64(`api:${this.apiKey}`).replace(/\\r\\n/g, \"\");\n const response = await fetch(`${this.baseUrl}/v3/${this.domain}/messages`, {\n method: \"POST\",\n headers: {\n Authorization: `Basic ${auth}`,\n },\n body: form,\n });\n\n const payload = (await response.json()) as { id?: string; message?: string };\n\n if (!response.ok) {\n throw new MailgunError(payload.message ?? \"Mailgun API error\", response.status, payload);\n }\n\n const toEmails = extractEmails(options.to);\n return {\n messageId: payload.id ?? \"\",\n accepted: toEmails,\n rejected: [],\n response: payload.message ?? \"queued\",\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies the Mailgun API key by listing domains. */\n async verify(): Promise<VerifyResult> {\n try {\n const auth = encodeBase64(`api:${this.apiKey}`).replace(/\\r\\n/g, \"\");\n const response = await fetch(`${this.baseUrl}/v3/domains`, {\n headers: {\n Authorization: `Basic ${auth}`,\n },\n });\n\n if (!response.ok) {\n const payload = (await response.json().catch(() => ({}))) as { message?: string };\n return {\n ok: false,\n provider: \"mailgun\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return { ok: true, provider: \"mailgun\", message: \"API key is valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"mailgun\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
5
+ "/**\n * @module\n * Mailgun HTTP transport for sently.\n * Uses the Mailgun Messages API v3 with multipart/form-data.\n *\n * @example\n * ```ts\n * import { MailgunTransport } from \"sently/transports/mailgun\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new MailgunTransport({\n * apiKey: \"key-...\",\n * domain: \"mg.example.com\",\n * }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses, toMIMEHeader } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type {\n MailgunConfig,\n MailOptions,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Error thrown when the Mailgun API returns a non-success response. */\nexport class MailgunError extends SentlyError {\n /** Creates a Mailgun API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"mailgun\",\n cause: apiError,\n });\n this.name = \"MailgunError\";\n }\n}\n\n/**\n * Mailgun HTTP API transport (multipart/form-data).\n */\nexport class MailgunTransport implements Transport {\n /** Mailgun API key for basic authentication. */\n private readonly apiKey: string;\n /** Mailgun sending domain. */\n private readonly domain: string;\n /** Mailgun API base URL (US or EU region). */\n private readonly baseUrl: string;\n\n /** Creates a Mailgun transport with the given API key and domain. */\n constructor(config: MailgunConfig) {\n this.apiKey = config.apiKey;\n this.domain = config.domain;\n this.baseUrl =\n config.region === \"eu\" ? \"https://api.eu.mailgun.net\" : \"https://api.mailgun.net\";\n }\n\n /** Sends an email via the Mailgun Messages API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n const form = new FormData();\n\n form.append(\"from\", from ? toMIMEHeader(from) : \"\");\n form.append(\"to\", parseAddresses(options.to).map(toMIMEHeader).join(\", \"));\n\n if (options.cc) {\n form.append(\"cc\", parseAddresses(options.cc).map(toMIMEHeader).join(\", \"));\n }\n if (options.bcc) {\n form.append(\"bcc\", parseAddresses(options.bcc).map(toMIMEHeader).join(\", \"));\n }\n\n form.append(\"subject\", options.subject);\n\n if (options.text) {\n form.append(\"text\", options.text);\n }\n if (options.html) {\n form.append(\"html\", options.html);\n }\n if (options.replyTo) {\n const replyTo = parseAddresses(options.replyTo)[0];\n if (replyTo) {\n form.append(\"h:Reply-To\", toMIMEHeader(replyTo));\n }\n }\n\n for (const attachment of attachments) {\n const content =\n attachment.content instanceof Uint8Array\n ? attachment.content\n : new TextEncoder().encode(String(attachment.content ?? \"\"));\n const blob = new Blob([new Uint8Array(content)], {\n type: attachment.contentType ?? \"application/octet-stream\",\n });\n form.append(\"attachment\", blob, attachment.filename);\n }\n\n const auth = encodeBase64(`api:${this.apiKey}`).replace(/\\r\\n/g, \"\");\n const response = await fetch(`${this.baseUrl}/v3/${this.domain}/messages`, {\n method: \"POST\",\n headers: {\n Authorization: `Basic ${auth}`,\n },\n body: form,\n });\n\n const payload = (await response.json()) as { id?: string; message?: string };\n\n if (!response.ok) {\n throw new MailgunError(payload.message ?? \"Mailgun API error\", response.status, payload);\n }\n\n const toEmails = extractEmails(options.to);\n return {\n messageId: payload.id ?? \"\",\n accepted: toEmails,\n rejected: [],\n response: payload.message ?? \"queued\",\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies the Mailgun API key by listing domains. */\n async verify(): Promise<VerifyResult> {\n try {\n const auth = encodeBase64(`api:${this.apiKey}`).replace(/\\r\\n/g, \"\");\n const response = await fetch(`${this.baseUrl}/v3/domains`, {\n headers: {\n Authorization: `Basic ${auth}`,\n },\n });\n\n if (!response.ok) {\n const payload = (await response.json().catch(() => ({}))) as { message?: string };\n return {\n ok: false,\n provider: \"mailgun\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return { ok: true, provider: \"mailgun\", message: \"API key is valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"mailgun\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
6
6
  ],
7
- "mappings": "qLA8BO,CAAM,KAAqB,AAAM,CAIpB,WACA,SAHlB,WAAW,CACT,EACgB,EACA,EAChB,CACA,MAAM,CAAO,EAHG,kBACA,gBAGhB,KAAK,KAAO,eAEhB,CAKO,MAAM,CAAsC,CAEhC,OAEA,OAEA,QAGjB,WAAW,CAAC,EAAuB,CACjC,KAAK,OAAS,EAAO,OACrB,KAAK,OAAS,EAAO,OACrB,KAAK,QACH,EAAO,SAAW,KAAO,6BAA+B,+BAItD,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAO,EAAe,EAAQ,IAAI,EAAE,GACpC,EAAO,IAAI,SAKjB,GAHA,EAAK,OAAO,OAAQ,EAAO,EAAa,CAAI,EAAI,EAAE,EAClD,EAAK,OAAO,KAAM,EAAe,EAAQ,EAAE,EAAE,IAAI,CAAY,EAAE,KAAK,IAAI,CAAC,EAErE,EAAQ,GACV,EAAK,OAAO,KAAM,EAAe,EAAQ,EAAE,EAAE,IAAI,CAAY,EAAE,KAAK,IAAI,CAAC,EAE3E,GAAI,EAAQ,IACV,EAAK,OAAO,MAAO,EAAe,EAAQ,GAAG,EAAE,IAAI,CAAY,EAAE,KAAK,IAAI,CAAC,EAK7E,GAFA,EAAK,OAAO,UAAW,EAAQ,OAAO,EAElC,EAAQ,KACV,EAAK,OAAO,OAAQ,EAAQ,IAAI,EAElC,GAAI,EAAQ,KACV,EAAK,OAAO,OAAQ,EAAQ,IAAI,EAElC,GAAI,EAAQ,QAAS,CACnB,IAAM,EAAU,EAAe,EAAQ,OAAO,EAAE,GAChD,GAAI,EACF,EAAK,OAAO,aAAc,EAAa,CAAO,CAAC,EAInD,QAAW,KAAc,EAAa,CACpC,IAAM,EACJ,EAAW,mBAAmB,WAC1B,EAAW,QACX,IAAI,YAAY,EAAE,OAAO,OAAO,EAAW,SAAW,EAAE,CAAC,EACzD,EAAO,IAAI,KAAK,CAAC,IAAI,WAAW,CAAO,CAAC,EAAG,CAC/C,KAAM,EAAW,aAAe,0BAClC,CAAC,EACD,EAAK,OAAO,aAAc,EAAM,EAAW,QAAQ,EAGrD,IAAM,EAAO,EAAa,OAAO,KAAK,QAAQ,EAAE,QAAQ,QAAS,EAAE,EAC7D,EAAW,MAAM,MAAM,GAAG,KAAK,cAAc,KAAK,kBAAmB,CACzE,OAAQ,OACR,QAAS,CACP,cAAe,SAAS,GAC1B,EACA,KAAM,CACR,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAErC,GAAI,CAAC,EAAS,GACZ,MAAM,IAAI,EAAa,EAAQ,SAAW,oBAAqB,EAAS,OAAQ,CAAO,EAGzF,IAAM,EAAW,EAAc,EAAQ,EAAE,EACzC,MAAO,CACL,UAAW,EAAQ,IAAM,GACzB,SAAU,EACV,SAAU,CAAC,EACX,SAAU,EAAQ,SAAW,SAC7B,SAAU,CACR,KAAM,GAAM,SAAW,GACvB,GAAI,CACN,CACF,OAII,OAAM,EAA0B,CACpC,GAAI,CACF,IAAM,EAAO,EAAa,OAAO,KAAK,QAAQ,EAAE,QAAQ,QAAS,EAAE,EAC7D,EAAW,MAAM,MAAM,GAAG,KAAK,qBAAsB,CACzD,QAAS,CACP,cAAe,SAAS,GAC1B,CACF,CAAC,EAED,GAAI,CAAC,EAAS,GAEZ,MAAO,CACL,GAAI,GACJ,SAAU,UACV,SAJe,MAAM,EAAS,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,GAIpC,SAAW,QAAQ,EAAS,QAC/C,EAGF,MAAO,CAAE,GAAI,GAAM,SAAU,UAAW,QAAS,kBAAmB,EACpE,MAAO,EAAK,CACZ,MAAO,CACL,GAAI,GACJ,SAAU,UACV,QAAS,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,CAC1D,GAGN",
8
- "debugId": "58D5A6ECB5B1262364756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "152C6D6A1205828E64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,4 @@
1
+ import { SentlyError } from "../core/errors.js";
1
2
  import type { MailOptions, SendResult, Transport, VerifyResult } from "../core/types.js";
2
3
  /** Postmark API configuration. */
3
4
  export interface PostmarkConfig {
@@ -5,7 +6,7 @@ export interface PostmarkConfig {
5
6
  serverToken: string;
6
7
  }
7
8
  /** Error thrown when the Postmark API returns a non-success response. */
8
- export declare class PostmarkError extends Error {
9
+ export declare class PostmarkError extends SentlyError {
9
10
  readonly statusCode: number;
10
11
  readonly apiError: unknown;
11
12
  /** Creates a Postmark API error with status code and response payload. */
@@ -1,3 +1,3 @@
1
- import{l as O}from"../chunk-cvmk9h1r.js";import{n as C,o as D,p as F}from"../chunk-kdqxhybf.js";import{G as L}from"../chunk-32dj0v9g.js";import"../chunk-rhade81v.js";class Q extends Error{statusCode;apiError;constructor(q,z,G){super(q);this.statusCode=z;this.apiError=G;this.name="PostmarkError"}}class T{serverToken;constructor(q){this.serverToken=q.serverToken}async send(q){let z=await O(q.attachments),G=C(q.from)[0],R={From:G?D(G):"",To:C(q.to).map(D).join(", "),Subject:q.subject,...q.cc?{Cc:C(q.cc).map(D).join(", ")}:{},...q.bcc?{Bcc:C(q.bcc).map(D).join(", ")}:{},...q.replyTo?{ReplyTo:C(q.replyTo).map(D).join(", ")}:{},...q.text?{TextBody:q.text}:{},...q.html?{HtmlBody:q.html}:{},...q.headers?{Headers:Object.entries(q.headers).map(([w,S])=>({Name:w,Value:S}))}:{},...z.length>0?{Attachments:z.map((w)=>({Name:w.filename,Content:w.content instanceof Uint8Array?L(w.content).replace(/\r\n/g,""):w.content,ContentType:w.contentType??"application/octet-stream",...w.contentId?{ContentID:w.contentId}:{}}))}:{}},K=await fetch("https://api.postmarkapp.com/email",{method:"POST",headers:{"X-Postmark-Server-Token":this.serverToken,"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(R)}),J=await K.json();if(!K.ok)throw new Q(J.Message??"Postmark API error",K.status,J);return{messageId:J.MessageID??q.messageId??"",accepted:F(q.to),rejected:[],response:J.Message??"OK",envelope:{from:G?.address??"",to:[...F(q.to),...q.cc?F(q.cc):[],...q.bcc?F(q.bcc):[]]}}}async verify(){try{let q=await fetch("https://api.postmarkapp.com/server",{headers:{"X-Postmark-Server-Token":this.serverToken,Accept:"application/json"}}),z=await q.json();if(!q.ok)return{ok:!1,provider:"postmark",message:z.Message??`HTTP ${q.status}`};return{ok:!0,provider:"postmark",...z.Name?{message:z.Name}:{}}}catch(q){return{ok:!1,provider:"postmark",message:q instanceof Error?q.message:String(q)}}}}export{T as PostmarkTransport,Q as PostmarkError};
1
+ import{s as U}from"../chunk-z7j2n9k8.js";import{A as J,y as D,z as F}from"../chunk-xdx6ypr5.js";import{O as Q,P as R}from"../chunk-9ckbc9mk.js";import{R as O}from"../chunk-50ycrgx6.js";import"../chunk-rszb1n1d.js";class W extends Q{statusCode;apiError;constructor(q,w,G){super(q,R(w),{statusCode:w,provider:"postmark",cause:G});this.statusCode=w;this.apiError=G;this.name="PostmarkError"}}class Z{serverToken;constructor(q){this.serverToken=q.serverToken}async send(q){let w=await U(q.attachments),G=D(q.from)[0],X={From:G?F(G):"",To:D(q.to).map(F).join(", "),Subject:q.subject,...q.cc?{Cc:D(q.cc).map(F).join(", ")}:{},...q.bcc?{Bcc:D(q.bcc).map(F).join(", ")}:{},...q.replyTo?{ReplyTo:D(q.replyTo).map(F).join(", ")}:{},...q.text?{TextBody:q.text}:{},...q.html?{HtmlBody:q.html}:{},...q.headers?{Headers:Object.entries(q.headers).map(([z,Y])=>({Name:z,Value:Y}))}:{},...w.length>0?{Attachments:w.map((z)=>({Name:z.filename,Content:z.content instanceof Uint8Array?O(z.content).replace(/\r\n/g,""):z.content,ContentType:z.contentType??"application/octet-stream",...z.contentId?{ContentID:z.contentId}:{}}))}:{}},L=await fetch("https://api.postmarkapp.com/email",{method:"POST",headers:{"X-Postmark-Server-Token":this.serverToken,"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(X)}),K=await L.json();if(!L.ok)throw new W(K.Message??"Postmark API error",L.status,K);return{messageId:K.MessageID??q.messageId??"",accepted:J(q.to),rejected:[],response:K.Message??"OK",envelope:{from:G?.address??"",to:[...J(q.to),...q.cc?J(q.cc):[],...q.bcc?J(q.bcc):[]]}}}async verify(){try{let q=await fetch("https://api.postmarkapp.com/server",{headers:{"X-Postmark-Server-Token":this.serverToken,Accept:"application/json"}}),w=await q.json();if(!q.ok)return{ok:!1,provider:"postmark",message:w.Message??`HTTP ${q.status}`};return{ok:!0,provider:"postmark",...w.Name?{message:w.Name}:{}}}catch(q){return{ok:!1,provider:"postmark",message:q instanceof Error?q.message:String(q)}}}}export{Z as PostmarkTransport,W as PostmarkError};
2
2
 
3
- //# debugId=F549E280C6C8948364756E2164756E21
3
+ //# debugId=8DEF5AEC1DB785AC64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/transports/postmark.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * @module\n * Postmark HTTP API transport for sending email via api.postmarkapp.com.\n *\n * @example\n * ```ts\n * import { PostmarkTransport } from \"sently/transports/postmark\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new PostmarkTransport({ serverToken: process.env.POSTMARK_TOKEN! }),\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, toMIMEHeader } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Postmark API configuration. */\nexport interface PostmarkConfig {\n /** Postmark server API token. */\n serverToken: string;\n}\n\n/** Error thrown when the Postmark API returns a non-success response. */\nexport class PostmarkError extends Error {\n /** Creates a Postmark API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message);\n this.name = \"PostmarkError\";\n }\n}\n\n/**\n * Postmark HTTP API transport.\n */\nexport class PostmarkTransport implements Transport {\n /** Postmark server API token for the X-Postmark-Server-Token header. */\n private readonly serverToken: string;\n\n /** Creates a Postmark transport with the given server token. */\n constructor(config: PostmarkConfig) {\n this.serverToken = config.serverToken;\n }\n\n /** Sends an email via the Postmark 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 From: from ? toMIMEHeader(from) : \"\",\n To: parseAddresses(options.to).map(toMIMEHeader).join(\", \"),\n Subject: options.subject,\n ...(options.cc ? { Cc: parseAddresses(options.cc).map(toMIMEHeader).join(\", \") } : {}),\n ...(options.bcc ? { Bcc: parseAddresses(options.bcc).map(toMIMEHeader).join(\", \") } : {}),\n ...(options.replyTo\n ? { ReplyTo: parseAddresses(options.replyTo).map(toMIMEHeader).join(\", \") }\n : {}),\n ...(options.text ? { TextBody: options.text } : {}),\n ...(options.html ? { HtmlBody: options.html } : {}),\n ...(options.headers\n ? { Headers: Object.entries(options.headers).map(([Name, Value]) => ({ Name, Value })) }\n : {}),\n ...(attachments.length > 0\n ? {\n Attachments: attachments.map((att) => ({\n Name: att.filename,\n Content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n ContentType: att.contentType ?? \"application/octet-stream\",\n ...(att.contentId ? { ContentID: att.contentId } : {}),\n })),\n }\n : {}),\n };\n\n const response = await fetch(\"https://api.postmarkapp.com/email\", {\n method: \"POST\",\n headers: {\n \"X-Postmark-Server-Token\": this.serverToken,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as {\n MessageID?: string;\n Message?: string;\n ErrorCode?: number;\n };\n\n if (!response.ok) {\n throw new PostmarkError(payload.Message ?? \"Postmark API error\", response.status, payload);\n }\n\n return {\n messageId: payload.MessageID ?? options.messageId ?? \"\",\n accepted: extractEmails(options.to),\n rejected: [],\n response: payload.Message ?? \"OK\",\n envelope: {\n from: from?.address ?? \"\",\n to: [\n ...extractEmails(options.to),\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ],\n },\n };\n }\n\n /** Verifies the Postmark server token by fetching server info. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(\"https://api.postmarkapp.com/server\", {\n headers: {\n \"X-Postmark-Server-Token\": this.serverToken,\n Accept: \"application/json\",\n },\n });\n\n const payload = (await response.json()) as { Name?: string; Message?: string };\n\n if (!response.ok) {\n return {\n ok: false,\n provider: \"postmark\",\n message: payload.Message ?? `HTTP ${response.status}`,\n };\n }\n\n return {\n ok: true,\n provider: \"postmark\",\n ...(payload.Name ? { message: payload.Name } : {}),\n };\n } catch (err) {\n return {\n ok: false,\n provider: \"postmark\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
5
+ "/**\n * @module\n * Postmark HTTP API transport for sending email via api.postmarkapp.com.\n *\n * @example\n * ```ts\n * import { PostmarkTransport } from \"sently/transports/postmark\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new PostmarkTransport({ serverToken: process.env.POSTMARK_TOKEN! }),\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, 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/** Postmark API configuration. */\nexport interface PostmarkConfig {\n /** Postmark server API token. */\n serverToken: string;\n}\n\n/** Error thrown when the Postmark API returns a non-success response. */\nexport class PostmarkError extends SentlyError {\n /** Creates a Postmark 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: \"postmark\",\n cause: apiError,\n });\n this.name = \"PostmarkError\";\n }\n}\n\n/**\n * Postmark HTTP API transport.\n */\nexport class PostmarkTransport implements Transport {\n /** Postmark server API token for the X-Postmark-Server-Token header. */\n private readonly serverToken: string;\n\n /** Creates a Postmark transport with the given server token. */\n constructor(config: PostmarkConfig) {\n this.serverToken = config.serverToken;\n }\n\n /** Sends an email via the Postmark 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 From: from ? toMIMEHeader(from) : \"\",\n To: parseAddresses(options.to).map(toMIMEHeader).join(\", \"),\n Subject: options.subject,\n ...(options.cc ? { Cc: parseAddresses(options.cc).map(toMIMEHeader).join(\", \") } : {}),\n ...(options.bcc ? { Bcc: parseAddresses(options.bcc).map(toMIMEHeader).join(\", \") } : {}),\n ...(options.replyTo\n ? { ReplyTo: parseAddresses(options.replyTo).map(toMIMEHeader).join(\", \") }\n : {}),\n ...(options.text ? { TextBody: options.text } : {}),\n ...(options.html ? { HtmlBody: options.html } : {}),\n ...(options.headers\n ? { Headers: Object.entries(options.headers).map(([Name, Value]) => ({ Name, Value })) }\n : {}),\n ...(attachments.length > 0\n ? {\n Attachments: attachments.map((att) => ({\n Name: att.filename,\n Content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n ContentType: att.contentType ?? \"application/octet-stream\",\n ...(att.contentId ? { ContentID: att.contentId } : {}),\n })),\n }\n : {}),\n };\n\n const response = await fetch(\"https://api.postmarkapp.com/email\", {\n method: \"POST\",\n headers: {\n \"X-Postmark-Server-Token\": this.serverToken,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as {\n MessageID?: string;\n Message?: string;\n ErrorCode?: number;\n };\n\n if (!response.ok) {\n throw new PostmarkError(payload.Message ?? \"Postmark API error\", response.status, payload);\n }\n\n return {\n messageId: payload.MessageID ?? options.messageId ?? \"\",\n accepted: extractEmails(options.to),\n rejected: [],\n response: payload.Message ?? \"OK\",\n envelope: {\n from: from?.address ?? \"\",\n to: [\n ...extractEmails(options.to),\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ],\n },\n };\n }\n\n /** Verifies the Postmark server token by fetching server info. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(\"https://api.postmarkapp.com/server\", {\n headers: {\n \"X-Postmark-Server-Token\": this.serverToken,\n Accept: \"application/json\",\n },\n });\n\n const payload = (await response.json()) as { Name?: string; Message?: string };\n\n if (!response.ok) {\n return {\n ok: false,\n provider: \"postmark\",\n message: payload.Message ?? `HTTP ${response.status}`,\n };\n }\n\n return {\n ok: true,\n provider: \"postmark\",\n ...(payload.Name ? { message: payload.Name } : {}),\n };\n } catch (err) {\n return {\n ok: false,\n provider: \"postmark\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
6
6
  ],
7
- "mappings": "qLAiCO,CAAM,KAAsB,AAAM,CAIrB,WACA,SAHlB,WAAW,CACT,EACgB,EACA,EAChB,CACA,MAAM,CAAO,EAHG,kBACA,gBAGhB,KAAK,KAAO,gBAEhB,CAKO,MAAM,CAAuC,CAEjC,YAGjB,WAAW,CAAC,EAAwB,CAClC,KAAK,YAAc,EAAO,iBAItB,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAO,EAAe,EAAQ,IAAI,EAAE,GAEpC,EAAO,CACX,KAAM,EAAO,EAAa,CAAI,EAAI,GAClC,GAAI,EAAe,EAAQ,EAAE,EAAE,IAAI,CAAY,EAAE,KAAK,IAAI,EAC1D,QAAS,EAAQ,WACb,EAAQ,GAAK,CAAE,GAAI,EAAe,EAAQ,EAAE,EAAE,IAAI,CAAY,EAAE,KAAK,IAAI,CAAE,EAAI,CAAC,KAChF,EAAQ,IAAM,CAAE,IAAK,EAAe,EAAQ,GAAG,EAAE,IAAI,CAAY,EAAE,KAAK,IAAI,CAAE,EAAI,CAAC,KACnF,EAAQ,QACR,CAAE,QAAS,EAAe,EAAQ,OAAO,EAAE,IAAI,CAAY,EAAE,KAAK,IAAI,CAAE,EACxE,CAAC,KACD,EAAQ,KAAO,CAAE,SAAU,EAAQ,IAAK,EAAI,CAAC,KAC7C,EAAQ,KAAO,CAAE,SAAU,EAAQ,IAAK,EAAI,CAAC,KAC7C,EAAQ,QACR,CAAE,QAAS,OAAO,QAAQ,EAAQ,OAAO,EAAE,IAAI,EAAE,EAAM,MAAY,CAAE,OAAM,OAAM,EAAE,CAAE,EACrF,CAAC,KACD,EAAY,OAAS,EACrB,CACE,YAAa,EAAY,IAAI,CAAC,KAAS,CACrC,KAAM,EAAI,SACV,QACE,EAAI,mBAAmB,WACnB,EAAa,EAAI,OAAO,EAAE,QAAQ,QAAS,EAAE,EAC7C,EAAI,QACV,YAAa,EAAI,aAAe,8BAC5B,EAAI,UAAY,CAAE,UAAW,EAAI,SAAU,EAAI,CAAC,CACtD,EAAE,CACJ,EACA,CAAC,CACP,EAEM,EAAW,MAAM,MAAM,oCAAqC,CAChE,OAAQ,OACR,QAAS,CACP,0BAA2B,KAAK,YAChC,eAAgB,mBAChB,OAAQ,kBACV,EACA,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAMrC,GAAI,CAAC,EAAS,GACZ,MAAM,IAAI,EAAc,EAAQ,SAAW,qBAAsB,EAAS,OAAQ,CAAO,EAG3F,MAAO,CACL,UAAW,EAAQ,WAAa,EAAQ,WAAa,GACrD,SAAU,EAAc,EAAQ,EAAE,EAClC,SAAU,CAAC,EACX,SAAU,EAAQ,SAAW,KAC7B,SAAU,CACR,KAAM,GAAM,SAAW,GACvB,GAAI,CACF,GAAG,EAAc,EAAQ,EAAE,EAC3B,GAAI,EAAQ,GAAK,EAAc,EAAQ,EAAE,EAAI,CAAC,EAC9C,GAAI,EAAQ,IAAM,EAAc,EAAQ,GAAG,EAAI,CAAC,CAClD,CACF,CACF,OAII,OAAM,EAA0B,CACpC,GAAI,CACF,IAAM,EAAW,MAAM,MAAM,qCAAsC,CACjE,QAAS,CACP,0BAA2B,KAAK,YAChC,OAAQ,kBACV,CACF,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAErC,GAAI,CAAC,EAAS,GACZ,MAAO,CACL,GAAI,GACJ,SAAU,WACV,QAAS,EAAQ,SAAW,QAAQ,EAAS,QAC/C,EAGF,MAAO,CACL,GAAI,GACJ,SAAU,cACN,EAAQ,KAAO,CAAE,QAAS,EAAQ,IAAK,EAAI,CAAC,CAClD,EACA,MAAO,EAAK,CACZ,MAAO,CACL,GAAI,GACJ,SAAU,WACV,QAAS,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,CAC1D,GAGN",
8
- "debugId": "F549E280C6C8948364756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "8DEF5AEC1DB785AC64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,3 @@
1
- import{k as L}from"../chunk-nhec1319.js";import{l as N}from"../chunk-cvmk9h1r.js";import{p as K}from"../chunk-kdqxhybf.js";import"../chunk-32dj0v9g.js";import{K as J}from"../chunk-rhade81v.js";function T(k){return k.replace(/[^a-zA-Z0-9]+/g,"-").replace(/^-|-$/g,"").slice(0,40)||"no-subject"}function U(k){if(k==="darwin")return"open";if(k==="win32")return"start";return"xdg-open"}class V{outDir;open;format;constructor(k){this.outDir=k?.outDir??"./.emails",this.open=k?.open??!1,this.format=k?.format??"eml"}async send(k){let H=await N(k.attachments),P={...k,attachments:H},D=await L(P),F=await import("node:fs/promises");await F.mkdir(this.outDir,{recursive:!0});let Q=`${Date.now()}-${T(k.subject)}.${this.format}`,B=`${this.outDir}/${Q}`;if(this.format==="html"){let G=k.html??(k.text?`<pre>${k.text.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}</pre>`:"");await F.writeFile(B,G,"utf8")}else await F.writeFile(B,D.raw);if(console.log(`[sently preview] Written: ${B}`),this.open){let{spawn:G}=await import("node:child_process"),R=U(process.platform);G(R,[B],{detached:!0,stdio:"ignore"}).unref()}return{messageId:D.messageId,accepted:K(k.to),rejected:[],response:`preview: ${B}`,envelope:D.envelope}}async verify(){return{ok:!0,provider:"preview"}}}export{V as PreviewTransport};
1
+ import{r as L}from"../chunk-v8eh0n8d.js";import{s as N}from"../chunk-z7j2n9k8.js";import{A as K}from"../chunk-xdx6ypr5.js";import"../chunk-50ycrgx6.js";import{V as J}from"../chunk-rszb1n1d.js";function T(k){return k.replace(/[^a-zA-Z0-9]+/g,"-").replace(/^-|-$/g,"").slice(0,40)||"no-subject"}function U(k){if(k==="darwin")return"open";if(k==="win32")return"start";return"xdg-open"}class V{outDir;open;format;constructor(k){this.outDir=k?.outDir??"./.emails",this.open=k?.open??!1,this.format=k?.format??"eml"}async send(k){let H=await N(k.attachments),P={...k,attachments:H},D=await L(P),F=await import("node:fs/promises");await F.mkdir(this.outDir,{recursive:!0});let Q=`${Date.now()}-${T(k.subject)}.${this.format}`,B=`${this.outDir}/${Q}`;if(this.format==="html"){let G=k.html??(k.text?`<pre>${k.text.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}</pre>`:"");await F.writeFile(B,G,"utf8")}else await F.writeFile(B,D.raw);if(console.log(`[sently preview] Written: ${B}`),this.open){let{spawn:G}=await import("node:child_process"),R=U(process.platform);G(R,[B],{detached:!0,stdio:"ignore"}).unref()}return{messageId:D.messageId,accepted:K(k.to),rejected:[],response:`preview: ${B}`,envelope:D.envelope}}async verify(){return{ok:!0,provider:"preview"}}}export{V as PreviewTransport};
2
2
 
3
- //# debugId=64A43FCD4659806964756E2164756E21
3
+ //# debugId=8D1AFBC1DE72071D64756E2164756E21
@@ -5,6 +5,6 @@
5
5
  "/**\n * @module\n * PreviewTransport — development transport that writes emails to disk\n * instead of sending them. Optionally opens in the default browser.\n * Use in place of a real transport during local development.\n *\n * @example\n * ```ts\n * import { PreviewTransport } from 'sently/transports/preview'\n * const transport = new PreviewTransport({ outDir: './.emails', open: true })\n * ```\n */\nimport { extractEmails } from \"../core/address.js\";\nimport { buildMIME } from \"../core/mime.js\";\nimport type {\n MailOptions,\n PreviewConfig,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\nfunction sanitizeSubject(subject: string): string {\n const sanitized = subject.replace(/[^a-zA-Z0-9]+/g, \"-\").replace(/^-|-$/g, \"\");\n return sanitized.slice(0, 40) || \"no-subject\";\n}\n\nfunction getOpenCommand(platform: string): string {\n if (platform === \"darwin\") {\n return \"open\";\n }\n if (platform === \"win32\") {\n return \"start\";\n }\n return \"xdg-open\";\n}\n\n/**\n * Development transport that writes emails to disk instead of sending them.\n */\nexport class PreviewTransport implements Transport {\n /** Directory where preview `.eml` or `.html` files are written. */\n private readonly outDir: string;\n /** Whether to open the written file in the default browser. */\n private readonly open: boolean;\n /** Output format: full MIME (`.eml`) or HTML body only. */\n private readonly format: \"eml\" | \"html\";\n\n /** Creates a preview transport with optional output directory and format. */\n constructor(config?: PreviewConfig) {\n this.outDir = config?.outDir ?? \"./.emails\";\n this.open = config?.open ?? false;\n this.format = config?.format ?? \"eml\";\n }\n\n /** Writes the message to disk and returns a synthetic SendResult. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const resolvedOptions = { ...options, attachments };\n const mime = await buildMIME(resolvedOptions);\n\n const fs = await import(\"node:fs/promises\");\n await fs.mkdir(this.outDir, { recursive: true });\n\n const filename = `${Date.now()}-${sanitizeSubject(options.subject)}.${this.format}`;\n const filepath = `${this.outDir}/${filename}`;\n\n if (this.format === \"html\") {\n const html =\n options.html ??\n (options.text\n ? `<pre>${options.text.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\")}</pre>`\n : \"\");\n await fs.writeFile(filepath, html, \"utf8\");\n } else {\n await fs.writeFile(filepath, mime.raw);\n }\n\n console.log(`[sently preview] Written: ${filepath}`);\n\n if (this.open) {\n const { spawn } = await import(\"node:child_process\");\n const command = getOpenCommand(process.platform);\n spawn(command, [filepath], { detached: true, stdio: \"ignore\" }).unref();\n }\n\n return {\n messageId: mime.messageId,\n accepted: extractEmails(options.to),\n rejected: [],\n response: `preview: ${filepath}`,\n envelope: mime.envelope,\n };\n }\n\n /** Always succeeds — preview transport requires no external connectivity. */\n async verify(): Promise<VerifyResult> {\n return { ok: true, provider: \"preview\" };\n }\n}\n"
6
6
  ],
7
7
  "mappings": "qNAuBA,IAAS,JAAe,JAAC,HAAyB,CAEhD,OADkB,EAAQ,QAAQ,iBAAkB,GAAG,EAAE,QAAQ,SAAU,EAAE,EAC5D,MAAM,EAAG,EAAE,GAAK,aAGnC,SAAS,CAAc,CAAC,EAA0B,CAChD,GAAI,IAAa,SACf,MAAO,OAET,GAAI,IAAa,QACf,MAAO,QAET,MAAO,WAMF,MAAM,CAAsC,CAEhC,OAEA,KAEA,OAGjB,WAAW,CAAC,EAAwB,CAClC,KAAK,OAAS,GAAQ,QAAU,YAChC,KAAK,KAAO,GAAQ,MAAQ,GAC5B,KAAK,OAAS,GAAQ,QAAU,WAI5B,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAkB,IAAK,EAAS,aAAY,EAC5C,EAAO,MAAM,EAAU,CAAe,EAEtC,EAAK,KAAa,4BACxB,MAAM,EAAG,MAAM,KAAK,OAAQ,CAAE,UAAW,EAAK,CAAC,EAE/C,IAAM,EAAW,GAAG,KAAK,IAAI,KAAK,EAAgB,EAAQ,OAAO,KAAK,KAAK,SACrE,EAAW,GAAG,KAAK,UAAU,IAEnC,GAAI,KAAK,SAAW,OAAQ,CAC1B,IAAM,EACJ,EAAQ,OACP,EAAQ,KACL,QAAQ,EAAQ,KAAK,QAAQ,KAAM,OAAO,EAAE,QAAQ,KAAM,MAAM,EAAE,QAAQ,KAAM,MAAM,UACtF,IACN,MAAM,EAAG,UAAU,EAAU,EAAM,MAAM,EAEzC,WAAM,EAAG,UAAU,EAAU,EAAK,GAAG,EAKvC,GAFA,QAAQ,IAAI,6BAA6B,GAAU,EAE/C,KAAK,KAAM,CACb,IAAQ,SAAU,KAAa,8BACzB,EAAU,EAAe,QAAQ,QAAQ,EAC/C,EAAM,EAAS,CAAC,CAAQ,EAAG,CAAE,SAAU,GAAM,MAAO,QAAS,CAAC,EAAE,MAAM,EAGxE,MAAO,CACL,UAAW,EAAK,UAChB,SAAU,EAAc,EAAQ,EAAE,EAClC,SAAU,CAAC,EACX,SAAU,YAAY,IACtB,SAAU,EAAK,QACjB,OAII,OAAM,EAA0B,CACpC,MAAO,CAAE,GAAI,GAAM,SAAU,SAAU,EAE3C",
8
- "debugId": "64A43FCD4659806964756E2164756E21",
8
+ "debugId": "8D1AFBC1DE72071D64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,4 @@
1
+ import { SentlyError } from "../core/errors.js";
1
2
  import type { MailOptions, SendResult, Transport, VerifyResult } from "../core/types.js";
2
3
  /** Resend API configuration. */
3
4
  export interface ResendConfig {
@@ -13,7 +14,7 @@ export interface ResendConfig {
13
14
  /** Maximum messages per Resend batch request (overridable per account). */
14
15
  export declare const RESEND_BATCH_MAX = 100;
15
16
  /** Error thrown when the Resend API returns a non-success response. */
16
- export declare class ResendError extends Error {
17
+ export declare class ResendError extends SentlyError {
17
18
  readonly statusCode: number;
18
19
  readonly apiError: unknown;
19
20
  /** Creates a Resend API error with status code and response payload. */
@@ -1,3 +1,3 @@
1
- import{j as K}from"../chunk-zwm5bfa4.js";import{l as I}from"../chunk-cvmk9h1r.js";import{n as $,o as C,p as G}from"../chunk-kdqxhybf.js";import{t as V}from"../chunk-ntqmn6pe.js";import{G as v}from"../chunk-32dj0v9g.js";import"../chunk-rhade81v.js";var Z=100;class Y extends Error{statusCode;apiError;constructor(q,w,z){super(q);this.statusCode=w;this.apiError=z;this.name="ResendError"}}class S{apiKey;baseUrl;rateLimiter;batchMax=Z;constructor(q){this.apiKey=q.apiKey,this.baseUrl=q.baseUrl??"https://api.resend.com";let w=q.rateDelta??2;if(w>0)this.rateLimiter=new V(w,q.rateLimit??1000);else this.rateLimiter=null}async buildEmailBody(q){let w=await I(q.attachments),z=$(q.from)[0];return{from:z?C(z):"",to:G(q.to),subject:q.subject,...q.cc?{cc:G(q.cc)}:{},...q.bcc?{bcc:G(q.bcc)}:{},...q.replyTo?{reply_to:G(q.replyTo)}:{},...q.text?{text:q.text}:{},...q.html?{html:q.html}:{},...q.headers?{headers:q.headers}:{},...w.length>0?{attachments:w.map((F)=>({filename:F.filename,content:F.content instanceof Uint8Array?v(F.content).replace(/\r\n/g,""):F.content,...F.contentType?{content_type:F.contentType}:{}}))}:{}}}toSendResult(q,w){let z=$(q.from)[0];return{messageId:w.id??q.messageId??"",accepted:G(q.to),rejected:[],response:w.message??"Email sent",envelope:{from:z?.address??"",to:[...G(q.to),...q.cc?G(q.cc):[],...q.bcc?G(q.bcc):[]]},...w.batchError!==void 0?{batchError:w.batchError}:{}}}async send(q){let w=await this.buildEmailBody(q),z=K(q),F=await fetch(`${this.baseUrl}/emails`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json",...z?{"Idempotency-Key":z}:{}},body:JSON.stringify(w)}),O=await F.json();if(!F.ok)throw new Y(O.message??"Resend API error",F.status,O);return this.toSendResult(q,O)}async sendBatch(q){if(q.length===0)return[];let w=[];for(let z=0;z<q.length;z+=Z){if(z>0&&this.rateLimiter)await this.rateLimiter.acquire();let F=q.slice(z,z+Z),O=await Promise.all(F.map((J)=>this.buildEmailBody(J))),U=await fetch(`${this.baseUrl}/emails/batch`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(O)}),Q=await U.json();if(!U.ok)throw new Y(Q.message??"Resend batch API error",U.status,Q);let N=Q.data??[];for(let J=0;J<F.length;J++){let L=F[J],W=N[J];if(W?.id!==void 0){w.push(this.toSendResult(L,{id:W.id,...Q.message!==void 0?{message:Q.message}:{}}));continue}let P=W?.error??"Batch item failed";w.push(this.toSendResult(L,{message:P,batchError:new Y(P,U.status,W??{})}))}}return w}async verify(){try{let q=await fetch(`${this.baseUrl}/domains`,{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!q.ok)return{ok:!1,provider:"resend",message:(await q.json().catch(()=>({}))).message??`HTTP ${q.status}`};return{ok:!0,provider:"resend",message:"API key is valid"}}catch(q){return{ok:!1,provider:"resend",message:q instanceof Error?q.message:String(q)}}}}export{S as ResendTransport,Y as ResendError,Z as RESEND_BATCH_MAX};
1
+ import{q as b}from"../chunk-qp4y0kp2.js";import{s as X}from"../chunk-z7j2n9k8.js";import{w as I}from"../chunk-tfxje74k.js";import{A as G,y as $,z as v}from"../chunk-xdx6ypr5.js";import{O as K,P as N}from"../chunk-9ckbc9mk.js";import{R as V}from"../chunk-50ycrgx6.js";import"../chunk-rszb1n1d.js";var Z=100;class Y extends K{statusCode;apiError;constructor(q,w,z){super(q,N(w),{statusCode:w,provider:"resend",cause:z});this.statusCode=w;this.apiError=z;this.name="ResendError"}}class H{apiKey;baseUrl;rateLimiter;batchMax=Z;constructor(q){this.apiKey=q.apiKey,this.baseUrl=q.baseUrl??"https://api.resend.com";let w=q.rateDelta??2;if(w>0)this.rateLimiter=new I(w,q.rateLimit??1000);else this.rateLimiter=null}async buildEmailBody(q){let w=await X(q.attachments),z=$(q.from)[0];return{from:z?v(z):"",to:G(q.to),subject:q.subject,...q.cc?{cc:G(q.cc)}:{},...q.bcc?{bcc:G(q.bcc)}:{},...q.replyTo?{reply_to:G(q.replyTo)}:{},...q.text?{text:q.text}:{},...q.html?{html:q.html}:{},...q.headers?{headers:q.headers}:{},...w.length>0?{attachments:w.map((F)=>({filename:F.filename,content:F.content instanceof Uint8Array?V(F.content).replace(/\r\n/g,""):F.content,...F.contentType?{content_type:F.contentType}:{}}))}:{}}}toSendResult(q,w){let z=$(q.from)[0];return{messageId:w.id??q.messageId??"",accepted:G(q.to),rejected:[],response:w.message??"Email sent",envelope:{from:z?.address??"",to:[...G(q.to),...q.cc?G(q.cc):[],...q.bcc?G(q.bcc):[]]},...w.batchError!==void 0?{batchError:w.batchError}:{}}}async send(q){let w=await this.buildEmailBody(q),z=b(q),F=await fetch(`${this.baseUrl}/emails`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json",...z?{"Idempotency-Key":z}:{}},body:JSON.stringify(w)}),O=await F.json();if(!F.ok)throw new Y(O.message??"Resend API error",F.status,O);return this.toSendResult(q,O)}async sendBatch(q){if(q.length===0)return[];let w=[];for(let z=0;z<q.length;z+=Z){if(z>0&&this.rateLimiter)await this.rateLimiter.acquire();let F=q.slice(z,z+Z),O=await Promise.all(F.map((J)=>this.buildEmailBody(J))),U=await fetch(`${this.baseUrl}/emails/batch`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(O)}),Q=await U.json();if(!U.ok)throw new Y(Q.message??"Resend batch API error",U.status,Q);let B=Q.data??[];for(let J=0;J<F.length;J++){let L=F[J],W=B[J];if(W?.id!==void 0){w.push(this.toSendResult(L,{id:W.id,...Q.message!==void 0?{message:Q.message}:{}}));continue}let P=W?.error??"Batch item failed";w.push(this.toSendResult(L,{message:P,batchError:new Y(P,U.status,W??{})}))}}return w}async verify(){try{let q=await fetch(`${this.baseUrl}/domains`,{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!q.ok)return{ok:!1,provider:"resend",message:(await q.json().catch(()=>({}))).message??`HTTP ${q.status}`};return{ok:!0,provider:"resend",message:"API key is valid"}}catch(q){return{ok:!1,provider:"resend",message:q instanceof Error?q.message:String(q)}}}}export{H as ResendTransport,Y as ResendError,Z as RESEND_BATCH_MAX};
2
2
 
3
- //# debugId=8E2BC77BDE920DD964756E2164756E21
3
+ //# debugId=0065781B51F4526264756E2164756E21