@voltro/plugin-notifications 0.24.0 → 0.26.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/dist/index.js CHANGED
@@ -1,18 +1,18 @@
1
- import { clearQuietHoursDescriptor as e, inboxDescriptor as t, markReadDescriptor as n, notificationsRpcClientImports as r, preferencesDescriptor as i, setPreferenceDescriptor as a, setQuietHoursDescriptor as o, subscribeDescriptor as s, unreadCountDescriptor as c, unsubscribeDescriptor as l } from "./rpc.js";
2
- import { Context as u, Effect as d, Layer as f, Schema as p } from "effect";
3
- import { and as m, boolean as h, eq as g, generateId as _, id as v, integer as y, isNotNull as ee, isNull as b, json as x, lte as S, registerRetention as C, retentionTtlMsFromEnv as w, table as T, text as E, timestamp as D } from "@voltro/database";
4
- import { definePlugin as O } from "@voltro/protocol";
5
- import { randomUUID as k } from "node:crypto";
1
+ import { archiveDescriptor as e, clearQuietHoursDescriptor as t, inboxDescriptor as n, markAllReadDescriptor as r, markReadDescriptor as i, markUnreadDescriptor as a, notificationsRpcClientImports as o, preferencesDescriptor as s, setPreferenceDescriptor as c, setQuietHoursDescriptor as l, subscribeDescriptor as u, unarchiveDescriptor as ee, unreadCountDescriptor as d, unsubscribeDescriptor as te } from "./rpc.js";
2
+ import { Context as f, Effect as p, Layer as m, Schema as h } from "effect";
3
+ import { and as g, boolean as _, eq as v, generateId as y, id as b, integer as x, isNotNull as ne, isNull as S, json as C, lte as w, registerRetention as T, retentionTtlMsFromEnv as E, table as D, text as O, timestamp as k } from "@voltro/database";
4
+ import { definePlugin as A } from "@voltro/protocol";
5
+ import { randomUUID as j } from "node:crypto";
6
6
  //#region src/errors.ts
7
- var A = class extends p.TaggedError()("PushTokenRejected", {
8
- token: p.String,
9
- reason: p.String
10
- }) {}, j = () => ({
7
+ var M = class extends h.TaggedError()("PushTokenRejected", {
8
+ token: h.String,
9
+ reason: h.String
10
+ }) {}, N = () => ({
11
11
  id: "console",
12
12
  deliver: async (e) => {
13
13
  process.stdout.write(`[notify:${e.category}] → ${e.to}: ${e.title} — ${e.body}\n`);
14
14
  }
15
- }), M = (e) => ({
15
+ }), P = (e) => ({
16
16
  id: e.id ?? "slack",
17
17
  deliver: async (t) => {
18
18
  let n = e.format ? e.format(t) : { text: `*${t.title}*\n${t.body}` }, r = await fetch(e.url, {
@@ -25,7 +25,7 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
25
25
  });
26
26
  if (!r.ok) throw Error(`webhook ${e.id ?? "slack"} POST ${r.status}`);
27
27
  }
28
- }), N = (e) => ({
28
+ }), F = (e) => ({
29
29
  id: "email",
30
30
  deliver: (t) => e({
31
31
  to: t.to,
@@ -33,13 +33,13 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
33
33
  html: `<p>${t.body}</p>`,
34
34
  text: t.body
35
35
  })
36
- }), P = (e) => ({
36
+ }), I = (e) => ({
37
37
  id: "sms",
38
38
  deliver: (t) => e(t.to, `${t.title}: ${t.body}`)
39
- }), F = (e, t) => ({
39
+ }), re = (e, t) => ({
40
40
  id: e,
41
41
  deliver: t
42
- }), I = (e) => ({
42
+ }), ie = (e) => ({
43
43
  id: e.id ?? "push",
44
44
  deliver: async (t) => {
45
45
  let n = await e.tokensFor(t.to), r = t.data?.badge, i = typeof r == "number" ? r : void 0;
@@ -54,7 +54,7 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
54
54
  try {
55
55
  await e.transport(n);
56
56
  } catch (e) {
57
- throw e instanceof A ? e : new A({
57
+ throw e instanceof M ? e : new M({
58
58
  token: r,
59
59
  reason: e instanceof Error ? e.message : String(e)
60
60
  });
@@ -71,6 +71,7 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
71
71
  e.unshift({
72
72
  ...t,
73
73
  readAt: null,
74
+ archivedAt: null,
74
75
  data: t.data ?? {},
75
76
  tenantId: t.tenantId ?? null
76
77
  });
@@ -86,6 +87,31 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
86
87
  readAt: (/* @__PURE__ */ new Date(0)).toISOString()
87
88
  }, !0);
88
89
  },
90
+ markUnread: async (t, n) => {
91
+ let r = e.findIndex((e) => e.id === t && e.subjectId === n);
92
+ return r === -1 ? !1 : (e[r] = {
93
+ ...e[r],
94
+ readAt: null
95
+ }, !0);
96
+ },
97
+ markAllRead: async (t) => {
98
+ let n = 0;
99
+ for (let r = 0; r < e.length; r++) {
100
+ let i = e[r];
101
+ i.subjectId !== t || i.readAt !== null || (e[r] = {
102
+ ...i,
103
+ readAt: (/* @__PURE__ */ new Date(0)).toISOString()
104
+ }, n++);
105
+ }
106
+ return n;
107
+ },
108
+ setArchived: async (t, n, r) => {
109
+ let i = e.findIndex((e) => e.id === t && e.subjectId === n);
110
+ return i === -1 ? !1 : (e[i] = {
111
+ ...e[i],
112
+ archivedAt: r ? (/* @__PURE__ */ new Date(0)).toISOString() : null
113
+ }, !0);
114
+ },
89
115
  unreadCount: async (t) => e.filter((e) => e.subjectId === t && e.readAt === null).length,
90
116
  getPreferences: async (e) => [...t.values()].filter((t) => t.subjectId === e),
91
117
  setPreference: async (e) => {
@@ -135,6 +161,7 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
135
161
  body: String(e.body),
136
162
  data: e.data ?? {},
137
163
  readAt: e.readAt == null ? null : z(e.readAt),
164
+ archivedAt: e.archivedAt == null ? null : z(e.archivedAt),
138
165
  createdAt: z(e.createdAt),
139
166
  tenantId: e.tenantId ?? null
140
167
  }), r = (e) => ({
@@ -182,22 +209,30 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
182
209
  body: t.body,
183
210
  data: t.data ?? {},
184
211
  readAt: null,
212
+ archivedAt: null,
185
213
  createdAt: t.createdAt,
186
214
  tenantId: t.tenantId ?? null
187
215
  });
188
216
  },
189
217
  listInbox: async (e, r) => {
190
- let i = r?.unreadOnly ? m(g("subjectId", e), b("readAt")) : g("subjectId", e);
218
+ let i = r?.unreadOnly ? g(v("subjectId", e), S("readAt")) : v("subjectId", e);
191
219
  return (await t("_voltro_notification_inbox", i, [{
192
220
  column: "createdAt",
193
221
  direction: "desc"
194
222
  }], r?.limit)).map(n);
195
223
  },
196
- markRead: async (n, r) => (await t("_voltro_notification_inbox", m(g("id", n), g("subjectId", r)), [], 1))[0] ? (await e.update("_voltro_notification_inbox", n, { readAt: (/* @__PURE__ */ new Date()).toISOString() }), !0) : !1,
197
- unreadCount: async (e) => (await t("_voltro_notification_inbox", m(g("subjectId", e), b("readAt")), [], void 0)).length,
198
- getPreferences: async (e) => (await t("_voltro_notification_preferences", g("subjectId", e), [], void 0)).map(r),
224
+ markRead: async (n, r) => (await t("_voltro_notification_inbox", g(v("id", n), v("subjectId", r)), [], 1))[0] ? (await e.update("_voltro_notification_inbox", n, { readAt: (/* @__PURE__ */ new Date()).toISOString() }), !0) : !1,
225
+ markUnread: async (n, r) => (await t("_voltro_notification_inbox", g(v("id", n), v("subjectId", r)), [], 1))[0] ? (await e.update("_voltro_notification_inbox", n, { readAt: null }), !0) : !1,
226
+ markAllRead: async (n) => {
227
+ let r = await t("_voltro_notification_inbox", g(v("subjectId", n), S("readAt")), [], void 0), i = (/* @__PURE__ */ new Date()).toISOString();
228
+ for (let t of r) await e.update("_voltro_notification_inbox", String(t.id), { readAt: i });
229
+ return r.length;
230
+ },
231
+ setArchived: async (n, r, i) => (await t("_voltro_notification_inbox", g(v("id", n), v("subjectId", r)), [], 1))[0] ? (await e.update("_voltro_notification_inbox", n, { archivedAt: i ? (/* @__PURE__ */ new Date()).toISOString() : null }), !0) : !1,
232
+ unreadCount: async (e) => (await t("_voltro_notification_inbox", g(v("subjectId", e), S("readAt")), [], void 0)).length,
233
+ getPreferences: async (e) => (await t("_voltro_notification_preferences", v("subjectId", e), [], void 0)).map(r),
199
234
  setPreference: async (t) => {
200
- let n = _({
235
+ let n = y({
201
236
  kind: "typeid",
202
237
  prefix: "notifpref"
203
238
  }, "_voltro_notification_preferences");
@@ -232,7 +267,7 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
232
267
  direction: "desc"
233
268
  }], e?.limit)).map(i),
234
269
  subscribeTopic: async (t) => {
235
- let n = _({
270
+ let n = y({
236
271
  kind: "typeid",
237
272
  prefix: "notiftsub"
238
273
  }, "_voltro_notification_topic_subscriptions");
@@ -247,16 +282,16 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
247
282
  });
248
283
  },
249
284
  unsubscribeTopic: async (n, r) => {
250
- let i = await t("_voltro_notification_topic_subscriptions", m(g("topic", n), g("subjectId", r)), [], 1);
285
+ let i = await t("_voltro_notification_topic_subscriptions", g(v("topic", n), v("subjectId", r)), [], 1);
251
286
  i[0] && await e.delete("_voltro_notification_topic_subscriptions", String(i[0].id));
252
287
  },
253
- listTopicSubscribers: async (e) => (await t("_voltro_notification_topic_subscriptions", g("topic", e), [], void 0)).map(a),
288
+ listTopicSubscribers: async (e) => (await t("_voltro_notification_topic_subscriptions", v("topic", e), [], void 0)).map(a),
254
289
  getQuietHours: async (e) => {
255
- let n = await t("_voltro_notification_quiet_hours", g("subjectId", e), [], 1);
290
+ let n = await t("_voltro_notification_quiet_hours", v("subjectId", e), [], 1);
256
291
  return n[0] ? o(n[0]) : null;
257
292
  },
258
293
  setQuietHours: async (t) => {
259
- let n = _({
294
+ let n = y({
260
295
  kind: "typeid",
261
296
  prefix: "notifqh"
262
297
  }, "_voltro_notification_quiet_hours");
@@ -278,7 +313,7 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
278
313
  });
279
314
  },
280
315
  clearQuietHours: async (n) => {
281
- let r = await t("_voltro_notification_quiet_hours", g("subjectId", n), [], 1);
316
+ let r = await t("_voltro_notification_quiet_hours", v("subjectId", n), [], 1);
282
317
  r[0] && await e.delete("_voltro_notification_quiet_hours", String(r[0].id));
283
318
  },
284
319
  enqueueHeld: async (t) => {
@@ -290,11 +325,11 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
290
325
  flushAt: t.flushAt
291
326
  });
292
327
  },
293
- pendingDigest: async (e) => (await t("_voltro_notification_held", m(g("kind", "digest"), g("subjectId", e)), [{
328
+ pendingDigest: async (e) => (await t("_voltro_notification_held", g(v("kind", "digest"), v("subjectId", e)), [{
294
329
  column: "flushAt",
295
330
  direction: "asc"
296
331
  }], void 0)).map(s),
297
- dueHeld: async (e) => (await t("_voltro_notification_held", S("flushAt", e), [{
332
+ dueHeld: async (e) => (await t("_voltro_notification_held", w("flushAt", e), [{
298
333
  column: "flushAt",
299
334
  direction: "asc"
300
335
  }], void 0)).map(s),
@@ -306,7 +341,7 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
306
341
  id: "inApp",
307
342
  deliver: async (t) => {
308
343
  await e.addInbox({
309
- id: k(),
344
+ id: j(),
310
345
  subjectId: t.to,
311
346
  category: t.category,
312
347
  title: t.title,
@@ -349,7 +384,7 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
349
384
  let a = t.get(n);
350
385
  if (!a) continue;
351
386
  let o = {
352
- id: k(),
387
+ id: j(),
353
388
  to: i.to,
354
389
  category: i.category,
355
390
  channel: n,
@@ -385,7 +420,7 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
385
420
  skipped: [],
386
421
  held: "quiet-drop"
387
422
  } : (await e.store.enqueueHeld({
388
- id: k(),
423
+ id: j(),
389
424
  kind: "quiet",
390
425
  subjectId: t.to,
391
426
  send: t,
@@ -399,7 +434,7 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
399
434
  if (i > 0 && !t.channels) {
400
435
  let n = (await e.store.pendingDigest(t.to))[0]?.flushAt ?? new Date(o.getTime() + i).toISOString();
401
436
  return await e.store.enqueueHeld({
402
- id: k(),
437
+ id: j(),
403
438
  kind: "digest",
404
439
  subjectId: t.to,
405
440
  send: t,
@@ -418,6 +453,9 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
418
453
  inbox: (t, n) => e.store.listInbox(t, n),
419
454
  unreadCount: (t) => e.store.unreadCount(t),
420
455
  markRead: (t, n) => e.store.markRead(t, n),
456
+ markUnread: (t, n) => e.store.markUnread(t, n),
457
+ markAllRead: (t) => e.store.markAllRead(t),
458
+ setArchived: (t, n, r) => e.store.setArchived(t, n, r),
421
459
  getPreferences: (t) => e.store.getPreferences(t),
422
460
  setPreference: (t) => e.store.setPreference(t),
423
461
  subscribe: (t, n, r) => e.store.subscribeTopic({
@@ -450,11 +488,11 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
450
488
  let t = i.get(e.subjectId) ?? [];
451
489
  t.push(e), i.set(e.subjectId, t);
452
490
  }
453
- for (let [e, t] of i) await a(te(e, t));
491
+ for (let [e, t] of i) await a(ae(e, t));
454
492
  return await e.store.clearHeld(n.map((e) => e.id)), n.length;
455
493
  }
456
494
  };
457
- }, te = (e, t) => {
495
+ }, ae = (e, t) => {
458
496
  if (t.length === 1) return t[0].send;
459
497
  let n = t.map((e) => ({
460
498
  category: e.send.category,
@@ -469,118 +507,142 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
469
507
  data: { items: n },
470
508
  tenantId: t[0].send.tenantId ?? null
471
509
  };
472
- }, K = T("_voltro_notification_inbox", {
473
- id: v({ prefix: "notif" }),
474
- subjectId: E(),
475
- category: E(),
476
- title: E(),
477
- body: E(),
478
- data: x(),
479
- readAt: D().nullable(),
480
- createdAt: D().default("now"),
481
- tenantId: E().nullable()
482
- }).renamedFrom("notification_inbox").index("byInboxSubject", ["subjectId"]), q = T("_voltro_notification_preferences", {
483
- id: v({ prefix: "notifpref" }),
484
- subjectId: E(),
485
- category: E(),
486
- channel: E(),
487
- enabled: h().default(!0)
510
+ }, K = D("_voltro_notification_inbox", {
511
+ id: b({ prefix: "notif" }),
512
+ subjectId: O(),
513
+ category: O(),
514
+ title: O(),
515
+ body: O(),
516
+ data: C(),
517
+ readAt: k().nullable(),
518
+ archivedAt: k().nullable(),
519
+ createdAt: k().default("now"),
520
+ tenantId: O().nullable()
521
+ }).renamedFrom("notification_inbox").index("byInboxSubject", ["subjectId"]), q = D("_voltro_notification_preferences", {
522
+ id: b({ prefix: "notifpref" }),
523
+ subjectId: O(),
524
+ category: O(),
525
+ channel: O(),
526
+ enabled: _().default(!0)
488
527
  }).renamedFrom("notification_preferences").unique("byPreferencesSubjectCategoryChannel", [
489
528
  "subjectId",
490
529
  "category",
491
530
  "channel"
492
- ]), J = T("_voltro_notification_deliveries", {
493
- id: v({ prefix: "notifdlv" }),
494
- recipient: E(),
495
- category: E(),
496
- channel: E(),
497
- status: E(),
498
- error: E().nullable(),
499
- at: D().default("now")
500
- }).renamedFrom("notification_deliveries").index("byDeliveriesAt", ["at"]), Y = T("_voltro_notification_topic_subscriptions", {
501
- id: v({ prefix: "notiftsub" }),
502
- topic: E(),
503
- subjectId: E(),
504
- tenantId: E().nullable()
505
- }).renamedFrom("notification_topic_subscriptions").unique("byTopicSubject", ["topic", "subjectId"]).index("byTopic", ["topic"]), X = T("_voltro_notification_quiet_hours", {
506
- id: v({ prefix: "notifqh" }),
507
- subjectId: E(),
508
- startMinute: y(),
509
- endMinute: y(),
510
- tz: E().default("UTC"),
511
- policy: E().default("hold")
512
- }).renamedFrom("notification_quiet_hours").unique("byQuietHoursSubject", ["subjectId"]), Z = T("_voltro_notification_held", {
513
- id: v({ prefix: "notifheld" }),
514
- kind: E(),
515
- subjectId: E(),
516
- send: x(),
517
- flushAt: D()
518
- }).renamedFrom("notification_held").index("byHeldFlushAt", ["flushAt"]).index("byHeldSubjectKind", ["subjectId", "kind"]), Q = class extends u.Tag("@voltro/plugin-notifications/NotificationService")() {}, $ = (e) => e.request.subject.id ?? "anonymous", ne = (e) => e.request.subject.tenantId ?? null, re = (u = {}) => {
519
- let p = u.name ? `@voltro/plugin-notifications#${u.name}` : "@voltro/plugin-notifications";
520
- C({
531
+ ]), J = D("_voltro_notification_deliveries", {
532
+ id: b({ prefix: "notifdlv" }),
533
+ recipient: O(),
534
+ category: O(),
535
+ channel: O(),
536
+ status: O(),
537
+ error: O().nullable(),
538
+ at: k().default("now")
539
+ }).renamedFrom("notification_deliveries").index("byDeliveriesAt", ["at"]), Y = D("_voltro_notification_topic_subscriptions", {
540
+ id: b({ prefix: "notiftsub" }),
541
+ topic: O(),
542
+ subjectId: O(),
543
+ tenantId: O().nullable()
544
+ }).renamedFrom("notification_topic_subscriptions").unique("byTopicSubject", ["topic", "subjectId"]).index("byTopic", ["topic"]), X = D("_voltro_notification_quiet_hours", {
545
+ id: b({ prefix: "notifqh" }),
546
+ subjectId: O(),
547
+ startMinute: x(),
548
+ endMinute: x(),
549
+ tz: O().default("UTC"),
550
+ policy: O().default("hold")
551
+ }).renamedFrom("notification_quiet_hours").unique("byQuietHoursSubject", ["subjectId"]), Z = D("_voltro_notification_held", {
552
+ id: b({ prefix: "notifheld" }),
553
+ kind: O(),
554
+ subjectId: O(),
555
+ send: C(),
556
+ flushAt: k()
557
+ }).renamedFrom("notification_held").index("byHeldFlushAt", ["flushAt"]).index("byHeldSubjectKind", ["subjectId", "kind"]), Q = class extends f.Tag("@voltro/plugin-notifications/NotificationService")() {}, $ = (e) => e.request.subject.id ?? "anonymous", oe = (e) => e.request.subject.tenantId ?? null, se = (f = {}) => {
558
+ let h = f.name ? `@voltro/plugin-notifications#${f.name}` : "@voltro/plugin-notifications";
559
+ T({
521
560
  table: "_voltro_notification_deliveries",
522
561
  timeColumn: "at",
523
- ttlMs: w(process.env.VOLTRO_NOTIFICATION_DELIVERIES_TTL_HOURS, 2160)
524
- }), C({
562
+ ttlMs: E(process.env.VOLTRO_NOTIFICATION_DELIVERIES_TTL_HOURS, 2160)
563
+ }), T({
525
564
  table: "_voltro_notification_inbox",
526
565
  timeColumn: "createdAt",
527
- ttlMs: w(process.env.VOLTRO_NOTIFICATION_INBOX_READ_TTL_HOURS, 4320),
528
- where: ee("readAt")
529
- }), C({
566
+ ttlMs: E(process.env.VOLTRO_NOTIFICATION_INBOX_READ_TTL_HOURS, 4320),
567
+ where: ne("readAt")
568
+ }), T({
530
569
  table: "_voltro_notification_held",
531
570
  timeColumn: "flushAt",
532
- ttlMs: w(process.env.VOLTRO_NOTIFICATION_HELD_TTL_HOURS, 168)
571
+ ttlMs: E(process.env.VOLTRO_NOTIFICATION_HELD_TTL_HOURS, 168)
533
572
  });
534
- let m = u.store === void 0, h = { current: u.store ?? R() }, g = {
535
- addInbox: (e) => h.current.addInbox(e),
536
- listInbox: (e, t) => h.current.listInbox(e, t),
537
- markRead: (e, t) => h.current.markRead(e, t),
538
- unreadCount: (e) => h.current.unreadCount(e),
539
- getPreferences: (e) => h.current.getPreferences(e),
540
- setPreference: (e) => h.current.setPreference(e),
541
- recordDelivery: (e) => h.current.recordDelivery(e),
542
- listDeliveries: (e) => h.current.listDeliveries(e),
543
- subscribeTopic: (e) => h.current.subscribeTopic(e),
544
- unsubscribeTopic: (e, t) => h.current.unsubscribeTopic(e, t),
545
- listTopicSubscribers: (e) => h.current.listTopicSubscribers(e),
546
- getQuietHours: (e) => h.current.getQuietHours(e),
547
- setQuietHours: (e) => h.current.setQuietHours(e),
548
- clearQuietHours: (e) => h.current.clearQuietHours(e),
549
- enqueueHeld: (e) => h.current.enqueueHeld(e),
550
- pendingDigest: (e) => h.current.pendingDigest(e),
551
- dueHeld: (e) => h.current.dueHeld(e),
552
- clearHeld: (e) => h.current.clearHeld(e)
553
- }, _ = u.channels ?? [j()], v = _.some((e) => e.id === "inApp") ? _ : [..._, V(g)], y = u.digestWindowMs ?? 0, b = G({
554
- channels: v,
555
- store: g,
556
- digestWindowMs: y
557
- }), x, S = [
573
+ let g = f.store === void 0, _ = { current: f.store ?? R() }, v = {
574
+ addInbox: (e) => _.current.addInbox(e),
575
+ listInbox: (e, t) => _.current.listInbox(e, t),
576
+ markRead: (e, t) => _.current.markRead(e, t),
577
+ markUnread: (e, t) => _.current.markUnread(e, t),
578
+ markAllRead: (e) => _.current.markAllRead(e),
579
+ setArchived: (e, t, n) => _.current.setArchived(e, t, n),
580
+ unreadCount: (e) => _.current.unreadCount(e),
581
+ getPreferences: (e) => _.current.getPreferences(e),
582
+ setPreference: (e) => _.current.setPreference(e),
583
+ recordDelivery: (e) => _.current.recordDelivery(e),
584
+ listDeliveries: (e) => _.current.listDeliveries(e),
585
+ subscribeTopic: (e) => _.current.subscribeTopic(e),
586
+ unsubscribeTopic: (e, t) => _.current.unsubscribeTopic(e, t),
587
+ listTopicSubscribers: (e) => _.current.listTopicSubscribers(e),
588
+ getQuietHours: (e) => _.current.getQuietHours(e),
589
+ setQuietHours: (e) => _.current.setQuietHours(e),
590
+ clearQuietHours: (e) => _.current.clearQuietHours(e),
591
+ enqueueHeld: (e) => _.current.enqueueHeld(e),
592
+ pendingDigest: (e) => _.current.pendingDigest(e),
593
+ dueHeld: (e) => _.current.dueHeld(e),
594
+ clearHeld: (e) => _.current.clearHeld(e)
595
+ }, y = f.channels ?? [N()], b = y.some((e) => e.id === "inApp") ? y : [...y, V(v)], x = f.digestWindowMs ?? 0, S = G({
596
+ channels: b,
597
+ store: v,
598
+ digestWindowMs: x
599
+ }), C, w = [
558
600
  {
559
- ...t,
601
+ ...n,
560
602
  description: "The caller's in-app inbox items.",
561
- execute: (e, t) => d.promise(() => b.inbox($(t), e))
603
+ execute: (e, t) => p.promise(() => S.inbox($(t), e))
562
604
  },
563
605
  {
564
- ...c,
606
+ ...d,
565
607
  description: "The caller's unread count.",
566
- execute: (e, t) => d.promise(async () => ({ count: await b.unreadCount($(t)) }))
608
+ execute: (e, t) => p.promise(async () => ({ count: await S.unreadCount($(t)) }))
567
609
  },
568
610
  {
569
- ...n,
611
+ ...i,
570
612
  description: "Mark an inbox item read.",
571
- execute: (e, t) => d.promise(async () => ({ ok: await b.markRead(e.id, $(t)) }))
613
+ execute: (e, t) => p.promise(async () => ({ ok: await S.markRead(e.id, $(t)) }))
572
614
  },
573
615
  {
574
- ...i,
616
+ ...a,
617
+ description: "Mark an inbox item unread again.",
618
+ execute: (e, t) => p.promise(async () => ({ ok: await S.markUnread(e.id, $(t)) }))
619
+ },
620
+ {
621
+ ...r,
622
+ description: "Mark every unread item in the caller's inbox read.",
623
+ execute: (e, t) => p.promise(async () => ({ count: await S.markAllRead($(t)) }))
624
+ },
625
+ {
626
+ ...e,
627
+ description: "Archive an inbox item — a different state from read; this is what empties the inbox.",
628
+ execute: (e, t) => p.promise(async () => ({ ok: await S.setArchived(e.id, $(t), !0) }))
629
+ },
630
+ {
631
+ ...ee,
632
+ description: "Return an archived item to the inbox.",
633
+ execute: (e, t) => p.promise(async () => ({ ok: await S.setArchived(e.id, $(t), !1) }))
634
+ },
635
+ {
636
+ ...s,
575
637
  description: "The caller's channel preferences.",
576
- execute: (e, t) => d.promise(() => b.getPreferences($(t)))
638
+ execute: (e, t) => p.promise(() => S.getPreferences($(t)))
577
639
  },
578
640
  {
579
- ...a,
641
+ ...c,
580
642
  description: "Toggle a channel on/off for a category.",
581
- execute: (e, t) => d.promise(async () => {
643
+ execute: (e, t) => p.promise(async () => {
582
644
  let { category: n, channel: r, enabled: i } = e;
583
- return await b.setPreference({
645
+ return await S.setPreference({
584
646
  subjectId: $(t),
585
647
  category: n,
586
648
  channel: r,
@@ -589,21 +651,21 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
589
651
  })
590
652
  },
591
653
  {
592
- ...s,
654
+ ...u,
593
655
  description: "Subscribe the caller to a broadcast topic.",
594
- execute: (e, t) => d.promise(async () => (await b.subscribe(e.topic, $(t), ne(t)), { ok: !0 }))
656
+ execute: (e, t) => p.promise(async () => (await S.subscribe(e.topic, $(t), oe(t)), { ok: !0 }))
595
657
  },
596
658
  {
597
- ...l,
659
+ ...te,
598
660
  description: "Unsubscribe the caller from a broadcast topic.",
599
- execute: (e, t) => d.promise(async () => (await b.unsubscribe(e.topic, $(t)), { ok: !0 }))
661
+ execute: (e, t) => p.promise(async () => (await S.unsubscribe(e.topic, $(t)), { ok: !0 }))
600
662
  },
601
663
  {
602
- ...o,
664
+ ...l,
603
665
  description: "Set the caller's quiet-hours (DND) window.",
604
- execute: (e, t) => d.promise(async () => {
666
+ execute: (e, t) => p.promise(async () => {
605
667
  let n = e;
606
- return await b.setQuietHours({
668
+ return await S.setQuietHours({
607
669
  subjectId: $(t),
608
670
  startMinute: n.startMinute,
609
671
  endMinute: n.endMinute,
@@ -613,20 +675,20 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
613
675
  })
614
676
  },
615
677
  {
616
- ...e,
678
+ ...t,
617
679
  description: "Clear the caller's quiet-hours window.",
618
- execute: (e, t) => d.promise(async () => (await b.clearQuietHours($(t)), { ok: !0 }))
680
+ execute: (e, t) => p.promise(async () => (await S.clearQuietHours($(t)), { ok: !0 }))
619
681
  }
620
- ], T = (e) => ({
682
+ ], D = (e) => ({
621
683
  kind: "json",
622
684
  data: e
623
- }), E = (e, t) => ({
685
+ }), O = (e, t) => ({
624
686
  kind: "json",
625
687
  status: e,
626
688
  data: { error: t }
627
- }), D = (e, t) => new URLSearchParams(new URL(e, "http://x").search).get(t);
628
- return O({
629
- name: p,
689
+ }), k = (e, t) => new URLSearchParams(new URL(e, "http://x").search).get(t);
690
+ return A({
691
+ name: h,
630
692
  description: "Unified notifications — email/slack/sms/push/in-app with preference routing, inbox + delivery log.",
631
693
  permissions: ["inspect:read", "store:write"],
632
694
  extendSchema: { tables: [
@@ -637,19 +699,19 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
637
699
  X,
638
700
  Z
639
701
  ] },
640
- services: f.succeed(Q, b),
641
- routes: S,
642
- rpcClientDescriptors: r,
702
+ services: m.succeed(Q, S),
703
+ routes: w,
704
+ rpcClientDescriptors: o,
643
705
  inspectEndpoints: [{
644
706
  method: "GET",
645
707
  path: "/deliveries",
646
708
  description: "Recent delivery records (newest first) + per-channel + per-status counts.",
647
- handler: () => d.promise(async () => {
648
- let e = await g.listDeliveries({ limit: 200 }), t = {}, n = 0, r = 0;
709
+ handler: () => p.promise(async () => {
710
+ let e = await v.listDeliveries({ limit: 200 }), t = {}, n = 0, r = 0;
649
711
  for (let i of e) t[i.channel] = (t[i.channel] ?? 0) + 1, i.status === "sent" ? n++ : r++;
650
- return T({
712
+ return D({
651
713
  deliveries: e.slice(0, 100),
652
- channels: v.map((e) => e.id),
714
+ channels: b.map((e) => e.id),
653
715
  stats: {
654
716
  sent: n,
655
717
  failed: r,
@@ -662,28 +724,28 @@ var A = class extends p.TaggedError()("PushTokenRejected", {
662
724
  path: "/inbox",
663
725
  description: "In-app inbox for a subject (?subjectId=…).",
664
726
  handler: (e) => {
665
- let t = D(e.url, "subjectId");
666
- return t ? d.promise(async () => T({
667
- items: await g.listInbox(t, { limit: 100 }),
668
- unread: await g.unreadCount(t)
669
- })) : d.succeed(E(400, "subjectId query param required"));
727
+ let t = k(e.url, "subjectId");
728
+ return t ? p.promise(async () => D({
729
+ items: await v.listInbox(t, { limit: 100 }),
730
+ unread: await v.unreadCount(t)
731
+ })) : p.succeed(O(400, "subjectId query param required"));
670
732
  }
671
733
  }],
672
734
  bindDataStore: (e) => {
673
- m && (h.current = B(e));
674
- let t = u.flushIntervalMs ?? 3e4;
675
- x = setInterval(() => {
676
- b.flushDue().catch(() => {});
677
- }, t), typeof x.unref == "function" && x.unref();
678
- },
679
- onActivate: (e) => d.sync(() => e.logger.info("notifications active", {
680
- channels: v.map((e) => e.id),
681
- digestWindowMs: y
735
+ g && (_.current = B(e));
736
+ let t = f.flushIntervalMs ?? 3e4;
737
+ C = setInterval(() => {
738
+ S.flushDue().catch(() => {});
739
+ }, t), typeof C.unref == "function" && C.unref();
740
+ },
741
+ onActivate: (e) => p.sync(() => e.logger.info("notifications active", {
742
+ channels: b.map((e) => e.id),
743
+ digestWindowMs: x
682
744
  })),
683
- onDeactivate: () => d.sync(() => {
684
- x && clearInterval(x), x = void 0;
745
+ onDeactivate: () => p.sync(() => {
746
+ C && clearInterval(C), C = void 0;
685
747
  })
686
748
  });
687
749
  };
688
750
  //#endregion
689
- export { Q as NotificationService, A as PushTokenRejected, G as buildNotificationService, j as consoleChannel, F as customChannel, B as dataStoreNotificationStore, J as deliveriesTable, N as emailChannel, Z as heldTable, V as inAppChannel, U as inQuietHours, K as inboxTable, R as memoryNotificationStore, H as minuteOfDayInZone, re as notificationsPlugin, q as preferencesTable, I as pushChannel, W as quietHoursEnd, X as quietHoursTable, L as resolveChannels, P as smsChannel, Y as topicSubscriptionsTable, M as webhookChannel };
751
+ export { Q as NotificationService, M as PushTokenRejected, G as buildNotificationService, N as consoleChannel, re as customChannel, B as dataStoreNotificationStore, J as deliveriesTable, F as emailChannel, Z as heldTable, V as inAppChannel, U as inQuietHours, K as inboxTable, R as memoryNotificationStore, H as minuteOfDayInZone, se as notificationsPlugin, q as preferencesTable, ie as pushChannel, W as quietHoursEnd, X as quietHoursTable, L as resolveChannels, I as smsChannel, Y as topicSubscriptionsTable, P as webhookChannel };