seedcord 0.1.0-alpha.0 → 0.1.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -63,9 +63,7 @@ var Globals = class extends envapt.Envapter {
63
63
  // Unknown Exception Webhook URL
64
64
  static unknownExceptionWebhookUrl;
65
65
  // Variables
66
- /**
67
- * The default color of the bot's embeds. Can simply override by `Globals.botColor =`
68
- */
66
+ /** Default color for bot embeds - can be overridden by setting Globals.botColor */
69
67
  static botColor = this.isProduction ? "#fe565a" : "#3fa045";
70
68
  };
71
69
  _ts_decorate([
@@ -111,9 +109,9 @@ var BuilderTypes = {
111
109
  menu_mentionable: discord_js.MentionableSelectMenuBuilder,
112
110
  menu_role: discord_js.RoleSelectMenuBuilder,
113
111
  modal: discord_js.ModalBuilder,
112
+ context_menu: discord_js.ContextMenuCommandBuilder,
114
113
  subcommand: discord_js.SlashCommandSubcommandBuilder,
115
- group: discord_js.SlashCommandSubcommandGroupBuilder,
116
- context_menu: discord_js.ContextMenuCommandBuilder
114
+ group: discord_js.SlashCommandSubcommandGroupBuilder
117
115
  };
118
116
  var RowTypes = {
119
117
  button: discord_js.ActionRowBuilder,
@@ -137,9 +135,12 @@ var BaseComponent = class BaseComponent2 {
137
135
  this._component = new ComponentClass();
138
136
  }
139
137
  /**
140
- * @description Returns the instantiated component
141
- * @note Use this for configuring the component using its instance setters.
142
- * @usage `this.instance.someMethod()`
138
+ * Gets the component instance for configuration
139
+ *
140
+ * Use this to access Discord.js builder methods like setTitle(), setDescription(), etc.
141
+ *
142
+ * @protected Use this in your component classes to configure the builder
143
+ * @example this.instance.setTitle('My Modal')
143
144
  */
144
145
  get instance() {
145
146
  return this._component;
@@ -160,6 +161,16 @@ var BuilderComponent = class extends BaseComponent {
160
161
  get component() {
161
162
  return this.instance;
162
163
  }
164
+ /**
165
+ * Builds a customId string for interactive components
166
+ *
167
+ * Creates customIds in the format "prefix:arg1-arg2-arg3" for buttons, modals, etc.
168
+ * Arguments are joined with hyphens and separated from prefix with a colon.
169
+ *
170
+ * @param prefix - The route prefix that handlers will match against
171
+ * @param args - Additional arguments to encode in the customId
172
+ * @returns Formatted customId string
173
+ */
163
174
  buildCustomId(prefix, ...args) {
164
175
  if (args.length === 0) return prefix;
165
176
  return `${prefix}:${args.join("-")}`;
@@ -181,6 +192,11 @@ var ModalRow = class ModalRow2 extends RowComponent {
181
192
  static {
182
193
  __name(this, "ModalRow");
183
194
  }
195
+ /**
196
+ * Creates a new modal action row with the specified component.
197
+ *
198
+ * @param component - The modal field component to wrap in an action row
199
+ */
184
200
  constructor(component) {
185
201
  super("modal");
186
202
  this.instance.addComponents(component);
@@ -202,6 +218,9 @@ var BaseErrorEmbed = class extends BuilderComponent {
202
218
  static {
203
219
  __name(this, "BaseErrorEmbed");
204
220
  }
221
+ /**
222
+ * Creates a new error embed with default configuration.
223
+ */
205
224
  constructor() {
206
225
  super("embed");
207
226
  this.instance.setTitle("Cannot Proceed");
@@ -218,27 +237,32 @@ var CustomError = class extends Error {
218
237
  super(message), this.message = message;
219
238
  Error.captureStackTrace(this, this.constructor);
220
239
  }
240
+ /**
241
+ * Whether this error should be emitted to logs
242
+ *
243
+ * Controls logging behavior. Errors with emit=true will always be logged,
244
+ * while emit=false errors may be suppressed in production.
245
+ *
246
+ * @returns True if the error should be logged
247
+ */
221
248
  get emit() {
222
249
  return this._emit;
223
250
  }
224
251
  };
225
252
 
226
253
  // src/bot/errors/Database.ts
227
- var DatabaseConnectionFailure = class extends CustomError {
228
- static {
229
- __name(this, "DatabaseConnectionFailure");
230
- }
231
- constructor(message = "Failed to connect to the database.") {
232
- super(message);
233
- this.response.setDescription("Failed to connect to the database.");
234
- }
235
- };
236
254
  var DatabaseError = class extends CustomError {
237
255
  static {
238
256
  __name(this, "DatabaseError");
239
257
  }
240
258
  uuid;
241
259
  _emit = true;
260
+ /**
261
+ * Creates a new DatabaseError.
262
+ *
263
+ * @param message - The error message describing what went wrong
264
+ * @param uuid - A unique identifier for this specific error instance
265
+ */
242
266
  constructor(message, uuid) {
243
267
  super(message), this.uuid = uuid;
244
268
  this.name = "DatabaseError";
@@ -346,48 +370,125 @@ ${parts.join(" ")}`;
346
370
  transports: transportsArray
347
371
  });
348
372
  }
373
+ /**
374
+ * Logs an error message with optional additional data.
375
+ *
376
+ * @param msg - The error message to log
377
+ * @param args - Additional data to include in the log entry
378
+ */
349
379
  error(msg, ...args) {
350
380
  this.logger.error(msg, ...args);
351
381
  }
382
+ /**
383
+ * Logs a warning message with optional additional data.
384
+ *
385
+ * @param msg - The warning message to log
386
+ * @param args - Additional data to include in the log entry
387
+ */
352
388
  warn(msg, ...args) {
353
389
  this.logger.warn(msg, ...args);
354
390
  }
391
+ /**
392
+ * Logs an informational message with optional additional data.
393
+ *
394
+ * @param msg - The informational message to log
395
+ * @param args - Additional data to include in the log entry
396
+ */
355
397
  info(msg, ...args) {
356
398
  this.logger.info(msg, ...args);
357
399
  }
400
+ /**
401
+ * Logs an HTTP-related message with optional additional data.
402
+ *
403
+ * @param msg - The HTTP message to log
404
+ * @param args - Additional data to include in the log entry
405
+ */
358
406
  http(msg, ...args) {
359
407
  this.logger.http(msg, ...args);
360
408
  }
409
+ /**
410
+ * Logs a verbose message with optional additional data.
411
+ *
412
+ * @param msg - The verbose message to log
413
+ * @param args - Additional data to include in the log entry
414
+ */
361
415
  verbose(msg, ...args) {
362
416
  this.logger.verbose(msg, ...args);
363
417
  }
418
+ /**
419
+ * Logs a debug message with optional additional data.
420
+ *
421
+ * @param msg - The debug message to log
422
+ * @param args - Additional data to include in the log entry
423
+ */
364
424
  debug(msg, ...args) {
365
425
  this.logger.debug(msg, ...args);
366
426
  }
427
+ /**
428
+ * Logs a silly/trace level message with optional additional data.
429
+ *
430
+ * @param msg - The silly message to log
431
+ * @param args - Additional data to include in the log entry
432
+ */
367
433
  silly(msg, ...args) {
368
434
  this.logger.silly(msg, ...args);
369
435
  }
370
- // eslint-disable-next-line @typescript-eslint/naming-convention
436
+ /**
437
+ * Static method to log an error message with a specific prefix.
438
+ * Creates or retrieves a logger instance for the given prefix.
439
+ *
440
+ * @param prefix - The logger prefix/label to use
441
+ * @param msg - The error message to log
442
+ * @param args - Additional data to include in the log entry
443
+ */
371
444
  static Error(prefix, msg, ...args) {
372
445
  const logger = this.instance(prefix);
373
446
  logger.error(msg, ...args);
374
447
  }
375
- // eslint-disable-next-line @typescript-eslint/naming-convention
448
+ /**
449
+ * Static method to log an informational message with a specific prefix.
450
+ * Creates or retrieves a logger instance for the given prefix.
451
+ *
452
+ * @param prefix - The logger prefix/label to use
453
+ * @param msg - The informational message to log
454
+ * @param args - Additional data to include in the log entry
455
+ */
376
456
  static Info(prefix, msg, ...args) {
377
457
  const logger = this.instance(prefix);
378
458
  logger.info(msg, ...args);
379
459
  }
380
- // eslint-disable-next-line @typescript-eslint/naming-convention
460
+ /**
461
+ * Static method to log a warning message with a specific prefix.
462
+ * Creates or retrieves a logger instance for the given prefix.
463
+ *
464
+ * @param prefix - The logger prefix/label to use
465
+ * @param msg - The warning message to log
466
+ * @param args - Additional data to include in the log entry
467
+ */
381
468
  static Warn(prefix, msg, ...args) {
382
469
  const logger = this.instance(prefix);
383
470
  logger.warn(msg, ...args);
384
471
  }
385
- // eslint-disable-next-line @typescript-eslint/naming-convention
472
+ /**
473
+ * Static method to log a debug message with a specific prefix.
474
+ * Creates or retrieves a logger instance for the given prefix.
475
+ *
476
+ * @param prefix - The logger prefix/label to use
477
+ * @param msg - The debug message to log
478
+ * @param args - Additional data to include in the log entry
479
+ */
386
480
  static Debug(prefix, msg, ...args) {
387
481
  const logger = this.instance(prefix);
388
482
  logger.debug(msg, ...args);
389
483
  }
390
- // eslint-disable-next-line @typescript-eslint/naming-convention
484
+ /**
485
+ * Static method to log a silly/trace level message with a specific prefix.
486
+ * Creates or retrieves a logger instance for the given prefix.
487
+ *
488
+ * @param prefix - The logger prefix/label to use
489
+ * @param msg - The silly message to log
490
+ * @param args - Additional data to include in the log entry
491
+ */
391
492
  static Silly(prefix, msg, ...args) {
392
493
  const logger = this.instance(prefix);
393
494
  logger.silly(msg, ...args);
@@ -498,17 +599,17 @@ async function traverseDirectory(dir, callback) {
498
599
  }
499
600
  }
500
601
  __name(traverseDirectory, "traverseDirectory");
501
- function throwCustomError(error, message, customError) {
602
+ function throwCustomError(error, message, CustomError2) {
502
603
  const uuid = crypto.randomUUID();
503
604
  Logger.Error("Throwing Custom Error", error.name);
504
- if (typeof customError === typeof DatabaseError) {
605
+ if (typeof CustomError2 === typeof DatabaseError) {
505
606
  const errorMessage = error instanceof Error ? error.message : message;
506
- throw new customError(errorMessage, uuid);
607
+ throw new CustomError2(errorMessage, uuid);
507
608
  } else {
508
609
  if (error instanceof Error) {
509
- throw new customError(`${message}: ${error.message ? error.message : error.toString()}`);
610
+ throw new CustomError2(`${message}: ${error.message ? error.message : error.toString()}`);
510
611
  } else {
511
- throw new customError(message);
612
+ throw new CustomError2(message);
512
613
  }
513
614
  }
514
615
  }
@@ -647,14 +748,18 @@ var BaseHandler = class BaseHandler2 {
647
748
  return this.event;
648
749
  }
649
750
  /**
650
- * Get the arguments passed from the customId
651
- * For example, if customId is "accept:user123-guild456", args will be ["user123", "guild456"]
751
+ * Gets arguments parsed from interaction customId
752
+ *
753
+ * Arguments are extracted from customId using ":" and "-" separators.
754
+ * For customId "accept:user123-guild456", returns ["user123", "guild456"]
652
755
  */
653
756
  getArgs() {
654
757
  return this.args;
655
758
  }
656
759
  /**
657
- * Get a specific argument by index
760
+ * Gets a specific argument by index from parsed customId
761
+ * @param index - Zero-based index of the argument to retrieve
762
+ * @returns The argument at the specified index, or undefined if not found
658
763
  */
659
764
  getArg(index) {
660
765
  return this.args[index];
@@ -680,6 +785,7 @@ var AutocompleteHandler = class extends BaseHandler {
680
785
  static {
681
786
  __name(this, "AutocompleteHandler");
682
787
  }
788
+ /** The currently focused autocomplete option (Based on what you set in \@AutocompleteRoute) */
683
789
  focused;
684
790
  constructor(event, core, args) {
685
791
  super(event, core, args);
@@ -886,11 +992,23 @@ var ErrorHandlingUtils = class {
886
992
  __name(this, "ErrorHandlingUtils");
887
993
  }
888
994
  static logger = new Logger("Errors");
889
- static handleError(error, core, guild, user) {
995
+ /**
996
+ * Processes an error and extracts the standardized response, if available.
997
+ *
998
+ * Handles different error types (CustomError, DatabaseError) with appropriate
999
+ * logging, side effects, and user-facing error messages.
1000
+ *
1001
+ * @param error - The error to process
1002
+ * @param core - The core framework instance
1003
+ * @param guild - The guild where the error occurred (if any)
1004
+ * @param user - The user who triggered the error (if any)
1005
+ * @returns Object containing UUID and formatted error response embed
1006
+ */
1007
+ static extractErrorResponse(error, core, guild, user) {
890
1008
  const uuid = crypto2__namespace.randomUUID();
891
1009
  if (error instanceof CustomError) {
892
1010
  if (error instanceof DatabaseError) {
893
- core.hooks.emit("unknownException", {
1011
+ core.effects.emit("unknownException", {
894
1012
  uuid,
895
1013
  error,
896
1014
  guild,
@@ -906,7 +1024,7 @@ var ErrorHandlingUtils = class {
906
1024
  };
907
1025
  }
908
1026
  this.logger.error(uuid, error);
909
- core.hooks.emit("unknownException", {
1027
+ core.effects.emit("unknownException", {
910
1028
  uuid,
911
1029
  error,
912
1030
  guild,
@@ -945,7 +1063,7 @@ function Catchable(options) {
945
1063
  if (!(error instanceof Error)) throw error;
946
1064
  this.setErrored();
947
1065
  if (log) console.error(error);
948
- const { response } = ErrorHandlingUtils.handleError(error, this.core, interaction.guild, interaction.user);
1066
+ const { response } = ErrorHandlingUtils.extractErrorResponse(error, this.core, interaction.guild, interaction.user);
949
1067
  const res = {
950
1068
  embeds: [
951
1069
  response
@@ -1314,6 +1432,24 @@ var Pluggable = class _Pluggable {
1314
1432
  this.isInitialized = true;
1315
1433
  return this;
1316
1434
  }
1435
+ /**
1436
+ * Attaches a plugin to this instance
1437
+ *
1438
+ * Plugins provide external functionality and are initialized during the specified startup phase.
1439
+ * The plugin instance becomes available as a property in `core` wherever it's available.
1440
+ *
1441
+ * Make sure to augment the {@link Core} interface with the plugin type to ensure TypeScript recognizes it and provides intellisense.
1442
+ *
1443
+ * @template Key - The property name for accessing the plugin
1444
+ * @template Ctor - The plugin constructor type
1445
+ * @param key - Property name to access the plugin instance
1446
+ * @param Plugin - Plugin constructor class
1447
+ * @param startupPhase - When during startup to initialize this plugin ({@link StartupPhase})
1448
+ * @param args - Additional arguments to pass to the plugin constructor
1449
+ * @returns This instance with the plugin attached as a typed property
1450
+ * @throws An {@link Error} When called after initialization or if key already exists
1451
+ * @example seedcord.attach('db', Mongo, StartupPhase.Configuration, { uri: 'mongodb://...' })
1452
+ */
1317
1453
  attach(key, Plugin2, startupPhase, ...args) {
1318
1454
  if (this.isInitialized) throw new Error("Cannot attach a plugin after initialization.");
1319
1455
  if (this[key]) throw new Error(`Plugin with key "${key}" already exists.`);
@@ -1345,7 +1481,21 @@ var CoordinatedLifecycle = class {
1345
1481
  this.phaseOrder.forEach((phase) => this.tasksMap.set(phase, []));
1346
1482
  }
1347
1483
  /**
1348
- * Add a task to a specific phase
1484
+ * Adds a lifecycle task to a specific phase.
1485
+ *
1486
+ * Tasks are executed in phase order during lifecycle operations.
1487
+ * Each task has a timeout to prevent hanging operations.
1488
+ *
1489
+ * @param phase - The lifecycle phase to add the task to
1490
+ * @param taskName - Unique name for the task (used for logging and removal)
1491
+ * @param task - Async function to execute during the phase
1492
+ * @param timeoutMs - Maximum time allowed for task execution in milliseconds
1493
+ * @example
1494
+ * ```typescript
1495
+ * lifecycle.addTask(StartupPhase.Services, 'start-database', async () => {
1496
+ * await database.connect();
1497
+ * }, 10000);
1498
+ * ```
1349
1499
  */
1350
1500
  addTask(phase, taskName, task, timeoutMs) {
1351
1501
  if (!this.canAddTask()) return;
@@ -1359,7 +1509,11 @@ var CoordinatedLifecycle = class {
1359
1509
  this.logger.debug(`${chalk6__default.default.italic("Added")} ${this.getTaskType()} task ${chalk6__default.default.bold.cyan(taskName)} to phase ${chalk6__default.default.bold.magenta(this.phaseEnum[phase])}`);
1360
1510
  }
1361
1511
  /**
1362
- * Remove a task by name from a specific phase
1512
+ * Removes a lifecycle task from a specific phase.
1513
+ *
1514
+ * @param phase - The lifecycle phase to remove the task from
1515
+ * @param taskName - Name of the task to remove
1516
+ * @returns True if the task was found and removed, false otherwise
1363
1517
  */
1364
1518
  removeTask(phase, taskName) {
1365
1519
  if (!this.canRemoveTask()) return false;
@@ -1493,19 +1647,40 @@ var CoordinatedShutdown = class extends CoordinatedLifecycle {
1493
1647
  });
1494
1648
  }
1495
1649
  /**
1496
- * Add a task to a specific shutdown phase
1650
+ * Adds a task to a specific shutdown phase with timeout.
1651
+ *
1652
+ * @param phase - The shutdown phase from {@link ShutdownPhase}
1653
+ * @param taskName - Unique identifier for the task
1654
+ * @param task - Async function to execute
1655
+ * @param timeoutMs - Task timeout in milliseconds (default: 5000)
1497
1656
  */
1498
1657
  addTask(phase, taskName, task, timeoutMs = 5e3) {
1499
1658
  super.addTask(phase, taskName, task, timeoutMs);
1500
1659
  }
1501
1660
  /**
1502
- * Remove a task by name from a specific phase
1661
+ * Removes a task from a specific shutdown phase.
1662
+ *
1663
+ * @param phase - The shutdown phase to remove from
1664
+ * @param taskName - Name of the task to remove
1665
+ * @returns True if task was found and removed
1503
1666
  */
1504
1667
  removeTask(phase, taskName) {
1505
1668
  return super.removeTask(phase, taskName);
1506
1669
  }
1507
1670
  /**
1508
- * Start the coordinated shutdown sequence
1671
+ * Executes the coordinated shutdown sequence.
1672
+ *
1673
+ * Runs all registered tasks across shutdown phases in reverse order.
1674
+ * Tasks within each phase are executed in parallel for faster shutdown.
1675
+ * Process exits with the specified code when complete.
1676
+ *
1677
+ * @param exitCode - Process exit code (default: 0)
1678
+ * @returns Promise that resolves when shutdown is complete
1679
+ * @example
1680
+ * ```typescript
1681
+ * shutdown.addTask(ShutdownPhase.Services, 'database', () => db.disconnect(), 5000);
1682
+ * await shutdown.run(0); // Graceful shutdown
1683
+ * ```
1509
1684
  */
1510
1685
  async run(exitCode = 0) {
1511
1686
  if (this.isShuttingDown) {
@@ -1559,6 +1734,10 @@ var Bot = class extends Plugin {
1559
1734
  events;
1560
1735
  commands;
1561
1736
  emojiInjector;
1737
+ /**
1738
+ * @param core - Seedcord core instance
1739
+ * @internal
1740
+ */
1562
1741
  constructor(core) {
1563
1742
  super(core), this.core = core;
1564
1743
  this._client = new discord_js.Client(core.config.bot.clientOptions);
@@ -1568,6 +1747,10 @@ var Bot = class extends Plugin {
1568
1747
  this.emojiInjector = new EmojiInjector(this.core);
1569
1748
  this.core.shutdown.addTask(ShutdownPhase.DiscordCleanup, "stop-bot", async () => await this.stop());
1570
1749
  }
1750
+ /**
1751
+ * Initializes Discord client and all controllers
1752
+ * @internal
1753
+ */
1571
1754
  async init() {
1572
1755
  if (this.isInitialized) {
1573
1756
  return;
@@ -1580,15 +1763,27 @@ var Bot = class extends Plugin {
1580
1763
  await this.commands.setCommands();
1581
1764
  await this.emojiInjector.init();
1582
1765
  }
1766
+ /**
1767
+ * Stops the bot and cleans up connections
1768
+ * @internal
1769
+ */
1583
1770
  async stop() {
1584
1771
  this._client.removeAllListeners();
1585
1772
  await this.logout();
1586
1773
  }
1774
+ /**
1775
+ * Logs the bot into Discord using the configured token
1776
+ * @private
1777
+ */
1587
1778
  async login() {
1588
1779
  await this._client.login(Globals.botToken);
1589
1780
  this.logger.info(`Logged in as ${chalk6__default.default.bold.magenta(this._client.user?.username)}!`);
1590
1781
  return this;
1591
1782
  }
1783
+ /**
1784
+ * Logs out and destroys the Discord client connection
1785
+ * @private
1786
+ */
1592
1787
  async logout() {
1593
1788
  await this._client.destroy();
1594
1789
  this.logger.info(chalk6__default.default.bold.red("Logged out of Discord!"));
@@ -1598,22 +1793,28 @@ var Bot = class extends Plugin {
1598
1793
  }
1599
1794
  };
1600
1795
 
1601
- // src/hooks/decorators/RegisterHook.ts
1602
- var HookMetadataKey = Symbol("hook:metadata");
1603
- function RegisterHook(hook) {
1796
+ // src/effects/decorators/RegisterEffect.ts
1797
+ var EffectMetadataKey = Symbol("effect:metadata");
1798
+ function RegisterEffect(effect) {
1604
1799
  return function(constructor) {
1605
- Reflect.defineMetadata(HookMetadataKey, hook, constructor);
1800
+ Reflect.defineMetadata(EffectMetadataKey, effect, constructor);
1606
1801
  };
1607
1802
  }
1608
- __name(RegisterHook, "RegisterHook");
1803
+ __name(RegisterEffect, "RegisterEffect");
1609
1804
 
1610
- // src/hooks/interfaces/HookHandler.ts
1611
- var HookHandler = class {
1805
+ // src/effects/interfaces/EffectsHandler.ts
1806
+ var EffectsHandler = class {
1612
1807
  static {
1613
- __name(this, "HookHandler");
1808
+ __name(this, "EffectsHandler");
1614
1809
  }
1615
1810
  data;
1616
1811
  core;
1812
+ /**
1813
+ * Creates a new effects handler instance.
1814
+ *
1815
+ * @param data - The effect event data
1816
+ * @param core - The core framework instance
1817
+ */
1617
1818
  constructor(data, core) {
1618
1819
  this.data = data;
1619
1820
  this.core = core;
@@ -1622,8 +1823,8 @@ var HookHandler = class {
1622
1823
  }
1623
1824
  };
1624
1825
 
1625
- // src/hooks/interfaces/abstracts/WebhookLog.ts
1626
- var WebhookLog = class extends HookHandler {
1826
+ // src/effects/interfaces/abstracts/WebhookLog.ts
1827
+ var WebhookLog = class extends EffectsHandler {
1627
1828
  static {
1628
1829
  __name(this, "WebhookLog");
1629
1830
  }
@@ -1632,7 +1833,7 @@ var WebhookLog = class extends HookHandler {
1632
1833
  }
1633
1834
  };
1634
1835
 
1635
- // src/hooks/default/UnknownException.ts
1836
+ // src/effects/default/UnknownException.ts
1636
1837
  function _ts_decorate3(decorators, target, key, desc) {
1637
1838
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1638
1839
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1658,7 +1859,7 @@ var UnknownException = class extends WebhookLog {
1658
1859
  }
1659
1860
  };
1660
1861
  UnknownException = _ts_decorate3([
1661
- RegisterHook("unknownException")
1862
+ RegisterEffect("unknownException")
1662
1863
  ], UnknownException);
1663
1864
  var UnhandledErrorEmbed = class UnhandledErrorEmbed2 extends BuilderComponent {
1664
1865
  static {
@@ -1695,83 +1896,107 @@ var UnhandledErrorEmbed = class UnhandledErrorEmbed2 extends BuilderComponent {
1695
1896
  ]);
1696
1897
  }
1697
1898
  };
1698
- var HookEmitter = class {
1899
+ var EffectsEmitter = class {
1699
1900
  static {
1700
- __name(this, "HookEmitter");
1901
+ __name(this, "EffectsEmitter");
1701
1902
  }
1702
1903
  emitter = new events.EventEmitter();
1904
+ /**
1905
+ * Registers a listener for the specified side effect.
1906
+ *
1907
+ * @template KeyOfEffects - The side effect name type
1908
+ * @param event - The side effect name to listen for
1909
+ * @param listener - Function to call when the event is emitted
1910
+ * @returns This EffectsEmitter instance for chaining
1911
+ */
1703
1912
  on(event, listener) {
1704
1913
  this.emitter.on(event, listener);
1705
1914
  return this;
1706
1915
  }
1916
+ /**
1917
+ * Registers a one-time listener for the specified side effect.
1918
+ *
1919
+ * @template KeyOfEffects - The side effect name type
1920
+ * @param event - The side effect name to listen for once
1921
+ * @param listener - Function to call when the event is emitted
1922
+ * @returns This EffectsEmitter instance for chaining
1923
+ */
1707
1924
  once(event, listener) {
1708
1925
  this.emitter.once(event, listener);
1709
1926
  return this;
1710
1927
  }
1928
+ /**
1929
+ * Emits a side effect with the provided data.
1930
+ *
1931
+ * @template KeyOfEffects - The side effect name type
1932
+ * @param event - The side effect name to emit
1933
+ * @param data - The data to pass to registered listeners
1934
+ * @returns True if the event had listeners, false otherwise
1935
+ */
1711
1936
  emit(event, data) {
1712
1937
  return this.emitter.emit(event, data);
1713
1938
  }
1714
1939
  };
1715
1940
 
1716
- // src/hooks/HookController.ts
1717
- var HookController = class extends Plugin {
1941
+ // src/effects/EffectsRegistry.ts
1942
+ var EffectsRegistry = class extends Plugin {
1718
1943
  static {
1719
- __name(this, "HookController");
1944
+ __name(this, "EffectsRegistry");
1720
1945
  }
1721
1946
  core;
1722
- logger = new Logger("Hooks");
1947
+ logger = new Logger("Effects");
1723
1948
  isInitialized = false;
1724
- hookMap = /* @__PURE__ */ new Map();
1725
- emitter = new HookEmitter();
1949
+ effectsMap = /* @__PURE__ */ new Map();
1950
+ emitter = new EffectsEmitter();
1726
1951
  constructor(core) {
1727
1952
  super(core), this.core = core;
1728
1953
  }
1729
1954
  async init() {
1730
1955
  if (this.isInitialized) return;
1731
1956
  this.isInitialized = true;
1732
- const hooksDir = this.core.config.hooks.path;
1733
- this.logger.info(chalk6__default.default.bold(hooksDir));
1734
- this.registerHook("unknownException", UnknownException);
1735
- await this.loadHooks(hooksDir);
1736
- this.attachHooks();
1737
- const totalHooks = Array.from(this.hookMap.values()).reduce((acc, handlers) => acc + handlers.length, 0);
1738
- this.logger.info(`${chalk6__default.default.bold.green("Loaded")}: ${chalk6__default.default.bold.magenta(totalHooks)} hooks`);
1739
- }
1740
- async loadHooks(dir) {
1957
+ const effectsDir = this.core.config.effects.path;
1958
+ this.logger.info(chalk6__default.default.bold(effectsDir));
1959
+ this.registerEffect("unknownException", UnknownException);
1960
+ await this.loadEffects(effectsDir);
1961
+ this.attachEffects();
1962
+ const totalEffects = Array.from(this.effectsMap.values()).reduce((acc, handlers) => acc + handlers.length, 0);
1963
+ this.logger.info(`${chalk6__default.default.bold.green("Loaded")}: ${chalk6__default.default.bold.magenta(totalEffects)} side effects`);
1964
+ }
1965
+ async loadEffects(dir) {
1741
1966
  await traverseDirectory(dir, (_fullPath, relativePath, imported) => {
1742
1967
  for (const exportName of Object.keys(imported)) {
1743
1968
  const val = imported[exportName];
1744
- if (this.isHookHandler(val)) {
1745
- const hookName = Reflect.getMetadata(HookMetadataKey, val);
1746
- if (hookName) {
1747
- this.registerHook(hookName, val);
1969
+ if (this.isEffectHandler(val)) {
1970
+ const effectName = Reflect.getMetadata(EffectMetadataKey, val);
1971
+ if (effectName) {
1972
+ this.registerEffect(effectName, val);
1748
1973
  this.logger.info(`${chalk6__default.default.italic("Registered")} ${chalk6__default.default.bold.yellow(val.name)} from ${chalk6__default.default.gray(relativePath)}`);
1749
1974
  }
1750
1975
  }
1751
1976
  }
1752
1977
  });
1753
1978
  }
1754
- registerHook(hookName, handler) {
1755
- let handlers = this.hookMap.get(hookName);
1979
+ registerEffect(effectName, handler) {
1980
+ let handlers = this.effectsMap.get(effectName);
1756
1981
  if (!handlers) {
1757
1982
  handlers = [];
1758
- this.hookMap.set(hookName, handlers);
1983
+ this.effectsMap.set(effectName, handlers);
1759
1984
  }
1760
1985
  handlers.push(handler);
1761
1986
  }
1762
- isHookHandler(obj) {
1987
+ isEffectHandler(obj) {
1763
1988
  if (typeof obj !== "function") return false;
1764
- return obj.prototype instanceof HookHandler;
1989
+ return obj.prototype instanceof EffectsHandler;
1765
1990
  }
1766
- attachHooks() {
1767
- for (const [hookName, handlerCtors] of this.hookMap) {
1768
- this.emitter.on(hookName, (data) => {
1991
+ attachEffects() {
1992
+ for (const [effectName, handlerCtors] of this.effectsMap) {
1993
+ this.emitter.on(effectName, (data) => {
1769
1994
  for (const HandlerCtor of handlerCtors) {
1770
1995
  try {
1771
1996
  const instance = new HandlerCtor(data, this.core);
1772
1997
  void instance.execute();
1773
1998
  } catch (err) {
1774
- this.logger.error(`Error in hook ${String(hookName)} handler ${HandlerCtor.name}:`, err);
1999
+ this.logger.error(`Error in side effect ${String(effectName)} handler ${HandlerCtor.name}:`, err);
1775
2000
  }
1776
2001
  }
1777
2002
  });
@@ -1827,6 +2052,11 @@ var HealthCheck = class extends Plugin {
1827
2052
  });
1828
2053
  });
1829
2054
  }
2055
+ /**
2056
+ * Stops the health check server.
2057
+ *
2058
+ * @returns Promise that resolves when the server is closed
2059
+ */
1830
2060
  stop() {
1831
2061
  return new Promise((shutdownResolve) => {
1832
2062
  this.server?.close(() => {
@@ -1865,7 +2095,12 @@ var CoordinatedStartup = class extends CoordinatedLifecycle {
1865
2095
  super("CoordinatedStartup", PHASE_ORDER2, StartupPhase);
1866
2096
  }
1867
2097
  /**
1868
- * Add a task to a specific startup phase
2098
+ * Adds a task to a specific startup phase with timeout.
2099
+ *
2100
+ * @param phase - The startup phase from {@link StartupPhase}
2101
+ * @param taskName - Unique identifier for the task
2102
+ * @param task - Async function to execute
2103
+ * @param timeoutMs - Task timeout in milliseconds (default: 10000)
1869
2104
  */
1870
2105
  addTask(phase, taskName, task, timeoutMs = 1e4) {
1871
2106
  super.addTask(phase, taskName, task, timeoutMs);
@@ -1902,7 +2137,20 @@ var CoordinatedStartup = class extends CoordinatedLifecycle {
1902
2137
  return results;
1903
2138
  }
1904
2139
  /**
1905
- * Start the coordinated startup sequence
2140
+ * Executes the coordinated startup sequence.
2141
+ *
2142
+ * Runs all registered tasks across startup phases in the correct order.
2143
+ * Each phase completes before the next phase begins. Tasks within a phase
2144
+ * are executed sequentially to maintain predictable initialization.
2145
+ *
2146
+ * @returns Promise that resolves when startup is complete
2147
+ * @throws An {@link Error} If startup fails or is called multiple times
2148
+ * @example
2149
+ * ```typescript
2150
+ * const startup = new CoordinatedStartup();
2151
+ * startup.addTask(StartupPhase.Services, 'database', () => db.connect(), 10000);
2152
+ * await startup.run();
2153
+ * ```
1906
2154
  */
1907
2155
  async run() {
1908
2156
  if (this.hasStarted) {
@@ -1962,11 +2210,22 @@ var Seedcord = class _Seedcord extends Pluggable {
1962
2210
  }
1963
2211
  config;
1964
2212
  static isInstantiated = false;
2213
+ /** @see {@link CoordinatedShutdown} */
1965
2214
  shutdown;
2215
+ /** @see {@link CoordinatedStartup} */
1966
2216
  startup;
1967
- hooks;
2217
+ /** @see {@link EffectsRegistry} */
2218
+ effects;
2219
+ /** @see {@link Bot} */
1968
2220
  bot;
2221
+ /** @see {@link HealthCheck} */
1969
2222
  healthCheck;
2223
+ /**
2224
+ * Creates a new Seedcord instance
2225
+ *
2226
+ * @param config - Bot configuration including paths and Discord client options
2227
+ * @throws An {@link Error} When attempting to create multiple instances (singleton)
2228
+ */
1970
2229
  constructor(config) {
1971
2230
  const shutdown = new CoordinatedShutdown();
1972
2231
  const startup = new CoordinatedStartup();
@@ -1977,16 +2236,20 @@ var Seedcord = class _Seedcord extends Pluggable {
1977
2236
  throw new Error("Seedcord can only be instantiated once. Use the existing instance instead.");
1978
2237
  }
1979
2238
  _Seedcord.isInstantiated = true;
1980
- this.hooks = new HookController(this);
2239
+ this.effects = new EffectsRegistry(this);
1981
2240
  this.bot = new Bot(this);
1982
2241
  this.healthCheck = new HealthCheck(this);
1983
2242
  this.registerStartupTasks();
1984
2243
  }
2244
+ /**
2245
+ * Registers default startup tasks
2246
+ * @internal
2247
+ */
1985
2248
  registerStartupTasks() {
1986
- this.startup.addTask(StartupPhase.Configuration, "Hook Initialization", async () => {
1987
- this.hooks.logger.info(chalk6__default.default.bold("Initializing"));
1988
- await this.hooks.init();
1989
- this.hooks.logger.info(chalk6__default.default.bold("Initialized"));
2249
+ this.startup.addTask(StartupPhase.Configuration, "Effect Initialization", async () => {
2250
+ this.effects.logger.info(chalk6__default.default.bold("Initializing"));
2251
+ await this.effects.init();
2252
+ this.effects.logger.info(chalk6__default.default.bold("Initialized"));
1990
2253
  });
1991
2254
  this.startup.addTask(StartupPhase.Instantiation, "Bot Initialization", async () => {
1992
2255
  this.bot.logger.info(chalk6__default.default.bold("Initializing"));
@@ -1999,6 +2262,11 @@ var Seedcord = class _Seedcord extends Pluggable {
1999
2262
  this.healthCheck.logger.info(chalk6__default.default.bold("Initialized"));
2000
2263
  });
2001
2264
  }
2265
+ /**
2266
+ * Starts the bot and runs all initialization tasks
2267
+ *
2268
+ * @returns This Seedcord instance when fully initialized
2269
+ */
2002
2270
  async start() {
2003
2271
  await super.init();
2004
2272
  return this;
@@ -2028,11 +2296,11 @@ function EventCatchable(log) {
2028
2296
  this.getEvent()
2029
2297
  ];
2030
2298
  const msg = eventArgs.find((x) => x instanceof discord_js.Message);
2031
- const result = ErrorHandlingUtils.handleError(err, this.core, msg?.guild ?? null, msg?.author ?? null);
2299
+ const { response } = ErrorHandlingUtils.extractErrorResponse(err, this.core, msg?.guild ?? null, msg?.author ?? null);
2032
2300
  if (!msg) return;
2033
2301
  await msg.reply({
2034
2302
  embeds: [
2035
- result.response
2303
+ response
2036
2304
  ],
2037
2305
  components: []
2038
2306
  });
@@ -2051,19 +2319,32 @@ var CooldownManager = class {
2051
2319
  Err;
2052
2320
  msg;
2053
2321
  map = /* @__PURE__ */ new Map();
2322
+ /**
2323
+ * Creates a new CooldownManager instance.
2324
+ *
2325
+ * @param opts - Configuration options for the cooldown behavior
2326
+ */
2054
2327
  constructor(opts = {}) {
2055
2328
  this.window = opts.cooldown ?? 1e3;
2056
2329
  this.Err = opts.err ?? Error;
2057
2330
  this.msg = opts.message ?? "Cooldown active";
2058
2331
  }
2059
- /** Record usage without any checks. */
2332
+ /**
2333
+ * Records usage timestamp for a key without any cooldown checks.
2334
+ *
2335
+ * @param key - The unique identifier for the cooldown entry
2336
+ */
2060
2337
  set(key) {
2061
2338
  this.map.set(key, Date.now());
2062
2339
  }
2063
2340
  /**
2064
- * Verify cooldown for `key`.\
2065
- * If active → throws the custom error.\
2066
- * If not active updates timestamp and returns void.
2341
+ * Verifies cooldown status for a key and updates timestamp if not active.
2342
+ *
2343
+ * If the cooldown is still active, throws the configured error.
2344
+ * If not active, updates the timestamp and returns successfully.
2345
+ *
2346
+ * @param key - The unique identifier to check cooldown for
2347
+ * @throws An {@link Err} When the cooldown is still active for the given key
2067
2348
  */
2068
2349
  check(key) {
2069
2350
  const now = Date.now();
@@ -2077,12 +2358,21 @@ var CooldownManager = class {
2077
2358
  }
2078
2359
  this.map.set(key, now);
2079
2360
  }
2080
- /** Returns true if the key is still cooling down (does not update timestamp). */
2361
+ /**
2362
+ * Checks if a key is currently cooling down without updating timestamp.
2363
+ *
2364
+ * @param key - The unique identifier to check
2365
+ * @returns True if the key is still cooling down, false otherwise
2366
+ */
2081
2367
  isActive(key) {
2082
2368
  const last = this.map.get(key);
2083
2369
  return last !== void 0 && Date.now() - last < this.window;
2084
2370
  }
2085
- /** Remove a key from the map (useful for manual resets). */
2371
+ /**
2372
+ * Removes a key from the cooldown map.
2373
+ *
2374
+ * @param key - The unique identifier to remove (useful for manual resets)
2375
+ */
2086
2376
  clear(key) {
2087
2377
  this.map.delete(key);
2088
2378
  }
@@ -2164,7 +2454,9 @@ var Mongo = class extends Plugin {
2164
2454
  tls: true,
2165
2455
  ssl: true
2166
2456
  }
2167
- }).then((i) => this.logger.info(`Connected to MongoDB: ${chalk6__default.default.bold.magenta(i.connection.name)}`)).catch((err) => throwCustomError(err, "Could not connect to MongoDB", DatabaseConnectionFailure));
2457
+ }).then((i) => this.logger.info(`Connected to MongoDB: ${chalk6__default.default.bold.magenta(i.connection.name)}`)).catch((err) => {
2458
+ throw new Error(`Could not connect to MongoDB`, err);
2459
+ });
2168
2460
  }
2169
2461
  async disconnect() {
2170
2462
  await mongoose2__default.default.disconnect().then(() => this.logger.info(chalk6__default.default.red.bold("Disconnected from MongoDB"))).catch((err) => this.logger.error(`Could not disconnect from MongoDB: ${err.message}`));
@@ -2230,15 +2522,15 @@ exports.CustomError = CustomError;
2230
2522
  exports.DBCatchable = DBCatchable;
2231
2523
  exports.DatabaseModel = DatabaseModel;
2232
2524
  exports.DatabaseService = DatabaseService;
2525
+ exports.EffectMetadataKey = EffectMetadataKey;
2526
+ exports.EffectsEmitter = EffectsEmitter;
2527
+ exports.EffectsHandler = EffectsHandler;
2528
+ exports.EffectsRegistry = EffectsRegistry;
2233
2529
  exports.EventCatchable = EventCatchable;
2234
2530
  exports.EventHandler = EventHandler;
2235
2531
  exports.EventMetadataKey = EventMetadataKey;
2236
2532
  exports.Globals = Globals;
2237
2533
  exports.HealthCheck = HealthCheck;
2238
- exports.HookController = HookController;
2239
- exports.HookEmitter = HookEmitter;
2240
- exports.HookHandler = HookHandler;
2241
- exports.HookMetadataKey = HookMetadataKey;
2242
2534
  exports.InteractionHandler = InteractionHandler;
2243
2535
  exports.InteractionMetadataKey = InteractionMetadataKey;
2244
2536
  exports.InteractionMiddleware = InteractionMiddleware;
@@ -2251,8 +2543,8 @@ exports.Mongo = Mongo;
2251
2543
  exports.Pluggable = Pluggable;
2252
2544
  exports.Plugin = Plugin;
2253
2545
  exports.RegisterCommand = RegisterCommand;
2546
+ exports.RegisterEffect = RegisterEffect;
2254
2547
  exports.RegisterEvent = RegisterEvent;
2255
- exports.RegisterHook = RegisterHook;
2256
2548
  exports.RowComponent = RowComponent;
2257
2549
  exports.Seedcord = Seedcord;
2258
2550
  exports.SelectMenuRoute = SelectMenuRoute;