sently 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/adapters/deno.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * @module\n * Deno socket adapter for SMTP connections via Deno.connect and Deno.startTls.\n *\n * @example\n * ```ts\n * import { DenoAdapter } from \"sently/adapters/deno\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * host: \"smtp.example.com\",\n * adapter: new DenoAdapter(),\n * auth: { user: \"you@example.com\", pass: \"secret\" },\n * });\n * ```\n */\nimport type { SocketAdapter, TLSOptions } from \"../core/types.js\";\n\ndeclare const Deno: {\n connect(options: { hostname: string; port: number }): Promise<DenoTcpConn>;\n connectTls(options: {\n hostname: string;\n port: number;\n [key: string]: unknown;\n }): Promise<DenoTlsConn>;\n startTls(conn: DenoTcpConn, options?: { hostname?: string }): Promise<DenoTlsConn>;\n};\n\ninterface DenoConn {\n read(p: Uint8Array): Promise<number | null>;\n write(p: Uint8Array): Promise<number>;\n close(): void;\n}\n\ninterface DenoTcpConn extends DenoConn {}\ninterface DenoTlsConn extends DenoConn {}\n\n/** Configuration options for {@link DenoAdapter}. */\nexport interface DenoAdapterOptions {\n secure?: boolean;\n connectionTimeout?: number;\n tls?: TLSOptions;\n}\n\n/**\n * Deno socket adapter using Deno.connect / Deno.startTls.\n */\nexport class DenoAdapter implements SocketAdapter {\n private conn: DenoConn | null = null;\n private _secure: boolean;\n private _connected = false;\n private readonly tlsOptions: TLSOptions;\n\n /** Creates a Deno socket adapter (requires the Deno runtime). */\n constructor(options: DenoAdapterOptions = {}) {\n if (typeof Deno === \"undefined\") {\n throw new Error(\"DenoAdapter requires the Deno runtime\");\n }\n this._secure = options.secure ?? false;\n this.tlsOptions = options.tls ?? {};\n }\n\n /** Whether the connection uses TLS. */\n get secure(): boolean {\n return this._secure;\n }\n\n /** Whether the socket is currently connected. */\n get connected(): boolean {\n return this._connected;\n }\n\n /** Opens a TCP or TLS connection to the given host and port. */\n async connect(host: string, port: number): Promise<void> {\n if (this._secure) {\n this.conn = await Deno.connectTls({\n hostname: host,\n port,\n ...(this.tlsOptions.servername ? { servername: this.tlsOptions.servername } : {}),\n });\n } else {\n this.conn = await Deno.connect({ hostname: host, port });\n }\n this._connected = true;\n }\n\n /** Upgrades a plain connection to TLS via STARTTLS. */\n async startTLS(options?: TLSOptions): Promise<void> {\n if (!this.conn || this._secure) {\n throw new Error(\"Cannot STARTTLS: no plain connection available\");\n }\n\n const merged = { ...this.tlsOptions, ...options };\n this.conn = await Deno.startTls(this.conn as DenoTcpConn, {\n ...(merged.servername ? { hostname: merged.servername } : {}),\n });\n this._secure = true;\n }\n\n /** Writes raw bytes to the socket. */\n async write(data: Uint8Array): Promise<void> {\n if (!this.conn) {\n throw new Error(\"Socket not connected\");\n }\n await this.conn.write(data);\n }\n\n /** Reads incoming socket data as an async iterable of byte chunks. */\n async *read(): AsyncGenerator<Uint8Array, void, unknown> {\n if (!this.conn) {\n throw new Error(\"Socket not connected\");\n }\n\n const buffer = new Uint8Array(8192);\n while (true) {\n const n = await this.conn.read(buffer);\n if (n === null) {\n break;\n }\n yield buffer.slice(0, n);\n }\n }\n\n /** Closes the socket connection. */\n async close(): Promise<void> {\n this.conn?.close();\n this.conn = null;\n this._connected = false;\n }\n}\n"
6
+ ],
7
+ "mappings": ";;;AA+CO,MAAM,YAAqC;AAAA,EACxC,OAAwB;AAAA,EACxB;AAAA,EACA,aAAa;AAAA,EACJ;AAAA,EAGjB,WAAW,CAAC,UAA8B,CAAC,GAAG;AAAA,IAC5C,IAAI,OAAO,SAAS,aAAa;AAAA,MAC/B,MAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAAA,IACA,KAAK,UAAU,QAAQ,UAAU;AAAA,IACjC,KAAK,aAAa,QAAQ,OAAO,CAAC;AAAA;AAAA,MAIhC,MAAM,GAAY;AAAA,IACpB,OAAO,KAAK;AAAA;AAAA,MAIV,SAAS,GAAY;AAAA,IACvB,OAAO,KAAK;AAAA;AAAA,OAIR,QAAO,CAAC,MAAc,MAA6B;AAAA,IACvD,IAAI,KAAK,SAAS;AAAA,MAChB,KAAK,OAAO,MAAM,KAAK,WAAW;AAAA,QAChC,UAAU;AAAA,QACV;AAAA,WACI,KAAK,WAAW,aAAa,EAAE,YAAY,KAAK,WAAW,WAAW,IAAI,CAAC;AAAA,MACjF,CAAC;AAAA,IACH,EAAO;AAAA,MACL,KAAK,OAAO,MAAM,KAAK,QAAQ,EAAE,UAAU,MAAM,KAAK,CAAC;AAAA;AAAA,IAEzD,KAAK,aAAa;AAAA;AAAA,OAId,SAAQ,CAAC,SAAqC;AAAA,IAClD,IAAI,CAAC,KAAK,QAAQ,KAAK,SAAS;AAAA,MAC9B,MAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AAAA,IAEA,MAAM,SAAS,KAAK,KAAK,eAAe,QAAQ;AAAA,IAChD,KAAK,OAAO,MAAM,KAAK,SAAS,KAAK,MAAqB;AAAA,SACpD,OAAO,aAAa,EAAE,UAAU,OAAO,WAAW,IAAI,CAAC;AAAA,IAC7D,CAAC;AAAA,IACD,KAAK,UAAU;AAAA;AAAA,OAIX,MAAK,CAAC,MAAiC;AAAA,IAC3C,IAAI,CAAC,KAAK,MAAM;AAAA,MACd,MAAM,IAAI,MAAM,sBAAsB;AAAA,IACxC;AAAA,IACA,MAAM,KAAK,KAAK,MAAM,IAAI;AAAA;AAAA,SAIrB,IAAI,GAA8C;AAAA,IACvD,IAAI,CAAC,KAAK,MAAM;AAAA,MACd,MAAM,IAAI,MAAM,sBAAsB;AAAA,IACxC;AAAA,IAEA,MAAM,SAAS,IAAI,WAAW,IAAI;AAAA,IAClC,OAAO,MAAM;AAAA,MACX,MAAM,IAAI,MAAM,KAAK,KAAK,KAAK,MAAM;AAAA,MACrC,IAAI,MAAM,MAAM;AAAA,QACd;AAAA,MACF;AAAA,MACA,MAAM,OAAO,MAAM,GAAG,CAAC;AAAA,IACzB;AAAA;AAAA,OAII,MAAK,GAAkB;AAAA,IAC3B,KAAK,MAAM,MAAM;AAAA,IACjB,KAAK,OAAO;AAAA,IACZ,KAAK,aAAa;AAAA;AAEtB;",
8
+ "debugId": "871B95B1C9304E4564756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,172 @@
1
+ import"../../chunk-v0bahtg2.js";
2
+
3
+ // src/adapters/node.ts
4
+ import net from "node:net";
5
+ import tls from "node:tls";
6
+
7
+ class NodeAdapter {
8
+ socket = null;
9
+ _secure;
10
+ _connected = false;
11
+ connectionTimeout;
12
+ tlsOptions;
13
+ constructor(options = {}) {
14
+ this._secure = options.secure ?? false;
15
+ this.connectionTimeout = options.connectionTimeout ?? 30000;
16
+ this.tlsOptions = options.tls ?? {};
17
+ }
18
+ get secure() {
19
+ return this._secure;
20
+ }
21
+ get connected() {
22
+ return this._connected;
23
+ }
24
+ async connect(host, port) {
25
+ if (this._secure) {
26
+ await this.connectTls(host, port);
27
+ } else {
28
+ await this.connectPlain(host, port);
29
+ }
30
+ this._connected = true;
31
+ }
32
+ async startTLS(options) {
33
+ if (!this.socket || this._secure) {
34
+ throw new Error("Cannot STARTTLS: no plain socket available");
35
+ }
36
+ const plain = this.socket;
37
+ const merged = { ...this.tlsOptions, ...options };
38
+ await new Promise((resolve, reject) => {
39
+ const tlsSocket = tls.connect({
40
+ socket: plain,
41
+ servername: merged.servername,
42
+ rejectUnauthorized: merged.rejectUnauthorized ?? true
43
+ });
44
+ tlsSocket.once("secureConnect", () => {
45
+ this.socket = tlsSocket;
46
+ this._secure = true;
47
+ resolve();
48
+ });
49
+ tlsSocket.once("error", reject);
50
+ });
51
+ }
52
+ async write(data) {
53
+ if (!this.socket) {
54
+ throw new Error("Socket not connected");
55
+ }
56
+ await new Promise((resolve, reject) => {
57
+ this.socket?.write(Buffer.from(data), (err) => {
58
+ if (err) {
59
+ reject(err);
60
+ } else {
61
+ resolve();
62
+ }
63
+ });
64
+ });
65
+ }
66
+ async* read() {
67
+ if (!this.socket) {
68
+ throw new Error("Socket not connected");
69
+ }
70
+ const socket = this.socket;
71
+ const queue = [];
72
+ let resolveNext = null;
73
+ let done = false;
74
+ let error = null;
75
+ const onData = (chunk) => {
76
+ const data = new Uint8Array(chunk);
77
+ if (resolveNext) {
78
+ resolveNext({ value: data, done: false });
79
+ resolveNext = null;
80
+ } else {
81
+ queue.push(data);
82
+ }
83
+ };
84
+ const onError = (err) => {
85
+ error = err;
86
+ if (resolveNext) {
87
+ resolveNext({ value: undefined, done: true });
88
+ resolveNext = null;
89
+ }
90
+ done = true;
91
+ };
92
+ const onClose = () => {
93
+ done = true;
94
+ if (resolveNext) {
95
+ resolveNext({ value: undefined, done: true });
96
+ resolveNext = null;
97
+ }
98
+ };
99
+ socket.on("data", onData);
100
+ socket.on("error", onError);
101
+ socket.on("close", onClose);
102
+ try {
103
+ while (!done || queue.length > 0) {
104
+ if (error) {
105
+ throw error;
106
+ }
107
+ if (queue.length > 0) {
108
+ yield queue.shift();
109
+ continue;
110
+ }
111
+ if (done) {
112
+ break;
113
+ }
114
+ const chunk = await new Promise((resolve) => {
115
+ resolveNext = resolve;
116
+ });
117
+ if (chunk.done) {
118
+ break;
119
+ }
120
+ yield chunk.value;
121
+ }
122
+ } finally {
123
+ socket.off("data", onData);
124
+ socket.off("error", onError);
125
+ socket.off("close", onClose);
126
+ }
127
+ }
128
+ async close() {
129
+ if (!this.socket) {
130
+ return;
131
+ }
132
+ await new Promise((resolve) => {
133
+ this.socket?.end(() => resolve());
134
+ });
135
+ this.socket = null;
136
+ this._connected = false;
137
+ }
138
+ connectPlain(host, port) {
139
+ return new Promise((resolve, reject) => {
140
+ const socket = net.connect({ host, port }, () => resolve());
141
+ socket.setTimeout(this.connectionTimeout);
142
+ socket.once("timeout", () => {
143
+ socket.destroy();
144
+ reject(new Error("Connection timeout"));
145
+ });
146
+ socket.once("error", reject);
147
+ this.socket = socket;
148
+ });
149
+ }
150
+ connectTls(host, port) {
151
+ return new Promise((resolve, reject) => {
152
+ const socket = tls.connect({
153
+ host,
154
+ port,
155
+ servername: this.tlsOptions.servername ?? host,
156
+ rejectUnauthorized: this.tlsOptions.rejectUnauthorized ?? true
157
+ }, () => resolve());
158
+ socket.setTimeout(this.connectionTimeout);
159
+ socket.once("timeout", () => {
160
+ socket.destroy();
161
+ reject(new Error("Connection timeout"));
162
+ });
163
+ socket.once("error", reject);
164
+ this.socket = socket;
165
+ });
166
+ }
167
+ }
168
+ export {
169
+ NodeAdapter
170
+ };
171
+
172
+ //# debugId=5ED07673F11D19B164756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/adapters/node.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * @module\n * Node.js socket adapter for SMTP connections via node:net and node:tls.\n *\n * @example\n * ```ts\n * import { NodeAdapter } from \"sently/adapters/node\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * host: \"smtp.example.com\",\n * adapter: new NodeAdapter(),\n * auth: { user: \"you@example.com\", pass: \"secret\" },\n * });\n * ```\n */\nimport net from \"node:net\";\nimport tls from \"node:tls\";\nimport type { SocketAdapter, TLSOptions } from \"../core/types.js\";\n\n/** Configuration options for {@link NodeAdapter}. */\nexport interface NodeAdapterOptions {\n secure?: boolean;\n connectionTimeout?: number;\n tls?: TLSOptions;\n}\n\n/**\n * Node.js socket adapter using node:net and node:tls.\n */\nexport class NodeAdapter implements SocketAdapter {\n private socket: net.Socket | tls.TLSSocket | null = null;\n private _secure: boolean;\n private _connected = false;\n private readonly connectionTimeout: number;\n private readonly tlsOptions: TLSOptions;\n\n /** Creates a Node.js socket adapter. */\n constructor(options: NodeAdapterOptions = {}) {\n this._secure = options.secure ?? false;\n this.connectionTimeout = options.connectionTimeout ?? 30_000;\n this.tlsOptions = options.tls ?? {};\n }\n\n /** Whether the connection uses TLS. */\n get secure(): boolean {\n return this._secure;\n }\n\n /** Whether the socket is currently connected. */\n get connected(): boolean {\n return this._connected;\n }\n\n /** Opens a TCP or TLS connection to the given host and port. */\n async connect(host: string, port: number): Promise<void> {\n if (this._secure) {\n await this.connectTls(host, port);\n } else {\n await this.connectPlain(host, port);\n }\n this._connected = true;\n }\n\n /** Upgrades a plain connection to TLS via STARTTLS. */\n async startTLS(options?: TLSOptions): Promise<void> {\n if (!this.socket || this._secure) {\n throw new Error(\"Cannot STARTTLS: no plain socket available\");\n }\n\n const plain = this.socket;\n const merged = { ...this.tlsOptions, ...options };\n\n await new Promise<void>((resolve, reject) => {\n const tlsSocket = tls.connect({\n socket: plain,\n servername: merged.servername,\n rejectUnauthorized: merged.rejectUnauthorized ?? true,\n });\n\n tlsSocket.once(\"secureConnect\", () => {\n this.socket = tlsSocket;\n this._secure = true;\n resolve();\n });\n tlsSocket.once(\"error\", reject);\n });\n }\n\n /** Writes raw bytes to the socket. */\n async write(data: Uint8Array): Promise<void> {\n if (!this.socket) {\n throw new Error(\"Socket not connected\");\n }\n\n await new Promise<void>((resolve, reject) => {\n this.socket?.write(Buffer.from(data), (err: Error | null | undefined) => {\n if (err) {\n reject(err);\n } else {\n resolve();\n }\n });\n });\n }\n\n /** Reads incoming socket data as an async iterable of byte chunks. */\n async *read(): AsyncGenerator<Uint8Array, void, unknown> {\n if (!this.socket) {\n throw new Error(\"Socket not connected\");\n }\n\n const socket = this.socket;\n const queue: Uint8Array[] = [];\n let resolveNext: ((value: IteratorResult<Uint8Array>) => void) | null = null;\n let done = false;\n let error: Error | null = null;\n\n const onData = (chunk: Buffer): void => {\n const data = new Uint8Array(chunk);\n if (resolveNext) {\n resolveNext({ value: data, done: false });\n resolveNext = null;\n } else {\n queue.push(data);\n }\n };\n\n const onError = (err: Error): void => {\n error = err;\n if (resolveNext) {\n resolveNext({ value: undefined as unknown as Uint8Array, done: true });\n resolveNext = null;\n }\n done = true;\n };\n\n const onClose = (): void => {\n done = true;\n if (resolveNext) {\n resolveNext({ value: undefined as unknown as Uint8Array, done: true });\n resolveNext = null;\n }\n };\n\n socket.on(\"data\", onData);\n socket.on(\"error\", onError);\n socket.on(\"close\", onClose);\n\n try {\n while (!done || queue.length > 0) {\n if (error) {\n throw error;\n }\n if (queue.length > 0) {\n yield queue.shift() as Uint8Array;\n continue;\n }\n if (done) {\n break;\n }\n const chunk = await new Promise<IteratorResult<Uint8Array>>((resolve) => {\n resolveNext = resolve;\n });\n if (chunk.done) {\n break;\n }\n yield chunk.value;\n }\n } finally {\n socket.off(\"data\", onData);\n socket.off(\"error\", onError);\n socket.off(\"close\", onClose);\n }\n }\n\n /** Closes the socket connection. */\n async close(): Promise<void> {\n if (!this.socket) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n this.socket?.end(() => resolve());\n });\n this.socket = null;\n this._connected = false;\n }\n\n private connectPlain(host: string, port: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const socket = net.connect({ host, port }, () => resolve());\n socket.setTimeout(this.connectionTimeout);\n socket.once(\"timeout\", () => {\n socket.destroy();\n reject(new Error(\"Connection timeout\"));\n });\n socket.once(\"error\", reject);\n this.socket = socket;\n });\n }\n\n private connectTls(host: string, port: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const socket = tls.connect(\n {\n host,\n port,\n servername: this.tlsOptions.servername ?? host,\n rejectUnauthorized: this.tlsOptions.rejectUnauthorized ?? true,\n },\n () => resolve(),\n );\n socket.setTimeout(this.connectionTimeout);\n socket.once(\"timeout\", () => {\n socket.destroy();\n reject(new Error(\"Connection timeout\"));\n });\n socket.once(\"error\", reject);\n this.socket = socket;\n });\n }\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "5ED07673F11D19B164756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,14 @@
1
+ import {
2
+ GOOGLE_TOKEN_URL,
3
+ MICROSOFT_TOKEN_URL,
4
+ OAuth2Client
5
+ } from "../../chunk-vm70w4e3.js";
6
+ import"../../chunk-794hc3m4.js";
7
+ import"../../chunk-v0bahtg2.js";
8
+ export {
9
+ OAuth2Client,
10
+ MICROSOFT_TOKEN_URL,
11
+ GOOGLE_TOKEN_URL
12
+ };
13
+
14
+ //# debugId=4C0D32028E4F04FA64756E2164756E21
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [
5
+ ],
6
+ "mappings": "",
7
+ "debugId": "4C0D32028E4F04FA64756E2164756E21",
8
+ "names": []
9
+ }
@@ -0,0 +1,12 @@
1
+ import"../chunk-v0bahtg2.js";
2
+ export {
3
+ detectRuntime,
4
+ createMailer,
5
+ SMTPPool,
6
+ SMTPError,
7
+ OAuth2Client,
8
+ MICROSOFT_TOKEN_URL,
9
+ GOOGLE_TOKEN_URL
10
+ };
11
+
12
+ //# debugId=35CAC96851D6DCB964756E2164756E21
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [
5
+ ],
6
+ "mappings": "",
7
+ "debugId": "35CAC96851D6DCB964756E2164756E21",
8
+ "names": []
9
+ }
@@ -0,0 +1,263 @@
1
+ import {
2
+ buildMIME,
3
+ closeSMTPSession,
4
+ deliverSMTPMessage,
5
+ openSMTPSession,
6
+ resolveSMTPConfig
7
+ } from "../../chunk-tch6s785.js";
8
+ import"../../chunk-vm70w4e3.js";
9
+ import {
10
+ resolveAttachments
11
+ } from "../../chunk-hdqpvsm8.js";
12
+ import"../../chunk-794hc3m4.js";
13
+ import"../../chunk-v0bahtg2.js";
14
+
15
+ // src/pool/connection.ts
16
+ async function createPooledConnection(options) {
17
+ const config = resolveSMTPConfig(options.config);
18
+ const adapter = await options.createAdapter();
19
+ await adapter.connect(options.connectHost, config.port);
20
+ await openSMTPSession(adapter, config);
21
+ let messageCount = 0;
22
+ let idle = true;
23
+ let sendChain = Promise.resolve();
24
+ const maxMessages = options.maxMessages;
25
+ return {
26
+ get idle() {
27
+ return idle;
28
+ },
29
+ get messageCount() {
30
+ return messageCount;
31
+ },
32
+ get usable() {
33
+ return messageCount < maxMessages;
34
+ },
35
+ async send(mailOptions) {
36
+ const run = async () => {
37
+ idle = false;
38
+ try {
39
+ const resolvedOptions = {
40
+ ...mailOptions,
41
+ attachments: await resolveAttachments(mailOptions.attachments)
42
+ };
43
+ const mime = await buildMIME(resolvedOptions, config.dkim);
44
+ const result = await deliverSMTPMessage(adapter, mime);
45
+ messageCount += 1;
46
+ return result;
47
+ } finally {
48
+ idle = true;
49
+ }
50
+ };
51
+ const resultPromise = sendChain.then(run);
52
+ sendChain = resultPromise.then(() => {
53
+ return;
54
+ }, () => {
55
+ return;
56
+ });
57
+ return resultPromise;
58
+ },
59
+ async close() {
60
+ await sendChain;
61
+ await closeSMTPSession(adapter);
62
+ }
63
+ };
64
+ }
65
+
66
+ // src/pool/pool.ts
67
+ class RateLimiter {
68
+ rateDelta;
69
+ rateLimit;
70
+ now;
71
+ tokens;
72
+ lastRefill;
73
+ waiters = [];
74
+ constructor(rateDelta, rateLimit, now = Date.now) {
75
+ this.rateDelta = rateDelta;
76
+ this.rateLimit = rateLimit;
77
+ this.now = now;
78
+ this.tokens = rateDelta;
79
+ this.lastRefill = now();
80
+ }
81
+ async acquire() {
82
+ for (;; ) {
83
+ this.refill();
84
+ if (this.tokens > 0) {
85
+ this.tokens -= 1;
86
+ return;
87
+ }
88
+ await new Promise((resolve) => {
89
+ this.waiters.push(resolve);
90
+ });
91
+ }
92
+ }
93
+ notify() {
94
+ this.refill();
95
+ }
96
+ refill() {
97
+ const t = this.now();
98
+ const elapsed = t - this.lastRefill;
99
+ if (elapsed >= this.rateLimit) {
100
+ const periods = Math.floor(elapsed / this.rateLimit);
101
+ this.tokens = Math.min(this.rateDelta, this.tokens + periods * this.rateDelta);
102
+ this.lastRefill += periods * this.rateLimit;
103
+ while (this.tokens > 0 && this.waiters.length > 0) {
104
+ this.tokens -= 1;
105
+ const next = this.waiters.shift();
106
+ next?.();
107
+ }
108
+ }
109
+ }
110
+ }
111
+
112
+ class SMTPPool {
113
+ config;
114
+ maxConnections;
115
+ maxMessages;
116
+ createAdapterFn;
117
+ rateLimiter;
118
+ connections = [];
119
+ queue = [];
120
+ draining = false;
121
+ closed = false;
122
+ processChain = Promise.resolve();
123
+ constructor(config, options) {
124
+ this.config = config;
125
+ this.maxConnections = config.maxConnections ?? 5;
126
+ this.maxMessages = config.maxMessages ?? 100;
127
+ if (options?.createAdapter) {
128
+ const factory = options.createAdapter;
129
+ this.createAdapterFn = async () => factory();
130
+ } else if (config.adapter) {
131
+ this.createAdapterFn = async () => config.adapter;
132
+ } else {
133
+ throw new Error("SMTPPool requires config.adapter or options.createAdapter");
134
+ }
135
+ if (config.rateDelta !== undefined && config.rateDelta > 0) {
136
+ this.rateLimiter = new RateLimiter(config.rateDelta, config.rateLimit ?? 1000, options?.now);
137
+ } else {
138
+ this.rateLimiter = null;
139
+ }
140
+ }
141
+ async send(options) {
142
+ if (this.closed) {
143
+ throw new Error("SMTPPool is closed");
144
+ }
145
+ if (this.rateLimiter) {
146
+ await this.rateLimiter.acquire();
147
+ }
148
+ return new Promise((resolve, reject) => {
149
+ this.queue.push({ options, resolve, reject });
150
+ this.scheduleProcess();
151
+ });
152
+ }
153
+ scheduleProcess() {
154
+ this.processChain = this.processChain.then(() => this.processQueue()).catch(() => {
155
+ return;
156
+ });
157
+ }
158
+ async verify() {
159
+ const conn = await this.spawnConnection();
160
+ try {
161
+ return true;
162
+ } finally {
163
+ await conn.close();
164
+ this.removeConnection(conn);
165
+ }
166
+ }
167
+ async close() {
168
+ this.closed = true;
169
+ this.draining = true;
170
+ await this.drainQueue();
171
+ await Promise.all(this.connections.map((c) => c.close()));
172
+ this.connections.length = 0;
173
+ }
174
+ get connectionCount() {
175
+ return this.connections.length;
176
+ }
177
+ get queueSize() {
178
+ return this.queue.length;
179
+ }
180
+ async processQueue() {
181
+ if (this.draining) {
182
+ return;
183
+ }
184
+ while (this.queue.length > 0) {
185
+ const idleConn = this.connections.find((c) => c.idle && c.usable);
186
+ if (idleConn) {
187
+ const entry = this.queue.shift();
188
+ if (!entry) {
189
+ break;
190
+ }
191
+ try {
192
+ const result = await idleConn.send(entry.options);
193
+ entry.resolve(result);
194
+ if (!idleConn.usable) {
195
+ await idleConn.close();
196
+ this.removeConnection(idleConn);
197
+ }
198
+ } catch (err) {
199
+ entry.reject(err);
200
+ await idleConn.close().catch(() => {
201
+ return;
202
+ });
203
+ this.removeConnection(idleConn);
204
+ }
205
+ continue;
206
+ }
207
+ if (this.connections.length < this.maxConnections) {
208
+ const entry = this.queue.shift();
209
+ if (!entry) {
210
+ break;
211
+ }
212
+ const conn = await this.spawnConnection();
213
+ try {
214
+ const result = await conn.send(entry.options);
215
+ entry.resolve(result);
216
+ if (!conn.usable) {
217
+ await conn.close();
218
+ this.removeConnection(conn);
219
+ }
220
+ } catch (err) {
221
+ entry.reject(err);
222
+ await conn.close().catch(() => {
223
+ return;
224
+ });
225
+ this.removeConnection(conn);
226
+ }
227
+ continue;
228
+ }
229
+ break;
230
+ }
231
+ }
232
+ async spawnConnection() {
233
+ const resolved = resolveSMTPConfig(this.config);
234
+ const conn = await createPooledConnection({
235
+ config: this.config,
236
+ maxMessages: this.maxMessages,
237
+ connectHost: resolved.host,
238
+ createAdapter: this.createAdapterFn
239
+ });
240
+ this.connections.push(conn);
241
+ return conn;
242
+ }
243
+ removeConnection(conn) {
244
+ const index = this.connections.indexOf(conn);
245
+ if (index >= 0) {
246
+ this.connections.splice(index, 1);
247
+ }
248
+ }
249
+ async drainQueue() {
250
+ while (this.queue.length > 0 || this.connections.some((c) => !c.idle)) {
251
+ await this.processQueue();
252
+ if (this.queue.length > 0) {
253
+ await new Promise((r) => setTimeout(r, 10));
254
+ }
255
+ }
256
+ }
257
+ }
258
+ export {
259
+ SMTPPool,
260
+ RateLimiter
261
+ };
262
+
263
+ //# debugId=A46A2DDD71D8492864756E2164756E21
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/pool/connection.ts", "../src/pool/pool.ts"],
4
+ "sourcesContent": [
5
+ "import { buildMIME } from \"../core/mime.js\";\nimport type { MailOptions, SendResult, SMTPConfig } from \"../core/types.js\";\nimport { resolveAttachments } from \"../transports/resolve-attachments.js\";\nimport {\n closeSMTPSession,\n deliverSMTPMessage,\n openSMTPSession,\n resolveSMTPConfig,\n} from \"../transports/smtp.js\";\n\n/** A single pooled SMTP connection with a persistent session. */\nexport interface PooledConnection {\n /** Send one message over this connection. */\n send(options: MailOptions): Promise<SendResult>;\n /** Whether this connection is idle and available for work. */\n readonly idle: boolean;\n /** Number of messages sent on this connection. */\n readonly messageCount: number;\n /** Whether this connection can accept more messages before recycle. */\n readonly usable: boolean;\n /** Close the connection and end the SMTP session. */\n close(): Promise<void>;\n}\n\n/** Options for creating a pooled connection. */\nexport interface PooledConnectionOptions {\n config: SMTPConfig;\n maxMessages: number;\n connectHost: string;\n createAdapter: () => Promise<import(\"../core/types.js\").SocketAdapter>;\n}\n\n/**\n * Create a pooled SMTP connection with an open authenticated session.\n */\nexport async function createPooledConnection(\n options: PooledConnectionOptions,\n): Promise<PooledConnection> {\n const config = resolveSMTPConfig(options.config);\n const adapter = await options.createAdapter();\n await adapter.connect(options.connectHost, config.port);\n await openSMTPSession(adapter, config);\n\n let messageCount = 0;\n let idle = true;\n let sendChain: Promise<void> = Promise.resolve();\n\n const maxMessages = options.maxMessages;\n\n return {\n get idle(): boolean {\n return idle;\n },\n get messageCount(): number {\n return messageCount;\n },\n get usable(): boolean {\n return messageCount < maxMessages;\n },\n\n async send(mailOptions: MailOptions): Promise<SendResult> {\n const run = async (): Promise<SendResult> => {\n idle = false;\n try {\n const resolvedOptions = {\n ...mailOptions,\n attachments: await resolveAttachments(mailOptions.attachments),\n };\n const mime = await buildMIME(resolvedOptions, config.dkim);\n const result = await deliverSMTPMessage(adapter, mime);\n messageCount += 1;\n return result;\n } finally {\n idle = true;\n }\n };\n\n const resultPromise = sendChain.then(run);\n sendChain = resultPromise.then(\n () => undefined,\n () => undefined,\n );\n return resultPromise;\n },\n\n async close(): Promise<void> {\n await sendChain;\n await closeSMTPSession(adapter);\n },\n };\n}\n",
6
+ "import type {\n MailOptions,\n PoolConfig,\n SendResult,\n SMTPConfig,\n SocketAdapter,\n Transport,\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 * Token bucket rate limiter with lazy refill on acquire.\n */\nclass RateLimiter {\n private tokens: number;\n private lastRefill: number;\n private waiters: Array<() => void> = [];\n\n constructor(\n private readonly rateDelta: number,\n private readonly rateLimit: number,\n private readonly now: () => number = Date.now,\n ) {\n this.tokens = rateDelta;\n this.lastRefill = now();\n }\n\n /** Wait until a token is available, then consume one. */\n async acquire(): Promise<void> {\n for (;;) {\n this.refill();\n if (this.tokens > 0) {\n this.tokens -= 1;\n return;\n }\n await new Promise<void>((resolve) => {\n this.waiters.push(resolve);\n });\n }\n }\n\n /** Wake waiters after the clock advances (for testing). */\n notify(): void {\n this.refill();\n }\n\n private refill(): void {\n const t = this.now();\n const elapsed = t - this.lastRefill;\n if (elapsed >= this.rateLimit) {\n const periods = Math.floor(elapsed / this.rateLimit);\n this.tokens = Math.min(this.rateDelta, this.tokens + periods * this.rateDelta);\n this.lastRefill += periods * this.rateLimit;\n while (this.tokens > 0 && this.waiters.length > 0) {\n this.tokens -= 1;\n const next = this.waiters.shift();\n next?.();\n }\n }\n }\n}\n\n/**\n * SMTP connection pool with optional rate limiting.\n */\nexport class SMTPPool implements Transport {\n private readonly config: SMTPConfig & PoolConfig;\n private readonly maxConnections: number;\n private readonly maxMessages: number;\n private readonly createAdapterFn: () => Promise<SocketAdapter>;\n private readonly rateLimiter: RateLimiter | null;\n private readonly connections: PooledConnection[] = [];\n private readonly queue: QueueEntry[] = [];\n private draining = false;\n private closed = false;\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.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 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<boolean> {\n const conn = await this.spawnConnection();\n try {\n return true;\n } finally {\n await conn.close();\n this.removeConnection(conn);\n }\n }\n\n /** Drains the queue and closes all connections. */\n async close(): Promise<void> {\n this.closed = true;\n this.draining = true;\n await this.drainQueue();\n await Promise.all(this.connections.map((c) => c.close()));\n this.connections.length = 0;\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 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 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 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 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 };\n"
7
+ ],
8
+ "mappings": ";;;;;;;;;;;;;;;AAmCA,eAAsB,sBAAsB,CAC1C,SAC2B;AAAA,EAC3B,MAAM,SAAS,kBAAkB,QAAQ,MAAM;AAAA,EAC/C,MAAM,UAAU,MAAM,QAAQ,cAAc;AAAA,EAC5C,MAAM,QAAQ,QAAQ,QAAQ,aAAa,OAAO,IAAI;AAAA,EACtD,MAAM,gBAAgB,SAAS,MAAM;AAAA,EAErC,IAAI,eAAe;AAAA,EACnB,IAAI,OAAO;AAAA,EACX,IAAI,YAA2B,QAAQ,QAAQ;AAAA,EAE/C,MAAM,cAAc,QAAQ;AAAA,EAE5B,OAAO;AAAA,QACD,IAAI,GAAY;AAAA,MAClB,OAAO;AAAA;AAAA,QAEL,YAAY,GAAW;AAAA,MACzB,OAAO;AAAA;AAAA,QAEL,MAAM,GAAY;AAAA,MACpB,OAAO,eAAe;AAAA;AAAA,SAGlB,KAAI,CAAC,aAA+C;AAAA,MACxD,MAAM,MAAM,YAAiC;AAAA,QAC3C,OAAO;AAAA,QACP,IAAI;AAAA,UACF,MAAM,kBAAkB;AAAA,eACnB;AAAA,YACH,aAAa,MAAM,mBAAmB,YAAY,WAAW;AAAA,UAC/D;AAAA,UACA,MAAM,OAAO,MAAM,UAAU,iBAAiB,OAAO,IAAI;AAAA,UACzD,MAAM,SAAS,MAAM,mBAAmB,SAAS,IAAI;AAAA,UACrD,gBAAgB;AAAA,UAChB,OAAO;AAAA,kBACP;AAAA,UACA,OAAO;AAAA;AAAA;AAAA,MAIX,MAAM,gBAAgB,UAAU,KAAK,GAAG;AAAA,MACxC,YAAY,cAAc,KACxB,MAAG;AAAA,QAAG;AAAA,SACN,MAAG;AAAA,QAAG;AAAA,OACR;AAAA,MACA,OAAO;AAAA;AAAA,SAGH,MAAK,GAAkB;AAAA,MAC3B,MAAM;AAAA,MACN,MAAM,iBAAiB,OAAO;AAAA;AAAA,EAElC;AAAA;;;AC7DF,MAAM,YAAY;AAAA,EAMG;AAAA,EACA;AAAA,EACA;AAAA,EAPX;AAAA,EACA;AAAA,EACA,UAA6B,CAAC;AAAA,EAEtC,WAAW,CACQ,WACA,WACA,MAAoB,KAAK,KAC1C;AAAA,IAHiB;AAAA,IACA;AAAA,IACA;AAAA,IAEjB,KAAK,SAAS;AAAA,IACd,KAAK,aAAa,IAAI;AAAA;AAAA,OAIlB,QAAO,GAAkB;AAAA,IAC7B,UAAS;AAAA,MACP,KAAK,OAAO;AAAA,MACZ,IAAI,KAAK,SAAS,GAAG;AAAA,QACnB,KAAK,UAAU;AAAA,QACf;AAAA,MACF;AAAA,MACA,MAAM,IAAI,QAAc,CAAC,YAAY;AAAA,QACnC,KAAK,QAAQ,KAAK,OAAO;AAAA,OAC1B;AAAA,IACH;AAAA;AAAA,EAIF,MAAM,GAAS;AAAA,IACb,KAAK,OAAO;AAAA;AAAA,EAGN,MAAM,GAAS;AAAA,IACrB,MAAM,IAAI,KAAK,IAAI;AAAA,IACnB,MAAM,UAAU,IAAI,KAAK;AAAA,IACzB,IAAI,WAAW,KAAK,WAAW;AAAA,MAC7B,MAAM,UAAU,KAAK,MAAM,UAAU,KAAK,SAAS;AAAA,MACnD,KAAK,SAAS,KAAK,IAAI,KAAK,WAAW,KAAK,SAAS,UAAU,KAAK,SAAS;AAAA,MAC7E,KAAK,cAAc,UAAU,KAAK;AAAA,MAClC,OAAO,KAAK,SAAS,KAAK,KAAK,QAAQ,SAAS,GAAG;AAAA,QACjD,KAAK,UAAU;AAAA,QACf,MAAM,OAAO,KAAK,QAAQ,MAAM;AAAA,QAChC,OAAO;AAAA,MACT;AAAA,IACF;AAAA;AAEJ;AAAA;AAKO,MAAM,SAA8B;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAkC,CAAC;AAAA,EACnC,QAAsB,CAAC;AAAA,EAChC,WAAW;AAAA,EACX,SAAS;AAAA,EACT,eAA8B,QAAQ,QAAQ;AAAA,EAGtD,WAAW,CAAC,QAAiC,SAA2B;AAAA,IACtE,KAAK,SAAS;AAAA,IACd,KAAK,iBAAiB,OAAO,kBAAkB;AAAA,IAC/C,KAAK,cAAc,OAAO,eAAe;AAAA,IAEzC,IAAI,SAAS,eAAe;AAAA,MAC1B,MAAM,UAAU,QAAQ;AAAA,MACxB,KAAK,kBAAkB,YAAY,QAAQ;AAAA,IAC7C,EAAO,SAAI,OAAO,SAAS;AAAA,MACzB,KAAK,kBAAkB,YAAY,OAAO;AAAA,IAC5C,EAAO;AAAA,MACL,MAAM,IAAI,MAAM,2DAA2D;AAAA;AAAA,IAG7E,IAAI,OAAO,cAAc,aAAa,OAAO,YAAY,GAAG;AAAA,MAC1D,KAAK,cAAc,IAAI,YAAY,OAAO,WAAW,OAAO,aAAa,MAAM,SAAS,GAAG;AAAA,IAC7F,EAAO;AAAA,MACL,KAAK,cAAc;AAAA;AAAA;AAAA,OAKjB,KAAI,CAAC,SAA2C;AAAA,IACpD,IAAI,KAAK,QAAQ;AAAA,MACf,MAAM,IAAI,MAAM,oBAAoB;AAAA,IACtC;AAAA,IACA,IAAI,KAAK,aAAa;AAAA,MACpB,MAAM,KAAK,YAAY,QAAQ;AAAA,IACjC;AAAA,IACA,OAAO,IAAI,QAAoB,CAAC,SAAS,WAAW;AAAA,MAClD,KAAK,MAAM,KAAK,EAAE,SAAS,SAAS,OAAO,CAAC;AAAA,MAC5C,KAAK,gBAAgB;AAAA,KACtB;AAAA;AAAA,EAGK,eAAe,GAAS;AAAA,IAC9B,KAAK,eAAe,KAAK,aAAa,KAAK,MAAM,KAAK,aAAa,CAAC,EAAE,MAAM,MAAG;AAAA,MAAG;AAAA,KAAS;AAAA;AAAA,OAIvF,OAAM,GAAqB;AAAA,IAC/B,MAAM,OAAO,MAAM,KAAK,gBAAgB;AAAA,IACxC,IAAI;AAAA,MACF,OAAO;AAAA,cACP;AAAA,MACA,MAAM,KAAK,MAAM;AAAA,MACjB,KAAK,iBAAiB,IAAI;AAAA;AAAA;AAAA,OAKxB,MAAK,GAAkB;AAAA,IAC3B,KAAK,SAAS;AAAA,IACd,KAAK,WAAW;AAAA,IAChB,MAAM,KAAK,WAAW;AAAA,IACtB,MAAM,QAAQ,IAAI,KAAK,YAAY,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAAA,IACxD,KAAK,YAAY,SAAS;AAAA;AAAA,MAIxB,eAAe,GAAW;AAAA,IAC5B,OAAO,KAAK,YAAY;AAAA;AAAA,MAItB,SAAS,GAAW;AAAA,IACtB,OAAO,KAAK,MAAM;AAAA;AAAA,OAGN,aAAY,GAAkB;AAAA,IAC1C,IAAI,KAAK,UAAU;AAAA,MACjB;AAAA,IACF;AAAA,IAEA,OAAO,KAAK,MAAM,SAAS,GAAG;AAAA,MAC5B,MAAM,WAAW,KAAK,YAAY,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM;AAAA,MAChE,IAAI,UAAU;AAAA,QACZ,MAAM,QAAQ,KAAK,MAAM,MAAM;AAAA,QAC/B,IAAI,CAAC,OAAO;AAAA,UACV;AAAA,QACF;AAAA,QACA,IAAI;AAAA,UACF,MAAM,SAAS,MAAM,SAAS,KAAK,MAAM,OAAO;AAAA,UAChD,MAAM,QAAQ,MAAM;AAAA,UACpB,IAAI,CAAC,SAAS,QAAQ;AAAA,YACpB,MAAM,SAAS,MAAM;AAAA,YACrB,KAAK,iBAAiB,QAAQ;AAAA,UAChC;AAAA,UACA,OAAO,KAAK;AAAA,UACZ,MAAM,OAAO,GAAG;AAAA,UAChB,MAAM,SAAS,MAAM,EAAE,MAAM,MAAG;AAAA,YAAG;AAAA,WAAS;AAAA,UAC5C,KAAK,iBAAiB,QAAQ;AAAA;AAAA,QAEhC;AAAA,MACF;AAAA,MAEA,IAAI,KAAK,YAAY,SAAS,KAAK,gBAAgB;AAAA,QACjD,MAAM,QAAQ,KAAK,MAAM,MAAM;AAAA,QAC/B,IAAI,CAAC,OAAO;AAAA,UACV;AAAA,QACF;AAAA,QACA,MAAM,OAAO,MAAM,KAAK,gBAAgB;AAAA,QACxC,IAAI;AAAA,UACF,MAAM,SAAS,MAAM,KAAK,KAAK,MAAM,OAAO;AAAA,UAC5C,MAAM,QAAQ,MAAM;AAAA,UACpB,IAAI,CAAC,KAAK,QAAQ;AAAA,YAChB,MAAM,KAAK,MAAM;AAAA,YACjB,KAAK,iBAAiB,IAAI;AAAA,UAC5B;AAAA,UACA,OAAO,KAAK;AAAA,UACZ,MAAM,OAAO,GAAG;AAAA,UAChB,MAAM,KAAK,MAAM,EAAE,MAAM,MAAG;AAAA,YAAG;AAAA,WAAS;AAAA,UACxC,KAAK,iBAAiB,IAAI;AAAA;AAAA,QAE5B;AAAA,MACF;AAAA,MAEA;AAAA,IACF;AAAA;AAAA,OAGY,gBAAe,GAA8B;AAAA,IACzD,MAAM,WAAW,kBAAkB,KAAK,MAAM;AAAA,IAC9C,MAAM,OAAO,MAAM,uBAAuB;AAAA,MACxC,QAAQ,KAAK;AAAA,MACb,aAAa,KAAK;AAAA,MAClB,aAAa,SAAS;AAAA,MACtB,eAAe,KAAK;AAAA,IACtB,CAAC;AAAA,IACD,KAAK,YAAY,KAAK,IAAI;AAAA,IAC1B,OAAO;AAAA;AAAA,EAGD,gBAAgB,CAAC,MAA8B;AAAA,IACrD,MAAM,QAAQ,KAAK,YAAY,QAAQ,IAAI;AAAA,IAC3C,IAAI,SAAS,GAAG;AAAA,MACd,KAAK,YAAY,OAAO,OAAO,CAAC;AAAA,IAClC;AAAA;AAAA,OAGY,WAAU,GAAkB;AAAA,IACxC,OAAO,KAAK,MAAM,SAAS,KAAK,KAAK,YAAY,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,GAAG;AAAA,MACrE,MAAM,KAAK,aAAa;AAAA,MACxB,IAAI,KAAK,MAAM,SAAS,GAAG;AAAA,QACzB,MAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAAA,MAC5C;AAAA,IACF;AAAA;AAEJ;",
9
+ "debugId": "A46A2DDD71D8492864756E2164756E21",
10
+ "names": []
11
+ }
@@ -0,0 +1,85 @@
1
+ import {
2
+ extractEmails,
3
+ parseAddresses,
4
+ resolveAttachments,
5
+ toMIMEHeader
6
+ } from "../../chunk-hdqpvsm8.js";
7
+ import {
8
+ encodeBase64
9
+ } from "../../chunk-794hc3m4.js";
10
+ import"../../chunk-v0bahtg2.js";
11
+
12
+ // src/transports/postmark.ts
13
+ class PostmarkError extends Error {
14
+ statusCode;
15
+ apiError;
16
+ constructor(message, statusCode, apiError) {
17
+ super(message);
18
+ this.statusCode = statusCode;
19
+ this.apiError = apiError;
20
+ this.name = "PostmarkError";
21
+ }
22
+ }
23
+
24
+ class PostmarkTransport {
25
+ serverToken;
26
+ constructor(config) {
27
+ this.serverToken = config.serverToken;
28
+ }
29
+ async send(options) {
30
+ const attachments = await resolveAttachments(options.attachments);
31
+ const from = parseAddresses(options.from)[0];
32
+ const body = {
33
+ From: from ? toMIMEHeader(from) : "",
34
+ To: parseAddresses(options.to).map(toMIMEHeader).join(", "),
35
+ Subject: options.subject,
36
+ ...options.cc ? { Cc: parseAddresses(options.cc).map(toMIMEHeader).join(", ") } : {},
37
+ ...options.bcc ? { Bcc: parseAddresses(options.bcc).map(toMIMEHeader).join(", ") } : {},
38
+ ...options.replyTo ? { ReplyTo: parseAddresses(options.replyTo).map(toMIMEHeader).join(", ") } : {},
39
+ ...options.text ? { TextBody: options.text } : {},
40
+ ...options.html ? { HtmlBody: options.html } : {},
41
+ ...options.headers ? { Headers: Object.entries(options.headers).map(([Name, Value]) => ({ Name, Value })) } : {},
42
+ ...attachments.length > 0 ? {
43
+ Attachments: attachments.map((att) => ({
44
+ Name: att.filename,
45
+ Content: att.content instanceof Uint8Array ? encodeBase64(att.content).replace(/\r\n/g, "") : att.content,
46
+ ContentType: att.contentType ?? "application/octet-stream",
47
+ ...att.contentId ? { ContentID: att.contentId } : {}
48
+ }))
49
+ } : {}
50
+ };
51
+ const response = await fetch("https://api.postmarkapp.com/email", {
52
+ method: "POST",
53
+ headers: {
54
+ "X-Postmark-Server-Token": this.serverToken,
55
+ "Content-Type": "application/json",
56
+ Accept: "application/json"
57
+ },
58
+ body: JSON.stringify(body)
59
+ });
60
+ const payload = await response.json();
61
+ if (!response.ok) {
62
+ throw new PostmarkError(payload.Message ?? "Postmark API error", response.status, payload);
63
+ }
64
+ return {
65
+ messageId: payload.MessageID ?? options.messageId ?? "",
66
+ accepted: extractEmails(options.to),
67
+ rejected: [],
68
+ response: payload.Message ?? "OK",
69
+ envelope: {
70
+ from: from?.address ?? "",
71
+ to: [
72
+ ...extractEmails(options.to),
73
+ ...options.cc ? extractEmails(options.cc) : [],
74
+ ...options.bcc ? extractEmails(options.bcc) : []
75
+ ]
76
+ }
77
+ };
78
+ }
79
+ }
80
+ export {
81
+ PostmarkTransport,
82
+ PostmarkError
83
+ };
84
+
85
+ //# debugId=882FB10A12306FDB64756E2164756E21