@astralibx/email-rule-engine 12.10.0 → 12.11.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
@@ -128,7 +128,7 @@ interface QueryTarget {
128
128
  role: string;
129
129
  platform: string;
130
130
  conditions: RuleCondition[];
131
- collection?: string;
131
+ collectionName?: string;
132
132
  }
133
133
  interface ListTarget {
134
134
  mode: 'list';
@@ -578,18 +578,25 @@ declare function createEmailRuleRunLogSchema(collectionPrefix?: string): Schema<
578
578
  __v: number;
579
579
  }>;
580
580
 
581
+ interface SendWindowConfig {
582
+ startHour: number;
583
+ endHour: number;
584
+ timezone: string;
585
+ }
581
586
  interface EmailThrottleConfig {
582
587
  _id: string;
583
588
  maxPerUserPerDay: number;
584
589
  maxPerUserPerWeek: number;
585
590
  minGapDays: number;
586
591
  throttleWindow: ThrottleWindow;
592
+ sendWindow?: SendWindowConfig;
587
593
  updatedAt: Date;
588
594
  }
589
595
  interface UpdateEmailThrottleConfigInput {
590
596
  maxPerUserPerDay?: number;
591
597
  maxPerUserPerWeek?: number;
592
598
  minGapDays?: number;
599
+ sendWindow?: SendWindowConfig | null;
593
600
  }
594
601
 
595
602
  interface IEmailThrottleConfig extends Omit<EmailThrottleConfig, '_id'> {
@@ -881,4 +888,4 @@ interface EmailRuleEngine {
881
888
  */
882
889
  declare function createEmailRuleEngine(config: EmailRuleEngineConfig): EmailRuleEngine;
883
890
 
884
- export { type AgentSelection, AlxEmailError, type BeforeSendParams, type BeforeSendResult, type CollectionSchema, type CompiledTemplate, type ConditionValidationError, ConfigValidationError, type CreateEmailRuleInput, type CreateEmailTemplateInput, DuplicateSlugError, EMAIL_SEND_STATUS, EMAIL_TYPE, type EmailAttachment, type EmailRule, type EmailRuleDocument, type EmailRuleEngine, type EmailRuleEngineConfig, type EmailRuleModel, type EmailRuleRunLog, type EmailRuleRunLogDocument, type EmailRuleRunLogModel, type EmailRuleRunLogStatics, type EmailRuleSend, type EmailRuleSendDocument, type EmailRuleSendModel, type EmailRuleSendStatics, type EmailRuleStatics, type EmailSendStatus, type EmailTemplate, type EmailTemplateDocument, type EmailTemplateModel, type EmailTemplateStatics, type EmailThrottleConfig, type EmailThrottleConfigDocument, type EmailThrottleConfigModel, type EmailThrottleConfigStatics, type EmailType, FIELD_TYPE, type FieldDefinition, type FieldType, type FlattenedField, type IEmailRule, type IEmailRuleRunLog, type IEmailRuleSend, type IEmailTemplate, type IEmailThrottleConfig, type JoinDefinition, type ListTarget, LockAcquisitionError, type PerRuleStats, type QueryTarget, RULE_OPERATOR, RUN_LOG_STATUS, RUN_TRIGGER, type RecipientIdentifier, type RenderResult, type RuleCondition, RuleNotFoundError, type RuleOperator, type RuleRunStats, RuleRunnerService, RuleService, type RuleTarget, RuleTemplateIncompatibleError, type RunLogStatus, type RunTrigger, SchedulerService, type SendEmailParams, TARGET_MODE, TEMPLATE_AUDIENCE, TEMPLATE_CATEGORY, THROTTLE_WINDOW, TYPE_OPERATORS, type TargetMode, type TemplateAudience, type TemplateCategory, TemplateNotFoundError, TemplateRenderService, TemplateService, TemplateSyntaxError, type ThrottleWindow, type UpdateEmailRuleInput, type UpdateEmailTemplateInput, type UpdateEmailThrottleConfigInput, createEmailRuleEngine, createEmailRuleRunLogSchema, createEmailRuleSchema, createEmailRuleSendSchema, createEmailTemplateSchema, createEmailThrottleConfigSchema, flattenFields, validateConditions, validateConfig };
891
+ export { type AgentSelection, AlxEmailError, type BeforeSendParams, type BeforeSendResult, type CollectionSchema, type CompiledTemplate, type ConditionValidationError, ConfigValidationError, type CreateEmailRuleInput, type CreateEmailTemplateInput, DuplicateSlugError, EMAIL_SEND_STATUS, EMAIL_TYPE, type EmailAttachment, type EmailRule, type EmailRuleDocument, type EmailRuleEngine, type EmailRuleEngineConfig, type EmailRuleModel, type EmailRuleRunLog, type EmailRuleRunLogDocument, type EmailRuleRunLogModel, type EmailRuleRunLogStatics, type EmailRuleSend, type EmailRuleSendDocument, type EmailRuleSendModel, type EmailRuleSendStatics, type EmailRuleStatics, type EmailSendStatus, type EmailTemplate, type EmailTemplateDocument, type EmailTemplateModel, type EmailTemplateStatics, type EmailThrottleConfig, type EmailThrottleConfigDocument, type EmailThrottleConfigModel, type EmailThrottleConfigStatics, type EmailType, FIELD_TYPE, type FieldDefinition, type FieldType, type FlattenedField, type IEmailRule, type IEmailRuleRunLog, type IEmailRuleSend, type IEmailTemplate, type IEmailThrottleConfig, type JoinDefinition, type ListTarget, LockAcquisitionError, type PerRuleStats, type QueryTarget, RULE_OPERATOR, RUN_LOG_STATUS, RUN_TRIGGER, type RecipientIdentifier, type RenderResult, type RuleCondition, RuleNotFoundError, type RuleOperator, type RuleRunStats, RuleRunnerService, RuleService, type RuleTarget, RuleTemplateIncompatibleError, type RunLogStatus, type RunTrigger, SchedulerService, type SendEmailParams, type SendWindowConfig, TARGET_MODE, TEMPLATE_AUDIENCE, TEMPLATE_CATEGORY, THROTTLE_WINDOW, TYPE_OPERATORS, type TargetMode, type TemplateAudience, type TemplateCategory, TemplateNotFoundError, TemplateRenderService, TemplateService, TemplateSyntaxError, type ThrottleWindow, type UpdateEmailRuleInput, type UpdateEmailTemplateInput, type UpdateEmailThrottleConfigInput, createEmailRuleEngine, createEmailRuleRunLogSchema, createEmailRuleSchema, createEmailRuleSendSchema, createEmailTemplateSchema, createEmailThrottleConfigSchema, flattenFields, validateConditions, validateConfig };
package/dist/index.d.ts CHANGED
@@ -128,7 +128,7 @@ interface QueryTarget {
128
128
  role: string;
129
129
  platform: string;
130
130
  conditions: RuleCondition[];
131
- collection?: string;
131
+ collectionName?: string;
132
132
  }
133
133
  interface ListTarget {
134
134
  mode: 'list';
@@ -578,18 +578,25 @@ declare function createEmailRuleRunLogSchema(collectionPrefix?: string): Schema<
578
578
  __v: number;
579
579
  }>;
580
580
 
581
+ interface SendWindowConfig {
582
+ startHour: number;
583
+ endHour: number;
584
+ timezone: string;
585
+ }
581
586
  interface EmailThrottleConfig {
582
587
  _id: string;
583
588
  maxPerUserPerDay: number;
584
589
  maxPerUserPerWeek: number;
585
590
  minGapDays: number;
586
591
  throttleWindow: ThrottleWindow;
592
+ sendWindow?: SendWindowConfig;
587
593
  updatedAt: Date;
588
594
  }
589
595
  interface UpdateEmailThrottleConfigInput {
590
596
  maxPerUserPerDay?: number;
591
597
  maxPerUserPerWeek?: number;
592
598
  minGapDays?: number;
599
+ sendWindow?: SendWindowConfig | null;
593
600
  }
594
601
 
595
602
  interface IEmailThrottleConfig extends Omit<EmailThrottleConfig, '_id'> {
@@ -881,4 +888,4 @@ interface EmailRuleEngine {
881
888
  */
882
889
  declare function createEmailRuleEngine(config: EmailRuleEngineConfig): EmailRuleEngine;
883
890
 
884
- export { type AgentSelection, AlxEmailError, type BeforeSendParams, type BeforeSendResult, type CollectionSchema, type CompiledTemplate, type ConditionValidationError, ConfigValidationError, type CreateEmailRuleInput, type CreateEmailTemplateInput, DuplicateSlugError, EMAIL_SEND_STATUS, EMAIL_TYPE, type EmailAttachment, type EmailRule, type EmailRuleDocument, type EmailRuleEngine, type EmailRuleEngineConfig, type EmailRuleModel, type EmailRuleRunLog, type EmailRuleRunLogDocument, type EmailRuleRunLogModel, type EmailRuleRunLogStatics, type EmailRuleSend, type EmailRuleSendDocument, type EmailRuleSendModel, type EmailRuleSendStatics, type EmailRuleStatics, type EmailSendStatus, type EmailTemplate, type EmailTemplateDocument, type EmailTemplateModel, type EmailTemplateStatics, type EmailThrottleConfig, type EmailThrottleConfigDocument, type EmailThrottleConfigModel, type EmailThrottleConfigStatics, type EmailType, FIELD_TYPE, type FieldDefinition, type FieldType, type FlattenedField, type IEmailRule, type IEmailRuleRunLog, type IEmailRuleSend, type IEmailTemplate, type IEmailThrottleConfig, type JoinDefinition, type ListTarget, LockAcquisitionError, type PerRuleStats, type QueryTarget, RULE_OPERATOR, RUN_LOG_STATUS, RUN_TRIGGER, type RecipientIdentifier, type RenderResult, type RuleCondition, RuleNotFoundError, type RuleOperator, type RuleRunStats, RuleRunnerService, RuleService, type RuleTarget, RuleTemplateIncompatibleError, type RunLogStatus, type RunTrigger, SchedulerService, type SendEmailParams, TARGET_MODE, TEMPLATE_AUDIENCE, TEMPLATE_CATEGORY, THROTTLE_WINDOW, TYPE_OPERATORS, type TargetMode, type TemplateAudience, type TemplateCategory, TemplateNotFoundError, TemplateRenderService, TemplateService, TemplateSyntaxError, type ThrottleWindow, type UpdateEmailRuleInput, type UpdateEmailTemplateInput, type UpdateEmailThrottleConfigInput, createEmailRuleEngine, createEmailRuleRunLogSchema, createEmailRuleSchema, createEmailRuleSendSchema, createEmailTemplateSchema, createEmailThrottleConfigSchema, flattenFields, validateConditions, validateConfig };
891
+ export { type AgentSelection, AlxEmailError, type BeforeSendParams, type BeforeSendResult, type CollectionSchema, type CompiledTemplate, type ConditionValidationError, ConfigValidationError, type CreateEmailRuleInput, type CreateEmailTemplateInput, DuplicateSlugError, EMAIL_SEND_STATUS, EMAIL_TYPE, type EmailAttachment, type EmailRule, type EmailRuleDocument, type EmailRuleEngine, type EmailRuleEngineConfig, type EmailRuleModel, type EmailRuleRunLog, type EmailRuleRunLogDocument, type EmailRuleRunLogModel, type EmailRuleRunLogStatics, type EmailRuleSend, type EmailRuleSendDocument, type EmailRuleSendModel, type EmailRuleSendStatics, type EmailRuleStatics, type EmailSendStatus, type EmailTemplate, type EmailTemplateDocument, type EmailTemplateModel, type EmailTemplateStatics, type EmailThrottleConfig, type EmailThrottleConfigDocument, type EmailThrottleConfigModel, type EmailThrottleConfigStatics, type EmailType, FIELD_TYPE, type FieldDefinition, type FieldType, type FlattenedField, type IEmailRule, type IEmailRuleRunLog, type IEmailRuleSend, type IEmailTemplate, type IEmailThrottleConfig, type JoinDefinition, type ListTarget, LockAcquisitionError, type PerRuleStats, type QueryTarget, RULE_OPERATOR, RUN_LOG_STATUS, RUN_TRIGGER, type RecipientIdentifier, type RenderResult, type RuleCondition, RuleNotFoundError, type RuleOperator, type RuleRunStats, RuleRunnerService, RuleService, type RuleTarget, RuleTemplateIncompatibleError, type RunLogStatus, type RunTrigger, SchedulerService, type SendEmailParams, type SendWindowConfig, TARGET_MODE, TEMPLATE_AUDIENCE, TEMPLATE_CATEGORY, THROTTLE_WINDOW, TYPE_OPERATORS, type TargetMode, type TemplateAudience, type TemplateCategory, TemplateNotFoundError, TemplateRenderService, TemplateService, TemplateSyntaxError, type ThrottleWindow, type UpdateEmailRuleInput, type UpdateEmailTemplateInput, type UpdateEmailThrottleConfigInput, createEmailRuleEngine, createEmailRuleRunLogSchema, createEmailRuleSchema, createEmailRuleSendSchema, createEmailTemplateSchema, createEmailThrottleConfigSchema, flattenFields, validateConditions, validateConfig };
package/dist/index.mjs CHANGED
@@ -198,7 +198,7 @@ function createEmailRuleSchema(platformValues, audienceValues, collectionPrefix)
198
198
  },
199
199
  conditions: [RuleConditionSchema],
200
200
  identifiers: [{ type: String }],
201
- collection: { type: String }
201
+ collectionName: { type: String }
202
202
  }, { _id: false });
203
203
  const RuleRunStatsSchema = createRunStatsSchema();
204
204
  const schema = new Schema(
@@ -368,7 +368,16 @@ function createEmailThrottleConfigSchema(collectionPrefix) {
368
368
  maxPerUserPerDay: { type: Number, default: 1 },
369
369
  maxPerUserPerWeek: { type: Number, default: 2 },
370
370
  minGapDays: { type: Number, default: 3 },
371
- throttleWindow: { type: String, enum: Object.values(THROTTLE_WINDOW), default: THROTTLE_WINDOW.Rolling }
371
+ throttleWindow: { type: String, enum: Object.values(THROTTLE_WINDOW), default: THROTTLE_WINDOW.Rolling },
372
+ sendWindow: {
373
+ type: {
374
+ startHour: { type: Number, min: 0, max: 23 },
375
+ endHour: { type: Number, min: 0, max: 23 },
376
+ timezone: { type: String }
377
+ },
378
+ _id: false,
379
+ default: void 0
380
+ }
372
381
  },
373
382
  {
374
383
  timestamps: true,
@@ -1086,8 +1095,8 @@ var RuleService = class {
1086
1095
  throw new RuleTemplateIncompatibleError("target.identifiers must be a non-empty array for list mode, validation failed");
1087
1096
  }
1088
1097
  }
1089
- if (isQueryTarget(input.target) && input.target.collection && this.config.collections?.length) {
1090
- const condErrors = validateConditions(input.target.conditions, input.target.collection, this.config.collections);
1098
+ if (isQueryTarget(input.target) && input.target.collectionName && this.config.collections?.length) {
1099
+ const condErrors = validateConditions(input.target.conditions, input.target.collectionName, this.config.collections);
1091
1100
  if (condErrors.length > 0) {
1092
1101
  throw new RuleTemplateIncompatibleError(
1093
1102
  `Invalid conditions: ${condErrors.map((e) => e.message).join("; ")}`
@@ -1125,8 +1134,8 @@ var RuleService = class {
1125
1134
  }
1126
1135
  if (isQueryTarget(effectiveTarget)) {
1127
1136
  const qt = effectiveTarget;
1128
- if (qt.collection && this.config.collections?.length) {
1129
- const condErrors = validateConditions(qt.conditions || [], qt.collection, this.config.collections);
1137
+ if (qt.collectionName && this.config.collections?.length) {
1138
+ const condErrors = validateConditions(qt.conditions || [], qt.collectionName, this.config.collections);
1130
1139
  if (condErrors.length > 0) {
1131
1140
  throw new RuleTemplateIncompatibleError(
1132
1141
  `Invalid conditions: ${condErrors.map((e) => e.message).join("; ")}`
@@ -1184,7 +1193,7 @@ var RuleService = class {
1184
1193
  return { matchedCount: matchedCount2, effectiveLimit, willProcess: willProcess2, ruleId: id, sample: sample2 };
1185
1194
  }
1186
1195
  const queryTarget = rule.target;
1187
- const collectionName = queryTarget.collection;
1196
+ const collectionName = queryTarget.collectionName;
1188
1197
  const collectionSchema = collectionName ? this.config.collections?.find((c) => c.name === collectionName) : void 0;
1189
1198
  const users = await this.config.adapters.queryUsers(rule.target, 5e4, collectionSchema ? { collectionSchema } : void 0);
1190
1199
  const matchedCount = users.length;
@@ -1272,16 +1281,6 @@ var RuleRunnerService = class {
1272
1281
  async runAllRules(triggeredBy = RUN_TRIGGER.Cron, runId) {
1273
1282
  if (!runId) runId = crypto.randomUUID();
1274
1283
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
1275
- if (this.config.options?.sendWindow) {
1276
- const { startHour, endHour, timezone } = this.config.options.sendWindow;
1277
- const now = /* @__PURE__ */ new Date();
1278
- const formatter = new Intl.DateTimeFormat("en-US", { hour: "numeric", hour12: false, timeZone: timezone });
1279
- const currentHour = parseInt(formatter.format(now), 10);
1280
- if (currentHour < startHour || currentHour >= endHour) {
1281
- this.logger.info("Outside send window, skipping run", { currentHour, startHour, endHour, timezone });
1282
- return { runId };
1283
- }
1284
- }
1285
1284
  const lockAcquired = await this.lock.acquire();
1286
1285
  if (!lockAcquired) {
1287
1286
  this.logger.warn("Rule runner already executing, skipping");
@@ -1300,9 +1299,20 @@ var RuleRunnerService = class {
1300
1299
  let runStatus = "completed";
1301
1300
  try {
1302
1301
  const throttleConfig = await this.EmailThrottleConfig.getConfig();
1302
+ const sendWindow = throttleConfig.sendWindow ?? this.config.options?.sendWindow;
1303
+ if (sendWindow) {
1304
+ const { startHour, endHour, timezone } = sendWindow;
1305
+ const now2 = /* @__PURE__ */ new Date();
1306
+ const formatter = new Intl.DateTimeFormat("en-US", { hour: "numeric", hour12: false, timeZone: timezone });
1307
+ const currentHour = parseInt(formatter.format(now2), 10);
1308
+ if (currentHour < startHour || currentHour >= endHour) {
1309
+ this.logger.info("Outside send window, skipping run", { currentHour, startHour, endHour, timezone });
1310
+ return { runId };
1311
+ }
1312
+ }
1303
1313
  const allActiveRules = await this.EmailRule.findActive();
1304
1314
  const now = /* @__PURE__ */ new Date();
1305
- const tz = this.config.options?.sendWindow?.timezone;
1315
+ const tz = sendWindow?.timezone ?? this.config.options?.sendWindow?.timezone;
1306
1316
  const activeRules = allActiveRules.filter((rule) => {
1307
1317
  if (rule.validFrom) {
1308
1318
  const localNow = getLocalDate(now, tz);
@@ -1703,7 +1713,7 @@ var RuleRunnerService = class {
1703
1713
  const limit = rule.maxPerRun || this.config.options?.defaultMaxPerRun || 500;
1704
1714
  let users;
1705
1715
  try {
1706
- const collectionName = rule.target?.collection;
1716
+ const collectionName = rule.target?.collectionName;
1707
1717
  const collectionSchema = collectionName ? this.config.collections?.find((c) => c.name === collectionName) : void 0;
1708
1718
  users = await this.config.adapters.queryUsers(rule.target, limit, collectionSchema ? { collectionSchema } : void 0);
1709
1719
  } catch (err) {
@@ -2204,7 +2214,7 @@ function createSettingsController(EmailThrottleConfig) {
2204
2214
  res.json({ success: true, data: { config } });
2205
2215
  });
2206
2216
  const updateThrottleConfig = asyncHandler(async (req, res) => {
2207
- const { maxPerUserPerDay, maxPerUserPerWeek, minGapDays } = req.body;
2217
+ const { maxPerUserPerDay, maxPerUserPerWeek, minGapDays, sendWindow } = req.body;
2208
2218
  const updates = {};
2209
2219
  if (maxPerUserPerDay !== void 0) {
2210
2220
  if (!Number.isInteger(maxPerUserPerDay) || maxPerUserPerDay < 1) {
@@ -2224,6 +2234,23 @@ function createSettingsController(EmailThrottleConfig) {
2224
2234
  }
2225
2235
  updates.minGapDays = minGapDays;
2226
2236
  }
2237
+ if (sendWindow !== void 0) {
2238
+ if (sendWindow === null) {
2239
+ updates.sendWindow = void 0;
2240
+ } else {
2241
+ const { startHour, endHour, timezone } = sendWindow;
2242
+ if (!Number.isInteger(startHour) || startHour < 0 || startHour > 23) {
2243
+ return res.status(400).json({ success: false, error: "sendWindow.startHour must be an integer 0-23" });
2244
+ }
2245
+ if (!Number.isInteger(endHour) || endHour < 0 || endHour > 23) {
2246
+ return res.status(400).json({ success: false, error: "sendWindow.endHour must be an integer 0-23" });
2247
+ }
2248
+ if (typeof timezone !== "string" || !timezone.trim()) {
2249
+ return res.status(400).json({ success: false, error: "sendWindow.timezone must be a non-empty string" });
2250
+ }
2251
+ updates.sendWindow = { startHour, endHour, timezone: timezone.trim() };
2252
+ }
2253
+ }
2227
2254
  if (Object.keys(updates).length === 0) {
2228
2255
  return res.status(400).json({ success: false, error: "No valid fields to update" });
2229
2256
  }
@@ -2233,9 +2260,21 @@ function createSettingsController(EmailThrottleConfig) {
2233
2260
  if (finalWeekly < finalDaily) {
2234
2261
  return res.status(400).json({ success: false, error: "maxPerUserPerWeek must be >= maxPerUserPerDay" });
2235
2262
  }
2263
+ const setFields = {};
2264
+ const unsetFields = {};
2265
+ for (const [key, value] of Object.entries(updates)) {
2266
+ if (value === void 0) {
2267
+ unsetFields[key] = "";
2268
+ } else {
2269
+ setFields[key] = value;
2270
+ }
2271
+ }
2272
+ const updateOp = {};
2273
+ if (Object.keys(setFields).length > 0) updateOp["$set"] = setFields;
2274
+ if (Object.keys(unsetFields).length > 0) updateOp["$unset"] = unsetFields;
2236
2275
  const updated = await EmailThrottleConfig.findByIdAndUpdate(
2237
2276
  config._id,
2238
- { $set: updates },
2277
+ updateOp,
2239
2278
  { new: true }
2240
2279
  );
2241
2280
  res.json({ success: true, data: { config: updated } });