@seedcord/services 0.3.3 → 0.4.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 +380 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +247 -38
- package/dist/index.d.ts +247 -38
- package/dist/index.mjs +371 -70
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
var envapt = require('envapt');
|
|
4
4
|
var winston = require('winston');
|
|
5
|
+
var chalk2 = require('chalk');
|
|
5
6
|
var http = require('http');
|
|
6
|
-
var chalk = require('chalk');
|
|
7
7
|
var events = require('events');
|
|
8
8
|
|
|
9
9
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
10
|
|
|
11
|
-
var
|
|
11
|
+
var chalk2__default = /*#__PURE__*/_interopDefault(chalk2);
|
|
12
12
|
|
|
13
13
|
var __defProp = Object.defineProperty;
|
|
14
14
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
@@ -236,6 +236,232 @@ ${parts.join(" ")}`;
|
|
|
236
236
|
logger.silly(msg, ...args);
|
|
237
237
|
}
|
|
238
238
|
};
|
|
239
|
+
|
|
240
|
+
// src/CooldownManager.ts
|
|
241
|
+
var CooldownManager = class {
|
|
242
|
+
static {
|
|
243
|
+
__name(this, "CooldownManager");
|
|
244
|
+
}
|
|
245
|
+
window;
|
|
246
|
+
Err;
|
|
247
|
+
msg;
|
|
248
|
+
map = /* @__PURE__ */ new Map();
|
|
249
|
+
/**
|
|
250
|
+
* Creates a new CooldownManager instance.
|
|
251
|
+
*
|
|
252
|
+
* @param opts - Configuration options for the cooldown behavior
|
|
253
|
+
*/
|
|
254
|
+
constructor(opts = {}) {
|
|
255
|
+
this.window = opts.cooldown ?? 1e3;
|
|
256
|
+
this.Err = opts.err ?? Error;
|
|
257
|
+
this.msg = opts.message ?? "Cooldown active";
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Records usage timestamp for a key without any cooldown checks.
|
|
261
|
+
*
|
|
262
|
+
* @param key - The unique identifier for the cooldown entry
|
|
263
|
+
*/
|
|
264
|
+
set(key) {
|
|
265
|
+
this.map.set(key, Date.now());
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Verifies cooldown status for a key and updates timestamp if not active.
|
|
269
|
+
*
|
|
270
|
+
* If the cooldown is still active, throws the configured error.
|
|
271
|
+
* If not active, updates the timestamp and returns successfully.
|
|
272
|
+
*
|
|
273
|
+
* @param key - The unique identifier to check cooldown for
|
|
274
|
+
* @throws An {@link Err} When the cooldown is still active for the given key
|
|
275
|
+
*/
|
|
276
|
+
check(key) {
|
|
277
|
+
const now = Date.now();
|
|
278
|
+
const last = this.map.get(key);
|
|
279
|
+
const remaining = this.window - (now - (last ?? 0));
|
|
280
|
+
if (envapt.Envapter.isDevelopment && remaining > 0) {
|
|
281
|
+
Logger.Debug("CooldownManager", `${key} - ${remaining}ms remaining`);
|
|
282
|
+
}
|
|
283
|
+
if (last !== void 0 && remaining > 0) {
|
|
284
|
+
throw new this.Err(this.msg, remaining);
|
|
285
|
+
}
|
|
286
|
+
this.map.set(key, now);
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Checks if a key is currently cooling down without updating timestamp.
|
|
290
|
+
*
|
|
291
|
+
* @param key - The unique identifier to check
|
|
292
|
+
* @returns True if the key is still cooling down, false otherwise
|
|
293
|
+
*/
|
|
294
|
+
isActive(key) {
|
|
295
|
+
const last = this.map.get(key);
|
|
296
|
+
return last !== void 0 && Date.now() - last < this.window;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Removes a key from the cooldown map.
|
|
300
|
+
*
|
|
301
|
+
* @param key - The unique identifier to remove (useful for manual resets)
|
|
302
|
+
*/
|
|
303
|
+
clear(key) {
|
|
304
|
+
this.map.delete(key);
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// src/Errors/ErrorCodes.ts
|
|
309
|
+
var SeedcordErrorCode = /* @__PURE__ */ (function(SeedcordErrorCode2) {
|
|
310
|
+
SeedcordErrorCode2[SeedcordErrorCode2["ConfigMissingDiscordToken"] = 1001] = "ConfigMissingDiscordToken";
|
|
311
|
+
SeedcordErrorCode2[SeedcordErrorCode2["ConfigUnknownExceptionWebhookMissing"] = 1002] = "ConfigUnknownExceptionWebhookMissing";
|
|
312
|
+
SeedcordErrorCode2[SeedcordErrorCode2["ConfigUnknownExceptionWebhookInvalid"] = 1003] = "ConfigUnknownExceptionWebhookInvalid";
|
|
313
|
+
SeedcordErrorCode2[SeedcordErrorCode2["LifecycleAddAfterCompletion"] = 1101] = "LifecycleAddAfterCompletion";
|
|
314
|
+
SeedcordErrorCode2[SeedcordErrorCode2["LifecycleAddDuringRun"] = 1102] = "LifecycleAddDuringRun";
|
|
315
|
+
SeedcordErrorCode2[SeedcordErrorCode2["LifecycleRemoveDuringRun"] = 1103] = "LifecycleRemoveDuringRun";
|
|
316
|
+
SeedcordErrorCode2[SeedcordErrorCode2["LifecycleUnknownPhase"] = 1104] = "LifecycleUnknownPhase";
|
|
317
|
+
SeedcordErrorCode2[SeedcordErrorCode2["LifecyclePhaseFailures"] = 1105] = "LifecyclePhaseFailures";
|
|
318
|
+
SeedcordErrorCode2[SeedcordErrorCode2["LifecycleTaskTimeout"] = 1106] = "LifecycleTaskTimeout";
|
|
319
|
+
SeedcordErrorCode2[SeedcordErrorCode2["CoreSingletonViolation"] = 1201] = "CoreSingletonViolation";
|
|
320
|
+
SeedcordErrorCode2[SeedcordErrorCode2["CorePluginAfterInit"] = 1202] = "CorePluginAfterInit";
|
|
321
|
+
SeedcordErrorCode2[SeedcordErrorCode2["CorePluginKeyExists"] = 1203] = "CorePluginKeyExists";
|
|
322
|
+
SeedcordErrorCode2[SeedcordErrorCode2["CoreClientUserUnavailable"] = 1204] = "CoreClientUserUnavailable";
|
|
323
|
+
SeedcordErrorCode2[SeedcordErrorCode2["CoreBotRoleMissing"] = 1205] = "CoreBotRoleMissing";
|
|
324
|
+
SeedcordErrorCode2[SeedcordErrorCode2["DecoratorInteractionEventFilter"] = 1301] = "DecoratorInteractionEventFilter";
|
|
325
|
+
SeedcordErrorCode2[SeedcordErrorCode2["DecoratorMethodNotFound"] = 1302] = "DecoratorMethodNotFound";
|
|
326
|
+
SeedcordErrorCode2[SeedcordErrorCode2["DecoratorCommandAlreadyRegistered"] = 1303] = "DecoratorCommandAlreadyRegistered";
|
|
327
|
+
SeedcordErrorCode2[SeedcordErrorCode2["DecoratorCommandGlobalWithGuilds"] = 1304] = "DecoratorCommandGlobalWithGuilds";
|
|
328
|
+
SeedcordErrorCode2[SeedcordErrorCode2["DecoratorCommandGuildWithoutGuilds"] = 1305] = "DecoratorCommandGuildWithoutGuilds";
|
|
329
|
+
SeedcordErrorCode2[SeedcordErrorCode2["DecoratorInvalidMiddlewarePriority"] = 1306] = "DecoratorInvalidMiddlewarePriority";
|
|
330
|
+
SeedcordErrorCode2[SeedcordErrorCode2["UtilHexInputType"] = 1401] = "UtilHexInputType";
|
|
331
|
+
SeedcordErrorCode2[SeedcordErrorCode2["UtilHexInvalid"] = 1402] = "UtilHexInvalid";
|
|
332
|
+
SeedcordErrorCode2[SeedcordErrorCode2["UtilInvalidSlashRouteArgument"] = 1403] = "UtilInvalidSlashRouteArgument";
|
|
333
|
+
SeedcordErrorCode2[SeedcordErrorCode2["PluginMongoServiceDecoratorMissing"] = 2101] = "PluginMongoServiceDecoratorMissing";
|
|
334
|
+
SeedcordErrorCode2[SeedcordErrorCode2["PluginMongoModelDecoratorMissing"] = 2102] = "PluginMongoModelDecoratorMissing";
|
|
335
|
+
SeedcordErrorCode2[SeedcordErrorCode2["PluginMongoConnectionFailed"] = 2103] = "PluginMongoConnectionFailed";
|
|
336
|
+
SeedcordErrorCode2[SeedcordErrorCode2["PluginKpgServiceDecoratorMissing"] = 2201] = "PluginKpgServiceDecoratorMissing";
|
|
337
|
+
SeedcordErrorCode2[SeedcordErrorCode2["PluginKpgServiceTableMissing"] = 2202] = "PluginKpgServiceTableMissing";
|
|
338
|
+
SeedcordErrorCode2[SeedcordErrorCode2["PluginKpgInvalidStepCount"] = 2203] = "PluginKpgInvalidStepCount";
|
|
339
|
+
SeedcordErrorCode2[SeedcordErrorCode2["PluginKpgUnknownDirection"] = 2204] = "PluginKpgUnknownDirection";
|
|
340
|
+
SeedcordErrorCode2[SeedcordErrorCode2["PluginKpgUnresolvedMigrationsPath"] = 2205] = "PluginKpgUnresolvedMigrationsPath";
|
|
341
|
+
SeedcordErrorCode2[SeedcordErrorCode2["PluginKpgNoMigrationFiles"] = 2206] = "PluginKpgNoMigrationFiles";
|
|
342
|
+
SeedcordErrorCode2[SeedcordErrorCode2["PluginKpgInvalidMigrationModule"] = 2207] = "PluginKpgInvalidMigrationModule";
|
|
343
|
+
SeedcordErrorCode2[SeedcordErrorCode2["PluginKpgNonErrorFailure"] = 2208] = "PluginKpgNonErrorFailure";
|
|
344
|
+
return SeedcordErrorCode2;
|
|
345
|
+
})({});
|
|
346
|
+
|
|
347
|
+
// src/Errors/ErrorMessages.ts
|
|
348
|
+
var messages = {
|
|
349
|
+
[SeedcordErrorCode.ConfigMissingDiscordToken]: () => "Missing DISCORD_BOT_TOKEN environment variable.",
|
|
350
|
+
[SeedcordErrorCode.ConfigUnknownExceptionWebhookMissing]: () => "Missing UNKNOWN_EXCEPTION_WEBHOOK_URL environment variable.",
|
|
351
|
+
[SeedcordErrorCode.ConfigUnknownExceptionWebhookInvalid]: () => "Invalid UNKNOWN_EXCEPTION_WEBHOOK_URL value.",
|
|
352
|
+
[SeedcordErrorCode.LifecycleAddAfterCompletion]: () => "Cannot add tasks after startup sequence has already completed.",
|
|
353
|
+
[SeedcordErrorCode.LifecycleAddDuringRun]: () => "Cannot add tasks while startup sequence is in progress.",
|
|
354
|
+
[SeedcordErrorCode.LifecycleRemoveDuringRun]: () => "Cannot remove tasks while startup sequence is in progress.",
|
|
355
|
+
[SeedcordErrorCode.LifecycleUnknownPhase]: (phase) => `Unknown phase: ${String(phase)}.`,
|
|
356
|
+
[SeedcordErrorCode.LifecyclePhaseFailures]: (phase, failures) => `Phase ${phase} completed with ${failures} failed task${failures === 1 ? "" : "s"}.`,
|
|
357
|
+
[SeedcordErrorCode.LifecycleTaskTimeout]: (taskName, timeout) => `Task "${taskName}" timed out after ${timeout}ms.`,
|
|
358
|
+
[SeedcordErrorCode.CoreSingletonViolation]: () => "Seedcord can only be instantiated once. Use the existing instance instead.",
|
|
359
|
+
[SeedcordErrorCode.CorePluginAfterInit]: () => "Cannot attach a plugin after initialization.",
|
|
360
|
+
[SeedcordErrorCode.CorePluginKeyExists]: (key) => `Plugin with key "${key}" already exists.`,
|
|
361
|
+
[SeedcordErrorCode.CoreClientUserUnavailable]: () => "Client user is not available.",
|
|
362
|
+
[SeedcordErrorCode.CoreBotRoleMissing]: (guildId) => guildId ? `Bot role not found in guild ${guildId}.` : "Bot role not found in guild.",
|
|
363
|
+
[SeedcordErrorCode.DecoratorInteractionEventFilter]: () => "Interaction middleware cannot specify event filters.",
|
|
364
|
+
[SeedcordErrorCode.DecoratorMethodNotFound]: () => "Decorator could not locate the original method. Ensure the method exists before applying the decorator.",
|
|
365
|
+
[SeedcordErrorCode.DecoratorCommandAlreadyRegistered]: (commandName, existingScope, requestedScope) => `Command "${commandName}" is already registered as a "${existingScope}" command and cannot be re-registered as a "${requestedScope}" command.`,
|
|
366
|
+
[SeedcordErrorCode.DecoratorCommandGlobalWithGuilds]: () => 'RegisterCommand("global") cannot have guilds specified.',
|
|
367
|
+
[SeedcordErrorCode.DecoratorCommandGuildWithoutGuilds]: () => 'RegisterCommand("guild") requires a non-empty guilds array.',
|
|
368
|
+
[SeedcordErrorCode.DecoratorInvalidMiddlewarePriority]: () => "Middleware priority must be a finite number.",
|
|
369
|
+
[SeedcordErrorCode.UtilHexInputType]: () => "hexToNumber expects a string input.",
|
|
370
|
+
[SeedcordErrorCode.UtilHexInvalid]: () => "Invalid hex string.",
|
|
371
|
+
[SeedcordErrorCode.UtilInvalidSlashRouteArgument]: () => "Invalid argument passed to buildSlashRoute.",
|
|
372
|
+
[SeedcordErrorCode.PluginMongoServiceDecoratorMissing]: (className) => `Missing @RegisterMongoService on ${className}.`,
|
|
373
|
+
[SeedcordErrorCode.PluginMongoModelDecoratorMissing]: (className) => `Missing @RegisterMongoModel on ${className}.`,
|
|
374
|
+
[SeedcordErrorCode.PluginMongoConnectionFailed]: (databaseName) => databaseName ? `Could not connect to MongoDB (${databaseName}).` : "Could not connect to MongoDB.",
|
|
375
|
+
[SeedcordErrorCode.PluginKpgServiceDecoratorMissing]: (className) => `Missing @RegisterKpgService on ${className}.`,
|
|
376
|
+
[SeedcordErrorCode.PluginKpgServiceTableMissing]: (className) => `Missing table metadata for ${className}. Provide a table via @RegisterKpgService().`,
|
|
377
|
+
[SeedcordErrorCode.PluginKpgInvalidStepCount]: () => "Migration step count must be a non-negative integer.",
|
|
378
|
+
[SeedcordErrorCode.PluginKpgUnknownDirection]: (direction) => `Unknown migration direction: ${String(direction)}.`,
|
|
379
|
+
[SeedcordErrorCode.PluginKpgUnresolvedMigrationsPath]: (label) => `Unable to resolve migrations at path: ${label}.`,
|
|
380
|
+
[SeedcordErrorCode.PluginKpgNoMigrationFiles]: () => "No migration files provided.",
|
|
381
|
+
[SeedcordErrorCode.PluginKpgInvalidMigrationModule]: (filePath) => `Migration file ${filePath} must export async functions up and down.`,
|
|
382
|
+
[SeedcordErrorCode.PluginKpgNonErrorFailure]: (message) => `Migration failure: ${message}.`
|
|
383
|
+
};
|
|
384
|
+
function formatSeedcordErrorMessage(code, args) {
|
|
385
|
+
const formatter = messages[code];
|
|
386
|
+
const resolvedArgs = args ?? [];
|
|
387
|
+
return formatter(...resolvedArgs);
|
|
388
|
+
}
|
|
389
|
+
__name(formatSeedcordErrorMessage, "formatSeedcordErrorMessage");
|
|
390
|
+
function resolveIdentifier(code) {
|
|
391
|
+
return SeedcordErrorCode[code];
|
|
392
|
+
}
|
|
393
|
+
__name(resolveIdentifier, "resolveIdentifier");
|
|
394
|
+
function resolveMessage(code, args) {
|
|
395
|
+
return formatSeedcordErrorMessage(code, args);
|
|
396
|
+
}
|
|
397
|
+
__name(resolveMessage, "resolveMessage");
|
|
398
|
+
function formatErrorName(name, _identifier, code) {
|
|
399
|
+
return `${chalk2__default.default.bold.red(name)}[${chalk2__default.default.gray(code)}]`;
|
|
400
|
+
}
|
|
401
|
+
__name(formatErrorName, "formatErrorName");
|
|
402
|
+
var SeedcordError = class extends Error {
|
|
403
|
+
static {
|
|
404
|
+
__name(this, "SeedcordError");
|
|
405
|
+
}
|
|
406
|
+
code;
|
|
407
|
+
identifier;
|
|
408
|
+
constructor(code, args, options) {
|
|
409
|
+
const message = resolveMessage(code, args);
|
|
410
|
+
super(message, options);
|
|
411
|
+
this.code = code;
|
|
412
|
+
this.identifier = resolveIdentifier(code);
|
|
413
|
+
this.name = formatErrorName(new.target.name, this.identifier, this.code);
|
|
414
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
415
|
+
if (typeof Error.captureStackTrace === "function") {
|
|
416
|
+
Error.captureStackTrace(this, new.target);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
var SeedcordTypeError = class extends TypeError {
|
|
421
|
+
static {
|
|
422
|
+
__name(this, "SeedcordTypeError");
|
|
423
|
+
}
|
|
424
|
+
code;
|
|
425
|
+
identifier;
|
|
426
|
+
constructor(code, args, options) {
|
|
427
|
+
const message = resolveMessage(code, args);
|
|
428
|
+
super(message, options);
|
|
429
|
+
this.code = code;
|
|
430
|
+
this.identifier = resolveIdentifier(code);
|
|
431
|
+
this.name = formatErrorName(new.target.name, this.identifier, this.code);
|
|
432
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
433
|
+
if (typeof Error.captureStackTrace === "function") {
|
|
434
|
+
Error.captureStackTrace(this, new.target);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
var SeedcordRangeError = class extends RangeError {
|
|
439
|
+
static {
|
|
440
|
+
__name(this, "SeedcordRangeError");
|
|
441
|
+
}
|
|
442
|
+
code;
|
|
443
|
+
identifier;
|
|
444
|
+
constructor(code, args, options) {
|
|
445
|
+
const message = resolveMessage(code, args);
|
|
446
|
+
super(message, options);
|
|
447
|
+
this.code = code;
|
|
448
|
+
this.identifier = resolveIdentifier(code);
|
|
449
|
+
this.name = formatErrorName(new.target.name, this.identifier, this.code);
|
|
450
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
451
|
+
if (typeof Error.captureStackTrace === "function") {
|
|
452
|
+
Error.captureStackTrace(this, new.target);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
var SeedcordErrors = {
|
|
457
|
+
Error: SeedcordError,
|
|
458
|
+
TypeError: SeedcordTypeError,
|
|
459
|
+
RangeError: SeedcordRangeError
|
|
460
|
+
};
|
|
461
|
+
function isSeedcordError(error) {
|
|
462
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "number" && "identifier" in error && typeof error.identifier === "string";
|
|
463
|
+
}
|
|
464
|
+
__name(isSeedcordError, "isSeedcordError");
|
|
239
465
|
var CoordinatedLifecycle = class {
|
|
240
466
|
static {
|
|
241
467
|
__name(this, "CoordinatedLifecycle");
|
|
@@ -271,13 +497,17 @@ var CoordinatedLifecycle = class {
|
|
|
271
497
|
addTask(phase, taskName, task, timeoutMs) {
|
|
272
498
|
if (!this.canAddTask()) return;
|
|
273
499
|
const tasks = this.tasksMap.get(phase);
|
|
274
|
-
if (!tasks)
|
|
500
|
+
if (!tasks) {
|
|
501
|
+
throw new SeedcordError(SeedcordErrorCode.LifecycleUnknownPhase, [
|
|
502
|
+
phase
|
|
503
|
+
]);
|
|
504
|
+
}
|
|
275
505
|
tasks.push({
|
|
276
506
|
name: taskName,
|
|
277
507
|
task,
|
|
278
508
|
timeout: timeoutMs
|
|
279
509
|
});
|
|
280
|
-
this.logger.debug(`${
|
|
510
|
+
this.logger.debug(`${chalk2__default.default.italic("Added")} ${this.getTaskType()} task ${chalk2__default.default.bold.cyan(taskName)} to phase ${chalk2__default.default.bold.magenta(this.phaseEnum[phase])}`);
|
|
281
511
|
}
|
|
282
512
|
/**
|
|
283
513
|
* Removes a lifecycle task from a specific phase.
|
|
@@ -295,7 +525,7 @@ var CoordinatedLifecycle = class {
|
|
|
295
525
|
this.tasksMap.set(phase, filteredTasks);
|
|
296
526
|
const removed = initialLength !== filteredTasks.length;
|
|
297
527
|
if (removed) {
|
|
298
|
-
this.logger.debug(`${
|
|
528
|
+
this.logger.debug(`${chalk2__default.default.italic("Removed")} ${this.getTaskType()} task ${chalk2__default.default.bold.cyan(taskName)} from phase ${chalk2__default.default.bold.magenta(this.phaseEnum[phase])}`);
|
|
299
529
|
}
|
|
300
530
|
return removed;
|
|
301
531
|
}
|
|
@@ -305,18 +535,20 @@ var CoordinatedLifecycle = class {
|
|
|
305
535
|
async runPhase(phase) {
|
|
306
536
|
const tasks = this.tasksMap.get(phase) ?? [];
|
|
307
537
|
if (tasks.length === 0) {
|
|
308
|
-
this.logger.warn(`No tasks to run in phase ${
|
|
538
|
+
this.logger.warn(`No tasks to run in phase ${chalk2__default.default.bold.magenta(this.phaseEnum[phase])}`);
|
|
309
539
|
return;
|
|
310
540
|
}
|
|
311
|
-
this.logger.info(`${
|
|
541
|
+
this.logger.info(`${chalk2__default.default.bold.yellow("Running")} ${this.getTaskType()} phase ${chalk2__default.default.bold.magenta(this.phaseEnum[phase])} with ${chalk2__default.default.bold.cyan(tasks.length)} tasks`);
|
|
312
542
|
this.emit(`phase:${phase}:start`);
|
|
313
543
|
const results = await this.executeTasksInPhase(phase, tasks);
|
|
314
544
|
const failures = results.filter((r) => r.status === "rejected").length;
|
|
315
545
|
if (failures > 0) {
|
|
316
|
-
|
|
317
|
-
|
|
546
|
+
throw new SeedcordError(SeedcordErrorCode.LifecyclePhaseFailures, [
|
|
547
|
+
chalk2__default.default.bold.magenta(this.phaseEnum[phase]),
|
|
548
|
+
failures
|
|
549
|
+
]);
|
|
318
550
|
} else {
|
|
319
|
-
this.logger.info(`Phase ${
|
|
551
|
+
this.logger.info(`Phase ${chalk2__default.default.bold.magenta(this.phaseEnum[phase])} ${chalk2__default.default.bold.green("completed successfully")}`);
|
|
320
552
|
}
|
|
321
553
|
this.emit(`phase:${phase}:complete`);
|
|
322
554
|
}
|
|
@@ -324,19 +556,22 @@ var CoordinatedLifecycle = class {
|
|
|
324
556
|
* Run a single task with timeout
|
|
325
557
|
*/
|
|
326
558
|
async runTaskWithTimeout(phase, task) {
|
|
327
|
-
this.logger.info(`${
|
|
559
|
+
this.logger.info(`${chalk2__default.default.italic("Starting")} task ${chalk2__default.default.bold.cyan(task.name)} in phase ${chalk2__default.default.bold.magenta(this.phaseEnum[phase])}`);
|
|
328
560
|
try {
|
|
329
561
|
await Promise.race([
|
|
330
562
|
task.task(),
|
|
331
563
|
new Promise((_, reject) => {
|
|
332
564
|
setTimeout(() => {
|
|
333
|
-
reject(new
|
|
565
|
+
reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [
|
|
566
|
+
task.name,
|
|
567
|
+
task.timeout
|
|
568
|
+
]));
|
|
334
569
|
}, task.timeout);
|
|
335
570
|
})
|
|
336
571
|
]);
|
|
337
|
-
this.logger.info(`${
|
|
572
|
+
this.logger.info(`${chalk2__default.default.italic("Completed")} task ${chalk2__default.default.bold.cyan(task.name)} in phase ${chalk2__default.default.bold.magenta(this.phaseEnum[phase])}`);
|
|
338
573
|
} catch (error) {
|
|
339
|
-
this.logger.error(`${
|
|
574
|
+
this.logger.error(`${chalk2__default.default.italic("Failed")} task ${chalk2__default.default.bold.cyan(task.name)} in phase ${chalk2__default.default.bold.magenta(this.phaseEnum[phase])}:`, error);
|
|
340
575
|
throw error;
|
|
341
576
|
}
|
|
342
577
|
}
|
|
@@ -424,11 +659,11 @@ var CoordinatedShutdown = class extends CoordinatedLifecycle {
|
|
|
424
659
|
registerSignalHandlers() {
|
|
425
660
|
if (!this.isShutdownEnabled) return;
|
|
426
661
|
process.on("SIGTERM", () => {
|
|
427
|
-
this.logger.info(`Received ${
|
|
662
|
+
this.logger.info(`Received ${chalk2__default.default.yellow.bold("SIGTERM")} signal`);
|
|
428
663
|
void this.run(0);
|
|
429
664
|
});
|
|
430
665
|
process.on("SIGINT", () => {
|
|
431
|
-
this.logger.info(`Received ${
|
|
666
|
+
this.logger.info(`Received ${chalk2__default.default.yellow.bold("SIGINT")} signal`);
|
|
432
667
|
void this.run(0);
|
|
433
668
|
});
|
|
434
669
|
}
|
|
@@ -475,19 +710,19 @@ var CoordinatedShutdown = class extends CoordinatedLifecycle {
|
|
|
475
710
|
}
|
|
476
711
|
this.isShuttingDown = true;
|
|
477
712
|
this.exitCode = exitCode;
|
|
478
|
-
this.logger.info(`${
|
|
713
|
+
this.logger.info(`${chalk2__default.default.bold.yellow("Starting")} coordinated shutdown with exit code ${chalk2__default.default.bold.cyan(exitCode)}`);
|
|
479
714
|
this.emit("shutdown:start");
|
|
480
715
|
try {
|
|
481
716
|
for (const phase of PHASE_ORDER) {
|
|
482
717
|
await this.runPhase(phase);
|
|
483
718
|
}
|
|
484
|
-
this.logger.info(`${
|
|
719
|
+
this.logger.info(`${chalk2__default.default.bold.green("Coordinated shutdown completed")} successfully`);
|
|
485
720
|
this.emit("shutdown:complete");
|
|
486
721
|
} catch (error) {
|
|
487
|
-
this.logger.error(`${
|
|
722
|
+
this.logger.error(`${chalk2__default.default.bold.red("Coordinated shutdown failed")}`);
|
|
488
723
|
this.emit("shutdown:error", error);
|
|
489
724
|
} finally {
|
|
490
|
-
this.logger.info(`${
|
|
725
|
+
this.logger.info(`${chalk2__default.default.bold.red("Exiting")} process with code ${chalk2__default.default.bold.cyan(this.exitCode)}`);
|
|
491
726
|
setTimeout(() => {
|
|
492
727
|
process.exit(this.exitCode);
|
|
493
728
|
}, LOG_FLUSH_DELAY_MS);
|
|
@@ -563,7 +798,7 @@ var HealthCheck = class {
|
|
|
563
798
|
this.server.on("error", reject);
|
|
564
799
|
this.server.once("listening", () => {
|
|
565
800
|
const address = this.host ?? "localhost";
|
|
566
|
-
this.logger.info(`${
|
|
801
|
+
this.logger.info(`${chalk2__default.default.green.bold("\u2713")} Health check server listening on ${chalk2__default.default.cyan(`http://${address}:${this.port}${this.path}`)}`);
|
|
567
802
|
resolve();
|
|
568
803
|
});
|
|
569
804
|
if (this.host) {
|
|
@@ -586,7 +821,7 @@ var HealthCheck = class {
|
|
|
586
821
|
return new Promise((resolve) => {
|
|
587
822
|
server.once("close", () => resolve());
|
|
588
823
|
server.close(() => {
|
|
589
|
-
this.logger.info(
|
|
824
|
+
this.logger.info(chalk2__default.default.bold.red("Health check server stopped"));
|
|
590
825
|
});
|
|
591
826
|
});
|
|
592
827
|
}
|
|
@@ -609,70 +844,136 @@ _ts_decorate2([
|
|
|
609
844
|
envapt.Envapt("HEALTH_CHECK_HOST"),
|
|
610
845
|
_ts_metadata2("design:type", Object)
|
|
611
846
|
], HealthCheck.prototype, "host", void 0);
|
|
612
|
-
var
|
|
847
|
+
var StrictEventEmitter = class extends events.EventEmitter {
|
|
613
848
|
static {
|
|
614
|
-
__name(this, "
|
|
849
|
+
__name(this, "StrictEventEmitter");
|
|
615
850
|
}
|
|
616
|
-
window;
|
|
617
|
-
Err;
|
|
618
|
-
msg;
|
|
619
|
-
map = /* @__PURE__ */ new Map();
|
|
620
851
|
/**
|
|
621
|
-
*
|
|
852
|
+
* Registers a persistent listener with tuple-safe arguments for the given event.
|
|
622
853
|
*
|
|
623
|
-
* @param
|
|
854
|
+
* @param event - The event name to attach to
|
|
855
|
+
* @param listener - Callback operating on the typed argument tuple for the event
|
|
856
|
+
* @returns This emitter instance for chaining
|
|
624
857
|
*/
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
this.Err = opts.err ?? Error;
|
|
628
|
-
this.msg = opts.message ?? "Cooldown active";
|
|
858
|
+
on(event, listener) {
|
|
859
|
+
return super.on(event, listener);
|
|
629
860
|
}
|
|
630
861
|
/**
|
|
631
|
-
*
|
|
862
|
+
* Registers a one time listener that is removed after the first invocation.
|
|
632
863
|
*
|
|
633
|
-
* @param
|
|
864
|
+
* @param event - The event name to attach to
|
|
865
|
+
* @param listener - Callback operating on the typed argument tuple for the event
|
|
866
|
+
* @returns This emitter instance for chaining
|
|
634
867
|
*/
|
|
635
|
-
|
|
636
|
-
|
|
868
|
+
once(event, listener) {
|
|
869
|
+
return super.once(event, listener);
|
|
637
870
|
}
|
|
638
871
|
/**
|
|
639
|
-
*
|
|
872
|
+
* Removes a previously registered listener for the given event.
|
|
640
873
|
*
|
|
641
|
-
*
|
|
642
|
-
*
|
|
874
|
+
* @param event - The event name whose listener should be removed
|
|
875
|
+
* @param listener - Callback originally registered for the event
|
|
876
|
+
* @returns This emitter instance for chaining
|
|
877
|
+
*/
|
|
878
|
+
off(event, listener) {
|
|
879
|
+
return super.off(event, listener);
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Alias of {@link StrictEventEmitter.on} for compatibility with Node.js EventEmitter APIs.
|
|
643
883
|
*
|
|
644
|
-
* @param
|
|
645
|
-
* @
|
|
884
|
+
* @param event - The event name to attach to
|
|
885
|
+
* @param listener - Callback operating on the typed argument tuple for the event
|
|
886
|
+
* @returns This emitter instance for chaining
|
|
646
887
|
*/
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
const last = this.map.get(key);
|
|
650
|
-
const remaining = this.window - (now - (last ?? 0));
|
|
651
|
-
if (envapt.Envapter.isDevelopment && remaining > 0) {
|
|
652
|
-
Logger.Debug("CooldownManager", `${key} - ${remaining}ms remaining`);
|
|
653
|
-
}
|
|
654
|
-
if (last !== void 0 && remaining > 0) {
|
|
655
|
-
throw new this.Err(this.msg, remaining);
|
|
656
|
-
}
|
|
657
|
-
this.map.set(key, now);
|
|
888
|
+
addListener(event, listener) {
|
|
889
|
+
return this.on(event, listener);
|
|
658
890
|
}
|
|
659
891
|
/**
|
|
660
|
-
*
|
|
892
|
+
* Alias of {@link StrictEventEmitter.off} for compatibility with Node.js EventEmitter APIs.
|
|
661
893
|
*
|
|
662
|
-
* @param
|
|
663
|
-
* @
|
|
894
|
+
* @param event - The event name whose listener should be removed
|
|
895
|
+
* @param listener - Callback originally registered for the event
|
|
896
|
+
* @returns This emitter instance for chaining
|
|
664
897
|
*/
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
return last !== void 0 && Date.now() - last < this.window;
|
|
898
|
+
removeListener(event, listener) {
|
|
899
|
+
return super.removeListener(event, listener);
|
|
668
900
|
}
|
|
669
901
|
/**
|
|
670
|
-
*
|
|
902
|
+
* Emits an event with the strictly typed argument tuple for the event name.
|
|
671
903
|
*
|
|
672
|
-
* @param
|
|
904
|
+
* @param event - The event name to emit
|
|
905
|
+
* @param args - Tuple payload for the event
|
|
906
|
+
* @returns True when the event had listeners, false otherwise
|
|
673
907
|
*/
|
|
674
|
-
|
|
675
|
-
|
|
908
|
+
emit(event, ...args) {
|
|
909
|
+
return super.emit(event, ...args);
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* Retrieves the listener list for a given event with the correct tuple signature.
|
|
913
|
+
*
|
|
914
|
+
* @param event - The event name to inspect
|
|
915
|
+
* @returns Array of listeners registered for the event
|
|
916
|
+
*/
|
|
917
|
+
listeners(event) {
|
|
918
|
+
return super.listeners(event);
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Counts listeners for an event without widening the return type of {@link EventEmitter.listenerCount}.
|
|
922
|
+
*
|
|
923
|
+
* @param event - The event name to inspect
|
|
924
|
+
* @returns The total number of listeners registered for the event
|
|
925
|
+
*/
|
|
926
|
+
listenerCountTyped(event) {
|
|
927
|
+
return super.listenerCount(event);
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Returns the list of event names known to the emitter with the mapped key type.
|
|
931
|
+
*
|
|
932
|
+
* @returns Array of event keys supported by the emitter
|
|
933
|
+
*/
|
|
934
|
+
eventNamesTyped() {
|
|
935
|
+
return super.eventNames();
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Waits for an event to be emitted, resolving with the listener arguments tuple once triggered.
|
|
939
|
+
* Supports optional abort signals and timeouts for cancellation semantics.
|
|
940
|
+
*
|
|
941
|
+
* @param event - The event name to wait for
|
|
942
|
+
* @param opts - Optional abort signal or timeout in milliseconds
|
|
943
|
+
* @returns Promise resolving with the emitted argument tuple; rejects when aborted or timed out
|
|
944
|
+
*/
|
|
945
|
+
waitFor(event, opts) {
|
|
946
|
+
return new Promise((resolve, reject) => {
|
|
947
|
+
const onEvent = /* @__PURE__ */ __name((...args) => {
|
|
948
|
+
cleanup();
|
|
949
|
+
resolve(args);
|
|
950
|
+
}, "onEvent");
|
|
951
|
+
const onAbort = /* @__PURE__ */ __name(() => {
|
|
952
|
+
cleanup();
|
|
953
|
+
reject(Object.assign(new Error("Aborted"), {
|
|
954
|
+
name: "AbortError"
|
|
955
|
+
}));
|
|
956
|
+
}, "onAbort");
|
|
957
|
+
let timeoutId = null;
|
|
958
|
+
const cleanup = /* @__PURE__ */ __name(() => {
|
|
959
|
+
this.off(event, onEvent);
|
|
960
|
+
opts?.signal?.removeEventListener("abort", onAbort);
|
|
961
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
962
|
+
}, "cleanup");
|
|
963
|
+
this.once(event, onEvent);
|
|
964
|
+
if (opts?.signal) {
|
|
965
|
+
if (opts.signal.aborted) return onAbort();
|
|
966
|
+
opts.signal.addEventListener("abort", onAbort, {
|
|
967
|
+
once: true
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
if (opts?.timeoutMs !== void 0) {
|
|
971
|
+
timeoutId = setTimeout(() => {
|
|
972
|
+
cleanup();
|
|
973
|
+
reject(new Error("Timed out"));
|
|
974
|
+
}, opts.timeoutMs);
|
|
975
|
+
}
|
|
976
|
+
});
|
|
676
977
|
}
|
|
677
978
|
};
|
|
678
979
|
var StartupPhase = /* @__PURE__ */ (function(StartupPhase2) {
|
|
@@ -716,16 +1017,16 @@ var CoordinatedStartup = class extends CoordinatedLifecycle {
|
|
|
716
1017
|
}
|
|
717
1018
|
canAddTask() {
|
|
718
1019
|
if (this.hasStarted) {
|
|
719
|
-
throw new
|
|
1020
|
+
throw new SeedcordError(SeedcordErrorCode.LifecycleAddAfterCompletion);
|
|
720
1021
|
}
|
|
721
1022
|
if (this.isStartingUp) {
|
|
722
|
-
throw new
|
|
1023
|
+
throw new SeedcordError(SeedcordErrorCode.LifecycleAddDuringRun);
|
|
723
1024
|
}
|
|
724
1025
|
return true;
|
|
725
1026
|
}
|
|
726
1027
|
canRemoveTask() {
|
|
727
1028
|
if (this.isStartingUp) {
|
|
728
|
-
throw new
|
|
1029
|
+
throw new SeedcordError(SeedcordErrorCode.LifecycleRemoveDuringRun);
|
|
729
1030
|
}
|
|
730
1031
|
return true;
|
|
731
1032
|
}
|
|
@@ -775,15 +1076,15 @@ var CoordinatedStartup = class extends CoordinatedLifecycle {
|
|
|
775
1076
|
return;
|
|
776
1077
|
}
|
|
777
1078
|
this.isStartingUp = true;
|
|
778
|
-
this.logger.info(`${
|
|
1079
|
+
this.logger.info(`${chalk2__default.default.bold.green("Starting")} coordinated startup sequence`);
|
|
779
1080
|
this.emit("startup:start");
|
|
780
1081
|
try {
|
|
781
1082
|
for (const phase of PHASE_ORDER2) await this.runPhase(phase);
|
|
782
1083
|
this.hasStarted = true;
|
|
783
|
-
this.logger.info(`${
|
|
1084
|
+
this.logger.info(`${chalk2__default.default.bold.green("Coordinated startup completed")} successfully`);
|
|
784
1085
|
this.emit("startup:complete");
|
|
785
1086
|
} catch (error) {
|
|
786
|
-
this.logger.error(`${
|
|
1087
|
+
this.logger.error(`${chalk2__default.default.bold.red("Coordinated startup failed")}`);
|
|
787
1088
|
this.emit("startup:error", error);
|
|
788
1089
|
throw error;
|
|
789
1090
|
} finally {
|
|
@@ -822,7 +1123,16 @@ exports.CoordinatedShutdown = CoordinatedShutdown;
|
|
|
822
1123
|
exports.CoordinatedStartup = CoordinatedStartup;
|
|
823
1124
|
exports.HealthCheck = HealthCheck;
|
|
824
1125
|
exports.Logger = Logger;
|
|
1126
|
+
exports.SeedcordError = SeedcordError;
|
|
1127
|
+
exports.SeedcordErrorCode = SeedcordErrorCode;
|
|
1128
|
+
exports.SeedcordErrors = SeedcordErrors;
|
|
1129
|
+
exports.SeedcordRangeError = SeedcordRangeError;
|
|
1130
|
+
exports.SeedcordTypeError = SeedcordTypeError;
|
|
825
1131
|
exports.ShutdownPhase = ShutdownPhase;
|
|
826
1132
|
exports.StartupPhase = StartupPhase;
|
|
1133
|
+
exports.StrictEventEmitter = StrictEventEmitter;
|
|
1134
|
+
exports.formatSeedcordErrorMessage = formatSeedcordErrorMessage;
|
|
1135
|
+
exports.isSeedcordError = isSeedcordError;
|
|
1136
|
+
exports.seedcordErrorMessages = messages;
|
|
827
1137
|
//# sourceMappingURL=index.cjs.map
|
|
828
1138
|
//# sourceMappingURL=index.cjs.map
|