@seedcord/core 0.1.0-next.4 → 0.1.0-next.5

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.
@@ -0,0 +1,521 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_rolldown_runtime = require('./rolldown-runtime-C_NdSu1c.cjs');
3
+ let _seedcord_errors = require("@seedcord/errors");
4
+ let _seedcord_errors_internal = require("@seedcord/errors/internal");
5
+ let _seedcord_logger = require("@seedcord/logger");
6
+ let chalk = require("chalk");
7
+ chalk = require_rolldown_runtime.__toESM(chalk, 1);
8
+ let _seedcord_event_emitter = require("@seedcord/event-emitter");
9
+ let node_http = require("node:http");
10
+
11
+ //#region src/node/Lifecycle/CoordinatedLifecycle.ts
12
+ /**
13
+ * Abstract base class for coordinated lifecycle management (startup/shutdown)
14
+ */
15
+ var CoordinatedLifecycle = class extends _seedcord_event_emitter.TypedEventEmitter {
16
+ phaseOrder;
17
+ phaseEnum;
18
+ logger;
19
+ tasksMap = /* @__PURE__ */ new Map();
20
+ constructor(loggerName, phaseOrder, phaseEnum) {
21
+ super();
22
+ this.phaseOrder = phaseOrder;
23
+ this.phaseEnum = phaseEnum;
24
+ this.logger = new _seedcord_logger.Logger(loggerName);
25
+ this.phaseOrder.forEach((phase) => this.tasksMap.set(phase, []));
26
+ }
27
+ /**
28
+ * Adds a lifecycle task to a specific phase.
29
+ *
30
+ * Tasks are executed in phase order during lifecycle operations.
31
+ * Each task has a timeout to prevent hanging operations.
32
+ *
33
+ * @param phase - The lifecycle phase to add the task to
34
+ * @param taskName - Unique name for the task (used for logging and removal)
35
+ * @param task - Async function to execute during the phase
36
+ * @param timeoutMs - Maximum time allowed for task execution in milliseconds
37
+ * @example
38
+ * ```typescript
39
+ * lifecycle.addTask(StartupPhase.Services, 'start-database', async () => {
40
+ * await database.connect();
41
+ * }, 10000);
42
+ * ```
43
+ */
44
+ addTask(phase, taskName, task, timeoutMs) {
45
+ if (!this.canAddTask()) return;
46
+ const tasks = this.tasksMap.get(phase);
47
+ if (!tasks) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleUnknownPhase, [phase]);
48
+ tasks.push({
49
+ name: taskName,
50
+ task,
51
+ timeout: timeoutMs
52
+ });
53
+ this.logger.debug(`${chalk.default.italic("Added")} ${this.getTaskType()} task ${chalk.default.bold.cyan(taskName)} to phase ${chalk.default.bold.magenta(this.phaseEnum[phase])}`);
54
+ }
55
+ /**
56
+ * Removes a lifecycle task from a specific phase.
57
+ *
58
+ * @param phase - The lifecycle phase to remove the task from
59
+ * @param taskName - Name of the task to remove
60
+ * @returns True if the task was found and removed, false otherwise
61
+ */
62
+ removeTask(phase, taskName) {
63
+ if (!this.canRemoveTask()) return false;
64
+ const tasks = this.tasksMap.get(phase);
65
+ if (!tasks) return false;
66
+ const initialLength = tasks.length;
67
+ const filteredTasks = tasks.filter((task) => task.name !== taskName);
68
+ this.tasksMap.set(phase, filteredTasks);
69
+ const removed = initialLength !== filteredTasks.length;
70
+ if (removed) this.logger.debug(`${chalk.default.italic("Removed")} ${this.getTaskType()} task ${chalk.default.bold.cyan(taskName)} from phase ${chalk.default.bold.magenta(this.phaseEnum[phase])}`);
71
+ return removed;
72
+ }
73
+ /**
74
+ * Run all tasks in a specific phase
75
+ */
76
+ async runPhase(phase) {
77
+ const tasks = this.tasksMap.get(phase) ?? [];
78
+ if (tasks.length === 0) {
79
+ this.logger.warn(`No tasks to run in phase ${chalk.default.bold.magenta(this.phaseEnum[phase])}`);
80
+ return;
81
+ }
82
+ 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`);
83
+ this.emitPhase(phase, "start");
84
+ const failures = (await this.executeTasksInPhase(phase, tasks)).filter((r) => r.status === "rejected").length;
85
+ if (failures > 0) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecyclePhaseFailures, [this.phaseEnum[phase], failures]);
86
+ this.logger.info(`Phase ${chalk.default.bold.magenta(this.phaseEnum[phase])} ${chalk.default.bold.green("completed successfully")}`);
87
+ this.emitPhase(phase, "complete");
88
+ }
89
+ /**
90
+ * Run a single task with timeout
91
+ */
92
+ async runTaskWithTimeout(phase, task) {
93
+ this.logger.info(`${chalk.default.italic("Starting")} task ${chalk.default.bold.cyan(task.name)} in phase ${chalk.default.bold.magenta(this.phaseEnum[phase])}`);
94
+ let timeoutId;
95
+ try {
96
+ await Promise.race([task.task(), new Promise((_, reject) => {
97
+ timeoutId = setTimeout(() => {
98
+ reject(new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));
99
+ }, task.timeout);
100
+ })]);
101
+ this.logger.info(`${chalk.default.italic("Completed")} task ${chalk.default.bold.cyan(task.name)} in phase ${chalk.default.bold.magenta(this.phaseEnum[phase])}`);
102
+ } catch (error) {
103
+ this.logger.error(`${chalk.default.italic("Failed")} task ${chalk.default.bold.cyan(task.name)} in phase ${chalk.default.bold.magenta(this.phaseEnum[phase])}:`, error);
104
+ throw error;
105
+ } finally {
106
+ if (timeoutId) clearTimeout(timeoutId);
107
+ }
108
+ }
109
+ emitPhase(phase, action) {
110
+ this.emitSafeRaw(`phase:${phase}:${action}`);
111
+ }
112
+ onListenerError(error, event) {
113
+ this.logger.error(`listener for ${String(event)} threw`, error);
114
+ }
115
+ };
116
+
117
+ //#endregion
118
+ //#region src/node/Lifecycle/CoordinatedShutdown.ts
119
+ /**
120
+ * Shutdown phases for coordinated application shutdown.
121
+ */
122
+ let ShutdownPhase = /* @__PURE__ */ function(ShutdownPhase) {
123
+ /** Stop accepting new requests/interactions */
124
+ ShutdownPhase[ShutdownPhase["StopAcceptingRequests"] = 1] = "StopAcceptingRequests";
125
+ /** Stop background services (health checks, etc.) */
126
+ ShutdownPhase[ShutdownPhase["StopServices"] = 2] = "StopServices";
127
+ /** Disconnect from external resources (database, APIs) */
128
+ ShutdownPhase[ShutdownPhase["ExternalResources"] = 3] = "ExternalResources";
129
+ /** Disconnect from Discord */
130
+ ShutdownPhase[ShutdownPhase["DiscordCleanup"] = 4] = "DiscordCleanup";
131
+ /** Final cleanup tasks */
132
+ ShutdownPhase[ShutdownPhase["FinalCleanup"] = 5] = "FinalCleanup";
133
+ return ShutdownPhase;
134
+ }({});
135
+ /** Define the order of phases */
136
+ const PHASE_ORDER$1 = [
137
+ 1,
138
+ 2,
139
+ 3,
140
+ 4,
141
+ 5
142
+ ];
143
+ const LOG_FLUSH_DELAY_MS = 500;
144
+ /**
145
+ * CoordinatedShutdown manages graceful application shutdown by executing registered tasks across defined phases.
146
+ *
147
+ * It listens for termination signals (SIGINT, SIGTERM) and runs tasks in parallel within each phase.
148
+ * Tasks can be added or removed dynamically, and each task has an associated timeout.
149
+ */
150
+ var CoordinatedShutdown = class extends CoordinatedLifecycle {
151
+ isShutdownEnabled;
152
+ isShuttingDown = false;
153
+ exitCode = 0;
154
+ onSigTerm = null;
155
+ onSigInt = null;
156
+ constructor(enabled = true) {
157
+ super("Shutdown", PHASE_ORDER$1, ShutdownPhase);
158
+ this.isShutdownEnabled = enabled;
159
+ this.registerSignalHandlers();
160
+ }
161
+ canAddTask() {
162
+ return this.isShutdownEnabled;
163
+ }
164
+ canRemoveTask() {
165
+ return true;
166
+ }
167
+ getTaskType() {
168
+ return "shutdown";
169
+ }
170
+ async executeTasksInPhase(phase, tasks) {
171
+ const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));
172
+ return Promise.allSettled(promises);
173
+ }
174
+ registerSignalHandlers() {
175
+ if (!this.isShutdownEnabled) return;
176
+ this.onSigTerm = () => {
177
+ this.logger.info(`Received ${chalk.default.yellow.bold("SIGTERM")} signal`);
178
+ this.run(0);
179
+ };
180
+ this.onSigInt = () => {
181
+ this.logger.info(`Received ${chalk.default.yellow.bold("SIGINT")} signal`);
182
+ this.run(0);
183
+ };
184
+ process.on("SIGTERM", this.onSigTerm);
185
+ process.on("SIGINT", this.onSigInt);
186
+ }
187
+ removeSignalHandlers() {
188
+ if (this.onSigTerm) {
189
+ process.off("SIGTERM", this.onSigTerm);
190
+ this.onSigTerm = null;
191
+ }
192
+ if (this.onSigInt) {
193
+ process.off("SIGINT", this.onSigInt);
194
+ this.onSigInt = null;
195
+ }
196
+ }
197
+ /**
198
+ * Adds a task to a specific shutdown phase with timeout.
199
+ *
200
+ * @param phase - The shutdown phase from {@link ShutdownPhase}
201
+ * @param taskName - Unique identifier for the task
202
+ * @param task - Async function to execute
203
+ * @param timeoutMs - Task timeout in milliseconds. {@default `5000`}
204
+ */
205
+ addTask(phase, taskName, task, timeoutMs = 5e3) {
206
+ super.addTask(phase, taskName, task, timeoutMs);
207
+ }
208
+ /**
209
+ * Removes a task from a specific shutdown phase.
210
+ *
211
+ * @param phase - The shutdown phase to remove from
212
+ * @param taskName - Name of the task to remove
213
+ * @returns True if task was found and removed
214
+ */
215
+ removeTask(phase, taskName) {
216
+ return super.removeTask(phase, taskName);
217
+ }
218
+ /**
219
+ * Executes the coordinated shutdown sequence.
220
+ *
221
+ * Runs all registered tasks across shutdown phases in reverse order.
222
+ * Tasks within each phase are executed in parallel for faster shutdown.
223
+ * Process exits with the specified code when complete.
224
+ *
225
+ * @param exitCode - Process exit code. {@default `0`}
226
+ * @param exitProcess - Whether to exit the process after shutdown. {@default `true`}
227
+ * @returns Promise that resolves when shutdown is complete
228
+ * @example
229
+ * ```typescript
230
+ * shutdown.addTask(ShutdownPhase.Services, 'database', () => db.disconnect(), 5000);
231
+ * await shutdown.run(0); // Graceful shutdown
232
+ * ```
233
+ */
234
+ async run(exitCode = 0, exitProcess = true) {
235
+ this.removeSignalHandlers();
236
+ if (this.isShuttingDown) {
237
+ this.logger.warn("Shutdown sequence already in progress");
238
+ return;
239
+ }
240
+ this.isShuttingDown = true;
241
+ this.exitCode = exitCode;
242
+ this.logger.info(`${chalk.default.bold.yellow("Starting")} coordinated shutdown with exit code ${chalk.default.bold.cyan(exitCode)}`);
243
+ this.emitSafe("shutdown:start");
244
+ try {
245
+ for (const phase of PHASE_ORDER$1) await this.runPhase(phase);
246
+ this.logger.info(`${chalk.default.bold.green("Coordinated shutdown completed")} successfully`);
247
+ this.emitSafe("shutdown:complete");
248
+ } catch (error) {
249
+ this.logger.error(`${chalk.default.bold.red("Coordinated shutdown failed")}`);
250
+ this.emitSafe("shutdown:error", error);
251
+ } finally {
252
+ if (exitProcess) {
253
+ this.logger.info(`${chalk.default.bold.red("Exiting")} process with code ${chalk.default.bold.cyan(this.exitCode)}`);
254
+ setTimeout(() => {
255
+ process.exit(this.exitCode);
256
+ }, LOG_FLUSH_DELAY_MS);
257
+ } else {
258
+ this.logger.info(`${chalk.default.bold.yellow("Skipping")} process exit (dev mode)`);
259
+ this.isShuttingDown = false;
260
+ }
261
+ }
262
+ }
263
+ };
264
+
265
+ //#endregion
266
+ //#region src/node/Lifecycle/CoordinatedStartup.ts
267
+ /**
268
+ * Startup phases for coordinated initialization
269
+ *
270
+ * Defines the order in which different components are initialized during bot startup.
271
+ */
272
+ let StartupPhase = /* @__PURE__ */ function(StartupPhase) {
273
+ /** Validate environment variables and config files */
274
+ StartupPhase[StartupPhase["Validation"] = 1] = "Validation";
275
+ /** Discover plugin constructors via decorators or registry */
276
+ StartupPhase[StartupPhase["Discovery"] = 2] = "Discovery";
277
+ /** Register plugin metadata and declared dependencies */
278
+ StartupPhase[StartupPhase["Registration"] = 3] = "Registration";
279
+ /** Inject and validate plugin-specific configuration */
280
+ StartupPhase[StartupPhase["Configuration"] = 4] = "Configuration";
281
+ /** Instantiate plugin classes with Core and arguments */
282
+ StartupPhase[StartupPhase["Instantiation"] = 5] = "Instantiation";
283
+ /** Activate plugins by calling their init/setup methods */
284
+ StartupPhase[StartupPhase["Activation"] = 6] = "Activation";
285
+ /** Mark seedcord as ready and start handling interactions */
286
+ StartupPhase[StartupPhase["Ready"] = 7] = "Ready";
287
+ return StartupPhase;
288
+ }({});
289
+ /** Define the order of phases */
290
+ const PHASE_ORDER = [
291
+ 1,
292
+ 2,
293
+ 3,
294
+ 4,
295
+ 5,
296
+ 6,
297
+ 7
298
+ ];
299
+ /**
300
+ * Manages bot startup lifecycle with ordered phases
301
+ *
302
+ * Coordinates initialization of all bot components in a predictable sequence.
303
+ * Tasks are executed within their designated phases to ensure proper dependency order.
304
+ */
305
+ var CoordinatedStartup = class extends CoordinatedLifecycle {
306
+ isStartingUp = false;
307
+ hasStarted = false;
308
+ constructor() {
309
+ super("Startup", PHASE_ORDER, StartupPhase);
310
+ }
311
+ /**
312
+ * Adds a task to a specific startup phase with timeout.
313
+ *
314
+ * @param phase - The startup phase from {@link StartupPhase}
315
+ * @param taskName - Unique identifier for the task
316
+ * @param task - Async function to execute
317
+ * @param timeoutMs - Task timeout in milliseconds. {@default `10000`}
318
+ */
319
+ addTask(phase, taskName, task, timeoutMs = 1e4) {
320
+ super.addTask(phase, taskName, task, timeoutMs);
321
+ }
322
+ canAddTask() {
323
+ if (this.hasStarted) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleAddAfterCompletion);
324
+ if (this.isStartingUp) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleAddDuringRun);
325
+ return true;
326
+ }
327
+ canRemoveTask() {
328
+ if (this.isStartingUp) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleRemoveDuringRun);
329
+ return true;
330
+ }
331
+ getTaskType() {
332
+ return "startup";
333
+ }
334
+ async executeTasksInPhase(phase, tasks) {
335
+ const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));
336
+ return Promise.allSettled(promises);
337
+ }
338
+ /**
339
+ * Executes the coordinated startup sequence.
340
+ *
341
+ * Runs all registered tasks across startup phases in the correct order.
342
+ * Each phase completes before the next phase begins. Tasks within a phase
343
+ * are executed sequentially to maintain predictable initialization.
344
+ *
345
+ * @returns Promise that resolves when startup is complete
346
+ * @throws An {@link Error} If startup fails or is called multiple times
347
+ * @example
348
+ * ```typescript
349
+ * const startup = new CoordinatedStartup();
350
+ * startup.addTask(StartupPhase.Services, 'database', () => db.connect(), 10000);
351
+ * await startup.run();
352
+ * ```
353
+ */
354
+ async run() {
355
+ if (this.hasStarted) {
356
+ this.logger.warn("Startup sequence has already completed");
357
+ return;
358
+ }
359
+ if (this.isStartingUp) {
360
+ this.logger.warn("Startup sequence already in progress");
361
+ return;
362
+ }
363
+ this.isStartingUp = true;
364
+ this.logger.info(`${chalk.default.bold.green("Starting")} coordinated startup sequence`);
365
+ this.emitSafe("startup:start");
366
+ try {
367
+ for (const phase of PHASE_ORDER) {
368
+ if (!this.isStartingUp) {
369
+ this.logger.warn("Startup sequence aborted");
370
+ return;
371
+ }
372
+ await this.runPhase(phase);
373
+ }
374
+ this.hasStarted = true;
375
+ this.logger.info(`${chalk.default.bold.green("Coordinated startup completed")} successfully`);
376
+ this.emitSafe("startup:complete");
377
+ } catch (error) {
378
+ if (!this.isStartingUp) {
379
+ this.logger.warn("Startup sequence aborted during error handling");
380
+ return;
381
+ }
382
+ this.logger.error(`${chalk.default.bold.red("Coordinated startup failed")}`);
383
+ this.emitSafe("startup:error", error);
384
+ throw error;
385
+ } finally {
386
+ this.isStartingUp = false;
387
+ }
388
+ }
389
+ async runTaskWithTimeout(phase, task) {
390
+ this.logger.info(`${chalk.default.italic("Starting")} task ${chalk.default.bold.cyan(task.name)} in phase ${chalk.default.bold.magenta(StartupPhase[phase])}`);
391
+ let timeoutId;
392
+ try {
393
+ await Promise.race([task.task(), new Promise((_, reject) => {
394
+ timeoutId = setTimeout(() => {
395
+ reject(new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));
396
+ }, task.timeout);
397
+ })]);
398
+ this.logger.info(`${chalk.default.italic("Completed")} task ${chalk.default.bold.cyan(task.name)} in phase ${chalk.default.bold.magenta(StartupPhase[phase])}`);
399
+ } catch (error) {
400
+ if (!this.isStartingUp) return;
401
+ this.logger.error(`${chalk.default.italic("Failed")} task ${chalk.default.bold.cyan(task.name)} in phase ${chalk.default.bold.magenta(StartupPhase[phase])}:`, error);
402
+ throw error;
403
+ } finally {
404
+ if (timeoutId) clearTimeout(timeoutId);
405
+ }
406
+ }
407
+ /**
408
+ * Aborts the startup sequence if it is currently running.
409
+ */
410
+ abort() {
411
+ if (!this.isStartingUp) return;
412
+ this.isStartingUp = false;
413
+ this.logger.warn("Aborting coordinated startup sequence");
414
+ }
415
+ /**
416
+ * Check if startup has completed
417
+ */
418
+ get isReady() {
419
+ return this.hasStarted;
420
+ }
421
+ /**
422
+ * Check if startup is currently running
423
+ */
424
+ get isRunning() {
425
+ return this.isStartingUp;
426
+ }
427
+ };
428
+
429
+ //#endregion
430
+ //#region src/node/HealthCheck.ts
431
+ const HTTP_OK = 200;
432
+ const HTTP_NOT_FOUND = 404;
433
+ const DEFAULT_HEALTH_CHECK_PORT = 6967;
434
+ const DEFAULT_HEALTH_CHECK_PATH = "/healthcheck";
435
+ /**
436
+ * HTTP health check service for monitoring bot status.
437
+ *
438
+ * Provides a simple HTTP endpoint that responds with JSON status
439
+ * information, useful for container orchestration and monitoring.
440
+ */
441
+ var HealthCheck = class {
442
+ /** @internal */
443
+ logger = new _seedcord_logger.Logger("HealthCheck");
444
+ port;
445
+ path;
446
+ host;
447
+ server;
448
+ constructor(shutdown, options) {
449
+ this.port = options?.port ?? DEFAULT_HEALTH_CHECK_PORT;
450
+ this.path = options?.path ?? DEFAULT_HEALTH_CHECK_PATH;
451
+ this.host = options?.host;
452
+ shutdown.addTask(2, "stop-healthcheck-server", async () => await this.stop());
453
+ }
454
+ /**
455
+ * Starts the health check server.
456
+ * @returns Promise that resolves when the server is listening
457
+ * @internal
458
+ */
459
+ async init() {
460
+ return new Promise((resolve, reject) => {
461
+ const server = (0, node_http.createServer)((req, res) => {
462
+ if (req.method === "GET" && req.url === this.path) {
463
+ res.writeHead(HTTP_OK, { "Content-Type": "application/json" });
464
+ res.end(JSON.stringify({
465
+ status: "ok",
466
+ timestamp: Date.now()
467
+ }));
468
+ } else {
469
+ res.writeHead(HTTP_NOT_FOUND, { "Content-Type": "application/json" });
470
+ res.end(JSON.stringify({ status: "not found" }));
471
+ }
472
+ });
473
+ this.server = server;
474
+ const onListenError = (err) => reject(err);
475
+ server.on("error", onListenError);
476
+ server.once("listening", () => {
477
+ server.removeListener("error", onListenError);
478
+ server.on("error", (err) => this.logger.error("Health check server error", err));
479
+ const address = this.host ?? "localhost";
480
+ this.logger.info(`${chalk.default.green.bold("✓")} Health check server listening on ${chalk.default.cyan(`http://${address}:${this.port}${this.path}`)}`);
481
+ resolve();
482
+ });
483
+ if (this.host) {
484
+ this.logger.debug(`Binding health check server to ${this.host}`);
485
+ server.listen(this.port, this.host);
486
+ } else {
487
+ this.logger.debug("Binding health check server to all interfaces");
488
+ server.listen(this.port);
489
+ }
490
+ });
491
+ }
492
+ /**
493
+ * Stops the health check server.
494
+ *
495
+ * @returns Promise that resolves when the server is closed
496
+ * @internal
497
+ */
498
+ stop() {
499
+ const server = this.server;
500
+ if (!server?.listening) return Promise.resolve();
501
+ return new Promise((resolve, reject) => {
502
+ server.once("close", () => resolve());
503
+ server.close((err) => {
504
+ if (err) {
505
+ reject(err);
506
+ return;
507
+ }
508
+ this.logger.info(chalk.default.bold.red("Health check server stopped"));
509
+ });
510
+ });
511
+ }
512
+ };
513
+
514
+ //#endregion
515
+ exports.CoordinatedLifecycle = CoordinatedLifecycle;
516
+ exports.CoordinatedShutdown = CoordinatedShutdown;
517
+ exports.CoordinatedStartup = CoordinatedStartup;
518
+ exports.HealthCheck = HealthCheck;
519
+ exports.ShutdownPhase = ShutdownPhase;
520
+ exports.StartupPhase = StartupPhase;
521
+ //# sourceMappingURL=node.index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.index.cjs","names":["TypedEventEmitter","Logger","SeedcordError","SeedcordErrorCode","PHASE_ORDER","SeedcordError","SeedcordErrorCode","Logger"],"sources":["../src/node/Lifecycle/CoordinatedLifecycle.ts","../src/node/Lifecycle/CoordinatedShutdown.ts","../src/node/Lifecycle/CoordinatedStartup.ts","../src/node/HealthCheck.ts"],"sourcesContent":["/*\n * Inspired by Akka Coordinated Shutdown: https://doc.akka.io/libraries/akka-core/current/coordinated-shutdown.html\n * and Lewis's implementation in a private repo elsewhere (https://github.com/Yomanz)\n */\n\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { TypedEventEmitter } from '@seedcord/event-emitter';\nimport { Logger } from '@seedcord/logger';\nimport chalk from 'chalk';\n\nimport type { LifecycleTask } from './LifecycleTypes';\nimport type { EventMap } from '@seedcord/event-emitter';\n\n/**\n * Abstract base class for coordinated lifecycle management (startup/shutdown)\n */\nexport abstract class CoordinatedLifecycle<\n TPhase extends number,\n TEvents extends EventMap<TEvents>\n> extends TypedEventEmitter<TEvents> {\n protected readonly logger: Logger;\n protected readonly tasksMap = new Map<TPhase, LifecycleTask[]>();\n\n protected constructor(\n loggerName: string,\n protected readonly phaseOrder: TPhase[],\n protected readonly phaseEnum: Record<number, string>\n ) {\n super();\n this.logger = new Logger(loggerName);\n this.phaseOrder.forEach((phase) => this.tasksMap.set(phase, []));\n }\n\n /**\n * Adds a lifecycle task to a specific phase.\n *\n * Tasks are executed in phase order during lifecycle operations.\n * Each task has a timeout to prevent hanging operations.\n *\n * @param phase - The lifecycle phase to add the task to\n * @param taskName - Unique name for the task (used for logging and removal)\n * @param task - Async function to execute during the phase\n * @param timeoutMs - Maximum time allowed for task execution in milliseconds\n * @example\n * ```typescript\n * lifecycle.addTask(StartupPhase.Services, 'start-database', async () => {\n * await database.connect();\n * }, 10000);\n * ```\n */\n public addTask(phase: TPhase, taskName: string, task: () => Promise<void>, timeoutMs: number): void {\n if (!this.canAddTask()) return;\n\n const tasks = this.tasksMap.get(phase);\n if (!tasks) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleUnknownPhase, [phase]);\n }\n\n tasks.push({ name: taskName, task, timeout: timeoutMs });\n this.logger.debug(\n `${chalk.italic('Added')} ${this.getTaskType()} task ${chalk.bold.cyan(taskName)} to phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n }\n\n /**\n * Removes a lifecycle task from a specific phase.\n *\n * @param phase - The lifecycle phase to remove the task from\n * @param taskName - Name of the task to remove\n * @returns True if the task was found and removed, false otherwise\n */\n public removeTask(phase: TPhase, taskName: string): boolean {\n if (!this.canRemoveTask()) return false;\n\n const tasks = this.tasksMap.get(phase);\n if (!tasks) return false;\n\n const initialLength = tasks.length;\n const filteredTasks = tasks.filter((task) => task.name !== taskName);\n this.tasksMap.set(phase, filteredTasks);\n\n const removed = initialLength !== filteredTasks.length;\n if (removed) {\n this.logger.debug(\n `${chalk.italic('Removed')} ${this.getTaskType()} task ${chalk.bold.cyan(taskName)} from phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n }\n\n return removed;\n }\n\n /**\n * Run all tasks in a specific phase\n */\n protected async runPhase(phase: TPhase): Promise<void> {\n const tasks = this.tasksMap.get(phase) ?? [];\n if (tasks.length === 0) {\n this.logger.warn(`No tasks to run in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`);\n return;\n }\n\n this.logger.info(\n `${chalk.bold.yellow('Running')} ${this.getTaskType()} phase ${chalk.bold.magenta(this.phaseEnum[phase])} with ${chalk.bold.cyan(tasks.length)} tasks`\n );\n this.emitPhase(phase, 'start');\n\n const results: PromiseSettledResult<void>[] = await this.executeTasksInPhase(phase, tasks);\n\n const failures = results.filter((r) => r.status === 'rejected').length;\n if (failures > 0) {\n // raw phase name here. chalk's ANSI codes would otherwise leak into the serialized\n // error message (e.g. the unknown-exception webhook payload).\n throw new SeedcordError(SeedcordErrorCode.LifecyclePhaseFailures, [this.phaseEnum[phase], failures]);\n }\n\n this.logger.info(\n `Phase ${chalk.bold.magenta(this.phaseEnum[phase])} ${chalk.bold.green('completed successfully')}`\n );\n\n this.emitPhase(phase, 'complete');\n }\n\n /**\n * Run a single task with timeout\n */\n protected async runTaskWithTimeout(phase: TPhase, task: LifecycleTask): Promise<void> {\n this.logger.info(\n `${chalk.italic('Starting')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n\n let timeoutId: NodeJS.Timeout | undefined;\n\n try {\n await Promise.race([\n task.task(),\n new Promise<void>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));\n }, task.timeout);\n })\n ]);\n\n this.logger.info(\n `${chalk.italic('Completed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n } catch (error) {\n this.logger.error(\n `${chalk.italic('Failed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}:`,\n error\n );\n throw error;\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n // The phase key is interpolated from phaseOrder at runtime and is always a valid no-payload key of\n // the subclass event map, but TS can't correlate a template-literal key with the generic TEvents.\n private emitPhase(phase: TPhase, action: 'start' | 'complete'): void {\n this.emitSafeRaw(`phase:${phase}:${action}`);\n }\n\n protected override onListenerError(error: unknown, event: string | symbol): void {\n this.logger.error(`listener for ${String(event)} threw`, error);\n }\n\n // Abstract methods to be implemented by subclasses\n protected abstract canAddTask(): boolean;\n protected abstract canRemoveTask(): boolean;\n protected abstract getTaskType(): string;\n protected abstract executeTasksInPhase(\n phase: TPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]>;\n}\n","import chalk from 'chalk';\n\nimport { CoordinatedLifecycle } from './CoordinatedLifecycle';\n\nimport type { LifecycleTask, PhaseEventMap } from './LifecycleTypes';\nimport type { UnionToTuple } from 'type-fest';\n\n/**\n * Shutdown phases for coordinated application shutdown.\n */\nexport enum ShutdownPhase {\n /** Stop accepting new requests/interactions */\n StopAcceptingRequests = 1,\n /** Stop background services (health checks, etc.) */\n StopServices,\n /** Disconnect from external resources (database, APIs) */\n ExternalResources,\n /** Disconnect from Discord */\n DiscordCleanup,\n /** Final cleanup tasks */\n FinalCleanup\n}\n\n/** Define the order of phases */\nconst PHASE_ORDER: ShutdownPhase[] = [\n ShutdownPhase.StopAcceptingRequests,\n ShutdownPhase.StopServices,\n ShutdownPhase.ExternalResources,\n ShutdownPhase.DiscordCleanup,\n ShutdownPhase.FinalCleanup\n];\n\n/**\n * Strict-event-emitter payload map for coordinated shutdown phases.\n */\nexport type CoordinatedShutdownEvents = PhaseEventMap<'shutdown', UnionToTuple<ShutdownPhase>>;\n\n// Delay process.exit so the logger's file sink has a window to flush before the event loop dies.\nconst LOG_FLUSH_DELAY_MS = 500;\n\n/**\n * CoordinatedShutdown manages graceful application shutdown by executing registered tasks across defined phases.\n *\n * It listens for termination signals (SIGINT, SIGTERM) and runs tasks in parallel within each phase.\n * Tasks can be added or removed dynamically, and each task has an associated timeout.\n */\nexport class CoordinatedShutdown extends CoordinatedLifecycle<ShutdownPhase, CoordinatedShutdownEvents> {\n private readonly isShutdownEnabled: boolean;\n\n private isShuttingDown = false;\n private exitCode = 0;\n private onSigTerm: (() => void) | null = null;\n private onSigInt: (() => void) | null = null;\n\n public constructor(enabled = true) {\n super('Shutdown', PHASE_ORDER, ShutdownPhase);\n\n this.isShutdownEnabled = enabled;\n this.registerSignalHandlers();\n }\n\n protected canAddTask(): boolean {\n return this.isShutdownEnabled;\n }\n\n protected canRemoveTask(): boolean {\n return true;\n }\n\n protected getTaskType(): string {\n return 'shutdown';\n }\n\n protected async executeTasksInPhase(\n phase: ShutdownPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]> {\n const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));\n return Promise.allSettled(promises);\n }\n\n private registerSignalHandlers(): void {\n if (!this.isShutdownEnabled) return;\n\n this.onSigTerm = () => {\n this.logger.info(`Received ${chalk.yellow.bold('SIGTERM')} signal`);\n void this.run(0);\n };\n\n this.onSigInt = () => {\n this.logger.info(`Received ${chalk.yellow.bold('SIGINT')} signal`);\n void this.run(0);\n };\n\n process.on('SIGTERM', this.onSigTerm);\n process.on('SIGINT', this.onSigInt);\n }\n\n private removeSignalHandlers(): void {\n if (this.onSigTerm) {\n process.off('SIGTERM', this.onSigTerm);\n this.onSigTerm = null;\n }\n if (this.onSigInt) {\n process.off('SIGINT', this.onSigInt);\n this.onSigInt = null;\n }\n }\n\n /**\n * Adds a task to a specific shutdown phase with timeout.\n *\n * @param phase - The shutdown phase from {@link ShutdownPhase}\n * @param taskName - Unique identifier for the task\n * @param task - Async function to execute\n * @param timeoutMs - Task timeout in milliseconds. {@default `5000`}\n */\n public override addTask(phase: ShutdownPhase, taskName: string, task: () => Promise<void>, timeoutMs = 5000): void {\n super.addTask(phase, taskName, task, timeoutMs);\n }\n\n /**\n * Removes a task from a specific shutdown phase.\n *\n * @param phase - The shutdown phase to remove from\n * @param taskName - Name of the task to remove\n * @returns True if task was found and removed\n */\n public override removeTask(phase: ShutdownPhase, taskName: string): boolean {\n return super.removeTask(phase, taskName);\n }\n\n /**\n * Executes the coordinated shutdown sequence.\n *\n * Runs all registered tasks across shutdown phases in reverse order.\n * Tasks within each phase are executed in parallel for faster shutdown.\n * Process exits with the specified code when complete.\n *\n * @param exitCode - Process exit code. {@default `0`}\n * @param exitProcess - Whether to exit the process after shutdown. {@default `true`}\n * @returns Promise that resolves when shutdown is complete\n * @example\n * ```typescript\n * shutdown.addTask(ShutdownPhase.Services, 'database', () => db.disconnect(), 5000);\n * await shutdown.run(0); // Graceful shutdown\n * ```\n */\n public async run(exitCode = 0, exitProcess = true): Promise<void> {\n this.removeSignalHandlers();\n\n if (this.isShuttingDown) {\n this.logger.warn('Shutdown sequence already in progress');\n return;\n }\n\n this.isShuttingDown = true;\n this.exitCode = exitCode;\n this.logger.info(\n `${chalk.bold.yellow('Starting')} coordinated shutdown with exit code ${chalk.bold.cyan(exitCode)}`\n );\n this.emitSafe('shutdown:start');\n\n try {\n for (const phase of PHASE_ORDER) {\n await this.runPhase(phase);\n }\n\n this.logger.info(`${chalk.bold.green('Coordinated shutdown completed')} successfully`);\n this.emitSafe('shutdown:complete');\n } catch (error) {\n this.logger.error(`${chalk.bold.red('Coordinated shutdown failed')}`);\n this.emitSafe('shutdown:error', error);\n } finally {\n if (exitProcess) {\n this.logger.info(`${chalk.bold.red('Exiting')} process with code ${chalk.bold.cyan(this.exitCode)}`);\n setTimeout(() => {\n process.exit(this.exitCode);\n }, LOG_FLUSH_DELAY_MS);\n } else {\n this.logger.info(`${chalk.bold.yellow('Skipping')} process exit (dev mode)`);\n this.isShuttingDown = false;\n }\n }\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport chalk from 'chalk';\n\nimport { CoordinatedLifecycle } from './CoordinatedLifecycle';\n\nimport type { LifecycleTask, PhaseEventMap } from './LifecycleTypes';\nimport type { UnionToTuple } from 'type-fest';\n\n/**\n * Startup phases for coordinated initialization\n *\n * Defines the order in which different components are initialized during bot startup.\n */\nexport enum StartupPhase {\n /** Validate environment variables and config files */\n Validation = 1,\n /** Discover plugin constructors via decorators or registry */\n Discovery,\n /** Register plugin metadata and declared dependencies */\n Registration,\n /** Inject and validate plugin-specific configuration */\n Configuration,\n /** Instantiate plugin classes with Core and arguments */\n Instantiation,\n /** Activate plugins by calling their init/setup methods */\n Activation,\n /** Mark seedcord as ready and start handling interactions */\n Ready\n}\n\n/** Define the order of phases */\nconst PHASE_ORDER: StartupPhase[] = [\n StartupPhase.Validation,\n StartupPhase.Discovery,\n StartupPhase.Registration,\n StartupPhase.Configuration,\n StartupPhase.Instantiation,\n StartupPhase.Activation,\n StartupPhase.Ready\n];\n\n/**\n * Strict-event-emitter payload map for coordinated startup phases.\n */\nexport type CoordinatedStartupEvents = PhaseEventMap<'startup', UnionToTuple<StartupPhase>>;\n\n/**\n * Manages bot startup lifecycle with ordered phases\n *\n * Coordinates initialization of all bot components in a predictable sequence.\n * Tasks are executed within their designated phases to ensure proper dependency order.\n */\nexport class CoordinatedStartup extends CoordinatedLifecycle<StartupPhase, CoordinatedStartupEvents> {\n private isStartingUp = false;\n private hasStarted = false;\n\n public constructor() {\n super('Startup', PHASE_ORDER, StartupPhase);\n }\n\n /**\n * Adds a task to a specific startup phase with timeout.\n *\n * @param phase - The startup phase from {@link StartupPhase}\n * @param taskName - Unique identifier for the task\n * @param task - Async function to execute\n * @param timeoutMs - Task timeout in milliseconds. {@default `10000`}\n */\n public override addTask(\n phase: StartupPhase,\n taskName: string,\n task: () => Promise<void>,\n timeoutMs = 10_000\n ): void {\n super.addTask(phase, taskName, task, timeoutMs);\n }\n\n protected canAddTask(): boolean {\n if (this.hasStarted) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleAddAfterCompletion);\n }\n\n if (this.isStartingUp) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleAddDuringRun);\n }\n\n return true;\n }\n\n protected canRemoveTask(): boolean {\n if (this.isStartingUp) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleRemoveDuringRun);\n }\n\n return true;\n }\n\n protected getTaskType(): string {\n return 'startup';\n }\n\n protected async executeTasksInPhase(\n phase: StartupPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]> {\n const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));\n return Promise.allSettled(promises);\n }\n\n /**\n * Executes the coordinated startup sequence.\n *\n * Runs all registered tasks across startup phases in the correct order.\n * Each phase completes before the next phase begins. Tasks within a phase\n * are executed sequentially to maintain predictable initialization.\n *\n * @returns Promise that resolves when startup is complete\n * @throws An {@link Error} If startup fails or is called multiple times\n * @example\n * ```typescript\n * const startup = new CoordinatedStartup();\n * startup.addTask(StartupPhase.Services, 'database', () => db.connect(), 10000);\n * await startup.run();\n * ```\n */\n public async run(): Promise<void> {\n if (this.hasStarted) {\n this.logger.warn('Startup sequence has already completed');\n return;\n }\n\n if (this.isStartingUp) {\n this.logger.warn('Startup sequence already in progress');\n return;\n }\n\n this.isStartingUp = true;\n this.logger.info(`${chalk.bold.green('Starting')} coordinated startup sequence`);\n this.emitSafe('startup:start');\n\n try {\n for (const phase of PHASE_ORDER) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- abort() can flip isStartingUp to false mid-loop from the cli\n if (!this.isStartingUp) {\n this.logger.warn('Startup sequence aborted');\n return;\n }\n await this.runPhase(phase);\n }\n\n this.hasStarted = true;\n this.logger.info(`${chalk.bold.green('Coordinated startup completed')} successfully`);\n this.emitSafe('startup:complete');\n } catch (error) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- abort() can flip isStartingUp to false before this catch runs\n if (!this.isStartingUp) {\n this.logger.warn('Startup sequence aborted during error handling');\n return;\n }\n this.logger.error(`${chalk.bold.red('Coordinated startup failed')}`);\n this.emitSafe('startup:error', error);\n throw error;\n } finally {\n this.isStartingUp = false;\n }\n }\n\n protected override async runTaskWithTimeout(phase: StartupPhase, task: LifecycleTask): Promise<void> {\n this.logger.info(\n `${chalk.italic('Starting')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(StartupPhase[phase])}`\n );\n\n let timeoutId: NodeJS.Timeout | undefined;\n\n try {\n await Promise.race([\n task.task(),\n new Promise<void>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));\n }, task.timeout);\n })\n ]);\n\n this.logger.info(\n `${chalk.italic('Completed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(StartupPhase[phase])}`\n );\n } catch (error) {\n if (!this.isStartingUp) {\n return;\n }\n\n this.logger.error(\n `${chalk.italic('Failed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(StartupPhase[phase])}:`,\n error\n );\n throw error;\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n /**\n * Aborts the startup sequence if it is currently running.\n */\n public abort(): void {\n if (!this.isStartingUp) return;\n\n this.isStartingUp = false;\n this.logger.warn('Aborting coordinated startup sequence');\n }\n\n /**\n * Check if startup has completed\n */\n public get isReady(): boolean {\n return this.hasStarted;\n }\n\n /**\n * Check if startup is currently running\n */\n public get isRunning(): boolean {\n return this.isStartingUp;\n }\n}\n","import { createServer } from 'node:http';\n\nimport { Logger } from '@seedcord/logger';\nimport chalk from 'chalk';\n\nimport { ShutdownPhase } from './Lifecycle/CoordinatedShutdown';\n\nimport type { CoordinatedShutdown } from './Lifecycle/CoordinatedShutdown';\nimport type { HealthCheckConfig } from '@seedcord/types';\nimport type { IncomingMessage, Server, ServerResponse } from 'node:http';\n\nconst HTTP_OK = 200;\nconst HTTP_NOT_FOUND = 404;\n\nconst DEFAULT_HEALTH_CHECK_PORT = 6967;\nconst DEFAULT_HEALTH_CHECK_PATH = '/healthcheck';\n\n/**\n * HTTP health check service for monitoring bot status.\n *\n * Provides a simple HTTP endpoint that responds with JSON status\n * information, useful for container orchestration and monitoring.\n */\nexport class HealthCheck {\n /** @internal */\n public readonly logger = new Logger('HealthCheck');\n\n public readonly port: number;\n public readonly path: string;\n public readonly host: string | undefined;\n\n private server?: Server;\n\n constructor(shutdown: CoordinatedShutdown, options?: HealthCheckConfig) {\n this.port = options?.port ?? DEFAULT_HEALTH_CHECK_PORT;\n this.path = options?.path ?? DEFAULT_HEALTH_CHECK_PATH;\n this.host = options?.host;\n\n shutdown.addTask(ShutdownPhase.StopServices, 'stop-healthcheck-server', async () => await this.stop());\n }\n\n /**\n * Starts the health check server.\n * @returns Promise that resolves when the server is listening\n * @internal\n */\n public async init(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n if (req.method === 'GET' && req.url === this.path) {\n res.writeHead(HTTP_OK, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok', timestamp: Date.now() }));\n } else {\n res.writeHead(HTTP_NOT_FOUND, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'not found' }));\n }\n });\n this.server = server;\n\n const onListenError = (err: Error): void => reject(err);\n server.on('error', onListenError);\n\n server.once('listening', () => {\n // Swap the listen-time reject handler for a logging one: keeping it would reject an\n // already-settled promise on a late error, and removing it without a replacement\n // would crash the process on an unhandled 'error' event.\n server.removeListener('error', onListenError);\n server.on('error', (err) => this.logger.error('Health check server error', err));\n\n const address = this.host ?? 'localhost';\n this.logger.info(\n `${chalk.green.bold('✓')} Health check server listening on ${chalk.cyan(`http://${address}:${this.port}${this.path}`)}`\n );\n resolve();\n });\n\n if (this.host) {\n this.logger.debug(`Binding health check server to ${this.host}`);\n server.listen(this.port, this.host);\n } else {\n this.logger.debug('Binding health check server to all interfaces');\n server.listen(this.port);\n }\n });\n }\n\n /**\n * Stops the health check server.\n *\n * @returns Promise that resolves when the server is closed\n * @internal\n */\n public stop(): Promise<void> {\n const server = this.server;\n // close() on a non-listening server invokes its callback with ERR_SERVER_NOT_RUNNING and\n // never fires 'close', so without this guard the promise would hang the shutdown phase.\n if (!server?.listening) return Promise.resolve();\n\n return new Promise((resolve, reject) => {\n server.once('close', () => resolve());\n server.close((err) => {\n if (err) {\n reject(err);\n return;\n }\n this.logger.info(chalk.bold.red('Health check server stopped'));\n });\n });\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export type * from './node.index.d.mts'