@plyaz/types 1.13.3 → 1.13.5
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/dist/api/client/types.d.ts +3 -3
- package/dist/api/config/types.d.ts +62 -5
- package/dist/api/debugger/enums.d.ts +12 -0
- package/dist/api/debugger/types.d.ts +1 -1
- package/dist/api/errors/types.d.ts +46 -238
- package/dist/api/events/factories/errors/types.d.ts +26 -27
- package/dist/api/index.cjs +1457 -669
- package/dist/api/index.cjs.map +1 -1
- package/dist/api/index.d.ts +5 -2
- package/dist/api/index.js +1385 -603
- package/dist/api/index.js.map +1 -1
- package/dist/db/DatabaseAdapter.d.ts +2 -2
- package/dist/db/DatabaseService.d.ts +3 -3
- package/dist/db/config.types.d.ts +1 -1
- package/dist/db/database.types.d.ts +2 -2
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js.map +1 -1
- package/dist/errors/codes.d.ts +253 -0
- package/dist/errors/enums.d.ts +199 -0
- package/dist/errors/index.cjs +1307 -0
- package/dist/errors/index.cjs.map +1 -1
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.js +1284 -1
- package/dist/errors/index.js.map +1 -1
- package/dist/errors/types.d.ts +630 -14
- package/dist/index.cjs +2182 -1218
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +43 -1
- package/dist/index.js +1849 -911
- package/dist/index.js.map +1 -1
- package/dist/notifications/enums.d.ts +140 -0
- package/dist/notifications/index.cjs +4353 -0
- package/dist/notifications/index.cjs.map +1 -0
- package/dist/notifications/index.js +145 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/schemas.d.ts +73 -0
- package/dist/notifications/types.d.ts +1937 -0
- package/dist/payments/base-error/enum.d.ts +79 -0
- package/dist/payments/base-error/index.d.ts +2 -0
- package/dist/payments/base-error/types.d.ts +180 -0
- package/dist/payments/currency/enums.d.ts +37 -0
- package/dist/payments/currency/index.d.ts +1 -37
- package/dist/payments/index.cjs +40 -40
- package/dist/payments/index.cjs.map +1 -1
- package/dist/payments/index.d.ts +2 -1
- package/dist/payments/index.js +40 -40
- package/dist/payments/index.js.map +1 -1
- package/dist/payments/transaction/types.d.ts +3 -3
- package/dist/store/index.d.ts +1 -1
- package/dist/store/types.d.ts +2 -3
- package/package.json +6 -1
- package/dist/api/errors/enum.d.ts +0 -214
- package/dist/api/errors/index.d.ts +0 -6
- /package/dist/db/{enhanced-config.types.d.ts → features-config.types.d.ts} +0 -0
|
@@ -0,0 +1,1937 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Core type definitions for the @plyaz/notifications package
|
|
5
|
+
* All notification-related types, interfaces, and type utilities
|
|
6
|
+
*
|
|
7
|
+
* @module notifications/types
|
|
8
|
+
*/
|
|
9
|
+
import type { UnknownRecord, Promisable } from 'type-fest';
|
|
10
|
+
import type { MessageCatalog } from '../errors';
|
|
11
|
+
import type { WEBHOOK_ENCRYPTION_METHOD, WEBHOOK_EVENT_TYPE, SIGNATURE_METHOD } from './enums';
|
|
12
|
+
import type z from 'zod';
|
|
13
|
+
/**
|
|
14
|
+
* Notification channels supported by the system
|
|
15
|
+
*/
|
|
16
|
+
export type NotificationChannel = 'email' | 'sms' | 'push';
|
|
17
|
+
/**
|
|
18
|
+
* Notification priority levels for queue processing
|
|
19
|
+
*/
|
|
20
|
+
export type QueuePriority = 'high' | 'normal' | 'low';
|
|
21
|
+
/**
|
|
22
|
+
* Notification category for compliance and user preferences (FR-4.3)
|
|
23
|
+
*
|
|
24
|
+
* Categories determine how notifications can be opted out:
|
|
25
|
+
* - transactional: Cannot be opted out (password resets, order confirmations)
|
|
26
|
+
* - marketing: User can opt-out (newsletters, promotions)
|
|
27
|
+
* - social: User can opt-out (likes, comments, follows)
|
|
28
|
+
* - system: System alerts and notifications (security alerts, system status)
|
|
29
|
+
* - promotional: Promotional offers and campaigns
|
|
30
|
+
*/
|
|
31
|
+
export type NotificationCategory = 'transactional' | 'marketing' | 'social' | 'system' | 'promotional';
|
|
32
|
+
/**
|
|
33
|
+
* Notification status
|
|
34
|
+
*/
|
|
35
|
+
export type NotificationStatus = 'queued' | 'processing' | 'sent' | 'delivered' | 'failed' | 'bounced';
|
|
36
|
+
/**
|
|
37
|
+
* Circuit breaker states
|
|
38
|
+
*/
|
|
39
|
+
export type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN';
|
|
40
|
+
/**
|
|
41
|
+
* Provider names - type-safe constants for provider identification
|
|
42
|
+
*/
|
|
43
|
+
export type ProviderName = string;
|
|
44
|
+
/**
|
|
45
|
+
* Result pattern for type-safe error handling
|
|
46
|
+
* Eliminates need for try-catch in happy path
|
|
47
|
+
*
|
|
48
|
+
* Note: This is the same as Result from @plyaz/types/common
|
|
49
|
+
* We re-export here for convenience, but use the common one to avoid duplication
|
|
50
|
+
*/
|
|
51
|
+
export type NotificationResult<T, E = Error> = {
|
|
52
|
+
success: true;
|
|
53
|
+
data: T;
|
|
54
|
+
} | {
|
|
55
|
+
success: false;
|
|
56
|
+
error: E;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Email validation result
|
|
60
|
+
* Returned by validateEmails() method on email adapters
|
|
61
|
+
*/
|
|
62
|
+
export interface EmailValidationResult {
|
|
63
|
+
/** Email address that was validated */
|
|
64
|
+
email: string;
|
|
65
|
+
/** Whether the email is valid and deliverable */
|
|
66
|
+
valid: boolean;
|
|
67
|
+
/** Reason why email is invalid (optional) */
|
|
68
|
+
reason?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Circuit breaker configuration
|
|
72
|
+
*/
|
|
73
|
+
export interface CircuitBreakerConfig {
|
|
74
|
+
/** Number of failures before opening circuit (default: 5) */
|
|
75
|
+
threshold: number;
|
|
76
|
+
/** Milliseconds before attempting half-open (default: 60000) */
|
|
77
|
+
timeout: number;
|
|
78
|
+
/** Successful attempts needed in half-open to close (default: 3) */
|
|
79
|
+
halfOpenAttempts: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Single webhook handler configuration
|
|
83
|
+
*/
|
|
84
|
+
export interface WebhookHandlerConfig {
|
|
85
|
+
/** Webhook adapter class or instance */
|
|
86
|
+
adapter: unknown;
|
|
87
|
+
/** Webhook secret for signature verification */
|
|
88
|
+
secret: string;
|
|
89
|
+
/** Public key for ECDSA verification (SendGrid) */
|
|
90
|
+
publicKey?: string;
|
|
91
|
+
/** Verification method */
|
|
92
|
+
verificationMethod?: 'ecdsa' | 'hmac';
|
|
93
|
+
/** Priority (lower = higher priority, executed first) */
|
|
94
|
+
priority?: number;
|
|
95
|
+
/** Maximum webhook age in seconds */
|
|
96
|
+
maxWebhookAge?: number;
|
|
97
|
+
/** Whether this webhook handler is enabled */
|
|
98
|
+
enabled?: boolean;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Webhook configuration for a provider
|
|
102
|
+
* Supports single handler or array of handlers (for SoC)
|
|
103
|
+
*/
|
|
104
|
+
export interface WebhookConfig {
|
|
105
|
+
/** Whether webhooks are enabled globally */
|
|
106
|
+
enabled: boolean;
|
|
107
|
+
/** Webhook endpoint URL (optional, for documentation) */
|
|
108
|
+
url?: string;
|
|
109
|
+
/**
|
|
110
|
+
* Webhook handlers - can be:
|
|
111
|
+
* - Single handler config
|
|
112
|
+
* - Array of handler configs (for Separation of Concerns)
|
|
113
|
+
*/
|
|
114
|
+
handlers: WebhookHandlerConfig | WebhookHandlerConfig[];
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Notification Provider configuration
|
|
118
|
+
* Defines how a notification provider is initialized and configured
|
|
119
|
+
*/
|
|
120
|
+
export interface NotificationProviderConfig {
|
|
121
|
+
/** Provider name */
|
|
122
|
+
name: ProviderName;
|
|
123
|
+
/** Whether provider is enabled */
|
|
124
|
+
enabled: boolean;
|
|
125
|
+
/** Priority (1 = highest, 999 = lowest/fallback) */
|
|
126
|
+
priority: number;
|
|
127
|
+
/** Provider-specific credentials and settings */
|
|
128
|
+
credentials: UnknownRecord;
|
|
129
|
+
/** Optional circuit breaker configuration */
|
|
130
|
+
circuitBreaker?: CircuitBreakerConfig;
|
|
131
|
+
/** Optional webhook configuration (Post-MVP) */
|
|
132
|
+
webhook?: WebhookConfig;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* API client configuration for providers that use @plyaz/api
|
|
136
|
+
*
|
|
137
|
+
* This allows passing any ApiConfig to override global defaults.
|
|
138
|
+
* Engineers can customize error handling, retries, interceptors, etc. per adapter.
|
|
139
|
+
*
|
|
140
|
+
* Common overrides:
|
|
141
|
+
* - baseURL, headers, timeout (required/recommended)
|
|
142
|
+
* - retryStrategy, retryCount, retryDelay (retry behavior)
|
|
143
|
+
* - onError, errorHandlers (custom error handling)
|
|
144
|
+
* - onRequest, onResponse, onRetry (custom interceptors)
|
|
145
|
+
*/
|
|
146
|
+
export type ApiClientConfig = {
|
|
147
|
+
/** Base URL for the API (required) */
|
|
148
|
+
baseURL: string;
|
|
149
|
+
} & Partial<Record<string, unknown>>;
|
|
150
|
+
/**
|
|
151
|
+
* Base adapter configuration
|
|
152
|
+
*/
|
|
153
|
+
export interface BaseAdapterConfig {
|
|
154
|
+
/** Provider name */
|
|
155
|
+
providerName: string;
|
|
156
|
+
/** Channel this adapter handles */
|
|
157
|
+
channel: NotificationChannel;
|
|
158
|
+
/** Priority for fallback ordering */
|
|
159
|
+
priority: number;
|
|
160
|
+
/** Optional fallback adapter */
|
|
161
|
+
fallbackAdapter?: unknown;
|
|
162
|
+
/** Optional logger */
|
|
163
|
+
logger?: unknown;
|
|
164
|
+
/** Attachment resolver options */
|
|
165
|
+
attachmentResolverOptions?: AttachmentResolverOptions;
|
|
166
|
+
/** API client configuration (for providers using @plyaz/api) */
|
|
167
|
+
apiClientConfig?: ApiClientConfig;
|
|
168
|
+
/**
|
|
169
|
+
* Retry configuration for this adapter
|
|
170
|
+
* Defaults to RETRY_CONFIG values if not provided
|
|
171
|
+
* Set to false to disable retries for this adapter
|
|
172
|
+
*/
|
|
173
|
+
retryConfig?: NotificationRetryConfig | false;
|
|
174
|
+
/**
|
|
175
|
+
* Webhook configuration for this adapter
|
|
176
|
+
* Supports single handler or array of handlers for Separation of Concerns
|
|
177
|
+
*/
|
|
178
|
+
webhookConfig?: WebhookConfig;
|
|
179
|
+
/**
|
|
180
|
+
* Token generation configuration for unsubscribe/preference links
|
|
181
|
+
* Enables automatic token generation for compliance features
|
|
182
|
+
*/
|
|
183
|
+
tokenConfig?: TokenConfig;
|
|
184
|
+
/**
|
|
185
|
+
* Token URL configuration
|
|
186
|
+
* Defines base URLs for different token types (unsubscribe, preferences, etc.)
|
|
187
|
+
*/
|
|
188
|
+
tokenUrls?: TokenUrlConfig;
|
|
189
|
+
/**
|
|
190
|
+
* SMS validation configuration (SMS adapters only)
|
|
191
|
+
* Configure character limits, encoding detection, and truncation behavior
|
|
192
|
+
* Overrides service-level SMS validation config
|
|
193
|
+
*/
|
|
194
|
+
smsValidation?: SMSValidationConfig;
|
|
195
|
+
/**
|
|
196
|
+
* URL shortener configuration
|
|
197
|
+
* Configure URL shortening for SMS and Push notifications
|
|
198
|
+
* Helps save characters in SMS messages
|
|
199
|
+
*/
|
|
200
|
+
urlShortener?: UrlShortenerConfig;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Infobip Email adapter configuration
|
|
204
|
+
*/
|
|
205
|
+
export interface InfobipEmailConfig extends UnknownRecord {
|
|
206
|
+
/** Infobip API Key from your account */
|
|
207
|
+
apiKey: string;
|
|
208
|
+
/**
|
|
209
|
+
* Infobip API Base URL
|
|
210
|
+
* Default: https://api.infobip.com
|
|
211
|
+
* Regional URLs available (EU, US, etc.)
|
|
212
|
+
*/
|
|
213
|
+
baseUrl?: string;
|
|
214
|
+
/** Default sender email address (must be verified in your Infobip account) */
|
|
215
|
+
fromEmail: string;
|
|
216
|
+
/** Default sender name (optional) */
|
|
217
|
+
fromName?: string;
|
|
218
|
+
/** Priority for adapter selection (lower = higher priority) */
|
|
219
|
+
priority: number;
|
|
220
|
+
/** Fallback adapter if Infobip fails */
|
|
221
|
+
fallbackAdapter?: unknown;
|
|
222
|
+
/** Webhook configuration for delivery tracking */
|
|
223
|
+
webhookConfig?: WebhookConfig;
|
|
224
|
+
/** Token configuration for unsubscribe/preference management */
|
|
225
|
+
tokenConfig?: TokenConfig;
|
|
226
|
+
/** Token URL configuration */
|
|
227
|
+
tokenUrls?: TokenUrlConfig;
|
|
228
|
+
/**
|
|
229
|
+
* Request timeout in milliseconds
|
|
230
|
+
* Default: 30000 (30 seconds)
|
|
231
|
+
*/
|
|
232
|
+
timeout?: number;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* SendGrid adapter configuration
|
|
236
|
+
*/
|
|
237
|
+
export interface SendGridConfig extends UnknownRecord {
|
|
238
|
+
/** SendGrid API Key */
|
|
239
|
+
apiKey: string;
|
|
240
|
+
/** Default sender email address */
|
|
241
|
+
fromEmail: string;
|
|
242
|
+
/** Default sender name (optional) */
|
|
243
|
+
fromName?: string;
|
|
244
|
+
/** Priority for adapter selection (lower = higher priority) */
|
|
245
|
+
priority: number;
|
|
246
|
+
/** Fallback adapter if SendGrid fails */
|
|
247
|
+
fallbackAdapter?: unknown;
|
|
248
|
+
/**
|
|
249
|
+
* Webhook configuration for SendGrid Event Webhook
|
|
250
|
+
* Supports single handler or array of handlers for Separation of Concerns
|
|
251
|
+
*/
|
|
252
|
+
webhookConfig?: WebhookConfig;
|
|
253
|
+
/**
|
|
254
|
+
* Token generation configuration for unsubscribe/preference links
|
|
255
|
+
* Enables CAN-SPAM compliance features
|
|
256
|
+
*/
|
|
257
|
+
tokenConfig?: TokenConfig;
|
|
258
|
+
/**
|
|
259
|
+
* Token URL configuration
|
|
260
|
+
* Defines base URLs for different token types (unsubscribe, preferences, etc.)
|
|
261
|
+
*/
|
|
262
|
+
tokenUrls?: TokenUrlConfig;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Mock provider configuration (for testing)
|
|
266
|
+
*/
|
|
267
|
+
export interface MockProviderConfig extends UnknownRecord {
|
|
268
|
+
providerName: string;
|
|
269
|
+
priority: number;
|
|
270
|
+
shouldFail?: boolean;
|
|
271
|
+
delayMs?: number;
|
|
272
|
+
fallbackAdapter?: unknown;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Attachment definition
|
|
276
|
+
*/
|
|
277
|
+
export interface Attachment {
|
|
278
|
+
/** File name */
|
|
279
|
+
filename: string;
|
|
280
|
+
/** File content (base64 encoded or Buffer) */
|
|
281
|
+
content?: string | globalThis.Buffer;
|
|
282
|
+
/** URL to fetch the file from */
|
|
283
|
+
url?: string;
|
|
284
|
+
/** MIME type */
|
|
285
|
+
type?: string;
|
|
286
|
+
/** Content disposition (attachment or inline) */
|
|
287
|
+
disposition?: 'attachment' | 'inline';
|
|
288
|
+
/** Content ID for inline attachments */
|
|
289
|
+
contentId?: string;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Notification input sent to adapters
|
|
293
|
+
*/
|
|
294
|
+
export interface NotificationInput {
|
|
295
|
+
/** Recipient address (email, phone, or device token) */
|
|
296
|
+
to: string;
|
|
297
|
+
/** Subject (email and push only) */
|
|
298
|
+
subject?: string;
|
|
299
|
+
/** Plain text content */
|
|
300
|
+
text: string;
|
|
301
|
+
/** HTML content (email only) */
|
|
302
|
+
html?: string;
|
|
303
|
+
/** Template data for variable substitution */
|
|
304
|
+
templateData?: UnknownRecord;
|
|
305
|
+
/** File attachments (email only) */
|
|
306
|
+
attachments?: NotificationAttachment[];
|
|
307
|
+
/** Custom headers (email only, e.g., List-Unsubscribe) */
|
|
308
|
+
headers?: Record<string, string>;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Notification send result from adapters
|
|
312
|
+
*/
|
|
313
|
+
export interface NotificationSendResult {
|
|
314
|
+
/** Provider's message ID for tracking */
|
|
315
|
+
messageId: string;
|
|
316
|
+
/** Provider name that sent the notification */
|
|
317
|
+
provider: string;
|
|
318
|
+
/** Timestamp when sent */
|
|
319
|
+
timestamp: Date;
|
|
320
|
+
/** Number of retry attempts made (1 = success on first try, 2+ = retries) */
|
|
321
|
+
retryAttempts?: number;
|
|
322
|
+
/** Failure reason if send failed (for tracking) */
|
|
323
|
+
failureReason?: string;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Notification response returned to application
|
|
327
|
+
*/
|
|
328
|
+
export interface NotificationResponse {
|
|
329
|
+
/** Unique notification ID (correlation ID) */
|
|
330
|
+
notificationId: string;
|
|
331
|
+
/** Current status */
|
|
332
|
+
status: NotificationStatus;
|
|
333
|
+
/** Provider that sent it (if sent) */
|
|
334
|
+
provider?: string;
|
|
335
|
+
/** Provider's message ID (if sent) */
|
|
336
|
+
providerMessageId?: string;
|
|
337
|
+
/** Number of retry attempts made before success/failure */
|
|
338
|
+
retryAttempts?: number;
|
|
339
|
+
/** Failure reason if notification failed */
|
|
340
|
+
failureReason?: string;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Send email parameters
|
|
344
|
+
*/
|
|
345
|
+
export interface SendEmailParams {
|
|
346
|
+
/** Recipient ID (user ID) */
|
|
347
|
+
recipientId: string;
|
|
348
|
+
/** Email address */
|
|
349
|
+
to: string;
|
|
350
|
+
/** Template ID to use */
|
|
351
|
+
templateId: string;
|
|
352
|
+
/** Data for template variables */
|
|
353
|
+
templateData?: UnknownRecord;
|
|
354
|
+
/** Locale for template (default: 'en') */
|
|
355
|
+
locale?: string;
|
|
356
|
+
/** Priority (default: 'normal') */
|
|
357
|
+
priority?: QueuePriority;
|
|
358
|
+
/** Correlation ID for tracking (auto-generated if not provided) */
|
|
359
|
+
correlationId?: string;
|
|
360
|
+
/** File attachments for the email */
|
|
361
|
+
attachments?: Attachment[];
|
|
362
|
+
/**
|
|
363
|
+
* Notification category (default: 'transactional')
|
|
364
|
+
* Determines opt-out behavior
|
|
365
|
+
*/
|
|
366
|
+
category?: NotificationCategory;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Send SMS parameters
|
|
370
|
+
*/
|
|
371
|
+
export interface SendSMSParams {
|
|
372
|
+
/** Recipient ID (user ID) */
|
|
373
|
+
recipientId: string;
|
|
374
|
+
/** Phone number (E.164 format) */
|
|
375
|
+
to: string;
|
|
376
|
+
/** Template ID to use */
|
|
377
|
+
templateId: string;
|
|
378
|
+
/** Data for template variables */
|
|
379
|
+
templateData?: UnknownRecord;
|
|
380
|
+
/** Locale for template (default: 'en') */
|
|
381
|
+
locale?: string;
|
|
382
|
+
/** Priority (default: 'normal') */
|
|
383
|
+
priority?: QueuePriority;
|
|
384
|
+
/** Correlation ID for tracking (auto-generated if not provided) */
|
|
385
|
+
correlationId?: string;
|
|
386
|
+
/**
|
|
387
|
+
* Notification category (default: 'transactional')
|
|
388
|
+
* Determines opt-out behavior
|
|
389
|
+
*/
|
|
390
|
+
category?: NotificationCategory;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Send push notification parameters
|
|
394
|
+
*/
|
|
395
|
+
export interface SendPushParams {
|
|
396
|
+
/** Recipient ID (user ID) */
|
|
397
|
+
recipientId: string;
|
|
398
|
+
/** Device token */
|
|
399
|
+
to: string;
|
|
400
|
+
/** Template ID to use */
|
|
401
|
+
templateId: string;
|
|
402
|
+
/** Data for template variables */
|
|
403
|
+
templateData?: UnknownRecord;
|
|
404
|
+
/** Locale for template (default: 'en') */
|
|
405
|
+
locale?: string;
|
|
406
|
+
/**
|
|
407
|
+
* Notification category (default: 'transactional')
|
|
408
|
+
* Determines opt-out behavior
|
|
409
|
+
*/
|
|
410
|
+
category?: NotificationCategory;
|
|
411
|
+
/** Priority (default: 'normal') */
|
|
412
|
+
priority?: QueuePriority;
|
|
413
|
+
/** Correlation ID for tracking (auto-generated if not provided) */
|
|
414
|
+
correlationId?: string;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Queued notification for internal queue
|
|
418
|
+
*/
|
|
419
|
+
export interface QueuedNotification {
|
|
420
|
+
/** Correlation ID */
|
|
421
|
+
id: string;
|
|
422
|
+
/** Recipient ID */
|
|
423
|
+
recipientId: string;
|
|
424
|
+
/** Channel type */
|
|
425
|
+
channel: NotificationChannel;
|
|
426
|
+
/** Template ID */
|
|
427
|
+
templateId: string;
|
|
428
|
+
/** Priority for queue sorting */
|
|
429
|
+
priority: QueuePriority;
|
|
430
|
+
/** Notification input */
|
|
431
|
+
input: NotificationInput;
|
|
432
|
+
/** Timestamp queued */
|
|
433
|
+
queuedAt: Date;
|
|
434
|
+
/** Retry count */
|
|
435
|
+
retryCount: number;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Available layout variants
|
|
439
|
+
* - branded: Full branding with logo, colors, and styling
|
|
440
|
+
* - minimal: Simple, clean layout with basic styling
|
|
441
|
+
* - transactional: Ultra-minimal for transactional emails
|
|
442
|
+
* - none: No layout (disable header/footer)
|
|
443
|
+
*/
|
|
444
|
+
export type LayoutVariant = 'branded' | 'minimal' | 'transactional' | 'none';
|
|
445
|
+
/**
|
|
446
|
+
* Layout configuration for templates
|
|
447
|
+
* Can be:
|
|
448
|
+
* - string: Use same variant for header and footer
|
|
449
|
+
* - object: Configure header and footer separately
|
|
450
|
+
* - false/null: Disable all layouts
|
|
451
|
+
*/
|
|
452
|
+
export type LayoutConfig = LayoutVariant | {
|
|
453
|
+
/** Header layout variant (or false to disable) */
|
|
454
|
+
header?: LayoutVariant | false;
|
|
455
|
+
/** Footer layout variant (or false to disable) */
|
|
456
|
+
footer?: LayoutVariant | false;
|
|
457
|
+
} | false | null;
|
|
458
|
+
/**
|
|
459
|
+
* Resolved layout configuration (after applying defaults)
|
|
460
|
+
*/
|
|
461
|
+
export interface ResolvedLayoutConfig {
|
|
462
|
+
/** Header variant to use (null if disabled) */
|
|
463
|
+
header: LayoutVariant | null;
|
|
464
|
+
/** Footer variant to use (null if disabled) */
|
|
465
|
+
footer: LayoutVariant | null;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Token generation strategies
|
|
469
|
+
* - aes-256-gcm: Encrypted token (most secure, larger size)
|
|
470
|
+
* - jwt: Standard JWT (readable payload, signed)
|
|
471
|
+
* - hmac-sha256: HMAC-signed token (compact, fast)
|
|
472
|
+
*/
|
|
473
|
+
export type TokenStrategy = 'aes-256-gcm' | 'jwt' | 'hmac-sha256';
|
|
474
|
+
/**
|
|
475
|
+
* Action destinations
|
|
476
|
+
* - url: Opens a web page (FE calls API with token)
|
|
477
|
+
* - webhook: Calls API directly (immediate processing)
|
|
478
|
+
*/
|
|
479
|
+
export type ActionDestination = 'url' | 'webhook';
|
|
480
|
+
/**
|
|
481
|
+
* Token types (action_destination format)
|
|
482
|
+
* - unsubscribe_url: User clicks → Opens unsubscribe page
|
|
483
|
+
* - unsubscribe_webhook: User clicks → Calls API directly
|
|
484
|
+
* - preference_url: User clicks → Opens preference center
|
|
485
|
+
* - preference_webhook: User clicks → Updates preferences via webhook
|
|
486
|
+
* - tracking_pixel: Invisible pixel for open tracking (always webhook)
|
|
487
|
+
*/
|
|
488
|
+
export type TokenType = 'unsubscribe_url' | 'unsubscribe_webhook' | 'preference_url' | 'preference_webhook' | 'tracking_pixel';
|
|
489
|
+
/**
|
|
490
|
+
* Token configuration for generation
|
|
491
|
+
*/
|
|
492
|
+
export interface TokenConfig {
|
|
493
|
+
/** Token generation strategy */
|
|
494
|
+
strategy: TokenStrategy;
|
|
495
|
+
/** Secret key for encryption/signing */
|
|
496
|
+
secret: string;
|
|
497
|
+
/** Token expiry in seconds (default: 2592000 = 30 days) */
|
|
498
|
+
expirySeconds?: number;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Token URL configuration
|
|
502
|
+
*/
|
|
503
|
+
export interface TokenUrlConfig {
|
|
504
|
+
/** Base URLs for each token type */
|
|
505
|
+
baseUrls: {
|
|
506
|
+
unsubscribe_url?: string;
|
|
507
|
+
unsubscribe_webhook?: string;
|
|
508
|
+
preference_url?: string;
|
|
509
|
+
preference_webhook?: string;
|
|
510
|
+
tracking_pixel?: string;
|
|
511
|
+
};
|
|
512
|
+
/** Query parameters to include in generated URLs */
|
|
513
|
+
includeQueryParams?: {
|
|
514
|
+
/** Include template name */
|
|
515
|
+
template?: boolean;
|
|
516
|
+
/** Include email address (hashed) */
|
|
517
|
+
email?: boolean;
|
|
518
|
+
/** Include notification category */
|
|
519
|
+
category?: boolean;
|
|
520
|
+
/** Include timestamp */
|
|
521
|
+
timestamp?: boolean;
|
|
522
|
+
};
|
|
523
|
+
/** Per-category URL overrides */
|
|
524
|
+
categoryOverrides?: {
|
|
525
|
+
[category: string]: {
|
|
526
|
+
unsubscribe_url?: string;
|
|
527
|
+
unsubscribe_webhook?: string;
|
|
528
|
+
preference_url?: string;
|
|
529
|
+
preference_webhook?: string;
|
|
530
|
+
};
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Token payload (encrypted/signed data)
|
|
535
|
+
*/
|
|
536
|
+
export interface TokenPayload {
|
|
537
|
+
/** User/recipient ID */
|
|
538
|
+
recipientId: string;
|
|
539
|
+
/** Token type */
|
|
540
|
+
type: TokenType;
|
|
541
|
+
/** Notification category */
|
|
542
|
+
category: string;
|
|
543
|
+
/** Expiration timestamp (Unix seconds) */
|
|
544
|
+
expiresAt: number;
|
|
545
|
+
/** Optional correlation ID */
|
|
546
|
+
correlationId?: string;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Template unsubscribe configuration
|
|
550
|
+
* Can be:
|
|
551
|
+
* - false: Disable unsubscribe completely
|
|
552
|
+
* - true: Enable with default settings
|
|
553
|
+
* - object: Custom configuration
|
|
554
|
+
*/
|
|
555
|
+
export type TemplateUnsubscribeConfig = boolean | {
|
|
556
|
+
/** Enable unsubscribe for this template */
|
|
557
|
+
enabled?: boolean;
|
|
558
|
+
/** Action destination type */
|
|
559
|
+
type?: ActionDestination;
|
|
560
|
+
/** Custom URL override (bypasses adapter config) */
|
|
561
|
+
customUrl?: string;
|
|
562
|
+
/**
|
|
563
|
+
* Include List-Unsubscribe email header (RFC 8058)
|
|
564
|
+
* Allows one-click unsubscribe in email clients.
|
|
565
|
+
*/
|
|
566
|
+
includeListUnsubscribeHeader?: boolean;
|
|
567
|
+
};
|
|
568
|
+
/**
|
|
569
|
+
* Template frontmatter metadata
|
|
570
|
+
*/
|
|
571
|
+
export interface TemplateFrontmatter {
|
|
572
|
+
/** Email subject template */
|
|
573
|
+
subject?: string;
|
|
574
|
+
/** From email address */
|
|
575
|
+
from?: string;
|
|
576
|
+
/** From name */
|
|
577
|
+
fromName?: string;
|
|
578
|
+
/** Channel this template is for */
|
|
579
|
+
channel: NotificationChannel;
|
|
580
|
+
/** Notification category */
|
|
581
|
+
category?: NotificationCategory;
|
|
582
|
+
/** Preheader text (email only) */
|
|
583
|
+
preheader?: string;
|
|
584
|
+
/** Unsubscribe configuration */
|
|
585
|
+
unsubscribe?: TemplateUnsubscribeConfig;
|
|
586
|
+
/** Layout configuration */
|
|
587
|
+
layout?: LayoutConfig;
|
|
588
|
+
/** Any additional metadata */
|
|
589
|
+
[key: string]: unknown;
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Rendered template output
|
|
593
|
+
*/
|
|
594
|
+
export interface RenderedTemplate {
|
|
595
|
+
/** Template metadata from frontmatter */
|
|
596
|
+
metadata: TemplateFrontmatter;
|
|
597
|
+
/** Rendered subject (with variables substituted) */
|
|
598
|
+
subject?: string;
|
|
599
|
+
/** HTML content (email only) */
|
|
600
|
+
html?: string;
|
|
601
|
+
/** Plain text content */
|
|
602
|
+
text: string;
|
|
603
|
+
/** Locale used */
|
|
604
|
+
locale: string;
|
|
605
|
+
/** Optional headers (e.g., List-Unsubscribe) */
|
|
606
|
+
headers?: Record<string, string>;
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* SMS Encoding Type
|
|
610
|
+
* Determines character limits and segment sizes
|
|
611
|
+
*/
|
|
612
|
+
export type SMSEncoding = 'GSM-7' | 'UCS-2';
|
|
613
|
+
/**
|
|
614
|
+
* SMS Validation Behavior
|
|
615
|
+
* Determines what happens when SMS exceeds character limits
|
|
616
|
+
*/
|
|
617
|
+
export type SMSValidationBehavior = 'warn' | 'error' | 'truncate' | 'allow';
|
|
618
|
+
/**
|
|
619
|
+
* SMS Validation Configuration
|
|
620
|
+
* Configurable limits and behavior for SMS character validation
|
|
621
|
+
*/
|
|
622
|
+
export interface SMSValidationConfig {
|
|
623
|
+
/**
|
|
624
|
+
* Enable SMS character validation
|
|
625
|
+
* @default true
|
|
626
|
+
*/
|
|
627
|
+
enabled?: boolean;
|
|
628
|
+
/**
|
|
629
|
+
* Maximum number of SMS segments to allow
|
|
630
|
+
* @default 10
|
|
631
|
+
*/
|
|
632
|
+
maxSegments?: number;
|
|
633
|
+
/**
|
|
634
|
+
* Warning threshold as percentage of limit (0-100)
|
|
635
|
+
* @default 90
|
|
636
|
+
*/
|
|
637
|
+
warningThreshold?: number;
|
|
638
|
+
/**
|
|
639
|
+
* Behavior when message exceeds character limit
|
|
640
|
+
* @default 'warn'
|
|
641
|
+
*/
|
|
642
|
+
onExceedLimit?: SMSValidationBehavior;
|
|
643
|
+
/**
|
|
644
|
+
* Behavior when message exceeds max segments
|
|
645
|
+
* @default 'error'
|
|
646
|
+
*/
|
|
647
|
+
onExceedSegments?: SMSValidationBehavior;
|
|
648
|
+
/**
|
|
649
|
+
* Custom single message limit (GSM-7 encoding)
|
|
650
|
+
* @default 160
|
|
651
|
+
*/
|
|
652
|
+
gsm7SingleLimit?: number;
|
|
653
|
+
/**
|
|
654
|
+
* Custom multi-part segment limit (GSM-7 encoding)
|
|
655
|
+
* @default 153
|
|
656
|
+
*/
|
|
657
|
+
gsm7MultiPartLimit?: number;
|
|
658
|
+
/**
|
|
659
|
+
* Custom single message limit (UCS-2 encoding)
|
|
660
|
+
* @default 70
|
|
661
|
+
*/
|
|
662
|
+
ucs2SingleLimit?: number;
|
|
663
|
+
/**
|
|
664
|
+
* Custom multi-part segment limit (UCS-2 encoding)
|
|
665
|
+
* @default 67
|
|
666
|
+
*/
|
|
667
|
+
ucs2MultiPartLimit?: number;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* SMS Character Analysis Result
|
|
671
|
+
* Information about SMS message encoding and segmentation
|
|
672
|
+
*/
|
|
673
|
+
export interface SMSCharacterAnalysis {
|
|
674
|
+
/** Total character count */
|
|
675
|
+
characterCount: number;
|
|
676
|
+
/** Detected encoding type (GSM-7 or UCS-2) */
|
|
677
|
+
encoding: SMSEncoding;
|
|
678
|
+
/** Number of SMS segments required */
|
|
679
|
+
segmentCount: number;
|
|
680
|
+
/** Characters per segment for this message */
|
|
681
|
+
charactersPerSegment: number;
|
|
682
|
+
/** Characters remaining in current segment */
|
|
683
|
+
remainingInSegment: number;
|
|
684
|
+
/** Whether message exceeds single segment */
|
|
685
|
+
isMultiPart: boolean;
|
|
686
|
+
/** Whether message contains unicode/emoji */
|
|
687
|
+
containsUnicode: boolean;
|
|
688
|
+
/** Whether message exceeds configured limits */
|
|
689
|
+
exceedsLimit: boolean;
|
|
690
|
+
/** Whether message exceeds max segments */
|
|
691
|
+
exceedsMaxSegments: boolean;
|
|
692
|
+
/** Warning message if applicable */
|
|
693
|
+
warning?: string;
|
|
694
|
+
/** List of non-GSM-7 characters that triggered UCS-2 encoding */
|
|
695
|
+
unicodeCharacters?: string[];
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* URL Shortening Options
|
|
699
|
+
*/
|
|
700
|
+
export interface UrlShortenOptions {
|
|
701
|
+
/** Custom short code (if supported by adapter) */
|
|
702
|
+
customCode?: string;
|
|
703
|
+
/** Expiration time in seconds */
|
|
704
|
+
expiresIn?: number;
|
|
705
|
+
/** Metadata to store with the shortened URL */
|
|
706
|
+
metadata?: UnknownRecord;
|
|
707
|
+
/** Notification context (for tracking) */
|
|
708
|
+
notificationContext?: {
|
|
709
|
+
recipientId: string;
|
|
710
|
+
templateId: string;
|
|
711
|
+
channel: NotificationChannel;
|
|
712
|
+
category?: string;
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* URL Analytics Data
|
|
717
|
+
*/
|
|
718
|
+
export interface UrlAnalytics {
|
|
719
|
+
/** Shortened URL */
|
|
720
|
+
shortUrl: string;
|
|
721
|
+
/** Original long URL */
|
|
722
|
+
originalUrl: string;
|
|
723
|
+
/** Number of clicks */
|
|
724
|
+
clicks: number;
|
|
725
|
+
/** Creation timestamp */
|
|
726
|
+
createdAt: number;
|
|
727
|
+
/** Last clicked timestamp */
|
|
728
|
+
lastClickedAt?: number;
|
|
729
|
+
/** Expiration timestamp */
|
|
730
|
+
expiresAt?: number;
|
|
731
|
+
/** Additional metadata */
|
|
732
|
+
metadata?: UnknownRecord;
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* URL Shortener Adapter Interface
|
|
736
|
+
* Implement this interface to create custom URL shortening providers
|
|
737
|
+
*/
|
|
738
|
+
export interface UrlShortenerAdapter {
|
|
739
|
+
/**
|
|
740
|
+
* Shorten a long URL
|
|
741
|
+
*/
|
|
742
|
+
shorten(url: string, options?: UrlShortenOptions): Promise<string>;
|
|
743
|
+
/**
|
|
744
|
+
* Expand a shortened URL back to original (optional)
|
|
745
|
+
*/
|
|
746
|
+
expand?(shortUrl: string): Promise<string | null>;
|
|
747
|
+
/**
|
|
748
|
+
* Get analytics for a shortened URL (optional)
|
|
749
|
+
*/
|
|
750
|
+
getAnalytics?(shortUrl: string): Promise<UrlAnalytics | null>;
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* URL Shortener Storage Interface
|
|
754
|
+
* Implement this to provide persistent storage for self-hosted URL shortening
|
|
755
|
+
*/
|
|
756
|
+
export interface UrlShortenerStorage {
|
|
757
|
+
/**
|
|
758
|
+
* Store a URL mapping
|
|
759
|
+
*/
|
|
760
|
+
store(shortCode: string, originalUrl: string, options?: {
|
|
761
|
+
expiresAt?: number;
|
|
762
|
+
metadata?: UnknownRecord;
|
|
763
|
+
}): Promise<void>;
|
|
764
|
+
/**
|
|
765
|
+
* Retrieve original URL by short code
|
|
766
|
+
*/
|
|
767
|
+
retrieve(shortCode: string): Promise<string | null>;
|
|
768
|
+
/**
|
|
769
|
+
* Check if a short code exists
|
|
770
|
+
*/
|
|
771
|
+
exists(shortCode: string): Promise<boolean>;
|
|
772
|
+
/**
|
|
773
|
+
* Increment click count for a short code
|
|
774
|
+
*/
|
|
775
|
+
incrementClicks?(shortCode: string): Promise<void>;
|
|
776
|
+
/**
|
|
777
|
+
* Get analytics for a short code
|
|
778
|
+
*/
|
|
779
|
+
getAnalytics?(shortCode: string): Promise<UrlAnalytics | null>;
|
|
780
|
+
/**
|
|
781
|
+
* Clean up expired URLs (optional maintenance method)
|
|
782
|
+
*/
|
|
783
|
+
cleanup?(): Promisable<void>;
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* URL Shortener Configuration
|
|
787
|
+
*/
|
|
788
|
+
export interface UrlShortenerConfig {
|
|
789
|
+
/**
|
|
790
|
+
* Enable URL shortening per channel
|
|
791
|
+
* @default { sms: true, push: false, email: false }
|
|
792
|
+
*/
|
|
793
|
+
enabled?: boolean | {
|
|
794
|
+
sms?: boolean;
|
|
795
|
+
push?: boolean;
|
|
796
|
+
email?: boolean;
|
|
797
|
+
};
|
|
798
|
+
/**
|
|
799
|
+
* URL shortener adapter
|
|
800
|
+
*/
|
|
801
|
+
adapter: UrlShortenerAdapter;
|
|
802
|
+
/**
|
|
803
|
+
* Minimum URL length to trigger shortening
|
|
804
|
+
* @default 50
|
|
805
|
+
*/
|
|
806
|
+
minUrlLength?: number;
|
|
807
|
+
/**
|
|
808
|
+
* Only shorten URLs matching these domain patterns
|
|
809
|
+
* @default []
|
|
810
|
+
*/
|
|
811
|
+
allowedDomains?: string[];
|
|
812
|
+
/**
|
|
813
|
+
* Skip shortening URLs matching these domain patterns
|
|
814
|
+
* @default ['bit.ly', 'tinyurl.com', 't.co', 'goo.gl']
|
|
815
|
+
*/
|
|
816
|
+
skipDomains?: string[];
|
|
817
|
+
/**
|
|
818
|
+
* Base URL for shortened links (for self-hosted)
|
|
819
|
+
*/
|
|
820
|
+
baseUrl?: string;
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Provider configuration item - supports two patterns:
|
|
824
|
+
* 1. Direct adapter instance (simple): new SendGridAdapter({...}, logger)
|
|
825
|
+
* 2. Config object (dynamic/runtime): { name: 'SendGrid', enabled: true, credentials: {...} }
|
|
826
|
+
*/
|
|
827
|
+
export type ProviderConfigOrAdapter = NotificationProviderConfig | unknown;
|
|
828
|
+
/**
|
|
829
|
+
* Notification providers configuration
|
|
830
|
+
* Organized by channel for easy access
|
|
831
|
+
*
|
|
832
|
+
* Supports two patterns:
|
|
833
|
+
* 1. Simple (direct adapters): email: [new SendGridAdapter({...}, logger)]
|
|
834
|
+
* 2. Dynamic (config-driven): email: [{ name: 'SendGrid', enabled: true, credentials: {...} }]
|
|
835
|
+
*/
|
|
836
|
+
export interface NotificationProvidersConfig {
|
|
837
|
+
/** Email provider configurations or adapter instances */
|
|
838
|
+
email: ProviderConfigOrAdapter[];
|
|
839
|
+
/** SMS provider configurations or adapter instances */
|
|
840
|
+
sms: ProviderConfigOrAdapter[];
|
|
841
|
+
/** Push notification provider configurations or adapter instances */
|
|
842
|
+
push: ProviderConfigOrAdapter[];
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* Notification health status
|
|
846
|
+
*/
|
|
847
|
+
export interface NotificationHealthStatus {
|
|
848
|
+
/** Overall health */
|
|
849
|
+
healthy: boolean;
|
|
850
|
+
/** Timestamp */
|
|
851
|
+
timestamp: Date;
|
|
852
|
+
/** Provider statuses */
|
|
853
|
+
providers: ProviderStatus[];
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Provider status
|
|
857
|
+
*/
|
|
858
|
+
export interface ProviderStatus {
|
|
859
|
+
/** Provider name */
|
|
860
|
+
provider: string;
|
|
861
|
+
/** Channel */
|
|
862
|
+
channel: NotificationChannel;
|
|
863
|
+
/** Healthy? */
|
|
864
|
+
healthy: boolean;
|
|
865
|
+
/** Latency in ms */
|
|
866
|
+
latency?: number;
|
|
867
|
+
/** Error message if unhealthy */
|
|
868
|
+
error?: string;
|
|
869
|
+
/** Circuit breaker stats */
|
|
870
|
+
circuitBreaker?: CircuitBreakerStats;
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Circuit breaker statistics
|
|
874
|
+
*/
|
|
875
|
+
export interface CircuitBreakerStats {
|
|
876
|
+
state: CircuitState;
|
|
877
|
+
failureCount: number;
|
|
878
|
+
successCount: number;
|
|
879
|
+
lastFailureTime?: number;
|
|
880
|
+
lastSuccessTime?: number;
|
|
881
|
+
nextAttemptTime?: number;
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Retry configuration for notifications
|
|
885
|
+
* Renamed to avoid collision with api/retry types
|
|
886
|
+
*/
|
|
887
|
+
export interface NotificationRetryConfig {
|
|
888
|
+
/** Maximum retry attempts */
|
|
889
|
+
maxAttempts?: number;
|
|
890
|
+
/** Initial delay in ms */
|
|
891
|
+
initialDelay?: number;
|
|
892
|
+
/** Maximum delay in ms */
|
|
893
|
+
maxDelay?: number;
|
|
894
|
+
/** Backoff multiplier */
|
|
895
|
+
backoffMultiplier?: number;
|
|
896
|
+
/** Add random jitter to prevent thundering herd */
|
|
897
|
+
jitter?: boolean;
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Retry result for notifications
|
|
901
|
+
* Renamed to avoid collision with api/retry types
|
|
902
|
+
*/
|
|
903
|
+
export interface NotificationRetryResult<T> {
|
|
904
|
+
success: boolean;
|
|
905
|
+
data?: T;
|
|
906
|
+
error?: Error;
|
|
907
|
+
attempts: number;
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Token verification result
|
|
911
|
+
*/
|
|
912
|
+
export interface TokenVerificationResult {
|
|
913
|
+
valid: boolean;
|
|
914
|
+
payload?: TokenPayload;
|
|
915
|
+
error?: string;
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Notification queue statistics (simple version)
|
|
919
|
+
*/
|
|
920
|
+
export interface NotificationQueueStats {
|
|
921
|
+
size: number;
|
|
922
|
+
processing: number;
|
|
923
|
+
processed: number;
|
|
924
|
+
failed: number;
|
|
925
|
+
}
|
|
926
|
+
/**
|
|
927
|
+
* Notification queue configuration (simple version)
|
|
928
|
+
*/
|
|
929
|
+
export interface NotificationQueueConfig {
|
|
930
|
+
maxSize?: number;
|
|
931
|
+
processImmediately?: boolean;
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* InMemory queue statistics (detailed version)
|
|
935
|
+
* Used by InMemoryQueue implementation
|
|
936
|
+
*/
|
|
937
|
+
export interface InMemoryQueueStats {
|
|
938
|
+
size: number;
|
|
939
|
+
processing: boolean;
|
|
940
|
+
highPriority: number;
|
|
941
|
+
normalPriority: number;
|
|
942
|
+
lowPriority: number;
|
|
943
|
+
processedCount: number;
|
|
944
|
+
failedCount: number;
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* InMemory queue configuration
|
|
948
|
+
* Used by InMemoryQueue implementation
|
|
949
|
+
*/
|
|
950
|
+
export interface InMemoryQueueConfig {
|
|
951
|
+
/** Maximum queue size (default: 10000) */
|
|
952
|
+
maxSize?: number;
|
|
953
|
+
/** Enable auto-processing (default: false for manual control) */
|
|
954
|
+
autoProcess?: boolean;
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* HMAC signature verification options
|
|
958
|
+
*/
|
|
959
|
+
export interface HMACSignatureOptions {
|
|
960
|
+
secret: string;
|
|
961
|
+
payload: string | globalThis.Buffer;
|
|
962
|
+
signature: string;
|
|
963
|
+
timestamp?: string;
|
|
964
|
+
encoding?: 'hex' | 'base64';
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Webhook signature computation options
|
|
968
|
+
*/
|
|
969
|
+
export interface WebhookSignatureOptions {
|
|
970
|
+
secret: string;
|
|
971
|
+
payload: string | globalThis.Buffer;
|
|
972
|
+
timestamp: string;
|
|
973
|
+
method?: 'hmac' | 'ecdsa';
|
|
974
|
+
encoding?: 'hex' | 'base64';
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Template render options
|
|
978
|
+
* Renamed to avoid collision with testing RenderOptions
|
|
979
|
+
*/
|
|
980
|
+
export interface NotificationRenderOptions {
|
|
981
|
+
/** Locale for template */
|
|
982
|
+
locale?: string;
|
|
983
|
+
/** Fallback locale if preferred not found */
|
|
984
|
+
fallbackLocale?: string;
|
|
985
|
+
/** Layout data for header/footer */
|
|
986
|
+
layoutData?: {
|
|
987
|
+
unsubscribeUrl?: string;
|
|
988
|
+
preferencesUrl?: string;
|
|
989
|
+
companyName?: string;
|
|
990
|
+
companyAddress?: string;
|
|
991
|
+
[key: string]: unknown;
|
|
992
|
+
};
|
|
993
|
+
/** SMS validation config override */
|
|
994
|
+
smsValidation?: SMSValidationConfig;
|
|
995
|
+
/** URL shortener config override */
|
|
996
|
+
urlShortener?: UrlShortenerConfig;
|
|
997
|
+
/** Notification context for event emission */
|
|
998
|
+
notificationContext?: {
|
|
999
|
+
recipientId: string;
|
|
1000
|
+
templateId: string;
|
|
1001
|
+
channel: NotificationChannel;
|
|
1002
|
+
category?: string;
|
|
1003
|
+
correlationId?: string;
|
|
1004
|
+
};
|
|
1005
|
+
/** URL shortening event callback */
|
|
1006
|
+
onUrlShortened?: (payload: {
|
|
1007
|
+
originalUrl: string;
|
|
1008
|
+
shortUrl: string;
|
|
1009
|
+
channel: NotificationChannel;
|
|
1010
|
+
recipientId: string;
|
|
1011
|
+
templateId: string;
|
|
1012
|
+
category?: string;
|
|
1013
|
+
correlationId?: string;
|
|
1014
|
+
charactersSaved: number;
|
|
1015
|
+
timestamp: Date;
|
|
1016
|
+
metadata?: UnknownRecord;
|
|
1017
|
+
}) => Promisable<void>;
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Attachment disposition
|
|
1021
|
+
* - attachment: Download as file
|
|
1022
|
+
* - inline: Embed in content (e.g., images in email)
|
|
1023
|
+
*/
|
|
1024
|
+
export type AttachmentDisposition = 'attachment' | 'inline';
|
|
1025
|
+
/**
|
|
1026
|
+
* Attachment source type
|
|
1027
|
+
*/
|
|
1028
|
+
export type AttachmentSourceType = 'buffer' | 'url' | 'path' | 'cloud';
|
|
1029
|
+
/**
|
|
1030
|
+
* Cloud storage provider types
|
|
1031
|
+
*/
|
|
1032
|
+
export type CloudStorageProvider = 's3' | 'r2' | 'gcs' | 'azure-blob' | 'custom';
|
|
1033
|
+
/**
|
|
1034
|
+
* Basic authentication credentials
|
|
1035
|
+
*/
|
|
1036
|
+
export interface BasicAuthCredentials {
|
|
1037
|
+
username: string;
|
|
1038
|
+
password: string;
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Bearer token authentication
|
|
1042
|
+
*/
|
|
1043
|
+
export interface BearerAuthCredentials {
|
|
1044
|
+
token: string;
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* API key authentication
|
|
1048
|
+
*/
|
|
1049
|
+
export interface ApiKeyAuthCredentials {
|
|
1050
|
+
key: string;
|
|
1051
|
+
/** Header name (default: 'X-API-Key') */
|
|
1052
|
+
headerName?: string;
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* OAuth2 credentials
|
|
1056
|
+
*/
|
|
1057
|
+
export interface OAuth2Credentials {
|
|
1058
|
+
accessToken: string;
|
|
1059
|
+
tokenType?: string;
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* AWS Signature V4 credentials (for S3, R2, etc.)
|
|
1063
|
+
*/
|
|
1064
|
+
export interface AWSSignatureCredentials {
|
|
1065
|
+
accessKeyId: string;
|
|
1066
|
+
secretAccessKey: string;
|
|
1067
|
+
region?: string;
|
|
1068
|
+
sessionToken?: string;
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Custom authentication (provide your own headers)
|
|
1072
|
+
*/
|
|
1073
|
+
export interface CustomAuthCredentials {
|
|
1074
|
+
headers: Record<string, string>;
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Authentication for URL/cloud attachments
|
|
1078
|
+
*/
|
|
1079
|
+
export interface AttachmentAuth {
|
|
1080
|
+
/** Authentication type */
|
|
1081
|
+
type: 'basic' | 'bearer' | 'api-key' | 'oauth2' | 'aws-signature' | 'custom';
|
|
1082
|
+
/** Credentials */
|
|
1083
|
+
credentials: BasicAuthCredentials | BearerAuthCredentials | ApiKeyAuthCredentials | OAuth2Credentials | AWSSignatureCredentials | CustomAuthCredentials;
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Cloud storage configuration
|
|
1087
|
+
*/
|
|
1088
|
+
export interface CloudStorageConfig {
|
|
1089
|
+
/** Provider type */
|
|
1090
|
+
provider: CloudStorageProvider;
|
|
1091
|
+
/** Bucket/container name */
|
|
1092
|
+
bucket: string;
|
|
1093
|
+
/** Object key/path */
|
|
1094
|
+
key: string;
|
|
1095
|
+
/** Region (for AWS S3, R2) */
|
|
1096
|
+
region?: string;
|
|
1097
|
+
/** Custom endpoint (for R2, MinIO, etc.) */
|
|
1098
|
+
endpoint?: string;
|
|
1099
|
+
/** Authentication credentials */
|
|
1100
|
+
auth: AttachmentAuth;
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Attachment source configuration
|
|
1104
|
+
*/
|
|
1105
|
+
export type AttachmentSource = {
|
|
1106
|
+
/** Direct buffer/string content */
|
|
1107
|
+
type: 'buffer';
|
|
1108
|
+
content: globalThis.Buffer | string;
|
|
1109
|
+
} | {
|
|
1110
|
+
/** HTTP/HTTPS URL */
|
|
1111
|
+
type: 'url';
|
|
1112
|
+
url: string;
|
|
1113
|
+
/** Optional authentication for protected URLs */
|
|
1114
|
+
auth?: AttachmentAuth;
|
|
1115
|
+
/** Custom headers */
|
|
1116
|
+
headers?: Record<string, string>;
|
|
1117
|
+
} | {
|
|
1118
|
+
/** Local file system path */
|
|
1119
|
+
type: 'path';
|
|
1120
|
+
path: string;
|
|
1121
|
+
} | {
|
|
1122
|
+
/** Cloud storage (S3, R2, GCS, Azure) */
|
|
1123
|
+
type: 'cloud';
|
|
1124
|
+
config: CloudStorageConfig;
|
|
1125
|
+
};
|
|
1126
|
+
/**
|
|
1127
|
+
* Notification attachment interface (channel-agnostic)
|
|
1128
|
+
* Prefixed to avoid collision if imported with other Attachment types
|
|
1129
|
+
*/
|
|
1130
|
+
export interface NotificationAttachment {
|
|
1131
|
+
/** Filename with extension (e.g., "invoice.pdf") */
|
|
1132
|
+
filename: string;
|
|
1133
|
+
/** Attachment source (buffer, URL, path, or cloud storage) */
|
|
1134
|
+
source: AttachmentSource;
|
|
1135
|
+
/** MIME type (e.g., "application/pdf", "image/png") */
|
|
1136
|
+
contentType: string;
|
|
1137
|
+
/** Size in bytes (auto-calculated if not provided) */
|
|
1138
|
+
size?: number;
|
|
1139
|
+
/** How to display: 'attachment' (download) or 'inline' (embed) */
|
|
1140
|
+
disposition?: AttachmentDisposition;
|
|
1141
|
+
/** Content ID for inline images (e.g., "logo" for cid:logo) */
|
|
1142
|
+
contentId?: string;
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* Provider-specific attachment capabilities
|
|
1146
|
+
*
|
|
1147
|
+
* Each adapter declares what attachments it supports
|
|
1148
|
+
*/
|
|
1149
|
+
export interface AttachmentCapabilities {
|
|
1150
|
+
/** Maximum number of attachments allowed */
|
|
1151
|
+
maxAttachments: number;
|
|
1152
|
+
/** Maximum size per attachment in bytes */
|
|
1153
|
+
maxSizePerAttachment: number;
|
|
1154
|
+
/** Maximum total size of all attachments in bytes */
|
|
1155
|
+
maxTotalSize: number;
|
|
1156
|
+
/** Supported MIME types (e.g., ['application/pdf', 'image/*']) */
|
|
1157
|
+
supportedMimeTypes: string[];
|
|
1158
|
+
/** Supported file extensions (e.g., ['.pdf', '.jpg', '.png']) */
|
|
1159
|
+
supportedExtensions: string[];
|
|
1160
|
+
/** Supports inline attachments (embedded images) */
|
|
1161
|
+
supportsInline: boolean;
|
|
1162
|
+
/** Accepts base64-encoded content */
|
|
1163
|
+
supportsBase64: boolean;
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Attachment validation result
|
|
1167
|
+
*/
|
|
1168
|
+
export interface AttachmentValidationResult {
|
|
1169
|
+
valid: boolean;
|
|
1170
|
+
errors: AttachmentValidationError[];
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Attachment validation error
|
|
1174
|
+
*/
|
|
1175
|
+
export interface AttachmentValidationError {
|
|
1176
|
+
type: AttachmentErrorType;
|
|
1177
|
+
message: string;
|
|
1178
|
+
attachment?: Attachment;
|
|
1179
|
+
metadata?: Record<string, unknown>;
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* Attachment error types
|
|
1183
|
+
*/
|
|
1184
|
+
export type AttachmentErrorType = 'EXCEEDS_MAX_ATTACHMENTS' | 'EXCEEDS_SIZE_LIMIT' | 'EXCEEDS_TOTAL_SIZE' | 'UNSUPPORTED_MIME_TYPE' | 'UNSUPPORTED_EXTENSION' | 'INVALID_FILENAME' | 'INVALID_CONTENT' | 'INLINE_NOT_SUPPORTED';
|
|
1185
|
+
/**
|
|
1186
|
+
* Transformed attachment for provider use
|
|
1187
|
+
* This is the common format used by most email providers (SendGrid, Mailgun, etc.)
|
|
1188
|
+
* Providers can use this directly or create their own format
|
|
1189
|
+
*/
|
|
1190
|
+
export interface TransformedAttachment {
|
|
1191
|
+
/** Attachment filename */
|
|
1192
|
+
filename: string;
|
|
1193
|
+
/** Base64-encoded content */
|
|
1194
|
+
content: string;
|
|
1195
|
+
/** MIME type */
|
|
1196
|
+
type: string;
|
|
1197
|
+
/** Disposition: 'attachment' or 'inline' */
|
|
1198
|
+
disposition: 'attachment' | 'inline';
|
|
1199
|
+
/** Content ID for inline attachments (optional) */
|
|
1200
|
+
content_id?: string;
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Resolved attachment with buffer content
|
|
1204
|
+
* Result of resolving an attachment from its source
|
|
1205
|
+
*/
|
|
1206
|
+
export interface ResolvedAttachment {
|
|
1207
|
+
filename: string;
|
|
1208
|
+
content: globalThis.Buffer;
|
|
1209
|
+
contentType: string;
|
|
1210
|
+
size: number;
|
|
1211
|
+
disposition?: 'attachment' | 'inline';
|
|
1212
|
+
contentId?: string;
|
|
1213
|
+
}
|
|
1214
|
+
/**
|
|
1215
|
+
* Attachment resolver options
|
|
1216
|
+
* Configuration for resolving attachments from various sources
|
|
1217
|
+
*/
|
|
1218
|
+
export interface AttachmentResolverOptions {
|
|
1219
|
+
/** Timeout for HTTP requests in milliseconds */
|
|
1220
|
+
httpTimeout?: number;
|
|
1221
|
+
/** Maximum file size to fetch */
|
|
1222
|
+
maxFileSize?: number;
|
|
1223
|
+
/** Allow local file paths (default: true) */
|
|
1224
|
+
allowLocalPaths?: boolean;
|
|
1225
|
+
/** Logger */
|
|
1226
|
+
logger?: unknown;
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Base event payload with generic metadata support
|
|
1230
|
+
*/
|
|
1231
|
+
export interface NotificationEventPayload<TMetadata = UnknownRecord> {
|
|
1232
|
+
notificationId: string;
|
|
1233
|
+
recipientId: string;
|
|
1234
|
+
channel: NotificationChannel;
|
|
1235
|
+
templateId: string;
|
|
1236
|
+
timestamp: Date;
|
|
1237
|
+
category: NotificationCategory;
|
|
1238
|
+
metadata?: TMetadata;
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Sent event payload
|
|
1242
|
+
*/
|
|
1243
|
+
export interface NotificationSentPayload extends NotificationEventPayload {
|
|
1244
|
+
provider: string;
|
|
1245
|
+
providerMessageId: string;
|
|
1246
|
+
attemptedProviders: string[];
|
|
1247
|
+
fallbackUsed: boolean;
|
|
1248
|
+
metadata?: {
|
|
1249
|
+
provider?: string;
|
|
1250
|
+
messageId?: string;
|
|
1251
|
+
[key: string]: unknown;
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Failed event payload
|
|
1256
|
+
*/
|
|
1257
|
+
export interface NotificationFailedPayload extends NotificationEventPayload {
|
|
1258
|
+
error?: {
|
|
1259
|
+
message: string;
|
|
1260
|
+
code?: string;
|
|
1261
|
+
[key: string]: unknown;
|
|
1262
|
+
};
|
|
1263
|
+
attemptedProviders: string[];
|
|
1264
|
+
retryCount: number;
|
|
1265
|
+
nextRetryAt?: Date;
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
1268
|
+
* Delivered event payload (from webhooks)
|
|
1269
|
+
*/
|
|
1270
|
+
export interface NotificationDeliveredPayload extends NotificationEventPayload {
|
|
1271
|
+
provider: string;
|
|
1272
|
+
providerMessageId: string;
|
|
1273
|
+
deliveredAt: Date;
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Engagement event payload (clicks, opens)
|
|
1277
|
+
*/
|
|
1278
|
+
export interface NotificationEngagementPayload extends NotificationEventPayload {
|
|
1279
|
+
provider: string;
|
|
1280
|
+
providerMessageId: string;
|
|
1281
|
+
eventType: 'opened' | 'clicked';
|
|
1282
|
+
timestamp: Date;
|
|
1283
|
+
metadata?: {
|
|
1284
|
+
url?: string;
|
|
1285
|
+
[key: string]: unknown;
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* URL Shortening Event Payload
|
|
1290
|
+
* Emitted when a URL is shortened during template rendering
|
|
1291
|
+
*/
|
|
1292
|
+
export interface UrlShortenedPayload {
|
|
1293
|
+
/** Original long URL */
|
|
1294
|
+
originalUrl: string;
|
|
1295
|
+
/** Shortened URL */
|
|
1296
|
+
shortUrl: string;
|
|
1297
|
+
/** Notification channel (sms, push, email) */
|
|
1298
|
+
channel: NotificationChannel;
|
|
1299
|
+
/** Recipient ID */
|
|
1300
|
+
recipientId: string;
|
|
1301
|
+
/** Template ID */
|
|
1302
|
+
templateId: string;
|
|
1303
|
+
/** Notification category */
|
|
1304
|
+
category?: string;
|
|
1305
|
+
/** Correlation ID for tracking */
|
|
1306
|
+
correlationId?: string;
|
|
1307
|
+
/** Characters saved by shortening */
|
|
1308
|
+
charactersSaved: number;
|
|
1309
|
+
/** Timestamp when URL was shortened */
|
|
1310
|
+
timestamp: Date;
|
|
1311
|
+
/** Optional metadata */
|
|
1312
|
+
metadata?: UnknownRecord;
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Debug event payload
|
|
1316
|
+
*/
|
|
1317
|
+
export interface DebugEventPayload<TMetadata = UnknownRecord> {
|
|
1318
|
+
correlationId: string;
|
|
1319
|
+
userId?: string;
|
|
1320
|
+
event: string;
|
|
1321
|
+
level: 'debug' | 'info' | 'warn' | 'error';
|
|
1322
|
+
message: string;
|
|
1323
|
+
timestamp: string;
|
|
1324
|
+
data: UnknownRecord;
|
|
1325
|
+
metadata?: TMetadata;
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* Generic event callback function type
|
|
1329
|
+
* Used by EventManager for type-safe event handling
|
|
1330
|
+
*/
|
|
1331
|
+
export type EventCallback<T = unknown> = (payload: T) => void | Promise<void>;
|
|
1332
|
+
/**
|
|
1333
|
+
* Event callback types
|
|
1334
|
+
*/
|
|
1335
|
+
export interface NotificationEventCallbacks {
|
|
1336
|
+
onQueued?: (payload: NotificationEventPayload) => Promisable<void>;
|
|
1337
|
+
onProcessing?: (payload: NotificationEventPayload) => Promisable<void>;
|
|
1338
|
+
onSent?: (payload: NotificationSentPayload) => Promisable<void>;
|
|
1339
|
+
onFailed?: (payload: NotificationFailedPayload) => Promisable<void>;
|
|
1340
|
+
onDelivered?: (payload: NotificationDeliveredPayload) => Promisable<void>;
|
|
1341
|
+
onBounced?: (payload: NotificationEventPayload) => Promisable<void>;
|
|
1342
|
+
onComplained?: (payload: NotificationEventPayload) => Promisable<void>;
|
|
1343
|
+
onOpened?: (payload: NotificationEngagementPayload) => Promisable<void>;
|
|
1344
|
+
onClicked?: (payload: NotificationEngagementPayload) => Promisable<void>;
|
|
1345
|
+
onUrlShortened?: (payload: UrlShortenedPayload) => Promisable<void>;
|
|
1346
|
+
}
|
|
1347
|
+
/**
|
|
1348
|
+
* Debug event callbacks
|
|
1349
|
+
*/
|
|
1350
|
+
export interface NotificationDebugEventCallbacks {
|
|
1351
|
+
onQueued?: (payload: DebugEventPayload) => Promisable<void>;
|
|
1352
|
+
onProviderSelected?: (payload: DebugEventPayload) => Promisable<void>;
|
|
1353
|
+
onRateLimitChecked?: (payload: DebugEventPayload) => Promisable<void>;
|
|
1354
|
+
onTemplateRendered?: (payload: DebugEventPayload) => Promisable<void>;
|
|
1355
|
+
onSendingToProvider?: (payload: DebugEventPayload) => Promisable<void>;
|
|
1356
|
+
onProviderSuccess?: (payload: DebugEventPayload) => Promisable<void>;
|
|
1357
|
+
onProviderFailure?: (payload: DebugEventPayload) => Promisable<void>;
|
|
1358
|
+
onWebhookReceived?: (payload: DebugEventPayload) => Promisable<void>;
|
|
1359
|
+
onDelivered?: (payload: DebugEventPayload) => Promisable<void>;
|
|
1360
|
+
onCircuitBreakerOpened?: (payload: DebugEventPayload) => Promisable<void>;
|
|
1361
|
+
onFallbackTriggered?: (payload: DebugEventPayload) => Promisable<void>;
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Logger interface
|
|
1365
|
+
*/
|
|
1366
|
+
export interface Logger {
|
|
1367
|
+
debug(message: string, meta?: UnknownRecord): void;
|
|
1368
|
+
info(message: string, meta?: UnknownRecord): void;
|
|
1369
|
+
warn(message: string, meta?: UnknownRecord): void;
|
|
1370
|
+
error(message: string, meta?: UnknownRecord): void;
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Log level type
|
|
1374
|
+
*/
|
|
1375
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
1376
|
+
/**
|
|
1377
|
+
* NotificationAdapter - Base interface all providers must implement
|
|
1378
|
+
*
|
|
1379
|
+
* This is the contract for all notification providers (SendGrid, Twilio, FCM, etc.)
|
|
1380
|
+
* The adapter pattern ensures providers are swappable without code changes.
|
|
1381
|
+
*/
|
|
1382
|
+
export interface NotificationAdapter {
|
|
1383
|
+
/** Provider name (type-safe from @plyaz/types) */
|
|
1384
|
+
readonly providerName: string;
|
|
1385
|
+
/** Priority for fallback ordering (1 = highest, 999 = lowest) */
|
|
1386
|
+
readonly priority: number;
|
|
1387
|
+
/** Channel this adapter handles */
|
|
1388
|
+
readonly channel: NotificationChannel;
|
|
1389
|
+
/** Optional fallback adapter to use if this adapter fails */
|
|
1390
|
+
readonly fallbackAdapter?: NotificationAdapter;
|
|
1391
|
+
/** Optional webhook adapters for this provider */
|
|
1392
|
+
readonly webhooks?: unknown[];
|
|
1393
|
+
/** Check if provider is available (credentials set, etc.) */
|
|
1394
|
+
isAvailable(): boolean;
|
|
1395
|
+
/** Health check for monitoring */
|
|
1396
|
+
healthCheck(): Promise<{
|
|
1397
|
+
healthy: boolean;
|
|
1398
|
+
latency?: number;
|
|
1399
|
+
error?: string;
|
|
1400
|
+
}>;
|
|
1401
|
+
/** Send notification via this provider */
|
|
1402
|
+
send(input: NotificationInput): Promise<NotificationResult<NotificationSendResult>>;
|
|
1403
|
+
/** Send bulk notifications via this provider */
|
|
1404
|
+
sendBulk?(inputs: NotificationInput[]): Promise<NotificationResult<NotificationSendResult>[]>;
|
|
1405
|
+
/** Get attachment capabilities for this provider */
|
|
1406
|
+
getAttachmentCapabilities(): AttachmentCapabilities;
|
|
1407
|
+
/** Check if attachment is supported by this provider */
|
|
1408
|
+
isSupportedAttachment(attachment: NotificationAttachment): boolean;
|
|
1409
|
+
/** Get list of supported file extensions */
|
|
1410
|
+
getSupportedAttachmentExtensions(): string[];
|
|
1411
|
+
/** Get SMS validation configuration for this adapter (SMS adapters only) */
|
|
1412
|
+
getSMSValidationConfig?(): SMSValidationConfig | undefined;
|
|
1413
|
+
/** Get URL shortener configuration for this adapter */
|
|
1414
|
+
getUrlShortenerConfig?(): UrlShortenerConfig | undefined;
|
|
1415
|
+
/** Validate email addresses (Email adapters only) */
|
|
1416
|
+
validateEmails?(emails: string[]): Promise<NotificationResult<EmailValidationResult[]>>;
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* ProviderPlugin - Factory pattern for creating provider adapters
|
|
1420
|
+
*
|
|
1421
|
+
* Plugins decouple provider registration from implementation.
|
|
1422
|
+
*/
|
|
1423
|
+
export interface ProviderPlugin<TConfig = UnknownRecord> {
|
|
1424
|
+
/** Unique provider name */
|
|
1425
|
+
name: ProviderName;
|
|
1426
|
+
/** Channel this provider supports (email, sms, push) */
|
|
1427
|
+
channel: NotificationChannel;
|
|
1428
|
+
/** Factory method to create adapter instance */
|
|
1429
|
+
create(config: TConfig, logger: Logger): NotificationAdapter;
|
|
1430
|
+
}
|
|
1431
|
+
/**
|
|
1432
|
+
* TranslationService - Interface for template loading
|
|
1433
|
+
*
|
|
1434
|
+
* Abstracts template storage (file system, database, CDN, etc.)
|
|
1435
|
+
*/
|
|
1436
|
+
export interface TranslationService {
|
|
1437
|
+
/** Get template content by path */
|
|
1438
|
+
getTemplate(path: string): Promise<string>;
|
|
1439
|
+
}
|
|
1440
|
+
/**
|
|
1441
|
+
* PreferenceChecker - Interface for user preference checking
|
|
1442
|
+
*
|
|
1443
|
+
* Allows external applications to enforce user preferences without vendor lock-in.
|
|
1444
|
+
*/
|
|
1445
|
+
export interface PreferenceChecker {
|
|
1446
|
+
/**
|
|
1447
|
+
* Check if a notification should be sent to a recipient
|
|
1448
|
+
*/
|
|
1449
|
+
shouldSend(recipientId: string, category: NotificationCategory, channel: NotificationChannel): Promisable<boolean>;
|
|
1450
|
+
/**
|
|
1451
|
+
* Bulk preference check for batch operations
|
|
1452
|
+
*/
|
|
1453
|
+
shouldSendBulk?(recipients: Array<{
|
|
1454
|
+
recipientId: string;
|
|
1455
|
+
category: NotificationCategory;
|
|
1456
|
+
channel: NotificationChannel;
|
|
1457
|
+
}>): Promisable<boolean[]>;
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Error handler callbacks
|
|
1461
|
+
*/
|
|
1462
|
+
export interface NotificationErrorHandlers {
|
|
1463
|
+
onProviderError?: (error: Error) => Promisable<void>;
|
|
1464
|
+
onQueueError?: (error: Error) => Promisable<void>;
|
|
1465
|
+
onWebhookError?: (error: Error) => Promisable<void>;
|
|
1466
|
+
onAnyError?: (error: Error) => Promisable<void>;
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* NotificationServiceConfig - Main service configuration
|
|
1470
|
+
*
|
|
1471
|
+
* This is the primary configuration interface for creating NotificationService
|
|
1472
|
+
*/
|
|
1473
|
+
export interface NotificationServiceConfig {
|
|
1474
|
+
/** Required: Provider configuration */
|
|
1475
|
+
providers: NotificationProvidersConfig;
|
|
1476
|
+
/** Optional: Locale configuration */
|
|
1477
|
+
locale?: {
|
|
1478
|
+
default: string;
|
|
1479
|
+
fallback: string;
|
|
1480
|
+
};
|
|
1481
|
+
/** Optional: Additional error message catalogs */
|
|
1482
|
+
additionalErrorMessages?: Record<string, MessageCatalog>;
|
|
1483
|
+
/** Optional: Event handlers for lifecycle hooks */
|
|
1484
|
+
events?: NotificationEventCallbacks;
|
|
1485
|
+
/** Optional: Error handlers */
|
|
1486
|
+
errorHandlers?: NotificationErrorHandlers;
|
|
1487
|
+
/** Optional: Debug event handlers (for observability/analytics) */
|
|
1488
|
+
debugEvents?: NotificationDebugEventCallbacks;
|
|
1489
|
+
/** Optional: External dependencies */
|
|
1490
|
+
logger?: Logger;
|
|
1491
|
+
translationService?: TranslationService;
|
|
1492
|
+
/** Optional: User preference checker */
|
|
1493
|
+
preferenceChecker?: PreferenceChecker;
|
|
1494
|
+
/** Optional: SMS validation configuration */
|
|
1495
|
+
smsValidation?: SMSValidationConfig;
|
|
1496
|
+
}
|
|
1497
|
+
/**
|
|
1498
|
+
* NotificationService - Main public API interface
|
|
1499
|
+
*
|
|
1500
|
+
* This is the public interface that applications interact with
|
|
1501
|
+
*/
|
|
1502
|
+
export interface NotificationServiceInstance {
|
|
1503
|
+
sendEmail(params: SendEmailParams): Promise<NotificationResult<NotificationResponse>>;
|
|
1504
|
+
sendSMS(params: SendSMSParams): Promise<NotificationResult<NotificationResponse>>;
|
|
1505
|
+
sendPush(params: SendPushParams): Promise<NotificationResult<NotificationResponse>>;
|
|
1506
|
+
sendBulkEmail(requests: SendEmailParams[]): Promise<NotificationResult<NotificationResponse>[]>;
|
|
1507
|
+
sendBulkSMS(requests: SendSMSParams[]): Promise<NotificationResult<NotificationResponse>[]>;
|
|
1508
|
+
sendBulkPush(requests: SendPushParams[]): Promise<NotificationResult<NotificationResponse>[]>;
|
|
1509
|
+
validateEmails(emails: string[], providerName?: string): Promise<NotificationResult<EmailValidationResult[]>>;
|
|
1510
|
+
healthCheck(): Promise<NotificationHealthStatus>;
|
|
1511
|
+
getProviderStatus(): Promise<ProviderStatus[]>;
|
|
1512
|
+
}
|
|
1513
|
+
/**
|
|
1514
|
+
* Processing function type
|
|
1515
|
+
* Takes a queued notification and processes it
|
|
1516
|
+
*/
|
|
1517
|
+
export type ProcessFunction = (notification: QueuedNotification) => Promise<void>;
|
|
1518
|
+
/**
|
|
1519
|
+
* Queue processor configuration
|
|
1520
|
+
*/
|
|
1521
|
+
export interface QueueProcessorConfig {
|
|
1522
|
+
/** Number of concurrent workers */
|
|
1523
|
+
concurrency?: number;
|
|
1524
|
+
/** Processing interval in ms */
|
|
1525
|
+
interval?: number;
|
|
1526
|
+
/** Maximum retries per notification */
|
|
1527
|
+
maxRetries?: number;
|
|
1528
|
+
/** Enable auto-start */
|
|
1529
|
+
autoStart?: boolean;
|
|
1530
|
+
/** Optional event manager */
|
|
1531
|
+
eventManager?: unknown;
|
|
1532
|
+
/** Optional logger */
|
|
1533
|
+
logger?: Logger;
|
|
1534
|
+
}
|
|
1535
|
+
/**
|
|
1536
|
+
* Raw webhook payload from HTTP request
|
|
1537
|
+
* Renamed to avoid collision with payments WebhookPayload
|
|
1538
|
+
*/
|
|
1539
|
+
export interface NotificationWebhookPayload<TBody = unknown> {
|
|
1540
|
+
/** HTTP headers from webhook request */
|
|
1541
|
+
headers: Record<string, string | string[] | undefined>;
|
|
1542
|
+
/** Parsed body from webhook request */
|
|
1543
|
+
body: TBody;
|
|
1544
|
+
/** Query parameters if any */
|
|
1545
|
+
query?: Record<string, string | string[]>;
|
|
1546
|
+
/** Request method (usually POST) */
|
|
1547
|
+
method?: string;
|
|
1548
|
+
/** Full URL path */
|
|
1549
|
+
url?: string;
|
|
1550
|
+
}
|
|
1551
|
+
/**
|
|
1552
|
+
* Processed webhook event ready for internal handling
|
|
1553
|
+
*/
|
|
1554
|
+
export interface ProcessedWebhookEvent {
|
|
1555
|
+
/** Internal notification ID (from our system) */
|
|
1556
|
+
notificationId: string;
|
|
1557
|
+
/** Provider's message ID */
|
|
1558
|
+
providerMessageId: string;
|
|
1559
|
+
/** Recipient identifier */
|
|
1560
|
+
recipientId?: string;
|
|
1561
|
+
/** Event type */
|
|
1562
|
+
eventType: WEBHOOK_EVENT_TYPE;
|
|
1563
|
+
/** Provider name */
|
|
1564
|
+
provider: string;
|
|
1565
|
+
/** Channel (email, sms, push) */
|
|
1566
|
+
channel: NotificationChannel;
|
|
1567
|
+
/** Notification category */
|
|
1568
|
+
category?: NotificationCategory;
|
|
1569
|
+
/** When the event occurred */
|
|
1570
|
+
timestamp: Date;
|
|
1571
|
+
/** Additional provider-specific data */
|
|
1572
|
+
metadata?: UnknownRecord;
|
|
1573
|
+
/** Error details if event is failure-related */
|
|
1574
|
+
error?: {
|
|
1575
|
+
code?: string;
|
|
1576
|
+
message?: string;
|
|
1577
|
+
reason?: string;
|
|
1578
|
+
};
|
|
1579
|
+
/** Engagement data for opens/clicks */
|
|
1580
|
+
engagement?: {
|
|
1581
|
+
url?: string;
|
|
1582
|
+
userAgent?: string;
|
|
1583
|
+
ipAddress?: string;
|
|
1584
|
+
location?: string;
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
/**
|
|
1588
|
+
* Verification result from signature check
|
|
1589
|
+
*/
|
|
1590
|
+
export interface VerificationResult {
|
|
1591
|
+
/** Whether signature is valid */
|
|
1592
|
+
valid: boolean;
|
|
1593
|
+
/** Error message if invalid */
|
|
1594
|
+
error?: string;
|
|
1595
|
+
/** Additional verification details */
|
|
1596
|
+
details?: UnknownRecord;
|
|
1597
|
+
}
|
|
1598
|
+
/**
|
|
1599
|
+
* Webhook processing result
|
|
1600
|
+
*/
|
|
1601
|
+
export interface WebhookProcessingResult {
|
|
1602
|
+
/** Whether processing succeeded */
|
|
1603
|
+
success: boolean;
|
|
1604
|
+
/** Processed events (can be multiple events in one webhook) */
|
|
1605
|
+
events: ProcessedWebhookEvent[];
|
|
1606
|
+
/** Error if processing failed */
|
|
1607
|
+
error?: Error;
|
|
1608
|
+
/** Whether this was a duplicate (idempotency check) */
|
|
1609
|
+
duplicate?: boolean;
|
|
1610
|
+
/** Processing metadata */
|
|
1611
|
+
metadata?: {
|
|
1612
|
+
processingTime?: number;
|
|
1613
|
+
eventCount?: number;
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
/**
|
|
1617
|
+
* Main Webhook Adapter Interface
|
|
1618
|
+
*
|
|
1619
|
+
* All provider webhook adapters must implement this interface
|
|
1620
|
+
*
|
|
1621
|
+
* @template TPayload - Provider-specific payload type
|
|
1622
|
+
* @template TSchema - Zod schema type for validation
|
|
1623
|
+
*/
|
|
1624
|
+
export interface WebhookAdapter<TPayload = unknown, TSchema extends z.ZodType<TPayload> = z.ZodType<TPayload>> {
|
|
1625
|
+
/** Provider name (sendgrid, infobip, twilio, etc.) */
|
|
1626
|
+
readonly providerName: string;
|
|
1627
|
+
/** Channel this webhook handles */
|
|
1628
|
+
readonly channel: NotificationChannel;
|
|
1629
|
+
/** Zod schema for payload validation */
|
|
1630
|
+
readonly schema: TSchema;
|
|
1631
|
+
/** Priority for webhook processing (lower = higher priority) */
|
|
1632
|
+
readonly priority?: number;
|
|
1633
|
+
/**
|
|
1634
|
+
* Verify webhook signature/authenticity
|
|
1635
|
+
* CRITICAL: Always verify before processing to prevent spoofing
|
|
1636
|
+
*/
|
|
1637
|
+
verify(payload: NotificationWebhookPayload<unknown>): Promisable<VerificationResult>;
|
|
1638
|
+
/**
|
|
1639
|
+
* Parse and process webhook payload
|
|
1640
|
+
* Maps provider events to internal event format
|
|
1641
|
+
*/
|
|
1642
|
+
process(payload: NotificationWebhookPayload<TPayload>): Promisable<ProcessedWebhookEvent[]>;
|
|
1643
|
+
/**
|
|
1644
|
+
* Extract idempotency key from payload
|
|
1645
|
+
* Used to prevent duplicate processing
|
|
1646
|
+
*/
|
|
1647
|
+
getIdempotencyKey?(payload: NotificationWebhookPayload<TPayload>): string;
|
|
1648
|
+
/**
|
|
1649
|
+
* Optional: Check if this adapter should handle this webhook
|
|
1650
|
+
* Useful when multiple adapters are registered for same provider
|
|
1651
|
+
*/
|
|
1652
|
+
shouldProcess?(payload: NotificationWebhookPayload<unknown>): boolean;
|
|
1653
|
+
/**
|
|
1654
|
+
* Optional: Custom error handler for this adapter
|
|
1655
|
+
*/
|
|
1656
|
+
handleError?(error: Error, payload: NotificationWebhookPayload<unknown>): Promisable<void>;
|
|
1657
|
+
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Webhook middleware for pre/post processing
|
|
1660
|
+
*/
|
|
1661
|
+
export interface WebhookMiddleware {
|
|
1662
|
+
/** Middleware name for logging */
|
|
1663
|
+
name: string;
|
|
1664
|
+
/** Priority (lower = runs earlier) */
|
|
1665
|
+
priority?: number;
|
|
1666
|
+
/**
|
|
1667
|
+
* Execute before webhook processing
|
|
1668
|
+
*/
|
|
1669
|
+
before?(payload: NotificationWebhookPayload<unknown>): Promisable<NotificationWebhookPayload<unknown> | void>;
|
|
1670
|
+
/**
|
|
1671
|
+
* Execute after webhook processing
|
|
1672
|
+
*/
|
|
1673
|
+
after?(result: WebhookProcessingResult, payload: NotificationWebhookPayload<unknown>): Promisable<WebhookProcessingResult | void>;
|
|
1674
|
+
/**
|
|
1675
|
+
* Execute on error
|
|
1676
|
+
*/
|
|
1677
|
+
onError?(error: Error, payload: NotificationWebhookPayload<unknown>): Promisable<void>;
|
|
1678
|
+
}
|
|
1679
|
+
/**
|
|
1680
|
+
* Configuration for webhook adapter
|
|
1681
|
+
*/
|
|
1682
|
+
export interface WebhookAdapterConfig {
|
|
1683
|
+
/** Secret key for signature verification */
|
|
1684
|
+
secret: string;
|
|
1685
|
+
/** Enable/disable this webhook adapter */
|
|
1686
|
+
enabled?: boolean;
|
|
1687
|
+
/** Custom timeout for webhook processing (ms) */
|
|
1688
|
+
timeout?: number;
|
|
1689
|
+
/** Custom idempotency TTL (ms) */
|
|
1690
|
+
idempotencyTTL?: number;
|
|
1691
|
+
/** Additional provider-specific configuration */
|
|
1692
|
+
[key: string]: unknown;
|
|
1693
|
+
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Webhook encryption methods
|
|
1696
|
+
*/
|
|
1697
|
+
export type WebhookEncryptionMethod = 'hmac' | 'ecdsa' | 'rsa' | 'custom';
|
|
1698
|
+
/**
|
|
1699
|
+
* Webhook encryption configuration
|
|
1700
|
+
*/
|
|
1701
|
+
export interface WebhookEncryption {
|
|
1702
|
+
/** Encryption/signing method */
|
|
1703
|
+
method: WebhookEncryptionMethod;
|
|
1704
|
+
/** Secret key for HMAC */
|
|
1705
|
+
secret?: string;
|
|
1706
|
+
/** Public key for ECDSA/RSA verification */
|
|
1707
|
+
publicKey?: string;
|
|
1708
|
+
/** Private key for ECDSA/RSA signing (if generating webhooks) */
|
|
1709
|
+
privateKey?: string;
|
|
1710
|
+
/** Algorithm (e.g., 'sha256', 'sha512') */
|
|
1711
|
+
algorithm?: string;
|
|
1712
|
+
/** Encoding (e.g., 'hex', 'base64') */
|
|
1713
|
+
encoding?: 'hex' | 'base64';
|
|
1714
|
+
/** Custom verification function */
|
|
1715
|
+
verify?: (payload: string, signature: string) => Promisable<boolean>;
|
|
1716
|
+
/** Custom signing function */
|
|
1717
|
+
sign?: (payload: string) => Promisable<string>;
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Webhook Encryption Configuration
|
|
1721
|
+
* Used to automatically hash PII before sending to providers
|
|
1722
|
+
*/
|
|
1723
|
+
export interface WebhookEncryptionConfig {
|
|
1724
|
+
/** Enable/disable automatic PII hashing */
|
|
1725
|
+
enabled: boolean;
|
|
1726
|
+
/** Encryption method */
|
|
1727
|
+
method: WEBHOOK_ENCRYPTION_METHOD;
|
|
1728
|
+
/** Secret key for hashing (MUST be stored securely) */
|
|
1729
|
+
secret: string;
|
|
1730
|
+
/** Hash length (characters), default: 16 */
|
|
1731
|
+
hashLength?: number;
|
|
1732
|
+
/** Fields to automatically hash, default: ['recipientId', 'userId'] */
|
|
1733
|
+
fieldsToHash?: string[];
|
|
1734
|
+
/** Whether to store ID mappings automatically */
|
|
1735
|
+
storeMapping?: boolean;
|
|
1736
|
+
/** Optional: External store for ID mappings */
|
|
1737
|
+
idMappingStore?: IdMappingStore;
|
|
1738
|
+
}
|
|
1739
|
+
/**
|
|
1740
|
+
* ID Mapping Store Interface
|
|
1741
|
+
* Implement this to store hashed ID mappings in your database
|
|
1742
|
+
*/
|
|
1743
|
+
export interface IdMappingStore {
|
|
1744
|
+
/** Store a mapping between original and hashed ID */
|
|
1745
|
+
store(originalId: string, hashedId: string): Promise<void>;
|
|
1746
|
+
/** Retrieve original ID from hashed ID */
|
|
1747
|
+
getOriginalId(hashedId: string): Promise<string | undefined>;
|
|
1748
|
+
/** Retrieve hashed ID from original ID */
|
|
1749
|
+
getHashedId(originalId: string): Promise<string | undefined>;
|
|
1750
|
+
/** Delete a mapping */
|
|
1751
|
+
delete(originalId: string): Promise<void>;
|
|
1752
|
+
/** Bulk store mappings (for performance) */
|
|
1753
|
+
storeBulk?(mappings: Array<{
|
|
1754
|
+
originalId: string;
|
|
1755
|
+
hashedId: string;
|
|
1756
|
+
}>): Promise<void>;
|
|
1757
|
+
}
|
|
1758
|
+
/**
|
|
1759
|
+
* Hashed ID result
|
|
1760
|
+
*/
|
|
1761
|
+
export interface HashedIdResult {
|
|
1762
|
+
/** Original ID (PII) */
|
|
1763
|
+
originalId: string;
|
|
1764
|
+
/** Hashed ID (PII-safe) */
|
|
1765
|
+
hashedId: string;
|
|
1766
|
+
/** Encryption method used */
|
|
1767
|
+
method: WEBHOOK_ENCRYPTION_METHOD;
|
|
1768
|
+
/** When it was hashed */
|
|
1769
|
+
hashedAt: Date;
|
|
1770
|
+
}
|
|
1771
|
+
/**
|
|
1772
|
+
* External idempotency store interface
|
|
1773
|
+
* Implement this to use Redis, Database, etc.
|
|
1774
|
+
*/
|
|
1775
|
+
export interface ExternalIdempotencyStore {
|
|
1776
|
+
/** Check if key exists and is not expired */
|
|
1777
|
+
has(key: string): Promise<boolean>;
|
|
1778
|
+
/** Store key with TTL */
|
|
1779
|
+
set(key: string, ttl: number, metadata?: UnknownRecord): Promise<void>;
|
|
1780
|
+
/** Remove key */
|
|
1781
|
+
delete(key: string): Promise<void>;
|
|
1782
|
+
/** Clear all keys (for testing) */
|
|
1783
|
+
clear?(): Promise<void>;
|
|
1784
|
+
}
|
|
1785
|
+
/**
|
|
1786
|
+
* Idempotency Store Configuration
|
|
1787
|
+
*/
|
|
1788
|
+
export interface IdempotencyStoreConfig {
|
|
1789
|
+
/** Default TTL for idempotency keys (ms) */
|
|
1790
|
+
defaultTTL?: number;
|
|
1791
|
+
/** Cleanup interval for expired keys (ms) */
|
|
1792
|
+
cleanupInterval?: number;
|
|
1793
|
+
/** Maximum keys to store in memory */
|
|
1794
|
+
maxKeys?: number;
|
|
1795
|
+
/** External store (Redis, DB) - optional */
|
|
1796
|
+
externalStore?: ExternalIdempotencyStore;
|
|
1797
|
+
}
|
|
1798
|
+
/**
|
|
1799
|
+
* Webhook Manager Configuration
|
|
1800
|
+
*/
|
|
1801
|
+
export interface WebhookManagerConfig {
|
|
1802
|
+
/** Idempotency store configuration */
|
|
1803
|
+
idempotency?: IdempotencyStoreConfig;
|
|
1804
|
+
/** Global timeout for webhook processing (ms) */
|
|
1805
|
+
timeout?: number;
|
|
1806
|
+
/** Enable/disable webhook processing globally */
|
|
1807
|
+
enabled?: boolean;
|
|
1808
|
+
/** Logger instance */
|
|
1809
|
+
logger?: Logger;
|
|
1810
|
+
}
|
|
1811
|
+
/**
|
|
1812
|
+
* Base webhook adapter configuration
|
|
1813
|
+
*/
|
|
1814
|
+
export interface BaseWebhookAdapterConfig extends WebhookAdapterConfig {
|
|
1815
|
+
/** Secret key for signature verification */
|
|
1816
|
+
secret: string;
|
|
1817
|
+
/** Signature verification method */
|
|
1818
|
+
signatureMethod?: SIGNATURE_METHOD;
|
|
1819
|
+
/** Header name containing the signature */
|
|
1820
|
+
signatureHeader?: string;
|
|
1821
|
+
/** Logger instance */
|
|
1822
|
+
logger?: unknown;
|
|
1823
|
+
}
|
|
1824
|
+
/**
|
|
1825
|
+
* Idempotency record (internal use)
|
|
1826
|
+
* Used by IdempotencyStore to track processed webhook events
|
|
1827
|
+
*/
|
|
1828
|
+
export interface IdempotencyRecord {
|
|
1829
|
+
/** Unique idempotency key */
|
|
1830
|
+
key: string;
|
|
1831
|
+
/** When this key was first processed */
|
|
1832
|
+
processedAt: Date;
|
|
1833
|
+
/** When this key expires and can be removed */
|
|
1834
|
+
expiresAt: Date;
|
|
1835
|
+
/** Optional metadata associated with this key */
|
|
1836
|
+
metadata?: Record<string, unknown>;
|
|
1837
|
+
}
|
|
1838
|
+
/**
|
|
1839
|
+
* Template cache entry (internal use)
|
|
1840
|
+
* Used by TemplateEngine for caching parsed templates
|
|
1841
|
+
*/
|
|
1842
|
+
export interface TemplateCacheEntry {
|
|
1843
|
+
/** Template source code */
|
|
1844
|
+
source: string;
|
|
1845
|
+
/** Parsed template metadata/frontmatter */
|
|
1846
|
+
metadata: TemplateFrontmatter;
|
|
1847
|
+
}
|
|
1848
|
+
/**
|
|
1849
|
+
* Infobip Email Delivery Webhook configuration
|
|
1850
|
+
*/
|
|
1851
|
+
export interface InfobipDeliveryWebhookConfig {
|
|
1852
|
+
/**
|
|
1853
|
+
* Secret key for signature verification (optional)
|
|
1854
|
+
* Infobip doesn't provide signature verification by default
|
|
1855
|
+
* You can implement IP allowlisting or custom verification
|
|
1856
|
+
*/
|
|
1857
|
+
secret?: string;
|
|
1858
|
+
/** Enable/disable this webhook handler */
|
|
1859
|
+
enabled?: boolean;
|
|
1860
|
+
/**
|
|
1861
|
+
* Enable/disable strict validation
|
|
1862
|
+
* If true, will validate all fields according to schema
|
|
1863
|
+
* Default: true
|
|
1864
|
+
*/
|
|
1865
|
+
strictValidation?: boolean;
|
|
1866
|
+
/**
|
|
1867
|
+
* Priority for webhook processing (lower = higher priority)
|
|
1868
|
+
* Default: 100
|
|
1869
|
+
*/
|
|
1870
|
+
priority?: number;
|
|
1871
|
+
/** Logger instance */
|
|
1872
|
+
logger?: unknown;
|
|
1873
|
+
}
|
|
1874
|
+
/**
|
|
1875
|
+
* Infobip Email Tracking Webhook configuration
|
|
1876
|
+
*/
|
|
1877
|
+
export interface InfobipTrackingWebhookConfig {
|
|
1878
|
+
/**
|
|
1879
|
+
* Secret key for signature verification (optional)
|
|
1880
|
+
* Infobip doesn't provide signature verification by default
|
|
1881
|
+
* You can implement IP allowlisting or custom verification
|
|
1882
|
+
*/
|
|
1883
|
+
secret?: string;
|
|
1884
|
+
/** Enable/disable this webhook handler */
|
|
1885
|
+
enabled?: boolean;
|
|
1886
|
+
/**
|
|
1887
|
+
* Enable/disable strict validation
|
|
1888
|
+
* If true, will validate all fields according to schema
|
|
1889
|
+
* Default: true
|
|
1890
|
+
*/
|
|
1891
|
+
strictValidation?: boolean;
|
|
1892
|
+
/**
|
|
1893
|
+
* Priority for webhook processing (lower = higher priority)
|
|
1894
|
+
* Default: 101 (slightly lower priority than delivery webhooks)
|
|
1895
|
+
*/
|
|
1896
|
+
priority?: number;
|
|
1897
|
+
/** Logger instance */
|
|
1898
|
+
logger?: unknown;
|
|
1899
|
+
}
|
|
1900
|
+
/**
|
|
1901
|
+
* SendGrid Webhook configuration
|
|
1902
|
+
*/
|
|
1903
|
+
export interface SendGridWebhookConfig {
|
|
1904
|
+
/**
|
|
1905
|
+
* Verification method
|
|
1906
|
+
* - 'ecdsa': Use ECDSA with public key (RECOMMENDED, more secure)
|
|
1907
|
+
* - 'hmac': Use HMAC with secret (legacy/fallback method)
|
|
1908
|
+
*
|
|
1909
|
+
* Default: 'ecdsa' if publicKey is provided, otherwise 'hmac'
|
|
1910
|
+
*/
|
|
1911
|
+
verificationMethod?: 'ecdsa' | 'hmac';
|
|
1912
|
+
/**
|
|
1913
|
+
* Public key for ECDSA verification (base64 PEM format)
|
|
1914
|
+
* Get from: GET https://api.sendgrid.com/v3/user/webhooks/event/settings/signed
|
|
1915
|
+
* Required if verificationMethod is 'ecdsa'
|
|
1916
|
+
*/
|
|
1917
|
+
publicKey?: string;
|
|
1918
|
+
/**
|
|
1919
|
+
* Secret key for HMAC verification
|
|
1920
|
+
* Required if verificationMethod is 'hmac' or as fallback
|
|
1921
|
+
*/
|
|
1922
|
+
secret: string;
|
|
1923
|
+
/**
|
|
1924
|
+
* Maximum webhook age in seconds (default: 600 = 10 minutes)
|
|
1925
|
+
* Rejects webhooks older than this to prevent replay attacks
|
|
1926
|
+
*/
|
|
1927
|
+
maxWebhookAge?: number;
|
|
1928
|
+
/** Enable/disable this webhook handler */
|
|
1929
|
+
enabled?: boolean;
|
|
1930
|
+
/**
|
|
1931
|
+
* Priority for webhook processing (lower = higher priority)
|
|
1932
|
+
* Default: 100
|
|
1933
|
+
*/
|
|
1934
|
+
priority?: number;
|
|
1935
|
+
/** Logger instance */
|
|
1936
|
+
logger?: unknown;
|
|
1937
|
+
}
|