@astralibx/email-rule-engine 4.0.0 → 6.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/dist/index.mjs CHANGED
@@ -71,6 +71,7 @@ function createEmailTemplateSchema(platformValues, audienceValues, categoryValue
71
71
  textBody: String,
72
72
  subjects: { type: [{ type: String }], required: true, validate: [(v) => v.length >= 1, "At least one subject is required"] },
73
73
  bodies: { type: [{ type: String }], required: true, validate: [(v) => v.length >= 1, "At least one body is required"] },
74
+ fields: { type: Schema.Types.Mixed, default: {} },
74
75
  variables: [{ type: String }],
75
76
  version: { type: Number, default: 1 },
76
77
  isActive: { type: Boolean, default: true, index: true }
@@ -105,6 +106,7 @@ function createEmailTemplateSchema(platformValues, audienceValues, categoryValue
105
106
  textBody: input.textBody,
106
107
  subjects: input.subjects,
107
108
  bodies: input.bodies,
109
+ fields: input.fields || {},
108
110
  variables: input.variables || [],
109
111
  version: 1,
110
112
  isActive: true
@@ -153,6 +155,8 @@ function createEmailRuleSchema(platformValues, audienceValues, collectionPrefix)
153
155
  cooldownDays: Number,
154
156
  autoApprove: { type: Boolean, default: true },
155
157
  maxPerRun: Number,
158
+ validFrom: { type: Date },
159
+ validTill: { type: Date },
156
160
  bypassThrottle: { type: Boolean, default: false },
157
161
  emailType: { type: String, enum: Object.values(EMAIL_TYPE), default: EMAIL_TYPE.Automated },
158
162
  totalSent: { type: Number, default: 0 },
@@ -183,6 +187,8 @@ function createEmailRuleSchema(platformValues, audienceValues, collectionPrefix)
183
187
  cooldownDays: input.cooldownDays,
184
188
  autoApprove: input.autoApprove ?? true,
185
189
  maxPerRun: input.maxPerRun,
190
+ validFrom: input.validFrom,
191
+ validTill: input.validTill,
186
192
  bypassThrottle: input.bypassThrottle ?? false,
187
193
  emailType: input.emailType ?? EMAIL_TYPE.Automated,
188
194
  totalSent: 0,
@@ -555,7 +561,8 @@ var UPDATEABLE_FIELDS = /* @__PURE__ */ new Set([
555
561
  "subjects",
556
562
  "bodies",
557
563
  "variables",
558
- "isActive"
564
+ "isActive",
565
+ "fields"
559
566
  ]);
560
567
  function slugify(name) {
561
568
  return name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
@@ -719,7 +726,9 @@ var UPDATEABLE_FIELDS2 = /* @__PURE__ */ new Set([
719
726
  "autoApprove",
720
727
  "maxPerRun",
721
728
  "bypassThrottle",
722
- "emailType"
729
+ "emailType",
730
+ "validFrom",
731
+ "validTill"
723
732
  ]);
724
733
  function validateRuleTemplateCompat(targetRole, targetPlatform, template) {
725
734
  const templateAudience = template.audience;
@@ -958,7 +967,13 @@ var RuleRunnerService = class {
958
967
  let runStatus = "completed";
959
968
  try {
960
969
  const throttleConfig = await this.EmailThrottleConfig.getConfig();
961
- const activeRules = await this.EmailRule.findActive();
970
+ const allActiveRules = await this.EmailRule.findActive();
971
+ const now = /* @__PURE__ */ new Date();
972
+ const activeRules = allActiveRules.filter((rule) => {
973
+ if (rule.validFrom && now < new Date(rule.validFrom)) return false;
974
+ if (rule.validTill && now > new Date(rule.validTill)) return false;
975
+ return true;
976
+ });
962
977
  this.config.hooks?.onRunStart?.({ rulesCount: activeRules.length, triggeredBy });
963
978
  await this.updateRunProgress(runId, {
964
979
  progress: { rulesTotal: activeRules.length, rulesCompleted: 0, sent: 0, failed: 0, skipped: 0, invalid: 0 }
@@ -1162,7 +1177,8 @@ var RuleRunnerService = class {
1162
1177
  continue;
1163
1178
  }
1164
1179
  const user = { _id: identifier.id, email };
1165
- const templateData = this.config.adapters.resolveData(user);
1180
+ const resolvedData = this.config.adapters.resolveData(user);
1181
+ const templateData = { ...template.fields || {}, ...resolvedData };
1166
1182
  const si = Math.floor(Math.random() * compiledVariants.subjectFns.length);
1167
1183
  const bi = Math.floor(Math.random() * compiledVariants.bodyFns.length);
1168
1184
  const renderedSubject = compiledVariants.subjectFns[si](templateData);
@@ -1245,6 +1261,29 @@ var RuleRunnerService = class {
1245
1261
  $set: { lastRunAt: /* @__PURE__ */ new Date(), lastRunStats: stats },
1246
1262
  $inc: { totalSent: stats.sent, totalSkipped: stats.skipped }
1247
1263
  });
1264
+ if (rule.sendOnce) {
1265
+ const allIdentifiers = rule.target.identifiers;
1266
+ const sends = await this.EmailRuleSend.find({
1267
+ ruleId: rule._id
1268
+ }).lean();
1269
+ const sentOrProcessedIds = new Set(
1270
+ sends.filter((s) => s.status !== "throttled").map((s) => String(s.userId || s.emailIdentifierId))
1271
+ );
1272
+ let pendingCount = 0;
1273
+ for (const email of allIdentifiers) {
1274
+ const identifier = identifierMap.get(email.toLowerCase().trim());
1275
+ if (!identifier) continue;
1276
+ if (!sentOrProcessedIds.has(String(identifier.id))) {
1277
+ pendingCount++;
1278
+ }
1279
+ }
1280
+ const throttledCount = sends.filter((s) => s.status === "throttled").length;
1281
+ pendingCount += throttledCount;
1282
+ if (pendingCount === 0) {
1283
+ await this.EmailRule.findByIdAndUpdate(rule._id, { $set: { isActive: false } });
1284
+ this.logger.info(`Rule '${rule.name}' auto-disabled \u2014 all identifiers processed`);
1285
+ }
1286
+ }
1248
1287
  this.config.hooks?.onRuleComplete?.({ ruleId, ruleName: rule.name, stats });
1249
1288
  return stats;
1250
1289
  }
@@ -1341,7 +1380,8 @@ var RuleRunnerService = class {
1341
1380
  this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped" });
1342
1381
  continue;
1343
1382
  }
1344
- const templateData = this.config.adapters.resolveData(user);
1383
+ const resolvedData = this.config.adapters.resolveData(user);
1384
+ const templateData = { ...template.fields || {}, ...resolvedData };
1345
1385
  const si = Math.floor(Math.random() * compiledVariants.subjectFns.length);
1346
1386
  const bi = Math.floor(Math.random() * compiledVariants.bodyFns.length);
1347
1387
  const renderedSubject = compiledVariants.subjectFns[si](templateData);