@astralibx/email-rule-engine 14.0.0 → 14.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -41,7 +41,7 @@ const engine = createEmailRuleEngine({
41
41
  text: params.textBody,
42
42
  });
43
43
  },
44
- selectAgent: async () => ({ accountId: 'default', email: 'noreply@myapp.com', metadata: {} }),
44
+ selectAgent: async () => ({ accountId: 'default', contactValue: 'noreply@myapp.com', metadata: {} }),
45
45
  findIdentifier: async (email) => {
46
46
  const contact = await Contact.findOne({ email });
47
47
  return contact ? { id: contact._id.toString(), contactId: contact._id.toString() } : null;
package/dist/index.cjs CHANGED
@@ -37,9 +37,8 @@ function renderMjml(body) {
37
37
  minify: false
38
38
  });
39
39
  if (result.errors?.length) {
40
- const critical = result.errors.filter((e) => e.tagName !== void 0);
41
- if (critical.length > 0) {
42
- throw new Error(`MJML compilation errors: ${critical.map((e) => e.message).join("; ")}`);
40
+ for (const err of result.errors) {
41
+ console.warn(`MJML warning: ${err.message || JSON.stringify(err)}`);
43
42
  }
44
43
  }
45
44
  return result.html;
@@ -58,7 +57,10 @@ var DATE_FORMAT_OPTIONS = {
58
57
  month: "short",
59
58
  year: "numeric"
60
59
  };
60
+ var registered = false;
61
61
  function registerEmailHelpers() {
62
+ if (registered) return;
63
+ registered = true;
62
64
  Handlebars__default.default.registerHelper("currency", (val) => {
63
65
  const num = Number(val);
64
66
  if (isNaN(num)) return String(val ?? "");
@@ -92,13 +94,13 @@ function createEmailRuleEngine(config) {
92
94
  textBody: text,
93
95
  ruleId: params.ruleId,
94
96
  autoApprove: params.autoApprove,
95
- attachments: params.metadata?.attachments ?? void 0
97
+ attachments: Array.isArray(params.metadata?.attachments) ? params.metadata.attachments : void 0
96
98
  });
97
99
  },
98
100
  sendTest: config.adapters.sendTestEmail ? async (to, body, subject, metadata) => {
99
101
  const html = renderMjml(body);
100
102
  const text = htmlToPlainText(html);
101
- await config.adapters.sendTestEmail(to, subject || "", html, text, metadata?.attachments ?? void 0);
103
+ await config.adapters.sendTestEmail(to, subject || "", html, text, Array.isArray(metadata?.attachments) ? metadata.attachments : void 0);
102
104
  } : void 0
103
105
  }
104
106
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mjml-renderer.ts","../src/email-helpers.ts","../src/index.ts"],"names":["mjml2html","convert","Handlebars","createRuleEngine"],"mappings":";;;;;;;;;;;;;AAGA,IAAM,cAAA,GAAiB,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAAA,CAAA;AAYvB,IAAM,eAAA,GAAkB,CAAA;AAAA;AAAA;AAAA;AAAA,OAAA,CAAA;AAMjB,SAAS,WAAW,IAAA,EAAsB;AAC/C,EAAA,IAAI,CAAC,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA,IAAK,CAAC,IAAA,CAAK,IAAA,EAAK,CAAE,UAAA,CAAW,OAAO,CAAA,EAAG;AAC9D,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,IAAA,EAAK,CAAE,UAAA,CAAW,OAAO,CAAA,GAC3C,IAAA,GACA,CAAA,EAAG,cAAc,CAAA,EAAG,IAAI,GAAG,eAAe,CAAA,CAAA;AAE9C,EAAA,MAAM,MAAA,GAASA,2BAAU,QAAA,EAAU;AAAA,IACjC,eAAA,EAAiB,MAAA;AAAA,IACjB,MAAA,EAAQ;AAAA,GACT,CAAA;AAED,EAAA,IAAI,MAAA,CAAO,QAAQ,MAAA,EAAQ;AACzB,IAAA,MAAM,QAAA,GAAW,OAAO,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA,KAAW,CAAA,CAAE,YAAY,MAAS,CAAA;AACzE,IAAA,IAAI,QAAA,CAAS,SAAS,CAAA,EAAG;AACvB,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,yBAAA,EAA4B,QAAA,CAAS,GAAA,CAAI,CAAC,CAAA,KAAW,CAAA,CAAE,OAAO,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA,IAC9F;AAAA,EACF;AAEA,EAAA,OAAO,MAAA,CAAO,IAAA;AAChB;AAEO,SAAS,gBAAgB,IAAA,EAAsB;AACpD,EAAA,OAAOC,mBAAQ,IAAA,EAAM;AAAA,IACnB,QAAA,EAAU,EAAA;AAAA,IACV,SAAA,EAAW;AAAA,MACT,EAAE,QAAA,EAAU,GAAA,EAAK,SAAS,EAAE,wBAAA,EAA0B,MAAK,EAAE;AAAA,MAC7D,EAAE,QAAA,EAAU,KAAA,EAAO,MAAA,EAAQ,MAAA;AAAO;AACpC,GACD,CAAA;AACH;ACnDA,IAAM,mBAAA,GAAkD;AAAA,EACtD,GAAA,EAAK,SAAA;AAAA,EACL,KAAA,EAAO,OAAA;AAAA,EACP,IAAA,EAAM;AACR,CAAA;AAEO,SAAS,oBAAA,GAA6B;AAC3C,EAAAC,2BAAA,CAAW,cAAA,CAAe,UAAA,EAAY,CAAC,GAAA,KAAa;AAClD,IAAA,MAAM,GAAA,GAAM,OAAO,GAAG,CAAA;AACtB,IAAA,IAAI,MAAM,GAAG,CAAA,EAAG,OAAO,MAAA,CAAO,OAAO,EAAE,CAAA;AACvC,IAAA,OAAO,CAAA,MAAA,EAAI,GAAA,CAAI,cAAA,CAAe,OAAO,CAAC,CAAA,CAAA;AAAA,EACxC,CAAC,CAAA;AAED,EAAAA,2BAAA,CAAW,cAAA,CAAe,YAAA,EAAc,CAAC,IAAA,KAAc;AACrD,IAAA,IAAI,CAAC,MAAM,OAAO,EAAA;AAClB,IAAA,MAAM,CAAA,GAAI,IAAI,IAAA,CAAK,IAAI,CAAA;AACvB,IAAA,IAAI,MAAM,CAAA,CAAE,OAAA,EAAS,CAAA,EAAG,OAAO,OAAO,IAAI,CAAA;AAC1C,IAAA,OAAO,CAAA,CAAE,kBAAA,CAAmB,OAAA,EAAS,mBAAmB,CAAA;AAAA,EAC1D,CAAC,CAAA;AACH;ACOO,SAAS,sBAAsB,MAAA,EAA2C;AAE/E,EAAA,oBAAA,EAAqB;AAErB,EAAA,MAAM,UAAA,GAA+B;AAAA,IACnC,GAAG,MAAA;AAAA,IACH,QAAA,EAAU;AAAA,MACR,UAAA,EAAY,OAAO,QAAA,CAAS,UAAA;AAAA,MAC5B,WAAA,EAAa,OAAO,QAAA,CAAS,WAAA;AAAA,MAC7B,WAAA,EAAa,OAAO,QAAA,CAAS,WAAA;AAAA,MAC7B,cAAA,EAAgB,OAAO,QAAA,CAAS,cAAA;AAAA,MAChC,IAAA,EAAM,OAAO,MAAA,KAAuB;AAClC,QAAA,MAAM,IAAA,GAAO,UAAA,CAAW,MAAA,CAAO,IAAI,CAAA;AACnC,QAAA,MAAM,IAAA,GAAO,MAAA,CAAO,QAAA,IAAY,eAAA,CAAgB,IAAI,CAAA;AACpD,QAAA,MAAM,MAAA,CAAO,SAAS,SAAA,CAAU;AAAA,UAC9B,cAAc,MAAA,CAAO,YAAA;AAAA,UACrB,WAAW,MAAA,CAAO,SAAA;AAAA,UAClB,WAAW,MAAA,CAAO,SAAA;AAAA,UAClB,OAAA,EAAS,OAAO,OAAA,IAAW,EAAA;AAAA,UAC3B,QAAA,EAAU,IAAA;AAAA,UACV,QAAA,EAAU,IAAA;AAAA,UACV,QAAQ,MAAA,CAAO,MAAA;AAAA,UACf,aAAa,MAAA,CAAO,WAAA;AAAA,UACpB,WAAA,EAAc,MAAA,CAAO,QAAA,EAAU,WAAA,IAAyB;AAAA,SACzD,CAAA;AAAA,MACH,CAAA;AAAA,MACA,QAAA,EAAU,OAAO,QAAA,CAAS,aAAA,GACtB,OAAO,EAAA,EAAY,IAAA,EAAc,SAAkB,QAAA,KAAuC;AACxF,QAAA,MAAM,IAAA,GAAO,WAAW,IAAI,CAAA;AAC5B,QAAA,MAAM,IAAA,GAAO,gBAAgB,IAAI,CAAA;AACjC,QAAA,MAAM,MAAA,CAAO,QAAA,CAAS,aAAA,CAAe,EAAA,EAAI,OAAA,IAAW,IAAI,IAAA,EAAM,IAAA,EAAO,QAAA,EAAU,WAAA,IAAyB,MAAS,CAAA;AAAA,MACnH,CAAA,GACA;AAAA;AACN,GACF;AAEA,EAAA,OAAOC,4BAAiB,UAAU,CAAA;AACpC","file":"index.cjs","sourcesContent":["import mjml2html from 'mjml';\nimport { convert } from 'html-to-text';\n\nconst MJML_BASE_OPEN = `<mjml>\n <mj-head>\n <mj-attributes>\n <mj-all font-family=\"Arial, sans-serif\" />\n <mj-text font-size=\"15px\" color=\"#333333\" line-height=\"1.6\" />\n </mj-attributes>\n </mj-head>\n <mj-body background-color=\"#ffffff\">\n <mj-section padding=\"20px\">\n <mj-column>\n <mj-text>`;\n\nconst MJML_BASE_CLOSE = ` </mj-text>\n </mj-column>\n </mj-section>\n </mj-body>\n</mjml>`;\n\nexport function renderMjml(body: string): string {\n if (!body.includes('<mj-') && !body.trim().startsWith('<mjml')) {\n return body;\n }\n\n const fullMjml = body.trim().startsWith('<mjml')\n ? body\n : `${MJML_BASE_OPEN}${body}${MJML_BASE_CLOSE}`;\n\n const result = mjml2html(fullMjml, {\n validationLevel: 'soft',\n minify: false,\n });\n\n if (result.errors?.length) {\n const critical = result.errors.filter((e: any) => e.tagName !== undefined);\n if (critical.length > 0) {\n throw new Error(`MJML compilation errors: ${critical.map((e: any) => e.message).join('; ')}`);\n }\n }\n\n return result.html;\n}\n\nexport function htmlToPlainText(html: string): string {\n return convert(html, {\n wordwrap: 80,\n selectors: [\n { selector: 'a', options: { hideLinkHrefIfSameAsText: true } },\n { selector: 'img', format: 'skip' },\n ],\n });\n}\n","import Handlebars from 'handlebars';\n\nconst DATE_FORMAT_OPTIONS: Intl.DateTimeFormatOptions = {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n};\n\nexport function registerEmailHelpers(): void {\n Handlebars.registerHelper('currency', (val: any) => {\n const num = Number(val);\n if (isNaN(num)) return String(val ?? '');\n return `₹${num.toLocaleString('en-IN')}`;\n });\n\n Handlebars.registerHelper('formatDate', (date: any) => {\n if (!date) return '';\n const d = new Date(date);\n if (isNaN(d.getTime())) return String(date);\n return d.toLocaleDateString('en-IN', DATE_FORMAT_OPTIONS);\n });\n}\n","import {\n createRuleEngine,\n type RuleEngine,\n type RuleEngineConfig,\n type SendParams,\n} from '@astralibx/rule-engine';\nimport { renderMjml, htmlToPlainText } from './mjml-renderer';\nimport { registerEmailHelpers } from './email-helpers';\n\nexport interface EmailSendParams {\n identifierId: string;\n contactId: string;\n accountId: string;\n subject: string;\n htmlBody: string;\n textBody: string;\n ruleId: string;\n autoApprove: boolean;\n attachments?: Array<{ filename: string; url: string; contentType: string }>;\n}\n\nexport interface EmailRuleEngineConfig extends Omit<RuleEngineConfig, 'adapters'> {\n adapters: Omit<RuleEngineConfig['adapters'], 'send' | 'sendTest'> & {\n sendEmail: (params: EmailSendParams) => Promise<void>;\n sendTestEmail?: (to: string, subject: string, html: string, text: string, attachments?: Array<{ filename: string; url: string; contentType: string }>) => Promise<void>;\n };\n}\n\nexport function createEmailRuleEngine(config: EmailRuleEngineConfig): RuleEngine {\n // Register email-specific helpers before engine creation\n registerEmailHelpers();\n\n const coreConfig: RuleEngineConfig = {\n ...config,\n adapters: {\n queryUsers: config.adapters.queryUsers,\n resolveData: config.adapters.resolveData,\n selectAgent: config.adapters.selectAgent,\n findIdentifier: config.adapters.findIdentifier,\n send: async (params: SendParams) => {\n const html = renderMjml(params.body);\n const text = params.textBody || htmlToPlainText(html);\n await config.adapters.sendEmail({\n identifierId: params.identifierId,\n contactId: params.contactId,\n accountId: params.accountId,\n subject: params.subject || '',\n htmlBody: html,\n textBody: text,\n ruleId: params.ruleId,\n autoApprove: params.autoApprove,\n attachments: (params.metadata?.attachments as any[]) ?? undefined,\n });\n },\n sendTest: config.adapters.sendTestEmail\n ? async (to: string, body: string, subject?: string, metadata?: Record<string, unknown>) => {\n const html = renderMjml(body);\n const text = htmlToPlainText(html);\n await config.adapters.sendTestEmail!(to, subject || '', html, text, (metadata?.attachments as any[]) ?? undefined);\n }\n : undefined,\n },\n };\n\n return createRuleEngine(coreConfig);\n}\n\n// Re-export everything from core\nexport * from '@astralibx/rule-engine';\n\n// Export email-specific utilities\nexport { renderMjml, htmlToPlainText } from './mjml-renderer';\nexport { registerEmailHelpers } from './email-helpers';\n"]}
1
+ {"version":3,"sources":["../src/mjml-renderer.ts","../src/email-helpers.ts","../src/index.ts"],"names":["mjml2html","convert","Handlebars","createRuleEngine"],"mappings":"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","file":"index.cjs","sourcesContent":["import mjml2html from 'mjml';\nimport { convert } from 'html-to-text';\n\nconst MJML_BASE_OPEN = `<mjml>\n <mj-head>\n <mj-attributes>\n <mj-all font-family=\"Arial, sans-serif\" />\n <mj-text font-size=\"15px\" color=\"#333333\" line-height=\"1.6\" />\n </mj-attributes>\n </mj-head>\n <mj-body background-color=\"#ffffff\">\n <mj-section padding=\"20px\">\n <mj-column>\n <mj-text>`;\n\nconst MJML_BASE_CLOSE = ` </mj-text>\n </mj-column>\n </mj-section>\n </mj-body>\n</mjml>`;\n\nexport function renderMjml(body: string): string {\n if (!body.includes('<mj-') && !body.trim().startsWith('<mjml')) {\n return body;\n }\n\n const fullMjml = body.trim().startsWith('<mjml')\n ? body\n : `${MJML_BASE_OPEN}${body}${MJML_BASE_CLOSE}`;\n\n const result = mjml2html(fullMjml, {\n validationLevel: 'soft',\n minify: false,\n });\n\n if (result.errors?.length) {\n // Log all MJML warnings/errors for debugging\n for (const err of result.errors) {\n console.warn(`MJML warning: ${(err as any).message || JSON.stringify(err)}`);\n }\n }\n\n return result.html;\n}\n\nexport function htmlToPlainText(html: string): string {\n return convert(html, {\n wordwrap: 80,\n selectors: [\n { selector: 'a', options: { hideLinkHrefIfSameAsText: true } },\n { selector: 'img', format: 'skip' },\n ],\n });\n}\n","import Handlebars from 'handlebars';\n\nconst DATE_FORMAT_OPTIONS: Intl.DateTimeFormatOptions = {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n};\n\nlet registered = false;\n\nexport function registerEmailHelpers(): void {\n if (registered) return;\n registered = true;\n Handlebars.registerHelper('currency', (val: any) => {\n const num = Number(val);\n if (isNaN(num)) return String(val ?? '');\n return `₹${num.toLocaleString('en-IN')}`;\n });\n\n Handlebars.registerHelper('formatDate', (date: any) => {\n if (!date) return '';\n const d = new Date(date);\n if (isNaN(d.getTime())) return String(date);\n return d.toLocaleDateString('en-IN', DATE_FORMAT_OPTIONS);\n });\n}\n","import {\n createRuleEngine,\n type RuleEngine,\n type RuleEngineConfig,\n type SendParams,\n} from '@astralibx/rule-engine';\nimport { renderMjml, htmlToPlainText } from './mjml-renderer';\nimport { registerEmailHelpers } from './email-helpers';\n\nexport interface EmailSendParams {\n identifierId: string;\n contactId: string;\n accountId: string;\n subject: string;\n htmlBody: string;\n textBody: string;\n ruleId: string;\n autoApprove: boolean;\n attachments?: Array<{ filename: string; url: string; contentType: string }>;\n}\n\nexport interface EmailRuleEngineConfig extends Omit<RuleEngineConfig, 'adapters'> {\n adapters: Omit<RuleEngineConfig['adapters'], 'send' | 'sendTest'> & {\n sendEmail: (params: EmailSendParams) => Promise<void>;\n sendTestEmail?: (to: string, subject: string, html: string, text: string, attachments?: Array<{ filename: string; url: string; contentType: string }>) => Promise<void>;\n };\n}\n\nexport function createEmailRuleEngine(config: EmailRuleEngineConfig): RuleEngine {\n // Register email-specific helpers before engine creation\n registerEmailHelpers();\n\n const coreConfig: RuleEngineConfig = {\n ...config,\n adapters: {\n queryUsers: config.adapters.queryUsers,\n resolveData: config.adapters.resolveData,\n selectAgent: config.adapters.selectAgent,\n findIdentifier: config.adapters.findIdentifier,\n send: async (params: SendParams) => {\n const html = renderMjml(params.body);\n const text = params.textBody || htmlToPlainText(html);\n await config.adapters.sendEmail({\n identifierId: params.identifierId,\n contactId: params.contactId,\n accountId: params.accountId,\n subject: params.subject || '',\n htmlBody: html,\n textBody: text,\n ruleId: params.ruleId,\n autoApprove: params.autoApprove,\n attachments: Array.isArray(params.metadata?.attachments) ? params.metadata.attachments : undefined,\n });\n },\n sendTest: config.adapters.sendTestEmail\n ? async (to: string, body: string, subject?: string, metadata?: Record<string, unknown>) => {\n const html = renderMjml(body);\n const text = htmlToPlainText(html);\n await config.adapters.sendTestEmail!(to, subject || '', html, text, Array.isArray(metadata?.attachments) ? metadata.attachments : undefined);\n }\n : undefined,\n },\n };\n\n return createRuleEngine(coreConfig);\n}\n\n// Re-export everything from core\nexport * from '@astralibx/rule-engine';\n\n// Export email-specific utilities\nexport { renderMjml, htmlToPlainText } from './mjml-renderer';\nexport { registerEmailHelpers } from './email-helpers';\n"]}
package/dist/index.mjs CHANGED
@@ -31,9 +31,8 @@ function renderMjml(body) {
31
31
  minify: false
32
32
  });
33
33
  if (result.errors?.length) {
34
- const critical = result.errors.filter((e) => e.tagName !== void 0);
35
- if (critical.length > 0) {
36
- throw new Error(`MJML compilation errors: ${critical.map((e) => e.message).join("; ")}`);
34
+ for (const err of result.errors) {
35
+ console.warn(`MJML warning: ${err.message || JSON.stringify(err)}`);
37
36
  }
38
37
  }
39
38
  return result.html;
@@ -52,7 +51,10 @@ var DATE_FORMAT_OPTIONS = {
52
51
  month: "short",
53
52
  year: "numeric"
54
53
  };
54
+ var registered = false;
55
55
  function registerEmailHelpers() {
56
+ if (registered) return;
57
+ registered = true;
56
58
  Handlebars.registerHelper("currency", (val) => {
57
59
  const num = Number(val);
58
60
  if (isNaN(num)) return String(val ?? "");
@@ -86,13 +88,13 @@ function createEmailRuleEngine(config) {
86
88
  textBody: text,
87
89
  ruleId: params.ruleId,
88
90
  autoApprove: params.autoApprove,
89
- attachments: params.metadata?.attachments ?? void 0
91
+ attachments: Array.isArray(params.metadata?.attachments) ? params.metadata.attachments : void 0
90
92
  });
91
93
  },
92
94
  sendTest: config.adapters.sendTestEmail ? async (to, body, subject, metadata) => {
93
95
  const html = renderMjml(body);
94
96
  const text = htmlToPlainText(html);
95
- await config.adapters.sendTestEmail(to, subject || "", html, text, metadata?.attachments ?? void 0);
97
+ await config.adapters.sendTestEmail(to, subject || "", html, text, Array.isArray(metadata?.attachments) ? metadata.attachments : void 0);
96
98
  } : void 0
97
99
  }
98
100
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mjml-renderer.ts","../src/email-helpers.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import mjml2html from 'mjml';\nimport { convert } from 'html-to-text';\n\nconst MJML_BASE_OPEN = `<mjml>\n <mj-head>\n <mj-attributes>\n <mj-all font-family=\"Arial, sans-serif\" />\n <mj-text font-size=\"15px\" color=\"#333333\" line-height=\"1.6\" />\n </mj-attributes>\n </mj-head>\n <mj-body background-color=\"#ffffff\">\n <mj-section padding=\"20px\">\n <mj-column>\n <mj-text>`;\n\nconst MJML_BASE_CLOSE = ` </mj-text>\n </mj-column>\n </mj-section>\n </mj-body>\n</mjml>`;\n\nexport function renderMjml(body: string): string {\n if (!body.includes('<mj-') && !body.trim().startsWith('<mjml')) {\n return body;\n }\n\n const fullMjml = body.trim().startsWith('<mjml')\n ? body\n : `${MJML_BASE_OPEN}${body}${MJML_BASE_CLOSE}`;\n\n const result = mjml2html(fullMjml, {\n validationLevel: 'soft',\n minify: false,\n });\n\n if (result.errors?.length) {\n const critical = result.errors.filter((e: any) => e.tagName !== undefined);\n if (critical.length > 0) {\n throw new Error(`MJML compilation errors: ${critical.map((e: any) => e.message).join('; ')}`);\n }\n }\n\n return result.html;\n}\n\nexport function htmlToPlainText(html: string): string {\n return convert(html, {\n wordwrap: 80,\n selectors: [\n { selector: 'a', options: { hideLinkHrefIfSameAsText: true } },\n { selector: 'img', format: 'skip' },\n ],\n });\n}\n","import Handlebars from 'handlebars';\n\nconst DATE_FORMAT_OPTIONS: Intl.DateTimeFormatOptions = {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n};\n\nexport function registerEmailHelpers(): void {\n Handlebars.registerHelper('currency', (val: any) => {\n const num = Number(val);\n if (isNaN(num)) return String(val ?? '');\n return `₹${num.toLocaleString('en-IN')}`;\n });\n\n Handlebars.registerHelper('formatDate', (date: any) => {\n if (!date) return '';\n const d = new Date(date);\n if (isNaN(d.getTime())) return String(date);\n return d.toLocaleDateString('en-IN', DATE_FORMAT_OPTIONS);\n });\n}\n","import {\n createRuleEngine,\n type RuleEngine,\n type RuleEngineConfig,\n type SendParams,\n} from '@astralibx/rule-engine';\nimport { renderMjml, htmlToPlainText } from './mjml-renderer';\nimport { registerEmailHelpers } from './email-helpers';\n\nexport interface EmailSendParams {\n identifierId: string;\n contactId: string;\n accountId: string;\n subject: string;\n htmlBody: string;\n textBody: string;\n ruleId: string;\n autoApprove: boolean;\n attachments?: Array<{ filename: string; url: string; contentType: string }>;\n}\n\nexport interface EmailRuleEngineConfig extends Omit<RuleEngineConfig, 'adapters'> {\n adapters: Omit<RuleEngineConfig['adapters'], 'send' | 'sendTest'> & {\n sendEmail: (params: EmailSendParams) => Promise<void>;\n sendTestEmail?: (to: string, subject: string, html: string, text: string, attachments?: Array<{ filename: string; url: string; contentType: string }>) => Promise<void>;\n };\n}\n\nexport function createEmailRuleEngine(config: EmailRuleEngineConfig): RuleEngine {\n // Register email-specific helpers before engine creation\n registerEmailHelpers();\n\n const coreConfig: RuleEngineConfig = {\n ...config,\n adapters: {\n queryUsers: config.adapters.queryUsers,\n resolveData: config.adapters.resolveData,\n selectAgent: config.adapters.selectAgent,\n findIdentifier: config.adapters.findIdentifier,\n send: async (params: SendParams) => {\n const html = renderMjml(params.body);\n const text = params.textBody || htmlToPlainText(html);\n await config.adapters.sendEmail({\n identifierId: params.identifierId,\n contactId: params.contactId,\n accountId: params.accountId,\n subject: params.subject || '',\n htmlBody: html,\n textBody: text,\n ruleId: params.ruleId,\n autoApprove: params.autoApprove,\n attachments: (params.metadata?.attachments as any[]) ?? undefined,\n });\n },\n sendTest: config.adapters.sendTestEmail\n ? async (to: string, body: string, subject?: string, metadata?: Record<string, unknown>) => {\n const html = renderMjml(body);\n const text = htmlToPlainText(html);\n await config.adapters.sendTestEmail!(to, subject || '', html, text, (metadata?.attachments as any[]) ?? undefined);\n }\n : undefined,\n },\n };\n\n return createRuleEngine(coreConfig);\n}\n\n// Re-export everything from core\nexport * from '@astralibx/rule-engine';\n\n// Export email-specific utilities\nexport { renderMjml, htmlToPlainText } from './mjml-renderer';\nexport { registerEmailHelpers } from './email-helpers';\n"]}
1
+ {"version":3,"sources":["../src/mjml-renderer.ts","../src/email-helpers.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import mjml2html from 'mjml';\nimport { convert } from 'html-to-text';\n\nconst MJML_BASE_OPEN = `<mjml>\n <mj-head>\n <mj-attributes>\n <mj-all font-family=\"Arial, sans-serif\" />\n <mj-text font-size=\"15px\" color=\"#333333\" line-height=\"1.6\" />\n </mj-attributes>\n </mj-head>\n <mj-body background-color=\"#ffffff\">\n <mj-section padding=\"20px\">\n <mj-column>\n <mj-text>`;\n\nconst MJML_BASE_CLOSE = ` </mj-text>\n </mj-column>\n </mj-section>\n </mj-body>\n</mjml>`;\n\nexport function renderMjml(body: string): string {\n if (!body.includes('<mj-') && !body.trim().startsWith('<mjml')) {\n return body;\n }\n\n const fullMjml = body.trim().startsWith('<mjml')\n ? body\n : `${MJML_BASE_OPEN}${body}${MJML_BASE_CLOSE}`;\n\n const result = mjml2html(fullMjml, {\n validationLevel: 'soft',\n minify: false,\n });\n\n if (result.errors?.length) {\n // Log all MJML warnings/errors for debugging\n for (const err of result.errors) {\n console.warn(`MJML warning: ${(err as any).message || JSON.stringify(err)}`);\n }\n }\n\n return result.html;\n}\n\nexport function htmlToPlainText(html: string): string {\n return convert(html, {\n wordwrap: 80,\n selectors: [\n { selector: 'a', options: { hideLinkHrefIfSameAsText: true } },\n { selector: 'img', format: 'skip' },\n ],\n });\n}\n","import Handlebars from 'handlebars';\n\nconst DATE_FORMAT_OPTIONS: Intl.DateTimeFormatOptions = {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n};\n\nlet registered = false;\n\nexport function registerEmailHelpers(): void {\n if (registered) return;\n registered = true;\n Handlebars.registerHelper('currency', (val: any) => {\n const num = Number(val);\n if (isNaN(num)) return String(val ?? '');\n return `₹${num.toLocaleString('en-IN')}`;\n });\n\n Handlebars.registerHelper('formatDate', (date: any) => {\n if (!date) return '';\n const d = new Date(date);\n if (isNaN(d.getTime())) return String(date);\n return d.toLocaleDateString('en-IN', DATE_FORMAT_OPTIONS);\n });\n}\n","import {\n createRuleEngine,\n type RuleEngine,\n type RuleEngineConfig,\n type SendParams,\n} from '@astralibx/rule-engine';\nimport { renderMjml, htmlToPlainText } from './mjml-renderer';\nimport { registerEmailHelpers } from './email-helpers';\n\nexport interface EmailSendParams {\n identifierId: string;\n contactId: string;\n accountId: string;\n subject: string;\n htmlBody: string;\n textBody: string;\n ruleId: string;\n autoApprove: boolean;\n attachments?: Array<{ filename: string; url: string; contentType: string }>;\n}\n\nexport interface EmailRuleEngineConfig extends Omit<RuleEngineConfig, 'adapters'> {\n adapters: Omit<RuleEngineConfig['adapters'], 'send' | 'sendTest'> & {\n sendEmail: (params: EmailSendParams) => Promise<void>;\n sendTestEmail?: (to: string, subject: string, html: string, text: string, attachments?: Array<{ filename: string; url: string; contentType: string }>) => Promise<void>;\n };\n}\n\nexport function createEmailRuleEngine(config: EmailRuleEngineConfig): RuleEngine {\n // Register email-specific helpers before engine creation\n registerEmailHelpers();\n\n const coreConfig: RuleEngineConfig = {\n ...config,\n adapters: {\n queryUsers: config.adapters.queryUsers,\n resolveData: config.adapters.resolveData,\n selectAgent: config.adapters.selectAgent,\n findIdentifier: config.adapters.findIdentifier,\n send: async (params: SendParams) => {\n const html = renderMjml(params.body);\n const text = params.textBody || htmlToPlainText(html);\n await config.adapters.sendEmail({\n identifierId: params.identifierId,\n contactId: params.contactId,\n accountId: params.accountId,\n subject: params.subject || '',\n htmlBody: html,\n textBody: text,\n ruleId: params.ruleId,\n autoApprove: params.autoApprove,\n attachments: Array.isArray(params.metadata?.attachments) ? params.metadata.attachments : undefined,\n });\n },\n sendTest: config.adapters.sendTestEmail\n ? async (to: string, body: string, subject?: string, metadata?: Record<string, unknown>) => {\n const html = renderMjml(body);\n const text = htmlToPlainText(html);\n await config.adapters.sendTestEmail!(to, subject || '', html, text, Array.isArray(metadata?.attachments) ? metadata.attachments : undefined);\n }\n : undefined,\n },\n };\n\n return createRuleEngine(coreConfig);\n}\n\n// Re-export everything from core\nexport * from '@astralibx/rule-engine';\n\n// Export email-specific utilities\nexport { renderMjml, htmlToPlainText } from './mjml-renderer';\nexport { registerEmailHelpers } from './email-helpers';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astralibx/email-rule-engine",
3
- "version": "14.0.0",
3
+ "version": "14.0.1",
4
4
  "description": "Email automation engine — thin wrapper over @astralibx/rule-engine with MJML rendering",
5
5
  "repository": {
6
6
  "type": "git",
@@ -49,8 +49,8 @@
49
49
  "mjml": "^4.0.0"
50
50
  },
51
51
  "peerDependencies": {
52
- "@astralibx/rule-engine": "^1.0.0",
53
52
  "express": "^4.18.0 || ^5.0.0",
53
+ "handlebars": "^4.7.0",
54
54
  "ioredis": "^5.0.0",
55
55
  "mongoose": "^7.0.0 || ^8.0.0"
56
56
  },
@@ -60,9 +60,7 @@
60
60
  "@types/node": "^22.0.0",
61
61
  "@vitest/coverage-v8": "^3.0.0",
62
62
  "express": "^5.0.0",
63
- "html-to-text": "^9.0.5",
64
63
  "ioredis": "^5.4.2",
65
- "mjml": "^4.15.3",
66
64
  "mongodb-memory-server": "^10.0.0",
67
65
  "mongoose": "^8.12.1",
68
66
  "typescript": "^5.8.2",