@geekmidas/emailkit 0.0.1
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 +423 -0
- package/dist/__tests__/client.spec.cjs +13218 -0
- package/dist/__tests__/client.spec.mjs +13222 -0
- package/dist/chunk-CUT6urMc.cjs +30 -0
- package/dist/client-7k6es7pg.cjs +68 -0
- package/dist/client-B7LjRjRM.mjs +56 -0
- package/dist/client.cjs +4 -0
- package/dist/client.mjs +3 -0
- package/dist/index.cjs +4 -0
- package/dist/index.mjs +3 -0
- package/dist/magic-string.es-BGRTPV9O.mjs +1014 -0
- package/dist/magic-string.es-BrLHy2bw.cjs +1015 -0
- package/dist/templates.cjs +176 -0
- package/dist/templates.mjs +171 -0
- package/dist/types.cjs +0 -0
- package/dist/types.mjs +0 -0
- package/package.json +39 -0
- package/src/__tests__/client.spec.tsx +182 -0
- package/src/client.ts +87 -0
- package/src/index.ts +11 -0
- package/src/templates.tsx +182 -0
- package/src/types.ts +94 -0
package/README.md
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
# @geekmidas/emailkit
|
|
2
|
+
|
|
3
|
+
Type-safe email client with SMTP support and React templates.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Type-Safe Templates**: Templates are provided at construction time with full TypeScript inference for both template names and their corresponding props
|
|
8
|
+
- **SMTP Support**: Works with any SMTP server via nodemailer configuration
|
|
9
|
+
- **React Templates**: Uses `react-dom/server` to render React components to HTML
|
|
10
|
+
- **Built-in Templates**: Includes common email templates (welcome, password reset, notifications)
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @geekmidas/emailkit
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { createEmailClient } from '@geekmidas/emailkit';
|
|
22
|
+
|
|
23
|
+
// Define your templates with props
|
|
24
|
+
const WelcomeEmail = ({ name, confirmationUrl }: {
|
|
25
|
+
name: string;
|
|
26
|
+
confirmationUrl?: string;
|
|
27
|
+
}) => (
|
|
28
|
+
<div style={{ fontFamily: 'Arial, sans-serif', maxWidth: '600px' }}>
|
|
29
|
+
<h1>Welcome, {name}!</h1>
|
|
30
|
+
<p>We're excited to have you on board.</p>
|
|
31
|
+
{confirmationUrl && (
|
|
32
|
+
<p>
|
|
33
|
+
<a href={confirmationUrl} style={{
|
|
34
|
+
backgroundColor: '#007bff',
|
|
35
|
+
color: 'white',
|
|
36
|
+
padding: '10px 20px',
|
|
37
|
+
textDecoration: 'none',
|
|
38
|
+
borderRadius: '4px'
|
|
39
|
+
}}>
|
|
40
|
+
Confirm Email
|
|
41
|
+
</a>
|
|
42
|
+
</p>
|
|
43
|
+
)}
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const templates = {
|
|
48
|
+
welcome: WelcomeEmail,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Create client with templates - types are fully inferred
|
|
52
|
+
const client = createEmailClient({
|
|
53
|
+
smtp: {
|
|
54
|
+
host: 'smtp.example.com',
|
|
55
|
+
port: 587,
|
|
56
|
+
auth: {
|
|
57
|
+
user: 'user@example.com',
|
|
58
|
+
pass: 'password',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
templates,
|
|
62
|
+
defaults: {
|
|
63
|
+
from: 'noreply@example.com',
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Send email with full type safety
|
|
68
|
+
await client.sendTemplate('welcome', {
|
|
69
|
+
from: 'welcome@example.com',
|
|
70
|
+
to: 'user@example.com',
|
|
71
|
+
subject: 'Welcome to our service!',
|
|
72
|
+
props: {
|
|
73
|
+
name: 'John Doe',
|
|
74
|
+
confirmationUrl: 'https://example.com/confirm/123',
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## API Reference
|
|
80
|
+
|
|
81
|
+
### `createEmailClient<T>(config: EmailClientConfig<T>): SMTPClient<T>`
|
|
82
|
+
|
|
83
|
+
Creates a new email client with type-safe template support.
|
|
84
|
+
|
|
85
|
+
#### Configuration
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
interface EmailClientConfig<T extends TemplateRecord> {
|
|
89
|
+
smtp: SMTPConfig;
|
|
90
|
+
templates: T;
|
|
91
|
+
defaults?: {
|
|
92
|
+
from?: string | Address;
|
|
93
|
+
replyTo?: string | Address;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface SMTPConfig {
|
|
98
|
+
host: string;
|
|
99
|
+
port: number;
|
|
100
|
+
secure?: boolean;
|
|
101
|
+
auth?: {
|
|
102
|
+
user: string;
|
|
103
|
+
pass: string;
|
|
104
|
+
};
|
|
105
|
+
tls?: {
|
|
106
|
+
rejectUnauthorized?: boolean;
|
|
107
|
+
servername?: string;
|
|
108
|
+
};
|
|
109
|
+
pool?: boolean;
|
|
110
|
+
maxConnections?: number;
|
|
111
|
+
maxMessages?: number;
|
|
112
|
+
rateLimit?: number;
|
|
113
|
+
logger?: boolean;
|
|
114
|
+
debug?: boolean;
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Client Methods
|
|
119
|
+
|
|
120
|
+
#### `send(options: PlainEmailOptions): Promise<SendResult>`
|
|
121
|
+
|
|
122
|
+
Send a plain text or HTML email.
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
await client.send({
|
|
126
|
+
from: 'info@example.com',
|
|
127
|
+
to: 'user@example.com',
|
|
128
|
+
subject: 'Plain email',
|
|
129
|
+
text: 'This is a plain text email',
|
|
130
|
+
html: '<p>This is an HTML email</p>',
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### `sendTemplate<K>(template: K, options): Promise<SendResult>`
|
|
135
|
+
|
|
136
|
+
Send an email using a React template with type-safe props.
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
await client.sendTemplate('welcome', {
|
|
140
|
+
from: 'welcome@example.com',
|
|
141
|
+
to: 'user@example.com',
|
|
142
|
+
subject: 'Welcome!',
|
|
143
|
+
props: { name: 'John', confirmationUrl: 'https://...' },
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### `verify(): Promise<boolean>`
|
|
148
|
+
|
|
149
|
+
Verify the SMTP connection.
|
|
150
|
+
|
|
151
|
+
#### `close(): Promise<void>`
|
|
152
|
+
|
|
153
|
+
Close the SMTP connection.
|
|
154
|
+
|
|
155
|
+
#### `getTemplateNames(): string[]`
|
|
156
|
+
|
|
157
|
+
Get available template names.
|
|
158
|
+
|
|
159
|
+
## Built-in Template Examples
|
|
160
|
+
|
|
161
|
+
### Welcome Email
|
|
162
|
+
|
|
163
|
+
```tsx
|
|
164
|
+
interface WelcomeEmailProps {
|
|
165
|
+
name: string;
|
|
166
|
+
confirmationUrl?: string;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const WelcomeEmail = ({ name, confirmationUrl }: WelcomeEmailProps) => (
|
|
170
|
+
<html>
|
|
171
|
+
<head>
|
|
172
|
+
<meta charSet="utf-8" />
|
|
173
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
174
|
+
<title>Welcome!</title>
|
|
175
|
+
</head>
|
|
176
|
+
<body style={{ margin: 0, padding: 0, backgroundColor: '#f4f4f4' }}>
|
|
177
|
+
<div style={{
|
|
178
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
179
|
+
lineHeight: 1.6,
|
|
180
|
+
color: '#333',
|
|
181
|
+
maxWidth: '600px',
|
|
182
|
+
margin: '0 auto',
|
|
183
|
+
padding: '20px',
|
|
184
|
+
}}>
|
|
185
|
+
<h1 style={{ color: '#2c3e50', marginBottom: '20px' }}>
|
|
186
|
+
Welcome, {name}!
|
|
187
|
+
</h1>
|
|
188
|
+
<p>
|
|
189
|
+
We're excited to have you on board. Your account has been successfully created.
|
|
190
|
+
</p>
|
|
191
|
+
{confirmationUrl && (
|
|
192
|
+
<>
|
|
193
|
+
<p>Please confirm your email address by clicking the button below:</p>
|
|
194
|
+
<p style={{ textAlign: 'center', margin: '30px 0' }}>
|
|
195
|
+
<a href={confirmationUrl} style={{
|
|
196
|
+
display: 'inline-block',
|
|
197
|
+
padding: '12px 24px',
|
|
198
|
+
backgroundColor: '#3498db',
|
|
199
|
+
color: '#ffffff',
|
|
200
|
+
textDecoration: 'none',
|
|
201
|
+
borderRadius: '4px',
|
|
202
|
+
fontWeight: 'bold',
|
|
203
|
+
}}>
|
|
204
|
+
Confirm Email
|
|
205
|
+
</a>
|
|
206
|
+
</p>
|
|
207
|
+
</>
|
|
208
|
+
)}
|
|
209
|
+
<div style={{
|
|
210
|
+
marginTop: '40px',
|
|
211
|
+
paddingTop: '20px',
|
|
212
|
+
borderTop: '1px solid #eee',
|
|
213
|
+
fontSize: '14px',
|
|
214
|
+
color: '#666',
|
|
215
|
+
}}>
|
|
216
|
+
<p>If you have any questions, feel free to reply to this email.</p>
|
|
217
|
+
<p>Best regards,<br />The Team</p>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
</body>
|
|
221
|
+
</html>
|
|
222
|
+
);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Password Reset Email
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
interface PasswordResetEmailProps {
|
|
229
|
+
name: string;
|
|
230
|
+
resetUrl: string;
|
|
231
|
+
expiresIn: string;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const PasswordResetEmail = ({ name, resetUrl, expiresIn }: PasswordResetEmailProps) => (
|
|
235
|
+
<html>
|
|
236
|
+
<head>
|
|
237
|
+
<meta charSet="utf-8" />
|
|
238
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
239
|
+
<title>Password Reset Request</title>
|
|
240
|
+
</head>
|
|
241
|
+
<body style={{ margin: 0, padding: 0, backgroundColor: '#f4f4f4' }}>
|
|
242
|
+
<div style={{
|
|
243
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
244
|
+
lineHeight: 1.6,
|
|
245
|
+
color: '#333',
|
|
246
|
+
maxWidth: '600px',
|
|
247
|
+
margin: '0 auto',
|
|
248
|
+
padding: '20px',
|
|
249
|
+
}}>
|
|
250
|
+
<h1 style={{ color: '#2c3e50', marginBottom: '20px' }}>
|
|
251
|
+
Password Reset Request
|
|
252
|
+
</h1>
|
|
253
|
+
<p>Hi {name},</p>
|
|
254
|
+
<p>
|
|
255
|
+
We received a request to reset your password. Click the button below to create a new password:
|
|
256
|
+
</p>
|
|
257
|
+
<p style={{ textAlign: 'center', margin: '30px 0' }}>
|
|
258
|
+
<a href={resetUrl} style={{
|
|
259
|
+
display: 'inline-block',
|
|
260
|
+
padding: '12px 24px',
|
|
261
|
+
backgroundColor: '#3498db',
|
|
262
|
+
color: '#ffffff',
|
|
263
|
+
textDecoration: 'none',
|
|
264
|
+
borderRadius: '4px',
|
|
265
|
+
fontWeight: 'bold',
|
|
266
|
+
}}>
|
|
267
|
+
Reset Password
|
|
268
|
+
</a>
|
|
269
|
+
</p>
|
|
270
|
+
<p>
|
|
271
|
+
This link will expire in {expiresIn}. If you didn't request a password reset,
|
|
272
|
+
you can safely ignore this email.
|
|
273
|
+
</p>
|
|
274
|
+
<div style={{
|
|
275
|
+
marginTop: '40px',
|
|
276
|
+
paddingTop: '20px',
|
|
277
|
+
borderTop: '1px solid #eee',
|
|
278
|
+
fontSize: '14px',
|
|
279
|
+
color: '#666',
|
|
280
|
+
}}>
|
|
281
|
+
<p>For security reasons, this link can only be used once.</p>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
</body>
|
|
285
|
+
</html>
|
|
286
|
+
);
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Notification Email
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
interface NotificationEmailProps {
|
|
293
|
+
name: string;
|
|
294
|
+
title: string;
|
|
295
|
+
message: string;
|
|
296
|
+
actionUrl?: string;
|
|
297
|
+
actionText?: string;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const NotificationEmail = ({
|
|
301
|
+
name,
|
|
302
|
+
title,
|
|
303
|
+
message,
|
|
304
|
+
actionUrl,
|
|
305
|
+
actionText = 'View Details'
|
|
306
|
+
}: NotificationEmailProps) => (
|
|
307
|
+
<html>
|
|
308
|
+
<head>
|
|
309
|
+
<meta charSet="utf-8" />
|
|
310
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
311
|
+
<title>{title}</title>
|
|
312
|
+
</head>
|
|
313
|
+
<body style={{ margin: 0, padding: 0, backgroundColor: '#f4f4f4' }}>
|
|
314
|
+
<div style={{
|
|
315
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
316
|
+
lineHeight: 1.6,
|
|
317
|
+
color: '#333',
|
|
318
|
+
maxWidth: '600px',
|
|
319
|
+
margin: '0 auto',
|
|
320
|
+
padding: '20px',
|
|
321
|
+
}}>
|
|
322
|
+
<h1 style={{ color: '#2c3e50', marginBottom: '20px' }}>
|
|
323
|
+
{title}
|
|
324
|
+
</h1>
|
|
325
|
+
<p>Hi {name},</p>
|
|
326
|
+
<p>{message}</p>
|
|
327
|
+
{actionUrl && (
|
|
328
|
+
<p style={{ textAlign: 'center', margin: '30px 0' }}>
|
|
329
|
+
<a href={actionUrl} style={{
|
|
330
|
+
display: 'inline-block',
|
|
331
|
+
padding: '12px 24px',
|
|
332
|
+
backgroundColor: '#3498db',
|
|
333
|
+
color: '#ffffff',
|
|
334
|
+
textDecoration: 'none',
|
|
335
|
+
borderRadius: '4px',
|
|
336
|
+
fontWeight: 'bold',
|
|
337
|
+
}}>
|
|
338
|
+
{actionText}
|
|
339
|
+
</a>
|
|
340
|
+
</p>
|
|
341
|
+
)}
|
|
342
|
+
<div style={{
|
|
343
|
+
marginTop: '40px',
|
|
344
|
+
paddingTop: '20px',
|
|
345
|
+
borderTop: '1px solid #eee',
|
|
346
|
+
fontSize: '14px',
|
|
347
|
+
color: '#666',
|
|
348
|
+
}}>
|
|
349
|
+
<p>This is an automated notification from our system.</p>
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
352
|
+
</body>
|
|
353
|
+
</html>
|
|
354
|
+
);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Multiple Templates Example
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
import { createEmailClient } from '@geekmidas/emailkit';
|
|
361
|
+
|
|
362
|
+
const templates = {
|
|
363
|
+
welcome: WelcomeEmail,
|
|
364
|
+
passwordReset: PasswordResetEmail,
|
|
365
|
+
notification: NotificationEmail,
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
const client = createEmailClient({
|
|
369
|
+
smtp: {
|
|
370
|
+
host: 'smtp.example.com',
|
|
371
|
+
port: 587,
|
|
372
|
+
auth: {
|
|
373
|
+
user: 'user@example.com',
|
|
374
|
+
pass: 'password',
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
templates,
|
|
378
|
+
defaults: {
|
|
379
|
+
from: 'noreply@example.com',
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// All template names and props are fully type-safe
|
|
384
|
+
await client.sendTemplate('welcome', {
|
|
385
|
+
from: 'welcome@example.com',
|
|
386
|
+
to: 'user@example.com',
|
|
387
|
+
subject: 'Welcome to our service!',
|
|
388
|
+
props: { name: 'John Doe', confirmationUrl: 'https://example.com/confirm/123' },
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
await client.sendTemplate('passwordReset', {
|
|
392
|
+
from: 'security@example.com',
|
|
393
|
+
to: 'user@example.com',
|
|
394
|
+
subject: 'Reset your password',
|
|
395
|
+
props: { name: 'John Doe', resetUrl: 'https://example.com/reset/456', expiresIn: '24 hours' },
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
await client.sendTemplate('notification', {
|
|
399
|
+
from: 'notifications@example.com',
|
|
400
|
+
to: 'user@example.com',
|
|
401
|
+
subject: 'Important notification',
|
|
402
|
+
props: {
|
|
403
|
+
name: 'John Doe',
|
|
404
|
+
title: 'Account Update',
|
|
405
|
+
message: 'Your account settings have been updated.',
|
|
406
|
+
actionUrl: 'https://example.com/settings',
|
|
407
|
+
actionText: 'View Settings'
|
|
408
|
+
},
|
|
409
|
+
});
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## TypeScript Support
|
|
413
|
+
|
|
414
|
+
The library provides full TypeScript support with:
|
|
415
|
+
|
|
416
|
+
- **Template name inference** - Only valid template names are accepted
|
|
417
|
+
- **Props type checking** - Props are validated based on the template's prop types
|
|
418
|
+
- **Autocomplete support** - IDE autocomplete for template names and props
|
|
419
|
+
- **Compile-time safety** - Catch template and prop errors at build time
|
|
420
|
+
|
|
421
|
+
## License
|
|
422
|
+
|
|
423
|
+
MIT
|