@bernierllc/email-webhook-events 1.2.0 → 1.3.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/.eslintrc.cjs CHANGED
@@ -1,29 +1,30 @@
1
- /*
2
- Copyright (c) 2025 Bernier LLC
3
-
4
- This file is licensed to the client under a limited-use license.
5
- The client may use and modify this code *only within the scope of the project it was delivered for*.
6
- Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
- */
8
-
9
1
  module.exports = {
10
2
  parser: '@typescript-eslint/parser',
3
+ plugins: ['@typescript-eslint'],
4
+ extends: [
5
+ 'eslint:recommended',
6
+ 'plugin:@typescript-eslint/recommended'
7
+ ],
11
8
  parserOptions: {
12
9
  ecmaVersion: 2020,
13
10
  sourceType: 'module',
14
11
  project: './tsconfig.json',
12
+ tsconfigRootDir: __dirname
13
+ },
14
+ env: {
15
+ node: true,
16
+ es6: true,
17
+ jest: true
15
18
  },
16
- plugins: ['@typescript-eslint'],
17
- extends: [
18
- 'eslint:recommended',
19
- 'plugin:@typescript-eslint/recommended',
20
- 'plugin:@typescript-eslint/recommended-requiring-type-checking',
21
- ],
22
19
  rules: {
23
- '@typescript-eslint/explicit-function-return-type': 'warn',
24
- '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
25
- '@typescript-eslint/no-explicit-any': 'error',
26
- '@typescript-eslint/strict-boolean-expressions': 'off',
20
+ '@typescript-eslint/no-explicit-any': 'off',
21
+ '@typescript-eslint/no-unused-vars': ['error', {
22
+ argsIgnorePattern: '^_',
23
+ varsIgnorePattern: '^_',
24
+ caughtErrorsIgnorePattern: '^_'
25
+ }],
26
+ 'no-unused-vars': 'off',
27
+ 'no-console': 'off'
27
28
  },
28
- ignorePatterns: ['dist/', 'node_modules/', '*.cjs'],
29
+ ignorePatterns: ['dist/', 'node_modules/', '*.js', '*.cjs', 'coverage/']
29
30
  };
package/CHANGELOG.md CHANGED
@@ -3,6 +3,28 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.3.1](https://github.com/bernierllc/tools/compare/@bernierllc/email-webhook-events@1.3.0...@bernierllc/email-webhook-events@1.3.1) (2026-03-09)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **email:** sync provider type unions across email packages ([b515e54](https://github.com/bernierllc/tools/commit/b515e5456da4ce6fd6067d20e4da010d658e3a2b))
12
+
13
+
14
+
15
+
16
+
17
+ # [1.3.0](https://github.com/bernierllc/tools/compare/@bernierllc/email-webhook-events@1.2.0...@bernierllc/email-webhook-events@1.3.0) (2026-03-03)
18
+
19
+
20
+ ### Features
21
+
22
+ * **email:** add multi-provider capability matrix with smart routing ([#29](https://github.com/bernierllc/tools/issues/29)) ([b9c38c0](https://github.com/bernierllc/tools/commit/b9c38c0a93985dc1675681657cc2ec3a209a9435))
23
+
24
+
25
+
26
+
27
+
6
28
  # 1.2.0 (2025-12-25)
7
29
 
8
30
 
package/README.md CHANGED
@@ -363,6 +363,72 @@ and used to generate accurate mocks for unit tests.
363
363
 
364
364
  See `__tests__/fixtures/sendgrid-webhooks/README.md` for more details.
365
365
 
366
+ ## Provider Capability Support
367
+
368
+ This section documents how webhook event processing behaves across email providers, based on the canonical `CAPABILITY_MATRIX` in `@bernierllc/email-manager`.
369
+
370
+ ### Capability Levels by Provider
371
+
372
+ | Provider | `webhooksReceive` | `webhookNormalization` | Notes |
373
+ |----------|------------------|----------------------|-------|
374
+ | SendGrid | **provider** | **enhanced** | Native Event Webhook with platform normalization |
375
+ | Mailgun | **provider** | **enhanced** | Native webhooks with platform normalization |
376
+ | Postmark | **provider** | **enhanced** | Native webhooks for delivery, bounce, and spam events |
377
+ | SES | **provider** | **enhanced** | Event notifications via SNS (requires SNS topic configuration) |
378
+ | SMTP | **unsupported** | **unsupported** | SMTP does not provide webhook callbacks |
379
+
380
+ ### How It Works
381
+
382
+ - **Provider-level webhooks** (SendGrid, Mailgun, Postmark, SES): The email provider pushes event data (delivered, opened, clicked, bounced, etc.) to your configured webhook endpoint. This package receives those raw payloads, validates signatures, and passes them to the normalizer.
383
+
384
+ - **Enhanced normalization** (SendGrid, Mailgun, Postmark, SES): Each provider sends events in its own format. This package normalizes all provider-specific payloads into a unified `EmailEvent` format with consistent event types, timestamps, and metadata. Provider-specific details are preserved in the normalized event's metadata.
385
+
386
+ - **Unsupported** (SMTP): Plain SMTP does not provide any webhook or event callback mechanism. No delivery tracking, open tracking, or bounce notifications are available through SMTP alone.
387
+
388
+ ### Degradation Behavior
389
+
390
+ When used with an unsupported provider (SMTP):
391
+
392
+ - **Strategy**: No automatic fallback -- SMTP simply does not emit events
393
+ - **Impact**: No delivery tracking, no bounce notifications, no open/click tracking
394
+ - **Workaround**: SMTP users who need event tracking should either:
395
+ 1. Use a provider that supports webhooks (SendGrid, Mailgun, Postmark, SES)
396
+ 2. Set up an external webhook relay service that monitors the SMTP server's logs
397
+ 3. Implement polling-based delivery verification using DSN (Delivery Status Notifications) if the SMTP server supports it
398
+
399
+ ### Override Options
400
+
401
+ The webhook receive capability is marked `overridable: true` for all providers with native support. This allows you to:
402
+
403
+ - Replace the built-in normalizer for a specific provider with a custom implementation
404
+ - Add custom event types beyond the standard normalized set
405
+ - Route raw (unnormalized) events to a separate processing pipeline
406
+
407
+ ```typescript
408
+ const service = new EmailWebhookEventsService({
409
+ providers: [
410
+ {
411
+ provider: EmailProvider.SENDGRID,
412
+ enabled: true,
413
+ webhookUrl: '/webhooks/sendgrid',
414
+ secretKey: process.env.SENDGRID_WEBHOOK_SECRET!,
415
+ // Custom normalizer override
416
+ normalizer: myCustomSendGridNormalizer,
417
+ },
418
+ ],
419
+ });
420
+ ```
421
+
422
+ ### SES-Specific Notes
423
+
424
+ SES webhook support requires additional infrastructure setup:
425
+
426
+ - An SNS topic must be configured to receive SES event notifications
427
+ - An SNS subscription (HTTPS or Lambda) must forward events to this package's webhook endpoint
428
+ - SES configuration sets must be created to route events to the SNS topic
429
+
430
+ This is reflected in the capability matrix as a limitation: `"Requires SNS topic configuration"`.
431
+
366
432
  ## Integration Status
367
433
 
368
434
  - **Logger**: integrated - Uses MockLogger (pending @bernierllc/logger publication)
@@ -147,9 +147,11 @@ describe('EmailWebhookEventsService', () => {
147
147
  }
148
148
  }));
149
149
 
150
+ const rangeStart = new Date(Date.now() - 24 * 60 * 60 * 1000);
151
+ const rangeEnd = new Date(Date.now() + 24 * 60 * 60 * 1000);
150
152
  const analytics = await service.getAnalytics(
151
- new Date('2025-01-01'),
152
- new Date('2025-12-31')
153
+ rangeStart,
154
+ rangeEnd
153
155
  );
154
156
 
155
157
  expect(analytics.totalDelivered).toBe(2);
@@ -164,9 +166,11 @@ describe('EmailWebhookEventsService', () => {
164
166
  provider: EmailProvider.SENDGRID
165
167
  }));
166
168
 
169
+ const rangeStart = new Date(Date.now() - 24 * 60 * 60 * 1000);
170
+ const rangeEnd = new Date(Date.now() + 24 * 60 * 60 * 1000);
167
171
  const analytics = await service.getAnalytics(
168
- new Date('2025-01-01'),
169
- new Date('2025-12-31'),
172
+ rangeStart,
173
+ rangeEnd,
170
174
  { provider: EmailProvider.SENDGRID }
171
175
  );
172
176
 
@@ -0,0 +1,343 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+
9
+ import { EmailWebhookEventsService } from '../src/EmailWebhookEventsService';
10
+ import {
11
+ EmailEventType,
12
+ EmailProvider,
13
+ WebhookPayload,
14
+ EmailEvent,
15
+ WebhookEventCallbacks,
16
+ EmailWebhookEventsConfig
17
+ } from '../src/types';
18
+
19
+ describe('WebhookEventCallbacks', () => {
20
+ const createWebhook = (eventType: string, overrides: Partial<WebhookPayload> = {}): WebhookPayload => ({
21
+ provider: EmailProvider.SENDGRID,
22
+ signature: 'test-signature',
23
+ payload: {
24
+ event: eventType,
25
+ email: 'user@example.com',
26
+ timestamp: Math.floor(Date.now() / 1000),
27
+ sg_message_id: 'msg-123',
28
+ email_id: 'email-456'
29
+ },
30
+ headers: {},
31
+ ...overrides
32
+ });
33
+
34
+ const createService = async (callbacks: WebhookEventCallbacks): Promise<EmailWebhookEventsService> => {
35
+ const config: EmailWebhookEventsConfig = {
36
+ providers: [{
37
+ provider: EmailProvider.SENDGRID,
38
+ enabled: true,
39
+ webhookUrl: '/webhooks/sendgrid',
40
+ secretKey: 'test-secret',
41
+ signatureHeader: 'X-Twilio-Email-Event-Webhook-Signature',
42
+ signatureAlgorithm: 'sha256'
43
+ }],
44
+ persistence: {
45
+ enabled: true,
46
+ adapter: 'memory'
47
+ },
48
+ analytics: {
49
+ realTimeEnabled: true,
50
+ aggregationInterval: 5000,
51
+ retentionDays: 90
52
+ },
53
+ callbacks
54
+ };
55
+
56
+ const service = new EmailWebhookEventsService(config);
57
+ await service.initialize();
58
+ return service;
59
+ };
60
+
61
+ it('should invoke onBounce callback for bounce events', async () => {
62
+ const onBounce = jest.fn();
63
+ const service = await createService({ onBounce });
64
+
65
+ const webhook = createWebhook('bounce', {
66
+ payload: {
67
+ event: 'bounce',
68
+ email: 'user@example.com',
69
+ timestamp: Math.floor(Date.now() / 1000),
70
+ sg_message_id: 'msg-123',
71
+ status: '5.1.1',
72
+ reason: 'User unknown'
73
+ }
74
+ });
75
+
76
+ const result = await service.processWebhook(webhook);
77
+
78
+ expect(result.success).toBe(true);
79
+ expect(onBounce).toHaveBeenCalledTimes(1);
80
+ expect(onBounce).toHaveBeenCalledWith(
81
+ expect.objectContaining({
82
+ type: EmailEventType.BOUNCED,
83
+ recipient: 'user@example.com'
84
+ })
85
+ );
86
+ });
87
+
88
+ it('should invoke onComplaint callback for complaint events', async () => {
89
+ const onComplaint = jest.fn();
90
+ const service = await createService({ onComplaint });
91
+
92
+ const webhook = createWebhook('spamreport', {
93
+ payload: {
94
+ event: 'spamreport',
95
+ email: 'user@example.com',
96
+ timestamp: Math.floor(Date.now() / 1000),
97
+ sg_message_id: 'msg-123'
98
+ }
99
+ });
100
+
101
+ const result = await service.processWebhook(webhook);
102
+
103
+ expect(result.success).toBe(true);
104
+ expect(onComplaint).toHaveBeenCalledTimes(1);
105
+ expect(onComplaint).toHaveBeenCalledWith(
106
+ expect.objectContaining({
107
+ type: EmailEventType.COMPLAINED,
108
+ recipient: 'user@example.com'
109
+ })
110
+ );
111
+ });
112
+
113
+ it('should invoke onUnsubscribe callback for unsubscribe events', async () => {
114
+ const onUnsubscribe = jest.fn();
115
+ const service = await createService({ onUnsubscribe });
116
+
117
+ const webhook = createWebhook('unsubscribe', {
118
+ payload: {
119
+ event: 'unsubscribe',
120
+ email: 'user@example.com',
121
+ timestamp: Math.floor(Date.now() / 1000),
122
+ sg_message_id: 'msg-123'
123
+ }
124
+ });
125
+
126
+ const result = await service.processWebhook(webhook);
127
+
128
+ expect(result.success).toBe(true);
129
+ expect(onUnsubscribe).toHaveBeenCalledTimes(1);
130
+ expect(onUnsubscribe).toHaveBeenCalledWith(
131
+ expect.objectContaining({
132
+ type: EmailEventType.UNSUBSCRIBED,
133
+ recipient: 'user@example.com'
134
+ })
135
+ );
136
+ });
137
+
138
+ it('should invoke onDelivery callback for delivery events', async () => {
139
+ const onDelivery = jest.fn();
140
+ const service = await createService({ onDelivery });
141
+
142
+ const webhook = createWebhook('delivered');
143
+ const result = await service.processWebhook(webhook);
144
+
145
+ expect(result.success).toBe(true);
146
+ expect(onDelivery).toHaveBeenCalledTimes(1);
147
+ expect(onDelivery).toHaveBeenCalledWith(
148
+ expect.objectContaining({
149
+ type: EmailEventType.DELIVERED,
150
+ recipient: 'user@example.com'
151
+ })
152
+ );
153
+ });
154
+
155
+ it('should invoke onOpen callback for open events', async () => {
156
+ const onOpen = jest.fn();
157
+ const service = await createService({ onOpen });
158
+
159
+ const webhook = createWebhook('open');
160
+ const result = await service.processWebhook(webhook);
161
+
162
+ expect(result.success).toBe(true);
163
+ expect(onOpen).toHaveBeenCalledTimes(1);
164
+ expect(onOpen).toHaveBeenCalledWith(
165
+ expect.objectContaining({
166
+ type: EmailEventType.OPENED
167
+ })
168
+ );
169
+ });
170
+
171
+ it('should invoke onClick callback for click events', async () => {
172
+ const onClick = jest.fn();
173
+ const service = await createService({ onClick });
174
+
175
+ const webhook = createWebhook('click', {
176
+ payload: {
177
+ event: 'click',
178
+ email: 'user@example.com',
179
+ timestamp: Math.floor(Date.now() / 1000),
180
+ sg_message_id: 'msg-123',
181
+ url: 'https://example.com/link'
182
+ }
183
+ });
184
+
185
+ const result = await service.processWebhook(webhook);
186
+
187
+ expect(result.success).toBe(true);
188
+ expect(onClick).toHaveBeenCalledTimes(1);
189
+ expect(onClick).toHaveBeenCalledWith(
190
+ expect.objectContaining({
191
+ type: EmailEventType.CLICKED,
192
+ metadata: expect.objectContaining({
193
+ url: 'https://example.com/link'
194
+ })
195
+ })
196
+ );
197
+ });
198
+
199
+ it('should invoke onFailure callback for failed events', async () => {
200
+ const onFailure = jest.fn();
201
+ const service = await createService({ onFailure });
202
+
203
+ // An unmapped SendGrid event type falls through to EmailEventType.FAILED
204
+ const webhook = createWebhook('unknown_failure_event', {
205
+ payload: {
206
+ event: 'unknown_failure_event',
207
+ email: 'user@example.com',
208
+ timestamp: Math.floor(Date.now() / 1000),
209
+ sg_message_id: 'msg-123',
210
+ reason: 'Invalid email'
211
+ }
212
+ });
213
+
214
+ const result = await service.processWebhook(webhook);
215
+
216
+ expect(result.success).toBe(true);
217
+ expect(onFailure).toHaveBeenCalledTimes(1);
218
+ expect(onFailure).toHaveBeenCalledWith(
219
+ expect.objectContaining({
220
+ type: EmailEventType.FAILED
221
+ })
222
+ );
223
+ });
224
+
225
+ it('should not fail webhook processing if callback throws', async () => {
226
+ const onDelivery = jest.fn().mockRejectedValue(new Error('Callback exploded'));
227
+ const service = await createService({ onDelivery });
228
+
229
+ const webhook = createWebhook('delivered');
230
+ const result = await service.processWebhook(webhook);
231
+
232
+ expect(result.success).toBe(true);
233
+ expect(result.event).toBeDefined();
234
+ expect(onDelivery).toHaveBeenCalledTimes(1);
235
+ });
236
+
237
+ it('should not fail webhook processing if sync callback throws', async () => {
238
+ const onDelivery = jest.fn().mockImplementation(() => {
239
+ throw new Error('Sync callback exploded');
240
+ });
241
+ const service = await createService({ onDelivery });
242
+
243
+ const webhook = createWebhook('delivered');
244
+ const result = await service.processWebhook(webhook);
245
+
246
+ expect(result.success).toBe(true);
247
+ expect(result.event).toBeDefined();
248
+ expect(onDelivery).toHaveBeenCalledTimes(1);
249
+ });
250
+
251
+ it('should handle async callbacks', async () => {
252
+ let resolvedValue: string | undefined;
253
+ const onDelivery = jest.fn().mockImplementation(async (event: EmailEvent) => {
254
+ await new Promise(resolve => setTimeout(resolve, 10));
255
+ resolvedValue = event.recipient;
256
+ });
257
+ const service = await createService({ onDelivery });
258
+
259
+ const webhook = createWebhook('delivered');
260
+ const result = await service.processWebhook(webhook);
261
+
262
+ expect(result.success).toBe(true);
263
+ expect(onDelivery).toHaveBeenCalledTimes(1);
264
+ expect(resolvedValue).toBe('user@example.com');
265
+ });
266
+
267
+ it('should work when no callbacks configured', async () => {
268
+ const config: EmailWebhookEventsConfig = {
269
+ providers: [{
270
+ provider: EmailProvider.SENDGRID,
271
+ enabled: true,
272
+ webhookUrl: '/webhooks/sendgrid',
273
+ secretKey: 'test-secret',
274
+ signatureHeader: 'X-Twilio-Email-Event-Webhook-Signature',
275
+ signatureAlgorithm: 'sha256'
276
+ }],
277
+ persistence: {
278
+ enabled: true,
279
+ adapter: 'memory'
280
+ },
281
+ analytics: {
282
+ realTimeEnabled: true,
283
+ aggregationInterval: 5000,
284
+ retentionDays: 90
285
+ }
286
+ // No callbacks property
287
+ };
288
+
289
+ const service = new EmailWebhookEventsService(config);
290
+ await service.initialize();
291
+
292
+ const webhook = createWebhook('delivered');
293
+ const result = await service.processWebhook(webhook);
294
+
295
+ expect(result.success).toBe(true);
296
+ expect(result.event).toBeDefined();
297
+ });
298
+
299
+ it('should work with empty callbacks object', async () => {
300
+ const service = await createService({});
301
+
302
+ const webhook = createWebhook('delivered');
303
+ const result = await service.processWebhook(webhook);
304
+
305
+ expect(result.success).toBe(true);
306
+ expect(result.event).toBeDefined();
307
+ });
308
+
309
+ it('should invoke multiple different callbacks for different events', async () => {
310
+ const onDelivery = jest.fn();
311
+ const onBounce = jest.fn();
312
+ const onOpen = jest.fn();
313
+ const service = await createService({ onDelivery, onBounce, onOpen });
314
+
315
+ await service.processWebhook(createWebhook('delivered'));
316
+ await service.processWebhook(createWebhook('bounce', {
317
+ payload: {
318
+ event: 'bounce',
319
+ email: 'user@example.com',
320
+ timestamp: Math.floor(Date.now() / 1000),
321
+ sg_message_id: 'msg-123',
322
+ status: '5.1.1',
323
+ reason: 'Unknown user'
324
+ }
325
+ }));
326
+ await service.processWebhook(createWebhook('open'));
327
+
328
+ expect(onDelivery).toHaveBeenCalledTimes(1);
329
+ expect(onBounce).toHaveBeenCalledTimes(1);
330
+ expect(onOpen).toHaveBeenCalledTimes(1);
331
+ });
332
+
333
+ it('should not invoke unrelated callbacks', async () => {
334
+ const onBounce = jest.fn();
335
+ const onComplaint = jest.fn();
336
+ const service = await createService({ onBounce, onComplaint });
337
+
338
+ await service.processWebhook(createWebhook('delivered'));
339
+
340
+ expect(onBounce).not.toHaveBeenCalled();
341
+ expect(onComplaint).not.toHaveBeenCalled();
342
+ });
343
+ });
@@ -11,6 +11,8 @@ import { EmailEvent, EmailEventType, EmailProvider } from '../../src/types';
11
11
 
12
12
  describe('AnalyticsEngine', () => {
13
13
  let engine: AnalyticsEngine;
14
+ let rangeStart: Date;
15
+ let rangeEnd: Date;
14
16
 
15
17
  beforeEach(() => {
16
18
  engine = new AnalyticsEngine({
@@ -18,6 +20,10 @@ describe('AnalyticsEngine', () => {
18
20
  aggregationInterval: 5000,
19
21
  retentionDays: 90
20
22
  });
23
+
24
+ // Use dynamic date range that always encompasses "now"
25
+ rangeStart = new Date(Date.now() - 24 * 60 * 60 * 1000); // 1 day ago
26
+ rangeEnd = new Date(Date.now() + 24 * 60 * 60 * 1000); // 1 day from now
21
27
  });
22
28
 
23
29
  const createEvent = (type: EmailEventType, overrides: Partial<EmailEvent> = {}): EmailEvent => ({
@@ -37,8 +43,8 @@ describe('AnalyticsEngine', () => {
37
43
  await engine.recordEvent(event);
38
44
 
39
45
  const analytics = await engine.getAnalytics(
40
- new Date('2025-01-01'),
41
- new Date('2025-12-31')
46
+ rangeStart,
47
+ rangeEnd
42
48
  );
43
49
 
44
50
  expect(analytics.totalDelivered).toBe(1);
@@ -50,8 +56,8 @@ describe('AnalyticsEngine', () => {
50
56
  await engine.recordEvent(createEvent(EmailEventType.BOUNCED));
51
57
 
52
58
  const analytics = await engine.getAnalytics(
53
- new Date('2025-01-01'),
54
- new Date('2025-12-31')
59
+ rangeStart,
60
+ rangeEnd
55
61
  );
56
62
 
57
63
  expect(analytics.totalSent).toBe(3);
@@ -65,8 +71,8 @@ describe('AnalyticsEngine', () => {
65
71
  await engine.recordEvent(createEvent(EmailEventType.OPENED));
66
72
 
67
73
  const analytics = await engine.getAnalytics(
68
- new Date('2025-01-01'),
69
- new Date('2025-12-31')
74
+ rangeStart,
75
+ rangeEnd
70
76
  );
71
77
 
72
78
  expect(analytics.totalDelivered).toBe(2);
@@ -80,8 +86,8 @@ describe('AnalyticsEngine', () => {
80
86
  await engine.recordEvent(createEvent(EmailEventType.CLICKED));
81
87
 
82
88
  const analytics = await engine.getAnalytics(
83
- new Date('2025-01-01'),
84
- new Date('2025-12-31')
89
+ rangeStart,
90
+ rangeEnd
85
91
  );
86
92
 
87
93
  expect(analytics.totalOpened).toBe(2);
@@ -91,8 +97,8 @@ describe('AnalyticsEngine', () => {
91
97
 
92
98
  it('should handle zero denominators gracefully', async () => {
93
99
  const analytics = await engine.getAnalytics(
94
- new Date('2025-01-01'),
95
- new Date('2025-12-31')
100
+ rangeStart,
101
+ rangeEnd
96
102
  );
97
103
 
98
104
  expect(analytics.deliveryRate).toBe(0);
@@ -105,8 +111,8 @@ describe('AnalyticsEngine', () => {
105
111
  await engine.recordEvent(createEvent(EmailEventType.BOUNCED));
106
112
 
107
113
  const analytics = await engine.getAnalytics(
108
- new Date('2025-01-01'),
109
- new Date('2025-12-31')
114
+ rangeStart,
115
+ rangeEnd
110
116
  );
111
117
 
112
118
  expect(analytics.bounceRate).toBe(0.5);
@@ -117,8 +123,8 @@ describe('AnalyticsEngine', () => {
117
123
  await engine.recordEvent(createEvent(EmailEventType.DELIVERED, { provider: EmailProvider.MAILGUN }));
118
124
 
119
125
  const analytics = await engine.getAnalytics(
120
- new Date('2025-01-01'),
121
- new Date('2025-12-31')
126
+ rangeStart,
127
+ rangeEnd
122
128
  );
123
129
 
124
130
  expect(analytics.byProvider[EmailProvider.SENDGRID].delivered).toBe(1);
@@ -131,8 +137,8 @@ describe('AnalyticsEngine', () => {
131
137
  await engine.recordEvent(createEvent(EmailEventType.CLICKED));
132
138
 
133
139
  const analytics = await engine.getAnalytics(
134
- new Date('2025-01-01'),
135
- new Date('2025-12-31')
140
+ rangeStart,
141
+ rangeEnd
136
142
  );
137
143
 
138
144
  expect(analytics.byEventType[EmailEventType.DELIVERED]).toBe(1);
@@ -153,8 +159,8 @@ describe('AnalyticsEngine', () => {
153
159
  );
154
160
 
155
161
  const analytics = await engine.getAnalytics(
156
- new Date('2025-01-01'),
157
- new Date('2025-12-31')
162
+ rangeStart,
163
+ rangeEnd
158
164
  );
159
165
 
160
166
  expect(analytics.byBounceType).toBeDefined();
@@ -183,8 +189,8 @@ describe('AnalyticsEngine', () => {
183
189
  );
184
190
 
185
191
  const analytics = await engine.getAnalytics(
186
- new Date('2025-01-01'),
187
- new Date('2025-12-31')
192
+ rangeStart,
193
+ rangeEnd
188
194
  );
189
195
 
190
196
  expect(analytics.topLinks).toBeDefined();
@@ -206,8 +212,8 @@ describe('AnalyticsEngine', () => {
206
212
  );
207
213
 
208
214
  const analytics = await engine.getAnalytics(
209
- new Date('2025-01-01'),
210
- new Date('2025-12-31')
215
+ rangeStart,
216
+ rangeEnd
211
217
  );
212
218
 
213
219
  expect(analytics.byDevice).toBeDefined();
@@ -220,8 +226,8 @@ describe('AnalyticsEngine', () => {
220
226
  await engine.recordEvent(createEvent(EmailEventType.DELIVERED, { provider: EmailProvider.MAILGUN }));
221
227
 
222
228
  const analytics = await engine.getAnalytics(
223
- new Date('2025-01-01'),
224
- new Date('2025-12-31'),
229
+ rangeStart,
230
+ rangeEnd,
225
231
  { provider: EmailProvider.SENDGRID }
226
232
  );
227
233
 
@@ -233,8 +239,8 @@ describe('AnalyticsEngine', () => {
233
239
  await engine.recordEvent(createEvent(EmailEventType.OPENED));
234
240
 
235
241
  const analytics = await engine.getAnalytics(
236
- new Date('2025-01-01'),
237
- new Date('2025-12-31'),
242
+ rangeStart,
243
+ rangeEnd,
238
244
  { eventTypes: [EmailEventType.DELIVERED] }
239
245
  );
240
246
 
@@ -45,6 +45,10 @@ export declare class EmailWebhookEventsService {
45
45
  * Send notification via channel
46
46
  */
47
47
  private sendNotification;
48
+ /**
49
+ * Invoke registered event callbacks
50
+ */
51
+ private invokeCallbacks;
48
52
  /**
49
53
  * Create persistence adapter
50
54
  */
@@ -1 +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"}
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;IAkD1E;;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,eAAe;IAqC7B;;OAEG;YACW,wBAAwB;CAWvC"}
@@ -64,6 +64,8 @@ class EmailWebhookEventsService {
64
64
  }
65
65
  // Update analytics
66
66
  await this.analyticsEngine.recordEvent(normalizedEvent);
67
+ // Invoke event callbacks
68
+ await this.invokeCallbacks(normalizedEvent);
67
69
  // Publish critical events
68
70
  if (this.isCriticalEvent(normalizedEvent)) {
69
71
  await this.publishCriticalEvent(normalizedEvent);
@@ -180,6 +182,46 @@ class EmailWebhookEventsService {
180
182
  recipient: event.recipient
181
183
  });
182
184
  }
185
+ /**
186
+ * Invoke registered event callbacks
187
+ */
188
+ async invokeCallbacks(event) {
189
+ const callbacks = this.config.callbacks;
190
+ if (!callbacks)
191
+ return;
192
+ try {
193
+ switch (event.type) {
194
+ case types_1.EmailEventType.BOUNCED:
195
+ await callbacks.onBounce?.(event);
196
+ break;
197
+ case types_1.EmailEventType.COMPLAINED:
198
+ await callbacks.onComplaint?.(event);
199
+ break;
200
+ case types_1.EmailEventType.UNSUBSCRIBED:
201
+ await callbacks.onUnsubscribe?.(event);
202
+ break;
203
+ case types_1.EmailEventType.DELIVERED:
204
+ await callbacks.onDelivery?.(event);
205
+ break;
206
+ case types_1.EmailEventType.OPENED:
207
+ await callbacks.onOpen?.(event);
208
+ break;
209
+ case types_1.EmailEventType.CLICKED:
210
+ await callbacks.onClick?.(event);
211
+ break;
212
+ case types_1.EmailEventType.FAILED:
213
+ await callbacks.onFailure?.(event);
214
+ break;
215
+ }
216
+ }
217
+ catch (error) {
218
+ this.logger.error('Event callback failed', {
219
+ eventType: event.type,
220
+ eventId: event.id,
221
+ error: error instanceof Error ? error.message : 'Unknown error'
222
+ });
223
+ }
224
+ }
183
225
  /**
184
226
  * Create persistence adapter
185
227
  */
@@ -1 +1 @@
1
- {"version":3,"file":"EmailWebhookEventsService.js","sourceRoot":"","sources":["../src/EmailWebhookEventsService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"EmailWebhookEventsService.js","sourceRoot":"","sources":["../src/EmailWebhookEventsService.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { EmailWebhookEventsService } from './EmailWebhookEventsService';
2
- export { EmailEvent, EmailEventType, EmailProvider, EmailEventMetadata, EmailAnalytics, ProviderAnalytics, EmailWebhookEventsConfig, ProviderWebhookConfig, NotificationChannel, EmailWebhookResult, WebhookPayload, AnalyticsFilters, EventQuery, EmailWebhookError, EmailWebhookErrorCode } from './types';
2
+ export { EmailEvent, EmailEventType, EmailProvider, EmailEventMetadata, EmailAnalytics, ProviderAnalytics, EmailWebhookEventsConfig, ProviderWebhookConfig, NotificationChannel, EmailWebhookResult, WebhookPayload, AnalyticsFilters, EventQuery, EmailWebhookError, EmailWebhookErrorCode, WebhookEventCallbacks } from './types';
3
3
  export { EventNormalizer } from './normalizers/EventNormalizer';
4
4
  export { SendGridNormalizer } from './normalizers/SendGridNormalizer';
5
5
  export { MailgunNormalizer } from './normalizers/MailgunNormalizer';
@@ -1 +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"}
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,EACrB,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.map CHANGED
@@ -1 +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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;;AAEF,yEAAwE;AAA/D,sIAAA,yBAAyB,OAAA;AAElC,iCAiBiB;AAff,uGAAA,cAAc,OAAA;AACd,sGAAA,aAAa,OAAA;AAYb,8GAAA,qBAAqB,OAAA;AAKvB,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"}
package/dist/types.d.ts CHANGED
@@ -26,7 +26,8 @@ export declare enum EmailProvider {
26
26
  AWS_SES = "aws_ses",
27
27
  POSTMARK = "postmark",
28
28
  SMTP2GO = "smtp2go",
29
- GENERIC_SMTP = "generic_smtp"
29
+ GENERIC_SMTP = "generic_smtp",
30
+ MANDRILL = "mandrill"
30
31
  }
31
32
  export interface EmailEventMetadata {
32
33
  url?: string;
@@ -97,6 +98,15 @@ export interface ProviderAnalytics {
97
98
  openRate: number;
98
99
  clickRate: number;
99
100
  }
101
+ export interface WebhookEventCallbacks {
102
+ readonly onBounce?: (event: EmailEvent) => void | Promise<void>;
103
+ readonly onComplaint?: (event: EmailEvent) => void | Promise<void>;
104
+ readonly onUnsubscribe?: (event: EmailEvent) => void | Promise<void>;
105
+ readonly onDelivery?: (event: EmailEvent) => void | Promise<void>;
106
+ readonly onOpen?: (event: EmailEvent) => void | Promise<void>;
107
+ readonly onClick?: (event: EmailEvent) => void | Promise<void>;
108
+ readonly onFailure?: (event: EmailEvent) => void | Promise<void>;
109
+ }
100
110
  export interface EmailWebhookEventsConfig {
101
111
  providers: ProviderWebhookConfig[];
102
112
  persistence?: {
@@ -114,6 +124,7 @@ export interface EmailWebhookEventsConfig {
114
124
  criticalEvents: EmailEventType[];
115
125
  channels: NotificationChannel[];
116
126
  };
127
+ callbacks?: WebhookEventCallbacks;
117
128
  rateLimiting?: {
118
129
  enabled: boolean;
119
130
  maxEventsPerSecond: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,UAAU;IAEzB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAGlB,IAAI,EAAE,cAAc,CAAC;IAGrB,SAAS,EAAE,MAAM,CAAC;IAGlB,SAAS,EAAE,IAAI,CAAC;IAGhB,QAAQ,EAAE,aAAa,CAAC;IAGxB,QAAQ,EAAE,kBAAkB,CAAC;IAG7B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,oBAAY,cAAc;IACxB,SAAS,cAAc;IACvB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,UAAU,eAAe;IACzB,YAAY,iBAAiB;IAC7B,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,OAAO,YAAY;CACpB;AAED,oBAAY,aAAa;IACvB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,YAAY,iBAAiB;IAC7B,QAAQ,aAAa;CACtB;AAED,MAAM,WAAW,kBAAkB;IAEjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAGhB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAG/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,IAAI,CAAC;IAGnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IACzD,QAAQ,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAGD,MAAM,WAAW,cAAc;IAE7B,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,EAAE,IAAI,CAAC;IAGd,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IAGpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IAGxB,UAAU,EAAE,MAAM,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;IACrD,WAAW,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAC5C,YAAY,CAAC,EAAE;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IAGF,QAAQ,CAAC,EAAE,KAAK,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IAGH,QAAQ,CAAC,EAAE;QACT,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAGD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAGD,MAAM,WAAW,wBAAwB;IAEvC,SAAS,EAAE,qBAAqB,EAAE,CAAC;IAGnC,WAAW,CAAC,EAAE;QACZ,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,GAAG,QAAQ,CAAC;QAC1D,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;IAGF,SAAS,CAAC,EAAE;QACV,eAAe,EAAE,OAAO,CAAC;QACzB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IAGF,aAAa,CAAC,EAAE;QACd,OAAO,EAAE,OAAO,CAAC;QACjB,cAAc,EAAE,cAAc,EAAE,CAAC;QACjC,QAAQ,EAAE,mBAAmB,EAAE,CAAC;KACjC,CAAC;IAGF,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAGlC,YAAY,CAAC,EAAE;QACb,OAAO,EAAE,OAAO,CAAC;QACjB,kBAAkB,EAAE,MAAM,CAAC;KAC5B,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;CAClD;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;IACvC,MAAM,EAAE;QACN,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,aAAa,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,qBAAqB,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,oBAAY,qBAAqB;IAC/B,iBAAiB,sBAAsB;IACvC,oBAAoB,yBAAyB;IAC7C,oBAAoB,yBAAyB;IAC7C,kBAAkB,uBAAuB;IACzC,uBAAuB,4BAA4B;IACnD,mBAAmB,wBAAwB;CAC5C"}
package/dist/types.js CHANGED
@@ -28,6 +28,7 @@ var EmailProvider;
28
28
  EmailProvider["POSTMARK"] = "postmark";
29
29
  EmailProvider["SMTP2GO"] = "smtp2go";
30
30
  EmailProvider["GENERIC_SMTP"] = "generic_smtp";
31
+ EmailProvider["MANDRILL"] = "mandrill";
31
32
  })(EmailProvider || (exports.EmailProvider = EmailProvider = {}));
32
33
  var EmailWebhookErrorCode;
33
34
  (function (EmailWebhookErrorCode) {
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;;AA4BF,IAAY,cAUX;AAVD,WAAY,cAAc;IACxB,yCAAuB,CAAA;IACvB,mCAAiB,CAAA;IACjB,qCAAmB,CAAA;IACnB,qCAAmB,CAAA;IACnB,2CAAyB,CAAA;IACzB,+CAA6B,CAAA;IAC7B,mCAAiB,CAAA;IACjB,uCAAqB,CAAA;IACrB,qCAAmB,CAAA;AACrB,CAAC,EAVW,cAAc,8BAAd,cAAc,QAUzB;AAED,IAAY,aAOX;AAPD,WAAY,aAAa;IACvB,sCAAqB,CAAA;IACrB,oCAAmB,CAAA;IACnB,oCAAmB,CAAA;IACnB,sCAAqB,CAAA;IACrB,oCAAmB,CAAA;IACnB,8CAA6B,CAAA;AAC/B,CAAC,EAPW,aAAa,6BAAb,aAAa,QAOxB;AAyLD,IAAY,qBAOX;AAPD,WAAY,qBAAqB;IAC/B,gEAAuC,CAAA;IACvC,sEAA6C,CAAA;IAC7C,sEAA6C,CAAA;IAC7C,kEAAyC,CAAA;IACzC,4EAAmD,CAAA;IACnD,oEAA2C,CAAA;AAC7C,CAAC,EAPW,qBAAqB,qCAArB,qBAAqB,QAOhC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;;AA4BF,IAAY,cAUX;AAVD,WAAY,cAAc;IACxB,yCAAuB,CAAA;IACvB,mCAAiB,CAAA;IACjB,qCAAmB,CAAA;IACnB,qCAAmB,CAAA;IACnB,2CAAyB,CAAA;IACzB,+CAA6B,CAAA;IAC7B,mCAAiB,CAAA;IACjB,uCAAqB,CAAA;IACrB,qCAAmB,CAAA;AACrB,CAAC,EAVW,cAAc,8BAAd,cAAc,QAUzB;AAED,IAAY,aAQX;AARD,WAAY,aAAa;IACvB,sCAAqB,CAAA;IACrB,oCAAmB,CAAA;IACnB,oCAAmB,CAAA;IACnB,sCAAqB,CAAA;IACrB,oCAAmB,CAAA;IACnB,8CAA6B,CAAA;IAC7B,sCAAqB,CAAA;AACvB,CAAC,EARW,aAAa,6BAAb,aAAa,QAQxB;AAuMD,IAAY,qBAOX;AAPD,WAAY,qBAAqB;IAC/B,gEAAuC,CAAA;IACvC,sEAA6C,CAAA;IAC7C,sEAA6C,CAAA;IAC7C,kEAAyC,CAAA;IACzC,4EAAmD,CAAA;IACnD,oEAA2C,CAAA;AAC7C,CAAC,EAPW,qBAAqB,qCAArB,qBAAqB,QAOhC"}
package/package.json CHANGED
@@ -1,17 +1,9 @@
1
1
  {
2
2
  "name": "@bernierllc/email-webhook-events",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Email event tracking, normalization, and analytics service for processing webhooks from all email providers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
- "scripts": {
8
- "build": "tsc",
9
- "test": "jest",
10
- "test:run": "jest",
11
- "test:coverage": "jest --coverage",
12
- "lint": "eslint src --ext .ts",
13
- "clean": "rm -rf dist"
14
- },
15
7
  "keywords": [
16
8
  "email",
17
9
  "webhooks",
@@ -28,7 +20,7 @@
28
20
  "author": "Bernier LLC",
29
21
  "license": "SEE LICENSE IN LICENSE",
30
22
  "dependencies": {
31
- "@bernierllc/email-manager": "0.4.0"
23
+ "@bernierllc/email-manager": "0.6.0"
32
24
  },
33
25
  "devDependencies": {
34
26
  "@ngrok/ngrok": "^1.7.0",
@@ -55,5 +47,12 @@
55
47
  "logger": "integrated"
56
48
  }
57
49
  },
58
- "gitHead": "af7e74b3715d56d3a193e1bb6743b337c2b0df6d"
59
- }
50
+ "scripts": {
51
+ "build": "tsc",
52
+ "test": "jest",
53
+ "test:run": "jest --forceExit",
54
+ "test:coverage": "jest --coverage",
55
+ "lint": "eslint src --ext .ts",
56
+ "clean": "rm -rf dist"
57
+ }
58
+ }
@@ -96,6 +96,9 @@ export class EmailWebhookEventsService {
96
96
  // Update analytics
97
97
  await this.analyticsEngine.recordEvent(normalizedEvent);
98
98
 
99
+ // Invoke event callbacks
100
+ await this.invokeCallbacks(normalizedEvent);
101
+
99
102
  // Publish critical events
100
103
  if (this.isCriticalEvent(normalizedEvent)) {
101
104
  await this.publishCriticalEvent(normalizedEvent);
@@ -230,6 +233,46 @@ export class EmailWebhookEventsService {
230
233
  });
231
234
  }
232
235
 
236
+ /**
237
+ * Invoke registered event callbacks
238
+ */
239
+ private async invokeCallbacks(event: EmailEvent): Promise<void> {
240
+ const callbacks = this.config.callbacks;
241
+ if (!callbacks) return;
242
+
243
+ try {
244
+ switch (event.type) {
245
+ case EmailEventType.BOUNCED:
246
+ await callbacks.onBounce?.(event);
247
+ break;
248
+ case EmailEventType.COMPLAINED:
249
+ await callbacks.onComplaint?.(event);
250
+ break;
251
+ case EmailEventType.UNSUBSCRIBED:
252
+ await callbacks.onUnsubscribe?.(event);
253
+ break;
254
+ case EmailEventType.DELIVERED:
255
+ await callbacks.onDelivery?.(event);
256
+ break;
257
+ case EmailEventType.OPENED:
258
+ await callbacks.onOpen?.(event);
259
+ break;
260
+ case EmailEventType.CLICKED:
261
+ await callbacks.onClick?.(event);
262
+ break;
263
+ case EmailEventType.FAILED:
264
+ await callbacks.onFailure?.(event);
265
+ break;
266
+ }
267
+ } catch (error) {
268
+ this.logger.error('Event callback failed', {
269
+ eventType: event.type,
270
+ eventId: event.id,
271
+ error: error instanceof Error ? error.message : 'Unknown error'
272
+ });
273
+ }
274
+ }
275
+
233
276
  /**
234
277
  * Create persistence adapter
235
278
  */
package/src/index.ts CHANGED
@@ -23,7 +23,8 @@ export {
23
23
  AnalyticsFilters,
24
24
  EventQuery,
25
25
  EmailWebhookError,
26
- EmailWebhookErrorCode
26
+ EmailWebhookErrorCode,
27
+ WebhookEventCallbacks
27
28
  } from './types';
28
29
 
29
30
  export { EventNormalizer } from './normalizers/EventNormalizer';
package/src/types.ts CHANGED
@@ -50,7 +50,8 @@ export enum EmailProvider {
50
50
  AWS_SES = 'aws_ses',
51
51
  POSTMARK = 'postmark',
52
52
  SMTP2GO = 'smtp2go',
53
- GENERIC_SMTP = 'generic_smtp'
53
+ GENERIC_SMTP = 'generic_smtp',
54
+ MANDRILL = 'mandrill',
54
55
  }
55
56
 
56
57
  export interface EmailEventMetadata {
@@ -148,6 +149,17 @@ export interface ProviderAnalytics {
148
149
  clickRate: number;
149
150
  }
150
151
 
152
+ // Event callback hooks
153
+ export interface WebhookEventCallbacks {
154
+ readonly onBounce?: (event: EmailEvent) => void | Promise<void>;
155
+ readonly onComplaint?: (event: EmailEvent) => void | Promise<void>;
156
+ readonly onUnsubscribe?: (event: EmailEvent) => void | Promise<void>;
157
+ readonly onDelivery?: (event: EmailEvent) => void | Promise<void>;
158
+ readonly onOpen?: (event: EmailEvent) => void | Promise<void>;
159
+ readonly onClick?: (event: EmailEvent) => void | Promise<void>;
160
+ readonly onFailure?: (event: EmailEvent) => void | Promise<void>;
161
+ }
162
+
151
163
  // Configuration
152
164
  export interface EmailWebhookEventsConfig {
153
165
  // Provider configurations
@@ -174,6 +186,9 @@ export interface EmailWebhookEventsConfig {
174
186
  channels: NotificationChannel[];
175
187
  };
176
188
 
189
+ // Event callbacks
190
+ callbacks?: WebhookEventCallbacks;
191
+
177
192
  // Rate limiting
178
193
  rateLimiting?: {
179
194
  enabled: boolean;