@astralibx/email-rule-engine 6.0.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -15
- package/dist/index.d.mts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +79 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +79 -25
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -102,21 +102,18 @@ The `createEmailRuleEngine(config)` factory accepts an `EmailRuleEngineConfig` o
|
|
|
102
102
|
|
|
103
103
|
See [docs/configuration.md](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/configuration.md) for the full reference with examples.
|
|
104
104
|
|
|
105
|
-
##
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
| [Error Handling](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/error-handling.md) | All error classes with codes and when thrown |
|
|
118
|
-
| [Constants](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/constants.md) | All exported constants and derived types |
|
|
119
|
-
| [Migration v1 to v2](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/migration-v1-to-v2.md) | Breaking changes from v1 |
|
|
105
|
+
## Getting Started Guide
|
|
106
|
+
|
|
107
|
+
1. [Configuration](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/configuration.md) — Set up database, Redis, and options
|
|
108
|
+
2. [Adapters](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/adapters.md) — Implement the 6 required adapter functions
|
|
109
|
+
3. [Templates](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/templates.md) — Create email templates with MJML + Handlebars
|
|
110
|
+
4. [Rules](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/rules.md) — Define targeting rules with conditions or explicit lists
|
|
111
|
+
5. [Execution Flow](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/execution-flow.md) — Understand how the runner processes rules
|
|
112
|
+
6. [Throttling](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/throttling.md) — Configure per-user send limits
|
|
113
|
+
|
|
114
|
+
Reference: [API Routes](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/api-routes.md) | [Programmatic API](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/programmatic-api.md) | [Types](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/types.md) | [Constants](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/constants.md) | [Error Handling](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/error-handling.md)
|
|
115
|
+
|
|
116
|
+
> **Important:** Configure throttle settings before running rules. Default limits (1/day, 2/week) may be too restrictive. See [Throttling](https://github.com/Hariprakash1997/astralib/blob/main/packages/email-rule-engine/docs/throttling.md).
|
|
120
117
|
|
|
121
118
|
## License
|
|
122
119
|
|
package/dist/index.d.mts
CHANGED
|
@@ -295,6 +295,7 @@ interface EmailTemplate {
|
|
|
295
295
|
textBody?: string;
|
|
296
296
|
subjects: string[];
|
|
297
297
|
bodies: string[];
|
|
298
|
+
preheaders?: string[];
|
|
298
299
|
fields?: Record<string, string>;
|
|
299
300
|
variables: string[];
|
|
300
301
|
version: number;
|
|
@@ -312,6 +313,7 @@ interface CreateEmailTemplateInput {
|
|
|
312
313
|
textBody?: string;
|
|
313
314
|
subjects: string[];
|
|
314
315
|
bodies: string[];
|
|
316
|
+
preheaders?: string[];
|
|
315
317
|
fields?: Record<string, string>;
|
|
316
318
|
variables?: string[];
|
|
317
319
|
}
|
|
@@ -324,6 +326,7 @@ interface UpdateEmailTemplateInput {
|
|
|
324
326
|
textBody?: string;
|
|
325
327
|
subjects?: string[];
|
|
326
328
|
bodies?: string[];
|
|
329
|
+
preheaders?: string[];
|
|
327
330
|
fields?: Record<string, string>;
|
|
328
331
|
variables?: string[];
|
|
329
332
|
isActive?: boolean;
|
|
@@ -382,6 +385,7 @@ interface IEmailRuleSend {
|
|
|
382
385
|
subject?: string;
|
|
383
386
|
subjectIndex?: number;
|
|
384
387
|
bodyIndex?: number;
|
|
388
|
+
preheaderIndex?: number;
|
|
385
389
|
failureReason?: string;
|
|
386
390
|
}
|
|
387
391
|
type EmailRuleSendDocument = HydratedDocument<IEmailRuleSend>;
|
|
@@ -395,6 +399,7 @@ interface EmailRuleSendStatics {
|
|
|
395
399
|
subject?: string;
|
|
396
400
|
subjectIndex?: number;
|
|
397
401
|
bodyIndex?: number;
|
|
402
|
+
preheaderIndex?: number;
|
|
398
403
|
failureReason?: string;
|
|
399
404
|
}): Promise<EmailRuleSendDocument>;
|
|
400
405
|
}
|
|
@@ -625,10 +630,11 @@ declare class TemplateRenderService {
|
|
|
625
630
|
constructor();
|
|
626
631
|
renderSingle(subject: string, body: string, data: Record<string, unknown>, textBody?: string): RenderResult;
|
|
627
632
|
compileBatch(subject: string, body: string, textBody?: string): CompiledTemplate;
|
|
628
|
-
compileBatchVariants(subjects: string[], bodies: string[], textBody?: string): {
|
|
633
|
+
compileBatchVariants(subjects: string[], bodies: string[], textBody?: string, preheaders?: string[]): {
|
|
629
634
|
subjectFns: HandlebarsTemplateDelegate[];
|
|
630
635
|
bodyFns: HandlebarsTemplateDelegate[];
|
|
631
636
|
textBodyFn?: HandlebarsTemplateDelegate;
|
|
637
|
+
preheaderFns?: HandlebarsTemplateDelegate[];
|
|
632
638
|
};
|
|
633
639
|
renderFromCompiled(compiled: CompiledTemplate, data: Record<string, unknown>): RenderResult;
|
|
634
640
|
renderPreview(subject: string, body: string, data: Record<string, unknown>, textBody?: string): RenderResult;
|
package/dist/index.d.ts
CHANGED
|
@@ -295,6 +295,7 @@ interface EmailTemplate {
|
|
|
295
295
|
textBody?: string;
|
|
296
296
|
subjects: string[];
|
|
297
297
|
bodies: string[];
|
|
298
|
+
preheaders?: string[];
|
|
298
299
|
fields?: Record<string, string>;
|
|
299
300
|
variables: string[];
|
|
300
301
|
version: number;
|
|
@@ -312,6 +313,7 @@ interface CreateEmailTemplateInput {
|
|
|
312
313
|
textBody?: string;
|
|
313
314
|
subjects: string[];
|
|
314
315
|
bodies: string[];
|
|
316
|
+
preheaders?: string[];
|
|
315
317
|
fields?: Record<string, string>;
|
|
316
318
|
variables?: string[];
|
|
317
319
|
}
|
|
@@ -324,6 +326,7 @@ interface UpdateEmailTemplateInput {
|
|
|
324
326
|
textBody?: string;
|
|
325
327
|
subjects?: string[];
|
|
326
328
|
bodies?: string[];
|
|
329
|
+
preheaders?: string[];
|
|
327
330
|
fields?: Record<string, string>;
|
|
328
331
|
variables?: string[];
|
|
329
332
|
isActive?: boolean;
|
|
@@ -382,6 +385,7 @@ interface IEmailRuleSend {
|
|
|
382
385
|
subject?: string;
|
|
383
386
|
subjectIndex?: number;
|
|
384
387
|
bodyIndex?: number;
|
|
388
|
+
preheaderIndex?: number;
|
|
385
389
|
failureReason?: string;
|
|
386
390
|
}
|
|
387
391
|
type EmailRuleSendDocument = HydratedDocument<IEmailRuleSend>;
|
|
@@ -395,6 +399,7 @@ interface EmailRuleSendStatics {
|
|
|
395
399
|
subject?: string;
|
|
396
400
|
subjectIndex?: number;
|
|
397
401
|
bodyIndex?: number;
|
|
402
|
+
preheaderIndex?: number;
|
|
398
403
|
failureReason?: string;
|
|
399
404
|
}): Promise<EmailRuleSendDocument>;
|
|
400
405
|
}
|
|
@@ -625,10 +630,11 @@ declare class TemplateRenderService {
|
|
|
625
630
|
constructor();
|
|
626
631
|
renderSingle(subject: string, body: string, data: Record<string, unknown>, textBody?: string): RenderResult;
|
|
627
632
|
compileBatch(subject: string, body: string, textBody?: string): CompiledTemplate;
|
|
628
|
-
compileBatchVariants(subjects: string[], bodies: string[], textBody?: string): {
|
|
633
|
+
compileBatchVariants(subjects: string[], bodies: string[], textBody?: string, preheaders?: string[]): {
|
|
629
634
|
subjectFns: HandlebarsTemplateDelegate[];
|
|
630
635
|
bodyFns: HandlebarsTemplateDelegate[];
|
|
631
636
|
textBodyFn?: HandlebarsTemplateDelegate;
|
|
637
|
+
preheaderFns?: HandlebarsTemplateDelegate[];
|
|
632
638
|
};
|
|
633
639
|
renderFromCompiled(compiled: CompiledTemplate, data: Record<string, unknown>): RenderResult;
|
|
634
640
|
renderPreview(subject: string, body: string, data: Record<string, unknown>, textBody?: string): RenderResult;
|
package/dist/index.js
CHANGED
|
@@ -79,7 +79,18 @@ function createEmailTemplateSchema(platformValues, audienceValues, categoryValue
|
|
|
79
79
|
textBody: String,
|
|
80
80
|
subjects: { type: [{ type: String }], required: true, validate: [(v) => v.length >= 1, "At least one subject is required"] },
|
|
81
81
|
bodies: { type: [{ type: String }], required: true, validate: [(v) => v.length >= 1, "At least one body is required"] },
|
|
82
|
-
|
|
82
|
+
preheaders: [{ type: String }],
|
|
83
|
+
fields: {
|
|
84
|
+
type: mongoose.Schema.Types.Mixed,
|
|
85
|
+
default: {},
|
|
86
|
+
validate: {
|
|
87
|
+
validator: (v) => {
|
|
88
|
+
if (!v || typeof v !== "object") return true;
|
|
89
|
+
return Object.values(v).every((val) => typeof val === "string");
|
|
90
|
+
},
|
|
91
|
+
message: "All field values must be strings"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
83
94
|
variables: [{ type: String }],
|
|
84
95
|
version: { type: Number, default: 1 },
|
|
85
96
|
isActive: { type: Boolean, default: true, index: true }
|
|
@@ -114,6 +125,7 @@ function createEmailTemplateSchema(platformValues, audienceValues, categoryValue
|
|
|
114
125
|
textBody: input.textBody,
|
|
115
126
|
subjects: input.subjects,
|
|
116
127
|
bodies: input.bodies,
|
|
128
|
+
preheaders: input.preheaders || [],
|
|
117
129
|
fields: input.fields || {},
|
|
118
130
|
variables: input.variables || [],
|
|
119
131
|
version: 1,
|
|
@@ -224,6 +236,7 @@ function createEmailRuleSendSchema(collectionPrefix) {
|
|
|
224
236
|
subject: { type: String },
|
|
225
237
|
subjectIndex: { type: Number },
|
|
226
238
|
bodyIndex: { type: Number },
|
|
239
|
+
preheaderIndex: { type: Number },
|
|
227
240
|
failureReason: { type: String }
|
|
228
241
|
},
|
|
229
242
|
{
|
|
@@ -448,7 +461,7 @@ var TemplateRenderService = class {
|
|
|
448
461
|
const textBodyFn = textBody ? Handlebars__default.default.compile(textBody, { strict: true }) : void 0;
|
|
449
462
|
return { subjectFn, bodyFn, textBodyFn };
|
|
450
463
|
}
|
|
451
|
-
compileBatchVariants(subjects, bodies, textBody) {
|
|
464
|
+
compileBatchVariants(subjects, bodies, textBody, preheaders) {
|
|
452
465
|
const subjectFns = subjects.map((s) => Handlebars__default.default.compile(s, { strict: true }));
|
|
453
466
|
const bodyFns = bodies.map((b) => {
|
|
454
467
|
const mjmlSource = wrapInMjml(b);
|
|
@@ -456,7 +469,8 @@ var TemplateRenderService = class {
|
|
|
456
469
|
return Handlebars__default.default.compile(htmlWithHandlebars, { strict: true });
|
|
457
470
|
});
|
|
458
471
|
const textBodyFn = textBody ? Handlebars__default.default.compile(textBody, { strict: true }) : void 0;
|
|
459
|
-
|
|
472
|
+
const preheaderFns = preheaders && preheaders.length > 0 ? preheaders.map((p) => Handlebars__default.default.compile(p, { strict: true })) : void 0;
|
|
473
|
+
return { subjectFns, bodyFns, textBodyFn, preheaderFns };
|
|
460
474
|
}
|
|
461
475
|
renderFromCompiled(compiled, data) {
|
|
462
476
|
const subject = compiled.subjectFn(data);
|
|
@@ -568,6 +582,7 @@ var UPDATEABLE_FIELDS = /* @__PURE__ */ new Set([
|
|
|
568
582
|
"textBody",
|
|
569
583
|
"subjects",
|
|
570
584
|
"bodies",
|
|
585
|
+
"preheaders",
|
|
571
586
|
"variables",
|
|
572
587
|
"isActive",
|
|
573
588
|
"fields"
|
|
@@ -610,7 +625,7 @@ var TemplateService = class {
|
|
|
610
625
|
throw new TemplateSyntaxError(`Template validation failed: ${validation.errors.join("; ")}`, validation.errors);
|
|
611
626
|
}
|
|
612
627
|
}
|
|
613
|
-
const allContent = [...subjects, ...bodies, input.textBody || ""].join(" ");
|
|
628
|
+
const allContent = [...subjects, ...bodies, ...input.preheaders || [], input.textBody || ""].join(" ");
|
|
614
629
|
const variables = input.variables || this.renderService.extractVariables(allContent);
|
|
615
630
|
return this.EmailTemplate.createTemplate({
|
|
616
631
|
...input,
|
|
@@ -638,11 +653,12 @@ var TemplateService = class {
|
|
|
638
653
|
}
|
|
639
654
|
}
|
|
640
655
|
}
|
|
641
|
-
if (input.textBody || input.subjects || input.bodies) {
|
|
656
|
+
if (input.textBody || input.subjects || input.bodies || input.preheaders) {
|
|
642
657
|
const subjects = input.subjects ?? template.subjects;
|
|
643
658
|
const bodies = input.bodies ?? template.bodies;
|
|
659
|
+
const preheaders = input.preheaders ?? template.preheaders ?? [];
|
|
644
660
|
const textBody = input.textBody ?? template.textBody;
|
|
645
|
-
const allContent = [...subjects, ...bodies, textBody || ""].join(" ");
|
|
661
|
+
const allContent = [...subjects, ...bodies, ...preheaders, textBody || ""].join(" ");
|
|
646
662
|
input.variables = this.renderService.extractVariables(allContent);
|
|
647
663
|
}
|
|
648
664
|
const setFields = {};
|
|
@@ -652,7 +668,7 @@ var TemplateService = class {
|
|
|
652
668
|
}
|
|
653
669
|
}
|
|
654
670
|
const update = { $set: setFields };
|
|
655
|
-
if (input.textBody || input.subjects || input.bodies) {
|
|
671
|
+
if (input.textBody || input.subjects || input.bodies || input.preheaders) {
|
|
656
672
|
update["$inc"] = { version: 1 };
|
|
657
673
|
}
|
|
658
674
|
return this.EmailTemplate.findByIdAndUpdate(
|
|
@@ -904,6 +920,21 @@ var RedisLock = class {
|
|
|
904
920
|
var MS_PER_DAY = 864e5;
|
|
905
921
|
var DEFAULT_LOCK_TTL_MS = 30 * 60 * 1e3;
|
|
906
922
|
var IDENTIFIER_CHUNK_SIZE = 50;
|
|
923
|
+
function getLocalDate(date, timezone) {
|
|
924
|
+
if (!timezone) return date;
|
|
925
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
926
|
+
timeZone: timezone,
|
|
927
|
+
year: "numeric",
|
|
928
|
+
month: "2-digit",
|
|
929
|
+
day: "2-digit",
|
|
930
|
+
hour: "2-digit",
|
|
931
|
+
minute: "2-digit",
|
|
932
|
+
second: "2-digit",
|
|
933
|
+
hour12: false
|
|
934
|
+
}).formatToParts(date);
|
|
935
|
+
const get = (type) => parts.find((p) => p.type === type)?.value || "0";
|
|
936
|
+
return /* @__PURE__ */ new Date(`${get("year")}-${get("month")}-${get("day")}T${get("hour")}:${get("minute")}:${get("second")}`);
|
|
937
|
+
}
|
|
907
938
|
async function processInChunks(items, fn, chunkSize) {
|
|
908
939
|
const results = [];
|
|
909
940
|
for (let i = 0; i < items.length; i += chunkSize) {
|
|
@@ -977,9 +1008,18 @@ var RuleRunnerService = class {
|
|
|
977
1008
|
const throttleConfig = await this.EmailThrottleConfig.getConfig();
|
|
978
1009
|
const allActiveRules = await this.EmailRule.findActive();
|
|
979
1010
|
const now = /* @__PURE__ */ new Date();
|
|
1011
|
+
const tz = this.config.options?.sendWindow?.timezone;
|
|
980
1012
|
const activeRules = allActiveRules.filter((rule) => {
|
|
981
|
-
if (rule.validFrom
|
|
982
|
-
|
|
1013
|
+
if (rule.validFrom) {
|
|
1014
|
+
const localNow = getLocalDate(now, tz);
|
|
1015
|
+
const localValidFrom = getLocalDate(new Date(rule.validFrom), tz);
|
|
1016
|
+
if (localNow < localValidFrom) return false;
|
|
1017
|
+
}
|
|
1018
|
+
if (rule.validTill) {
|
|
1019
|
+
const localNow = getLocalDate(now, tz);
|
|
1020
|
+
const localValidTill = getLocalDate(new Date(rule.validTill), tz);
|
|
1021
|
+
if (localNow > localValidTill) return false;
|
|
1022
|
+
}
|
|
983
1023
|
return true;
|
|
984
1024
|
});
|
|
985
1025
|
this.config.hooks?.onRunStart?.({ rulesCount: activeRules.length, triggeredBy });
|
|
@@ -1136,10 +1176,12 @@ var RuleRunnerService = class {
|
|
|
1136
1176
|
sendMap.set(uid, send);
|
|
1137
1177
|
}
|
|
1138
1178
|
}
|
|
1179
|
+
const preheaders = template.preheaders || [];
|
|
1139
1180
|
const compiledVariants = this.templateRenderer.compileBatchVariants(
|
|
1140
1181
|
template.subjects,
|
|
1141
1182
|
template.bodies,
|
|
1142
|
-
template.textBody
|
|
1183
|
+
template.textBody,
|
|
1184
|
+
preheaders
|
|
1143
1185
|
);
|
|
1144
1186
|
let totalProcessed = 0;
|
|
1145
1187
|
for (let i = 0; i < emailsToProcess.length; i++) {
|
|
@@ -1195,6 +1237,15 @@ var RuleRunnerService = class {
|
|
|
1195
1237
|
let finalHtml = renderedHtml;
|
|
1196
1238
|
let finalText = renderedText;
|
|
1197
1239
|
let finalSubject = renderedSubject;
|
|
1240
|
+
let pi;
|
|
1241
|
+
if (compiledVariants.preheaderFns && compiledVariants.preheaderFns.length > 0) {
|
|
1242
|
+
pi = Math.floor(Math.random() * compiledVariants.preheaderFns.length);
|
|
1243
|
+
const renderedPreheader = compiledVariants.preheaderFns[pi](templateData);
|
|
1244
|
+
if (renderedPreheader) {
|
|
1245
|
+
const preheaderHtml = `<div style="display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">${renderedPreheader}</div>`;
|
|
1246
|
+
finalHtml = finalHtml.replace(/(<body[^>]*>)/i, `$1${preheaderHtml}`);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1198
1249
|
if (this.config.hooks?.beforeSend) {
|
|
1199
1250
|
try {
|
|
1200
1251
|
const modified = await this.config.hooks.beforeSend({
|
|
@@ -1237,7 +1288,7 @@ var RuleRunnerService = class {
|
|
|
1237
1288
|
dedupKey,
|
|
1238
1289
|
identifier.id,
|
|
1239
1290
|
void 0,
|
|
1240
|
-
{ status: "sent", accountId: agentSelection.accountId, subject: finalSubject, subjectIndex: si, bodyIndex: bi }
|
|
1291
|
+
{ status: "sent", accountId: agentSelection.accountId, subject: finalSubject, subjectIndex: si, bodyIndex: bi, preheaderIndex: pi }
|
|
1241
1292
|
);
|
|
1242
1293
|
const current = throttleMap.get(dedupKey) || { today: 0, thisWeek: 0, lastSentDate: null };
|
|
1243
1294
|
throttleMap.set(dedupKey, {
|
|
@@ -1270,24 +1321,16 @@ var RuleRunnerService = class {
|
|
|
1270
1321
|
$inc: { totalSent: stats.sent, totalSkipped: stats.skipped }
|
|
1271
1322
|
});
|
|
1272
1323
|
if (rule.sendOnce) {
|
|
1273
|
-
const allIdentifiers = rule.target.identifiers;
|
|
1324
|
+
const allIdentifiers = rule.target.identifiers || [];
|
|
1325
|
+
const totalIdentifiers = new Set(allIdentifiers.map((e) => e.toLowerCase().trim()).filter(Boolean)).size;
|
|
1274
1326
|
const sends = await this.EmailRuleSend.find({
|
|
1275
1327
|
ruleId: rule._id
|
|
1276
1328
|
}).lean();
|
|
1277
1329
|
const sentOrProcessedIds = new Set(
|
|
1278
1330
|
sends.filter((s) => s.status !== "throttled").map((s) => String(s.userId || s.emailIdentifierId))
|
|
1279
1331
|
);
|
|
1280
|
-
let pendingCount = 0;
|
|
1281
|
-
for (const email of allIdentifiers) {
|
|
1282
|
-
const identifier = identifierMap.get(email.toLowerCase().trim());
|
|
1283
|
-
if (!identifier) continue;
|
|
1284
|
-
if (!sentOrProcessedIds.has(String(identifier.id))) {
|
|
1285
|
-
pendingCount++;
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
1332
|
const throttledCount = sends.filter((s) => s.status === "throttled").length;
|
|
1289
|
-
|
|
1290
|
-
if (pendingCount === 0) {
|
|
1333
|
+
if (sentOrProcessedIds.size >= totalIdentifiers && throttledCount === 0) {
|
|
1291
1334
|
await this.EmailRule.findByIdAndUpdate(rule._id, { $set: { isActive: false } });
|
|
1292
1335
|
this.logger.info(`Rule '${rule.name}' auto-disabled \u2014 all identifiers processed`);
|
|
1293
1336
|
}
|
|
@@ -1332,10 +1375,12 @@ var RuleRunnerService = class {
|
|
|
1332
1375
|
identifierMap.set(result.email, { id: result.id, contactId: result.contactId });
|
|
1333
1376
|
}
|
|
1334
1377
|
}
|
|
1378
|
+
const preheadersQ = template.preheaders || [];
|
|
1335
1379
|
const compiledVariants = this.templateRenderer.compileBatchVariants(
|
|
1336
1380
|
template.subjects,
|
|
1337
1381
|
template.bodies,
|
|
1338
|
-
template.textBody
|
|
1382
|
+
template.textBody,
|
|
1383
|
+
preheadersQ
|
|
1339
1384
|
);
|
|
1340
1385
|
const ruleId = rule._id.toString();
|
|
1341
1386
|
const templateId = rule.templateId.toString();
|
|
@@ -1398,6 +1443,15 @@ var RuleRunnerService = class {
|
|
|
1398
1443
|
let finalHtml = renderedHtml;
|
|
1399
1444
|
let finalText = renderedText;
|
|
1400
1445
|
let finalSubject = renderedSubject;
|
|
1446
|
+
let pi;
|
|
1447
|
+
if (compiledVariants.preheaderFns && compiledVariants.preheaderFns.length > 0) {
|
|
1448
|
+
pi = Math.floor(Math.random() * compiledVariants.preheaderFns.length);
|
|
1449
|
+
const renderedPreheader = compiledVariants.preheaderFns[pi](templateData);
|
|
1450
|
+
if (renderedPreheader) {
|
|
1451
|
+
const preheaderHtml = `<div style="display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">${renderedPreheader}</div>`;
|
|
1452
|
+
finalHtml = finalHtml.replace(/(<body[^>]*>)/i, `$1${preheaderHtml}`);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1401
1455
|
if (this.config.hooks?.beforeSend) {
|
|
1402
1456
|
try {
|
|
1403
1457
|
const modified = await this.config.hooks.beforeSend({
|
|
@@ -1440,7 +1494,7 @@ var RuleRunnerService = class {
|
|
|
1440
1494
|
userId,
|
|
1441
1495
|
identifier.id,
|
|
1442
1496
|
void 0,
|
|
1443
|
-
{ status: "sent", accountId: agentSelection.accountId, subject: finalSubject, subjectIndex: si, bodyIndex: bi }
|
|
1497
|
+
{ status: "sent", accountId: agentSelection.accountId, subject: finalSubject, subjectIndex: si, bodyIndex: bi, preheaderIndex: pi }
|
|
1444
1498
|
);
|
|
1445
1499
|
const current = throttleMap.get(userId) || { today: 0, thisWeek: 0, lastSentDate: null };
|
|
1446
1500
|
throttleMap.set(userId, {
|
|
@@ -1669,7 +1723,7 @@ function createTemplateController(templateService, options) {
|
|
|
1669
1723
|
}
|
|
1670
1724
|
async function create(req, res) {
|
|
1671
1725
|
try {
|
|
1672
|
-
const { name, subjects, bodies, category, audience, platform } = req.body;
|
|
1726
|
+
const { name, subjects, bodies, category, audience, platform, preheaders } = req.body;
|
|
1673
1727
|
if (!name || !subjects || subjects.length === 0 || !bodies || bodies.length === 0 || !category || !audience || !platform) {
|
|
1674
1728
|
return res.status(400).json({ success: false, error: "name, subjects, bodies, category, audience, and platform are required" });
|
|
1675
1729
|
}
|