@arkstack/notifications 0.16.10 → 0.16.12
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.d.ts +91 -18
- package/dist/index.js +112 -33
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Model } from "@arkstack/database";
|
|
|
4
4
|
import { User } from "@app/models/User";
|
|
5
5
|
import { User as User$1 } from "@arkstack/auth";
|
|
6
6
|
import { PhoneNumber } from "@kanun-hq/plugin-phone";
|
|
7
|
+
import { ArkormCollection, LengthAwarePaginator } from "arkormx";
|
|
7
8
|
|
|
8
9
|
//#region src/Contracts/UserNotification.d.ts
|
|
9
10
|
declare abstract class UserNotification extends Model {
|
|
@@ -67,6 +68,8 @@ type FirebaseTransportConfig = {
|
|
|
67
68
|
project_id?: string;
|
|
68
69
|
client_email?: string;
|
|
69
70
|
private_key?: string;
|
|
71
|
+
app_name?: string;
|
|
72
|
+
admin_sdk_path?: string;
|
|
70
73
|
};
|
|
71
74
|
type RealtimeDriverOptions = {
|
|
72
75
|
transport?: RealtimeDriverName; /** Channel/topic to broadcast on. Defaults to `${channel_prefix}${user.id}`. */
|
|
@@ -90,7 +93,7 @@ type RealtimeNotificationPayload = {
|
|
|
90
93
|
};
|
|
91
94
|
/** The result of a realtime broadcast (plus the stored record when `store` is on). */
|
|
92
95
|
type RealtimeBroadcastResult = {
|
|
93
|
-
channel: string;
|
|
96
|
+
channel: string | string[];
|
|
94
97
|
event: string;
|
|
95
98
|
payload: RealtimeNotificationPayload;
|
|
96
99
|
stored?: UserNotification;
|
|
@@ -186,6 +189,10 @@ interface NotificationConfig {
|
|
|
186
189
|
project_id: string;
|
|
187
190
|
client_email: string;
|
|
188
191
|
private_key: string;
|
|
192
|
+
app_name?: string;
|
|
193
|
+
} | {
|
|
194
|
+
app_name?: string;
|
|
195
|
+
admin_sdk_path: string;
|
|
189
196
|
};
|
|
190
197
|
};
|
|
191
198
|
}
|
|
@@ -290,9 +297,13 @@ declare class MailNotification extends NotificationContract {
|
|
|
290
297
|
/**
|
|
291
298
|
* A realtime transport (Pusher, Firebase, …) broadcasts a notification payload
|
|
292
299
|
* to a channel/topic that connected clients subscribe to.
|
|
300
|
+
*
|
|
301
|
+
* `channel` may be an array: for Pusher it fans out to multiple channels, and
|
|
302
|
+
* for Firebase it is treated as a list of device registration tokens delivered
|
|
303
|
+
* via a multicast send.
|
|
293
304
|
*/
|
|
294
305
|
interface RealtimeDriver {
|
|
295
|
-
broadcast(channel: string, event: string, payload: RealtimeNotificationPayload): Promise<unknown>;
|
|
306
|
+
broadcast(channel: string | string[], event: string, payload: RealtimeNotificationPayload): Promise<unknown>;
|
|
296
307
|
}
|
|
297
308
|
//#endregion
|
|
298
309
|
//#region src/drivers/RealtimeNotification.d.ts
|
|
@@ -316,7 +327,8 @@ declare class RealtimeNotification extends NotificationContract<RealtimeBroadcas
|
|
|
316
327
|
from(_from: string): this;
|
|
317
328
|
subject(subject: string): this;
|
|
318
329
|
/**
|
|
319
|
-
* Set the recipient: a `User` (derives the channel)
|
|
330
|
+
* Set the recipient: a `User` (derives the channel), an explicit channel
|
|
331
|
+
* string, or an array of channels (Pusher) / device tokens (Firebase).
|
|
320
332
|
*
|
|
321
333
|
* @param recipient
|
|
322
334
|
* @returns
|
|
@@ -324,11 +336,14 @@ declare class RealtimeNotification extends NotificationContract<RealtimeBroadcas
|
|
|
324
336
|
recipient(recipient: NotificationRecipient | User): this;
|
|
325
337
|
/**
|
|
326
338
|
* Broadcast on an explicit channel/topic instead of the per-user default.
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
339
|
+
* An array broadcasts to multiple Pusher channels, or — for Firebase — to a
|
|
340
|
+
* list of device registration tokens (multicast).
|
|
341
|
+
*
|
|
342
|
+
* @param channel
|
|
343
|
+
* @returns
|
|
344
|
+
*/
|
|
331
345
|
channel(channel: string): this;
|
|
346
|
+
channel(channel: string[]): this;
|
|
332
347
|
/**
|
|
333
348
|
* The event name clients subscribe to (default `notification`).
|
|
334
349
|
*
|
|
@@ -343,7 +358,7 @@ declare class RealtimeNotification extends NotificationContract<RealtimeBroadcas
|
|
|
343
358
|
* @returns
|
|
344
359
|
*/
|
|
345
360
|
store(store?: boolean): this;
|
|
346
|
-
type(type
|
|
361
|
+
type(type?: DbNotificationType | null): this;
|
|
347
362
|
action(text?: string | null, link?: string | null): this;
|
|
348
363
|
meta(meta?: NotificationData | null): this;
|
|
349
364
|
private resolveChannel;
|
|
@@ -407,12 +422,52 @@ declare class Notification<D extends keyof DriverMap = keyof DriverMap> {
|
|
|
407
422
|
//#region src/UserNotificationCenter.d.ts
|
|
408
423
|
declare class UserNotificationCenter {
|
|
409
424
|
private static getModel;
|
|
425
|
+
/**
|
|
426
|
+
* Create a database notification then broadcast it using the configured realtime driver
|
|
427
|
+
*
|
|
428
|
+
* @param user
|
|
429
|
+
* @param payload
|
|
430
|
+
*/
|
|
431
|
+
static send(user: User, payload: DbNotificationPayload, channel?: string[]): Promise<RealtimeBroadcastResult>;
|
|
432
|
+
/**
|
|
433
|
+
* Create a database notification
|
|
434
|
+
*
|
|
435
|
+
* @param user
|
|
436
|
+
* @param payload
|
|
437
|
+
*/
|
|
410
438
|
static create(user: User, payload: DbNotificationPayload): Promise<UserNotification>;
|
|
411
|
-
static forUser(user: User): Promise<
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
439
|
+
static forUser(user: User): Promise<ArkormCollection<UserNotification, UserNotification[]>>;
|
|
440
|
+
/**
|
|
441
|
+
* Fetch all the users unread messages
|
|
442
|
+
*
|
|
443
|
+
* @param user
|
|
444
|
+
*/
|
|
445
|
+
static unreadForUser(user: User): Promise<ArkormCollection<UserNotification, UserNotification[]>>;
|
|
446
|
+
/**
|
|
447
|
+
* Fetch all the users unread messages with a lenght aware paginator instance
|
|
448
|
+
*
|
|
449
|
+
* @param user
|
|
450
|
+
* @param perPage
|
|
451
|
+
*/
|
|
452
|
+
static unreadForUser(user: User, perPage: number): Promise<LengthAwarePaginator<UserNotification>>;
|
|
453
|
+
/**
|
|
454
|
+
* Mark the notification as read
|
|
455
|
+
*
|
|
456
|
+
* @param notification
|
|
457
|
+
*/
|
|
458
|
+
static markRead(notification: UserNotification | string | number): Promise<void>;
|
|
459
|
+
/**
|
|
460
|
+
* Mark all unread notifications as read
|
|
461
|
+
*
|
|
462
|
+
* @param user
|
|
463
|
+
*/
|
|
464
|
+
static markAllRead(user: User, ids?: string[] | number[]): Promise<void>;
|
|
465
|
+
/**
|
|
466
|
+
* Delete the indicated notifications
|
|
467
|
+
*
|
|
468
|
+
* @param notification
|
|
469
|
+
*/
|
|
470
|
+
static delete(notification: UserNotification | string | number | UserNotification[] | string[] | number[]): Promise<void>;
|
|
416
471
|
}
|
|
417
472
|
//#endregion
|
|
418
473
|
//#region src/config.d.ts
|
|
@@ -430,13 +485,26 @@ declare class PusherRealtimeDriver implements RealtimeDriver {
|
|
|
430
485
|
private clientPromise?;
|
|
431
486
|
constructor(options?: PusherTransportConfig);
|
|
432
487
|
private client;
|
|
433
|
-
broadcast(channel: string, event: string, payload: RealtimeNotificationPayload): Promise<unknown>;
|
|
488
|
+
broadcast(channel: string | string[], event: string, payload: RealtimeNotificationPayload): Promise<unknown>;
|
|
434
489
|
}
|
|
435
490
|
//#endregion
|
|
436
491
|
//#region src/drivers/realtime/FirebaseRealtimeDriver.d.ts
|
|
437
492
|
/**
|
|
438
|
-
*
|
|
439
|
-
|
|
493
|
+
* The outcome of a token multicast: totals plus the tokens FCM rejected as dead.
|
|
494
|
+
*/
|
|
495
|
+
interface FirebaseMulticastResult {
|
|
496
|
+
successCount: number;
|
|
497
|
+
failureCount: number;
|
|
498
|
+
/**
|
|
499
|
+
* Tokens FCM reported as unregistered/invalid — delete these from your store.
|
|
500
|
+
*/
|
|
501
|
+
invalidTokens: string[];
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Broadcasts notifications over [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging).
|
|
505
|
+
* A single channel maps to an FCM topic; an array of channels is treated as
|
|
506
|
+
* device registration tokens and delivered via a multicast send (chunked to
|
|
507
|
+
* FCM's 500-token limit), returning the tokens that should be pruned.
|
|
440
508
|
*
|
|
441
509
|
* `firebase-admin` is an optional peer dependency, imported lazily so the
|
|
442
510
|
* package installs without it; it is only required when this transport is used.
|
|
@@ -447,10 +515,15 @@ declare class FirebaseRealtimeDriver implements RealtimeDriver {
|
|
|
447
515
|
private messagingPromise?;
|
|
448
516
|
constructor(options?: FirebaseTransportConfig);
|
|
449
517
|
private messaging;
|
|
450
|
-
broadcast(channel: string, event: string, payload: RealtimeNotificationPayload): Promise<string>;
|
|
518
|
+
broadcast(channel: string | string[], event: string, payload: RealtimeNotificationPayload): Promise<string | FirebaseMulticastResult>;
|
|
519
|
+
/**
|
|
520
|
+
* Send to many device tokens at once, chunked to FCM's 500-token limit, and
|
|
521
|
+
* collect the tokens FCM rejects as dead so the caller can prune them.
|
|
522
|
+
*/
|
|
523
|
+
private multicast;
|
|
451
524
|
}
|
|
452
525
|
//#endregion
|
|
453
526
|
//#region src/utils/template.d.ts
|
|
454
527
|
declare const interpolate: (value: string, data?: NotificationData) => string;
|
|
455
528
|
//#endregion
|
|
456
|
-
export { AfricasTalkingSmsDriver, DbNotification, DbNotificationPayload, DbNotificationType, DriverResult, FirebaseRealtimeDriver, FirebaseTransportConfig, MailDriverOptions, MailNotification, MailRecipient, MailRecipientAddress, MergedTransportConfig, Notification, NotificationChannel, NotificationConfig, NotificationContract, NotificationData, NotificationDriverMap, NotificationRecipient, PusherRealtimeDriver, PusherTransportConfig, RealtimeBroadcastResult, RealtimeDriver, RealtimeDriverName, RealtimeDriverOptions, RealtimeNotification, RealtimeNotificationPayload, SmsDriverName, SmsDriverOptions, SmsNotification, TwilioSmsDriver, UserNotification, UserNotificationCenter, configure, interpolate };
|
|
529
|
+
export { AfricasTalkingSmsDriver, DbNotification, DbNotificationPayload, DbNotificationType, DriverResult, FirebaseMulticastResult, FirebaseRealtimeDriver, FirebaseTransportConfig, MailDriverOptions, MailNotification, MailRecipient, MailRecipientAddress, MergedTransportConfig, Notification, NotificationChannel, NotificationConfig, NotificationContract, NotificationData, NotificationDriverMap, NotificationRecipient, PusherRealtimeDriver, PusherTransportConfig, RealtimeBroadcastResult, RealtimeDriver, RealtimeDriverName, RealtimeDriverOptions, RealtimeNotification, RealtimeNotificationPayload, SmsDriverName, SmsDriverOptions, SmsNotification, TwilioSmsDriver, UserNotification, UserNotificationCenter, configure, interpolate };
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { config, env, getModel } from "@arkstack/common";
|
|
2
2
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
-
import { join } from "node:path";
|
|
3
|
+
import path, { join } from "node:path";
|
|
4
4
|
import nodemailer from "nodemailer";
|
|
5
5
|
import { Arkstack } from "@arkstack/contract";
|
|
6
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
7
|
import { randomUUID } from "node:crypto";
|
|
7
8
|
import africastalking from "africastalking";
|
|
8
9
|
import twilio from "twilio";
|
|
@@ -28,6 +29,21 @@ var UserNotificationCenter = class {
|
|
|
28
29
|
static async getModel() {
|
|
29
30
|
return await getModel("UserNotification");
|
|
30
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Create a database notification then broadcast it using the configured realtime driver
|
|
34
|
+
*
|
|
35
|
+
* @param user
|
|
36
|
+
* @param payload
|
|
37
|
+
*/
|
|
38
|
+
static async send(user, payload, channel = []) {
|
|
39
|
+
return await Notification.realtime().store().channel(user.pushTokens ?? channel).meta(payload.meta).type(payload.type).subject(payload.title).action(payload.actionText, payload.actionLink).send(payload.description, payload.title, void 0, payload);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create a database notification
|
|
43
|
+
*
|
|
44
|
+
* @param user
|
|
45
|
+
* @param payload
|
|
46
|
+
*/
|
|
31
47
|
static async create(user, payload) {
|
|
32
48
|
return await (await this.getModel()).query().create({
|
|
33
49
|
userId: user.id,
|
|
@@ -42,12 +58,19 @@ var UserNotificationCenter = class {
|
|
|
42
58
|
static async forUser(user) {
|
|
43
59
|
return await (await this.getModel()).query().where({ userId: user.id }).get();
|
|
44
60
|
}
|
|
45
|
-
static async unreadForUser(user) {
|
|
46
|
-
|
|
61
|
+
static async unreadForUser(user, perPage) {
|
|
62
|
+
const query = (await this.getModel()).query().where({
|
|
47
63
|
userId: user.id,
|
|
48
64
|
readAt: null
|
|
49
|
-
})
|
|
65
|
+
});
|
|
66
|
+
if (perPage) return await query.paginate();
|
|
67
|
+
return await query.get();
|
|
50
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Mark the notification as read
|
|
71
|
+
*
|
|
72
|
+
* @param notification
|
|
73
|
+
*/
|
|
51
74
|
static async markRead(notification) {
|
|
52
75
|
const Model = await this.getModel();
|
|
53
76
|
const id = typeof notification === "object" ? notification.id : notification;
|
|
@@ -55,16 +78,33 @@ var UserNotificationCenter = class {
|
|
|
55
78
|
await Model.query().where({ id }).update({ readAt });
|
|
56
79
|
if (typeof notification === "object") notification.readAt = readAt;
|
|
57
80
|
}
|
|
58
|
-
|
|
59
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Mark all unread notifications as read
|
|
83
|
+
*
|
|
84
|
+
* @param user
|
|
85
|
+
*/
|
|
86
|
+
static async markAllRead(user, ids) {
|
|
87
|
+
const query = (await this.getModel()).query().where({
|
|
60
88
|
userId: user.id,
|
|
61
89
|
readAt: null
|
|
62
|
-
})
|
|
90
|
+
});
|
|
91
|
+
if (ids) query.whereIn("id", ids);
|
|
92
|
+
await query.update({ readAt: /* @__PURE__ */ new Date() });
|
|
63
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Delete the indicated notifications
|
|
96
|
+
*
|
|
97
|
+
* @param notification
|
|
98
|
+
*/
|
|
64
99
|
static async delete(notification) {
|
|
65
100
|
const Model = await this.getModel();
|
|
66
|
-
|
|
67
|
-
|
|
101
|
+
if (Array.isArray(notification)) {
|
|
102
|
+
const ids = notification.map((e) => typeof e === "object" ? e.id : e);
|
|
103
|
+
await Model.query().whereIn("id", ids).delete();
|
|
104
|
+
} else {
|
|
105
|
+
const id = typeof notification === "object" ? notification.id : notification;
|
|
106
|
+
await Model.query().where({ id }).delete();
|
|
107
|
+
}
|
|
68
108
|
}
|
|
69
109
|
};
|
|
70
110
|
//#endregion
|
|
@@ -323,9 +363,19 @@ var MailNotification = class extends NotificationContract {
|
|
|
323
363
|
};
|
|
324
364
|
//#endregion
|
|
325
365
|
//#region src/drivers/realtime/FirebaseRealtimeDriver.ts
|
|
366
|
+
/** FCM caps a multicast at 500 tokens per call. */
|
|
367
|
+
const MULTICAST_LIMIT = 500;
|
|
368
|
+
/** Error codes that mean a token is dead and should be pruned by the app. */
|
|
369
|
+
const DEAD_TOKEN_CODES = /* @__PURE__ */ new Set([
|
|
370
|
+
"messaging/registration-token-not-registered",
|
|
371
|
+
"messaging/invalid-registration-token",
|
|
372
|
+
"messaging/invalid-argument"
|
|
373
|
+
]);
|
|
326
374
|
/**
|
|
327
|
-
* Broadcasts notifications over [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging)
|
|
328
|
-
*
|
|
375
|
+
* Broadcasts notifications over [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging).
|
|
376
|
+
* A single channel maps to an FCM topic; an array of channels is treated as
|
|
377
|
+
* device registration tokens and delivered via a multicast send (chunked to
|
|
378
|
+
* FCM's 500-token limit), returning the tokens that should be pruned.
|
|
329
379
|
*
|
|
330
380
|
* `firebase-admin` is an optional peer dependency, imported lazily so the
|
|
331
381
|
* package installs without it; it is only required when this transport is used.
|
|
@@ -338,16 +388,23 @@ var FirebaseRealtimeDriver = class {
|
|
|
338
388
|
this.options = options;
|
|
339
389
|
}
|
|
340
390
|
messaging() {
|
|
391
|
+
const appSpecifier = "firebase-admin/app";
|
|
392
|
+
const messagingSpecifier = "firebase-admin/messaging";
|
|
341
393
|
this.messagingPromise ??= (async () => {
|
|
342
|
-
const [app, messaging] = await Promise.all([import(
|
|
394
|
+
const [app, messaging] = await Promise.all([import(appSpecifier), import(messagingSpecifier)]).catch(() => {
|
|
343
395
|
throw new Error("The \"firebase-admin\" package is required for the Firebase realtime transport. Install it with `npm i firebase-admin`.");
|
|
344
396
|
});
|
|
345
|
-
const
|
|
397
|
+
const adminsdk = path.join(process.cwd(), this.options.admin_sdk_path ?? env("FIREBASE_ADMINSDK", "firebase-adminsdk.json"));
|
|
398
|
+
let serviceAccount;
|
|
399
|
+
try {
|
|
400
|
+
if (existsSync(adminsdk)) serviceAccount = JSON.parse(readFileSync(adminsdk, { encoding: "utf-8" }));
|
|
401
|
+
} catch {}
|
|
402
|
+
const credential = app.cert(serviceAccount ?? {
|
|
346
403
|
projectId: this.options.project_id ?? env("FIREBASE_PROJECT_ID", ""),
|
|
347
404
|
clientEmail: this.options.client_email ?? env("FIREBASE_CLIENT_EMAIL", ""),
|
|
348
405
|
privateKey: (this.options.private_key ?? env("FIREBASE_PRIVATE_KEY", ""))?.replace(/\\n/g, "\n")
|
|
349
406
|
});
|
|
350
|
-
const name = "arkstack-realtime";
|
|
407
|
+
const name = String(this.options.app_name ?? env("FIREBASE_APP_NAME", "arkstack-realtime")).replaceAll(" ", "-").toLowerCase();
|
|
351
408
|
const instance = app.getApps().find((a) => a.name === name) ?? app.initializeApp({ credential }, name);
|
|
352
409
|
return messaging.getMessaging(instance);
|
|
353
410
|
})();
|
|
@@ -355,15 +412,41 @@ var FirebaseRealtimeDriver = class {
|
|
|
355
412
|
}
|
|
356
413
|
async broadcast(channel, event, payload) {
|
|
357
414
|
const messaging = await this.messaging();
|
|
415
|
+
const data = {
|
|
416
|
+
event,
|
|
417
|
+
payload: JSON.stringify(payload)
|
|
418
|
+
};
|
|
419
|
+
if (Array.isArray(channel)) return await this.multicast(messaging, channel, data);
|
|
358
420
|
const topic = channel.replace(/[^a-zA-Z0-9-_.~%]/g, "_");
|
|
359
421
|
return await messaging.send({
|
|
360
422
|
topic,
|
|
361
|
-
data
|
|
362
|
-
event,
|
|
363
|
-
payload: JSON.stringify(payload)
|
|
364
|
-
}
|
|
423
|
+
data
|
|
365
424
|
});
|
|
366
425
|
}
|
|
426
|
+
/**
|
|
427
|
+
* Send to many device tokens at once, chunked to FCM's 500-token limit, and
|
|
428
|
+
* collect the tokens FCM rejects as dead so the caller can prune them.
|
|
429
|
+
*/
|
|
430
|
+
async multicast(messaging, tokens, data) {
|
|
431
|
+
const result = {
|
|
432
|
+
successCount: 0,
|
|
433
|
+
failureCount: 0,
|
|
434
|
+
invalidTokens: []
|
|
435
|
+
};
|
|
436
|
+
for (let i = 0; i < tokens.length; i += MULTICAST_LIMIT) {
|
|
437
|
+
const batch = tokens.slice(i, i + MULTICAST_LIMIT);
|
|
438
|
+
const response = await messaging.sendEachForMulticast({
|
|
439
|
+
tokens: batch,
|
|
440
|
+
data
|
|
441
|
+
});
|
|
442
|
+
result.successCount += response.successCount;
|
|
443
|
+
result.failureCount += response.failureCount;
|
|
444
|
+
response.responses.forEach((res, index) => {
|
|
445
|
+
if (!res.success && res.error?.code && DEAD_TOKEN_CODES.has(res.error.code)) result.invalidTokens.push(batch[index]);
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
return result;
|
|
449
|
+
}
|
|
367
450
|
};
|
|
368
451
|
//#endregion
|
|
369
452
|
//#region src/drivers/realtime/PusherRealtimeDriver.ts
|
|
@@ -380,8 +463,9 @@ var PusherRealtimeDriver = class {
|
|
|
380
463
|
this.options = options;
|
|
381
464
|
}
|
|
382
465
|
client() {
|
|
466
|
+
const specifier = "pusher";
|
|
383
467
|
this.clientPromise ??= (async () => {
|
|
384
|
-
const mod = await import(
|
|
468
|
+
const mod = await import(specifier).catch(() => {
|
|
385
469
|
throw new Error("The \"pusher\" package is required for the Pusher realtime transport. Install it with `npm i pusher`.");
|
|
386
470
|
});
|
|
387
471
|
return new (mod.default ?? mod)({
|
|
@@ -440,29 +524,24 @@ var RealtimeNotification = class extends NotificationContract {
|
|
|
440
524
|
this.payload.title = subject;
|
|
441
525
|
return this;
|
|
442
526
|
}
|
|
443
|
-
/**
|
|
444
|
-
* Set the recipient: a `User` (derives the channel)
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
* @
|
|
527
|
+
/**
|
|
528
|
+
* Set the recipient: a `User` (derives the channel), an explicit channel
|
|
529
|
+
* string, or an array of channels (Pusher) / device tokens (Firebase).
|
|
530
|
+
*
|
|
531
|
+
* @param recipient
|
|
532
|
+
* @returns
|
|
448
533
|
*/
|
|
449
534
|
recipient(recipient) {
|
|
450
535
|
if (typeof recipient === "object" && !Array.isArray(recipient) && typeof recipient.id !== "undefined") {
|
|
451
536
|
this.user = recipient;
|
|
452
537
|
return this;
|
|
453
538
|
}
|
|
454
|
-
if (typeof recipient === "string") {
|
|
539
|
+
if (typeof recipient === "string" || Array.isArray(recipient)) {
|
|
455
540
|
this.channelName = recipient;
|
|
456
541
|
return this;
|
|
457
542
|
}
|
|
458
543
|
throw new Error("Realtime notifications require a user recipient or a channel name");
|
|
459
544
|
}
|
|
460
|
-
/**
|
|
461
|
-
* Broadcast on an explicit channel/topic instead of the per-user default.
|
|
462
|
-
*
|
|
463
|
-
* @param channel
|
|
464
|
-
* @returns
|
|
465
|
-
*/
|
|
466
545
|
channel(channel) {
|
|
467
546
|
this.channelName = channel;
|
|
468
547
|
return this;
|
|
@@ -501,7 +580,7 @@ var RealtimeNotification = class extends NotificationContract {
|
|
|
501
580
|
return this;
|
|
502
581
|
}
|
|
503
582
|
resolveChannel() {
|
|
504
|
-
if (this.channelName) return this.channelName;
|
|
583
|
+
if (this.channelName !== void 0) return this.channelName;
|
|
505
584
|
if (this.user) return `${this.channelPrefix}${this.user.id}`;
|
|
506
585
|
throw new Error("No channel resolved for realtime notification (provide a user or channel)");
|
|
507
586
|
}
|
|
@@ -528,7 +607,7 @@ var RealtimeNotification = class extends NotificationContract {
|
|
|
528
607
|
read_at: stored?.readAt ? new Date(stored.readAt).toISOString() : null,
|
|
529
608
|
created_at: stored?.createdAt ? new Date(stored.createdAt).toISOString() : (/* @__PURE__ */ new Date()).toISOString()
|
|
530
609
|
};
|
|
531
|
-
await this.driver.broadcast(channel, this.eventName, payload);
|
|
610
|
+
if (channel && channel.length > 0) await this.driver.broadcast(channel, this.eventName, payload);
|
|
532
611
|
return {
|
|
533
612
|
channel,
|
|
534
613
|
event: this.eventName,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arkstack/notifications",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Framework-agnostic notification module for Arkstack and Nodejs, providing support for multi-channel notification delivery.",
|
|
6
6
|
"homepage": "https://arkstack.toneflix.net/guide/notifications",
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
"africastalking": "^0.8.0",
|
|
35
35
|
"nodemailer": "^8.0.7",
|
|
36
36
|
"twilio": "^6.0.0",
|
|
37
|
-
"@arkstack/common": "^0.16.
|
|
37
|
+
"@arkstack/common": "^0.16.12"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@kanun-hq/plugin-phone": "^0.1.8",
|
|
41
41
|
"firebase-admin": "^13.0.0",
|
|
42
42
|
"pusher": "^5.2.0",
|
|
43
|
-
"@arkstack/contract": "^0.16.
|
|
44
|
-
"@arkstack/database": "^0.16.
|
|
43
|
+
"@arkstack/contract": "^0.16.12",
|
|
44
|
+
"@arkstack/database": "^0.16.12"
|
|
45
45
|
},
|
|
46
46
|
"peerDependenciesMeta": {
|
|
47
47
|
"pusher": {
|