npm package: @impruthvi/nodemail
Description: A unified mail service for Node.js/TypeScript with Laravel-like simplicity. Support for multiple providers (SMTP, SendGrid, AWS SES, Mailgun, Resend, Postmark) and template engines (Handlebars, EJS, Pug) - just change environment variables, no code change
Author: Pruthvisinh Rajput
Latest version: 1.1.6
Deprecated: This package has been renamed to 'laramail'. Install it with: npm install laramail

# @impruthvi/nodemail

[![npm version](https://badge.fury.io/js/@impruthvi%2Fnodemail.svg)](https://www.npmjs.com/package/@impruthvi/nodemail)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.6-blue.svg)](https://www.typescriptlang.org/)
[![Tests](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fnodemail.impruthvi.me%2Fstats.json&query=%24.tests&label=tests&suffix=%20passing&color=brightgreen)](https://github.com/impruthvi/nodemail)
[![Coverage](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fnodemail.impruthvi.me%2Fstats.json&query=%24.coverage&label=coverage&suffix=%25&color=brightgreen)](https://github.com/impruthvi/nodemail)
[![Documentation](https://img.shields.io/badge/docs-nodemail.impruthvi.me-blue)](https://nodemail.impruthvi.me/)

**@impruthvi/nodemail** brings the simplicity and elegance of Laravel's Mail system to the Node.js ecosystem with full TypeScript support.

## 🎯 Vision

A lightweight, developer-friendly email library where you can:

- Switch email providers by just changing environment variables
- Use elegant, class-based Mailable patterns
- Keep your package lightweight (install only what you need)
- Write clean, maintainable email code

Inspired by [Laravel's Mail system](https://laravel.com/docs/mail).

## ✨ Features

### ✅ Available Now (v1.1.0)

- 🎯 **Multiple Providers** - SMTP (Nodemailer), SendGrid, AWS SES, Mailgun, Resend, Postmark
- 🎨 **Template Engines** - Handlebars, EJS, Pug support with dynamic loading
- 📝 **Mailable Classes** - Reusable email definitions with template support
- 📋 **Markdown Mail** - Write emails in Markdown with components (button, panel, table)
- 📦 **Queue Support** - Background email sending with Bull/BullMQ
- 🔄 **Provider Failover** - Automatic failover chain with retries, delays, and callbacks
- 🔔 **Email Events** - `sending`, `sent`, `failed` lifecycle hooks for logging, analytics, and cancellation
- 🔍 **Email Preview** - Preview rendered emails without sending (debug templates, verify headers)
- ⏱️ **Rate Limiting** - Per-provider rate limiting with sliding window algorithm
- 🧪 **Testing Utilities** - Mail::fake() for testing (Laravel-style assertions)
- 🎨 **CLI Tools** - Queue management, email preview, code generation, config validation
- 🪶 **Lightweight** - Only ~25MB with SMTP, install additional providers as needed
- 🔒 **Type-Safe** - Full TypeScript support with strict typing
- ✨ **Complete Fluent API** - Chain to(), subject(), html(), template(), data(), cc(), bcc(), attachments(), headers()
- ⚡ **Dynamic Loading** - Providers and templates loaded only when installed (peerDependencies)
- 🛡️ **Error Handling** - Graceful degradation with helpful error messages

### 🚧 Coming Soon

- 🔔 **Notifications** - Multi-channel notification system
- 🌍 **i18n Support** - Multi-language emails
- 🚀 **More Providers** - Mailtrap and others

## 📦 Installation

```bash
npm install @impruthvi/nodemail
```

Or install a specific version:

```bash
npm install @impruthvi/nodemail@1.1.0
```

**Lightweight by default!** Only includes SMTP support (~25MB).

### Adding Providers (Optional)

**Currently Supported:**

```bash
# SendGrid (✅ Implemented)
npm install @sendgrid/mail

# AWS SES (✅ Implemented)
npm install @aws-sdk/client-ses

# Mailgun (✅ Implemented)
npm install mailgun.js form-data

# Resend (✅ Implemented)
npm install resend

# Postmark (✅ Implemented)
npm install postmark
```

### Adding Template Engines (Optional)

**Currently Supported:**

```bash
# Handlebars (✅ Implemented)
npm install handlebars

# EJS (✅ Implemented)
npm install ejs

# Pug (✅ Implemented)
npm install pug
```

### Adding Markdown Mail Support (Optional)

```bash
npm install marked juice
```

## 🚀 Quick Start

### SMTP (Nodemailer)

```typescript
import { Mail } from 'nodemail';

Mail.configure({
  default: 'smtp',
  from: {
    address: 'noreply@example.com',
    name: 'My App',
  },
  mailers: {
    smtp: {
      driver: 'smtp',
      host: process.env.SMTP_HOST,
      port: 587,
      auth: {
        user: process.env.SMTP_USER,
        pass: process.env.SMTP_PASS,
      },
    },
  },
});

// Send emails
await Mail.to('user@example.com').subject('Welcome!').html('<h1>Hello World!</h1>').send();
```

### SendGrid

```typescript
// npm install @sendgrid/mail
import { Mail } from '@impruthvi/nodemail';

Mail.configure({
  default: 'sendgrid',
  from: { address: 'noreply@example.com', name: 'My App' },
  mailers: {
    sendgrid: {
      driver: 'sendgrid',
      apiKey: process.env.SENDGRID_API_KEY,
    },
  },
});
```

### AWS SES

```typescript
// npm install @aws-sdk/client-ses
import { Mail } from '@impruthvi/nodemail';

Mail.configure({
  default: 'ses',
  from: { address: 'noreply@example.com', name: 'My App' },
  mailers: {
    ses: {
      driver: 'ses',
      region: 'us-east-1',
      accessKeyId: process.env.AWS_ACCESS_KEY_ID,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
    },
  },
});
```

### Mailgun

```typescript
// npm install mailgun.js form-data
import { Mail } from '@impruthvi/nodemail';

Mail.configure({
  default: 'mailgun',
  from: { address: 'noreply@yourdomain.com', name: 'My App' },
  mailers: {
    mailgun: {
      driver: 'mailgun',
      domain: process.env.MAILGUN_DOMAIN,
      apiKey: process.env.MAILGUN_API_KEY,
      region: 'us', // or 'eu'
    },
  },
});
```

### Resend

```typescript
// npm install resend
import { Mail } from '@impruthvi/nodemail';

Mail.configure({
  default: 'resend',
  from: { address: 'noreply@yourdomain.com', name: 'My App' },
  mailers: {
    resend: {
      driver: 'resend',
      apiKey: process.env.RESEND_API_KEY,
    },
  },
});
```

### Postmark

```typescript
// npm install postmark
import { Mail } from '@impruthvi/nodemail';

Mail.configure({
  default: 'postmark',
  from: { address: 'noreply@yourdomain.com', name: 'My App' },
  mailers: {
    postmark: {
      driver: 'postmark',
      serverToken: process.env.POSTMARK_SERVER_TOKEN,
    },
  },
});
```

## 🎨 Template Engines

### Using Handlebars

```typescript
// npm install handlebars
import { Mail } from '@impruthvi/nodemail';

Mail.configure({
  default: 'smtp',
  from: { address: 'noreply@example.com', name: 'My App' },
  mailers: {
    /* your mailer config */
  },
  templates: {
    engine: 'handlebars',
    viewsPath: './views/emails',
    extension: '.hbs',
    cache: true,
  },
});

// Send with template
await Mail.to('user@example.com')
  .subject('Welcome!')
  .template('welcome')
  .data({ name: 'John', appName: 'My App' })
  .send();
```

**Template file** (`views/emails/welcome.hbs`):

```handlebars
<h1>Welcome, {{name}}!</h1>
<p>Thank you for joining {{appName}}.</p>
```

### Using EJS

```typescript
// npm install ejs
Mail.configure({
  templates: {
    engine: 'ejs',
    viewsPath: './views/emails',
    extension: '.ejs',
  },
});

await Mail.to('customer@example.com')
  .subject('Your Invoice')
  .template('invoice')
  .data({ items: [...], total: 99.99 })
  .send();
```

### Using Pug

```typescript
// npm install pug
Mail.configure({
  templates: {
    engine: 'pug',
    viewsPath: './views/emails',
    cache: true,
  },
});

await Mail.to('user@example.com')
  .subject('Notification')
  .template('notification')
  .data({ title: 'Update', message: 'New features!' })
  .send();
```

## 📋 Markdown Mail

Write beautiful emails in Markdown with built-in components. Requires `npm install marked juice`.

### MarkdownMailable

```typescript
import { MarkdownMailable, Mail } from '@impruthvi/nodemail';

class WelcomeEmail extends MarkdownMailable {
  constructor(
    private user: { name: string },
    private appName: string
  ) {
    super();
  }

  build(): this {
    return this.subject(`Welcome to ${this.appName}!`)
      .from('noreply@example.com')
      .markdown(
        `# Welcome, {{name}}!

Thank you for joining **{{appName}}**.

[button url="https://example.com/start" color="primary"]Get Started[/button]

[panel]Need help? Contact support@example.com[/panel]`,
        {
          name: this.user.name,
          appName: this.appName,
        }
      );
  }
}

await Mail.to('user@example.com').send(new WelcomeEmail(user, 'My App'));
```

### Components

**Button** - Call-to-action buttons with color variants:

```markdown
[button url="https://example.com" color="primary"]Click Here[/button]
[button url="https://example.com" color="success"]Confirm[/button]
[button url="https://example.com" color="error"]Delete[/button]
```

**Panel** - Bordered callout sections:

```markdown
[panel]
**Important:** This is a highlighted notice.
[/panel]
```

**Table** - Styled table wrapper:

```markdown
[table]
| Name | Price |
|-------|--------|
| Item | $9.99 |
[/table]
```

### Custom Themes

```typescript
class BrandedEmail extends MarkdownMailable {
  build(): this {
    return this.subject('Update').markdown('# News\n\nLatest updates...').theme({
      css: 'h1 { color: #e94560; } .button-primary { background: #e94560; }',
      headerHtml: '<img src="https://example.com/logo.png" alt="Logo">',
      footerHtml: '<p>&copy; 2026 Company</p>',
    });
  }
}
```

### Markdown Configuration

```typescript
Mail.configure({
  // ... mailer config
  markdown: {
    theme: {
      css: '/* custom global CSS */',
      headerHtml: '<img src="logo.png">',
      footerHtml: '<p>Footer</p>',
    },
    customCss: '.button { border-radius: 8px; }',
  },
});
```

## 📦 Queue Support

Send emails in the background with Bull or BullMQ. Requires `npm install bullmq` (or `bull`).

```typescript
Mail.configure({
  // ... mailer config
  queue: {
    driver: 'bullmq',
    connection: { host: 'localhost', port: 6379 },
    retries: 3,
    backoff: { type: 'exponential', delay: 1000 },
  },
});

// Queue immediately
await Mail.to('user@example.com').subject('Welcome!').html('<h1>Welcome!</h1>').queue();

// Delayed sending (60 seconds)
await Mail.to('user@example.com').later(60, new WelcomeEmail(user));

// Scheduled delivery
await Mail.to('user@example.com').at(new Date('2026-12-25'), new ChristmasEmail());

// Process queued emails (in worker)
await Mail.processQueue();
```

## 🎨 CLI Tools

The CLI provides commands for queue management, email preview, code generation, and configuration validation.

### Quick Reference

```bash
# Queue management
npx nodemail queue:work              # Start processing queued emails
npx nodemail queue:status            # Show queue job counts
npx nodemail queue:clear --status failed   # Clear failed jobs
npx nodemail queue:retry             # Retry failed jobs

# Email preview and testing
npx nodemail preview --mailable ./src/mail/WelcomeEmail.ts
npx nodemail send:test --to you@example.com

# Code generation
npx nodemail make:mailable WelcomeEmail
npx nodemail make:mailable NewsletterEmail --markdown

# Configuration
npx nodemail config:check            # Validate configuration
npx nodemail config:check --test     # Validate and test connections
```

### Configuration File

Create a `nodemail.config.ts` in your project root:

```typescript
import { defineConfig } from '@impruthvi/nodemail';

export default defineConfig({
  default: 'smtp',
  from: { address: 'noreply@example.com', name: 'My App' },
  mailers: {
    smtp: {
      driver: 'smtp',
      host: process.env.SMTP_HOST,
      port: 587,
      auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
    },
  },
  queue: {
    driver: 'bullmq',
    connection: { host: 'localhost', port: 6379 },
  },
});
```

For styled terminal output, install chalk: `npm install chalk`

## 🔄 Provider Failover

Automatically fail over to backup providers when the primary provider fails. Supports retries, delays, and monitoring callbacks.

### Global Failover Configuration

```typescript
Mail.configure({
  default: 'smtp',
  from: { address: 'noreply@example.com', name: 'My App' },
  mailers: {
    smtp: {
      driver: 'smtp',
      host: process.env.SMTP_HOST,
      port: 587,
      auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
    },
    sendgrid: {
      driver: 'sendgrid',
      apiKey: process.env.SENDGRID_API_KEY,
    },
    ses: {
      driver: 'ses',
      region: 'us-east-1',
      accessKeyId: process.env.AWS_ACCESS_KEY_ID,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
    },
  },
  failover: {
    chain: ['sendgrid', 'ses'],
    maxRetriesPerProvider: 2,
    retryDelay: 1000,
    failoverDelay: 500,
  },
});

// Sends via SMTP first; if SMTP fails (after 2 retries), tries SendGrid, then SES
await Mail.to('user@example.com').subject('Hello!').html('<h1>Hello!</h1>').send();
```

### Per-mailer Failover Override

Override the global failover config for a specific mailer:

```typescript
Mail.configure({
  default: 'smtp',
  mailers: {
    smtp: {
      driver: 'smtp',
      host: process.env.SMTP_HOST,
      port: 587,
      auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
      failover: {
        chain: ['postmark'], // Only fail over to Postmark for SMTP
        maxRetriesPerProvider: 3,
      },
    },
    postmark: {
      driver: 'postmark',
      serverToken: process.env.POSTMARK_SERVER_TOKEN,
    },
  },
});
```

### onFailover Callback

Monitor failover events for logging or alerting:

```typescript
Mail.configure({
  // ... mailer config
  failover: {
    chain: ['sendgrid', 'ses'],
    onFailover: (event) => {
      console.log(`Failover: ${event.failedMailer} → ${event.nextMailer}`);
      console.log(`Error: ${event.error}`);
      console.log(`Attempt: ${event.attemptIndex}, Time: ${event.timestamp}`);
    },
  },
});
```

### Response Metadata

After sending, the response includes failover details:

```typescript
const result = await Mail.to('user@example.com').subject('Hello!').html('<h1>Hello!</h1>').send();

console.log(result.provider); // 'sendgrid' (which provider actually sent)
console.log(result.failoverUsed); // true (failover was triggered)
console.log(result.failoverAttempts); // Array of FailoverDetail objects
```

### FailoverConfig Reference

| Property                | Type                             | Default    | Description                                      |
| ----------------------- | -------------------------------- | ---------- | ------------------------------------------------ |
| `chain`                 | `string[]`                       | (required) | Ordered list of backup mailer names              |
| `maxRetriesPerProvider` | `number`                         | `1`        | Retries per provider before moving to the next   |
| `retryDelay`            | `number`                         | `0`        | Delay (ms) between retries on the same provider  |
| `failoverDelay`         | `number`                         | `0`        | Delay (ms) before switching to the next provider |
| `onFailover`            | `(event: FailoverEvent) => void` | —          | Callback fired on each failover transition       |

## 🔔 Email Events

Hook into the email lifecycle for logging, analytics, or to modify/cancel emails before sending.

```typescript
// Log all outgoing emails
Mail.onSending((event) => {
  console.log(`Sending to ${event.options.to} via ${event.mailer}`);
  // Modify options before send
  event.options.headers = { ...event.options.headers, 'X-Tracking': '123' };
});

// Log successful deliveries
Mail.onSent((event) => {
  console.log(`Sent! ID: ${event.response.messageId}`);
});

// Log failures
Mail.onFailed((event) => {
  console.error(`Failed: ${event.error}`);
});

// Cancel a send by returning false
Mail.onSending((event) => {
  if (event.options.to === 'blocked@example.com') return false;
});

// Remove all listeners
Mail.clearListeners();
```

### Event Methods

| Method                     | Description                                                                                |
| -------------------------- | ------------------------------------------------------------------------------------------ |
| `Mail.onSending(listener)` | Register listener before send (can cancel with `return false`, can mutate `event.options`) |
| `Mail.onSent(listener)`    | Register listener after successful send                                                    |
| `Mail.onFailed(listener)`  | Register listener on send failure                                                          |
| `Mail.clearListeners()`    | Remove all event listeners                                                                 |

Events work in both real and fake mode. Listener errors are silently caught and never break email delivery. See [docs/email-events.md](docs/email-events.md) for the full guide.

## 🔍 Email Preview

Preview emails without sending — useful for debugging templates, verifying headers, and building preview UIs.

```typescript
// Preview via fluent builder
const preview = await Mail.to('user@example.com')
  .subject('Hello')
  .html('<p>Hi</p>')
  .priority('high')
  .preview();

console.log(preview.html); // '<p>Hi</p>'
console.log(preview.headers); // { 'X-Priority': '1', ... }

// Preview a Mailable
const preview = await Mail.preview(new WelcomeMail().to('user@example.com'));
```

Preview applies full preprocessing (markdown, templates, priority headers) but fires no events and stores no messages. Works in both real and fake mode. See [docs/email-preview.md](docs/email-preview.md) for the full guide.

## ⏱️ Rate Limiting

Per-provider rate limiting to prevent exceeding email provider API limits. Uses an in-memory sliding window algorithm.

```typescript
// Global rate limit (applies to all mailers)
Mail.configure({
  default: 'smtp',
  from: { address: 'noreply@example.com', name: 'App' },
  mailers: {
    smtp: { driver: 'smtp', host: 'localhost', port: 587 },
  },
  rateLimit: { maxPerWindow: 100, windowMs: 60000 },
});

// Per-mailer override (takes precedence over global)
Mail.configure({
  default: 'smtp',
  from: { address: 'noreply@example.com', name: 'App' },
  mailers: {
    smtp: {
      driver: 'smtp',
      host: 'localhost',
      port: 587,
      rateLimit: { maxPerWindow: 10, windowMs: 1000 },
    },
    sendgrid: {
      driver: 'sendgrid',
      apiKey: '...',
      rateLimit: { maxPerWindow: 100, windowMs: 1000 },
    },
  },
});

// When exceeded — returns { success: false }, no throw
const result = await Mail.to('user@example.com').subject('Hi').html('<p>Hi</p>').send();
// result = { success: false, error: 'Rate limit exceeded for mailer "smtp". Try again in 450ms.' }

// Optional callback
Mail.configure({
  rateLimit: {
    maxPerWindow: 10,
    windowMs: 1000,
    onRateLimited: (event) => {
      console.log(`Rate limited on ${event.mailer}, retry in ${event.retryAfterMs}ms`);
    },
  },
});
```

Rate-limited sends don't fire `sending`/`sent`/`failed` events. See [docs/rate-limiting.md](docs/rate-limiting.md) for the full guide.

## 📨 Complete Fluent API

```typescript
await Mail.to('user@example.com')
  .subject('Complete Example')
  .html('<h1>Hello!</h1>')
  .text('Hello!')
  .from('custom@example.com')
  .cc(['manager@example.com', 'team@example.com'])
  .bcc('archive@example.com')
  .replyTo('support@example.com')
  .attachments([
    { filename: 'report.pdf', path: './files/report.pdf' },
    { filename: 'image.png', content: buffer },
  ])
  .priority('high')
  .headers({ 'X-Custom-Header': 'value' })
  .send();
```

## 📝 Mailable Classes

Create reusable email classes with Laravel-like syntax:

```typescript
import { Mailable } from '@impruthvi/nodemail';

class WelcomeEmail extends Mailable {
  constructor(
    private user: { name: string; email: string },
    private appName: string
  ) {
    super();
  }

  build() {
    return this.subject(`Welcome to ${this.appName}!`).view('welcome', {
      name: this.user.name,
      email: this.user.email,
      appName: this.appName,
    });
  }
}

// Method 1: Laravel-style (recommended)
await Mail.to('user@example.com').send(new WelcomeEmail(user, 'My App'));

// Method 2: Direct sending
await new WelcomeEmail(user, 'My App').to('user@example.com').send();
```

## 🧪 Testing with Mail::fake()

Test your emails without actually sending them - just like Laravel's `Mail::fake()`:

```typescript
import { Mail, Mailable } from '@impruthvi/nodemail';

// Your Mailable class
class WelcomeEmail extends Mailable {
  constructor(public userName: string) {
    super();
  }

  build() {
    return this.subject(`Welcome, ${this.userName}!`).html(`<h1>Hello ${this.userName}!</h1>`);
  }
}

// In your tests
describe('User Registration', () => {
  beforeEach(() => {
    Mail.fake(); // Enable fake mode
  });

  afterEach(() => {
    Mail.restore(); // Restore real mailer
  });

  it('sends welcome email on registration', async () => {
    // Your application code that sends email
    await Mail.to('user@example.com').send(new WelcomeEmail('John'));

    // Assert email was sent
    Mail.assertSent(WelcomeEmail);

    // Assert with conditions
    Mail.assertSent(WelcomeEmail, (mail) => {
      return mail.hasTo('user@example.com') && mail.subjectContains('Welcome');
    });

    // Assert sent count
    Mail.assertSentCount(WelcomeEmail, 1);

    // Assert other mailables were NOT sent
    Mail.assertNotSent(PasswordResetEmail);
  });

  it('does not send email when validation fails', async () => {
    // Code that doesn't send email
    Mail.assertNothingSent();
  });
});
```

### Available Assertions

| Method                                  | Description                                        |
| --------------------------------------- | -------------------------------------------------- |
| `Mail.fake()`                           | Enable fake mode (store emails instead of sending) |
| `Mail.restore()`                        | Restore real mailer                                |
| `Mail.assertSent(Mailable)`             | Assert mailable was sent                           |
| `Mail.assertSent(Mailable, callback)`   | Assert with custom conditions                      |
| `Mail.assertSentCount(Mailable, count)` | Assert sent exactly N times                        |
| `Mail.assertNotSent(Mailable)`          | Assert mailable was NOT sent                       |
| `Mail.assertNothingSent()`              | Assert no emails were sent                         |
| `Mail.assertQueued(Mailable)`           | Assert mailable was queued                         |
| `Mail.assertNothingQueued()`            | Assert nothing was queued                          |
| `Mail.sent()`                           | Get all sent messages                              |
| `Mail.sent(Mailable)`                   | Get sent messages of specific type                 |
| `Mail.hasSent()`                        | Check if any messages were sent                    |
| `Mail.hasQueued()`                      | Check if any messages were queued                  |
| `Mail.simulateFailures(n)`              | Simulate failures for the first N sends            |
| `Mail.resetFailures()`                  | Clear failure simulation state                     |

### AssertableMessage Methods

When inspecting sent messages, you can use these helper methods:

```typescript
const sent = Mail.sent(WelcomeEmail)[0];

// Check recipients
sent.hasTo('user@example.com'); // Check TO
sent.hasCc('cc@example.com'); // Check CC
sent.hasBcc('bcc@example.com'); // Check BCC

// Check content
sent.hasSubject('Welcome!'); // Exact subject match
sent.subjectContains('Welcome'); // Subject contains
sent.htmlContains('Hello'); // HTML contains
sent.textContains('Hello'); // Plain text contains

// Check markdown
sent.isMarkdown(); // Was built from markdown
sent.getMarkdown(); // Get raw markdown source
sent.markdownContains('[button'); // Markdown source contains

// Check failover
sent.wasFailoverUsed(); // Whether failover was triggered
sent.getProvider(); // Provider that actually sent
sent.getFailoverAttempts(); // Array of FailoverDetail objects

// Check attachments
sent.hasAttachments(); // Has any attachments
sent.hasAttachment('file.pdf'); // Has specific attachment

// Check priority
sent.hasPriority('high'); // Has specific priority
sent.getPriority(); // Get priority level

// Check headers
sent.hasHeader('X-Custom'); // Has header
sent.hasHeader('X-Custom', 'value'); // Header with value

// Get values
sent.getTo(); // Get recipients array
sent.getSubject(); // Get subject
sent.getHtml(); // Get HTML content
```

## 🛠️ Current Status

**Phase 1: Project Setup** ✅ Complete

- TypeScript 5.6 configuration
- ESLint 9 (flat config)
- Modern tooling setup
- Package structure
- Core type definitions
- Lightweight architecture (peerDependencies)

**Phase 2: Core Implementation** ✅ Complete

- ✅ Mail Manager & Facade
- ✅ SMTP Provider (nodemailer)
- ✅ SendGrid Provider (@sendgrid/mail)
- ✅ AWS SES Provider (@aws-sdk/client-ses)
- ✅ Message builder with complete fluent API
- ✅ Configuration system
- ✅ Error handling & graceful degradation

**Phase 3: Additional Providers** ✅ Complete

- ✅ Mailgun Provider (mailgun.js)
- ✅ Resend Provider (resend)
- ✅ Postmark Provider (postmark)
- ✅ Dynamic loading for all providers
- ✅ Comprehensive provider tests

**Phase 4: Template Engines & Mailable** ✅ Complete

- ✅ Template engines (Handlebars, EJS, Pug)
- ✅ Laravel-like Mailable classes with template support
- ✅ Complete fluent API (cc, bcc, replyTo, attachments, headers)
- ✅ Dynamic template loading with caching

**Phase 5: Testing Utilities** ✅ Complete (v0.5.0)

- ✅ Mail::fake() for testing
- ✅ assertSent(), assertNotSent(), assertNothingSent()
- ✅ assertQueued(), assertNothingQueued()
- ✅ AssertableMessage with inspection methods

**Phase 6: Queue Management** ✅ Complete (v0.6.0)

- ✅ QueueManager with Bull and BullMQ drivers
- ✅ Immediate, delayed, and scheduled sending
- ✅ Automatic retries with configurable backoff
- ✅ MailFake queue assertion support

**Phase 7: Markdown Mail** ✅ Complete (v0.7.0)

- ✅ MarkdownMailable base class
- ✅ MarkdownRenderer with CSS inlining
- ✅ Components: button, panel, table
- ✅ Default responsive email theme
- ✅ Custom themes and CSS support
- ✅ AssertableMessage markdown assertions

**Phase 8: Provider Failover** ✅ Complete (v1.0.0)

- ✅ FailoverManager with automatic provider chain
- ✅ Configurable retries per provider (`maxRetriesPerProvider`)
- ✅ Retry and failover delays (`retryDelay`, `failoverDelay`)
- ✅ `onFailover` callback for monitoring/logging
- ✅ Per-mailer failover overrides
- ✅ MailFake `simulateFailures()` / `resetFailures()` for testing
- ✅ Response metadata: `provider`, `failoverUsed`, `failoverAttempts`
- ✅ 269 passing tests

**Post v1.0.0: Incremental Features** ✅ Complete (v1.0.1)

- ✅ Embedded Images (CID support) for inline images in HTML
- ✅ Email Priority Levels (`high`, `normal`, `low`) with X-Priority headers
- ✅ Email Events System (`sending`, `sent`, `failed` lifecycle hooks)
- ✅ Email Preview — render emails without sending (debug templates, verify headers)
- ✅ Rate Limiting — per-provider sliding window algorithm
- ✅ 522 passing tests

**Phase 9: Enhanced CLI** ✅ Complete (v1.1.0)

- ✅ CLI tool with 8 commands for queue management, email preview, and code generation
- ✅ `queue:work` - Start processing queued emails with concurrency control
- ✅ `queue:status` - Show queue job counts
- ✅ `queue:clear` - Clear jobs by status (completed, failed, delayed, waiting)
- ✅ `queue:retry` - Retry failed jobs with optional limit
- ✅ `preview` - Preview email in browser without sending
- ✅ `send:test` - Send test email to verify configuration
- ✅ `make:mailable` - Generate Mailable class (with markdown/template options)
- ✅ `config:check` - Validate configuration with optional connection testing
- ✅ `defineConfig` helper for TypeScript autocomplete in config files
- ✅ Auto-detection of nodemail.config.ts/js config files

**Phase 10+** 🚧 Coming Soon

- 🔔 Notifications - Multi-channel notification system
- 🌍 i18n Support - Multi-language emails
- 🚀 More Providers - Mailtrap and others

## 🤝 Contributing

This project is in early development. Contributions, ideas, and feedback are welcome!

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

### Development Setup

```bash
# Clone the repository
git clone https://github.com/impruthvi/nodemail.git
cd nodemail

# Install dependencies
npm install

# Build
npm run build

# Lint
npm run lint

# Format code
npm run format
```

## 💡 Why nodemail?

### Inspired by Laravel, Built for Node.js

If you've used Laravel's Mail system, you know how elegant it is:

```php
// Laravel (PHP)
Mail::to($user->email)->send(new WelcomeEmail($user));
```

**@impruthvi/nodemail** brings this same elegance to Node.js/TypeScript:

```typescript
// @impruthvi/nodemail (TypeScript)
await Mail.to(user.email).send(new WelcomeEmail(user));
```

### Lightweight by Design

Unlike other packages that bundle everything:

- **Base package**: ~25MB (SMTP only)
- **Add providers as needed**: `npm install @sendgrid/mail`
- **No bloat**: Only install what you use

## 📊 Package Philosophy

- **Modular**: Install only the providers you need
- **Type-Safe**: Full TypeScript support with strict typing
- **Developer-Friendly**: Clean, intuitive API
- **Production-Ready**: Built with best practices
- **Well-Tested**: 269 passing tests with 85%+ coverage

## 📄 License

MIT © [Pruthvi](https://github.com/impruthvi)

## 🙏 Acknowledgments

Inspired by [Laravel's Mail system](https://laravel.com/docs/mail) - bringing elegant email handling to Node.js.

## 📞 Support & Community

- 📚 [Documentation](https://nodemail.impruthvi.me/) - Complete guides and API reference
- 📫 [GitHub Issues](https://github.com/impruthvi/nodemail/issues) - Bug reports and feature requests
- 💬 [GitHub Discussions](https://github.com/impruthvi/nodemail/discussions) - Questions and community chat

## 📦 Postinstall Message

After installation, nodemail displays a short thank-you message in your terminal. To suppress it, set one of these environment variables:

```bash
# Disable the postinstall message
NODEMAIL_NO_POSTINSTALL=1 npm install @impruthvi/nodemail

# Also auto-suppressed in CI environments (CI, GITHUB_ACTIONS, TRAVIS, etc.)
```

---

**⭐ If you like this idea, please star the repo!** It helps gauge interest and motivates development.

**🚀 Want to contribute?** Check out the issues labeled `good first issue` or `help wanted`.