@astralibx/email-rule-engine 5.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.d.mts CHANGED
@@ -92,6 +92,8 @@ interface EmailRule {
92
92
  cooldownDays?: number;
93
93
  autoApprove: boolean;
94
94
  maxPerRun?: number;
95
+ validFrom?: Date;
96
+ validTill?: Date;
95
97
  bypassThrottle: boolean;
96
98
  emailType: EmailType;
97
99
  totalSent: number;
@@ -112,6 +114,8 @@ interface CreateEmailRuleInput {
112
114
  cooldownDays?: number;
113
115
  autoApprove?: boolean;
114
116
  maxPerRun?: number;
117
+ validFrom?: Date;
118
+ validTill?: Date;
115
119
  bypassThrottle?: boolean;
116
120
  emailType?: EmailType;
117
121
  }
@@ -127,6 +131,8 @@ interface UpdateEmailRuleInput {
127
131
  cooldownDays?: number;
128
132
  autoApprove?: boolean;
129
133
  maxPerRun?: number;
134
+ validFrom?: Date;
135
+ validTill?: Date;
130
136
  bypassThrottle?: boolean;
131
137
  emailType?: EmailType;
132
138
  }
@@ -289,6 +295,7 @@ interface EmailTemplate {
289
295
  textBody?: string;
290
296
  subjects: string[];
291
297
  bodies: string[];
298
+ fields?: Record<string, string>;
292
299
  variables: string[];
293
300
  version: number;
294
301
  isActive: boolean;
@@ -305,6 +312,7 @@ interface CreateEmailTemplateInput {
305
312
  textBody?: string;
306
313
  subjects: string[];
307
314
  bodies: string[];
315
+ fields?: Record<string, string>;
308
316
  variables?: string[];
309
317
  }
310
318
  interface UpdateEmailTemplateInput {
@@ -316,6 +324,7 @@ interface UpdateEmailTemplateInput {
316
324
  textBody?: string;
317
325
  subjects?: string[];
318
326
  bodies?: string[];
327
+ fields?: Record<string, string>;
319
328
  variables?: string[];
320
329
  isActive?: boolean;
321
330
  }
package/dist/index.d.ts CHANGED
@@ -92,6 +92,8 @@ interface EmailRule {
92
92
  cooldownDays?: number;
93
93
  autoApprove: boolean;
94
94
  maxPerRun?: number;
95
+ validFrom?: Date;
96
+ validTill?: Date;
95
97
  bypassThrottle: boolean;
96
98
  emailType: EmailType;
97
99
  totalSent: number;
@@ -112,6 +114,8 @@ interface CreateEmailRuleInput {
112
114
  cooldownDays?: number;
113
115
  autoApprove?: boolean;
114
116
  maxPerRun?: number;
117
+ validFrom?: Date;
118
+ validTill?: Date;
115
119
  bypassThrottle?: boolean;
116
120
  emailType?: EmailType;
117
121
  }
@@ -127,6 +131,8 @@ interface UpdateEmailRuleInput {
127
131
  cooldownDays?: number;
128
132
  autoApprove?: boolean;
129
133
  maxPerRun?: number;
134
+ validFrom?: Date;
135
+ validTill?: Date;
130
136
  bypassThrottle?: boolean;
131
137
  emailType?: EmailType;
132
138
  }
@@ -289,6 +295,7 @@ interface EmailTemplate {
289
295
  textBody?: string;
290
296
  subjects: string[];
291
297
  bodies: string[];
298
+ fields?: Record<string, string>;
292
299
  variables: string[];
293
300
  version: number;
294
301
  isActive: boolean;
@@ -305,6 +312,7 @@ interface CreateEmailTemplateInput {
305
312
  textBody?: string;
306
313
  subjects: string[];
307
314
  bodies: string[];
315
+ fields?: Record<string, string>;
308
316
  variables?: string[];
309
317
  }
310
318
  interface UpdateEmailTemplateInput {
@@ -316,6 +324,7 @@ interface UpdateEmailTemplateInput {
316
324
  textBody?: string;
317
325
  subjects?: string[];
318
326
  bodies?: string[];
327
+ fields?: Record<string, string>;
319
328
  variables?: string[];
320
329
  isActive?: boolean;
321
330
  }
package/dist/index.js CHANGED
@@ -79,6 +79,7 @@ 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
+ fields: { type: mongoose.Schema.Types.Mixed, default: {} },
82
83
  variables: [{ type: String }],
83
84
  version: { type: Number, default: 1 },
84
85
  isActive: { type: Boolean, default: true, index: true }
@@ -113,6 +114,7 @@ function createEmailTemplateSchema(platformValues, audienceValues, categoryValue
113
114
  textBody: input.textBody,
114
115
  subjects: input.subjects,
115
116
  bodies: input.bodies,
117
+ fields: input.fields || {},
116
118
  variables: input.variables || [],
117
119
  version: 1,
118
120
  isActive: true
@@ -161,6 +163,8 @@ function createEmailRuleSchema(platformValues, audienceValues, collectionPrefix)
161
163
  cooldownDays: Number,
162
164
  autoApprove: { type: Boolean, default: true },
163
165
  maxPerRun: Number,
166
+ validFrom: { type: Date },
167
+ validTill: { type: Date },
164
168
  bypassThrottle: { type: Boolean, default: false },
165
169
  emailType: { type: String, enum: Object.values(EMAIL_TYPE), default: EMAIL_TYPE.Automated },
166
170
  totalSent: { type: Number, default: 0 },
@@ -191,6 +195,8 @@ function createEmailRuleSchema(platformValues, audienceValues, collectionPrefix)
191
195
  cooldownDays: input.cooldownDays,
192
196
  autoApprove: input.autoApprove ?? true,
193
197
  maxPerRun: input.maxPerRun,
198
+ validFrom: input.validFrom,
199
+ validTill: input.validTill,
194
200
  bypassThrottle: input.bypassThrottle ?? false,
195
201
  emailType: input.emailType ?? EMAIL_TYPE.Automated,
196
202
  totalSent: 0,
@@ -563,7 +569,8 @@ var UPDATEABLE_FIELDS = /* @__PURE__ */ new Set([
563
569
  "subjects",
564
570
  "bodies",
565
571
  "variables",
566
- "isActive"
572
+ "isActive",
573
+ "fields"
567
574
  ]);
568
575
  function slugify(name) {
569
576
  return name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
@@ -727,7 +734,9 @@ var UPDATEABLE_FIELDS2 = /* @__PURE__ */ new Set([
727
734
  "autoApprove",
728
735
  "maxPerRun",
729
736
  "bypassThrottle",
730
- "emailType"
737
+ "emailType",
738
+ "validFrom",
739
+ "validTill"
731
740
  ]);
732
741
  function validateRuleTemplateCompat(targetRole, targetPlatform, template) {
733
742
  const templateAudience = template.audience;
@@ -966,7 +975,13 @@ var RuleRunnerService = class {
966
975
  let runStatus = "completed";
967
976
  try {
968
977
  const throttleConfig = await this.EmailThrottleConfig.getConfig();
969
- const activeRules = await this.EmailRule.findActive();
978
+ const allActiveRules = await this.EmailRule.findActive();
979
+ const now = /* @__PURE__ */ new Date();
980
+ const activeRules = allActiveRules.filter((rule) => {
981
+ if (rule.validFrom && now < new Date(rule.validFrom)) return false;
982
+ if (rule.validTill && now > new Date(rule.validTill)) return false;
983
+ return true;
984
+ });
970
985
  this.config.hooks?.onRunStart?.({ rulesCount: activeRules.length, triggeredBy });
971
986
  await this.updateRunProgress(runId, {
972
987
  progress: { rulesTotal: activeRules.length, rulesCompleted: 0, sent: 0, failed: 0, skipped: 0, invalid: 0 }
@@ -1170,7 +1185,8 @@ var RuleRunnerService = class {
1170
1185
  continue;
1171
1186
  }
1172
1187
  const user = { _id: identifier.id, email };
1173
- const templateData = this.config.adapters.resolveData(user);
1188
+ const resolvedData = this.config.adapters.resolveData(user);
1189
+ const templateData = { ...template.fields || {}, ...resolvedData };
1174
1190
  const si = Math.floor(Math.random() * compiledVariants.subjectFns.length);
1175
1191
  const bi = Math.floor(Math.random() * compiledVariants.bodyFns.length);
1176
1192
  const renderedSubject = compiledVariants.subjectFns[si](templateData);
@@ -1253,6 +1269,29 @@ var RuleRunnerService = class {
1253
1269
  $set: { lastRunAt: /* @__PURE__ */ new Date(), lastRunStats: stats },
1254
1270
  $inc: { totalSent: stats.sent, totalSkipped: stats.skipped }
1255
1271
  });
1272
+ if (rule.sendOnce) {
1273
+ const allIdentifiers = rule.target.identifiers;
1274
+ const sends = await this.EmailRuleSend.find({
1275
+ ruleId: rule._id
1276
+ }).lean();
1277
+ const sentOrProcessedIds = new Set(
1278
+ sends.filter((s) => s.status !== "throttled").map((s) => String(s.userId || s.emailIdentifierId))
1279
+ );
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
+ const throttledCount = sends.filter((s) => s.status === "throttled").length;
1289
+ pendingCount += throttledCount;
1290
+ if (pendingCount === 0) {
1291
+ await this.EmailRule.findByIdAndUpdate(rule._id, { $set: { isActive: false } });
1292
+ this.logger.info(`Rule '${rule.name}' auto-disabled \u2014 all identifiers processed`);
1293
+ }
1294
+ }
1256
1295
  this.config.hooks?.onRuleComplete?.({ ruleId, ruleName: rule.name, stats });
1257
1296
  return stats;
1258
1297
  }
@@ -1349,7 +1388,8 @@ var RuleRunnerService = class {
1349
1388
  this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped" });
1350
1389
  continue;
1351
1390
  }
1352
- const templateData = this.config.adapters.resolveData(user);
1391
+ const resolvedData = this.config.adapters.resolveData(user);
1392
+ const templateData = { ...template.fields || {}, ...resolvedData };
1353
1393
  const si = Math.floor(Math.random() * compiledVariants.subjectFns.length);
1354
1394
  const bi = Math.floor(Math.random() * compiledVariants.bodyFns.length);
1355
1395
  const renderedSubject = compiledVariants.subjectFns[si](templateData);