@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.mjs CHANGED
@@ -1,5 +1,9 @@
1
1
  // src/notification_center.ts
2
2
  var NotificationCenter = class {
3
+ /**
4
+ * Initializes a new NotificationCenter with the provided configuration.
5
+ * @param config - The configuration options including adapters and transports.
6
+ */
3
7
  constructor(config) {
4
8
  this.config = config;
5
9
  this.storage = config.storage;
@@ -16,6 +20,13 @@ var NotificationCenter = class {
16
20
  this.isRunning = false;
17
21
  }
18
22
  // ========== DISPATCH ==========
23
+ /**
24
+ * Dispatches a single notification based on the provided input.
25
+ * If a queue is configured, the delivery takes place asynchronously.
26
+ *
27
+ * @param input - The notification payload and routing instructions.
28
+ * @returns A promise that resolves to the processed Notification object.
29
+ */
19
30
  async send(input) {
20
31
  let notification = this.buildNotification(input);
21
32
  notification = await this.applyBeforeSendMiddleware(notification);
@@ -45,12 +56,25 @@ var NotificationCenter = class {
45
56
  throw error;
46
57
  }
47
58
  }
59
+ /**
60
+ * Dispatches an array of notifications sequentially or concurrently.
61
+ *
62
+ * @param inputs - An array of notification inputs.
63
+ * @returns A promise resolving to an array of generated Notification objects.
64
+ */
48
65
  async sendBatch(inputs) {
49
66
  const notifications = await Promise.all(
50
67
  inputs.map((input) => this.send(input))
51
68
  );
52
69
  return notifications;
53
70
  }
71
+ /**
72
+ * Sends an identical notification to a list of users (multicast).
73
+ * Automatically optimizes push/sms transport delivery if transport supports bulk send.
74
+ *
75
+ * @param input - Multicast payload including the target `userIds`.
76
+ * @returns A promise resolving to the dispatched notifications.
77
+ */
54
78
  async sendMulticast(input) {
55
79
  const notifications = await Promise.all(input.userIds.map(async (userId) => {
56
80
  const notificationInput = { ...input, userId };
@@ -85,15 +109,56 @@ var NotificationCenter = class {
85
109
  await Promise.all(receipts.map((r) => this.storage.saveReceipt(r)));
86
110
  }
87
111
  } else {
88
- await Promise.all(validNotifications.map((n) => this.sendNow(n)));
112
+ await Promise.all(validNotifications.map(async (n) => {
113
+ const prefs = await this.storage.getPreferences(n.userId);
114
+ if (transport.canSend(n, prefs)) {
115
+ try {
116
+ const receipt = await transport.send(n, prefs);
117
+ if (this.storage.saveReceipt)
118
+ await this.storage.saveReceipt(receipt);
119
+ } catch (error) {
120
+ const receipt = {
121
+ notificationId: n.id,
122
+ channel,
123
+ status: "failed",
124
+ attempts: 1,
125
+ lastAttempt: /* @__PURE__ */ new Date(),
126
+ error: error.message
127
+ };
128
+ if (this.storage.saveReceipt)
129
+ await this.storage.saveReceipt(receipt);
130
+ }
131
+ }
132
+ }));
89
133
  }
90
134
  } catch (error) {
91
135
  console.error(`Multicast failed for channel ${channel}:`, error);
92
136
  await Promise.all(validNotifications.map((n) => this.applyErrorMiddleware(error, n)));
93
137
  }
94
138
  }
139
+ await Promise.all(validNotifications.map(async (notification) => {
140
+ notification.status = "sent";
141
+ await this.applyAfterSendMiddleware(notification);
142
+ if (notification.channels.includes("inapp") || channels.includes("inapp")) {
143
+ this.notifySubscribers(notification);
144
+ }
145
+ this.notifyEventSubscribers({
146
+ type: "sent",
147
+ notification,
148
+ timestamp: /* @__PURE__ */ new Date()
149
+ });
150
+ await this.storage.save(notification);
151
+ }));
95
152
  return validNotifications;
96
153
  }
154
+ /**
155
+ * Schedules a notification to be sent at a specific future date/time.
156
+ * Automatically enqueues it if the queue adapter supports delayed jobs.
157
+ *
158
+ * @param input - The notification input.
159
+ * @param when - The Date at which the notification should be delivered.
160
+ * @returns A promise resolving to the generated Notification ID.
161
+ */
97
162
  async schedule(input, when) {
98
163
  let notification = this.buildNotification({ ...input, scheduledFor: when });
99
164
  let _notification = await this.applyBeforeSendMiddleware(notification);
@@ -112,15 +177,40 @@ var NotificationCenter = class {
112
177
  return _notification.id;
113
178
  }
114
179
  // ========== QUERYING ==========
180
+ /**
181
+ * Retrieves notifications intended for a specific user ID.
182
+ *
183
+ * @param userId - The target user's ID.
184
+ * @param filters - Optional filters like status, type, limit, offset.
185
+ * @returns A promise resolving to the list of matched notifications.
186
+ */
115
187
  async getForUser(userId, filters) {
116
188
  return this.storage.findByUser(userId, filters);
117
189
  }
190
+ /**
191
+ * Retrieves the current number of unread notifications for a specified user.
192
+ *
193
+ * @param userId - The target user's ID.
194
+ * @returns The count of unread notifications.
195
+ */
118
196
  async getUnreadCount(userId) {
119
197
  return this.storage.countUnread(userId);
120
198
  }
199
+ /**
200
+ * Retrieves a specific notification by its unique ID.
201
+ *
202
+ * @param id - The Notification ID.
203
+ * @returns The notification object if found, otherwise null.
204
+ */
121
205
  async getById(id) {
122
206
  return this.storage.findById(id);
123
207
  }
208
+ /**
209
+ * Aggregates notification statistics for a specified user (e.g., total, unread, counts by channel).
210
+ *
211
+ * @param userId - The user's ID to fetch stats for.
212
+ * @returns An object containing grouped statistics.
213
+ */
124
214
  async getStats(userId) {
125
215
  const all = await this.storage.findByUser(userId, {});
126
216
  const unread = await this.storage.countUnread(userId);
@@ -141,6 +231,11 @@ var NotificationCenter = class {
141
231
  return stats;
142
232
  }
143
233
  // ========== STATE MANAGEMENT ==========
234
+ /**
235
+ * Marks a specific notification as "read" and triggers real-time updates for listeners.
236
+ *
237
+ * @param notificationId - The unique ID of the notification.
238
+ */
144
239
  async markAsRead(notificationId) {
145
240
  await this.storage.markAsRead(notificationId);
146
241
  const notification = await this.storage.findById(notificationId);
@@ -154,10 +249,20 @@ var NotificationCenter = class {
154
249
  this.notifyUnreadSubscribers(notification.userId, count);
155
250
  }
156
251
  }
252
+ /**
253
+ * Marks all notifications belonging to a user as "read".
254
+ *
255
+ * @param userId - The user's ID.
256
+ */
157
257
  async markAllAsRead(userId) {
158
258
  await this.storage.markAllAsRead(userId);
159
259
  this.notifyUnreadSubscribers(userId, 0);
160
260
  }
261
+ /**
262
+ * Reverts a notification status back to "unread".
263
+ *
264
+ * @param notificationId - The unique ID of the notification.
265
+ */
161
266
  async markAsUnread(notificationId) {
162
267
  await this.storage.markAsUnread(notificationId);
163
268
  const notification = await this.storage.findById(notificationId);
@@ -171,10 +276,20 @@ var NotificationCenter = class {
171
276
  this.notifyUnreadSubscribers(notification.userId, count);
172
277
  }
173
278
  }
279
+ /**
280
+ * Marks all notifications belonging to a user as "unread".
281
+ *
282
+ * @param userId - The target user's ID.
283
+ */
174
284
  async markAllAsUnread(userId) {
175
285
  await this.storage.markAllAsUnread(userId);
176
286
  this.notifyUnreadSubscribers(userId, 0);
177
287
  }
288
+ /**
289
+ * Deletes a specific notification from storage.
290
+ *
291
+ * @param notificationId - The Notification ID to delete.
292
+ */
178
293
  async delete(notificationId) {
179
294
  const notification = await this.storage.findById(notificationId);
180
295
  await this.storage.delete(notificationId);
@@ -183,6 +298,11 @@ var NotificationCenter = class {
183
298
  this.notifyUnreadSubscribers(notification.userId, count);
184
299
  }
185
300
  }
301
+ /**
302
+ * Deletes all notifications for a specific user.
303
+ *
304
+ * @param userId - The user's ID.
305
+ */
186
306
  async deleteAll(userId) {
187
307
  const notifications = await this.storage.findByUser(userId, {});
188
308
  await Promise.all(
@@ -191,9 +311,21 @@ var NotificationCenter = class {
191
311
  this.notifyUnreadSubscribers(userId, 0);
192
312
  }
193
313
  // ========== PREFERENCES ==========
314
+ /**
315
+ * Retrieves the notification opt-in/opt-out routing preferences for a given user.
316
+ *
317
+ * @param userId - The user's ID.
318
+ * @returns A promise resolving to the user's NotificationPreferences.
319
+ */
194
320
  async getPreferences(userId) {
195
321
  return this.storage.getPreferences(userId);
196
322
  }
323
+ /**
324
+ * Updates the notification routing and delivery preferences for an user.
325
+ *
326
+ * @param userId - The user's ID.
327
+ * @param prefs - A partial object containing the updated preferences.
328
+ */
197
329
  async updatePreferences(userId, prefs) {
198
330
  const current = await this.storage.getPreferences(userId);
199
331
  const updated = {
@@ -205,16 +337,38 @@ var NotificationCenter = class {
205
337
  await this.storage.savePreferences(userId, updated);
206
338
  }
207
339
  // ========== TEMPLATES ==========
340
+ /**
341
+ * Registers a notification template, mapping a named key to dynamic defaults/formatting.
342
+ *
343
+ * @param template - The NotificationTemplate definitions.
344
+ */
208
345
  registerTemplate(template) {
209
346
  this.templates.set(template.id, template);
210
347
  }
348
+ /**
349
+ * Looks up a registered template by its ID.
350
+ *
351
+ * @param id - The ID of the template.
352
+ * @returns The template object, or undefined if not found.
353
+ */
211
354
  getTemplate(id) {
212
355
  return this.templates.get(id);
213
356
  }
357
+ /**
358
+ * Unregisters a template from the NotificationCenter.
359
+ *
360
+ * @param id - The template ID to remove.
361
+ */
214
362
  unregisterTemplate(id) {
215
363
  this.templates.delete(id);
216
364
  }
217
365
  // ========== DIGEST ==========
366
+ /**
367
+ * Enables batch notification "digests" for a specific user to prevent notification fatigue.
368
+ *
369
+ * @param userId - The user's ID.
370
+ * @param config - The timeframe and configuration for the user's digest.
371
+ */
218
372
  async enableDigest(userId, config) {
219
373
  const prefs = await this.getPreferences(userId);
220
374
  await this.updatePreferences(userId, {
@@ -225,6 +379,11 @@ var NotificationCenter = class {
225
379
  }
226
380
  });
227
381
  }
382
+ /**
383
+ * Disables the digest feature for a user, reverting to immediate delivery.
384
+ *
385
+ * @param userId - The target user's ID.
386
+ */
228
387
  async disableDigest(userId) {
229
388
  const prefs = await this.getPreferences(userId);
230
389
  const newData = { ...prefs.data || {} };
@@ -234,17 +393,36 @@ var NotificationCenter = class {
234
393
  data: newData
235
394
  });
236
395
  }
396
+ /**
397
+ * Fetches the current digest rules configured for a user.
398
+ *
399
+ * @param userId - The user's ID.
400
+ * @returns The digest configuration, or null if disabled.
401
+ */
237
402
  async getDigestConfig(userId) {
238
403
  const prefs = await this.getPreferences(userId);
239
404
  return prefs.data?.digestConfig || null;
240
405
  }
241
406
  // ========== DELIVERY STATUS ==========
407
+ /**
408
+ * Retrieves all delivery receipts generated from transports for a specific notification.
409
+ * Useful to see which channels succeeded and which failed.
410
+ *
411
+ * @param notificationId - The notification's ID.
412
+ * @returns An array of delivery receipts.
413
+ */
242
414
  async getDeliveryStatus(notificationId) {
243
415
  if (!this.storage.getReceipts) {
244
416
  return [];
245
417
  }
246
418
  return this.storage.getReceipts(notificationId);
247
419
  }
420
+ /**
421
+ * Attempts to resend a notification for any channels that previously marked it as "failed".
422
+ *
423
+ * @param notificationId - The target notification ID.
424
+ * @param channel - Optional channel scope. If provided, retries only on this specific transport.
425
+ */
248
426
  async retryFailed(notificationId, channel) {
249
427
  const notification = await this.storage.findById(notificationId);
250
428
  if (!notification) {
@@ -263,6 +441,13 @@ var NotificationCenter = class {
263
441
  }
264
442
  }
265
443
  // ========== SUBSCRIPTIONS (Reactive) ==========
444
+ /**
445
+ * Registers a callback to be invoked whenever a notification is sent to a specific user.
446
+ *
447
+ * @param userId - The target user's ID.
448
+ * @param callback - Function to handle the raw notification map.
449
+ * @returns A cleanup function to unsubscribe from this event.
450
+ */
266
451
  subscribe(userId, callback) {
267
452
  const sid = String(userId);
268
453
  if (!this.subscribers.has(sid)) {
@@ -279,6 +464,13 @@ var NotificationCenter = class {
279
464
  }
280
465
  };
281
466
  }
467
+ /**
468
+ * Registers a callback for state changes (e.g. read/unread status updates) for a user's notifications.
469
+ *
470
+ * @param userId - The target user's ID.
471
+ * @param callback - Lifecycle event listener.
472
+ * @returns A cleanup function to unsubscribe.
473
+ */
282
474
  subscribeToEvents(userId, callback) {
283
475
  const sid = String(userId);
284
476
  if (!this.eventSubscribers.has(sid)) {
@@ -295,6 +487,13 @@ var NotificationCenter = class {
295
487
  }
296
488
  };
297
489
  }
490
+ /**
491
+ * Registers a listener to react to changes in the unread notification count for a user.
492
+ *
493
+ * @param userId - The user's ID.
494
+ * @param callback - Handlers receiving the current unread count.
495
+ * @returns Unsubscribe function.
496
+ */
298
497
  onUnreadCountChange(userId, callback) {
299
498
  const sid = String(userId);
300
499
  if (!this.unreadSubscribers.has(sid)) {
@@ -312,13 +511,26 @@ var NotificationCenter = class {
312
511
  };
313
512
  }
314
513
  // ========== MIDDLEWARE ==========
514
+ /**
515
+ * Injects a middleware interceptor to apply custom logic before or after notifications are sent.
516
+ *
517
+ * @param middleware - The middleware object conforming to `NotificationMiddleware`.
518
+ */
315
519
  use(middleware) {
316
520
  this.middleware.push(middleware);
317
521
  }
522
+ /**
523
+ * Removes a previously configured middleware globally.
524
+ *
525
+ * @param name - The name identifier of the middleware to remove.
526
+ */
318
527
  removeMiddleware(name) {
319
528
  this.middleware = this.middleware.filter((m) => m.name !== name);
320
529
  }
321
530
  // ========== LIFECYCLE ==========
531
+ /**
532
+ * Boots up the NotificationCenter, initializing storage, queues, and worker loops if enabled.
533
+ */
322
534
  async start() {
323
535
  if (this.isRunning) {
324
536
  return;
@@ -337,6 +549,9 @@ var NotificationCenter = class {
337
549
  this.startCleanup();
338
550
  }
339
551
  }
552
+ /**
553
+ * Gracefully shuts down the NotificationCenter, terminating background queues and workers.
554
+ */
340
555
  async stop() {
341
556
  if (!this.isRunning) {
342
557
  return;
@@ -350,6 +565,11 @@ var NotificationCenter = class {
350
565
  await this.queue.stop();
351
566
  }
352
567
  }
568
+ /**
569
+ * Pings all registered transports to report on their current health and active status.
570
+ *
571
+ * @returns A map representing the readiness boolean state of each registered transport.
572
+ */
353
573
  async healthCheck() {
354
574
  const results = {};
355
575
  for (const [name, transport] of this.transports) {
@@ -366,6 +586,9 @@ var NotificationCenter = class {
366
586
  return results;
367
587
  }
368
588
  // ========== PRIVATE METHODS ==========
589
+ /**
590
+ * Applies metadata, templates, and defaults to construct the internal Notification shape.
591
+ */
369
592
  buildNotification(input) {
370
593
  const allChannels = Array.from(this.transports.keys());
371
594
  if (input.template) {
@@ -421,6 +644,9 @@ var NotificationCenter = class {
421
644
  actions: input.actions
422
645
  };
423
646
  }
647
+ /**
648
+ * Internal mechanism sending payload out across desired transports without queue delay.
649
+ */
424
650
  async sendNow(notification) {
425
651
  const prefs = await this.getPreferences(notification.userId);
426
652
  const receipts = await Promise.all(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vynelix/vynemit-core",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Framework-agnostic notification system with unified dispatch, storage, and transport layers",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",