@nathapp/nestjs-notification 2.2.0 → 3.0.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 (92) hide show
  1. package/CLAUDE.md +21 -0
  2. package/dist/email/email.interface.d.ts +1 -1
  3. package/dist/email/email.provider.d.ts +2 -2
  4. package/dist/email/email.provider.js +7 -5
  5. package/dist/email/email.provider.js.map +1 -1
  6. package/dist/email/index.d.ts +1 -3
  7. package/dist/email/index.js +1 -6
  8. package/dist/email/index.js.map +1 -1
  9. package/dist/index.d.ts +4 -4
  10. package/dist/index.js +9 -8
  11. package/dist/index.js.map +1 -1
  12. package/dist/interface/index.d.ts +1 -1
  13. package/dist/interface/message.interface.d.ts +1 -2
  14. package/dist/interface/{payload.inteface.d.ts → payload.interface.d.ts} +3 -5
  15. package/dist/interface/{payload.inteface.js → payload.interface.js} +1 -1
  16. package/dist/interface/payload.interface.js.map +1 -0
  17. package/dist/interface/result.interface.d.ts +2 -1
  18. package/dist/notification/notification-provider.factory.js +2 -2
  19. package/dist/notification/notification-provider.factory.js.map +1 -1
  20. package/dist/notification/notification.module.d.ts +1 -1
  21. package/dist/notification/notification.module.js +33 -21
  22. package/dist/notification/notification.module.js.map +1 -1
  23. package/dist/notification/notification.service.js +5 -5
  24. package/dist/notification/notification.service.js.map +1 -1
  25. package/dist/notification/provider-config.d.ts +4 -4
  26. package/dist/push-notification/fcm-push-notification.provider.d.ts +3 -0
  27. package/dist/push-notification/fcm-push-notification.provider.js +17 -2
  28. package/dist/push-notification/fcm-push-notification.provider.js.map +1 -1
  29. package/dist/push-notification/firebase.interface.d.ts +1 -0
  30. package/dist/push-notification/index.d.ts +1 -4
  31. package/dist/push-notification/index.js +1 -5
  32. package/dist/push-notification/index.js.map +1 -1
  33. package/dist/push-notification/push-notification-provider.manager.d.ts +8 -3
  34. package/dist/push-notification/push-notification-provider.manager.js +12 -3
  35. package/dist/push-notification/push-notification-provider.manager.js.map +1 -1
  36. package/dist/push-notification/push-notification.provider.d.ts +1 -0
  37. package/dist/push-notification/push-notification.provider.js.map +1 -1
  38. package/dist/push-notification/push-notification.service.js +1 -1
  39. package/dist/push-notification/push-notification.service.js.map +1 -1
  40. package/dist/push-notification/user-token.store.js +7 -7
  41. package/dist/push-notification/user-token.store.js.map +1 -1
  42. package/dist/sms/constant.d.ts +1 -0
  43. package/dist/sms/constant.js +5 -0
  44. package/dist/sms/constant.js.map +1 -0
  45. package/dist/sms/index.d.ts +3 -2
  46. package/dist/sms/index.js +6 -2
  47. package/dist/sms/index.js.map +1 -1
  48. package/dist/sms/sms.provider.d.ts +9 -0
  49. package/dist/sms/sms.provider.js +7 -0
  50. package/dist/sms/sms.provider.js.map +1 -0
  51. package/dist/sms/sms.service.d.ts +8 -7
  52. package/dist/sms/sms.service.js +30 -2
  53. package/dist/sms/sms.service.js.map +1 -1
  54. package/dist/template/fake-template.loader.js +14 -18
  55. package/dist/template/fake-template.loader.js.map +1 -1
  56. package/dist/whatsapp/constant.d.ts +1 -0
  57. package/dist/whatsapp/constant.js +2 -1
  58. package/dist/whatsapp/constant.js.map +1 -1
  59. package/dist/whatsapp/index.d.ts +3 -2
  60. package/dist/whatsapp/index.js +8 -16
  61. package/dist/whatsapp/index.js.map +1 -1
  62. package/dist/whatsapp/whatsapp.provider.d.ts +9 -0
  63. package/dist/whatsapp/whatsapp.provider.js +7 -0
  64. package/dist/whatsapp/whatsapp.provider.js.map +1 -0
  65. package/dist/whatsapp/whatsapp.service.d.ts +7 -5
  66. package/dist/whatsapp/whatsapp.service.js +30 -2
  67. package/dist/whatsapp/whatsapp.service.js.map +1 -1
  68. package/docs/20260214-fix-plan-notification.md +149 -0
  69. package/docs/20260214-fix-plan-post-refactor.md +118 -0
  70. package/docs/20260214-nestjs-notification-review.md +390 -0
  71. package/docs/20260214-review-post-refactor.md +285 -0
  72. package/docs/20260215-review-notification-post-fix.md +933 -0
  73. package/package.json +1 -1
  74. package/dist/email/email-provider.factory.d.ts +0 -5
  75. package/dist/email/email-provider.factory.js +0 -18
  76. package/dist/email/email-provider.factory.js.map +0 -1
  77. package/dist/email/email.module.d.ts +0 -27
  78. package/dist/email/email.module.js +0 -110
  79. package/dist/email/email.module.js.map +0 -1
  80. package/dist/interface/payload.inteface.js.map +0 -1
  81. package/dist/push-notification/push-notification-module.interface.d.ts +0 -33
  82. package/dist/push-notification/push-notification-module.interface.js +0 -4
  83. package/dist/push-notification/push-notification-module.interface.js.map +0 -1
  84. package/dist/push-notification/push-notification-provider-module.factory.d.ts +0 -5
  85. package/dist/push-notification/push-notification-provider-module.factory.js +0 -13
  86. package/dist/push-notification/push-notification-provider-module.factory.js.map +0 -1
  87. package/dist/push-notification/push-notification-provider.factory.d.ts +0 -5
  88. package/dist/push-notification/push-notification-provider.factory.js +0 -16
  89. package/dist/push-notification/push-notification-provider.factory.js.map +0 -1
  90. package/dist/push-notification/push-notification.module.d.ts +0 -15
  91. package/dist/push-notification/push-notification.module.js +0 -189
  92. package/dist/push-notification/push-notification.module.js.map +0 -1
@@ -0,0 +1,285 @@
1
+ # Post-Refactor Code Review: @nathapp/nestjs-notification
2
+
3
+ **Date:** 2026-02-14
4
+ **Branch:** `feat/notification-enterprise-upgrade`
5
+ **Purpose:** Ensure the refactored package is clean and consistent before redesigning external provider packages.
6
+
7
+ ---
8
+
9
+ ## 1. Provider/Service Consistency
10
+
11
+ ### Current Provider Contracts
12
+
13
+ | Channel | Abstract Class | Methods | Extra Requirements |
14
+ |---------|---------------|---------|-------------------|
15
+ | Email | `EmailProvider` | `send(EmailMessage)`, `get isSupportAttachments` | ⚠️ Extra getter |
16
+ | Push | `PushNotificationProvider` | `send(PushMessage)`, `sendMulticast(...)` | ⚠️ `readonly type: PushNotificationPlatform` + extra method |
17
+ | SMS | `SmsProvider` | `send(SmsMessage)` | ✅ Clean |
18
+ | WhatsApp | `WhatsAppProvider` | `send(WhatsAppMessage)` | ✅ Clean |
19
+
20
+ ### 🟡 M1: EmailProvider Has Extra `isSupportAttachments` Getter
21
+
22
+ ```typescript
23
+ // EmailProvider
24
+ abstract send(message: EmailMessage): Promise<SendResult>;
25
+ abstract get isSupportAttachments(): boolean; // ← extra requirement
26
+ ```
27
+
28
+ SMS, WhatsApp, and Push don't have capability-query methods. An external dev writing `BrevoEmailProvider` must implement this extra getter, which is inconsistent.
29
+
30
+ **Recommendation:** Either remove `isSupportAttachments` (most modern email providers support attachments) or add it to the interface doc as an explicit email-only requirement.
31
+
32
+ ### 🟡 M2: PushNotificationProvider Has Extra `type` Property and `sendMulticast`
33
+
34
+ ```typescript
35
+ // PushNotificationProvider
36
+ abstract readonly type: PushNotificationPlatform; // ← extra
37
+ abstract send(message: PushMessage): Promise<SendResult>;
38
+ abstract sendMulticast(tokens: string[], message: PushMessageOnly): Promise<BatchSendResult>; // ← extra method
39
+ ```
40
+
41
+ This is justified (push has android/ios/huawei platforms and multicast), but it makes push the most complex channel to implement. Should be documented clearly.
42
+
43
+ ### 🟢 L1: Interface Naming Inconsistency
44
+
45
+ - Email: `IEmailProvider`
46
+ - SMS: `ISmsProvider`
47
+ - WhatsApp: `IWhatsAppProvider`
48
+ - Push: `IPushNotificationProvider`
49
+
50
+ All fine, but `EmailProvider` also has `SmtpEmailProvider` in the same file — a concrete built-in impl mixed with the abstract. SMS/WhatsApp keep them separate (no built-in impl in core).
51
+
52
+ ---
53
+
54
+ ## 2. Public API Surface
55
+
56
+ ### What External Devs Need to Import
57
+
58
+ | Channel | Import | Extend | Implement |
59
+ |---------|--------|--------|-----------|
60
+ | Email | `EmailProvider`, `EmailMessage`, `SendResult` | `EmailProvider` | `send()`, `isSupportAttachments` |
61
+ | Push | `PushNotificationProvider`, `PushMessage`, `SendResult`, `BatchSendResult`, `PushNotificationPlatform` | `PushNotificationProvider` | `send()`, `sendMulticast()`, `type` |
62
+ | SMS | `SmsProvider`, `SmsMessage`, `SendResult` | `SmsProvider` | `send()` |
63
+ | WhatsApp | `WhatsAppProvider`, `WhatsAppMessage`, `SendResult` | `WhatsAppProvider` | `send()` |
64
+
65
+ ### 🟡 M3: Unnecessary Exports Leaking Internals
66
+
67
+ The barrel `lib/index.ts` exports internal implementation details that external devs shouldn't depend on:
68
+
69
+ - `EMAIL_OPTIONS`, `EMAIL_SERVICE` — internal DI tokens from old `EmailModule`
70
+ - `EmailOption`, `SmtpOption`, `SmtpHostOption`, `SmtpUriOption`, `CustomEmailOption` — old config types
71
+ - `EmailProviderFactory` — superseded by `NotificationProviderFactory`
72
+ - `FCMPushNotificationOption`, `PushNotificationOption` — FCM-specific config
73
+ - `PushNotificationProviderFactory` (via `push-notification/index.ts`) — old factory
74
+ - `DefaultProviderManager`, `InMemoryUserTokenStore` — internal implementations
75
+ - `WHATSAPP_SERVICE` — unused constant
76
+
77
+ **Risk:** If we change any of these internals, external packages importing them will break.
78
+
79
+ **Recommendation:** Create a clean public API surface. Only export what external devs actually need:
80
+ - Abstract providers (`EmailProvider`, `SmsProvider`, `WhatsAppProvider`, `PushNotificationProvider`)
81
+ - Message types (`EmailMessage`, `SmsMessage`, `WhatsAppMessage`, `PushMessage`)
82
+ - Result types (`SendResult`, `BatchSendResult`)
83
+ - `NotificationModule` and its option interfaces
84
+ - `ProviderConfig` for registration
85
+
86
+ ---
87
+
88
+ ## 3. Breaking Changes for Existing Consumers
89
+
90
+ | Change | Impact | Migration |
91
+ |--------|--------|-----------|
92
+ | `SmsService` no longer abstract | Packages extending `SmsService` break | Extend `SmsProvider` instead |
93
+ | `WhatsAppService` no longer abstract | Packages extending `WhatsAppService` break | Extend `WhatsAppProvider` instead |
94
+ | New injection tokens `SMS_PROVIDER`, `WHATSAPP_PROVIDER` | Code using `@Inject(SmsService)` still works | No change needed |
95
+ | `strict: true` by default | Unconfigured channels now **throw** instead of returning `{ success: false }` | Set `strict: false` to keep old behavior |
96
+
97
+ ---
98
+
99
+ ## 4. Registration Flow Trace
100
+
101
+ ```
102
+ NotificationModule.forRoot({
103
+ providers: [
104
+ { channel: 'email', provider: BrevoEmailProvider, options: { apiKey: '...' } }
105
+ ]
106
+ })
107
+ ↓
108
+ createChannelProviders(configs)
109
+ ↓
110
+ groupProvidersByChannel(configs) → { email: [...], push: [...], sms: [...], whatsapp: [...] }
111
+ ↓
112
+ NotificationProviderFactory.create(config)
113
+ → new config.provider(config.options) // creates instance
114
+ → instanceof check against EmailProvider // validates
115
+ ↓
116
+ Register as { provide: EMAIL_PROVIDER, useValue: instance }
117
+ Register as { provide: EmailService, useFactory: (p) => new EmailService(p), inject: [EMAIL_PROVIDER] }
118
+ ↓
119
+ NotificationService injects @Optional() EmailService
120
+ ```
121
+
122
+ ### 🟢 L2: `forRootAsync` — Providers Created in Factory, Not at Module Init
123
+
124
+ In `forRootAsync`, channel providers are created inside `useFactory` callbacks that depend on `NOTIFICATION_OPTIONS`. This is correct — options are resolved first, then providers created. No race condition.
125
+
126
+ ### 🟢 L3: Null Providers with `@Optional()`
127
+
128
+ When a channel is not configured, its service is `null`. `NotificationService` handles this with null checks before each `send*` call. Clean.
129
+
130
+ ---
131
+
132
+ ## 5. Type Safety: `ProviderConfig.options: any`
133
+
134
+ ### 🟡 M4: No Type Constraint on Provider Options
135
+
136
+ ```typescript
137
+ export interface ProviderConfig<C extends NotificationChannel = NotificationChannel> {
138
+ channel: C;
139
+ provider: Type<ChannelProviderMap[C]>;
140
+ options?: any; // ← no type safety
141
+ }
142
+ ```
143
+
144
+ A developer can pass `{ channel: 'email', provider: BrevoEmailProvider, options: 12345 }` and TypeScript won't complain. The error only surfaces at runtime in the provider constructor.
145
+
146
+ **Options to fix:**
147
+ 1. Generic options type: `ProviderConfig<C, O = any>` — but this makes registration verbose
148
+ 2. Provider-level static typing: each provider exports a helper `BrevoEmailProvider.config(opts): ProviderConfig` — cleanest for consumers
149
+ 3. Leave as `any` but document clearly — pragmatic choice
150
+
151
+ **Recommendation:** Option 3 for now (document), option 2 for external provider packages (each exports a typed helper).
152
+
153
+ ---
154
+
155
+ ## 6. Remaining Dead Code
156
+
157
+ ### 🟡 M5: Legacy Modules Still Present
158
+
159
+ | File | Status | Action |
160
+ |------|--------|--------|
161
+ | `email/email.module.ts` | Superseded by `NotificationModule` | Deprecate or remove |
162
+ | `email/email-provider.factory.ts` | Superseded by `NotificationProviderFactory` | Deprecate or remove |
163
+ | `push-notification/push-notification.module.ts` | Superseded | Deprecate or remove |
164
+ | `push-notification/push-notification-provider.factory.ts` | Superseded | Deprecate or remove |
165
+ | `push-notification/push-notification-provider-module.factory.ts` | Superseded | Remove |
166
+ | `push-notification/push-notification-module.interface.ts` | Superseded | Remove |
167
+
168
+ These add ~500 lines of dead code and confuse developers about which pattern to use.
169
+
170
+ **Recommendation:** Add `@deprecated` JSDoc to `EmailModule` and `PushNotificationModule`. Remove the internal factories that are no longer used by any code path.
171
+
172
+ ---
173
+
174
+ ## 7. External Provider Developer Guide
175
+
176
+ ### Minimum Requirements per Channel
177
+
178
+ #### Email Provider
179
+ ```typescript
180
+ import { EmailProvider, EmailMessage, SendResult } from '@nathapp/nestjs-notification';
181
+
182
+ export interface MyEmailOptions {
183
+ apiKey: string;
184
+ }
185
+
186
+ export class MyEmailProvider extends EmailProvider {
187
+ constructor(private readonly options: MyEmailOptions) {
188
+ super();
189
+ }
190
+
191
+ get isSupportAttachments(): boolean {
192
+ return true;
193
+ }
194
+
195
+ async send(message: EmailMessage): Promise<SendResult> {
196
+ // message.recipient, message.subject, message.message, message.attachments
197
+ return { success: true, requestId: '...' };
198
+ }
199
+ }
200
+ ```
201
+
202
+ #### SMS Provider
203
+ ```typescript
204
+ import { SmsProvider, SmsMessage, SendResult } from '@nathapp/nestjs-notification';
205
+
206
+ export class MySmsProvider extends SmsProvider {
207
+ constructor(private readonly options: any) { super(); }
208
+
209
+ async send(message: SmsMessage): Promise<SendResult> {
210
+ // message.recipient, message.message, message.countryIso2, message.dialCode
211
+ return { success: true };
212
+ }
213
+ }
214
+ ```
215
+
216
+ #### WhatsApp Provider
217
+ ```typescript
218
+ import { WhatsAppProvider, WhatsAppMessage, SendResult } from '@nathapp/nestjs-notification';
219
+
220
+ export class MyWhatsAppProvider extends WhatsAppProvider {
221
+ constructor(private readonly options: any) { super(); }
222
+
223
+ async send(message: WhatsAppMessage): Promise<SendResult> {
224
+ // message.recipient, message.message, message.templateName, message.bodyParams
225
+ return { success: true };
226
+ }
227
+ }
228
+ ```
229
+
230
+ #### Push Provider
231
+ ```typescript
232
+ import { PushNotificationProvider, PushMessage, PushMessageOnly, SendResult, BatchSendResult, PushNotificationPlatform } from '@nathapp/nestjs-notification';
233
+
234
+ export class MyPushProvider extends PushNotificationProvider {
235
+ readonly type: PushNotificationPlatform = 'firebase'; // or custom string
236
+
237
+ constructor(private readonly options: any) { super(); }
238
+
239
+ async send(message: PushMessage): Promise<SendResult> {
240
+ return { success: true };
241
+ }
242
+
243
+ async sendMulticast(tokens: string[], message: PushMessageOnly): Promise<BatchSendResult> {
244
+ return { successCount: tokens.length, failureCount: 0, responses: [] };
245
+ }
246
+ }
247
+ ```
248
+
249
+ ### Consumer Registration
250
+ ```typescript
251
+ import { NotificationModule } from '@nathapp/nestjs-notification';
252
+ import { BrevoEmailProvider } from '@nathapp/nestjs-notification-brevo';
253
+
254
+ @Module({
255
+ imports: [
256
+ NotificationModule.forRoot({
257
+ providers: [
258
+ { channel: 'email', provider: BrevoEmailProvider, options: { apiKey: '...' } },
259
+ ],
260
+ templateLoader: MyTemplateLoader,
261
+ }),
262
+ ],
263
+ })
264
+ export class AppModule {}
265
+ ```
266
+
267
+ ---
268
+
269
+ ## Summary
270
+
271
+ | Severity | Count | Key Items |
272
+ |----------|-------|-----------|
273
+ | 🔴 Critical | 0 | None — previous fixes resolved all criticals |
274
+ | 🟡 Medium | 5 | Provider inconsistencies (M1-M2), leaky exports (M3), untyped options (M4), dead code (M5) |
275
+ | 🟢 Low | 3 | Naming inconsistency (L1), async flow (L2-L3) |
276
+
277
+ ### Before Merging to Master
278
+ 1. Decide: remove `isSupportAttachments` from `EmailProvider` or keep as email-only requirement
279
+ 2. Decide: deprecate or remove legacy modules (`EmailModule`, `PushNotificationModule`)
280
+ 3. Clean up barrel exports to only expose public API
281
+
282
+ ### Before Redesigning External Providers
283
+ - The provider contract is clear and documented above
284
+ - Each external package: export provider class + options interface, peer dep on `@nathapp/nestjs-notification`
285
+ - Consider typed config helpers per provider package