@astralibx/email-rule-engine 12.0.1 → 12.2.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 +39 -29
- 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 +39 -29
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1022,7 +1022,7 @@ var RuleRunnerService = class {
|
|
|
1022
1022
|
}
|
|
1023
1023
|
return true;
|
|
1024
1024
|
});
|
|
1025
|
-
this.config.hooks?.onRunStart?.({ rulesCount: activeRules.length, triggeredBy });
|
|
1025
|
+
this.config.hooks?.onRunStart?.({ rulesCount: activeRules.length, triggeredBy, runId });
|
|
1026
1026
|
await this.updateRunProgress(runId, {
|
|
1027
1027
|
progress: { rulesTotal: activeRules.length, rulesCompleted: 0, sent: 0, failed: 0, skipped: 0, invalid: 0 }
|
|
1028
1028
|
});
|
|
@@ -1111,7 +1111,7 @@ var RuleRunnerService = class {
|
|
|
1111
1111
|
status: runStatus
|
|
1112
1112
|
});
|
|
1113
1113
|
await this.updateRunProgress(runId, { status: runStatus, currentRule: "", elapsed: Date.now() - runStartTime });
|
|
1114
|
-
this.config.hooks?.onRunComplete?.({ duration: Date.now() - runStartTime, totalStats, perRuleStats });
|
|
1114
|
+
this.config.hooks?.onRunComplete?.({ duration: Date.now() - runStartTime, totalStats, perRuleStats, runId });
|
|
1115
1115
|
this.logger.info("Rule run completed", {
|
|
1116
1116
|
triggeredBy,
|
|
1117
1117
|
rulesProcessed: activeRules.length,
|
|
@@ -1150,7 +1150,7 @@ var RuleRunnerService = class {
|
|
|
1150
1150
|
stats.matched = emailsToProcess.length;
|
|
1151
1151
|
const ruleId = rule._id.toString();
|
|
1152
1152
|
const templateId = rule.templateId.toString();
|
|
1153
|
-
this.config.hooks?.onRuleStart?.({ ruleId, ruleName: rule.name, matchedCount: emailsToProcess.length });
|
|
1153
|
+
this.config.hooks?.onRuleStart?.({ ruleId, ruleName: rule.name, matchedCount: emailsToProcess.length, templateId, runId: runId || "" });
|
|
1154
1154
|
if (emailsToProcess.length === 0) return stats;
|
|
1155
1155
|
const identifierResults = await processInChunks(
|
|
1156
1156
|
emailsToProcess,
|
|
@@ -1195,7 +1195,7 @@ var RuleRunnerService = class {
|
|
|
1195
1195
|
const identifier = identifierMap.get(email);
|
|
1196
1196
|
if (!identifier) {
|
|
1197
1197
|
stats.skipped++;
|
|
1198
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "invalid" });
|
|
1198
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "invalid", accountId: "", templateId, runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: "invalid email" });
|
|
1199
1199
|
continue;
|
|
1200
1200
|
}
|
|
1201
1201
|
const dedupKey = identifier.id;
|
|
@@ -1203,27 +1203,27 @@ var RuleRunnerService = class {
|
|
|
1203
1203
|
if (lastSend) {
|
|
1204
1204
|
if (rule.sendOnce && !rule.resendAfterDays) {
|
|
1205
1205
|
stats.skipped++;
|
|
1206
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped" });
|
|
1206
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped", accountId: "", templateId, runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: "send once" });
|
|
1207
1207
|
continue;
|
|
1208
1208
|
}
|
|
1209
1209
|
if (rule.resendAfterDays) {
|
|
1210
1210
|
const daysSince = (Date.now() - new Date(lastSend.sentAt).getTime()) / MS_PER_DAY;
|
|
1211
1211
|
if (daysSince < rule.resendAfterDays) {
|
|
1212
1212
|
stats.skipped++;
|
|
1213
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped" });
|
|
1213
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped", accountId: "", templateId, runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: "resend too soon" });
|
|
1214
1214
|
continue;
|
|
1215
1215
|
}
|
|
1216
1216
|
} else {
|
|
1217
1217
|
stats.skipped++;
|
|
1218
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped" });
|
|
1218
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped", accountId: "", templateId, runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: "send once" });
|
|
1219
1219
|
continue;
|
|
1220
1220
|
}
|
|
1221
1221
|
}
|
|
1222
|
-
if (!this.checkThrottle(rule, dedupKey, email, throttleMap, throttleConfig, stats)) continue;
|
|
1222
|
+
if (!this.checkThrottle(rule, dedupKey, email, throttleMap, throttleConfig, stats, templateId, runId)) continue;
|
|
1223
1223
|
const agentSelection = await this.config.adapters.selectAgent(identifier.id, { ruleId, templateId });
|
|
1224
1224
|
if (!agentSelection) {
|
|
1225
1225
|
stats.skipped++;
|
|
1226
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped" });
|
|
1226
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped", accountId: "", templateId, runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: "no account available" });
|
|
1227
1227
|
continue;
|
|
1228
1228
|
}
|
|
1229
1229
|
const user = { _id: identifier.id, email };
|
|
@@ -1261,6 +1261,11 @@ var RuleRunnerService = class {
|
|
|
1261
1261
|
id: dedupKey,
|
|
1262
1262
|
email,
|
|
1263
1263
|
name: ""
|
|
1264
|
+
},
|
|
1265
|
+
context: {
|
|
1266
|
+
ruleId,
|
|
1267
|
+
templateId,
|
|
1268
|
+
runId: runId || ""
|
|
1264
1269
|
}
|
|
1265
1270
|
});
|
|
1266
1271
|
finalHtml = modified.htmlBody;
|
|
@@ -1269,7 +1274,7 @@ var RuleRunnerService = class {
|
|
|
1269
1274
|
} catch (hookErr) {
|
|
1270
1275
|
this.logger.error(`beforeSend hook failed for email ${email}: ${hookErr.message}`);
|
|
1271
1276
|
stats.errorCount++;
|
|
1272
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "error" });
|
|
1277
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "error", accountId: agentSelection.accountId, templateId, runId: runId || "", subjectIndex: si, bodyIndex: bi, failureReason: hookErr.message });
|
|
1273
1278
|
continue;
|
|
1274
1279
|
}
|
|
1275
1280
|
}
|
|
@@ -1297,7 +1302,7 @@ var RuleRunnerService = class {
|
|
|
1297
1302
|
lastSentDate: /* @__PURE__ */ new Date()
|
|
1298
1303
|
});
|
|
1299
1304
|
stats.sent++;
|
|
1300
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "sent" });
|
|
1305
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "sent", accountId: agentSelection.accountId, templateId, runId: runId || "", subjectIndex: si, bodyIndex: bi });
|
|
1301
1306
|
totalProcessed++;
|
|
1302
1307
|
if (runId && totalProcessed % 10 === 0) {
|
|
1303
1308
|
await this.updateRunSendProgress(runId, stats);
|
|
@@ -1312,7 +1317,7 @@ var RuleRunnerService = class {
|
|
|
1312
1317
|
}
|
|
1313
1318
|
} catch (err) {
|
|
1314
1319
|
stats.errorCount++;
|
|
1315
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "error" });
|
|
1320
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "error", accountId: "", templateId, runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: err.message || "unknown error" });
|
|
1316
1321
|
this.logger.error(`Rule "${rule.name}" failed for identifier ${email}`, { error: err });
|
|
1317
1322
|
}
|
|
1318
1323
|
}
|
|
@@ -1335,7 +1340,7 @@ var RuleRunnerService = class {
|
|
|
1335
1340
|
this.logger.info(`Rule '${rule.name}' auto-disabled \u2014 all identifiers processed`);
|
|
1336
1341
|
}
|
|
1337
1342
|
}
|
|
1338
|
-
this.config.hooks?.onRuleComplete?.({ ruleId, ruleName: rule.name, stats });
|
|
1343
|
+
this.config.hooks?.onRuleComplete?.({ ruleId, ruleName: rule.name, stats, templateId, runId: runId || "" });
|
|
1339
1344
|
return stats;
|
|
1340
1345
|
}
|
|
1341
1346
|
async executeQueryMode(rule, template, throttleMap, throttleConfig, stats, runId) {
|
|
@@ -1348,7 +1353,7 @@ var RuleRunnerService = class {
|
|
|
1348
1353
|
return stats;
|
|
1349
1354
|
}
|
|
1350
1355
|
stats.matched = users.length;
|
|
1351
|
-
this.config.hooks?.onRuleStart?.({ ruleId: rule._id.toString(), ruleName: rule.name, matchedCount: users.length });
|
|
1356
|
+
this.config.hooks?.onRuleStart?.({ ruleId: rule._id.toString(), ruleName: rule.name, matchedCount: users.length, templateId: rule.templateId.toString(), runId: runId || "" });
|
|
1352
1357
|
if (users.length === 0) return stats;
|
|
1353
1358
|
const userIds = users.map((u) => u._id?.toString()).filter(Boolean);
|
|
1354
1359
|
const emails = users.map((u) => u.email).filter(Boolean);
|
|
@@ -1397,40 +1402,40 @@ var RuleRunnerService = class {
|
|
|
1397
1402
|
const email = user.email;
|
|
1398
1403
|
if (!userId || !email) {
|
|
1399
1404
|
stats.skipped++;
|
|
1400
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email: email || "unknown", status: "invalid" });
|
|
1405
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email: email || "unknown", status: "invalid", accountId: "", templateId, runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: "invalid email" });
|
|
1401
1406
|
continue;
|
|
1402
1407
|
}
|
|
1403
1408
|
const lastSend = sendMap.get(userId);
|
|
1404
1409
|
if (lastSend) {
|
|
1405
1410
|
if (rule.sendOnce && !rule.resendAfterDays) {
|
|
1406
1411
|
stats.skipped++;
|
|
1407
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped" });
|
|
1412
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped", accountId: "", templateId, runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: "send once" });
|
|
1408
1413
|
continue;
|
|
1409
1414
|
}
|
|
1410
1415
|
if (rule.resendAfterDays) {
|
|
1411
1416
|
const daysSince = (Date.now() - new Date(lastSend.sentAt).getTime()) / MS_PER_DAY;
|
|
1412
1417
|
if (daysSince < rule.resendAfterDays) {
|
|
1413
1418
|
stats.skipped++;
|
|
1414
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped" });
|
|
1419
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped", accountId: "", templateId, runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: "resend too soon" });
|
|
1415
1420
|
continue;
|
|
1416
1421
|
}
|
|
1417
1422
|
} else {
|
|
1418
1423
|
stats.skipped++;
|
|
1419
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped" });
|
|
1424
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped", accountId: "", templateId, runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: "send once" });
|
|
1420
1425
|
continue;
|
|
1421
1426
|
}
|
|
1422
1427
|
}
|
|
1423
1428
|
const identifier = identifierMap.get(email.toLowerCase().trim());
|
|
1424
1429
|
if (!identifier) {
|
|
1425
1430
|
stats.skipped++;
|
|
1426
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "invalid" });
|
|
1431
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "invalid", accountId: "", templateId, runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: "invalid email" });
|
|
1427
1432
|
continue;
|
|
1428
1433
|
}
|
|
1429
|
-
if (!this.checkThrottle(rule, userId, email, throttleMap, throttleConfig, stats)) continue;
|
|
1434
|
+
if (!this.checkThrottle(rule, userId, email, throttleMap, throttleConfig, stats, templateId, runId)) continue;
|
|
1430
1435
|
const agentSelection = await this.config.adapters.selectAgent(identifier.id, { ruleId, templateId });
|
|
1431
1436
|
if (!agentSelection) {
|
|
1432
1437
|
stats.skipped++;
|
|
1433
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped" });
|
|
1438
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "skipped", accountId: "", templateId, runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: "no account available" });
|
|
1434
1439
|
continue;
|
|
1435
1440
|
}
|
|
1436
1441
|
const resolvedData = this.config.adapters.resolveData(user);
|
|
@@ -1467,6 +1472,11 @@ var RuleRunnerService = class {
|
|
|
1467
1472
|
id: String(userId),
|
|
1468
1473
|
email,
|
|
1469
1474
|
name: String(user.name || user.firstName || "")
|
|
1475
|
+
},
|
|
1476
|
+
context: {
|
|
1477
|
+
ruleId,
|
|
1478
|
+
templateId,
|
|
1479
|
+
runId: runId || ""
|
|
1470
1480
|
}
|
|
1471
1481
|
});
|
|
1472
1482
|
finalHtml = modified.htmlBody;
|
|
@@ -1475,7 +1485,7 @@ var RuleRunnerService = class {
|
|
|
1475
1485
|
} catch (hookErr) {
|
|
1476
1486
|
this.logger.error(`beforeSend hook failed for email ${email}: ${hookErr.message}`);
|
|
1477
1487
|
stats.errorCount++;
|
|
1478
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "error" });
|
|
1488
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "error", accountId: agentSelection.accountId, templateId, runId: runId || "", subjectIndex: si, bodyIndex: bi, failureReason: hookErr.message });
|
|
1479
1489
|
continue;
|
|
1480
1490
|
}
|
|
1481
1491
|
}
|
|
@@ -1503,7 +1513,7 @@ var RuleRunnerService = class {
|
|
|
1503
1513
|
lastSentDate: /* @__PURE__ */ new Date()
|
|
1504
1514
|
});
|
|
1505
1515
|
stats.sent++;
|
|
1506
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "sent" });
|
|
1516
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email, status: "sent", accountId: agentSelection.accountId, templateId, runId: runId || "", subjectIndex: si, bodyIndex: bi });
|
|
1507
1517
|
totalProcessed++;
|
|
1508
1518
|
if (runId && totalProcessed % 10 === 0) {
|
|
1509
1519
|
await this.updateRunSendProgress(runId, stats);
|
|
@@ -1518,7 +1528,7 @@ var RuleRunnerService = class {
|
|
|
1518
1528
|
}
|
|
1519
1529
|
} catch (err) {
|
|
1520
1530
|
stats.errorCount++;
|
|
1521
|
-
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email: user.email || "unknown", status: "error" });
|
|
1531
|
+
this.config.hooks?.onSend?.({ ruleId, ruleName: rule.name, email: user.email || "unknown", status: "error", accountId: "", templateId, runId: runId || "", subjectIndex: -1, bodyIndex: -1, failureReason: err.message || "unknown error" });
|
|
1522
1532
|
this.logger.error(`Rule "${rule.name}" failed for user ${user._id?.toString()}`, { error: err });
|
|
1523
1533
|
}
|
|
1524
1534
|
}
|
|
@@ -1526,27 +1536,27 @@ var RuleRunnerService = class {
|
|
|
1526
1536
|
$set: { lastRunAt: /* @__PURE__ */ new Date(), lastRunStats: stats },
|
|
1527
1537
|
$inc: { totalSent: stats.sent, totalSkipped: stats.skipped }
|
|
1528
1538
|
});
|
|
1529
|
-
this.config.hooks?.onRuleComplete?.({ ruleId, ruleName: rule.name, stats });
|
|
1539
|
+
this.config.hooks?.onRuleComplete?.({ ruleId, ruleName: rule.name, stats, templateId, runId: runId || "" });
|
|
1530
1540
|
return stats;
|
|
1531
1541
|
}
|
|
1532
|
-
checkThrottle(rule, userId, email, throttleMap, config, stats) {
|
|
1542
|
+
checkThrottle(rule, userId, email, throttleMap, config, stats, templateId, runId) {
|
|
1533
1543
|
if (rule.emailType === EMAIL_TYPE.Transactional || rule.bypassThrottle) return true;
|
|
1534
1544
|
const userThrottle = throttleMap.get(userId) || { today: 0, thisWeek: 0, lastSentDate: null };
|
|
1535
1545
|
if (userThrottle.today >= config.maxPerUserPerDay) {
|
|
1536
1546
|
stats.skippedByThrottle++;
|
|
1537
|
-
this.config.hooks?.onSend?.({ ruleId: rule._id.toString(), ruleName: rule.name, email, status: "throttled" });
|
|
1547
|
+
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" });
|
|
1538
1548
|
return false;
|
|
1539
1549
|
}
|
|
1540
1550
|
if (userThrottle.thisWeek >= config.maxPerUserPerWeek) {
|
|
1541
1551
|
stats.skippedByThrottle++;
|
|
1542
|
-
this.config.hooks?.onSend?.({ ruleId: rule._id.toString(), ruleName: rule.name, email, status: "throttled" });
|
|
1552
|
+
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" });
|
|
1543
1553
|
return false;
|
|
1544
1554
|
}
|
|
1545
1555
|
if (userThrottle.lastSentDate) {
|
|
1546
1556
|
const daysSinceLastSend = (Date.now() - userThrottle.lastSentDate.getTime()) / MS_PER_DAY;
|
|
1547
1557
|
if (daysSinceLastSend < config.minGapDays) {
|
|
1548
1558
|
stats.skippedByThrottle++;
|
|
1549
|
-
this.config.hooks?.onSend?.({ ruleId: rule._id.toString(), ruleName: rule.name, email, status: "throttled" });
|
|
1559
|
+
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" });
|
|
1550
1560
|
return false;
|
|
1551
1561
|
}
|
|
1552
1562
|
}
|