@astralibx/email-rule-engine 12.3.0 → 12.4.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.cjs CHANGED
@@ -178,6 +178,15 @@ function createEmailRuleSchema(platformValues, audienceValues, collectionPrefix)
178
178
  validFrom: { type: Date },
179
179
  validTill: { type: Date },
180
180
  bypassThrottle: { type: Boolean, default: false },
181
+ throttleOverride: {
182
+ type: {
183
+ maxPerUserPerDay: { type: Number },
184
+ maxPerUserPerWeek: { type: Number },
185
+ minGapDays: { type: Number }
186
+ },
187
+ _id: false,
188
+ default: void 0
189
+ },
181
190
  emailType: { type: String, enum: Object.values(EMAIL_TYPE), default: EMAIL_TYPE.Automated },
182
191
  totalSent: { type: Number, default: 0 },
183
192
  totalSkipped: { type: Number, default: 0 },
@@ -210,6 +219,7 @@ function createEmailRuleSchema(platformValues, audienceValues, collectionPrefix)
210
219
  validFrom: input.validFrom,
211
220
  validTill: input.validTill,
212
221
  bypassThrottle: input.bypassThrottle ?? false,
222
+ throttleOverride: input.throttleOverride,
213
223
  emailType: input.emailType ?? EMAIL_TYPE.Automated,
214
224
  totalSent: 0,
215
225
  totalSkipped: 0
@@ -750,6 +760,7 @@ var UPDATEABLE_FIELDS2 = /* @__PURE__ */ new Set([
750
760
  "autoApprove",
751
761
  "maxPerRun",
752
762
  "bypassThrottle",
763
+ "throttleOverride",
753
764
  "emailType",
754
765
  "validFrom",
755
766
  "validTill"
@@ -883,13 +894,19 @@ var RuleService = class {
883
894
  if (!rule) {
884
895
  throw new RuleNotFoundError(id);
885
896
  }
897
+ const defaultMaxPerRun = this.config.options?.defaultMaxPerRun;
898
+ const effectiveLimit = rule.maxPerRun || defaultMaxPerRun || 500;
886
899
  const target = rule.target;
887
900
  if (target.mode === "list") {
888
901
  const identifiers = target.identifiers || [];
889
- return { matchedCount: identifiers.length, ruleId: id };
902
+ const matchedCount2 = identifiers.length;
903
+ const willProcess2 = Math.min(matchedCount2, effectiveLimit);
904
+ return { matchedCount: matchedCount2, effectiveLimit, willProcess: willProcess2, ruleId: id };
890
905
  }
891
906
  const users = await this.config.adapters.queryUsers(rule.target, 5e4);
892
- return { matchedCount: users.length, ruleId: id };
907
+ const matchedCount = users.length;
908
+ const willProcess = Math.min(matchedCount, effectiveLimit);
909
+ return { matchedCount, effectiveLimit, willProcess, ruleId: id };
893
910
  }
894
911
  async getRunHistory(limit = 20) {
895
912
  return this.EmailRuleRunLog.getRecent(limit);
@@ -1146,6 +1163,9 @@ var RuleRunnerService = class {
1146
1163
  const rawIdentifiers = rule.target.identifiers || [];
1147
1164
  const uniqueEmails = [...new Set(rawIdentifiers.map((e) => e.toLowerCase().trim()).filter(Boolean))];
1148
1165
  const limit = rule.maxPerRun || this.config.options?.defaultMaxPerRun || 500;
1166
+ if (uniqueEmails.length > limit) {
1167
+ this.logger.warn(`Rule "${rule.name}" matched ${uniqueEmails.length} users but maxPerRun is ${limit} \u2014 only ${limit} will be processed`, { ruleId: rule._id.toString(), matchedCount: uniqueEmails.length, maxPerRun: limit });
1168
+ }
1149
1169
  const emailsToProcess = uniqueEmails.slice(0, limit);
1150
1170
  stats.matched = emailsToProcess.length;
1151
1171
  const ruleId = rule._id.toString();
@@ -1344,14 +1364,18 @@ var RuleRunnerService = class {
1344
1364
  return stats;
1345
1365
  }
1346
1366
  async executeQueryMode(rule, template, throttleMap, throttleConfig, stats, runId) {
1367
+ const limit = rule.maxPerRun || this.config.options?.defaultMaxPerRun || 500;
1347
1368
  let users;
1348
1369
  try {
1349
- users = await this.config.adapters.queryUsers(rule.target, rule.maxPerRun || this.config.options?.defaultMaxPerRun || 500);
1370
+ users = await this.config.adapters.queryUsers(rule.target, limit);
1350
1371
  } catch (err) {
1351
1372
  this.logger.error(`Rule "${rule.name}": query failed`, { error: err });
1352
1373
  stats.errorCount = 1;
1353
1374
  return stats;
1354
1375
  }
1376
+ if (users.length > limit) {
1377
+ this.logger.warn(`Rule "${rule.name}" matched ${users.length} users but maxPerRun is ${limit} \u2014 only ${limit} will be processed`, { ruleId: rule._id.toString(), matchedCount: users.length, maxPerRun: limit });
1378
+ }
1355
1379
  stats.matched = users.length;
1356
1380
  this.config.hooks?.onRuleStart?.({ ruleId: rule._id.toString(), ruleName: rule.name, matchedCount: users.length, templateId: rule.templateId.toString(), runId: runId || "" });
1357
1381
  if (users.length === 0) return stats;
@@ -1541,20 +1565,24 @@ var RuleRunnerService = class {
1541
1565
  }
1542
1566
  checkThrottle(rule, userId, email, throttleMap, config, stats, templateId, runId) {
1543
1567
  if (rule.emailType === EMAIL_TYPE.Transactional || rule.bypassThrottle) return true;
1568
+ const overrides = rule.throttleOverride || {};
1569
+ const dailyLimit = overrides.maxPerUserPerDay ?? config.maxPerUserPerDay;
1570
+ const weeklyLimit = overrides.maxPerUserPerWeek ?? config.maxPerUserPerWeek;
1571
+ const minGap = overrides.minGapDays ?? config.minGapDays;
1544
1572
  const userThrottle = throttleMap.get(userId) || { today: 0, thisWeek: 0, lastSentDate: null };
1545
- if (userThrottle.today >= config.maxPerUserPerDay) {
1573
+ if (userThrottle.today >= dailyLimit) {
1546
1574
  stats.skippedByThrottle++;
1547
1575
  this.config.hooks?.onSend?.({ ruleId: rule._id.toString(), ruleName: rule.name, email, status: "throttled", accountId: "", templateId: templateId || "", runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: "daily throttle limit" });
1548
1576
  return false;
1549
1577
  }
1550
- if (userThrottle.thisWeek >= config.maxPerUserPerWeek) {
1578
+ if (userThrottle.thisWeek >= weeklyLimit) {
1551
1579
  stats.skippedByThrottle++;
1552
1580
  this.config.hooks?.onSend?.({ ruleId: rule._id.toString(), ruleName: rule.name, email, status: "throttled", accountId: "", templateId: templateId || "", runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: "weekly throttle limit" });
1553
1581
  return false;
1554
1582
  }
1555
1583
  if (userThrottle.lastSentDate) {
1556
1584
  const daysSinceLastSend = (Date.now() - userThrottle.lastSentDate.getTime()) / MS_PER_DAY;
1557
- if (daysSinceLastSend < config.minGapDays) {
1585
+ if (daysSinceLastSend < minGap) {
1558
1586
  stats.skippedByThrottle++;
1559
1587
  this.config.hooks?.onSend?.({ ruleId: rule._id.toString(), ruleName: rule.name, email, status: "throttled", accountId: "", templateId: templateId || "", runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: "min gap days" });
1560
1588
  return false;