@astralibx/email-rule-engine 12.10.1 → 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.cjs CHANGED
@@ -375,7 +375,16 @@ function createEmailThrottleConfigSchema(collectionPrefix) {
375
375
  maxPerUserPerDay: { type: Number, default: 1 },
376
376
  maxPerUserPerWeek: { type: Number, default: 2 },
377
377
  minGapDays: { type: Number, default: 3 },
378
- throttleWindow: { type: String, enum: Object.values(THROTTLE_WINDOW), default: THROTTLE_WINDOW.Rolling }
378
+ throttleWindow: { type: String, enum: Object.values(THROTTLE_WINDOW), default: THROTTLE_WINDOW.Rolling },
379
+ sendWindow: {
380
+ type: {
381
+ startHour: { type: Number, min: 0, max: 23 },
382
+ endHour: { type: Number, min: 0, max: 23 },
383
+ timezone: { type: String }
384
+ },
385
+ _id: false,
386
+ default: void 0
387
+ }
379
388
  },
380
389
  {
381
390
  timestamps: true,
@@ -1279,16 +1288,6 @@ var RuleRunnerService = class {
1279
1288
  async runAllRules(triggeredBy = RUN_TRIGGER.Cron, runId) {
1280
1289
  if (!runId) runId = crypto__default.default.randomUUID();
1281
1290
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
1282
- if (this.config.options?.sendWindow) {
1283
- const { startHour, endHour, timezone } = this.config.options.sendWindow;
1284
- const now = /* @__PURE__ */ new Date();
1285
- const formatter = new Intl.DateTimeFormat("en-US", { hour: "numeric", hour12: false, timeZone: timezone });
1286
- const currentHour = parseInt(formatter.format(now), 10);
1287
- if (currentHour < startHour || currentHour >= endHour) {
1288
- this.logger.info("Outside send window, skipping run", { currentHour, startHour, endHour, timezone });
1289
- return { runId };
1290
- }
1291
- }
1292
1291
  const lockAcquired = await this.lock.acquire();
1293
1292
  if (!lockAcquired) {
1294
1293
  this.logger.warn("Rule runner already executing, skipping");
@@ -1307,9 +1306,20 @@ var RuleRunnerService = class {
1307
1306
  let runStatus = "completed";
1308
1307
  try {
1309
1308
  const throttleConfig = await this.EmailThrottleConfig.getConfig();
1309
+ const sendWindow = throttleConfig.sendWindow ?? this.config.options?.sendWindow;
1310
+ if (sendWindow) {
1311
+ const { startHour, endHour, timezone } = sendWindow;
1312
+ const now2 = /* @__PURE__ */ new Date();
1313
+ const formatter = new Intl.DateTimeFormat("en-US", { hour: "numeric", hour12: false, timeZone: timezone });
1314
+ const currentHour = parseInt(formatter.format(now2), 10);
1315
+ if (currentHour < startHour || currentHour >= endHour) {
1316
+ this.logger.info("Outside send window, skipping run", { currentHour, startHour, endHour, timezone });
1317
+ return { runId };
1318
+ }
1319
+ }
1310
1320
  const allActiveRules = await this.EmailRule.findActive();
1311
1321
  const now = /* @__PURE__ */ new Date();
1312
- const tz = this.config.options?.sendWindow?.timezone;
1322
+ const tz = sendWindow?.timezone ?? this.config.options?.sendWindow?.timezone;
1313
1323
  const activeRules = allActiveRules.filter((rule) => {
1314
1324
  if (rule.validFrom) {
1315
1325
  const localNow = getLocalDate(now, tz);
@@ -2211,7 +2221,7 @@ function createSettingsController(EmailThrottleConfig) {
2211
2221
  res.json({ success: true, data: { config } });
2212
2222
  });
2213
2223
  const updateThrottleConfig = asyncHandler(async (req, res) => {
2214
- const { maxPerUserPerDay, maxPerUserPerWeek, minGapDays } = req.body;
2224
+ const { maxPerUserPerDay, maxPerUserPerWeek, minGapDays, sendWindow } = req.body;
2215
2225
  const updates = {};
2216
2226
  if (maxPerUserPerDay !== void 0) {
2217
2227
  if (!Number.isInteger(maxPerUserPerDay) || maxPerUserPerDay < 1) {
@@ -2231,6 +2241,23 @@ function createSettingsController(EmailThrottleConfig) {
2231
2241
  }
2232
2242
  updates.minGapDays = minGapDays;
2233
2243
  }
2244
+ if (sendWindow !== void 0) {
2245
+ if (sendWindow === null) {
2246
+ updates.sendWindow = void 0;
2247
+ } else {
2248
+ const { startHour, endHour, timezone } = sendWindow;
2249
+ if (!Number.isInteger(startHour) || startHour < 0 || startHour > 23) {
2250
+ return res.status(400).json({ success: false, error: "sendWindow.startHour must be an integer 0-23" });
2251
+ }
2252
+ if (!Number.isInteger(endHour) || endHour < 0 || endHour > 23) {
2253
+ return res.status(400).json({ success: false, error: "sendWindow.endHour must be an integer 0-23" });
2254
+ }
2255
+ if (typeof timezone !== "string" || !timezone.trim()) {
2256
+ return res.status(400).json({ success: false, error: "sendWindow.timezone must be a non-empty string" });
2257
+ }
2258
+ updates.sendWindow = { startHour, endHour, timezone: timezone.trim() };
2259
+ }
2260
+ }
2234
2261
  if (Object.keys(updates).length === 0) {
2235
2262
  return res.status(400).json({ success: false, error: "No valid fields to update" });
2236
2263
  }
@@ -2240,9 +2267,21 @@ function createSettingsController(EmailThrottleConfig) {
2240
2267
  if (finalWeekly < finalDaily) {
2241
2268
  return res.status(400).json({ success: false, error: "maxPerUserPerWeek must be >= maxPerUserPerDay" });
2242
2269
  }
2270
+ const setFields = {};
2271
+ const unsetFields = {};
2272
+ for (const [key, value] of Object.entries(updates)) {
2273
+ if (value === void 0) {
2274
+ unsetFields[key] = "";
2275
+ } else {
2276
+ setFields[key] = value;
2277
+ }
2278
+ }
2279
+ const updateOp = {};
2280
+ if (Object.keys(setFields).length > 0) updateOp["$set"] = setFields;
2281
+ if (Object.keys(unsetFields).length > 0) updateOp["$unset"] = unsetFields;
2243
2282
  const updated = await EmailThrottleConfig.findByIdAndUpdate(
2244
2283
  config._id,
2245
- { $set: updates },
2284
+ updateOp,
2246
2285
  { new: true }
2247
2286
  );
2248
2287
  res.json({ success: true, data: { config: updated } });