@astralibx/email-rule-engine 12.3.0 → 12.5.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
@@ -95,6 +95,11 @@ interface EmailRule {
95
95
  validFrom?: Date;
96
96
  validTill?: Date;
97
97
  bypassThrottle: boolean;
98
+ throttleOverride?: {
99
+ maxPerUserPerDay?: number;
100
+ maxPerUserPerWeek?: number;
101
+ minGapDays?: number;
102
+ };
98
103
  emailType: EmailType;
99
104
  totalSent: number;
100
105
  totalSkipped: number;
@@ -117,6 +122,11 @@ interface CreateEmailRuleInput {
117
122
  validFrom?: Date;
118
123
  validTill?: Date;
119
124
  bypassThrottle?: boolean;
125
+ throttleOverride?: {
126
+ maxPerUserPerDay?: number;
127
+ maxPerUserPerWeek?: number;
128
+ minGapDays?: number;
129
+ };
120
130
  emailType?: EmailType;
121
131
  }
122
132
  interface UpdateEmailRuleInput {
@@ -134,6 +144,11 @@ interface UpdateEmailRuleInput {
134
144
  validFrom?: Date;
135
145
  validTill?: Date;
136
146
  bypassThrottle?: boolean;
147
+ throttleOverride?: {
148
+ maxPerUserPerDay?: number;
149
+ maxPerUserPerWeek?: number;
150
+ minGapDays?: number;
151
+ };
137
152
  emailType?: EmailType;
138
153
  }
139
154
  interface EmailRuleSend {
@@ -557,6 +572,8 @@ declare class RuleService {
557
572
  toggleActive(id: string): Promise<EmailRuleDocument | null>;
558
573
  dryRun(id: string): Promise<{
559
574
  matchedCount: number;
575
+ effectiveLimit: number;
576
+ willProcess: number;
560
577
  ruleId: string;
561
578
  }>;
562
579
  getRunHistory(limit?: number): Promise<unknown[]>;
package/dist/index.d.ts CHANGED
@@ -95,6 +95,11 @@ interface EmailRule {
95
95
  validFrom?: Date;
96
96
  validTill?: Date;
97
97
  bypassThrottle: boolean;
98
+ throttleOverride?: {
99
+ maxPerUserPerDay?: number;
100
+ maxPerUserPerWeek?: number;
101
+ minGapDays?: number;
102
+ };
98
103
  emailType: EmailType;
99
104
  totalSent: number;
100
105
  totalSkipped: number;
@@ -117,6 +122,11 @@ interface CreateEmailRuleInput {
117
122
  validFrom?: Date;
118
123
  validTill?: Date;
119
124
  bypassThrottle?: boolean;
125
+ throttleOverride?: {
126
+ maxPerUserPerDay?: number;
127
+ maxPerUserPerWeek?: number;
128
+ minGapDays?: number;
129
+ };
120
130
  emailType?: EmailType;
121
131
  }
122
132
  interface UpdateEmailRuleInput {
@@ -134,6 +144,11 @@ interface UpdateEmailRuleInput {
134
144
  validFrom?: Date;
135
145
  validTill?: Date;
136
146
  bypassThrottle?: boolean;
147
+ throttleOverride?: {
148
+ maxPerUserPerDay?: number;
149
+ maxPerUserPerWeek?: number;
150
+ minGapDays?: number;
151
+ };
137
152
  emailType?: EmailType;
138
153
  }
139
154
  interface EmailRuleSend {
@@ -557,6 +572,8 @@ declare class RuleService {
557
572
  toggleActive(id: string): Promise<EmailRuleDocument | null>;
558
573
  dryRun(id: string): Promise<{
559
574
  matchedCount: number;
575
+ effectiveLimit: number;
576
+ willProcess: number;
560
577
  ruleId: string;
561
578
  }>;
562
579
  getRunHistory(limit?: number): Promise<unknown[]>;
package/dist/index.mjs CHANGED
@@ -170,6 +170,15 @@ function createEmailRuleSchema(platformValues, audienceValues, collectionPrefix)
170
170
  validFrom: { type: Date },
171
171
  validTill: { type: Date },
172
172
  bypassThrottle: { type: Boolean, default: false },
173
+ throttleOverride: {
174
+ type: {
175
+ maxPerUserPerDay: { type: Number },
176
+ maxPerUserPerWeek: { type: Number },
177
+ minGapDays: { type: Number }
178
+ },
179
+ _id: false,
180
+ default: void 0
181
+ },
173
182
  emailType: { type: String, enum: Object.values(EMAIL_TYPE), default: EMAIL_TYPE.Automated },
174
183
  totalSent: { type: Number, default: 0 },
175
184
  totalSkipped: { type: Number, default: 0 },
@@ -202,6 +211,7 @@ function createEmailRuleSchema(platformValues, audienceValues, collectionPrefix)
202
211
  validFrom: input.validFrom,
203
212
  validTill: input.validTill,
204
213
  bypassThrottle: input.bypassThrottle ?? false,
214
+ throttleOverride: input.throttleOverride,
205
215
  emailType: input.emailType ?? EMAIL_TYPE.Automated,
206
216
  totalSent: 0,
207
217
  totalSkipped: 0
@@ -742,6 +752,7 @@ var UPDATEABLE_FIELDS2 = /* @__PURE__ */ new Set([
742
752
  "autoApprove",
743
753
  "maxPerRun",
744
754
  "bypassThrottle",
755
+ "throttleOverride",
745
756
  "emailType",
746
757
  "validFrom",
747
758
  "validTill"
@@ -875,13 +886,19 @@ var RuleService = class {
875
886
  if (!rule) {
876
887
  throw new RuleNotFoundError(id);
877
888
  }
889
+ const defaultMaxPerRun = this.config.options?.defaultMaxPerRun;
890
+ const effectiveLimit = rule.maxPerRun || defaultMaxPerRun || 500;
878
891
  const target = rule.target;
879
892
  if (target.mode === "list") {
880
893
  const identifiers = target.identifiers || [];
881
- return { matchedCount: identifiers.length, ruleId: id };
894
+ const matchedCount2 = identifiers.length;
895
+ const willProcess2 = Math.min(matchedCount2, effectiveLimit);
896
+ return { matchedCount: matchedCount2, effectiveLimit, willProcess: willProcess2, ruleId: id };
882
897
  }
883
898
  const users = await this.config.adapters.queryUsers(rule.target, 5e4);
884
- return { matchedCount: users.length, ruleId: id };
899
+ const matchedCount = users.length;
900
+ const willProcess = Math.min(matchedCount, effectiveLimit);
901
+ return { matchedCount, effectiveLimit, willProcess, ruleId: id };
885
902
  }
886
903
  async getRunHistory(limit = 20) {
887
904
  return this.EmailRuleRunLog.getRecent(limit);
@@ -1138,6 +1155,9 @@ var RuleRunnerService = class {
1138
1155
  const rawIdentifiers = rule.target.identifiers || [];
1139
1156
  const uniqueEmails = [...new Set(rawIdentifiers.map((e) => e.toLowerCase().trim()).filter(Boolean))];
1140
1157
  const limit = rule.maxPerRun || this.config.options?.defaultMaxPerRun || 500;
1158
+ if (uniqueEmails.length > limit) {
1159
+ 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 });
1160
+ }
1141
1161
  const emailsToProcess = uniqueEmails.slice(0, limit);
1142
1162
  stats.matched = emailsToProcess.length;
1143
1163
  const ruleId = rule._id.toString();
@@ -1336,14 +1356,18 @@ var RuleRunnerService = class {
1336
1356
  return stats;
1337
1357
  }
1338
1358
  async executeQueryMode(rule, template, throttleMap, throttleConfig, stats, runId) {
1359
+ const limit = rule.maxPerRun || this.config.options?.defaultMaxPerRun || 500;
1339
1360
  let users;
1340
1361
  try {
1341
- users = await this.config.adapters.queryUsers(rule.target, rule.maxPerRun || this.config.options?.defaultMaxPerRun || 500);
1362
+ users = await this.config.adapters.queryUsers(rule.target, limit);
1342
1363
  } catch (err) {
1343
1364
  this.logger.error(`Rule "${rule.name}": query failed`, { error: err });
1344
1365
  stats.errorCount = 1;
1345
1366
  return stats;
1346
1367
  }
1368
+ if (users.length > limit) {
1369
+ 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 });
1370
+ }
1347
1371
  stats.matched = users.length;
1348
1372
  this.config.hooks?.onRuleStart?.({ ruleId: rule._id.toString(), ruleName: rule.name, matchedCount: users.length, templateId: rule.templateId.toString(), runId: runId || "" });
1349
1373
  if (users.length === 0) return stats;
@@ -1533,20 +1557,24 @@ var RuleRunnerService = class {
1533
1557
  }
1534
1558
  checkThrottle(rule, userId, email, throttleMap, config, stats, templateId, runId) {
1535
1559
  if (rule.emailType === EMAIL_TYPE.Transactional || rule.bypassThrottle) return true;
1560
+ const overrides = rule.throttleOverride || {};
1561
+ const dailyLimit = overrides.maxPerUserPerDay ?? config.maxPerUserPerDay;
1562
+ const weeklyLimit = overrides.maxPerUserPerWeek ?? config.maxPerUserPerWeek;
1563
+ const minGap = overrides.minGapDays ?? config.minGapDays;
1536
1564
  const userThrottle = throttleMap.get(userId) || { today: 0, thisWeek: 0, lastSentDate: null };
1537
- if (userThrottle.today >= config.maxPerUserPerDay) {
1565
+ if (userThrottle.today >= dailyLimit) {
1538
1566
  stats.skippedByThrottle++;
1539
1567
  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" });
1540
1568
  return false;
1541
1569
  }
1542
- if (userThrottle.thisWeek >= config.maxPerUserPerWeek) {
1570
+ if (userThrottle.thisWeek >= weeklyLimit) {
1543
1571
  stats.skippedByThrottle++;
1544
1572
  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" });
1545
1573
  return false;
1546
1574
  }
1547
1575
  if (userThrottle.lastSentDate) {
1548
1576
  const daysSinceLastSend = (Date.now() - userThrottle.lastSentDate.getTime()) / MS_PER_DAY;
1549
- if (daysSinceLastSend < config.minGapDays) {
1577
+ if (daysSinceLastSend < minGap) {
1550
1578
  stats.skippedByThrottle++;
1551
1579
  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" });
1552
1580
  return false;