av6-core 1.5.7 → 1.5.8

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.
Files changed (3) hide show
  1. package/dist/index.js +107 -62
  2. package/dist/index.mjs +107 -62
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2451,95 +2451,115 @@ var SmsProvider = class {
2451
2451
  constructor(logger = console, args) {
2452
2452
  this.logger = logger;
2453
2453
  this.args = args;
2454
- this.logger.info(`[NotificationService] args for sms : ${JSON.stringify(args)}`);
2454
+ const safeArgs = { ...args, apiKey: args.apiKey ? "***" : "" };
2455
+ this.logger.info(`[NotificationService] args for sms : ${JSON.stringify(safeArgs)}`);
2455
2456
  this.url = args.apiUrl || "https://web.nsemfua.com/api/http/sms/send";
2456
- this.token = "13|R3XfH6wHx3zdflBzpGMlNiTWKuOUEkGutORyGcuS";
2457
+ this.token = (args.apiKey || "").trim();
2457
2458
  this.sender = args.senderId || "AlmaMedLab";
2459
+ this.countryCode = (args.countryCode || "+233").trim();
2458
2460
  }
2459
2461
  url;
2460
2462
  token;
2461
2463
  sender;
2464
+ countryCode;
2465
+ /**
2466
+ * Single recipient wrapper (still same API).
2467
+ */
2462
2468
  async send(args) {
2463
- const to = args.recipient;
2464
- if (!to) {
2465
- return {
2466
- ok: false,
2467
- provider: "SMS" /* SMS */,
2468
- error: "Missing recipient.phone"
2469
- };
2469
+ const phone = args?.recipient?.phone;
2470
+ if (!phone) {
2471
+ return { ok: false, provider: "SMS" /* SMS */, error: "Missing recipient.phone" };
2472
+ }
2473
+ const bulk = await this.sendBulk({
2474
+ phones: [phone],
2475
+ message: args.body,
2476
+ includeMaster: true
2477
+ });
2478
+ if (!bulk.ok) {
2479
+ return { ok: false, provider: "SMS" /* SMS */, error: bulk.error, meta: bulk.meta };
2480
+ }
2481
+ return { ok: true, provider: "SMS" /* SMS */, externalId: bulk.externalId, meta: bulk.meta };
2482
+ }
2483
+ /**
2484
+ * ✅ Bulk SMS in ONE HTTP call (like PHP: recipient: "988.., 977..")
2485
+ */
2486
+ async sendBulk(args) {
2487
+ if (!this.url) {
2488
+ return { ok: false, provider: "SMS" /* SMS */, recipients: [], error: "Missing sms apiUrl" };
2470
2489
  }
2471
- const allRecipients = [to];
2472
- if (this.args.masterNumber) {
2473
- allRecipients.push({ phone: this.args.masterNumber });
2490
+ if (!this.token) {
2491
+ return { ok: false, provider: "SMS" /* SMS */, recipients: [], error: "Missing sms apiKey" };
2474
2492
  }
2475
- const recipients = this.normalizeRecipients(allRecipients);
2493
+ const includeMaster = args.includeMaster ?? true;
2494
+ const phones = [...args.phones ?? []];
2495
+ if (includeMaster && this.args.masterNumber) phones.push(this.args.masterNumber);
2496
+ const recipients = this.normalizePhones(phones);
2476
2497
  if (!recipients.length) {
2477
- return {
2478
- ok: false,
2479
- provider: "SMS" /* SMS */,
2480
- error: "Missing recipient.phone"
2481
- };
2498
+ return { ok: false, provider: "SMS" /* SMS */, recipients: [], error: "No valid phone recipients" };
2482
2499
  }
2500
+ const recipientStr = recipients.join(", ");
2483
2501
  try {
2484
2502
  const payload = {
2485
2503
  api_token: this.token,
2486
- recipient: recipients.join(", "),
2487
- // API accepts comma-separated list
2504
+ recipient: recipientStr,
2488
2505
  sender_id: this.sender,
2489
2506
  type: "plain",
2490
- message: this.cleanMessage(args.body)
2491
- // schedule_time: "" // add if you need scheduling
2507
+ message: this.cleanMessage(args.message)
2492
2508
  };
2493
2509
  const res = await fetch(this.url, {
2494
2510
  method: "POST",
2495
- headers: {
2496
- "Content-Type": "application/json",
2497
- Accept: "application/json"
2498
- },
2511
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
2499
2512
  body: JSON.stringify(payload)
2500
2513
  });
2501
2514
  const data = await res.json().catch(() => ({}));
2502
- const isError = data?.status === "error" || !res.ok;
2515
+ const isError = !res.ok || data?.status === "error";
2503
2516
  if (isError) {
2504
2517
  return {
2505
2518
  ok: false,
2506
2519
  provider: "SMS" /* SMS */,
2507
- error: data?.message || data?.error || `Gateway error (${res.status} ${res.statusText})`
2520
+ recipients,
2521
+ error: data?.message || data?.error || `Gateway error (${res.status} ${res.statusText})`,
2522
+ meta: data
2508
2523
  };
2509
2524
  }
2510
- const externalId = data?.id || data?.data?.id || data?.message_id || data?.data?.message_id || void 0;
2525
+ const externalId = data?.id || data?.data?.id || data?.message_id || data?.data?.message_id;
2511
2526
  return {
2512
2527
  ok: true,
2513
2528
  provider: "SMS" /* SMS */,
2529
+ recipients,
2514
2530
  externalId,
2515
- meta: {
2516
- gatewayStatus: data?.status,
2517
- recipient: payload.recipient
2518
- }
2531
+ meta: data
2519
2532
  };
2520
2533
  } catch (err) {
2521
2534
  return {
2522
2535
  ok: false,
2523
2536
  provider: "SMS" /* SMS */,
2537
+ recipients,
2524
2538
  error: err?.message ?? String(err)
2525
2539
  };
2526
2540
  }
2527
2541
  }
2528
- /** Accepts single/multiple Recipient objects and returns sanitized numbers */
2529
- normalizeRecipients(input) {
2530
- return input.map((r) => r?.phone || "").map((p) => this.stripCountryCode(p)).map((p) => this.onlyDialable(p)).filter(Boolean);
2531
- }
2532
- /** Match PHP: remove leading +233 (Ghana) only */
2533
- stripCountryCode(phone) {
2534
- return (phone || "").trim().replace(this.args.countryCode || "+233", "");
2535
- }
2536
- /** Remove spaces, dashes, parentheses; keep numbers only (and commas handled elsewhere) */
2537
- onlyDialable(phone) {
2538
- return phone.replace(/[^\d]/g, "");
2542
+ // ---------------- helpers ----------------
2543
+ /**
2544
+ * Normalize phones:
2545
+ * - trim
2546
+ * - strip leading country code digits if present (e.g., +233 or 233)
2547
+ * - keep digits only
2548
+ * - dedupe
2549
+ */
2550
+ normalizePhones(input) {
2551
+ const ccDigits = this.countryCode.replace(/[^\d]/g, "");
2552
+ const normalized = (input ?? []).map((x) => String(x ?? "").trim()).filter(Boolean).map((x) => x.replace(/[^\d]/g, "")).map((digits) => {
2553
+ if (ccDigits && digits.startsWith(ccDigits) && digits.length > ccDigits.length) {
2554
+ return digits.slice(ccDigits.length);
2555
+ }
2556
+ return digits;
2557
+ }).filter((x) => x.length >= 6);
2558
+ return Array.from(new Set(normalized));
2539
2559
  }
2540
2560
  /** Mirror PHP cleanup (strip tags, collapse nbsp, trim) */
2541
2561
  cleanMessage(msg) {
2542
- const noTags = msg.replace(/<\/?[^>]+(>|$)/g, "");
2562
+ const noTags = String(msg ?? "").replace(/<\/?[^>]+(>|$)/g, "");
2543
2563
  return noTags.replace(/&nbsp;|&amp;nbsp;/g, " ").trim();
2544
2564
  }
2545
2565
  };
@@ -3151,32 +3171,57 @@ var NotificationService = class {
3151
3171
  const { cfg, evt, deliveryId } = args;
3152
3172
  const tpl = await this.prisma.template.findUnique({ where: { id: cfg.smsTemplateId } });
3153
3173
  if (!tpl) return;
3154
- const recipients = args.recipients;
3174
+ const recipients = args.recipients ?? [];
3155
3175
  if (!recipients.length) {
3156
3176
  this.logger.info("[NotificationService] SMS: no recipients resolved");
3157
3177
  return;
3158
3178
  }
3159
3179
  const body = renderTemplate(tpl.bodyText ?? "", evt.data ?? {});
3160
3180
  const provider = new SmsProvider(this.logger, {
3161
- apiUrl: cfg.serviceEvent.smsApiUrl,
3181
+ apiUrl: cfg.serviceEvent.smsApiUrl ?? void 0,
3182
+ apiKey: cfg.serviceEvent.smsApiKey ?? void 0,
3162
3183
  countryCode: cfg.serviceEvent.countryCode ?? "+91",
3163
- senderId: cfg.serviceEvent.smsSenderId ?? void 0
3164
- // apiKey: cfg.serviceEvent.smsApiKey ?? undefined, // if your SmsProvider supports it
3184
+ senderId: cfg.serviceEvent.smsSenderId ?? void 0,
3185
+ masterNumber: cfg.serviceEvent.masterPhone ?? void 0
3186
+ // if you want master included here too
3165
3187
  });
3166
- await this.sendPerRecipient({
3167
- deliveryId,
3168
- notificationType: "SMS" /* SMS */,
3169
- recipients,
3170
- messageContent: body,
3171
- sendOne: async (recipient) => {
3172
- const res = await provider.send({
3173
- recipient: { phone: recipient },
3174
- body,
3175
- priority: evt.priority
3176
- });
3177
- return this.mapProviderResult(res);
3178
- }
3188
+ const bulk = await provider.sendBulk({
3189
+ phones: recipients,
3190
+ message: body,
3191
+ includeMaster: true
3179
3192
  });
3193
+ const now = /* @__PURE__ */ new Date();
3194
+ const auditRecipients = (bulk.recipients?.length ? bulk.recipients : recipients).filter(Boolean);
3195
+ if (bulk.ok) {
3196
+ await this.prisma.eventDeliveryItem.createMany({
3197
+ data: auditRecipients.map((r) => ({
3198
+ deliveryId,
3199
+ notificationType: "SMS" /* SMS */,
3200
+ recipient: r,
3201
+ messageContent: body,
3202
+ thirdPartyResponse: bulk.meta ?? void 0,
3203
+ isSent: true,
3204
+ sentAt: now,
3205
+ externalId: bulk.externalId ?? null,
3206
+ error: null
3207
+ }))
3208
+ });
3209
+ } else {
3210
+ const errMsg = bulk.error ?? "SMS bulk send failed";
3211
+ await this.prisma.eventDeliveryItem.createMany({
3212
+ data: auditRecipients.map((r) => ({
3213
+ deliveryId,
3214
+ notificationType: "SMS" /* SMS */,
3215
+ recipient: r,
3216
+ messageContent: body,
3217
+ thirdPartyResponse: bulk.meta ?? void 0,
3218
+ isSent: false,
3219
+ sentAt: null,
3220
+ externalId: bulk.externalId ?? null,
3221
+ error: errMsg
3222
+ }))
3223
+ });
3224
+ }
3180
3225
  }
3181
3226
  async processAppChannel(args) {
3182
3227
  const { cfg, evt, deliveryId } = args;
package/dist/index.mjs CHANGED
@@ -2401,95 +2401,115 @@ var SmsProvider = class {
2401
2401
  constructor(logger = console, args) {
2402
2402
  this.logger = logger;
2403
2403
  this.args = args;
2404
- this.logger.info(`[NotificationService] args for sms : ${JSON.stringify(args)}`);
2404
+ const safeArgs = { ...args, apiKey: args.apiKey ? "***" : "" };
2405
+ this.logger.info(`[NotificationService] args for sms : ${JSON.stringify(safeArgs)}`);
2405
2406
  this.url = args.apiUrl || "https://web.nsemfua.com/api/http/sms/send";
2406
- this.token = "13|R3XfH6wHx3zdflBzpGMlNiTWKuOUEkGutORyGcuS";
2407
+ this.token = (args.apiKey || "").trim();
2407
2408
  this.sender = args.senderId || "AlmaMedLab";
2409
+ this.countryCode = (args.countryCode || "+233").trim();
2408
2410
  }
2409
2411
  url;
2410
2412
  token;
2411
2413
  sender;
2414
+ countryCode;
2415
+ /**
2416
+ * Single recipient wrapper (still same API).
2417
+ */
2412
2418
  async send(args) {
2413
- const to = args.recipient;
2414
- if (!to) {
2415
- return {
2416
- ok: false,
2417
- provider: "SMS" /* SMS */,
2418
- error: "Missing recipient.phone"
2419
- };
2419
+ const phone = args?.recipient?.phone;
2420
+ if (!phone) {
2421
+ return { ok: false, provider: "SMS" /* SMS */, error: "Missing recipient.phone" };
2422
+ }
2423
+ const bulk = await this.sendBulk({
2424
+ phones: [phone],
2425
+ message: args.body,
2426
+ includeMaster: true
2427
+ });
2428
+ if (!bulk.ok) {
2429
+ return { ok: false, provider: "SMS" /* SMS */, error: bulk.error, meta: bulk.meta };
2430
+ }
2431
+ return { ok: true, provider: "SMS" /* SMS */, externalId: bulk.externalId, meta: bulk.meta };
2432
+ }
2433
+ /**
2434
+ * ✅ Bulk SMS in ONE HTTP call (like PHP: recipient: "988.., 977..")
2435
+ */
2436
+ async sendBulk(args) {
2437
+ if (!this.url) {
2438
+ return { ok: false, provider: "SMS" /* SMS */, recipients: [], error: "Missing sms apiUrl" };
2420
2439
  }
2421
- const allRecipients = [to];
2422
- if (this.args.masterNumber) {
2423
- allRecipients.push({ phone: this.args.masterNumber });
2440
+ if (!this.token) {
2441
+ return { ok: false, provider: "SMS" /* SMS */, recipients: [], error: "Missing sms apiKey" };
2424
2442
  }
2425
- const recipients = this.normalizeRecipients(allRecipients);
2443
+ const includeMaster = args.includeMaster ?? true;
2444
+ const phones = [...args.phones ?? []];
2445
+ if (includeMaster && this.args.masterNumber) phones.push(this.args.masterNumber);
2446
+ const recipients = this.normalizePhones(phones);
2426
2447
  if (!recipients.length) {
2427
- return {
2428
- ok: false,
2429
- provider: "SMS" /* SMS */,
2430
- error: "Missing recipient.phone"
2431
- };
2448
+ return { ok: false, provider: "SMS" /* SMS */, recipients: [], error: "No valid phone recipients" };
2432
2449
  }
2450
+ const recipientStr = recipients.join(", ");
2433
2451
  try {
2434
2452
  const payload = {
2435
2453
  api_token: this.token,
2436
- recipient: recipients.join(", "),
2437
- // API accepts comma-separated list
2454
+ recipient: recipientStr,
2438
2455
  sender_id: this.sender,
2439
2456
  type: "plain",
2440
- message: this.cleanMessage(args.body)
2441
- // schedule_time: "" // add if you need scheduling
2457
+ message: this.cleanMessage(args.message)
2442
2458
  };
2443
2459
  const res = await fetch(this.url, {
2444
2460
  method: "POST",
2445
- headers: {
2446
- "Content-Type": "application/json",
2447
- Accept: "application/json"
2448
- },
2461
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
2449
2462
  body: JSON.stringify(payload)
2450
2463
  });
2451
2464
  const data = await res.json().catch(() => ({}));
2452
- const isError = data?.status === "error" || !res.ok;
2465
+ const isError = !res.ok || data?.status === "error";
2453
2466
  if (isError) {
2454
2467
  return {
2455
2468
  ok: false,
2456
2469
  provider: "SMS" /* SMS */,
2457
- error: data?.message || data?.error || `Gateway error (${res.status} ${res.statusText})`
2470
+ recipients,
2471
+ error: data?.message || data?.error || `Gateway error (${res.status} ${res.statusText})`,
2472
+ meta: data
2458
2473
  };
2459
2474
  }
2460
- const externalId = data?.id || data?.data?.id || data?.message_id || data?.data?.message_id || void 0;
2475
+ const externalId = data?.id || data?.data?.id || data?.message_id || data?.data?.message_id;
2461
2476
  return {
2462
2477
  ok: true,
2463
2478
  provider: "SMS" /* SMS */,
2479
+ recipients,
2464
2480
  externalId,
2465
- meta: {
2466
- gatewayStatus: data?.status,
2467
- recipient: payload.recipient
2468
- }
2481
+ meta: data
2469
2482
  };
2470
2483
  } catch (err) {
2471
2484
  return {
2472
2485
  ok: false,
2473
2486
  provider: "SMS" /* SMS */,
2487
+ recipients,
2474
2488
  error: err?.message ?? String(err)
2475
2489
  };
2476
2490
  }
2477
2491
  }
2478
- /** Accepts single/multiple Recipient objects and returns sanitized numbers */
2479
- normalizeRecipients(input) {
2480
- return input.map((r) => r?.phone || "").map((p) => this.stripCountryCode(p)).map((p) => this.onlyDialable(p)).filter(Boolean);
2481
- }
2482
- /** Match PHP: remove leading +233 (Ghana) only */
2483
- stripCountryCode(phone) {
2484
- return (phone || "").trim().replace(this.args.countryCode || "+233", "");
2485
- }
2486
- /** Remove spaces, dashes, parentheses; keep numbers only (and commas handled elsewhere) */
2487
- onlyDialable(phone) {
2488
- return phone.replace(/[^\d]/g, "");
2492
+ // ---------------- helpers ----------------
2493
+ /**
2494
+ * Normalize phones:
2495
+ * - trim
2496
+ * - strip leading country code digits if present (e.g., +233 or 233)
2497
+ * - keep digits only
2498
+ * - dedupe
2499
+ */
2500
+ normalizePhones(input) {
2501
+ const ccDigits = this.countryCode.replace(/[^\d]/g, "");
2502
+ const normalized = (input ?? []).map((x) => String(x ?? "").trim()).filter(Boolean).map((x) => x.replace(/[^\d]/g, "")).map((digits) => {
2503
+ if (ccDigits && digits.startsWith(ccDigits) && digits.length > ccDigits.length) {
2504
+ return digits.slice(ccDigits.length);
2505
+ }
2506
+ return digits;
2507
+ }).filter((x) => x.length >= 6);
2508
+ return Array.from(new Set(normalized));
2489
2509
  }
2490
2510
  /** Mirror PHP cleanup (strip tags, collapse nbsp, trim) */
2491
2511
  cleanMessage(msg) {
2492
- const noTags = msg.replace(/<\/?[^>]+(>|$)/g, "");
2512
+ const noTags = String(msg ?? "").replace(/<\/?[^>]+(>|$)/g, "");
2493
2513
  return noTags.replace(/&nbsp;|&amp;nbsp;/g, " ").trim();
2494
2514
  }
2495
2515
  };
@@ -3101,32 +3121,57 @@ var NotificationService = class {
3101
3121
  const { cfg, evt, deliveryId } = args;
3102
3122
  const tpl = await this.prisma.template.findUnique({ where: { id: cfg.smsTemplateId } });
3103
3123
  if (!tpl) return;
3104
- const recipients = args.recipients;
3124
+ const recipients = args.recipients ?? [];
3105
3125
  if (!recipients.length) {
3106
3126
  this.logger.info("[NotificationService] SMS: no recipients resolved");
3107
3127
  return;
3108
3128
  }
3109
3129
  const body = renderTemplate(tpl.bodyText ?? "", evt.data ?? {});
3110
3130
  const provider = new SmsProvider(this.logger, {
3111
- apiUrl: cfg.serviceEvent.smsApiUrl,
3131
+ apiUrl: cfg.serviceEvent.smsApiUrl ?? void 0,
3132
+ apiKey: cfg.serviceEvent.smsApiKey ?? void 0,
3112
3133
  countryCode: cfg.serviceEvent.countryCode ?? "+91",
3113
- senderId: cfg.serviceEvent.smsSenderId ?? void 0
3114
- // apiKey: cfg.serviceEvent.smsApiKey ?? undefined, // if your SmsProvider supports it
3134
+ senderId: cfg.serviceEvent.smsSenderId ?? void 0,
3135
+ masterNumber: cfg.serviceEvent.masterPhone ?? void 0
3136
+ // if you want master included here too
3115
3137
  });
3116
- await this.sendPerRecipient({
3117
- deliveryId,
3118
- notificationType: "SMS" /* SMS */,
3119
- recipients,
3120
- messageContent: body,
3121
- sendOne: async (recipient) => {
3122
- const res = await provider.send({
3123
- recipient: { phone: recipient },
3124
- body,
3125
- priority: evt.priority
3126
- });
3127
- return this.mapProviderResult(res);
3128
- }
3138
+ const bulk = await provider.sendBulk({
3139
+ phones: recipients,
3140
+ message: body,
3141
+ includeMaster: true
3129
3142
  });
3143
+ const now = /* @__PURE__ */ new Date();
3144
+ const auditRecipients = (bulk.recipients?.length ? bulk.recipients : recipients).filter(Boolean);
3145
+ if (bulk.ok) {
3146
+ await this.prisma.eventDeliveryItem.createMany({
3147
+ data: auditRecipients.map((r) => ({
3148
+ deliveryId,
3149
+ notificationType: "SMS" /* SMS */,
3150
+ recipient: r,
3151
+ messageContent: body,
3152
+ thirdPartyResponse: bulk.meta ?? void 0,
3153
+ isSent: true,
3154
+ sentAt: now,
3155
+ externalId: bulk.externalId ?? null,
3156
+ error: null
3157
+ }))
3158
+ });
3159
+ } else {
3160
+ const errMsg = bulk.error ?? "SMS bulk send failed";
3161
+ await this.prisma.eventDeliveryItem.createMany({
3162
+ data: auditRecipients.map((r) => ({
3163
+ deliveryId,
3164
+ notificationType: "SMS" /* SMS */,
3165
+ recipient: r,
3166
+ messageContent: body,
3167
+ thirdPartyResponse: bulk.meta ?? void 0,
3168
+ isSent: false,
3169
+ sentAt: null,
3170
+ externalId: bulk.externalId ?? null,
3171
+ error: errMsg
3172
+ }))
3173
+ });
3174
+ }
3130
3175
  }
3131
3176
  async processAppChannel(args) {
3132
3177
  const { cfg, evt, deliveryId } = args;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "av6-core",
3
- "version": "1.5.7",
3
+ "version": "1.5.8",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",