@vynelix/vynemit-core 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -29,6 +29,10 @@ module.exports = __toCommonJS(src_exports);
29
29
 
30
30
  // src/notification_center.ts
31
31
  var NotificationCenter = class {
32
+ /**
33
+ * Initializes a new NotificationCenter with the provided configuration.
34
+ * @param config - The configuration options including adapters and transports.
35
+ */
32
36
  constructor(config) {
33
37
  this.config = config;
34
38
  this.storage = config.storage;
@@ -45,6 +49,13 @@ var NotificationCenter = class {
45
49
  this.isRunning = false;
46
50
  }
47
51
  // ========== DISPATCH ==========
52
+ /**
53
+ * Dispatches a single notification based on the provided input.
54
+ * If a queue is configured, the delivery takes place asynchronously.
55
+ *
56
+ * @param input - The notification payload and routing instructions.
57
+ * @returns A promise that resolves to the processed Notification object.
58
+ */
48
59
  async send(input) {
49
60
  let notification = this.buildNotification(input);
50
61
  notification = await this.applyBeforeSendMiddleware(notification);
@@ -74,12 +85,25 @@ var NotificationCenter = class {
74
85
  throw error;
75
86
  }
76
87
  }
88
+ /**
89
+ * Dispatches an array of notifications sequentially or concurrently.
90
+ *
91
+ * @param inputs - An array of notification inputs.
92
+ * @returns A promise resolving to an array of generated Notification objects.
93
+ */
77
94
  async sendBatch(inputs) {
78
95
  const notifications = await Promise.all(
79
96
  inputs.map((input) => this.send(input))
80
97
  );
81
98
  return notifications;
82
99
  }
100
+ /**
101
+ * Sends an identical notification to a list of users (multicast).
102
+ * Automatically optimizes push/sms transport delivery if transport supports bulk send.
103
+ *
104
+ * @param input - Multicast payload including the target `userIds`.
105
+ * @returns A promise resolving to the dispatched notifications.
106
+ */
83
107
  async sendMulticast(input) {
84
108
  const notifications = await Promise.all(input.userIds.map(async (userId) => {
85
109
  const notificationInput = { ...input, userId };
@@ -114,15 +138,56 @@ var NotificationCenter = class {
114
138
  await Promise.all(receipts.map((r) => this.storage.saveReceipt(r)));
115
139
  }
116
140
  } else {
117
- await Promise.all(validNotifications.map((n) => this.sendNow(n)));
141
+ await Promise.all(validNotifications.map(async (n) => {
142
+ const prefs = await this.storage.getPreferences(n.userId);
143
+ if (transport.canSend(n, prefs)) {
144
+ try {
145
+ const receipt = await transport.send(n, prefs);
146
+ if (this.storage.saveReceipt)
147
+ await this.storage.saveReceipt(receipt);
148
+ } catch (error) {
149
+ const receipt = {
150
+ notificationId: n.id,
151
+ channel,
152
+ status: "failed",
153
+ attempts: 1,
154
+ lastAttempt: /* @__PURE__ */ new Date(),
155
+ error: error.message
156
+ };
157
+ if (this.storage.saveReceipt)
158
+ await this.storage.saveReceipt(receipt);
159
+ }
160
+ }
161
+ }));
118
162
  }
119
163
  } catch (error) {
120
164
  console.error(`Multicast failed for channel ${channel}:`, error);
121
165
  await Promise.all(validNotifications.map((n) => this.applyErrorMiddleware(error, n)));
122
166
  }
123
167
  }
168
+ await Promise.all(validNotifications.map(async (notification) => {
169
+ notification.status = "sent";
170
+ await this.applyAfterSendMiddleware(notification);
171
+ if (notification.channels.includes("inapp") || channels.includes("inapp")) {
172
+ this.notifySubscribers(notification);
173
+ }
174
+ this.notifyEventSubscribers({
175
+ type: "sent",
176
+ notification,
177
+ timestamp: /* @__PURE__ */ new Date()
178
+ });
179
+ await this.storage.save(notification);
180
+ }));
124
181
  return validNotifications;
125
182
  }
183
+ /**
184
+ * Schedules a notification to be sent at a specific future date/time.
185
+ * Automatically enqueues it if the queue adapter supports delayed jobs.
186
+ *
187
+ * @param input - The notification input.
188
+ * @param when - The Date at which the notification should be delivered.
189
+ * @returns A promise resolving to the generated Notification ID.
190
+ */
126
191
  async schedule(input, when) {
127
192
  let notification = this.buildNotification({ ...input, scheduledFor: when });
128
193
  let _notification = await this.applyBeforeSendMiddleware(notification);
@@ -141,15 +206,40 @@ var NotificationCenter = class {
141
206
  return _notification.id;
142
207
  }
143
208
  // ========== QUERYING ==========
209
+ /**
210
+ * Retrieves notifications intended for a specific user ID.
211
+ *
212
+ * @param userId - The target user's ID.
213
+ * @param filters - Optional filters like status, type, limit, offset.
214
+ * @returns A promise resolving to the list of matched notifications.
215
+ */
144
216
  async getForUser(userId, filters) {
145
217
  return this.storage.findByUser(userId, filters);
146
218
  }
219
+ /**
220
+ * Retrieves the current number of unread notifications for a specified user.
221
+ *
222
+ * @param userId - The target user's ID.
223
+ * @returns The count of unread notifications.
224
+ */
147
225
  async getUnreadCount(userId) {
148
226
  return this.storage.countUnread(userId);
149
227
  }
228
+ /**
229
+ * Retrieves a specific notification by its unique ID.
230
+ *
231
+ * @param id - The Notification ID.
232
+ * @returns The notification object if found, otherwise null.
233
+ */
150
234
  async getById(id) {
151
235
  return this.storage.findById(id);
152
236
  }
237
+ /**
238
+ * Aggregates notification statistics for a specified user (e.g., total, unread, counts by channel).
239
+ *
240
+ * @param userId - The user's ID to fetch stats for.
241
+ * @returns An object containing grouped statistics.
242
+ */
153
243
  async getStats(userId) {
154
244
  const all = await this.storage.findByUser(userId, {});
155
245
  const unread = await this.storage.countUnread(userId);
@@ -170,6 +260,11 @@ var NotificationCenter = class {
170
260
  return stats;
171
261
  }
172
262
  // ========== STATE MANAGEMENT ==========
263
+ /**
264
+ * Marks a specific notification as "read" and triggers real-time updates for listeners.
265
+ *
266
+ * @param notificationId - The unique ID of the notification.
267
+ */
173
268
  async markAsRead(notificationId) {
174
269
  await this.storage.markAsRead(notificationId);
175
270
  const notification = await this.storage.findById(notificationId);
@@ -183,10 +278,20 @@ var NotificationCenter = class {
183
278
  this.notifyUnreadSubscribers(notification.userId, count);
184
279
  }
185
280
  }
281
+ /**
282
+ * Marks all notifications belonging to a user as "read".
283
+ *
284
+ * @param userId - The user's ID.
285
+ */
186
286
  async markAllAsRead(userId) {
187
287
  await this.storage.markAllAsRead(userId);
188
288
  this.notifyUnreadSubscribers(userId, 0);
189
289
  }
290
+ /**
291
+ * Reverts a notification status back to "unread".
292
+ *
293
+ * @param notificationId - The unique ID of the notification.
294
+ */
190
295
  async markAsUnread(notificationId) {
191
296
  await this.storage.markAsUnread(notificationId);
192
297
  const notification = await this.storage.findById(notificationId);
@@ -200,10 +305,20 @@ var NotificationCenter = class {
200
305
  this.notifyUnreadSubscribers(notification.userId, count);
201
306
  }
202
307
  }
308
+ /**
309
+ * Marks all notifications belonging to a user as "unread".
310
+ *
311
+ * @param userId - The target user's ID.
312
+ */
203
313
  async markAllAsUnread(userId) {
204
314
  await this.storage.markAllAsUnread(userId);
205
315
  this.notifyUnreadSubscribers(userId, 0);
206
316
  }
317
+ /**
318
+ * Deletes a specific notification from storage.
319
+ *
320
+ * @param notificationId - The Notification ID to delete.
321
+ */
207
322
  async delete(notificationId) {
208
323
  const notification = await this.storage.findById(notificationId);
209
324
  await this.storage.delete(notificationId);
@@ -212,6 +327,11 @@ var NotificationCenter = class {
212
327
  this.notifyUnreadSubscribers(notification.userId, count);
213
328
  }
214
329
  }
330
+ /**
331
+ * Deletes all notifications for a specific user.
332
+ *
333
+ * @param userId - The user's ID.
334
+ */
215
335
  async deleteAll(userId) {
216
336
  const notifications = await this.storage.findByUser(userId, {});
217
337
  await Promise.all(
@@ -220,9 +340,21 @@ var NotificationCenter = class {
220
340
  this.notifyUnreadSubscribers(userId, 0);
221
341
  }
222
342
  // ========== PREFERENCES ==========
343
+ /**
344
+ * Retrieves the notification opt-in/opt-out routing preferences for a given user.
345
+ *
346
+ * @param userId - The user's ID.
347
+ * @returns A promise resolving to the user's NotificationPreferences.
348
+ */
223
349
  async getPreferences(userId) {
224
350
  return this.storage.getPreferences(userId);
225
351
  }
352
+ /**
353
+ * Updates the notification routing and delivery preferences for an user.
354
+ *
355
+ * @param userId - The user's ID.
356
+ * @param prefs - A partial object containing the updated preferences.
357
+ */
226
358
  async updatePreferences(userId, prefs) {
227
359
  const current = await this.storage.getPreferences(userId);
228
360
  const updated = {
@@ -234,16 +366,38 @@ var NotificationCenter = class {
234
366
  await this.storage.savePreferences(userId, updated);
235
367
  }
236
368
  // ========== TEMPLATES ==========
369
+ /**
370
+ * Registers a notification template, mapping a named key to dynamic defaults/formatting.
371
+ *
372
+ * @param template - The NotificationTemplate definitions.
373
+ */
237
374
  registerTemplate(template) {
238
375
  this.templates.set(template.id, template);
239
376
  }
377
+ /**
378
+ * Looks up a registered template by its ID.
379
+ *
380
+ * @param id - The ID of the template.
381
+ * @returns The template object, or undefined if not found.
382
+ */
240
383
  getTemplate(id) {
241
384
  return this.templates.get(id);
242
385
  }
386
+ /**
387
+ * Unregisters a template from the NotificationCenter.
388
+ *
389
+ * @param id - The template ID to remove.
390
+ */
243
391
  unregisterTemplate(id) {
244
392
  this.templates.delete(id);
245
393
  }
246
394
  // ========== DIGEST ==========
395
+ /**
396
+ * Enables batch notification "digests" for a specific user to prevent notification fatigue.
397
+ *
398
+ * @param userId - The user's ID.
399
+ * @param config - The timeframe and configuration for the user's digest.
400
+ */
247
401
  async enableDigest(userId, config) {
248
402
  const prefs = await this.getPreferences(userId);
249
403
  await this.updatePreferences(userId, {
@@ -254,6 +408,11 @@ var NotificationCenter = class {
254
408
  }
255
409
  });
256
410
  }
411
+ /**
412
+ * Disables the digest feature for a user, reverting to immediate delivery.
413
+ *
414
+ * @param userId - The target user's ID.
415
+ */
257
416
  async disableDigest(userId) {
258
417
  const prefs = await this.getPreferences(userId);
259
418
  const newData = { ...prefs.data || {} };
@@ -263,17 +422,36 @@ var NotificationCenter = class {
263
422
  data: newData
264
423
  });
265
424
  }
425
+ /**
426
+ * Fetches the current digest rules configured for a user.
427
+ *
428
+ * @param userId - The user's ID.
429
+ * @returns The digest configuration, or null if disabled.
430
+ */
266
431
  async getDigestConfig(userId) {
267
432
  const prefs = await this.getPreferences(userId);
268
433
  return prefs.data?.digestConfig || null;
269
434
  }
270
435
  // ========== DELIVERY STATUS ==========
436
+ /**
437
+ * Retrieves all delivery receipts generated from transports for a specific notification.
438
+ * Useful to see which channels succeeded and which failed.
439
+ *
440
+ * @param notificationId - The notification's ID.
441
+ * @returns An array of delivery receipts.
442
+ */
271
443
  async getDeliveryStatus(notificationId) {
272
444
  if (!this.storage.getReceipts) {
273
445
  return [];
274
446
  }
275
447
  return this.storage.getReceipts(notificationId);
276
448
  }
449
+ /**
450
+ * Attempts to resend a notification for any channels that previously marked it as "failed".
451
+ *
452
+ * @param notificationId - The target notification ID.
453
+ * @param channel - Optional channel scope. If provided, retries only on this specific transport.
454
+ */
277
455
  async retryFailed(notificationId, channel) {
278
456
  const notification = await this.storage.findById(notificationId);
279
457
  if (!notification) {
@@ -292,6 +470,13 @@ var NotificationCenter = class {
292
470
  }
293
471
  }
294
472
  // ========== SUBSCRIPTIONS (Reactive) ==========
473
+ /**
474
+ * Registers a callback to be invoked whenever a notification is sent to a specific user.
475
+ *
476
+ * @param userId - The target user's ID.
477
+ * @param callback - Function to handle the raw notification map.
478
+ * @returns A cleanup function to unsubscribe from this event.
479
+ */
295
480
  subscribe(userId, callback) {
296
481
  const sid = String(userId);
297
482
  if (!this.subscribers.has(sid)) {
@@ -308,6 +493,13 @@ var NotificationCenter = class {
308
493
  }
309
494
  };
310
495
  }
496
+ /**
497
+ * Registers a callback for state changes (e.g. read/unread status updates) for a user's notifications.
498
+ *
499
+ * @param userId - The target user's ID.
500
+ * @param callback - Lifecycle event listener.
501
+ * @returns A cleanup function to unsubscribe.
502
+ */
311
503
  subscribeToEvents(userId, callback) {
312
504
  const sid = String(userId);
313
505
  if (!this.eventSubscribers.has(sid)) {
@@ -324,6 +516,13 @@ var NotificationCenter = class {
324
516
  }
325
517
  };
326
518
  }
519
+ /**
520
+ * Registers a listener to react to changes in the unread notification count for a user.
521
+ *
522
+ * @param userId - The user's ID.
523
+ * @param callback - Handlers receiving the current unread count.
524
+ * @returns Unsubscribe function.
525
+ */
327
526
  onUnreadCountChange(userId, callback) {
328
527
  const sid = String(userId);
329
528
  if (!this.unreadSubscribers.has(sid)) {
@@ -341,13 +540,26 @@ var NotificationCenter = class {
341
540
  };
342
541
  }
343
542
  // ========== MIDDLEWARE ==========
543
+ /**
544
+ * Injects a middleware interceptor to apply custom logic before or after notifications are sent.
545
+ *
546
+ * @param middleware - The middleware object conforming to `NotificationMiddleware`.
547
+ */
344
548
  use(middleware) {
345
549
  this.middleware.push(middleware);
346
550
  }
551
+ /**
552
+ * Removes a previously configured middleware globally.
553
+ *
554
+ * @param name - The name identifier of the middleware to remove.
555
+ */
347
556
  removeMiddleware(name) {
348
557
  this.middleware = this.middleware.filter((m) => m.name !== name);
349
558
  }
350
559
  // ========== LIFECYCLE ==========
560
+ /**
561
+ * Boots up the NotificationCenter, initializing storage, queues, and worker loops if enabled.
562
+ */
351
563
  async start() {
352
564
  if (this.isRunning) {
353
565
  return;
@@ -366,6 +578,9 @@ var NotificationCenter = class {
366
578
  this.startCleanup();
367
579
  }
368
580
  }
581
+ /**
582
+ * Gracefully shuts down the NotificationCenter, terminating background queues and workers.
583
+ */
369
584
  async stop() {
370
585
  if (!this.isRunning) {
371
586
  return;
@@ -379,6 +594,11 @@ var NotificationCenter = class {
379
594
  await this.queue.stop();
380
595
  }
381
596
  }
597
+ /**
598
+ * Pings all registered transports to report on their current health and active status.
599
+ *
600
+ * @returns A map representing the readiness boolean state of each registered transport.
601
+ */
382
602
  async healthCheck() {
383
603
  const results = {};
384
604
  for (const [name, transport] of this.transports) {
@@ -395,6 +615,9 @@ var NotificationCenter = class {
395
615
  return results;
396
616
  }
397
617
  // ========== PRIVATE METHODS ==========
618
+ /**
619
+ * Applies metadata, templates, and defaults to construct the internal Notification shape.
620
+ */
398
621
  buildNotification(input) {
399
622
  const allChannels = Array.from(this.transports.keys());
400
623
  if (input.template) {
@@ -450,6 +673,9 @@ var NotificationCenter = class {
450
673
  actions: input.actions
451
674
  };
452
675
  }
676
+ /**
677
+ * Internal mechanism sending payload out across desired transports without queue delay.
678
+ */
453
679
  async sendNow(notification) {
454
680
  const prefs = await this.getPreferences(notification.userId);
455
681
  const receipts = await Promise.all(