@seedcord/services 0.7.1 → 0.8.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 CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/seedcord/seedcord/main/assets/banner.png" alt="seedcord" width="100%" />
2
+ <img src="https://cdn.seedcord.org/assets/banner.webp" alt="seedcord" width="100%" />
3
3
  </p>
4
4
 
5
5
  ---
package/dist/index.cjs CHANGED
@@ -1,22 +1,128 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_SeedcordError = require('./SeedcordError-C1GYc1BF.cjs');
3
- let envapt = require("envapt");
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
+ key = keys[i];
13
+ if (!__hasOwnProp.call(to, key) && key !== except) {
14
+ __defProp(to, key, {
15
+ get: ((k) => from[k]).bind(null, key),
16
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
+ });
18
+ }
19
+ }
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
24
+ value: mod,
25
+ enumerable: true
26
+ }) : target, mod));
27
+
28
+ //#endregion
29
+ let http = require("http");
30
+ let chalk = require("chalk");
31
+ chalk = __toESM(chalk, 1);
32
+ let node_events = require("node:events");
33
+ let _seedcord_errors = require("@seedcord/errors");
34
+ let _seedcord_errors_internal = require("@seedcord/errors/internal");
4
35
  let node_path = require("node:path");
5
- node_path = require_SeedcordError.__toESM(node_path, 1);
36
+ node_path = __toESM(node_path, 1);
37
+ let envapt = require("envapt");
6
38
  let winston = require("winston");
7
- winston = require_SeedcordError.__toESM(winston, 1);
39
+ winston = __toESM(winston, 1);
8
40
  let node_fs = require("node:fs");
9
- node_fs = require_SeedcordError.__toESM(node_fs, 1);
41
+ node_fs = __toESM(node_fs, 1);
10
42
  let strip_ansi = require("strip-ansi");
11
- strip_ansi = require_SeedcordError.__toESM(strip_ansi, 1);
43
+ strip_ansi = __toESM(strip_ansi, 1);
12
44
  let winston_transport = require("winston-transport");
13
- winston_transport = require_SeedcordError.__toESM(winston_transport, 1);
45
+ winston_transport = __toESM(winston_transport, 1);
14
46
  let _seedcord_utils = require("@seedcord/utils");
15
- let chalk = require("chalk");
16
- chalk = require_SeedcordError.__toESM(chalk, 1);
17
- let http = require("http");
18
- let node_events = require("node:events");
19
47
 
48
+ //#region src/RateLimiter.ts
49
+ const SWEEP_INTERVAL_MS = 6e4;
50
+ /**
51
+ * Tracks per-key usage windows and reports when a key is limited and when it frees up.
52
+ *
53
+ * Each key holds a sliding window of hit expiry times, and is limited once its live-hit count
54
+ * reaches the window's `limit`. Expired hits are dropped on the next read, and a background sweep
55
+ * drops fully-expired keys so the map does not grow without bound.
56
+ */
57
+ var RateLimiter = class {
58
+ map = /* @__PURE__ */ new Map();
59
+ constructor() {
60
+ setInterval(() => {
61
+ this.sweep();
62
+ }, SWEEP_INTERVAL_MS).unref();
63
+ }
64
+ /** Number of keys currently tracked. */
65
+ get size() {
66
+ return this.map.size;
67
+ }
68
+ /**
69
+ * Records a hit for `key` and reports whether the key is now limited.
70
+ *
71
+ * @param key - The bucket to record against. The caller builds it from its own scope.
72
+ * @param window - The usage window to apply for this hit.
73
+ */
74
+ hit(key, window) {
75
+ const now = Date.now();
76
+ const limit = Math.max(1, window.limit ?? 1);
77
+ const live = this.live(key, now);
78
+ if (live.length >= limit) {
79
+ this.map.set(key, live);
80
+ return {
81
+ limited: true,
82
+ expires: Math.min(...live)
83
+ };
84
+ }
85
+ const expires = now + window.delay;
86
+ live.push(expires);
87
+ this.map.set(key, live);
88
+ return {
89
+ limited: false,
90
+ expires
91
+ };
92
+ }
93
+ /**
94
+ * Reports whether `key` is limited right now without recording a hit.
95
+ *
96
+ * The read half of a peek-then-commit. A gate calls `peek` to decide whether to refuse, and
97
+ * `hit` to charge the slot only once it is the gate that let the request through.
98
+ */
99
+ peek(key, window) {
100
+ const now = Date.now();
101
+ const limit = Math.max(1, window.limit ?? 1);
102
+ const live = this.live(key, now);
103
+ if (live.length >= limit) return {
104
+ limited: true,
105
+ expires: Math.min(...live)
106
+ };
107
+ return {
108
+ limited: false,
109
+ expires: now + window.delay
110
+ };
111
+ }
112
+ live(key, now) {
113
+ return (this.map.get(key) ?? []).filter((exp) => exp > now);
114
+ }
115
+ sweep() {
116
+ const now = Date.now();
117
+ for (const [key, live] of this.map) {
118
+ const kept = live.filter((exp) => exp > now);
119
+ if (kept.length === 0) this.map.delete(key);
120
+ else this.map.set(key, kept);
121
+ }
122
+ }
123
+ };
124
+
125
+ //#endregion
20
126
  //#region src/Logger/LogFormatter.ts
21
127
  /**
22
128
  * Formats log records for console and file outputs.
@@ -672,26 +778,13 @@ var Logger = class Logger {
672
778
  channel;
673
779
  registry = LoggerChannelRegistry.instance;
674
780
  utils;
675
- static instances = /* @__PURE__ */ new Map();
676
- static instance(prefix, channel) {
677
- const key = channel ? `${channel}::${prefix}` : prefix;
678
- let instance = this.instances.get(key);
679
- if (!instance) {
680
- instance = new Logger(prefix, channel ? { channel } : void 0);
681
- this.instances.set(key, instance);
682
- }
683
- return instance;
684
- }
685
781
  /**
686
- * Configures global logger settings.
687
- *
688
- * Applies configuration to all channels and clears instance cache.
782
+ * Configures global logger settings, applied to all channels.
689
783
  *
690
784
  * @param config - Partial configuration to merge with defaults
691
785
  */
692
786
  static configure(config) {
693
787
  LoggerChannelRegistry.instance.configure(config);
694
- this.instances.clear();
695
788
  }
696
789
  /**
697
790
  * Creates a new Logger instance.
@@ -721,12 +814,12 @@ var Logger = class Logger {
721
814
  });
722
815
  }
723
816
  /**
724
- * Returns a new Logger instance configured for the specified channel. Loggers are cached per (label, channel) pair.
817
+ * Returns a new Logger for this label on the specified channel.
725
818
  *
726
819
  * @param channel - Channel name to use
727
820
  */
728
821
  inChannel(channel) {
729
- return Logger.instance(this.label, channel);
822
+ return new Logger(this.label, { channel });
730
823
  }
731
824
  /**
732
825
  * Logs an error message with optional additional data.
@@ -793,75 +886,6 @@ var Logger = class Logger {
793
886
  }
794
887
  };
795
888
 
796
- //#endregion
797
- //#region src/CooldownManager.ts
798
- const logger = new Logger("CooldownManager");
799
- /**
800
- * Lightweight utility for per-key cooldowns.
801
- *
802
- * Manages time-based restrictions on operations by key,
803
- * useful for rate limiting, command cooldowns, and spam prevention.
804
- */
805
- var CooldownManager = class {
806
- window;
807
- Err;
808
- msg;
809
- map = /* @__PURE__ */ new Map();
810
- /**
811
- * Creates a new CooldownManager instance.
812
- *
813
- * @param opts - Configuration options for the cooldown behavior
814
- */
815
- constructor(opts = {}) {
816
- this.window = opts.cooldown ?? 1e3;
817
- this.Err = opts.err ?? Error;
818
- this.msg = opts.message ?? "Cooldown active";
819
- }
820
- /**
821
- * Records usage timestamp for a key without any cooldown checks.
822
- *
823
- * @param key - The unique identifier for the cooldown entry
824
- */
825
- set(key) {
826
- this.map.set(key, Date.now());
827
- }
828
- /**
829
- * Verifies cooldown status for a key and updates timestamp if not active.
830
- *
831
- * If the cooldown is still active, throws the configured error.
832
- * If not active, updates the timestamp and returns successfully.
833
- *
834
- * @param key - The unique identifier to check cooldown for
835
- * @throws An {@link Err} When the cooldown is still active for the given key
836
- */
837
- check(key) {
838
- const now = Date.now();
839
- const last = this.map.get(key);
840
- const remaining = this.window - (now - (last ?? 0));
841
- if (envapt.Envapter.isDevelopment && remaining > 0) logger.debug(`${key} - ${remaining}ms remaining`);
842
- if (last !== void 0 && remaining > 0) throw new this.Err(this.msg, remaining);
843
- this.map.set(key, now);
844
- }
845
- /**
846
- * Checks if a key is currently cooling down without updating timestamp.
847
- *
848
- * @param key - The unique identifier to check
849
- * @returns True if the key is still cooling down, false otherwise
850
- */
851
- isActive(key) {
852
- const last = this.map.get(key);
853
- return last !== void 0 && Date.now() - last < this.window;
854
- }
855
- /**
856
- * Removes a key from the cooldown map.
857
- *
858
- * @param key - The unique identifier to remove (useful for manual resets)
859
- */
860
- clear(key) {
861
- this.map.delete(key);
862
- }
863
- };
864
-
865
889
  //#endregion
866
890
  //#region src/StrictEventEmitter.ts
867
891
  /**
@@ -972,7 +996,7 @@ var StrictEventEmitter = class extends node_events.EventEmitter {
972
996
  };
973
997
  const onAbort = () => {
974
998
  cleanup();
975
- reject(new require_SeedcordError.SeedcordError(1501));
999
+ reject(new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.EventEmitterWaitForAborted));
976
1000
  };
977
1001
  let timeoutId = null;
978
1002
  const cleanup = () => {
@@ -989,7 +1013,7 @@ var StrictEventEmitter = class extends node_events.EventEmitter {
989
1013
  const timeoutMs = opts.timeoutMs;
990
1014
  timeoutId = setTimeout(() => {
991
1015
  cleanup();
992
- reject(new require_SeedcordError.SeedcordError(1502, [timeoutMs]));
1016
+ reject(new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.EventEmitterWaitForTimeout, [timeoutMs]));
993
1017
  }, timeoutMs);
994
1018
  }
995
1019
  });
@@ -1033,7 +1057,7 @@ var CoordinatedLifecycle = class extends StrictEventEmitter {
1033
1057
  addTask(phase, taskName, task, timeoutMs) {
1034
1058
  if (!this.canAddTask()) return;
1035
1059
  const tasks = this.tasksMap.get(phase);
1036
- if (!tasks) throw new require_SeedcordError.SeedcordError(1104, [phase]);
1060
+ if (!tasks) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleUnknownPhase, [phase]);
1037
1061
  tasks.push({
1038
1062
  name: taskName,
1039
1063
  task,
@@ -1071,7 +1095,7 @@ var CoordinatedLifecycle = class extends StrictEventEmitter {
1071
1095
  this.logger.info(`${chalk.default.bold.yellow("Running")} ${this.getTaskType()} phase ${chalk.default.bold.magenta(this.phaseEnum[phase])} with ${chalk.default.bold.cyan(tasks.length)} tasks`);
1072
1096
  this.emitPhase(phase, "start");
1073
1097
  const failures = (await this.executeTasksInPhase(phase, tasks)).filter((r) => r.status === "rejected").length;
1074
- if (failures > 0) throw new require_SeedcordError.SeedcordError(1105, [this.phaseEnum[phase], failures]);
1098
+ if (failures > 0) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecyclePhaseFailures, [this.phaseEnum[phase], failures]);
1075
1099
  else this.logger.info(`Phase ${chalk.default.bold.magenta(this.phaseEnum[phase])} ${chalk.default.bold.green("completed successfully")}`);
1076
1100
  this.emitPhase(phase, "complete");
1077
1101
  }
@@ -1084,7 +1108,7 @@ var CoordinatedLifecycle = class extends StrictEventEmitter {
1084
1108
  try {
1085
1109
  await Promise.race([task.task(), new Promise((_, reject) => {
1086
1110
  timeoutId = setTimeout(() => {
1087
- reject(new require_SeedcordError.SeedcordError(1106, [task.name, task.timeout]));
1111
+ reject(new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));
1088
1112
  }, task.timeout);
1089
1113
  })]);
1090
1114
  this.logger.info(`${chalk.default.italic("Completed")} task ${chalk.default.bold.cyan(task.name)} in phase ${chalk.default.bold.magenta(this.phaseEnum[phase])}`);
@@ -1186,7 +1210,7 @@ var CoordinatedShutdown = class extends CoordinatedLifecycle {
1186
1210
  * @param phase - The shutdown phase from {@link ShutdownPhase}
1187
1211
  * @param taskName - Unique identifier for the task
1188
1212
  * @param task - Async function to execute
1189
- * @param timeoutMs - Task timeout in milliseconds {@default 5000}
1213
+ * @param timeoutMs - Task timeout in milliseconds. {@default `5000`}
1190
1214
  */
1191
1215
  addTask(phase, taskName, task, timeoutMs = 5e3) {
1192
1216
  super.addTask(phase, taskName, task, timeoutMs);
@@ -1208,8 +1232,8 @@ var CoordinatedShutdown = class extends CoordinatedLifecycle {
1208
1232
  * Tasks within each phase are executed in parallel for faster shutdown.
1209
1233
  * Process exits with the specified code when complete.
1210
1234
  *
1211
- * @param exitCode - Process exit code {@default 0}
1212
- * @param exitProcess - Whether to exit the process after shutdown {@default true}
1235
+ * @param exitCode - Process exit code. {@default `0`}
1236
+ * @param exitProcess - Whether to exit the process after shutdown. {@default `true`}
1213
1237
  * @returns Promise that resolves when shutdown is complete
1214
1238
  * @example
1215
1239
  * ```typescript
@@ -1382,18 +1406,18 @@ var CoordinatedStartup = class extends CoordinatedLifecycle {
1382
1406
  * @param phase - The startup phase from {@link StartupPhase}
1383
1407
  * @param taskName - Unique identifier for the task
1384
1408
  * @param task - Async function to execute
1385
- * @param timeoutMs - Task timeout in milliseconds {@default 10000}
1409
+ * @param timeoutMs - Task timeout in milliseconds. {@default `10000`}
1386
1410
  */
1387
1411
  addTask(phase, taskName, task, timeoutMs = 1e4) {
1388
1412
  super.addTask(phase, taskName, task, timeoutMs);
1389
1413
  }
1390
1414
  canAddTask() {
1391
- if (this.hasStarted) throw new require_SeedcordError.SeedcordError(1101);
1392
- if (this.isStartingUp) throw new require_SeedcordError.SeedcordError(1102);
1415
+ if (this.hasStarted) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleAddAfterCompletion);
1416
+ if (this.isStartingUp) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleAddDuringRun);
1393
1417
  return true;
1394
1418
  }
1395
1419
  canRemoveTask() {
1396
- if (this.isStartingUp) throw new require_SeedcordError.SeedcordError(1103);
1420
+ if (this.isStartingUp) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleRemoveDuringRun);
1397
1421
  return true;
1398
1422
  }
1399
1423
  getTaskType() {
@@ -1460,7 +1484,7 @@ var CoordinatedStartup = class extends CoordinatedLifecycle {
1460
1484
  try {
1461
1485
  await Promise.race([task.task(), new Promise((_, reject) => {
1462
1486
  timeoutId = setTimeout(() => {
1463
- reject(new require_SeedcordError.SeedcordError(1106, [task.name, task.timeout]));
1487
+ reject(new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));
1464
1488
  }, task.timeout);
1465
1489
  })]);
1466
1490
  this.logger.info(`${chalk.default.italic("Completed")} task ${chalk.default.bold.cyan(task.name)} in phase ${chalk.default.bold.magenta(StartupPhase[phase])}`);
@@ -1498,10 +1522,9 @@ var CoordinatedStartup = class extends CoordinatedLifecycle {
1498
1522
  //#endregion
1499
1523
  //#region src/index.ts
1500
1524
  /** Package version */
1501
- const version = "0.7.1";
1525
+ const version = "0.8.0";
1502
1526
 
1503
1527
  //#endregion
1504
- exports.CooldownManager = CooldownManager;
1505
1528
  exports.CoordinatedLifecycle = CoordinatedLifecycle;
1506
1529
  exports.CoordinatedShutdown = CoordinatedShutdown;
1507
1530
  exports.CoordinatedStartup = CoordinatedStartup;
@@ -1509,10 +1532,9 @@ exports.HealthCheck = HealthCheck;
1509
1532
  exports.Logger = Logger;
1510
1533
  exports.LoggerChannelRegistry = LoggerChannelRegistry;
1511
1534
  exports.LoggerUtilities = LoggerUtilities;
1512
- exports.SeedcordErrorCode = require_SeedcordError.SeedcordErrorCode;
1535
+ exports.RateLimiter = RateLimiter;
1513
1536
  exports.ShutdownPhase = ShutdownPhase;
1514
1537
  exports.StartupPhase = StartupPhase;
1515
1538
  exports.StrictEventEmitter = StrictEventEmitter;
1516
- exports.isSeedcordError = require_SeedcordError.isSeedcordError;
1517
1539
  exports.version = version;
1518
1540
  //# sourceMappingURL=index.cjs.map