@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.
Files changed (55) hide show
  1. package/dist/api/client/types.d.ts +3 -3
  2. package/dist/api/config/types.d.ts +62 -5
  3. package/dist/api/debugger/enums.d.ts +12 -0
  4. package/dist/api/debugger/types.d.ts +1 -1
  5. package/dist/api/errors/types.d.ts +46 -238
  6. package/dist/api/events/factories/errors/types.d.ts +26 -27
  7. package/dist/api/index.cjs +1457 -669
  8. package/dist/api/index.cjs.map +1 -1
  9. package/dist/api/index.d.ts +5 -2
  10. package/dist/api/index.js +1385 -603
  11. package/dist/api/index.js.map +1 -1
  12. package/dist/db/DatabaseAdapter.d.ts +2 -2
  13. package/dist/db/DatabaseService.d.ts +3 -3
  14. package/dist/db/config.types.d.ts +1 -1
  15. package/dist/db/database.types.d.ts +2 -2
  16. package/dist/db/index.cjs.map +1 -1
  17. package/dist/db/index.d.ts +1 -1
  18. package/dist/db/index.js.map +1 -1
  19. package/dist/errors/codes.d.ts +253 -0
  20. package/dist/errors/enums.d.ts +199 -0
  21. package/dist/errors/index.cjs +1307 -0
  22. package/dist/errors/index.cjs.map +1 -1
  23. package/dist/errors/index.d.ts +1 -0
  24. package/dist/errors/index.js +1284 -1
  25. package/dist/errors/index.js.map +1 -1
  26. package/dist/errors/types.d.ts +630 -14
  27. package/dist/index.cjs +2182 -1218
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.ts +43 -1
  30. package/dist/index.js +1849 -911
  31. package/dist/index.js.map +1 -1
  32. package/dist/notifications/enums.d.ts +140 -0
  33. package/dist/notifications/index.cjs +4353 -0
  34. package/dist/notifications/index.cjs.map +1 -0
  35. package/dist/notifications/index.js +145 -0
  36. package/dist/notifications/index.js.map +1 -0
  37. package/dist/notifications/schemas.d.ts +73 -0
  38. package/dist/notifications/types.d.ts +1937 -0
  39. package/dist/payments/base-error/enum.d.ts +79 -0
  40. package/dist/payments/base-error/index.d.ts +2 -0
  41. package/dist/payments/base-error/types.d.ts +180 -0
  42. package/dist/payments/currency/enums.d.ts +37 -0
  43. package/dist/payments/currency/index.d.ts +1 -37
  44. package/dist/payments/index.cjs +40 -40
  45. package/dist/payments/index.cjs.map +1 -1
  46. package/dist/payments/index.d.ts +2 -1
  47. package/dist/payments/index.js +40 -40
  48. package/dist/payments/index.js.map +1 -1
  49. package/dist/payments/transaction/types.d.ts +3 -3
  50. package/dist/store/index.d.ts +1 -1
  51. package/dist/store/types.d.ts +2 -3
  52. package/package.json +6 -1
  53. package/dist/api/errors/enum.d.ts +0 -214
  54. package/dist/api/errors/index.d.ts +0 -6
  55. /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
+ }