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