mikromail 0.0.7 → 1.0.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 +43 -0
- package/lib/Configuration.js +3 -1
- package/lib/Configuration.mjs +1 -1
- package/lib/MikroMail.d.mts +1 -0
- package/lib/MikroMail.d.ts +1 -0
- package/lib/MikroMail.js +30 -18
- package/lib/MikroMail.mjs +4 -4
- package/lib/SMTPClient.js +17 -11
- package/lib/SMTPClient.mjs +2 -2
- package/lib/{chunk-UDLJWUFN.mjs → chunk-5BK3VY6I.mjs} +2 -2
- package/lib/{chunk-422R3NOG.mjs → chunk-IYCQK5YZ.mjs} +13 -9
- package/lib/{chunk-NGT3KX7A.mjs → chunk-QCFOUQRH.mjs} +22 -16
- package/lib/{chunk-YVXB6HCK.mjs → chunk-ULVDQIAT.mjs} +4 -2
- package/lib/index.js +30 -18
- package/lib/index.mjs +4 -4
- package/lib/interfaces/index.d.mts +2 -0
- package/lib/interfaces/index.d.ts +2 -0
- package/lib/utils/index.js +1 -1
- package/lib/utils/index.mjs +1 -1
- package/package.json +4 -5
package/README.md
CHANGED
|
@@ -89,6 +89,49 @@ const emailOptions = {
|
|
|
89
89
|
await new MikroMail({ config }).send(emailOptions);
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
## Provider-specific configurations
|
|
93
|
+
|
|
94
|
+
### Proton Mail
|
|
95
|
+
|
|
96
|
+
Proton Mail works reliably with port 465 and implicit TLS:
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const config = {
|
|
100
|
+
user: 'your-email@proton.me',
|
|
101
|
+
password: 'YOUR_APP_PASSWORD',
|
|
102
|
+
host: 'smtp.protonmail.ch',
|
|
103
|
+
port: 465,
|
|
104
|
+
secure: true
|
|
105
|
+
};
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Note: Port 587 with STARTTLS (`secure: false`) may have connectivity issues with some Proton Mail configurations. Use port 465 for the most reliable connection.
|
|
109
|
+
|
|
110
|
+
### Test providers (Mailtrap, etc.)
|
|
111
|
+
|
|
112
|
+
Some test SMTP providers use long random IDs instead of email addresses. To support these providers, use the `skipEmailValidation` and `skipMXRecordCheck` options:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const config = {
|
|
116
|
+
user: 'your-mailtrap-username',
|
|
117
|
+
password: 'your-mailtrap-password',
|
|
118
|
+
host: 'smtp.mailtrap.io',
|
|
119
|
+
port: 587,
|
|
120
|
+
secure: false,
|
|
121
|
+
skipEmailValidation: true,
|
|
122
|
+
skipMXRecordCheck: true
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const emailOptions = {
|
|
126
|
+
from: 'sender@example.com',
|
|
127
|
+
subject: 'Test Email',
|
|
128
|
+
text: 'Hello!',
|
|
129
|
+
to: 'a1b2c3d4e5f6g7' // Long random ID used by test providers
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
await new MikroMail({ config }).send(emailOptions);
|
|
133
|
+
```
|
|
134
|
+
|
|
92
135
|
## Testing
|
|
93
136
|
|
|
94
137
|
Some of the tests require faking an SMTP server. Here we use [Mailpit](https://github.com/axllent/mailpit), which will run a server on `http://localhost:8025`.
|
package/lib/Configuration.js
CHANGED
|
@@ -67,7 +67,9 @@ var Configuration = class {
|
|
|
67
67
|
port: 465,
|
|
68
68
|
secure: true,
|
|
69
69
|
debug: false,
|
|
70
|
-
maxRetries: 2
|
|
70
|
+
maxRetries: 2,
|
|
71
|
+
skipEmailValidation: false,
|
|
72
|
+
skipMXRecordCheck: false
|
|
71
73
|
};
|
|
72
74
|
let fileConfig = {};
|
|
73
75
|
if ((0, import_node_fs.existsSync)(configFilePath)) {
|
package/lib/Configuration.mjs
CHANGED
package/lib/MikroMail.d.mts
CHANGED
|
@@ -20,6 +20,7 @@ import { ConfigurationOptions, EmailOptions } from './interfaces/index.mjs';
|
|
|
20
20
|
*/
|
|
21
21
|
declare class MikroMail {
|
|
22
22
|
private readonly smtpClient;
|
|
23
|
+
private readonly config;
|
|
23
24
|
constructor(options?: ConfigurationOptions);
|
|
24
25
|
/**
|
|
25
26
|
* Sends an email to valid domains.
|
package/lib/MikroMail.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { ConfigurationOptions, EmailOptions } from './interfaces/index.js';
|
|
|
20
20
|
*/
|
|
21
21
|
declare class MikroMail {
|
|
22
22
|
private readonly smtpClient;
|
|
23
|
+
private readonly config;
|
|
23
24
|
constructor(options?: ConfigurationOptions);
|
|
24
25
|
/**
|
|
25
26
|
* Sends an email to valid domains.
|
package/lib/MikroMail.js
CHANGED
|
@@ -79,7 +79,9 @@ var Configuration = class {
|
|
|
79
79
|
port: 465,
|
|
80
80
|
secure: true,
|
|
81
81
|
debug: false,
|
|
82
|
-
maxRetries: 2
|
|
82
|
+
maxRetries: 2,
|
|
83
|
+
skipEmailValidation: false,
|
|
84
|
+
skipMXRecordCheck: false
|
|
83
85
|
};
|
|
84
86
|
let fileConfig = {};
|
|
85
87
|
if ((0, import_node_fs.existsSync)(configFilePath)) {
|
|
@@ -189,7 +191,7 @@ function validateEmail(email) {
|
|
|
189
191
|
return false;
|
|
190
192
|
for (const part of domainParts) {
|
|
191
193
|
if (!part || part.length > 63) return false;
|
|
192
|
-
if (!/^[a-zA-Z0-9]([a-zA-Z0-9
|
|
194
|
+
if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(part)) return false;
|
|
193
195
|
}
|
|
194
196
|
return true;
|
|
195
197
|
} catch (_error) {
|
|
@@ -237,7 +239,9 @@ var SMTPClient = class {
|
|
|
237
239
|
clientName: config.clientName ?? import_node_os.default.hostname(),
|
|
238
240
|
maxRetries: config.maxRetries ?? 3,
|
|
239
241
|
retryDelay: config.retryDelay ?? 1e3,
|
|
240
|
-
skipAuthentication: config.skipAuthentication || false
|
|
242
|
+
skipAuthentication: config.skipAuthentication || false,
|
|
243
|
+
skipEmailValidation: config.skipEmailValidation || false,
|
|
244
|
+
skipMXRecordCheck: config.skipMXRecordCheck || false
|
|
241
245
|
};
|
|
242
246
|
this.socket = null;
|
|
243
247
|
this.connected = false;
|
|
@@ -642,20 +646,24 @@ ${text || ""}`;
|
|
|
642
646
|
error: "Missing required email parameters (from, to, subject, and either text or html)"
|
|
643
647
|
};
|
|
644
648
|
}
|
|
645
|
-
if (!validateEmail(from)) {
|
|
646
|
-
return {
|
|
647
|
-
success: false,
|
|
648
|
-
error: "Invalid email address format"
|
|
649
|
-
};
|
|
650
|
-
}
|
|
651
649
|
const recipients = Array.isArray(options.to) ? options.to : [options.to];
|
|
652
|
-
|
|
653
|
-
if (!validateEmail(
|
|
650
|
+
if (!this.config.skipEmailValidation) {
|
|
651
|
+
if (!validateEmail(from)) {
|
|
654
652
|
return {
|
|
655
653
|
success: false,
|
|
656
|
-
error:
|
|
654
|
+
error: "Invalid email address format"
|
|
657
655
|
};
|
|
658
656
|
}
|
|
657
|
+
for (const recipient of recipients) {
|
|
658
|
+
if (!validateEmail(recipient)) {
|
|
659
|
+
return {
|
|
660
|
+
success: false,
|
|
661
|
+
error: `Invalid recipient email address format: ${recipient}`
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
} else {
|
|
666
|
+
this.log("Email validation skipped (testing mode)");
|
|
659
667
|
}
|
|
660
668
|
for (this.retryCount = 0; this.retryCount <= this.config.maxRetries; this.retryCount++) {
|
|
661
669
|
try {
|
|
@@ -754,10 +762,12 @@ ${text || ""}`;
|
|
|
754
762
|
// src/MikroMail.ts
|
|
755
763
|
var MikroMail = class {
|
|
756
764
|
smtpClient;
|
|
765
|
+
config;
|
|
757
766
|
constructor(options) {
|
|
758
767
|
const config = new Configuration(options).get();
|
|
759
768
|
const smtpClient = new SMTPClient(config);
|
|
760
769
|
this.smtpClient = smtpClient;
|
|
770
|
+
this.config = config;
|
|
761
771
|
}
|
|
762
772
|
/**
|
|
763
773
|
* Sends an email to valid domains.
|
|
@@ -765,12 +775,14 @@ var MikroMail = class {
|
|
|
765
775
|
async send(emailOptions) {
|
|
766
776
|
try {
|
|
767
777
|
const recipients = Array.isArray(emailOptions.to) ? emailOptions.to : [emailOptions.to];
|
|
768
|
-
|
|
769
|
-
const
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
778
|
+
if (!this.config.skipMXRecordCheck) {
|
|
779
|
+
for (const recipient of recipients) {
|
|
780
|
+
const hasMXRecords = await verifyEmailDomain(recipient);
|
|
781
|
+
if (!hasMXRecords)
|
|
782
|
+
console.error(
|
|
783
|
+
`Warning: No MX records found for recipient domain: ${recipient}`
|
|
784
|
+
);
|
|
785
|
+
}
|
|
774
786
|
}
|
|
775
787
|
const result = await this.smtpClient.sendEmail(emailOptions);
|
|
776
788
|
if (result.success) console.log(`Message ID: ${result.messageId}`);
|
package/lib/MikroMail.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MikroMail
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-IYCQK5YZ.mjs";
|
|
4
|
+
import "./chunk-ULVDQIAT.mjs";
|
|
5
|
+
import "./chunk-QCFOUQRH.mjs";
|
|
6
6
|
import "./chunk-47VXJTWV.mjs";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-5BK3VY6I.mjs";
|
|
8
8
|
export {
|
|
9
9
|
MikroMail
|
|
10
10
|
};
|
package/lib/SMTPClient.js
CHANGED
|
@@ -62,7 +62,7 @@ function validateEmail(email) {
|
|
|
62
62
|
return false;
|
|
63
63
|
for (const part of domainParts) {
|
|
64
64
|
if (!part || part.length > 63) return false;
|
|
65
|
-
if (!/^[a-zA-Z0-9]([a-zA-Z0-9
|
|
65
|
+
if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(part)) return false;
|
|
66
66
|
}
|
|
67
67
|
return true;
|
|
68
68
|
} catch (_error) {
|
|
@@ -93,7 +93,9 @@ var SMTPClient = class {
|
|
|
93
93
|
clientName: config.clientName ?? import_node_os.default.hostname(),
|
|
94
94
|
maxRetries: config.maxRetries ?? 3,
|
|
95
95
|
retryDelay: config.retryDelay ?? 1e3,
|
|
96
|
-
skipAuthentication: config.skipAuthentication || false
|
|
96
|
+
skipAuthentication: config.skipAuthentication || false,
|
|
97
|
+
skipEmailValidation: config.skipEmailValidation || false,
|
|
98
|
+
skipMXRecordCheck: config.skipMXRecordCheck || false
|
|
97
99
|
};
|
|
98
100
|
this.socket = null;
|
|
99
101
|
this.connected = false;
|
|
@@ -498,20 +500,24 @@ ${text || ""}`;
|
|
|
498
500
|
error: "Missing required email parameters (from, to, subject, and either text or html)"
|
|
499
501
|
};
|
|
500
502
|
}
|
|
501
|
-
if (!validateEmail(from)) {
|
|
502
|
-
return {
|
|
503
|
-
success: false,
|
|
504
|
-
error: "Invalid email address format"
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
503
|
const recipients = Array.isArray(options.to) ? options.to : [options.to];
|
|
508
|
-
|
|
509
|
-
if (!validateEmail(
|
|
504
|
+
if (!this.config.skipEmailValidation) {
|
|
505
|
+
if (!validateEmail(from)) {
|
|
510
506
|
return {
|
|
511
507
|
success: false,
|
|
512
|
-
error:
|
|
508
|
+
error: "Invalid email address format"
|
|
513
509
|
};
|
|
514
510
|
}
|
|
511
|
+
for (const recipient of recipients) {
|
|
512
|
+
if (!validateEmail(recipient)) {
|
|
513
|
+
return {
|
|
514
|
+
success: false,
|
|
515
|
+
error: `Invalid recipient email address format: ${recipient}`
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
} else {
|
|
520
|
+
this.log("Email validation skipped (testing mode)");
|
|
515
521
|
}
|
|
516
522
|
for (this.retryCount = 0; this.retryCount <= this.config.maxRetries; this.retryCount++) {
|
|
517
523
|
try {
|
package/lib/SMTPClient.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/utils/index.ts
|
|
2
|
-
import { promises as dnsPromises } from "
|
|
2
|
+
import { promises as dnsPromises } from "dns";
|
|
3
3
|
function encodeQuotedPrintable(text) {
|
|
4
4
|
let result = text.replace(/\r?\n/g, "\r\n");
|
|
5
5
|
result = result.replace(/=/g, "=3D");
|
|
@@ -50,7 +50,7 @@ function validateEmail(email) {
|
|
|
50
50
|
return false;
|
|
51
51
|
for (const part of domainParts) {
|
|
52
52
|
if (!part || part.length > 63) return false;
|
|
53
|
-
if (!/^[a-zA-Z0-9]([a-zA-Z0-9
|
|
53
|
+
if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(part)) return false;
|
|
54
54
|
}
|
|
55
55
|
return true;
|
|
56
56
|
} catch (_error) {
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Configuration
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ULVDQIAT.mjs";
|
|
4
4
|
import {
|
|
5
5
|
SMTPClient
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-QCFOUQRH.mjs";
|
|
7
7
|
import {
|
|
8
8
|
verifyEmailDomain
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-5BK3VY6I.mjs";
|
|
10
10
|
|
|
11
11
|
// src/MikroMail.ts
|
|
12
12
|
var MikroMail = class {
|
|
13
13
|
smtpClient;
|
|
14
|
+
config;
|
|
14
15
|
constructor(options) {
|
|
15
16
|
const config = new Configuration(options).get();
|
|
16
17
|
const smtpClient = new SMTPClient(config);
|
|
17
18
|
this.smtpClient = smtpClient;
|
|
19
|
+
this.config = config;
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
20
22
|
* Sends an email to valid domains.
|
|
@@ -22,12 +24,14 @@ var MikroMail = class {
|
|
|
22
24
|
async send(emailOptions) {
|
|
23
25
|
try {
|
|
24
26
|
const recipients = Array.isArray(emailOptions.to) ? emailOptions.to : [emailOptions.to];
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
if (!this.config.skipMXRecordCheck) {
|
|
28
|
+
for (const recipient of recipients) {
|
|
29
|
+
const hasMXRecords = await verifyEmailDomain(recipient);
|
|
30
|
+
if (!hasMXRecords)
|
|
31
|
+
console.error(
|
|
32
|
+
`Warning: No MX records found for recipient domain: ${recipient}`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
31
35
|
}
|
|
32
36
|
const result = await this.smtpClient.sendEmail(emailOptions);
|
|
33
37
|
if (result.success) console.log(`Message ID: ${result.messageId}`);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
validateEmail
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5BK3VY6I.mjs";
|
|
4
4
|
|
|
5
5
|
// src/SMTPClient.ts
|
|
6
|
-
import { Buffer } from "
|
|
7
|
-
import crypto from "
|
|
8
|
-
import net from "
|
|
9
|
-
import os from "
|
|
10
|
-
import tls from "
|
|
6
|
+
import { Buffer } from "buffer";
|
|
7
|
+
import crypto from "crypto";
|
|
8
|
+
import net from "net";
|
|
9
|
+
import os from "os";
|
|
10
|
+
import tls from "tls";
|
|
11
11
|
var SMTPClient = class {
|
|
12
12
|
config;
|
|
13
13
|
socket;
|
|
@@ -30,7 +30,9 @@ var SMTPClient = class {
|
|
|
30
30
|
clientName: config.clientName ?? os.hostname(),
|
|
31
31
|
maxRetries: config.maxRetries ?? 3,
|
|
32
32
|
retryDelay: config.retryDelay ?? 1e3,
|
|
33
|
-
skipAuthentication: config.skipAuthentication || false
|
|
33
|
+
skipAuthentication: config.skipAuthentication || false,
|
|
34
|
+
skipEmailValidation: config.skipEmailValidation || false,
|
|
35
|
+
skipMXRecordCheck: config.skipMXRecordCheck || false
|
|
34
36
|
};
|
|
35
37
|
this.socket = null;
|
|
36
38
|
this.connected = false;
|
|
@@ -435,20 +437,24 @@ ${text || ""}`;
|
|
|
435
437
|
error: "Missing required email parameters (from, to, subject, and either text or html)"
|
|
436
438
|
};
|
|
437
439
|
}
|
|
438
|
-
if (!validateEmail(from)) {
|
|
439
|
-
return {
|
|
440
|
-
success: false,
|
|
441
|
-
error: "Invalid email address format"
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
440
|
const recipients = Array.isArray(options.to) ? options.to : [options.to];
|
|
445
|
-
|
|
446
|
-
if (!validateEmail(
|
|
441
|
+
if (!this.config.skipEmailValidation) {
|
|
442
|
+
if (!validateEmail(from)) {
|
|
447
443
|
return {
|
|
448
444
|
success: false,
|
|
449
|
-
error:
|
|
445
|
+
error: "Invalid email address format"
|
|
450
446
|
};
|
|
451
447
|
}
|
|
448
|
+
for (const recipient of recipients) {
|
|
449
|
+
if (!validateEmail(recipient)) {
|
|
450
|
+
return {
|
|
451
|
+
success: false,
|
|
452
|
+
error: `Invalid recipient email address format: ${recipient}`
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
this.log("Email validation skipped (testing mode)");
|
|
452
458
|
}
|
|
453
459
|
for (this.retryCount = 0; this.retryCount <= this.config.maxRetries; this.retryCount++) {
|
|
454
460
|
try {
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-47VXJTWV.mjs";
|
|
4
4
|
|
|
5
5
|
// src/Configuration.ts
|
|
6
|
-
import { existsSync, readFileSync } from "
|
|
6
|
+
import { existsSync, readFileSync } from "fs";
|
|
7
7
|
var Configuration = class {
|
|
8
8
|
config;
|
|
9
9
|
defaults = {
|
|
@@ -35,7 +35,9 @@ var Configuration = class {
|
|
|
35
35
|
port: 465,
|
|
36
36
|
secure: true,
|
|
37
37
|
debug: false,
|
|
38
|
-
maxRetries: 2
|
|
38
|
+
maxRetries: 2,
|
|
39
|
+
skipEmailValidation: false,
|
|
40
|
+
skipMXRecordCheck: false
|
|
39
41
|
};
|
|
40
42
|
let fileConfig = {};
|
|
41
43
|
if (existsSync(configFilePath)) {
|
package/lib/index.js
CHANGED
|
@@ -79,7 +79,9 @@ var Configuration = class {
|
|
|
79
79
|
port: 465,
|
|
80
80
|
secure: true,
|
|
81
81
|
debug: false,
|
|
82
|
-
maxRetries: 2
|
|
82
|
+
maxRetries: 2,
|
|
83
|
+
skipEmailValidation: false,
|
|
84
|
+
skipMXRecordCheck: false
|
|
83
85
|
};
|
|
84
86
|
let fileConfig = {};
|
|
85
87
|
if ((0, import_node_fs.existsSync)(configFilePath)) {
|
|
@@ -189,7 +191,7 @@ function validateEmail(email) {
|
|
|
189
191
|
return false;
|
|
190
192
|
for (const part of domainParts) {
|
|
191
193
|
if (!part || part.length > 63) return false;
|
|
192
|
-
if (!/^[a-zA-Z0-9]([a-zA-Z0-9
|
|
194
|
+
if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(part)) return false;
|
|
193
195
|
}
|
|
194
196
|
return true;
|
|
195
197
|
} catch (_error) {
|
|
@@ -237,7 +239,9 @@ var SMTPClient = class {
|
|
|
237
239
|
clientName: config.clientName ?? import_node_os.default.hostname(),
|
|
238
240
|
maxRetries: config.maxRetries ?? 3,
|
|
239
241
|
retryDelay: config.retryDelay ?? 1e3,
|
|
240
|
-
skipAuthentication: config.skipAuthentication || false
|
|
242
|
+
skipAuthentication: config.skipAuthentication || false,
|
|
243
|
+
skipEmailValidation: config.skipEmailValidation || false,
|
|
244
|
+
skipMXRecordCheck: config.skipMXRecordCheck || false
|
|
241
245
|
};
|
|
242
246
|
this.socket = null;
|
|
243
247
|
this.connected = false;
|
|
@@ -642,20 +646,24 @@ ${text || ""}`;
|
|
|
642
646
|
error: "Missing required email parameters (from, to, subject, and either text or html)"
|
|
643
647
|
};
|
|
644
648
|
}
|
|
645
|
-
if (!validateEmail(from)) {
|
|
646
|
-
return {
|
|
647
|
-
success: false,
|
|
648
|
-
error: "Invalid email address format"
|
|
649
|
-
};
|
|
650
|
-
}
|
|
651
649
|
const recipients = Array.isArray(options.to) ? options.to : [options.to];
|
|
652
|
-
|
|
653
|
-
if (!validateEmail(
|
|
650
|
+
if (!this.config.skipEmailValidation) {
|
|
651
|
+
if (!validateEmail(from)) {
|
|
654
652
|
return {
|
|
655
653
|
success: false,
|
|
656
|
-
error:
|
|
654
|
+
error: "Invalid email address format"
|
|
657
655
|
};
|
|
658
656
|
}
|
|
657
|
+
for (const recipient of recipients) {
|
|
658
|
+
if (!validateEmail(recipient)) {
|
|
659
|
+
return {
|
|
660
|
+
success: false,
|
|
661
|
+
error: `Invalid recipient email address format: ${recipient}`
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
} else {
|
|
666
|
+
this.log("Email validation skipped (testing mode)");
|
|
659
667
|
}
|
|
660
668
|
for (this.retryCount = 0; this.retryCount <= this.config.maxRetries; this.retryCount++) {
|
|
661
669
|
try {
|
|
@@ -754,10 +762,12 @@ ${text || ""}`;
|
|
|
754
762
|
// src/MikroMail.ts
|
|
755
763
|
var MikroMail = class {
|
|
756
764
|
smtpClient;
|
|
765
|
+
config;
|
|
757
766
|
constructor(options) {
|
|
758
767
|
const config = new Configuration(options).get();
|
|
759
768
|
const smtpClient = new SMTPClient(config);
|
|
760
769
|
this.smtpClient = smtpClient;
|
|
770
|
+
this.config = config;
|
|
761
771
|
}
|
|
762
772
|
/**
|
|
763
773
|
* Sends an email to valid domains.
|
|
@@ -765,12 +775,14 @@ var MikroMail = class {
|
|
|
765
775
|
async send(emailOptions) {
|
|
766
776
|
try {
|
|
767
777
|
const recipients = Array.isArray(emailOptions.to) ? emailOptions.to : [emailOptions.to];
|
|
768
|
-
|
|
769
|
-
const
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
778
|
+
if (!this.config.skipMXRecordCheck) {
|
|
779
|
+
for (const recipient of recipients) {
|
|
780
|
+
const hasMXRecords = await verifyEmailDomain(recipient);
|
|
781
|
+
if (!hasMXRecords)
|
|
782
|
+
console.error(
|
|
783
|
+
`Warning: No MX records found for recipient domain: ${recipient}`
|
|
784
|
+
);
|
|
785
|
+
}
|
|
774
786
|
}
|
|
775
787
|
const result = await this.smtpClient.sendEmail(emailOptions);
|
|
776
788
|
if (result.success) console.log(`Message ID: ${result.messageId}`);
|
package/lib/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MikroMail
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-IYCQK5YZ.mjs";
|
|
4
|
+
import "./chunk-ULVDQIAT.mjs";
|
|
5
|
+
import "./chunk-QCFOUQRH.mjs";
|
|
6
6
|
import "./chunk-47VXJTWV.mjs";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-5BK3VY6I.mjs";
|
|
8
8
|
export {
|
|
9
9
|
MikroMail
|
|
10
10
|
};
|
package/lib/utils/index.js
CHANGED
|
@@ -77,7 +77,7 @@ function validateEmail(email) {
|
|
|
77
77
|
return false;
|
|
78
78
|
for (const part of domainParts) {
|
|
79
79
|
if (!part || part.length > 63) return false;
|
|
80
|
-
if (!/^[a-zA-Z0-9]([a-zA-Z0-9
|
|
80
|
+
if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(part)) return false;
|
|
81
81
|
}
|
|
82
82
|
return true;
|
|
83
83
|
} catch (_error) {
|
package/lib/utils/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mikromail",
|
|
3
3
|
"description": "Lightweight replacement for Nodemailer, supporting HTML, international symbols, and more.",
|
|
4
|
-
"version": "0.0
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"author": "Mikael Vesavuori",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
],
|
|
32
32
|
"scripts": {
|
|
33
33
|
"test": "npm run test:licenses && npm run test:types && npm run lint && npm run test:unit",
|
|
34
|
-
"test:data": "rm -rf test-db && npx tsx random-data.ts",
|
|
35
34
|
"test:types": "npx type-coverage --at-least 95 --strict --ignore-files \"tests/**/*.ts\" --ignore-files \"*.ts\" --ignore-files \"src/errors/*.ts\" --ignore-files \"testdata/*.ts\"",
|
|
36
35
|
"test:licenses": "npx license-compliance --direct --allow 'MIT;ISC;0BSD;BSD-2-Clause;BSD-3-Clause;Apache-2.0;Unlicense;CC0-1.0'",
|
|
37
36
|
"test:unit": "npx vitest run --coverage",
|
|
@@ -45,9 +44,9 @@
|
|
|
45
44
|
"prepare": "husky"
|
|
46
45
|
},
|
|
47
46
|
"devDependencies": {
|
|
48
|
-
"@biomejs/biome": "
|
|
47
|
+
"@biomejs/biome": "2",
|
|
49
48
|
"@types/node": "latest",
|
|
50
|
-
"@vitest/coverage-v8": "
|
|
49
|
+
"@vitest/coverage-v8": "4",
|
|
51
50
|
"husky": "9",
|
|
52
51
|
"license-compliance": "latest",
|
|
53
52
|
"tslib": "latest",
|
|
@@ -55,6 +54,6 @@
|
|
|
55
54
|
"tsx": "latest",
|
|
56
55
|
"type-coverage": "2",
|
|
57
56
|
"typescript": "5",
|
|
58
|
-
"vitest": "
|
|
57
|
+
"vitest": "4"
|
|
59
58
|
}
|
|
60
59
|
}
|