@nathapp/nestjs-notification 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/UPGRADE-PLAN-PHASE7-8.md +307 -0
  2. package/UPGRADE-PLAN-PHASE9.md +361 -0
  3. package/UPGRADE-PLAN.md +425 -0
  4. package/dist/index.d.ts +3 -2
  5. package/dist/index.js +20 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/interface/index.d.ts +3 -3
  8. package/dist/interface/message.interface.d.ts +10 -2
  9. package/dist/interface/payload.inteface.d.ts +51 -0
  10. package/dist/notification/errors.d.ts +3 -0
  11. package/dist/notification/errors.js +11 -0
  12. package/dist/notification/errors.js.map +1 -0
  13. package/dist/notification/index.d.ts +6 -2
  14. package/dist/notification/index.js +7 -1
  15. package/dist/notification/index.js.map +1 -1
  16. package/dist/notification/notification-provider.factory.d.ts +5 -0
  17. package/dist/notification/notification-provider.factory.js +33 -0
  18. package/dist/notification/notification-provider.factory.js.map +1 -0
  19. package/dist/notification/notification.interceptor.d.ts +18 -0
  20. package/dist/notification/notification.interceptor.js +5 -0
  21. package/dist/notification/notification.interceptor.js.map +1 -0
  22. package/dist/notification/notification.module.d.ts +28 -18
  23. package/dist/notification/notification.module.js +324 -63
  24. package/dist/notification/notification.module.js.map +1 -1
  25. package/dist/notification/notification.service.d.ts +18 -2
  26. package/dist/notification/notification.service.js +274 -13
  27. package/dist/notification/notification.service.js.map +1 -1
  28. package/dist/notification/provider-config.d.ts +17 -0
  29. package/dist/notification/provider-config.js +3 -0
  30. package/dist/notification/provider-config.js.map +1 -0
  31. package/dist/push-notification/push-notification.service.js +2 -1
  32. package/dist/push-notification/push-notification.service.js.map +1 -1
  33. package/dist/template/constant.d.ts +1 -0
  34. package/dist/template/constant.js +2 -1
  35. package/dist/template/constant.js.map +1 -1
  36. package/dist/template/engines/string-format.engine.d.ts +6 -0
  37. package/dist/template/engines/string-format.engine.js +17 -0
  38. package/dist/template/engines/string-format.engine.js.map +1 -0
  39. package/dist/template/index.d.ts +4 -2
  40. package/dist/template/index.js +4 -1
  41. package/dist/template/index.js.map +1 -1
  42. package/dist/template/template-engine.d.ts +5 -0
  43. package/dist/template/template-engine.js +3 -0
  44. package/dist/template/template-engine.js.map +1 -0
  45. package/dist/template/template.loader.d.ts +4 -1
  46. package/dist/template/template.loader.js +22 -14
  47. package/dist/template/template.loader.js.map +1 -1
  48. package/dist/whatsapp/constant.d.ts +1 -0
  49. package/dist/whatsapp/constant.js +5 -0
  50. package/dist/whatsapp/constant.js.map +1 -0
  51. package/dist/whatsapp/index.d.ts +2 -0
  52. package/dist/whatsapp/index.js +19 -0
  53. package/dist/whatsapp/index.js.map +1 -0
  54. package/dist/whatsapp/whatsapp.service.d.ts +7 -0
  55. package/dist/whatsapp/whatsapp.service.js +7 -0
  56. package/dist/whatsapp/whatsapp.service.js.map +1 -0
  57. package/package.json +2 -1
@@ -0,0 +1,425 @@
1
+ # @nathapp/nestjs-notification Enterprise Upgrade Plan
2
+
3
+ ## Overview
4
+
5
+ Upgrade `@nathapp/nestjs-notification` (v2.1.4) to be more enterprise-grade, extensible, and plugin-friendly while maintaining full backwards compatibility.
6
+
7
+ **Branch:** `feat/notification-enterprise-upgrade`
8
+ **Scope:** Core library only (`packages/nestjs-notification`). No business logic, no tenant context.
9
+
10
+ ---
11
+
12
+ ## Phase 1: Pluggable Template Engine
13
+
14
+ ### Problem
15
+ `TemplateLoader.resolve()` hardcodes `string-format` for variable interpolation. Apps needing conditionals, loops, or partials (e.g., Handlebars) must override the entire `resolve()` method.
16
+
17
+ ### Changes
18
+
19
+ #### 1.1 Create `TemplateEngine` interface
20
+ **File:** `lib/template/template-engine.ts` (NEW)
21
+
22
+ ```typescript
23
+ export interface TemplateEngine {
24
+ /**
25
+ * Compile and render a template string with the given context.
26
+ * @param template - Raw template string (e.g., "Hello {name}" or "Hello {{name}}")
27
+ * @param context - Key-value pairs for interpolation
28
+ * @returns Rendered string
29
+ */
30
+ render(template: string, context: Record<string, any>): string;
31
+ }
32
+ ```
33
+
34
+ #### 1.2 Create `StringFormatEngine` (default)
35
+ **File:** `lib/template/engines/string-format.engine.ts` (NEW)
36
+
37
+ ```typescript
38
+ import Format from 'string-format';
39
+ import { TemplateEngine } from '../template-engine';
40
+
41
+ export class StringFormatEngine implements TemplateEngine {
42
+ render(template: string, context: Record<string, any>): string {
43
+ return Format(template, context);
44
+ }
45
+ }
46
+ ```
47
+
48
+ #### 1.3 Create `TEMPLATE_ENGINE` injection token
49
+ **File:** `lib/template/constant.ts` (UPDATE)
50
+
51
+ Add: `export const TEMPLATE_ENGINE = 'TEMPLATE_ENGINE';`
52
+
53
+ #### 1.4 Update `TemplateLoader.resolve()` to use engine
54
+ **File:** `lib/template/template.loader.ts` (UPDATE)
55
+
56
+ - Inject `TemplateEngine` (optional, defaults to `StringFormatEngine`)
57
+ - Replace direct `Format()` calls with `this.engine.render()`
58
+ - `CachableTemplateLoader` same treatment
59
+ - **Backwards compat:** If no engine injected, fall back to `StringFormatEngine` internally
60
+
61
+ #### 1.5 Update `NotificationModule` to accept `templateEngine` option
62
+ **File:** `lib/notification/notification.module.ts` (UPDATE)
63
+
64
+ - Add `templateEngine?: Type<TemplateEngine> | ProviderAsyncOptions<TemplateEngine>` to module options
65
+ - Default to `StringFormatEngine` if not provided
66
+ - Register as provider with `TEMPLATE_ENGINE` token
67
+
68
+ #### 1.6 Export new types
69
+ **File:** `lib/template/index.ts` (UPDATE)
70
+ **File:** `lib/index.ts` (UPDATE)
71
+
72
+ Export `TemplateEngine`, `StringFormatEngine`, `TEMPLATE_ENGINE`.
73
+
74
+ ### Unit Tests
75
+
76
+ **File:** `test/template/string-format.engine.spec.ts` (NEW)
77
+ - `render()` with simple variables
78
+ - `render()` with nested objects
79
+ - `render()` with missing variables (should not throw, leave placeholder)
80
+
81
+ **File:** `test/template/template-loader.spec.ts` (NEW)
82
+ - `resolve()` uses injected `TemplateEngine`
83
+ - `resolve()` defaults to `StringFormatEngine` when no engine provided
84
+ - `CachableTemplateLoader.resolve()` uses engine after cache hit
85
+ - `CachableTemplateLoader.resolve()` uses engine after cache miss
86
+
87
+ ### E2E Tests
88
+
89
+ **File:** `test/module/notification-custom-engine.module.e2e.spec.ts` (NEW)
90
+ - Register `NotificationModule.forRoot()` with a custom `TemplateEngine` (e.g., a mock Handlebars-like engine)
91
+ - Verify `NotificationService.sendEmail()` renders templates through the custom engine
92
+ - Verify default `StringFormatEngine` works when no custom engine specified
93
+
94
+ ---
95
+
96
+ ## Phase 2: Configurable Strict Mode
97
+
98
+ ### Problem
99
+ When `EmailService` or `PushNotificationService` isn't configured, `NotificationService` silently returns `{ success: false }`. This hides configuration bugs.
100
+
101
+ ### Changes
102
+
103
+ #### 2.1 Add `strict` option to module options
104
+ **File:** `lib/notification/notification.module.ts` (UPDATE)
105
+
106
+ ```typescript
107
+ export interface NotificationModuleOptions {
108
+ // ... existing
109
+ strict?: boolean; // default: true
110
+ }
111
+ ```
112
+
113
+ #### 2.2 Update `NotificationService` to respect strict mode
114
+ **File:** `lib/notification/notification.service.ts` (UPDATE)
115
+
116
+ - Inject `NOTIFICATION_OPTIONS` to read `strict` flag
117
+ - When `strict: true` (default) and service not configured → throw `Error('EmailService is not configured. Configure emailOptions in NotificationModule or set strict: false.')`
118
+ - When `strict: false` → keep current behaviour (return `{ success: false }`)
119
+
120
+ #### 2.3 Create custom error class
121
+ **File:** `lib/notification/errors.ts` (NEW)
122
+
123
+ ```typescript
124
+ export class NotificationConfigError extends Error {
125
+ constructor(service: string) {
126
+ super(`${service} is not configured. Configure it in NotificationModule or set strict: false.`);
127
+ this.name = 'NotificationConfigError';
128
+ }
129
+ }
130
+ ```
131
+
132
+ ### Unit Tests
133
+
134
+ **File:** `test/notification/notification-strict.service.spec.ts` (NEW)
135
+ - `strict: true` + no email configured → `sendEmail()` throws `NotificationConfigError`
136
+ - `strict: true` + no push configured → `sendPushNotification()` throws `NotificationConfigError`
137
+ - `strict: false` + no email configured → `sendEmail()` returns `{ success: false }`
138
+ - `strict: false` + no push configured → `sendPushNotification()` returns `{ success: false }`
139
+ - Default (no strict option) → behaves as `strict: true`
140
+
141
+ ### E2E Tests
142
+
143
+ **File:** `test/module/notification-strict.module.e2e.spec.ts` (NEW)
144
+ - Module with `strict: true`, no email options → calling `sendEmail` throws
145
+ - Module with `strict: false`, no email options → calling `sendEmail` returns `{ success: false }`
146
+
147
+ ---
148
+
149
+ ## Phase 3: Unified `send()` Method
150
+
151
+ ### Problem
152
+ Callers must use separate methods (`sendEmail`, `sendPushNotification`) per channel. No way to send to multiple channels in one call.
153
+
154
+ ### Changes
155
+
156
+ #### 3.1 Create unified payload interface
157
+ **File:** `lib/interface/payload.inteface.ts` (UPDATE - add to existing)
158
+
159
+ ```typescript
160
+ export type NotificationChannel = 'email' | 'push' | 'sms';
161
+
162
+ export interface NotificationPayload {
163
+ templateCode: string;
164
+ channels: NotificationChannel[];
165
+ options?: {
166
+ locale?: string;
167
+ fallbackLocale?: string;
168
+ args?: any;
169
+ };
170
+ // Channel-specific recipient data
171
+ email?: string;
172
+ userId?: string | number;
173
+ deviceToken?: DeviceToken;
174
+ phoneNumber?: string;
175
+ // Optional
176
+ attachments?: EmailAttachment[];
177
+ }
178
+
179
+ export interface NotificationResult {
180
+ results: Record<NotificationChannel, SendResult>;
181
+ /** True only if ALL channels succeeded */
182
+ success: boolean;
183
+ }
184
+ ```
185
+
186
+ #### 3.2 Add `send()` to `NotificationService`
187
+ **File:** `lib/notification/notification.service.ts` (UPDATE)
188
+
189
+ ```typescript
190
+ async send(payload: NotificationPayload): Promise<NotificationResult> {
191
+ const results: Record<string, SendResult> = {};
192
+
193
+ for (const channel of payload.channels) {
194
+ switch (channel) {
195
+ case 'email':
196
+ results.email = await this.sendEmail({
197
+ templateCode: payload.templateCode,
198
+ email: payload.email,
199
+ options: payload.options,
200
+ attachments: payload.attachments,
201
+ });
202
+ break;
203
+ case 'push':
204
+ results.push = await this.sendPushNotification({
205
+ templateCode: payload.templateCode,
206
+ userId: payload.userId,
207
+ deviceToken: payload.deviceToken,
208
+ options: payload.options,
209
+ });
210
+ break;
211
+ case 'sms':
212
+ // Delegate to SmsService when available
213
+ results.sms = { success: false, message: 'SMS not configured' };
214
+ break;
215
+ }
216
+ }
217
+
218
+ return {
219
+ results: results as Record<NotificationChannel, SendResult>,
220
+ success: Object.values(results).every(r => r.success),
221
+ };
222
+ }
223
+ ```
224
+
225
+ #### 3.3 Export new types
226
+ **File:** `lib/interface/index.ts` (UPDATE)
227
+
228
+ Export `NotificationPayload`, `NotificationResult`, `NotificationChannel`.
229
+
230
+ ### Unit Tests
231
+
232
+ **File:** `test/notification/notification-unified-send.service.spec.ts` (NEW)
233
+ - `send()` with `channels: ['email']` → calls `sendEmail`, returns result
234
+ - `send()` with `channels: ['push']` → calls `sendPushNotification`, returns result
235
+ - `send()` with `channels: ['email', 'push']` → calls both, aggregates results
236
+ - `send()` with `channels: ['sms']` → returns `{ success: false }` for sms
237
+ - `send()` overall `success` is `true` only when all channels succeed
238
+ - `send()` overall `success` is `false` when any channel fails
239
+
240
+ ### E2E Tests
241
+
242
+ **File:** `test/module/notification-unified-send.module.e2e.spec.ts` (NEW)
243
+ - Full module with email configured → `send({ channels: ['email'] })` works end-to-end
244
+ - Full module with email + push → `send({ channels: ['email', 'push'] })` dispatches to both
245
+
246
+ ---
247
+
248
+ ## Phase 4: Backwards-Compatible `platform` Alias
249
+
250
+ ### Problem
251
+ `PushMessage.paltform` is a typo. Fixing it directly is a breaking change.
252
+
253
+ ### Changes
254
+
255
+ #### 4.1 Add `platform` property, deprecate `paltform`
256
+ **File:** `lib/interface/message.interface.ts` (UPDATE)
257
+
258
+ ```typescript
259
+ export interface PushMessage extends BaseMessage {
260
+ /** @deprecated Use `platform` instead */
261
+ paltform?: PlatformOS;
262
+ platform?: PlatformOS;
263
+ subject: string;
264
+ data?: { [key: string]: string };
265
+ }
266
+ ```
267
+
268
+ #### 4.2 Update all internal usages to prefer `platform`
269
+ **Files:** `lib/push-notification/push-notification.service.ts`, `lib/notification/notification.service.ts`
270
+
271
+ ```typescript
272
+ // Resolve with fallback
273
+ const platform = message.platform || message.paltform;
274
+ ```
275
+
276
+ ### Unit Tests
277
+
278
+ **File:** `test/push-notification/platform-alias.spec.ts` (NEW)
279
+ - Message with `platform: 'android'` → uses android provider
280
+ - Message with `paltform: 'android'` (legacy) → still works
281
+ - Message with both `platform` and `paltform` → `platform` takes precedence
282
+
283
+ ---
284
+
285
+ ## Phase 5: SMS Integration Point
286
+
287
+ ### Problem
288
+ `SmsService` is abstract with no integration into `NotificationModule` or `NotificationService`.
289
+
290
+ ### Changes
291
+
292
+ #### 5.1 Add `SmsService` injection to `NotificationService`
293
+ **File:** `lib/notification/notification.service.ts` (UPDATE)
294
+
295
+ - Add `@Optional() @Inject(SmsService) private readonly smsService: SmsService` to constructor
296
+ - Implement `sendSms(data: SmsNotificationPayload): Promise<SendResult>` using template resolution + `smsService.send()`
297
+ - Update `send()` unified method to use `sendSms` for SMS channel
298
+
299
+ #### 5.2 Add SMS payload interface
300
+ **File:** `lib/interface/payload.inteface.ts` (UPDATE)
301
+
302
+ ```typescript
303
+ export interface SmsNotificationPayload {
304
+ phoneNumber: string;
305
+ templateCode: string;
306
+ options?: {
307
+ locale?: string;
308
+ fallbackLocale?: string;
309
+ args?: any;
310
+ };
311
+ countryIso2?: string;
312
+ dialCode?: string;
313
+ }
314
+ ```
315
+
316
+ #### 5.3 Add SMS options to `NotificationModule`
317
+ **File:** `lib/notification/notification.module.ts` (UPDATE)
318
+
319
+ ```typescript
320
+ export interface NotificationModuleOptions {
321
+ // ... existing
322
+ smsService?: Type<SmsService>;
323
+ }
324
+ ```
325
+
326
+ #### 5.4 Export new types
327
+ **File:** `lib/sms/index.ts` (UPDATE)
328
+ **File:** `lib/interface/index.ts` (UPDATE)
329
+
330
+ ### Unit Tests
331
+
332
+ **File:** `test/notification/notification-sms.service.spec.ts` (NEW)
333
+ - `sendSms()` with configured SMS service → resolves template, calls `smsService.send()`
334
+ - `sendSms()` without SMS service + `strict: true` → throws
335
+ - `sendSms()` without SMS service + `strict: false` → returns `{ success: false }`
336
+ - `send({ channels: ['sms'] })` routes to `sendSms()`
337
+
338
+ ### E2E Tests
339
+
340
+ **File:** `test/module/notification-sms.module.e2e.spec.ts` (NEW)
341
+ - Register module with a mock `SmsService` implementation → `sendSms()` works
342
+
343
+ ---
344
+
345
+ ## Phase 6: Interceptors / Hooks (beforeSend, afterSend)
346
+
347
+ ### Problem
348
+ No way to globally intercept notifications for logging, auditing, or message modification.
349
+
350
+ ### Changes
351
+
352
+ #### 6.1 Create `NotificationInterceptor` interface
353
+ **File:** `lib/notification/notification.interceptor.ts` (NEW)
354
+
355
+ ```typescript
356
+ export interface NotificationContext {
357
+ channel: NotificationChannel;
358
+ templateCode: string;
359
+ recipient: string;
360
+ resolvedMessage?: { subject?: string; content: string };
361
+ }
362
+
363
+ export interface NotificationInterceptor {
364
+ beforeSend?(context: NotificationContext): Promise<NotificationContext> | NotificationContext;
365
+ afterSend?(context: NotificationContext, result: SendResult): Promise<void> | void;
366
+ onError?(context: NotificationContext, error: Error): Promise<void> | void;
367
+ }
368
+
369
+ export const NOTIFICATION_INTERCEPTORS = 'NOTIFICATION_INTERCEPTORS';
370
+ ```
371
+
372
+ #### 6.2 Update `NotificationService` to run interceptors
373
+ **File:** `lib/notification/notification.service.ts` (UPDATE)
374
+
375
+ - Inject `@Optional() @Inject(NOTIFICATION_INTERCEPTORS) interceptors: NotificationInterceptor[]`
376
+ - Before each send: run all `beforeSend` hooks (allows modifying context)
377
+ - After each send: run all `afterSend` hooks
378
+ - On error: run all `onError` hooks
379
+
380
+ #### 6.3 Add `interceptors` option to module
381
+ **File:** `lib/notification/notification.module.ts` (UPDATE)
382
+
383
+ ```typescript
384
+ export interface NotificationModuleOptions {
385
+ // ... existing
386
+ interceptors?: Type<NotificationInterceptor>[];
387
+ }
388
+ ```
389
+
390
+ ### Unit Tests
391
+
392
+ **File:** `test/notification/notification-interceptor.spec.ts` (NEW)
393
+ - `beforeSend` is called before provider send
394
+ - `beforeSend` can modify context (e.g., append unsubscribe link)
395
+ - `afterSend` is called after successful send with result
396
+ - `onError` is called when send fails
397
+ - Multiple interceptors run in order
398
+ - No interceptors registered → sends normally (no errors)
399
+
400
+ ### E2E Tests
401
+
402
+ **File:** `test/module/notification-interceptor.module.e2e.spec.ts` (NEW)
403
+ - Register module with a logging interceptor → verify hooks called during `sendEmail()`
404
+
405
+ ---
406
+
407
+ ## Implementation Order
408
+
409
+ 1. **Phase 1** (Template Engine) — Foundation, no breaking changes
410
+ 2. **Phase 2** (Strict Mode) — Defaults change but existing `strict: false` opt-out available
411
+ 3. **Phase 4** (Platform Alias) — Quick fix, no breaking changes
412
+ 4. **Phase 3** (Unified Send) — New method, additive
413
+ 5. **Phase 5** (SMS Integration) — Additive
414
+ 6. **Phase 6** (Interceptors) — Additive
415
+
416
+ ## Breaking Changes
417
+
418
+ **None.** All changes are additive or have backwards-compatible defaults:
419
+ - `strict` defaults to `true` which IS a behaviour change, but it only surfaces existing bugs (calling unconfigured services). Can be set to `false` to restore old behaviour.
420
+ - `paltform` is deprecated but still works.
421
+ - `string-format` remains the default template engine.
422
+
423
+ ## Version
424
+
425
+ This should be released as **v3.0.0** due to the `strict: true` default change (even though technically backwards compat, it changes runtime behaviour).
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- export * from "./email";
1
+ export { EMAIL_OPTIONS, EMAIL_PROVIDER, EMAIL_SERVICE, EmailOption, SmtpOption, SmtpHostOption, SmtpUriOption, CustomEmailOption, EmailService, EmailProvider, SmtpEmailProvider, EmailProviderFactory, } from "./email";
2
+ export { PushNotificationService, FCMPushNotificationOption, PushNotificationOption, FCMPushNotificationProvider, PushNotificationProvider, PushNotificationPlatform, PlatformOS, ProviderManager, PUSH_NOTIFICATION_PROVIDERS, DefaultProviderManager, DeviceToken, TokenId, IUserTokenStore, UserTokenStore, InMemoryUserTokenStore, } from "./push-notification";
2
3
  export * from "./interface";
3
4
  export * from "./notification";
4
- export * from "./push-notification";
5
5
  export * from "./sms";
6
6
  export * from "./template";
7
+ export * from "./whatsapp";
package/dist/index.js CHANGED
@@ -14,10 +14,28 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./email"), exports);
17
+ exports.InMemoryUserTokenStore = exports.UserTokenStore = exports.DefaultProviderManager = exports.PUSH_NOTIFICATION_PROVIDERS = exports.ProviderManager = exports.PlatformOS = exports.PushNotificationProvider = exports.FCMPushNotificationProvider = exports.PushNotificationService = exports.EmailProviderFactory = exports.SmtpEmailProvider = exports.EmailProvider = exports.EmailService = exports.EMAIL_SERVICE = exports.EMAIL_PROVIDER = exports.EMAIL_OPTIONS = void 0;
18
+ var email_1 = require("./email");
19
+ Object.defineProperty(exports, "EMAIL_OPTIONS", { enumerable: true, get: function () { return email_1.EMAIL_OPTIONS; } });
20
+ Object.defineProperty(exports, "EMAIL_PROVIDER", { enumerable: true, get: function () { return email_1.EMAIL_PROVIDER; } });
21
+ Object.defineProperty(exports, "EMAIL_SERVICE", { enumerable: true, get: function () { return email_1.EMAIL_SERVICE; } });
22
+ Object.defineProperty(exports, "EmailService", { enumerable: true, get: function () { return email_1.EmailService; } });
23
+ Object.defineProperty(exports, "EmailProvider", { enumerable: true, get: function () { return email_1.EmailProvider; } });
24
+ Object.defineProperty(exports, "SmtpEmailProvider", { enumerable: true, get: function () { return email_1.SmtpEmailProvider; } });
25
+ Object.defineProperty(exports, "EmailProviderFactory", { enumerable: true, get: function () { return email_1.EmailProviderFactory; } });
26
+ var push_notification_1 = require("./push-notification");
27
+ Object.defineProperty(exports, "PushNotificationService", { enumerable: true, get: function () { return push_notification_1.PushNotificationService; } });
28
+ Object.defineProperty(exports, "FCMPushNotificationProvider", { enumerable: true, get: function () { return push_notification_1.FCMPushNotificationProvider; } });
29
+ Object.defineProperty(exports, "PushNotificationProvider", { enumerable: true, get: function () { return push_notification_1.PushNotificationProvider; } });
30
+ Object.defineProperty(exports, "PlatformOS", { enumerable: true, get: function () { return push_notification_1.PlatformOS; } });
31
+ Object.defineProperty(exports, "ProviderManager", { enumerable: true, get: function () { return push_notification_1.ProviderManager; } });
32
+ Object.defineProperty(exports, "PUSH_NOTIFICATION_PROVIDERS", { enumerable: true, get: function () { return push_notification_1.PUSH_NOTIFICATION_PROVIDERS; } });
33
+ Object.defineProperty(exports, "DefaultProviderManager", { enumerable: true, get: function () { return push_notification_1.DefaultProviderManager; } });
34
+ Object.defineProperty(exports, "UserTokenStore", { enumerable: true, get: function () { return push_notification_1.UserTokenStore; } });
35
+ Object.defineProperty(exports, "InMemoryUserTokenStore", { enumerable: true, get: function () { return push_notification_1.InMemoryUserTokenStore; } });
18
36
  __exportStar(require("./interface"), exports);
19
37
  __exportStar(require("./notification"), exports);
20
- __exportStar(require("./push-notification"), exports);
21
38
  __exportStar(require("./sms"), exports);
22
39
  __exportStar(require("./template"), exports);
40
+ __exportStar(require("./whatsapp"), exports);
23
41
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,8CAA4B;AAC5B,iDAA+B;AAC/B,sDAAoC;AACpC,wCAAsB;AACtB,6CAA2B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AACA,iCAaiB;AAZf,sGAAA,aAAa,OAAA;AACb,uGAAA,cAAc,OAAA;AACd,sGAAA,aAAa,OAAA;AAMb,qGAAA,YAAY,OAAA;AACZ,sGAAA,aAAa,OAAA;AACb,0GAAA,iBAAiB,OAAA;AACjB,6GAAA,oBAAoB,OAAA;AAItB,yDAgB6B;AAf3B,4HAAA,uBAAuB,OAAA;AAGvB,gIAAA,2BAA2B,OAAA;AAC3B,6HAAA,wBAAwB,OAAA;AAExB,+GAAA,UAAU,OAAA;AACV,oHAAA,eAAe,OAAA;AACf,gIAAA,2BAA2B,OAAA;AAC3B,2HAAA,sBAAsB,OAAA;AAItB,mHAAA,cAAc,OAAA;AACd,2HAAA,sBAAsB,OAAA;AAIxB,8CAA4B;AAC5B,iDAA+B;AAC/B,wCAAsB;AACtB,6CAA2B;AAC3B,6CAA2B"}
@@ -1,4 +1,4 @@
1
- import { BaseMessage, SmsMessage, EmailMessage, PushMessage, Message, PushMessageOnly, EmailAttachment } from "./message.interface";
2
- import { EmailNotificationPayload, PushNotificationPayload } from "./payload.inteface";
1
+ import { BaseMessage, SmsMessage, EmailMessage, PushMessage, WhatsAppMessage, Message, PushMessageOnly, EmailAttachment } from "./message.interface";
2
+ import { EmailNotificationPayload, PushNotificationPayload, SmsNotificationPayload, WhatsAppNotificationPayload, NotificationPayload, NotificationResult, NotificationChannel } from "./payload.inteface";
3
3
  import { BatchSendResult, SendResult } from "./result.interface";
4
- export { BaseMessage, SmsMessage, EmailMessage, PushMessage, PushMessageOnly, Message, SendResult, BatchSendResult, EmailAttachment, EmailNotificationPayload, PushNotificationPayload, };
4
+ export { BaseMessage, SmsMessage, EmailMessage, PushMessage, WhatsAppMessage, PushMessageOnly, Message, SendResult, BatchSendResult, EmailAttachment, EmailNotificationPayload, PushNotificationPayload, SmsNotificationPayload, WhatsAppNotificationPayload, NotificationPayload, NotificationResult, NotificationChannel, };
@@ -19,10 +19,18 @@ export interface EmailMessage extends BaseMessage {
19
19
  }
20
20
  export interface PushMessage extends BaseMessage {
21
21
  paltform?: PlatformOS;
22
+ platform?: PlatformOS;
22
23
  subject: string;
23
24
  data?: {
24
25
  [key: string]: string;
25
26
  };
26
27
  }
27
- export type PushMessageOnly = Omit<PushMessage, "recipient" | "paltform">;
28
- export type Message = SmsMessage | EmailMessage | PushMessage;
28
+ export interface WhatsAppMessage extends BaseMessage {
29
+ templateName?: string;
30
+ languageCode?: string;
31
+ headerParams?: string[];
32
+ bodyParams?: string[];
33
+ mediaUrl?: string;
34
+ }
35
+ export type PushMessageOnly = Omit<PushMessage, "recipient" | "paltform" | "platform">;
36
+ export type Message = SmsMessage | EmailMessage | PushMessage | WhatsAppMessage;
@@ -20,3 +20,54 @@ export interface PushNotificationPayload {
20
20
  args?: any;
21
21
  };
22
22
  }
23
+ export type NotificationChannel = 'email' | 'push' | 'sms' | 'whatsapp';
24
+ export interface NotificationPayload {
25
+ templateCode: string;
26
+ channels: NotificationChannel[];
27
+ fallback?: NotificationChannel[];
28
+ options?: {
29
+ locale?: string;
30
+ fallbackLocale?: string;
31
+ args?: any;
32
+ };
33
+ email?: string;
34
+ userId?: string | number;
35
+ deviceToken?: DeviceToken;
36
+ phoneNumber?: string;
37
+ whatsappTemplateName?: string;
38
+ mediaUrl?: string;
39
+ attachments?: EmailAttachment[];
40
+ }
41
+ export interface SendResult {
42
+ success: boolean;
43
+ message?: string;
44
+ error?: any;
45
+ }
46
+ export interface SmsNotificationPayload {
47
+ phoneNumber: string;
48
+ templateCode: string;
49
+ options?: {
50
+ locale?: string;
51
+ fallbackLocale?: string;
52
+ args?: any;
53
+ };
54
+ countryIso2?: string;
55
+ dialCode?: string;
56
+ }
57
+ export interface WhatsAppNotificationPayload {
58
+ phoneNumber: string;
59
+ templateCode: string;
60
+ options?: {
61
+ locale?: string;
62
+ fallbackLocale?: string;
63
+ args?: any;
64
+ };
65
+ whatsappTemplateName?: string;
66
+ mediaUrl?: string;
67
+ }
68
+ export interface NotificationResult {
69
+ results: Partial<Record<NotificationChannel, SendResult>>;
70
+ success: boolean;
71
+ fallbackAttempted?: NotificationChannel[];
72
+ fallbackSucceeded?: NotificationChannel;
73
+ }
@@ -0,0 +1,3 @@
1
+ export declare class NotificationConfigError extends Error {
2
+ constructor(service: string);
3
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NotificationConfigError = void 0;
4
+ class NotificationConfigError extends Error {
5
+ constructor(service) {
6
+ super(`${service} is not configured. Configure it in NotificationModule or set strict: false.`);
7
+ this.name = 'NotificationConfigError';
8
+ }
9
+ }
10
+ exports.NotificationConfigError = NotificationConfigError;
11
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../lib/notification/errors.ts"],"names":[],"mappings":";;;AAAA,MAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,OAAe;QACzB,KAAK,CAAC,GAAG,OAAO,8EAA8E,CAAC,CAAC;QAChG,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF;AALD,0DAKC"}
@@ -1,4 +1,8 @@
1
1
  import { NOTIFICATION_OPTIONS, NOTIFICATION_SERVICE, TEMPLATE_SERVICE } from "./constant";
2
- import { NotificationModule, NotificationModuleOptions } from "./notification.module";
2
+ import { NotificationModule, NotificationModuleAsyncOptions, NotificationModuleOptions, NotificationModuleOptionsFactory } from "./notification.module";
3
3
  import { NotificationService } from "./notification.service";
4
- export { NOTIFICATION_OPTIONS, NOTIFICATION_SERVICE, TEMPLATE_SERVICE, NotificationModule, NotificationModuleOptions, NotificationService, };
4
+ import { NotificationConfigError } from "./errors";
5
+ import { NotificationInterceptor, NotificationContext, NOTIFICATION_INTERCEPTORS } from "./notification.interceptor";
6
+ import { ProviderConfig, ChannelProviderMap } from "./provider-config";
7
+ import { NotificationProviderFactory } from "./notification-provider.factory";
8
+ export { NOTIFICATION_OPTIONS, NOTIFICATION_SERVICE, TEMPLATE_SERVICE, NotificationModule, NotificationModuleOptions, NotificationModuleAsyncOptions, NotificationModuleOptionsFactory, NotificationService, NotificationConfigError, NotificationInterceptor, NotificationContext, NOTIFICATION_INTERCEPTORS, ProviderConfig, ChannelProviderMap, NotificationProviderFactory, };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NotificationService = exports.NotificationModule = exports.TEMPLATE_SERVICE = exports.NOTIFICATION_SERVICE = exports.NOTIFICATION_OPTIONS = void 0;
3
+ exports.NotificationProviderFactory = exports.NOTIFICATION_INTERCEPTORS = exports.NotificationConfigError = exports.NotificationService = exports.NotificationModule = exports.TEMPLATE_SERVICE = exports.NOTIFICATION_SERVICE = exports.NOTIFICATION_OPTIONS = void 0;
4
4
  const constant_1 = require("./constant");
5
5
  Object.defineProperty(exports, "NOTIFICATION_OPTIONS", { enumerable: true, get: function () { return constant_1.NOTIFICATION_OPTIONS; } });
6
6
  Object.defineProperty(exports, "NOTIFICATION_SERVICE", { enumerable: true, get: function () { return constant_1.NOTIFICATION_SERVICE; } });
@@ -9,4 +9,10 @@ const notification_module_1 = require("./notification.module");
9
9
  Object.defineProperty(exports, "NotificationModule", { enumerable: true, get: function () { return notification_module_1.NotificationModule; } });
10
10
  const notification_service_1 = require("./notification.service");
11
11
  Object.defineProperty(exports, "NotificationService", { enumerable: true, get: function () { return notification_service_1.NotificationService; } });
12
+ const errors_1 = require("./errors");
13
+ Object.defineProperty(exports, "NotificationConfigError", { enumerable: true, get: function () { return errors_1.NotificationConfigError; } });
14
+ const notification_interceptor_1 = require("./notification.interceptor");
15
+ Object.defineProperty(exports, "NOTIFICATION_INTERCEPTORS", { enumerable: true, get: function () { return notification_interceptor_1.NOTIFICATION_INTERCEPTORS; } });
16
+ const notification_provider_factory_1 = require("./notification-provider.factory");
17
+ Object.defineProperty(exports, "NotificationProviderFactory", { enumerable: true, get: function () { return notification_provider_factory_1.NotificationProviderFactory; } });
12
18
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/notification/index.ts"],"names":[],"mappings":";;;AAAA,yCAA0F;AAKxF,qGALO,+BAAoB,OAKP;AACpB,qGAN6B,+BAAoB,OAM7B;AACpB,iGAPmD,2BAAgB,OAOnD;AANlB,+DAAwJ;AAOtJ,mGAPO,wCAAkB,OAOP;AANpB,iEAA6D;AAU3D,oGAVO,0CAAmB,OAUP"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/notification/index.ts"],"names":[],"mappings":";;;AAAA,yCAA0F;AASxF,qGATO,+BAAoB,OASP;AACpB,qGAV6B,+BAAoB,OAU7B;AACpB,iGAXmD,2BAAgB,OAWnD;AAVlB,+DAAwJ;AAWtJ,mGAXO,wCAAkB,OAWP;AAVpB,iEAA6D;AAc3D,oGAdO,0CAAmB,OAcP;AAbrB,qCAAmD;AAcjD,wGAdO,gCAAuB,OAcP;AAbzB,yEAAqH;AAgBnH,0GAhBqD,oDAAyB,OAgBrD;AAd3B,mFAA8E;AAiB5E,4GAjBO,2DAA2B,OAiBP"}
@@ -0,0 +1,5 @@
1
+ import { ProviderConfig, ChannelProviderMap } from './provider-config';
2
+ export declare class NotificationProviderFactory {
3
+ private static readonly logger;
4
+ static create<C extends keyof ChannelProviderMap>(config: ProviderConfig<C>): ChannelProviderMap[C];
5
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NotificationProviderFactory = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ const email_1 = require("../email");
6
+ const push_notification_1 = require("../push-notification");
7
+ const sms_1 = require("../sms");
8
+ const whatsapp_1 = require("../whatsapp");
9
+ const CHANNEL_BASE_CLASSES = {
10
+ email: email_1.EmailProvider,
11
+ push: push_notification_1.PushNotificationProvider,
12
+ sms: sms_1.SmsService,
13
+ whatsapp: whatsapp_1.WhatsAppService,
14
+ };
15
+ class NotificationProviderFactory {
16
+ static create(config) {
17
+ const { channel, provider, options } = config;
18
+ const baseClass = CHANNEL_BASE_CLASSES[channel];
19
+ if (!baseClass) {
20
+ throw new Error(`Unknown notification channel: ${channel}`);
21
+ }
22
+ const instance = new provider(options);
23
+ if (!(instance instanceof baseClass)) {
24
+ throw new Error(`${provider.name} is not a valid ${channel} provider. ` +
25
+ `Expected ${baseClass.name} subclass.`);
26
+ }
27
+ this.logger.log(`Created ${channel} provider: ${provider.name}`);
28
+ return instance;
29
+ }
30
+ }
31
+ exports.NotificationProviderFactory = NotificationProviderFactory;
32
+ NotificationProviderFactory.logger = new common_1.Logger(NotificationProviderFactory.name);
33
+ //# sourceMappingURL=notification-provider.factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-provider.factory.js","sourceRoot":"","sources":["../../lib/notification/notification-provider.factory.ts"],"names":[],"mappings":";;;AAAA,2CAAwC;AAExC,oCAAyC;AACzC,4DAAgE;AAChE,gCAAoC;AACpC,0CAA8C;AAK9C,MAAM,oBAAoB,GAA6B;IACrD,KAAK,EAAE,qBAAa;IACpB,IAAI,EAAE,4CAAwB;IAC9B,GAAG,EAAE,gBAAU;IACf,QAAQ,EAAE,0BAAe;CAC1B,CAAC;AAKF,MAAa,2BAA2B;IAoBtC,MAAM,CAAC,MAAM,CACX,MAAyB;QAEzB,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;QAC9C,MAAM,SAAS,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;QAEhD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,iCAAiC,OAAO,EAAE,CAAC,CAAC;QAC9D,CAAC;QAGD,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;QAGvC,IAAI,CAAC,CAAC,QAAQ,YAAY,SAAS,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CACb,GAAG,QAAQ,CAAC,IAAI,mBAAmB,OAAO,aAAa;gBACvD,YAAY,SAAS,CAAC,IAAI,YAAY,CACvC,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,OAAO,cAAc,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;QACjE,OAAO,QAAiC,CAAC;IAC3C,CAAC;;AA3CH,kEA4CC;AA3CyB,kCAAM,GAAG,IAAI,eAAM,CAAC,2BAA2B,CAAC,IAAI,CAAC,CAAC"}