Lockness Mail

Lockness Mail

VIEW

Email sending library with fluent API and multiple driver support.

Overview

@lockness/mail provides an expressive email builder with support for SMTP, Resend, Console (dev), and Memory (testing) drivers. Full email features including CC, BCC, Reply-To, attachments, and JSX templates.

Installation

typescript
import { configureMail, mail } from '@lockness/mail'

Configuration

typescript
configureMail({
    driver: 'smtp', // 'smtp' | 'resend' | 'console' | 'memory'
    from: { email: 'noreply@example.com', name: 'My App' },
    smtp: {
        host: 'smtp.example.com',
        port: 587,
        auth: { user: 'username', pass: 'password' },
    },
    resend: {
        apiKey: 'your-api-key',
    },
})

Basic Usage

Simple Email

typescript
await mail()
    .to('user@example.com')
    .subject('Welcome!')
    .html('<h1>Hello World!</h1>')
    .send()

Full Example

typescript
await mail()
    .from('sender@example.com', 'Sender Name')
    .to('recipient@example.com')
    .cc('cc@example.com')
    .bcc('bcc@example.com')
    .replyTo('reply@example.com')
    .subject('Test Email')
    .text('Plain text version')
    .html('<strong>HTML version</strong>')
    .attach('file.txt', 'File content')
    .send()

With JSX Templates

typescript
const EmailTemplate = ({ name }: { name: string }) => (
    <html>
        <body>
            <h1>Welcome, {name}!</h1>
            <p>Thanks for joining our service.</p>
        </body>
    </html>
)

await mail()
    .to('user@example.com')
    .subject('Welcome!')
    .view(<EmailTemplate name='John' />)
    .send()

Drivers

Console Driver (Development)

Prints emails to console instead of sending them.

typescript
configureMail({ driver: 'console' })

Memory Driver (Testing)

Stores emails in memory for testing.

typescript
import { MemoryMailDriver } from '@lockness/mail'

configureMail({ driver: 'memory' })

// Send email
await mail().to('test@example.com').subject('Test').send()

// Get sent emails
const emails = MemoryMailDriver.getSentEmails()
const lastEmail = MemoryMailDriver.getLastEmail()

// Clear
MemoryMailDriver.clear()

SMTP Driver

typescript
configureMail({
    driver: 'smtp',
    smtp: {
        host: 'smtp.gmail.com',
        port: 587,
        auth: {
            user: 'your-email@gmail.com',
            pass: 'your-password',
        },
    },
})

Resend Driver

typescript
configureMail({
    driver: 'resend',
    resend: {
        apiKey: Deno.env.get('RESEND_API_KEY'),
    },
})

API Reference

Mail Builder Methods

  • from(email, name?) - Set sender
  • to(email, name?) - Set recipient (can call multiple times)
  • cc(email, name?) - Add CC recipient
  • bcc(email, name?) - Add BCC recipient
  • replyTo(email) - Set reply-to address
  • subject(subject) - Set email subject
  • text(content) - Set plain text content
  • html(content) - Set HTML content
  • view(jsx) - Set JSX template content
  • attach(filename, content) - Add attachment
  • send() - Send the email

Common Use Cases

Password Reset Email

typescript
import { mail } from '@lockness/mail'

async function sendPasswordReset(user: User, resetToken: string) {
    const resetUrl = `https://example.com/reset-password?token=${resetToken}`

    await mail()
        .to(user.email, user.name)
        .subject('Reset Your Password')
        .html(`
            <h1>Password Reset Request</h1>
            <p>Click the link below to reset your password:</p>
            <a href="${resetUrl}">Reset Password</a>
            <p>This link expires in 1 hour.</p>
        `)
        .send()
}

Welcome Email with Template

typescript
const WelcomeEmail = (
    { name, verifyUrl }: { name: string; verifyUrl: string },
) => (
    <html>
        <body style='font-family: Arial, sans-serif;'>
            <h1>Welcome, {name}!</h1>
            <p>Thanks for signing up. Please verify your email:</p>
            <a
                href={verifyUrl}
                style='background: #007bff; color: white; padding: 10px 20px; text-decoration: none;'
            >
                Verify Email
            </a>
        </body>
    </html>
)

await mail()
    .to(user.email, user.name)
    .subject('Welcome to Our App')
    .view(<WelcomeEmail name={user.name} verifyUrl={verifyUrl} />)
    .send()

Email with Attachments

typescript
await mail()
    .to('user@example.com')
    .subject('Invoice')
    .html('<p>Your invoice is attached.</p>')
    .attach('invoice.pdf', pdfBytes)
    .attach('receipt.txt', 'Receipt content')
    .send()

Testing Emails

typescript
import { MemoryMailDriver } from '@lockness/mail'

// Setup
configureMail({ driver: 'memory' })

// Your code sends email
await sendWelcomeEmail(user)

// Assert in tests
const emails = MemoryMailDriver.getSentEmails()
assertEquals(emails.length, 1)
assertEquals(emails[0].to[0].email, 'user@example.com')
assertEquals(emails[0].subject, 'Welcome!')

// Cleanup
MemoryMailDriver.clear()

Best Practices

  • Use Console driver in development to avoid accidental emails
  • Use Memory driver in tests with MemoryMailDriver.clear() in afterEach
  • Set default from address in configuration
  • Use JSX templates for complex emails with consistent styling
  • Always include both text and html content for best compatibility
  • Store SMTP credentials and API keys in environment variables
  • For bulk emails, consider implementing rate limiting
  • Use proper error handling around mail.send() calls

Environment Variables

bash
# .env
MAIL_DRIVER=smtp
MAIL_FROM_EMAIL=noreply@example.com
MAIL_FROM_NAME="My App"

# SMTP
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password

# Resend
RESEND_API_KEY=re_123456789