@alis-kit/mailer
v0.1.0
Published
Email delivery abstraction with template engine and TC39 native decorators
Readme
Mailer Kit
Email delivery abstraction with a template engine and TC39 native decorators (Stage 3).
Features
- TC39 Native Decorators — Uses stage 3 decorators with
Symbol.metadata, noreflect-metadataneeded - Template Engine — Dynamic
${variable}and${nested.variable}interpolation - Provider Agnostic — Switch between Nodemailer, Resend, or custom providers
- Async Queues — Send emails synchronously or queue them for background delivery
- Type Safe — Full TypeScript support with strict mode
Requirements
- Node.js >= 18.0.0
- TypeScript >= 5.5.0
Installation
npm install @alis-kit/mailerQuick Start
1. Setup
import { MailerKit } from "@alis-kit/mailer"
MailerKit.setup({
provider: "nodemailer",
config: {
host: "smtp.gmail.com",
port: 587,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
},
from: "[email protected]"
})2. Define a Template
import { EmailTemplate } from "@alis-kit/mailer"
/**
* Welcome email sent to new users after registration.
*
* @example
* ```ts
* await MailerKit.send(WelcomeEmail, {
* to: "[email protected]",
* name: "John"
* })
* ```
*/
@EmailTemplate("welcome", {
template: "./templates/welcome.html",
subject: "Welcome to Our Platform!"
})
export class WelcomeEmail {
to!: string
name!: string
}3. Create the HTML Template
<!-- templates/welcome.html -->
<h1>Welcome, ${name}!</h1>
<p>We're excited to have you on board.</p>4. Send
await MailerKit.send(WelcomeEmail, {
to: "[email protected]",
name: "John"
})API Reference
MailerKit.setup(config)
Initialize the mailer service. Must be called before sending.
MailerKit.setup({
provider: "nodemailer",
config: {
host: "smtp.example.com",
port: 587,
auth: { user: "...", pass: "..." }
},
from: "[email protected]",
queue: {
engine: "memory" // or "bullmq"
}
})| Option | Type | Required | Description |
|--------|------|----------|-------------|
| provider | "nodemailer" | Yes | Email provider to use |
| config | object | Yes | Provider-specific configuration |
| from | string | Yes | Default sender email address |
| queue.engine | "memory" \| "bullmq" | No | Queue engine for async delivery |
@EmailTemplate(name, options)
Class decorator that links a class to an HTML template file. Uses TC39 stage 3 native decorators.
@EmailTemplate("notification", {
template: "./templates/notification.html",
subject: "You have a new notification"
})
export class NotificationEmail {
to!: string
title!: string
message!: string
}| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| name | string | Yes | Unique identifier for the template |
| options.template | string | Yes | Path to the HTML template file |
| options.subject | string | No | Default subject line |
MailerKit.send(TemplateClass, data)
Send an email immediately.
await MailerKit.send(NotificationEmail, {
to: "[email protected]",
title: "New Message",
message: "You have a new notification!"
})| Parameter | Type | Description |
|-----------|------|-------------|
| TemplateClass | Class | A class decorated with @EmailTemplate |
| data | object | Email data including to, template variables, and optional overrides |
Data properties:
| Property | Type | Description |
|----------|------|-------------|
| to | string \| string[] | Recipient(s) — required |
| subject | string | Override default subject |
| cc | string \| string[] | CC recipients |
| bcc | string \| string[] | BCC recipients |
| replyTo | string | Reply-to address |
| attachments | Attachment[] | File attachments (pdf, doc, docx, xls, xlsx, jpg, jpeg, png, zip) |
MailerKit.queue(TemplateClass, data, options?)
Queue an email for asynchronous delivery.
await MailerKit.queue(WelcomeEmail, {
to: "[email protected]",
name: "John"
}, { delay: "5s" })| Option | Type | Description |
|--------|------|-------------|
| delay | string | Delay before sending (e.g., "5s", "2m", "1h") |
| priority | number | Priority level (for BullMQ) |
TemplateEngine.render(templatePath, variables)
Render an HTML template with variable interpolation.
import { TemplateEngine } from "@alis-kit/mailer"
const html = await TemplateEngine.render("./template.html", {
name: "John",
user: { email: "[email protected]" }
})Supported syntax:
| Syntax | Example | Description |
|--------|---------|-------------|
| ${var} | ${name} | Simple variable |
| ${nested} | ${user.email} | Dot-notation for nested objects |
| ${array} | ${items} | Arrays are JSON-stringified |
MailerKit.getTemplateMetadata(TemplateClass)
Retrieve the metadata attached to a decorated class.
const metadata = MailerKit.getTemplateMetadata(WelcomeEmail)
// { name: "welcome", template: "./templates/welcome.html", subject: "Welcome!" }Template Variables
Properties defined in your decorated class act as template variables:
@EmailTemplate("invoice", {
template: "./templates/invoice.html",
subject: "Your Invoice"
})
export class InvoiceEmail {
to!: string
invoiceNumber!: string
total!: number
items!: Array<{ name: string; price: number }>
}<!-- templates/invoice.html -->
<h1>Invoice #${invoiceNumber}</h1>
<p>Total: $${total}</p>
<ul>
${items}
</ul>Error Handling
| Error | Cause |
|-------|-------|
| MailerKit not initialized. Call setup() first. | send() or queue() called before setup() |
| Unsupported mail provider: <name> | Invalid provider in setup() config |
| Class <Name> is not a valid @EmailTemplate | Class not decorated with @EmailTemplate |
| Class <Name> is already decorated with @EmailTemplate | Duplicate @EmailTemplate on same class |
| Unsupported attachment type: .<ext> | Attachment file extension not in allowed list |
| Template file not found at: <path> | HTML template file does not exist |
| Queue not configured | queue() called without queue in config |
| BullMQ integration not implemented | Using "bullmq" engine (not yet supported) |
Testing
# Run all tests
npm test
# Run tests in watch mode
npm run test:watchArchitecture
This project follows the Functional Core + Decorator Sugar pattern:
core/— Pure functions and classes with all business logicdecorators/— Thin wrappers that store metadata viaSymbol.metadataproviders/— Email provider adapters (Nodemailer, Resend, etc.)template/— Template engine and path resolution
Decorators never contain business logic — they only attach metadata that core/ reads.
License
ISC
