@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/index.js
CHANGED
|
@@ -2,76 +2,76 @@ import { PushTokenRejected as e } from "./errors.js";
|
|
|
2
2
|
import { consoleChannel as t, customChannel as n, emailChannel as r, pushChannel as i, resolveChannels as a, richPushOf as o, smsChannel as s, webhookChannel as c } from "./channels.js";
|
|
3
3
|
import { dataStoreNotificationStore as l, memoryNotificationStore as u } from "./store.js";
|
|
4
4
|
import { buildNotificationService as d, inAppChannel as f, inQuietHours as p, minuteOfDayInZone as m, quietHoursEnd as h } from "./service.js";
|
|
5
|
-
import { BASE_NAME as g, archiveDescriptor as
|
|
6
|
-
import { clickTokenMatches as
|
|
7
|
-
import { assertWebPushConfigured as
|
|
8
|
-
import { defineNotification as
|
|
9
|
-
import { sendNotification as
|
|
10
|
-
import { NOTIFICATION_SEND_EFFECT as
|
|
11
|
-
import { APNS_HOSTS as
|
|
12
|
-
import { FCM_SCOPE as
|
|
13
|
-
import { EXPO_PUSH_ENDPOINT as
|
|
14
|
-
import { Context as
|
|
15
|
-
import { boolean as
|
|
16
|
-
import { definePlugin as
|
|
17
|
-
|
|
18
|
-
var W =
|
|
19
|
-
id:
|
|
20
|
-
subjectId:
|
|
21
|
-
category:
|
|
22
|
-
title:
|
|
23
|
-
body:
|
|
24
|
-
data:
|
|
25
|
-
readAt:
|
|
26
|
-
archivedAt:
|
|
27
|
-
createdAt:
|
|
28
|
-
tenantId:
|
|
29
|
-
}).renamedFrom("notification_inbox").index("byInboxSubject", ["subjectId"]).index("byInboxSubjectCreatedAt", ["subjectId", "createdAt"]).index("byInboxSubjectUnread", ["subjectId"], { where: "\"readAt\" IS NULL" }), G =
|
|
30
|
-
id:
|
|
31
|
-
subjectId:
|
|
32
|
-
category:
|
|
33
|
-
channel:
|
|
34
|
-
enabled:
|
|
5
|
+
import { BASE_NAME as g, archiveDescriptor as ee, clearQuietHoursDescriptor as te, inboxDescriptor as ne, inboxFeed as re, markAllReadDescriptor as ie, markReadDescriptor as ae, markUnreadDescriptor as oe, notificationsRpcClientImports as _, preferencesDescriptor as se, preferencesFeed as ce, setPreferenceDescriptor as le, setQuietHoursDescriptor as v, subscribeDescriptor as ue, unarchiveDescriptor as de, unreadCountDescriptor as fe, unsubscribeDescriptor as pe, webPushPublicKeyDescriptor as me, webPushRpcClientImports as he, webPushStatusDescriptor as ge, webPushSubscribeDescriptor as _e, webPushUnsubscribeDescriptor as ve } from "./rpc.js";
|
|
6
|
+
import { clickTokenMatches as ye, decryptWebPushPayload as y, encryptWebPushPayload as b, generateVapidPrivateKey as x, sendWebPush as S, vapidAuthorization as C, vapidPublicKeyFor as w } from "./webPushProtocol.js";
|
|
7
|
+
import { assertWebPushConfigured as be, bindWebPushStore as xe, dataStorePushSubscriptionStore as T, endpointHashOf as E, memoryPushSubscriptionStore as D, pushSubscriptionsTable as O, resetWebPushStoreForTest as k, webPushCell as A, webPushChannel as j, webPushPublicKey as M } from "./webPush.js";
|
|
8
|
+
import { defineNotification as Se } from "./define.js";
|
|
9
|
+
import { sendNotification as Ce } from "./sendNotification.js";
|
|
10
|
+
import { NOTIFICATION_SEND_EFFECT as we, enqueueNotification as Te, notificationOutboxHandler as Ee } from "./durable.js";
|
|
11
|
+
import { APNS_HOSTS as De, APNS_TOKEN_REJECTIONS as Oe, apnsBody as ke, apnsProviderToken as Ae, apnsTransport as je } from "./apnsPush.js";
|
|
12
|
+
import { FCM_SCOPE as Me, FCM_TOKEN_REJECTIONS as N, GOOGLE_TOKEN_URI as Ne, fcmAssertion as Pe, fcmMessage as Fe, fcmSendEndpoint as Ie, fcmTransport as Le, parseServiceAccount as Re } from "./fcmPush.js";
|
|
13
|
+
import { EXPO_PUSH_ENDPOINT as ze, expoPushTransport as Be, isExpoPushToken as Ve } from "./expoPush.js";
|
|
14
|
+
import { Context as He, Effect as P, Layer as Ue } from "effect";
|
|
15
|
+
import { boolean as We, id as F, integer as I, isNotNull as Ge, json as L, registerRetention as R, retentionTtlMsFromEnv as z, table as B, text as V, timestamp as H } from "@voltro/database";
|
|
16
|
+
import { definePlugin as Ke, pluginInstanceName as qe, publishReactivity as U } from "@voltro/protocol";
|
|
17
|
+
// src/index.ts
|
|
18
|
+
var W = B("_voltro_notification_inbox", {
|
|
19
|
+
id: F({ prefix: "notif" }),
|
|
20
|
+
subjectId: V(),
|
|
21
|
+
category: V(),
|
|
22
|
+
title: V(),
|
|
23
|
+
body: V(),
|
|
24
|
+
data: L(),
|
|
25
|
+
readAt: H().nullable(),
|
|
26
|
+
archivedAt: H().nullable(),
|
|
27
|
+
createdAt: H().default("now"),
|
|
28
|
+
tenantId: V().nullable()
|
|
29
|
+
}).renamedFrom("notification_inbox").index("byInboxSubject", ["subjectId"]).index("byInboxSubjectCreatedAt", ["subjectId", "createdAt"]).index("byInboxSubjectUnread", ["subjectId"], { where: "\"readAt\" IS NULL" }), G = B("_voltro_notification_preferences", {
|
|
30
|
+
id: F({ prefix: "notifpref" }),
|
|
31
|
+
subjectId: V(),
|
|
32
|
+
category: V(),
|
|
33
|
+
channel: V(),
|
|
34
|
+
enabled: We().default(!0)
|
|
35
35
|
}).renamedFrom("notification_preferences").unique("byPreferencesSubjectCategoryChannel", [
|
|
36
36
|
"subjectId",
|
|
37
37
|
"category",
|
|
38
38
|
"channel"
|
|
39
|
-
]), K =
|
|
40
|
-
id:
|
|
41
|
-
recipient:
|
|
42
|
-
category:
|
|
43
|
-
channel:
|
|
44
|
-
status:
|
|
45
|
-
error:
|
|
46
|
-
at:
|
|
47
|
-
endpoint:
|
|
48
|
-
clickToken:
|
|
49
|
-
clickedAt:
|
|
50
|
-
}).renamedFrom("notification_deliveries").index("byDeliveriesAt", ["at"]).index("byDeliveriesClickToken", ["clickToken"]), q =
|
|
51
|
-
id:
|
|
52
|
-
topic:
|
|
53
|
-
subjectId:
|
|
54
|
-
tenantId:
|
|
55
|
-
}).renamedFrom("notification_topic_subscriptions").unique("byTopicSubject", ["topic", "subjectId"]).index("byTopic", ["topic"]), J =
|
|
56
|
-
id:
|
|
57
|
-
subjectId:
|
|
58
|
-
startMinute:
|
|
59
|
-
endMinute:
|
|
60
|
-
tz:
|
|
61
|
-
policy:
|
|
62
|
-
}).renamedFrom("notification_quiet_hours").unique("byQuietHoursSubject", ["subjectId"]), Y =
|
|
63
|
-
id:
|
|
64
|
-
kind:
|
|
65
|
-
subjectId:
|
|
66
|
-
send:
|
|
67
|
-
flushAt:
|
|
68
|
-
}).renamedFrom("notification_held").index("byHeldFlushAt", ["flushAt"]).index("byHeldSubjectKind", ["subjectId", "kind"]), X = class extends
|
|
39
|
+
]), K = B("_voltro_notification_deliveries", {
|
|
40
|
+
id: F({ prefix: "notifdlv" }),
|
|
41
|
+
recipient: V(),
|
|
42
|
+
category: V(),
|
|
43
|
+
channel: V(),
|
|
44
|
+
status: V(),
|
|
45
|
+
error: V().nullable(),
|
|
46
|
+
at: H().default("now"),
|
|
47
|
+
endpoint: V().nullable(),
|
|
48
|
+
clickToken: V().nullable(),
|
|
49
|
+
clickedAt: H().nullable()
|
|
50
|
+
}).renamedFrom("notification_deliveries").index("byDeliveriesAt", ["at"]).index("byDeliveriesClickToken", ["clickToken"]), q = B("_voltro_notification_topic_subscriptions", {
|
|
51
|
+
id: F({ prefix: "notiftsub" }),
|
|
52
|
+
topic: V(),
|
|
53
|
+
subjectId: V(),
|
|
54
|
+
tenantId: V().nullable()
|
|
55
|
+
}).renamedFrom("notification_topic_subscriptions").unique("byTopicSubject", ["topic", "subjectId"]).index("byTopic", ["topic"]), J = B("_voltro_notification_quiet_hours", {
|
|
56
|
+
id: F({ prefix: "notifqh" }),
|
|
57
|
+
subjectId: V(),
|
|
58
|
+
startMinute: I(),
|
|
59
|
+
endMinute: I(),
|
|
60
|
+
tz: V().default("UTC"),
|
|
61
|
+
policy: V().default("hold")
|
|
62
|
+
}).renamedFrom("notification_quiet_hours").unique("byQuietHoursSubject", ["subjectId"]), Y = B("_voltro_notification_held", {
|
|
63
|
+
id: F({ prefix: "notifheld" }),
|
|
64
|
+
kind: V(),
|
|
65
|
+
subjectId: V(),
|
|
66
|
+
send: L(),
|
|
67
|
+
flushAt: H()
|
|
68
|
+
}).renamedFrom("notification_held").index("byHeldFlushAt", ["flushAt"]).index("byHeldSubjectKind", ["subjectId", "kind"]), X = class extends He.Service()("@voltro/plugin-notifications/NotificationService") {}, Z = (e) => async (t) => {
|
|
69
69
|
if (e !== void 0) try {
|
|
70
70
|
let n = await e(t);
|
|
71
71
|
if (typeof n == "string" && n !== "") return n;
|
|
72
72
|
} catch {}
|
|
73
73
|
return t.request.subject.id ?? "anonymous";
|
|
74
|
-
}, Q = (e) => e.request.subject.tenantId ?? null, $ = g,
|
|
74
|
+
}, Q = (e) => e.request.subject.tenantId ?? null, $ = g, Je = { collects: [
|
|
75
75
|
{
|
|
76
76
|
category: "device-id",
|
|
77
77
|
linked: !0,
|
|
@@ -90,33 +90,33 @@ var W = V("_voltro_notification_inbox", {
|
|
|
90
90
|
tracking: !1,
|
|
91
91
|
purposes: ["app-functionality"]
|
|
92
92
|
}
|
|
93
|
-
] },
|
|
94
|
-
let n = Z(e.resolveSubjectId), r =
|
|
93
|
+
] }, Ye = (e = {}) => {
|
|
94
|
+
let n = Z(e.resolveSubjectId), r = qe({
|
|
95
95
|
base: $,
|
|
96
96
|
alias: e.alias,
|
|
97
97
|
instance: e.name
|
|
98
98
|
});
|
|
99
|
-
|
|
99
|
+
R({
|
|
100
100
|
source: "plugin",
|
|
101
101
|
table: "_voltro_notification_deliveries",
|
|
102
102
|
timeColumn: "at",
|
|
103
|
-
ttlMs:
|
|
103
|
+
ttlMs: z(process.env.VOLTRO_NOTIFICATION_DELIVERIES_TTL_HOURS, 2160),
|
|
104
104
|
envVar: "VOLTRO_NOTIFICATION_DELIVERIES_TTL_HOURS"
|
|
105
|
-
}),
|
|
105
|
+
}), R({
|
|
106
106
|
source: "plugin",
|
|
107
107
|
table: "_voltro_notification_inbox",
|
|
108
108
|
timeColumn: "createdAt",
|
|
109
|
-
ttlMs:
|
|
109
|
+
ttlMs: z(process.env.VOLTRO_NOTIFICATION_INBOX_READ_TTL_HOURS, 4320),
|
|
110
110
|
envVar: "VOLTRO_NOTIFICATION_INBOX_READ_TTL_HOURS",
|
|
111
|
-
where:
|
|
112
|
-
}),
|
|
111
|
+
where: Ge("readAt")
|
|
112
|
+
}), R({
|
|
113
113
|
source: "plugin",
|
|
114
114
|
table: "_voltro_notification_held",
|
|
115
115
|
timeColumn: "flushAt",
|
|
116
|
-
ttlMs:
|
|
116
|
+
ttlMs: z(process.env.VOLTRO_NOTIFICATION_HELD_TTL_HOURS, 168),
|
|
117
117
|
envVar: "VOLTRO_NOTIFICATION_HELD_TTL_HOURS"
|
|
118
118
|
});
|
|
119
|
-
let i = e.store === void 0, a = { current: e.store ?? u() }, o, s = (e) => e.then((e) => (
|
|
119
|
+
let i = e.store === void 0, a = { current: e.store ?? u() }, o, s = (e) => e.then((e) => (U(o, re), e)), c = (e) => e.then((e) => (U(o, ce), e)), p = {
|
|
120
120
|
addInbox: (e) => s(a.current.addInbox(e)),
|
|
121
121
|
listInbox: (e, t) => a.current.listInbox(e, t),
|
|
122
122
|
markRead: (e, t) => s(a.current.markRead(e, t)),
|
|
@@ -125,7 +125,7 @@ var W = V("_voltro_notification_inbox", {
|
|
|
125
125
|
setArchived: (e, t, n) => s(a.current.setArchived(e, t, n)),
|
|
126
126
|
unreadCount: (e) => a.current.unreadCount(e),
|
|
127
127
|
getPreferences: (e) => a.current.getPreferences(e),
|
|
128
|
-
setPreference: (e) => a.current.setPreference(e),
|
|
128
|
+
setPreference: (e) => c(a.current.setPreference(e)),
|
|
129
129
|
recordDelivery: (e) => a.current.recordDelivery(e),
|
|
130
130
|
listDeliveries: (e) => a.current.listDeliveries(e),
|
|
131
131
|
markClicked: (e) => a.current.markClicked(e),
|
|
@@ -139,55 +139,56 @@ var W = V("_voltro_notification_inbox", {
|
|
|
139
139
|
pendingDigest: (e) => a.current.pendingDigest(e),
|
|
140
140
|
dueHeld: (e) => a.current.dueHeld(e),
|
|
141
141
|
clearHeld: (e) => a.current.clearHeld(e)
|
|
142
|
-
},
|
|
143
|
-
channels:
|
|
144
|
-
store:
|
|
145
|
-
digestWindowMs:
|
|
142
|
+
}, m = e.channels ?? [t()], h = m.some((e) => e.id === "inApp") ? m : [...m, f(p)], g = e.digestWindowMs ?? 0, y = h.some((e) => e.id === "webPush"), b = d({
|
|
143
|
+
channels: h,
|
|
144
|
+
store: p,
|
|
145
|
+
digestWindowMs: g,
|
|
146
|
+
...e.deliveryGuards === void 0 ? {} : { deliveryGuards: e.deliveryGuards }
|
|
146
147
|
}), x, S = [
|
|
147
148
|
{
|
|
148
|
-
...
|
|
149
|
+
...ne,
|
|
149
150
|
description: "The caller's in-app inbox items.",
|
|
150
|
-
execute: (e, t) =>
|
|
151
|
+
execute: (e, t) => P.promise(async () => b.inbox(await n(t), e))
|
|
151
152
|
},
|
|
152
153
|
{
|
|
153
|
-
...
|
|
154
|
+
...fe,
|
|
154
155
|
description: "The caller's unread count.",
|
|
155
|
-
execute: (e, t) =>
|
|
156
|
+
execute: (e, t) => P.promise(async () => ({ count: await b.unreadCount(await n(t)) }))
|
|
156
157
|
},
|
|
157
158
|
{
|
|
158
|
-
...
|
|
159
|
+
...ae,
|
|
159
160
|
description: "Mark an inbox item read.",
|
|
160
|
-
execute: (e, t) =>
|
|
161
|
+
execute: (e, t) => P.promise(async () => ({ ok: await b.markRead(e.id, await n(t)) }))
|
|
161
162
|
},
|
|
162
163
|
{
|
|
163
|
-
...
|
|
164
|
+
...oe,
|
|
164
165
|
description: "Mark an inbox item unread again.",
|
|
165
|
-
execute: (e, t) =>
|
|
166
|
+
execute: (e, t) => P.promise(async () => ({ ok: await b.markUnread(e.id, await n(t)) }))
|
|
166
167
|
},
|
|
167
168
|
{
|
|
168
|
-
...
|
|
169
|
+
...ie,
|
|
169
170
|
description: "Mark every unread item in the caller's inbox read.",
|
|
170
|
-
execute: (e, t) =>
|
|
171
|
+
execute: (e, t) => P.promise(async () => ({ count: await b.markAllRead(await n(t)) }))
|
|
171
172
|
},
|
|
172
173
|
{
|
|
173
|
-
...
|
|
174
|
+
...ee,
|
|
174
175
|
description: "Archive an inbox item — a different state from read; this is what empties the inbox.",
|
|
175
|
-
execute: (e, t) =>
|
|
176
|
+
execute: (e, t) => P.promise(async () => ({ ok: await b.setArchived(e.id, await n(t), !0) }))
|
|
176
177
|
},
|
|
177
178
|
{
|
|
178
|
-
...
|
|
179
|
+
...de,
|
|
179
180
|
description: "Return an archived item to the inbox.",
|
|
180
|
-
execute: (e, t) =>
|
|
181
|
+
execute: (e, t) => P.promise(async () => ({ ok: await b.setArchived(e.id, await n(t), !1) }))
|
|
181
182
|
},
|
|
182
183
|
{
|
|
183
|
-
...
|
|
184
|
+
...se,
|
|
184
185
|
description: "The caller's channel preferences.",
|
|
185
|
-
execute: (e, t) =>
|
|
186
|
+
execute: (e, t) => P.promise(async () => b.getPreferences(await n(t)))
|
|
186
187
|
},
|
|
187
188
|
{
|
|
188
|
-
...
|
|
189
|
+
...le,
|
|
189
190
|
description: "Toggle a channel on/off for a category.",
|
|
190
|
-
execute: (e, t) =>
|
|
191
|
+
execute: (e, t) => P.promise(async () => {
|
|
191
192
|
let { category: r, channel: i, enabled: a } = e;
|
|
192
193
|
return await b.setPreference({
|
|
193
194
|
subjectId: await n(t),
|
|
@@ -198,19 +199,19 @@ var W = V("_voltro_notification_inbox", {
|
|
|
198
199
|
})
|
|
199
200
|
},
|
|
200
201
|
{
|
|
201
|
-
...
|
|
202
|
+
...ue,
|
|
202
203
|
description: "Subscribe the caller to a broadcast topic.",
|
|
203
|
-
execute: (e, t) =>
|
|
204
|
+
execute: (e, t) => P.promise(async () => (await b.subscribe(e.topic, await n(t), Q(t)), { ok: !0 }))
|
|
204
205
|
},
|
|
205
206
|
{
|
|
206
|
-
...
|
|
207
|
+
...pe,
|
|
207
208
|
description: "Unsubscribe the caller from a broadcast topic.",
|
|
208
|
-
execute: (e, t) =>
|
|
209
|
+
execute: (e, t) => P.promise(async () => (await b.unsubscribe(e.topic, await n(t)), { ok: !0 }))
|
|
209
210
|
},
|
|
210
211
|
{
|
|
211
|
-
...
|
|
212
|
+
...v,
|
|
212
213
|
description: "Set the caller's quiet-hours (DND) window.",
|
|
213
|
-
execute: (e, t) =>
|
|
214
|
+
execute: (e, t) => P.promise(async () => {
|
|
214
215
|
let r = e;
|
|
215
216
|
return await b.setQuietHours({
|
|
216
217
|
subjectId: await n(t),
|
|
@@ -222,25 +223,25 @@ var W = V("_voltro_notification_inbox", {
|
|
|
222
223
|
})
|
|
223
224
|
},
|
|
224
225
|
{
|
|
225
|
-
...
|
|
226
|
+
...te,
|
|
226
227
|
description: "Clear the caller's quiet-hours window.",
|
|
227
|
-
execute: (e, t) =>
|
|
228
|
+
execute: (e, t) => P.promise(async () => (await b.clearQuietHours(await n(t)), { ok: !0 }))
|
|
228
229
|
}
|
|
229
|
-
], C =
|
|
230
|
+
], C = y ? [
|
|
230
231
|
{
|
|
231
|
-
...
|
|
232
|
+
...me,
|
|
232
233
|
description: "The VAPID application-server key a subscribing browser needs.",
|
|
233
|
-
execute: () =>
|
|
234
|
+
execute: () => P.sync(() => ({ key: M(A().channelOptions?.privateKey) }))
|
|
234
235
|
},
|
|
235
236
|
{
|
|
236
|
-
...
|
|
237
|
+
..._e,
|
|
237
238
|
description: "Register the calling subject's browser push subscription (one row per endpoint).",
|
|
238
|
-
execute: (e, t) =>
|
|
239
|
+
execute: (e, t) => P.promise(async () => {
|
|
239
240
|
let r = e;
|
|
240
241
|
return await A().store.upsert({
|
|
241
242
|
subjectId: await n(t),
|
|
242
243
|
endpoint: r.endpoint,
|
|
243
|
-
endpointHash:
|
|
244
|
+
endpointHash: E(r.endpoint),
|
|
244
245
|
p256dh: r.p256dh,
|
|
245
246
|
auth: r.auth,
|
|
246
247
|
ua: r.ua ?? null,
|
|
@@ -249,14 +250,14 @@ var W = V("_voltro_notification_inbox", {
|
|
|
249
250
|
})
|
|
250
251
|
},
|
|
251
252
|
{
|
|
252
|
-
...
|
|
253
|
+
...ve,
|
|
253
254
|
description: "Remove one of the calling subject's own subscriptions.",
|
|
254
|
-
execute: (e, t) =>
|
|
255
|
+
execute: (e, t) => P.promise(async () => ({ ok: await A().store.removeForSubject(E(e.endpoint), await n(t)) }))
|
|
255
256
|
},
|
|
256
257
|
{
|
|
257
|
-
...
|
|
258
|
+
...ge,
|
|
258
259
|
description: "How many push endpoints the calling subject has registered.",
|
|
259
|
-
execute: (e, t) =>
|
|
260
|
+
execute: (e, t) => P.promise(async () => ({ endpoints: await A().store.countForSubject(await n(t)) }))
|
|
260
261
|
}
|
|
261
262
|
] : [], w = (e) => ({
|
|
262
263
|
kind: "json",
|
|
@@ -265,18 +266,18 @@ var W = V("_voltro_notification_inbox", {
|
|
|
265
266
|
kind: "json",
|
|
266
267
|
status: e,
|
|
267
268
|
data: { error: t }
|
|
268
|
-
}),
|
|
269
|
+
}), D = (e, t) => new URLSearchParams(new URL(e, "http://x").search).get(t), k = [{
|
|
269
270
|
method: "GET",
|
|
270
271
|
path: "/deliveries",
|
|
271
272
|
scope: "process",
|
|
272
273
|
description: "Recent delivery records (newest first) + per-channel + per-status counts.",
|
|
273
|
-
handler: () =>
|
|
274
|
-
let e = await
|
|
274
|
+
handler: () => P.promise(async () => {
|
|
275
|
+
let e = await p.listDeliveries({ limit: 200 }), t = {}, n = 0, r = 0;
|
|
275
276
|
for (let i of e) t[i.channel] = (t[i.channel] ?? 0) + 1, i.status === "sent" ? n++ : i.status === "failed" && r++;
|
|
276
277
|
let i = e.slice(0, 100).map(({ clickToken: e, ...t }) => t);
|
|
277
278
|
return w({
|
|
278
279
|
deliveries: i,
|
|
279
|
-
channels:
|
|
280
|
+
channels: h.map((e) => e.id),
|
|
280
281
|
stats: {
|
|
281
282
|
sent: n,
|
|
282
283
|
failed: r,
|
|
@@ -290,15 +291,15 @@ var W = V("_voltro_notification_inbox", {
|
|
|
290
291
|
scope: "process",
|
|
291
292
|
description: "In-app inbox for a subject (?subjectId=…).",
|
|
292
293
|
handler: (e) => {
|
|
293
|
-
let t =
|
|
294
|
-
return t ?
|
|
295
|
-
items: await
|
|
296
|
-
unread: await
|
|
297
|
-
})) :
|
|
294
|
+
let t = D(e.url, "subjectId");
|
|
295
|
+
return t ? P.promise(async () => w({
|
|
296
|
+
items: await p.listInbox(t, { limit: 100 }),
|
|
297
|
+
unread: await p.unreadCount(t)
|
|
298
|
+
})) : P.succeed(T(400, "subjectId query param required"));
|
|
298
299
|
}
|
|
299
300
|
}];
|
|
300
|
-
return
|
|
301
|
-
privacy:
|
|
301
|
+
return Ke({
|
|
302
|
+
privacy: Je,
|
|
302
303
|
name: r,
|
|
303
304
|
baseName: $,
|
|
304
305
|
description: "Unified notifications — email/slack/sms/push/in-app with preference routing, inbox + delivery log.",
|
|
@@ -310,9 +311,9 @@ var W = V("_voltro_notification_inbox", {
|
|
|
310
311
|
q,
|
|
311
312
|
J,
|
|
312
313
|
Y,
|
|
313
|
-
|
|
314
|
+
O
|
|
314
315
|
] } },
|
|
315
|
-
...
|
|
316
|
+
...y ? {
|
|
316
317
|
declaredEnv: [{
|
|
317
318
|
name: "VOLTRO_VAPID_PRIVATE_KEY",
|
|
318
319
|
required: !0,
|
|
@@ -328,12 +329,12 @@ var W = V("_voltro_notification_inbox", {
|
|
|
328
329
|
try {
|
|
329
330
|
t = JSON.parse(Buffer.from(e.rawBody).toString("utf8")).token;
|
|
330
331
|
} catch {}
|
|
331
|
-
if (typeof t != "string" || t === "" || !
|
|
332
|
+
if (typeof t != "string" || t === "" || !ye(t, t)) return {
|
|
332
333
|
status: 400,
|
|
333
334
|
body: JSON.stringify({ error: "token required" }),
|
|
334
335
|
contentType: "application/json"
|
|
335
336
|
};
|
|
336
|
-
let n = await
|
|
337
|
+
let n = await p.markClicked(t);
|
|
337
338
|
return {
|
|
338
339
|
status: n ? 200 : 404,
|
|
339
340
|
body: JSON.stringify({ ok: n }),
|
|
@@ -343,27 +344,26 @@ var W = V("_voltro_notification_inbox", {
|
|
|
343
344
|
maxBodyBytes: 4096
|
|
344
345
|
}]
|
|
345
346
|
} : {},
|
|
346
|
-
services:
|
|
347
|
+
services: Ue.succeed(X, b),
|
|
347
348
|
routes: [...S, ...C],
|
|
348
|
-
rpcClientDescriptors:
|
|
349
|
-
inspectEndpoints:
|
|
349
|
+
rpcClientDescriptors: y ? [..._, ...he] : _,
|
|
350
|
+
inspectEndpoints: k,
|
|
350
351
|
bindDataStore: (t) => {
|
|
351
|
-
o = t, i && (a.current = l(t)),
|
|
352
|
+
o = t, i && (a.current = l(t)), xe(t);
|
|
352
353
|
let n = e.flushIntervalMs ?? 3e4;
|
|
353
354
|
x = setInterval(() => {
|
|
354
355
|
b.flushDue().catch(() => {});
|
|
355
356
|
}, n), typeof x.unref == "function" && x.unref();
|
|
356
357
|
},
|
|
357
|
-
onActivate: (e) =>
|
|
358
|
-
|
|
359
|
-
channels:
|
|
360
|
-
digestWindowMs:
|
|
358
|
+
onActivate: (e) => P.sync(() => {
|
|
359
|
+
y && be(), e.logger.info("notifications active", {
|
|
360
|
+
channels: h.map((e) => e.id),
|
|
361
|
+
digestWindowMs: g
|
|
361
362
|
});
|
|
362
363
|
}),
|
|
363
|
-
onDeactivate: () =>
|
|
364
|
+
onDeactivate: () => P.sync(() => {
|
|
364
365
|
x && clearInterval(x), x = void 0;
|
|
365
366
|
})
|
|
366
367
|
});
|
|
367
368
|
};
|
|
368
|
-
|
|
369
|
-
export { Te as APNS_HOSTS, Ee as APNS_TOKEN_REJECTIONS, Le as EXPO_PUSH_ENDPOINT, Ae as FCM_SCOPE, je as FCM_TOKEN_REJECTIONS, P as GOOGLE_TOKEN_URI, Se as NOTIFICATION_SEND_EFFECT, X as NotificationService, e as PushTokenRejected, De as apnsBody, Oe as apnsProviderToken, ke as apnsTransport, d as buildNotificationService, t as consoleChannel, n as customChannel, l as dataStoreNotificationStore, E as dataStorePushSubscriptionStore, b as decryptWebPushPayload, N as defineNotification, K as deliveriesTable, r as emailChannel, x as encryptWebPushPayload, D as endpointHashOf, Ce as enqueueNotification, Re as expoPushTransport, Me as fcmAssertion, Ne as fcmMessage, Pe as fcmSendEndpoint, Fe as fcmTransport, S as generateVapidPrivateKey, Y as heldTable, f as inAppChannel, p as inQuietHours, W as inboxTable, ze as isExpoPushToken, Z as makeSubjectId, u as memoryNotificationStore, O as memoryPushSubscriptionStore, m as minuteOfDayInZone, we as notificationOutboxHandler, Je as notificationsPlugin, Ie as parseServiceAccount, G as preferencesTable, i as pushChannel, k as pushSubscriptionsTable, h as quietHoursEnd, J as quietHoursTable, be as resetWebPushStoreForTest, a as resolveChannels, o as richPushOf, xe as sendNotification, C as sendWebPush, s as smsChannel, q as topicSubscriptionsTable, w as vapidAuthorization, T as vapidPublicKeyFor, j as webPushChannel, M as webPushPublicKey, c as webhookChannel };
|
|
369
|
+
export { De as APNS_HOSTS, Oe as APNS_TOKEN_REJECTIONS, ze as EXPO_PUSH_ENDPOINT, Me as FCM_SCOPE, N as FCM_TOKEN_REJECTIONS, Ne as GOOGLE_TOKEN_URI, we as NOTIFICATION_SEND_EFFECT, X as NotificationService, e as PushTokenRejected, ke as apnsBody, Ae as apnsProviderToken, je as apnsTransport, d as buildNotificationService, t as consoleChannel, n as customChannel, l as dataStoreNotificationStore, T as dataStorePushSubscriptionStore, y as decryptWebPushPayload, Se as defineNotification, K as deliveriesTable, r as emailChannel, b as encryptWebPushPayload, E as endpointHashOf, Te as enqueueNotification, Be as expoPushTransport, Pe as fcmAssertion, Fe as fcmMessage, Ie as fcmSendEndpoint, Le as fcmTransport, x as generateVapidPrivateKey, Y as heldTable, f as inAppChannel, p as inQuietHours, W as inboxTable, Ve as isExpoPushToken, Z as makeSubjectId, u as memoryNotificationStore, D as memoryPushSubscriptionStore, m as minuteOfDayInZone, Ee as notificationOutboxHandler, Ye as notificationsPlugin, Re as parseServiceAccount, G as preferencesTable, i as pushChannel, O as pushSubscriptionsTable, h as quietHoursEnd, J as quietHoursTable, k as resetWebPushStoreForTest, a as resolveChannels, o as richPushOf, Ce as sendNotification, S as sendWebPush, s as smsChannel, q as topicSubscriptionsTable, C as vapidAuthorization, w as vapidPublicKeyFor, j as webPushChannel, M as webPushPublicKey, c as webhookChannel };
|
package/dist/rpc.d.ts
CHANGED
|
@@ -22,6 +22,14 @@ export declare const archiveDescriptor: MutationProcedureDescriptor<"notificatio
|
|
|
22
22
|
*/
|
|
23
23
|
export declare const BASE_NAME = "@voltro/plugin-notifications";
|
|
24
24
|
|
|
25
|
+
/** One channel switch of the calling subject for one category. */
|
|
26
|
+
export declare const ChannelPreferenceSchema: Schema.Struct<{
|
|
27
|
+
readonly subjectId: Schema.String;
|
|
28
|
+
readonly category: Schema.String;
|
|
29
|
+
readonly channel: Schema.String;
|
|
30
|
+
readonly enabled: Schema.Boolean;
|
|
31
|
+
}>;
|
|
32
|
+
|
|
25
33
|
export declare const clearQuietHoursDescriptor: MutationProcedureDescriptor<"notifications.clearQuietHours", Schema.Struct<{}>, Schema.Struct<{
|
|
26
34
|
readonly ok: Schema.Boolean;
|
|
27
35
|
}>, Schema.Never>;
|
|
@@ -55,7 +63,15 @@ export declare const markUnreadDescriptor: MutationProcedureDescriptor<"notifica
|
|
|
55
63
|
* emitted into the generated client group. Kept in lockstep with the exports. */
|
|
56
64
|
export declare const notificationsRpcClientImports: ReadonlyArray<PluginRpcClientDescriptor>;
|
|
57
65
|
|
|
58
|
-
export declare const preferencesDescriptor: QueryProcedureDescriptor<"notifications.preferences", Schema.Struct<{}>, Schema.$Array<Schema
|
|
66
|
+
export declare const preferencesDescriptor: QueryProcedureDescriptor<"notifications.preferences", Schema.Struct<{}>, Schema.$Array<Schema.Struct<{
|
|
67
|
+
readonly subjectId: Schema.String;
|
|
68
|
+
readonly category: Schema.String;
|
|
69
|
+
readonly channel: Schema.String;
|
|
70
|
+
readonly enabled: Schema.Boolean;
|
|
71
|
+
}>>, Schema.Never>;
|
|
72
|
+
|
|
73
|
+
/** Every preference write publishes this channel, so `notifications.preferences` subscribers see the change live. */
|
|
74
|
+
export declare const preferencesFeed: ReactivityChannel;
|
|
59
75
|
|
|
60
76
|
export declare const setPreferenceDescriptor: MutationProcedureDescriptor<"notifications.setPreference", Schema.Struct<{
|
|
61
77
|
readonly category: Schema.String;
|
package/dist/rpc.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { Schema as e } from "effect";
|
|
2
2
|
import { defineMutation as t, defineQuery as n, reactivityChannel as r } from "@voltro/protocol";
|
|
3
|
-
|
|
4
|
-
var i = "@voltro/plugin-notifications", a = r("notifications-inbox"), o =
|
|
3
|
+
// src/rpc.ts
|
|
4
|
+
var i = "@voltro/plugin-notifications", a = r("notifications-inbox"), o = r("notifications-preferences"), s = e.Struct({
|
|
5
|
+
subjectId: e.String,
|
|
6
|
+
category: e.String,
|
|
7
|
+
channel: e.String,
|
|
8
|
+
enabled: e.Boolean
|
|
9
|
+
}), c = n({
|
|
5
10
|
name: "notifications.inbox",
|
|
6
11
|
source: a,
|
|
7
12
|
input: e.Struct({
|
|
@@ -10,43 +15,44 @@ var i = "@voltro/plugin-notifications", a = r("notifications-inbox"), o = n({
|
|
|
10
15
|
}),
|
|
11
16
|
output: e.Array(e.Record(e.String, e.Unknown)),
|
|
12
17
|
openAccess: "self-scoped read: returns only the calling subject's own inbox items"
|
|
13
|
-
}),
|
|
18
|
+
}), l = n({
|
|
14
19
|
name: "notifications.unreadCount",
|
|
15
20
|
source: a,
|
|
16
21
|
input: e.Struct({}),
|
|
17
22
|
output: e.Struct({ count: e.Number }),
|
|
18
23
|
openAccess: "self-scoped read: counts only the calling subject's own unread items"
|
|
19
|
-
}),
|
|
24
|
+
}), u = t({
|
|
20
25
|
name: "notifications.markRead",
|
|
21
26
|
input: e.Struct({ id: e.String }),
|
|
22
27
|
output: e.Struct({ ok: e.Boolean }),
|
|
23
28
|
openAccess: "self-scoped write: marks an item read only within the calling subject's own inbox"
|
|
24
|
-
}),
|
|
29
|
+
}), d = t({
|
|
25
30
|
name: "notifications.markUnread",
|
|
26
31
|
input: e.Struct({ id: e.String }),
|
|
27
32
|
output: e.Struct({ ok: e.Boolean }),
|
|
28
33
|
openAccess: "self-scoped write: marks an item unread only within the calling subject's own inbox"
|
|
29
|
-
}),
|
|
34
|
+
}), f = t({
|
|
30
35
|
name: "notifications.markAllRead",
|
|
31
36
|
input: e.Struct({}),
|
|
32
37
|
output: e.Struct({ count: e.Number }),
|
|
33
38
|
openAccess: "self-scoped write: clears unread state only in the calling subject's own inbox"
|
|
34
|
-
}),
|
|
39
|
+
}), p = t({
|
|
35
40
|
name: "notifications.archive",
|
|
36
41
|
input: e.Struct({ id: e.String }),
|
|
37
42
|
output: e.Struct({ ok: e.Boolean }),
|
|
38
43
|
openAccess: "self-scoped write: archives an item only within the calling subject's own inbox"
|
|
39
|
-
}),
|
|
44
|
+
}), m = t({
|
|
40
45
|
name: "notifications.unarchive",
|
|
41
46
|
input: e.Struct({ id: e.String }),
|
|
42
47
|
output: e.Struct({ ok: e.Boolean }),
|
|
43
48
|
openAccess: "self-scoped write: unarchives an item only within the calling subject's own inbox"
|
|
44
|
-
}),
|
|
49
|
+
}), h = n({
|
|
45
50
|
name: "notifications.preferences",
|
|
51
|
+
source: o,
|
|
46
52
|
input: e.Struct({}),
|
|
47
|
-
output: e.Array(
|
|
53
|
+
output: e.Array(s),
|
|
48
54
|
openAccess: "self-scoped read: returns only the calling subject's own channel preferences"
|
|
49
|
-
}),
|
|
55
|
+
}), g = t({
|
|
50
56
|
name: "notifications.setPreference",
|
|
51
57
|
input: e.Struct({
|
|
52
58
|
category: e.String,
|
|
@@ -55,17 +61,17 @@ var i = "@voltro/plugin-notifications", a = r("notifications-inbox"), o = n({
|
|
|
55
61
|
}),
|
|
56
62
|
output: e.Struct({ ok: e.Boolean }),
|
|
57
63
|
openAccess: "self-scoped write: toggles a channel only on the calling subject's own preferences"
|
|
58
|
-
}),
|
|
64
|
+
}), _ = t({
|
|
59
65
|
name: "notifications.subscribe",
|
|
60
66
|
input: e.Struct({ topic: e.String }),
|
|
61
67
|
output: e.Struct({ ok: e.Boolean }),
|
|
62
68
|
openAccess: "self-service: subscribes the calling subject itself to a topic (the subject is never caller-supplied)"
|
|
63
|
-
}),
|
|
69
|
+
}), v = t({
|
|
64
70
|
name: "notifications.unsubscribe",
|
|
65
71
|
input: e.Struct({ topic: e.String }),
|
|
66
72
|
output: e.Struct({ ok: e.Boolean }),
|
|
67
73
|
openAccess: "self-service: unsubscribes the calling subject itself from a topic"
|
|
68
|
-
}),
|
|
74
|
+
}), y = t({
|
|
69
75
|
name: "notifications.setQuietHours",
|
|
70
76
|
input: e.Struct({
|
|
71
77
|
startMinute: e.Number,
|
|
@@ -75,17 +81,17 @@ var i = "@voltro/plugin-notifications", a = r("notifications-inbox"), o = n({
|
|
|
75
81
|
}),
|
|
76
82
|
output: e.Struct({ ok: e.Boolean }),
|
|
77
83
|
openAccess: "self-scoped write: sets only the calling subject's own quiet-hours window"
|
|
78
|
-
}),
|
|
84
|
+
}), b = t({
|
|
79
85
|
name: "notifications.clearQuietHours",
|
|
80
86
|
input: e.Struct({}),
|
|
81
87
|
output: e.Struct({ ok: e.Boolean }),
|
|
82
88
|
openAccess: "self-scoped write: clears only the calling subject's own quiet-hours window"
|
|
83
|
-
}),
|
|
89
|
+
}), x = n({
|
|
84
90
|
name: "notifications.webPushPublicKey",
|
|
85
91
|
input: e.Struct({}),
|
|
86
92
|
output: e.Struct({ key: e.String }),
|
|
87
93
|
openAccess: "public application-server key — the value every subscribing browser is handed by design"
|
|
88
|
-
}),
|
|
94
|
+
}), S = t({
|
|
89
95
|
name: "notifications.webPushSubscribe",
|
|
90
96
|
input: e.Struct({
|
|
91
97
|
endpoint: e.String,
|
|
@@ -95,17 +101,17 @@ var i = "@voltro/plugin-notifications", a = r("notifications-inbox"), o = n({
|
|
|
95
101
|
}),
|
|
96
102
|
output: e.Struct({ ok: e.Boolean }),
|
|
97
103
|
openAccess: "self-scoped write: registers the calling subject's own browser subscription (the subject is never caller-supplied)"
|
|
98
|
-
}),
|
|
104
|
+
}), C = t({
|
|
99
105
|
name: "notifications.webPushUnsubscribe",
|
|
100
106
|
input: e.Struct({ endpoint: e.String }),
|
|
101
107
|
output: e.Struct({ ok: e.Boolean }),
|
|
102
108
|
openAccess: "self-scoped write: removes only a subscription the calling subject owns"
|
|
103
|
-
}),
|
|
109
|
+
}), w = n({
|
|
104
110
|
name: "notifications.webPushStatus",
|
|
105
111
|
input: e.Struct({}),
|
|
106
112
|
output: e.Struct({ endpoints: e.Number }),
|
|
107
113
|
openAccess: "self-scoped read: counts only the calling subject's own subscriptions"
|
|
108
|
-
}),
|
|
114
|
+
}), T = [
|
|
109
115
|
{
|
|
110
116
|
tag: "notifications.webPushPublicKey",
|
|
111
117
|
kind: "query",
|
|
@@ -138,7 +144,7 @@ var i = "@voltro/plugin-notifications", a = r("notifications-inbox"), o = n({
|
|
|
138
144
|
name: "webPushStatusDescriptor"
|
|
139
145
|
}
|
|
140
146
|
}
|
|
141
|
-
],
|
|
147
|
+
], E = [
|
|
142
148
|
{
|
|
143
149
|
tag: "notifications.inbox",
|
|
144
150
|
kind: "query",
|
|
@@ -244,5 +250,4 @@ var i = "@voltro/plugin-notifications", a = r("notifications-inbox"), o = n({
|
|
|
244
250
|
}
|
|
245
251
|
}
|
|
246
252
|
];
|
|
247
|
-
|
|
248
|
-
export { i as BASE_NAME, d as archiveDescriptor, v as clearQuietHoursDescriptor, o as inboxDescriptor, a as inboxFeed, u as markAllReadDescriptor, c as markReadDescriptor, l as markUnreadDescriptor, w as notificationsRpcClientImports, p as preferencesDescriptor, m as setPreferenceDescriptor, _ as setQuietHoursDescriptor, h as subscribeDescriptor, f as unarchiveDescriptor, s as unreadCountDescriptor, g as unsubscribeDescriptor, y as webPushPublicKeyDescriptor, C as webPushRpcClientImports, S as webPushStatusDescriptor, b as webPushSubscribeDescriptor, x as webPushUnsubscribeDescriptor };
|
|
253
|
+
export { i as BASE_NAME, s as ChannelPreferenceSchema, p as archiveDescriptor, b as clearQuietHoursDescriptor, c as inboxDescriptor, a as inboxFeed, f as markAllReadDescriptor, u as markReadDescriptor, d as markUnreadDescriptor, E as notificationsRpcClientImports, h as preferencesDescriptor, o as preferencesFeed, g as setPreferenceDescriptor, y as setQuietHoursDescriptor, _ as subscribeDescriptor, m as unarchiveDescriptor, l as unreadCountDescriptor, v as unsubscribeDescriptor, x as webPushPublicKeyDescriptor, T as webPushRpcClientImports, w as webPushStatusDescriptor, S as webPushSubscribeDescriptor, C as webPushUnsubscribeDescriptor };
|