No description
.claude | ||
examples | ||
src | ||
.eslintrc.js | ||
.gitignore | ||
.prettierrc | ||
bun.lock | ||
CLAUDE.md | ||
LICENSE | ||
package-lock.json | ||
package.json | ||
README.md | ||
tsconfig.cjs.json | ||
tsconfig.esm.json | ||
tsconfig.json | ||
tsconfig.types.json |
@siwats/mxrelay-consumer
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
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
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
npm install @siwats/mxrelay-consumer
# or
bun add @siwats/mxrelay-consumer
🚀 Quick Start
Direct Client Usage (New Session-Based API)
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)
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
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
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
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
// 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
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
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:
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 processPROCESSING
- Session currently being processed (mutex locked)FAILED
- Session failed, will retry or rejectCOMPLETED
- Session successfully completed
Connection States
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
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
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
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 processinggetStats()
- Get session and email statisticsgetConnectionState()
- Get current connection stategetSessionState()
- Get current session processing stategetQueueSize()
- Get session queue sizeshutdown(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 authenticationoptions
- Optional transport and session configuration
🔍 Debugging
Enable Debug Logging
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
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
# 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
# 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 file for details.
🆘 Support
- Issues: Git Repository Issues
- Documentation: Project Repository
Built by SiwatSystem | Session-based SMTP over WebSocket