@voltro/plugin-notifications 0.84.0 → 0.86.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.
- package/CHANGELOG.md +1215 -0
- package/THIRD-PARTY-NOTICES.md +5 -89
- package/dist/apnsPush.js +97 -0
- package/dist/channels.js +92 -0
- package/dist/define.d.ts +4 -4
- package/dist/durable.js +42 -0
- package/dist/errors.js +8 -0
- package/dist/expoPush.js +35 -0
- package/dist/fcmPush.js +82 -0
- package/dist/index.d.ts +10 -11
- package/dist/index.js +202 -1309
- package/dist/rpc.d.ts +52 -52
- package/dist/rpc.js +3 -9
- package/dist/sendNotification.js +42 -0
- package/dist/service.js +212 -0
- package/dist/store.js +303 -0
- package/dist/webPush.js +161 -0
- package/dist/webPushProtocol.js +117 -0
- package/package.json +5 -5
package/dist/rpc.d.ts
CHANGED
|
@@ -5,10 +5,10 @@ import { ReactivityChannel } from '@voltro/protocol';
|
|
|
5
5
|
import { Schema } from 'effect';
|
|
6
6
|
|
|
7
7
|
export declare const archiveDescriptor: MutationProcedureDescriptor<"notifications.archive", Schema.Struct<{
|
|
8
|
-
id:
|
|
8
|
+
readonly id: Schema.String;
|
|
9
9
|
}>, Schema.Struct<{
|
|
10
|
-
ok:
|
|
11
|
-
}>,
|
|
10
|
+
readonly ok: Schema.Boolean;
|
|
11
|
+
}>, Schema.Never>;
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* The plugin's canonical package name — the key an app's `alias` is recorded
|
|
@@ -23,104 +23,104 @@ export declare const archiveDescriptor: MutationProcedureDescriptor<"notificatio
|
|
|
23
23
|
export declare const BASE_NAME = "@voltro/plugin-notifications";
|
|
24
24
|
|
|
25
25
|
export declare const clearQuietHoursDescriptor: MutationProcedureDescriptor<"notifications.clearQuietHours", Schema.Struct<{}>, Schema.Struct<{
|
|
26
|
-
ok:
|
|
27
|
-
}>,
|
|
26
|
+
readonly ok: Schema.Boolean;
|
|
27
|
+
}>, Schema.Never>;
|
|
28
28
|
|
|
29
29
|
export declare const inboxDescriptor: QueryProcedureDescriptor<"notifications.inbox", Schema.Struct<{
|
|
30
|
-
unreadOnly: Schema.optional<
|
|
31
|
-
limit: Schema.optional<
|
|
32
|
-
}>, Schema
|
|
30
|
+
readonly unreadOnly: Schema.optional<Schema.Boolean>;
|
|
31
|
+
readonly limit: Schema.optional<Schema.Number>;
|
|
32
|
+
}>, Schema.$Array<Schema.$Record<Schema.String, Schema.Unknown>>, Schema.Never>;
|
|
33
33
|
|
|
34
34
|
/** One push channel for the inbox: every in-app delivery and read-state
|
|
35
35
|
* change publishes, and `inbox` / `unreadCount` re-run for subscribers. */
|
|
36
36
|
export declare const inboxFeed: ReactivityChannel;
|
|
37
37
|
|
|
38
38
|
export declare const markAllReadDescriptor: MutationProcedureDescriptor<"notifications.markAllRead", Schema.Struct<{}>, Schema.Struct<{
|
|
39
|
-
count:
|
|
40
|
-
}>,
|
|
39
|
+
readonly count: Schema.Number;
|
|
40
|
+
}>, Schema.Never>;
|
|
41
41
|
|
|
42
42
|
export declare const markReadDescriptor: MutationProcedureDescriptor<"notifications.markRead", Schema.Struct<{
|
|
43
|
-
id:
|
|
43
|
+
readonly id: Schema.String;
|
|
44
44
|
}>, Schema.Struct<{
|
|
45
|
-
ok:
|
|
46
|
-
}>,
|
|
45
|
+
readonly ok: Schema.Boolean;
|
|
46
|
+
}>, Schema.Never>;
|
|
47
47
|
|
|
48
48
|
export declare const markUnreadDescriptor: MutationProcedureDescriptor<"notifications.markUnread", Schema.Struct<{
|
|
49
|
-
id:
|
|
49
|
+
readonly id: Schema.String;
|
|
50
50
|
}>, Schema.Struct<{
|
|
51
|
-
ok:
|
|
52
|
-
}>,
|
|
51
|
+
readonly ok: Schema.Boolean;
|
|
52
|
+
}>, Schema.Never>;
|
|
53
53
|
|
|
54
54
|
/** Declared for the codegen (`VoltroPlugin.rpcClientDescriptors`) so each tag is
|
|
55
55
|
* emitted into the generated client group. Kept in lockstep with the exports. */
|
|
56
56
|
export declare const notificationsRpcClientImports: ReadonlyArray<PluginRpcClientDescriptor>;
|
|
57
57
|
|
|
58
|
-
export declare const preferencesDescriptor: QueryProcedureDescriptor<"notifications.preferences", Schema.Struct<{}>, Schema
|
|
58
|
+
export declare const preferencesDescriptor: QueryProcedureDescriptor<"notifications.preferences", Schema.Struct<{}>, Schema.$Array<Schema.$Record<Schema.String, Schema.Unknown>>, Schema.Never>;
|
|
59
59
|
|
|
60
60
|
export declare const setPreferenceDescriptor: MutationProcedureDescriptor<"notifications.setPreference", Schema.Struct<{
|
|
61
|
-
category:
|
|
62
|
-
channel:
|
|
63
|
-
enabled:
|
|
61
|
+
readonly category: Schema.String;
|
|
62
|
+
readonly channel: Schema.String;
|
|
63
|
+
readonly enabled: Schema.Boolean;
|
|
64
64
|
}>, Schema.Struct<{
|
|
65
|
-
ok:
|
|
66
|
-
}>,
|
|
65
|
+
readonly ok: Schema.Boolean;
|
|
66
|
+
}>, Schema.Never>;
|
|
67
67
|
|
|
68
68
|
export declare const setQuietHoursDescriptor: MutationProcedureDescriptor<"notifications.setQuietHours", Schema.Struct<{
|
|
69
|
-
startMinute:
|
|
70
|
-
endMinute:
|
|
71
|
-
tz: Schema.optional<
|
|
72
|
-
policy: Schema.optional<Schema.
|
|
69
|
+
readonly startMinute: Schema.Number;
|
|
70
|
+
readonly endMinute: Schema.Number;
|
|
71
|
+
readonly tz: Schema.optional<Schema.String>;
|
|
72
|
+
readonly policy: Schema.optional<Schema.Literals<readonly ["hold", "drop"]>>;
|
|
73
73
|
}>, Schema.Struct<{
|
|
74
|
-
ok:
|
|
75
|
-
}>,
|
|
74
|
+
readonly ok: Schema.Boolean;
|
|
75
|
+
}>, Schema.Never>;
|
|
76
76
|
|
|
77
77
|
export declare const subscribeDescriptor: MutationProcedureDescriptor<"notifications.subscribe", Schema.Struct<{
|
|
78
|
-
topic:
|
|
78
|
+
readonly topic: Schema.String;
|
|
79
79
|
}>, Schema.Struct<{
|
|
80
|
-
ok:
|
|
81
|
-
}>,
|
|
80
|
+
readonly ok: Schema.Boolean;
|
|
81
|
+
}>, Schema.Never>;
|
|
82
82
|
|
|
83
83
|
export declare const unarchiveDescriptor: MutationProcedureDescriptor<"notifications.unarchive", Schema.Struct<{
|
|
84
|
-
id:
|
|
84
|
+
readonly id: Schema.String;
|
|
85
85
|
}>, Schema.Struct<{
|
|
86
|
-
ok:
|
|
87
|
-
}>,
|
|
86
|
+
readonly ok: Schema.Boolean;
|
|
87
|
+
}>, Schema.Never>;
|
|
88
88
|
|
|
89
89
|
export declare const unreadCountDescriptor: QueryProcedureDescriptor<"notifications.unreadCount", Schema.Struct<{}>, Schema.Struct<{
|
|
90
|
-
count:
|
|
91
|
-
}>,
|
|
90
|
+
readonly count: Schema.Number;
|
|
91
|
+
}>, Schema.Never>;
|
|
92
92
|
|
|
93
93
|
export declare const unsubscribeDescriptor: MutationProcedureDescriptor<"notifications.unsubscribe", Schema.Struct<{
|
|
94
|
-
topic:
|
|
94
|
+
readonly topic: Schema.String;
|
|
95
95
|
}>, Schema.Struct<{
|
|
96
|
-
ok:
|
|
97
|
-
}>,
|
|
96
|
+
readonly ok: Schema.Boolean;
|
|
97
|
+
}>, Schema.Never>;
|
|
98
98
|
|
|
99
99
|
export declare const webPushPublicKeyDescriptor: QueryProcedureDescriptor<"notifications.webPushPublicKey", Schema.Struct<{}>, Schema.Struct<{
|
|
100
|
-
key:
|
|
101
|
-
}>,
|
|
100
|
+
readonly key: Schema.String;
|
|
101
|
+
}>, Schema.Never>;
|
|
102
102
|
|
|
103
103
|
/** Registered only when the app configures a web-push channel — see
|
|
104
104
|
* `notificationsPlugin`. */
|
|
105
105
|
export declare const webPushRpcClientImports: ReadonlyArray<PluginRpcClientDescriptor>;
|
|
106
106
|
|
|
107
107
|
export declare const webPushStatusDescriptor: QueryProcedureDescriptor<"notifications.webPushStatus", Schema.Struct<{}>, Schema.Struct<{
|
|
108
|
-
endpoints:
|
|
109
|
-
}>,
|
|
108
|
+
readonly endpoints: Schema.Number;
|
|
109
|
+
}>, Schema.Never>;
|
|
110
110
|
|
|
111
111
|
export declare const webPushSubscribeDescriptor: MutationProcedureDescriptor<"notifications.webPushSubscribe", Schema.Struct<{
|
|
112
|
-
endpoint:
|
|
113
|
-
p256dh:
|
|
114
|
-
auth:
|
|
115
|
-
ua: Schema.optional<
|
|
112
|
+
readonly endpoint: Schema.String;
|
|
113
|
+
readonly p256dh: Schema.String;
|
|
114
|
+
readonly auth: Schema.String;
|
|
115
|
+
readonly ua: Schema.optional<Schema.String>;
|
|
116
116
|
}>, Schema.Struct<{
|
|
117
|
-
ok:
|
|
118
|
-
}>,
|
|
117
|
+
readonly ok: Schema.Boolean;
|
|
118
|
+
}>, Schema.Never>;
|
|
119
119
|
|
|
120
120
|
export declare const webPushUnsubscribeDescriptor: MutationProcedureDescriptor<"notifications.webPushUnsubscribe", Schema.Struct<{
|
|
121
|
-
endpoint:
|
|
121
|
+
readonly endpoint: Schema.String;
|
|
122
122
|
}>, Schema.Struct<{
|
|
123
|
-
ok:
|
|
124
|
-
}>,
|
|
123
|
+
readonly ok: Schema.Boolean;
|
|
124
|
+
}>, Schema.Never>;
|
|
125
125
|
|
|
126
126
|
export { }
|
package/dist/rpc.js
CHANGED
|
@@ -8,10 +8,7 @@ var i = "@voltro/plugin-notifications", a = r("notifications-inbox"), o = n({
|
|
|
8
8
|
unreadOnly: e.optional(e.Boolean),
|
|
9
9
|
limit: e.optional(e.Number)
|
|
10
10
|
}),
|
|
11
|
-
output: e.Array(e.Record(
|
|
12
|
-
key: e.String,
|
|
13
|
-
value: e.Unknown
|
|
14
|
-
})),
|
|
11
|
+
output: e.Array(e.Record(e.String, e.Unknown)),
|
|
15
12
|
openAccess: "self-scoped read: returns only the calling subject's own inbox items"
|
|
16
13
|
}), s = n({
|
|
17
14
|
name: "notifications.unreadCount",
|
|
@@ -47,10 +44,7 @@ var i = "@voltro/plugin-notifications", a = r("notifications-inbox"), o = n({
|
|
|
47
44
|
}), p = n({
|
|
48
45
|
name: "notifications.preferences",
|
|
49
46
|
input: e.Struct({}),
|
|
50
|
-
output: e.Array(e.Record(
|
|
51
|
-
key: e.String,
|
|
52
|
-
value: e.Unknown
|
|
53
|
-
})),
|
|
47
|
+
output: e.Array(e.Record(e.String, e.Unknown)),
|
|
54
48
|
openAccess: "self-scoped read: returns only the calling subject's own channel preferences"
|
|
55
49
|
}), m = t({
|
|
56
50
|
name: "notifications.setPreference",
|
|
@@ -77,7 +71,7 @@ var i = "@voltro/plugin-notifications", a = r("notifications-inbox"), o = n({
|
|
|
77
71
|
startMinute: e.Number,
|
|
78
72
|
endMinute: e.Number,
|
|
79
73
|
tz: e.optional(e.String),
|
|
80
|
-
policy: e.optional(e.
|
|
74
|
+
policy: e.optional(e.Literals(["hold", "drop"]))
|
|
81
75
|
}),
|
|
82
76
|
output: e.Struct({ ok: e.Boolean }),
|
|
83
77
|
openAccess: "self-scoped write: sets only the calling subject's own quiet-hours window"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Schema as e } from "effect";
|
|
2
|
+
//#region src/sendNotification.ts
|
|
3
|
+
var t = (e) => {
|
|
4
|
+
if (typeof e == "object" && e) {
|
|
5
|
+
let t = e;
|
|
6
|
+
if (typeof t.userId == "string") return [t.userId];
|
|
7
|
+
if (Array.isArray(t.userIds)) return t.userIds.filter((e) => typeof e == "string");
|
|
8
|
+
}
|
|
9
|
+
throw Error("sendNotification: pass `recipients` — the audience has neither `userId` nor `userIds`");
|
|
10
|
+
}, n = async (n, i, a) => {
|
|
11
|
+
let o = e.decodeUnknownSync(i.audience)(a.audience), s = e.decodeUnknownSync(i.payload)(a.payload), c = a.recipients ? await a.recipients(o) : t(o), l = [];
|
|
12
|
+
for (let e of c) {
|
|
13
|
+
let t = (a.localeFor ? await a.localeFor(e) : null) ?? a.defaultLocale ?? "en", o = await n.send(r(i, s, e, t, a.tenantId));
|
|
14
|
+
l.push({
|
|
15
|
+
recipientId: e,
|
|
16
|
+
locale: t,
|
|
17
|
+
result: o
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return l;
|
|
21
|
+
}, r = (e, t, n, r, i) => {
|
|
22
|
+
let a = e.render(t, {
|
|
23
|
+
locale: r,
|
|
24
|
+
recipientId: n
|
|
25
|
+
});
|
|
26
|
+
return {
|
|
27
|
+
to: n,
|
|
28
|
+
category: e.category,
|
|
29
|
+
title: a.title,
|
|
30
|
+
body: a.body,
|
|
31
|
+
data: {
|
|
32
|
+
...a.data,
|
|
33
|
+
notification: e.name,
|
|
34
|
+
...a.image === void 0 ? {} : { image: a.image },
|
|
35
|
+
...a.actions === void 0 ? {} : { actions: a.actions }
|
|
36
|
+
},
|
|
37
|
+
...e.channels === void 0 ? {} : { channels: e.channels },
|
|
38
|
+
...i === void 0 ? {} : { tenantId: i }
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
//#endregion
|
|
42
|
+
export { t as defaultRecipients, r as sendInputFor, n as sendNotification };
|
package/dist/service.js
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { resolveChannels as e } from "./channels.js";
|
|
2
|
+
import { randomUUID as t } from "node:crypto";
|
|
3
|
+
//#region src/service.ts
|
|
4
|
+
var n = (e) => ({
|
|
5
|
+
id: "inApp",
|
|
6
|
+
deliver: async (n) => {
|
|
7
|
+
await e.addInbox({
|
|
8
|
+
id: t(),
|
|
9
|
+
subjectId: n.to,
|
|
10
|
+
category: n.category,
|
|
11
|
+
title: n.title,
|
|
12
|
+
body: n.body,
|
|
13
|
+
data: n.data ?? {},
|
|
14
|
+
tenantId: n.tenantId ?? null,
|
|
15
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}), r = (e, t) => {
|
|
19
|
+
try {
|
|
20
|
+
let n = new Intl.DateTimeFormat("en-US", {
|
|
21
|
+
timeZone: t,
|
|
22
|
+
hour: "2-digit",
|
|
23
|
+
minute: "2-digit",
|
|
24
|
+
hour12: !1
|
|
25
|
+
}).formatToParts(e), r = Number(n.find((e) => e.type === "hour")?.value ?? "0") % 24, i = Number(n.find((e) => e.type === "minute")?.value ?? "0");
|
|
26
|
+
return r * 60 + i;
|
|
27
|
+
} catch {
|
|
28
|
+
return e.getUTCHours() * 60 + e.getUTCMinutes();
|
|
29
|
+
}
|
|
30
|
+
}, i = (e, t) => {
|
|
31
|
+
if (e.startMinute === e.endMinute) return !1;
|
|
32
|
+
let n = r(t, e.tz ?? "UTC");
|
|
33
|
+
return e.startMinute < e.endMinute ? n >= e.startMinute && n < e.endMinute : n >= e.startMinute || n < e.endMinute;
|
|
34
|
+
}, a = (e, t) => {
|
|
35
|
+
let n = r(t, e.tz ?? "UTC"), i = e.endMinute - n;
|
|
36
|
+
return i <= 0 && (i += 1440), new Date(t.getTime() + i * 6e4);
|
|
37
|
+
}, o = (n) => {
|
|
38
|
+
let r = new Map(n.channels.map((e) => [e.id, e])), o = n.channels.map((e) => e.id), c = n.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()), l = n.digestWindowMs ?? 0, u = async (i) => {
|
|
39
|
+
let a = await n.store.getPreferences(i.to), s = e(o, i.category, i.channels, a, i.to), l = o.filter((e) => !s.includes(e)), u = [], d = [], f = {
|
|
40
|
+
to: i.to,
|
|
41
|
+
category: i.category,
|
|
42
|
+
title: i.title,
|
|
43
|
+
body: i.body,
|
|
44
|
+
...i.data ? { data: i.data } : {},
|
|
45
|
+
tenantId: i.tenantId ?? null
|
|
46
|
+
};
|
|
47
|
+
for (let e of s) {
|
|
48
|
+
let a = r.get(e);
|
|
49
|
+
if (!a) continue;
|
|
50
|
+
if (a.deliverDetailed !== void 0) {
|
|
51
|
+
try {
|
|
52
|
+
let r = await a.deliverDetailed(f);
|
|
53
|
+
for (let a of r) await n.store.recordDelivery({
|
|
54
|
+
id: t(),
|
|
55
|
+
to: i.to,
|
|
56
|
+
category: i.category,
|
|
57
|
+
channel: e,
|
|
58
|
+
at: c(),
|
|
59
|
+
status: a.status,
|
|
60
|
+
endpoint: a.endpoint,
|
|
61
|
+
...a.error === void 0 ? {} : { error: a.error },
|
|
62
|
+
...a.clickToken === void 0 ? {} : { clickToken: a.clickToken }
|
|
63
|
+
});
|
|
64
|
+
let o = r.filter((e) => e.status === "failed");
|
|
65
|
+
r.length === 0 || o.length < r.length ? u.push(e) : d.push({
|
|
66
|
+
channel: e,
|
|
67
|
+
error: o[0]?.error ?? "all endpoints failed"
|
|
68
|
+
});
|
|
69
|
+
} catch (r) {
|
|
70
|
+
let a = r instanceof Error ? r.message : String(r);
|
|
71
|
+
d.push({
|
|
72
|
+
channel: e,
|
|
73
|
+
error: a
|
|
74
|
+
}), await n.store.recordDelivery({
|
|
75
|
+
id: t(),
|
|
76
|
+
to: i.to,
|
|
77
|
+
category: i.category,
|
|
78
|
+
channel: e,
|
|
79
|
+
at: c(),
|
|
80
|
+
status: "failed",
|
|
81
|
+
error: a
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
let o = {
|
|
87
|
+
id: t(),
|
|
88
|
+
to: i.to,
|
|
89
|
+
category: i.category,
|
|
90
|
+
channel: e,
|
|
91
|
+
at: c()
|
|
92
|
+
};
|
|
93
|
+
try {
|
|
94
|
+
await a.deliver(f), u.push(e), await n.store.recordDelivery({
|
|
95
|
+
...o,
|
|
96
|
+
status: "sent"
|
|
97
|
+
});
|
|
98
|
+
} catch (t) {
|
|
99
|
+
let r = t instanceof Error ? t.message : String(t);
|
|
100
|
+
d.push({
|
|
101
|
+
channel: e,
|
|
102
|
+
error: r
|
|
103
|
+
}), await n.store.recordDelivery({
|
|
104
|
+
...o,
|
|
105
|
+
status: "failed",
|
|
106
|
+
error: r
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
delivered: u,
|
|
112
|
+
failed: d,
|
|
113
|
+
skipped: l
|
|
114
|
+
};
|
|
115
|
+
}, d = async (e) => {
|
|
116
|
+
let r = await n.store.getQuietHours(e.to), o = new Date(c());
|
|
117
|
+
if (r && i(r, o)) return (r.policy ?? "hold") === "drop" ? {
|
|
118
|
+
delivered: [],
|
|
119
|
+
failed: [],
|
|
120
|
+
skipped: [],
|
|
121
|
+
held: "quiet-drop"
|
|
122
|
+
} : (await n.store.enqueueHeld({
|
|
123
|
+
id: t(),
|
|
124
|
+
kind: "quiet",
|
|
125
|
+
subjectId: e.to,
|
|
126
|
+
send: e,
|
|
127
|
+
flushAt: a(r, o).toISOString()
|
|
128
|
+
}), {
|
|
129
|
+
delivered: [],
|
|
130
|
+
failed: [],
|
|
131
|
+
skipped: [],
|
|
132
|
+
held: "quiet-held"
|
|
133
|
+
});
|
|
134
|
+
if (l > 0 && !e.channels) {
|
|
135
|
+
let r = (await n.store.pendingDigest(e.to))[0]?.flushAt ?? new Date(o.getTime() + l).toISOString();
|
|
136
|
+
return await n.store.enqueueHeld({
|
|
137
|
+
id: t(),
|
|
138
|
+
kind: "digest",
|
|
139
|
+
subjectId: e.to,
|
|
140
|
+
send: e,
|
|
141
|
+
flushAt: r
|
|
142
|
+
}), {
|
|
143
|
+
delivered: [],
|
|
144
|
+
failed: [],
|
|
145
|
+
skipped: [],
|
|
146
|
+
held: "digest"
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return u(e);
|
|
150
|
+
};
|
|
151
|
+
return {
|
|
152
|
+
send: d,
|
|
153
|
+
inbox: (e, t) => n.store.listInbox(e, t),
|
|
154
|
+
unreadCount: (e) => n.store.unreadCount(e),
|
|
155
|
+
markRead: (e, t) => n.store.markRead(e, t),
|
|
156
|
+
markUnread: (e, t) => n.store.markUnread(e, t),
|
|
157
|
+
markAllRead: (e) => n.store.markAllRead(e),
|
|
158
|
+
setArchived: (e, t, r) => n.store.setArchived(e, t, r),
|
|
159
|
+
getPreferences: (e) => n.store.getPreferences(e),
|
|
160
|
+
setPreference: (e) => n.store.setPreference(e),
|
|
161
|
+
subscribe: (e, t, r) => n.store.subscribeTopic({
|
|
162
|
+
topic: e,
|
|
163
|
+
subjectId: t,
|
|
164
|
+
tenantId: r ?? null
|
|
165
|
+
}),
|
|
166
|
+
unsubscribe: (e, t) => n.store.unsubscribeTopic(e, t),
|
|
167
|
+
broadcast: async (e, t) => {
|
|
168
|
+
let r = await n.store.listTopicSubscribers(e), i = [];
|
|
169
|
+
for (let e of r) i.push(await d({
|
|
170
|
+
...t,
|
|
171
|
+
to: e.subjectId,
|
|
172
|
+
tenantId: t.tenantId ?? e.tenantId ?? null
|
|
173
|
+
}));
|
|
174
|
+
return {
|
|
175
|
+
recipients: r.length,
|
|
176
|
+
results: i
|
|
177
|
+
};
|
|
178
|
+
},
|
|
179
|
+
setQuietHours: (e) => n.store.setQuietHours(e),
|
|
180
|
+
clearQuietHours: (e) => n.store.clearQuietHours(e),
|
|
181
|
+
getQuietHours: (e) => n.store.getQuietHours(e),
|
|
182
|
+
flushDue: async (e = new Date(c())) => {
|
|
183
|
+
let t = await n.store.dueHeld(e.toISOString());
|
|
184
|
+
if (t.length === 0) return 0;
|
|
185
|
+
for (let e of t.filter((e) => e.kind === "quiet")) await u(e.send);
|
|
186
|
+
let r = /* @__PURE__ */ new Map();
|
|
187
|
+
for (let e of t.filter((e) => e.kind === "digest")) {
|
|
188
|
+
let t = r.get(e.subjectId) ?? [];
|
|
189
|
+
t.push(e), r.set(e.subjectId, t);
|
|
190
|
+
}
|
|
191
|
+
for (let [e, t] of r) await u(s(e, t));
|
|
192
|
+
return await n.store.clearHeld(t.map((e) => e.id)), t.length;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}, s = (e, t) => {
|
|
196
|
+
if (t.length === 1) return t[0].send;
|
|
197
|
+
let n = t.map((e) => ({
|
|
198
|
+
category: e.send.category,
|
|
199
|
+
title: e.send.title,
|
|
200
|
+
body: e.send.body
|
|
201
|
+
}));
|
|
202
|
+
return {
|
|
203
|
+
to: e,
|
|
204
|
+
category: "digest",
|
|
205
|
+
title: `${t.length} new notifications`,
|
|
206
|
+
body: n.map((e) => `• ${e.title}`).join("\n"),
|
|
207
|
+
data: { items: n },
|
|
208
|
+
tenantId: t[0].send.tenantId ?? null
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
//#endregion
|
|
212
|
+
export { o as buildNotificationService, n as inAppChannel, i as inQuietHours, r as minuteOfDayInZone, a as quietHoursEnd };
|