npm package: @siwats/mxrelay-consumer
Description: An internal TypeScript client library for transporting SMTP messages
Author: Siwat Sirichai
Latest version: 1.2.8

# @siwats/mxrelay-consumer

[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A production-ready TypeScript client library for SMTP over WebSocket protocol with **dedicated SMTP sessions**, automatic reconnection, and comprehensive error handling. Each email gets its own complete SMTP session for maximum protocol compliance and isolation.

## 🚀 Key Features

### **Dedicated SMTP Sessions**
- **Isolated SMTP Sessions**: One email = One complete SMTP session (EHLO → AUTH → Email → QUIT)
- **Session Mutex**: Only one SMTP session active at a time (protocol compliant)
- **Maximum Isolation**: Each email in its own session for better error handling
- **Resource Optimization**: Connect only when needed, disconnect when idle

### **Intelligent Queue Management**
- **Session Queuing**: Each email queued as its own dedicated session
- **Priority-Based Processing**: CRITICAL > HIGH > NORMAL > LOW priority levels
- **One-to-One Mapping**: One email per SMTP session for maximum isolation
- **Smart Resource Usage**: Auto-connect/disconnect based on queue state

### **Production-Ready Reliability**
- **Comprehensive Error Handling**: Structured error classification and meaningful messages
- **Automatic Reconnection**: Exponential backoff with configurable retry limits
- **Timeout Management**: Session, email, and connection-level timeouts
- **Connection State Management**: Full lifecycle state tracking

### **Nodemailer Integration**
- **Full Nodemailer Compatibility**: Drop-in replacement for existing transports
- **Standard API**: Use familiar Nodemailer methods and options
- **Advanced Features**: Attachments, HTML, multipart messages, custom headers

## 📊 Architecture Overview

```mermaid
graph TB
    subgraph "Client Application"
        App[Your Application]
        NM[Nodemailer]
        Transport[SMTPWSTransport]
    end
    
    subgraph "Session Management"
        Client[SMTPOverWSClient]
        Queue[Session Queue]
        SM[Session Manager]
    end
    
    subgraph "Protocol Layer"
        WS[WebSocket Connection]
        Auth[Authentication]
        Channel[SMTP Channel]
    end
    
    subgraph "Remote Server"
        Relay[SMTP WebSocket Relay]
        SMTP[SMTP Server]
    end
    
    App --> NM
    NM --> Transport
    Transport --> Client
    Client --> Queue
    Queue --> SM
    SM --> WS
    WS --> Auth
    Auth --> Channel
    Channel --> Relay
    Relay --> SMTP
    
    style Client fill:#e1f5fe
    style Queue fill:#f3e5f5
    style SM fill:#fff3e0
    style Channel fill:#e8f5e8
```

## 🔄 Session Processing Flow

```mermaid
sequenceDiagram
    participant App as Application
    participant Client as SMTPOverWSClient
    participant Queue as Session Queue
    participant WS as WebSocket
    participant Server as SMTP Server
    
    Note over App,Server: Email Submission
    App->>Client: queueEmail(from, to[], data)
    Client->>Queue: Add to session or create new
    
    Note over App,Server: Session Processing (Mutex)
    Queue->>Client: Process next session
    Client->>WS: Connect (if needed)
    WS->>Server: WebSocket connection
    Server-->>WS: Connection established
    
    Client->>Server: AUTHENTICATE
    Server-->>Client: AUTH_SUCCESS
    
    Client->>Server: SMTP_CHANNEL_OPEN
    Server-->>Client: SMTP_CHANNEL_READY
    Server-->>Client: 220 SMTP ready
    
    Client->>Server: EHLO client
    Server-->>Client: 250 EHLO OK
    
    Client->>Server: AUTH PLAIN <credentials>
    Server-->>Client: 235 AUTH OK
    
    Client->>Server: MAIL FROM:<sender>
    Server-->>Client: 250 Sender OK
    
    Client->>Server: RCPT TO:<recipient>
    Server-->>Client: 250 Recipient OK
    
    Client->>Server: DATA
    Server-->>Client: 354 Start data
    
    Client->>Server: <email content>\r\n.
    Server-->>Client: 250 Message accepted
    
    Client->>App: Email resolved
    
    Client->>Server: QUIT
    Server-->>Client: 221 Goodbye
    
    Client->>Server: SMTP_CHANNEL_CLOSE
    WS->>WS: Disconnect (if queue empty)
    
    Client->>App: Session completed
```

## 📦 Installation

```bash
npm install @siwats/mxrelay-consumer
# or
bun add @siwats/mxrelay-consumer
```

## 🚀 Quick Start

### Direct Client Usage (New Session-Based API)

```typescript
import { SMTPOverWSClient } from '@siwats/mxrelay-consumer';

const client = new SMTPOverWSClient({
    url: 'wss://api.siwatsystem.com/smtp',
    apiKey: 'your-api-key',
    debug: true,
    sessionTimeout: 300000   // 5 minute session timeout
});

// Queue individual emails - each gets its own dedicated SMTP session
try {
    // Each email will be sent in its own complete SMTP session
    const emailId1 = await client.queueEmail(
        'sender@example.com',
        ['recipient1@example.com'],
        'Subject: Email 1\r\n\r\nFirst email content'
    );
    
    const emailId2 = await client.queueEmail(
        'sender@example.com', 
        ['recipient2@example.com'],
        'Subject: Email 2\r\n\r\nSecond email content'
    );
    
    console.log('Emails queued:', emailId1, emailId2);
} catch (error) {
    console.error('Email error:', error.message);
} finally {
    await client.shutdown();
}
```

### Nodemailer Transport (Recommended)

```typescript
import nodemailer from 'nodemailer';
import { createTransport } from '@siwats/mxrelay-consumer';

// Create transport with dedicated sessions
const transport = createTransport('your-api-key', {
    host: 'api.siwatsystem.com',
    port: 443,
    secure: true,
    debug: true,
    sessionBatchTimeout: 2000   // Processing delay between sessions
});

const transporter = nodemailer.createTransporter(transport);

// Send multiple emails - automatically batched into sessions
const emails = [
    {
        from: 'sender@example.com',
        to: 'user1@example.com',
        subject: 'Welcome Email',
        html: '<h1>Welcome to our service!</h1>'
    },
    {
        from: 'sender@example.com',
        to: 'user2@example.com', 
        subject: 'Newsletter',
        html: '<h1>Monthly Newsletter</h1>',
        attachments: [{ filename: 'report.pdf', path: './report.pdf' }]
    }
];

// Send all emails - each gets its own dedicated SMTP session
for (const email of emails) {
    try {
        const info = await transporter.sendMail(email);
        console.log('Email sent:', info.messageId);
    } catch (error) {
        console.error('Failed to send email:', error.message);
    }
}

await transport.close();
```

## ⚙️ Configuration

### Client Configuration

```typescript
interface SMTPClientConfig {
    url: string;                        // WebSocket server URL
    apiKey: string;                     // Authentication API key
    
    // Session Management
    sessionTimeout?: number;            // Session timeout (default: 300000ms)
    sessionBatchTimeout?: number;       // Processing delay between sessions (default: 1000ms)
    
    // Connection Management  
    debug?: boolean;                    // Enable debug logging (default: false)
    maxQueueSize?: number;              // Session queue limit (default: 100)
    reconnectInterval?: number;         // Reconnect delay (default: 5000ms)
    maxReconnectAttempts?: number;      // Max retry attempts (default: 10)
    authTimeout?: number;               // Auth timeout (default: 30000ms)
    channelTimeout?: number;            // Channel timeout (default: 10000ms)
    messageTimeout?: number;            // SMTP command timeout (default: 60000ms)
    heartbeatInterval?: number;         // Heartbeat interval (default: 30000ms)
}
```

### Transport Configuration

```typescript
interface TransportOptions extends Omit<SMTPClientConfig, 'url' | 'apiKey'> {
    host?: string;                      // Server host (default: 'api.siwatsystem.com')
    port?: number;                      // Server port (default: 443)
    secure?: boolean;                   // Use wss:// (default: true)
    apiKey?: string;                    // API key for authentication
}
```

## 🎯 Advanced Usage

### Session Priority and Options

```typescript
import { MessagePriority, SessionSendOptions } from '@siwats/mxrelay-consumer';

// High priority email (processed first)
await client.queueEmail(
    'urgent@company.com',
    ['admin@company.com'],
    'Subject: URGENT ALERT\r\n\r\nSystem down!',
    {
        priority: MessagePriority.HIGH,
        timeout: 30000,
        retries: 5
    }
);

// Critical priority email (highest priority)
await client.queueEmail(
    'security@company.com',
    ['security-team@company.com'],
    'Subject: SECURITY BREACH\r\n\r\nImmediate action required!',
    {
        priority: MessagePriority.CRITICAL,
        immediate: true  // Send with highest priority
    }
);
```

### Session Event Monitoring

```typescript
// Session-level events
client.on('sessionQueued', (sessionId, queueSize) => {
    console.log(`Session ${sessionId} queued. Queue size: ${queueSize}`);
});

client.on('sessionStarted', (sessionId, emailCount) => {
    console.log(`Processing session ${sessionId} with ${emailCount} emails`);
});

client.on('sessionCompleted', (sessionId, results) => {
    console.log(`Session ${sessionId} completed with ${results.length} emails`);
});

// Email-level events within sessions
client.on('emailProcessed', (sessionId, emailId, responseTime) => {
    console.log(`Email ${emailId} in session ${sessionId} processed in ${responseTime}ms`);
});

client.on('emailFailed', (sessionId, emailId, error) => {
    console.error(`Email ${emailId} in session ${sessionId} failed:`, error.message);
});

// Connection events
client.on('connected', () => console.log('WebSocket connected'));
client.on('authenticated', () => console.log('Authentication successful'));
client.on('disconnected', () => console.log('Connection lost'));
```

### Session Statistics

```typescript
const stats = client.getStats();
console.log('Session Statistics:', {
    sessionsQueued: stats.sessionsQueued,
    sessionsProcessed: stats.sessionsProcessed,
    sessionsFailed: stats.sessionsFailed,
    emailsProcessed: stats.emailsProcessed,
    emailsFailed: stats.emailsFailed,
    averageSessionTime: stats.averageSessionTime,
    averageEmailTime: stats.averageEmailTime,
    queueSize: stats.queueSize
});

console.log('Session State:', client.getSessionState()); // IDLE, PROCESSING, FAILED
console.log('Connection State:', client.getConnectionState()); // DISCONNECTED, CONNECTED, etc.
```

## 🛡️ Error Handling

### SMTP Session Errors

```typescript
try {
    await client.queueEmail(
        'invalid-sender@domain.com',
        ['recipient@example.com'],
        'Subject: Test\r\n\r\nTest message'
    );
} catch (error) {
    if (error instanceof MessageError) {
        console.error('SMTP Error:', error.message);
        console.log('Email ID:', error.messageId);
        console.log('Retry Count:', error.retryCount);
    }
}
```

### Session State Management

The client manages session states automatically:

```mermaid
stateDiagram-v2
    [*] --> IDLE
    IDLE --> PROCESSING: Session queued
    PROCESSING --> IDLE: Session completed
    PROCESSING --> FAILED: Session error
    FAILED --> PROCESSING: Retry session
    FAILED --> IDLE: Max retries reached
    IDLE --> [*]: Client shutdown
```

- `IDLE` - No active session, ready to process
- `PROCESSING` - Session currently being processed (mutex locked)
- `FAILED` - Session failed, will retry or reject
- `COMPLETED` - Session successfully completed

### Connection States

```mermaid
stateDiagram-v2
    [*] --> DISCONNECTED
    DISCONNECTED --> CONNECTING: Connect requested
    CONNECTING --> CONNECTED: WebSocket established
    CONNECTED --> AUTHENTICATING: Send credentials
    AUTHENTICATING --> AUTHENTICATED: Auth successful
    AUTHENTICATED --> CHANNEL_OPENING: Open SMTP channel
    CHANNEL_OPENING --> CHANNEL_READY: Channel established
    CHANNEL_READY --> CHANNEL_CLOSED: Session complete
    CHANNEL_CLOSED --> AUTHENTICATED: Ready for next session
    AUTHENTICATED --> DISCONNECTED: Queue empty
    
    CONNECTING --> RECONNECTING: Connection failed
    AUTHENTICATING --> RECONNECTING: Auth failed
    CHANNEL_OPENING --> RECONNECTING: Channel failed
    RECONNECTING --> CONNECTING: Retry attempt
    RECONNECTING --> FAILED: Max retries reached
    
    FAILED --> [*]: Shutdown
    DISCONNECTED --> [*]: Shutdown
```

## 🔧 Session Queue Architecture

```mermaid
flowchart TD
    subgraph Emails ["📧 Email Submission"]
        E1[Email 1 - HIGH]
        E2[Email 2 - NORMAL] 
        E3[Email 3 - HIGH]
        E4[Email 4 - CRITICAL]
    end
    
    subgraph Queue ["📋 Session Creation"]
        SQ[Session Queue]
        S1[Session 1 - CRITICAL - Email 4]
        S2[Session 2 - HIGH - Email 1]
        S3[Session 3 - HIGH - Email 3]
        S4[Session 4 - NORMAL - Email 2]
    end
    
    subgraph Processing ["⚡ Session Processing"]
        SP[Session Processor]
        SC[SMTP Connection]
        SM[Session Manager]
    end
    
    E1 --> SQ
    E2 --> SQ
    E3 --> SQ
    E4 --> SQ
    
    SQ --> S1
    SQ --> S2  
    SQ --> S3
    SQ --> S4
    
    S1 --> SP
    S2 --> SP
    S3 --> SP
    S4 --> SP
    
    SP --> SC
    SC --> SM
    
    style S1 fill:#ffcdd2
    style S2 fill:#fff3e0
    style S3 fill:#e8f5e8
    style SP fill:#e1f5fe
    style SC fill:#f3e5f5
```

## 🏭 Production Configuration

### Optimal Settings

```typescript
const client = new SMTPOverWSClient({
    url: 'wss://api.siwatsystem.com/smtp',
    apiKey: process.env.MXRELAY_API_KEY,
    
    // Production optimizations
    debug: false,
    sessionBatchTimeout: 5000,      // Longer delay between sessions
    sessionTimeout: 600000,         // 10 minute session timeout
    maxQueueSize: 1000,             // Higher queue capacity
    reconnectInterval: 15000,       // Longer reconnect delay
    maxReconnectAttempts: 3,        // Fewer retries in production
    messageTimeout: 120000          // 2 minute SMTP timeout
});
```

### Graceful Shutdown

```typescript
process.on('SIGTERM', async () => {
    console.log('Shutting down SMTP client...');
    try {
        // Wait for current sessions to complete
        await client.shutdown(60000); // 60 second timeout
        console.log('SMTP client shutdown complete');
    } catch (error) {
        console.error('Forced SMTP client shutdown');
    }
    process.exit(0);
});
```

## 📚 API Reference

### SMTPOverWSClient

#### Methods
- `queueEmail(from, to[], data, options?)` - Queue email for session processing
- `getStats()` - Get session and email statistics
- `getConnectionState()` - Get current connection state
- `getSessionState()` - Get current session processing state
- `getQueueSize()` - Get session queue size
- `shutdown(timeout?)` - Graceful shutdown with session completion

#### Events
- **Session Events**: `sessionQueued`, `sessionStarted`, `sessionCompleted`, `sessionFailed`
- **Email Events**: `emailProcessed`, `emailFailed`  
- **Connection Events**: `connecting`, `connected`, `authenticated`, `disconnected`, `reconnecting`
- **Queue Events**: `queueProcessingStarted`, `queueProcessingCompleted`
- **State Events**: `stateChanged`, `error`

### createTransport(apiKey, options?)

Creates a Nodemailer-compatible transport with dedicated SMTP sessions.

- `apiKey` - Your API key for authentication
- `options` - Optional transport and session configuration

## 🔍 Debugging

### Enable Debug Logging

```typescript
const client = new SMTPOverWSClient({
    url: 'wss://api.siwatsystem.com/smtp',
    apiKey: 'your-api-key',
    debug: true  // Enable comprehensive logging
});

// Debug output includes:
// - Session creation (one per email)
// - SMTP protocol commands and responses
// - Connection state transitions
// - Queue processing details
// - Error stack traces
```

### Monitor Session Processing

```typescript
client.on('sessionStarted', (sessionId, emailCount) => {
    console.log(`🚀 Starting session ${sessionId} with ${emailCount} emails`);
});

client.on('emailProcessed', (sessionId, emailId, responseTime) => {
    console.log(`✅ Email ${emailId} processed in ${responseTime}ms`);
});

client.on('sessionCompleted', (sessionId, results) => {
    const successful = results.filter(r => r.success).length;
    const failed = results.filter(r => !r.success).length;
    console.log(`🎉 Session ${sessionId} complete: ${successful} sent, ${failed} failed`);
});
```

## 🧪 Development

### Setup

```bash
# Clone repository
git clone https://git.siwatsystem.com/siwatsystem-public/mxrelay-consumer.git
cd mxrelay-consumer

# Install dependencies
bun install

# Run examples directly (TypeScript)
bun run examples/nodemailer-transport.ts
```

### Build & Test

```bash
# Build all formats
bun run build

# Run tests
bun test

# Run linting
bun run lint

# Format code
bun run format
```

## 📄 License

MIT License - see [LICENSE](LICENSE) file for details.

## 🆘 Support

- **Issues**: [Git Repository Issues](https://git.siwatsystem.com/siwatsystem-public/mxrelay-consumer/issues)
- **Documentation**: [Project Repository](https://git.siwatsystem.com/siwatsystem-public/mxrelay-consumer)

---

**Built by SiwatSystem** | *Session-based SMTP over WebSocket*