@bernierllc/email-sender-manager
v1.2.0
Published
Database-backed email sender configuration management with provider verification integration
Readme
@bernierllc/email-sender-manager
Database-backed email sender configuration management with provider verification integration. Manage, verify, and intelligently select email sender configurations for your applications.
Installation
npm install @bernierllc/email-sender-managerFeatures
- Database-Backed Storage: Persistent sender configurations with transaction support
- Provider Integration: Verify sender configurations with email providers (SendGrid, Mailgun, etc.)
- Smart Sender Selection: Intelligent sender selection based on domain matching, priority, or round-robin strategies
- Bootstrap System: Automatic database schema creation and default sender seeding
- Full TypeScript Support: Complete type definitions with strict mode compliance
- Comprehensive Validation: Email format validation, domain restrictions, and configuration checks
Quick Start
import { EmailSenderManager } from '@bernierllc/email-sender-manager';
import type { DatabaseAdapter } from '@bernierllc/email-sender-manager';
// Implement your database adapter
const databaseAdapter: DatabaseAdapter = {
async execute(query: string, params?: unknown[]): Promise<void> {
// Your database execute logic
},
async query<T = unknown>(query: string, params?: unknown[]): Promise<T[]> {
// Your database query logic
},
async queryOne<T = unknown>(query: string, params?: unknown[]): Promise<T | null> {
// Your database queryOne logic
},
async transaction<T>(callback: (adapter: DatabaseAdapter) => Promise<T>): Promise<T> {
// Your transaction logic
},
};
// Initialize manager
const manager = new EmailSenderManager({
database: databaseAdapter,
bootstrap: {
enabled: true,
createTables: true,
seedDefaultSenders: true,
defaultSenders: [
{
name: 'Default Sender',
fromEmail: '[email protected]',
fromName: 'My App',
provider: 'sendgrid',
priority: 1,
},
],
environment: 'production',
},
selection: {
strategy: 'domain_match',
fallbackToDefault: true,
domainMatchingRules: [],
},
});
// Bootstrap database
await manager.bootstrapDatabase();Core Concepts
Sender Configuration
A sender configuration represents an email address that can send emails through your application:
interface SenderConfiguration {
id: string;
name: string;
description?: string;
fromEmail: string;
fromName: string;
replyToEmail?: string;
replyToName?: string;
provider: string;
providerSenderId?: string;
providerMetadata?: Record<string, unknown>;
isVerified: boolean;
verificationStatus: 'pending' | 'verified' | 'failed' | 'expired';
lastVerifiedAt?: Date;
isDefault: boolean;
isActive: boolean;
priority: number; // Lower = higher priority
allowedDomains?: string[];
domain: string;
createdAt: Date;
updatedAt: Date;
createdBy: string;
lastModifiedBy: string;
}Provider Integration
Providers verify sender configurations with email service providers:
interface SenderProviderPlugin {
readonly providerId: string;
readonly name: string;
validateSender(email: string): Promise<ProviderValidationResult>;
getVerifiedSenders(): Promise<ProviderSender[]>;
syncSender(sender: SenderConfiguration): Promise<SyncResult>;
getCapabilities(): string[];
}Usage Examples
Creating Senders
// Create a new sender
const sender = await manager.createSender({
name: 'Marketing Team',
fromEmail: '[email protected]',
fromName: 'Marketing Department',
replyToEmail: '[email protected]',
provider: 'sendgrid',
priority: 10,
allowedDomains: ['example.com', '*.example.com'],
createdBy: 'admin-user',
});
console.log(`Created sender: ${sender.id}`);Updating Senders
// Update sender configuration
const updated = await manager.updateSender(sender.id, {
name: 'Updated Name',
priority: 5,
isActive: true,
lastModifiedBy: 'admin-user',
});Listing Senders
// List all active senders
const activeSenders = await manager.listSenders({
isActive: true,
orderBy: 'priority',
orderDirection: 'asc',
});
// List verified senders for a specific provider
const verifiedSenders = await manager.listSenders({
provider: 'sendgrid',
isVerified: true,
limit: 10,
offset: 0,
});
// Paginate through results
console.log(`Total: ${activeSenders.total}, Showing: ${activeSenders.items.length}`);
console.log(`Has more: ${activeSenders.hasMore}`);Sender Selection
// Select best sender for an email
const bestSender = await manager.selectBestSender('[email protected]', {
strategy: 'domain_match', // or 'priority' or 'round_robin'
allowUnverified: false,
});
if (bestSender) {
console.log(`Using sender: ${bestSender.fromEmail} (priority: ${bestSender.priority})`);
}
// Select sender for specific provider
const sendgridSender = await manager.selectBestSender('[email protected]', {
provider: 'sendgrid',
strategy: 'priority',
});Provider Verification
import type { SenderProviderPlugin } from '@bernierllc/email-sender-manager';
// Implement a provider plugin
class SendGridProvider implements SenderProviderPlugin {
readonly providerId = 'sendgrid';
readonly name = 'SendGrid';
constructor(private apiKey: string) {}
async validateSender(email: string): Promise<ProviderValidationResult> {
// Call SendGrid API to verify sender
const verifiedSenders = await this.getVerifiedSenders();
const match = verifiedSenders.find((s) => s.email === email);
return {
isVerified: Boolean(match?.isVerified),
verificationStatus: match?.verificationStatus || 'not_found',
lastChecked: new Date(),
metadata: { providerId: match?.id },
};
}
async getVerifiedSenders(): Promise<ProviderSender[]> {
// Fetch from SendGrid API
return [];
}
async syncSender(sender: SenderConfiguration): Promise<SyncResult> {
// Sync with SendGrid
return { success: true, senderId: sender.id, synced: true };
}
getCapabilities(): string[] {
return ['verification', 'sync'];
}
}
// Register provider
const provider = new SendGridProvider(process.env.SENDGRID_API_KEY!);
await manager.registerProvider(provider);
// Verify a sender
const verification = await manager.verifySender(sender.id);
console.log(`Verified: ${verification.isVerified}`);Bootstrap Configuration
const manager = new EmailSenderManager({
database: databaseAdapter,
bootstrap: {
enabled: true,
createTables: true,
seedDefaultSenders: true,
defaultSenders: [
{
name: 'Default Sender',
fromEmail: '[email protected]',
fromName: 'My Application',
provider: 'sendgrid',
priority: 1,
},
{
name: 'Marketing Sender',
fromEmail: '[email protected]',
fromName: 'Marketing Team',
provider: 'sendgrid',
priority: 10,
allowedDomains: ['example.com'],
},
],
environment: 'production',
},
});
// Run bootstrap
const result = await manager.bootstrapDatabase();
console.log(result.message);
// Output: "Bootstrap completed successfully. Tables created: true, Senders seeded: 2"Selection Strategies
Domain Matching
Selects senders based on domain relationships:
- Exact domain match:
example.comsender for[email protected] - Subdomain match:
example.comsender for[email protected] - Allowed domains: Senders with
allowedDomainsconfiguration - Fallback to priority: If no domain match found
const sender = await manager.selectBestSender('[email protected]', {
strategy: 'domain_match',
});Priority-Based
Selects sender with lowest priority number (higher priority):
const sender = await manager.selectBestSender('[email protected]', {
strategy: 'priority',
});Round-Robin
Distributes selections evenly across available senders:
const sender = await manager.selectBestSender('[email protected]', {
strategy: 'round_robin',
});Domain Utilities
import {
extractDomain,
isDomainMatch,
isValidEmail,
getParentDomain,
isSubdomainOf,
} from '@bernierllc/email-sender-manager';
// Extract domain from email
const domain = extractDomain('[email protected]'); // "example.com"
// Check domain matching with wildcards
const matches = isDomainMatch('app.example.com', '*.example.com'); // true
// Validate email format
const valid = isValidEmail('[email protected]'); // true
// Get parent domain
const parent = getParentDomain('app.example.com'); // "example.com"
// Check subdomain relationship
const isSub = isSubdomainOf('app.example.com', 'example.com'); // trueValidation
import { validateCreateSenderRequest } from '@bernierllc/email-sender-manager';
// Validate sender creation request
const validation = validateCreateSenderRequest({
name: 'Test Sender',
fromEmail: '[email protected]',
fromName: 'Test',
provider: 'sendgrid',
createdBy: 'admin',
});
if (validation.errors.length > 0) {
console.error('Validation errors:', validation.errors);
}
if (validation.warnings.length > 0) {
console.warn('Validation warnings:', validation.warnings);
}Database Schema
The package automatically creates the following database schema:
CREATE TABLE IF NOT EXISTS email_senders (
id VARCHAR(255) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
from_email VARCHAR(255) NOT NULL UNIQUE,
from_name VARCHAR(255) NOT NULL,
reply_to_email VARCHAR(255),
reply_to_name VARCHAR(255),
provider VARCHAR(100) NOT NULL,
provider_sender_id VARCHAR(255),
provider_metadata JSON,
is_verified BOOLEAN DEFAULT FALSE,
verification_status VARCHAR(50) DEFAULT 'pending',
last_verified_at TIMESTAMP NULL,
verification_error TEXT,
is_default BOOLEAN DEFAULT FALSE,
is_active BOOLEAN DEFAULT TRUE,
priority INTEGER DEFAULT 100,
allowed_domains JSON,
domain VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
created_by VARCHAR(255),
last_modified_by VARCHAR(255),
INDEX idx_email_senders_from_email (from_email),
INDEX idx_email_senders_domain (domain),
INDEX idx_email_senders_provider (provider),
INDEX idx_email_senders_active (is_active),
INDEX idx_email_senders_default (is_default),
INDEX idx_email_senders_priority (priority)
);API Reference
EmailSenderManager
Main class for managing sender configurations.
Constructor
constructor(config: SenderManagerConfig)Methods
bootstrapDatabase(): Promise<BootstrapResult>- Initialize database and seed default senderscreateSender(request: CreateSenderRequest): Promise<SenderConfiguration>- Create new senderupdateSender(id: string, updates: UpdateSenderRequest): Promise<SenderConfiguration>- Update existing senderdeleteSender(id: string): Promise<void>- Delete sendergetSender(id: string): Promise<SenderConfiguration | null>- Get sender by IDlistSenders(options?: ListSendersOptions): Promise<PaginatedResult<SenderConfiguration>>- List senders with filtersvalidateSender(id: string): Promise<SenderValidationResult>- Validate sender configurationverifySender(id: string, provider?: string): Promise<VerificationResult>- Verify sender with providerselectBestSender(fromEmail: string, options?: SelectionOptions): Promise<SenderConfiguration | null>- Select optimal sendergetDefaultSender(provider?: string): Promise<SenderConfiguration | null>- Get default sendergetSendersByDomain(domain: string): Promise<SenderConfiguration[]>- Get senders for domainregisterProvider(provider: SenderProviderPlugin): Promise<void>- Register provider pluginsyncWithProviders(): Promise<SyncResult[]>- Sync all senders with providerscleanupInactiveSenders(): Promise<CleanupResult>- Remove inactive senders
Integration Status
- Logger: Not applicable - Core utility package with database-backed state (logging handled at service layer)
- Docs-Suite: Ready - Comprehensive API documentation with TypeDoc comments exported
- NeverHub: Not applicable - Core utility package focused on email sender configuration management. NeverHub integration is appropriate at the service layer where sender selection events and metrics would be published to the event bus. This package provides the foundational primitives for sender management without environment-dependent behavior.
Testing
The package includes comprehensive tests with 90%+ coverage:
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Run tests once
npm run test:runLicense
Copyright (c) 2025 Bernier LLC
This file is licensed to the client under a limited-use license. The client may use and modify this code only within the scope of the project it was delivered for. Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
See Also
- @bernierllc/email-sender - Email sending abstraction
- @bernierllc/logger - Logging utilities
- @bernierllc/retry-policy - Retry logic utilities
