@seedcord/gateway 0.1.0-next.0 → 0.1.0-next.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
@@ -27,26 +27,34 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
 
28
28
  //#endregion
29
29
  require("reflect-metadata");
30
+ let _seedcord_logger_node = require("@seedcord/logger/node");
30
31
  let _seedcord_core = require("@seedcord/core");
31
32
  let _seedcord_core_internal = require("@seedcord/core/internal");
32
33
  let discord_js = require("discord.js");
33
34
  let _seedcord_errors = require("@seedcord/errors");
34
35
  let _seedcord_errors_internal = require("@seedcord/errors/internal");
35
36
  let _seedcord_utils = require("@seedcord/utils");
36
- let _seedcord_services = require("@seedcord/services");
37
- let chalk = require("chalk");
38
- chalk = __toESM(chalk, 1);
37
+ let _seedcord_logger = require("@seedcord/logger");
39
38
  let _discordjs_builders = require("@discordjs/builders");
40
39
  let _seedcord_utils_internal = require("@seedcord/utils/internal");
41
- let node_fs = require("node:fs");
42
- let node_path = require("node:path");
43
- let node_url = require("node:url");
40
+ let _seedcord_event_emitter = require("@seedcord/event-emitter");
44
41
  let _seedcord_rate_limiter = require("@seedcord/rate-limiter");
42
+ let _seedcord_services = require("@seedcord/services");
45
43
  let _seedcord_types_internal = require("@seedcord/types/internal");
46
44
  let envapt = require("envapt");
45
+ let chalk = require("chalk");
46
+ chalk = __toESM(chalk, 1);
47
+ let envapt_legacy = require("envapt/legacy");
48
+ let node_path = require("node:path");
49
+ let _seedcord_core_hmr = require("@seedcord/core/hmr");
50
+ let _seedcord_utils_node = require("@seedcord/utils/node");
47
51
  let node_crypto = require("node:crypto");
48
52
  node_crypto = __toESM(node_crypto, 1);
49
53
 
54
+ //#region src/logger-bootstrap.ts
55
+ (0, _seedcord_logger_node.installNodeDefaults)();
56
+
57
+ //#endregion
50
58
  //#region src/bot/decorators/Events.ts
51
59
  /**
52
60
  * Registers an event handler class with one or more Discord.js events.
@@ -683,14 +691,14 @@ const Emojis = emojiStorage;
683
691
  */
684
692
  var EmojiInjector = class {
685
693
  core;
686
- logger = new _seedcord_services.Logger("Emojis");
694
+ logger = new _seedcord_logger.Logger("Emojis");
687
695
  constructor(core) {
688
696
  this.core = core;
689
697
  }
690
698
  async init() {
691
699
  this.clearEmojis();
692
700
  if (!this.core.config.bot.emojis || Object.keys(this.core.config.bot.emojis).length === 0) {
693
- this.logger.debug(chalk.default.bold.yellow("No emojis configured, skipping emoji injection."));
701
+ this.logger.debug("No emojis configured, skipping emoji injection.");
694
702
  return;
695
703
  }
696
704
  const configEmojis = this.core.config.bot.emojis;
@@ -758,7 +766,7 @@ const MULTI_GUILD = Symbol("seedcord:commandmentions:multi-guild");
758
766
  */
759
767
  var CommandMentionInjector = class {
760
768
  core;
761
- logger = new _seedcord_services.Logger("CommandMentions");
769
+ logger = new _seedcord_logger.Logger("CommandMentions");
762
770
  constructor(core) {
763
771
  this.core = core;
764
772
  }
@@ -885,7 +893,7 @@ function checkBotPermissions(target, scope, inverse = false, errors) {
885
893
  }
886
894
  const me = target.guild.members.me;
887
895
  if (!me) {
888
- new _seedcord_services.Logger("checkBotPermissions").warn(`Bot member is unavailable in guild ${target.guild.id} while checking permissions in channel ${target.id}`);
896
+ new _seedcord_logger.Logger("checkBotPermissions").warn(`Bot member is unavailable in guild ${target.guild.id} while checking permissions in channel ${target.id}`);
889
897
  const names = scope.map((bit) => PermissionNames.get(bit) ?? String(bit));
890
898
  throw new ((errors?.missing) ?? MissingPermissions)("Missing Permissions", target, names);
891
899
  }
@@ -1111,7 +1119,7 @@ function flagsFor(ephemeral) {
1111
1119
  */
1112
1120
  var ReplySender = class {
1113
1121
  interaction;
1114
- logger = new _seedcord_services.Logger("ReplySender");
1122
+ logger = new _seedcord_logger.Logger("ReplySender");
1115
1123
  constructor(interaction) {
1116
1124
  this.interaction = interaction;
1117
1125
  }
@@ -1247,16 +1255,6 @@ async function getConfirmation(interaction, prompt, options) {
1247
1255
  return confirmed;
1248
1256
  }
1249
1257
 
1250
- //#endregion
1251
- //#region src/hmr/devChannel.ts
1252
- let channel;
1253
- function setDevChannel(next) {
1254
- channel = next;
1255
- }
1256
- function getDevChannel() {
1257
- return channel;
1258
- }
1259
-
1260
1258
  //#endregion
1261
1259
  //#region src/interfaces/Plugin.ts
1262
1260
  /**
@@ -1265,7 +1263,7 @@ function getDevChannel() {
1265
1263
  * Extend this class to create plugins that integrate with the Seedcord lifecycle.
1266
1264
  * Plugins have access to the core instance and must implement initialization logic.
1267
1265
  */
1268
- var Plugin = class extends _seedcord_services.StrictEventEmitter {
1266
+ var Plugin = class extends _seedcord_event_emitter.TypedEventEmitter {
1269
1267
  pluggable;
1270
1268
  name = this.constructor.name;
1271
1269
  constructor(pluggable) {
@@ -1284,19 +1282,15 @@ var Plugin = class extends _seedcord_services.StrictEventEmitter {
1284
1282
  * @param patterns - Glob patterns relative to the project root
1285
1283
  */
1286
1284
  registerCriticalFiles(patterns) {
1287
- getDevChannel()?.send("seedcord:register-critical-files", { patterns });
1288
- }
1289
- /** @internal */
1290
- setMaxListeners(n) {
1291
- return super.setMaxListeners(n);
1285
+ (0, _seedcord_core_internal.getDevChannel)()?.send("seedcord:register-critical-files", { patterns });
1292
1286
  }
1293
1287
  /** @internal */
1294
1288
  removeListener(event, listener) {
1295
1289
  return super.removeListener(event, listener);
1296
1290
  }
1297
1291
  /** @internal */
1298
- removeAllListeners(eventName) {
1299
- return super.removeAllListeners(eventName);
1292
+ removeAllListeners(event) {
1293
+ return super.removeAllListeners(event);
1300
1294
  }
1301
1295
  };
1302
1296
  /**
@@ -1305,7 +1299,7 @@ var Plugin = class extends _seedcord_services.StrictEventEmitter {
1305
1299
  * Provides plugin attachment capabilities and lifecycle management. Plugins are attached during
1306
1300
  * configuration and initialized during startup. Not constructed directly, the host is a {@link Seedcord}.
1307
1301
  */
1308
- var Pluggable = class Pluggable extends _seedcord_services.StrictEventEmitter {
1302
+ var Pluggable = class Pluggable extends _seedcord_event_emitter.TypedEventEmitter {
1309
1303
  isInitialized = false;
1310
1304
  shutdown;
1311
1305
  startup;
@@ -1358,16 +1352,12 @@ var Pluggable = class Pluggable extends _seedcord_services.StrictEventEmitter {
1358
1352
  return Object.assign(this, entry);
1359
1353
  }
1360
1354
  /** @internal */
1361
- setMaxListeners(n) {
1362
- return super.setMaxListeners(n);
1363
- }
1364
- /** @internal */
1365
1355
  removeListener(event, listener) {
1366
1356
  return super.removeListener(event, listener);
1367
1357
  }
1368
1358
  /** @internal */
1369
- removeAllListeners(eventName) {
1370
- return super.removeAllListeners(eventName);
1359
+ removeAllListeners(event) {
1360
+ return super.removeAllListeners(event);
1371
1361
  }
1372
1362
  };
1373
1363
 
@@ -1380,10 +1370,12 @@ var BaseHandler = class {
1380
1370
  core;
1381
1371
  event;
1382
1372
  logger;
1383
- constructor(event, core) {
1373
+ dispatch;
1374
+ constructor(event, core, dispatch) {
1384
1375
  this.core = core;
1385
1376
  this.event = event;
1386
- this.logger = new _seedcord_services.Logger(this.constructor.name);
1377
+ this.logger = new _seedcord_logger.Logger(this.constructor.name);
1378
+ this.dispatch = dispatch;
1387
1379
  }
1388
1380
  /** @internal */
1389
1381
  getEvent() {
@@ -1419,8 +1411,8 @@ var InteractionMiddleware = class extends BaseHandler {
1419
1411
  * @typeParam Repliable - The interaction type this handler processes
1420
1412
  */
1421
1413
  var InteractionHandler = class extends BaseHandler {
1422
- constructor(event, core) {
1423
- super(event, core);
1414
+ constructor(event, core, dispatch) {
1415
+ super(event, core, dispatch);
1424
1416
  }
1425
1417
  };
1426
1418
 
@@ -1565,8 +1557,8 @@ var ContextMenuHandler = class extends InteractionHandler {
1565
1557
  * ```
1566
1558
  */
1567
1559
  var AutocompleteHandler = class extends BaseHandler {
1568
- constructor(event, core) {
1569
- super(event, core);
1560
+ constructor(event, core, dispatch) {
1561
+ super(event, core, dispatch);
1570
1562
  }
1571
1563
  /**
1572
1564
  * The focused option for this interaction. `value` is always the raw partial string the user is typing,
@@ -2125,7 +2117,7 @@ var Subscriber = class {
2125
2117
  constructor(data, core) {
2126
2118
  this.data = data;
2127
2119
  this.core = core;
2128
- this.logger = new _seedcord_services.Logger(this.constructor.name);
2120
+ this.logger = new _seedcord_logger.Logger(this.constructor.name);
2129
2121
  }
2130
2122
  };
2131
2123
 
@@ -2183,171 +2175,6 @@ var WebhookLog = class extends Subscriber {
2183
2175
  }
2184
2176
  };
2185
2177
 
2186
- //#endregion
2187
- //#region src/hmr/HmrModuleHandler.ts
2188
- /**
2189
- * Reloads THandler and optional TMiddleware modules on HMR updates.
2190
- *
2191
- * You can either implement a custom `onHmr()` method in your class by extending `HmrAware`, or if it's a plugin, override the existing `onHmr()` with fully custom logic. Or, use this class's `handle()` method inside your `onHmr()` to get standard HMR handling for your modules based on the provided options you give to the constructor.
2192
- *
2193
- * This is only useful during development.
2194
- */
2195
- var HmrModuleHandler = class {
2196
- store;
2197
- options;
2198
- constructor(options) {
2199
- this.options = {
2200
- ...options,
2201
- logger: options.logger.inChannel("hmr")
2202
- };
2203
- this.store = {
2204
- fileToHandlers: /* @__PURE__ */ new Map(),
2205
- fileToMiddlewares: /* @__PURE__ */ new Map(),
2206
- handlerArtifacts: /* @__PURE__ */ new Map()
2207
- };
2208
- }
2209
- /**
2210
- * Handles an HMR update event by reloading affected modules based on the event details and config. It receives an {@link HmrUpdateEvent}, then checks the type of update.
2211
- *
2212
- * - If it's a deletion, it unloads the module.
2213
- * - If it's an update/creation, it checks if the file is tracked, unloads it if so, then reloads the file if it is in the handlers or middlewares directory and is a valid handler/middleware.
2214
- */
2215
- async handle(event) {
2216
- const { file, affectedModules, type } = event;
2217
- const { logger, handlersDir, middlewaresDir } = this.options;
2218
- if (type === "delete" || type === "deleteDir") {
2219
- this.unload(file);
2220
- return;
2221
- }
2222
- if (type === "update" && !(0, node_fs.existsSync)(file)) {
2223
- if (this.isTracked(file)) {
2224
- logger.info(`Received update for non-existent file: ${(0, _seedcord_utils.formatFilePath)(file)}, treating as delete`);
2225
- this.unload(file);
2226
- }
2227
- return;
2228
- }
2229
- const rollbackEnabled = event.rollback ?? true;
2230
- const filesToReload = affectedModules && affectedModules.length > 0 ? affectedModules : [file];
2231
- const absHandlersDir = (0, node_path.resolve)(process.cwd(), handlersDir);
2232
- const absMiddlewaresDir = middlewaresDir ? (0, node_path.resolve)(process.cwd(), middlewaresDir) : null;
2233
- for (const fileToReload of filesToReload) {
2234
- const isHandler = fileToReload.startsWith(absHandlersDir);
2235
- const isMiddleware = absMiddlewaresDir ? fileToReload.startsWith(absMiddlewaresDir) : false;
2236
- if (!isHandler && !isMiddleware) continue;
2237
- await this.reloadWithRollback(fileToReload, rollbackEnabled);
2238
- }
2239
- }
2240
- async reloadWithRollback(file, rollbackEnabled) {
2241
- const { logger } = this.options;
2242
- const snapshot = this.snapshotUnits(file);
2243
- this.unload(file);
2244
- if (await this.reloadFile(file)) return;
2245
- this.unload(file);
2246
- if (!rollbackEnabled) return;
2247
- this.restoreUnits(file, snapshot);
2248
- logger.warn(`${chalk.default.yellow.bold("Rolled back")} ${chalk.default.gray((0, _seedcord_utils.formatFilePath)(file))} to the last-good version`);
2249
- }
2250
- /**
2251
- * Tracks a handler module as being loaded from a specific file.
2252
- * @param file - The file path the handler was loaded from.
2253
- * @param handler - The handler module instance.
2254
- */
2255
- trackHandler(file, handler) {
2256
- let set = this.store.fileToHandlers.get(file);
2257
- if (!set) {
2258
- set = /* @__PURE__ */ new Set();
2259
- this.store.fileToHandlers.set(file, set);
2260
- }
2261
- set.add(handler);
2262
- if (this.options.getArtifacts) {
2263
- const artifacts = this.options.getArtifacts(handler);
2264
- this.store.handlerArtifacts.set(handler, artifacts);
2265
- }
2266
- }
2267
- /**
2268
- * Tracks a middleware module as being loaded from a specific file.
2269
- * @param file - The file path the middleware was loaded from.
2270
- * @param middleware - The middleware module instance.
2271
- */
2272
- trackMiddleware(file, middleware) {
2273
- let set = this.store.fileToMiddlewares.get(file);
2274
- if (!set) {
2275
- set = /* @__PURE__ */ new Set();
2276
- this.store.fileToMiddlewares.set(file, set);
2277
- }
2278
- set.add(middleware);
2279
- }
2280
- isTracked(file) {
2281
- return this.store.fileToHandlers.has(file) || this.store.fileToMiddlewares.has(file);
2282
- }
2283
- unload(file) {
2284
- const { logger } = this.options;
2285
- const handlers = this.store.fileToHandlers.get(file);
2286
- if (handlers) {
2287
- for (const handler of handlers) {
2288
- const artifacts = this.store.handlerArtifacts.get(handler);
2289
- this.options.unregisterHandler(handler, artifacts);
2290
- this.store.handlerArtifacts.delete(handler);
2291
- const name = handler.name ?? "Handler";
2292
- logger.info(`${chalk.default.red.bold("Unloaded")} ${chalk.default.cyan.bold(name)} from ${chalk.default.gray((0, _seedcord_utils.formatFilePath)(file))}`);
2293
- }
2294
- this.store.fileToHandlers.delete(file);
2295
- }
2296
- const middlewares = this.store.fileToMiddlewares.get(file);
2297
- if (middlewares) {
2298
- for (const middleware of middlewares) {
2299
- this.options.unregisterMiddleware?.(middleware);
2300
- const name = middleware.name ?? "Middleware";
2301
- logger.info(`${chalk.default.red.bold("Unloaded")} ${chalk.default.cyan.bold(name)} from ${chalk.default.gray((0, _seedcord_utils.formatFilePath)(file))}`);
2302
- }
2303
- this.store.fileToMiddlewares.delete(file);
2304
- }
2305
- }
2306
- async reloadFile(file) {
2307
- const { logger, isHandler, isMiddleware, registerHandler, registerMiddleware } = this.options;
2308
- if (!(0, node_fs.existsSync)(file)) {
2309
- logger.info(`File does not exist, skipping reload: ${(0, _seedcord_utils.formatFilePath)(file)}`);
2310
- return false;
2311
- }
2312
- try {
2313
- let fileUrl = (0, node_url.pathToFileURL)(file).href;
2314
- if (process.env.VITEST === "true") fileUrl += `?update=${Date.now()}`;
2315
- const imported = await import(fileUrl);
2316
- for (const val of Object.values(imported)) if (isHandler(val)) {
2317
- registerHandler(val, file);
2318
- this.trackHandler(file, val);
2319
- const name = val.name ?? "Handler";
2320
- logger.info(`${chalk.default.blue.bold("Reloaded")} ${chalk.default.cyan.bold(name)} from ${chalk.default.gray((0, _seedcord_utils.formatFilePath)(file))}`);
2321
- } else if (isMiddleware && registerMiddleware && isMiddleware(val)) {
2322
- registerMiddleware(val, file);
2323
- this.trackMiddleware(file, val);
2324
- const name = val.name ?? "Middleware";
2325
- logger.info(`${chalk.default.blue.bold("Reloaded")} ${chalk.default.cyan.bold(name)} from ${chalk.default.gray((0, _seedcord_utils.formatFilePath)(file))}`);
2326
- }
2327
- return true;
2328
- } catch (error) {
2329
- logger.error(`Failed to reload file: ${file}`, error);
2330
- return false;
2331
- }
2332
- }
2333
- snapshotUnits(file) {
2334
- return {
2335
- handlers: [...this.store.fileToHandlers.get(file) ?? []],
2336
- middlewares: [...this.store.fileToMiddlewares.get(file) ?? []]
2337
- };
2338
- }
2339
- restoreUnits(file, snapshot) {
2340
- for (const handler of snapshot.handlers) {
2341
- this.options.registerHandler(handler, file);
2342
- this.trackHandler(file, handler);
2343
- }
2344
- if (this.options.registerMiddleware) for (const middleware of snapshot.middlewares) {
2345
- this.options.registerMiddleware(middleware, file);
2346
- this.trackMiddleware(file, middleware);
2347
- }
2348
- }
2349
- };
2350
-
2351
2178
  //#endregion
2352
2179
  //#region src/bot/utilities/miscellaneous/contextMenuLeaves.ts
2353
2180
  /**
@@ -2403,11 +2230,13 @@ var CommandRegistry = class {
2403
2230
  core;
2404
2231
  onDeployed;
2405
2232
  name = "Commands";
2406
- logger = new _seedcord_services.Logger("Commands");
2233
+ logger = new _seedcord_logger.Logger("Commands");
2407
2234
  isInitialised = false;
2408
2235
  globalCommands = [];
2409
2236
  guildCommands = new discord_js.Collection();
2410
2237
  ctorToCommand = /* @__PURE__ */ new Map();
2238
+ loading = false;
2239
+ loadedCommands = [];
2411
2240
  hmrHandler;
2412
2241
  pendingEvents = /* @__PURE__ */ new Map();
2413
2242
  constructor(core, onDeployed) {
@@ -2416,7 +2245,7 @@ var CommandRegistry = class {
2416
2245
  const commandsDir = this.core.config.bot.commands.path;
2417
2246
  if (!commandsDir) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.CoreControllerPathMissing, ["CommandRegistry", "commands"]);
2418
2247
  if (!envapt.Envapter.isDevelopment && !envapt.Envapter.isTest) return;
2419
- this.hmrHandler = new HmrModuleHandler({
2248
+ this.hmrHandler = new _seedcord_core_hmr.HmrModuleHandler({
2420
2249
  handlersDir: commandsDir,
2421
2250
  isHandler: this.isCommandClass.bind(this),
2422
2251
  registerHandler: this.registerCommand.bind(this),
@@ -2434,15 +2263,32 @@ var CommandRegistry = class {
2434
2263
  this.isInitialised = true;
2435
2264
  const commandsDir = this.core.config.bot.commands.path;
2436
2265
  if (!commandsDir) return;
2437
- this.logger.info(chalk.default.bold(commandsDir));
2438
- await this.loadCommands(commandsDir);
2439
- this.logger.utils.summary("Loaded commands", {
2266
+ this.loading = true;
2267
+ this.loadedCommands.length = 0;
2268
+ try {
2269
+ await this.loadCommands(commandsDir);
2270
+ } finally {
2271
+ this.loading = false;
2272
+ }
2273
+ this.reportLoad();
2274
+ (0, _seedcord_core_internal.getDevChannel)()?.on("seedcord:refresh-commands", (data) => {
2275
+ this.refresh(data.shouldRefresh);
2276
+ });
2277
+ }
2278
+ reportLoad() {
2279
+ const { utils } = this.logger;
2280
+ utils.summary("Loaded commands", {
2440
2281
  global: this.globalCommands.length,
2441
2282
  "guild groups": this.guildCommands.size
2442
2283
  });
2443
- getDevChannel()?.on("seedcord:refresh-commands", (data) => {
2444
- this.refresh(data.shouldRefresh);
2445
- });
2284
+ let slash = 0;
2285
+ let menu = 0;
2286
+ for (const command of this.loadedCommands) if (command.kind === "slash command") slash++;
2287
+ else menu++;
2288
+ utils.block("Loaded commands", [...utils.entries(this.loadedCommands), ...utils.counts({
2289
+ "slash commands": slash,
2290
+ "context menus": menu
2291
+ })]);
2446
2292
  }
2447
2293
  /** @internal */
2448
2294
  async refresh(shouldRefresh = true) {
@@ -2462,11 +2308,11 @@ var CommandRegistry = class {
2462
2308
  if (commandsDir && event.file.startsWith((0, node_path.resolve)(process.cwd(), commandsDir))) {
2463
2309
  this.pendingEvents.delete(event.file);
2464
2310
  this.pendingEvents.set(event.file, event);
2465
- getDevChannel()?.send("seedcord:commands-update-prompt", { files: [...this.pendingEvents.keys()].map((f) => (0, _seedcord_utils.formatFilePath)(f)) });
2311
+ (0, _seedcord_core_internal.getDevChannel)()?.send("seedcord:commands-update-prompt", { files: [...this.pendingEvents.keys()].map((f) => (0, _seedcord_utils.formatFilePath)(f)) });
2466
2312
  } else await this.hmrHandler?.handle(event);
2467
2313
  }
2468
2314
  async loadCommands(dir) {
2469
- await (0, _seedcord_utils.traverseDirectory)(dir, (fullPath, rel, mod) => {
2315
+ await (0, _seedcord_utils_node.traverseDirectory)(dir, (fullPath, rel, mod) => {
2470
2316
  for (const exported of Object.values(mod)) if (this.isCommandClass(exported)) {
2471
2317
  this.registerCommand(exported, rel);
2472
2318
  this.hmrHandler?.trackHandler(fullPath, exported);
@@ -2481,25 +2327,27 @@ var CommandRegistry = class {
2481
2327
  const meta = Reflect.getMetadata(_seedcord_core_internal.CommandMetadataKey, ctor);
2482
2328
  if (!meta) return;
2483
2329
  const comp = new ctor().component;
2484
- const commandType = comp instanceof _discordjs_builders.SlashCommandBuilder ? "slash command" : "context menu command";
2485
- if (meta.scope === "global") {
2486
- this.globalCommands.push(comp);
2487
- this.logger.utils.registration(ctor.name, rel);
2488
- this.logger.utils.item(`Global ${commandType}: ${chalk.default.bold.cyan(comp.name)}`);
2489
- } else {
2490
- for (const g of meta.guilds) {
2491
- const arr = this.guildCommands.get(g) ?? [];
2492
- arr.push(comp);
2493
- this.guildCommands.set(g, arr);
2494
- }
2495
- this.logger.utils.registration(ctor.name, rel);
2496
- this.logger.utils.item(`Guild ${commandType}: ${chalk.default.bold.cyan(comp.name)} for ${chalk.default.magenta.bold(meta.guilds.length)} guild(s)`);
2330
+ const kind = comp instanceof _discordjs_builders.SlashCommandBuilder ? "slash command" : "context menu";
2331
+ if (meta.scope === "global") this.globalCommands.push(comp);
2332
+ else for (const g of meta.guilds) {
2333
+ const arr = this.guildCommands.get(g) ?? [];
2334
+ arr.push(comp);
2335
+ this.guildCommands.set(g, arr);
2497
2336
  }
2498
2337
  this.ctorToCommand.set(ctor, {
2499
2338
  name: comp.name,
2500
2339
  scope: meta.scope,
2501
2340
  ...meta.scope === "guild" && { guilds: meta.guilds }
2502
2341
  });
2342
+ if (this.loading) {
2343
+ this.loadedCommands.push({
2344
+ name: comp.name,
2345
+ from: (0, _seedcord_utils.formatFilePath)(rel),
2346
+ kind
2347
+ });
2348
+ return;
2349
+ }
2350
+ this.logger.utils.registration(comp.name, rel, `${meta.scope} ${kind}`);
2503
2351
  }
2504
2352
  unregisterCommand(ctor, artifacts) {
2505
2353
  const info = artifacts ?? this.ctorToCommand.get(ctor);
@@ -2526,8 +2374,7 @@ var CommandRegistry = class {
2526
2374
  const deployed = await this.core.bot.client.application?.commands.set(this.globalCommands);
2527
2375
  if (deployed) result.global = deployed;
2528
2376
  const tag = this.globalCommands.length === 1 ? "command" : "commands";
2529
- this.logger.utils.summary("Configured global", { [tag]: this.globalCommands.length });
2530
- this.logger.utils.item(`${this.globalCommands.map((command) => chalk.default.bold.cyan(command.name)).join(", ")}`);
2377
+ this.logger.utils.block(`Deployed ${this.globalCommands.length} global ${tag}`, this.logger.utils.wrap(this.globalCommands.map((command) => command.name)), "info");
2531
2378
  }
2532
2379
  for (const [guildId, commands] of this.guildCommands.entries()) {
2533
2380
  const guild = this.core.bot.client.guilds.cache.get(guildId);
@@ -2538,8 +2385,7 @@ var CommandRegistry = class {
2538
2385
  const deployed = await guild.commands.set(commands);
2539
2386
  result.guilds.set(guildId, deployed);
2540
2387
  const tag = commands.length === 1 ? "command" : "commands";
2541
- this.logger.utils.summary(`Configured commands for ${chalk.default.bold.yellow(guild.name)}`, { [tag]: commands.length });
2542
- this.logger.utils.item(`${commands.map((command) => chalk.default.bold.cyan(command.name)).join(", ")}`);
2388
+ this.logger.utils.block(`Deployed ${commands.length} ${tag} to ${_seedcord_logger.paint.amber.bold(guild.name)}`, this.logger.utils.wrap(commands.map((command) => command.name)), "info");
2543
2389
  }
2544
2390
  this.onDeployed?.(result);
2545
2391
  return result;
@@ -2602,7 +2448,8 @@ function interactionGateContext(interaction, core) {
2602
2448
  guildId: interaction.guildId,
2603
2449
  channelId: interaction.channelId,
2604
2450
  memberRoleIds,
2605
- memberPermissions: interaction.memberPermissions?.bitfield ?? null
2451
+ memberPermissions: interaction.memberPermissions?.bitfield ?? null,
2452
+ routeId: null
2606
2453
  };
2607
2454
  }
2608
2455
  function eventGateContext(eventName, args, core) {
@@ -2619,7 +2466,8 @@ function eventGateContext(eventName, args, core) {
2619
2466
  guildId: actor.guild?.id ?? null,
2620
2467
  channelId: actor.channelId,
2621
2468
  memberRoleIds: actor.member ? [...actor.member.roles.cache.keys()].filter((id) => id !== actor.guild?.id) : [],
2622
- memberPermissions: actor.member?.permissions.bitfield ?? null
2469
+ memberPermissions: actor.member?.permissions.bitfield ?? null,
2470
+ routeId: null
2623
2471
  };
2624
2472
  }
2625
2473
 
@@ -2658,7 +2506,7 @@ var FaultThrottle = class {
2658
2506
 
2659
2507
  //#endregion
2660
2508
  //#region src/miscellaneous/extractErrorResponse.ts
2661
- const logger$2 = new _seedcord_services.Logger("ErrorsHandling");
2509
+ const logger$2 = new _seedcord_logger.Logger("ErrorsHandling");
2662
2510
  /**
2663
2511
  * The single throttle for every fault publish, so a recurring fault reports once per window across both
2664
2512
  * the interaction and event paths. Exported so tests reset it between cases.
@@ -2798,7 +2646,7 @@ function interactionKind(interaction) {
2798
2646
 
2799
2647
  //#endregion
2800
2648
  //#region src/bot/handleEventFault.ts
2801
- const logger$1 = new _seedcord_services.Logger("EventBoundary");
2649
+ const logger$1 = new _seedcord_logger.Logger("EventBoundary");
2802
2650
  /**
2803
2651
  * The event controller boundary. Reports a throw from an event handler and never replies, since a
2804
2652
  * generic event has no reply target. A {@link Silence} or a non-reporting {@link Notice} stops quietly, a
@@ -2842,20 +2690,23 @@ function handleEventFault(caught, eventName, handlerName, args, core) {
2842
2690
  */
2843
2691
  var EventDispatcher = class {
2844
2692
  core;
2845
- logger = new _seedcord_services.Logger("Events");
2693
+ logger = new _seedcord_logger.Logger("Events");
2846
2694
  isInitialized = false;
2847
2695
  name = "Events";
2848
2696
  eventMap = /* @__PURE__ */ new Map();
2849
2697
  middlewares = [];
2850
2698
  executedOnceHandlers = /* @__PURE__ */ new Set();
2851
2699
  attachedEvents = /* @__PURE__ */ new Set();
2700
+ loading = false;
2701
+ loadedHandlers = [];
2702
+ loadedMiddlewares = [];
2852
2703
  hmrHandler;
2853
2704
  constructor(core) {
2854
2705
  this.core = core;
2855
2706
  const eventsDir = this.core.config.bot.events.path;
2856
2707
  if (!eventsDir) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.CoreControllerPathMissing, ["EventDispatcher", "events"]);
2857
2708
  if (!envapt.Envapter.isDevelopment && !envapt.Envapter.isTest) return;
2858
- this.hmrHandler = new HmrModuleHandler({
2709
+ this.hmrHandler = new _seedcord_core_hmr.HmrModuleHandler({
2859
2710
  handlersDir: eventsDir,
2860
2711
  ...(0, _seedcord_utils.hasKeys)(this.core.config.bot.events, ["middlewares"]) && { middlewaresDir: this.core.config.bot.events.middlewares },
2861
2712
  isHandler: this.isEventHandlerClass.bind(this),
@@ -2878,23 +2729,32 @@ var EventDispatcher = class {
2878
2729
  this.isInitialized = true;
2879
2730
  const handlersDir = this.core.config.bot.events.path;
2880
2731
  if (!handlersDir) return;
2881
- this.logger.info(chalk.default.bold(handlersDir));
2882
2732
  const middlewareDir = (0, _seedcord_utils.hasKeys)(this.core.config.bot.events, ["middlewares"]) ? this.core.config.bot.events.middlewares : void 0;
2883
- if (middlewareDir) {
2884
- this.logger.info(`${chalk.default.bold(middlewareDir)} ${chalk.default.gray("(middlewares)")}`);
2885
- await this.loadMiddlewares(middlewareDir);
2733
+ this.loading = true;
2734
+ this.loadedHandlers.length = 0;
2735
+ this.loadedMiddlewares.length = 0;
2736
+ try {
2737
+ if (middlewareDir) await this.loadMiddlewares(middlewareDir);
2738
+ await this.loadHandlers(handlersDir);
2739
+ } finally {
2740
+ this.loading = false;
2886
2741
  }
2887
- await this.loadHandlers(handlersDir);
2888
2742
  this.attachToClient();
2889
- this.logger.info(`${chalk.default.bold.green("Loaded event handlers:")}`);
2890
- const eventList = [`${chalk.default.magenta.bold(this.middlewares.length)} middlewares`];
2891
- this.eventMap.forEach((handlers, eventName) => {
2892
- eventList.push(`${chalk.default.magenta.bold(handlers.length)} ${eventName}`);
2743
+ this.reportLoad();
2744
+ }
2745
+ reportLoad() {
2746
+ const { utils } = this.logger;
2747
+ utils.summary("Loaded", {
2748
+ "event handlers": this.loadedHandlers.length,
2749
+ middlewares: this.loadedMiddlewares.length
2893
2750
  });
2894
- this.logger.utils.list(eventList);
2751
+ if (this.loadedMiddlewares.length > 0) utils.block("Loaded event middlewares", utils.entries(this.loadedMiddlewares));
2752
+ const perEvent = {};
2753
+ for (const [event, handlers] of this.eventMap) perEvent[event] = handlers.length;
2754
+ utils.block("Loaded event handlers", [...utils.entries(this.loadedHandlers), ...utils.counts(perEvent)]);
2895
2755
  }
2896
2756
  async loadHandlers(dir) {
2897
- await (0, _seedcord_utils.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
2757
+ await (0, _seedcord_utils_node.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
2898
2758
  for (const val of Object.values(imported)) {
2899
2759
  if (!this.isEventHandlerClass(val)) continue;
2900
2760
  this.registerHandler(val, relativePath);
@@ -2903,7 +2763,7 @@ var EventDispatcher = class {
2903
2763
  }, this.logger);
2904
2764
  }
2905
2765
  async loadMiddlewares(dir) {
2906
- await (0, _seedcord_utils.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
2766
+ await (0, _seedcord_utils_node.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
2907
2767
  for (const val of Object.values(imported)) {
2908
2768
  if (!this.isMiddlewareClass(val)) continue;
2909
2769
  this.registerMiddleware(val, relativePath);
@@ -2941,7 +2801,14 @@ var EventDispatcher = class {
2941
2801
  ...metadata.events && { events: metadata.events }
2942
2802
  });
2943
2803
  this.middlewares.sort((a, b) => a.priority - b.priority);
2944
- this.logger.utils.registration(`${middlewareCtor.name} ${chalk.default.gray(`(${metadata.priority})`)}`, relativePath, "event middleware");
2804
+ if (this.loading) {
2805
+ this.loadedMiddlewares.push({
2806
+ name: `${middlewareCtor.name} (${metadata.priority})`,
2807
+ from: (0, _seedcord_utils.formatFilePath)(relativePath)
2808
+ });
2809
+ return;
2810
+ }
2811
+ this.logger.utils.registration(`${middlewareCtor.name} ${_seedcord_logger.paint.mute(`(${metadata.priority})`)}`, relativePath, "event middleware");
2945
2812
  }
2946
2813
  async runMiddlewares(eventName, args) {
2947
2814
  for (const { ctor, events } of this.middlewares) {
@@ -2982,7 +2849,12 @@ var EventDispatcher = class {
2982
2849
  const names = areRoutes(raw) ? raw : typeof raw === "string" ? [raw] : [];
2983
2850
  for (const name of names) register(name, "on");
2984
2851
  }
2985
- this.logger.utils.registration(handlerClass.name, relativePath);
2852
+ const from = (0, _seedcord_utils.formatFilePath)(relativePath);
2853
+ if (this.loading) this.loadedHandlers.push({
2854
+ name: handlerClass.name,
2855
+ from
2856
+ });
2857
+ else this.logger.utils.registration(handlerClass.name, from);
2986
2858
  }
2987
2859
  attachToClient() {
2988
2860
  for (const [eventName] of this.eventMap) this.attachListener(eventName);
@@ -2991,12 +2863,12 @@ var EventDispatcher = class {
2991
2863
  if (this.attachedEvents.has(eventName)) return;
2992
2864
  this.attachedEvents.add(eventName);
2993
2865
  const handlerEntries = this.eventMap.get(eventName);
2994
- this.logger.debug(`Attaching ${chalk.default.bold.green(eventName)} to the client with ${chalk.default.gray(handlerEntries?.length ?? 0)} handler(s)`);
2866
+ this.logger.debug(`Attaching ${_seedcord_logger.paint.mint.bold(eventName)} to the client with ${_seedcord_logger.paint.mute(handlerEntries?.length ?? 0)} handler(s)`);
2995
2867
  this.core.bot.client.on(eventName, (...args) => {
2996
- this.core.bot.emit("any:event", eventName, ...args);
2868
+ this.core.bot.emitSafe("any:event", eventName, ...args);
2997
2869
  (async () => {
2998
2870
  await this.processEvent(eventName, args).catch((err) => {
2999
- this.logger.error(`[${chalk.default.bold.red("UNHANDLED ERROR AT ROOT")}] ${err.name}`, err.stack);
2871
+ this.logger.error(`[${_seedcord_logger.paint.coral.bold("UNHANDLED ERROR AT ROOT")}] ${err.name}`, err.stack);
3000
2872
  this.core.bot.emit("error:unhandled:event", err);
3001
2873
  });
3002
2874
  })();
@@ -3018,7 +2890,7 @@ var EventDispatcher = class {
3018
2890
  }
3019
2891
  async processHandler(eventName, ctor, args) {
3020
2892
  try {
3021
- this.logger.debug(`Processing ${chalk.default.bold.green(eventName)} with ${chalk.default.gray(ctor.name)}`);
2893
+ this.logger.debug(`Processing ${_seedcord_logger.paint.mint.bold(eventName)} with ${_seedcord_logger.paint.mute(ctor.name)}`);
3022
2894
  const handler = new ctor(args, this.core, eventName);
3023
2895
  await (0, _seedcord_core_internal.runHandlerGates)(ctor, eventGateContext(eventName, args, this.core));
3024
2896
  await handler.execute();
@@ -3047,7 +2919,7 @@ var UnhandledEvent = class extends InteractionHandler {
3047
2919
 
3048
2920
  //#endregion
3049
2921
  //#region src/bot/handleInteractionFault.ts
3050
- const logger = new _seedcord_services.Logger("InteractionBoundary");
2922
+ const logger = new _seedcord_logger.Logger("InteractionBoundary");
3051
2923
  /**
3052
2924
  * The interaction controller boundary. Sorts a throw from any handler-lifecycle phase into the right
3053
2925
  * user reply plus the right bus event, building a {@link ReplySender} lazily so it reads the live
@@ -3094,7 +2966,7 @@ async function handleInteractionFault(caught, interaction, core) {
3094
2966
  */
3095
2967
  var InteractionDispatcher = class {
3096
2968
  core;
3097
- logger = new _seedcord_services.Logger("Interactions");
2969
+ logger = new _seedcord_logger.Logger("Interactions");
3098
2970
  isInitialized = false;
3099
2971
  name = "Interactions";
3100
2972
  slashMap = /* @__PURE__ */ new Map();
@@ -3110,6 +2982,9 @@ var InteractionDispatcher = class {
3110
2982
  autocompleteMap = /* @__PURE__ */ new Map();
3111
2983
  keysToIgnore = /* @__PURE__ */ new Set();
3112
2984
  middlewares = [];
2985
+ loading = false;
2986
+ loadedHandlers = [];
2987
+ loadedMiddlewares = [];
3113
2988
  hmrHandler;
3114
2989
  routeTypes = [
3115
2990
  [_seedcord_core_internal.InteractionRoutes.Slash, this.slashMap],
@@ -3132,7 +3007,7 @@ var InteractionDispatcher = class {
3132
3007
  const interactionsDir = this.core.config.bot.interactions.path;
3133
3008
  if (!interactionsDir) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.CoreControllerPathMissing, ["InteractionDispatcher", "interactions"]);
3134
3009
  if (!envapt.Envapter.isDevelopment && !envapt.Envapter.isTest) return;
3135
- this.hmrHandler = new HmrModuleHandler({
3010
+ this.hmrHandler = new _seedcord_core_hmr.HmrModuleHandler({
3136
3011
  handlersDir: interactionsDir,
3137
3012
  ...(0, _seedcord_utils.hasKeys)(this.core.config.bot.interactions, ["middlewares"]) && this.core.config.bot.interactions.middlewares && { middlewaresDir: this.core.config.bot.interactions.middlewares },
3138
3013
  isHandler: this.isHandlerClass.bind(this),
@@ -3152,7 +3027,7 @@ var InteractionDispatcher = class {
3152
3027
  * @internal
3153
3028
  */
3154
3029
  warnUnhandledRoutes(commandLeaves) {
3155
- for (const route of commandLeaves) if (!this.slashMap.has(route)) this.logger.warn(`Slash route ${chalk.default.bold.cyan(route)} has no registered ${chalk.default.bold("@SlashRoute")} handler.`);
3030
+ for (const route of commandLeaves) if (!this.slashMap.has(route)) this.logger.warn(`Slash route ${_seedcord_logger.paint.sky.bold(route)} has no registered ${chalk.default.bold("@SlashRoute")} handler.`);
3156
3031
  }
3157
3032
  /**
3158
3033
  * Warns for each context-menu command with no matching handler, which would fall through to UnhandledEvent
@@ -3161,8 +3036,8 @@ var InteractionDispatcher = class {
3161
3036
  * @internal
3162
3037
  */
3163
3038
  warnUnhandledContextMenuRoutes(leaves) {
3164
- for (const name of leaves.user) if (!this.userContextMenuMap.has(name)) this.logger.warn(`User context menu ${chalk.default.bold.cyan(name)} has no registered ${chalk.default.bold("@ContextMenuRoute")} handler.`);
3165
- for (const name of leaves.message) if (!this.messageContextMenuMap.has(name)) this.logger.warn(`Message context menu ${chalk.default.bold.cyan(name)} has no registered ${chalk.default.bold("@ContextMenuRoute")} handler.`);
3039
+ for (const name of leaves.user) if (!this.userContextMenuMap.has(name)) this.logger.warn(`User context menu ${_seedcord_logger.paint.sky.bold(name)} has no registered ${chalk.default.bold("@ContextMenuRoute")} handler.`);
3040
+ for (const name of leaves.message) if (!this.messageContextMenuMap.has(name)) this.logger.warn(`Message context menu ${_seedcord_logger.paint.sky.bold(name)} has no registered ${chalk.default.bold("@ContextMenuRoute")} handler.`);
3166
3041
  }
3167
3042
  getArtifacts(handlerClass) {
3168
3043
  const artifacts = [];
@@ -3180,32 +3055,42 @@ var InteractionDispatcher = class {
3180
3055
  this.isInitialized = true;
3181
3056
  const handlersDir = this.core.config.bot.interactions.path;
3182
3057
  if (!handlersDir) return;
3183
- this.logger.info(chalk.default.bold(handlersDir));
3184
3058
  const middlewareDir = (0, _seedcord_utils.hasKeys)(this.core.config.bot.interactions, ["middlewares"]) ? this.core.config.bot.interactions.middlewares : void 0;
3185
- if (middlewareDir) {
3186
- this.logger.info(`${chalk.default.bold(middlewareDir)} ${chalk.default.gray("(middlewares)")}`);
3187
- await this.loadMiddlewares(middlewareDir);
3059
+ this.loading = true;
3060
+ this.loadedHandlers.length = 0;
3061
+ this.loadedMiddlewares.length = 0;
3062
+ try {
3063
+ if (middlewareDir) await this.loadMiddlewares(middlewareDir);
3064
+ await this.loadHandlers(handlersDir);
3065
+ } finally {
3066
+ this.loading = false;
3188
3067
  }
3189
- await this.loadHandlers(handlersDir);
3190
3068
  this.attachToClient();
3191
- this.logger.info(`${chalk.default.bold.green("Loaded interaction handlers:")}`);
3192
- this.logger.utils.list([
3193
- `${chalk.default.magenta.bold(this.middlewares.length)} middlewares`,
3194
- `${chalk.default.magenta.bold(this.slashMap.size)} slash commands`,
3195
- `${chalk.default.magenta.bold(this.buttonMap.size)} buttons`,
3196
- `${chalk.default.magenta.bold(this.modalMap.size)} modals`,
3197
- `${chalk.default.magenta.bold(this.stringSelectMap.size)} string selects`,
3198
- `${chalk.default.magenta.bold(this.userSelectMap.size)} user selects`,
3199
- `${chalk.default.magenta.bold(this.roleSelectMap.size)} role selects`,
3200
- `${chalk.default.magenta.bold(this.channelSelectMap.size)} channel selects`,
3201
- `${chalk.default.magenta.bold(this.mentionableSelectMap.size)} mentionable selects`,
3202
- `${chalk.default.magenta.bold(this.messageContextMenuMap.size)} message context menus`,
3203
- `${chalk.default.magenta.bold(this.userContextMenuMap.size)} user context menus`,
3204
- `${chalk.default.magenta.bold(this.autocompleteMap.size)} autocomplete`
3205
- ]);
3069
+ this.reportLoad();
3070
+ }
3071
+ reportLoad() {
3072
+ const { utils } = this.logger;
3073
+ utils.summary("Loaded", {
3074
+ "interaction handlers": this.loadedHandlers.length,
3075
+ middlewares: this.loadedMiddlewares.length
3076
+ });
3077
+ if (this.loadedMiddlewares.length > 0) utils.block("Loaded middlewares", utils.entries(this.loadedMiddlewares));
3078
+ utils.block("Loaded interaction handlers", [...utils.entries(this.loadedHandlers), ...utils.counts({
3079
+ slash: this.slashMap.size,
3080
+ buttons: this.buttonMap.size,
3081
+ modals: this.modalMap.size,
3082
+ "string selects": this.stringSelectMap.size,
3083
+ "user selects": this.userSelectMap.size,
3084
+ "role selects": this.roleSelectMap.size,
3085
+ "channel selects": this.channelSelectMap.size,
3086
+ "mentionable selects": this.mentionableSelectMap.size,
3087
+ "message menus": this.messageContextMenuMap.size,
3088
+ "user menus": this.userContextMenuMap.size,
3089
+ autocomplete: this.autocompleteMap.size
3090
+ })]);
3206
3091
  }
3207
3092
  async loadHandlers(dir) {
3208
- await (0, _seedcord_utils.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
3093
+ await (0, _seedcord_utils_node.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
3209
3094
  for (const val of Object.values(imported)) {
3210
3095
  if (!this.isHandlerClass(val)) continue;
3211
3096
  this.registerHandler(val, relativePath);
@@ -3214,7 +3099,7 @@ var InteractionDispatcher = class {
3214
3099
  }, this.logger);
3215
3100
  }
3216
3101
  async loadMiddlewares(dir) {
3217
- await (0, _seedcord_utils.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
3102
+ await (0, _seedcord_utils_node.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
3218
3103
  for (const val of Object.values(imported)) {
3219
3104
  if (!this.isMiddlewareClass(val)) continue;
3220
3105
  this.registerMiddleware(val, relativePath);
@@ -3232,7 +3117,14 @@ var InteractionDispatcher = class {
3232
3117
  priority: metadata.priority
3233
3118
  });
3234
3119
  this.middlewares.sort((a, b) => a.priority - b.priority);
3235
- this.logger.utils.registration(`${middlewareCtor.name} ${chalk.default.gray(`(${metadata.priority})`)}`, relativePath, "middleware");
3120
+ if (this.loading) {
3121
+ this.loadedMiddlewares.push({
3122
+ name: `${middlewareCtor.name} (${metadata.priority})`,
3123
+ from: (0, _seedcord_utils.formatFilePath)(relativePath)
3124
+ });
3125
+ return;
3126
+ }
3127
+ this.logger.utils.registration(`${middlewareCtor.name} ${_seedcord_logger.paint.mute(`(${metadata.priority})`)}`, relativePath, "middleware");
3236
3128
  }
3237
3129
  isHandlerClass(obj) {
3238
3130
  if (typeof obj !== "function") return false;
@@ -3256,9 +3148,15 @@ var InteractionDispatcher = class {
3256
3148
  ]);
3257
3149
  writes.push([map, route]);
3258
3150
  }
3259
- this.logger.utils.registration(handlerClass.name, (0, _seedcord_utils.formatFilePath)(relativePath));
3260
3151
  }
3152
+ if (writes.length === 0) return;
3261
3153
  for (const [map, route] of writes) map.set(route, handlerClass);
3154
+ const from = (0, _seedcord_utils.formatFilePath)(relativePath);
3155
+ if (this.loading) this.loadedHandlers.push({
3156
+ name: handlerClass.name,
3157
+ from
3158
+ });
3159
+ else this.logger.utils.registration(handlerClass.name, from);
3262
3160
  }
3263
3161
  /** @internal For use in dev mode */
3264
3162
  async onHmr(event) {
@@ -3284,9 +3182,9 @@ var InteractionDispatcher = class {
3284
3182
  }
3285
3183
  attachToClient() {
3286
3184
  this.core.bot.client.on(discord_js.Events.InteractionCreate, (interaction) => {
3287
- this.core.bot.emit("any:interaction", interaction);
3185
+ this.core.bot.emitSafe("any:interaction", interaction);
3288
3186
  this.handleInteraction(interaction).catch((err) => {
3289
- this.logger.error(`[${chalk.default.bold.red("UNHANDLED ERROR AT ROOT")}] ${err.name}`, err.stack);
3187
+ this.logger.error(`[${_seedcord_logger.paint.coral.bold("UNHANDLED ERROR AT ROOT")}] ${err.name}`, err.stack);
3290
3188
  this.core.bot.emit("error:unhandled:interaction", err);
3291
3189
  });
3292
3190
  });
@@ -3303,11 +3201,12 @@ var InteractionDispatcher = class {
3303
3201
  if (!interaction.isAutocomplete()) for (const { ctor } of this.middlewares) await new ctor(interaction, this.core).execute();
3304
3202
  let HandlerCtor = getHandler(key);
3305
3203
  if (!HandlerCtor) {
3306
- this.logger.warn(`No handler found for key ${chalk.default.bold.cyan(key)}. Falling back to UnhandledEvent.`);
3204
+ this.logger.warn(`No handler found for key ${_seedcord_logger.paint.sky.bold(key)}. Falling back to UnhandledEvent.`);
3307
3205
  HandlerCtor = UnhandledEvent;
3308
3206
  }
3309
- this.logger.debug(`Processing ${chalk.default.bold.green(key)} with ${chalk.default.gray(HandlerCtor.name)}`);
3310
- const handler = new HandlerCtor(interaction, this.core);
3207
+ this.logger.debug(`Processing ${_seedcord_logger.paint.mint.bold(key)} with ${_seedcord_logger.paint.mute(HandlerCtor.name)}`);
3208
+ const dispatch = new _seedcord_core.DispatchContext((0, _seedcord_core_internal.routeIdOf)(HandlerCtor));
3209
+ const handler = new HandlerCtor(interaction, this.core, dispatch);
3311
3210
  if (!interaction.isAutocomplete()) await (0, _seedcord_core_internal.runHandlerGates)(HandlerCtor, interactionGateContext(interaction, this.core));
3312
3211
  await handler.execute();
3313
3212
  } catch (caught) {
@@ -3388,7 +3287,7 @@ var InteractionDispatcher = class {
3388
3287
  async handleAutocomplete(interaction) {
3389
3288
  const route = slashRouteOf(interaction);
3390
3289
  if (!this.autocompleteMap.has(route)) {
3391
- this.logger.warn(`No autocomplete handler for ${chalk.default.bold.cyan(route)}.`);
3290
+ this.logger.warn(`No autocomplete handler for ${_seedcord_logger.paint.sky.bold(route)}.`);
3392
3291
  await interaction.respond([]);
3393
3292
  return;
3394
3293
  }
@@ -3420,7 +3319,7 @@ function __decorate(decorators, target, key, desc) {
3420
3319
  */
3421
3320
  var Bot = class extends Plugin {
3422
3321
  /** @internal */
3423
- logger = new _seedcord_services.Logger("Bot");
3322
+ logger = new _seedcord_logger.Logger("Bot");
3424
3323
  isInitialized = false;
3425
3324
  _client;
3426
3325
  interactions;
@@ -3498,48 +3397,14 @@ var Bot = class extends Plugin {
3498
3397
  emit(event, ...args) {
3499
3398
  return super.emit(event, ...args);
3500
3399
  }
3501
- };
3502
- __decorate([(0, envapt.Envapt)("DISCORD_BOT_TOKEN", { converter: (raw) => (0, _seedcord_errors_internal.validateDiscordToken)(raw) }), __decorateMetadata("design:type", String)], Bot.prototype, "botToken", void 0);
3503
-
3504
- //#endregion
3505
- //#region src/hmr/HmrManager.ts
3506
- var HmrManager = class {
3507
- logger = new _seedcord_services.Logger("HMR", { channel: "hmr" });
3508
- listeners = /* @__PURE__ */ new Set();
3509
- constructor() {}
3510
- /** @internal */
3511
- init() {
3512
- if (!{}.hot) return;
3513
- setDevChannel((0, _seedcord_types_internal.wrapHot)({}.hot));
3514
- if (envapt.Envapter.isDevelopment || envapt.Envapter.isTest) {
3515
- this.logger.info("Enabled");
3516
- getDevChannel()?.on("seedcord:hmr", (payload) => {
3517
- const affected = payload.affectedModules?.length ?? 0;
3518
- this.logger.info(`${chalk.default.bold("1")} module changed, ${chalk.default.bold(affected)} affected modules`);
3519
- if (payload.affectedModules) this.logger.utils.list(payload.affectedModules.map((mod) => (0, _seedcord_utils.formatFilePath)(mod)), "Affected modules: ");
3520
- this.handleUpdate(payload);
3521
- });
3522
- }
3523
- }
3524
- /** @internal */
3525
- register(listener) {
3526
- this.listeners.add(listener);
3527
- }
3528
- /** @internal */
3529
- unregister(listener) {
3530
- this.listeners.delete(listener);
3400
+ emitSafe(event, ...args) {
3401
+ return super.emitSafe(event, ...args);
3531
3402
  }
3532
- async handleUpdate(event) {
3533
- const promises = [...this.listeners].map(async (listener) => {
3534
- try {
3535
- await listener.onHmr(event);
3536
- } catch (error) {
3537
- this.logger.error(`Error handling HMR update in ${chalk.default.bold(listener.name)}: `, error);
3538
- }
3539
- });
3540
- await Promise.all(promises);
3403
+ onListenerError(error, event) {
3404
+ this.logger.error(`listener for ${String(event)} threw`, error);
3541
3405
  }
3542
3406
  };
3407
+ __decorate([(0, envapt_legacy.Envapt)("DISCORD_BOT_TOKEN", { converter: (raw) => (0, _seedcord_errors_internal.validateDiscordToken)(raw) }), __decorateMetadata("design:type", String)], Bot.prototype, "botToken", void 0);
3543
3408
 
3544
3409
  //#endregion
3545
3410
  //#region src/subscribers/bases/webhookHelpers.ts
@@ -3606,7 +3471,7 @@ let HandledException = class HandledException extends WebhookLog {
3606
3471
  }
3607
3472
  }
3608
3473
  };
3609
- __decorate([(0, envapt.Envapt)("HANDLED_EXCEPTION_WEBHOOK_URL", { converter: (raw) => webhookUrlValidator$1(raw) }), __decorateMetadata("design:type", String)], HandledException, "handledExceptionWebhookUrl", void 0);
3474
+ __decorate([(0, envapt_legacy.Envapt)("HANDLED_EXCEPTION_WEBHOOK_URL", { converter: (raw) => webhookUrlValidator$1(raw) }), __decorateMetadata("design:type", String)], HandledException, "handledExceptionWebhookUrl", void 0);
3610
3475
  HandledException = _HandledException = __decorate([Subscribe("handledException")], HandledException);
3611
3476
  var HandledExceptionContainer = class extends _seedcord_core.BuilderComponent {
3612
3477
  constructor(data) {
@@ -3676,7 +3541,7 @@ let UnknownException = class UnknownException extends WebhookLog {
3676
3541
  }
3677
3542
  }
3678
3543
  };
3679
- __decorate([(0, envapt.Envapt)("UNKNOWN_EXCEPTION_WEBHOOK_URL", { converter: (raw) => webhookUrlValidator(raw) }), __decorateMetadata("design:type", String)], UnknownException, "unknownExceptionWebhookUrl", void 0);
3544
+ __decorate([(0, envapt_legacy.Envapt)("UNKNOWN_EXCEPTION_WEBHOOK_URL", { converter: (raw) => webhookUrlValidator(raw) }), __decorateMetadata("design:type", String)], UnknownException, "unknownExceptionWebhookUrl", void 0);
3680
3545
  UnknownException = _UnknownException = __decorate([Subscribe("unknownException")], UnknownException);
3681
3546
  var UnhandledErrorContainer = class extends _seedcord_core.BuilderComponent {
3682
3547
  constructor(data) {
@@ -3715,7 +3580,7 @@ var UnhandledErrorContainer = class extends _seedcord_core.BuilderComponent {
3715
3580
  */
3716
3581
  var Bus = class extends Plugin {
3717
3582
  core;
3718
- logger = new _seedcord_services.Logger("Subscribers");
3583
+ logger = new _seedcord_logger.Logger("Subscribers");
3719
3584
  /** @internal */
3720
3585
  name = "Subscribers";
3721
3586
  isInitialized = false;
@@ -3727,7 +3592,7 @@ var Bus = class extends Plugin {
3727
3592
  this.core = core;
3728
3593
  if (this.core.config.subscribers.path) {
3729
3594
  if (!envapt.Envapter.isDevelopment && !envapt.Envapter.isTest) return;
3730
- this.hmrHandler = new HmrModuleHandler({
3595
+ this.hmrHandler = new _seedcord_core_hmr.HmrModuleHandler({
3731
3596
  handlersDir: this.core.config.subscribers.path,
3732
3597
  isHandler: this.isSubscriber.bind(this),
3733
3598
  registerHandler: this.registerSubscriber.bind(this),
@@ -3760,7 +3625,7 @@ var Bus = class extends Plugin {
3760
3625
  if (this.hmrHandler) await this.hmrHandler.handle(event);
3761
3626
  }
3762
3627
  async loadSubscribers(dir) {
3763
- await (0, _seedcord_utils.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
3628
+ await (0, _seedcord_utils_node.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
3764
3629
  for (const exportName of Object.keys(imported)) {
3765
3630
  const val = imported[exportName];
3766
3631
  if (this.isSubscriber(val)) {
@@ -3831,7 +3696,7 @@ var Bus = class extends Plugin {
3831
3696
  //#endregion
3832
3697
  //#region src/version.ts
3833
3698
  /** Package version */
3834
- const version = "0.1.0-next.0";
3699
+ const version = "0.1.0-next.2";
3835
3700
 
3836
3701
  //#endregion
3837
3702
  //#region src/Seedcord.ts
@@ -3850,6 +3715,8 @@ var Seedcord = class Seedcord extends Pluggable {
3850
3715
  /** @internal */
3851
3716
  version = version;
3852
3717
  static isInstantiated = false;
3718
+ healthCheck;
3719
+ hmrManager;
3853
3720
  /** @see {@link CoordinatedShutdown} */
3854
3721
  shutdown;
3855
3722
  /** @see {@link CoordinatedStartup} */
@@ -3860,10 +3727,6 @@ var Seedcord = class Seedcord extends Pluggable {
3860
3727
  bot;
3861
3728
  /** @see {@link IRateLimiter} */
3862
3729
  rateLimiter;
3863
- /** @see {@link HealthCheck} */
3864
- healthCheck;
3865
- /** @see {@link HmrManager} */
3866
- hmrManager;
3867
3730
  /**
3868
3731
  * Creates a new Seedcord instance
3869
3732
  *
@@ -3877,25 +3740,26 @@ var Seedcord = class Seedcord extends Pluggable {
3877
3740
  const startup = new _seedcord_services.CoordinatedStartup();
3878
3741
  super(shutdown, startup);
3879
3742
  this.config = config;
3743
+ (0, _seedcord_logger_node.installNodeDefaults)(config.logger);
3880
3744
  (0, _seedcord_core_internal.setBotColor)(config.botColor);
3881
3745
  this.shutdown = shutdown;
3882
3746
  this.startup = startup;
3883
- this.hmrManager = new HmrManager();
3747
+ this.hmrManager = new _seedcord_core_internal.HmrManager();
3884
3748
  this.hmrManager.init();
3885
3749
  this.bus = new Bus(this);
3886
3750
  this.bot = new Bot(this);
3887
- this.rateLimiter = new _seedcord_rate_limiter.MemoryRateLimiter();
3751
+ this.rateLimiter = config.store ?? new _seedcord_rate_limiter.MemoryRateLimiter();
3888
3752
  this.healthCheck = new _seedcord_services.HealthCheck(this.shutdown, config.healthCheck);
3889
3753
  this.registerStartupTasks();
3890
3754
  }
3891
- /** @internal */
3755
+ /** The bot's discord username, populated after login. */
3756
+ get username() {
3757
+ return this.bot.client.user?.username;
3758
+ }
3892
3759
  static reset() {
3893
3760
  Seedcord.isInstantiated = false;
3761
+ _seedcord_logger.LoggerChannelRegistry.instance.reset();
3894
3762
  }
3895
- /**
3896
- * Registers default startup tasks
3897
- * @internal
3898
- */
3899
3763
  registerStartupTasks() {
3900
3764
  if (envapt.Envapter.isDevelopment || envapt.Envapter.isTest) this.registerHmrAwareModules();
3901
3765
  this.startup.addTask(_seedcord_services.StartupPhase.Configuration, "Bus Initialization", async () => {
@@ -3947,7 +3811,6 @@ exports.Emojis = Emojis;
3947
3811
  exports.EventHandler = EventHandler;
3948
3812
  exports.EventMiddleware = EventMiddleware;
3949
3813
  exports.Gated = Gated;
3950
- exports.HmrModuleHandler = HmrModuleHandler;
3951
3814
  exports.IgnoreBots = IgnoreBots;
3952
3815
  exports.InteractionHandler = InteractionHandler;
3953
3816
  exports.InteractionMiddleware = InteractionMiddleware;
@@ -4006,6 +3869,20 @@ Object.keys(_seedcord_errors).forEach(function (k) {
4006
3869
  });
4007
3870
  });
4008
3871
 
3872
+ Object.keys(_seedcord_event_emitter).forEach(function (k) {
3873
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
3874
+ enumerable: true,
3875
+ get: function () { return _seedcord_event_emitter[k]; }
3876
+ });
3877
+ });
3878
+
3879
+ Object.keys(_seedcord_logger).forEach(function (k) {
3880
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
3881
+ enumerable: true,
3882
+ get: function () { return _seedcord_logger[k]; }
3883
+ });
3884
+ });
3885
+
4009
3886
  Object.keys(_seedcord_rate_limiter).forEach(function (k) {
4010
3887
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
4011
3888
  enumerable: true,