@bernierllc/email-service 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,578 @@
1
+ # @bernierllc/email-service
2
+
3
+ Comprehensive email service orchestrating template management, multi-provider delivery, tracking, and subscriber management.
4
+
5
+ ## Features
6
+
7
+ - **Multi-Provider Support** - SendGrid, Mailgun, AWS SES, and SMTP
8
+ - **Template Management** - Create, update, and render email templates with variable substitution
9
+ - **Delivery Tracking** - Track email delivery, opens, clicks, and bounces
10
+ - **Subscriber Management** - Manage subscribers, lists, and preferences
11
+ - **Queue Integration** - Schedule and send emails in background
12
+ - **Webhook Handling** - Process provider webhooks for delivery events
13
+ - **Database Persistence** - Store templates, delivery records, and subscribers
14
+ - **Analytics** - Comprehensive delivery statistics and reporting
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @bernierllc/email-service
20
+ ```
21
+
22
+ ### Peer Dependencies
23
+
24
+ Install email provider(s) you need:
25
+
26
+ ```bash
27
+ # SendGrid
28
+ npm install @sendgrid/mail
29
+
30
+ # SMTP (included)
31
+ npm install nodemailer
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ### Quick Start
37
+
38
+ ```typescript
39
+ import { EmailService } from '@bernierllc/email-service';
40
+
41
+ const service = new EmailService({
42
+ providers: [
43
+ {
44
+ type: 'smtp',
45
+ smtp: {
46
+ host: 'smtp.gmail.com',
47
+ port: 587,
48
+ secure: false,
49
+ auth: { user: 'you@gmail.com', pass: 'password' }
50
+ }
51
+ }
52
+ ],
53
+ defaultProvider: 'smtp',
54
+ database: {
55
+ type: 'sqlite',
56
+ database: './data/email.db'
57
+ },
58
+ templates: {
59
+ cacheEnabled: true,
60
+ cacheTTL: 3600
61
+ },
62
+ tracking: {
63
+ enabled: true,
64
+ domain: 'yourdomain.com'
65
+ }
66
+ });
67
+
68
+ await service.initialize();
69
+
70
+ // Send an email
71
+ const result = await service.send({
72
+ from: 'noreply@yourdomain.com',
73
+ to: ['user@example.com'],
74
+ subject: 'Welcome!',
75
+ html: '<h1>Hello!</h1><p>Welcome to our service.</p>'
76
+ });
77
+
78
+ console.log(`Email sent: ${result.messageId}`);
79
+ ```
80
+
81
+ ## Core Concepts
82
+
83
+ ### 1. Multi-Provider Email Sending
84
+
85
+ ```typescript
86
+ // Configure multiple providers
87
+ const service = new EmailService({
88
+ providers: [
89
+ {
90
+ type: 'sendgrid',
91
+ apiKey: process.env.SENDGRID_API_KEY
92
+ },
93
+ {
94
+ type: 'mailgun',
95
+ apiKey: process.env.MAILGUN_API_KEY,
96
+ domain: 'mg.yourdomain.com'
97
+ },
98
+ {
99
+ type: 'ses',
100
+ region: 'us-east-1',
101
+ apiKey: process.env.AWS_ACCESS_KEY,
102
+ secretKey: process.env.AWS_SECRET_KEY
103
+ },
104
+ {
105
+ type: 'smtp',
106
+ smtp: {
107
+ host: 'smtp.gmail.com',
108
+ port: 587,
109
+ secure: false,
110
+ auth: {
111
+ user: process.env.SMTP_USER,
112
+ pass: process.env.SMTP_PASS
113
+ }
114
+ }
115
+ }
116
+ ],
117
+ defaultProvider: 'sendgrid',
118
+ database: { type: 'sqlite', database: './email.db' }
119
+ });
120
+
121
+ // Send via specific provider
122
+ await service.send({
123
+ from: 'noreply@yourdomain.com',
124
+ to: ['user@example.com'],
125
+ subject: 'Test',
126
+ html: '<p>Test</p>',
127
+ provider: 'mailgun' // Override default
128
+ });
129
+ ```
130
+
131
+ ### 2. Template Management
132
+
133
+ ```typescript
134
+ // Create template
135
+ const template = await service.createTemplate({
136
+ name: 'welcome-email',
137
+ subject: 'Welcome {{name}}!',
138
+ html: `
139
+ <h1>Hello {{name}}</h1>
140
+ <p>Welcome to {{company}}!</p>
141
+ <p>Your account email is: {{email}}</p>
142
+ `,
143
+ text: 'Hello {{name}}, welcome to {{company}}!'
144
+ });
145
+
146
+ // Send using template
147
+ await service.send({
148
+ from: 'noreply@yourdomain.com',
149
+ to: ['user@example.com'],
150
+ templateId: template.id,
151
+ templateData: {
152
+ name: 'John Doe',
153
+ company: 'Acme Corp',
154
+ email: 'john@example.com'
155
+ }
156
+ });
157
+
158
+ // Update template
159
+ await service.updateTemplate(template.id, {
160
+ subject: 'Welcome to {{company}}, {{name}}!',
161
+ html: '<h1>Welcome {{name}}!</h1>'
162
+ });
163
+
164
+ // Get template
165
+ const retrieved = await service.getTemplate(template.id);
166
+ console.log(retrieved.variables); // ['name', 'company', 'email']
167
+
168
+ // Delete template
169
+ await service.deleteTemplate(template.id);
170
+ ```
171
+
172
+ ### 3. Delivery Tracking
173
+
174
+ ```typescript
175
+ // Send email
176
+ const result = await service.send({
177
+ from: 'noreply@yourdomain.com',
178
+ to: ['user@example.com'],
179
+ subject: 'Order Confirmation',
180
+ html: '<h1>Thanks for your order!</h1>'
181
+ });
182
+
183
+ // Get delivery record
184
+ const delivery = await service.getDelivery(result.deliveryId);
185
+ console.log(delivery.status); // 'sent', 'delivered', 'opened', 'clicked', etc.
186
+
187
+ // Track opens
188
+ await service.trackOpen(result.deliveryId);
189
+
190
+ // Track clicks
191
+ await service.trackClick(result.deliveryId);
192
+
193
+ // Get statistics
194
+ const stats = await service.getDeliveryStats();
195
+ console.log(`Open rate: ${stats.openRate}%`);
196
+ console.log(`Click rate: ${stats.clickRate}%`);
197
+ console.log(`Bounce rate: ${stats.bounceRate}%`);
198
+
199
+ // Get statistics for date range
200
+ const monthStats = await service.getDeliveryStats(
201
+ new Date('2025-01-01'),
202
+ new Date('2025-01-31')
203
+ );
204
+ ```
205
+
206
+ ### 4. Subscriber Management
207
+
208
+ ```typescript
209
+ // Create subscriber
210
+ const subscriber = await service.createSubscriber({
211
+ email: 'user@example.com',
212
+ name: 'John Doe',
213
+ lists: ['newsletter', 'updates'],
214
+ tags: ['premium', 'early-adopter'],
215
+ metadata: {
216
+ source: 'website',
217
+ signupDate: new Date()
218
+ }
219
+ });
220
+
221
+ // Get subscriber
222
+ const found = await service.getSubscriber(subscriber.id);
223
+
224
+ // Update subscriber
225
+ await service.updateSubscriber(subscriber.id, {
226
+ name: 'John Smith',
227
+ tags: ['premium', 'verified']
228
+ });
229
+
230
+ // Unsubscribe
231
+ await service.unsubscribe('user@example.com');
232
+
233
+ // Create subscriber list
234
+ const list = await service.createList(
235
+ 'Monthly Newsletter',
236
+ 'Our monthly product updates and news'
237
+ );
238
+ ```
239
+
240
+ ### 5. Scheduled Emails
241
+
242
+ ```typescript
243
+ // Configure queue
244
+ const service = new EmailService({
245
+ providers: [/* ... */],
246
+ database: { type: 'sqlite', database: './email.db' },
247
+ queue: {
248
+ backend: new MemoryBackend(),
249
+ concurrency: 5,
250
+ retryPolicy: {
251
+ maxAttempts: 3,
252
+ backoff: { type: 'exponential', delay: 1000 }
253
+ }
254
+ }
255
+ });
256
+
257
+ // Schedule for later
258
+ await service.send({
259
+ from: 'noreply@yourdomain.com',
260
+ to: ['user@example.com'],
261
+ subject: 'Reminder',
262
+ html: '<p>This is your reminder</p>',
263
+ scheduledAt: new Date(Date.now() + 24 * 60 * 60 * 1000) // 24 hours
264
+ });
265
+
266
+ // Send with priority
267
+ await service.send({
268
+ from: 'alerts@yourdomain.com',
269
+ to: ['admin@yourdomain.com'],
270
+ subject: 'URGENT: System Alert',
271
+ html: '<p>Critical alert</p>',
272
+ priority: 'high' // high, normal, low
273
+ });
274
+ ```
275
+
276
+ ### 6. Bulk Sending
277
+
278
+ ```typescript
279
+ const results = await service.sendBulk([
280
+ {
281
+ from: 'noreply@yourdomain.com',
282
+ to: ['user1@example.com'],
283
+ subject: 'Personalized for User 1',
284
+ html: '<p>Hello User 1</p>'
285
+ },
286
+ {
287
+ from: 'noreply@yourdomain.com',
288
+ to: ['user2@example.com'],
289
+ subject: 'Personalized for User 2',
290
+ html: '<p>Hello User 2</p>'
291
+ }
292
+ ]);
293
+
294
+ results.forEach((result, index) => {
295
+ console.log(`Email ${index + 1}: ${result.success ? 'sent' : 'failed'}`);
296
+ });
297
+ ```
298
+
299
+ ### 7. Webhook Handling
300
+
301
+ ```typescript
302
+ // Register webhook handler
303
+ service.registerWebhookHandler('delivered', async (event) => {
304
+ console.log(`Email ${event.messageId} was delivered`);
305
+ // Update your database, trigger workflows, etc.
306
+ });
307
+
308
+ service.registerWebhookHandler('opened', async (event) => {
309
+ console.log(`Email ${event.messageId} was opened`);
310
+ // Track user engagement
311
+ });
312
+
313
+ service.registerWebhookHandler('clicked', async (event) => {
314
+ console.log(`Link clicked in ${event.messageId}`);
315
+ // Track click-through rates
316
+ });
317
+
318
+ service.registerWebhookHandler('bounced', async (event) => {
319
+ console.log(`Email ${event.messageId} bounced`);
320
+ // Update subscriber status
321
+ await service.unsubscribe(event.data.email);
322
+ });
323
+
324
+ // Process webhook from provider
325
+ await service.handleWebhook({
326
+ provider: 'sendgrid',
327
+ event: 'delivered',
328
+ messageId: 'msg_123',
329
+ timestamp: new Date(),
330
+ data: { /* provider-specific data */ }
331
+ });
332
+ ```
333
+
334
+ ## API Reference
335
+
336
+ ### EmailService
337
+
338
+ #### Constructor
339
+
340
+ ```typescript
341
+ new EmailService(config: EmailServiceConfig)
342
+ ```
343
+
344
+ **Configuration:**
345
+
346
+ ```typescript
347
+ interface EmailServiceConfig {
348
+ providers: ProviderConfig[];
349
+ defaultProvider?: EmailProvider;
350
+ database: DatabaseConfig;
351
+ queue?: QueueOptions;
352
+ templates?: {
353
+ cacheEnabled?: boolean;
354
+ cacheTTL?: number;
355
+ };
356
+ tracking?: {
357
+ enabled?: boolean;
358
+ domain?: string;
359
+ };
360
+ compliance?: {
361
+ unsubscribeLink?: boolean;
362
+ footerRequired?: boolean;
363
+ };
364
+ }
365
+
366
+ interface ProviderConfig {
367
+ type: 'sendgrid' | 'mailgun' | 'ses' | 'smtp';
368
+ apiKey?: string;
369
+ domain?: string;
370
+ region?: string;
371
+ smtp?: SMTPConfig;
372
+ rateLimit?: RateLimitConfig;
373
+ }
374
+ ```
375
+
376
+ #### Methods
377
+
378
+ ##### Email Sending
379
+
380
+ - `send(request: EmailRequest): Promise<SendEmailResult>` - Send email
381
+ - `sendBulk(requests: EmailRequest[]): Promise<SendEmailResult[]>` - Send multiple emails
382
+
383
+ ##### Template Management
384
+
385
+ - `createTemplate(request: TemplateCreateRequest): Promise<EmailTemplate>` - Create template
386
+ - `getTemplate(id: string): Promise<EmailTemplate | null>` - Get template
387
+ - `updateTemplate(id: string, update: TemplateUpdateRequest): Promise<EmailTemplate>` - Update template
388
+ - `deleteTemplate(id: string): Promise<boolean>` - Delete template
389
+
390
+ ##### Delivery Tracking
391
+
392
+ - `getDelivery(id: string): Promise<DeliveryRecord | null>` - Get delivery record
393
+ - `trackOpen(deliveryId: string): Promise<void>` - Track email open
394
+ - `trackClick(deliveryId: string): Promise<void>` - Track link click
395
+ - `getDeliveryStats(startDate?: Date, endDate?: Date): Promise<DeliveryStats>` - Get statistics
396
+
397
+ ##### Subscriber Management
398
+
399
+ - `createSubscriber(request: SubscriberCreateRequest): Promise<Subscriber>` - Create subscriber
400
+ - `getSubscriber(id: string): Promise<Subscriber | null>` - Get subscriber
401
+ - `updateSubscriber(id: string, update: SubscriberUpdateRequest): Promise<Subscriber>` - Update subscriber
402
+ - `unsubscribe(email: string): Promise<boolean>` - Unsubscribe email
403
+ - `createList(name: string, description?: string): Promise<SubscriberList>` - Create list
404
+
405
+ ##### Webhook Handling
406
+
407
+ - `registerWebhookHandler(event: string, handler: WebhookHandler): void` - Register handler
408
+ - `handleWebhook(event: WebhookEvent): Promise<void>` - Process webhook
409
+
410
+ ##### Lifecycle
411
+
412
+ - `initialize(): Promise<void>` - Initialize service
413
+ - `shutdown(): Promise<void>` - Cleanup and shutdown
414
+
415
+ ## Type Definitions
416
+
417
+ ```typescript
418
+ interface EmailRequest {
419
+ from: EmailAddress | string;
420
+ to: EmailAddress[] | string[];
421
+ cc?: EmailAddress[] | string[];
422
+ bcc?: EmailAddress[] | string[];
423
+ subject: string;
424
+ html?: string;
425
+ text?: string;
426
+ templateId?: string;
427
+ templateData?: TemplateContext;
428
+ attachments?: EmailAttachment[];
429
+ headers?: Record<string, string>;
430
+ tags?: string[];
431
+ metadata?: Record<string, any>;
432
+ priority?: 'high' | 'normal' | 'low';
433
+ scheduledAt?: Date;
434
+ provider?: EmailProvider;
435
+ }
436
+
437
+ interface SendEmailResult {
438
+ success: boolean;
439
+ messageId?: string;
440
+ deliveryId?: string;
441
+ provider?: EmailProvider;
442
+ error?: string;
443
+ timestamp: Date;
444
+ queuedForLater?: boolean;
445
+ }
446
+
447
+ interface DeliveryStats {
448
+ total: number;
449
+ sent: number;
450
+ delivered: number;
451
+ opened: number;
452
+ clicked: number;
453
+ bounced: number;
454
+ failed: number;
455
+ openRate: number;
456
+ clickRate: number;
457
+ bounceRate: number;
458
+ }
459
+
460
+ enum DeliveryStatus {
461
+ QUEUED = 'queued',
462
+ SENDING = 'sending',
463
+ SENT = 'sent',
464
+ DELIVERED = 'delivered',
465
+ OPENED = 'opened',
466
+ CLICKED = 'clicked',
467
+ BOUNCED = 'bounced',
468
+ FAILED = 'failed',
469
+ UNSUBSCRIBED = 'unsubscribed'
470
+ }
471
+ ```
472
+
473
+ ## Best Practices
474
+
475
+ ### Error Handling
476
+
477
+ ```typescript
478
+ try {
479
+ await service.send(emailRequest);
480
+ } catch (error) {
481
+ if (error instanceof ProviderError) {
482
+ // Switch to backup provider
483
+ console.error('Provider failed:', error.provider);
484
+ } else if (error instanceof TemplateError) {
485
+ // Template rendering failed
486
+ console.error('Template error:', error.message);
487
+ } else if (error instanceof DeliveryError) {
488
+ // Delivery failed
489
+ console.error('Delivery error:', error.message);
490
+ }
491
+ }
492
+ ```
493
+
494
+ ### Template Variables
495
+
496
+ ```typescript
497
+ // Always validate template data matches template variables
498
+ const template = await service.getTemplate(templateId);
499
+ const missingVars = template.variables.filter(
500
+ v => !(v in templateData)
501
+ );
502
+ if (missingVars.length > 0) {
503
+ throw new Error(`Missing template variables: ${missingVars.join(', ')}`);
504
+ }
505
+ ```
506
+
507
+ ### Rate Limiting
508
+
509
+ ```typescript
510
+ // Configure per-provider rate limits
511
+ const service = new EmailService({
512
+ providers: [
513
+ {
514
+ type: 'sendgrid',
515
+ apiKey: process.env.SENDGRID_API_KEY,
516
+ rateLimit: {
517
+ maxPerSecond: 100,
518
+ maxPerMinute: 5000,
519
+ maxPerHour: 100000
520
+ }
521
+ }
522
+ ],
523
+ // ...
524
+ });
525
+ ```
526
+
527
+ ### Monitoring
528
+
529
+ ```typescript
530
+ // Regular health monitoring
531
+ setInterval(async () => {
532
+ const stats = await service.getDeliveryStats();
533
+
534
+ if (stats.bounceRate > 5) {
535
+ console.warn('High bounce rate detected:', stats.bounceRate);
536
+ }
537
+
538
+ if (stats.failed > 100) {
539
+ console.error('High failure count:', stats.failed);
540
+ }
541
+ }, 60000); // Every minute
542
+ ```
543
+
544
+ ## Dependencies
545
+
546
+ This package orchestrates:
547
+
548
+ - [@bernierllc/email-sender](../../core/email-sender) - Multi-provider email delivery
549
+ - [@bernierllc/email-parser](../../core/email-parser) - Email content parsing
550
+ - [@bernierllc/template-engine](../../core/template-engine) - Template rendering
551
+ - [@bernierllc/queue-manager](../../core/queue-manager) - Background job processing
552
+ - [@bernierllc/webhook-validator](../../core/webhook-validator) - Webhook verification
553
+ - [@bernierllc/database-adapter](../../core/database-adapter) - Data persistence
554
+ - [@bernierllc/logger](../../core/logger) - Structured logging
555
+ - [@bernierllc/config-manager](../../core/config-manager) - Configuration management
556
+
557
+ ## Integration Status
558
+
559
+ - **Logger**: Integrated - Uses @bernierllc/logger for structured logging
560
+ - **Docs-Suite**: Ready - Markdown documentation available
561
+ - **NeverHub integration**: Planned - Service discovery and event bus integration with @bernierllc/neverhub-adapter
562
+
563
+ ## Examples
564
+
565
+ See the [examples directory](./examples) for complete working examples:
566
+
567
+ - `basic-smtp.ts` - Basic SMTP email sending
568
+ - `templates.ts` - Template management and rendering
569
+ - `tracking.ts` - Delivery tracking and analytics
570
+ - `subscribers.ts` - Subscriber management
571
+ - `webhooks.ts` - Webhook handling
572
+ - `multi-provider.ts` - Multi-provider configuration
573
+
574
+ ## License
575
+
576
+ Copyright (c) 2025 Bernier LLC. All rights reserved.
577
+
578
+ This package is proprietary software licensed for use only within the scope of the project it was delivered for.
@@ -0,0 +1,39 @@
1
+ import type { EmailServiceConfig, EmailRequest, SendEmailResult, EmailTemplate, TemplateCreateRequest, TemplateUpdateRequest, DeliveryRecord, DeliveryStats, Subscriber, SubscriberCreateRequest, SubscriberUpdateRequest, SubscriberList, WebhookEvent, WebhookHandler } from './types.js';
2
+ export declare class EmailService {
3
+ private db;
4
+ private queue?;
5
+ private templateEngine;
6
+ private emailSenders;
7
+ private logger;
8
+ private config;
9
+ private webhookHandlers;
10
+ private templateCache;
11
+ constructor(config: EmailServiceConfig);
12
+ private initializeProviders;
13
+ initialize(): Promise<void>;
14
+ private createTables;
15
+ send(request: EmailRequest): Promise<SendEmailResult>;
16
+ private scheduleEmail;
17
+ sendBulk(requests: EmailRequest[]): Promise<SendEmailResult[]>;
18
+ createTemplate(request: TemplateCreateRequest): Promise<EmailTemplate>;
19
+ getTemplate(id: string): Promise<EmailTemplate | null>;
20
+ updateTemplate(id: string, update: TemplateUpdateRequest): Promise<EmailTemplate>;
21
+ deleteTemplate(id: string): Promise<boolean>;
22
+ private renderTemplate;
23
+ private extractVariables;
24
+ getDelivery(id: string): Promise<DeliveryRecord | null>;
25
+ trackOpen(deliveryId: string): Promise<void>;
26
+ trackClick(deliveryId: string): Promise<void>;
27
+ getDeliveryStats(startDate?: Date, endDate?: Date): Promise<DeliveryStats>;
28
+ private parseDeliveryRecord;
29
+ createSubscriber(request: SubscriberCreateRequest): Promise<Subscriber>;
30
+ getSubscriber(id: string): Promise<Subscriber | null>;
31
+ updateSubscriber(id: string, update: SubscriberUpdateRequest): Promise<Subscriber>;
32
+ unsubscribe(email: string): Promise<boolean>;
33
+ private parseSubscriber;
34
+ createList(name: string, description?: string): Promise<SubscriberList>;
35
+ registerWebhookHandler(event: string, handler: WebhookHandler): void;
36
+ handleWebhook(event: WebhookEvent): Promise<void>;
37
+ shutdown(): Promise<void>;
38
+ private generateId;
39
+ }