@porulle/plugin-notifications 0.64.0 → 0.65.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.
@@ -40,8 +40,6 @@ export declare class NotificationService {
40
40
  data?: Record<string, unknown>;
41
41
  metadata?: Record<string, unknown>;
42
42
  }): Promise<Result<NotificationLogEntry>>;
43
- sendSMS(orgId: string, to: string, body: string): Promise<Result<NotificationLogEntry>>;
44
- sendPush(orgId: string, deviceToken: string, title: string, body: string, data?: Record<string, unknown>): Promise<Result<NotificationLogEntry>>;
45
43
  listLog(orgId: string, filters?: {
46
44
  channel?: string;
47
45
  event?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"notification-service.d.ts","sourceRoot":"","sources":["../../src/services/notification-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"notification-service.d.ts","sourceRoot":"","sources":["../../src/services/notification-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,EAAE,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,OAAO,EAAE,kBAAkB,EAC3E,MAAM,EACP,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAA2B,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE1F,qBAAa,mBAAmB;IAIlB,OAAO,CAAC,EAAE;IAHtB,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,WAAW,CAA0B;gBAEzB,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,oBAAoB;IAOrD,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;QACzC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;KACzE,GAAG,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAkBnC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAC;KACnC,GAAG,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAQrC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAO7E,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;QACrD,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;KAC7D,GAAG,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAanC,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAatF;;;;OAIG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM;IAcvE;;;OAGG;IACG,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;QAC/B,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,GAAG,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAgFnC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QACrC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;KAC/E,GAAG,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAC,CAAC;CAU5C"}
@@ -153,55 +153,6 @@ export class NotificationService {
153
153
  }).returning();
154
154
  return Ok(logRows[0]);
155
155
  }
156
- // ── Direct Channel Sends ───────────────────────────────────────────
157
- async sendSMS(orgId, to, body) {
158
- let adapterError;
159
- let adapterMessageId;
160
- if (this.smsAdapter) {
161
- const result = await this.smsAdapter.send({ to, body });
162
- if (!result.ok) {
163
- adapterError = result.error;
164
- }
165
- else {
166
- adapterMessageId = result.value.messageId;
167
- }
168
- }
169
- const status = adapterError ? "failed" : "sent";
170
- const rows = await this.db.insert(notificationLog).values({
171
- organizationId: orgId,
172
- channel: "sms",
173
- event: "direct.sms",
174
- recipient: to,
175
- status,
176
- error: adapterError,
177
- metadata: adapterMessageId ? { adapterMessageId } : {},
178
- }).returning();
179
- return Ok(rows[0]);
180
- }
181
- async sendPush(orgId, deviceToken, title, body, data) {
182
- let adapterError;
183
- let adapterMessageId;
184
- if (this.pushAdapter) {
185
- const result = await this.pushAdapter.send({ deviceToken, title, body, ...(data != null ? { data } : {}) });
186
- if (!result.ok) {
187
- adapterError = result.error;
188
- }
189
- else {
190
- adapterMessageId = result.value.messageId;
191
- }
192
- }
193
- const status = adapterError ? "failed" : "sent";
194
- const rows = await this.db.insert(notificationLog).values({
195
- organizationId: orgId,
196
- channel: "push",
197
- event: "direct.push",
198
- recipient: deviceToken,
199
- status,
200
- error: adapterError,
201
- metadata: adapterMessageId ? { adapterMessageId } : {},
202
- }).returning();
203
- return Ok(rows[0]);
204
- }
205
156
  // ── Log Queries ────────────────────────────────────────────────────
206
157
  async listLog(orgId, filters) {
207
158
  const conditions = [eq(notificationLog.organizationId, orgId)];
@@ -9,15 +9,5 @@ export declare class PreferenceService {
9
9
  setPreference(orgId: string, customerId: string, channel: PrefChannel, isEnabled: boolean, destination?: string): Promise<Result<CustomerNotificationPref>>;
10
10
  /** Get all notification preferences for a customer. */
11
11
  getPreferences(orgId: string, customerId: string): Promise<Result<CustomerNotificationPref[]>>;
12
- /**
13
- * Determine which channels should fire for a given customer+event.
14
- *
15
- * Logic:
16
- * 1. Find all active templates for the event.
17
- * 2. For each template channel (email/sms/push), check customer preferences.
18
- * 3. If no preference exists, the channel is considered enabled (opt-out model).
19
- * 4. Returns channels that have an active template AND are not disabled by the customer.
20
- */
21
- getActiveChannels(orgId: string, customerId: string, event: string): Promise<Result<PrefChannel[]>>;
22
12
  }
23
13
  //# sourceMappingURL=preference-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"preference-service.d.ts","sourceRoot":"","sources":["../../src/services/preference-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAAE,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrF,qBAAa,iBAAiB;IAChB,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,EAAE;IAE1B;;;OAGG;IACG,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC;IA2BjK,uDAAuD;IACjD,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,wBAAwB,EAAE,CAAC,CAAC;IASpG;;;;;;;;OAQG;IACG,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;CAiC1G"}
1
+ {"version":3,"file":"preference-service.d.ts","sourceRoot":"","sources":["../../src/services/preference-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAAE,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrF,qBAAa,iBAAiB;IAChB,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,EAAE;IAE1B;;;OAGG;IACG,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC;IA2BjK,uDAAuD;IACjD,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,wBAAwB,EAAE,CAAC,CAAC;CASrG"}
@@ -1,5 +1,5 @@
1
1
  import { eq, and } from "@porulle/core/drizzle";
2
- import { customerNotificationPrefs, notificationTemplates } from "../schema.js";
2
+ import { customerNotificationPrefs } from "../schema.js";
3
3
  import { Ok } from "../types.js";
4
4
  export class PreferenceService {
5
5
  db;
@@ -36,34 +36,4 @@ export class PreferenceService {
36
36
  .where(and(eq(customerNotificationPrefs.organizationId, orgId), eq(customerNotificationPrefs.customerId, customerId)));
37
37
  return Ok(rows);
38
38
  }
39
- /**
40
- * Determine which channels should fire for a given customer+event.
41
- *
42
- * Logic:
43
- * 1. Find all active templates for the event.
44
- * 2. For each template channel (email/sms/push), check customer preferences.
45
- * 3. If no preference exists, the channel is considered enabled (opt-out model).
46
- * 4. Returns channels that have an active template AND are not disabled by the customer.
47
- */
48
- async getActiveChannels(orgId, customerId, event) {
49
- // Find active templates for this event (email, sms, push only — not print)
50
- const templates = await this.db.select().from(notificationTemplates)
51
- .where(and(eq(notificationTemplates.organizationId, orgId), eq(notificationTemplates.event, event), eq(notificationTemplates.isActive, true)));
52
- const templateChannels = templates
53
- .map((t) => t.channel)
54
- .filter((ch) => ch === "email" || ch === "sms" || ch === "push");
55
- if (templateChannels.length === 0)
56
- return Ok([]);
57
- // Get customer preferences
58
- const prefs = await this.db.select().from(customerNotificationPrefs)
59
- .where(and(eq(customerNotificationPrefs.organizationId, orgId), eq(customerNotificationPrefs.customerId, customerId)));
60
- const prefMap = new Map(prefs.map((p) => [p.channel, p.isEnabled]));
61
- // Filter: include channel if no preference (opt-out model) or explicitly enabled
62
- const active = templateChannels.filter((ch) => {
63
- const enabled = prefMap.get(ch);
64
- return enabled !== false; // undefined (no pref) → allowed; true → allowed; false → blocked
65
- });
66
- // Deduplicate
67
- return Ok([...new Set(active)]);
68
- }
69
39
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@porulle/plugin-notifications",
3
- "version": "0.64.0",
3
+ "version": "0.65.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -19,14 +19,14 @@
19
19
  "dependencies": {
20
20
  "@hono/zod-openapi": "^1.2.2",
21
21
  "hono": "^4.12.5",
22
- "@porulle/core": "0.64.0"
22
+ "@porulle/core": "0.65.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^24.5.2",
26
26
  "typescript": "5.9.2",
27
27
  "vitest": "^3.2.4",
28
- "@porulle/typescript-config": "0.1.0",
29
- "@porulle/eslint-config": "0.1.0"
28
+ "@porulle/eslint-config": "0.1.0",
29
+ "@porulle/typescript-config": "0.1.0"
30
30
  },
31
31
  "publishConfig": {
32
32
  "access": "public"
@@ -191,68 +191,6 @@ export class NotificationService {
191
191
  return Ok(logRows[0]!);
192
192
  }
193
193
 
194
- // ── Direct Channel Sends ───────────────────────────────────────────
195
-
196
- async sendSMS(orgId: string, to: string, body: string): Promise<Result<NotificationLogEntry>> {
197
- let adapterError: string | undefined;
198
- let adapterMessageId: string | undefined;
199
-
200
- if (this.smsAdapter) {
201
- const result = await this.smsAdapter.send({ to, body });
202
- if (!result.ok) {
203
- adapterError = result.error;
204
- } else {
205
- adapterMessageId = result.value.messageId;
206
- }
207
- }
208
-
209
- const status: NotificationStatus = adapterError ? "failed" : "sent";
210
- const rows = await this.db.insert(notificationLog).values({
211
- organizationId: orgId,
212
- channel: "sms",
213
- event: "direct.sms",
214
- recipient: to,
215
- status,
216
- error: adapterError,
217
- metadata: adapterMessageId ? { adapterMessageId } : {},
218
- }).returning();
219
-
220
- return Ok(rows[0]!);
221
- }
222
-
223
- async sendPush(
224
- orgId: string,
225
- deviceToken: string,
226
- title: string,
227
- body: string,
228
- data?: Record<string, unknown>,
229
- ): Promise<Result<NotificationLogEntry>> {
230
- let adapterError: string | undefined;
231
- let adapterMessageId: string | undefined;
232
-
233
- if (this.pushAdapter) {
234
- const result = await this.pushAdapter.send({ deviceToken, title, body, ...(data != null ? { data } : {}) });
235
- if (!result.ok) {
236
- adapterError = result.error;
237
- } else {
238
- adapterMessageId = result.value.messageId;
239
- }
240
- }
241
-
242
- const status: NotificationStatus = adapterError ? "failed" : "sent";
243
- const rows = await this.db.insert(notificationLog).values({
244
- organizationId: orgId,
245
- channel: "push",
246
- event: "direct.push",
247
- recipient: deviceToken,
248
- status,
249
- error: adapterError,
250
- metadata: adapterMessageId ? { adapterMessageId } : {},
251
- }).returning();
252
-
253
- return Ok(rows[0]!);
254
- }
255
-
256
194
  // ── Log Queries ────────────────────────────────────────────────────
257
195
 
258
196
  async listLog(orgId: string, filters?: {
@@ -1,5 +1,5 @@
1
1
  import { eq, and } from "@porulle/core/drizzle";
2
- import { customerNotificationPrefs, notificationTemplates } from "../schema.js";
2
+ import { customerNotificationPrefs } from "../schema.js";
3
3
  import type { Db, CustomerNotificationPref, PrefChannel, Result } from "../types.js";
4
4
  import { Ok } from "../types.js";
5
5
 
@@ -47,46 +47,4 @@ export class PreferenceService {
47
47
  return Ok(rows);
48
48
  }
49
49
 
50
- /**
51
- * Determine which channels should fire for a given customer+event.
52
- *
53
- * Logic:
54
- * 1. Find all active templates for the event.
55
- * 2. For each template channel (email/sms/push), check customer preferences.
56
- * 3. If no preference exists, the channel is considered enabled (opt-out model).
57
- * 4. Returns channels that have an active template AND are not disabled by the customer.
58
- */
59
- async getActiveChannels(orgId: string, customerId: string, event: string): Promise<Result<PrefChannel[]>> {
60
- // Find active templates for this event (email, sms, push only — not print)
61
- const templates = await this.db.select().from(notificationTemplates)
62
- .where(and(
63
- eq(notificationTemplates.organizationId, orgId),
64
- eq(notificationTemplates.event, event),
65
- eq(notificationTemplates.isActive, true),
66
- ));
67
-
68
- const templateChannels = templates
69
- .map((t) => t.channel)
70
- .filter((ch): ch is PrefChannel => ch === "email" || ch === "sms" || ch === "push");
71
-
72
- if (templateChannels.length === 0) return Ok([]);
73
-
74
- // Get customer preferences
75
- const prefs = await this.db.select().from(customerNotificationPrefs)
76
- .where(and(
77
- eq(customerNotificationPrefs.organizationId, orgId),
78
- eq(customerNotificationPrefs.customerId, customerId),
79
- ));
80
-
81
- const prefMap = new Map(prefs.map((p) => [p.channel, p.isEnabled]));
82
-
83
- // Filter: include channel if no preference (opt-out model) or explicitly enabled
84
- const active = templateChannels.filter((ch) => {
85
- const enabled = prefMap.get(ch);
86
- return enabled !== false; // undefined (no pref) → allowed; true → allowed; false → blocked
87
- });
88
-
89
- // Deduplicate
90
- return Ok([...new Set(active)]);
91
- }
92
50
  }