@voltro/plugin-notifications 0.86.0 → 0.88.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 +5484 -0
- package/THIRD-PARTY-NOTICES.md +6 -230
- package/dist/apnsPush.js +1 -2
- package/dist/channels.js +1 -2
- package/dist/define.js +1 -2
- package/dist/durable.js +1 -2
- package/dist/errors.js +1 -2
- package/dist/expoPush.js +1 -2
- package/dist/fcmPush.js +1 -2
- package/dist/index.d.ts +18 -0
- package/dist/index.js +138 -138
- package/dist/rpc.d.ts +17 -1
- package/dist/rpc.js +28 -23
- package/dist/sendNotification.js +1 -2
- package/dist/service.js +61 -28
- package/dist/store.js +1 -2
- package/dist/web.js +1 -2
- package/dist/webPush.js +1 -2
- package/dist/webPushProtocol.js +1 -2
- package/package.json +5 -5
package/dist/sendNotification.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Schema as e } from "effect";
|
|
2
|
-
|
|
2
|
+
// src/sendNotification.ts
|
|
3
3
|
var t = (e) => {
|
|
4
4
|
if (typeof e == "object" && e) {
|
|
5
5
|
let t = e;
|
|
@@ -38,5 +38,4 @@ var t = (e) => {
|
|
|
38
38
|
...i === void 0 ? {} : { tenantId: i }
|
|
39
39
|
};
|
|
40
40
|
};
|
|
41
|
-
//#endregion
|
|
42
41
|
export { t as defaultRecipients, r as sendInputFor, n as sendNotification };
|
package/dist/service.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveChannels as e } from "./channels.js";
|
|
2
2
|
import { randomUUID as t } from "node:crypto";
|
|
3
|
-
|
|
3
|
+
// src/service.ts
|
|
4
4
|
var n = (e) => ({
|
|
5
5
|
id: "inApp",
|
|
6
6
|
deliver: async (n) => {
|
|
@@ -35,8 +35,32 @@ var n = (e) => ({
|
|
|
35
35
|
let n = r(t, e.tz ?? "UTC"), i = e.endMinute - n;
|
|
36
36
|
return i <= 0 && (i += 1440), new Date(t.getTime() + i * 6e4);
|
|
37
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 =
|
|
39
|
-
|
|
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 = new Map(Object.entries(n.deliveryGuards ?? {}));
|
|
39
|
+
if (u.size > 256 || [...u].some(([e, t]) => !/^[a-z][a-zA-Z0-9._-]{0,127}$/.test(e) || typeof t != "function")) throw Error("Invalid notification delivery guard registry");
|
|
40
|
+
let d = async (e) => {
|
|
41
|
+
let t = e.deliveryGuard;
|
|
42
|
+
if (t === void 0) return !0;
|
|
43
|
+
if (!t || typeof t.id != "string" || !t.payload || typeof t.payload != "object" || Array.isArray(t.payload)) throw Error("Invalid notification delivery guard reference");
|
|
44
|
+
let n = u.get(t.id);
|
|
45
|
+
if (!n) throw Error("Notification delivery guard unavailable");
|
|
46
|
+
let r = JSON.stringify(t.payload);
|
|
47
|
+
if (typeof r != "string" || r.length > 8192) throw Error("Notification delivery guard payload exceeds budget");
|
|
48
|
+
let i = JSON.parse(r);
|
|
49
|
+
if (typeof i != "object" || !i || Array.isArray(i)) throw Error("Invalid notification delivery guard payload");
|
|
50
|
+
let a = await n({
|
|
51
|
+
recipientId: e.to,
|
|
52
|
+
tenantId: e.tenantId ?? null,
|
|
53
|
+
payload: i
|
|
54
|
+
});
|
|
55
|
+
if (a !== !0 && a !== !1) throw Error("Invalid notification delivery guard decision");
|
|
56
|
+
return a;
|
|
57
|
+
}, f = () => ({
|
|
58
|
+
delivered: [],
|
|
59
|
+
failed: [],
|
|
60
|
+
skipped: [...o]
|
|
61
|
+
}), p = async (i) => {
|
|
62
|
+
if (!await d(i)) return f();
|
|
63
|
+
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 = [], p = [], m = {
|
|
40
64
|
to: i.to,
|
|
41
65
|
category: i.category,
|
|
42
66
|
title: i.title,
|
|
@@ -45,11 +69,15 @@ var n = (e) => ({
|
|
|
45
69
|
tenantId: i.tenantId ?? null
|
|
46
70
|
};
|
|
47
71
|
for (let e of s) {
|
|
72
|
+
if (!await d(i)) {
|
|
73
|
+
l.push(e);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
48
76
|
let a = r.get(e);
|
|
49
77
|
if (!a) continue;
|
|
50
78
|
if (a.deliverDetailed !== void 0) {
|
|
51
79
|
try {
|
|
52
|
-
let r = await a.deliverDetailed(
|
|
80
|
+
let r = await a.deliverDetailed(m);
|
|
53
81
|
for (let a of r) await n.store.recordDelivery({
|
|
54
82
|
id: t(),
|
|
55
83
|
to: i.to,
|
|
@@ -62,13 +90,13 @@ var n = (e) => ({
|
|
|
62
90
|
...a.clickToken === void 0 ? {} : { clickToken: a.clickToken }
|
|
63
91
|
});
|
|
64
92
|
let o = r.filter((e) => e.status === "failed");
|
|
65
|
-
r.length === 0 || o.length < r.length ? u.push(e) :
|
|
93
|
+
r.length === 0 || o.length < r.length ? u.push(e) : p.push({
|
|
66
94
|
channel: e,
|
|
67
95
|
error: o[0]?.error ?? "all endpoints failed"
|
|
68
96
|
});
|
|
69
97
|
} catch (r) {
|
|
70
98
|
let a = r instanceof Error ? r.message : String(r);
|
|
71
|
-
|
|
99
|
+
p.push({
|
|
72
100
|
channel: e,
|
|
73
101
|
error: a
|
|
74
102
|
}), await n.store.recordDelivery({
|
|
@@ -91,13 +119,13 @@ var n = (e) => ({
|
|
|
91
119
|
at: c()
|
|
92
120
|
};
|
|
93
121
|
try {
|
|
94
|
-
await a.deliver(
|
|
122
|
+
await a.deliver(m), u.push(e), await n.store.recordDelivery({
|
|
95
123
|
...o,
|
|
96
124
|
status: "sent"
|
|
97
125
|
});
|
|
98
126
|
} catch (t) {
|
|
99
127
|
let r = t instanceof Error ? t.message : String(t);
|
|
100
|
-
|
|
128
|
+
p.push({
|
|
101
129
|
channel: e,
|
|
102
130
|
error: r
|
|
103
131
|
}), await n.store.recordDelivery({
|
|
@@ -109,12 +137,14 @@ var n = (e) => ({
|
|
|
109
137
|
}
|
|
110
138
|
return {
|
|
111
139
|
delivered: u,
|
|
112
|
-
failed:
|
|
140
|
+
failed: p,
|
|
113
141
|
skipped: l
|
|
114
142
|
};
|
|
115
|
-
},
|
|
116
|
-
let r =
|
|
117
|
-
if (
|
|
143
|
+
}, m = async (e) => {
|
|
144
|
+
let r = structuredClone(e);
|
|
145
|
+
if (!await d(r)) return f();
|
|
146
|
+
let o = await n.store.getQuietHours(r.to), s = new Date(c());
|
|
147
|
+
if (o && i(o, s)) return (o.policy ?? "hold") === "drop" ? {
|
|
118
148
|
delivered: [],
|
|
119
149
|
failed: [],
|
|
120
150
|
skipped: [],
|
|
@@ -122,23 +152,23 @@ var n = (e) => ({
|
|
|
122
152
|
} : (await n.store.enqueueHeld({
|
|
123
153
|
id: t(),
|
|
124
154
|
kind: "quiet",
|
|
125
|
-
subjectId:
|
|
126
|
-
send:
|
|
127
|
-
flushAt: a(
|
|
155
|
+
subjectId: r.to,
|
|
156
|
+
send: r,
|
|
157
|
+
flushAt: a(o, s).toISOString()
|
|
128
158
|
}), {
|
|
129
159
|
delivered: [],
|
|
130
160
|
failed: [],
|
|
131
161
|
skipped: [],
|
|
132
162
|
held: "quiet-held"
|
|
133
163
|
});
|
|
134
|
-
if (l > 0 && !
|
|
135
|
-
let
|
|
164
|
+
if (l > 0 && !r.channels) {
|
|
165
|
+
let e = (await n.store.pendingDigest(r.to))[0]?.flushAt ?? new Date(s.getTime() + l).toISOString();
|
|
136
166
|
return await n.store.enqueueHeld({
|
|
137
167
|
id: t(),
|
|
138
168
|
kind: "digest",
|
|
139
|
-
subjectId:
|
|
140
|
-
send:
|
|
141
|
-
flushAt:
|
|
169
|
+
subjectId: r.to,
|
|
170
|
+
send: r,
|
|
171
|
+
flushAt: e
|
|
142
172
|
}), {
|
|
143
173
|
delivered: [],
|
|
144
174
|
failed: [],
|
|
@@ -146,10 +176,10 @@ var n = (e) => ({
|
|
|
146
176
|
held: "digest"
|
|
147
177
|
};
|
|
148
178
|
}
|
|
149
|
-
return
|
|
179
|
+
return p(r);
|
|
150
180
|
};
|
|
151
181
|
return {
|
|
152
|
-
send:
|
|
182
|
+
send: m,
|
|
153
183
|
inbox: (e, t) => n.store.listInbox(e, t),
|
|
154
184
|
unreadCount: (e) => n.store.unreadCount(e),
|
|
155
185
|
markRead: (e, t) => n.store.markRead(e, t),
|
|
@@ -166,7 +196,7 @@ var n = (e) => ({
|
|
|
166
196
|
unsubscribe: (e, t) => n.store.unsubscribeTopic(e, t),
|
|
167
197
|
broadcast: async (e, t) => {
|
|
168
198
|
let r = await n.store.listTopicSubscribers(e), i = [];
|
|
169
|
-
for (let e of r) i.push(await
|
|
199
|
+
for (let e of r) i.push(await m({
|
|
170
200
|
...t,
|
|
171
201
|
to: e.subjectId,
|
|
172
202
|
tenantId: t.tenantId ?? e.tenantId ?? null
|
|
@@ -182,13 +212,17 @@ var n = (e) => ({
|
|
|
182
212
|
flushDue: async (e = new Date(c())) => {
|
|
183
213
|
let t = await n.store.dueHeld(e.toISOString());
|
|
184
214
|
if (t.length === 0) return 0;
|
|
185
|
-
for (let e of t.filter((e) => e.kind === "quiet")) await
|
|
215
|
+
for (let e of t.filter((e) => e.kind === "quiet")) await p(e.send);
|
|
186
216
|
let r = /* @__PURE__ */ new Map();
|
|
187
217
|
for (let e of t.filter((e) => e.kind === "digest")) {
|
|
188
|
-
|
|
189
|
-
|
|
218
|
+
if (e.send.deliveryGuard !== void 0) {
|
|
219
|
+
await p(e.send);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
let t = JSON.stringify([e.send.tenantId ?? null, e.subjectId]), n = r.get(t) ?? [];
|
|
223
|
+
n.push(e), r.set(t, n);
|
|
190
224
|
}
|
|
191
|
-
for (let
|
|
225
|
+
for (let e of r.values()) await p(s(e[0].subjectId, e));
|
|
192
226
|
return await n.store.clearHeld(t.map((e) => e.id)), t.length;
|
|
193
227
|
}
|
|
194
228
|
};
|
|
@@ -208,5 +242,4 @@ var n = (e) => ({
|
|
|
208
242
|
tenantId: t[0].send.tenantId ?? null
|
|
209
243
|
};
|
|
210
244
|
};
|
|
211
|
-
//#endregion
|
|
212
245
|
export { o as buildNotificationService, n as inAppChannel, i as inQuietHours, r as minuteOfDayInZone, a as quietHoursEnd };
|
package/dist/store.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { and as e, eq as t, generateId as n, isNull as r, lte as i } from "@voltro/database";
|
|
2
|
-
|
|
2
|
+
// src/store.ts
|
|
3
3
|
var a = () => {
|
|
4
4
|
let e = [], t = /* @__PURE__ */ new Map(), n = [], r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map(), a = [], o = (e) => `${e.subjectId}|${e.category}|${e.channel}`, s = (e, t) => `${e}|${t}`;
|
|
5
5
|
return {
|
|
@@ -299,5 +299,4 @@ var a = () => {
|
|
|
299
299
|
}
|
|
300
300
|
};
|
|
301
301
|
};
|
|
302
|
-
//#endregion
|
|
303
302
|
export { s as dataStoreNotificationStore, a as memoryNotificationStore };
|
package/dist/web.js
CHANGED
|
@@ -2,7 +2,7 @@ import { BASE_NAME as e } from "./rpc.js";
|
|
|
2
2
|
import { pluginTag as t } from "@voltro/protocol";
|
|
3
3
|
import { useEffect as n, useState as r } from "react";
|
|
4
4
|
import { useMutation as i, useSubscription as a } from "@voltro/client";
|
|
5
|
-
|
|
5
|
+
// src/web.ts
|
|
6
6
|
var o = (n) => t(e, n), s = (e = {}, t = "app") => {
|
|
7
7
|
let { data: n } = a(t, o("inbox"), e);
|
|
8
8
|
return n ?? [];
|
|
@@ -95,5 +95,4 @@ var o = (n) => t(e, n), s = (e = {}, t = "app") => {
|
|
|
95
95
|
clear: () => n.mutate({})
|
|
96
96
|
};
|
|
97
97
|
};
|
|
98
|
-
//#endregion
|
|
99
98
|
export { s as useInbox, l as useMarkRead, m as useQuietHours, u as useSetNotificationPreference, d as useTopicSubscription, c as useUnreadCount, p as useWebPush };
|
package/dist/webPush.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { sendWebPush as e, vapidPublicKeyFor as t } from "./webPushProtocol.js";
|
|
2
2
|
import { and as n, eq as r, generateId as i, id as a, table as o, text as s, timestamp as c } from "@voltro/database";
|
|
3
3
|
import { createHash as l, randomBytes as u } from "node:crypto";
|
|
4
|
-
|
|
4
|
+
// src/webPush.ts
|
|
5
5
|
var d = o("_voltro_notification_push_subscriptions", {
|
|
6
6
|
id: a({ prefix: "notifpush" }),
|
|
7
7
|
subjectId: s(),
|
|
@@ -157,5 +157,4 @@ var d = o("_voltro_notification_push_subscriptions", {
|
|
|
157
157
|
}
|
|
158
158
|
return a;
|
|
159
159
|
}, D = (e) => t(b(e));
|
|
160
|
-
//#endregion
|
|
161
160
|
export { w as assertWebPushConfigured, v as bindWebPushStore, S as buildPushPayload, h as dataStorePushSubscriptionStore, f as endpointHashOf, p as memoryPushSubscriptionStore, d as pushSubscriptionsTable, y as resetWebPushStoreForTest, b as resolveVapidPrivateKey, _ as webPushCell, C as webPushChannel, D as webPushPublicKey };
|
package/dist/webPushProtocol.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createCipheriv as e, createDecipheriv as t, createECDH as n, createHmac as r, createPrivateKey as i, randomBytes as a, sign as o, timingSafeEqual as s } from "node:crypto";
|
|
2
|
-
|
|
2
|
+
// src/webPushProtocol.ts
|
|
3
3
|
var c = (e) => e.toString("base64url"), l = (e) => Buffer.from(e, "base64url"), u = (e, t) => r("sha256", e).update(t).digest(), d = (e, t, n) => u(e, Buffer.concat([t, Buffer.from([1])])).subarray(0, n), f = () => {
|
|
4
4
|
let e = n("prime256v1");
|
|
5
5
|
return e.generateKeys(), c(p(e.getPrivateKey()));
|
|
@@ -113,5 +113,4 @@ var c = (e) => e.toString("base64url"), l = (e) => Buffer.from(e, "base64url"),
|
|
|
113
113
|
let n = Buffer.from(e), r = Buffer.from(t);
|
|
114
114
|
return n.length === r.length && s(n, r);
|
|
115
115
|
};
|
|
116
|
-
//#endregion
|
|
117
116
|
export { T as clickTokenMatches, C as decryptWebPushPayload, S as encryptWebPushPayload, f as generateVapidPrivateKey, w as sendWebPush, _ as vapidAuthorization, h as vapidPublicKeyFor };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/plugin-notifications",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.88.0",
|
|
4
4
|
"description": "One unified notification surface — email, Slack/webhook, SMS, push, in-app — with per-subject preference routing, an in-app inbox, and a delivery log. NotificationService + typed routes + useInbox/useUnreadCount hooks.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
"node": ">=24.0.0"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@voltro/client": "0.
|
|
53
|
-
"@voltro/database": "0.
|
|
54
|
-
"@voltro/protocol": "0.
|
|
52
|
+
"@voltro/client": "0.88.0",
|
|
53
|
+
"@voltro/database": "0.88.0",
|
|
54
|
+
"@voltro/protocol": "0.88.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"effect": "4.0
|
|
57
|
+
"effect": "4.1.0",
|
|
58
58
|
"react": "^19.0.0"
|
|
59
59
|
},
|
|
60
60
|
"publishConfig": {
|