@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 +20 -19
- package/CHANGELOG.md +22 -0
- package/README.md +66 -0
- package/__tests__/EmailWebhookEventsService.test.ts +8 -4
- package/__tests__/WebhookEventCallbacks.test.ts +343 -0
- package/__tests__/analytics/AnalyticsEngine.test.ts +32 -26
- package/dist/EmailWebhookEventsService.d.ts +4 -0
- package/dist/EmailWebhookEventsService.d.ts.map +1 -1
- package/dist/EmailWebhookEventsService.js +42 -0
- package/dist/EmailWebhookEventsService.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +12 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +11 -12
- package/src/EmailWebhookEventsService.ts +43 -0
- package/src/index.ts +2 -1
- package/src/types.ts +16 -1
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-
|
|
24
|
-
'@typescript-eslint/no-unused-vars': ['error', {
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
152
|
-
|
|
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
|
-
|
|
169
|
-
|
|
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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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
|
-
|
|
69
|
-
|
|
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
|
-
|
|
84
|
-
|
|
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
|
-
|
|
95
|
-
|
|
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
|
-
|
|
109
|
-
|
|
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
|
-
|
|
121
|
-
|
|
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
|
-
|
|
135
|
-
|
|
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
|
-
|
|
157
|
-
|
|
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
|
-
|
|
187
|
-
|
|
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
|
-
|
|
210
|
-
|
|
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
|
-
|
|
224
|
-
|
|
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
|
-
|
|
237
|
-
|
|
242
|
+
rangeStart,
|
|
243
|
+
rangeEnd,
|
|
238
244
|
{ eventTypes: [EmailEventType.DELIVERED] }
|
|
239
245
|
);
|
|
240
246
|
|
|
@@ -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;
|
|
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":";AAAA;;;;;;EAME;;;AAEF,mCAWiB;AACjB,iEAA8D;AAE9D,yFAAsF;AAEtF,yEAAsE;AACtE,uEAAoE;AACpE,+DAA4D;AAC5D,yEAAsE;AACtE,uEAAoE;AASpE,MAAM,UAAU;IACd,IAAI,CAAC,OAAe,EAAE,IAA8B;QAClD,OAAO,CAAC,GAAG,CAAC,UAAU,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAA8B;QAClD,OAAO,CAAC,IAAI,CAAC,UAAU,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAA8B;QACnD,OAAO,CAAC,KAAK,CAAC,WAAW,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;CACF;AAED,MAAa,yBAAyB;IAKpC,YAAoB,MAAgC;QAAhC,WAAM,GAAN,MAAM,CAA0B;QAClD,IAAI,CAAC,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC/B,IAAI,CAAC,eAAe,GAAG,IAAI,iCAAe,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC/D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU;QACd,yBAAyB;QACzB,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC;YACrC,IAAI,CAAC,kBAAkB,GAAG,MAAM,IAAI,CAAC,wBAAwB,EAAE,CAAC;QAClE,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;IAC/D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,OAAuB;QAC1C,IAAI,CAAC;YACH,+CAA+C;YAC/C,0CAA0C;YAE1C,kBAAkB;YAClB,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YAC3D,IAAI,CAAC,eAAe,EAAE,CAAC;gBACrB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,mCAAmC,EAAE;oBACpD,QAAQ,EAAE,OAAO,CAAC,QAAQ;iBAC3B,CAAC,CAAC;gBACH,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC;YACjE,CAAC;YAED,gBAAgB;YAChB,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBAC5B,MAAM,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;YAC3D,CAAC;YAED,mBAAmB;YACnB,MAAM,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;YAExD,0BAA0B;YAC1B,IAAI,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,EAAE,CAAC;gBAC1C,MAAM,IAAI,CAAC,oBAAoB,CAAC,eAAe,CAAC,CAAC;YACnD,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oCAAoC,EAAE;gBACrD,OAAO,EAAE,eAAe,CAAC,EAAE;gBAC3B,IAAI,EAAE,eAAe,CAAC,IAAI;gBAC1B,SAAS,EAAE,eAAe,CAAC,SAAS;aACrC,CAAC,CAAC;YAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC;QAEnD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,EAAE;gBAClD,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;gBAC/D,QAAQ,EAAE,OAAO,CAAC,QAAQ;aAC3B,CAAC,CAAC;YACH,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;aAChE,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAChB,SAAe,EACf,OAAa,EACb,OAA0B;QAE1B,OAAO,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACxE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CAAC,KAAiB;QACjC,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;QACD,OAAO,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,iBAAiB,CAAC,OAAe;QACrC,OAAO,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IACvC,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,cAAc,CAAC,OAAuB;QAClD,MAAM,UAAU,GAAG,IAAI,CAAC,wBAAwB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnE,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,EAAE;gBACpD,QAAQ,EAAE,OAAO,CAAC,QAAQ;aAC3B,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAED;;OAEG;IACK,wBAAwB,CAAC,QAAuB;QACtD,QAAQ,QAAQ,EAAE,CAAC;YACjB,KAAK,qBAAa,CAAC,QAAQ;gBACzB,OAAO,IAAI,uCAAkB,EAAE,CAAC;YAClC,KAAK,qBAAa,CAAC,OAAO;gBACxB,OAAO,IAAI,qCAAiB,EAAE,CAAC;YACjC,KAAK,qBAAa,CAAC,OAAO;gBACxB,OAAO,IAAI,6BAAa,EAAE,CAAC;YAC7B,KAAK,qBAAa,CAAC,QAAQ;gBACzB,OAAO,IAAI,uCAAkB,EAAE,CAAC;YAClC,KAAK,qBAAa,CAAC,OAAO;gBACxB,OAAO,IAAI,qCAAiB,EAAE,CAAC;YACjC;gBACE,OAAO,IAAI,CAAC;QAChB,CAAC;IACH,CAAC;IAED;;OAEG;IACK,eAAe,CAAC,KAAiB;QACvC,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,cAAc,IAAI;YACjE,sBAAc,CAAC,OAAO;YACtB,sBAAc,CAAC,UAAU;YACzB,sBAAc,CAAC,MAAM;SACtB,CAAC;QACF,OAAO,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,oBAAoB,CAAC,KAAiB;QAClD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,OAAO;YAAE,OAAO;QAEhD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;YACzD,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAC9C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE;oBAC/C,OAAO,EAAE,OAAO,CAAC,IAAI;oBACrB,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;iBAChE,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,gBAAgB,CAC5B,OAA4B,EAC5B,KAAiB;QAEjB,0DAA0D;QAC1D,oBAAoB;QACpB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,6BAA6B,EAAE;YAC9C,OAAO,EAAE,OAAO,CAAC,IAAI;YACrB,SAAS,EAAE,KAAK,CAAC,IAAI;YACrB,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,wBAAwB;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,IAAI,QAAQ,CAAC;QAE7D,QAAQ,OAAO,EAAE,CAAC;YAChB,KAAK,QAAQ;gBACX,OAAO,IAAI,uDAA0B,EAAE,CAAC;YAC1C,2CAA2C;YAC3C;gBACE,MAAM,IAAI,KAAK,CAAC,oCAAoC,OAAO,EAAE,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"EmailWebhookEventsService.js","sourceRoot":"","sources":["../src/EmailWebhookEventsService.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;;AAEF,mCAWiB;AACjB,iEAA8D;AAE9D,yFAAsF;AAEtF,yEAAsE;AACtE,uEAAoE;AACpE,+DAA4D;AAC5D,yEAAsE;AACtE,uEAAoE;AASpE,MAAM,UAAU;IACd,IAAI,CAAC,OAAe,EAAE,IAA8B;QAClD,OAAO,CAAC,GAAG,CAAC,UAAU,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAA8B;QAClD,OAAO,CAAC,IAAI,CAAC,UAAU,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAA8B;QACnD,OAAO,CAAC,KAAK,CAAC,WAAW,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;CACF;AAED,MAAa,yBAAyB;IAKpC,YAAoB,MAAgC;QAAhC,WAAM,GAAN,MAAM,CAA0B;QAClD,IAAI,CAAC,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC/B,IAAI,CAAC,eAAe,GAAG,IAAI,iCAAe,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC/D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU;QACd,yBAAyB;QACzB,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC;YACrC,IAAI,CAAC,kBAAkB,GAAG,MAAM,IAAI,CAAC,wBAAwB,EAAE,CAAC;QAClE,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;IAC/D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,OAAuB;QAC1C,IAAI,CAAC;YACH,+CAA+C;YAC/C,0CAA0C;YAE1C,kBAAkB;YAClB,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YAC3D,IAAI,CAAC,eAAe,EAAE,CAAC;gBACrB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,mCAAmC,EAAE;oBACpD,QAAQ,EAAE,OAAO,CAAC,QAAQ;iBAC3B,CAAC,CAAC;gBACH,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC;YACjE,CAAC;YAED,gBAAgB;YAChB,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBAC5B,MAAM,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;YAC3D,CAAC;YAED,mBAAmB;YACnB,MAAM,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;YAExD,yBAAyB;YACzB,MAAM,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC;YAE5C,0BAA0B;YAC1B,IAAI,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,EAAE,CAAC;gBAC1C,MAAM,IAAI,CAAC,oBAAoB,CAAC,eAAe,CAAC,CAAC;YACnD,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oCAAoC,EAAE;gBACrD,OAAO,EAAE,eAAe,CAAC,EAAE;gBAC3B,IAAI,EAAE,eAAe,CAAC,IAAI;gBAC1B,SAAS,EAAE,eAAe,CAAC,SAAS;aACrC,CAAC,CAAC;YAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC;QAEnD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,EAAE;gBAClD,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;gBAC/D,QAAQ,EAAE,OAAO,CAAC,QAAQ;aAC3B,CAAC,CAAC;YACH,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;aAChE,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAChB,SAAe,EACf,OAAa,EACb,OAA0B;QAE1B,OAAO,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACxE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CAAC,KAAiB;QACjC,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;QACD,OAAO,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,iBAAiB,CAAC,OAAe;QACrC,OAAO,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IACvC,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,cAAc,CAAC,OAAuB;QAClD,MAAM,UAAU,GAAG,IAAI,CAAC,wBAAwB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnE,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,EAAE;gBACpD,QAAQ,EAAE,OAAO,CAAC,QAAQ;aAC3B,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAED;;OAEG;IACK,wBAAwB,CAAC,QAAuB;QACtD,QAAQ,QAAQ,EAAE,CAAC;YACjB,KAAK,qBAAa,CAAC,QAAQ;gBACzB,OAAO,IAAI,uCAAkB,EAAE,CAAC;YAClC,KAAK,qBAAa,CAAC,OAAO;gBACxB,OAAO,IAAI,qCAAiB,EAAE,CAAC;YACjC,KAAK,qBAAa,CAAC,OAAO;gBACxB,OAAO,IAAI,6BAAa,EAAE,CAAC;YAC7B,KAAK,qBAAa,CAAC,QAAQ;gBACzB,OAAO,IAAI,uCAAkB,EAAE,CAAC;YAClC,KAAK,qBAAa,CAAC,OAAO;gBACxB,OAAO,IAAI,qCAAiB,EAAE,CAAC;YACjC;gBACE,OAAO,IAAI,CAAC;QAChB,CAAC;IACH,CAAC;IAED;;OAEG;IACK,eAAe,CAAC,KAAiB;QACvC,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,cAAc,IAAI;YACjE,sBAAc,CAAC,OAAO;YACtB,sBAAc,CAAC,UAAU;YACzB,sBAAc,CAAC,MAAM;SACtB,CAAC;QACF,OAAO,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,oBAAoB,CAAC,KAAiB;QAClD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,OAAO;YAAE,OAAO;QAEhD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;YACzD,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAC9C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE;oBAC/C,OAAO,EAAE,OAAO,CAAC,IAAI;oBACrB,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;iBAChE,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,gBAAgB,CAC5B,OAA4B,EAC5B,KAAiB;QAEjB,0DAA0D;QAC1D,oBAAoB;QACpB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,6BAA6B,EAAE;YAC9C,OAAO,EAAE,OAAO,CAAC,IAAI;YACrB,SAAS,EAAE,KAAK,CAAC,IAAI;YACrB,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,eAAe,CAAC,KAAiB;QAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;QACxC,IAAI,CAAC,SAAS;YAAE,OAAO;QAEvB,IAAI,CAAC;YACH,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;gBACnB,KAAK,sBAAc,CAAC,OAAO;oBACzB,MAAM,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC;oBAClC,MAAM;gBACR,KAAK,sBAAc,CAAC,UAAU;oBAC5B,MAAM,SAAS,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC;oBACrC,MAAM;gBACR,KAAK,sBAAc,CAAC,YAAY;oBAC9B,MAAM,SAAS,CAAC,aAAa,EAAE,CAAC,KAAK,CAAC,CAAC;oBACvC,MAAM;gBACR,KAAK,sBAAc,CAAC,SAAS;oBAC3B,MAAM,SAAS,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC;oBACpC,MAAM;gBACR,KAAK,sBAAc,CAAC,MAAM;oBACxB,MAAM,SAAS,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC;oBAChC,MAAM;gBACR,KAAK,sBAAc,CAAC,OAAO;oBACzB,MAAM,SAAS,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;oBACjC,MAAM;gBACR,KAAK,sBAAc,CAAC,MAAM;oBACxB,MAAM,SAAS,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC;oBACnC,MAAM;YACV,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE;gBACzC,SAAS,EAAE,KAAK,CAAC,IAAI;gBACrB,OAAO,EAAE,KAAK,CAAC,EAAE;gBACjB,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;aAChE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,wBAAwB;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,IAAI,QAAQ,CAAC;QAE7D,QAAQ,OAAO,EAAE,CAAC;YAChB,KAAK,QAAQ;gBACX,OAAO,IAAI,uDAA0B,EAAE,CAAC;YAC1C,2CAA2C;YAC3C;gBACE,MAAM,IAAI,KAAK,CAAC,oCAAoC,OAAO,EAAE,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;CACF;AA9OD,8DA8OC"}
|
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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
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,
|
|
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.
|
|
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.
|
|
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
|
-
"
|
|
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
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;
|