@astralibx/email-rule-engine 12.2.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/README.md +20 -0
- package/dist/index.cjs +34 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.mjs +34 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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,
|
|
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 >=
|
|
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 >=
|
|
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 <
|
|
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;
|