@kumix/email 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +395 -0
- package/dist/components.d.ts +3 -0
- package/dist/components.js +11 -0
- package/dist/components.js.map +1 -0
- package/dist/helpers.d.ts +325 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +447 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +401 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +454 -0
- package/dist/index.js.map +1 -0
- package/package.json +77 -0
package/README.md
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# @kumix/email
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@kumix/email)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
A flexible email package for SaaS applications. Supports Resend and Nodemailer/SMTP, React
|
|
7
|
+
Email templates, and all JavaScript runtimes.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Node.js / Bun
|
|
13
|
+
bun add @kumix/email resend
|
|
14
|
+
# or
|
|
15
|
+
npm install @kumix/email resend
|
|
16
|
+
|
|
17
|
+
# Optional: install nodemailer for SMTP (Node.js / Bun / Deno only)
|
|
18
|
+
bun add nodemailer
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Configuration by Runtime
|
|
22
|
+
|
|
23
|
+
### Node.js / Bun
|
|
24
|
+
|
|
25
|
+
Set environment variables and call `createEmail()` — it auto-detects your provider.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# .env
|
|
29
|
+
KUMIX_EMAIL_RESEND_API_KEY=re_xxxx
|
|
30
|
+
KUMIX_EMAIL_FROM_NAME=My App
|
|
31
|
+
KUMIX_EMAIL_FROM_EMAIL=noreply@myapp.com
|
|
32
|
+
KUMIX_EMAIL_REPLY_TO=support@myapp.com
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { createEmail } from "@kumix/email";
|
|
37
|
+
|
|
38
|
+
// Reads process.env automatically
|
|
39
|
+
const email = createEmail();
|
|
40
|
+
|
|
41
|
+
await email.sendEmail({
|
|
42
|
+
to: "user@example.com",
|
|
43
|
+
subject: "Welcome!",
|
|
44
|
+
html: "<h1>Hello World</h1>",
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Bun works identically to Node.js — `process.env` is natively supported.
|
|
49
|
+
|
|
50
|
+
### Cloudflare Workers
|
|
51
|
+
|
|
52
|
+
Pass `ctx.env` as the second argument. Resend only (Nodemailer is Node-only).
|
|
53
|
+
|
|
54
|
+
```toml
|
|
55
|
+
# wrangler.toml
|
|
56
|
+
[vars]
|
|
57
|
+
KUMIX_EMAIL_RESEND_API_KEY = "re_xxxx"
|
|
58
|
+
KUMIX_EMAIL_FROM_NAME = "My App"
|
|
59
|
+
KUMIX_EMAIL_FROM_EMAIL = "noreply@myapp.com"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// worker.ts
|
|
64
|
+
import { createEmail, type EnvRecord } from "@kumix/email";
|
|
65
|
+
|
|
66
|
+
interface Env extends EnvRecord {
|
|
67
|
+
KUMIX_EMAIL_RESEND_API_KEY: string;
|
|
68
|
+
KUMIX_EMAIL_FROM_NAME: string;
|
|
69
|
+
KUMIX_EMAIL_FROM_EMAIL: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export default {
|
|
73
|
+
async fetch(req: Request, env: Env) {
|
|
74
|
+
const email = createEmail(undefined, env);
|
|
75
|
+
|
|
76
|
+
await email.sendEmail({
|
|
77
|
+
to: "user@example.com",
|
|
78
|
+
subject: "Hello from Workers!",
|
|
79
|
+
html: "<p>Sent from Cloudflare Workers</p>",
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return new Response("Email sent");
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
With React templates in Workers:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { createEmail, type EnvRecord } from "@kumix/email";
|
|
91
|
+
|
|
92
|
+
interface WelcomeProps {
|
|
93
|
+
userName: string;
|
|
94
|
+
loginUrl: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const WelcomeEmail: React.FC<WelcomeProps> = ({ userName, loginUrl }) => (
|
|
98
|
+
<div>
|
|
99
|
+
<h1>Welcome, {userName}!</h1>
|
|
100
|
+
<a href={loginUrl}>Login here</a>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
export default {
|
|
105
|
+
async fetch(req: Request, env: Env) {
|
|
106
|
+
const email = createEmail(undefined, env);
|
|
107
|
+
|
|
108
|
+
await email.sendTemplate(
|
|
109
|
+
WelcomeEmail,
|
|
110
|
+
{ userName: "Alice", loginUrl: "https://app.example.com" },
|
|
111
|
+
{ to: "alice@example.com", subject: "Welcome!" },
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
return new Response("Template email sent");
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Deno
|
|
120
|
+
|
|
121
|
+
Deno supports `process.env` natively (requires `--allow-env`). You can also pass env explicitly.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# .env
|
|
125
|
+
KUMIX_EMAIL_RESEND_API_KEY=re_xxxx
|
|
126
|
+
KUMIX_EMAIL_FROM_NAME=My App
|
|
127
|
+
KUMIX_EMAIL_FROM_EMAIL=noreply@myapp.com
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
// main.ts
|
|
132
|
+
import { createEmail } from "@kumix/email";
|
|
133
|
+
|
|
134
|
+
// Option A: let the package read process.env (Deno supports it)
|
|
135
|
+
const email = createEmail();
|
|
136
|
+
|
|
137
|
+
// Option B: pass env explicitly
|
|
138
|
+
const email = createEmail(undefined, Deno.env.toObject());
|
|
139
|
+
|
|
140
|
+
await email.sendEmail({
|
|
141
|
+
to: "user@example.com",
|
|
142
|
+
subject: "Hello from Deno!",
|
|
143
|
+
html: "<h1>Deno works</h1>",
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
deno run --allow-env --allow-net main.ts
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
With Nodemailer (SMTP) in Deno:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { createNodemailer } from "@kumix/email";
|
|
155
|
+
|
|
156
|
+
const email = createNodemailer({
|
|
157
|
+
from: { name: "My App", email: "noreply@myapp.com" },
|
|
158
|
+
smtp: {
|
|
159
|
+
host: "smtp.gmail.com",
|
|
160
|
+
port: 587,
|
|
161
|
+
secure: false,
|
|
162
|
+
auth: { user: "you@gmail.com", pass: "app-password" },
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
await email.sendEmail({
|
|
167
|
+
to: "user@example.com",
|
|
168
|
+
subject: "SMTP from Deno",
|
|
169
|
+
html: "<p>Sent via SMTP</p>",
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Browser
|
|
174
|
+
|
|
175
|
+
Use manual config — no env vars. Resend only, since Nodemailer requires Node.js APIs.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
import { createResend } from "@kumix/email";
|
|
179
|
+
|
|
180
|
+
const email = createResend({
|
|
181
|
+
apiKey: "re_xxxx",
|
|
182
|
+
from: { name: "My App", email: "noreply@myapp.com" },
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
await email.sendEmail({
|
|
186
|
+
to: "user@example.com",
|
|
187
|
+
subject: "Hello from the browser!",
|
|
188
|
+
html: "<p>Sent from client-side JavaScript</p>",
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
> **Security note**: Exposing your Resend API key in browser code is insecure. Use this
|
|
193
|
+
> pattern behind an authenticated route in an admin dashboard, or proxy through your backend.
|
|
194
|
+
|
|
195
|
+
### Manual Configuration (any runtime)
|
|
196
|
+
|
|
197
|
+
Skip env vars entirely — pass your config object directly. Works in every runtime.
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import { createResend, createNodemailer } from "@kumix/email";
|
|
201
|
+
|
|
202
|
+
// Resend — works everywhere
|
|
203
|
+
const resend = createResend({
|
|
204
|
+
apiKey: "re_xxxx",
|
|
205
|
+
from: { name: "My App", email: "noreply@myapp.com" },
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Nodemailer — Node.js / Bun / Deno only
|
|
209
|
+
const nodemailer = createNodemailer({
|
|
210
|
+
from: { name: "My App", email: "noreply@myapp.com" },
|
|
211
|
+
smtp: {
|
|
212
|
+
host: "smtp.gmail.com",
|
|
213
|
+
port: 587,
|
|
214
|
+
secure: false,
|
|
215
|
+
auth: { user: "you@gmail.com", pass: "app-password" },
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## `EnvRecord` Pattern
|
|
221
|
+
|
|
222
|
+
All config and factory functions accept an optional `env` parameter of type
|
|
223
|
+
`Record<string, string | undefined>`. On Node.js / Bun it defaults to
|
|
224
|
+
`process.env`. On other runtimes, pass your environment explicitly:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
import {
|
|
228
|
+
createEmail,
|
|
229
|
+
createResend,
|
|
230
|
+
hasEmailConfig,
|
|
231
|
+
loadEmailConfig,
|
|
232
|
+
validateEmailEnvVars,
|
|
233
|
+
type EnvRecord,
|
|
234
|
+
} from "@kumix/email";
|
|
235
|
+
|
|
236
|
+
// All accept env as the last argument:
|
|
237
|
+
const email = createEmail(undefined, myEnv);
|
|
238
|
+
const config = loadEmailConfig(myEnv);
|
|
239
|
+
const ready = hasEmailConfig(myEnv);
|
|
240
|
+
const result = validateEmailEnvVars(myEnv);
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Sending Emails
|
|
244
|
+
|
|
245
|
+
### HTML Emails
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
await email.sendEmail({
|
|
249
|
+
to: "user@example.com",
|
|
250
|
+
subject: "Hello!",
|
|
251
|
+
html: "<p>This is an HTML email</p>",
|
|
252
|
+
text: "This is the plain text version",
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### React Templates
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { EmailTemplate } from "./templates/EmailTemplate";
|
|
260
|
+
|
|
261
|
+
await email.sendTemplate(
|
|
262
|
+
EmailTemplate,
|
|
263
|
+
{ userName: "John", resetLink: "https://..." },
|
|
264
|
+
{ to: "user@example.com", subject: "Password Reset" },
|
|
265
|
+
);
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Advanced Options
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
await email.sendEmail({
|
|
272
|
+
to: ["user1@example.com", "user2@example.com"],
|
|
273
|
+
cc: "manager@example.com",
|
|
274
|
+
bcc: "admin@example.com",
|
|
275
|
+
subject: "Important Update",
|
|
276
|
+
html: "<h1>Update</h1>",
|
|
277
|
+
attachments: [
|
|
278
|
+
{
|
|
279
|
+
filename: "document.pdf",
|
|
280
|
+
content: pdfBuffer,
|
|
281
|
+
contentType: "application/pdf",
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
headers: { "X-Custom-Header": "value" },
|
|
285
|
+
tags: { category: "notification" },
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Runtime Compatibility
|
|
290
|
+
|
|
291
|
+
| Feature | Node.js | Bun | CF Workers | Deno | Browser |
|
|
292
|
+
| ------------------- | ------- | --- | ---------- | ---- | ------- |
|
|
293
|
+
| Resend provider | Yes | Yes | Yes | Yes | Yes |
|
|
294
|
+
| Nodemailer/SMTP | Yes | Yes | No | Yes | No |
|
|
295
|
+
| `createEmail()` | Yes | Yes | Yes\* | Yes | No |
|
|
296
|
+
| Manual config | Yes | Yes | Yes | Yes | Yes |
|
|
297
|
+
| React templates | Yes | Yes | Yes | Yes | Yes |
|
|
298
|
+
| Env auto-detection | Yes | Yes | No† | Yes | No |
|
|
299
|
+
| `helpers` subpath | Yes | Yes | Yes | Yes | Yes |
|
|
300
|
+
| `components` export | Yes | Yes | Yes | Yes | Yes |
|
|
301
|
+
|
|
302
|
+
\* Pass env as second argument: `createEmail(undefined, ctx.env)`.
|
|
303
|
+
† Pass `env` explicitly via `EnvRecord` pattern.
|
|
304
|
+
|
|
305
|
+
## API Reference
|
|
306
|
+
|
|
307
|
+
### Factory Functions
|
|
308
|
+
|
|
309
|
+
- `createEmail(config?, env?)` — Create from config or env, auto-detects provider
|
|
310
|
+
- `createResend(config?, env?)` — Create Resend email service
|
|
311
|
+
- `createNodemailer(config?, env?)` — Create Nodemailer email service
|
|
312
|
+
- `isEmailConfigured(env?)` — Check if email is configured via env
|
|
313
|
+
- `getConfiguredProvider(env?)` — Get the detected provider from env
|
|
314
|
+
|
|
315
|
+
### EmailService Methods
|
|
316
|
+
|
|
317
|
+
- `sendEmail(options)` — Send HTML/text email
|
|
318
|
+
- `sendTemplate(component, props, options)` — Send React template email
|
|
319
|
+
- `getConfig()` — Get current configuration
|
|
320
|
+
- `updateConfig(config)` — Update configuration at runtime
|
|
321
|
+
- `validateConfig()` — Validate current configuration
|
|
322
|
+
|
|
323
|
+
### Config Helpers
|
|
324
|
+
|
|
325
|
+
- `loadEmailConfig(env?)` — Auto-detect and load config from env
|
|
326
|
+
- `loadResendConfig(env?)` — Load Resend config from env
|
|
327
|
+
- `loadNodemailerConfig(env?)` — Load SMTP config from env
|
|
328
|
+
- `validateEmailEnvVars(env?)` — Validate any configured provider
|
|
329
|
+
- `validateResendEnvVars(env?)` — Validate Resend env vars
|
|
330
|
+
- `validateNodemailerEnvVars(env?)` — Validate SMTP env vars
|
|
331
|
+
- `getEmailEnvVars(env?)` — Get env vars with secrets masked
|
|
332
|
+
- `hasEmailConfig(env?)` — Check if any provider is configured
|
|
333
|
+
|
|
334
|
+
### Helpers (`@kumix/email/helpers`)
|
|
335
|
+
|
|
336
|
+
- `renderEmailTemplate(Component, props)` — Render React component to HTML string
|
|
337
|
+
- `htmlToText(html)` — Convert HTML to plain text
|
|
338
|
+
- `isValidEmail(email)` — Validate email format (RFC 5322)
|
|
339
|
+
- `validateEmails(emails)` — Validate single or multiple emails
|
|
340
|
+
- `filterValidEmails(emails)` — Filter invalid emails from a list
|
|
341
|
+
- `formatEmailAddress(name, email)` — Format as `Name <email>`
|
|
342
|
+
- `extractEmail(formatted)` — Extract email from formatted address
|
|
343
|
+
- `extractDisplayName(formatted)` — Extract display name from formatted address
|
|
344
|
+
- `sanitizeHtml(html)` — Strip dangerous HTML (scripts, event handlers)
|
|
345
|
+
- `truncateText(text, maxLength, ellipsis?)` — Truncate with ellipsis
|
|
346
|
+
- `generatePreviewText(html, maxLength?)` — Generate email preview text
|
|
347
|
+
- `generateUnsubscribeLink(baseUrl, email, token?)` — Create unsubscribe URL
|
|
348
|
+
- `generateTrackingPixel(baseUrl, emailId, recipientId)` — Create tracking pixel URL
|
|
349
|
+
- `addUtmParams(url, params)` — Add UTM tracking parameters
|
|
350
|
+
- `parseEmailList(str)` — Parse comma/semicolon-separated emails
|
|
351
|
+
- `deduplicateEmails(emails)` — Deduplicate (case-insensitive)
|
|
352
|
+
- `chunkEmails(emails, chunkSize?)` — Split into batches (default 100)
|
|
353
|
+
- `getMimeType(filename)` — Get MIME type from extension
|
|
354
|
+
- `formatFileSize(bytes, decimals?)` — Format file size in human-readable form
|
|
355
|
+
|
|
356
|
+
### Types
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
import type {
|
|
360
|
+
EmailConfig,
|
|
361
|
+
ResendConfig,
|
|
362
|
+
NodemailerConfig,
|
|
363
|
+
SendEmailOptions,
|
|
364
|
+
EmailResult,
|
|
365
|
+
EmailProvider,
|
|
366
|
+
EnvRecord,
|
|
367
|
+
// ... and more
|
|
368
|
+
} from "@kumix/email";
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Environment Variables
|
|
372
|
+
|
|
373
|
+
| Variable | Provider | Required |
|
|
374
|
+
| ---------------------------- | ---------- | -------- |
|
|
375
|
+
| `KUMIX_EMAIL_RESEND_API_KEY` | Resend | Yes |
|
|
376
|
+
| `KUMIX_EMAIL_FROM_NAME` | All | Yes |
|
|
377
|
+
| `KUMIX_EMAIL_FROM_EMAIL` | All | Yes |
|
|
378
|
+
| `KUMIX_EMAIL_REPLY_TO` | All | No |
|
|
379
|
+
| `KUMIX_EMAIL_SMTP_HOST` | Nodemailer | Yes |
|
|
380
|
+
| `KUMIX_EMAIL_SMTP_PORT` | Nodemailer | Yes |
|
|
381
|
+
| `KUMIX_EMAIL_SMTP_SECURE` | Nodemailer | No |
|
|
382
|
+
| `KUMIX_EMAIL_SMTP_USER` | Nodemailer | Yes |
|
|
383
|
+
| `KUMIX_EMAIL_SMTP_PASS` | Nodemailer | Yes |
|
|
384
|
+
|
|
385
|
+
Legacy env vars (`RESEND_API_KEY`, `SMTP_HOST`, etc.) are also supported for backward compatibility.
|
|
386
|
+
|
|
387
|
+
## Links
|
|
388
|
+
|
|
389
|
+
- [npm Package](https://www.npmjs.com/package/@kumix/email)
|
|
390
|
+
- [Contributing Guide](../../CONTRIBUTING.md)
|
|
391
|
+
- [License](../../LICENSE)
|
|
392
|
+
|
|
393
|
+
## License
|
|
394
|
+
|
|
395
|
+
MIT © [Kumix Labs](../../LICENSE)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as ReactEmailComponents from "react-email";
|
|
2
|
+
export * from "react-email";
|
|
3
|
+
//#region src/components.ts
|
|
4
|
+
/**
|
|
5
|
+
* React Email components
|
|
6
|
+
* Re-exports all React Email components for building email templates
|
|
7
|
+
*/
|
|
8
|
+
//#endregion
|
|
9
|
+
export { ReactEmailComponents };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=components.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components.js","names":[],"sources":["../src/components.ts"],"sourcesContent":["/**\n * React Email components\n * Re-exports all React Email components for building email templates\n */\n\n// Import all components as a namespace for convenience\nimport * as ReactEmailComponents from \"react-email\";\n\n/**\n * Re-export all React Email components for easy access\n * Available components include Html, Head, Body, Container, Section, Row, Column, Text, Heading, Link, Button, Img, Hr, Font, Preview and more\n * @public\n *\n * @see https://react.email/docs/components/html\n */\nexport * from \"react-email\";\n\n/**\n * Export the namespace as well for alternative import style\n * @public\n *\n * @example\n * ```typescript\n * import { ReactEmailComponents } from '@kumix/email/components';\n *\n * const MyEmail = () => (\n * <ReactEmailComponents.Html>\n * <ReactEmailComponents.Body>\n * <ReactEmailComponents.Text>Hello World</ReactEmailComponents.Text>\n * </ReactEmailComponents.Body>\n * </ReactEmailComponents.Html>\n * );\n * ```\n */\nexport { ReactEmailComponents };\n"],"mappings":""}
|