@infuro/cms-core 1.0.44 → 1.0.46

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 (66) hide show
  1. package/README.md +19 -2
  2. package/dist/admin.cjs +1413 -620
  3. package/dist/admin.d.cts +8 -2
  4. package/dist/admin.d.ts +8 -2
  5. package/dist/admin.js +1414 -621
  6. package/dist/api.cjs +40 -37
  7. package/dist/api.d.cts +3 -1
  8. package/dist/api.d.ts +3 -1
  9. package/dist/api.js +8 -5
  10. package/dist/auth.cjs +23 -23
  11. package/dist/auth.d.cts +1 -0
  12. package/dist/auth.d.ts +1 -0
  13. package/dist/auth.js +2 -2
  14. package/dist/chunk-2KUCQVAQ.js +936 -0
  15. package/dist/chunk-3K5AIEIZ.js +256 -0
  16. package/dist/{chunk-I6NH72MO.js → chunk-57O3HZPG.js} +9 -1
  17. package/dist/chunk-7PMHZRF3.cjs +872 -0
  18. package/dist/chunk-BXYZDMTZ.cjs +953 -0
  19. package/dist/chunk-CQHXW4TR.js +107 -0
  20. package/dist/{chunk-NNQBWDCI.js → chunk-CRYKVJTO.js} +55 -959
  21. package/dist/chunk-DBPSJYLZ.js +47 -0
  22. package/dist/{chunk-TAHX46EI.cjs → chunk-FBKDMRQL.cjs} +9 -1
  23. package/dist/chunk-GUSHM5HN.js +862 -0
  24. package/dist/chunk-HY3AXLOI.cjs +265 -0
  25. package/dist/chunk-L3525VXI.js +243 -0
  26. package/dist/{chunk-ZMRQ72F6.cjs → chunk-NBY4NVTY.cjs} +3 -3
  27. package/dist/chunk-OY2YTBMP.cjs +50 -0
  28. package/dist/{chunk-BLR6H5GL.js → chunk-SF2XKMCI.js} +3 -3
  29. package/dist/{chunk-22FFHLTO.cjs → chunk-TCSGFAWB.cjs} +56 -970
  30. package/dist/{chunk-P2IWWBJV.cjs → chunk-UDVLFVEC.cjs} +1314 -601
  31. package/dist/{chunk-SQGBHYJF.js → chunk-UKC3HYXI.js} +1245 -548
  32. package/dist/chunk-V25RDUOU.cjs +248 -0
  33. package/dist/chunk-XMRMZ6NQ.cjs +109 -0
  34. package/dist/cli.cjs +9 -4
  35. package/dist/cli.js +9 -4
  36. package/dist/{email-queue-TPZWLTIV.cjs → email-queue-6NJY6HNM.cjs} +7 -5
  37. package/dist/email-queue-OI2HSCGE.js +4 -0
  38. package/dist/erp-order-invoice-3GXDE443.js +3 -0
  39. package/dist/erp-order-invoice-EYYNR526.cjs +24 -0
  40. package/dist/index.cjs +426 -359
  41. package/dist/index.d.cts +308 -20
  42. package/dist/index.d.ts +308 -20
  43. package/dist/index.js +31 -110
  44. package/dist/migrations/1781800000000-CustomerPhoneNullable.ts +53 -0
  45. package/dist/migrations/1781810000000-OrderItemsVariantId.ts +44 -0
  46. package/dist/migrations/1781900000000-UserInviteStatusToken.ts +46 -0
  47. package/dist/migrations/1782000000000-ComboSlug.ts +58 -0
  48. package/dist/migrations/1782100000000-DiscountDeviceType.ts +20 -0
  49. package/dist/order-inventory-2MYRKEPC.cjs +39 -0
  50. package/dist/order-inventory-76V4XIGW.js +2 -0
  51. package/dist/order-notification-dispatcher-SBQAMM5V.cjs +21 -0
  52. package/dist/{order-notification-dispatcher-XWIJYDGA.js → order-notification-dispatcher-ZJZB2HUN.js} +5 -2
  53. package/dist/retire-soft-deleted-unique-5VXBA5XX.cjs +15 -0
  54. package/dist/retire-soft-deleted-unique-NXQIH4BC.js +2 -0
  55. package/dist/send-order-placed-emails-B5ZVJT7T.cjs +15 -0
  56. package/dist/send-order-placed-emails-WPI37KRZ.js +6 -0
  57. package/package.json +5 -4
  58. package/src/admin/admin.css +27 -24
  59. package/dist/chunk-5ELSW2UP.js +0 -150
  60. package/dist/chunk-PNOKNSG2.cjs +0 -222
  61. package/dist/chunk-WEMDMVHQ.js +0 -216
  62. package/dist/chunk-XW5ATPRW.cjs +0 -155
  63. package/dist/email-queue-CGZBVVAS.js +0 -2
  64. package/dist/erp-order-invoice-3YISSOWW.js +0 -3
  65. package/dist/erp-order-invoice-ZR5F2KTA.cjs +0 -12
  66. package/dist/order-notification-dispatcher-RU7BTGGX.cjs +0 -18
@@ -1,581 +1,17 @@
1
1
  'use strict';
2
2
 
3
- var chunkUCKN4BBY_cjs = require('./chunk-UCKN4BBY.cjs');
4
- var chunkXW5ATPRW_cjs = require('./chunk-XW5ATPRW.cjs');
5
- var chunkOOTLNH63_cjs = require('./chunk-OOTLNH63.cjs');
3
+ var chunkV25RDUOU_cjs = require('./chunk-V25RDUOU.cjs');
4
+ var chunkBXYZDMTZ_cjs = require('./chunk-BXYZDMTZ.cjs');
6
5
  var chunkUSNT2KNT_cjs = require('./chunk-USNT2KNT.cjs');
7
6
  var clientSes = require('@aws-sdk/client-ses');
7
+ var module$1 = require('module');
8
8
  var nodemailer = require('nodemailer');
9
9
 
10
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
10
11
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
12
 
12
13
  var nodemailer__default = /*#__PURE__*/_interopDefault(nodemailer);
13
14
 
14
- // src/lib/event-order-template-context.ts
15
- function resolvePublicSiteUrl() {
16
- if (typeof process !== "undefined") {
17
- const u = (process.env.CMS_PUBLIC_URL || process.env.NEXT_PUBLIC_SITE_URL || "").trim();
18
- if (u) return u.replace(/\/+$/, "");
19
- const v = (process.env.VERCEL_URL || "").trim();
20
- if (v) return v.startsWith("http") ? v.replace(/\/+$/, "") : `https://${v.replace(/\/+$/, "")}`;
21
- }
22
- return "";
23
- }
24
- chunkUSNT2KNT_cjs.__name(resolvePublicSiteUrl, "resolvePublicSiteUrl");
25
- function buildOrderTrackUrl(qrToken, siteUrl) {
26
- const token = qrToken?.trim();
27
- if (!token) return "";
28
- const base = (siteUrl ?? resolvePublicSiteUrl()).replace(/\/+$/, "");
29
- if (!base) return `/track/${token}`;
30
- return `${base}/track/${token}`;
31
- }
32
- chunkUSNT2KNT_cjs.__name(buildOrderTrackUrl, "buildOrderTrackUrl");
33
- function buildOrderQrImageUrl(trackUrl, size = 200) {
34
- if (!trackUrl.trim()) return "";
35
- return `https://api.qrserver.com/v1/create-qr-code/?size=${size}x${size}&data=${encodeURIComponent(trackUrl)}`;
36
- }
37
- chunkUSNT2KNT_cjs.__name(buildOrderQrImageUrl, "buildOrderQrImageUrl");
38
- function formatOrderDetailsText(input) {
39
- const { orderNumber, total, currency, lines } = input;
40
- const header = `Order: ${orderNumber}
41
- Total: ${total} ${currency}`;
42
- if (!lines.length) return header;
43
- const items = lines.map((line) => `- ${line.name} \xD7 ${line.quantity} (${line.total} ${line.currency})`).join("\n");
44
- return `${header}
45
-
46
- Items:
47
- ${items}`;
48
- }
49
- chunkUSNT2KNT_cjs.__name(formatOrderDetailsText, "formatOrderDetailsText");
50
- function formatOrderDetailsHtml(input) {
51
- const text = formatOrderDetailsText(input);
52
- const escaped = text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
53
- return `<pre style="font-family:inherit;white-space:pre-wrap;margin:0">${escaped}</pre>`;
54
- }
55
- chunkUSNT2KNT_cjs.__name(formatOrderDetailsHtml, "formatOrderDetailsHtml");
56
- function buildEventOrderTemplateVariables(input) {
57
- const { event, order, contact, productNames, orderLines = [], siteUrl } = input;
58
- const trackUrl = buildOrderTrackUrl(order.qrToken, siteUrl);
59
- const qrImageUrl = buildOrderQrImageUrl(trackUrl);
60
- const orderDetailsPayload = {
61
- orderNumber: String(order.orderNumber ?? ""),
62
- total: String(order.total ?? ""),
63
- currency: String(order.currency ?? "INR"),
64
- lines: orderLines
65
- };
66
- return {
67
- eventName: event.name ?? "",
68
- eventSlug: event.slug ?? "",
69
- venue: event.venue ?? "",
70
- startDate: formatEventDate(event.startDate),
71
- orderNumber: String(order.orderNumber ?? ""),
72
- orderTotal: String(order.total ?? ""),
73
- currency: String(order.currency ?? "INR"),
74
- customerName: contact.name?.trim() || contact.email?.split("@")[0] || "Guest",
75
- customerEmail: contact.email?.trim() || "",
76
- customerPhone: contact.phone?.trim() || "",
77
- productNames,
78
- trackUrl,
79
- qrImageUrl,
80
- orderDetails: formatOrderDetailsText(orderDetailsPayload),
81
- orderDetailsHtml: formatOrderDetailsHtml(orderDetailsPayload)
82
- };
83
- }
84
- chunkUSNT2KNT_cjs.__name(buildEventOrderTemplateVariables, "buildEventOrderTemplateVariables");
85
- function formatEventDate(d) {
86
- if (!d) return "";
87
- try {
88
- return new Date(d).toLocaleString(void 0, {
89
- dateStyle: "medium",
90
- timeStyle: "short"
91
- });
92
- } catch {
93
- return String(d);
94
- }
95
- }
96
- chunkUSNT2KNT_cjs.__name(formatEventDate, "formatEventDate");
97
-
98
- // src/lib/order-notification-context.ts
99
- async function loadOrderLines(dataSource, entityMap, orderId, currency) {
100
- if (!entityMap.order_items) return [];
101
- const itemRows = await dataSource.getRepository(entityMap.order_items).find({
102
- where: {
103
- orderId
104
- },
105
- relations: [
106
- "product"
107
- ],
108
- order: {
109
- id: "ASC"
110
- }
111
- });
112
- return itemRows.map((item) => {
113
- const row = item;
114
- const name = row.product?.name || row.product?.title || `Product #${row.productId}`;
115
- return {
116
- name: String(name),
117
- quantity: Number(row.quantity) || 1,
118
- total: row.total,
119
- currency
120
- };
121
- });
122
- }
123
- chunkUSNT2KNT_cjs.__name(loadOrderLines, "loadOrderLines");
124
- async function loadPrimaryEvent(dataSource, entityMap, itemRows) {
125
- if (!entityMap.event_products || !entityMap.events) {
126
- return {
127
- productNames: []
128
- };
129
- }
130
- const epRepo = dataSource.getRepository(entityMap.event_products);
131
- const eventRepo = dataSource.getRepository(entityMap.events);
132
- const eventMap = /* @__PURE__ */ new Map();
133
- for (const item of itemRows) {
134
- const productId = item.productId;
135
- const productName = item.product?.name || item.product?.title || `Product #${productId}`;
136
- const eps = await epRepo.find({
137
- where: {
138
- productId,
139
- isActive: true
140
- }
141
- });
142
- for (const ep of eps) {
143
- const eventId = ep.eventId;
144
- const existing = eventMap.get(eventId);
145
- if (existing) {
146
- if (!existing.productNames.includes(String(productName))) {
147
- existing.productNames.push(String(productName));
148
- }
149
- } else {
150
- const event = await eventRepo.findOne({
151
- where: {
152
- id: eventId,
153
- deleted: false
154
- }
155
- });
156
- if (event) {
157
- eventMap.set(eventId, {
158
- event,
159
- productNames: [
160
- String(productName)
161
- ]
162
- });
163
- }
164
- }
165
- }
166
- }
167
- const first = eventMap.values().next().value;
168
- if (!first) return {
169
- productNames: []
170
- };
171
- return {
172
- event: first.event,
173
- productNames: first.productNames
174
- };
175
- }
176
- chunkUSNT2KNT_cjs.__name(loadPrimaryEvent, "loadPrimaryEvent");
177
- async function loadOrderNotificationContexts(orderId, deps) {
178
- if (!deps.entityMap.orders) return null;
179
- const order = await deps.dataSource.getRepository(deps.entityMap.orders).findOne({
180
- where: {
181
- id: orderId,
182
- deleted: false
183
- },
184
- relations: [
185
- "contact"
186
- ]
187
- });
188
- if (!order) return null;
189
- const o = order;
190
- const contact = o.contact ?? {};
191
- const currency = String(o.currency ?? "INR");
192
- const siteUrl = resolvePublicSiteUrl();
193
- const itemRows = await deps.dataSource.getRepository(deps.entityMap.order_items).find({
194
- where: {
195
- orderId
196
- },
197
- relations: [
198
- "product"
199
- ],
200
- order: {
201
- id: "ASC"
202
- }
203
- });
204
- const orderLines = await loadOrderLines(deps.dataSource, deps.entityMap, orderId, currency);
205
- const { event, productNames: eventProductNames } = await loadPrimaryEvent(deps.dataSource, deps.entityMap, itemRows);
206
- const allProductNames = orderLines.map((line) => line.name);
207
- const productNames = eventProductNames.length > 0 ? eventProductNames.join(", ") : allProductNames.join(", ");
208
- const variables = event ? buildEventOrderTemplateVariables({
209
- event,
210
- order: {
211
- orderNumber: o.orderNumber,
212
- total: o.total,
213
- currency,
214
- qrToken: o.qrToken
215
- },
216
- contact,
217
- productNames,
218
- orderLines,
219
- siteUrl
220
- }) : buildEventOrderTemplateVariables({
221
- event: {
222
- name: productNames || "Your order",
223
- slug: "",
224
- venue: "",
225
- startDate: null
226
- },
227
- order: {
228
- orderNumber: o.orderNumber,
229
- total: o.total,
230
- currency,
231
- qrToken: o.qrToken
232
- },
233
- contact,
234
- productNames,
235
- orderLines,
236
- siteUrl
237
- });
238
- return {
239
- order: {
240
- id: o.id,
241
- orderNumber: String(o.orderNumber ?? ""),
242
- total: o.total,
243
- currency,
244
- qrToken: o.qrToken
245
- },
246
- contact,
247
- orderLines,
248
- productNames,
249
- event,
250
- variables
251
- };
252
- }
253
- chunkUSNT2KNT_cjs.__name(loadOrderNotificationContexts, "loadOrderNotificationContexts");
254
-
255
- // src/plugins/whatsapp/whatsapp-queue.ts
256
- var WHATSAPP_QUEUE_NAME = "whatsapp";
257
- function maskPhone(raw) {
258
- const digits = raw.replace(/\D/g, "");
259
- if (digits.length <= 4) return "****";
260
- return `${"*".repeat(Math.max(0, digits.length - 4))}${digits.slice(-4)}`;
261
- }
262
- chunkUSNT2KNT_cjs.__name(maskPhone, "maskPhone");
263
- function registerWhatsAppQueueProcessor(cms) {
264
- const queue = cms.getPlugin("queue");
265
- const whatsapp = cms.getPlugin("whatsapp");
266
- if (!queue) {
267
- console.warn("[whatsapp] queue processor not registered: queue plugin missing");
268
- return;
269
- }
270
- if (!whatsapp || typeof whatsapp.send !== "function") {
271
- console.warn("[whatsapp] queue processor not registered: whatsapp plugin missing or not configured");
272
- return;
273
- }
274
- queue.registerProcessor(WHATSAPP_QUEUE_NAME, async (data) => {
275
- const payload = data;
276
- if (!payload.to) {
277
- console.warn("[whatsapp] queue job skipped: missing recipient");
278
- return;
279
- }
280
- console.log("[whatsapp] queue job processing", {
281
- to: maskPhone(payload.to),
282
- templateKey: payload.templateKey ?? null,
283
- externalTemplateRef: payload.externalTemplateRef ?? null
284
- });
285
- try {
286
- const ok = await whatsapp.send(payload);
287
- if (ok) {
288
- console.log("[whatsapp] queue job completed", {
289
- to: maskPhone(payload.to),
290
- sent: true
291
- });
292
- } else {
293
- console.warn("[whatsapp] queue job completed without delivery", {
294
- to: maskPhone(payload.to),
295
- sent: false
296
- });
297
- }
298
- } catch (err) {
299
- console.error("[whatsapp] queue job failed", {
300
- to: maskPhone(payload.to),
301
- error: err instanceof Error ? err.message : String(err)
302
- });
303
- }
304
- });
305
- console.log("[whatsapp] queue processor registered", {
306
- queue: WHATSAPP_QUEUE_NAME
307
- });
308
- }
309
- chunkUSNT2KNT_cjs.__name(registerWhatsAppQueueProcessor, "registerWhatsAppQueueProcessor");
310
- async function queueWhatsApp(cms, payload) {
311
- const queue = cms.getPlugin("queue");
312
- if (queue) {
313
- console.log("[whatsapp] enqueue", {
314
- to: maskPhone(payload.to),
315
- templateKey: payload.templateKey ?? null,
316
- externalTemplateRef: payload.externalTemplateRef ?? null
317
- });
318
- await queue.add(WHATSAPP_QUEUE_NAME, payload);
319
- return true;
320
- }
321
- const whatsapp = cms.getPlugin("whatsapp");
322
- if (whatsapp && typeof whatsapp.send === "function") {
323
- console.log("[whatsapp] no queue plugin \u2014 sending immediately", {
324
- to: maskPhone(payload.to)
325
- });
326
- return whatsapp.send(payload);
327
- }
328
- console.warn("[whatsapp] send skipped: neither queue nor whatsapp plugin available", {
329
- to: maskPhone(payload.to)
330
- });
331
- return false;
332
- }
333
- chunkUSNT2KNT_cjs.__name(queueWhatsApp, "queueWhatsApp");
334
-
335
- // src/plugins/whatsapp/whatsapp-service.ts
336
- function norm(v) {
337
- const t = v?.trim();
338
- return t || void 0;
339
- }
340
- chunkUSNT2KNT_cjs.__name(norm, "norm");
341
- function pick(plugin, db, env, ...keys) {
342
- for (const key of keys) {
343
- const pk = key;
344
- if (pk in plugin && plugin[pk] !== void 0 && String(plugin[pk]).trim() !== "") {
345
- const v = norm(String(plugin[pk]));
346
- if (v) return v;
347
- }
348
- if (norm(db[key])) return norm(db[key]);
349
- if (norm(env[key])) return norm(env[key]);
350
- }
351
- return void 0;
352
- }
353
- chunkUSNT2KNT_cjs.__name(pick, "pick");
354
- function mergeWhatsAppConfigLayers(env, db, plugin) {
355
- return {
356
- accessToken: pick(plugin, db, env, "accessToken", "WHATSAPP_ACCESS_TOKEN"),
357
- phoneNumberId: pick(plugin, db, env, "phoneNumberId", "WHATSAPP_PHONE_NUMBER_ID"),
358
- apiVersion: pick(plugin, db, env, "apiVersion", "WHATSAPP_API_VERSION") ?? "v21.0"
359
- };
360
- }
361
- chunkUSNT2KNT_cjs.__name(mergeWhatsAppConfigLayers, "mergeWhatsAppConfigLayers");
362
- function isWhatsAppPluginEnabled(db) {
363
- const v = (db.enabled ?? "true").toLowerCase();
364
- return v !== "false" && v !== "0";
365
- }
366
- chunkUSNT2KNT_cjs.__name(isWhatsAppPluginEnabled, "isWhatsAppPluginEnabled");
367
- function whatsAppConfigured(config) {
368
- return Boolean(config.accessToken?.trim() && config.phoneNumberId?.trim());
369
- }
370
- chunkUSNT2KNT_cjs.__name(whatsAppConfigured, "whatsAppConfigured");
371
- function normalizeWhatsAppRecipient(to) {
372
- const digits = to.replace(/\D/g, "");
373
- return digits;
374
- }
375
- chunkUSNT2KNT_cjs.__name(normalizeWhatsAppRecipient, "normalizeWhatsAppRecipient");
376
- function resolveMetaWhatsAppTemplateName(opts) {
377
- const ref = opts.externalTemplateRef?.trim();
378
- const rowName = opts.name?.trim();
379
- if (ref && !/^\d+$/.test(ref)) return ref;
380
- if (rowName) return rowName;
381
- return ref || void 0;
382
- }
383
- chunkUSNT2KNT_cjs.__name(resolveMetaWhatsAppTemplateName, "resolveMetaWhatsAppTemplateName");
384
- function maskPhone2(digits) {
385
- if (digits.length <= 4) return "****";
386
- return `${"*".repeat(Math.max(0, digits.length - 4))}${digits.slice(-4)}`;
387
- }
388
- chunkUSNT2KNT_cjs.__name(maskPhone2, "maskPhone");
389
- function logWhatsApp(level, message, extra) {
390
- const line = extra ? `${message} ${JSON.stringify(extra)}` : message;
391
- if (level === "info") console.log(`[whatsapp] ${line}`);
392
- else if (level === "warn") console.warn(`[whatsapp] ${line}`);
393
- else console.error(`[whatsapp] ${line}`);
394
- }
395
- chunkUSNT2KNT_cjs.__name(logWhatsApp, "logWhatsApp");
396
- var WhatsAppService = class {
397
- static {
398
- chunkUSNT2KNT_cjs.__name(this, "WhatsAppService");
399
- }
400
- getWhatsAppSettings;
401
- config;
402
- getMessageTemplateRow;
403
- constructor(env, plugin = {}, getWhatsAppSettings, getMessageTemplateRow) {
404
- this.getWhatsAppSettings = getWhatsAppSettings;
405
- this.getMessageTemplateRow = getMessageTemplateRow;
406
- this.config = mergeWhatsAppConfigLayers(env, {}, plugin);
407
- }
408
- async mergedConfig() {
409
- let db = {};
410
- try {
411
- db = await this.getWhatsAppSettings?.() ?? {};
412
- } catch {
413
- }
414
- return mergeWhatsAppConfigLayers((typeof process !== "undefined" ? process.env : {}) ?? {}, db, this.config);
415
- }
416
- async send(opts) {
417
- const cfg = await this.mergedConfig();
418
- if (!whatsAppConfigured(cfg)) {
419
- logWhatsApp("warn", "send skipped: not configured (Plugins \u2192 WhatsApp or WHATSAPP_ACCESS_TOKEN + WHATSAPP_PHONE_NUMBER_ID)");
420
- return false;
421
- }
422
- const to = normalizeWhatsAppRecipient(opts.to);
423
- if (!to) {
424
- logWhatsApp("warn", "send skipped: invalid recipient", {
425
- rawTo: opts.to
426
- });
427
- return false;
428
- }
429
- logWhatsApp("info", "send started", {
430
- to: maskPhone2(to),
431
- templateKey: opts.templateKey ?? null,
432
- externalTemplateRef: opts.externalTemplateRef ?? null,
433
- phoneNumberId: cfg.phoneNumberId
434
- });
435
- let templateName = resolveMetaWhatsAppTemplateName({
436
- externalTemplateRef: opts.externalTemplateRef
437
- });
438
- let templateLanguage = opts.templateLanguage?.trim();
439
- let body = opts.body?.trim() ?? "";
440
- const variables = opts.variables ?? {};
441
- if (opts.templateKey?.trim() && this.getMessageTemplateRow) {
442
- const { resolveEventOrderTemplate, EVENT_ORDER_TEMPLATE_KEY } = await import('./event-order-defaults-EMWBIGL3.cjs');
443
- if (opts.templateKey.trim() === EVENT_ORDER_TEMPLATE_KEY) {
444
- const resolved = await resolveEventOrderTemplate("whatsapp", async (ch, key) => {
445
- const row = await this.getMessageTemplateRow(ch, key);
446
- if (!row) return null;
447
- return {
448
- subject: null,
449
- body: row.body,
450
- externalTemplateRef: row.externalTemplateRef,
451
- enabled: row.enabled
452
- };
453
- });
454
- if (resolved && !resolved.enabled) {
455
- logWhatsApp("warn", "send skipped: template disabled in message_templates", {
456
- templateKey: opts.templateKey
457
- });
458
- return false;
459
- }
460
- if (resolved) {
461
- body = resolved.body;
462
- if (resolved.externalTemplateRef) templateName = resolved.externalTemplateRef;
463
- }
464
- }
465
- }
466
- const { applyTemplateVars: applyTemplateVars2 } = await import('./interpolate-WAKPN56R.cjs');
467
- if (body) body = applyTemplateVars2(body, variables);
468
- if (templateName) {
469
- const paramValues = opts.templateParamOrder?.length ? opts.templateParamOrder.map((key) => String(variables[key] ?? "").slice(0, 1024)) : Object.keys(variables).length ? Object.values(variables) : body ? [
470
- body
471
- ] : [];
472
- logWhatsApp("info", "sending template message", {
473
- to: maskPhone2(to),
474
- templateName,
475
- paramCount: paramValues.length,
476
- paramOrder: opts.templateParamOrder ?? null
477
- });
478
- return this.sendTemplateMessage(cfg, to, templateName, paramValues, templateLanguage);
479
- }
480
- if (!body) {
481
- logWhatsApp("warn", "send skipped: no template name and empty body", {
482
- to: maskPhone2(to)
483
- });
484
- return false;
485
- }
486
- logWhatsApp("info", "sending text message", {
487
- to: maskPhone2(to),
488
- bodyLength: body.length
489
- });
490
- return this.sendTextMessage(cfg, to, body);
491
- }
492
- async sendTextMessage(cfg, to, body) {
493
- const url = `https://graph.facebook.com/${cfg.apiVersion}/${cfg.phoneNumberId}/messages`;
494
- const res = await fetch(url, {
495
- method: "POST",
496
- headers: {
497
- Authorization: `Bearer ${cfg.accessToken}`,
498
- "Content-Type": "application/json"
499
- },
500
- body: JSON.stringify({
501
- messaging_product: "whatsapp",
502
- to,
503
- type: "text",
504
- text: {
505
- body
506
- }
507
- })
508
- });
509
- if (!res.ok) {
510
- const errText = await res.text().catch(() => "");
511
- logWhatsApp("error", "text send failed", {
512
- status: res.status,
513
- to: maskPhone2(to),
514
- error: errText
515
- });
516
- return false;
517
- }
518
- const data = await res.json().catch(() => ({}));
519
- logWhatsApp("info", "text send succeeded", {
520
- to: maskPhone2(to),
521
- messageId: data.messages?.[0]?.id ?? null
522
- });
523
- return true;
524
- }
525
- async sendTemplateMessage(cfg, to, templateName, paramValues, templateLanguage) {
526
- const components = paramValues.length > 0 ? [
527
- {
528
- type: "body",
529
- parameters: paramValues.map((text) => ({
530
- type: "text",
531
- text: String(text).slice(0, 1024)
532
- }))
533
- }
534
- ] : void 0;
535
- const url = `https://graph.facebook.com/${cfg.apiVersion}/${cfg.phoneNumberId}/messages`;
536
- const payload = {
537
- messaging_product: "whatsapp",
538
- to,
539
- type: "template",
540
- template: {
541
- name: templateName,
542
- language: {
543
- code: templateLanguage?.trim() || "en"
544
- },
545
- ...components ? {
546
- components
547
- } : {}
548
- }
549
- };
550
- const res = await fetch(url, {
551
- method: "POST",
552
- headers: {
553
- Authorization: `Bearer ${cfg.accessToken}`,
554
- "Content-Type": "application/json"
555
- },
556
- body: JSON.stringify(payload)
557
- });
558
- if (!res.ok) {
559
- const errText = await res.text().catch(() => "");
560
- logWhatsApp("error", "template send failed", {
561
- status: res.status,
562
- to: maskPhone2(to),
563
- templateName,
564
- paramCount: paramValues.length,
565
- error: errText
566
- });
567
- return false;
568
- }
569
- const data = await res.json().catch(() => ({}));
570
- logWhatsApp("info", "template send succeeded", {
571
- to: maskPhone2(to),
572
- templateName,
573
- messageId: data.messages?.[0]?.id ?? null
574
- });
575
- return true;
576
- }
577
- };
578
-
579
15
  // src/plugins/email/templates/layout.ts
580
16
  function escapeHtml(s) {
581
17
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -655,96 +91,6 @@ function renderLayout(options) {
655
91
  }
656
92
  chunkUSNT2KNT_cjs.__name(renderLayout, "renderLayout");
657
93
 
658
- // src/plugins/email/templates/types.ts
659
- function normalizeSocialLinkItem(o) {
660
- const url = String(o.url ?? "").trim();
661
- if (!url) return null;
662
- let iconUrl = String(o.iconUrl ?? o.icon_image ?? "").trim();
663
- let icon = String(o.icon ?? "").trim();
664
- if (!iconUrl && /^https?:\/\//i.test(icon)) {
665
- iconUrl = icon;
666
- icon = "";
667
- }
668
- const item = {
669
- url
670
- };
671
- if (iconUrl) item.iconUrl = iconUrl;
672
- if (icon) item.icon = icon;
673
- return item;
674
- }
675
- chunkUSNT2KNT_cjs.__name(normalizeSocialLinkItem, "normalizeSocialLinkItem");
676
- function parseSocialLinksJson(raw) {
677
- if (raw == null || raw.trim() === "") return void 0;
678
- try {
679
- const parsed = JSON.parse(raw);
680
- if (!Array.isArray(parsed)) return void 0;
681
- const out = [];
682
- for (const item of parsed) {
683
- if (item && typeof item === "object" && "url" in item) {
684
- const n = normalizeSocialLinkItem(item);
685
- if (n) out.push(n);
686
- }
687
- }
688
- return out.length ? out : void 0;
689
- } catch {
690
- return void 0;
691
- }
692
- }
693
- chunkUSNT2KNT_cjs.__name(parseSocialLinksJson, "parseSocialLinksJson");
694
- function mergeEmailLayoutCompanyDetails(branding, emailSettings) {
695
- const fromBranding = getCompanyDetailsFromSettings(branding);
696
- const pick2 = /* @__PURE__ */ chunkUSNT2KNT_cjs.__name((emailVal, fallback) => {
697
- const t = emailVal?.trim();
698
- return t || fallback?.trim() || void 0;
699
- }, "pick");
700
- const logoUrl = pick2(emailSettings.logoUrl ?? emailSettings.emailLogoUrl, fromBranding.logoUrl);
701
- const companyName = pick2(emailSettings.companyName ?? emailSettings.emailCompanyName, fromBranding.companyName);
702
- const supportEmail = pick2(emailSettings.supportEmail ?? emailSettings.emailSupportEmail, fromBranding.supportEmail);
703
- const supportPhone = pick2(emailSettings.supportPhone, void 0);
704
- const footerDisclaimer = pick2(emailSettings.footerDisclaimer, void 0);
705
- const followUsTitle = pick2(emailSettings.followUsTitle, "Follow Us") || "Follow Us";
706
- const socialFromEmail = parseSocialLinksJson(emailSettings.socialLinks);
707
- const socialLinks = socialFromEmail?.length ? socialFromEmail : fromBranding.socialLinks;
708
- return {
709
- logoUrl,
710
- companyName,
711
- supportEmail,
712
- supportPhone,
713
- socialLinks,
714
- footerDisclaimer,
715
- followUsTitle
716
- };
717
- }
718
- chunkUSNT2KNT_cjs.__name(mergeEmailLayoutCompanyDetails, "mergeEmailLayoutCompanyDetails");
719
- function getCompanyDetailsFromSettings(settingsGroup) {
720
- const logoUrl = settingsGroup.logo ?? settingsGroup.logoUrl ?? "";
721
- const companyName = settingsGroup.companyName ?? settingsGroup.company_name ?? "";
722
- const supportEmail = settingsGroup.supportEmail ?? settingsGroup.support_email ?? "";
723
- let socialLinks = [];
724
- const raw = settingsGroup.socialLinks ?? settingsGroup.social_links;
725
- if (typeof raw === "string") {
726
- try {
727
- const arr = JSON.parse(raw);
728
- if (Array.isArray(arr)) {
729
- for (const item of arr) {
730
- if (item && typeof item === "object") {
731
- const n = normalizeSocialLinkItem(item);
732
- if (n) socialLinks.push(n);
733
- }
734
- }
735
- }
736
- } catch {
737
- }
738
- }
739
- return {
740
- logoUrl: logoUrl || void 0,
741
- companyName: companyName || void 0,
742
- supportEmail: supportEmail || void 0,
743
- socialLinks: socialLinks.length ? socialLinks : void 0
744
- };
745
- }
746
- chunkUSNT2KNT_cjs.__name(getCompanyDetailsFromSettings, "getCompanyDetailsFromSettings");
747
-
748
94
  // src/plugins/email/templates/inline-cta.ts
749
95
  function escapeHtml2(s) {
750
96
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -949,7 +295,7 @@ function renderAddressesText(billing, shipping) {
949
295
  }
950
296
  chunkUSNT2KNT_cjs.__name(renderAddressesText, "renderAddressesText");
951
297
  function render4(ctx) {
952
- const { orderNumber, total, subtotal, tax, currency, customerName, companyDetails, audience = "customer", internalCustomerEmail, lineItems = [], billingAddress, shippingAddress } = ctx;
298
+ const { orderNumber, total, subtotal, tax, currency, customerName, companyDetails, audience = "customer", internalCustomerEmail, lineItems = [], billingAddress, shippingAddress, invoiceAttached = false } = ctx;
953
299
  const itemsHtml = renderLineItemsHtml(lineItems, currency);
954
300
  const itemsText = renderLineItemsText(lineItems, currency);
955
301
  const addrHtml = renderAddressesHtml(billingAddress, shippingAddress);
@@ -988,6 +334,7 @@ ${subtotalLine}${taxLine}${totalLine}`;
988
334
  ${addrHtml}
989
335
  ${itemsHtml}
990
336
  ${subtotalLine}${taxLine}${totalLine}
337
+ ${invoiceAttached ? `<p style="margin:16px 0 0 0;font-size:14px;line-height:1.5;color:#333;">Your order invoice is attached as a PDF.</p>` : ""}
991
338
  <p style="margin:16px 0 0 0;font-size:13px;line-height:1.5;color:#666;">If you have questions, reply to this email or contact us using the details below.</p>`;
992
339
  text = [
993
340
  `Order confirmed #${orderNumber}`,
@@ -998,6 +345,7 @@ ${subtotalLine}${taxLine}${totalLine}
998
345
  subtotal != null ? `Subtotal: ${subtotal}${currency ? ` ${currency}` : ""}` : "",
999
346
  tax != null && Number(tax) !== 0 ? `Tax: ${tax}${currency ? ` ${currency}` : ""}` : "",
1000
347
  total != null ? `Order total: ${total}${currency ? ` ${currency}` : ""}` : "",
348
+ invoiceAttached ? "Your order invoice is attached as a PDF." : "",
1001
349
  "",
1002
350
  "We will process your order shortly."
1003
351
  ].filter((line, i, arr) => !(line === "" && arr[i - 1] === "")).join("\n");
@@ -1324,8 +672,8 @@ function renderEmail(templateName, ctx, options) {
1324
672
  return render12(ctx);
1325
673
  }
1326
674
  chunkUSNT2KNT_cjs.__name(renderEmail, "renderEmail");
1327
-
1328
- // src/plugins/email/email-service.ts
675
+ var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-TCSGFAWB.cjs', document.baseURI).href)));
676
+ var MailComposer = require2("nodemailer/lib/mail-composer");
1329
677
  var SES_REGIONS_WITHOUT_SMTP = /* @__PURE__ */ new Set([
1330
678
  "af-south-1",
1331
679
  "ap-south-2",
@@ -1346,6 +694,10 @@ function sesRegionSupportsSmtp(region) {
1346
694
  return !SES_REGIONS_WITHOUT_SMTP.has(region.trim());
1347
695
  }
1348
696
  chunkUSNT2KNT_cjs.__name(sesRegionSupportsSmtp, "sesRegionSupportsSmtp");
697
+ function toBuffer(content) {
698
+ return Buffer.isBuffer(content) ? content : Buffer.from(content);
699
+ }
700
+ chunkUSNT2KNT_cjs.__name(toBuffer, "toBuffer");
1349
701
  var EmailService = class {
1350
702
  static {
1351
703
  chunkUSNT2KNT_cjs.__name(this, "EmailService");
@@ -1409,14 +761,47 @@ var EmailService = class {
1409
761
  throw new Error(`Unsupported email type: ${config.type}`);
1410
762
  }
1411
763
  }
764
+ nodemailerAttachments(attachments) {
765
+ if (!attachments?.length) return void 0;
766
+ return attachments.map((a) => ({
767
+ filename: a.filename,
768
+ content: toBuffer(a.content),
769
+ contentType: a.contentType || "application/octet-stream"
770
+ }));
771
+ }
1412
772
  async send(emailData) {
1413
773
  try {
774
+ const from = emailData.from || this.config.from;
775
+ const to = emailData.to || this.config.to;
776
+ const attachments = this.nodemailerAttachments(emailData.attachments);
1414
777
  if (this.config.type === "AWS" && this.sesClient) {
778
+ if (attachments?.length) {
779
+ const composer = new MailComposer({
780
+ from,
781
+ to,
782
+ subject: emailData.subject,
783
+ html: emailData.html,
784
+ text: emailData.text,
785
+ attachments
786
+ });
787
+ const raw = await new Promise((resolve, reject) => {
788
+ composer.compile().build((err, message) => {
789
+ if (err) reject(err);
790
+ else resolve(message);
791
+ });
792
+ });
793
+ await this.sesClient.send(new clientSes.SendRawEmailCommand({
794
+ RawMessage: {
795
+ Data: raw
796
+ }
797
+ }));
798
+ return true;
799
+ }
1415
800
  await this.sesClient.send(new clientSes.SendEmailCommand({
1416
- Source: emailData.from || this.config.from,
801
+ Source: from,
1417
802
  Destination: {
1418
803
  ToAddresses: [
1419
- emailData.to || this.config.to
804
+ to
1420
805
  ]
1421
806
  },
1422
807
  Message: {
@@ -1442,11 +827,12 @@ var EmailService = class {
1442
827
  }
1443
828
  if (this.transporter && (this.config.type === "SMTP" || this.config.type === "GMAIL" || this.config.type === "AWS" && this.awsViaSmtp)) {
1444
829
  await this.transporter.sendMail({
1445
- from: emailData.from || this.config.from,
1446
- to: emailData.to || this.config.to,
830
+ from,
831
+ to,
1447
832
  subject: emailData.subject,
1448
833
  html: emailData.html,
1449
- text: emailData.text
834
+ text: emailData.text,
835
+ attachments
1450
836
  });
1451
837
  return true;
1452
838
  }
@@ -1652,8 +1038,8 @@ function resolveChatEmailToolSettings(configMap, sources) {
1652
1038
  chunkUSNT2KNT_cjs.__name(resolveChatEmailToolSettings, "resolveChatEmailToolSettings");
1653
1039
  function intentByKey(intents, key) {
1654
1040
  if (!key) return null;
1655
- const norm2 = normalizeIntentKey(key);
1656
- return intents.find((i) => i.intent === norm2) ?? null;
1041
+ const norm = normalizeIntentKey(key);
1042
+ return intents.find((i) => i.intent === norm) ?? null;
1657
1043
  }
1658
1044
  chunkUSNT2KNT_cjs.__name(intentByKey, "intentByKey");
1659
1045
  function buildIntentTableForPrompt(intents) {
@@ -1930,14 +1316,14 @@ async function sendChatLeadEmail(cms, emailSettings, brandingSettings, input) {
1930
1316
  recipients,
1931
1317
  source: input.recipients?.length ? "intent_email_to" : "crm_or_smtp_fallback"
1932
1318
  });
1933
- const companyDetails = mergeEmailLayoutCompanyDetails(brandingSettings, emailSettings);
1319
+ const companyDetails = chunkBXYZDMTZ_cjs.mergeEmailLayoutCompanyDetails(brandingSettings, emailSettings);
1934
1320
  const ctx = {
1935
1321
  ...input,
1936
1322
  companyDetails: input.companyDetails ?? companyDetails
1937
1323
  };
1938
1324
  let anySent = false;
1939
1325
  for (const to of recipients) {
1940
- await chunkXW5ATPRW_cjs.queueEmail(cms, {
1326
+ await chunkV25RDUOU_cjs.queueEmail(cms, {
1941
1327
  to,
1942
1328
  templateName: "chatLead",
1943
1329
  ctx
@@ -2039,318 +1425,18 @@ function emailPlugin(config) {
2039
1425
  }
2040
1426
  chunkUSNT2KNT_cjs.__name(emailPlugin, "emailPlugin");
2041
1427
 
2042
- // src/lib/order-notification-dispatcher.ts
2043
- var initialized = false;
2044
- function maskPhone3(raw) {
2045
- const digits = String(raw ?? "").replace(/\D/g, "");
2046
- if (digits.length <= 4) return "(none)";
2047
- return `***${digits.slice(-4)}`;
2048
- }
2049
- chunkUSNT2KNT_cjs.__name(maskPhone3, "maskPhone");
2050
- async function getSettingsGroup(dataSource, entityMap, group) {
2051
- if (!entityMap.configs) return {};
2052
- const rows = await dataSource.getRepository(entityMap.configs).find({
2053
- where: {
2054
- settings: group,
2055
- deleted: false
2056
- }
2057
- });
2058
- return Object.fromEntries(rows.map((r) => [
2059
- r.key,
2060
- r.value
2061
- ]));
2062
- }
2063
- chunkUSNT2KNT_cjs.__name(getSettingsGroup, "getSettingsGroup");
2064
- async function isOrderNotificationsEnabled(dataSource, entityMap) {
2065
- const settings = await getSettingsGroup(dataSource, entityMap, "event_notifications");
2066
- return settings.enabled !== "false" && settings.enabled !== "0";
2067
- }
2068
- chunkUSNT2KNT_cjs.__name(isOrderNotificationsEnabled, "isOrderNotificationsEnabled");
2069
- async function isTriggerCatalogEnabled(triggerKey, dataSource, entityMap) {
2070
- if (!entityMap.order_notification_triggers) {
2071
- console.warn("[order-notifications] order_notification_triggers missing \u2014 run migration 1781230000000-AddOrderNotificationTriggers");
2072
- return false;
2073
- }
2074
- try {
2075
- const row = await dataSource.getRepository(entityMap.order_notification_triggers).findOne({
2076
- where: {
2077
- triggerKey,
2078
- enabled: true
2079
- }
2080
- });
2081
- if (!row) {
2082
- console.warn("[order-notifications] trigger disabled or not in DB", {
2083
- triggerKey
2084
- });
2085
- }
2086
- return Boolean(row);
2087
- } catch (err) {
2088
- console.error("[order-notifications] failed to load trigger catalog", {
2089
- triggerKey,
2090
- err
2091
- });
2092
- return false;
2093
- }
2094
- }
2095
- chunkUSNT2KNT_cjs.__name(isTriggerCatalogEnabled, "isTriggerCatalogEnabled");
2096
- async function loadBoundTemplate(triggerKey, channel, dataSource, entityMap) {
2097
- if (!entityMap.order_notification_bindings || !entityMap.message_templates) {
2098
- console.warn("[order-notifications] order_notification_bindings or message_templates missing from entityMap");
2099
- return null;
2100
- }
2101
- const binding = await dataSource.getRepository(entityMap.order_notification_bindings).findOne({
2102
- where: {
2103
- triggerKey,
2104
- channel,
2105
- enabled: true
2106
- }
2107
- });
2108
- if (!binding) {
2109
- console.warn("[order-notifications] no order_notification_bindings row", {
2110
- triggerKey,
2111
- channel
2112
- });
2113
- return null;
2114
- }
2115
- const templateId = binding.messageTemplateId;
2116
- if (templateId == null) {
2117
- console.warn("[order-notifications] binding exists but no template selected", {
2118
- triggerKey,
2119
- channel
2120
- });
2121
- return null;
2122
- }
2123
- const template = await dataSource.getRepository(entityMap.message_templates).findOne({
2124
- where: {
2125
- id: templateId,
2126
- channel,
2127
- deleted: false,
2128
- enabled: true
2129
- }
2130
- });
2131
- if (!template) {
2132
- console.warn("[order-notifications] bound template not found or disabled", {
2133
- triggerKey,
2134
- channel,
2135
- templateId
2136
- });
2137
- return null;
2138
- }
2139
- return template;
2140
- }
2141
- chunkUSNT2KNT_cjs.__name(loadBoundTemplate, "loadBoundTemplate");
2142
- function mergeTriggerVariables(payload, rich) {
2143
- const basic = {
2144
- orderId: String(payload.orderId ?? ""),
2145
- orderNumber: payload.orderNumber,
2146
- customerName: payload.customerName,
2147
- customerPhone: payload.customerPhone ?? "",
2148
- total: String(payload.total),
2149
- orderTotal: String(payload.total),
2150
- currency: payload.currency,
2151
- vendorId: String(payload.vendorId ?? ""),
2152
- ...payload.refundAmount != null ? {
2153
- refundAmount: String(payload.refundAmount)
2154
- } : {}
2155
- };
2156
- return {
2157
- ...basic,
2158
- ...rich ?? {}
2159
- };
2160
- }
2161
- chunkUSNT2KNT_cjs.__name(mergeTriggerVariables, "mergeTriggerVariables");
2162
- async function sendEmailForTrigger(triggerKey, orderId, variables, contactEmail, dataSource, entityMap, getCms) {
2163
- const template = await loadBoundTemplate(triggerKey, "email", dataSource, entityMap);
2164
- if (!template) return;
2165
- const [branding, emailSettings] = await Promise.all([
2166
- getSettingsGroup(dataSource, entityMap, "branding"),
2167
- getSettingsGroup(dataSource, entityMap, "email")
2168
- ]);
2169
- const companyDetails = mergeEmailLayoutCompanyDetails(branding, emailSettings);
2170
- const subject = chunkOOTLNH63_cjs.applyTemplateVars(template.subject ?? "Order update", variables);
2171
- const bodyHtml = chunkOOTLNH63_cjs.applyTemplateVars(template.body, variables);
2172
- const html = renderLayout({
2173
- bodyHtml,
2174
- companyDetails
2175
- });
2176
- const cms = await getCms();
2177
- await chunkXW5ATPRW_cjs.queueEmail(cms, {
2178
- to: contactEmail,
2179
- subject,
2180
- html,
2181
- text: bodyHtml.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim()
2182
- });
2183
- console.log("[order-notifications] email queued", {
2184
- triggerKey,
2185
- orderId,
2186
- to: contactEmail
2187
- });
2188
- }
2189
- chunkUSNT2KNT_cjs.__name(sendEmailForTrigger, "sendEmailForTrigger");
2190
- async function sendWhatsAppForTrigger(triggerKey, orderId, phone, variables, dataSource, entityMap, getCms) {
2191
- const template = await loadBoundTemplate(triggerKey, "whatsapp", dataSource, entityMap);
2192
- if (!template) return;
2193
- const paramOrder = template.providerMeta?.paramOrder ?? [];
2194
- if (paramOrder.length === 0) {
2195
- console.warn("[order-notifications] WhatsApp template has no paramOrder set \u2014 variables may send blank/wrong", {
2196
- triggerKey
2197
- });
2198
- }
2199
- const cms = await getCms();
2200
- const metaTemplateName = resolveMetaWhatsAppTemplateName({
2201
- externalTemplateRef: template.externalTemplateRef,
2202
- name: template.name
2203
- });
2204
- if (!metaTemplateName) {
2205
- console.warn("[order-notifications] bound WhatsApp template has no Meta template name", {
2206
- triggerKey,
2207
- templateId: template.id
2208
- });
2209
- return;
2210
- }
2211
- const sent = await queueWhatsApp(cms, {
2212
- to: phone.trim(),
2213
- templateKey: triggerKey,
2214
- externalTemplateRef: metaTemplateName,
2215
- templateLanguage: template.providerMeta?.language ?? void 0,
2216
- body: template.body,
2217
- variables,
2218
- templateParamOrder: paramOrder
2219
- });
2220
- if (!sent) {
2221
- console.warn("[order-notifications] WhatsApp send failed", {
2222
- triggerKey,
2223
- orderId,
2224
- hint: "Register whatsappPlugin() + queuePlugin() in getCms(), or set WHATSAPP_ACCESS_TOKEN and WHATSAPP_PHONE_NUMBER_ID"
2225
- });
2226
- } else {
2227
- console.log("[order-notifications] WhatsApp queued/sent", {
2228
- triggerKey,
2229
- orderId,
2230
- phone: maskPhone3(phone)
2231
- });
2232
- }
2233
- }
2234
- chunkUSNT2KNT_cjs.__name(sendWhatsAppForTrigger, "sendWhatsAppForTrigger");
2235
- async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms) {
2236
- console.log("[order-notifications] received", {
2237
- triggerKey,
2238
- orderId: payload.orderId,
2239
- phone: maskPhone3(payload.customerPhone)
2240
- });
2241
- const pluginEnabled = await isOrderNotificationsEnabled(dataSource, entityMap);
2242
- if (!pluginEnabled) {
2243
- console.warn("[order-notifications] skip: Plugins \u2192 Order notifications disabled");
2244
- return;
2245
- }
2246
- const triggerEnabled = await isTriggerCatalogEnabled(triggerKey, dataSource, entityMap);
2247
- if (!triggerEnabled) return;
2248
- const context = await loadOrderNotificationContexts(payload.orderId, {
2249
- dataSource,
2250
- entityMap
2251
- });
2252
- const variables = mergeTriggerVariables(payload, context?.variables);
2253
- const contactEmail = context?.contact.email?.trim() || "";
2254
- const contactPhone = context?.contact.phone?.trim() || payload.customerPhone?.trim() || "";
2255
- if (contactEmail) {
2256
- await sendEmailForTrigger(triggerKey, payload.orderId, variables, contactEmail, dataSource, entityMap, getCms);
2257
- } else {
2258
- console.warn("[order-notifications] skip email: no customer email", {
2259
- triggerKey,
2260
- orderId: payload.orderId
2261
- });
2262
- }
2263
- if (contactPhone) {
2264
- await sendWhatsAppForTrigger(triggerKey, payload.orderId, contactPhone, variables, dataSource, entityMap, getCms);
2265
- } else {
2266
- console.warn("[order-notifications] skip WhatsApp: no customer phone", {
2267
- triggerKey,
2268
- orderId: payload.orderId
2269
- });
2270
- }
2271
- }
2272
- chunkUSNT2KNT_cjs.__name(handleTrigger, "handleTrigger");
2273
- function initWhatsappTriggerDispatcher(deps) {
2274
- if (initialized) return;
2275
- initialized = true;
2276
- const { dataSource, entityMap, getCms } = deps;
2277
- chunkUCKN4BBY_cjs.notificationTriggerEmitter.onAnyTrigger((triggerKey, payload) => {
2278
- void handleTrigger(triggerKey, payload, dataSource, entityMap, getCms).catch((err) => {
2279
- console.error("[order-notifications]", triggerKey, err);
2280
- });
2281
- });
2282
- console.log("[order-notifications] dispatcher initialized (email + WhatsApp, unified via order_notification_bindings)");
2283
- }
2284
- chunkUSNT2KNT_cjs.__name(initWhatsappTriggerDispatcher, "initWhatsappTriggerDispatcher");
2285
- async function resendOrderNotification(triggerKey, orderId, deps) {
2286
- if (!deps.entityMap.orders) return {
2287
- ok: false,
2288
- error: "orders entity missing"
2289
- };
2290
- const pluginEnabled = await isOrderNotificationsEnabled(deps.dataSource, deps.entityMap);
2291
- if (!pluginEnabled) return {
2292
- ok: false,
2293
- error: "Order notifications plugin is disabled"
2294
- };
2295
- const triggerEnabled = await isTriggerCatalogEnabled(triggerKey, deps.dataSource, deps.entityMap);
2296
- if (!triggerEnabled) {
2297
- return {
2298
- ok: false,
2299
- error: `Trigger "${triggerKey}" is disabled or migration 1781230000000 not applied`
2300
- };
2301
- }
2302
- const order = await deps.dataSource.getRepository(deps.entityMap.orders).findOne({
2303
- where: {
2304
- id: orderId,
2305
- deleted: false
2306
- },
2307
- relations: [
2308
- "contact"
2309
- ]
2310
- });
2311
- if (!order) return {
2312
- ok: false,
2313
- error: "Order not found"
2314
- };
2315
- const o = order;
2316
- await handleTrigger(triggerKey, {
2317
- orderId,
2318
- orderNumber: String(o.orderNumber ?? ""),
2319
- customerName: o.contact?.name ?? "",
2320
- customerPhone: o.contact?.phone ?? null,
2321
- total: o.total ?? 0,
2322
- currency: String(o.currency ?? "INR"),
2323
- vendorId: o.vendorId ?? null
2324
- }, deps.dataSource, deps.entityMap, deps.getCms);
2325
- return {
2326
- ok: true
2327
- };
2328
- }
2329
- chunkUSNT2KNT_cjs.__name(resendOrderNotification, "resendOrderNotification");
2330
-
2331
1428
  exports.CHAT_EMAIL_LOG = CHAT_EMAIL_LOG;
2332
1429
  exports.EmailService = EmailService;
2333
- exports.WhatsAppService = WhatsAppService;
2334
1430
  exports.buildChatbotSystemPromptWithEmailTool = buildChatbotSystemPromptWithEmailTool;
2335
- exports.buildEventOrderTemplateVariables = buildEventOrderTemplateVariables;
2336
1431
  exports.buildTranscriptForLeadEmail = buildTranscriptForLeadEmail;
2337
1432
  exports.detectChatLeadIntent = detectChatLeadIntent;
2338
1433
  exports.emailPlugin = emailPlugin;
2339
1434
  exports.emailTemplates = emailTemplates;
2340
- exports.getCompanyDetailsFromSettings = getCompanyDetailsFromSettings;
2341
- exports.initWhatsappTriggerDispatcher = initWhatsappTriggerDispatcher;
2342
- exports.isWhatsAppPluginEnabled = isWhatsAppPluginEnabled;
2343
1435
  exports.joinRecipientsForSend = joinRecipientsForSend;
2344
- exports.mergeEmailLayoutCompanyDetails = mergeEmailLayoutCompanyDetails;
2345
- exports.mergeWhatsAppConfigLayers = mergeWhatsAppConfigLayers;
2346
1436
  exports.parseEmailRecipientsFromConfig = parseEmailRecipientsFromConfig;
2347
1437
  exports.parseIntentRecipientEmails = parseIntentRecipientEmails;
2348
- exports.queueWhatsApp = queueWhatsApp;
2349
- exports.registerWhatsAppQueueProcessor = registerWhatsAppQueueProcessor;
2350
1438
  exports.renderEmail = renderEmail;
2351
1439
  exports.renderLayout = renderLayout;
2352
- exports.resendOrderNotification = resendOrderNotification;
2353
1440
  exports.resolveChatEmailToolSettings = resolveChatEmailToolSettings;
2354
1441
  exports.sendChatLeadEmail = sendChatLeadEmail;
2355
1442
  exports.serializeEmailRecipients = serializeEmailRecipients;
2356
- exports.whatsAppConfigured = whatsAppConfigured;