seedcord 0.15.0 → 0.16.0-next.1

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
@@ -533,7 +533,7 @@ function Middleware(type, priority = 0, options = {}) {
533
533
  const metadata = {
534
534
  priority: normalizedPriority,
535
535
  type,
536
- ...options.events && options.events.length > 0 ? { events: options.events } : {}
536
+ ...options.events && options.events.length > 0 && { events: options.events }
537
537
  };
538
538
  Reflect.defineMetadata(MiddlewareMetadataKey, metadata, ctor);
539
539
  };
@@ -1081,7 +1081,7 @@ function checkPermissions(a, b, c, d, e) {
1081
1081
  in: b,
1082
1082
  scope: c,
1083
1083
  inverse: d ?? false,
1084
- ...e ?? {}
1084
+ ...e
1085
1085
  } : a;
1086
1086
  const Missing = missingCtor ?? MissingPermissions;
1087
1087
  const Dangerous = dangerousCtor ?? HasDangerousPermissions;
@@ -1348,7 +1348,7 @@ var CommandMentionInjector = class {
1348
1348
  return (0, discord_js.chatInputApplicationCommandMention)(name ?? route, id);
1349
1349
  }
1350
1350
  toPlain(route) {
1351
- return `/${route.split("/").join(" ")}`;
1351
+ return `/${route.replaceAll("/", " ")}`;
1352
1352
  }
1353
1353
  clear() {
1354
1354
  for (const key of Object.keys(mentionStorage)) Reflect.deleteProperty(mentionStorage, key);
@@ -1489,7 +1489,7 @@ async function fetchRoleInGuild(guild, roleId) {
1489
1489
  }
1490
1490
  }
1491
1491
  async function scanGuildsForRole(client, roleId) {
1492
- const cached = client.guilds.cache.map((guild) => guild.roles.cache.get(roleId)).find((role) => role);
1492
+ const cached = client.guilds.cache.map((guild) => guild.roles.cache.get(roleId)).find(Boolean);
1493
1493
  if (cached) return cached;
1494
1494
  for (const guild of client.guilds.cache.values()) try {
1495
1495
  const role = await guild.roles.fetch(roleId);
@@ -1774,6 +1774,16 @@ async function getConfirmation(interaction, prompt, options) {
1774
1774
  return confirmed;
1775
1775
  }
1776
1776
 
1777
+ //#endregion
1778
+ //#region src/hmr/devChannel.ts
1779
+ let channel;
1780
+ function setDevChannel(next) {
1781
+ channel = next;
1782
+ }
1783
+ function getDevChannel() {
1784
+ return channel;
1785
+ }
1786
+
1777
1787
  //#endregion
1778
1788
  //#region src/interfaces/Plugin.ts
1779
1789
  /**
@@ -1790,9 +1800,8 @@ var Plugin = class extends _seedcord_services.StrictEventEmitter {
1790
1800
  this.pluggable = pluggable;
1791
1801
  }
1792
1802
  /**
1793
- * Reloads plugin state on an HMR update.
1794
- * @param _event - The HMR update event
1795
- * @virtual Override this method to handle HMR updates
1803
+ * Reloads plugin state on an HMR update. The base implementation is a no-op, override it in your plugin.
1804
+ * @virtual
1796
1805
  */
1797
1806
  onHmr(_event) {
1798
1807
  return Promise.resolve();
@@ -1802,7 +1811,7 @@ var Plugin = class extends _seedcord_services.StrictEventEmitter {
1802
1811
  * @param patterns - Glob patterns relative to the project root
1803
1812
  */
1804
1813
  registerCriticalFiles(patterns) {
1805
- if ({}.hot) ({}).hot.send("seedcord:register-critical-files", { patterns });
1814
+ getDevChannel()?.send("seedcord:register-critical-files", { patterns });
1806
1815
  }
1807
1816
  /** @internal */
1808
1817
  setMaxListeners(n) {
@@ -2444,7 +2453,7 @@ var Controls = class {
2444
2453
  };
2445
2454
  }
2446
2455
  row(...keys) {
2447
- if (keys.length < 1) throw new _seedcord_errors_internal.SeedcordRangeError(_seedcord_errors.SeedcordErrorCode.PaginationEmptyControls);
2456
+ if (keys.length === 0) throw new _seedcord_errors_internal.SeedcordRangeError(_seedcord_errors.SeedcordErrorCode.PaginationEmptyControls);
2448
2457
  if (keys.length > MAX_BUTTONS_PER_ROW) throw new _seedcord_errors_internal.SeedcordRangeError(_seedcord_errors.SeedcordErrorCode.PaginationTooManyControls, [keys.length]);
2449
2458
  const seen = /* @__PURE__ */ new Set();
2450
2459
  for (const key of keys) {
@@ -2508,7 +2517,7 @@ const CURSOR_KEYS = [
2508
2517
  function renderPage(view, cursor, config) {
2509
2518
  const controls = new Controls(cursor, view);
2510
2519
  if (config.render) return toReplyResponse(config.render(view, controls));
2511
- return { components: [new PageContainer(view, config.renderItem ?? ((item) => String(item)), controls.row(...view.totalPages === void 0 ? CURSOR_KEYS : ARRAY_KEYS)).component] };
2520
+ return { components: [new PageContainer(view, config.renderItem ?? String, controls.row(...view.totalPages === void 0 ? CURSOR_KEYS : ARRAY_KEYS)).component] };
2512
2521
  }
2513
2522
 
2514
2523
  //#endregion
@@ -2585,7 +2594,7 @@ var ArraySource = class {
2585
2594
  constructor(load, opts) {
2586
2595
  this.load = load;
2587
2596
  this.perPage = opts?.perPage ?? DEFAULT_PER_PAGE;
2588
- if (!Number.isInteger(this.perPage) || this.perPage <= 0) throw new _seedcord_errors_internal.SeedcordRangeError(_seedcord_errors.SeedcordErrorCode.PaginationInvalidPerPage, [this.perPage]);
2597
+ if (!Number.isSafeInteger(this.perPage) || this.perPage <= 0) throw new _seedcord_errors_internal.SeedcordRangeError(_seedcord_errors.SeedcordErrorCode.PaginationInvalidPerPage, [this.perPage]);
2589
2598
  }
2590
2599
  async page(ctx, n) {
2591
2600
  return (0, _seedcord_kit.paginate)(await this.load(ctx), n, this.perPage);
@@ -2605,7 +2614,7 @@ var CursorSource = class {
2605
2614
  constructor(fetch, opts) {
2606
2615
  this.fetch = fetch;
2607
2616
  this.perPage = opts?.perPage ?? DEFAULT_PER_PAGE;
2608
- if (!Number.isInteger(this.perPage) || this.perPage <= 0) throw new _seedcord_errors_internal.SeedcordRangeError(_seedcord_errors.SeedcordErrorCode.PaginationInvalidPerPage, [this.perPage]);
2617
+ if (!Number.isSafeInteger(this.perPage) || this.perPage <= 0) throw new _seedcord_errors_internal.SeedcordRangeError(_seedcord_errors.SeedcordErrorCode.PaginationInvalidPerPage, [this.perPage]);
2609
2618
  }
2610
2619
  async page(ctx, n) {
2611
2620
  const page = Math.max(0, Math.trunc(n));
@@ -2718,15 +2727,7 @@ var HmrModuleHandler = class {
2718
2727
  ...options,
2719
2728
  logger: options.logger.inChannel("hmr")
2720
2729
  };
2721
- if ({}.hot) {
2722
- const data = {}.hot.data;
2723
- data.hmr ??= {};
2724
- this.store = data.hmr[this.options.name] = data.hmr[this.options.name] ?? {
2725
- fileToHandlers: /* @__PURE__ */ new Map(),
2726
- fileToMiddlewares: /* @__PURE__ */ new Map(),
2727
- handlerArtifacts: /* @__PURE__ */ new Map()
2728
- };
2729
- } else this.store = {
2730
+ this.store = {
2730
2731
  fileToHandlers: /* @__PURE__ */ new Map(),
2731
2732
  fileToMiddlewares: /* @__PURE__ */ new Map(),
2732
2733
  handlerArtifacts: /* @__PURE__ */ new Map()
@@ -2752,6 +2753,7 @@ var HmrModuleHandler = class {
2752
2753
  }
2753
2754
  return;
2754
2755
  }
2756
+ const rollbackEnabled = event.rollback ?? true;
2755
2757
  const filesToReload = affectedModules && affectedModules.length > 0 ? affectedModules : [file];
2756
2758
  const absHandlersDir = (0, node_path.resolve)(process.cwd(), handlersDir);
2757
2759
  const absMiddlewaresDir = middlewaresDir ? (0, node_path.resolve)(process.cwd(), middlewaresDir) : null;
@@ -2759,10 +2761,19 @@ var HmrModuleHandler = class {
2759
2761
  const isHandler = fileToReload.startsWith(absHandlersDir);
2760
2762
  const isMiddleware = absMiddlewaresDir ? fileToReload.startsWith(absMiddlewaresDir) : false;
2761
2763
  if (!isHandler && !isMiddleware) continue;
2762
- this.unload(fileToReload);
2763
- await this.reloadFile(fileToReload);
2764
+ await this.reloadWithRollback(fileToReload, rollbackEnabled);
2764
2765
  }
2765
2766
  }
2767
+ async reloadWithRollback(file, rollbackEnabled) {
2768
+ const { logger } = this.options;
2769
+ const snapshot = this.snapshotUnits(file);
2770
+ this.unload(file);
2771
+ if (await this.reloadFile(file)) return;
2772
+ this.unload(file);
2773
+ if (!rollbackEnabled) return;
2774
+ this.restoreUnits(file, snapshot);
2775
+ logger.warn(`${chalk.default.yellow.bold("Rolled back")} ${chalk.default.gray((0, _seedcord_utils.formatFilePath)(file))} to the last-good version`);
2776
+ }
2766
2777
  /**
2767
2778
  * Tracks a handler module as being loaded from a specific file.
2768
2779
  * @param file - The file path the handler was loaded from.
@@ -2823,7 +2834,7 @@ var HmrModuleHandler = class {
2823
2834
  const { logger, isHandler, isMiddleware, registerHandler, registerMiddleware } = this.options;
2824
2835
  if (!(0, node_fs.existsSync)(file)) {
2825
2836
  logger.info(`File does not exist, skipping reload: ${(0, _seedcord_utils.formatFilePath)(file)}`);
2826
- return;
2837
+ return false;
2827
2838
  }
2828
2839
  try {
2829
2840
  let fileUrl = (0, node_url.pathToFileURL)(file).href;
@@ -2840,8 +2851,26 @@ var HmrModuleHandler = class {
2840
2851
  const name = val.name ?? "Middleware";
2841
2852
  logger.info(`${chalk.default.blue.bold("Reloaded")} ${chalk.default.cyan.bold(name)} from ${chalk.default.gray((0, _seedcord_utils.formatFilePath)(file))}`);
2842
2853
  }
2854
+ return true;
2843
2855
  } catch (error) {
2844
2856
  logger.error(`Failed to reload file: ${file}`, error);
2857
+ return false;
2858
+ }
2859
+ }
2860
+ snapshotUnits(file) {
2861
+ return {
2862
+ handlers: [...this.store.fileToHandlers.get(file) ?? []],
2863
+ middlewares: [...this.store.fileToMiddlewares.get(file) ?? []]
2864
+ };
2865
+ }
2866
+ restoreUnits(file, snapshot) {
2867
+ for (const handler of snapshot.handlers) {
2868
+ this.options.registerHandler(handler, file);
2869
+ this.trackHandler(file, handler);
2870
+ }
2871
+ if (this.options.registerMiddleware) for (const middleware of snapshot.middlewares) {
2872
+ this.options.registerMiddleware(middleware, file);
2873
+ this.trackMiddleware(file, middleware);
2845
2874
  }
2846
2875
  }
2847
2876
  };
@@ -2895,7 +2924,7 @@ function slashRouteLeaves(commands) {
2895
2924
  * Manages Discord application command registration and deployment.
2896
2925
  *
2897
2926
  * Scans command directories, builds command structures, and registers both global and guild-scoped commands
2898
- * to Discord's API. Accessed via `core.bot.commands`, not constructed directly.
2927
+ * to Discord's API. Accessed via `core.bot.commands`. Do not construct it directly.
2899
2928
  */
2900
2929
  var CommandRegistry = class {
2901
2930
  core;
@@ -2920,8 +2949,7 @@ var CommandRegistry = class {
2920
2949
  registerHandler: this.registerCommand.bind(this),
2921
2950
  unregisterHandler: this.unregisterCommand.bind(this),
2922
2951
  getArtifacts: this.getArtifacts.bind(this),
2923
- logger: this.logger,
2924
- name: "Commands"
2952
+ logger: this.logger
2925
2953
  });
2926
2954
  }
2927
2955
  getArtifacts(ctor) {
@@ -2939,7 +2967,7 @@ var CommandRegistry = class {
2939
2967
  global: this.globalCommands.length,
2940
2968
  "guild groups": this.guildCommands.size
2941
2969
  });
2942
- if ({}.hot) ({}).hot.on("seedcord:refresh-commands", (data) => {
2970
+ getDevChannel()?.on("seedcord:refresh-commands", (data) => {
2943
2971
  this.refresh(data.shouldRefresh);
2944
2972
  });
2945
2973
  }
@@ -2961,7 +2989,7 @@ var CommandRegistry = class {
2961
2989
  if (commandsDir && event.file.startsWith((0, node_path.resolve)(process.cwd(), commandsDir))) {
2962
2990
  this.pendingEvents.delete(event.file);
2963
2991
  this.pendingEvents.set(event.file, event);
2964
- if ({}.hot) ({}).hot.send("seedcord:commands-update-prompt", { files: Array.from(this.pendingEvents.keys()).map((f) => (0, _seedcord_utils.formatFilePath)(f)) });
2992
+ getDevChannel()?.send("seedcord:commands-update-prompt", { files: [...this.pendingEvents.keys()].map((f) => (0, _seedcord_utils.formatFilePath)(f)) });
2965
2993
  } else await this.hmrHandler?.handle(event);
2966
2994
  }
2967
2995
  async loadCommands(dir) {
@@ -2997,7 +3025,7 @@ var CommandRegistry = class {
2997
3025
  this.ctorToCommand.set(ctor, {
2998
3026
  name: comp.name,
2999
3027
  scope: meta.scope,
3000
- ...meta.scope === "guild" ? { guilds: meta.guilds } : {}
3028
+ ...meta.scope === "guild" && { guilds: meta.guilds }
3001
3029
  });
3002
3030
  }
3003
3031
  unregisterCommand(ctor, artifacts) {
@@ -3241,7 +3269,7 @@ function handleEventFault(caught, eventName, handlerName, args, core) {
3241
3269
  if (caught.reason !== void 0) logger$1.debug(`Silence: ${caught.reason}`);
3242
3270
  return;
3243
3271
  }
3244
- if (!(caught instanceof Error)) throw caught;
3272
+ if (!Error.isError(caught)) throw caught;
3245
3273
  const ignore = new Set(core.config.errors?.ignoreEventApiCodes ?? []);
3246
3274
  if (caught instanceof discord_js.DiscordAPIError && ignore.has(caught.code)) {
3247
3275
  logger$1.debug(`swallowed api code ${caught.code}`);
@@ -3287,7 +3315,7 @@ var EventDispatcher = class {
3287
3315
  if (!envapt.Envapter.isDevelopment && !envapt.Envapter.isTest) return;
3288
3316
  this.hmrHandler = new HmrModuleHandler({
3289
3317
  handlersDir: eventsDir,
3290
- ...(0, _seedcord_utils.hasKeys)(this.core.config.bot.events, ["middlewares"]) ? { middlewaresDir: this.core.config.bot.events.middlewares } : {},
3318
+ ...(0, _seedcord_utils.hasKeys)(this.core.config.bot.events, ["middlewares"]) && { middlewaresDir: this.core.config.bot.events.middlewares },
3291
3319
  isHandler: this.isEventHandlerClass.bind(this),
3292
3320
  isMiddleware: this.isMiddlewareClass.bind(this),
3293
3321
  registerHandler: this.registerHandler.bind(this),
@@ -3295,8 +3323,7 @@ var EventDispatcher = class {
3295
3323
  unregisterHandler: this.unregisterHandler.bind(this),
3296
3324
  unregisterMiddleware: this.unregisterMiddleware.bind(this),
3297
3325
  getArtifacts: this.getArtifacts.bind(this),
3298
- logger: this.logger,
3299
- name: "Event"
3326
+ logger: this.logger
3300
3327
  });
3301
3328
  }
3302
3329
  getArtifacts(ctor) {
@@ -3347,7 +3374,7 @@ var EventDispatcher = class {
3347
3374
  await this.hmrHandler?.handle(event);
3348
3375
  }
3349
3376
  unregisterHandler(handlerClass, artifacts) {
3350
- const events = artifacts ?? Array.from(this.eventMap.keys());
3377
+ const events = artifacts ?? [...this.eventMap.keys()];
3351
3378
  for (const event of events) {
3352
3379
  const handlers = this.eventMap.get(event);
3353
3380
  if (!handlers) continue;
@@ -3357,7 +3384,6 @@ var EventDispatcher = class {
3357
3384
  if (handlers.length === 0) this.eventMap.delete(event);
3358
3385
  }
3359
3386
  }
3360
- this.executedOnceHandlers.delete(handlerClass);
3361
3387
  }
3362
3388
  unregisterMiddleware(middlewareCtor) {
3363
3389
  const index = this.middlewares.findIndex((entry) => entry.ctor === middlewareCtor);
@@ -3370,7 +3396,7 @@ var EventDispatcher = class {
3370
3396
  this.middlewares.push({
3371
3397
  ctor: middlewareCtor,
3372
3398
  priority: metadata.priority,
3373
- ...metadata.events ? { events: metadata.events } : {}
3399
+ ...metadata.events && { events: metadata.events }
3374
3400
  });
3375
3401
  this.middlewares.sort((a, b) => a.priority - b.priority);
3376
3402
  this.logger.utils.registration(`${middlewareCtor.name} ${chalk.default.gray(`(${metadata.priority})`)}`, relativePath, "event middleware");
@@ -3441,7 +3467,10 @@ var EventDispatcher = class {
3441
3467
  if (handlersToExecute.length === 0) return;
3442
3468
  if (!await this.runMiddlewares(eventName, args)) return;
3443
3469
  for (const entry of handlersToExecute) {
3444
- if (entry.frequency === "once") this.executedOnceHandlers.add(entry.ctor);
3470
+ if (entry.frequency === "once") {
3471
+ if (this.executedOnceHandlers.has(entry.ctor)) continue;
3472
+ this.executedOnceHandlers.add(entry.ctor);
3473
+ }
3445
3474
  await this.processHandler(eventName, entry.ctor, args);
3446
3475
  }
3447
3476
  }
@@ -3489,7 +3518,7 @@ async function handleInteractionFault(caught, interaction, core) {
3489
3518
  if (caught.reason !== void 0) logger.debug(`Silence: ${caught.reason}`);
3490
3519
  return;
3491
3520
  }
3492
- if (!(caught instanceof Error)) throw caught;
3521
+ if (!Error.isError(caught)) throw caught;
3493
3522
  const ignore = new Set(core.config.errors?.ignoreApiCodes ?? []);
3494
3523
  if (caught instanceof discord_js.DiscordAPIError && ignore.has(caught.code)) {
3495
3524
  logger.debug(`swallowed api code ${caught.code}`);
@@ -3563,7 +3592,7 @@ var InteractionDispatcher = class {
3563
3592
  if (!envapt.Envapter.isDevelopment && !envapt.Envapter.isTest) return;
3564
3593
  this.hmrHandler = new HmrModuleHandler({
3565
3594
  handlersDir: interactionsDir,
3566
- ...(0, _seedcord_utils.hasKeys)(this.core.config.bot.interactions, ["middlewares"]) && this.core.config.bot.interactions.middlewares ? { middlewaresDir: this.core.config.bot.interactions.middlewares } : {},
3595
+ ...(0, _seedcord_utils.hasKeys)(this.core.config.bot.interactions, ["middlewares"]) && this.core.config.bot.interactions.middlewares && { middlewaresDir: this.core.config.bot.interactions.middlewares },
3567
3596
  isHandler: this.isHandlerClass.bind(this),
3568
3597
  isMiddleware: this.isMiddlewareClass.bind(this),
3569
3598
  registerHandler: this.registerHandler.bind(this),
@@ -3571,13 +3600,12 @@ var InteractionDispatcher = class {
3571
3600
  unregisterHandler: this.unregisterHandler.bind(this),
3572
3601
  unregisterMiddleware: this.unregisterMiddleware.bind(this),
3573
3602
  getArtifacts: this.getArtifacts.bind(this),
3574
- logger: this.logger,
3575
- name: "Interaction"
3603
+ logger: this.logger
3576
3604
  });
3577
3605
  }
3578
3606
  /**
3579
3607
  * Warns for each command route with no registered `@SlashRoute` handler, which would fall through to
3580
- * UnhandledEvent at runtime. Warns rather than throws since a bot may route commands outside the registry.
3608
+ * UnhandledEvent at runtime. A bot may route commands outside the registry, so this does not throw.
3581
3609
  *
3582
3610
  * @internal
3583
3611
  */
@@ -3655,12 +3683,9 @@ var InteractionDispatcher = class {
3655
3683
  registerMiddleware(middlewareCtor, relativePath) {
3656
3684
  const metadata = Reflect.getMetadata(MiddlewareMetadataKey, middlewareCtor);
3657
3685
  if (metadata?.type !== "interaction") return;
3658
- const existingIndex = this.middlewares.findIndex((entry) => entry.ctor.name === middlewareCtor.name);
3659
- if (existingIndex !== -1) this.middlewares[existingIndex] = {
3660
- ctor: middlewareCtor,
3661
- priority: metadata.priority
3662
- };
3663
- else this.middlewares.push({
3686
+ if (this.middlewares.some((entry) => entry.ctor === middlewareCtor)) return;
3687
+ if (this.middlewares.some((entry) => entry.ctor.name === middlewareCtor.name)) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.InteractionDuplicateMiddleware, [middlewareCtor.name]);
3688
+ this.middlewares.push({
3664
3689
  ctor: middlewareCtor,
3665
3690
  priority: metadata.priority
3666
3691
  });
@@ -3676,12 +3701,22 @@ var InteractionDispatcher = class {
3676
3701
  return obj.prototype instanceof InteractionMiddleware && Reflect.hasMetadata(MiddlewareMetadataKey, obj);
3677
3702
  }
3678
3703
  registerHandler(handlerClass, relativePath) {
3704
+ const writes = [];
3679
3705
  for (const [routeType, map] of this.routeTypes) {
3680
3706
  const meta = Reflect.getMetadata(InteractionRouteKeys[routeType], handlerClass);
3681
3707
  if (!areRoutes(meta)) continue;
3682
- meta.forEach((route) => map.set(route, handlerClass));
3708
+ for (const route of meta) {
3709
+ const existing = map.get(route);
3710
+ if (existing && existing !== handlerClass) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.InteractionDuplicateRoute, [
3711
+ route,
3712
+ existing.name,
3713
+ handlerClass.name
3714
+ ]);
3715
+ writes.push([map, route]);
3716
+ }
3683
3717
  this.logger.utils.registration(handlerClass.name, (0, _seedcord_utils.formatFilePath)(relativePath));
3684
3718
  }
3719
+ for (const [map, route] of writes) map.set(route, handlerClass);
3685
3720
  }
3686
3721
  /** @internal For use in dev mode */
3687
3722
  async onHmr(event) {
@@ -3932,9 +3967,11 @@ var HmrManager = class {
3932
3967
  constructor() {}
3933
3968
  /** @internal */
3934
3969
  init() {
3935
- if ({}.hot && (envapt.Envapter.isDevelopment || envapt.Envapter.isTest)) {
3970
+ if (!{}.hot) return;
3971
+ setDevChannel((0, _seedcord_types_internal.wrapHot)({}.hot));
3972
+ if (envapt.Envapter.isDevelopment || envapt.Envapter.isTest) {
3936
3973
  this.logger.info("Enabled");
3937
- ({}).hot.on("seedcord:hmr", (payload) => {
3974
+ getDevChannel()?.on("seedcord:hmr", (payload) => {
3938
3975
  const affected = payload.affectedModules?.length ?? 0;
3939
3976
  this.logger.info(`${chalk.default.bold("1")} module changed, ${chalk.default.bold(affected)} affected modules`);
3940
3977
  if (payload.affectedModules) this.logger.utils.list(payload.affectedModules.map((mod) => (0, _seedcord_utils.formatFilePath)(mod)), "Affected modules: ");
@@ -3951,7 +3988,7 @@ var HmrManager = class {
3951
3988
  this.listeners.delete(listener);
3952
3989
  }
3953
3990
  async handleUpdate(event) {
3954
- const promises = Array.from(this.listeners).map(async (listener) => {
3991
+ const promises = [...this.listeners].map(async (listener) => {
3955
3992
  try {
3956
3993
  await listener.onHmr(event);
3957
3994
  } catch (error) {
@@ -3980,7 +4017,7 @@ function isDiscordWebhookUrl(value) {
3980
4017
  */
3981
4018
  function jsonAttachment(name, description, data) {
3982
4019
  const content = (0, _seedcord_utils.filterCirculars)(data);
3983
- return new discord_js.AttachmentBuilder(Buffer.from(JSON.stringify(content, void 0, 2), "utf-8"), {
4020
+ return new discord_js.AttachmentBuilder(Buffer.from(JSON.stringify(content, void 0, 2), "utf8"), {
3984
4021
  name,
3985
4022
  description
3986
4023
  });
@@ -4055,7 +4092,7 @@ function faultSummary(denial, source) {
4055
4092
  */
4056
4093
  function causeStack(denial) {
4057
4094
  const { cause } = denial;
4058
- if (cause instanceof Error) return cause.stack ?? cause.message;
4095
+ if (Error.isError(cause)) return cause.stack ?? cause.message;
4059
4096
  if (typeof cause === "string") return cause;
4060
4097
  if (typeof cause === "bigint" || typeof cause === "symbol" || typeof cause === "function") return cause.toString();
4061
4098
  if (cause !== void 0) try {
@@ -4131,8 +4168,8 @@ var UnhandledErrorContainer = class extends _seedcord_kit.BuilderComponent {
4131
4168
  *
4132
4169
  * Provides a centralized system for registering and executing custom subscribers
4133
4170
  * throughout the application lifecycle. Bus subscribers are loaded from configured directories
4134
- * and can be triggered programmatically or by framework events. Accessed via `core.bus`, not
4135
- * constructed directly.
4171
+ * and can be triggered programmatically or by framework events. Accessed via `core.bus`. Do not
4172
+ * construct it directly.
4136
4173
  */
4137
4174
  var Bus = class extends Plugin {
4138
4175
  core;
@@ -4154,8 +4191,7 @@ var Bus = class extends Plugin {
4154
4191
  registerHandler: this.registerSubscriber.bind(this),
4155
4192
  unregisterHandler: this.unregisterSubscriber.bind(this),
4156
4193
  getArtifacts: this.getArtifacts.bind(this),
4157
- logger: this.logger,
4158
- name: "Subscribers"
4194
+ logger: this.logger
4159
4195
  });
4160
4196
  }
4161
4197
  }
@@ -4173,7 +4209,7 @@ var Bus = class extends Plugin {
4173
4209
  if (subscribersDir) {
4174
4210
  this.logger.info(chalk.default.bold(subscribersDir));
4175
4211
  await this.loadSubscribers(subscribersDir);
4176
- const totalSubscribers = Array.from(this.subscribersMap.values()).reduce((acc, handlers) => acc + handlers.length, 0);
4212
+ const totalSubscribers = [...this.subscribersMap.values()].reduce((acc, handlers) => acc + handlers.length, 0);
4177
4213
  this.logger.utils.list([`${chalk.default.bold.magenta(totalSubscribers)} subscribers`], chalk.default.bold.green("Loaded"));
4178
4214
  }
4179
4215
  }
@@ -4206,14 +4242,13 @@ var Bus = class extends Plugin {
4206
4242
  });
4207
4243
  }
4208
4244
  unregisterSubscriber(handler, artifacts) {
4209
- const subscribers = artifacts ?? Array.from(this.subscribersMap.keys());
4245
+ const subscribers = artifacts ?? [...this.subscribersMap.keys()];
4210
4246
  for (const subscriber of subscribers) {
4211
4247
  const handlers = this.subscribersMap.get(subscriber);
4212
4248
  if (!handlers) continue;
4213
4249
  const index = handlers.findIndex((entry) => entry.ctor === handler);
4214
4250
  if (index !== -1) handlers.splice(index, 1);
4215
4251
  }
4216
- this.executedOnceHandlers.delete(handler);
4217
4252
  }
4218
4253
  isSubscriber(obj) {
4219
4254
  if (typeof obj !== "function") return false;
@@ -4347,6 +4382,7 @@ var Seedcord = class Seedcord extends Pluggable {
4347
4382
  registerHmrAwareModules() {
4348
4383
  this.startup.addTask(_seedcord_services.StartupPhase.Configuration, "HMR Registration", async () => {
4349
4384
  this.hmrManager.register(this.bot);
4385
+ this.hmrManager.register(this.bus);
4350
4386
  for (const plugin of this.plugins) this.hmrManager.register(plugin);
4351
4387
  await Promise.resolve();
4352
4388
  });
@@ -4356,7 +4392,7 @@ var Seedcord = class Seedcord extends Pluggable {
4356
4392
  //#endregion
4357
4393
  //#region src/index.ts
4358
4394
  /** Package version */
4359
- const version = "0.15.0";
4395
+ const version = "0.16.0-next.1";
4360
4396
 
4361
4397
  //#endregion
4362
4398
  exports.ArraySource = ArraySource;