@veloxts/mail 0.6.51
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/GUIDE.md +132 -0
- package/LICENSE +21 -0
- package/README.md +32 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +70 -0
- package/dist/mail.d.ts +47 -0
- package/dist/mail.js +55 -0
- package/dist/manager.d.ts +82 -0
- package/dist/manager.js +153 -0
- package/dist/plugin.d.ts +117 -0
- package/dist/plugin.js +145 -0
- package/dist/transports/log.d.ts +32 -0
- package/dist/transports/log.js +101 -0
- package/dist/transports/resend.d.ts +31 -0
- package/dist/transports/resend.js +95 -0
- package/dist/transports/smtp.d.ts +36 -0
- package/dist/transports/smtp.js +116 -0
- package/dist/types.d.ts +425 -0
- package/dist/types.js +6 -0
- package/dist/utils.d.ts +89 -0
- package/dist/utils.js +190 -0
- package/package.json +73 -0
package/GUIDE.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# @veloxts/mail Guide
|
|
2
|
+
|
|
3
|
+
## Drivers
|
|
4
|
+
|
|
5
|
+
### Log Driver (default)
|
|
6
|
+
|
|
7
|
+
Logs emails to console. Best for development.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { mailPlugin } from '@veloxts/mail';
|
|
11
|
+
|
|
12
|
+
app.use(mailPlugin({ driver: 'log' }));
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### SMTP Driver
|
|
16
|
+
|
|
17
|
+
Send via any SMTP server.
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
app.use(mailPlugin({
|
|
21
|
+
driver: 'smtp',
|
|
22
|
+
config: {
|
|
23
|
+
host: 'smtp.example.com',
|
|
24
|
+
port: 587,
|
|
25
|
+
secure: false,
|
|
26
|
+
auth: {
|
|
27
|
+
user: process.env.SMTP_USER,
|
|
28
|
+
pass: process.env.SMTP_PASS,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
from: { name: 'My App', email: 'noreply@myapp.com' },
|
|
32
|
+
}));
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Resend Driver
|
|
36
|
+
|
|
37
|
+
Send via Resend API.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install resend
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
app.use(mailPlugin({
|
|
45
|
+
driver: 'resend',
|
|
46
|
+
config: {
|
|
47
|
+
apiKey: process.env.RESEND_API_KEY,
|
|
48
|
+
},
|
|
49
|
+
from: { name: 'My App', email: 'noreply@myapp.com' },
|
|
50
|
+
}));
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Defining Email Templates
|
|
54
|
+
|
|
55
|
+
Using React Email components:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { defineMail } from '@veloxts/mail';
|
|
59
|
+
import { Html, Head, Body, Container, Text, Button } from '@react-email/components';
|
|
60
|
+
|
|
61
|
+
export const WelcomeEmail = defineMail({
|
|
62
|
+
name: 'welcome',
|
|
63
|
+
schema: z.object({
|
|
64
|
+
user: z.object({ name: z.string() }),
|
|
65
|
+
activationUrl: z.string().url(),
|
|
66
|
+
}),
|
|
67
|
+
subject: ({ user }) => `Welcome, ${user.name}!`,
|
|
68
|
+
template: ({ user, activationUrl }) => (
|
|
69
|
+
<Html>
|
|
70
|
+
<Head />
|
|
71
|
+
<Body>
|
|
72
|
+
<Container>
|
|
73
|
+
<Text>Hello {user.name}, welcome to our app!</Text>
|
|
74
|
+
<Button href={activationUrl}>Activate Account</Button>
|
|
75
|
+
</Container>
|
|
76
|
+
</Body>
|
|
77
|
+
</Html>
|
|
78
|
+
),
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Sending Emails
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Send immediately
|
|
86
|
+
await ctx.mail.send(WelcomeEmail, {
|
|
87
|
+
to: 'user@example.com',
|
|
88
|
+
data: { user, activationUrl },
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// With CC/BCC
|
|
92
|
+
await ctx.mail.send(WelcomeEmail, {
|
|
93
|
+
to: 'user@example.com',
|
|
94
|
+
cc: ['manager@example.com'],
|
|
95
|
+
bcc: ['archive@example.com'],
|
|
96
|
+
data: { user, activationUrl },
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// With attachments
|
|
100
|
+
await ctx.mail.send(InvoiceEmail, {
|
|
101
|
+
to: 'user@example.com',
|
|
102
|
+
data: { invoice },
|
|
103
|
+
attachments: [
|
|
104
|
+
{ filename: 'invoice.pdf', content: pdfBuffer },
|
|
105
|
+
],
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Bulk Sending
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const results = await ctx.mail.sendBulk(WelcomeEmail, [
|
|
113
|
+
{ to: 'a@example.com', data: { user: userA, activationUrl: urlA } },
|
|
114
|
+
{ to: 'b@example.com', data: { user: userB, activationUrl: urlB } },
|
|
115
|
+
]);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Preview Emails
|
|
119
|
+
|
|
120
|
+
Render without sending (useful for testing):
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
const { html, text, subject } = await ctx.mail.render(WelcomeEmail, {
|
|
124
|
+
data: { user, activationUrl },
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## CLI Commands
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
velox make mail WelcomeEmail # Generate email template
|
|
132
|
+
```
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 VeloxTS Framework Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# @veloxts/mail
|
|
2
|
+
|
|
3
|
+
> **Early Preview** - APIs may change before v1.0.
|
|
4
|
+
|
|
5
|
+
Email sending with React Email templates and multiple transport drivers.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @veloxts/mail
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { mailPlugin, defineMail } from '@veloxts/mail';
|
|
17
|
+
|
|
18
|
+
app.use(mailPlugin({ driver: 'log' }));
|
|
19
|
+
|
|
20
|
+
const WelcomeEmail = defineMail({
|
|
21
|
+
subject: ({ name }) => `Welcome, ${name}!`,
|
|
22
|
+
template: ({ name }) => <Text>Hello {name}, welcome to our app!</Text>,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
await ctx.mail.send(WelcomeEmail, { to: 'user@example.com', data: { name: 'John' } });
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
See [GUIDE.md](./GUIDE.md) for detailed documentation.
|
|
29
|
+
|
|
30
|
+
## License
|
|
31
|
+
|
|
32
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @veloxts/mail
|
|
3
|
+
*
|
|
4
|
+
* Email templating and sending for VeloxTS framework.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - React Email integration for type-safe templates
|
|
8
|
+
* - Multiple transport drivers: SMTP, Resend, Log (development)
|
|
9
|
+
* - Type-safe template props with Zod schemas
|
|
10
|
+
* - Automatic plain text generation from HTML
|
|
11
|
+
* - Fastify plugin for request context integration
|
|
12
|
+
* - Bulk email sending
|
|
13
|
+
* - Email preview/rendering without sending
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { mailPlugin, defineMail } from '@veloxts/mail';
|
|
18
|
+
* import { Html, Head, Body, Container, Heading, Text, Button } from '@react-email/components';
|
|
19
|
+
* import { z } from 'zod';
|
|
20
|
+
*
|
|
21
|
+
* // Define a mail template
|
|
22
|
+
* export const WelcomeEmail = defineMail({
|
|
23
|
+
* name: 'welcome',
|
|
24
|
+
* schema: z.object({
|
|
25
|
+
* user: z.object({ name: z.string() }),
|
|
26
|
+
* activationUrl: z.string().url(),
|
|
27
|
+
* }),
|
|
28
|
+
* subject: ({ user }) => `Welcome, ${user.name}!`,
|
|
29
|
+
* template: ({ user, activationUrl }) => (
|
|
30
|
+
* <Html>
|
|
31
|
+
* <Head />
|
|
32
|
+
* <Body>
|
|
33
|
+
* <Container>
|
|
34
|
+
* <Heading>Welcome, {user.name}!</Heading>
|
|
35
|
+
* <Text>Click below to activate your account:</Text>
|
|
36
|
+
* <Button href={activationUrl}>Activate</Button>
|
|
37
|
+
* </Container>
|
|
38
|
+
* </Body>
|
|
39
|
+
* </Html>
|
|
40
|
+
* ),
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* // Register plugin
|
|
44
|
+
* app.use(mailPlugin({
|
|
45
|
+
* driver: 'resend',
|
|
46
|
+
* config: { apiKey: process.env.RESEND_API_KEY },
|
|
47
|
+
* from: { email: 'hello@myapp.com', name: 'My App' },
|
|
48
|
+
* }));
|
|
49
|
+
*
|
|
50
|
+
* // Send email in procedures
|
|
51
|
+
* await ctx.mail.send(WelcomeEmail, {
|
|
52
|
+
* to: 'user@example.com',
|
|
53
|
+
* data: { user: { name: 'John' }, activationUrl: 'https://...' },
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @packageDocumentation
|
|
58
|
+
*/
|
|
59
|
+
export { defineMail, type MailDefinition, mail } from './mail.js';
|
|
60
|
+
export { createMailManager, type MailManager, mailer, type SendBulkOptions } from './manager.js';
|
|
61
|
+
export { closeMail, getMail, getMailFromInstance, initMail, mailPlugin, } from './plugin.js';
|
|
62
|
+
export { createLogTransport, DRIVER_NAME as LOG_DRIVER } from './transports/log.js';
|
|
63
|
+
export { createResendTransport, DRIVER_NAME as RESEND_DRIVER } from './transports/resend.js';
|
|
64
|
+
export { createSmtpTransport, DRIVER_NAME as SMTP_DRIVER } from './transports/smtp.js';
|
|
65
|
+
export type { Attachment, EmailAddress, LogConfig, MailConfig, MailDefinitionConfig, MailDriver, MailEnvelope, MailPluginOptions, MailTransport, Recipient, RenderedMail, ResendConfig, SendMailOptions, SendResult, SmtpConfig, } from './types.js';
|
|
66
|
+
export { escapeHtml, formatAddress, isValidEmail, normalizeRecipient, normalizeRecipients, sanitizeHeaderValue, stripHtml, validateRecipient, validateRecipients, validateTemplateName, } from './utils.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @veloxts/mail
|
|
3
|
+
*
|
|
4
|
+
* Email templating and sending for VeloxTS framework.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - React Email integration for type-safe templates
|
|
8
|
+
* - Multiple transport drivers: SMTP, Resend, Log (development)
|
|
9
|
+
* - Type-safe template props with Zod schemas
|
|
10
|
+
* - Automatic plain text generation from HTML
|
|
11
|
+
* - Fastify plugin for request context integration
|
|
12
|
+
* - Bulk email sending
|
|
13
|
+
* - Email preview/rendering without sending
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { mailPlugin, defineMail } from '@veloxts/mail';
|
|
18
|
+
* import { Html, Head, Body, Container, Heading, Text, Button } from '@react-email/components';
|
|
19
|
+
* import { z } from 'zod';
|
|
20
|
+
*
|
|
21
|
+
* // Define a mail template
|
|
22
|
+
* export const WelcomeEmail = defineMail({
|
|
23
|
+
* name: 'welcome',
|
|
24
|
+
* schema: z.object({
|
|
25
|
+
* user: z.object({ name: z.string() }),
|
|
26
|
+
* activationUrl: z.string().url(),
|
|
27
|
+
* }),
|
|
28
|
+
* subject: ({ user }) => `Welcome, ${user.name}!`,
|
|
29
|
+
* template: ({ user, activationUrl }) => (
|
|
30
|
+
* <Html>
|
|
31
|
+
* <Head />
|
|
32
|
+
* <Body>
|
|
33
|
+
* <Container>
|
|
34
|
+
* <Heading>Welcome, {user.name}!</Heading>
|
|
35
|
+
* <Text>Click below to activate your account:</Text>
|
|
36
|
+
* <Button href={activationUrl}>Activate</Button>
|
|
37
|
+
* </Container>
|
|
38
|
+
* </Body>
|
|
39
|
+
* </Html>
|
|
40
|
+
* ),
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* // Register plugin
|
|
44
|
+
* app.use(mailPlugin({
|
|
45
|
+
* driver: 'resend',
|
|
46
|
+
* config: { apiKey: process.env.RESEND_API_KEY },
|
|
47
|
+
* from: { email: 'hello@myapp.com', name: 'My App' },
|
|
48
|
+
* }));
|
|
49
|
+
*
|
|
50
|
+
* // Send email in procedures
|
|
51
|
+
* await ctx.mail.send(WelcomeEmail, {
|
|
52
|
+
* to: 'user@example.com',
|
|
53
|
+
* data: { user: { name: 'John' }, activationUrl: 'https://...' },
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @packageDocumentation
|
|
58
|
+
*/
|
|
59
|
+
// Mail definition
|
|
60
|
+
export { defineMail, mail } from './mail.js';
|
|
61
|
+
// Manager
|
|
62
|
+
export { createMailManager, mailer } from './manager.js';
|
|
63
|
+
// Plugin
|
|
64
|
+
export { closeMail, getMail, getMailFromInstance, initMail, mailPlugin, } from './plugin.js';
|
|
65
|
+
// Transports
|
|
66
|
+
export { createLogTransport, DRIVER_NAME as LOG_DRIVER } from './transports/log.js';
|
|
67
|
+
export { createResendTransport, DRIVER_NAME as RESEND_DRIVER } from './transports/resend.js';
|
|
68
|
+
export { createSmtpTransport, DRIVER_NAME as SMTP_DRIVER } from './transports/smtp.js';
|
|
69
|
+
// Utilities
|
|
70
|
+
export { escapeHtml, formatAddress, isValidEmail, normalizeRecipient, normalizeRecipients, sanitizeHeaderValue, stripHtml, validateRecipient, validateRecipients, validateTemplateName, } from './utils.js';
|
package/dist/mail.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mail Definition
|
|
3
|
+
*
|
|
4
|
+
* Type-safe email template definitions with React Email.
|
|
5
|
+
*/
|
|
6
|
+
import type { z } from 'zod';
|
|
7
|
+
import type { MailDefinition, MailDefinitionConfig } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Define a type-safe email template with React Email.
|
|
10
|
+
*
|
|
11
|
+
* @param config - Mail definition configuration
|
|
12
|
+
* @returns Mail definition
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { defineMail } from '@veloxts/mail';
|
|
17
|
+
* import { Html, Head, Body, Container, Heading, Text, Button } from '@react-email/components';
|
|
18
|
+
* import { z } from 'zod';
|
|
19
|
+
*
|
|
20
|
+
* export const WelcomeEmail = defineMail({
|
|
21
|
+
* name: 'welcome',
|
|
22
|
+
* schema: z.object({
|
|
23
|
+
* user: z.object({ name: z.string(), email: z.string() }),
|
|
24
|
+
* activationUrl: z.string().url(),
|
|
25
|
+
* }),
|
|
26
|
+
* subject: ({ user }) => `Welcome to our app, ${user.name}!`,
|
|
27
|
+
* template: ({ user, activationUrl }) => (
|
|
28
|
+
* <Html>
|
|
29
|
+
* <Head />
|
|
30
|
+
* <Body>
|
|
31
|
+
* <Container>
|
|
32
|
+
* <Heading>Welcome, {user.name}!</Heading>
|
|
33
|
+
* <Text>Click below to activate your account:</Text>
|
|
34
|
+
* <Button href={activationUrl}>Activate Account</Button>
|
|
35
|
+
* </Container>
|
|
36
|
+
* </Body>
|
|
37
|
+
* </Html>
|
|
38
|
+
* ),
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function defineMail<TSchema extends z.ZodType>(config: MailDefinitionConfig<TSchema>): MailDefinition<TSchema>;
|
|
43
|
+
/**
|
|
44
|
+
* Alias for defineMail.
|
|
45
|
+
*/
|
|
46
|
+
export declare const mail: typeof defineMail;
|
|
47
|
+
export type { MailDefinition } from './types.js';
|
package/dist/mail.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mail Definition
|
|
3
|
+
*
|
|
4
|
+
* Type-safe email template definitions with React Email.
|
|
5
|
+
*/
|
|
6
|
+
import { validateTemplateName } from './utils.js';
|
|
7
|
+
/**
|
|
8
|
+
* Define a type-safe email template with React Email.
|
|
9
|
+
*
|
|
10
|
+
* @param config - Mail definition configuration
|
|
11
|
+
* @returns Mail definition
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { defineMail } from '@veloxts/mail';
|
|
16
|
+
* import { Html, Head, Body, Container, Heading, Text, Button } from '@react-email/components';
|
|
17
|
+
* import { z } from 'zod';
|
|
18
|
+
*
|
|
19
|
+
* export const WelcomeEmail = defineMail({
|
|
20
|
+
* name: 'welcome',
|
|
21
|
+
* schema: z.object({
|
|
22
|
+
* user: z.object({ name: z.string(), email: z.string() }),
|
|
23
|
+
* activationUrl: z.string().url(),
|
|
24
|
+
* }),
|
|
25
|
+
* subject: ({ user }) => `Welcome to our app, ${user.name}!`,
|
|
26
|
+
* template: ({ user, activationUrl }) => (
|
|
27
|
+
* <Html>
|
|
28
|
+
* <Head />
|
|
29
|
+
* <Body>
|
|
30
|
+
* <Container>
|
|
31
|
+
* <Heading>Welcome, {user.name}!</Heading>
|
|
32
|
+
* <Text>Click below to activate your account:</Text>
|
|
33
|
+
* <Button href={activationUrl}>Activate Account</Button>
|
|
34
|
+
* </Container>
|
|
35
|
+
* </Body>
|
|
36
|
+
* </Html>
|
|
37
|
+
* ),
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export function defineMail(config) {
|
|
42
|
+
validateTemplateName(config.name);
|
|
43
|
+
return {
|
|
44
|
+
name: config.name,
|
|
45
|
+
schema: config.schema,
|
|
46
|
+
subject: config.subject,
|
|
47
|
+
template: config.template,
|
|
48
|
+
text: config.text,
|
|
49
|
+
from: config.from,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Alias for defineMail.
|
|
54
|
+
*/
|
|
55
|
+
export const mail = defineMail;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mail Manager
|
|
3
|
+
*
|
|
4
|
+
* High-level mail API for rendering and sending emails.
|
|
5
|
+
*/
|
|
6
|
+
import type { z } from 'zod';
|
|
7
|
+
import type { MailDefinition } from './mail.js';
|
|
8
|
+
import type { MailPluginOptions, RenderedMail, SendMailOptions, SendResult } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Mail manager interface.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Options for bulk email sending.
|
|
14
|
+
*/
|
|
15
|
+
export interface SendBulkOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Maximum number of concurrent email sends.
|
|
18
|
+
* Higher values = faster but more memory/CPU.
|
|
19
|
+
* @default 10
|
|
20
|
+
*/
|
|
21
|
+
concurrency?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface MailManager {
|
|
24
|
+
/**
|
|
25
|
+
* Send an email using a mail definition.
|
|
26
|
+
*/
|
|
27
|
+
send<TSchema extends z.ZodType>(mail: MailDefinition<TSchema>, options: SendMailOptions<TSchema>): Promise<SendResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Send an email to multiple recipients with different data.
|
|
30
|
+
* Uses parallel execution with configurable concurrency control.
|
|
31
|
+
*/
|
|
32
|
+
sendBulk<TSchema extends z.ZodType>(mail: MailDefinition<TSchema>, messages: Array<SendMailOptions<TSchema>>, options?: SendBulkOptions): Promise<SendResult[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Render an email without sending (for preview/testing).
|
|
35
|
+
*/
|
|
36
|
+
render<TSchema extends z.ZodType>(mail: MailDefinition<TSchema>, options: SendMailOptions<TSchema>): Promise<RenderedMail>;
|
|
37
|
+
/**
|
|
38
|
+
* Close the transport connection.
|
|
39
|
+
*/
|
|
40
|
+
close(): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create a mail manager.
|
|
44
|
+
*
|
|
45
|
+
* @param options - Mail plugin options
|
|
46
|
+
* @returns Mail manager instance
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* // Development (log to console)
|
|
51
|
+
* const mail = await createMailManager({ driver: 'log' });
|
|
52
|
+
*
|
|
53
|
+
* // Production (SMTP)
|
|
54
|
+
* const mail = await createMailManager({
|
|
55
|
+
* driver: 'smtp',
|
|
56
|
+
* config: {
|
|
57
|
+
* host: 'smtp.example.com',
|
|
58
|
+
* port: 587,
|
|
59
|
+
* auth: { user: '...', pass: '...' },
|
|
60
|
+
* },
|
|
61
|
+
* from: { email: 'hello@example.com', name: 'My App' },
|
|
62
|
+
* });
|
|
63
|
+
*
|
|
64
|
+
* // Production (Resend)
|
|
65
|
+
* const mail = await createMailManager({
|
|
66
|
+
* driver: 'resend',
|
|
67
|
+
* config: { apiKey: process.env.RESEND_API_KEY! },
|
|
68
|
+
* from: { email: 'hello@example.com', name: 'My App' },
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* // Send email
|
|
72
|
+
* await mail.send(WelcomeEmail, {
|
|
73
|
+
* to: 'user@example.com',
|
|
74
|
+
* data: { user: { name: 'John' }, activationUrl: 'https://...' },
|
|
75
|
+
* });
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export declare function createMailManager(options?: MailPluginOptions): Promise<MailManager>;
|
|
79
|
+
/**
|
|
80
|
+
* Alias for createMailManager.
|
|
81
|
+
*/
|
|
82
|
+
export declare const mailer: typeof createMailManager;
|
package/dist/manager.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mail Manager
|
|
3
|
+
*
|
|
4
|
+
* High-level mail API for rendering and sending emails.
|
|
5
|
+
*/
|
|
6
|
+
import { render } from '@react-email/render';
|
|
7
|
+
import { createLogTransport } from './transports/log.js';
|
|
8
|
+
import { createResendTransport } from './transports/resend.js';
|
|
9
|
+
import { createSmtpTransport } from './transports/smtp.js';
|
|
10
|
+
import { normalizeRecipient, normalizeRecipients, stripHtml, validateRecipients } from './utils.js';
|
|
11
|
+
/**
|
|
12
|
+
* Create a mail manager.
|
|
13
|
+
*
|
|
14
|
+
* @param options - Mail plugin options
|
|
15
|
+
* @returns Mail manager instance
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* // Development (log to console)
|
|
20
|
+
* const mail = await createMailManager({ driver: 'log' });
|
|
21
|
+
*
|
|
22
|
+
* // Production (SMTP)
|
|
23
|
+
* const mail = await createMailManager({
|
|
24
|
+
* driver: 'smtp',
|
|
25
|
+
* config: {
|
|
26
|
+
* host: 'smtp.example.com',
|
|
27
|
+
* port: 587,
|
|
28
|
+
* auth: { user: '...', pass: '...' },
|
|
29
|
+
* },
|
|
30
|
+
* from: { email: 'hello@example.com', name: 'My App' },
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* // Production (Resend)
|
|
34
|
+
* const mail = await createMailManager({
|
|
35
|
+
* driver: 'resend',
|
|
36
|
+
* config: { apiKey: process.env.RESEND_API_KEY! },
|
|
37
|
+
* from: { email: 'hello@example.com', name: 'My App' },
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* // Send email
|
|
41
|
+
* await mail.send(WelcomeEmail, {
|
|
42
|
+
* to: 'user@example.com',
|
|
43
|
+
* data: { user: { name: 'John' }, activationUrl: 'https://...' },
|
|
44
|
+
* });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export async function createMailManager(options = {}) {
|
|
48
|
+
const defaultFrom = options.from ? normalizeRecipient(options.from) : undefined;
|
|
49
|
+
const defaultReplyTo = options.replyTo ? normalizeRecipient(options.replyTo) : undefined;
|
|
50
|
+
let transport;
|
|
51
|
+
// Create the appropriate transport with type-safe config narrowing
|
|
52
|
+
if (options.driver === 'smtp') {
|
|
53
|
+
// Type narrows: options.config is SmtpConfig (required)
|
|
54
|
+
transport = await createSmtpTransport(options.config);
|
|
55
|
+
}
|
|
56
|
+
else if (options.driver === 'resend') {
|
|
57
|
+
// Type narrows: options.config is ResendConfig (required)
|
|
58
|
+
transport = await createResendTransport(options.config);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// Type narrows: options.config is LogConfig | undefined (driver is 'log' or undefined)
|
|
62
|
+
transport = createLogTransport(options.config);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Render a mail template to HTML.
|
|
66
|
+
*/
|
|
67
|
+
async function renderMail(mail, sendOptions) {
|
|
68
|
+
// Validate data against schema
|
|
69
|
+
const validatedData = mail.schema.parse(sendOptions.data);
|
|
70
|
+
// Validate recipients
|
|
71
|
+
validateRecipients(sendOptions.to);
|
|
72
|
+
// Determine from address
|
|
73
|
+
const from = sendOptions.from
|
|
74
|
+
? normalizeRecipient(sendOptions.from)
|
|
75
|
+
: mail.from
|
|
76
|
+
? normalizeRecipient(mail.from)
|
|
77
|
+
: defaultFrom;
|
|
78
|
+
if (!from) {
|
|
79
|
+
throw new Error('From address is required. Provide it in send options, mail definition, or plugin config.');
|
|
80
|
+
}
|
|
81
|
+
// Normalize recipients
|
|
82
|
+
const to = normalizeRecipients(sendOptions.to);
|
|
83
|
+
const cc = sendOptions.cc ? normalizeRecipients(sendOptions.cc) : undefined;
|
|
84
|
+
const bcc = sendOptions.bcc ? normalizeRecipients(sendOptions.bcc) : undefined;
|
|
85
|
+
const replyTo = sendOptions.replyTo ? normalizeRecipient(sendOptions.replyTo) : defaultReplyTo;
|
|
86
|
+
// Generate subject
|
|
87
|
+
const subject = typeof mail.subject === 'function' ? mail.subject(validatedData) : mail.subject;
|
|
88
|
+
// Render React Email template to HTML
|
|
89
|
+
const html = await render(mail.template(validatedData));
|
|
90
|
+
// Generate plain text (from custom function or by stripping HTML)
|
|
91
|
+
const text = mail.text ? mail.text(validatedData) : stripHtml(html);
|
|
92
|
+
return {
|
|
93
|
+
from,
|
|
94
|
+
to,
|
|
95
|
+
cc,
|
|
96
|
+
bcc,
|
|
97
|
+
replyTo,
|
|
98
|
+
subject,
|
|
99
|
+
html,
|
|
100
|
+
text,
|
|
101
|
+
attachments: sendOptions.attachments,
|
|
102
|
+
headers: sendOptions.headers,
|
|
103
|
+
tags: sendOptions.tags,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const manager = {
|
|
107
|
+
async send(mail, sendOptions) {
|
|
108
|
+
const rendered = await renderMail(mail, sendOptions);
|
|
109
|
+
return transport.send({
|
|
110
|
+
from: rendered.from,
|
|
111
|
+
to: rendered.to,
|
|
112
|
+
cc: rendered.cc,
|
|
113
|
+
bcc: rendered.bcc,
|
|
114
|
+
replyTo: rendered.replyTo,
|
|
115
|
+
subject: rendered.subject,
|
|
116
|
+
html: rendered.html,
|
|
117
|
+
text: rendered.text,
|
|
118
|
+
attachments: rendered.attachments,
|
|
119
|
+
headers: rendered.headers,
|
|
120
|
+
tags: rendered.tags,
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
async sendBulk(mail, messages, bulkOptions) {
|
|
124
|
+
if (messages.length === 0) {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
// Use concurrency control to avoid overwhelming the transport
|
|
128
|
+
const concurrency = bulkOptions?.concurrency ?? 10;
|
|
129
|
+
// Process in batches for controlled parallelism
|
|
130
|
+
const results = new Array(messages.length);
|
|
131
|
+
for (let i = 0; i < messages.length; i += concurrency) {
|
|
132
|
+
const batch = messages.slice(i, i + concurrency);
|
|
133
|
+
const batchResults = await Promise.all(batch.map((message) => manager.send(mail, message)));
|
|
134
|
+
// Place results at correct indices to maintain order
|
|
135
|
+
for (let j = 0; j < batchResults.length; j++) {
|
|
136
|
+
results[i + j] = batchResults[j];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return results;
|
|
140
|
+
},
|
|
141
|
+
async render(mail, sendOptions) {
|
|
142
|
+
return renderMail(mail, sendOptions);
|
|
143
|
+
},
|
|
144
|
+
async close() {
|
|
145
|
+
await transport.close();
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
return manager;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Alias for createMailManager.
|
|
152
|
+
*/
|
|
153
|
+
export const mailer = createMailManager;
|