@infuro/cms-core 1.0.61 → 1.0.63

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 (50) hide show
  1. package/dist/admin.cjs +3624 -2642
  2. package/dist/admin.js +2415 -1433
  3. package/dist/api.cjs +41 -37
  4. package/dist/api.js +9 -5
  5. package/dist/chunk-3VZAAQUV.js +106 -0
  6. package/dist/{chunk-UHT4INVW.js → chunk-77YWLP4X.js} +625 -252
  7. package/dist/{chunk-BXYZDMTZ.cjs → chunk-7L556RGL.cjs} +3 -94
  8. package/dist/{chunk-T3HPSF7K.js → chunk-AYWDQFJZ.js} +3 -0
  9. package/dist/{chunk-URN7GS23.js → chunk-BWOITUNI.js} +203 -1062
  10. package/dist/{chunk-CNAHVRRU.cjs → chunk-CJEOH6QC.cjs} +676 -312
  11. package/dist/{chunk-O2642WVS.js → chunk-DTUL3Q5L.js} +17 -3
  12. package/dist/{chunk-YSYX4GUD.cjs → chunk-EWXG4E74.cjs} +213 -1082
  13. package/dist/chunk-GWDI752Q.js +83 -0
  14. package/dist/chunk-H3IVLIPR.cjs +86 -0
  15. package/dist/chunk-JLDHK2QE.cjs +109 -0
  16. package/dist/chunk-KAOHOHCA.js +901 -0
  17. package/dist/{chunk-2KUCQVAQ.js → chunk-MWEK7N3F.js} +2 -91
  18. package/dist/{chunk-V25RDUOU.cjs → chunk-MXABMVF7.cjs} +38 -4
  19. package/dist/chunk-OVTOP42W.js +199 -0
  20. package/dist/{chunk-73WVHINE.cjs → chunk-RHYVNRYG.cjs} +18 -4
  21. package/dist/{chunk-L3525VXI.js → chunk-RXTUSF3R.js} +36 -2
  22. package/dist/{chunk-77I3KS7V.cjs → chunk-UEE4PTTB.cjs} +3 -0
  23. package/dist/chunk-WFMVD567.cjs +209 -0
  24. package/dist/chunk-YVP4NQII.cjs +914 -0
  25. package/dist/{email-queue-6NJY6HNM.cjs → email-queue-VTYL2KFA.cjs} +7 -6
  26. package/dist/{email-queue-OI2HSCGE.js → email-queue-YPRSVG56.js} +3 -2
  27. package/dist/email-service-ATCPJFWG.cjs +28 -0
  28. package/dist/email-service-DPKVQOKP.js +3 -0
  29. package/dist/{emit-order-notification-trigger-TNEZOYEE.cjs → emit-order-notification-trigger-WI4HGMKH.cjs} +4 -4
  30. package/dist/{emit-order-notification-trigger-CZU3V2WE.js → emit-order-notification-trigger-XVG2B5ZF.js} +2 -2
  31. package/dist/{erp-order-invoice-3GXDE443.js → erp-order-invoice-3UGWLICU.js} +2 -1
  32. package/dist/{erp-order-invoice-EYYNR526.cjs → erp-order-invoice-KJYJNWMG.cjs} +6 -5
  33. package/dist/index.cjs +366 -316
  34. package/dist/index.d.cts +61 -21
  35. package/dist/index.d.ts +61 -21
  36. package/dist/index.js +79 -29
  37. package/dist/migrations/1783000000000-AddOrderPendingProcessingCompletedTriggers.ts +56 -0
  38. package/dist/migrations/1783100000000-AddSmsToOrderNotificationBindingsChannelEnum.ts +17 -0
  39. package/dist/migrations/1783200000000-AddOrderAssigneeColumns.ts +30 -0
  40. package/dist/order-assignees-handlers-LKMAGLG6.js +150 -0
  41. package/dist/order-assignees-handlers-NPKD64P6.cjs +152 -0
  42. package/dist/order-completion-otp-handlers-IMMQGHDR.cjs +363 -0
  43. package/dist/order-completion-otp-handlers-WGM7HWG4.js +361 -0
  44. package/dist/order-notification-dispatcher-5TUFCDU7.cjs +35 -0
  45. package/dist/order-notification-dispatcher-JKFQJYGG.js +10 -0
  46. package/dist/sms-notification-templates-handlers-SJLO7JGZ.js +47 -0
  47. package/dist/sms-notification-templates-handlers-YSB3L2PK.cjs +49 -0
  48. package/package.json +1 -1
  49. package/dist/order-notification-dispatcher-KJVGLK5E.cjs +0 -24
  50. package/dist/order-notification-dispatcher-ZSFKEYMP.js +0 -7
@@ -0,0 +1,914 @@
1
+ 'use strict';
2
+
3
+ var chunkUSNT2KNT_cjs = require('./chunk-USNT2KNT.cjs');
4
+ var clientSes = require('@aws-sdk/client-ses');
5
+ var module$1 = require('module');
6
+ var nodemailer = require('nodemailer');
7
+
8
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
9
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
+
11
+ var nodemailer__default = /*#__PURE__*/_interopDefault(nodemailer);
12
+
13
+ // src/plugins/email/templates/layout.ts
14
+ function escapeHtml(s) {
15
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
16
+ }
17
+ chunkUSNT2KNT_cjs.__name(escapeHtml, "escapeHtml");
18
+ function socialLinkInnerHtml(s) {
19
+ if (s.iconUrl?.trim()) {
20
+ return `<img src="${escapeHtml(s.iconUrl.trim())}" alt="" width="20" height="20" style="display:inline-block;vertical-align:middle;border:0;" />`;
21
+ }
22
+ const label = s.icon && s.icon.trim() || "\u{1F517}";
23
+ return escapeHtml(label);
24
+ }
25
+ chunkUSNT2KNT_cjs.__name(socialLinkInnerHtml, "socialLinkInnerHtml");
26
+ function supportLinesHtml(supportEmail, supportPhone) {
27
+ const parts = [];
28
+ if (supportEmail) {
29
+ parts.push(`<a href="mailto:${escapeHtml(supportEmail)}" style="color:#6b7280;text-decoration:none;font-size:13px;">${escapeHtml(supportEmail)}</a>`);
30
+ }
31
+ if (supportPhone) {
32
+ parts.push(`<span style="color:#6b7280;font-size:13px;">${escapeHtml(supportPhone)}</span>`);
33
+ }
34
+ return parts.join('<span style="color:#d1d5db;padding:0 8px;">\xB7</span>');
35
+ }
36
+ chunkUSNT2KNT_cjs.__name(supportLinesHtml, "supportLinesHtml");
37
+ function renderLayout(options) {
38
+ const { bodyHtml, companyDetails } = options;
39
+ const { logoUrl, companyName, supportEmail, supportPhone, socialLinks, footerDisclaimer, followUsTitle = "Follow Us" } = companyDetails;
40
+ const brand = (companyName || "Store").trim();
41
+ const accent = "#ea580c";
42
+ const logoHtml = logoUrl ? `<img src="${escapeHtml(logoUrl)}" alt="${escapeHtml(brand)}" width="160" style="max-height:56px;max-width:180px;height:auto;width:auto;display:block;border:0;margin:0 auto;" />` : `<div style="font-size:22px;font-weight:700;letter-spacing:-0.02em;color:#111827;">${escapeHtml(brand)}</div>`;
43
+ const hasSocial = Boolean(socialLinks?.length);
44
+ const socialRowHtml = hasSocial && socialLinks ? `<div style="margin-top:12px;text-align:center;font-size:0;line-height:0;">${socialLinks.map((s) => `<a href="${escapeHtml(s.url)}" style="display:inline-block;margin:0 8px;text-decoration:none;color:#374151;font-size:18px;line-height:1;vertical-align:middle;">${socialLinkInnerHtml(s)}</a>`).join("")}</div>` : "";
45
+ const followTitleHtml = hasSocial && followUsTitle ? `<div style="font-size:12px;font-weight:600;color:#9ca3af;text-transform:uppercase;letter-spacing:0.06em;margin:0 0 4px 0;">${escapeHtml(followUsTitle)}</div>` : "";
46
+ const supportHtml = supportLinesHtml(supportEmail, supportPhone);
47
+ const disclaimerBlock = footerDisclaimer ? `<div style="margin-top:16px;font-size:11px;line-height:1.5;color:#9ca3af;">${escapeHtml(footerDisclaimer).replace(/\n/g, "<br/>")}</div>` : "";
48
+ const responsiveCss = `
49
+ @media only screen and (max-width: 620px) {
50
+ .email-shell { padding: 16px 10px !important; }
51
+ .email-card { border-radius: 12px !important; }
52
+ .email-body { padding: 24px 18px !important; }
53
+ .email-header { padding: 20px 18px !important; }
54
+ }
55
+ `;
56
+ return `<!DOCTYPE html>
57
+ <html lang="en">
58
+ <head>
59
+ <meta charset="utf-8">
60
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
61
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
62
+ <title>${escapeHtml(brand)}</title>
63
+ <style type="text/css">${responsiveCss}</style>
64
+ </head>
65
+ <body style="margin:0;padding:0;background:#f3f4f6;-webkit-font-smoothing:antialiased;">
66
+ <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f3f4f6;border-collapse:collapse;">
67
+ <tr>
68
+ <td class="email-shell" align="center" style="padding:32px 16px;">
69
+ <table role="presentation" class="email-card" width="100%" cellpadding="0" cellspacing="0" style="max-width:600px;background:#ffffff;border-radius:16px;overflow:hidden;border:1px solid #e5e7eb;border-collapse:collapse;">
70
+ <tr>
71
+ <td class="email-header" align="center" style="padding:28px 32px 20px 32px;background:#ffffff;">
72
+ ${logoHtml}
73
+ ${logoUrl && companyName ? `<div style="margin-top:10px;font-size:13px;color:#6b7280;">${escapeHtml(companyName)}</div>` : ""}
74
+ </td>
75
+ </tr>
76
+ <tr>
77
+ <td style="height:4px;line-height:4px;font-size:0;background:${accent};">&nbsp;</td>
78
+ </tr>
79
+ <tr>
80
+ <td class="email-body" style="padding:28px 32px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;font-size:15px;line-height:1.65;color:#1f2937;">
81
+ ${bodyHtml}
82
+ </td>
83
+ </tr>
84
+ <tr>
85
+ <td style="padding:0 32px;">
86
+ <div style="border-top:1px solid #e5e7eb;line-height:0;font-size:0;">&nbsp;</div>
87
+ </td>
88
+ </tr>
89
+ <tr>
90
+ <td align="center" style="padding:20px 32px 28px 32px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;">
91
+ ${followTitleHtml}
92
+ ${socialRowHtml}
93
+ ${supportHtml ? `<div style="margin-top:14px;">${supportHtml}</div>` : ""}
94
+ <div style="margin-top:12px;font-size:12px;color:#9ca3af;">\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} ${escapeHtml(brand)}</div>
95
+ ${disclaimerBlock}
96
+ </td>
97
+ </tr>
98
+ </table>
99
+ </td>
100
+ </tr>
101
+ </table>
102
+ </body>
103
+ </html>`;
104
+ }
105
+ chunkUSNT2KNT_cjs.__name(renderLayout, "renderLayout");
106
+
107
+ // src/plugins/email/templates/inline-cta.ts
108
+ function escapeHtml2(s) {
109
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
110
+ }
111
+ chunkUSNT2KNT_cjs.__name(escapeHtml2, "escapeHtml");
112
+ function escapeAttr(s) {
113
+ return escapeHtml2(s);
114
+ }
115
+ chunkUSNT2KNT_cjs.__name(escapeAttr, "escapeAttr");
116
+ function primaryCtaButton(href, label) {
117
+ return `<table role="presentation" cellpadding="0" cellspacing="0" style="margin:20px 0;">
118
+ <tr><td style="border-radius:6px;background:#1a1a1a;">
119
+ <a href="${escapeAttr(href)}" style="display:inline-block;padding:12px 22px;font-size:14px;font-weight:600;color:#ffffff;text-decoration:none;">${escapeHtml2(label)}</a>
120
+ </td></tr>
121
+ </table>`;
122
+ }
123
+ chunkUSNT2KNT_cjs.__name(primaryCtaButton, "primaryCtaButton");
124
+
125
+ // src/plugins/email/templates/signup.ts
126
+ function render(ctx) {
127
+ const { name, loginUrl, verifyEmailUrl, companyDetails } = ctx;
128
+ if (verifyEmailUrl) {
129
+ const subject2 = "Confirm your email";
130
+ const greeting = name && name.trim() ? `Hi ${escapeHtml2(name.trim())},` : "Hi,";
131
+ const bodyHtml2 = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${greeting}</p>
132
+ <p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">Thanks for signing up. Please confirm your email address to activate your account. Until you confirm, you won\u2019t be able to sign in.</p>
133
+ ${primaryCtaButton(verifyEmailUrl, "Confirm email address")}
134
+ <p style="margin:16px 0 0 0;font-size:12px;line-height:1.5;color:#888;">This link expires in a few days. If you didn\u2019t create an account, you can ignore this message.<br/><span style="word-break:break-all;">${escapeHtml2(verifyEmailUrl)}</span></p>`;
135
+ const text2 = [
136
+ greeting,
137
+ "",
138
+ "Confirm your email to activate your account:",
139
+ verifyEmailUrl,
140
+ "",
141
+ "You cannot sign in until your email is confirmed."
142
+ ].join("\n");
143
+ const html2 = renderLayout({
144
+ bodyHtml: bodyHtml2,
145
+ companyDetails
146
+ });
147
+ return {
148
+ subject: subject2,
149
+ html: html2,
150
+ text: text2
151
+ };
152
+ }
153
+ const subject = "Welcome";
154
+ const bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">Welcome${name ? `, ${escapeHtml2(name)}` : ""}.</p><p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">Your account has been created.</p>${loginUrl ? `${primaryCtaButton(loginUrl, "Sign in")}` : ""}`;
155
+ const text = `Welcome${name ? `, ${name}` : ""}. Your account has been created.${loginUrl ? ` Sign in: ${loginUrl}` : ""}`;
156
+ const html = renderLayout({
157
+ bodyHtml,
158
+ companyDetails
159
+ });
160
+ return {
161
+ subject,
162
+ html,
163
+ text
164
+ };
165
+ }
166
+ chunkUSNT2KNT_cjs.__name(render, "render");
167
+
168
+ // src/plugins/email/templates/passwordReset.ts
169
+ function render2(ctx) {
170
+ const { resetLink, companyDetails } = ctx;
171
+ const subject = "Reset your password";
172
+ const bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">Hello,</p>
173
+ <p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">We received a request to reset the password for your account. If you made this request, use the button below to choose a new password.</p>
174
+ <p style="margin:0 0 8px 0;font-size:14px;line-height:1.5;color:#555;">For your security, this link will stop working after a short time. If it expires, request a new reset from the sign-in page.</p>
175
+ ${primaryCtaButton(resetLink, "Reset password")}
176
+ <p style="margin:16px 0 0 0;font-size:12px;line-height:1.5;color:#888;">If you did not request a password reset, you can safely ignore this email\u2014your password will stay the same.</p>
177
+ <p style="margin:12px 0 0 0;font-size:12px;line-height:1.5;color:#888;">If the button does not work, copy and paste this URL into your browser:<br/><span style="word-break:break-all;">${escapeHtml2(resetLink)}</span></p>`;
178
+ const text = [
179
+ "Hello,",
180
+ "",
181
+ "We received a request to reset your password. Open the link below to set a new password:",
182
+ resetLink,
183
+ "",
184
+ "This link expires after a limited time.",
185
+ "",
186
+ "If you did not request this, you can ignore this email."
187
+ ].join("\n");
188
+ const html = renderLayout({
189
+ bodyHtml,
190
+ companyDetails
191
+ });
192
+ return {
193
+ subject,
194
+ html,
195
+ text
196
+ };
197
+ }
198
+ chunkUSNT2KNT_cjs.__name(render2, "render");
199
+
200
+ // src/plugins/email/templates/passwordChange.ts
201
+ function render3(ctx) {
202
+ const { name, companyDetails } = ctx;
203
+ const subject = "Password changed";
204
+ const bodyHtml = `<h2>Password changed</h2><p>Your password has been updated successfully${name ? `, ${escapeHtml3(name)}` : ""}.</p>`;
205
+ const text = `Your password has been updated successfully${name ? `, ${name}` : ""}.`;
206
+ const html = renderLayout({
207
+ bodyHtml,
208
+ companyDetails
209
+ });
210
+ return {
211
+ subject,
212
+ html,
213
+ text
214
+ };
215
+ }
216
+ chunkUSNT2KNT_cjs.__name(render3, "render");
217
+ function escapeHtml3(s) {
218
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
219
+ }
220
+ chunkUSNT2KNT_cjs.__name(escapeHtml3, "escapeHtml");
221
+
222
+ // src/plugins/email/templates/orderPlaced.ts
223
+ function formatMoney(amount, currency) {
224
+ const v = typeof amount === "string" ? parseFloat(amount) : amount;
225
+ const num = Number.isFinite(v) ? Number(v).toFixed(2) : String(amount);
226
+ return currency ? `${num} ${currency}` : num;
227
+ }
228
+ chunkUSNT2KNT_cjs.__name(formatMoney, "formatMoney");
229
+ function renderLineItemsHtml(items, currency) {
230
+ if (!items.length) {
231
+ return '<p style="margin:12px 0 0 0;font-size:14px;color:#666;">No line items.</p>';
232
+ }
233
+ const rows = items.map((it) => {
234
+ const name = escapeHtml2(it.productName);
235
+ const sku = it.sku && String(it.sku).trim() ? `<span style="font-size:12px;color:#888;"> (${escapeHtml2(String(it.sku).trim())})</span>` : "";
236
+ const hsn = it.hsn && String(it.hsn).trim() ? `<br/><span style="font-size:11px;color:#888;">HSN: ${escapeHtml2(String(it.hsn).trim())}</span>` : "";
237
+ const taxNote = it.tax != null && String(it.tax).trim() && Number(it.tax) !== 0 ? `<br/><span style="font-size:11px;color:#888;">Tax: ${escapeHtml2(formatMoney(it.tax, currency))}</span>` : "";
238
+ return `<tr>
239
+ <td style="padding:10px 8px 10px 0;border-bottom:1px solid #eee;vertical-align:top;font-size:14px;color:#333;">${name}${sku}${hsn}${taxNote}</td>
240
+ <td align="right" style="padding:10px 8px;border-bottom:1px solid #eee;vertical-align:top;font-size:14px;color:#333;white-space:nowrap;">${escapeHtml2(String(it.quantity))}</td>
241
+ <td align="right" style="padding:10px 8px;border-bottom:1px solid #eee;vertical-align:top;font-size:14px;color:#333;white-space:nowrap;">${escapeHtml2(formatMoney(it.unitPrice, currency))}</td>
242
+ <td align="right" style="padding:10px 0 10px 8px;border-bottom:1px solid #eee;vertical-align:top;font-size:14px;color:#333;white-space:nowrap;">${escapeHtml2(formatMoney(it.lineTotal, currency))}</td>
243
+ </tr>`;
244
+ }).join("");
245
+ return `<p style="margin:16px 0 8px 0;font-size:14px;font-weight:600;color:#111;">Order items</p>
246
+ <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;">
247
+ <tr>
248
+ <td style="padding:0 8px 8px 0;font-size:12px;font-weight:600;color:#555;text-transform:uppercase;letter-spacing:0.02em;">Item</td>
249
+ <td align="right" style="padding:0 8px 8px 8px;font-size:12px;font-weight:600;color:#555;text-transform:uppercase;">Qty</td>
250
+ <td align="right" style="padding:0 8px 8px 8px;font-size:12px;font-weight:600;color:#555;text-transform:uppercase;">Price</td>
251
+ <td align="right" style="padding:0 0 8px 8px;font-size:12px;font-weight:600;color:#555;text-transform:uppercase;">Total</td>
252
+ </tr>
253
+ ${rows}
254
+ </table>`;
255
+ }
256
+ chunkUSNT2KNT_cjs.__name(renderLineItemsHtml, "renderLineItemsHtml");
257
+ function renderLineItemsText(items, currency) {
258
+ if (!items.length) return "";
259
+ const lines = items.map((it) => {
260
+ let s = `- ${it.productName} \xD7 ${it.quantity} @ ${formatMoney(it.unitPrice, currency)} = ${formatMoney(it.lineTotal, currency)}`;
261
+ if (it.sku) s += ` [${it.sku}]`;
262
+ if (it.hsn && String(it.hsn).trim()) s += ` HSN:${it.hsn}`;
263
+ if (it.tax != null && Number(it.tax) !== 0) s += ` tax:${formatMoney(it.tax, currency)}`;
264
+ return s;
265
+ });
266
+ return [
267
+ "Items:",
268
+ ...lines
269
+ ].join("\n");
270
+ }
271
+ chunkUSNT2KNT_cjs.__name(renderLineItemsText, "renderLineItemsText");
272
+ function formatAddressLines(addr) {
273
+ if (!addr || typeof addr !== "object") return [];
274
+ const parts = [
275
+ addr.line1,
276
+ addr.line2,
277
+ [
278
+ addr.city,
279
+ addr.state
280
+ ].filter(Boolean).join(", "),
281
+ addr.postalCode,
282
+ addr.country
283
+ ].filter((x) => x != null && String(x).trim() !== "");
284
+ return parts.map((p) => String(p).trim()).filter(Boolean);
285
+ }
286
+ chunkUSNT2KNT_cjs.__name(formatAddressLines, "formatAddressLines");
287
+ function renderAddressesHtml(billing, shipping) {
288
+ const bLines = formatAddressLines(billing);
289
+ const sLines = formatAddressLines(shipping);
290
+ if (!bLines.length && !sLines.length) return "";
291
+ let html = '<p style="margin:16px 0 8px 0;font-size:14px;font-weight:600;color:#111;">Addresses</p>';
292
+ if (bLines.length) {
293
+ html += `<p style="margin:0 0 4px 0;font-size:12px;font-weight:600;color:#555;">Billing</p><p style="margin:0 0 12px 0;font-size:14px;line-height:1.5;color:#333;">${bLines.map((l) => escapeHtml2(l)).join("<br/>")}</p>`;
294
+ }
295
+ if (sLines.length) {
296
+ html += `<p style="margin:0 0 4px 0;font-size:12px;font-weight:600;color:#555;">Shipping</p><p style="margin:0 0 0 0;font-size:14px;line-height:1.5;color:#333;">${sLines.map((l) => escapeHtml2(l)).join("<br/>")}</p>`;
297
+ }
298
+ return html;
299
+ }
300
+ chunkUSNT2KNT_cjs.__name(renderAddressesHtml, "renderAddressesHtml");
301
+ function renderAddressesText(billing, shipping) {
302
+ const bLines = formatAddressLines(billing);
303
+ const sLines = formatAddressLines(shipping);
304
+ const out = [];
305
+ if (bLines.length) out.push("Billing:", ...bLines.map((l) => ` ${l}`));
306
+ if (sLines.length) out.push("Shipping:", ...sLines.map((l) => ` ${l}`));
307
+ return out.length ? out.join("\n") : "";
308
+ }
309
+ chunkUSNT2KNT_cjs.__name(renderAddressesText, "renderAddressesText");
310
+ function render4(ctx) {
311
+ const { orderNumber, total, subtotal, tax, currency, customerName, companyDetails, audience = "customer", internalCustomerEmail, lineItems = [], billingAddress, shippingAddress, invoiceAttached = false } = ctx;
312
+ const itemsHtml = renderLineItemsHtml(lineItems, currency);
313
+ const itemsText = renderLineItemsText(lineItems, currency);
314
+ const addrHtml = renderAddressesHtml(billingAddress, shippingAddress);
315
+ const addrText = renderAddressesText(billingAddress, shippingAddress);
316
+ const subtotalLine = subtotal != null && String(subtotal).trim() !== "" ? `<p style="margin:8px 0 0 0;font-size:14px;line-height:1.5;color:#333;"><strong>Subtotal:</strong> ${escapeHtml2(String(subtotal))}${currency ? ` ${escapeHtml2(currency)}` : ""}</p>` : "";
317
+ const taxLine = tax != null && String(tax).trim() !== "" && Number(tax) !== 0 ? `<p style="margin:4px 0 0 0;font-size:14px;line-height:1.5;color:#333;"><strong>Tax:</strong> ${escapeHtml2(String(tax))}${currency ? ` ${escapeHtml2(currency)}` : ""}</p>` : "";
318
+ const totalLine = total != null && String(total).trim() !== "" ? `<p style="margin:12px 0 0 0;font-size:15px;line-height:1.5;color:#333;"><strong>Order total:</strong> ${escapeHtml2(String(total))}${currency ? ` ${escapeHtml2(currency)}` : ""}</p>` : "";
319
+ let subject;
320
+ let bodyHtml;
321
+ let text;
322
+ if (audience === "sales") {
323
+ subject = `New order #${orderNumber}`;
324
+ const who = customerName || internalCustomerEmail ? `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;"><strong>Customer:</strong> ${escapeHtml2(customerName || "\u2014")}${internalCustomerEmail ? ` <span style="color:#555;">(${escapeHtml2(internalCustomerEmail)})</span>` : ""}</p>` : "";
325
+ bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">A new order has been placed and payment completed.</p>
326
+ <p style="margin:0 0 8px 0;font-size:15px;line-height:1.5;color:#333;"><strong>Order number:</strong> ${escapeHtml2(orderNumber)}</p>
327
+ ${who}
328
+ ${addrHtml}
329
+ ${itemsHtml}
330
+ ${subtotalLine}${taxLine}${totalLine}`;
331
+ text = [
332
+ `New order #${orderNumber}`,
333
+ customerName ? `Customer: ${customerName}` : "",
334
+ internalCustomerEmail ? `Email: ${internalCustomerEmail}` : "",
335
+ addrText,
336
+ itemsText,
337
+ subtotal != null ? `Subtotal: ${subtotal}${currency ? ` ${currency}` : ""}` : "",
338
+ tax != null && Number(tax) !== 0 ? `Tax: ${tax}${currency ? ` ${currency}` : ""}` : "",
339
+ total != null ? `Order total: ${total}${currency ? ` ${currency}` : ""}` : ""
340
+ ].filter(Boolean).join("\n\n");
341
+ } else {
342
+ subject = `Order confirmed #${orderNumber}`;
343
+ const thanksPlain = customerName && customerName.trim() ? `Thank you, ${customerName.trim()}.` : "Thank you for your order.";
344
+ const thanksHtml = `${escapeHtml2(thanksPlain)} We\u2019ve received your order and will process it shortly.`;
345
+ bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${thanksHtml}</p>
346
+ <p style="margin:0 0 8px 0;font-size:15px;line-height:1.5;color:#333;"><strong>Order number:</strong> ${escapeHtml2(orderNumber)}</p>
347
+ ${addrHtml}
348
+ ${itemsHtml}
349
+ ${subtotalLine}${taxLine}${totalLine}
350
+ ${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>` : ""}
351
+ <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>`;
352
+ text = [
353
+ `Order confirmed #${orderNumber}`,
354
+ `${thanksPlain} We\u2019ve received your order and will process it shortly.`,
355
+ "",
356
+ addrText,
357
+ itemsText,
358
+ subtotal != null ? `Subtotal: ${subtotal}${currency ? ` ${currency}` : ""}` : "",
359
+ tax != null && Number(tax) !== 0 ? `Tax: ${tax}${currency ? ` ${currency}` : ""}` : "",
360
+ total != null ? `Order total: ${total}${currency ? ` ${currency}` : ""}` : "",
361
+ invoiceAttached ? "Your order invoice is attached as a PDF." : "",
362
+ "",
363
+ "We will process your order shortly."
364
+ ].filter((line, i, arr) => !(line === "" && arr[i - 1] === "")).join("\n");
365
+ }
366
+ const html = renderLayout({
367
+ bodyHtml,
368
+ companyDetails
369
+ });
370
+ return {
371
+ subject,
372
+ html,
373
+ text
374
+ };
375
+ }
376
+ chunkUSNT2KNT_cjs.__name(render4, "render");
377
+
378
+ // src/plugins/email/templates/returnInitiated.ts
379
+ function render5(ctx) {
380
+ const { returnId, orderNumber, companyDetails } = ctx;
381
+ const subject = "Return initiated";
382
+ const bodyHtml = `<h2>Return initiated</h2><p>Your return request has been received.</p>${orderNumber ? `<p><strong>Order:</strong> ${escapeHtml4(orderNumber)}</p>` : ""}${returnId ? `<p><strong>Return ID:</strong> ${escapeHtml4(returnId)}</p>` : ""}`;
383
+ const text = `Return initiated.${orderNumber ? ` Order: ${orderNumber}` : ""}${returnId ? ` Return ID: ${returnId}` : ""}`;
384
+ const html = renderLayout({
385
+ bodyHtml,
386
+ companyDetails
387
+ });
388
+ return {
389
+ subject,
390
+ html,
391
+ text
392
+ };
393
+ }
394
+ chunkUSNT2KNT_cjs.__name(render5, "render");
395
+ function escapeHtml4(s) {
396
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
397
+ }
398
+ chunkUSNT2KNT_cjs.__name(escapeHtml4, "escapeHtml");
399
+
400
+ // src/plugins/email/templates/shippingUpdate.ts
401
+ function render6(ctx) {
402
+ const { orderNumber, status, trackingUrl, companyDetails } = ctx;
403
+ const subject = status ? `Shipping update: ${status}` : "Shipping update";
404
+ const bodyHtml = `<h2>Shipping update</h2>${status ? `<p><strong>Status:</strong> ${escapeHtml5(status)}</p>` : ""}${orderNumber ? `<p><strong>Order:</strong> ${escapeHtml5(orderNumber)}</p>` : ""}${trackingUrl ? `<p><a href="${escapeHtml5(trackingUrl)}">Track your order</a></p>` : ""}`;
405
+ const text = `Shipping update.${status ? ` Status: ${status}` : ""}${orderNumber ? ` Order: ${orderNumber}` : ""}${trackingUrl ? ` Track: ${trackingUrl}` : ""}`;
406
+ const html = renderLayout({
407
+ bodyHtml,
408
+ companyDetails
409
+ });
410
+ return {
411
+ subject,
412
+ html,
413
+ text
414
+ };
415
+ }
416
+ chunkUSNT2KNT_cjs.__name(render6, "render");
417
+ function escapeHtml5(s) {
418
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
419
+ }
420
+ chunkUSNT2KNT_cjs.__name(escapeHtml5, "escapeHtml");
421
+
422
+ // src/plugins/email/templates/invite.ts
423
+ function render7(ctx) {
424
+ const { inviteLink, email, inviteeName, companyDetails } = ctx;
425
+ const subject = "You're invited";
426
+ const greeting = inviteeName && inviteeName.trim() ? `Hello ${escapeHtml2(inviteeName.trim())},` : "Hello,";
427
+ const bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${greeting}</p>
428
+ <p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">You have been invited to create your account for <strong>${escapeHtml2(email)}</strong>. Use the secure link below to choose your password and activate access.</p>
429
+ <p style="margin:0 0 8px 0;font-size:14px;line-height:1.5;color:#555;">This link is personal to you. If you did not expect this invitation, you can ignore this message.</p>
430
+ ${primaryCtaButton(inviteLink, "Accept invitation & set password")}
431
+ <p style="margin:16px 0 0 0;font-size:12px;line-height:1.5;color:#888;">If the button does not work, copy and paste this URL into your browser:<br/><span style="word-break:break-all;">${escapeHtml2(inviteLink)}</span></p>`;
432
+ const text = [
433
+ inviteeName?.trim() ? `Hello ${inviteeName.trim()},` : "Hello,",
434
+ "",
435
+ `You have been invited to create your account (${email}).`,
436
+ "Open this link to set your password:",
437
+ inviteLink,
438
+ "",
439
+ "If you did not expect this invitation, you can ignore this email."
440
+ ].join("\n");
441
+ const html = renderLayout({
442
+ bodyHtml,
443
+ companyDetails
444
+ });
445
+ return {
446
+ subject,
447
+ html,
448
+ text
449
+ };
450
+ }
451
+ chunkUSNT2KNT_cjs.__name(render7, "render");
452
+
453
+ // src/plugins/email/templates/formSubmission.ts
454
+ function render8(ctx) {
455
+ const { formName, contactName, contactEmail, formData, formFieldRows, companyDetails } = ctx;
456
+ const subject = `New Form Submission: ${formName}`;
457
+ const fieldsBlock = formFieldRows && formFieldRows.length > 0 ? formFieldRows.map((r) => `<p style="margin:10px 0 0 0;line-height:1.5;"><strong>${escapeHtml6(r.label)}</strong><br/>${escapeHtml6(r.value)}</p>`).join("") : `<p style="margin:10px 0 0 0;font-size:13px;white-space:pre-wrap;">${escapeHtml6(JSON.stringify(formData, null, 2))}</p>`;
458
+ const bodyHtml = `<p style="margin:0 0 6px 0;font-size:18px;font-weight:600;color:#111;">New Form Submission</p>
459
+ <p style="margin:0 0 4px 0;"><strong>Form:</strong> ${escapeHtml6(formName)}</p>
460
+ <p style="margin:0 0 8px 0;"><strong>Contact:</strong> ${escapeHtml6(contactName)}${contactEmail ? ` (${escapeHtml6(contactEmail)})` : ""}</p>
461
+ ${fieldsBlock}`;
462
+ const textLines = [
463
+ "New Form Submission",
464
+ `Form: ${formName}`,
465
+ `Contact: ${contactName}${contactEmail ? ` (${contactEmail})` : ""}`,
466
+ "",
467
+ ...formFieldRows?.length ? formFieldRows.map((r) => `${r.label}: ${r.value}`) : [
468
+ JSON.stringify(formData, null, 2)
469
+ ]
470
+ ];
471
+ const text = textLines.join("\n");
472
+ const html = renderLayout({
473
+ bodyHtml,
474
+ companyDetails
475
+ });
476
+ return {
477
+ subject,
478
+ html,
479
+ text
480
+ };
481
+ }
482
+ chunkUSNT2KNT_cjs.__name(render8, "render");
483
+ function escapeHtml6(s) {
484
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
485
+ }
486
+ chunkUSNT2KNT_cjs.__name(escapeHtml6, "escapeHtml");
487
+
488
+ // src/plugins/email/templates/otp.ts
489
+ function render9(ctx) {
490
+ const { code, companyDetails } = ctx;
491
+ const name = companyDetails.companyName?.trim() || "Our store";
492
+ const bodyHtml = `
493
+ <p>Your verification code is:</p>
494
+ <p style="font-size:24px;font-weight:bold;letter-spacing:4px;">${escapeHtml7(code)}</p>
495
+ <p style="color:#666;font-size:14px;">This code expires in 10 minutes. If you did not request it, you can ignore this email.</p>
496
+ `;
497
+ const html = renderLayout({
498
+ bodyHtml,
499
+ companyDetails
500
+ });
501
+ return {
502
+ subject: `${name}: your sign-in code`,
503
+ html,
504
+ text: `Your code is ${code}. It expires in 10 minutes.`
505
+ };
506
+ }
507
+ chunkUSNT2KNT_cjs.__name(render9, "render");
508
+ function escapeHtml7(s) {
509
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
510
+ }
511
+ chunkUSNT2KNT_cjs.__name(escapeHtml7, "escapeHtml");
512
+
513
+ // src/plugins/email/templates/chatLead.ts
514
+ function render10(ctx) {
515
+ const { contactName, contactEmail, contactPhone, conversationId, latestMessage, intentCode, intentReason, transcript, companyDetails } = ctx;
516
+ const intentDisplay = intentCode?.trim() ? `${intentCode.trim()}${intentReason?.trim() ? ` \u2014 ${intentReason.trim()}` : ""}` : intentReason;
517
+ const subject = `Chat lead${intentCode?.trim() ? ` [${intentCode.trim()}]` : ""}: ${contactName || contactEmail || "Visitor"}`;
518
+ const transcriptBlock = transcript?.trim() ? `<div style="margin-top:16px;padding:12px;background:#f9fafb;border-radius:6px;border:1px solid #e5e7eb;">
519
+ <p style="margin:0 0 8px 0;font-size:13px;font-weight:600;color:#374151;">Recent conversation</p>
520
+ <pre style="margin:0;font-size:12px;white-space:pre-wrap;font-family:inherit;color:#4b5563;">${escapeHtml8(transcript.trim())}</pre>
521
+ </div>` : "";
522
+ const bodyHtml = `<p style="margin:0 0 6px 0;font-size:18px;font-weight:600;color:#111;">New chat lead</p>
523
+ <p style="margin:0 0 12px 0;font-size:14px;color:#374151;">A visitor showed interest based on your intent rules.</p>
524
+ <table style="width:100%;border-collapse:collapse;font-size:14px;">
525
+ <tr><td style="padding:4px 8px 4px 0;color:#6b7280;vertical-align:top;">Name</td><td>${escapeHtml8(contactName || "\u2014")}</td></tr>
526
+ <tr><td style="padding:4px 8px 4px 0;color:#6b7280;vertical-align:top;">Email</td><td><a href="mailto:${escapeHtml8(contactEmail)}">${escapeHtml8(contactEmail)}</a></td></tr>
527
+ <tr><td style="padding:4px 8px 4px 0;color:#6b7280;vertical-align:top;">Phone</td><td>${escapeHtml8(contactPhone?.trim() || "\u2014")}</td></tr>
528
+ <tr><td style="padding:4px 8px 4px 0;color:#6b7280;vertical-align:top;">Conversation</td><td>#${conversationId}</td></tr>
529
+ <tr><td style="padding:4px 8px 4px 0;color:#6b7280;vertical-align:top;">Intent</td><td>${escapeHtml8(intentReason)}</td></tr>
530
+ </table>
531
+ <p style="margin:16px 0 6px 0;font-size:13px;font-weight:600;color:#374151;">Latest message</p>
532
+ <p style="margin:0;font-size:14px;white-space:pre-wrap;">${escapeHtml8(latestMessage)}</p>
533
+ ${transcriptBlock}`;
534
+ const text = [
535
+ "New chat lead",
536
+ `Name: ${contactName || "\u2014"}`,
537
+ `Email: ${contactEmail}`,
538
+ `Phone: ${contactPhone?.trim() || "\u2014"}`,
539
+ `Conversation: #${conversationId}`,
540
+ `Intent: ${intentDisplay}`,
541
+ "",
542
+ "Latest message:",
543
+ latestMessage,
544
+ transcript?.trim() ? `
545
+ Recent conversation:
546
+ ${transcript.trim()}` : ""
547
+ ].join("\n");
548
+ const html = renderLayout({
549
+ bodyHtml,
550
+ companyDetails
551
+ });
552
+ return {
553
+ subject,
554
+ html,
555
+ text
556
+ };
557
+ }
558
+ chunkUSNT2KNT_cjs.__name(render10, "render");
559
+ function escapeHtml8(s) {
560
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
561
+ }
562
+ chunkUSNT2KNT_cjs.__name(escapeHtml8, "escapeHtml");
563
+
564
+ // src/plugins/email/templates/vendorOnboarded.ts
565
+ function render11(ctx) {
566
+ const { audience, activation, kind = "vendor_onboard", vendorName, vendorSlug, ownerName, ownerEmail, inviteLink, signInLink, companyDetails } = ctx;
567
+ const storeLabel = vendorName.trim() || "your store";
568
+ const greeting = ownerName && ownerName.trim() ? `Hello ${escapeHtml2(ownerName.trim())},` : "Hello,";
569
+ const slugNote = vendorSlug && vendorSlug.trim() ? `<p style="margin:0 0 8px 0;font-size:14px;line-height:1.5;color:#555;"><strong>Store URL slug:</strong> ${escapeHtml2(vendorSlug.trim())}</p>` : "";
570
+ let subject;
571
+ let bodyHtml;
572
+ let text;
573
+ if (audience === "internal") {
574
+ const action = kind === "team_invite" ? "A team member was invited to a vendor store." : activation === "invite" ? "A new vendor store was created and the owner was invited." : "A new vendor store was created with an active owner account.";
575
+ subject = kind === "team_invite" ? `Team invite: ${storeLabel}` : `New vendor: ${storeLabel}`;
576
+ bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${action}</p>
577
+ <p style="margin:0 0 8px 0;font-size:15px;line-height:1.5;color:#333;"><strong>Store:</strong> ${escapeHtml2(storeLabel)}</p>
578
+ ${slugNote}
579
+ <p style="margin:0 0 8px 0;font-size:15px;line-height:1.5;color:#333;"><strong>Owner / invitee:</strong> ${escapeHtml2(ownerName?.trim() || "\u2014")} <span style="color:#555;">(${escapeHtml2(ownerEmail)})</span></p>
580
+ <p style="margin:0 0 0 0;font-size:14px;line-height:1.5;color:#555;"><strong>Activation:</strong> ${escapeHtml2(activation === "invite" ? "Invitation" : "Password set")}</p>`;
581
+ text = [
582
+ subject,
583
+ "",
584
+ action,
585
+ `Store: ${storeLabel}`,
586
+ vendorSlug ? `Slug: ${vendorSlug}` : "",
587
+ `Owner / invitee: ${ownerName?.trim() || "\u2014"} (${ownerEmail})`,
588
+ `Activation: ${activation === "invite" ? "Invitation" : "Password set"}`
589
+ ].filter(Boolean).join("\n");
590
+ } else if (kind === "team_invite" && activation === "membership" && inviteLink) {
591
+ subject = `You're invited to ${storeLabel}`;
592
+ bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${greeting}</p>
593
+ <p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">You have been invited to join the vendor team for <strong>${escapeHtml2(storeLabel)}</strong>. You already have an account \u2014 accept below to join this store. You will not need to set a new password.</p>
594
+ ${primaryCtaButton(inviteLink, "Accept invitation")}
595
+ <p style="margin:16px 0 0 0;font-size:12px;line-height:1.5;color:#888;">If the button does not work, copy and paste this URL into your browser:<br/><span style="word-break:break-all;">${escapeHtml2(inviteLink)}</span></p>`;
596
+ text = [
597
+ ownerName?.trim() ? `Hello ${ownerName.trim()},` : "Hello,",
598
+ "",
599
+ `You have been invited to join ${storeLabel}.`,
600
+ "Open this link to accept (no new password required):",
601
+ inviteLink
602
+ ].join("\n");
603
+ } else if (kind === "team_invite" && activation === "invite" && inviteLink) {
604
+ subject = `You're invited to ${storeLabel}`;
605
+ bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${greeting}</p>
606
+ <p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">You have been invited to join the vendor team for <strong>${escapeHtml2(storeLabel)}</strong>. Use the secure link below to set your password and activate your account.</p>
607
+ ${primaryCtaButton(inviteLink, "Accept invitation & set password")}
608
+ <p style="margin:16px 0 0 0;font-size:12px;line-height:1.5;color:#888;">If the button does not work, copy and paste this URL into your browser:<br/><span style="word-break:break-all;">${escapeHtml2(inviteLink)}</span></p>`;
609
+ text = [
610
+ ownerName?.trim() ? `Hello ${ownerName.trim()},` : "Hello,",
611
+ "",
612
+ `You have been invited to join ${storeLabel}.`,
613
+ "Open this link to set your password:",
614
+ inviteLink
615
+ ].join("\n");
616
+ } else if (activation === "invite" && inviteLink) {
617
+ subject = `Your vendor store "${storeLabel}" is ready`;
618
+ bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${greeting}</p>
619
+ <p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">Your vendor store <strong>${escapeHtml2(storeLabel)}</strong> has been created. Accept the invitation below to set your password and access the admin dashboard.</p>
620
+ ${slugNote}
621
+ ${primaryCtaButton(inviteLink, "Accept invitation & set password")}
622
+ <p style="margin:16px 0 0 0;font-size:12px;line-height:1.5;color:#888;">If the button does not work, copy and paste this URL into your browser:<br/><span style="word-break:break-all;">${escapeHtml2(inviteLink)}</span></p>`;
623
+ text = [
624
+ ownerName?.trim() ? `Hello ${ownerName.trim()},` : "Hello,",
625
+ "",
626
+ `Your vendor store "${storeLabel}" has been created.`,
627
+ "Open this link to set your password:",
628
+ inviteLink
629
+ ].join("\n");
630
+ } else {
631
+ subject = `Welcome to ${storeLabel}`;
632
+ const loginUrl = signInLink?.trim() || inviteLink?.trim() || "";
633
+ bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${greeting}</p>
634
+ <p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">Your vendor store <strong>${escapeHtml2(storeLabel)}</strong> has been created and your owner account is ready. Sign in to the admin dashboard to manage your store.</p>
635
+ ${slugNote}
636
+ ${loginUrl ? primaryCtaButton(loginUrl, "Sign in to admin") : ""}
637
+ ${loginUrl ? `<p style="margin:16px 0 0 0;font-size:12px;line-height:1.5;color:#888;">Sign-in URL:<br/><span style="word-break:break-all;">${escapeHtml2(loginUrl)}</span></p>` : ""}`;
638
+ text = [
639
+ ownerName?.trim() ? `Hello ${ownerName.trim()},` : "Hello,",
640
+ "",
641
+ `Your vendor store "${storeLabel}" has been created.`,
642
+ loginUrl ? `Sign in: ${loginUrl}` : ""
643
+ ].filter(Boolean).join("\n");
644
+ }
645
+ const html = renderLayout({
646
+ bodyHtml,
647
+ companyDetails
648
+ });
649
+ return {
650
+ subject,
651
+ html,
652
+ text
653
+ };
654
+ }
655
+ chunkUSNT2KNT_cjs.__name(render11, "render");
656
+
657
+ // src/plugins/email/templates/index.ts
658
+ var templateRenderMap = {
659
+ signup: render,
660
+ passwordReset: render2,
661
+ passwordChange: render3,
662
+ orderPlaced: render4,
663
+ returnInitiated: render5,
664
+ shippingUpdate: render6,
665
+ invite: render7,
666
+ formSubmission: render8,
667
+ otp: render9,
668
+ chatLead: render10,
669
+ vendorOnboarded: render11
670
+ };
671
+ function getTemplateRenderer(name) {
672
+ return templateRenderMap[name];
673
+ }
674
+ chunkUSNT2KNT_cjs.__name(getTemplateRenderer, "getTemplateRenderer");
675
+
676
+ // src/plugins/email/renderer.ts
677
+ function renderEmail(templateName, ctx, options) {
678
+ const layoutFn = options?.renderLayout ?? renderLayout;
679
+ const getBody = options?.getBody;
680
+ if (getBody) {
681
+ const custom = getBody(templateName, ctx);
682
+ if (custom != null) {
683
+ const html = layoutFn({
684
+ bodyHtml: custom.bodyHtml,
685
+ companyDetails: ctx.companyDetails
686
+ });
687
+ return {
688
+ subject: custom.subject,
689
+ html,
690
+ text: custom.text
691
+ };
692
+ }
693
+ }
694
+ const render12 = getTemplateRenderer(templateName);
695
+ if (!render12) {
696
+ throw new Error(`Unknown email template: ${templateName}`);
697
+ }
698
+ return render12(ctx);
699
+ }
700
+ chunkUSNT2KNT_cjs.__name(renderEmail, "renderEmail");
701
+
702
+ // src/plugins/email/email-service.ts
703
+ var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-YVP4NQII.cjs', document.baseURI).href)));
704
+ var MailComposer = require2("nodemailer/lib/mail-composer");
705
+ var SES_REGIONS_WITHOUT_SMTP = /* @__PURE__ */ new Set([
706
+ "af-south-1",
707
+ "ap-south-2",
708
+ "ap-southeast-3",
709
+ "ap-southeast-5",
710
+ "ca-west-1",
711
+ "eu-central-2",
712
+ "eu-south-1",
713
+ "il-central-1",
714
+ "me-central-1",
715
+ "me-south-1"
716
+ ]);
717
+ function sesSmtpHost(region) {
718
+ return `email-smtp.${region}.amazonaws.com`;
719
+ }
720
+ chunkUSNT2KNT_cjs.__name(sesSmtpHost, "sesSmtpHost");
721
+ function sesRegionSupportsSmtp(region) {
722
+ return !SES_REGIONS_WITHOUT_SMTP.has(region.trim());
723
+ }
724
+ chunkUSNT2KNT_cjs.__name(sesRegionSupportsSmtp, "sesRegionSupportsSmtp");
725
+ function toBuffer(content) {
726
+ return Buffer.isBuffer(content) ? content : Buffer.from(content);
727
+ }
728
+ chunkUSNT2KNT_cjs.__name(toBuffer, "toBuffer");
729
+ var EmailService = class {
730
+ static {
731
+ chunkUSNT2KNT_cjs.__name(this, "EmailService");
732
+ }
733
+ config;
734
+ templateOptions;
735
+ sesClient;
736
+ transporter;
737
+ /** AWS via SMTP (nodemailer) vs SES API SDK. */
738
+ awsViaSmtp = false;
739
+ constructor(config) {
740
+ this.config = config;
741
+ this.templateOptions = config.templateOptions;
742
+ if (config.type === "AWS") {
743
+ if (!config.region) {
744
+ throw new Error("AWS SES configuration incomplete: AWS_REGION is required");
745
+ }
746
+ if (config.user && config.password) {
747
+ this.awsViaSmtp = true;
748
+ const host = config.host || sesSmtpHost(config.region);
749
+ const port = config.port ?? 587;
750
+ const secure = config.secure ?? false;
751
+ this.transporter = nodemailer__default.default.createTransport({
752
+ host,
753
+ port,
754
+ secure,
755
+ auth: {
756
+ user: config.user,
757
+ pass: config.password
758
+ }
759
+ });
760
+ } else if (config.accessKeyId && config.secretAccessKey) {
761
+ this.sesClient = new clientSes.SESClient({
762
+ region: config.region,
763
+ credentials: {
764
+ accessKeyId: config.accessKeyId,
765
+ secretAccessKey: config.secretAccessKey
766
+ }
767
+ });
768
+ } else {
769
+ throw new Error("AWS SES configuration incomplete: set SMTP_USER and SMTP_PASSWORD (SES SMTP) or AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY (SES API)");
770
+ }
771
+ } else if (config.type === "SMTP" || config.type === "GMAIL") {
772
+ if (!config.user || !config.password) throw new Error("SMTP configuration incomplete");
773
+ const host = config.type === "GMAIL" ? "smtp.gmail.com" : config.host;
774
+ if (!host) {
775
+ throw new Error("SMTP configuration incomplete: SMTP_HOST is required when SMTP_TYPE=SMTP (nodemailer otherwise connects to 127.0.0.1)");
776
+ }
777
+ const port = config.port ?? 587;
778
+ const secure = config.secure ?? false;
779
+ this.transporter = nodemailer__default.default.createTransport({
780
+ host,
781
+ port,
782
+ secure,
783
+ auth: {
784
+ user: config.user,
785
+ pass: config.password
786
+ }
787
+ });
788
+ } else {
789
+ throw new Error(`Unsupported email type: ${config.type}`);
790
+ }
791
+ }
792
+ nodemailerAttachments(attachments) {
793
+ if (!attachments?.length) return void 0;
794
+ return attachments.map((a) => ({
795
+ filename: a.filename,
796
+ content: toBuffer(a.content),
797
+ contentType: a.contentType || "application/octet-stream"
798
+ }));
799
+ }
800
+ async send(emailData) {
801
+ try {
802
+ const from = emailData.from || this.config.from;
803
+ const to = emailData.to || this.config.to;
804
+ const attachments = this.nodemailerAttachments(emailData.attachments);
805
+ if (this.config.type === "AWS" && this.sesClient) {
806
+ if (attachments?.length) {
807
+ const composer = new MailComposer({
808
+ from,
809
+ to,
810
+ subject: emailData.subject,
811
+ html: emailData.html,
812
+ text: emailData.text,
813
+ attachments
814
+ });
815
+ const raw = await new Promise((resolve, reject) => {
816
+ composer.compile().build((err, message) => {
817
+ if (err) reject(err);
818
+ else resolve(message);
819
+ });
820
+ });
821
+ await this.sesClient.send(new clientSes.SendRawEmailCommand({
822
+ RawMessage: {
823
+ Data: raw
824
+ }
825
+ }));
826
+ return true;
827
+ }
828
+ await this.sesClient.send(new clientSes.SendEmailCommand({
829
+ Source: from,
830
+ Destination: {
831
+ ToAddresses: [
832
+ to
833
+ ]
834
+ },
835
+ Message: {
836
+ Subject: {
837
+ Data: emailData.subject,
838
+ Charset: "UTF-8"
839
+ },
840
+ Body: {
841
+ Html: {
842
+ Data: emailData.html,
843
+ Charset: "UTF-8"
844
+ },
845
+ ...emailData.text && {
846
+ Text: {
847
+ Data: emailData.text,
848
+ Charset: "UTF-8"
849
+ }
850
+ }
851
+ }
852
+ }
853
+ }));
854
+ return true;
855
+ }
856
+ if (this.transporter && (this.config.type === "SMTP" || this.config.type === "GMAIL" || this.config.type === "AWS" && this.awsViaSmtp)) {
857
+ await this.transporter.sendMail({
858
+ from,
859
+ to,
860
+ subject: emailData.subject,
861
+ html: emailData.html,
862
+ text: emailData.text,
863
+ attachments
864
+ });
865
+ return true;
866
+ }
867
+ return false;
868
+ } catch (error) {
869
+ console.error("Email sending failed:", error);
870
+ return false;
871
+ }
872
+ }
873
+ renderTemplate(templateName, ctx) {
874
+ return renderEmail(templateName, ctx, this.templateOptions);
875
+ }
876
+ /** Default notification recipient from plugin config (SMTP_TO / plugin `to`). */
877
+ getDefaultTo() {
878
+ return this.config.to;
879
+ }
880
+ };
881
+ var emailTemplates = {
882
+ formSubmission: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name((data) => ({
883
+ subject: `New Form Submission: ${data.formName}`,
884
+ html: `<h2>New Form Submission</h2><p><strong>Form:</strong> ${data.formName}</p><p><strong>Contact:</strong> ${data.contactName} (${data.contactEmail})</p><pre>${JSON.stringify(data.formData, null, 2)}</pre>`,
885
+ text: `New Form Submission
886
+ Form: ${data.formName}
887
+ Contact: ${data.contactName} (${data.contactEmail})
888
+ ${JSON.stringify(data.formData, null, 2)}`
889
+ }), "formSubmission"),
890
+ contactSubmission: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name((data) => ({
891
+ subject: `New Contact Form Submission from ${data.name}`,
892
+ html: `<h2>New Contact Form Submission</h2><p><strong>Name:</strong> ${data.name}</p><p><strong>Email:</strong> ${data.email}</p>${data.phone ? `<p><strong>Phone:</strong> ${data.phone}</p>` : ""}${data.message ? `<p><strong>Message:</strong></p><p>${data.message}</p>` : ""}`,
893
+ text: `New Contact Form Submission
894
+ Name: ${data.name}
895
+ Email: ${data.email}
896
+ ${data.phone ? `Phone: ${data.phone}
897
+ ` : ""}${data.message ? `Message: ${data.message}` : ""}`
898
+ }), "contactSubmission"),
899
+ passwordReset: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name((data) => ({
900
+ subject: "Reset your password",
901
+ html: `<h2>Reset your password</h2><p>Click the link below to set a new password. This link expires in 1 hour.</p><p><a href="${data.resetLink}">${data.resetLink}</a></p>`,
902
+ text: `Reset your password: ${data.resetLink}
903
+
904
+ This link expires in 1 hour.`
905
+ }), "passwordReset")
906
+ };
907
+
908
+ exports.EmailService = EmailService;
909
+ exports.SES_REGIONS_WITHOUT_SMTP = SES_REGIONS_WITHOUT_SMTP;
910
+ exports.emailTemplates = emailTemplates;
911
+ exports.renderEmail = renderEmail;
912
+ exports.renderLayout = renderLayout;
913
+ exports.sesRegionSupportsSmtp = sesRegionSupportsSmtp;
914
+ exports.sesSmtpHost = sesSmtpHost;