sently 0.4.7 → 0.5.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.
Files changed (111) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +106 -22
  3. package/dist/adapters/bun.js +1 -1
  4. package/dist/adapters/cf.js +2 -2
  5. package/dist/adapters/cf.js.map +1 -1
  6. package/dist/adapters/deno.js +1 -1
  7. package/dist/adapters/node.js +1 -1
  8. package/dist/auth/oauth2.js +2 -2
  9. package/dist/auth/oauth2.js.map +1 -1
  10. package/dist/chunk-9ea4hx7m.js +4 -0
  11. package/dist/chunk-9ea4hx7m.js.map +11 -0
  12. package/dist/chunk-9p4vev6j.js +13 -0
  13. package/dist/{chunk-wgtbr6ge.js.map → chunk-9p4vev6j.js.map} +2 -2
  14. package/dist/{chunk-2t6hjer3.js → chunk-aeqxkeh3.js} +3 -3
  15. package/dist/{chunk-2t6hjer3.js.map → chunk-aeqxkeh3.js.map} +1 -1
  16. package/dist/chunk-b0qkndv9.js +4 -0
  17. package/dist/{chunk-va2awz12.js.map → chunk-b0qkndv9.js.map} +4 -5
  18. package/dist/{chunk-jfs80vhp.js → chunk-c8ya9em0.js} +2 -2
  19. package/dist/{chunk-jfs80vhp.js.map → chunk-c8ya9em0.js.map} +1 -1
  20. package/dist/{chunk-6yggz45h.js → chunk-gxq2aamv.js} +2 -2
  21. package/dist/{chunk-6yggz45h.js.map → chunk-gxq2aamv.js.map} +1 -1
  22. package/dist/chunk-k0gw3qmb.js +4 -0
  23. package/dist/chunk-k0gw3qmb.js.map +10 -0
  24. package/dist/{chunk-sqn04kae.js → chunk-mg8m26nr.js} +2 -2
  25. package/dist/{chunk-sqn04kae.js.map → chunk-mg8m26nr.js.map} +1 -1
  26. package/dist/chunk-pqp843mh.js +4 -0
  27. package/dist/chunk-pqp843mh.js.map +10 -0
  28. package/dist/chunk-q8k3m6qe.js +4 -0
  29. package/dist/chunk-q8k3m6qe.js.map +10 -0
  30. package/dist/chunk-qp4y0kp2.js +4 -0
  31. package/dist/chunk-qp4y0kp2.js.map +10 -0
  32. package/dist/chunk-vws105eb.js +4 -0
  33. package/dist/chunk-vws105eb.js.map +10 -0
  34. package/dist/{chunk-dgkh77yp.js → chunk-zkczgj2c.js} +3 -3
  35. package/dist/{chunk-dgkh77yp.js.map → chunk-zkczgj2c.js.map} +1 -1
  36. package/dist/core/idempotency-key.d.ts +6 -0
  37. package/dist/core/rate-limiter.d.ts +23 -0
  38. package/dist/core/smtp.js +2 -2
  39. package/dist/core/smtp.js.map +1 -1
  40. package/dist/core/types.d.ts +35 -0
  41. package/dist/detect.js +2 -2
  42. package/dist/detect.js.map +2 -2
  43. package/dist/dkim.js +2 -2
  44. package/dist/dkim.js.map +1 -1
  45. package/dist/idempotency.d.ts +46 -0
  46. package/dist/idempotency.js +3 -0
  47. package/dist/idempotency.js.map +10 -0
  48. package/dist/index.d.ts +32 -0
  49. package/dist/index.js +30 -0
  50. package/dist/mailer.d.ts +1 -0
  51. package/dist/mailer.js +2 -2
  52. package/dist/mailer.js.map +1 -1
  53. package/dist/plugins/react.d.ts +35 -0
  54. package/dist/plugins/react.js +3 -0
  55. package/dist/plugins/react.js.map +10 -0
  56. package/dist/plugins/template.js +1 -1
  57. package/dist/pool/pool.d.ts +1 -23
  58. package/dist/pool/pool.js +2 -2
  59. package/dist/pool/pool.js.map +3 -3
  60. package/dist/react.d.ts +20 -0
  61. package/dist/react.js +4 -0
  62. package/dist/transports/brevo.js +2 -2
  63. package/dist/transports/brevo.js.map +2 -2
  64. package/dist/transports/mailgun.js +2 -2
  65. package/dist/transports/mailgun.js.map +2 -2
  66. package/dist/transports/postmark.js +2 -2
  67. package/dist/transports/postmark.js.map +2 -2
  68. package/dist/transports/preview.js +2 -2
  69. package/dist/transports/preview.js.map +2 -2
  70. package/dist/transports/resend.d.ts +16 -0
  71. package/dist/transports/resend.js +2 -2
  72. package/dist/transports/resend.js.map +3 -3
  73. package/dist/transports/retry.d.ts +2 -0
  74. package/dist/transports/retry.js +2 -2
  75. package/dist/transports/retry.js.map +3 -3
  76. package/dist/transports/sendgrid.d.ts +8 -0
  77. package/dist/transports/sendgrid.js +2 -2
  78. package/dist/transports/sendgrid.js.map +3 -3
  79. package/dist/transports/ses.js +2 -2
  80. package/dist/transports/ses.js.map +2 -2
  81. package/dist/transports/smtp.js +2 -2
  82. package/dist/transports/smtp.js.map +1 -1
  83. package/dist/webhooks/brevo.d.ts +3 -0
  84. package/dist/webhooks/brevo.js +3 -0
  85. package/dist/webhooks/brevo.js.map +10 -0
  86. package/dist/webhooks/mailgun.d.ts +13 -0
  87. package/dist/webhooks/mailgun.js +3 -0
  88. package/dist/webhooks/mailgun.js.map +10 -0
  89. package/dist/webhooks/postmark.d.ts +3 -0
  90. package/dist/webhooks/postmark.js +3 -0
  91. package/dist/webhooks/postmark.js.map +10 -0
  92. package/dist/webhooks/resend.d.ts +12 -0
  93. package/dist/webhooks/resend.js +3 -0
  94. package/dist/webhooks/resend.js.map +10 -0
  95. package/dist/webhooks/sendgrid.d.ts +3 -0
  96. package/dist/webhooks/sendgrid.js +3 -0
  97. package/dist/webhooks/sendgrid.js.map +10 -0
  98. package/dist/webhooks/ses.d.ts +6 -0
  99. package/dist/webhooks/ses.js +3 -0
  100. package/dist/webhooks/ses.js.map +10 -0
  101. package/dist/webhooks/timing-safe-equal.d.ts +9 -0
  102. package/dist/webhooks/timing-safe-equal.js +3 -0
  103. package/dist/webhooks/timing-safe-equal.js.map +9 -0
  104. package/dist/webhooks/types.d.ts +32 -0
  105. package/dist/webhooks.d.ts +26 -0
  106. package/dist/webhooks.js +13 -0
  107. package/package.json +29 -1
  108. package/dist/chunk-2kcwa9gt.js +0 -4
  109. package/dist/chunk-2kcwa9gt.js.map +0 -11
  110. package/dist/chunk-va2awz12.js +0 -4
  111. package/dist/chunk-wgtbr6ge.js +0 -13
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/webhooks/ses.ts"],
4
+ "sourcesContent": [
5
+ "import type { EmailEvent } from \"./types.js\";\nimport { emailEvent, mapEventType, parseTimestamp } from \"./types.js\";\n\nconst SES_EVENT_MAP: Record<string, EmailEvent[\"type\"]> = {\n Delivery: \"delivered\",\n Bounce: \"bounced\",\n Complaint: \"complained\",\n Open: \"opened\",\n Click: \"clicked\",\n DeliveryDelay: \"deferred\",\n Send: \"unknown\",\n Reject: \"bounced\",\n \"Rendering Failure\": \"bounced\",\n Subscription: \"unknown\",\n};\n\nfunction resolveSesEventType(record: Record<string, unknown>): string {\n if (typeof record.eventType === \"string\") {\n return record.eventType;\n }\n if (typeof record.notificationType === \"string\") {\n return record.notificationType;\n }\n return \"\";\n}\n\nfunction extractSesRecipient(record: Record<string, unknown>): string | undefined {\n const delivery =\n typeof record.delivery === \"object\" && record.delivery !== null\n ? (record.delivery as Record<string, unknown>)\n : undefined;\n if (\n delivery &&\n Array.isArray(delivery.recipients) &&\n typeof delivery.recipients[0] === \"string\"\n ) {\n return delivery.recipients[0];\n }\n\n const bounce =\n typeof record.bounce === \"object\" && record.bounce !== null\n ? (record.bounce as Record<string, unknown>)\n : undefined;\n if (bounce && Array.isArray(bounce.bouncedRecipients)) {\n const first = bounce.bouncedRecipients[0];\n if (\n typeof first === \"object\" &&\n first !== null &&\n typeof (first as Record<string, unknown>).emailAddress === \"string\"\n ) {\n return (first as Record<string, unknown>).emailAddress as string;\n }\n }\n\n const complaint =\n typeof record.complaint === \"object\" && record.complaint !== null\n ? (record.complaint as Record<string, unknown>)\n : undefined;\n if (complaint && Array.isArray(complaint.complainedRecipients)) {\n const first = complaint.complainedRecipients[0];\n if (\n typeof first === \"object\" &&\n first !== null &&\n typeof (first as Record<string, unknown>).emailAddress === \"string\"\n ) {\n return (first as Record<string, unknown>).emailAddress as string;\n }\n }\n\n const mail =\n typeof record.mail === \"object\" && record.mail !== null\n ? (record.mail as Record<string, unknown>)\n : undefined;\n if (Array.isArray(mail?.destination) && typeof mail.destination[0] === \"string\") {\n return mail.destination[0];\n }\n\n return undefined;\n}\n\nfunction parseSesEvent(record: Record<string, unknown>): EmailEvent[] {\n const eventType = resolveSesEventType(record);\n const mail =\n typeof record.mail === \"object\" && record.mail !== null\n ? (record.mail as Record<string, unknown>)\n : undefined;\n\n const bounce =\n typeof record.bounce === \"object\" && record.bounce !== null\n ? (record.bounce as Record<string, unknown>)\n : undefined;\n const complaint =\n typeof record.complaint === \"object\" && record.complaint !== null\n ? (record.complaint as Record<string, unknown>)\n : undefined;\n const open =\n typeof record.open === \"object\" && record.open !== null\n ? (record.open as Record<string, unknown>)\n : undefined;\n const click =\n typeof record.click === \"object\" && record.click !== null\n ? (record.click as Record<string, unknown>)\n : undefined;\n const deliveryDelay =\n typeof record.deliveryDelay === \"object\" && record.deliveryDelay !== null\n ? (record.deliveryDelay as Record<string, unknown>)\n : undefined;\n\n const recipient = extractSesRecipient(record);\n const timestamp = parseTimestamp(\n record.timestamp ??\n bounce?.timestamp ??\n complaint?.timestamp ??\n open?.timestamp ??\n click?.timestamp ??\n deliveryDelay?.timestamp ??\n mail?.timestamp,\n );\n\n return [\n emailEvent({\n provider: \"ses\",\n type: mapEventType(eventType, SES_EVENT_MAP),\n ...(typeof mail?.messageId === \"string\" ? { messageId: mail.messageId } : {}),\n ...(recipient !== undefined ? { recipient } : {}),\n ...(timestamp !== undefined ? { timestamp } : {}),\n raw: record,\n }),\n ];\n}\n\n/**\n * Parse an AWS SES webhook payload (SNS envelope) into normalized {@link EmailEvent} records.\n * Handles SubscriptionConfirmation (returns `[]`) and double-encoded Notification messages.\n */\nexport function parse(payload: unknown): EmailEvent[] {\n if (typeof payload !== \"object\" || payload === null) {\n return [];\n }\n\n const envelope = payload as Record<string, unknown>;\n const snsType = typeof envelope.Type === \"string\" ? envelope.Type : \"\";\n\n if (snsType === \"SubscriptionConfirmation\") {\n return [];\n }\n\n if (snsType !== \"Notification\") {\n return [];\n }\n\n const messageField = envelope.Message;\n let inner: unknown;\n\n if (typeof messageField === \"string\") {\n try {\n inner = JSON.parse(messageField);\n } catch {\n return [\n {\n provider: \"ses\",\n type: \"unknown\" as const,\n raw: payload,\n },\n ];\n }\n } else if (typeof messageField === \"object\" && messageField !== null) {\n inner = messageField;\n } else {\n return [];\n }\n\n if (typeof inner !== \"object\" || inner === null) {\n return [\n {\n provider: \"ses\",\n type: \"unknown\" as const,\n raw: payload,\n },\n ];\n }\n\n return parseSesEvent(inner as Record<string, unknown>);\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "C5FC4A2C6082819B64756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Constant-time byte comparison for cryptographic signatures.
3
+ * Never short-circuits the XOR loop; length mismatch is folded into `diff`.
4
+ */
5
+ export declare function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean;
6
+ /** Decode a base64 string to bytes; returns null on invalid input. */
7
+ export declare function decodeBase64Bytes(value: string): Uint8Array | null;
8
+ /** Decode a lowercase hex string to bytes; returns null on invalid input. */
9
+ export declare function decodeHexBytes(value: string): Uint8Array | null;
@@ -0,0 +1,3 @@
1
+ import{a,b,c}from"../chunk-pqp843mh.js";import"../chunk-mg8m26nr.js";export{a as timingSafeEqual,c as decodeHexBytes,b as decodeBase64Bytes};
2
+
3
+ //# debugId=CA7D50FE7388E14864756E2164756E21
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [
5
+ ],
6
+ "mappings": "",
7
+ "debugId": "CA7D50FE7388E14864756E2164756E21",
8
+ "names": []
9
+ }
@@ -0,0 +1,32 @@
1
+ /** Normalized email lifecycle event from any provider webhook. */
2
+ export interface EmailEvent {
3
+ /** Provider identifier (e.g. `"resend"`, `"sendgrid"`). */
4
+ provider: string;
5
+ /** Normalized event type. */
6
+ type: "delivered" | "bounced" | "complained" | "opened" | "clicked" | "deferred" | "unknown";
7
+ /** Provider message identifier when available. */
8
+ messageId?: string;
9
+ /** Recipient email address when available. */
10
+ recipient?: string;
11
+ /** Event timestamp when available. */
12
+ timestamp?: Date;
13
+ /** Original provider payload for debugging or custom handling. */
14
+ raw: unknown;
15
+ }
16
+ /** Map a provider-specific event string to a normalized {@link EmailEvent.type}. */
17
+ export type EventTypeMapper = (event: string) => EmailEvent["type"];
18
+ /** Default mapper — returns `"unknown"` for unrecognized events. */
19
+ export declare function mapEventType(event: string, mapping: Record<string, EmailEvent["type"]>): EmailEvent["type"];
20
+ /** Parse an ISO or Unix timestamp into a Date, or undefined when invalid. */
21
+ export declare function parseTimestamp(value: unknown): Date | undefined;
22
+ /** Ensure payload is an array (for providers that batch events). */
23
+ export declare function asArray(payload: unknown): unknown[];
24
+ /** Build an {@link EmailEvent} respecting exact optional property types. */
25
+ export declare function emailEvent(input: {
26
+ provider: string;
27
+ type: EmailEvent["type"];
28
+ raw: unknown;
29
+ messageId?: string | undefined;
30
+ recipient?: string | undefined;
31
+ timestamp?: Date | undefined;
32
+ }): EmailEvent;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @module
3
+ * Provider webhook parsers — normalize delivery events into {@link EmailEvent}.
4
+ *
5
+ * Pure parsing only; no server framework required. Signature verification
6
+ * helpers are optional and never required to call `parse`.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { parseResendWebhook } from "sently/webhooks";
11
+ *
12
+ * const events = parseResendWebhook(await request.json());
13
+ * for (const event of events) {
14
+ * if (event.type === "bounced") {
15
+ * // handle bounce
16
+ * }
17
+ * }
18
+ * ```
19
+ */
20
+ export { parse as parseBrevoWebhook } from "./webhooks/brevo.js";
21
+ export { parse as parseMailgunWebhook, verifyPayload as verifyMailgunPayload, verifySignature as verifyMailgunSignature, } from "./webhooks/mailgun.js";
22
+ export { parse as parsePostmarkWebhook } from "./webhooks/postmark.js";
23
+ export { parse as parseResendWebhook, verifySignature as verifyResendSignature, } from "./webhooks/resend.js";
24
+ export { parse as parseSendGridWebhook } from "./webhooks/sendgrid.js";
25
+ export { parse as parseSesWebhook } from "./webhooks/ses.js";
26
+ export type { EmailEvent } from "./webhooks/types.js";
@@ -0,0 +1,13 @@
1
+ export { parse as parseBrevoWebhook } from "./webhooks/brevo.js";
2
+ export {
3
+ parse as parseMailgunWebhook,
4
+ verifyPayload as verifyMailgunPayload,
5
+ verifySignature as verifyMailgunSignature,
6
+ } from "./webhooks/mailgun.js";
7
+ export { parse as parsePostmarkWebhook } from "./webhooks/postmark.js";
8
+ export {
9
+ parse as parseResendWebhook,
10
+ verifySignature as verifyResendSignature,
11
+ } from "./webhooks/resend.js";
12
+ export { parse as parseSendGridWebhook } from "./webhooks/sendgrid.js";
13
+ export { parse as parseSesWebhook } from "./webhooks/ses.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sently",
3
- "version": "0.4.7",
3
+ "version": "0.5.2",
4
4
  "description": "Runtime-agnostic email library for Node.js, Bun, Deno, and Cloudflare Workers. ESM-native Nodemailer alternative with zero dependencies.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -41,7 +41,11 @@
41
41
  "devDependencies": {
42
42
  "@biomejs/biome": "2.4.16",
43
43
  "@modelcontextprotocol/sdk": "1.29.0",
44
+ "@react-email/render": "1.1.2",
44
45
  "@types/node": "25.9.1",
46
+ "@types/react": "19.2.15",
47
+ "react": "19.2.6",
48
+ "react-dom": "19.2.6",
45
49
  "typescript": "6.0.3"
46
50
  },
47
51
  "keywords": [
@@ -157,6 +161,30 @@
157
161
  "./dkim": {
158
162
  "import": "./dist/dkim.js",
159
163
  "types": "./dist/dkim.d.ts"
164
+ },
165
+ "./react": {
166
+ "import": "./dist/react.js",
167
+ "types": "./dist/react.d.ts"
168
+ },
169
+ "./idempotency": {
170
+ "import": "./dist/idempotency.js",
171
+ "types": "./dist/idempotency.d.ts"
172
+ },
173
+ "./webhooks": {
174
+ "import": "./dist/webhooks.js",
175
+ "types": "./dist/webhooks.d.ts"
176
+ }
177
+ },
178
+ "peerDependencies": {
179
+ "@react-email/render": ">=0.0.9",
180
+ "react": ">=18.0.0"
181
+ },
182
+ "peerDependenciesMeta": {
183
+ "@react-email/render": {
184
+ "optional": true
185
+ },
186
+ "react": {
187
+ "optional": true
160
188
  }
161
189
  }
162
190
  }
@@ -1,4 +0,0 @@
1
- async function V(k,z){if(!z||z.length===0)return k;let B=k;for(let A of z)B=await A(B);return B}async function Z(k){return new W(k.transport,k.plugins??[])}class W{transport;plugins;constructor(k,z=[]){this.transport=k;this.plugins=z}async send(k){let z=await V(k,this.plugins);return this.transport.send(z)}async sendBulk(k,z){let B=z?.concurrency??1,A=Array(k.length),J=[...k.entries()],K=0;await new Promise((G)=>{if(k.length===0){G();return}let L=()=>{if(J.length===0&&K===0)G()},U=()=>{if(J.length===0){L();return}let C=J.shift();if(C===void 0){L();return}let[H,Q]=C;K++,this.send(Q).then((F)=>{A[H]={status:"sent",result:F},z?.onSuccess?.(Q,H,F)}).catch((F)=>{A[H]={status:"failed",error:F},z?.onError?.(Q,H,F)}).finally(()=>{K--,U(),L()})};for(let C=0;C<B;C++)U()});let S=0,T=0;for(let G of A)if(G.status==="sent")S++;else T++;return{total:k.length,sent:S,failed:T,results:A}}verify(){if(this.transport.verify)return this.transport.verify();return Promise.resolve({ok:!0,provider:"mailer"})}close(){if(this.transport.close)return this.transport.close();return Promise.resolve()}}
2
- export{Z as q,W as r};
3
-
4
- //# debugId=3495BDF6CCDE500D64756E2164756E21
@@ -1,11 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/core/plugin.ts", "../src/mailer.ts"],
4
- "sourcesContent": [
5
- "/**\n * @module\n * Plugin pipeline for sently.\n * Plugins transform MailOptions before message construction.\n * They run sequentially — each receives the previous plugin's output.\n *\n * @example\n * ```ts\n * import { runPlugins } from \"sently/core/plugin\";\n * const result = await runPlugins(options, [pluginA, pluginB]);\n * ```\n */\nimport type { MailOptions, MailPlugin } from \"./types.js\";\n\n/**\n * Run a list of plugins sequentially over MailOptions.\n * If plugins is empty or undefined, returns options unchanged.\n * Each plugin may be sync or async.\n *\n * @param options - the original mail options\n * @param plugins - ordered list of plugins to apply\n * @returns transformed mail options after all plugins have run\n */\nexport async function runPlugins(\n options: MailOptions,\n plugins: MailPlugin[] | undefined,\n): Promise<MailOptions> {\n if (!plugins || plugins.length === 0) {\n return options;\n }\n\n let current = options;\n for (const plugin of plugins) {\n current = await plugin(current);\n }\n return current;\n}\n",
6
- "/**\n * @module\n * Lightweight mailer factory for custom transports — no SMTP code in the bundle.\n *\n * Use this entry instead of `sently` when you pass a transport explicitly\n * (Resend, SendGrid, etc.) and want the smallest bundle size.\n *\n * @example\n * ```ts\n * import { createMailer } from \"sently/mailer\";\n * import { ResendTransport } from \"sently/transports/resend\";\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 { runPlugins } from \"./core/plugin.js\";\nimport type {\n BulkSendOptions,\n BulkSendResult,\n Mailer,\n MailOptions,\n MailPlugin,\n SendResult,\n Transport,\n TransportMailerOptions,\n VerifyResult,\n} from \"./core/types.js\";\n\nexport type { TransportMailerOptions };\n\n/**\n * Create a mailer that wraps a custom {@link Transport} (HTTP API, preview, retry, etc.).\n */\nexport async function createMailer(options: TransportMailerOptions): Promise<Mailer> {\n return new MailerImpl(options.transport, options.plugins ?? []);\n}\n\n/** Internal mailer implementation shared with the full `sently` entry. */\nexport class MailerImpl implements Mailer {\n constructor(\n private readonly transport: Transport,\n private readonly plugins: MailPlugin[] = [],\n ) {}\n\n async send(options: MailOptions): Promise<SendResult> {\n const processed = await runPlugins(options, this.plugins);\n return this.transport.send(processed);\n }\n\n async sendBulk(messages: MailOptions[], options?: BulkSendOptions): Promise<BulkSendResult> {\n const concurrency = options?.concurrency ?? 1;\n const results: BulkSendResult[\"results\"] = new Array(messages.length);\n const queue = [...messages.entries()];\n let active = 0;\n\n await new Promise<void>((resolve) => {\n if (messages.length === 0) {\n resolve();\n return;\n }\n\n const maybeDone = (): void => {\n if (queue.length === 0 && active === 0) {\n resolve();\n }\n };\n\n const processNext = (): void => {\n if (queue.length === 0) {\n maybeDone();\n return;\n }\n\n const entry = queue.shift();\n if (entry === undefined) {\n maybeDone();\n return;\n }\n\n const [index, message] = entry;\n active++;\n\n void this.send(message)\n .then((result) => {\n results[index] = { status: \"sent\", result };\n options?.onSuccess?.(message, index, result);\n })\n .catch((error: unknown) => {\n results[index] = { status: \"failed\", error };\n options?.onError?.(message, index, error);\n })\n .finally(() => {\n active--;\n processNext();\n maybeDone();\n });\n };\n\n for (let i = 0; i < concurrency; i++) {\n processNext();\n }\n });\n\n let sent = 0;\n let failed = 0;\n for (const result of results) {\n if (result.status === \"sent\") {\n sent++;\n } else {\n failed++;\n }\n }\n\n return {\n total: messages.length,\n sent,\n failed,\n results,\n };\n }\n\n verify(): Promise<VerifyResult> {\n if (this.transport.verify) {\n return this.transport.verify();\n }\n return Promise.resolve({ ok: true, provider: \"mailer\" });\n }\n\n close(): Promise<void> {\n if (this.transport.close) {\n return this.transport.close();\n }\n return Promise.resolve();\n }\n}\n"
7
- ],
8
- "mappings": "AAuBA,eAAsB,CAAU,CAC9B,EACA,EACsB,CACtB,GAAI,CAAC,GAAW,EAAQ,SAAW,EACjC,OAAO,EAGT,IAAI,EAAU,EACd,QAAW,KAAU,EACnB,EAAU,MAAM,EAAO,CAAO,EAEhC,OAAO,ECOT,eAAsB,CAAY,CAAC,EAAkD,CACnF,OAAO,IAAI,EAAW,EAAQ,UAAW,EAAQ,SAAW,CAAC,CAAC,EAIzD,MAAM,CAA6B,CAErB,UACA,QAFnB,WAAW,CACQ,EACA,EAAwB,CAAC,EAC1C,CAFiB,iBACA,oBAGb,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAY,MAAM,EAAW,EAAS,KAAK,OAAO,EACxD,OAAO,KAAK,UAAU,KAAK,CAAS,OAGhC,SAAQ,CAAC,EAAyB,EAAoD,CAC1F,IAAM,EAAc,GAAS,aAAe,EACtC,EAAyC,MAAM,EAAS,MAAM,EAC9D,EAAQ,CAAC,GAAG,EAAS,QAAQ,CAAC,EAChC,EAAS,EAEb,MAAM,IAAI,QAAc,CAAC,IAAY,CACnC,GAAI,EAAS,SAAW,EAAG,CACzB,EAAQ,EACR,OAGF,IAAM,EAAY,IAAY,CAC5B,GAAI,EAAM,SAAW,GAAK,IAAW,EACnC,EAAQ,GAIN,EAAc,IAAY,CAC9B,GAAI,EAAM,SAAW,EAAG,CACtB,EAAU,EACV,OAGF,IAAM,EAAQ,EAAM,MAAM,EAC1B,GAAI,IAAU,OAAW,CACvB,EAAU,EACV,OAGF,IAAO,EAAO,GAAW,EACzB,IAEK,KAAK,KAAK,CAAO,EACnB,KAAK,CAAC,IAAW,CAChB,EAAQ,GAAS,CAAE,OAAQ,OAAQ,QAAO,EAC1C,GAAS,YAAY,EAAS,EAAO,CAAM,EAC5C,EACA,MAAM,CAAC,IAAmB,CACzB,EAAQ,GAAS,CAAE,OAAQ,SAAU,OAAM,EAC3C,GAAS,UAAU,EAAS,EAAO,CAAK,EACzC,EACA,QAAQ,IAAM,CACb,IACA,EAAY,EACZ,EAAU,EACX,GAGL,QAAS,EAAI,EAAG,EAAI,EAAa,IAC/B,EAAY,EAEf,EAED,IAAI,EAAO,EACP,EAAS,EACb,QAAW,KAAU,EACnB,GAAI,EAAO,SAAW,OACpB,IAEA,SAIJ,MAAO,CACL,MAAO,EAAS,OAChB,OACA,SACA,SACF,EAGF,MAAM,EAA0B,CAC9B,GAAI,KAAK,UAAU,OACjB,OAAO,KAAK,UAAU,OAAO,EAE/B,OAAO,QAAQ,QAAQ,CAAE,GAAI,GAAM,SAAU,QAAS,CAAC,EAGzD,KAAK,EAAkB,CACrB,GAAI,KAAK,UAAU,MACjB,OAAO,KAAK,UAAU,MAAM,EAE9B,OAAO,QAAQ,QAAQ,EAE3B",
9
- "debugId": "3495BDF6CCDE500D64756E2164756E21",
10
- "names": []
11
- }
@@ -1,4 +0,0 @@
1
- import{G as Z}from"./chunk-6yggz45h.js";import{I as q}from"./chunk-sqn04kae.js";function _(z){for(let G=0;G<z.length;G++){let E=z.charCodeAt(G);if(E<=31||E===127||E===8232||E===8233)return E}return-1}function V(z,G="address"){let E=_(z);if(E!==-1){let I=E.toString(16).padStart(2,"0");throw Error(`Email ${G} contains a forbidden control character (0x${I}). CR, LF, NUL, and other control characters are not allowed.`)}}function $(z){if(Array.isArray(z))return z.flatMap((E)=>$(E));if(typeof z==="object"){if(V(z.address,"address"),z.name!==void 0)V(z.name,"display name");return[{...z}]}V(z,"address");let G=z.trim();if(!G)return[];return H(G).map(T)}function x(z){if(V(z.address,"address"),z.name)return V(z.name,"display name"),`${Z(z.name)} <${z.address}>`;return z.address}function N(z){return $(z).map((G)=>G.address)}function C(z){if(_(z)!==-1)return!1;return/^[^\s@<>]+@[^\s@<>]+\.[^\s@<>]+$/.test(z)}function H(z){let G=[],E="",I=!1,J=!1;for(let O=0;O<z.length;O++){let K=z[O]??"";if(K==='"'&&z[O-1]!=="\\"){I=!I,E+=K;continue}if(K==="<"&&!I){J=!0,E+=K;continue}if(K===">"&&!I){J=!1,E+=K;continue}if(K===","&&!I&&!J){if(E.trim())G.push(E.trim());E="";continue}E+=K}if(E.trim())G.push(E.trim());return G}function T(z){let G=z.trim(),E=G.match(/^(?:"([^"]*)"|([^<]*?))\s*<([^>]+)>$/);if(E){let I=(E[1]??E[2]??"").trim(),J=(E[3]??"").trim();if(I)return{name:I,address:J};return{address:J}}if(G.startsWith('"')&&G.endsWith('"'))return{address:G.slice(1,-1)};return{address:G}}async function L(z,G){let E=z??[],I=[];for(let J of E){if(J.content instanceof Uint8Array){I.push(J);continue}if(J.path){let O;try{O=await import("node:fs/promises")}catch{throw Error("attachment.path is not supported on this runtime — use attachment.content (Uint8Array) instead")}if(G?.basePath){let{resolve:Y,sep:D}=await import("node:path"),W=Y(J.path),X=Y(G.basePath);if(!(W===X||W.startsWith(X+D)))throw Error(`[sently] Attachment path "${W}" escapes basePath "${X}". Use absolute paths within the allowed directory.`)}let K=await O.readFile(J.path),{path:U,...w}=J;I.push({...w,content:new Uint8Array(K)});continue}if(typeof J.content==="string"){I.push(J);continue}I.push(J)}return I}
2
- export{V as k,$ as l,x as m,N as n,C as o,L as p};
3
-
4
- //# debugId=67ECE72AE508464F64756E2164756E21
@@ -1,13 +0,0 @@
1
- import{E as O,F as K,H}from"./chunk-6yggz45h.js";var j=64;function X(q){return q>>>0}function U(q){let z=L(q),G=1732584193,Q=4023233417,N=2562383102,J=271733878;for(let F=0;F<z.length;F+=64){let $=z.subarray(F,F+64),w=new Uint32Array(16);for(let W=0;W<16;W++){let A=W*4;w[W]=X(($[A]??0)|($[A+1]??0)<<8|($[A+2]??0)<<16|($[A+3]??0)<<24)}let x=G,D=Q,I=N,R=J;for(let W=0;W<64;W++){let A,_;if(W<16)A=X(D&I|~D&R),_=W;else if(W<32)A=X(D&R|I&~R),_=X((5*W+1)%16);else if(W<48)A=X(D^I^R),_=X((3*W+5)%16);else A=X(I^(D|~R)),_=X(7*W%16);let v=R;R=I,I=D,D=X(D+B(X(x+A+X((C[W]??0)+(w[_]??0))),y[W]??0)),x=v}G=X(G+x),Q=X(Q+D),N=X(N+I),J=X(J+R)}let Y=new Uint8Array(16),V=new DataView(Y.buffer);return V.setUint32(0,G,!0),V.setUint32(4,Q,!0),V.setUint32(8,N,!0),V.setUint32(12,J,!0),Y}var y=[7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21],C=new Uint32Array([3614090360,3905402710,606105819,3250441966,4118548399,1200080426,2821735955,4249261313,1770035416,2336552879,4294925233,2304563134,1804603682,4254626195,2792965006,1236535329,4129170786,3225465664,643717713,3921069994,3593408605,38016083,3634488961,3889429448,568446438,3275163606,4107603335,1163531501,2850285829,4243563512,1735328473,2368359562,4294588738,2272392833,1839030562,4259657740,2763975236,1272893353,4139469664,3200236656,681279174,3936430074,3572445317,76029189,3654602809,3873151461,530742520,3299628645,4096336452,1126891415,2878612391,4237533241,1700485571,2399980690,4293915773,2240044497,1873313359,4264355552,2734768916,1309151649,4149444226,3174756917,718787259,3951481745]);function B(q,z){return X(q<<z|q>>>32-z)}function L(q){let z=q.length*8,G=(56-(q.length+1)%64+64)%64,Q=q.length+1+G+8,N=new Uint8Array(Q);N.set(q),N[q.length]=128;let J=new DataView(N.buffer);return J.setUint32(Q-8,z>>>0,!0),J.setUint32(Q-4,Math.floor(z/4294967296),!0),N}function P(q,z){let G=q;if(G.length>j)G=U(G);let Q=new Uint8Array(j);Q.set(G);let N=new Uint8Array(j),J=new Uint8Array(j);for(let $=0;$<j;$++)N[$]=(Q[$]??0)^54,J[$]=(Q[$]??0)^92;let Y=new Uint8Array(N.length+z.length);Y.set(N),Y.set(z,N.length);let V=U(Y),F=new Uint8Array(J.length+V.length);return F.set(J),F.set(V,J.length),U(F)}function T(q){return Array.from(q,(z)=>z.toString(16).padStart(2,"0")).join("")}async function E(q,z,G){let Q=K(q.trim()),N=H(G),J=P(N,Q),Y=T(J);return O(`${z} ${Y}`).replace(/\r\n/g,"")}class Z extends Error{code;command;response;constructor(q,z,G,Q){super(q);this.code=z;this.command=G;this.response=Q;this.name="SMTPError"}}function h(q){let z;switch(q.type){case"EHLO":{z=`EHLO ${q.domain.replace(/[\r\n]/g,"")}`;break}case"STARTTLS":z="STARTTLS";break;case"AUTH_LOGIN":z="AUTH LOGIN";break;case"AUTH_PLAIN":z=`AUTH PLAIN ${O(`\x00${q.user}\x00${q.pass}`).replace(/\r\n/g,"")}`;break;case"AUTH_CRAM_MD5_INIT":z="AUTH CRAM-MD5";break;case"AUTH_CRAM_MD5_RESPONSE":return H(`${q.response}\r
2
- `);case"AUTH_XOAUTH2":z=`AUTH XOAUTH2 ${q.xoauth2String}`;break;case"MAIL_FROM":if(/[\r\n]/.test(q.address))throw new Z("Invalid address: contains CRLF",0,"MAIL FROM",q.address);z=`MAIL FROM:<${q.address}>`;break;case"RCPT_TO":if(/[\r\n]/.test(q.address))throw new Z("Invalid address: contains CRLF",0,"RCPT TO",q.address);z=`RCPT TO:<${q.address}>`;break;case"DATA":z="DATA";break;case"DATA_BODY":return H(g(q.content));case"QUIT":z="QUIT";break;case"RSET":z="RSET";break;case"NOOP":z="NOOP";break}return H(`${z}\r
3
- `)}function k(q){let z=new TextDecoder().decode(q).trim(),G=z.split(/\r?\n/),N=(G[G.length-1]??"").match(/^(\d{3})([\s-])(.*)$/);if(!N)throw new Z("Invalid SMTP response",0,"PARSE",z);let J=Number.parseInt(N[1]??"0",10),Y=G.map((V)=>V.replace(/^\d{3}[\s-]/,"")).join(" ");return{code:J,message:Y,isSuccess:J>=200&&J<300,isReady:J>=300&&J<400,isError:J>=400}}function b(q){if(q.length===0)return null;let z=q.reduce((V,F)=>V+F.length,0),G=new Uint8Array(z),Q=0;for(let V of q)G.set(V,Q),Q+=V.length;let J=new TextDecoder().decode(G).split(/\r?\n/).filter((V)=>V.length>0);if(J.length===0)return null;let Y=J[J.length-1]??"";if(/^\d{3} /.test(Y))return G;return null}function u(q){let z=q.map((G)=>G.toUpperCase());if(z.some((G)=>G.includes("AUTH")&&G.includes("XOAUTH2")))return"OAUTH2";if(z.some((G)=>G.includes("AUTH")&&G.includes("CRAM-MD5")))return"CRAM-MD5";if(z.some((G)=>G.includes("AUTH")&&G.includes("LOGIN")))return"LOGIN";if(z.some((G)=>G.includes("AUTH")&&G.includes("PLAIN")))return"PLAIN";throw new Z("No supported AUTH method",0,"EHLO",q.join(" "))}function m(q){return q.message.split(/\s+/).flatMap((z)=>z.split(/\r?\n/)).filter(Boolean)}function l(q,z,G){if(!z.includes(q.code))throw new Z(`Unexpected SMTP response for ${G}`,q.code,G,q.message)}function g(q){return`${new TextDecoder().decode(q).split(/\r?\n/).map((N)=>N.startsWith(".")?`.${N}`:N).join(`\r
4
- `)}\r
5
- .\r
6
- `}function p(q){return H(`${O(q).replace(/\r\n/g,"")}\r
7
- `)}function r(q){return H(`${O(q).replace(/\r\n/g,"")}\r
8
- `)}function i(q){return H(`${q}\r
9
- `)}function o(q){return H(`${q}\r
10
- `)}
11
- export{E as s,Z as t,h as u,k as v,b as w,u as x,m as y,l as z,p as A,r as B,i as C,o as D};
12
-
13
- //# debugId=861EEA4F09257AA964756E2164756E21