@visulima/email 1.0.0-alpha.1 → 1.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/README.md +408 -72
- package/dist/crypto/index.js +1 -1
- package/dist/draft-mail-message.d.ts +13 -0
- package/dist/index.d.ts +3 -16
- package/dist/index.js +1 -1
- package/dist/mail-message.d.ts +425 -0
- package/dist/mail.d.ts +124 -275
- package/dist/packem_shared/{DkimSigner-Z8D4Il10.js → DkimSigner-BrJfmMey.js} +1 -1
- package/dist/packem_shared/Mail-CC7Oh--8.js +1 -0
- package/dist/packem_shared/MailMessage-n7kyh9vB.js +1 -0
- package/dist/packem_shared/{SmimeEncrypter-CBevU534.js → SmimeEncrypter-CtvWNS8B.js} +1 -1
- package/dist/packem_shared/{SmimeSigner-02aXVi90.js → SmimeSigner-DZ1hSOM3.js} +1 -1
- package/dist/packem_shared/ahaSendProvider-DlFKEQ6D.js +1 -0
- package/dist/packem_shared/{awsSesProvider-CkuFOzb0.js → awsSesProvider-Ba-eVJxZ.js} +6 -6
- package/dist/packem_shared/azureProvider-CQYAkgVF.js +1 -0
- package/dist/packem_shared/brevoProvider-5p6jjiK9.js +1 -0
- package/dist/packem_shared/build-mime-message-IYaUqqPJ.js +2 -0
- package/dist/packem_shared/create-logger-BiWdqFNg.js +1 -0
- package/dist/packem_shared/{detectMimeType-S8WRsNtY.js → detectMimeType-CrDG3LaZ.js} +1 -1
- package/dist/packem_shared/failoverProvider-CAHQQueo.js +1 -0
- package/dist/packem_shared/{generateBoundary-CZ8kJuY6.js → generate-boundary-Cx8nXYS0.js} +1 -1
- package/dist/packem_shared/{generateMessageId-11Ls5JsR.js → generate-message-id-D4uA8gkj.js} +1 -1
- package/dist/packem_shared/headers-to-record-Qo124ImV.js +1 -0
- package/dist/packem_shared/httpProvider-CZD6LZX3.js +1 -0
- package/dist/packem_shared/infobipProvider-CtLwrUaP.js +1 -0
- package/dist/packem_shared/{mailCrabProvider-BEwRjB3F.js → mailCrabProvider-CM_CFDca.js} +1 -1
- package/dist/packem_shared/mailPaceProvider-B6yKvh6z.js +1 -0
- package/dist/packem_shared/mailerSendProvider-CeeIXFnW.js +1 -0
- package/dist/packem_shared/mailgunProvider-mmjKzouh.js +1 -0
- package/dist/packem_shared/mailjetProvider-DwN6i0VA.js +1 -0
- package/dist/packem_shared/mailomatProvider-DMQmjKHT.js +1 -0
- package/dist/packem_shared/mailtrapProvider-BN3UBEQw.js +1 -0
- package/dist/packem_shared/{makeRequest-DwxHX0xo.js → make-request-BDzF9W9D.js} +1 -1
- package/dist/packem_shared/mandrillProvider-370y7CLu.js +1 -0
- package/dist/packem_shared/mockProvider-DN5ZwutD.js +1 -0
- package/dist/packem_shared/nodemailerProvider-_w8QXMU-.js +1 -0
- package/dist/packem_shared/opentelemetryProvider-C_ZXOLSd.js +1 -0
- package/dist/packem_shared/plunkProvider-DfJumQ4U.js +1 -0
- package/dist/packem_shared/postalProvider-Bavx2FcH.js +1 -0
- package/dist/packem_shared/postmarkProvider-DFC0uvjO.js +1 -0
- package/dist/packem_shared/provider-base-Cmzx6BTO.js +1 -0
- package/dist/packem_shared/readFile-BlZxbhCU-C8Z4K-ib.js +1 -0
- package/dist/packem_shared/resendProvider-CfqU7UdE.js +1 -0
- package/dist/packem_shared/roundRobinProvider-1WQnuKR8.js +1 -0
- package/dist/packem_shared/scalewayProvider-be1HPimL.js +1 -0
- package/dist/packem_shared/sendGridProvider-BVI1sq3n.js +1 -0
- package/dist/packem_shared/smtpProvider-BV-ufR53.js +23 -0
- package/dist/packem_shared/sweegoProvider-7419CSAq.js +1 -0
- package/dist/packem_shared/validate-email-options-DfJ7llf8.js +1 -0
- package/dist/packem_shared/zeptomailProvider-C2lh0Xmo.js +1 -0
- package/dist/providers/ahasend/index.js +1 -1
- package/dist/providers/aws-ses/index.js +1 -1
- package/dist/providers/azure/index.js +1 -1
- package/dist/providers/brevo/index.js +1 -1
- package/dist/providers/brevo/types.d.ts +10 -3
- package/dist/providers/failover/index.js +1 -1
- package/dist/providers/http/index.js +1 -1
- package/dist/providers/infobip/index.js +1 -1
- package/dist/providers/mailcrab/index.js +1 -1
- package/dist/providers/mailersend/index.js +1 -1
- package/dist/providers/mailgun/index.js +1 -1
- package/dist/providers/mailjet/index.js +1 -1
- package/dist/providers/mailomat/index.js +1 -1
- package/dist/providers/mailpace/index.js +1 -1
- package/dist/providers/mailtrap/index.js +1 -1
- package/dist/providers/mandrill/index.js +1 -1
- package/dist/providers/mock/index.js +1 -1
- package/dist/providers/nodemailer/index.js +1 -1
- package/dist/providers/opentelemetry/index.js +1 -1
- package/dist/providers/plunk/index.js +1 -1
- package/dist/providers/postal/index.js +1 -1
- package/dist/providers/postmark/index.js +1 -1
- package/dist/providers/resend/index.js +1 -1
- package/dist/providers/roundrobin/index.js +1 -1
- package/dist/providers/scaleway/index.js +1 -1
- package/dist/providers/sendgrid/index.js +1 -1
- package/dist/providers/smtp/index.js +1 -1
- package/dist/providers/sweego/index.js +1 -1
- package/dist/providers/zeptomail/index.js +1 -1
- package/dist/types.d.ts +18 -0
- package/dist/utils/cache.d.ts +54 -0
- package/dist/utils/cache.js +1 -0
- package/dist/utils/create-logger.d.ts +2 -4
- package/dist/utils/format-email-address.js +1 -0
- package/dist/utils/normalize-email-aliases.d.ts +22 -0
- package/dist/utils/normalize-email-aliases.js +1 -0
- package/dist/utils/parse-address.js +1 -0
- package/dist/utils/validation/check-mx-records.d.ts +42 -0
- package/dist/utils/validation/check-mx-records.js +1 -0
- package/dist/utils/validation/disposable-email-domains.d.ts +13 -0
- package/dist/utils/validation/disposable-email-domains.js +1 -0
- package/dist/utils/validation/role-accounts.d.ts +21 -0
- package/dist/utils/validation/role-accounts.js +1 -0
- package/dist/utils/{validate-email-options.d.ts → validation/validate-email-options.d.ts} +1 -1
- package/dist/utils/validation/verify-email.d.ts +47 -0
- package/dist/utils/validation/verify-email.js +1 -0
- package/dist/utils/validation/verify-smtp.d.ts +39 -0
- package/dist/utils/validation/verify-smtp.js +4 -0
- package/package.json +49 -3
- package/dist/packem_shared/MailMessage-Hdgowmvi.js +0 -1
- package/dist/packem_shared/ahaSendProvider-NUD_kwyT.js +0 -1
- package/dist/packem_shared/azureProvider-Ckdrpmw9.js +0 -1
- package/dist/packem_shared/brevoProvider-CB3IYW4n.js +0 -1
- package/dist/packem_shared/buildMimeMessage-BPtd0pno.js +0 -2
- package/dist/packem_shared/comparePriority-BfiwjVsV.js +0 -1
- package/dist/packem_shared/createLogger-DlElSVQP.js +0 -1
- package/dist/packem_shared/failoverProvider-sam9n1AG.js +0 -1
- package/dist/packem_shared/formatEmailAddress-CHeME3Vk.js +0 -1
- package/dist/packem_shared/formatEmailAddresses-UegVOe5A.js +0 -1
- package/dist/packem_shared/headersToRecord-BKUTr40L.js +0 -1
- package/dist/packem_shared/httpProvider-BhN0RrK-.js +0 -1
- package/dist/packem_shared/infobipProvider-D8vYTHV4.js +0 -1
- package/dist/packem_shared/isPortAvailable-5kfsfo8u.js +0 -1
- package/dist/packem_shared/mailPaceProvider-C47Izgaj.js +0 -1
- package/dist/packem_shared/mailerSendProvider-C4uAo-fc.js +0 -1
- package/dist/packem_shared/mailgunProvider-B7upu_OV.js +0 -1
- package/dist/packem_shared/mailjetProvider-ReErm08u.js +0 -1
- package/dist/packem_shared/mailomatProvider-OlCT_O2i.js +0 -1
- package/dist/packem_shared/mailtrapProvider-hVMV3h6r.js +0 -1
- package/dist/packem_shared/mandrillProvider-DdnbkHZI.js +0 -1
- package/dist/packem_shared/mockProvider-BDWZJpea.js +0 -1
- package/dist/packem_shared/nodemailerProvider-BV21eRGX.js +0 -1
- package/dist/packem_shared/opentelemetryProvider-kAz62mKm.js +0 -1
- package/dist/packem_shared/parseAddress-CATTKGe_.js +0 -1
- package/dist/packem_shared/plunkProvider-Bs6K51lT.js +0 -1
- package/dist/packem_shared/postalProvider-Bcsxp-z6.js +0 -1
- package/dist/packem_shared/postmarkProvider-BUq3wuYD.js +0 -1
- package/dist/packem_shared/provider-base-_hbWXBdK.js +0 -1
- package/dist/packem_shared/readFile-BlZxbhCU-C8bCdiA2.js +0 -1
- package/dist/packem_shared/resendProvider-D-_HQpN_.js +0 -1
- package/dist/packem_shared/retry-D1MBqS49.js +0 -1
- package/dist/packem_shared/roundRobinProvider-CejLM1rZ.js +0 -1
- package/dist/packem_shared/scalewayProvider-1n6ePiGl.js +0 -1
- package/dist/packem_shared/sendGridProvider-B1T62dyX.js +0 -1
- package/dist/packem_shared/smtpProvider-CcAoRrkt.js +0 -23
- package/dist/packem_shared/sweegoProvider-CxFmEUh6.js +0 -1
- package/dist/packem_shared/validateEmailOptions-BzlJECG5.js +0 -1
- package/dist/packem_shared/zeptomailProvider-CWYQPAJk.js +0 -1
- package/dist/utils/compare-priority.d.ts +0 -16
- /package/dist/utils/{validate-email.d.ts → validation/validate-email.d.ts} +0 -0
- /package/dist/{packem_shared/validateEmail-BkVdVioP.js → utils/validation/validate-email.js} +0 -0
package/README.md
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
<!-- START_PACKAGE_OG_IMAGE_PLACEHOLDER -->
|
|
2
|
+
|
|
3
|
+
<a href="https://www.anolilab.com/open-source" align="center">
|
|
4
|
+
|
|
5
|
+
<img src="__assets__/package-og.svg" alt="email" />
|
|
6
|
+
|
|
7
|
+
</a>
|
|
8
|
+
|
|
9
|
+
<h3 align="center">A comprehensive email library with multi-provider support, crypto utilities, and template engines</h3>
|
|
10
|
+
|
|
11
|
+
<!-- END_PACKAGE_OG_IMAGE_PLACEHOLDER -->
|
|
7
12
|
|
|
8
13
|
<br />
|
|
9
14
|
|
|
10
15
|
<div align="center">
|
|
11
16
|
|
|
12
|
-
[![typescript-image]
|
|
17
|
+
[![typescript-image][typescript-badge]][typescript-url]
|
|
18
|
+
[![mit licence][license-badge]][license]
|
|
19
|
+
[![npm downloads][npm-downloads-badge]][npm-downloads]
|
|
20
|
+
[![Chat][chat-badge]][chat]
|
|
21
|
+
[![PRs Welcome][prs-welcome-badge]][prs-welcome]
|
|
13
22
|
|
|
14
23
|
</div>
|
|
15
24
|
|
|
@@ -44,7 +53,7 @@ pnpm add @visulima/email
|
|
|
44
53
|
### Basic Usage
|
|
45
54
|
|
|
46
55
|
```typescript
|
|
47
|
-
import { createMail, resendProvider } from "@visulima/email";
|
|
56
|
+
import { createMail, MailMessage, resendProvider } from "@visulima/email";
|
|
48
57
|
|
|
49
58
|
// Create a provider
|
|
50
59
|
const resend = resendProvider({
|
|
@@ -54,18 +63,82 @@ const resend = resendProvider({
|
|
|
54
63
|
// Create a Mail instance
|
|
55
64
|
const mail = createMail(resend);
|
|
56
65
|
|
|
66
|
+
// Optional: Set default configuration for all emails
|
|
67
|
+
mail.setFrom({ email: "noreply@example.com", name: "My App" });
|
|
68
|
+
|
|
57
69
|
// Send an email using the message builder
|
|
58
|
-
const
|
|
70
|
+
const message = new MailMessage()
|
|
71
|
+
.to("user@example.com")
|
|
72
|
+
// .from() is optional if set via mail.setFrom()
|
|
73
|
+
.subject("Hello")
|
|
74
|
+
.html("<h1>Hello World</h1>");
|
|
75
|
+
|
|
76
|
+
const result = await mail.send(message);
|
|
59
77
|
|
|
60
78
|
if (result.success) {
|
|
61
79
|
console.log("Email sent:", result.data?.messageId);
|
|
62
80
|
}
|
|
63
81
|
```
|
|
64
82
|
|
|
83
|
+
### Default Configuration
|
|
84
|
+
|
|
85
|
+
You can configure default values for all emails sent through a Mail instance:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { createMail, MailMessage, resendProvider } from "@visulima/email";
|
|
89
|
+
|
|
90
|
+
const mail = createMail(resendProvider({ apiKey: "re_xxx" }));
|
|
91
|
+
|
|
92
|
+
// Set default from address
|
|
93
|
+
mail.setFrom({ email: "noreply@example.com", name: "My App" });
|
|
94
|
+
|
|
95
|
+
// Set default reply-to address
|
|
96
|
+
mail.setReplyTo({ email: "support@example.com" });
|
|
97
|
+
|
|
98
|
+
// Set default headers
|
|
99
|
+
mail.setHeaders({
|
|
100
|
+
"X-App-Name": "MyApp",
|
|
101
|
+
"X-Version": "1.0.0",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Or chain them together
|
|
105
|
+
const mail2 = createMail(resendProvider({ apiKey: "re_xxx" }))
|
|
106
|
+
.setFrom({ email: "noreply@example.com" })
|
|
107
|
+
.setReplyTo({ email: "support@example.com" })
|
|
108
|
+
.setHeaders({ "X-App-Name": "MyApp" });
|
|
109
|
+
|
|
110
|
+
// Now all emails will use these defaults if not specified in the message
|
|
111
|
+
const message = new MailMessage().to("user@example.com").subject("Hello").html("<h1>Hello World</h1>");
|
|
112
|
+
// No need to set .from() - it will use the default
|
|
113
|
+
|
|
114
|
+
await mail.send(message);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Creating Draft Emails
|
|
118
|
+
|
|
119
|
+
You can create draft emails in EML (RFC 822) format without sending them. The `draft()` method returns the email as an EML string with an `X-Unsent: 1` header automatically added:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { createMail, MailMessage, resendProvider } from "@visulima/email";
|
|
123
|
+
import { writeFile } from "fs/promises";
|
|
124
|
+
|
|
125
|
+
const mail = createMail(resendProvider({ apiKey: "re_xxx" }));
|
|
126
|
+
|
|
127
|
+
const message = new MailMessage().to("user@example.com").from("sender@example.com").subject("Hello").html("<h1>Hello World</h1>");
|
|
128
|
+
|
|
129
|
+
// Create a draft in EML format
|
|
130
|
+
const eml = await mail.draft(message);
|
|
131
|
+
|
|
132
|
+
// Save to file
|
|
133
|
+
await writeFile("draft.eml", eml);
|
|
134
|
+
|
|
135
|
+
// EML files can be opened by email clients
|
|
136
|
+
```
|
|
137
|
+
|
|
65
138
|
### Using Different Providers
|
|
66
139
|
|
|
67
140
|
```typescript
|
|
68
|
-
import { createMail, smtpProvider, resendProvider } from "@visulima/email";
|
|
141
|
+
import { createMail, MailMessage, smtpProvider, resendProvider } from "@visulima/email";
|
|
69
142
|
|
|
70
143
|
// SMTP provider
|
|
71
144
|
const smtp = smtpProvider({
|
|
@@ -86,30 +159,13 @@ const smtpMail = createMail(smtp);
|
|
|
86
159
|
const resendMail = createMail(resend);
|
|
87
160
|
|
|
88
161
|
// Send via specific provider
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
162
|
+
const message = new MailMessage()
|
|
163
|
+
.to("user@example.com")
|
|
164
|
+
.from("sender@example.com")
|
|
165
|
+
.subject("Hello")
|
|
166
|
+
.html("<h1>Hello World</h1>");
|
|
93
167
|
|
|
94
|
-
|
|
95
|
-
import { createMail, type Mailable, type EmailOptions } from "@visulima/email";
|
|
96
|
-
import { resendProvider } from "@visulima/email";
|
|
97
|
-
|
|
98
|
-
class WelcomeEmail implements Mailable {
|
|
99
|
-
constructor(private user: { name: string; email: string }) {}
|
|
100
|
-
|
|
101
|
-
build(): EmailOptions {
|
|
102
|
-
return {
|
|
103
|
-
from: { email: "noreply@example.com" },
|
|
104
|
-
to: { email: this.user.email, name: this.user.name },
|
|
105
|
-
subject: "Welcome!",
|
|
106
|
-
html: `<h1>Welcome ${this.user.name}!</h1>`,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const mail = createMail(resendProvider({ apiKey: "re_xxx" }));
|
|
112
|
-
const result = await mail.send(new WelcomeEmail({ name: "John", email: "john@example.com" }));
|
|
168
|
+
await resendMail.send(message);
|
|
113
169
|
```
|
|
114
170
|
|
|
115
171
|
### Direct Email Sending
|
|
@@ -126,7 +182,7 @@ const emailOptions: EmailOptions = {
|
|
|
126
182
|
html: "<h1>Hello World</h1>",
|
|
127
183
|
};
|
|
128
184
|
|
|
129
|
-
const result = await mail.
|
|
185
|
+
const result = await mail.send(emailOptions);
|
|
130
186
|
```
|
|
131
187
|
|
|
132
188
|
### Failover Provider
|
|
@@ -134,7 +190,7 @@ const result = await mail.sendEmail(emailOptions);
|
|
|
134
190
|
The failover provider allows you to configure multiple email providers as backups. If the primary provider fails, it will automatically try the next provider in the list.
|
|
135
191
|
|
|
136
192
|
```typescript
|
|
137
|
-
import { createMail, failoverProvider, resendProvider, smtpProvider } from "@visulima/email";
|
|
193
|
+
import { createMail, MailMessage, failoverProvider, resendProvider, smtpProvider } from "@visulima/email";
|
|
138
194
|
|
|
139
195
|
// Create individual providers
|
|
140
196
|
const resend = resendProvider({ apiKey: "re_xxx" });
|
|
@@ -157,7 +213,9 @@ const failover = failoverProvider({
|
|
|
157
213
|
// Use failover provider
|
|
158
214
|
const mail = createMail(failover);
|
|
159
215
|
|
|
160
|
-
const
|
|
216
|
+
const message = new MailMessage().to("user@example.com").from("sender@example.com").subject("Hello").html("<h1>Hello World</h1>");
|
|
217
|
+
|
|
218
|
+
const result = await mail.send(message);
|
|
161
219
|
|
|
162
220
|
// The failover provider will try Resend first, and if it fails,
|
|
163
221
|
// automatically try SMTP
|
|
@@ -188,7 +246,7 @@ const failover = failoverProvider({
|
|
|
188
246
|
The round-robin provider distributes your email sending workload across multiple providers. Each email is sent using the next available provider in rotation, providing load balancing across your mailers.
|
|
189
247
|
|
|
190
248
|
```typescript
|
|
191
|
-
import { createMail, roundRobinProvider, resendProvider, smtpProvider } from "@visulima/email";
|
|
249
|
+
import { createMail, MailMessage, roundRobinProvider, resendProvider, smtpProvider } from "@visulima/email";
|
|
192
250
|
|
|
193
251
|
// Create individual providers
|
|
194
252
|
const resend = resendProvider({ apiKey: "re_xxx" });
|
|
@@ -212,13 +270,16 @@ const roundRobin = roundRobinProvider({
|
|
|
212
270
|
const mail = createMail(roundRobin);
|
|
213
271
|
|
|
214
272
|
// Each email will be distributed across providers in rotation
|
|
215
|
-
|
|
273
|
+
const message1 = new MailMessage().to("user1@example.com").from("sender@example.com").subject("Email 1").html("<h1>Email 1</h1>");
|
|
274
|
+
await mail.send(message1);
|
|
216
275
|
// Uses resend (or random start)
|
|
217
276
|
|
|
218
|
-
|
|
277
|
+
const message2 = new MailMessage().to("user2@example.com").from("sender@example.com").subject("Email 2").html("<h1>Email 2</h1>");
|
|
278
|
+
await mail.send(message2);
|
|
219
279
|
// Uses smtp (next in rotation)
|
|
220
280
|
|
|
221
|
-
|
|
281
|
+
const message3 = new MailMessage().to("user3@example.com").from("sender@example.com").subject("Email 3").html("<h1>Email 3</h1>");
|
|
282
|
+
await mail.send(message3);
|
|
222
283
|
// Uses resend (back to first)
|
|
223
284
|
```
|
|
224
285
|
|
|
@@ -262,7 +323,8 @@ const mailgun = mailgunProvider({
|
|
|
262
323
|
const mail = createMail(mailgun);
|
|
263
324
|
|
|
264
325
|
// Send email with Mailgun-specific options
|
|
265
|
-
const
|
|
326
|
+
const message = new MailMessage().to("user@example.com").from("sender@example.com").subject("Welcome").html("<h1>Welcome!</h1>");
|
|
327
|
+
const result = await mail.send(message);
|
|
266
328
|
|
|
267
329
|
// Or use Mailgun-specific features
|
|
268
330
|
import type { MailgunEmailOptions } from "@visulima/email/providers/mailgun";
|
|
@@ -280,7 +342,7 @@ const mailgunOptions: MailgunEmailOptions = {
|
|
|
280
342
|
deliveryTime: Math.floor(Date.now() / 1000) + 3600, // Schedule for 1 hour from now
|
|
281
343
|
};
|
|
282
344
|
|
|
283
|
-
await mail.
|
|
345
|
+
await mail.send(mailgunOptions);
|
|
284
346
|
```
|
|
285
347
|
|
|
286
348
|
### Mailjet Provider
|
|
@@ -301,7 +363,8 @@ const mailjet = mailjetProvider({
|
|
|
301
363
|
const mail = createMail(mailjet);
|
|
302
364
|
|
|
303
365
|
// Send email with Mailjet-specific options
|
|
304
|
-
const
|
|
366
|
+
const message = new MailMessage().to("user@example.com").from("sender@example.com").subject("Welcome").html("<h1>Welcome!</h1>");
|
|
367
|
+
const result = await mail.send(message);
|
|
305
368
|
|
|
306
369
|
// Or use Mailjet-specific features
|
|
307
370
|
import type { MailjetEmailOptions } from "@visulima/email/providers/mailjet";
|
|
@@ -320,7 +383,7 @@ const mailjetOptions: MailjetEmailOptions = {
|
|
|
320
383
|
deliveryTime: Math.floor(Date.now() / 1000) + 3600, // Schedule for 1 hour from now
|
|
321
384
|
};
|
|
322
385
|
|
|
323
|
-
await mail.
|
|
386
|
+
await mail.send(mailjetOptions);
|
|
324
387
|
```
|
|
325
388
|
|
|
326
389
|
### MailerSend Provider
|
|
@@ -359,7 +422,7 @@ const mailerSendOptions: MailerSendEmailOptions = {
|
|
|
359
422
|
scheduledAt: Math.floor(Date.now() / 1000) + 3600, // Schedule for 1 hour from now
|
|
360
423
|
};
|
|
361
424
|
|
|
362
|
-
await mail.
|
|
425
|
+
await mail.send(mailerSendOptions);
|
|
363
426
|
```
|
|
364
427
|
|
|
365
428
|
### Mandrill Provider (Mailchimp Transactional)
|
|
@@ -393,7 +456,7 @@ const mandrillOptions: MandrillEmailOptions = {
|
|
|
393
456
|
metadata: { orderId: "123" },
|
|
394
457
|
};
|
|
395
458
|
|
|
396
|
-
await mail.
|
|
459
|
+
await mail.send(mandrillOptions);
|
|
397
460
|
```
|
|
398
461
|
|
|
399
462
|
### Postal Provider
|
|
@@ -426,7 +489,7 @@ const postalOptions: PostalEmailOptions = {
|
|
|
426
489
|
tags: ["welcome"],
|
|
427
490
|
};
|
|
428
491
|
|
|
429
|
-
await mail.
|
|
492
|
+
await mail.send(postalOptions);
|
|
430
493
|
```
|
|
431
494
|
|
|
432
495
|
### Mailtrap Provider
|
|
@@ -460,7 +523,7 @@ const mailtrapOptions: MailtrapEmailOptions = {
|
|
|
460
523
|
tags: ["welcome"],
|
|
461
524
|
};
|
|
462
525
|
|
|
463
|
-
await mail.
|
|
526
|
+
await mail.send(mailtrapOptions);
|
|
464
527
|
```
|
|
465
528
|
|
|
466
529
|
### MailPace Provider
|
|
@@ -493,7 +556,7 @@ const mailPaceOptions: MailPaceEmailOptions = {
|
|
|
493
556
|
listUnsubscribe: "<mailto:unsubscribe@example.com>",
|
|
494
557
|
};
|
|
495
558
|
|
|
496
|
-
await mail.
|
|
559
|
+
await mail.send(mailPaceOptions);
|
|
497
560
|
```
|
|
498
561
|
|
|
499
562
|
### Azure Communication Services Provider
|
|
@@ -530,7 +593,7 @@ const azureOptions: AzureEmailOptions = {
|
|
|
530
593
|
importance: "high", // "normal" or "high"
|
|
531
594
|
};
|
|
532
595
|
|
|
533
|
-
await mail.
|
|
596
|
+
await mail.send(azureOptions);
|
|
534
597
|
```
|
|
535
598
|
|
|
536
599
|
### Infobip Provider
|
|
@@ -564,7 +627,7 @@ const infobipOptions: InfobipEmailOptions = {
|
|
|
564
627
|
sendAt: Date.now() + 3600000, // Schedule for 1 hour from now (milliseconds)
|
|
565
628
|
};
|
|
566
629
|
|
|
567
|
-
await mail.
|
|
630
|
+
await mail.send(infobipOptions);
|
|
568
631
|
```
|
|
569
632
|
|
|
570
633
|
### Scaleway Provider
|
|
@@ -596,7 +659,7 @@ const scalewayOptions: ScalewayEmailOptions = {
|
|
|
596
659
|
projectId: "project-uuid", // Optional
|
|
597
660
|
};
|
|
598
661
|
|
|
599
|
-
await mail.
|
|
662
|
+
await mail.send(scalewayOptions);
|
|
600
663
|
```
|
|
601
664
|
|
|
602
665
|
### AhaSend Provider
|
|
@@ -628,7 +691,7 @@ const ahaSendOptions: AhaSendEmailOptions = {
|
|
|
628
691
|
tags: ["welcome"],
|
|
629
692
|
};
|
|
630
693
|
|
|
631
|
-
await mail.
|
|
694
|
+
await mail.send(ahaSendOptions);
|
|
632
695
|
```
|
|
633
696
|
|
|
634
697
|
### Mailomat Provider
|
|
@@ -660,7 +723,7 @@ const mailomatOptions: MailomatEmailOptions = {
|
|
|
660
723
|
tags: ["welcome"],
|
|
661
724
|
};
|
|
662
725
|
|
|
663
|
-
await mail.
|
|
726
|
+
await mail.send(mailomatOptions);
|
|
664
727
|
```
|
|
665
728
|
|
|
666
729
|
### Sweego Provider
|
|
@@ -692,7 +755,7 @@ const sweegoOptions: SweegoEmailOptions = {
|
|
|
692
755
|
tags: ["welcome"],
|
|
693
756
|
};
|
|
694
757
|
|
|
695
|
-
await mail.
|
|
758
|
+
await mail.send(sweegoOptions);
|
|
696
759
|
```
|
|
697
760
|
|
|
698
761
|
### Brevo Provider (formerly Sendinblue)
|
|
@@ -712,7 +775,8 @@ const brevo = brevoProvider({
|
|
|
712
775
|
const mail = createMail(brevo);
|
|
713
776
|
|
|
714
777
|
// Send email with Brevo-specific options
|
|
715
|
-
const
|
|
778
|
+
const message = new MailMessage().to("user@example.com").from("sender@example.com").subject("Welcome").html("<h1>Welcome!</h1>");
|
|
779
|
+
const result = await mail.send(message);
|
|
716
780
|
|
|
717
781
|
// Or use Brevo-specific features
|
|
718
782
|
import type { BrevoEmailOptions } from "@visulima/email/providers/brevo";
|
|
@@ -728,7 +792,7 @@ const brevoOptions: BrevoEmailOptions = {
|
|
|
728
792
|
scheduledAt: Math.floor(Date.now() / 1000) + 3600, // Schedule for 1 hour from now
|
|
729
793
|
};
|
|
730
794
|
|
|
731
|
-
await mail.
|
|
795
|
+
await mail.send(brevoOptions);
|
|
732
796
|
```
|
|
733
797
|
|
|
734
798
|
### Postmark Provider
|
|
@@ -748,7 +812,8 @@ const postmark = postmarkProvider({
|
|
|
748
812
|
const mail = createMail(postmark);
|
|
749
813
|
|
|
750
814
|
// Send email with Postmark-specific options
|
|
751
|
-
const
|
|
815
|
+
const message = new MailMessage().to("user@example.com").from("sender@example.com").subject("Welcome").html("<h1>Welcome!</h1>");
|
|
816
|
+
const result = await mail.send(message);
|
|
752
817
|
|
|
753
818
|
// Or use Postmark-specific features
|
|
754
819
|
import type { PostmarkEmailOptions } from "@visulima/email/providers/postmark";
|
|
@@ -767,7 +832,7 @@ const postmarkOptions: PostmarkEmailOptions = {
|
|
|
767
832
|
metadata: { userId: "123" }, // Custom metadata
|
|
768
833
|
};
|
|
769
834
|
|
|
770
|
-
await mail.
|
|
835
|
+
await mail.send(postmarkOptions);
|
|
771
836
|
```
|
|
772
837
|
|
|
773
838
|
### SendGrid Provider
|
|
@@ -787,7 +852,8 @@ const sendgrid = sendGridProvider({
|
|
|
787
852
|
const mail = createMail(sendgrid);
|
|
788
853
|
|
|
789
854
|
// Send email with SendGrid-specific options
|
|
790
|
-
const
|
|
855
|
+
const message = new MailMessage().to("user@example.com").from("sender@example.com").subject("Welcome").html("<h1>Welcome!</h1>");
|
|
856
|
+
const result = await mail.send(message);
|
|
791
857
|
|
|
792
858
|
// Or use SendGrid-specific features
|
|
793
859
|
import type { SendGridEmailOptions } from "@visulima/email/providers/sendgrid";
|
|
@@ -807,7 +873,7 @@ const sendGridOptions: SendGridEmailOptions = {
|
|
|
807
873
|
},
|
|
808
874
|
};
|
|
809
875
|
|
|
810
|
-
await mail.
|
|
876
|
+
await mail.send(sendGridOptions);
|
|
811
877
|
```
|
|
812
878
|
|
|
813
879
|
### Plunk Provider
|
|
@@ -827,7 +893,8 @@ const plunk = plunkProvider({
|
|
|
827
893
|
const mail = createMail(plunk);
|
|
828
894
|
|
|
829
895
|
// Send email with Plunk-specific options
|
|
830
|
-
const
|
|
896
|
+
const message = new MailMessage().to("user@example.com").from("sender@example.com").subject("Welcome").html("<h1>Welcome!</h1>");
|
|
897
|
+
const result = await mail.send(message);
|
|
831
898
|
|
|
832
899
|
// Or use Plunk-specific features
|
|
833
900
|
import type { PlunkEmailOptions } from "@visulima/email/providers/plunk";
|
|
@@ -843,7 +910,7 @@ const plunkOptions: PlunkEmailOptions = {
|
|
|
843
910
|
data: { name: "John" }, // Template data
|
|
844
911
|
};
|
|
845
912
|
|
|
846
|
-
await mail.
|
|
913
|
+
await mail.send(plunkOptions);
|
|
847
914
|
```
|
|
848
915
|
|
|
849
916
|
### Mock Provider (for Testing)
|
|
@@ -864,7 +931,8 @@ const mock = mockProvider({
|
|
|
864
931
|
const mail = createMail(mock);
|
|
865
932
|
|
|
866
933
|
// Send email (stored in memory)
|
|
867
|
-
const
|
|
934
|
+
const message = new MailMessage().to("user@example.com").from("sender@example.com").subject("Test").html("<h1>Test</h1>");
|
|
935
|
+
const result = await mail.send(message);
|
|
868
936
|
|
|
869
937
|
// Access stored emails
|
|
870
938
|
const sentEmails = mock.getSentEmails();
|
|
@@ -931,7 +999,8 @@ const customMailCrab = mailCrabProvider({
|
|
|
931
999
|
// Use MailCrab provider
|
|
932
1000
|
const mail = createMail(mailCrab);
|
|
933
1001
|
|
|
934
|
-
const
|
|
1002
|
+
const message = new MailMessage().to("user@example.com").from("sender@example.com").subject("Test Email").html("<h1>Test</h1>");
|
|
1003
|
+
const result = await mail.send(message);
|
|
935
1004
|
```
|
|
936
1005
|
|
|
937
1006
|
**Note:** Make sure MailCrab is running locally before using this provider. MailCrab can be installed via Docker or as a standalone application.
|
|
@@ -1054,6 +1123,263 @@ Template engines are optional peer dependencies and work where their underlying
|
|
|
1054
1123
|
|
|
1055
1124
|
\* Runtime support depends on the wrapped providers. Works if all wrapped providers support the runtime.
|
|
1056
1125
|
|
|
1126
|
+
## Disposable Email Detection
|
|
1127
|
+
|
|
1128
|
+
The package includes support for detecting disposable email addresses using the `@visulima/disposable-email-domains` package, which is included as a dependency.
|
|
1129
|
+
|
|
1130
|
+
### Usage
|
|
1131
|
+
|
|
1132
|
+
```typescript
|
|
1133
|
+
import { isDisposableEmail } from "@visulima/email/validation/disposable-email-domains";
|
|
1134
|
+
|
|
1135
|
+
// Check if an email is disposable
|
|
1136
|
+
if (isDisposableEmail("user@mailinator.com")) {
|
|
1137
|
+
console.log("Disposable email detected!");
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// With custom domains
|
|
1141
|
+
const customDomains = new Set(["my-disposable.com"]);
|
|
1142
|
+
if (isDisposableEmail("user@my-disposable.com", customDomains)) {
|
|
1143
|
+
console.log("Custom disposable email detected!");
|
|
1144
|
+
}
|
|
1145
|
+
```
|
|
1146
|
+
|
|
1147
|
+
**Note:** The `@visulima/disposable-email-domains` package is included as a dependency, so no additional installation is required.
|
|
1148
|
+
|
|
1149
|
+
## Email Verification
|
|
1150
|
+
|
|
1151
|
+
The package provides comprehensive email verification utilities including MX record checking, SMTP verification, and role account detection.
|
|
1152
|
+
|
|
1153
|
+
### MX Record Checking
|
|
1154
|
+
|
|
1155
|
+
Check if a domain has valid MX (Mail Exchange) records:
|
|
1156
|
+
|
|
1157
|
+
```typescript
|
|
1158
|
+
import { checkMxRecords } from "@visulima/email/validation/check-mx-records";
|
|
1159
|
+
|
|
1160
|
+
const result = await checkMxRecords("example.com");
|
|
1161
|
+
|
|
1162
|
+
if (result.valid) {
|
|
1163
|
+
console.log("MX records:", result.records);
|
|
1164
|
+
// Records are sorted by priority (lowest first)
|
|
1165
|
+
} else {
|
|
1166
|
+
console.error("No MX records found:", result.error);
|
|
1167
|
+
}
|
|
1168
|
+
```
|
|
1169
|
+
|
|
1170
|
+
#### Caching MX Records
|
|
1171
|
+
|
|
1172
|
+
Use caching to improve performance and reduce DNS lookups:
|
|
1173
|
+
|
|
1174
|
+
```typescript
|
|
1175
|
+
import { checkMxRecords } from "@visulima/email/validation/check-mx-records";
|
|
1176
|
+
import { InMemoryCache } from "@visulima/email/utils/cache";
|
|
1177
|
+
|
|
1178
|
+
// Create a cache instance
|
|
1179
|
+
const cache = new InMemoryCache();
|
|
1180
|
+
|
|
1181
|
+
// Use cache with default TTL (1 hour)
|
|
1182
|
+
const result1 = await checkMxRecords("example.com", { cache });
|
|
1183
|
+
|
|
1184
|
+
// Use cache with custom TTL (5 minutes)
|
|
1185
|
+
const result2 = await checkMxRecords("example.com", {
|
|
1186
|
+
cache,
|
|
1187
|
+
ttl: 5 * 60 * 1000, // 5 minutes in milliseconds
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
// Clear cache when needed
|
|
1191
|
+
await cache.clear();
|
|
1192
|
+
```
|
|
1193
|
+
|
|
1194
|
+
#### Custom Cache Implementation
|
|
1195
|
+
|
|
1196
|
+
You can implement your own cache using the `Cache` interface (e.g., for Redis, LRU cache, etc.):
|
|
1197
|
+
|
|
1198
|
+
```typescript
|
|
1199
|
+
import type { Cache, MxCheckResult } from "@visulima/email/utils/cache";
|
|
1200
|
+
import { checkMxRecords } from "@visulima/email/validation/check-mx-records";
|
|
1201
|
+
|
|
1202
|
+
// Example: Custom Redis cache implementation
|
|
1203
|
+
const redisCache: Cache<MxCheckResult> = {
|
|
1204
|
+
get: async (key: string) => {
|
|
1205
|
+
const cached = await redis.get(`mx:${key}`);
|
|
1206
|
+
return cached ? JSON.parse(cached) : undefined;
|
|
1207
|
+
},
|
|
1208
|
+
set: async (key: string, value: MxCheckResult, ttl: number) => {
|
|
1209
|
+
await redis.setex(`mx:${key}`, Math.floor(ttl / 1000), JSON.stringify(value));
|
|
1210
|
+
},
|
|
1211
|
+
delete: async (key: string) => {
|
|
1212
|
+
await redis.del(`mx:${key}`);
|
|
1213
|
+
},
|
|
1214
|
+
clear: async () => {
|
|
1215
|
+
// Clear all MX cache keys
|
|
1216
|
+
const keys = await redis.keys("mx:*");
|
|
1217
|
+
if (keys.length > 0) {
|
|
1218
|
+
await redis.del(...keys);
|
|
1219
|
+
}
|
|
1220
|
+
},
|
|
1221
|
+
};
|
|
1222
|
+
|
|
1223
|
+
// Use custom cache
|
|
1224
|
+
const result = await checkMxRecords("example.com", {
|
|
1225
|
+
cache: redisCache,
|
|
1226
|
+
ttl: 10 * 60 * 1000, // 10 minutes
|
|
1227
|
+
});
|
|
1228
|
+
```
|
|
1229
|
+
|
|
1230
|
+
### Role Account Detection
|
|
1231
|
+
|
|
1232
|
+
Detect if an email address is a role account (non-personal email like `noreply@`, `support@`, etc.):
|
|
1233
|
+
|
|
1234
|
+
```typescript
|
|
1235
|
+
import { isRoleAccount } from "@visulima/email/validation/role-accounts";
|
|
1236
|
+
|
|
1237
|
+
if (isRoleAccount("noreply@example.com")) {
|
|
1238
|
+
console.log("This is a role account");
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// With custom role prefixes
|
|
1242
|
+
const customPrefixes = new Set(["custom-role", "my-role"]);
|
|
1243
|
+
if (isRoleAccount("custom-role@example.com", customPrefixes)) {
|
|
1244
|
+
console.log("Custom role account detected");
|
|
1245
|
+
}
|
|
1246
|
+
```
|
|
1247
|
+
|
|
1248
|
+
### SMTP Verification
|
|
1249
|
+
|
|
1250
|
+
Verify if an email address exists by connecting to the mail server:
|
|
1251
|
+
|
|
1252
|
+
```typescript
|
|
1253
|
+
import { verifySmtp } from "@visulima/email/validation/verify-smtp";
|
|
1254
|
+
import { InMemoryCache } from "@visulima/email/utils/cache";
|
|
1255
|
+
import type { MxCheckResult, SmtpVerificationResult } from "@visulima/email/validation/check-mx-records";
|
|
1256
|
+
|
|
1257
|
+
// Create separate caches for MX records and SMTP results
|
|
1258
|
+
const mxCache = new InMemoryCache<MxCheckResult>();
|
|
1259
|
+
const smtpCache = new InMemoryCache<SmtpVerificationResult>();
|
|
1260
|
+
|
|
1261
|
+
const result = await verifySmtp("user@example.com", {
|
|
1262
|
+
timeout: 5000,
|
|
1263
|
+
fromEmail: "test@example.com",
|
|
1264
|
+
port: 25,
|
|
1265
|
+
cache: mxCache, // Optional: cache MX records
|
|
1266
|
+
smtpCache, // Optional: cache SMTP verification results
|
|
1267
|
+
ttl: 5 * 60 * 1000, // Cache for 5 minutes
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1270
|
+
if (result.valid) {
|
|
1271
|
+
console.log("Email address exists");
|
|
1272
|
+
} else {
|
|
1273
|
+
console.error("Verification failed:", result.error);
|
|
1274
|
+
}
|
|
1275
|
+
```
|
|
1276
|
+
|
|
1277
|
+
**Note:** Many mail servers block SMTP verification to prevent email harvesting. This method may not work for all domains.
|
|
1278
|
+
|
|
1279
|
+
### Comprehensive Email Verification
|
|
1280
|
+
|
|
1281
|
+
Combine all verification checks in a single function:
|
|
1282
|
+
|
|
1283
|
+
```typescript
|
|
1284
|
+
import { verifyEmail } from "@visulima/email/validation/verify-email";
|
|
1285
|
+
import { InMemoryCache } from "@visulima/email/utils/cache";
|
|
1286
|
+
import type { MxCheckResult, SmtpVerificationResult } from "@visulima/email/validation/check-mx-records";
|
|
1287
|
+
|
|
1288
|
+
// Create separate caches for MX records and SMTP results
|
|
1289
|
+
const mxCache = new InMemoryCache<MxCheckResult>();
|
|
1290
|
+
const smtpCache = new InMemoryCache<SmtpVerificationResult>();
|
|
1291
|
+
|
|
1292
|
+
const result = await verifyEmail("user@example.com", {
|
|
1293
|
+
checkDisposable: true,
|
|
1294
|
+
checkRoleAccount: true,
|
|
1295
|
+
checkMx: true,
|
|
1296
|
+
checkSmtp: false, // Optional, many servers block this
|
|
1297
|
+
cache: mxCache, // Optional: cache MX records
|
|
1298
|
+
smtpCache, // Optional: cache SMTP verification results
|
|
1299
|
+
customDisposableDomains: new Set(["custom-disposable.com"]),
|
|
1300
|
+
customRolePrefixes: new Set(["custom-role"]),
|
|
1301
|
+
});
|
|
1302
|
+
|
|
1303
|
+
if (result.valid) {
|
|
1304
|
+
console.log("Email is valid!");
|
|
1305
|
+
} else {
|
|
1306
|
+
console.error("Errors:", result.errors);
|
|
1307
|
+
console.warn("Warnings:", result.warnings);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
// Access individual check results
|
|
1311
|
+
console.log("Format valid:", result.formatValid);
|
|
1312
|
+
console.log("Is disposable:", result.disposable);
|
|
1313
|
+
console.log("Is role account:", result.roleAccount);
|
|
1314
|
+
console.log("MX valid:", result.mxValid);
|
|
1315
|
+
console.log("SMTP valid:", result.smtpValid);
|
|
1316
|
+
```
|
|
1317
|
+
|
|
1318
|
+
### Email Utilities
|
|
1319
|
+
|
|
1320
|
+
The package also exports standalone utility functions that can be used independently:
|
|
1321
|
+
|
|
1322
|
+
#### Email Validation
|
|
1323
|
+
|
|
1324
|
+
```typescript
|
|
1325
|
+
import { validateEmail } from "@visulima/email/validation/validate-email";
|
|
1326
|
+
|
|
1327
|
+
if (validateEmail("user@example.com")) {
|
|
1328
|
+
console.log("Valid email!");
|
|
1329
|
+
}
|
|
1330
|
+
```
|
|
1331
|
+
|
|
1332
|
+
#### Parse Email Address
|
|
1333
|
+
|
|
1334
|
+
```typescript
|
|
1335
|
+
import { parseAddress } from "@visulima/email/utils/parse-address";
|
|
1336
|
+
|
|
1337
|
+
// Parse email with name
|
|
1338
|
+
const address = parseAddress("John Doe <john@example.com>");
|
|
1339
|
+
// { name: "John Doe", email: "john@example.com" }
|
|
1340
|
+
|
|
1341
|
+
// Parse email without name
|
|
1342
|
+
const simple = parseAddress("jane@example.com");
|
|
1343
|
+
// { email: "jane@example.com" }
|
|
1344
|
+
```
|
|
1345
|
+
|
|
1346
|
+
#### Format Email Address
|
|
1347
|
+
|
|
1348
|
+
```typescript
|
|
1349
|
+
import { formatEmailAddress } from "@visulima/email/utils/format-email-address";
|
|
1350
|
+
|
|
1351
|
+
const formatted = formatEmailAddress({
|
|
1352
|
+
email: "user@example.com",
|
|
1353
|
+
name: "John Doe",
|
|
1354
|
+
});
|
|
1355
|
+
// "John Doe <user@example.com>"
|
|
1356
|
+
|
|
1357
|
+
const simple = formatEmailAddress({ email: "user@example.com" });
|
|
1358
|
+
// "user@example.com"
|
|
1359
|
+
```
|
|
1360
|
+
|
|
1361
|
+
#### Normalize Email Aliases
|
|
1362
|
+
|
|
1363
|
+
Normalize email aliases for supported providers (Gmail, Yahoo, Outlook, etc.):
|
|
1364
|
+
|
|
1365
|
+
```typescript
|
|
1366
|
+
import { normalizeEmailAliases } from "@visulima/email/utils/normalize-email-aliases";
|
|
1367
|
+
|
|
1368
|
+
// Gmail: removes dots and plus aliases
|
|
1369
|
+
normalizeEmailAliases("example+test@gmail.com"); // "example@gmail.com"
|
|
1370
|
+
normalizeEmailAliases("ex.ample@gmail.com"); // "example@gmail.com"
|
|
1371
|
+
|
|
1372
|
+
// Yahoo, Outlook, FastMail, Mail.com, GMX, etc.: removes plus aliases only
|
|
1373
|
+
normalizeEmailAliases("user+tag@yahoo.com"); // "user@yahoo.com"
|
|
1374
|
+
normalizeEmailAliases("user.name@yahoo.com"); // "user.name@yahoo.com" (dots preserved)
|
|
1375
|
+
normalizeEmailAliases("user+tag@fastmail.com"); // "user@fastmail.com"
|
|
1376
|
+
normalizeEmailAliases("user+tag@mail.com"); // "user@mail.com"
|
|
1377
|
+
normalizeEmailAliases("user+tag@gmx.com"); // "user@gmx.com"
|
|
1378
|
+
|
|
1379
|
+
// Unsupported domains return unchanged
|
|
1380
|
+
normalizeEmailAliases("user@example.com"); // "user@example.com"
|
|
1381
|
+
```
|
|
1382
|
+
|
|
1057
1383
|
## Related
|
|
1058
1384
|
|
|
1059
1385
|
## Supported Node.js Versions
|
|
@@ -1072,13 +1398,23 @@ If you would like to help take a look at the [list of issues](https://github.com
|
|
|
1072
1398
|
- [Daniel Bannert](https://github.com/prisis)
|
|
1073
1399
|
- [All Contributors](https://github.com/visulima/visulima/graphs/contributors)
|
|
1074
1400
|
|
|
1401
|
+
## Made with ❤️ at Anolilab
|
|
1402
|
+
|
|
1403
|
+
This is an open source project and will always remain free to use. If you think it's cool, please star it 🌟. [Anolilab](https://www.anolilab.com/open-source) is a Development and AI Studio. Contact us at [hello@anolilab.com](mailto:hello@anolilab.com) if you need any help with these technologies or just want to say hi!
|
|
1404
|
+
|
|
1075
1405
|
## License
|
|
1076
1406
|
|
|
1077
|
-
The visulima email is open-sourced software licensed under the [MIT][license
|
|
1407
|
+
The visulima email is open-sourced software licensed under the [MIT][license]
|
|
1408
|
+
|
|
1409
|
+
<!-- badges -->
|
|
1078
1410
|
|
|
1079
|
-
[
|
|
1080
|
-
[
|
|
1081
|
-
[
|
|
1082
|
-
[
|
|
1083
|
-
[
|
|
1084
|
-
[
|
|
1411
|
+
[license-badge]: https://img.shields.io/npm/l/@visulima/email?style=for-the-badge
|
|
1412
|
+
[license]: https://github.com/visulima/visulima/blob/main/LICENSE
|
|
1413
|
+
[npm-downloads-badge]: https://img.shields.io/npm/dm/@visulima/email?style=for-the-badge
|
|
1414
|
+
[npm-downloads]: https://www.npmjs.com/package/@visulima/email
|
|
1415
|
+
[prs-welcome-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=for-the-badge
|
|
1416
|
+
[prs-welcome]: https://github.com/visulima/visulima/blob/main/.github/CONTRIBUTING.md
|
|
1417
|
+
[chat-badge]: https://img.shields.io/discord/932323359193186354.svg?style=for-the-badge
|
|
1418
|
+
[chat]: https://discord.gg/TtFJY8xkFK
|
|
1419
|
+
[typescript-badge]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
|
|
1420
|
+
[typescript-url]: https://www.typescriptlang.org/
|