sently 0.6.0 → 0.6.2
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.
- package/CHANGELOG.md +32 -0
- package/README.md +24 -88
- package/dist/adapters/bun.js +1 -1
- package/dist/adapters/cf.js +2 -2
- package/dist/adapters/cf.js.map +1 -1
- package/dist/adapters/deno.js +1 -1
- package/dist/adapters/node.js +1 -1
- package/dist/auth/oauth2.js +1 -1
- package/dist/chunk-74rha3ac.js +4 -0
- package/dist/chunk-74rha3ac.js.map +10 -0
- package/dist/{chunk-9ckbc9mk.js → chunk-a1c87hn1.js} +2 -2
- package/dist/{chunk-9ckbc9mk.js.map → chunk-a1c87hn1.js.map} +1 -1
- package/dist/{chunk-gkec86xg.js → chunk-cvrbfke9.js} +3 -3
- package/dist/{chunk-gkec86xg.js.map → chunk-cvrbfke9.js.map} +1 -1
- package/dist/{chunk-50ycrgx6.js → chunk-d5rwm3br.js} +2 -2
- package/dist/{chunk-50ycrgx6.js.map → chunk-d5rwm3br.js.map} +1 -1
- package/dist/{chunk-bzw0fdmy.js → chunk-fjq6b8gj.js} +2 -2
- package/dist/{chunk-bzw0fdmy.js.map → chunk-fjq6b8gj.js.map} +1 -1
- package/dist/{chunk-xdx6ypr5.js → chunk-gh5n759p.js} +3 -3
- package/dist/{chunk-xdx6ypr5.js.map → chunk-gh5n759p.js.map} +1 -1
- package/dist/{chunk-v8eh0n8d.js → chunk-gkkv7yqm.js} +2 -2
- package/dist/{chunk-v8eh0n8d.js.map → chunk-gkkv7yqm.js.map} +1 -1
- package/dist/{chunk-tfxje74k.js → chunk-kebhy657.js} +2 -2
- package/dist/{chunk-tfxje74k.js.map → chunk-kebhy657.js.map} +1 -1
- package/dist/{chunk-rszb1n1d.js → chunk-keksa6fx.js} +2 -2
- package/dist/{chunk-rszb1n1d.js.map → chunk-keksa6fx.js.map} +1 -1
- package/dist/{chunk-z7j2n9k8.js → chunk-r7ee9tk7.js} +2 -2
- package/dist/{chunk-z7j2n9k8.js.map → chunk-r7ee9tk7.js.map} +1 -1
- package/dist/{chunk-zm7fe0wb.js → chunk-rm78s1ek.js} +2 -2
- package/dist/{chunk-zm7fe0wb.js.map → chunk-rm78s1ek.js.map} +1 -1
- package/dist/chunk-vae5rfb1.js +4 -0
- package/dist/chunk-vae5rfb1.js.map +11 -0
- package/dist/core/errors.js +3 -0
- package/dist/core/errors.js.map +9 -0
- package/dist/core/smtp.js +2 -2
- package/dist/core/smtp.js.map +1 -1
- package/dist/core/types.d.ts +5 -6
- package/dist/detect.d.ts +1 -7
- package/dist/detect.js +2 -2
- package/dist/detect.js.map +3 -4
- package/dist/dkim.js +2 -2
- package/dist/dkim.js.map +1 -1
- package/dist/idempotency.js +2 -2
- package/dist/idempotency.js.map +2 -2
- package/dist/index.d.ts +7 -3
- package/dist/index.js +8 -2
- package/dist/mailer.js +2 -2
- package/dist/mailer.js.map +1 -1
- package/dist/plugins/react.js +2 -2
- package/dist/plugins/react.js.map +1 -1
- package/dist/plugins/template.js +1 -1
- package/dist/pool/pool.js +2 -2
- package/dist/pool/pool.js.map +1 -1
- package/dist/smtp-mailer.d.ts +27 -0
- package/dist/smtp-mailer.js +3 -0
- package/dist/smtp-mailer.js.map +10 -0
- package/dist/transports/brevo.js +2 -2
- package/dist/transports/brevo.js.map +1 -1
- package/dist/transports/mailgun.js +2 -2
- package/dist/transports/mailgun.js.map +2 -2
- package/dist/transports/postmark.js +2 -2
- package/dist/transports/postmark.js.map +1 -1
- package/dist/transports/preview.js +2 -2
- package/dist/transports/preview.js.map +1 -1
- package/dist/transports/resend.js +2 -2
- package/dist/transports/resend.js.map +1 -1
- package/dist/transports/retry.js +2 -2
- package/dist/transports/retry.js.map +4 -3
- package/dist/transports/sendgrid.js +2 -2
- package/dist/transports/sendgrid.js.map +2 -2
- package/dist/transports/ses.js +3 -3
- package/dist/transports/ses.js.map +1 -1
- package/dist/transports/smtp.js +2 -2
- package/dist/transports/smtp.js.map +1 -1
- package/dist/webhooks/brevo.js +1 -1
- package/dist/webhooks/mailgun.js +1 -1
- package/dist/webhooks/postmark.js +1 -1
- package/dist/webhooks/resend.js +1 -1
- package/dist/webhooks/sendgrid.js +1 -1
- package/dist/webhooks/ses.js +1 -1
- package/dist/webhooks/timing-safe-equal.js +1 -1
- package/package.json +5 -1
- package/dist/chunk-1cmggzw0.js +0 -4
- package/dist/chunk-1cmggzw0.js.map +0 -10
- package/dist/chunk-wp4qxk1j.js +0 -4
- package/dist/chunk-wp4qxk1j.js.map +0 -11
package/dist/pool/pool.js.map
CHANGED
|
@@ -6,6 +6,6 @@
|
|
|
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
8
|
"mappings": "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",
|
|
9
|
-
"debugId": "
|
|
9
|
+
"debugId": "EEBF20CE45142C1F64756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* SMTP-capable mailer factory — host/port config, pooling, and runtime adapters.
|
|
4
|
+
*
|
|
5
|
+
* Import from `sently/smtp` or use {@link createSMTPMailer} from the main package.
|
|
6
|
+
* For HTTP transports, use `sently/mailer` instead (~10 KB smaller in app bundles).
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { createMailer } from "sently/smtp";
|
|
11
|
+
*
|
|
12
|
+
* const mailer = await createMailer({
|
|
13
|
+
* host: "smtp.example.com",
|
|
14
|
+
* port: 587,
|
|
15
|
+
* auth: { user: "you@example.com", pass: "secret" },
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
import type { Mailer, MailerHooks, SMTPConfig } from "./core/types.js";
|
|
20
|
+
/**
|
|
21
|
+
* Create a mailer from SMTP relay options (host, port, auth, pool, adapter).
|
|
22
|
+
*/
|
|
23
|
+
export declare function createSMTPMailer(options: SMTPConfig & {
|
|
24
|
+
hooks?: MailerHooks;
|
|
25
|
+
}): Promise<Mailer>;
|
|
26
|
+
/** Alias for {@link createSMTPMailer} — preferred import path is `sently/smtp`. */
|
|
27
|
+
export declare const createMailer: typeof createSMTPMailer;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{u as g}from"./chunk-74rha3ac.js";import{w as q}from"./chunk-vae5rfb1.js";import"./chunk-kebhy657.js";import"./chunk-gh5n759p.js";import"./chunk-d5rwm3br.js";import{W as w}from"./chunk-keksa6fx.js";async function C(b){let v={...b.secure!==void 0?{secure:b.secure}:{},...b.connectionTimeout!==void 0?{connectionTimeout:b.connectionTimeout}:{},...b.tls!==void 0?{tls:b.tls}:{}};if(b.pool){let{SMTPPool:B}=await import("./pool/pool.js");return new q(new B(b,{createAdapter:async()=>b.adapter??await g(v)}),b.plugins,b.hooks)}let x=b.adapter??await g(v),{SMTPTransport:z}=await import("./transports/smtp.js");return new q(new z({...b,adapter:x}),b.plugins,b.hooks)}var G=C;export{C as createSMTPMailer,G as createMailer};
|
|
2
|
+
|
|
3
|
+
//# debugId=7A21688D4CA004A464756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/smtp-mailer.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * @module\n * SMTP-capable mailer factory — host/port config, pooling, and runtime adapters.\n *\n * Import from `sently/smtp` or use {@link createSMTPMailer} from the main package.\n * For HTTP transports, use `sently/mailer` instead (~10 KB smaller in app bundles).\n *\n * @example\n * ```ts\n * import { createMailer } from \"sently/smtp\";\n *\n * const mailer = await createMailer({\n * host: \"smtp.example.com\",\n * port: 587,\n * auth: { user: \"you@example.com\", pass: \"secret\" },\n * });\n * ```\n */\nimport type { Mailer, MailerHooks, SMTPConfig } from \"./core/types.js\";\nimport { createDefaultAdapter } from \"./detect.js\";\nimport { MailerImpl } from \"./mailer.js\";\n\n/**\n * Create a mailer from SMTP relay options (host, port, auth, pool, adapter).\n */\nexport async function createSMTPMailer(\n options: SMTPConfig & { hooks?: MailerHooks },\n): Promise<Mailer> {\n const adapterOptions = {\n ...(options.secure !== undefined ? { secure: options.secure } : {}),\n ...(options.connectionTimeout !== undefined\n ? { connectionTimeout: options.connectionTimeout }\n : {}),\n ...(options.tls !== undefined ? { tls: options.tls } : {}),\n };\n\n if (options.pool) {\n const { SMTPPool } = await import(\"./pool/pool.js\");\n return new MailerImpl(\n new SMTPPool(options, {\n createAdapter: async () => options.adapter ?? (await createDefaultAdapter(adapterOptions)),\n }),\n options.plugins,\n options.hooks,\n );\n }\n\n const adapter = options.adapter ?? (await createDefaultAdapter(adapterOptions));\n const { SMTPTransport } = await import(\"./transports/smtp.js\");\n\n return new MailerImpl(new SMTPTransport({ ...options, adapter }), options.plugins, options.hooks);\n}\n\n/** Alias for {@link createSMTPMailer} — preferred import path is `sently/smtp`. */\nexport const createMailer = createSMTPMailer;\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "0OAyBA,SAAsB,LAAgB,LACpC,JACiB,LACjB,IAAM,EAAiB,IACjB,EAAQ,SAAW,OAAY,CAAE,OAAQ,EAAQ,MAAO,EAAI,CAAC,KAC7D,EAAQ,oBAAsB,OAC9B,CAAE,kBAAmB,EAAQ,iBAAkB,EAC/C,CAAC,KACD,EAAQ,MAAQ,OAAY,CAAE,IAAK,EAAQ,GAAI,EAAI,CAAC,CAC1D,EAEA,GAAI,EAAQ,KAAM,CAChB,IAAQ,YAAa,KAAa,0BAClC,OAAO,IAAI,EACT,IAAI,EAAS,EAAS,CACpB,cAAe,SAAY,EAAQ,SAAY,MAAM,EAAqB,CAAc,CAC1F,CAAC,EACD,EAAQ,QACR,EAAQ,KACV,EAGF,IAAM,EAAU,EAAQ,SAAY,MAAM,EAAqB,CAAc,GACrE,iBAAkB,KAAa,gCAEvC,OAAO,IAAI,EAAW,IAAI,EAAc,IAAK,EAAS,SAAQ,CAAC,EAAG,EAAQ,QAAS,EAAQ,KAAK,EAI3F,IAAM,EAAe",
|
|
8
|
+
"debugId": "7A21688D4CA004A464756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/dist/transports/brevo.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{s as R}from"../chunk-
|
|
1
|
+
import{s as R}from"../chunk-r7ee9tk7.js";import{B as M,z as G}from"../chunk-gh5n759p.js";import{P as L}from"../chunk-d5rwm3br.js";import{T as N,U as Q}from"../chunk-a1c87hn1.js";import"../chunk-keksa6fx.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=
|
|
3
|
+
//# debugId=2FDD13CA0A66D5BE64756E2164756E21
|
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
"/**\n * @module\n * Brevo (formerly Sendinblue) HTTP transport for sently.\n *\n * @example\n * ```ts\n * import { BrevoTransport } from \"sently/transports/brevo\";\n * import { createMailer } from \"sently\";\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
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": "
|
|
8
|
+
"debugId": "2FDD13CA0A66D5BE64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{s as W}from"../chunk-
|
|
1
|
+
import{s as W}from"../chunk-r7ee9tk7.js";import{A as J,B as Q,z as G}from"../chunk-gh5n759p.js";import{P as N}from"../chunk-d5rwm3br.js";import{T as R,U}from"../chunk-a1c87hn1.js";import"../chunk-keksa6fx.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=
|
|
3
|
+
//# debugId=5E0CE52DDAABE05064756E2164756E21
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"/**\n * @module\n * Mailgun HTTP transport for sently.\n * Uses the Mailgun Messages API v3 with multipart/form-data.\n *\n * @example\n * ```ts\n * import { MailgunTransport } from \"sently/transports/mailgun\";\n * import { createMailer } from \"sently\";\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": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "5E0CE52DDAABE05064756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{s as U}from"../chunk-
|
|
1
|
+
import{s as U}from"../chunk-r7ee9tk7.js";import{A as F,B as J,z as D}from"../chunk-gh5n759p.js";import{P as O}from"../chunk-d5rwm3br.js";import{T as Q,U as R}from"../chunk-a1c87hn1.js";import"../chunk-keksa6fx.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=
|
|
3
|
+
//# debugId=AC3184841BFF35F464756E2164756E21
|
|
@@ -5,6 +5,6 @@
|
|
|
5
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
7
|
"mappings": "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",
|
|
8
|
-
"debugId": "
|
|
8
|
+
"debugId": "AC3184841BFF35F464756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{r as L}from"../chunk-
|
|
1
|
+
import{r as L}from"../chunk-gkkv7yqm.js";import{s as N}from"../chunk-r7ee9tk7.js";import{B as K}from"../chunk-gh5n759p.js";import"../chunk-d5rwm3br.js";import{W as J}from"../chunk-keksa6fx.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,"&").replace(/</g,"<").replace(/>/g,">")}</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=
|
|
3
|
+
//# debugId=8DF25A35EE27060564756E2164756E21
|
|
@@ -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, \"&\").replace(/</g, \"<\").replace(/>/g, \">\")}</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": "
|
|
8
|
+
"debugId": "8DF25A35EE27060564756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{q as b}from"../chunk-qp4y0kp2.js";import{s as X}from"../chunk-
|
|
1
|
+
import{q as b}from"../chunk-qp4y0kp2.js";import{s as X}from"../chunk-r7ee9tk7.js";import{x as I}from"../chunk-kebhy657.js";import{A as v,B as G,z as $}from"../chunk-gh5n759p.js";import{P as V}from"../chunk-d5rwm3br.js";import{T as K,U as N}from"../chunk-a1c87hn1.js";import"../chunk-keksa6fx.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=
|
|
3
|
+
//# debugId=CA6FA1FE0F1D428564756E2164756E21
|
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
"/**\n * @module\n * Resend HTTP API transport for sending email via api.resend.com.\n *\n * @example\n * ```ts\n * import { ResendTransport } from \"sently/transports/resend\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new ResendTransport({ apiKey: process.env.RESEND_API_KEY! }),\n * });\n *\n * await mailer.send({\n * from: \"onboarding@yourdomain.com\",\n * to: \"recipient@example.com\",\n * subject: \"Hello\",\n * html: \"<p>Sent via Resend</p>\",\n * });\n * ```\n */\nimport { extractEmails, parseAddresses, toMIMEHeader } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport { resolveIdempotencyKey } from \"../core/idempotency-key.js\";\nimport { RateLimiter } from \"../core/rate-limiter.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Resend API configuration. */\nexport interface ResendConfig {\n /** Resend API key (starts with `re_`). */\n apiKey: string;\n /** API base URL. Default: `https://api.resend.com`. */\n baseUrl?: string;\n /** Max batch HTTP requests per rate window. Default: `2` (Resend API limit). Set `0` to disable. */\n rateDelta?: number;\n /** Rate limit window in milliseconds. Default: `1000`. */\n rateLimit?: number;\n}\n\n/** Maximum messages per Resend batch request (overridable per account). */\nexport const RESEND_BATCH_MAX = 100;\n\n/** Error thrown when the Resend API returns a non-success response. */\nexport class ResendError extends SentlyError {\n /** Creates a Resend API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"resend\",\n cause: apiError,\n });\n this.name = \"ResendError\";\n }\n}\n\n/**\n * Resend HTTP API transport.\n */\nexport class ResendTransport implements Transport {\n /** Resend API key for Bearer authentication. */\n private readonly apiKey: string;\n /** Resend API base URL. */\n private readonly baseUrl: string;\n /** Optional rate limiter for multi-chunk batch sends. */\n private readonly rateLimiter: RateLimiter | null;\n\n /** Maximum messages per batch HTTP request. */\n readonly batchMax = RESEND_BATCH_MAX;\n\n /** Creates a Resend transport with the given API key. */\n constructor(config: ResendConfig) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl ?? \"https://api.resend.com\";\n\n const rateDelta = config.rateDelta ?? 2;\n if (rateDelta > 0) {\n this.rateLimiter = new RateLimiter(rateDelta, config.rateLimit ?? 1000);\n } else {\n this.rateLimiter = null;\n }\n }\n\n /** Build the JSON body for a single Resend email. */\n private async buildEmailBody(options: MailOptions): Promise<Record<string, unknown>> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n return {\n from: from ? toMIMEHeader(from) : \"\",\n to: extractEmails(options.to),\n subject: options.subject,\n ...(options.cc ? { cc: extractEmails(options.cc) } : {}),\n ...(options.bcc ? { bcc: extractEmails(options.bcc) } : {}),\n ...(options.replyTo ? { reply_to: extractEmails(options.replyTo) } : {}),\n ...(options.text ? { text: options.text } : {}),\n ...(options.html ? { html: options.html } : {}),\n ...(options.headers ? { headers: options.headers } : {}),\n ...(attachments.length > 0\n ? {\n attachments: attachments.map((att) => ({\n filename: att.filename,\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n ...(att.contentType ? { content_type: att.contentType } : {}),\n })),\n }\n : {}),\n };\n }\n\n /** Map a Resend API response id to a normalized SendResult. */\n private toSendResult(\n options: MailOptions,\n payload: { id?: string; message?: string; batchError?: unknown },\n ): SendResult {\n const from = parseAddresses(options.from)[0];\n return {\n messageId: payload.id ?? options.messageId ?? \"\",\n accepted: extractEmails(options.to),\n rejected: [],\n response: payload.message ?? \"Email sent\",\n envelope: {\n from: from?.address ?? \"\",\n to: [\n ...extractEmails(options.to),\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ],\n },\n ...(payload.batchError !== undefined ? { batchError: payload.batchError } : {}),\n };\n }\n\n /** Sends an email via the Resend HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const body = await this.buildEmailBody(options);\n const idempotencyKey = resolveIdempotencyKey(options);\n\n const response = await fetch(`${this.baseUrl}/emails`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n ...(idempotencyKey ? { \"Idempotency-Key\": idempotencyKey } : {}),\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as { id?: string; message?: string };\n\n if (!response.ok) {\n throw new ResendError(payload.message ?? \"Resend API error\", response.status, payload);\n }\n\n return this.toSendResult(options, payload);\n }\n\n /** Send up to {@link RESEND_BATCH_MAX} attachment-free messages per batch request. */\n async sendBatch(messages: MailOptions[]): Promise<SendResult[]> {\n if (messages.length === 0) {\n return [];\n }\n\n const results: SendResult[] = [];\n for (let offset = 0; offset < messages.length; offset += RESEND_BATCH_MAX) {\n if (offset > 0 && this.rateLimiter) {\n await this.rateLimiter.acquire();\n }\n\n const chunk = messages.slice(offset, offset + RESEND_BATCH_MAX);\n const bodies = await Promise.all(chunk.map((message) => this.buildEmailBody(message)));\n\n const response = await fetch(`${this.baseUrl}/emails/batch`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(bodies),\n });\n\n const payload = (await response.json()) as {\n data?: Array<{ id?: string; error?: string }>;\n message?: string;\n };\n\n if (!response.ok) {\n throw new ResendError(\n payload.message ?? \"Resend batch API error\",\n response.status,\n payload,\n );\n }\n\n const ids = payload.data ?? [];\n for (let i = 0; i < chunk.length; i++) {\n const message = chunk[i] as MailOptions;\n const item = ids[i];\n if (item?.id !== undefined) {\n results.push(\n this.toSendResult(message, {\n id: item.id,\n ...(payload.message !== undefined ? { message: payload.message } : {}),\n }),\n );\n continue;\n }\n\n const errorMessage = item?.error ?? \"Batch item failed\";\n results.push(\n this.toSendResult(message, {\n message: errorMessage,\n batchError: new ResendError(errorMessage, response.status, item ?? {}),\n }),\n );\n }\n }\n\n return results;\n }\n\n /** Verifies the Resend API key by listing domains. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(`${this.baseUrl}/domains`, {\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n },\n });\n\n if (!response.ok) {\n const payload = (await response.json().catch(() => ({}))) as { message?: string };\n return {\n ok: false,\n provider: \"resend\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return { ok: true, provider: \"resend\", message: \"API key is valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"resend\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
|
|
6
6
|
],
|
|
7
7
|
"mappings": "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",
|
|
8
|
-
"debugId": "
|
|
8
|
+
"debugId": "CA6FA1FE0F1D428564756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/transports/retry.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{T as G}from"../chunk-a1c87hn1.js";import"../chunk-keksa6fx.js";var H=[429,500,502,503,504];function I(q,x,z){if(x==="exponential")return z*2**(q-1);if(x==="linear")return z*q;return z}function J(q,x){if(q instanceof G&&q.sentlyCode==="SMTP_AUTH_FAILED")return!1;if(typeof q==="object"&&q!==null&&"statusCode"in q&&typeof q.statusCode==="number")return x.includes(q.statusCode);return!0}class K{inner;_sleep;maxAttempts;backoff;baseDelay;retryOn;onRetry;mailerOnRetry;constructor(q,x,z=(B)=>new Promise((C)=>setTimeout(C,B))){this.inner=q;this._sleep=z;this.maxAttempts=x?.maxAttempts??3,this.backoff=x?.backoff??"exponential",this.baseDelay=x?.baseDelay??1000,this.retryOn=x?.retryOn??H,this.onRetry=x?.onRetry,this.mailerOnRetry=void 0}setMailerOnRetry(q){if(q!==void 0&&this.mailerOnRetry!==void 0&&this.mailerOnRetry!==q){if(!(typeof process<"u"&&process.env?.NODE_ENV==="production"))console.warn("[sently] RetryTransport.setMailerOnRetry: overwriting an active mailer onRetry callback. Do not share one RetryTransport instance across multiple mailers.")}this.mailerOnRetry=q}async send(q){let x;for(let z=1;z<=this.maxAttempts;z++)try{return await this.inner.send(q)}catch(B){if(x=B,z===this.maxAttempts)throw B;if(!J(B,this.retryOn))throw B;let C=I(z,this.backoff,this.baseDelay);this.onRetry?.(z,B),this.mailerOnRetry?.(z,B),await this._sleep(C)}throw x}async verify(){if(this.inner.verify)return this.inner.verify();return{ok:!0,provider:"retry"}}async close(){await this.inner.close?.()}async sendBatch(q){if(this.inner.sendBatch)return this.inner.sendBatch(q);return Promise.all(q.map((x)=>this.send(x)))}}export{K as RetryTransport};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=96C6C7CB9F0026C864756E2164756E21
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": [],
|
|
3
|
+
"sources": ["../src/transports/retry.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
+
"/**\n * @module\n * RetryTransport — decorator that wraps any sently transport\n * and retries failed sends with configurable backoff.\n * Works with SMTP, Resend, SendGrid, SES, and any custom transport.\n *\n * @example\n * ```ts\n * import { RetryTransport } from 'sently/transports/retry'\n * import { ResendTransport } from 'sently/transports/resend'\n *\n * const transport = new RetryTransport(\n * new ResendTransport({ apiKey }),\n * { maxAttempts: 3, backoff: 'exponential', retryOn: [429, 503] }\n * )\n * ```\n */\nimport { SentlyError } from \"../core/errors.js\";\nimport type {\n MailOptions,\n RetryConfig,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\n\nconst DEFAULT_RETRY_ON = [429, 500, 502, 503, 504];\n\nfunction computeDelay(attempt: number, backoff: string, base: number): number {\n if (backoff === \"exponential\") {\n return base * 2 ** (attempt - 1);\n }\n if (backoff === \"linear\") {\n return base * attempt;\n }\n return base;\n}\n\nfunction shouldRetry(err: unknown, retryOn: number[]): boolean {\n if (err instanceof SentlyError && err.sentlyCode === \"SMTP_AUTH_FAILED\") {\n return false;\n }\n\n if (\n typeof err === \"object\" &&\n err !== null &&\n \"statusCode\" in err &&\n typeof (err as { statusCode: unknown }).statusCode === \"number\"\n ) {\n return retryOn.includes((err as { statusCode: number }).statusCode);\n }\n\n return true;\n}\n\n/**\n * Decorator transport that retries failed sends with configurable backoff.\n */\nexport class RetryTransport implements Transport {\n /** Maximum send attempts including the initial try. */\n private readonly maxAttempts: number;\n /** Backoff strategy between retries. */\n private readonly backoff: \"exponential\" | \"linear\" | \"fixed\";\n /** Base delay in milliseconds before the first retry. */\n private readonly baseDelay: number;\n /** HTTP status codes that trigger a retry. */\n private readonly retryOn: number[];\n /** Optional callback invoked before each retry attempt. */\n private readonly onRetry: RetryConfig[\"onRetry\"];\n /** Additional onRetry callback wired by {@link MailerImpl} hooks per send. */\n private mailerOnRetry: RetryConfig[\"onRetry\"];\n\n /** Wraps an inner transport with retry logic and optional backoff configuration. */\n constructor(\n /** Transport that performs the actual send on each attempt. */\n private readonly inner: Transport,\n config?: RetryConfig,\n /** Injectable sleep function for testing backoff timing. */\n private readonly _sleep: (ms: number) => Promise<void> = (ms) =>\n new Promise((resolve) => setTimeout(resolve, ms)),\n ) {\n this.maxAttempts = config?.maxAttempts ?? 3;\n this.backoff = config?.backoff ?? \"exponential\";\n this.baseDelay = config?.baseDelay ?? 1000;\n this.retryOn = config?.retryOn ?? DEFAULT_RETRY_ON;\n this.onRetry = config?.onRetry;\n this.mailerOnRetry = undefined;\n }\n\n /**\n * Register a per-send onRetry callback from mailer lifecycle hooks.\n * Do not share one {@link RetryTransport} across multiple mailers — the last\n * `setMailerOnRetry` wins; overwriting an active callback logs a dev warning.\n * @internal Used by {@link MailerImpl}; cleared after each send.\n */\n setMailerOnRetry(callback: RetryConfig[\"onRetry\"] | undefined): void {\n if (\n callback !== undefined &&\n this.mailerOnRetry !== undefined &&\n this.mailerOnRetry !== callback\n ) {\n const isProduction = typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\";\n if (!isProduction) {\n console.warn(\n \"[sently] RetryTransport.setMailerOnRetry: overwriting an active mailer onRetry callback. Do not share one RetryTransport instance across multiple mailers.\",\n );\n }\n }\n this.mailerOnRetry = callback;\n }\n\n /** Sends with retries according to configured backoff and retry rules. */\n async send(options: MailOptions): Promise<SendResult> {\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= this.maxAttempts; attempt++) {\n try {\n return await this.inner.send(options);\n } catch (err) {\n lastError = err;\n if (attempt === this.maxAttempts) {\n throw err;\n }\n if (!shouldRetry(err, this.retryOn)) {\n throw err;\n }\n const delay = computeDelay(attempt, this.backoff, this.baseDelay);\n this.onRetry?.(attempt, err);\n this.mailerOnRetry?.(attempt, err);\n await this._sleep(delay);\n }\n }\n\n throw lastError;\n }\n\n /** Delegates to the inner transport verify or returns a default success result. */\n async verify(): Promise<VerifyResult> {\n if (this.inner.verify) {\n return this.inner.verify();\n }\n return { ok: true, provider: \"retry\" };\n }\n\n /** Delegates close to the inner transport if available. */\n async close(): Promise<void> {\n await this.inner.close?.();\n }\n\n /** Delegates batch sends to the inner transport when available. */\n async sendBatch(messages: MailOptions[]): Promise<SendResult[]> {\n if (this.inner.sendBatch) {\n return this.inner.sendBatch(messages);\n }\n return Promise.all(messages.map((message) => this.send(message)));\n }\n}\n"
|
|
5
6
|
],
|
|
6
|
-
"mappings": "",
|
|
7
|
-
"debugId": "
|
|
7
|
+
"mappings": "2EA0BA,DAAM,EAAmB,CAAC,IAAK,IAAK,IAAK,IAAK,GAAG,EAEjD,SAAS,CAAY,CAAC,EAAiB,EAAiB,EAAsB,CAC5E,GAAI,IAAY,cACd,OAAO,EAAO,IAAM,EAAU,GAEhC,GAAI,IAAY,SACd,OAAO,EAAO,EAEhB,OAAO,EAGT,SAAS,CAAW,CAAC,EAAc,EAA4B,CAC7D,GAAI,aAAe,GAAe,EAAI,aAAe,mBACnD,MAAO,GAGT,GACE,OAAO,IAAQ,UACf,IAAQ,MACR,eAAgB,GAChB,OAAQ,EAAgC,aAAe,SAEvD,OAAO,EAAQ,SAAU,EAA+B,UAAU,EAGpE,MAAO,GAMF,MAAM,CAAoC,CAiB5B,MAGA,OAlBF,YAEA,QAEA,UAEA,QAEA,QAET,cAGR,WAAW,CAEQ,EACjB,EAEiB,EAAwC,CAAC,IACxD,IAAI,QAAQ,CAAC,IAAY,WAAW,EAAS,CAAE,CAAC,EAClD,CALiB,aAGA,cAGjB,KAAK,YAAc,GAAQ,aAAe,EAC1C,KAAK,QAAU,GAAQ,SAAW,cAClC,KAAK,UAAY,GAAQ,WAAa,KACtC,KAAK,QAAU,GAAQ,SAAW,EAClC,KAAK,QAAU,GAAQ,QACvB,KAAK,cAAgB,OASvB,gBAAgB,CAAC,EAAoD,CACnE,GACE,IAAa,QACb,KAAK,gBAAkB,QACvB,KAAK,gBAAkB,GAGvB,GAAI,EADiB,OAAO,QAAY,KAAe,QAAQ,KAAK,WAAa,cAE/E,QAAQ,KACN,4JACF,EAGJ,KAAK,cAAgB,OAIjB,KAAI,CAAC,EAA2C,CACpD,IAAI,EAEJ,QAAS,EAAU,EAAG,GAAW,KAAK,YAAa,IACjD,GAAI,CACF,OAAO,MAAM,KAAK,MAAM,KAAK,CAAO,EACpC,MAAO,EAAK,CAEZ,GADA,EAAY,EACR,IAAY,KAAK,YACnB,MAAM,EAER,GAAI,CAAC,EAAY,EAAK,KAAK,OAAO,EAChC,MAAM,EAER,IAAM,EAAQ,EAAa,EAAS,KAAK,QAAS,KAAK,SAAS,EAChE,KAAK,UAAU,EAAS,CAAG,EAC3B,KAAK,gBAAgB,EAAS,CAAG,EACjC,MAAM,KAAK,OAAO,CAAK,EAI3B,MAAM,OAIF,OAAM,EAA0B,CACpC,GAAI,KAAK,MAAM,OACb,OAAO,KAAK,MAAM,OAAO,EAE3B,MAAO,CAAE,GAAI,GAAM,SAAU,OAAQ,OAIjC,MAAK,EAAkB,CAC3B,MAAM,KAAK,MAAM,QAAQ,OAIrB,UAAS,CAAC,EAAgD,CAC9D,GAAI,KAAK,MAAM,UACb,OAAO,KAAK,MAAM,UAAU,CAAQ,EAEtC,OAAO,QAAQ,IAAI,EAAS,IAAI,CAAC,IAAY,KAAK,KAAK,CAAO,CAAC,CAAC,EAEpE",
|
|
8
|
+
"debugId": "96C6C7CB9F0026C864756E2164756E21",
|
|
8
9
|
"names": []
|
|
9
10
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{s as R}from"../chunk-
|
|
1
|
+
import{s as R}from"../chunk-r7ee9tk7.js";import{B as K,z as H}from"../chunk-gh5n759p.js";import{P as N}from"../chunk-d5rwm3br.js";import{T as O,U as Q}from"../chunk-a1c87hn1.js";import"../chunk-keksa6fx.js";class M extends O{statusCode;apiError;constructor(q,w,D){super(q,Q(w),{statusCode:w,provider:"sendgrid",cause:D});this.statusCode=w;this.apiError=D;this.name="SendGridError"}}class U{apiKey;constructor(q){this.apiKey=q.apiKey}buildPersonalization(q){return{to:H(q.to).map((w)=>({email:w.address,name:w.name})),...q.cc?{cc:H(q.cc).map((w)=>({email:w.address,name:w.name}))}:{},...q.bcc?{bcc:H(q.bcc).map((w)=>({email:w.address,name:w.name}))}:{},subject:q.subject,...q.headers?{headers:q.headers}:{}}}buildContent(q){return[...q.text?[{type:"text/plain",value:q.text}]:[],...q.html?[{type:"text/html",value:q.html}]:[]]}toSendResult(q,w,D="Accepted"){let J=H(q.from)[0];return{messageId:w,accepted:K(q.to),rejected:[],response:D,envelope:{from:J?.address??"",to:[...K(q.to),...q.cc?K(q.cc):[],...q.bcc?K(q.bcc):[]]}}}async send(q){let w=await R(q.attachments),D=H(q.from)[0],J={personalizations:[this.buildPersonalization(q)],from:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},...q.replyTo?{reply_to:H(q.replyTo).map((z)=>({email:z.address,name:z.name}))[0]}:{},content:this.buildContent(q),...w.length>0?{attachments:w.map((z)=>({filename:z.filename,type:z.contentType??"application/octet-stream",content:z.content instanceof Uint8Array?N(z.content).replace(/\r\n/g,""):z.content,...z.contentId?{content_id:z.contentId}:{},disposition:z.inline?"inline":"attachment"}))}:{}},F=await fetch("https://api.sendgrid.com/v3/mail/send",{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(J)});if(!F.ok){let z=await F.text();throw new M("SendGrid API error",F.status,z)}let L=F.headers.get("x-message-id")??q.messageId??"";return this.toSendResult(q,L)}async sendBatch(q){if(q.length===0)return[];let w=q[0],D=H(w.from)[0],J={personalizations:q.map((z)=>this.buildPersonalization(z)),from:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},...w.replyTo?{reply_to:H(w.replyTo).map((z)=>({email:z.address,name:z.name}))[0]}:{},content:this.buildContent(w)},F=await fetch("https://api.sendgrid.com/v3/mail/send",{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(J)});if(!F.ok){let z=await F.text();throw new M("SendGrid API error",F.status,z)}let L=F.headers.get("x-message-id")??"";return q.map((z)=>this.toSendResult(z,L||z.messageId||""))}async verify(){try{let q=await fetch("https://api.sendgrid.com/v3/user/profile",{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!q.ok)return{ok:!1,provider:"sendgrid",message:await q.text().catch(()=>"")||`HTTP ${q.status}`};let w=await q.json();return{ok:!0,provider:"sendgrid",...w.username?{message:w.username}:{}}}catch(q){return{ok:!1,provider:"sendgrid",message:q instanceof Error?q.message:String(q)}}}}export{U as SendGridTransport,M as SendGridError};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=85BB76E29A0774C064756E2164756E21
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"/**\n * @module\n * SendGrid v3 HTTP API transport for sending email via api.sendgrid.com.\n *\n * @example\n * ```ts\n * import { SendGridTransport } from \"sently/transports/sendgrid\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new SendGridTransport({ apiKey: process.env.SENDGRID_API_KEY! }),\n * });\n *\n * await mailer.send({\n * from: \"sender@example.com\",\n * to: \"recipient@example.com\",\n * subject: \"Hello\",\n * text: \"Plain text body\",\n * });\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** SendGrid API configuration. */\nexport interface SendGridConfig {\n /** SendGrid API key (Bearer token). */\n apiKey: string;\n}\n\n/** Error thrown when the SendGrid API returns a non-success response. */\nexport class SendGridError extends SentlyError {\n /** Creates a SendGrid API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"sendgrid\",\n cause: apiError,\n });\n this.name = \"SendGridError\";\n }\n}\n\n/**\n * SendGrid v3 HTTP API transport.\n */\nexport class SendGridTransport implements Transport {\n /** SendGrid API key for Bearer authentication. */\n private readonly apiKey: string;\n\n /** Creates a SendGrid transport with the given API key. */\n constructor(config: SendGridConfig) {\n this.apiKey = config.apiKey;\n }\n\n /** Build a SendGrid personalization for one message. */\n private buildPersonalization(options: MailOptions): Record<string, unknown> {\n return {\n to: parseAddresses(options.to).map((addr) => ({ email: addr.address, name: addr.name })),\n ...(options.cc\n ? {\n cc: parseAddresses(options.cc).map((addr) => ({\n email: addr.address,\n name: addr.name,\n })),\n }\n : {}),\n ...(options.bcc\n ? {\n bcc: parseAddresses(options.bcc).map((addr) => ({\n email: addr.address,\n name: addr.name,\n })),\n }\n : {}),\n subject: options.subject,\n ...(options.headers ? { headers: options.headers } : {}),\n };\n }\n\n /** Build SendGrid content parts from text/html options. */\n private buildContent(options: MailOptions): Array<{ type: string; value: string }> {\n return [\n ...(options.text ? [{ type: \"text/plain\", value: options.text }] : []),\n ...(options.html ? [{ type: \"text/html\", value: options.html }] : []),\n ];\n }\n\n /** Map a SendGrid response to a normalized SendResult for one message. */\n private toSendResult(\n options: MailOptions,\n messageId: string,\n responseText = \"Accepted\",\n ): SendResult {\n const from = parseAddresses(options.from)[0];\n return {\n messageId,\n accepted: extractEmails(options.to),\n rejected: [],\n response: responseText,\n envelope: {\n from: from?.address ?? \"\",\n to: [\n ...extractEmails(options.to),\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ],\n },\n };\n }\n\n /** Sends an email via the SendGrid v3 HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n\n const body = {\n personalizations: [this.buildPersonalization(options)],\n from: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n ...(options.replyTo\n ? {\n reply_to: parseAddresses(options.replyTo).map((addr) => ({\n email: addr.address,\n name: addr.name,\n }))[0],\n }\n : {}),\n content: this.buildContent(options),\n ...(attachments.length > 0\n ? {\n attachments: attachments.map((att) => ({\n filename: att.filename,\n type: att.contentType ?? \"application/octet-stream\",\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n ...(att.contentId ? { content_id: att.contentId } : {}),\n disposition: att.inline ? \"inline\" : \"attachment\",\n })),\n }\n : {}),\n };\n\n const response = await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const apiError = await response.text();\n throw new SendGridError(\"SendGrid API error\", response.status, apiError);\n }\n\n const messageId = response.headers.get(\"x-message-id\") ?? options.messageId ?? \"\";\n\n return this.toSendResult(options, messageId);\n }\n\n /** Send multiple messages in one request using SendGrid personalizations. */\n async sendBatch(messages: MailOptions[]): Promise<SendResult[]> {\n if (messages.length === 0) {\n return [];\n }\n\n const first = messages[0] as MailOptions;\n const from = parseAddresses(first.from)[0];\n\n const body = {\n personalizations: messages.map((message) => this.buildPersonalization(message)),\n from: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n ...(first.replyTo\n ? {\n reply_to: parseAddresses(first.replyTo).map((addr) => ({\n email: addr.address,\n name: addr.name,\n }))[0],\n }\n : {}),\n content: this.buildContent(first),\n };\n\n const response = await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const apiError = await response.text();\n throw new SendGridError(\"SendGrid API error\", response.status, apiError);\n }\n\n const messageId = response.headers.get(\"x-message-id\") ?? \"\";\n return messages.map((message) =>\n this.toSendResult(message, messageId || message.messageId || \"\"),\n );\n }\n\n /** Verifies the SendGrid API key by fetching the user profile. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(\"https://api.sendgrid.com/v3/user/profile\", {\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n },\n });\n\n if (!response.ok) {\n const apiError = await response.text().catch(() => \"\");\n return {\n ok: false,\n provider: \"sendgrid\",\n message: apiError || `HTTP ${response.status}`,\n };\n }\n\n const payload = (await response.json()) as { username?: string };\n return {\n ok: true,\n provider: \"sendgrid\",\n ...(payload.username ? { message: payload.username } : {}),\n };\n } catch (err) {\n return {\n ok: false,\n provider: \"sendgrid\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "85BB76E29A0774C064756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/transports/ses.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{r as
|
|
1
|
+
import{r as A}from"../chunk-gkkv7yqm.js";import{s as b}from"../chunk-r7ee9tk7.js";import{A as R,B as D,z as C}from"../chunk-gh5n759p.js";import{P as x}from"../chunk-d5rwm3br.js";import{T as k,U as v}from"../chunk-a1c87hn1.js";import"../chunk-keksa6fx.js";var z=new TextEncoder;async function K(F){let G=await crypto.subtle.digest("SHA-256",z.encode(F));return T(new Uint8Array(G))}async function w(F,G){let J=typeof F==="string"?z.encode(F):new Uint8Array(F),X=await crypto.subtle.importKey("raw",J,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),L=await crypto.subtle.sign("HMAC",X,z.encode(G));return new Uint8Array(L)}async function B(F){let{method:G,url:J,body:X,credentials:L}=F,Y=new URL(J),Z=F._date??`${new Date().toISOString().replace(/[-:]/g,"").slice(0,15)}Z`,Q=Z.slice(0,8),$={...Object.fromEntries(Object.entries(F.headers).map(([N,S])=>[N.toLowerCase(),S.trim()])),host:Y.host,"x-amz-date":Z};if(L.sessionToken)$["x-amz-security-token"]=L.sessionToken;let O=Object.keys($).map((N)=>N.toLowerCase()).sort(),V=O.join(";"),P=`${O.map((N)=>`${N}:${$[N]}`).join(`
|
|
2
2
|
`)}
|
|
3
3
|
`,I=y(Y.searchParams),U=await K(X),W=[G.toUpperCase(),u(Y.pathname),I,P,V,U].join(`
|
|
4
4
|
`),_=`${Q}/${L.region}/${L.service}/aws4_request`,M=["AWS4-HMAC-SHA256",Z,_,await K(W)].join(`
|
|
5
|
-
`),H=await h(L.secretAccessKey,Q,L.region,L.service),f=T(await w(H,M)),E=[`AWS4-HMAC-SHA256 Credential=${L.accessKeyId}/${_}`,`SignedHeaders=${V}`,`Signature=${f}`].join(", ");return{headers:{...$,Authorization:E}}}async function h(F,G,J,X){let L=await w(`AWS4${F}`,G),Y=await w(L,J),Z=await w(Y,X);return w(Z,"aws4_request")}function u(F){if(!F||F==="/")return"/";return F.split("/").map((G)=>encodeURIComponent(decodeURIComponent(G))).join("/")}function y(F){let G=[];for(let[J,X]of F.entries())G.push(`${j(J)}=${j(X)}`);return G.sort(),G.join("&")}function j(F){return encodeURIComponent(F).replace(/[!'()*]/g,(G)=>`%${G.charCodeAt(0).toString(16).toUpperCase()}`)}function T(F){return Array.from(F,(G)=>G.toString(16).padStart(2,"0")).join("")}class q extends
|
|
5
|
+
`),H=await h(L.secretAccessKey,Q,L.region,L.service),f=T(await w(H,M)),E=[`AWS4-HMAC-SHA256 Credential=${L.accessKeyId}/${_}`,`SignedHeaders=${V}`,`Signature=${f}`].join(", ");return{headers:{...$,Authorization:E}}}async function h(F,G,J,X){let L=await w(`AWS4${F}`,G),Y=await w(L,J),Z=await w(Y,X);return w(Z,"aws4_request")}function u(F){if(!F||F==="/")return"/";return F.split("/").map((G)=>encodeURIComponent(decodeURIComponent(G))).join("/")}function y(F){let G=[];for(let[J,X]of F.entries())G.push(`${j(J)}=${j(X)}`);return G.sort(),G.join("&")}function j(F){return encodeURIComponent(F).replace(/[!'()*]/g,(G)=>`%${G.charCodeAt(0).toString(16).toUpperCase()}`)}function T(F){return Array.from(F,(G)=>G.toString(16).padStart(2,"0")).join("")}class q extends k{statusCode;requestId;constructor(F,G,J,X){super(F,v(G),{statusCode:G,provider:"ses"});this.statusCode=G;this.requestId=X;this.name="SESError",this.code=J}}class g{accessKeyId;secretAccessKey;region;sessionToken;dkim;constructor(F){this.accessKeyId=F.accessKeyId,this.secretAccessKey=F.secretAccessKey,this.region=F.region??"us-east-1",this.sessionToken=F.sessionToken,this.dkim=F.dkim}async send(F){let G=await b(F.attachments),J={...F,attachments:G},X=C(F.from)[0],L=X?R(X):"",Y=D(F.to),Z=F.cc?D(F.cc):[],Q=F.bcc?D(F.bcc):[],$={ToAddresses:Y,CcAddresses:Z,BccAddresses:Q},O;if(G.length>0){let M=await A(J,this.dkim);O={FromEmailAddress:L,Destination:$,Content:{Raw:{Data:x(M.raw).replace(/\r\n/g,"")}}}}else O={FromEmailAddress:L,Destination:$,Content:{Simple:{Subject:{Data:F.subject,Charset:"UTF-8"},Body:{...F.text?{Text:{Data:F.text,Charset:"UTF-8"}}:{},...F.html?{Html:{Data:F.html,Charset:"UTF-8"}}:{}}}}};let V=JSON.stringify(O),P=`https://email.${this.region}.amazonaws.com/v2/email/outbound-emails`,I=await B({method:"POST",url:P,headers:{"content-type":"application/json"},body:V,credentials:{accessKeyId:this.accessKeyId,secretAccessKey:this.secretAccessKey,region:this.region,service:"ses",...this.sessionToken?{sessionToken:this.sessionToken}:{}}}),U=await fetch(P,{method:"POST",headers:I.headers,body:V}),W=await U.json();if(!U.ok)throw new q(W.message??"SES API error",U.status,W.Code??"",U.headers.get("x-amzn-requestid")??"");let _=W.MessageId??"";return{messageId:_,accepted:[...Y,...Z,...Q],rejected:[],response:`MessageId: ${_}`,envelope:{from:X?.address??"",to:Y}}}async verify(){try{let F=`https://email.${this.region}.amazonaws.com/v2/email/configuration-sets`,G=await B({method:"GET",url:F,headers:{},body:"",credentials:{accessKeyId:this.accessKeyId,secretAccessKey:this.secretAccessKey,region:this.region,service:"ses",...this.sessionToken?{sessionToken:this.sessionToken}:{}}}),J=await fetch(F,{method:"GET",headers:G.headers}),X=await J.json().catch(()=>({}));if(!J.ok)return{ok:!1,provider:"ses",message:X.message??`HTTP ${J.status}`};return{ok:!0,provider:"ses",message:"Credentials are valid"}}catch(F){return{ok:!1,provider:"ses",message:F instanceof Error?F.message:String(F)}}}}export{g as SESTransport,q as SESError};
|
|
6
6
|
|
|
7
|
-
//# debugId=
|
|
7
|
+
//# debugId=329EDD5676E5B22B64756E2164756E21
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
"/**\n * @module\n * AWS SES v2 HTTP transport for sently.\n * Signs requests with AWS Signature Version 4 using Web Crypto.\n * Works on Node.js, Bun, Deno, and Cloudflare Workers.\n *\n * @example\n * ```ts\n * import { SESTransport } from \"sently/transports/ses\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new SESTransport({\n * accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n * region: \"us-east-1\",\n * }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses, toMIMEHeader } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport { buildMIME } from \"../core/mime.js\";\nimport { signRequest } from \"../core/sigv4.js\";\nimport type { MailOptions, SESConfig, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Error thrown when the AWS SES API returns a non-success response. */\nexport class SESError extends SentlyError {\n /** Creates an AWS SES API error with status code, error code, and request ID. */\n constructor(\n message: string,\n public readonly statusCode: number,\n awsCode: string,\n public readonly requestId: string,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"ses\",\n });\n this.name = \"SESError\";\n this.code = awsCode;\n }\n}\n\n/**\n * AWS SES v2 HTTP API transport.\n */\nexport class SESTransport implements Transport {\n /** AWS access key ID for SigV4 signing. */\n private readonly accessKeyId: string;\n /** AWS secret access key for SigV4 signing. */\n private readonly secretAccessKey: string;\n /** AWS region for the SES endpoint. */\n private readonly region: string;\n /** Optional STS session token for temporary credentials. */\n private readonly sessionToken: string | undefined;\n /** Optional DKIM config for raw MIME attachment sends. */\n private readonly dkim: SESConfig[\"dkim\"];\n\n /** Creates an SES transport with AWS credentials. */\n constructor(config: SESConfig) {\n this.accessKeyId = config.accessKeyId;\n this.secretAccessKey = config.secretAccessKey;\n this.region = config.region ?? \"us-east-1\";\n this.sessionToken = config.sessionToken;\n this.dkim = config.dkim;\n }\n\n /** Sends an email via the AWS SES v2 HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const resolvedOptions = { ...options, attachments };\n const from = parseAddresses(options.from)[0];\n const fromEmail = from ? toMIMEHeader(from) : \"\";\n const toEmails = extractEmails(options.to);\n const ccEmails = options.cc ? extractEmails(options.cc) : [];\n const bccEmails = options.bcc ? extractEmails(options.bcc) : [];\n\n const destination = {\n ToAddresses: toEmails,\n CcAddresses: ccEmails,\n BccAddresses: bccEmails,\n };\n\n let requestBody: Record<string, unknown>;\n\n if (attachments.length > 0) {\n const mime = await buildMIME(resolvedOptions, this.dkim);\n requestBody = {\n FromEmailAddress: fromEmail,\n Destination: destination,\n Content: {\n Raw: {\n Data: encodeBase64(mime.raw).replace(/\\r\\n/g, \"\"),\n },\n },\n };\n } else {\n requestBody = {\n FromEmailAddress: fromEmail,\n Destination: destination,\n Content: {\n Simple: {\n Subject: { Data: options.subject, Charset: \"UTF-8\" },\n Body: {\n ...(options.text ? { Text: { Data: options.text, Charset: \"UTF-8\" } } : {}),\n ...(options.html ? { Html: { Data: options.html, Charset: \"UTF-8\" } } : {}),\n },\n },\n },\n };\n }\n\n const body = JSON.stringify(requestBody);\n const url = `https://email.${this.region}.amazonaws.com/v2/email/outbound-emails`;\n const signed = await signRequest({\n method: \"POST\",\n url,\n headers: {\n \"content-type\": \"application/json\",\n },\n body,\n credentials: {\n accessKeyId: this.accessKeyId,\n secretAccessKey: this.secretAccessKey,\n region: this.region,\n service: \"ses\",\n ...(this.sessionToken ? { sessionToken: this.sessionToken } : {}),\n },\n });\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: signed.headers,\n body,\n });\n\n const payload = (await response.json()) as {\n MessageId?: string;\n message?: string;\n Code?: string;\n };\n\n if (!response.ok) {\n throw new SESError(\n payload.message ?? \"SES API error\",\n response.status,\n payload.Code ?? \"\",\n response.headers.get(\"x-amzn-requestid\") ?? \"\",\n );\n }\n\n const messageId = payload.MessageId ?? \"\";\n return {\n messageId,\n accepted: [...toEmails, ...ccEmails, ...bccEmails],\n rejected: [],\n response: `MessageId: ${messageId}`,\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies AWS credentials by listing SES configuration sets. */\n async verify(): Promise<VerifyResult> {\n try {\n const url = `https://email.${this.region}.amazonaws.com/v2/email/configuration-sets`;\n const signed = await signRequest({\n method: \"GET\",\n url,\n headers: {},\n body: \"\",\n credentials: {\n accessKeyId: this.accessKeyId,\n secretAccessKey: this.secretAccessKey,\n region: this.region,\n service: \"ses\",\n ...(this.sessionToken ? { sessionToken: this.sessionToken } : {}),\n },\n });\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: signed.headers,\n });\n\n const payload = (await response.json().catch(() => ({}))) as { message?: string };\n\n if (!response.ok) {\n return {\n ok: false,\n provider: \"ses\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return { ok: true, provider: \"ses\", message: \"Credentials are valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"ses\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
|
|
7
7
|
],
|
|
8
8
|
"mappings": "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",
|
|
9
|
-
"debugId": "
|
|
9
|
+
"debugId": "329EDD5676E5B22B64756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
package/dist/transports/smtp.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{h as b,i as c,j as d,k as e,l as f,m as g}from"../chunk-
|
|
1
|
+
import{h as b,i as c,j as d,k as e,l as f,m as g}from"../chunk-fjq6b8gj.js";import"../chunk-rm78s1ek.js";import"../chunk-gkkv7yqm.js";import"../chunk-r7ee9tk7.js";import"../chunk-gh5n759p.js";import{O as a}from"../chunk-cvrbfke9.js";import"../chunk-d5rwm3br.js";import"../chunk-a1c87hn1.js";import"../chunk-keksa6fx.js";export{c as resolveSMTPConfig,g as readSMTPResponse,d as openSMTPSession,a as encodeLine,e as deliverSMTPMessage,f as closeSMTPSession,b as SMTPTransport};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=F92133D9309E38BF64756E2164756E21
|
package/dist/webhooks/brevo.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{d as F,e as u,f as G,g as H}from"../chunk-q8k3m6qe.js";import"../chunk-
|
|
1
|
+
import{d as F,e as u,f as G,g as H}from"../chunk-q8k3m6qe.js";import"../chunk-keksa6fx.js";var K={delivered:"delivered",hard_bounce:"bounced",hardBounce:"bounced",soft_bounce:"bounced",soft_bounced:"bounced",softBounce:"bounced",blocked:"bounced",invalid:"bounced",invalid_email:"bounced",deferred:"deferred",spam:"complained",opened:"opened",unique_opened:"opened",uniqueOpened:"opened",click:"clicked",clicked:"clicked",unique_click:"clicked",uniqueClicked:"clicked",sent:"unknown",request:"unknown",unsubscribe:"complained",unsubscribed:"complained"};function L(f){return u(f.ts_event)??u(f.ts)??u(f.date_event)??u(f.date)??u(f.ts_epoch!==void 0?Number(f.ts_epoch):void 0)}function Q(f){if(typeof f["message-id"]==="string")return f["message-id"];if(typeof f.messageId==="string")return f.messageId;return}function U(f){return G(f).flatMap((x)=>{if(typeof x!=="object"||x===null)return[];let q=x,J=typeof q.event==="string"?q.event:"",C=L(q),D=Q(q);return[H({provider:"brevo",type:F(J,K),...D!==void 0?{messageId:D}:{},...typeof q.email==="string"?{recipient:q.email}:{},...C!==void 0?{timestamp:C}:{},raw:x})]})}export{U as parse};
|
|
2
2
|
|
|
3
3
|
//# debugId=916BC5D0AADEB5C764756E2164756E21
|
package/dist/webhooks/mailgun.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{a as G,c as H}from"../chunk-pqp843mh.js";import{d as Y,e as Z,g as $}from"../chunk-q8k3m6qe.js";import"../chunk-
|
|
1
|
+
import{a as G,c as H}from"../chunk-pqp843mh.js";import{d as Y,e as Z,g as $}from"../chunk-q8k3m6qe.js";import"../chunk-keksa6fx.js";var R={accepted:"unknown",delivered:"delivered",rejected:"bounced",complained:"complained",opened:"opened",clicked:"clicked",stored:"unknown",unsubscribed:"unknown"};function U(z){let J=typeof z.event==="string"?z.event:"";if(J==="failed")return z.severity==="temporary"?"deferred":"bounced";return Y(J,R)}function b(z){if(!z)return;let J=z.headers;if(typeof J==="object"&&J!==null){let C=J["message-id"];if(typeof C==="string")return C}return typeof z["message-id"]==="string"?z["message-id"]:void 0}function N(z){if(typeof z!=="object"||z===null)return[];let J=z,C=typeof J["event-data"]==="object"&&J["event-data"]!==null?J["event-data"]:J,O=typeof C.message==="object"&&C.message!==null?C.message:void 0,Q=b(O),W=Z(C.timestamp);return[$({provider:"mailgun",type:U(C),...Q!==void 0?{messageId:Q}:{},...typeof C.recipient==="string"?{recipient:C.recipient}:{},...W!==void 0?{timestamp:W}:{},raw:z})]}async function q(z,J,C,O){let Q=await crypto.subtle.importKey("raw",new TextEncoder().encode(O),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),W=new TextEncoder().encode(z+J),L=await crypto.subtle.sign("HMAC",Q,W),P=new Uint8Array(L),X=H(C);if(X===null)return!1;return G(P,X)}async function S(z,J){if(typeof z!=="object"||z===null)return!1;let C=z.signature;if(typeof C!=="object"||C===null)return!1;let O=C;if(typeof O.timestamp!=="string"||typeof O.token!=="string"||typeof O.signature!=="string")return!1;return q(O.timestamp,O.token,O.signature,J)}export{q as verifySignature,S as verifyPayload,N as parse};
|
|
2
2
|
|
|
3
3
|
//# debugId=7FB21CD9E29B4FE264756E2164756E21
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{d as u,e as q,g as x}from"../chunk-q8k3m6qe.js";import"../chunk-
|
|
1
|
+
import{d as u,e as q,g as x}from"../chunk-q8k3m6qe.js";import"../chunk-keksa6fx.js";var C={Delivery:"delivered",Bounce:"bounced",SpamComplaint:"complained",Open:"opened",Click:"clicked",SubscriptionChange:"unknown",Inbound:"unknown"};function F(g){if(typeof g!=="object"||g===null)return[];let f=g,B=typeof f.RecordType==="string"?f.RecordType:"";return[x({provider:"postmark",type:u(B,C),...typeof f.MessageID==="string"?{messageId:f.MessageID}:{},...typeof f.Recipient==="string"?{recipient:f.Recipient}:typeof f.Email==="string"?{recipient:f.Email}:{},...q(f.DeliveredAt??f.BouncedAt??f.ReceivedAt??f.ChangedAt)!==void 0?{timestamp:q(f.DeliveredAt??f.BouncedAt??f.ReceivedAt??f.ChangedAt)}:{},raw:g})]}export{F as parse};
|
|
2
2
|
|
|
3
3
|
//# debugId=7CD05BB54D484C3864756E2164756E21
|
package/dist/webhooks/resend.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{a as $,b as A}from"../chunk-pqp843mh.js";import{d as X,e as Y,g as Z}from"../chunk-q8k3m6qe.js";import"../chunk-
|
|
1
|
+
import{a as $,b as A}from"../chunk-pqp843mh.js";import{d as X,e as Y,g as Z}from"../chunk-q8k3m6qe.js";import"../chunk-keksa6fx.js";var j={"email.sent":"unknown","email.scheduled":"unknown","email.delivered":"delivered","email.delivery_delayed":"deferred","email.bounced":"bounced","email.failed":"bounced","email.complained":"complained","email.opened":"opened","email.clicked":"clicked","email.received":"unknown","email.suppressed":"unknown"};function k(G){let F=G.startsWith("whsec_")?G.slice(6):G,K=atob(F),J=new Uint8Array(K.length);for(let z=0;z<K.length;z++)J[z]=K.charCodeAt(z);return J.buffer}function H(G){if(typeof G!=="object"||G===null)return[];let F=G,K=typeof F.type==="string"?F.type:"",J=typeof F.data==="object"&&F.data!==null?F.data:{},z=J.to,L=Array.isArray(z)?typeof z[0]==="string"?z[0]:void 0:typeof z==="string"?z:void 0,O=Y(F.created_at??J.created_at);return[Z({provider:"resend",type:X(K,j),...typeof J.email_id==="string"?{messageId:J.email_id}:{},...L!==void 0?{recipient:L}:{},...O!==void 0?{timestamp:O}:{},raw:G})]}async function I(G,F,K){let J=F["svix-id"],z=F["svix-timestamp"],L=F["svix-signature"];if(!J||!z||!L)return!1;let O=`${J}.${z}.${G}`,D=await crypto.subtle.importKey("raw",k(K),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),M=await crypto.subtle.sign("HMAC",D,new TextEncoder().encode(O)),P=new Uint8Array(M);for(let W of L.split(" ")){let Q=W.split(",",2)[1];if(Q===void 0)continue;let U=A(Q);if(U===null)continue;if($(P,U))return!0}return!1}export{I as verifySignature,H as parse};
|
|
2
2
|
|
|
3
3
|
//# debugId=2EA62D07463E258664756E2164756E21
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{d as w,e as x,f as B,g as C}from"../chunk-q8k3m6qe.js";import"../chunk-
|
|
1
|
+
import{d as w,e as x,f as B,g as C}from"../chunk-q8k3m6qe.js";import"../chunk-keksa6fx.js";var F={processed:"unknown",delivered:"delivered",deferred:"deferred",bounce:"bounced",dropped:"bounced",spamreport:"complained",unsubscribe:"complained",group_unsubscribe:"complained",group_resubscribe:"unknown",open:"opened",click:"clicked"};function H(q){let g=typeof q.event==="string"?q.event:"";if(g==="bounce"&&q.type==="blocked")return"deferred";return w(g,F)}function K(q){return B(q).flatMap((g)=>{if(typeof g!=="object"||g===null)return[];let f=g,u=x(f.timestamp);return[C({provider:"sendgrid",type:H(f),...typeof f.sg_message_id==="string"?{messageId:f.sg_message_id}:typeof f["smtp-id"]==="string"?{messageId:f["smtp-id"]}:{},...typeof f.email==="string"?{recipient:f.email}:{},...u!==void 0?{timestamp:u}:{},raw:g})]})}export{K as parse};
|
|
2
2
|
|
|
3
3
|
//# debugId=D4C7DDEE719D449764756E2164756E21
|