No description
Find a file
2025-08-19 02:30:52 +07:00
.claude initial commit 2025-08-18 22:22:04 +07:00
examples feat: Update Nodemailer transport to support optional host and apiKey parameters, enhance error handling, and improve example usage 2025-08-19 02:30:52 +07:00
src feat: Update Nodemailer transport to support optional host and apiKey parameters, enhance error handling, and improve example usage 2025-08-19 02:30:52 +07:00
.eslintrc.js initial commit 2025-08-18 22:22:04 +07:00
.gitignore feat: Implement SMTP over WebSocket client with error handling and Nodemailer transport 2025-08-19 00:52:18 +07:00
.prettierrc initial commit 2025-08-18 22:22:04 +07:00
bun.lock Refactor code structure for improved readability and maintainability 2025-08-19 00:58:48 +07:00
CLAUDE.md feat: Update Nodemailer transport to support optional host and apiKey parameters, enhance error handling, and improve example usage 2025-08-19 02:30:52 +07:00
LICENSE initial commit 2025-08-18 22:22:04 +07:00
package-lock.json initial commit 2025-08-18 22:22:04 +07:00
package.json Refactor code structure for improved readability and maintainability 2025-08-19 00:58:48 +07:00
README.md feat: Update Nodemailer transport to support optional host and apiKey parameters, enhance error handling, and improve example usage 2025-08-19 02:30:52 +07:00
tsconfig.cjs.json initial commit 2025-08-18 22:22:04 +07:00
tsconfig.esm.json initial commit 2025-08-18 22:22:04 +07:00
tsconfig.json initial commit 2025-08-18 22:22:04 +07:00
tsconfig.types.json initial commit 2025-08-18 22:22:04 +07:00

@siwatsystem/mxrelay-consumer

TypeScript License: MIT

A production-ready TypeScript client library for SMTP over WebSocket protocol with intelligent queue management, automatic reconnection, and comprehensive error handling. Includes full Nodemailer transport compatibility.

Features

Intelligent Queue Management

  • Automatic WebSocket connection when messages are queued
  • Priority-based message processing (CRITICAL > HIGH > NORMAL > LOW)
  • Configurable queue limits and overflow protection
  • Auto-disconnect when queue is empty

Robust Connection Handling

  • Automatic reconnection with exponential backoff
  • Connection state management and lifecycle
  • Heartbeat monitoring and timeout handling
  • Graceful connection recovery

High Performance

  • Efficient SMTP channel cycling per message
  • Minimal resource usage with smart connection management
  • Concurrent message processing support
  • Optimized WebSocket communication

Enterprise-Grade Reliability

  • Comprehensive SMTP error handling with meaningful messages
  • Timeout management for all operations
  • Retry logic with configurable attempts
  • Structured error classification

Nodemailer Integration

  • Full Nodemailer transport compatibility
  • Transparent bridge for all email features
  • Support for attachments, HTML, multipart messages
  • Standard Nodemailer API compatibility

Installation

npm install @siwatsystem/mxrelay-consumer
# or
bun add @siwatsystem/mxrelay-consumer

Quick Start

Direct Client Usage

import { SMTPOverWSClient } from '@siwatsystem/mxrelay-consumer';

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

// Send SMTP commands directly
try {
    const response = await client.sendSMTPCommand(`
        MAIL FROM: <sender@example.com>
        RCPT TO: <recipient@example.com>
        DATA
        Subject: Test Email
        
        Hello from SMTP over WebSocket!
        .
        QUIT
    `);
    console.log('Email sent:', response);
} catch (error) {
    console.error('SMTP error:', error.message);
} finally {
    await client.shutdown();
}

Nodemailer Transport

import nodemailer from 'nodemailer';
import { createTransport } from '@siwatsystem/mxrelay-consumer';

// Create transport (uses defaults: api.siwatsystem.com:443 secure)
const transport = createTransport('your-api-key');

// Or with custom options
const transport = createTransport('your-api-key', {
    host: 'custom.server.com',
    port: 80,
    secure: false,
    debug: true
});

const transporter = nodemailer.createTransporter(transport);

// Send email using standard Nodemailer API
const info = await transporter.sendMail({
    from: 'sender@example.com',
    to: 'recipient@example.com',
    subject: 'Test Email via SMTP WebSocket',
    text: 'Plain text version',
    html: '<h1>HTML version</h1>',
    attachments: [
        {
            filename: 'document.pdf',
            path: './document.pdf'
        }
    ]
});

console.log('Message sent:', info.messageId);
await transport.close();

Configuration

Client Configuration

interface SMTPClientConfig {
    url: string;                        // WebSocket server URL
    apiKey: string;                     // Authentication API key
    debug?: boolean;                    // Enable debug logging (default: false)
    maxQueueSize?: number;              // Queue capacity limit (default: 1000)
    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;            // Message timeout (default: 60000ms)
    heartbeatInterval?: number;         // Heartbeat interval (default: 30000ms)
    maxConcurrentMessages?: number;     // Concurrent limit (default: 1)
}

Transport Configuration

interface TransportOptions {
    host?: string;                      // Server host (default: 'api.siwatsystem.com')
    port?: number;                      // Server port (default: 443)
    secure?: boolean;                   // Use wss:// (default: true)
    debug?: boolean;                    // Enable debug mode (default: false)
    // ... other SMTPClientConfig options
}

Advanced Usage

Priority-Based Messaging

import { MessagePriority } from '@siwatsystem/mxrelay-consumer';

// High priority (processed first)
await client.sendSMTPCommand('URGENT EMAIL DATA', {
    priority: MessagePriority.HIGH,
    timeout: 30000
});

// Critical priority (highest)
await client.sendSMTPCommand('CRITICAL ALERT EMAIL', {
    priority: MessagePriority.CRITICAL
});

Event Monitoring

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

// Queue events
client.on('messageQueued', (messageId, queueSize) => {
    console.log(`Message ${messageId} queued. Queue size: ${queueSize}`);
});

client.on('messageProcessed', (messageId, responseTime) => {
    console.log(`Message ${messageId} processed in ${responseTime}ms`);
});

// Error events
client.on('error', (error) => console.error('Client error:', error));

Statistics and Monitoring

const stats = client.getStats();
console.log('Client Statistics:', {
    messagesQueued: stats.messagesQueued,
    messagesProcessed: stats.messagesProcessed,
    messagesFailed: stats.messagesFailed,
    averageResponseTime: stats.averageResponseTime,
    queueSize: stats.queueSize
});

Error Handling

SMTP Error Detection

The transport properly detects and categorizes SMTP errors:

try {
    await transporter.sendMail({
        from: 'unauthorized@domain.com',  // Invalid sender
        to: 'recipient@example.com',
        subject: 'Test'
    });
} catch (error) {
    console.error('SMTP Error:', error.message);
    // Output: "Sender not authorized: Sender domain not authorized for your IP or subnet"
    
    console.log('Error details:', {
        smtpCode: error.context.smtpCode,        // "550"
        rejectedRecipients: error.context.rejectedRecipients
    });
}

Error Classification

import { 
    ConnectionError, 
    AuthenticationError, 
    MessageError,
    TimeoutError 
} from '@siwatsystem/mxrelay-consumer';

try {
    await client.sendSMTPCommand('MAIL FROM: <test@example.com>');
} catch (error) {
    if (error instanceof ConnectionError) {
        console.error('Connection failed:', error.message);
    } else if (error instanceof AuthenticationError) {
        console.error('Authentication failed:', error.message);
    } else if (error instanceof MessageError) {
        console.error('SMTP error:', error.message, 'Code:', error.context.smtpCode);
    } else if (error instanceof TimeoutError) {
        console.error('Operation timed out:', error.message);
    }
}

Connection States

The client manages connection states automatically:

  • DISCONNECTED - No connection
  • CONNECTING - Establishing WebSocket connection
  • CONNECTED - WebSocket connected, authentication pending
  • AUTHENTICATING - Sending credentials
  • AUTHENTICATED - Ready for SMTP operations
  • CHANNEL_OPENING - Opening SMTP channel
  • CHANNEL_READY - SMTP channel active
  • CHANNEL_CLOSED - SMTP channel closed
  • RECONNECTING - Attempting reconnection
  • FAILED - Connection failed, max retries reached

Development

This project uses Bun as the primary runtime. TypeScript files can be run directly without building.

Setup

# Clone repository
git clone https://git.siwatsystem.com/siwat/mxrelay-consumer.git
cd mxrelay-consumer

# Install dependencies
bun install

# Run examples directly
bun run examples/nodemailer-transport.ts

# Run with environment variable
MXRELAY_API_KEY=your-key bun run examples/nodemailer-transport.ts

Build & Test

# Build (optional - bun runs TypeScript directly)
bun run build

# Run tests
bun test

# Run linting
bun run lint

# Format code
bun run format

Protocol Implementation

WebSocket Message Types

  • AUTHENTICATE / AUTHENTICATE_RESPONSE - Authentication flow
  • SMTP_CHANNEL_OPEN / SMTP_CHANNEL_READY - Channel management
  • SMTP_CHANNEL_CLOSED / SMTP_CHANNEL_ERROR - Channel lifecycle
  • SMTP_TO_SERVER / SMTP_FROM_SERVER - SMTP data exchange

Connection Flow

  1. WebSocket Connection - Connect to relay server
  2. Authentication - Authenticate using API key
  3. Channel Management - Open SMTP channel per message
  4. Data Transfer - Exchange SMTP commands and responses
  5. Cleanup - Close channel and disconnect when queue empty

Best Practices

Production Configuration

const client = new SMTPOverWSClient({
    url: 'wss://api.siwatsystem.com/smtp',
    apiKey: process.env.MXRELAY_API_KEY,
    
    // Production settings
    debug: false,
    maxQueueSize: 5000,
    reconnectInterval: 10000,
    maxReconnectAttempts: 5,
    messageTimeout: 120000
});

Graceful Shutdown

process.on('SIGTERM', async () => {
    console.log('Shutting down...');
    try {
        await client.shutdown(30000); // 30 second timeout
        console.log('Shutdown complete');
    } catch (error) {
        console.error('Forced shutdown');
    }
    process.exit(0);
});

API Reference

createTransport(apiKey, options?)

Creates a Nodemailer-compatible transport.

  • apiKey - Your API key for authentication
  • options - Optional transport configuration

SMTPOverWSClient

Main client class for direct SMTP operations.

Methods

  • sendSMTPCommand(data, options?) - Send SMTP command
  • getStats() - Get client statistics
  • getConnectionState() - Get current state
  • getQueueSize() - Get queue size
  • shutdown(timeout?) - Graceful shutdown

Events

  • Connection: connecting, connected, authenticated, disconnected
  • Queue: messageQueued, messageProcessed, messageFailed
  • State: stateChanged, error

License

MIT License - see LICENSE file for details.

Support


Built by SiwatSystem