@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 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
  }