@bernierllc/email-webhook-events 1.0.0

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.
Files changed (97) hide show
  1. package/.eslintrc.cjs +29 -0
  2. package/README.md +349 -0
  3. package/__tests__/EmailWebhookEventsService.test.ts +247 -0
  4. package/__tests__/analytics/AnalyticsEngine.test.ts +244 -0
  5. package/__tests__/normalizers/MailgunNormalizer.test.ts +149 -0
  6. package/__tests__/normalizers/PostmarkNormalizer.test.ts +112 -0
  7. package/__tests__/normalizers/SESNormalizer.test.ts +168 -0
  8. package/__tests__/normalizers/SMTP2GONormalizer.test.ts +83 -0
  9. package/__tests__/normalizers/SendGridNormalizer.test.ts +181 -0
  10. package/__tests__/persistence/PersistenceAdapter.test.ts +103 -0
  11. package/coverage/clover.xml +328 -0
  12. package/coverage/coverage-final.json +10 -0
  13. package/coverage/lcov-report/base.css +224 -0
  14. package/coverage/lcov-report/block-navigation.js +87 -0
  15. package/coverage/lcov-report/favicon.png +0 -0
  16. package/coverage/lcov-report/index.html +161 -0
  17. package/coverage/lcov-report/prettify.css +1 -0
  18. package/coverage/lcov-report/prettify.js +2 -0
  19. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  20. package/coverage/lcov-report/sorter.js +210 -0
  21. package/coverage/lcov-report/src/EmailWebhookEventsService.ts.html +826 -0
  22. package/coverage/lcov-report/src/analytics/AnalyticsEngine.ts.html +775 -0
  23. package/coverage/lcov-report/src/analytics/index.html +116 -0
  24. package/coverage/lcov-report/src/index.html +131 -0
  25. package/coverage/lcov-report/src/normalizers/MailgunNormalizer.ts.html +301 -0
  26. package/coverage/lcov-report/src/normalizers/PostmarkNormalizer.ts.html +301 -0
  27. package/coverage/lcov-report/src/normalizers/SESNormalizer.ts.html +436 -0
  28. package/coverage/lcov-report/src/normalizers/SMTP2GONormalizer.ts.html +247 -0
  29. package/coverage/lcov-report/src/normalizers/SendGridNormalizer.ts.html +274 -0
  30. package/coverage/lcov-report/src/normalizers/index.html +176 -0
  31. package/coverage/lcov-report/src/persistence/InMemoryPersistenceAdapter.ts.html +289 -0
  32. package/coverage/lcov-report/src/persistence/index.html +116 -0
  33. package/coverage/lcov-report/src/types.ts.html +823 -0
  34. package/coverage/lcov.info +710 -0
  35. package/dist/EmailWebhookEventsService.d.ts +53 -0
  36. package/dist/EmailWebhookEventsService.d.ts.map +1 -0
  37. package/dist/EmailWebhookEventsService.js +198 -0
  38. package/dist/EmailWebhookEventsService.js.map +1 -0
  39. package/dist/analytics/AnalyticsEngine.d.ts +20 -0
  40. package/dist/analytics/AnalyticsEngine.d.ts.map +1 -0
  41. package/dist/analytics/AnalyticsEngine.js +160 -0
  42. package/dist/analytics/AnalyticsEngine.js.map +1 -0
  43. package/dist/index.d.ts +12 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +31 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/normalizers/EventNormalizer.d.ts +5 -0
  48. package/dist/normalizers/EventNormalizer.d.ts.map +1 -0
  49. package/dist/normalizers/EventNormalizer.js +10 -0
  50. package/dist/normalizers/EventNormalizer.js.map +1 -0
  51. package/dist/normalizers/MailgunNormalizer.d.ts +9 -0
  52. package/dist/normalizers/MailgunNormalizer.d.ts.map +1 -0
  53. package/dist/normalizers/MailgunNormalizer.js +73 -0
  54. package/dist/normalizers/MailgunNormalizer.js.map +1 -0
  55. package/dist/normalizers/PostmarkNormalizer.d.ts +10 -0
  56. package/dist/normalizers/PostmarkNormalizer.d.ts.map +1 -0
  57. package/dist/normalizers/PostmarkNormalizer.js +75 -0
  58. package/dist/normalizers/PostmarkNormalizer.js.map +1 -0
  59. package/dist/normalizers/SESNormalizer.d.ts +16 -0
  60. package/dist/normalizers/SESNormalizer.d.ts.map +1 -0
  61. package/dist/normalizers/SESNormalizer.js +107 -0
  62. package/dist/normalizers/SESNormalizer.js.map +1 -0
  63. package/dist/normalizers/SMTP2GONormalizer.d.ts +9 -0
  64. package/dist/normalizers/SMTP2GONormalizer.d.ts.map +1 -0
  65. package/dist/normalizers/SMTP2GONormalizer.js +55 -0
  66. package/dist/normalizers/SMTP2GONormalizer.js.map +1 -0
  67. package/dist/normalizers/SendGridNormalizer.d.ts +9 -0
  68. package/dist/normalizers/SendGridNormalizer.d.ts.map +1 -0
  69. package/dist/normalizers/SendGridNormalizer.js +64 -0
  70. package/dist/normalizers/SendGridNormalizer.js.map +1 -0
  71. package/dist/persistence/InMemoryPersistenceAdapter.d.ts +12 -0
  72. package/dist/persistence/InMemoryPersistenceAdapter.d.ts.map +1 -0
  73. package/dist/persistence/InMemoryPersistenceAdapter.js +58 -0
  74. package/dist/persistence/InMemoryPersistenceAdapter.js.map +1 -0
  75. package/dist/persistence/PersistenceAdapter.d.ts +7 -0
  76. package/dist/persistence/PersistenceAdapter.d.ts.map +1 -0
  77. package/dist/persistence/PersistenceAdapter.js +10 -0
  78. package/dist/persistence/PersistenceAdapter.js.map +1 -0
  79. package/dist/types.d.ts +178 -0
  80. package/dist/types.d.ts.map +1 -0
  81. package/dist/types.js +41 -0
  82. package/dist/types.js.map +1 -0
  83. package/jest.config.cjs +29 -0
  84. package/package.json +51 -0
  85. package/src/EmailWebhookEventsService.ts +247 -0
  86. package/src/analytics/AnalyticsEngine.ts +230 -0
  87. package/src/index.ts +39 -0
  88. package/src/normalizers/EventNormalizer.ts +13 -0
  89. package/src/normalizers/MailgunNormalizer.ts +72 -0
  90. package/src/normalizers/PostmarkNormalizer.ts +72 -0
  91. package/src/normalizers/SESNormalizer.ts +117 -0
  92. package/src/normalizers/SMTP2GONormalizer.ts +54 -0
  93. package/src/normalizers/SendGridNormalizer.ts +63 -0
  94. package/src/persistence/InMemoryPersistenceAdapter.ts +68 -0
  95. package/src/persistence/PersistenceAdapter.ts +15 -0
  96. package/src/types.ts +246 -0
  97. package/tsconfig.json +31 -0
@@ -0,0 +1,53 @@
1
+ import { EmailWebhookEventsConfig, EmailWebhookResult, WebhookPayload, EmailEvent, EmailAnalytics, AnalyticsFilters, EventQuery } from './types';
2
+ export declare class EmailWebhookEventsService {
3
+ private config;
4
+ private logger;
5
+ private persistenceAdapter?;
6
+ private analyticsEngine;
7
+ constructor(config: EmailWebhookEventsConfig);
8
+ /**
9
+ * Initialize the service
10
+ */
11
+ initialize(): Promise<void>;
12
+ /**
13
+ * Process incoming webhook
14
+ */
15
+ processWebhook(webhook: WebhookPayload): Promise<EmailWebhookResult>;
16
+ /**
17
+ * Get analytics for date range
18
+ */
19
+ getAnalytics(startDate: Date, endDate: Date, filters?: AnalyticsFilters): Promise<EmailAnalytics>;
20
+ /**
21
+ * Query events
22
+ */
23
+ queryEvents(query: EventQuery): Promise<EmailEvent[]>;
24
+ /**
25
+ * Get events for specific email
26
+ */
27
+ getEventsForEmail(emailId: string): Promise<EmailEvent[]>;
28
+ /**
29
+ * Normalize webhook event to unified format
30
+ */
31
+ private normalizeEvent;
32
+ /**
33
+ * Get normalizer for provider
34
+ */
35
+ private getNormalizerForProvider;
36
+ /**
37
+ * Check if event is critical (requires notification)
38
+ */
39
+ private isCriticalEvent;
40
+ /**
41
+ * Publish critical event to notification channels
42
+ */
43
+ private publishCriticalEvent;
44
+ /**
45
+ * Send notification via channel
46
+ */
47
+ private sendNotification;
48
+ /**
49
+ * Create persistence adapter
50
+ */
51
+ private createPersistenceAdapter;
52
+ }
53
+ //# sourceMappingURL=EmailWebhookEventsService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailWebhookEventsService.d.ts","sourceRoot":"","sources":["../src/EmailWebhookEventsService.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,cAAc,EAGd,gBAAgB,EAChB,UAAU,EAEX,MAAM,SAAS,CAAC;AAgCjB,qBAAa,yBAAyB;IAKxB,OAAO,CAAC,MAAM;IAJ1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,kBAAkB,CAAC,CAAqB;IAChD,OAAO,CAAC,eAAe,CAAkB;gBAErB,MAAM,EAAE,wBAAwB;IAKpD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IASjC;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA+C1E;;OAEG;IACG,YAAY,CAChB,SAAS,EAAE,IAAI,EACf,OAAO,EAAE,IAAI,EACb,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,cAAc,CAAC;IAI1B;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAO3D;;OAEG;IACG,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAI/D;;OAEG;YACW,cAAc;IAY5B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAiBhC;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;OAEG;YACW,oBAAoB;IAelC;;OAEG;YACW,gBAAgB;IAa9B;;OAEG;YACW,wBAAwB;CAWvC"}
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.EmailWebhookEventsService = void 0;
11
+ const types_1 = require("./types");
12
+ const AnalyticsEngine_1 = require("./analytics/AnalyticsEngine");
13
+ const InMemoryPersistenceAdapter_1 = require("./persistence/InMemoryPersistenceAdapter");
14
+ const SendGridNormalizer_1 = require("./normalizers/SendGridNormalizer");
15
+ const MailgunNormalizer_1 = require("./normalizers/MailgunNormalizer");
16
+ const SESNormalizer_1 = require("./normalizers/SESNormalizer");
17
+ const PostmarkNormalizer_1 = require("./normalizers/PostmarkNormalizer");
18
+ const SMTP2GONormalizer_1 = require("./normalizers/SMTP2GONormalizer");
19
+ class MockLogger {
20
+ info(message, meta) {
21
+ console.log(`[INFO] ${message}`, meta || '');
22
+ }
23
+ warn(message, meta) {
24
+ console.warn(`[WARN] ${message}`, meta || '');
25
+ }
26
+ error(message, meta) {
27
+ console.error(`[ERROR] ${message}`, meta || '');
28
+ }
29
+ }
30
+ class EmailWebhookEventsService {
31
+ constructor(config) {
32
+ this.config = config;
33
+ this.logger = new MockLogger();
34
+ this.analyticsEngine = new AnalyticsEngine_1.AnalyticsEngine(config.analytics);
35
+ }
36
+ /**
37
+ * Initialize the service
38
+ */
39
+ async initialize() {
40
+ // Initialize persistence
41
+ if (this.config.persistence?.enabled) {
42
+ this.persistenceAdapter = await this.createPersistenceAdapter();
43
+ }
44
+ this.logger.info('Email webhook events service initialized');
45
+ }
46
+ /**
47
+ * Process incoming webhook
48
+ */
49
+ async processWebhook(webhook) {
50
+ try {
51
+ // Validate webhook signature would happen here
52
+ // For now, we'll assume validation passes
53
+ // Normalize event
54
+ const normalizedEvent = await this.normalizeEvent(webhook);
55
+ if (!normalizedEvent) {
56
+ this.logger.warn('Failed to normalize webhook event', {
57
+ provider: webhook.provider
58
+ });
59
+ return { success: false, error: 'Event normalization failed' };
60
+ }
61
+ // Persist event
62
+ if (this.persistenceAdapter) {
63
+ await this.persistenceAdapter.saveEvent(normalizedEvent);
64
+ }
65
+ // Update analytics
66
+ await this.analyticsEngine.recordEvent(normalizedEvent);
67
+ // Publish critical events
68
+ if (this.isCriticalEvent(normalizedEvent)) {
69
+ await this.publishCriticalEvent(normalizedEvent);
70
+ }
71
+ this.logger.info('Email event processed successfully', {
72
+ eventId: normalizedEvent.id,
73
+ type: normalizedEvent.type,
74
+ recipient: normalizedEvent.recipient
75
+ });
76
+ return { success: true, event: normalizedEvent };
77
+ }
78
+ catch (error) {
79
+ this.logger.error('Error processing email webhook', {
80
+ error: error instanceof Error ? error.message : 'Unknown error',
81
+ provider: webhook.provider
82
+ });
83
+ return {
84
+ success: false,
85
+ error: error instanceof Error ? error.message : 'Unknown error'
86
+ };
87
+ }
88
+ }
89
+ /**
90
+ * Get analytics for date range
91
+ */
92
+ async getAnalytics(startDate, endDate, filters) {
93
+ return this.analyticsEngine.getAnalytics(startDate, endDate, filters);
94
+ }
95
+ /**
96
+ * Query events
97
+ */
98
+ async queryEvents(query) {
99
+ if (!this.persistenceAdapter) {
100
+ throw new Error('Event persistence not enabled');
101
+ }
102
+ return this.persistenceAdapter.queryEvents(query);
103
+ }
104
+ /**
105
+ * Get events for specific email
106
+ */
107
+ async getEventsForEmail(emailId) {
108
+ return this.queryEvents({ emailId });
109
+ }
110
+ /**
111
+ * Normalize webhook event to unified format
112
+ */
113
+ async normalizeEvent(webhook) {
114
+ const normalizer = this.getNormalizerForProvider(webhook.provider);
115
+ if (!normalizer) {
116
+ this.logger.error('No normalizer found for provider', {
117
+ provider: webhook.provider
118
+ });
119
+ return null;
120
+ }
121
+ return normalizer.normalize(webhook);
122
+ }
123
+ /**
124
+ * Get normalizer for provider
125
+ */
126
+ getNormalizerForProvider(provider) {
127
+ switch (provider) {
128
+ case types_1.EmailProvider.SENDGRID:
129
+ return new SendGridNormalizer_1.SendGridNormalizer();
130
+ case types_1.EmailProvider.MAILGUN:
131
+ return new MailgunNormalizer_1.MailgunNormalizer();
132
+ case types_1.EmailProvider.AWS_SES:
133
+ return new SESNormalizer_1.SESNormalizer();
134
+ case types_1.EmailProvider.POSTMARK:
135
+ return new PostmarkNormalizer_1.PostmarkNormalizer();
136
+ case types_1.EmailProvider.SMTP2GO:
137
+ return new SMTP2GONormalizer_1.SMTP2GONormalizer();
138
+ default:
139
+ return null;
140
+ }
141
+ }
142
+ /**
143
+ * Check if event is critical (requires notification)
144
+ */
145
+ isCriticalEvent(event) {
146
+ const criticalTypes = this.config.notifications?.criticalEvents || [
147
+ types_1.EmailEventType.BOUNCED,
148
+ types_1.EmailEventType.COMPLAINED,
149
+ types_1.EmailEventType.FAILED
150
+ ];
151
+ return criticalTypes.includes(event.type);
152
+ }
153
+ /**
154
+ * Publish critical event to notification channels
155
+ */
156
+ async publishCriticalEvent(event) {
157
+ if (!this.config.notifications?.enabled)
158
+ return;
159
+ for (const channel of this.config.notifications.channels) {
160
+ try {
161
+ await this.sendNotification(channel, event);
162
+ }
163
+ catch (error) {
164
+ this.logger.error('Failed to send notification', {
165
+ channel: channel.type,
166
+ error: error instanceof Error ? error.message : 'Unknown error'
167
+ });
168
+ }
169
+ }
170
+ }
171
+ /**
172
+ * Send notification via channel
173
+ */
174
+ async sendNotification(channel, event) {
175
+ // In a real implementation, this would send notifications
176
+ // For now, just log
177
+ this.logger.info('Critical event notification', {
178
+ channel: channel.type,
179
+ eventType: event.type,
180
+ recipient: event.recipient
181
+ });
182
+ }
183
+ /**
184
+ * Create persistence adapter
185
+ */
186
+ async createPersistenceAdapter() {
187
+ const adapter = this.config.persistence?.adapter || 'memory';
188
+ switch (adapter) {
189
+ case 'memory':
190
+ return new InMemoryPersistenceAdapter_1.InMemoryPersistenceAdapter();
191
+ // Other adapters would be implemented here
192
+ default:
193
+ throw new Error(`Unsupported persistence adapter: ${adapter}`);
194
+ }
195
+ }
196
+ }
197
+ exports.EmailWebhookEventsService = EmailWebhookEventsService;
198
+ //# sourceMappingURL=EmailWebhookEventsService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailWebhookEventsService.js","sourceRoot":"","sources":["../src/EmailWebhookEventsService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { EmailEvent, EmailAnalytics, AnalyticsFilters } from '../types';
2
+ export declare class AnalyticsEngine {
3
+ private events;
4
+ private config?;
5
+ constructor(config?: {
6
+ realTimeEnabled: boolean;
7
+ aggregationInterval: number;
8
+ retentionDays: number;
9
+ });
10
+ recordEvent(event: EmailEvent): Promise<void>;
11
+ getAnalytics(startDate: Date, endDate: Date, filters?: AnalyticsFilters): Promise<EmailAnalytics>;
12
+ private calculateCounts;
13
+ private calculateRates;
14
+ private calculateProviderBreakdown;
15
+ private calculateEventTypeBreakdown;
16
+ private calculateBounceTypeBreakdown;
17
+ private calculateTopLinks;
18
+ private calculateDeviceBreakdown;
19
+ }
20
+ //# sourceMappingURL=AnalyticsEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnalyticsEngine.d.ts","sourceRoot":"","sources":["../../src/analytics/AnalyticsEngine.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,UAAU,EACV,cAAc,EAId,gBAAgB,EACjB,MAAM,UAAU,CAAC;AAElB,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,MAAM,CAAC,CAIb;gBAEU,MAAM,CAAC,EAAE;QAAE,eAAe,EAAE,OAAO,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE;IAI/F,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAW7C,YAAY,CAChB,SAAS,EAAE,IAAI,EACf,OAAO,EAAE,IAAI,EACb,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,cAAc,CAAC;IAgD1B,OAAO,CAAC,eAAe;IAsBvB,OAAO,CAAC,cAAc;IA8BtB,OAAO,CAAC,0BAA0B;IA0BlC,OAAO,CAAC,2BAA2B;IAWnC,OAAO,CAAC,4BAA4B;IAUpC,OAAO,CAAC,iBAAiB;IAwBzB,OAAO,CAAC,wBAAwB;CAcjC"}
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.AnalyticsEngine = void 0;
11
+ const types_1 = require("../types");
12
+ class AnalyticsEngine {
13
+ constructor(config) {
14
+ this.events = [];
15
+ this.config = config;
16
+ }
17
+ async recordEvent(event) {
18
+ this.events.push(event);
19
+ // Cleanup old events based on retention policy
20
+ if (this.config?.retentionDays) {
21
+ const cutoffDate = new Date();
22
+ cutoffDate.setDate(cutoffDate.getDate() - this.config.retentionDays);
23
+ this.events = this.events.filter(e => e.timestamp >= cutoffDate);
24
+ }
25
+ }
26
+ async getAnalytics(startDate, endDate, filters) {
27
+ // Filter events by date range and filters
28
+ let filteredEvents = this.events.filter(e => e.timestamp >= startDate && e.timestamp <= endDate);
29
+ if (filters?.provider) {
30
+ filteredEvents = filteredEvents.filter(e => e.provider === filters.provider);
31
+ }
32
+ if (filters?.emailId) {
33
+ filteredEvents = filteredEvents.filter(e => e.emailId === filters.emailId);
34
+ }
35
+ if (filters?.recipient) {
36
+ filteredEvents = filteredEvents.filter(e => e.recipient === filters.recipient);
37
+ }
38
+ if (filters?.eventTypes && filters.eventTypes.length > 0) {
39
+ filteredEvents = filteredEvents.filter(e => filters.eventTypes.includes(e.type));
40
+ }
41
+ // Calculate counts
42
+ const counts = this.calculateCounts(filteredEvents);
43
+ // Calculate rates
44
+ const rates = this.calculateRates(counts);
45
+ // Calculate breakdowns
46
+ const byProvider = this.calculateProviderBreakdown(filteredEvents);
47
+ const byEventType = this.calculateEventTypeBreakdown(filteredEvents);
48
+ const byBounceType = this.calculateBounceTypeBreakdown(filteredEvents);
49
+ const topLinks = this.calculateTopLinks(filteredEvents);
50
+ const byDevice = this.calculateDeviceBreakdown(filteredEvents);
51
+ return {
52
+ startDate,
53
+ endDate,
54
+ ...counts,
55
+ ...rates,
56
+ byProvider,
57
+ byEventType,
58
+ byBounceType,
59
+ topLinks,
60
+ byDevice
61
+ };
62
+ }
63
+ calculateCounts(events) {
64
+ return {
65
+ totalSent: events.length, // Simplified - in real implementation, would track sent separately
66
+ totalDelivered: events.filter(e => e.type === types_1.EmailEventType.DELIVERED).length,
67
+ totalOpened: events.filter(e => e.type === types_1.EmailEventType.OPENED).length,
68
+ totalClicked: events.filter(e => e.type === types_1.EmailEventType.CLICKED).length,
69
+ totalBounced: events.filter(e => e.type === types_1.EmailEventType.BOUNCED).length,
70
+ totalComplained: events.filter(e => e.type === types_1.EmailEventType.COMPLAINED).length,
71
+ totalUnsubscribed: events.filter(e => e.type === types_1.EmailEventType.UNSUBSCRIBED).length,
72
+ totalFailed: events.filter(e => e.type === types_1.EmailEventType.FAILED).length
73
+ };
74
+ }
75
+ calculateRates(counts) {
76
+ const safeDiv = (num, denom) => (denom === 0 ? 0 : num / denom);
77
+ return {
78
+ deliveryRate: safeDiv(counts.totalDelivered, counts.totalSent),
79
+ openRate: safeDiv(counts.totalOpened, counts.totalDelivered),
80
+ clickRate: safeDiv(counts.totalClicked, counts.totalDelivered),
81
+ clickToOpenRate: safeDiv(counts.totalClicked, counts.totalOpened),
82
+ bounceRate: safeDiv(counts.totalBounced, counts.totalSent),
83
+ complaintRate: safeDiv(counts.totalComplained, counts.totalSent),
84
+ unsubscribeRate: safeDiv(counts.totalUnsubscribed, counts.totalDelivered)
85
+ };
86
+ }
87
+ calculateProviderBreakdown(events) {
88
+ const providers = Object.values(types_1.EmailProvider);
89
+ const breakdown = {};
90
+ for (const provider of providers) {
91
+ const providerEvents = events.filter(e => e.provider === provider);
92
+ const counts = this.calculateCounts(providerEvents);
93
+ const rates = this.calculateRates(counts);
94
+ breakdown[provider] = {
95
+ sent: counts.totalSent,
96
+ delivered: counts.totalDelivered,
97
+ opened: counts.totalOpened,
98
+ clicked: counts.totalClicked,
99
+ bounced: counts.totalBounced,
100
+ complained: counts.totalComplained,
101
+ failed: counts.totalFailed,
102
+ deliveryRate: rates.deliveryRate,
103
+ openRate: rates.openRate,
104
+ clickRate: rates.clickRate
105
+ };
106
+ }
107
+ return breakdown;
108
+ }
109
+ calculateEventTypeBreakdown(events) {
110
+ const eventTypes = Object.values(types_1.EmailEventType);
111
+ const breakdown = {};
112
+ for (const eventType of eventTypes) {
113
+ breakdown[eventType] = events.filter(e => e.type === eventType).length;
114
+ }
115
+ return breakdown;
116
+ }
117
+ calculateBounceTypeBreakdown(events) {
118
+ const bounceEvents = events.filter(e => e.type === types_1.EmailEventType.BOUNCED);
119
+ if (bounceEvents.length === 0)
120
+ return undefined;
121
+ return {
122
+ hard: bounceEvents.filter(e => e.metadata.bounceType === 'hard').length,
123
+ soft: bounceEvents.filter(e => e.metadata.bounceType === 'soft').length
124
+ };
125
+ }
126
+ calculateTopLinks(events) {
127
+ const clickEvents = events.filter(e => e.type === types_1.EmailEventType.CLICKED && e.metadata.url);
128
+ if (clickEvents.length === 0)
129
+ return undefined;
130
+ const linkStats = new Map();
131
+ for (const event of clickEvents) {
132
+ const url = event.metadata.url;
133
+ const existing = linkStats.get(url) || { clicks: 0, recipients: new Set() };
134
+ existing.clicks++;
135
+ existing.recipients.add(event.recipient);
136
+ linkStats.set(url, existing);
137
+ }
138
+ return Array.from(linkStats.entries())
139
+ .map(([url, stats]) => ({
140
+ url,
141
+ clicks: stats.clicks,
142
+ uniqueClicks: stats.recipients.size
143
+ }))
144
+ .sort((a, b) => b.clicks - a.clicks)
145
+ .slice(0, 10);
146
+ }
147
+ calculateDeviceBreakdown(events) {
148
+ const interactionEvents = events.filter(e => (e.type === types_1.EmailEventType.OPENED || e.type === types_1.EmailEventType.CLICKED) && e.metadata.deviceType);
149
+ if (interactionEvents.length === 0)
150
+ return undefined;
151
+ return {
152
+ desktop: interactionEvents.filter(e => e.metadata.deviceType === 'desktop').length,
153
+ mobile: interactionEvents.filter(e => e.metadata.deviceType === 'mobile').length,
154
+ tablet: interactionEvents.filter(e => e.metadata.deviceType === 'tablet').length,
155
+ unknown: interactionEvents.filter(e => e.metadata.deviceType === 'unknown').length
156
+ };
157
+ }
158
+ }
159
+ exports.AnalyticsEngine = AnalyticsEngine;
160
+ //# sourceMappingURL=AnalyticsEngine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnalyticsEngine.js","sourceRoot":"","sources":["../../src/analytics/AnalyticsEngine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export { EmailWebhookEventsService } from './EmailWebhookEventsService';
2
+ export { EmailEvent, EmailEventType, EmailProvider, EmailEventMetadata, EmailAnalytics, ProviderAnalytics, EmailWebhookEventsConfig, ProviderWebhookConfig, NotificationChannel, EmailWebhookResult, WebhookPayload, AnalyticsFilters, EventQuery, EmailWebhookError, EmailWebhookErrorCode } from './types';
3
+ export { EventNormalizer } from './normalizers/EventNormalizer';
4
+ export { SendGridNormalizer } from './normalizers/SendGridNormalizer';
5
+ export { MailgunNormalizer } from './normalizers/MailgunNormalizer';
6
+ export { SESNormalizer } from './normalizers/SESNormalizer';
7
+ export { PostmarkNormalizer } from './normalizers/PostmarkNormalizer';
8
+ export { SMTP2GONormalizer } from './normalizers/SMTP2GONormalizer';
9
+ export { AnalyticsEngine } from './analytics/AnalyticsEngine';
10
+ export { PersistenceAdapter } from './persistence/PersistenceAdapter';
11
+ export { InMemoryPersistenceAdapter } from './persistence/InMemoryPersistenceAdapter';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAExE,OAAO,EACL,UAAU,EACV,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.InMemoryPersistenceAdapter = exports.AnalyticsEngine = exports.SMTP2GONormalizer = exports.PostmarkNormalizer = exports.SESNormalizer = exports.MailgunNormalizer = exports.SendGridNormalizer = exports.EmailWebhookErrorCode = exports.EmailProvider = exports.EmailEventType = exports.EmailWebhookEventsService = void 0;
11
+ var EmailWebhookEventsService_1 = require("./EmailWebhookEventsService");
12
+ Object.defineProperty(exports, "EmailWebhookEventsService", { enumerable: true, get: function () { return EmailWebhookEventsService_1.EmailWebhookEventsService; } });
13
+ var types_1 = require("./types");
14
+ Object.defineProperty(exports, "EmailEventType", { enumerable: true, get: function () { return types_1.EmailEventType; } });
15
+ Object.defineProperty(exports, "EmailProvider", { enumerable: true, get: function () { return types_1.EmailProvider; } });
16
+ Object.defineProperty(exports, "EmailWebhookErrorCode", { enumerable: true, get: function () { return types_1.EmailWebhookErrorCode; } });
17
+ var SendGridNormalizer_1 = require("./normalizers/SendGridNormalizer");
18
+ Object.defineProperty(exports, "SendGridNormalizer", { enumerable: true, get: function () { return SendGridNormalizer_1.SendGridNormalizer; } });
19
+ var MailgunNormalizer_1 = require("./normalizers/MailgunNormalizer");
20
+ Object.defineProperty(exports, "MailgunNormalizer", { enumerable: true, get: function () { return MailgunNormalizer_1.MailgunNormalizer; } });
21
+ var SESNormalizer_1 = require("./normalizers/SESNormalizer");
22
+ Object.defineProperty(exports, "SESNormalizer", { enumerable: true, get: function () { return SESNormalizer_1.SESNormalizer; } });
23
+ var PostmarkNormalizer_1 = require("./normalizers/PostmarkNormalizer");
24
+ Object.defineProperty(exports, "PostmarkNormalizer", { enumerable: true, get: function () { return PostmarkNormalizer_1.PostmarkNormalizer; } });
25
+ var SMTP2GONormalizer_1 = require("./normalizers/SMTP2GONormalizer");
26
+ Object.defineProperty(exports, "SMTP2GONormalizer", { enumerable: true, get: function () { return SMTP2GONormalizer_1.SMTP2GONormalizer; } });
27
+ var AnalyticsEngine_1 = require("./analytics/AnalyticsEngine");
28
+ Object.defineProperty(exports, "AnalyticsEngine", { enumerable: true, get: function () { return AnalyticsEngine_1.AnalyticsEngine; } });
29
+ var InMemoryPersistenceAdapter_1 = require("./persistence/InMemoryPersistenceAdapter");
30
+ Object.defineProperty(exports, "InMemoryPersistenceAdapter", { enumerable: true, get: function () { return InMemoryPersistenceAdapter_1.InMemoryPersistenceAdapter; } });
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;;AAEF,yEAAwE;AAA/D,sIAAA,yBAAyB,OAAA;AAElC,iCAgBiB;AAdf,uGAAA,cAAc,OAAA;AACd,sGAAA,aAAa,OAAA;AAYb,8GAAA,qBAAqB,OAAA;AAIvB,uEAAsE;AAA7D,wHAAA,kBAAkB,OAAA;AAC3B,qEAAoE;AAA3D,sHAAA,iBAAiB,OAAA;AAC1B,6DAA4D;AAAnD,8GAAA,aAAa,OAAA;AACtB,uEAAsE;AAA7D,wHAAA,kBAAkB,OAAA;AAC3B,qEAAoE;AAA3D,sHAAA,iBAAiB,OAAA;AAE1B,+DAA8D;AAArD,kHAAA,eAAe,OAAA;AAGxB,uFAAsF;AAA7E,wIAAA,0BAA0B,OAAA"}
@@ -0,0 +1,5 @@
1
+ import { EmailEvent, WebhookPayload } from '../types';
2
+ export interface EventNormalizer {
3
+ normalize(webhook: WebhookPayload): EmailEvent;
4
+ }
5
+ //# sourceMappingURL=EventNormalizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventNormalizer.d.ts","sourceRoot":"","sources":["../../src/normalizers/EventNormalizer.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,UAAU,CAAC;CAChD"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ //# sourceMappingURL=EventNormalizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventNormalizer.js","sourceRoot":"","sources":["../../src/normalizers/EventNormalizer.ts"],"names":[],"mappings":";AAAA;;;;;;EAME"}
@@ -0,0 +1,9 @@
1
+ import { EventNormalizer } from './EventNormalizer';
2
+ import { EmailEvent, WebhookPayload } from '../types';
3
+ export declare class MailgunNormalizer implements EventNormalizer {
4
+ normalize(webhook: WebhookPayload): EmailEvent;
5
+ private mapEventType;
6
+ private generateEventId;
7
+ private detectDeviceType;
8
+ }
9
+ //# sourceMappingURL=MailgunNormalizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MailgunNormalizer.d.ts","sourceRoot":"","sources":["../../src/normalizers/MailgunNormalizer.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAiC,cAAc,EAAE,MAAM,UAAU,CAAC;AAErF,qBAAa,iBAAkB,YAAW,eAAe;IACvD,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,UAAU;IAmC9C,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,gBAAgB;CAOzB"}
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.MailgunNormalizer = void 0;
11
+ const types_1 = require("../types");
12
+ class MailgunNormalizer {
13
+ normalize(webhook) {
14
+ const payload = webhook.payload;
15
+ const event = payload['event-data'];
16
+ const message = event.message || {};
17
+ const headers = message.headers || {};
18
+ const deliveryStatus = event['delivery-status'];
19
+ const clientInfo = event['client-info'];
20
+ const geolocation = event.geolocation;
21
+ return {
22
+ id: this.generateEventId(event),
23
+ emailId: headers['message-id'] || '',
24
+ messageId: event.id || '',
25
+ type: this.mapEventType(event.event),
26
+ recipient: event.recipient || '',
27
+ timestamp: new Date(event.timestamp * 1000),
28
+ provider: types_1.EmailProvider.MAILGUN,
29
+ metadata: {
30
+ url: event.url,
31
+ bounceType: event.severity === 'permanent' ? 'hard' : 'soft',
32
+ bounceReason: deliveryStatus?.message,
33
+ bounceCode: deliveryStatus?.code?.toString(),
34
+ ipAddress: clientInfo?.['client-ip'],
35
+ userAgent: clientInfo?.['user-agent'],
36
+ deviceType: this.detectDeviceType(clientInfo?.['user-agent']),
37
+ location: {
38
+ country: geolocation?.country,
39
+ region: geolocation?.region,
40
+ city: geolocation?.city
41
+ }
42
+ },
43
+ rawEvent: event
44
+ };
45
+ }
46
+ mapEventType(mailgunEvent) {
47
+ const mapping = {
48
+ 'delivered': types_1.EmailEventType.DELIVERED,
49
+ 'opened': types_1.EmailEventType.OPENED,
50
+ 'clicked': types_1.EmailEventType.CLICKED,
51
+ 'bounced': types_1.EmailEventType.BOUNCED,
52
+ 'complained': types_1.EmailEventType.COMPLAINED,
53
+ 'unsubscribed': types_1.EmailEventType.UNSUBSCRIBED,
54
+ 'failed': types_1.EmailEventType.FAILED
55
+ };
56
+ return mapping[mailgunEvent] || types_1.EmailEventType.FAILED;
57
+ }
58
+ generateEventId(event) {
59
+ return `mailgun-${event.id}`;
60
+ }
61
+ detectDeviceType(userAgent) {
62
+ if (!userAgent)
63
+ return 'unknown';
64
+ const ua = userAgent.toLowerCase();
65
+ if (ua.includes('mobile'))
66
+ return 'mobile';
67
+ if (ua.includes('tablet') || ua.includes('ipad'))
68
+ return 'tablet';
69
+ return 'desktop';
70
+ }
71
+ }
72
+ exports.MailgunNormalizer = MailgunNormalizer;
73
+ //# sourceMappingURL=MailgunNormalizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MailgunNormalizer.js","sourceRoot":"","sources":["../../src/normalizers/MailgunNormalizer.ts"],"names":[],"mappings":"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"}