@seedcord/gateway 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,17 +1,18 @@
1
1
  import "reflect-metadata";
2
- import { installNodeDefaults } from "@seedcord/logger/node";
3
- import { BaseReplySender, EventMetadataKey, GateNotice, GatedMetadataKey, HmrManager, InteractionMetadataKey, InteractionRouteKeys, InteractionRoutes, MiddlewareMetadataKey, MissingPermissions, NoticeCard, PermissionNames, PublishDefault, accessorStore, areRoutes, asError, busLoggerOf, clearStore, decodeComponentRoute, deferFlags, guardedAccessor, isEmojiTuple, outcomeFor, pickNotice, prefixOf, queuedMsFor, reportDispatch, reportedWrite, routeIdOf, runHandlerGates, sendFlags, setBotColor, slowGateMonitor } from "@seedcord/core/internal";
2
+ import { WinstonConsoleSink, WinstonFileSink, installNodeDefaults } from "@seedcord/logger/node";
3
+ import { EventMetadataKey, GateNotice, GatedMetadataKey, HmrManager, InteractionMetadataKey, InteractionRouteKeys, InteractionRoutes, MiddlewareMetadataKey, MissingPermissions, NoticeCard, PermissionNames, PublishDefault, accessorStore, areRoutes, asError, busLoggerOf, clearStore, decodeComponentRoute, deferFlags, guardedAccessor, isEmojiTuple, outcomeFor, pickNotice, prefixOf, queuedMsFor, reportDispatch, reportedWrite, routeIdOf, runHandlerGates, sendFlags, setBotColor, slowGateMonitor } from "@seedcord/core/internal";
4
4
  import { SeedcordErrorCode, paint } from "@seedcord/errors";
5
5
  import { SeedcordError, SeedcordTypeError, validateDiscordToken } from "@seedcord/errors/internal";
6
- import { ArraySource as ArraySource$1, BaseHandler as BaseHandler$1, BuilderComponent, Bus, CursorSource as CursorSource$1, CustomId, DispatchContext, Fault, Notice, PaginatorBase, RepliableHandler as RepliableHandler$1, ResolvedEmoji, RowComponent, Silence, Subscriber as Subscriber$1, WebhookLog as WebhookLog$1, assertPermissions, defineGate } from "@seedcord/core";
6
+ import { ArraySourceBase, BaseHandler as BaseHandler$1, BaseReplySender, BuilderComponent, Bus, CursorSourceBase, CustomId, DispatchContext, Fault, Notice, PaginatorBase, RepliableHandler as RepliableHandler$1, ResolvedEmoji, RowComponent, Silence, Subscriber as Subscriber$1, WebhookLog as WebhookLog$1, assertPermissions, defineGate } from "@seedcord/core";
7
7
  import { Logger, LoggerChannelRegistry } from "@seedcord/logger";
8
8
  import { ButtonStyle, Client, ComponentType, DiscordAPIError, Events, GatewayIntentBits, Guild, GuildMember, IntentsBitField, InteractionContextType, Message, MessageFlags, PermissionFlagsBits, RESTJSONErrorCodes, Role, User } from "discord.js";
9
9
  import { ButtonBuilder, TextDisplayBuilder } from "@discordjs/builders";
10
10
  import { buildSlashRoute } from "@seedcord/utils/internal";
11
11
  import { Plugin as Plugin$1 } from "@seedcord/core/plugin";
12
- import { CommandRegistry, CoordinatedShutdown, CoordinatedStartup, HealthCheck, Pluggable, ShutdownPhase, StartupPhase, SubscriberLoader, settleWithin } from "@seedcord/core/node/internal";
12
+ import { CoordinatedShutdown, CoordinatedStartup, Pluggable } from "@seedcord/core/node";
13
+ import { CommandRegistry, HealthCheck, ShutdownPhase, StartupPhase, SubscriberLoader, settleWithin, shutdownOf } from "@seedcord/core/node/internal";
13
14
  import { MemoryRateLimiter } from "@seedcord/rate-limiter";
14
- import { SeedcordBrand } from "@seedcord/types/internal";
15
+ import { HostAugmentTarget, HostVersion, SeedcordBrand } from "@seedcord/types/internal";
15
16
  import { Envapter } from "envapt";
16
17
  import { Envapt } from "envapt/legacy";
17
18
  import { HmrModuleHandler } from "@seedcord/core/hmr";
@@ -160,7 +161,7 @@ function Middleware(type, priority = 0, options = {}) {
160
161
  return (ctor) => {
161
162
  const normalizedPriority = Number(priority);
162
163
  if (!Number.isFinite(normalizedPriority)) throw new SeedcordTypeError(SeedcordErrorCode.DecoratorInvalidMiddlewarePriority);
163
- if (type === "interaction" && Array.isArray(options.events) && options.events.length > 0) throw new SeedcordError(SeedcordErrorCode.DecoratorInteractionEventFilter);
164
+ if (type === "interaction" && Array.isArray(options.events) && options.events.length > 0) throw new SeedcordTypeError(SeedcordErrorCode.DecoratorInteractionEventFilter);
164
165
  const metadata = {
165
166
  priority: normalizedPriority,
166
167
  type,
@@ -200,6 +201,7 @@ const IgnoreBots = defineGate("IgnoreBots", (ctx) => {
200
201
  var NotNsfw = class extends GateNotice {
201
202
  constructor(message = "This can only be used in an age-restricted channel.") {
202
203
  super(message);
204
+ this.summary = "be in an age-restricted channel";
203
205
  }
204
206
  };
205
207
  var RoleHigherThanMe = class extends Notice {
@@ -486,7 +488,7 @@ async function getConfirmation(handler, prompt, options) {
486
488
  const { ephemeral = true, timeoutMs = DEFAULT_TIMEOUT_MS } = options ?? {};
487
489
  const response = typeof prompt === "string" ? defaultPrompt(prompt, options) : await prompt(CONFIRM_IDS);
488
490
  const interaction = handler.getEvent();
489
- const sender = handler.getSender();
491
+ const { sender } = handler;
490
492
  const message = await sender.send(response, { ephemeral });
491
493
  const winner = await collectChoice(message, interaction.user.id, timeoutMs);
492
494
  if (!winner) {
@@ -724,9 +726,15 @@ function slashRouteOf(interaction) {
724
726
  */
725
727
  var SlashHandler = class extends InteractionHandler {
726
728
  /**
727
- * The typed options for this command's route. Required options drop the null, choices narrow to their
728
- * literal union, and only the getters for kinds this command actually uses appear. Use `this.event.options`
729
- * directly for anything outside this view, such as narrowing a channel option by type.
729
+ * The typed options for this command's route. Only the getters for kinds this command declares appear.
730
+ * Each getter's name argument is restricted to that command's options of its kind.
731
+ *
732
+ * - A required option drops the null.
733
+ * - Choices narrow to their literal union.
734
+ * - A channel option declared with `addChannelTypes` narrows to those types.
735
+ *
736
+ * Use `this.event.options` directly for anything outside this view, such as `getSubcommand()`.
737
+ * However, for the common case, the methods in this would suffice.
730
738
  */
731
739
  get options() {
732
740
  return this.event.options;
@@ -748,8 +756,8 @@ var SlashHandler = class extends InteractionHandler {
748
756
  * class ModHandler extends SlashHandler<'ban' | 'kick'> {
749
757
  * async execute() {
750
758
  * await this.match({
751
- * ban: (options) => this.event.guild?.members.ban(options.getUser('target', true).id),
752
- * kick: (options) => this.event.guild?.members.kick(options.getUser('member', true).id)
759
+ * ban: (options) => this.event.guild?.members.ban(options.getUser('target').id),
760
+ * kick: (options) => this.event.guild?.members.kick(options.getUser('member').id)
753
761
  * });
754
762
  * }
755
763
  * }
@@ -758,13 +766,18 @@ var SlashHandler = class extends InteractionHandler {
758
766
  async match(arms) {
759
767
  const route = slashRouteOf(this.event);
760
768
  const arm = Object.hasOwn(arms, route) ? arms[route] : void 0;
761
- if (!arm) throw new SeedcordError(SeedcordErrorCode.SlashMatchArmMissing, [route]);
769
+ if (!arm) throw new SeedcordTypeError(SeedcordErrorCode.SlashMatchArmMissing, [route]);
762
770
  return await arm(this.options);
763
771
  }
764
772
  };
765
773
 
766
774
  //#endregion
767
775
  //#region src/handlers/interaction/ContextMenuHandler.ts
776
+ function armFor(arms, name) {
777
+ const arm = Object.hasOwn(arms, name) ? arms[name] : void 0;
778
+ if (typeof arm !== "function") throw new SeedcordTypeError(SeedcordErrorCode.ContextMenuMatchArmMissing, [name]);
779
+ return arm;
780
+ }
768
781
  /**
769
782
  * Base class for a user context-menu command handler (right-click a user).
770
783
  *
@@ -792,6 +805,38 @@ var UserContextMenuHandler = class extends InteractionHandler {
792
805
  get targetMember() {
793
806
  return this.event.targetMember;
794
807
  }
808
+ /** Which of the registered commands fired. */
809
+ get commandName() {
810
+ return this.event.commandName;
811
+ }
812
+ /**
813
+ * Run the arm for whichever command fired. Use this only when the handler is registered for several
814
+ * names.
815
+ *
816
+ * Provide one arm per registered name, keyed by the name Discord shows in the right-click menu. Each
817
+ * arm receives the clicked user and that user's guild member, both narrowed to the cache state of that
818
+ * one command. The arms must cover every name in the generic. A missing name or an unknown key is a
819
+ * compile error.
820
+ *
821
+ * @param arms - One handler per registered command name.
822
+ * @returns The result of the arm that ran.
823
+ *
824
+ * @example
825
+ * ```ts
826
+ * \@UserContextMenuRoute('View Profile', 'Warn')
827
+ * class Moderation extends UserContextMenuHandler<'View Profile' | 'Warn'> {
828
+ * async execute() {
829
+ * await this.match({
830
+ * 'View Profile': (user) => this.reply(`Profile for ${user.tag}.`),
831
+ * Warn: (user, member) => this.reply(`Warned ${member?.displayName ?? user.tag}.`)
832
+ * });
833
+ * }
834
+ * }
835
+ * ```
836
+ */
837
+ async match(arms) {
838
+ return await armFor(arms, this.event.commandName)(this.target, this.targetMember);
839
+ }
795
840
  };
796
841
  /**
797
842
  * Base class for a message context-menu command handler (right-click a message).
@@ -816,6 +861,37 @@ var MessageContextMenuHandler = class extends InteractionHandler {
816
861
  get target() {
817
862
  return this.event.targetMessage;
818
863
  }
864
+ /** Which of the registered commands fired. */
865
+ get commandName() {
866
+ return this.event.commandName;
867
+ }
868
+ /**
869
+ * Run the arm for whichever command fired. Use this only when the handler is registered for several
870
+ * names.
871
+ *
872
+ * Provide one arm per registered name, keyed by the name Discord shows in the right-click menu. Each
873
+ * arm receives the clicked message, narrowed to the cache state of that one command. The arms must
874
+ * cover every name in the generic. A missing name or an unknown key is a compile error.
875
+ *
876
+ * @param arms - One handler per registered command name.
877
+ * @returns The result of the arm that ran.
878
+ *
879
+ * @example
880
+ * ```ts
881
+ * \@MessageContextMenuRoute('Report Message', 'Pin Message')
882
+ * class MessageTools extends MessageContextMenuHandler<'Report Message' | 'Pin Message'> {
883
+ * async execute() {
884
+ * await this.match({
885
+ * 'Report Message': (message) => this.report(message),
886
+ * 'Pin Message': (message) => message.pin()
887
+ * });
888
+ * }
889
+ * }
890
+ * ```
891
+ */
892
+ async match(arms) {
893
+ return await armFor(arms, this.event.commandName)(this.target);
894
+ }
819
895
  };
820
896
 
821
897
  //#endregion
@@ -890,7 +966,7 @@ var AutocompleteHandler = class extends BaseHandler {
890
966
  const { name, value } = this.focused;
891
967
  const respond = (choices) => this.respond(choices);
892
968
  const arm = Object.hasOwn(arms, name) ? arms[name] : void 0;
893
- if (!arm) throw new SeedcordError(SeedcordErrorCode.AutocompleteMatchArmMissing, [name]);
969
+ if (!arm) throw new SeedcordTypeError(SeedcordErrorCode.AutocompleteMatchArmMissing, [name]);
894
970
  return await arm(value, respond);
895
971
  }
896
972
  /** Send autocomplete suggestions, callback type 8. Prefer {@link match}, which restricts each field's choices to its declared type. */
@@ -980,7 +1056,7 @@ var ComponentHandler = class extends InteractionHandler {
980
1056
  async match(arms) {
981
1057
  const { prefix, params } = this.route;
982
1058
  const arm = Object.hasOwn(arms, prefix) ? arms[prefix] : void 0;
983
- if (!arm) throw new SeedcordError(SeedcordErrorCode.CustomIdMatchArmMissing, [prefix]);
1059
+ if (!arm) throw new SeedcordTypeError(SeedcordErrorCode.CustomIdMatchArmMissing, [prefix]);
984
1060
  return await arm(params);
985
1061
  }
986
1062
  };
@@ -1018,8 +1094,8 @@ var ButtonHandler = class extends ComponentHandler {};
1018
1094
  *
1019
1095
  * Register the customId definitions this handler decodes with `@ModalRoute`, list the same ones in the
1020
1096
  * generic, then read `this.params` for a single route or `this.match` for several. Read the submitted
1021
- * inputs from `this.event.fields`, and reply through the handler members. Discord forbids opening a modal in
1022
- * response to a modal, so `showModal` fails compilation on this kind.
1097
+ * inputs from `this.fields`. Reply through the handler members. `showModal` fails compilation on this kind,
1098
+ * because Discord forbids opening a modal in response to a modal.
1023
1099
  *
1024
1100
  * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof ConfigId]`.
1025
1101
  * @typeParam Cache - The interaction cache state, `'cached'` by default.
@@ -1030,13 +1106,17 @@ var ButtonHandler = class extends ComponentHandler {};
1030
1106
  * class ConfigModal extends ModalHandler<[typeof ConfigId]> {
1031
1107
  * async execute() {
1032
1108
  * const { guildId } = this.params;
1033
- * const name = this.event.fields.getTextInputValue('name');
1109
+ * const name = this.fields.getTextInputValue('name');
1034
1110
  * await this.reply(`saved ${name} for ${guildId}`);
1035
1111
  * }
1036
1112
  * }
1037
1113
  * ```
1038
1114
  */
1039
1115
  var ModalHandler = class extends ComponentHandler {
1116
+ /** The fields this modal submitted, read by custom id. */
1117
+ get fields() {
1118
+ return this.event.fields;
1119
+ }
1040
1120
  /** Rewrite the message this modal was opened from. Throws when a command opened the modal. */
1041
1121
  update(response) {
1042
1122
  this.ensureSourceMessage("update");
@@ -1058,10 +1138,12 @@ var ModalHandler = class extends ComponentHandler {
1058
1138
  /**
1059
1139
  * Base class for a select menu handler.
1060
1140
  *
1061
- * Pass the select kind first and the customId definitions second, the same order as `@SelectMenuRoute`,
1062
- * so `this.event` and `this.event.values` are narrowed to that kind. Read `this.params` for a single
1063
- * route or `this.match` for several, and reply through the handler members or rewrite the source message
1064
- * with `this.update`.
1141
+ * Pass the select kind first and the customId definitions second, the same order as `@SelectMenuRoute`.
1142
+ * Read the picked ids from `this.values`. The kind decides which of `this.users`, `this.members`,
1143
+ * `this.roles`, and `this.channels` this handler carries. The rest are `never`.
1144
+ *
1145
+ * Read `this.params` for a single route or `this.match` for several. Reply through the handler members, or
1146
+ * rewrite the source message with `this.update`.
1065
1147
  *
1066
1148
  * @typeParam Kind - The select kind from {@link SelectMenuKind}, e.g. `SelectMenuKind.User`.
1067
1149
  * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof AssignId]`.
@@ -1073,12 +1155,36 @@ var ModalHandler = class extends ComponentHandler {
1073
1155
  * class AssignSelect extends SelectMenuHandler<SelectMenuKind.User, [typeof AssignId]> {
1074
1156
  * async execute() {
1075
1157
  * const { roleId } = this.params;
1076
- * await this.reply(`assigning ${this.event.values.length} member(s) to <@&${roleId}>`);
1158
+ * await this.reply(`assigning ${this.users.size} member(s) to <@&${roleId}>`);
1077
1159
  * }
1078
1160
  * }
1079
1161
  * ```
1080
1162
  */
1081
- var SelectMenuHandler = class extends ComponentHandler {};
1163
+ var SelectMenuHandler = class extends ComponentHandler {
1164
+ eventMember(key) {
1165
+ return this.event[key];
1166
+ }
1167
+ /** The ids this select picked. */
1168
+ get values() {
1169
+ return this.eventMember("values");
1170
+ }
1171
+ /** The users this select picked. */
1172
+ get users() {
1173
+ return this.eventMember("users");
1174
+ }
1175
+ /** The guild members behind the picked users. Discord resolves these only inside a guild. */
1176
+ get members() {
1177
+ return this.eventMember("members");
1178
+ }
1179
+ /** The roles this select picked. */
1180
+ get roles() {
1181
+ return this.eventMember("roles");
1182
+ }
1183
+ /** The channels this select picked. */
1184
+ get channels() {
1185
+ return this.eventMember("channels");
1186
+ }
1187
+ };
1082
1188
 
1083
1189
  //#endregion
1084
1190
  //#region src/handlers/event/EventHandler.ts
@@ -1137,9 +1243,9 @@ var EventHandler = class extends BaseHandler {
1137
1243
  */
1138
1244
  async match(arms) {
1139
1245
  const name = this.firedEvent;
1140
- if (name === void 0) throw new SeedcordError(SeedcordErrorCode.EventMatchArmMissing, ["<no event>"]);
1141
- const arm = arms[name];
1142
- if (typeof arm !== "function") throw new SeedcordError(SeedcordErrorCode.EventMatchArmMissing, [name]);
1246
+ if (name === void 0) throw new SeedcordTypeError(SeedcordErrorCode.EventMatchArmMissing, ["<no event>"]);
1247
+ const arm = Object.hasOwn(arms, name) ? arms[name] : void 0;
1248
+ if (typeof arm !== "function") throw new SeedcordTypeError(SeedcordErrorCode.EventMatchArmMissing, [name]);
1143
1249
  return await arm(...this.getEvent());
1144
1250
  }
1145
1251
  };
@@ -1208,25 +1314,33 @@ var Paginator = class extends PaginatorBase {
1208
1314
  Handler;
1209
1315
  constructor(config) {
1210
1316
  super(config);
1211
- const loadPage = (ctx, n) => this.page(ctx, n);
1317
+ const pageFor = (handler, n) => this.page(handler, n);
1212
1318
  this.Handler = class Nav extends ButtonHandler {
1213
1319
  async execute() {
1214
1320
  await this.deferUpdate();
1215
- const response = await loadPage(contextOf(this.event, this.core), this.params.page);
1321
+ const response = await pageFor(this, this.params.page);
1216
1322
  await this.update(response);
1217
1323
  }
1218
1324
  };
1219
1325
  }
1220
1326
  /**
1221
- * Render page 0 and send it through the handler's sender, which picks reply, edit, or followUp from its
1222
- * ack state.
1327
+ * Render a page as a {@link ReplyResponse} without sending anything.
1328
+ *
1329
+ * @param handler - The handler rendering the page, normally `this`.
1330
+ * @param n - The zero-based page.
1331
+ */
1332
+ async page(handler, n) {
1333
+ return this.buildPage(contextOf(handler.getEvent(), handler.core), n);
1334
+ }
1335
+ /**
1336
+ * Send a page through the handler's sender. The sender picks reply, edit, or followUp from its ack state.
1223
1337
  *
1224
1338
  * @param handler - The handler starting the paginator, normally `this`.
1339
+ * @param n - The zero-based page to open on.
1225
1340
  */
1226
- async start(handler) {
1227
- const interaction = handler.getEvent();
1228
- const response = await this.page(contextOf(interaction, handler.core), 0);
1229
- return handler.getSender().send(response, { ephemeral: this.config.ephemeral ?? false });
1341
+ async start(handler, n = 0) {
1342
+ const response = await this.page(handler, n);
1343
+ return handler.sender.send(response, { ephemeral: this.config.ephemeral ?? false });
1230
1344
  }
1231
1345
  };
1232
1346
 
@@ -1239,7 +1353,7 @@ var Paginator = class extends PaginatorBase {
1239
1353
  *
1240
1354
  * @typeParam Item - The item type, inferred from the loader's return.
1241
1355
  */
1242
- var ArraySource = class extends ArraySource$1 {};
1356
+ var ArraySource = class extends ArraySourceBase {};
1243
1357
  /**
1244
1358
  * A source for a large or unknown-length set you fetch one page at a time (SQL LIMIT/OFFSET, a paged API).
1245
1359
  * The fetcher receives the page index and the page size and reports whether a next page exists. A cursor
@@ -1248,7 +1362,7 @@ var ArraySource = class extends ArraySource$1 {};
1248
1362
  *
1249
1363
  * @typeParam Item - The item type, inferred from the fetcher's slice.
1250
1364
  */
1251
- var CursorSource = class extends CursorSource$1 {};
1365
+ var CursorSource = class extends CursorSourceBase {};
1252
1366
 
1253
1367
  //#endregion
1254
1368
  //#region src/subscribers/index.ts
@@ -1682,7 +1796,7 @@ var EventDispatcher = class {
1682
1796
  this.logger.debug(`Attaching ${paint.sky.bold(eventName)} to the client with ${paint.mute(handlerEntries?.length ?? 0)} handler(s)`);
1683
1797
  this.core.bot.client.on(eventName, (...args) => {
1684
1798
  if (this.draining) return;
1685
- this.core.bus[PublishDefault]("anyEvent", {
1799
+ this.core.bus[PublishDefault]("eventDispatching", {
1686
1800
  name: eventName,
1687
1801
  args
1688
1802
  });
@@ -2070,7 +2184,7 @@ var InteractionDispatcher = class {
2070
2184
  const dispatch = new DispatchContext(routeIdOf(HandlerCtor) ?? routeId);
2071
2185
  routeId = dispatch.routeId ?? routeId;
2072
2186
  const handler = this.buildHandler(HandlerCtor, interaction, dispatch, key, !matched);
2073
- if (handler instanceof RepliableHandler) sender = handler.getSender();
2187
+ if (handler instanceof RepliableHandler) sender = handler.sender;
2074
2188
  const refusal = interaction.isAutocomplete() ? null : await this.gateRefusal(HandlerCtor, interaction, dispatch.routeId);
2075
2189
  if (refusal) {
2076
2190
  await this.answer(refusal.caught, interaction, routeId, sender, report);
@@ -2213,105 +2327,116 @@ const DRAIN_TASK_TIMEOUT_MS = 6e3;
2213
2327
  const UNBIND_TIMEOUT_MS = 2e3;
2214
2328
  const LOGOUT_TIMEOUT_MS = 2e3;
2215
2329
  const loggerSlot = Symbol("seedcord:bot:logger");
2330
+ const initSlot = Symbol("seedcord:bot:init");
2331
+ const controllersSlot = Symbol("seedcord:bot:controllers");
2216
2332
  /**
2217
2333
  * The Discord client and its controllers. Access it through `core.bot`.
2218
2334
  */
2219
2335
  var Bot = class {
2220
- logger = new Logger("Bot", { channel: "bot" });
2336
+ #logger = new Logger("Bot", { channel: "bot" });
2221
2337
  /** @internal */
2222
- [loggerSlot] = this.logger;
2223
- isInitialized = false;
2224
- intents;
2225
- _client;
2226
- interactions;
2227
- events;
2228
- emojiInjector;
2229
- commandRegistry;
2338
+ [loggerSlot] = this.#logger;
2339
+ #isInitialized = false;
2340
+ #intents;
2341
+ #client;
2342
+ #emojiInjector;
2343
+ #interactions;
2344
+ #events;
2345
+ #commandRegistry;
2230
2346
  /** @internal */
2231
2347
  async onHmr(event) {
2232
- if (this.interactions) await this.interactions.onHmr(event);
2233
- if (this.events) await this.events.onHmr(event);
2234
- if (this.commandRegistry) await this.commandRegistry.onHmr(event);
2348
+ if (this.#interactions) await this.#interactions.onHmr(event);
2349
+ if (this.#events) await this.#events.onHmr(event);
2350
+ if (this.#commandRegistry) await this.#commandRegistry.onHmr(event);
2235
2351
  }
2236
2352
  /** @internal */
2237
2353
  constructor(core) {
2238
- this.intents = core.config.bot.clientOptions.intents;
2239
- this._client = new Client(core.config.bot.clientOptions);
2240
- if (core.config.bot.interactions.path) this.interactions = new InteractionDispatcher(core);
2241
- if (core.config.bot.events.path) this.events = new EventDispatcher(core);
2242
- if (core.config.bot.commands.path) this.commandRegistry = new CommandRegistry(core);
2243
- this.emojiInjector = new EmojiInjector(core);
2244
- this.registerShutdownTasks(core);
2354
+ this.#intents = core.config.bot.clientOptions.intents;
2355
+ this.#client = new Client(core.config.bot.clientOptions);
2356
+ if (core.config.bot.interactions.path) this.#interactions = new InteractionDispatcher(core);
2357
+ if (core.config.bot.events.path) this.#events = new EventDispatcher(core);
2358
+ if (core.config.bot.commands.path) this.#commandRegistry = new CommandRegistry(core);
2359
+ this.#emojiInjector = new EmojiInjector(core);
2360
+ this.#registerShutdownTasks(core);
2361
+ }
2362
+ /** @internal */
2363
+ get [controllersSlot]() {
2364
+ return {
2365
+ interactions: this.#interactions,
2366
+ events: this.#events,
2367
+ commandRegistry: this.#commandRegistry
2368
+ };
2245
2369
  }
2246
2370
  /** @internal */
2247
2371
  get applicationId() {
2248
- const { application } = this._client;
2372
+ const { application } = this.#client;
2249
2373
  if (!application) throw new SeedcordError(SeedcordErrorCode.CoreApplicationUnavailable);
2250
2374
  return application.id;
2251
2375
  }
2252
- registerShutdownTasks(core) {
2376
+ #registerShutdownTasks(core) {
2253
2377
  core.shutdown.addTask(ShutdownPhase.Unbind, "stop-dispatch", () => {
2254
- this.stopAccepting();
2378
+ this.#stopAccepting();
2255
2379
  return Promise.resolve();
2256
2380
  }, UNBIND_TIMEOUT_MS);
2257
- core.shutdown.addTask(ShutdownPhase.Drain, "drain-dispatch", () => this.drain(), DRAIN_TASK_TIMEOUT_MS);
2258
- core.shutdown.addTask(ShutdownPhase.Logout, "stop-bot", async () => await this.stop(), LOGOUT_TIMEOUT_MS);
2381
+ core.shutdown.addTask(ShutdownPhase.Drain, "drain-dispatch", () => this.#drain(), DRAIN_TASK_TIMEOUT_MS);
2382
+ core.shutdown.addTask(ShutdownPhase.Logout, "stop-bot", async () => await this.#stop(), LOGOUT_TIMEOUT_MS);
2259
2383
  }
2260
- stopAccepting() {
2261
- this.interactions?.stopAccepting();
2262
- this.events?.stopAccepting();
2384
+ #stopAccepting() {
2385
+ this.#interactions?.stopAccepting();
2386
+ this.#events?.stopAccepting();
2263
2387
  }
2264
- async drain() {
2265
- await Promise.allSettled([this.interactions?.drain(DISPATCH_DRAIN_TIMEOUT_MS), this.events?.drain(DISPATCH_DRAIN_TIMEOUT_MS)]);
2388
+ async #drain() {
2389
+ await Promise.allSettled([this.#interactions?.drain(DISPATCH_DRAIN_TIMEOUT_MS), this.#events?.drain(DISPATCH_DRAIN_TIMEOUT_MS)]);
2266
2390
  }
2267
2391
  /** @internal */
2268
- async init() {
2269
- if (this.isInitialized) return;
2270
- this.isInitialized = true;
2392
+ async [initSlot]() {
2393
+ if (this.#isInitialized) return;
2394
+ this.#isInitialized = true;
2271
2395
  const token = this.botToken;
2272
- if (this.interactions) await this.interactions.init();
2273
- if (this.events) await this.events.init();
2274
- await this.login(token);
2275
- await this.emojiInjector.init();
2276
- if (this.commandRegistry) {
2277
- await this.commandRegistry.init();
2278
- assertGuildsIntent(this.intents, this.commandRegistry.allCommands());
2279
- await this.commandRegistry.setCommands();
2280
- this.interactions?.warnUnhandledRoutes(this.commandRegistry.routeLeaves());
2281
- this.interactions?.warnUnhandledContextMenuRoutes(this.commandRegistry.contextMenuLeaves());
2396
+ if (this.#interactions) await this.#interactions.init();
2397
+ if (this.#events) await this.#events.init();
2398
+ await this.#login(token);
2399
+ await this.#emojiInjector.init();
2400
+ if (this.#commandRegistry) {
2401
+ await this.#commandRegistry.init();
2402
+ assertGuildsIntent(this.#intents, this.#commandRegistry.allCommands());
2403
+ await this.#commandRegistry.setCommands();
2404
+ this.#interactions?.warnUnhandledRoutes(this.#commandRegistry.routeLeaves());
2405
+ this.#interactions?.warnUnhandledContextMenuRoutes(this.#commandRegistry.contextMenuLeaves());
2282
2406
  }
2283
2407
  }
2284
- /** @internal */
2285
- async stop() {
2286
- this._client.removeAllListeners();
2287
- await this.logout();
2408
+ async #stop() {
2409
+ this.#client.removeAllListeners();
2410
+ await this.#logout();
2288
2411
  }
2289
- async login(token) {
2412
+ async #login(token) {
2290
2413
  const ready = Promise.withResolvers();
2291
- this._client.once(Events.ClientReady, () => ready.resolve());
2292
- this._client.login(token);
2414
+ this.#client.once(Events.ClientReady, () => ready.resolve());
2415
+ this.#client.login(token);
2293
2416
  await ready.promise;
2294
- this.logger.info(`Logged in as ${paint.sky.bold(this._client.user?.username)}!`);
2417
+ this.#logger.info(`Logged in as ${paint.sky.bold(this.#client.user?.username)}!`);
2295
2418
  return this;
2296
2419
  }
2297
- async logout() {
2298
- await this._client.destroy();
2299
- this.logger.info(paint.coral.bold("Logged out of Discord!"));
2420
+ async #logout() {
2421
+ await this.#client.destroy();
2422
+ this.#logger.info(paint.coral.bold("Logged out of Discord!"));
2300
2423
  }
2301
2424
  get client() {
2302
- return this._client;
2425
+ return this.#client;
2303
2426
  }
2304
2427
  };
2305
2428
  __decorate([Envapt("DISCORD_BOT_TOKEN", { converter: (raw) => validateDiscordToken(raw) })], Bot.prototype, "botToken", void 0);
2306
- /** @internal */
2307
2429
  function botLoggerOf(bot) {
2308
2430
  return bot[loggerSlot];
2309
2431
  }
2432
+ function initBot(bot) {
2433
+ return bot[initSlot]();
2434
+ }
2310
2435
 
2311
2436
  //#endregion
2312
2437
  //#region src/version.ts
2313
2438
  /** Package version */
2314
- const version = "0.3.1";
2439
+ const version = "0.4.0";
2315
2440
 
2316
2441
  //#endregion
2317
2442
  //#region src/Seedcord.ts
@@ -2324,9 +2449,9 @@ var Seedcord = class Seedcord extends Pluggable {
2324
2449
  /** @internal */
2325
2450
  [SeedcordBrand] = true;
2326
2451
  /** @internal */
2327
- augmentTarget = "@seedcord/gateway";
2452
+ [HostAugmentTarget] = "@seedcord/gateway";
2328
2453
  /** @internal */
2329
- version = version;
2454
+ [HostVersion] = version;
2330
2455
  healthCheck;
2331
2456
  hmrManager;
2332
2457
  /** @see {@link Bus} */
@@ -2358,16 +2483,17 @@ var Seedcord = class Seedcord extends Pluggable {
2358
2483
  this.bot = new Bot(this);
2359
2484
  this.rest = this.bot.client.rest;
2360
2485
  this.rateLimiter = config.store ?? new MemoryRateLimiter();
2361
- this.healthCheck = HealthCheck.fromOption(this.shutdown, config.healthCheck);
2486
+ this.healthCheck = HealthCheck.fromOption(shutdownOf(this), config.healthCheck);
2362
2487
  this.registerStartupTasks();
2363
2488
  }
2364
2489
  /** The bot's discord username, populated after login. */
2365
2490
  get username() {
2366
2491
  return this.bot.client.user?.username;
2367
2492
  }
2368
- static reset() {
2369
- super.reset();
2493
+ static reset(host) {
2494
+ if (!super.reset(host)) return false;
2370
2495
  LoggerChannelRegistry.instance.configure({});
2496
+ return true;
2371
2497
  }
2372
2498
  registerStartupTasks() {
2373
2499
  if (Envapter.isDevelopment || Envapter.isTest) this.registerHmrAwareModules();
@@ -2378,7 +2504,7 @@ var Seedcord = class Seedcord extends Pluggable {
2378
2504
  });
2379
2505
  this.startup.addTask(StartupPhase.Login, "bot-initialization", async () => {
2380
2506
  botLoggerOf(this.bot).utils.initialization("Bot", "start");
2381
- await this.bot.init();
2507
+ await initBot(this.bot);
2382
2508
  botLoggerOf(this.bot).utils.initialization("Bot", "end");
2383
2509
  });
2384
2510
  const { healthCheck } = this;
@@ -2397,8 +2523,8 @@ var Seedcord = class Seedcord extends Pluggable {
2397
2523
  try {
2398
2524
  await super.init();
2399
2525
  } catch (caught) {
2400
- await this.shutdown.run(1, false);
2401
- Seedcord.reset();
2526
+ await shutdownOf(this).run(1, false);
2527
+ Seedcord.reset(this);
2402
2528
  throw caught;
2403
2529
  }
2404
2530
  return this;
@@ -2414,5 +2540,5 @@ var Seedcord = class Seedcord extends Pluggable {
2414
2540
  };
2415
2541
 
2416
2542
  //#endregion
2417
- export { ArraySource, AutocompleteHandler, BaseHandler, ButtonHandler, ComponentHandler, CursorSource, Emojis, EventHandler, EventMiddleware, Gated, GatewayEmoji, IgnoreBots, InteractionHandler, InteractionMiddleware, MessageContextMenuHandler, Middleware, MiddlewareType, ModalHandler, Nsfw, Paginator, Plugin, RegisterEvent, RepliableHandler, Seedcord, SelectMenuHandler, SlashHandler, Subscriber, UserContextMenuHandler, WebhookLog, checkBotPermissions, checkPermissions, getConfirmation, hasPermsToAssign, version };
2543
+ export { ArraySource, AutocompleteHandler, BaseHandler, ButtonHandler, ComponentHandler, CursorSource, Emojis, EventHandler, EventMiddleware, Gated, GatewayEmoji, IgnoreBots, InteractionHandler, InteractionMiddleware, MessageContextMenuHandler, Middleware, MiddlewareType, ModalHandler, Nsfw, Paginator, Plugin, RegisterEvent, RepliableHandler, ReplySender, Seedcord, SelectMenuHandler, SlashHandler, Subscriber, UserContextMenuHandler, WebhookLog, WinstonConsoleSink, WinstonFileSink, checkBotPermissions, checkPermissions, getConfirmation, hasPermsToAssign, version };
2418
2544
  //# sourceMappingURL=index.mjs.map