@upyo/smtp 0.1.0-dev.9 → 0.1.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.
- package/README.md +25 -46
- package/dist/index.cjs +33 -46
- package/dist/index.d.cts +12 -69
- package/dist/index.d.ts +12 -69
- package/dist/index.js +33 -45
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -3,7 +3,16 @@
|
|
|
3
3
|
@upyo/smtp
|
|
4
4
|
==========
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
[![JSR][JSR badge]][JSR]
|
|
7
|
+
[![npm][npm badge]][npm]
|
|
8
|
+
|
|
9
|
+
SMTP transport implementation for the [Upyo] email library.
|
|
10
|
+
|
|
11
|
+
[JSR]: https://jsr.io/@upyo/smtp
|
|
12
|
+
[JSR badge]: https://jsr.io/badges/@upyo/smtp
|
|
13
|
+
[npm]: https://www.npmjs.com/package/@upyo/smtp
|
|
14
|
+
[npm badge]: https://img.shields.io/npm/v/@upyo/smtp?logo=npm
|
|
15
|
+
[Upyo]: https://upyo.org/
|
|
7
16
|
|
|
8
17
|
|
|
9
18
|
Features
|
|
@@ -47,8 +56,8 @@ Usage
|
|
|
47
56
|
### Basic Email Sending
|
|
48
57
|
|
|
49
58
|
~~~~ typescript
|
|
59
|
+
import { createMessage } from "@upyo/core";
|
|
50
60
|
import { SmtpTransport } from "@upyo/smtp";
|
|
51
|
-
import { type Message } from "@upyo/core";
|
|
52
61
|
|
|
53
62
|
const transport = new SmtpTransport({
|
|
54
63
|
host: "smtp.example.com",
|
|
@@ -60,53 +69,19 @@ const transport = new SmtpTransport({
|
|
|
60
69
|
},
|
|
61
70
|
});
|
|
62
71
|
|
|
63
|
-
const message
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
ccRecipients: [],
|
|
67
|
-
bccRecipients: [],
|
|
68
|
-
replyRecipients: [],
|
|
72
|
+
const message = createMessage({
|
|
73
|
+
from: "sender@example.com",
|
|
74
|
+
to: "recipient@example.net",
|
|
69
75
|
subject: "Hello from Upyo!",
|
|
70
76
|
content: { text: "This is a test email." },
|
|
71
|
-
|
|
72
|
-
priority: "normal",
|
|
73
|
-
tags: [],
|
|
74
|
-
headers: new Headers(),
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const receipt = await transport.send(message);
|
|
78
|
-
console.log("Email sent:", receipt.successful);
|
|
79
|
-
~~~~
|
|
80
|
-
|
|
81
|
-
### HTML Email with Attachments
|
|
82
|
-
|
|
83
|
-
~~~~ typescript
|
|
84
|
-
const message: Message = {
|
|
85
|
-
sender: { address: "sender@example.com" },
|
|
86
|
-
recipients: [{ address: "recipient@example.com" }],
|
|
87
|
-
ccRecipients: [],
|
|
88
|
-
bccRecipients: [],
|
|
89
|
-
replyRecipients: [],
|
|
90
|
-
subject: "HTML Email with Attachment",
|
|
91
|
-
content: {
|
|
92
|
-
html: "<h1>Hello!</h1><p>This is an <strong>HTML</strong> email.</p>",
|
|
93
|
-
text: "Hello!\nThis is an HTML email.",
|
|
94
|
-
},
|
|
95
|
-
attachments: [
|
|
96
|
-
{
|
|
97
|
-
filename: "document.pdf",
|
|
98
|
-
content: new Uint8Array(pdfBytes),
|
|
99
|
-
contentType: "application/pdf",
|
|
100
|
-
contentId: "doc1",
|
|
101
|
-
inline: false,
|
|
102
|
-
},
|
|
103
|
-
],
|
|
104
|
-
priority: "high",
|
|
105
|
-
tags: ["newsletter"],
|
|
106
|
-
headers: new Headers([["X-Campaign-ID", "12345"]]),
|
|
107
|
-
};
|
|
77
|
+
});
|
|
108
78
|
|
|
109
79
|
const receipt = await transport.send(message);
|
|
80
|
+
if (receipt.successful) {
|
|
81
|
+
console.log("Message sent with ID:", receipt.messageId);
|
|
82
|
+
} else {
|
|
83
|
+
console.error("Send failed:", receipt.errorMessages.join(", "));
|
|
84
|
+
}
|
|
110
85
|
~~~~
|
|
111
86
|
|
|
112
87
|
### Sending Multiple Emails
|
|
@@ -115,7 +90,11 @@ const receipt = await transport.send(message);
|
|
|
115
90
|
const messages = [message1, message2, message3];
|
|
116
91
|
|
|
117
92
|
for await (const receipt of transport.sendMany(messages)) {
|
|
118
|
-
|
|
93
|
+
if (receipt.successful) {
|
|
94
|
+
console.log(`Email sent with ID: ${receipt.messageId}`);
|
|
95
|
+
} else {
|
|
96
|
+
console.error(`Email failed: ${receipt.errorMessages.join(", ")}`);
|
|
97
|
+
}
|
|
119
98
|
}
|
|
120
99
|
~~~~
|
|
121
100
|
|
package/dist/index.cjs
CHANGED
|
@@ -31,21 +31,11 @@ const __upyo_core = __toESM(require("@upyo/core"));
|
|
|
31
31
|
*
|
|
32
32
|
* This function takes a partial SMTP configuration and returns a complete
|
|
33
33
|
* configuration with all optional fields filled with sensible defaults.
|
|
34
|
+
* It is used internally by the SMTP transport.
|
|
34
35
|
*
|
|
35
36
|
* @param config - The SMTP configuration with optional fields
|
|
36
37
|
* @returns A resolved configuration with all defaults applied
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* ```typescript
|
|
40
|
-
* const resolved = createSmtpConfig({
|
|
41
|
-
* host: 'smtp.example.com',
|
|
42
|
-
* auth: { user: 'user', pass: 'pass' }
|
|
43
|
-
* });
|
|
44
|
-
*
|
|
45
|
-
* // resolved.port will be 587 (default)
|
|
46
|
-
* // resolved.secure will be true (default)
|
|
47
|
-
* // resolved.poolSize will be 5 (default)
|
|
48
|
-
* ```
|
|
38
|
+
* @internal
|
|
49
39
|
*/
|
|
50
40
|
function createSmtpConfig(config) {
|
|
51
41
|
return {
|
|
@@ -266,7 +256,7 @@ var SmtpConnection = class {
|
|
|
266
256
|
|
|
267
257
|
//#endregion
|
|
268
258
|
//#region src/message-converter.ts
|
|
269
|
-
function convertMessage(message) {
|
|
259
|
+
async function convertMessage(message) {
|
|
270
260
|
const envelope = {
|
|
271
261
|
from: message.sender.address,
|
|
272
262
|
to: [
|
|
@@ -275,13 +265,13 @@ function convertMessage(message) {
|
|
|
275
265
|
...message.bccRecipients.map((r) => r.address)
|
|
276
266
|
]
|
|
277
267
|
};
|
|
278
|
-
const raw = buildRawMessage(message);
|
|
268
|
+
const raw = await buildRawMessage(message);
|
|
279
269
|
return {
|
|
280
270
|
envelope,
|
|
281
271
|
raw
|
|
282
272
|
};
|
|
283
273
|
}
|
|
284
|
-
function buildRawMessage(message) {
|
|
274
|
+
async function buildRawMessage(message) {
|
|
285
275
|
const lines = [];
|
|
286
276
|
const boundary = generateBoundary();
|
|
287
277
|
const hasAttachments = message.attachments.length > 0;
|
|
@@ -346,7 +336,7 @@ function buildRawMessage(message) {
|
|
|
346
336
|
lines.push(`Content-ID: <${attachment.contentId}>`);
|
|
347
337
|
} else lines.push(`Content-Disposition: attachment; filename="${attachment.filename}"`);
|
|
348
338
|
lines.push("");
|
|
349
|
-
lines.push(encodeBase64(attachment.content));
|
|
339
|
+
lines.push(encodeBase64(await attachment.content));
|
|
350
340
|
}
|
|
351
341
|
lines.push("");
|
|
352
342
|
lines.push(`--${boundary}--`);
|
|
@@ -428,9 +418,15 @@ function encodeBase64(data) {
|
|
|
428
418
|
* ```
|
|
429
419
|
*/
|
|
430
420
|
var SmtpTransport = class {
|
|
421
|
+
/**
|
|
422
|
+
* The SMTP configuration used by this transport.
|
|
423
|
+
*/
|
|
431
424
|
config;
|
|
432
|
-
|
|
425
|
+
/**
|
|
426
|
+
* The maximum number of connections in the pool.
|
|
427
|
+
*/
|
|
433
428
|
poolSize;
|
|
429
|
+
connectionPool = [];
|
|
434
430
|
/**
|
|
435
431
|
* Creates a new SMTP transport instance.
|
|
436
432
|
*
|
|
@@ -472,21 +468,19 @@ var SmtpTransport = class {
|
|
|
472
468
|
const connection = await this.getConnection(options?.signal);
|
|
473
469
|
try {
|
|
474
470
|
options?.signal?.throwIfAborted();
|
|
475
|
-
const smtpMessage = convertMessage(message);
|
|
471
|
+
const smtpMessage = await convertMessage(message);
|
|
476
472
|
options?.signal?.throwIfAborted();
|
|
477
473
|
const messageId = await connection.sendMessage(smtpMessage, options?.signal);
|
|
478
474
|
await this.returnConnection(connection);
|
|
479
475
|
return {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
successful: true
|
|
476
|
+
successful: true,
|
|
477
|
+
messageId
|
|
483
478
|
};
|
|
484
479
|
} catch (error) {
|
|
485
480
|
await this.discardConnection(connection);
|
|
486
481
|
return {
|
|
487
|
-
|
|
488
|
-
errorMessages: [error instanceof Error ? error.message : String(error)]
|
|
489
|
-
successful: false
|
|
482
|
+
successful: false,
|
|
483
|
+
errorMessages: [error instanceof Error ? error.message : String(error)]
|
|
490
484
|
};
|
|
491
485
|
}
|
|
492
486
|
}
|
|
@@ -528,27 +522,24 @@ var SmtpTransport = class {
|
|
|
528
522
|
options?.signal?.throwIfAborted();
|
|
529
523
|
if (!connectionValid) {
|
|
530
524
|
yield {
|
|
531
|
-
|
|
532
|
-
errorMessages: ["Connection is no longer valid"]
|
|
533
|
-
successful: false
|
|
525
|
+
successful: false,
|
|
526
|
+
errorMessages: ["Connection is no longer valid"]
|
|
534
527
|
};
|
|
535
528
|
continue;
|
|
536
529
|
}
|
|
537
530
|
try {
|
|
538
|
-
const smtpMessage = convertMessage(message);
|
|
531
|
+
const smtpMessage = await convertMessage(message);
|
|
539
532
|
options?.signal?.throwIfAborted();
|
|
540
533
|
const messageId = await connection.sendMessage(smtpMessage, options?.signal);
|
|
541
534
|
yield {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
successful: true
|
|
535
|
+
successful: true,
|
|
536
|
+
messageId
|
|
545
537
|
};
|
|
546
538
|
} catch (error) {
|
|
547
539
|
connectionValid = false;
|
|
548
540
|
yield {
|
|
549
|
-
|
|
550
|
-
errorMessages: [error instanceof Error ? error.message : String(error)]
|
|
551
|
-
successful: false
|
|
541
|
+
successful: false,
|
|
542
|
+
errorMessages: [error instanceof Error ? error.message : String(error)]
|
|
552
543
|
};
|
|
553
544
|
}
|
|
554
545
|
}
|
|
@@ -556,27 +547,24 @@ var SmtpTransport = class {
|
|
|
556
547
|
options?.signal?.throwIfAborted();
|
|
557
548
|
if (!connectionValid) {
|
|
558
549
|
yield {
|
|
559
|
-
|
|
560
|
-
errorMessages: ["Connection is no longer valid"]
|
|
561
|
-
successful: false
|
|
550
|
+
successful: false,
|
|
551
|
+
errorMessages: ["Connection is no longer valid"]
|
|
562
552
|
};
|
|
563
553
|
continue;
|
|
564
554
|
}
|
|
565
555
|
try {
|
|
566
|
-
const smtpMessage = convertMessage(message);
|
|
556
|
+
const smtpMessage = await convertMessage(message);
|
|
567
557
|
options?.signal?.throwIfAborted();
|
|
568
558
|
const messageId = await connection.sendMessage(smtpMessage, options?.signal);
|
|
569
559
|
yield {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
successful: true
|
|
560
|
+
successful: true,
|
|
561
|
+
messageId
|
|
573
562
|
};
|
|
574
563
|
} catch (error) {
|
|
575
564
|
connectionValid = false;
|
|
576
565
|
yield {
|
|
577
|
-
|
|
578
|
-
errorMessages: [error instanceof Error ? error.message : String(error)]
|
|
579
|
-
successful: false
|
|
566
|
+
successful: false,
|
|
567
|
+
errorMessages: [error instanceof Error ? error.message : String(error)]
|
|
580
568
|
};
|
|
581
569
|
}
|
|
582
570
|
}
|
|
@@ -661,5 +649,4 @@ var SmtpTransport = class {
|
|
|
661
649
|
};
|
|
662
650
|
|
|
663
651
|
//#endregion
|
|
664
|
-
exports.SmtpTransport = SmtpTransport;
|
|
665
|
-
exports.createSmtpConfig = createSmtpConfig;
|
|
652
|
+
exports.SmtpTransport = SmtpTransport;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { Message, Receipt, Transport, TransportOptions } from "@upyo/core";
|
|
2
|
-
import { Socket } from "node:net";
|
|
3
|
-
import { TLSSocket } from "node:tls";
|
|
4
2
|
|
|
5
3
|
//#region src/config.d.ts
|
|
6
4
|
|
|
@@ -154,67 +152,6 @@ interface SmtpTlsOptions {
|
|
|
154
152
|
* This type represents the final configuration after applying defaults,
|
|
155
153
|
* used internally by the SMTP transport implementation.
|
|
156
154
|
*/
|
|
157
|
-
type ResolvedSmtpConfig = Omit<Required<SmtpConfig>, "auth" | "tls"> & {
|
|
158
|
-
readonly auth?: SmtpAuth;
|
|
159
|
-
readonly tls?: SmtpTlsOptions;
|
|
160
|
-
};
|
|
161
|
-
/**
|
|
162
|
-
* Creates a resolved SMTP configuration by applying default values to optional fields.
|
|
163
|
-
*
|
|
164
|
-
* This function takes a partial SMTP configuration and returns a complete
|
|
165
|
-
* configuration with all optional fields filled with sensible defaults.
|
|
166
|
-
*
|
|
167
|
-
* @param config - The SMTP configuration with optional fields
|
|
168
|
-
* @returns A resolved configuration with all defaults applied
|
|
169
|
-
*
|
|
170
|
-
* @example
|
|
171
|
-
* ```typescript
|
|
172
|
-
* const resolved = createSmtpConfig({
|
|
173
|
-
* host: 'smtp.example.com',
|
|
174
|
-
* auth: { user: 'user', pass: 'pass' }
|
|
175
|
-
* });
|
|
176
|
-
*
|
|
177
|
-
* // resolved.port will be 587 (default)
|
|
178
|
-
* // resolved.secure will be true (default)
|
|
179
|
-
* // resolved.poolSize will be 5 (default)
|
|
180
|
-
* ```
|
|
181
|
-
*/
|
|
182
|
-
declare function createSmtpConfig(config: SmtpConfig): ResolvedSmtpConfig;
|
|
183
|
-
//#endregion
|
|
184
|
-
//#region src/message-converter.d.ts
|
|
185
|
-
interface SmtpMessage {
|
|
186
|
-
readonly envelope: SmtpEnvelope;
|
|
187
|
-
readonly raw: string;
|
|
188
|
-
}
|
|
189
|
-
interface SmtpEnvelope {
|
|
190
|
-
readonly from: string;
|
|
191
|
-
readonly to: string[];
|
|
192
|
-
}
|
|
193
|
-
//#endregion
|
|
194
|
-
//#region src/smtp-connection.d.ts
|
|
195
|
-
declare class SmtpConnection {
|
|
196
|
-
socket: Socket | TLSSocket | null;
|
|
197
|
-
config: ResolvedSmtpConfig;
|
|
198
|
-
authenticated: boolean;
|
|
199
|
-
capabilities: string[];
|
|
200
|
-
constructor(config: SmtpConfig);
|
|
201
|
-
connect(signal?: AbortSignal): Promise<void>;
|
|
202
|
-
sendCommand(command: string, signal?: AbortSignal): Promise<SmtpResponse>;
|
|
203
|
-
greeting(signal?: AbortSignal): Promise<SmtpResponse>;
|
|
204
|
-
ehlo(signal?: AbortSignal): Promise<void>;
|
|
205
|
-
authenticate(signal?: AbortSignal): Promise<void>;
|
|
206
|
-
private authPlain;
|
|
207
|
-
authLogin(signal?: AbortSignal): Promise<void>;
|
|
208
|
-
sendMessage(message: SmtpMessage, signal?: AbortSignal): Promise<string>;
|
|
209
|
-
extractMessageId(response: string): string;
|
|
210
|
-
quit(): Promise<void>;
|
|
211
|
-
reset(signal?: AbortSignal): Promise<void>;
|
|
212
|
-
}
|
|
213
|
-
interface SmtpResponse {
|
|
214
|
-
readonly code: number;
|
|
215
|
-
readonly message: string;
|
|
216
|
-
readonly raw: string;
|
|
217
|
-
}
|
|
218
155
|
//#endregion
|
|
219
156
|
//#region src/smtp-transport.d.ts
|
|
220
157
|
/**
|
|
@@ -251,9 +188,15 @@ interface SmtpResponse {
|
|
|
251
188
|
* ```
|
|
252
189
|
*/
|
|
253
190
|
declare class SmtpTransport implements Transport, AsyncDisposable {
|
|
191
|
+
/**
|
|
192
|
+
* The SMTP configuration used by this transport.
|
|
193
|
+
*/
|
|
254
194
|
config: SmtpConfig;
|
|
255
|
-
|
|
195
|
+
/**
|
|
196
|
+
* The maximum number of connections in the pool.
|
|
197
|
+
*/
|
|
256
198
|
poolSize: number;
|
|
199
|
+
private connectionPool;
|
|
257
200
|
/**
|
|
258
201
|
* Creates a new SMTP transport instance.
|
|
259
202
|
*
|
|
@@ -317,10 +260,10 @@ declare class SmtpTransport implements Transport, AsyncDisposable {
|
|
|
317
260
|
* @returns An async iterable of receipts, one for each message.
|
|
318
261
|
*/
|
|
319
262
|
sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt>;
|
|
320
|
-
getConnection
|
|
321
|
-
connectAndSetup
|
|
322
|
-
returnConnection
|
|
323
|
-
discardConnection
|
|
263
|
+
private getConnection;
|
|
264
|
+
private connectAndSetup;
|
|
265
|
+
private returnConnection;
|
|
266
|
+
private discardConnection;
|
|
324
267
|
/**
|
|
325
268
|
* Closes all active SMTP connections in the connection pool.
|
|
326
269
|
*
|
|
@@ -352,4 +295,4 @@ declare class SmtpTransport implements Transport, AsyncDisposable {
|
|
|
352
295
|
[Symbol.asyncDispose](): Promise<void>;
|
|
353
296
|
}
|
|
354
297
|
//#endregion
|
|
355
|
-
export { SmtpAuth, SmtpConfig, SmtpTlsOptions, SmtpTransport
|
|
298
|
+
export { SmtpAuth, SmtpConfig, SmtpTlsOptions, SmtpTransport };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { Socket } from "node:net";
|
|
2
|
-
import { TLSSocket } from "node:tls";
|
|
3
1
|
import { Message, Receipt, Transport, TransportOptions } from "@upyo/core";
|
|
4
2
|
|
|
5
3
|
//#region src/config.d.ts
|
|
@@ -154,67 +152,6 @@ interface SmtpTlsOptions {
|
|
|
154
152
|
* This type represents the final configuration after applying defaults,
|
|
155
153
|
* used internally by the SMTP transport implementation.
|
|
156
154
|
*/
|
|
157
|
-
type ResolvedSmtpConfig = Omit<Required<SmtpConfig>, "auth" | "tls"> & {
|
|
158
|
-
readonly auth?: SmtpAuth;
|
|
159
|
-
readonly tls?: SmtpTlsOptions;
|
|
160
|
-
};
|
|
161
|
-
/**
|
|
162
|
-
* Creates a resolved SMTP configuration by applying default values to optional fields.
|
|
163
|
-
*
|
|
164
|
-
* This function takes a partial SMTP configuration and returns a complete
|
|
165
|
-
* configuration with all optional fields filled with sensible defaults.
|
|
166
|
-
*
|
|
167
|
-
* @param config - The SMTP configuration with optional fields
|
|
168
|
-
* @returns A resolved configuration with all defaults applied
|
|
169
|
-
*
|
|
170
|
-
* @example
|
|
171
|
-
* ```typescript
|
|
172
|
-
* const resolved = createSmtpConfig({
|
|
173
|
-
* host: 'smtp.example.com',
|
|
174
|
-
* auth: { user: 'user', pass: 'pass' }
|
|
175
|
-
* });
|
|
176
|
-
*
|
|
177
|
-
* // resolved.port will be 587 (default)
|
|
178
|
-
* // resolved.secure will be true (default)
|
|
179
|
-
* // resolved.poolSize will be 5 (default)
|
|
180
|
-
* ```
|
|
181
|
-
*/
|
|
182
|
-
declare function createSmtpConfig(config: SmtpConfig): ResolvedSmtpConfig;
|
|
183
|
-
//#endregion
|
|
184
|
-
//#region src/message-converter.d.ts
|
|
185
|
-
interface SmtpMessage {
|
|
186
|
-
readonly envelope: SmtpEnvelope;
|
|
187
|
-
readonly raw: string;
|
|
188
|
-
}
|
|
189
|
-
interface SmtpEnvelope {
|
|
190
|
-
readonly from: string;
|
|
191
|
-
readonly to: string[];
|
|
192
|
-
}
|
|
193
|
-
//#endregion
|
|
194
|
-
//#region src/smtp-connection.d.ts
|
|
195
|
-
declare class SmtpConnection {
|
|
196
|
-
socket: Socket | TLSSocket | null;
|
|
197
|
-
config: ResolvedSmtpConfig;
|
|
198
|
-
authenticated: boolean;
|
|
199
|
-
capabilities: string[];
|
|
200
|
-
constructor(config: SmtpConfig);
|
|
201
|
-
connect(signal?: AbortSignal): Promise<void>;
|
|
202
|
-
sendCommand(command: string, signal?: AbortSignal): Promise<SmtpResponse>;
|
|
203
|
-
greeting(signal?: AbortSignal): Promise<SmtpResponse>;
|
|
204
|
-
ehlo(signal?: AbortSignal): Promise<void>;
|
|
205
|
-
authenticate(signal?: AbortSignal): Promise<void>;
|
|
206
|
-
private authPlain;
|
|
207
|
-
authLogin(signal?: AbortSignal): Promise<void>;
|
|
208
|
-
sendMessage(message: SmtpMessage, signal?: AbortSignal): Promise<string>;
|
|
209
|
-
extractMessageId(response: string): string;
|
|
210
|
-
quit(): Promise<void>;
|
|
211
|
-
reset(signal?: AbortSignal): Promise<void>;
|
|
212
|
-
}
|
|
213
|
-
interface SmtpResponse {
|
|
214
|
-
readonly code: number;
|
|
215
|
-
readonly message: string;
|
|
216
|
-
readonly raw: string;
|
|
217
|
-
}
|
|
218
155
|
//#endregion
|
|
219
156
|
//#region src/smtp-transport.d.ts
|
|
220
157
|
/**
|
|
@@ -251,9 +188,15 @@ interface SmtpResponse {
|
|
|
251
188
|
* ```
|
|
252
189
|
*/
|
|
253
190
|
declare class SmtpTransport implements Transport, AsyncDisposable {
|
|
191
|
+
/**
|
|
192
|
+
* The SMTP configuration used by this transport.
|
|
193
|
+
*/
|
|
254
194
|
config: SmtpConfig;
|
|
255
|
-
|
|
195
|
+
/**
|
|
196
|
+
* The maximum number of connections in the pool.
|
|
197
|
+
*/
|
|
256
198
|
poolSize: number;
|
|
199
|
+
private connectionPool;
|
|
257
200
|
/**
|
|
258
201
|
* Creates a new SMTP transport instance.
|
|
259
202
|
*
|
|
@@ -317,10 +260,10 @@ declare class SmtpTransport implements Transport, AsyncDisposable {
|
|
|
317
260
|
* @returns An async iterable of receipts, one for each message.
|
|
318
261
|
*/
|
|
319
262
|
sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt>;
|
|
320
|
-
getConnection
|
|
321
|
-
connectAndSetup
|
|
322
|
-
returnConnection
|
|
323
|
-
discardConnection
|
|
263
|
+
private getConnection;
|
|
264
|
+
private connectAndSetup;
|
|
265
|
+
private returnConnection;
|
|
266
|
+
private discardConnection;
|
|
324
267
|
/**
|
|
325
268
|
* Closes all active SMTP connections in the connection pool.
|
|
326
269
|
*
|
|
@@ -352,4 +295,4 @@ declare class SmtpTransport implements Transport, AsyncDisposable {
|
|
|
352
295
|
[Symbol.asyncDispose](): Promise<void>;
|
|
353
296
|
}
|
|
354
297
|
//#endregion
|
|
355
|
-
export { SmtpAuth, SmtpConfig, SmtpTlsOptions, SmtpTransport
|
|
298
|
+
export { SmtpAuth, SmtpConfig, SmtpTlsOptions, SmtpTransport };
|
package/dist/index.js
CHANGED
|
@@ -8,21 +8,11 @@ import { formatAddress } from "@upyo/core";
|
|
|
8
8
|
*
|
|
9
9
|
* This function takes a partial SMTP configuration and returns a complete
|
|
10
10
|
* configuration with all optional fields filled with sensible defaults.
|
|
11
|
+
* It is used internally by the SMTP transport.
|
|
11
12
|
*
|
|
12
13
|
* @param config - The SMTP configuration with optional fields
|
|
13
14
|
* @returns A resolved configuration with all defaults applied
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```typescript
|
|
17
|
-
* const resolved = createSmtpConfig({
|
|
18
|
-
* host: 'smtp.example.com',
|
|
19
|
-
* auth: { user: 'user', pass: 'pass' }
|
|
20
|
-
* });
|
|
21
|
-
*
|
|
22
|
-
* // resolved.port will be 587 (default)
|
|
23
|
-
* // resolved.secure will be true (default)
|
|
24
|
-
* // resolved.poolSize will be 5 (default)
|
|
25
|
-
* ```
|
|
15
|
+
* @internal
|
|
26
16
|
*/
|
|
27
17
|
function createSmtpConfig(config) {
|
|
28
18
|
return {
|
|
@@ -243,7 +233,7 @@ var SmtpConnection = class {
|
|
|
243
233
|
|
|
244
234
|
//#endregion
|
|
245
235
|
//#region src/message-converter.ts
|
|
246
|
-
function convertMessage(message) {
|
|
236
|
+
async function convertMessage(message) {
|
|
247
237
|
const envelope = {
|
|
248
238
|
from: message.sender.address,
|
|
249
239
|
to: [
|
|
@@ -252,13 +242,13 @@ function convertMessage(message) {
|
|
|
252
242
|
...message.bccRecipients.map((r) => r.address)
|
|
253
243
|
]
|
|
254
244
|
};
|
|
255
|
-
const raw = buildRawMessage(message);
|
|
245
|
+
const raw = await buildRawMessage(message);
|
|
256
246
|
return {
|
|
257
247
|
envelope,
|
|
258
248
|
raw
|
|
259
249
|
};
|
|
260
250
|
}
|
|
261
|
-
function buildRawMessage(message) {
|
|
251
|
+
async function buildRawMessage(message) {
|
|
262
252
|
const lines = [];
|
|
263
253
|
const boundary = generateBoundary();
|
|
264
254
|
const hasAttachments = message.attachments.length > 0;
|
|
@@ -323,7 +313,7 @@ function buildRawMessage(message) {
|
|
|
323
313
|
lines.push(`Content-ID: <${attachment.contentId}>`);
|
|
324
314
|
} else lines.push(`Content-Disposition: attachment; filename="${attachment.filename}"`);
|
|
325
315
|
lines.push("");
|
|
326
|
-
lines.push(encodeBase64(attachment.content));
|
|
316
|
+
lines.push(encodeBase64(await attachment.content));
|
|
327
317
|
}
|
|
328
318
|
lines.push("");
|
|
329
319
|
lines.push(`--${boundary}--`);
|
|
@@ -405,9 +395,15 @@ function encodeBase64(data) {
|
|
|
405
395
|
* ```
|
|
406
396
|
*/
|
|
407
397
|
var SmtpTransport = class {
|
|
398
|
+
/**
|
|
399
|
+
* The SMTP configuration used by this transport.
|
|
400
|
+
*/
|
|
408
401
|
config;
|
|
409
|
-
|
|
402
|
+
/**
|
|
403
|
+
* The maximum number of connections in the pool.
|
|
404
|
+
*/
|
|
410
405
|
poolSize;
|
|
406
|
+
connectionPool = [];
|
|
411
407
|
/**
|
|
412
408
|
* Creates a new SMTP transport instance.
|
|
413
409
|
*
|
|
@@ -449,21 +445,19 @@ var SmtpTransport = class {
|
|
|
449
445
|
const connection = await this.getConnection(options?.signal);
|
|
450
446
|
try {
|
|
451
447
|
options?.signal?.throwIfAborted();
|
|
452
|
-
const smtpMessage = convertMessage(message);
|
|
448
|
+
const smtpMessage = await convertMessage(message);
|
|
453
449
|
options?.signal?.throwIfAborted();
|
|
454
450
|
const messageId = await connection.sendMessage(smtpMessage, options?.signal);
|
|
455
451
|
await this.returnConnection(connection);
|
|
456
452
|
return {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
successful: true
|
|
453
|
+
successful: true,
|
|
454
|
+
messageId
|
|
460
455
|
};
|
|
461
456
|
} catch (error) {
|
|
462
457
|
await this.discardConnection(connection);
|
|
463
458
|
return {
|
|
464
|
-
|
|
465
|
-
errorMessages: [error instanceof Error ? error.message : String(error)]
|
|
466
|
-
successful: false
|
|
459
|
+
successful: false,
|
|
460
|
+
errorMessages: [error instanceof Error ? error.message : String(error)]
|
|
467
461
|
};
|
|
468
462
|
}
|
|
469
463
|
}
|
|
@@ -505,27 +499,24 @@ var SmtpTransport = class {
|
|
|
505
499
|
options?.signal?.throwIfAborted();
|
|
506
500
|
if (!connectionValid) {
|
|
507
501
|
yield {
|
|
508
|
-
|
|
509
|
-
errorMessages: ["Connection is no longer valid"]
|
|
510
|
-
successful: false
|
|
502
|
+
successful: false,
|
|
503
|
+
errorMessages: ["Connection is no longer valid"]
|
|
511
504
|
};
|
|
512
505
|
continue;
|
|
513
506
|
}
|
|
514
507
|
try {
|
|
515
|
-
const smtpMessage = convertMessage(message);
|
|
508
|
+
const smtpMessage = await convertMessage(message);
|
|
516
509
|
options?.signal?.throwIfAborted();
|
|
517
510
|
const messageId = await connection.sendMessage(smtpMessage, options?.signal);
|
|
518
511
|
yield {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
successful: true
|
|
512
|
+
successful: true,
|
|
513
|
+
messageId
|
|
522
514
|
};
|
|
523
515
|
} catch (error) {
|
|
524
516
|
connectionValid = false;
|
|
525
517
|
yield {
|
|
526
|
-
|
|
527
|
-
errorMessages: [error instanceof Error ? error.message : String(error)]
|
|
528
|
-
successful: false
|
|
518
|
+
successful: false,
|
|
519
|
+
errorMessages: [error instanceof Error ? error.message : String(error)]
|
|
529
520
|
};
|
|
530
521
|
}
|
|
531
522
|
}
|
|
@@ -533,27 +524,24 @@ var SmtpTransport = class {
|
|
|
533
524
|
options?.signal?.throwIfAborted();
|
|
534
525
|
if (!connectionValid) {
|
|
535
526
|
yield {
|
|
536
|
-
|
|
537
|
-
errorMessages: ["Connection is no longer valid"]
|
|
538
|
-
successful: false
|
|
527
|
+
successful: false,
|
|
528
|
+
errorMessages: ["Connection is no longer valid"]
|
|
539
529
|
};
|
|
540
530
|
continue;
|
|
541
531
|
}
|
|
542
532
|
try {
|
|
543
|
-
const smtpMessage = convertMessage(message);
|
|
533
|
+
const smtpMessage = await convertMessage(message);
|
|
544
534
|
options?.signal?.throwIfAborted();
|
|
545
535
|
const messageId = await connection.sendMessage(smtpMessage, options?.signal);
|
|
546
536
|
yield {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
successful: true
|
|
537
|
+
successful: true,
|
|
538
|
+
messageId
|
|
550
539
|
};
|
|
551
540
|
} catch (error) {
|
|
552
541
|
connectionValid = false;
|
|
553
542
|
yield {
|
|
554
|
-
|
|
555
|
-
errorMessages: [error instanceof Error ? error.message : String(error)]
|
|
556
|
-
successful: false
|
|
543
|
+
successful: false,
|
|
544
|
+
errorMessages: [error instanceof Error ? error.message : String(error)]
|
|
557
545
|
};
|
|
558
546
|
}
|
|
559
547
|
}
|
|
@@ -638,4 +626,4 @@ var SmtpTransport = class {
|
|
|
638
626
|
};
|
|
639
627
|
|
|
640
628
|
//#endregion
|
|
641
|
-
export { SmtpTransport
|
|
629
|
+
export { SmtpTransport };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@upyo/smtp",
|
|
3
|
-
"version": "0.1.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "SMTP transport for Upyo email library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"email",
|
|
@@ -53,12 +53,12 @@
|
|
|
53
53
|
},
|
|
54
54
|
"sideEffects": false,
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@upyo/core": "0.1.0
|
|
56
|
+
"@upyo/core": "0.1.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@dotenvx/dotenvx": "^1.47.3",
|
|
60
60
|
"tsdown": "^0.12.7",
|
|
61
|
-
"typescript": "
|
|
61
|
+
"typescript": "5.8.3"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"build": "tsdown",
|