@seedcord/services 0.7.1 → 0.8.0-next.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 +1 -1
- package/dist/index.cjs +135 -113
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +153 -102
- package/dist/index.mjs +102 -105
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -21
- package/dist/SeedcordError-C1GYc1BF.cjs +0 -413
- package/dist/SeedcordError-C1GYc1BF.cjs.map +0 -1
- package/dist/SeedcordError-D6uPv6qc.d.mts +0 -339
- package/dist/SeedcordError-E2D_RTuy.mjs +0 -350
- package/dist/SeedcordError-E2D_RTuy.mjs.map +0 -1
- package/dist/internal.index.cjs +0 -6
- package/dist/internal.index.d.cts +0 -1
- package/dist/internal.index.d.mts +0 -2
- package/dist/internal.index.mjs +0 -3
package/README.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1,22 +1,128 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
|
|
3
|
-
|
|
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 =
|
|
36
|
+
node_path = __toESM(node_path, 1);
|
|
37
|
+
let envapt = require("envapt");
|
|
6
38
|
let winston = require("winston");
|
|
7
|
-
winston =
|
|
39
|
+
winston = __toESM(winston, 1);
|
|
8
40
|
let node_fs = require("node:fs");
|
|
9
|
-
node_fs =
|
|
41
|
+
node_fs = __toESM(node_fs, 1);
|
|
10
42
|
let strip_ansi = require("strip-ansi");
|
|
11
|
-
strip_ansi =
|
|
43
|
+
strip_ansi = __toESM(strip_ansi, 1);
|
|
12
44
|
let winston_transport = require("winston-transport");
|
|
13
|
-
winston_transport =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1392
|
-
if (this.isStartingUp) throw new
|
|
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
|
|
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
|
|
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.
|
|
1525
|
+
const version = "0.8.0-next.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.
|
|
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
|