seedcord 0.13.0-next.0 → 0.13.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.cjs CHANGED
@@ -45,13 +45,36 @@ let envapt = require("envapt");
45
45
  let node_crypto = require("node:crypto");
46
46
  node_crypto = __toESM(node_crypto, 1);
47
47
 
48
+ //#region src/metadataKeys.ts
49
+ /** @internal */
50
+ const InteractionMetadataKey = Symbol("seedcord:interaction:metadata");
51
+ /** @internal */
52
+ const InteractionRouteKeys = {
53
+ ["slash"]: Symbol("seedcord:interaction:slash"),
54
+ ["button"]: Symbol("seedcord:interaction:button"),
55
+ ["modal"]: Symbol("seedcord:interaction:modal"),
56
+ ["stringMenu"]: Symbol("seedcord:interaction:stringMenu"),
57
+ ["userMenu"]: Symbol("seedcord:interaction:userMenu"),
58
+ ["roleMenu"]: Symbol("seedcord:interaction:roleMenu"),
59
+ ["channelMenu"]: Symbol("seedcord:interaction:channelMenu"),
60
+ ["mentionableMenu"]: Symbol("seedcord:interaction:mentionableMenu"),
61
+ ["messageContextMenu"]: Symbol("seedcord:interaction:messageContextMenu"),
62
+ ["userContextMenu"]: Symbol("seedcord:interaction:userContextMenu"),
63
+ ["autocomplete"]: Symbol("seedcord:interaction:autocomplete")
64
+ };
65
+ /** @internal */
66
+ const MiddlewareMetadataKey = Symbol("seedcord:middleware:metadata");
67
+ /** @internal */
68
+ const EventMetadataKey = Symbol("seedcord:event:metadata");
69
+ /** @internal */
70
+ const CommandMetadataKey = Symbol("seedcord:command:metadata");
71
+ /** @internal */
72
+ const GatedMetadataKey = Symbol("seedcord:gated:metadata");
73
+ /** @internal */
74
+ const SubscribeMetadataKey = Symbol("seedcord:subscribe:metadata");
75
+
76
+ //#endregion
48
77
  //#region src/bot/decorators/Command.ts
49
- /**
50
- * Metadata key for command registration information.
51
- *
52
- * @internal
53
- */
54
- const CommandMetadataKey = Symbol("command:metadata");
55
78
  function RegisterCommand(scope, guilds = []) {
56
79
  return (ctor) => {
57
80
  const meta = scope === "global" ? { scope } : {
@@ -73,12 +96,6 @@ function RegisterCommand(scope, guilds = []) {
73
96
  //#endregion
74
97
  //#region src/bot/decorators/Events.ts
75
98
  /**
76
- * Metadata key used to store event handler information
77
- *
78
- * @internal
79
- */
80
- const EventMetadataKey = Symbol("event:metadata");
81
- /**
82
99
  * Registers an event handler class with one or more Discord.js events.
83
100
  *
84
101
  * Associates the decorated class with the provided Discord client events for automatic registration and execution when those events are emitted.
@@ -245,8 +262,6 @@ function eventGateContext(eventName, args, core) {
245
262
 
246
263
  //#endregion
247
264
  //#region src/bot/decorators/Gated.ts
248
- /** @internal */
249
- const GatedMetadataKey = Symbol("gated:metadata");
250
265
  /**
251
266
  * Attaches gates to a handler. The gates run before `execute`, and a gate refusing stops the handler with
252
267
  * the reply or drop the gate threw. Multiple gates are ANDed, so each must pass. A gate that requires a
@@ -275,6 +290,8 @@ const GatedMetadataKey = Symbol("gated:metadata");
275
290
  * @param ctor - The handler class being decorated, rejected at compile time when a gate needs a context it lacks.
276
291
  *
277
292
  * @see {@link defineGate}
293
+ * @see {@link defineEffectGate}
294
+ * @decorator
278
295
  */
279
296
  function Gated(...gates) {
280
297
  return function(ctor) {
@@ -311,16 +328,14 @@ function areRoutes(routes) {
311
328
  /**
312
329
  * Types of select menus supported for routing
313
330
  */
314
- let SelectMenuType = /* @__PURE__ */ function(SelectMenuType) {
315
- SelectMenuType["String"] = "string";
316
- SelectMenuType["User"] = "user";
317
- SelectMenuType["Role"] = "role";
318
- SelectMenuType["Channel"] = "channel";
319
- SelectMenuType["Mentionable"] = "mentionable";
320
- return SelectMenuType;
331
+ let SelectMenuKind = /* @__PURE__ */ function(SelectMenuKind) {
332
+ SelectMenuKind["String"] = "string";
333
+ SelectMenuKind["User"] = "user";
334
+ SelectMenuKind["Role"] = "role";
335
+ SelectMenuKind["Channel"] = "channel";
336
+ SelectMenuKind["Mentionable"] = "mentionable";
337
+ return SelectMenuKind;
321
338
  }({});
322
- /** @internal */
323
- const InteractionMetadataKey = Symbol("interaction:metadata");
324
339
  /**
325
340
  * Routes one or more slash commands to a {@link SlashHandler}.
326
341
  *
@@ -352,7 +367,7 @@ const InteractionMetadataKey = Symbol("interaction:metadata");
352
367
  */
353
368
  function SlashRoute(...routes) {
354
369
  return function(constructor) {
355
- storeMetadata("interaction:slash", routes, constructor);
370
+ storeMetadata("slash", routes, constructor);
356
371
  };
357
372
  }
358
373
  /**
@@ -368,7 +383,7 @@ function SlashRoute(...routes) {
368
383
  */
369
384
  function ButtonRoute(...defs) {
370
385
  return function(constructor) {
371
- storeComponentRoute("interaction:button", defs, constructor);
386
+ storeComponentRoute("button", defs, constructor);
372
387
  };
373
388
  }
374
389
  /**
@@ -384,7 +399,7 @@ function ButtonRoute(...defs) {
384
399
  */
385
400
  function ModalRoute(...defs) {
386
401
  return function(constructor) {
387
- storeComponentRoute("interaction:modal", defs, constructor);
402
+ storeComponentRoute("modal", defs, constructor);
388
403
  };
389
404
  }
390
405
  /**
@@ -410,7 +425,7 @@ function ModalRoute(...defs) {
410
425
  */
411
426
  function ContextMenuRoute(kind, ...names) {
412
427
  return function(constructor) {
413
- storeMetadata(kind === discord_js.ApplicationCommandType.User ? "interaction:userContextMenu" : "interaction:messageContextMenu", names, constructor);
428
+ storeMetadata(kind === discord_js.ApplicationCommandType.User ? "userContextMenu" : "messageContextMenu", names, constructor);
414
429
  };
415
430
  }
416
431
  /**
@@ -435,7 +450,7 @@ function ContextMenuRoute(kind, ...names) {
435
450
  */
436
451
  function AutocompleteRoute(...routes) {
437
452
  return function(constructor) {
438
- storeMetadata("interaction:autocomplete", routes, constructor);
453
+ storeMetadata("autocomplete", routes, constructor);
439
454
  };
440
455
  }
441
456
  /**
@@ -445,14 +460,14 @@ function AutocompleteRoute(...routes) {
445
460
  * generic must list the same definitions, and its second generic argument must be the matching select
446
461
  * interaction type, or it is a compile error.
447
462
  *
448
- * @param type - Select menu kind from {@link SelectMenuType}.
463
+ * @param type - Select menu kind from {@link SelectMenuKind}.
449
464
  * @param defs - The customId definition(s) this handler decodes, one per route.
450
465
  * @decorator
451
466
  *
452
467
  * @example
453
468
  * ```typescript
454
- * \@SelectMenuRoute(SelectMenuType.User, AssignId)
455
- * class AssignSelect extends SelectHandler<SelectMenuType.User, [typeof AssignId]> {
469
+ * \@SelectMenuRoute(SelectMenuKind.User, AssignId)
470
+ * class AssignSelect extends SelectHandler<SelectMenuKind.User, [typeof AssignId]> {
456
471
  * // handles user select menus minted from AssignId
457
472
  * }
458
473
  * ```
@@ -460,23 +475,24 @@ function AutocompleteRoute(...routes) {
460
475
  function SelectMenuRoute(type, ...defs) {
461
476
  return function(constructor) {
462
477
  storeComponentRoute({
463
- ["string"]: "interaction:stringMenu",
464
- ["user"]: "interaction:userMenu",
465
- ["role"]: "interaction:roleMenu",
466
- ["channel"]: "interaction:channelMenu",
467
- ["mentionable"]: "interaction:mentionableMenu"
478
+ ["string"]: "stringMenu",
479
+ ["user"]: "userMenu",
480
+ ["role"]: "roleMenu",
481
+ ["channel"]: "channelMenu",
482
+ ["mentionable"]: "mentionableMenu"
468
483
  }[type], defs, constructor);
469
484
  };
470
485
  }
471
- function storeComponentRoute(symbol, defs, constructor) {
472
- storeMetadata(symbol, defs.map((def) => def.prefix), constructor);
486
+ function storeComponentRoute(route, defs, constructor) {
487
+ storeMetadata(route, defs.map((def) => def.prefix), constructor);
473
488
  Reflect.defineMetadata(_seedcord_kit_internal.ComponentDefsKey, defs, constructor);
474
489
  }
475
- function storeMetadata(symbol, routes, constructor) {
476
- const savedRoutes = Reflect.getMetadata(symbol, constructor);
490
+ function storeMetadata(route, routes, constructor) {
491
+ const key = InteractionRouteKeys[route];
492
+ const savedRoutes = Reflect.getMetadata(key, constructor);
477
493
  const existing = areRoutes(savedRoutes) ? savedRoutes : [];
478
494
  const toStore = Array.isArray(routes) ? routes : [routes];
479
- Reflect.defineMetadata(symbol, [...existing, ...toStore], constructor);
495
+ Reflect.defineMetadata(key, [...existing, ...toStore], constructor);
480
496
  Reflect.defineMetadata(InteractionMetadataKey, true, constructor);
481
497
  }
482
498
 
@@ -486,17 +502,11 @@ function storeMetadata(symbol, routes, constructor) {
486
502
  * Middleware types supported by Seedcord
487
503
  */
488
504
  let MiddlewareType = /* @__PURE__ */ function(MiddlewareType) {
489
- MiddlewareType["Interaction"] = "middleware:interaction";
490
- MiddlewareType["Event"] = "middleware:event";
505
+ MiddlewareType["Interaction"] = "interaction";
506
+ MiddlewareType["Event"] = "event";
491
507
  return MiddlewareType;
492
508
  }({});
493
509
  /**
494
- * Metadata key for middleware configuration
495
- *
496
- * @internal
497
- */
498
- const MiddlewareMetadataKey = Symbol("middleware:metadata");
499
- /**
500
510
  * Decorator used to register middleware with priority ordering. The lower the priority number, the earlier it runs.
501
511
  *
502
512
  * Interaction middleware cannot specify event filters.
@@ -512,14 +522,14 @@ const MiddlewareMetadataKey = Symbol("middleware:metadata");
512
522
  * \@Middleware(MiddlewareType.Event, 10, { events: [Events.MessageCreate, Events.MessageUpdate] })
513
523
  * class MyEventMiddleware extends EventMiddleware {}
514
524
  * ```
515
- * @throws A {@link SeedcordTypeError} If priority is not a finite number
516
- * @throws A {@link SeedcordError} If interaction middleware specifies event filters
525
+ * @throws A **SeedcordTypeError** If priority is not a finite number
526
+ * @throws A **SeedcordError** If interaction middleware specifies event filters
517
527
  */
518
528
  function Middleware(type, priority = 0, options = {}) {
519
529
  return (ctor) => {
520
530
  const normalizedPriority = Number(priority);
521
531
  if (!Number.isFinite(normalizedPriority)) throw new _seedcord_errors_internal.SeedcordTypeError(_seedcord_errors.SeedcordErrorCode.DecoratorInvalidMiddlewarePriority);
522
- if (type === "middleware:interaction" && Array.isArray(options.events) && options.events.length > 0) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.DecoratorInteractionEventFilter);
532
+ if (type === "interaction" && Array.isArray(options.events) && options.events.length > 0) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.DecoratorInteractionEventFilter);
523
533
  const metadata = {
524
534
  priority: normalizedPriority,
525
535
  type,
@@ -632,7 +642,61 @@ function defineEffectGate(name, check, commit) {
632
642
  }
633
643
 
634
644
  //#endregion
635
- //#region src/bot/gates/combinators.ts
645
+ //#region src/bot/notices/utils.ts
646
+ function mentionFor(subject) {
647
+ if (subject instanceof discord_js.Role) return `<@&${subject.id}>`;
648
+ if (subject instanceof discord_js.TextChannel) return `<#${subject.id}>`;
649
+ if (subject instanceof discord_js.GuildMember) return `<@${subject.id}>`;
650
+ return `\`${subject.name}\``;
651
+ }
652
+ function labelFor(subject) {
653
+ if (subject instanceof discord_js.Role) return "role";
654
+ if (subject instanceof discord_js.TextChannel) return "channel";
655
+ if (subject instanceof discord_js.GuildMember) return "member";
656
+ return "guild";
657
+ }
658
+
659
+ //#endregion
660
+ //#region src/bot/notices/index.ts
661
+ var GateNotice = class extends _seedcord_kit.Notice {
662
+ render() {
663
+ return { components: [new _seedcord_kit_internal.NoticeCard(this.message).component] };
664
+ }
665
+ };
666
+ var NotOwner = class extends GateNotice {
667
+ constructor(message = "Only the bot owner can use this.") {
668
+ super(message);
669
+ }
670
+ };
671
+ var NotInGuild = class extends GateNotice {
672
+ constructor(message = "This can only be used in a server.") {
673
+ super(message);
674
+ }
675
+ };
676
+ var NotInDm = class extends GateNotice {
677
+ constructor(message = "This can only be used in a direct message.") {
678
+ super(message);
679
+ }
680
+ };
681
+ var NotNsfw = class extends GateNotice {
682
+ constructor(message = "This can only be used in an age-restricted channel.") {
683
+ super(message);
684
+ }
685
+ };
686
+ var OnCooldown = class extends GateNotice {
687
+ expires;
688
+ constructor(expires, message) {
689
+ super(message ?? `You are doing that too fast. Try again <t:${(0, _seedcord_utils.toEpochSeconds)(expires)}:R>.`);
690
+ this.expires = expires;
691
+ }
692
+ };
693
+ var MissingRole = class extends GateNotice {
694
+ role;
695
+ constructor(message, role) {
696
+ super(message ?? (role ? `You need the ${role.name} role to use this.` : "You do not have the required role."));
697
+ this.role = role;
698
+ }
699
+ };
636
700
  var NotAllowed = class extends _seedcord_kit.Notice {
637
701
  constructor() {
638
702
  super("not allowed");
@@ -651,6 +715,93 @@ var NeedsAny = class extends _seedcord_kit.Notice {
651
715
  return { components: [new _seedcord_kit_internal.NoticeCard(`You need any of:\n${this.summaries.map((summary) => `• ${summary}`).join("\n")}`).component] };
652
716
  }
653
717
  };
718
+ var RoleHigherThanMe = class extends _seedcord_kit.Notice {
719
+ role;
720
+ botRole;
721
+ constructor(message, role, botRole) {
722
+ super(message);
723
+ this.role = role;
724
+ this.botRole = botRole;
725
+ }
726
+ render() {
727
+ return { components: [new _seedcord_kit_internal.NoticeCard(`I cannot assign a role that is higher than me.\n\nThe role <@&${this.role.id}> is higher than my role <@&${this.botRole.id}> in the hierarchy.`).component] };
728
+ }
729
+ };
730
+ var CannotAssignBotRole = class extends _seedcord_kit.Notice {
731
+ constructor(message = "I cannot assign a managed role.") {
732
+ super(message);
733
+ }
734
+ render() {
735
+ return { components: [new _seedcord_kit_internal.NoticeCard("I cannot assign a managed role.").component] };
736
+ }
737
+ };
738
+ var MissingPermissions = class extends _seedcord_kit.Notice {
739
+ where;
740
+ missingPerms;
741
+ constructor(message, where, missingPerms) {
742
+ super(message);
743
+ this.where = where;
744
+ this.missingPerms = missingPerms;
745
+ }
746
+ render() {
747
+ const bullets = this.missingPerms.map((perm) => `• ${perm}`).join("\n");
748
+ return { components: [new _seedcord_kit_internal.NoticeCard(`The ${labelFor(this.where)} ${mentionFor(this.where)} is missing the following permission entries:\n\n${bullets}`).component] };
749
+ }
750
+ };
751
+ var HasDangerousPermissions = class extends _seedcord_kit.Notice {
752
+ target;
753
+ dangerousPerms;
754
+ constructor(message, target, dangerousPerms) {
755
+ super(message);
756
+ this.target = target;
757
+ this.dangerousPerms = dangerousPerms;
758
+ }
759
+ render() {
760
+ const bullets = this.dangerousPerms.map((perm) => `• ${perm}`).join("\n");
761
+ return { components: [new _seedcord_kit_internal.NoticeCard(`The ${labelFor(this.target)} ${mentionFor(this.target)} has the following permission entries that must not be enabled:\n\n${bullets}`).component] };
762
+ }
763
+ };
764
+ var UserNotFound = class extends _seedcord_kit.Notice {
765
+ userArg;
766
+ constructor(userArg) {
767
+ super(`User not found: ${userArg}`);
768
+ this.userArg = userArg;
769
+ }
770
+ render() {
771
+ return { components: [new _seedcord_kit_internal.NoticeCard(`User probably doesn't exist or was deleted.\n**User Argument:** \`${this.userArg}\`\nPlease check the user ID and try again. Only pass valid user IDs as the argument.`, "User Not Found").component] };
772
+ }
773
+ };
774
+ var UserNotInGuild = class extends _seedcord_kit.Notice {
775
+ constructor(message = "User is not in the guild.") {
776
+ super(message);
777
+ }
778
+ render() {
779
+ return { components: [new _seedcord_kit_internal.NoticeCard(this.message).component] };
780
+ }
781
+ };
782
+ var RoleDoesNotExist = class extends _seedcord_kit.Notice {
783
+ roleId;
784
+ constructor(message, roleId) {
785
+ super(message);
786
+ this.roleId = roleId;
787
+ }
788
+ render() {
789
+ return { components: [new _seedcord_kit_internal.NoticeCard(`The role with ID \`${this.roleId}\` does not exist.`).component] };
790
+ }
791
+ };
792
+ var CouldNotFindChannel = class extends _seedcord_kit.Notice {
793
+ channelId;
794
+ constructor(message, channelId) {
795
+ super(message);
796
+ this.channelId = channelId;
797
+ }
798
+ render() {
799
+ return { components: [new _seedcord_kit_internal.NoticeCard(`Could not find channel with ID \`${this.channelId}\`. It could also be that the channel is not a text channel.`).component] };
800
+ }
801
+ };
802
+
803
+ //#endregion
804
+ //#region src/bot/gates/combinators.ts
654
805
  function and(...gates) {
655
806
  return defineGate("and", async (ctx) => {
656
807
  for (const gate of gates) await runCheck(gate, ctx);
@@ -687,43 +838,8 @@ function or(...args) {
687
838
  });
688
839
  }
689
840
 
690
- //#endregion
691
- //#region src/bot/gates/catalog/GateNotice.ts
692
- /**
693
- * Base for the catalog refusals. Renders the message as the bot's standard notice card, so matching this
694
- * one type in the boundary restyles or translates every built-in gate refusal in one place. A refusal reply,
695
- * distinct from a {@link Silence} quiet drop.
696
- *
697
- * @example
698
- * ```ts
699
- * import { GateNotice } from 'seedcord';
700
- *
701
- * class NotPremium extends GateNotice {
702
- * public constructor() {
703
- * super('This command is for premium members only.');
704
- * }
705
- * }
706
- *
707
- * // thrown from a custom gate's check, rendered as the standard notice card
708
- * defineGate('Premium', (ctx) => {
709
- * if (!isPremium(ctx.user)) throw new NotPremium();
710
- * });
711
- * ```
712
- */
713
- var GateNotice = class extends _seedcord_kit.Notice {
714
- render() {
715
- return { components: [new _seedcord_kit_internal.NoticeCard(this.message).component] };
716
- }
717
- };
718
-
719
841
  //#endregion
720
842
  //#region src/bot/gates/catalog/options.ts
721
- /**
722
- * Picks the refusal a catalog gate throws, the author override when given, else the gate's default.
723
- *
724
- * @param options - The override a catalog gate received, or undefined when the author passed none.
725
- * @param makeDefault - Builds the gate's default refusal, given the optional reworded message.
726
- */
727
843
  function pickNotice(options, makeDefault) {
728
844
  return options?.notice ?? makeDefault(options?.message);
729
845
  }
@@ -731,41 +847,11 @@ function pickNotice(options, makeDefault) {
731
847
  //#endregion
732
848
  //#region src/bot/gates/catalog/access.ts
733
849
  /**
734
- * Refusal shown when the caller is not a configured bot owner.
735
- *
736
- * @param message - Text shown in the refusal, defaulting to a bot-owner-only line.
737
- */
738
- var NotOwner = class extends GateNotice {
739
- constructor(message = "Only the bot owner can use this.") {
740
- super(message);
741
- }
742
- };
743
- /**
744
- * Refusal shown when a guild-only command runs in a DM.
745
- *
746
- * @param message - Text shown in the refusal, defaulting to a server-only line.
747
- */
748
- var NotInGuild = class extends GateNotice {
749
- constructor(message = "This can only be used in a server.") {
750
- super(message);
751
- }
752
- };
753
- /**
754
- * Refusal shown when a DM-only command runs in a guild.
755
- *
756
- * @param message - Text shown in the refusal, defaulting to a direct-message-only line.
757
- */
758
- var NotInDm = class extends GateNotice {
759
- constructor(message = "This can only be used in a direct message.") {
760
- super(message);
761
- }
762
- };
763
- /**
764
- * Passes only for a user id listed in `config.ownerIds`, else refuses with an ephemeral {@link NotOwner}.
850
+ * Passes only for a user id listed in `config.ownerIds`, else refuses.
765
851
  *
766
852
  * Agnostic, so it attaches to interaction and event handlers alike. With no `ownerIds` configured it refuses every caller. Pass {@link GateNoticeOptions} to reword or replace the refusal.
767
853
  *
768
- * @param options - Reword or replace the refusal. Omit to throw the default {@link NotOwner}.
854
+ * @param options - Reword the default refusal with `message`, or replace it with `notice`.
769
855
  *
770
856
  * @see {@link Gated}
771
857
  *
@@ -794,11 +880,11 @@ function OwnerOnly(options) {
794
880
  });
795
881
  }
796
882
  /**
797
- * Passes inside a guild, else refuses with {@link NotInGuild}.
883
+ * Passes inside a guild, else refuses.
798
884
  *
799
885
  * Agnostic, so it attaches to any handler kind. Often paired with {@link RequirePermissions} in an {@link and}.
800
886
  *
801
- * @param options - Reword or replace the refusal. Omit to throw the default {@link NotInGuild}.
887
+ * @param options - Reword the default refusal with `message`, or replace it with `notice`.
802
888
  *
803
889
  * @see {@link Gated}
804
890
  *
@@ -822,11 +908,11 @@ function GuildOnly(options) {
822
908
  });
823
909
  }
824
910
  /**
825
- * Passes in a direct message, else refuses with {@link NotInDm}.
911
+ * Passes in a direct message, else refuses.
826
912
  *
827
913
  * Agnostic, so it attaches to any handler kind. The inverse of {@link GuildOnly}, so combining the two in an {@link and} can never pass.
828
914
  *
829
- * @param options - Reword or replace the refusal. Omit to throw the default {@link NotInDm}.
915
+ * @param options - Reword the default refusal with `message`, or replace it with `notice`.
830
916
  *
831
917
  * @see {@link Gated}
832
918
  *
@@ -850,28 +936,6 @@ function DmOnly(options) {
850
936
 
851
937
  //#endregion
852
938
  //#region src/bot/gates/catalog/Cooldown.ts
853
- /**
854
- * Refusal shown while a Cooldown is still cooling down. Carries the epoch ms the key frees up, which the default
855
- * message renders as a relative timestamp.
856
- *
857
- * @param expires - Epoch ms at which the key frees up, rendered as a relative timestamp by the default message.
858
- * @param message - Optional text that replaces the default refusal.
859
- *
860
- * @example
861
- * ```ts
862
- * // refuse from a custom check, freeing up one minute from now
863
- * defineGate('SlowDown', (ctx) => {
864
- * if (tooFast(ctx.user)) throw new OnCooldown((Date.now() + 60_000) as EpochMs);
865
- * });
866
- * ```
867
- */
868
- var OnCooldown = class extends GateNotice {
869
- expires;
870
- constructor(expires, message) {
871
- super(message ?? `You are doing that too fast. Try again <t:${(0, _seedcord_utils.toEpochSeconds)(expires)}:R>.`);
872
- this.expires = expires;
873
- }
874
- };
875
939
  let bucketSeq = 0;
876
940
  function scopeValue(ctx, per) {
877
941
  if (per === "guild") return ctx.guildId ?? "global";
@@ -880,16 +944,16 @@ function scopeValue(ctx, per) {
880
944
  }
881
945
  /**
882
946
  * Allows `limit` uses per window, scoped by `per`. A number `duration` is **seconds**, a string is
883
- * a duration like `30m` or `24h`. An unparseable string throws a {@link SeedcordTypeError} at construction. The
947
+ * a duration like `30m` or `24h`. An unparseable string throws a **SeedcordTypeError** at construction. The
884
948
  * slot is charged in commit, only after the whole gate set passes, so a later refusal never burns the cooldown.
885
- * Each call gets its own bucket, so two handlers never share a window. Refuses with {@link OnCooldown}.
949
+ * Each call gets its own bucket, so two handlers never share a window. Reword the refusal with {@link CooldownOptions.message}
950
+ * or replace it with {@link CooldownOptions.notice}.
886
951
  *
887
- * @param duration - A number is seconds, a string is a duration like `30m` or `24h`. An unparseable string throws a {@link SeedcordTypeError}.
952
+ * @param duration - A number is seconds, a string is a duration like `30m` or `24h`. An unparseable string throws a **SeedcordTypeError**.
888
953
  * @param options - Sets the scope with `per`, the uses per window with `limit`, and the refusal text with `message` or `notice`.
889
954
  *
890
955
  * @see {@link Gated}
891
956
  * @see {@link RateLimiter}
892
- * @see {@link OnCooldown}
893
957
  *
894
958
  * @example
895
959
  * ```ts
@@ -951,7 +1015,7 @@ function Cooldown(duration, options) {
951
1015
  /**
952
1016
  * Drops a client event whose actor is a bot, with a {@link Silence} so nothing is replied. Event-only, because a
953
1017
  * Silence on an interaction would leave Discord's failed-interaction state. It rejects an interaction handler at
954
- * the decorator line.
1018
+ * the decorator line. Takes no options, so attach it directly without calling it.
955
1019
  *
956
1020
  * @see {@link Gated}
957
1021
  *
@@ -959,7 +1023,7 @@ function Cooldown(duration, options) {
959
1023
  * ```ts
960
1024
  * import { Events } from 'discord.js';
961
1025
  *
962
- * \@Gated(IgnoreBots())
1026
+ * \@Gated(IgnoreBots)
963
1027
  * class OnMessage extends EventHandler<Events.MessageCreate> {
964
1028
  * async execute() {
965
1029
  * // ...
@@ -967,41 +1031,25 @@ function Cooldown(duration, options) {
967
1031
  * }
968
1032
  * ```
969
1033
  */
970
- function IgnoreBots() {
971
- return defineGate("IgnoreBots", (ctx) => {
972
- if (ctx.user?.bot) throw new _seedcord_kit.Silence("actor is a bot");
973
- });
974
- }
1034
+ const IgnoreBots = defineGate("IgnoreBots", (ctx) => {
1035
+ if (ctx.user?.bot) throw new _seedcord_kit.Silence("actor is a bot");
1036
+ });
975
1037
 
976
1038
  //#endregion
977
1039
  //#region src/bot/gates/catalog/Nsfw.ts
978
- /**
979
- * Refusal shown when a command marked NSFW runs in a channel that is not age-restricted.
980
- *
981
- * @example
982
- * ```ts
983
- * // refuse from a custom check with a reworded message
984
- * defineGate('AdultOnly', (ctx: InteractionGateContext<NonModalInteraction>) => {
985
- * if (!isAgeRestricted(ctx.interaction.channel)) throw new NotNsfw('Adults only here.');
986
- * });
987
- * ```
988
- *
989
- * @param message - The text rendered in the refusal embed, defaulting to the age-restricted notice.
990
- */
991
- var NotNsfw = class extends GateNotice {
992
- constructor(message = "This can only be used in an age-restricted channel.") {
993
- super(message);
994
- }
995
- };
996
1040
  function channelIsNsfw(channel) {
997
1041
  if (!channel) return false;
998
1042
  if (channel.isThread()) return channel.parent?.nsfw ?? false;
999
1043
  return "nsfw" in channel ? channel.nsfw : false;
1000
1044
  }
1001
1045
  /**
1002
- * Requires an age-restricted channel, else refuses with {@link NotNsfw}. A thread inherits its parent channel's
1003
- * nsfw flag. ModalSubmit has no reliable channel, so it is excluded. Pass {@link GateNoticeOptions} to reword or
1004
- * replace the refusal.
1046
+ * Requires an age-restricted channel, else refuses. Pass {@link GateNoticeOptions} to reword or replace the refusal.
1047
+ *
1048
+ * A thread inherits its parent channel's nsfw flag. ModalSubmit has no reliable channel, so it is excluded.
1049
+ *
1050
+ * @param options - Reword the default refusal with `message`, or replace it with `notice`.
1051
+ *
1052
+ * @see {@link Gated}
1005
1053
  *
1006
1054
  * @example
1007
1055
  * ```ts
@@ -1013,10 +1061,6 @@ function channelIsNsfw(channel) {
1013
1061
  * }
1014
1062
  * }
1015
1063
  * ```
1016
- *
1017
- * @param options - Reword or replace the refusal. Omit to throw the default {@link NotNsfw}.
1018
- *
1019
- * @see {@link Gated}
1020
1064
  */
1021
1065
  function Nsfw(options) {
1022
1066
  return defineGate("Nsfw", (ctx) => {
@@ -1025,79 +1069,6 @@ function Nsfw(options) {
1025
1069
  });
1026
1070
  }
1027
1071
 
1028
- //#endregion
1029
- //#region src/bot/utilities/permissions/notices.ts
1030
- function mentionFor(subject) {
1031
- if (subject instanceof discord_js.Role) return `<@&${subject.id}>`;
1032
- if (subject instanceof discord_js.TextChannel) return `<#${subject.id}>`;
1033
- if (subject instanceof discord_js.GuildMember) return `<@${subject.id}>`;
1034
- return `\`${subject.name}\``;
1035
- }
1036
- function labelFor(subject) {
1037
- if (subject instanceof discord_js.Role) return "role";
1038
- if (subject instanceof discord_js.TextChannel) return "channel";
1039
- if (subject instanceof discord_js.GuildMember) return "member";
1040
- return "guild";
1041
- }
1042
- /**
1043
- * Error thrown when attempting to modify a role higher than the bot's highest role.
1044
- */
1045
- var RoleHigherThanMe = class extends _seedcord_kit.Notice {
1046
- role;
1047
- botRole;
1048
- constructor(message, role, botRole) {
1049
- super(message);
1050
- this.role = role;
1051
- this.botRole = botRole;
1052
- }
1053
- render() {
1054
- return { components: [new _seedcord_kit_internal.NoticeCard(`I cannot assign a role that is higher than me.\n\nThe role <@&${this.role.id}> is higher than my role <@&${this.botRole.id}> in the hierarchy.`).component] };
1055
- }
1056
- };
1057
- /**
1058
- * Error thrown when attempting to assign a managed/bot role.
1059
- */
1060
- var CannotAssignBotRole = class extends _seedcord_kit.Notice {
1061
- constructor(message = "I cannot assign a managed role.") {
1062
- super(message);
1063
- }
1064
- render() {
1065
- return { components: [new _seedcord_kit_internal.NoticeCard("I cannot assign a managed role.").component] };
1066
- }
1067
- };
1068
- /**
1069
- * Error thrown when required permissions are missing.
1070
- */
1071
- var MissingPermissions = class extends _seedcord_kit.Notice {
1072
- where;
1073
- missingPerms;
1074
- constructor(message, where, missingPerms) {
1075
- super(message);
1076
- this.where = where;
1077
- this.missingPerms = missingPerms;
1078
- }
1079
- render() {
1080
- const bullets = this.missingPerms.map((perm) => `• ${perm}`).join("\n");
1081
- return { components: [new _seedcord_kit_internal.NoticeCard(`The ${labelFor(this.where)} ${mentionFor(this.where)} is missing the following permission entries:\n\n${bullets}`).component] };
1082
- }
1083
- };
1084
- /**
1085
- * Error thrown when a target has permissions that must not be present.
1086
- */
1087
- var HasDangerousPermissions = class extends _seedcord_kit.Notice {
1088
- target;
1089
- dangerousPerms;
1090
- constructor(message, target, dangerousPerms) {
1091
- super(message);
1092
- this.target = target;
1093
- this.dangerousPerms = dangerousPerms;
1094
- }
1095
- render() {
1096
- const bullets = this.dangerousPerms.map((perm) => `• ${perm}`).join("\n");
1097
- return { components: [new _seedcord_kit_internal.NoticeCard(`The ${labelFor(this.target)} ${mentionFor(this.target)} has the following permission entries that must not be enabled:\n\n${bullets}`).component] };
1098
- }
1099
- };
1100
-
1101
1072
  //#endregion
1102
1073
  //#region src/bot/utilities/permissions/checkPermissions.ts
1103
1074
  /**
@@ -1129,36 +1100,15 @@ function checkPermissions(a, b, c, d, e) {
1129
1100
  //#endregion
1130
1101
  //#region src/bot/gates/catalog/permissions.ts
1131
1102
  /**
1132
- * Refusal shown when the caller is missing a required role. {@link RequireRole} throws it.
1133
- *
1134
- * The default message reads from `role.name` when the role resolved, otherwise a generic line.
1135
- *
1136
- * @example
1137
- * ```ts
1138
- * import type { Role } from 'discord.js';
1139
- *
1140
- * // throw it from a custom gate when the resolved role is missing
1141
- * defineGate('NeedsStaff', (ctx: InteractionGateContext<NonModalInteraction>) => {
1142
- * const role = ctx.guild?.roles.cache.get(STAFF_ROLE_ID) ?? null;
1143
- * if (!ctx.member?.roles.cache.has(STAFF_ROLE_ID)) throw new MissingRole(undefined, role);
1144
- * });
1145
- * ```
1146
- *
1147
- * @param message - Custom refusal text, or undefined to use the default that reads from `role.name`.
1148
- * @param role - The role the caller is missing, or null when it could not be resolved.
1149
- */
1150
- var MissingRole = class extends GateNotice {
1151
- role;
1152
- constructor(message, role) {
1153
- super(message ?? (role ? `You need the ${role.name} role to use this.` : "You do not have the required role."));
1154
- this.role = role;
1155
- }
1156
- };
1157
- /**
1158
1103
  * Requires the caller to hold every permission in `scope`, via `checkPermissions`. Refuses outside a guild.
1159
1104
  *
1160
1105
  * Interaction-only and excludes ModalSubmit, since a modal lacks a reliable cached caller member.
1161
1106
  *
1107
+ * @param scope - The permission flag bits the caller must all hold.
1108
+ * @param options - Override each refusal, the outside-guild one with `notInGuild` and the missing-permissions one with `missing`.
1109
+ *
1110
+ * @see {@link Gated}
1111
+ *
1162
1112
  * @example
1163
1113
  * ```ts
1164
1114
  * import { PermissionFlagsBits } from 'discord.js';
@@ -1171,16 +1121,11 @@ var MissingRole = class extends GateNotice {
1171
1121
  * }
1172
1122
  * }
1173
1123
  * ```
1174
- *
1175
- * @param scope - The permission flag bits the caller must all hold.
1176
- * @param options - Override the refusal Notice with a ctor that receives the where and the missing permission names.
1177
- *
1178
- * @see {@link Gated}
1179
1124
  */
1180
1125
  function RequirePermissions(scope, options) {
1181
1126
  return defineGate("RequirePermissions", (ctx) => {
1182
- if (!ctx.member || !ctx.guild) throw new NotInGuild();
1183
- checkPermissions(ctx.member, ctx.guild, scope, false, options?.notice ? { missing: options.notice } : void 0);
1127
+ if (!ctx.member || !ctx.guild) throw pickNotice(options?.notInGuild, (message) => new NotInGuild(message));
1128
+ checkPermissions(ctx.member, ctx.guild, scope, false, options?.missing ? { missing: options.missing } : void 0);
1184
1129
  });
1185
1130
  }
1186
1131
  /**
@@ -1188,6 +1133,11 @@ function RequirePermissions(scope, options) {
1188
1133
  *
1189
1134
  * Checks the bot's own member, so unlike {@link RequirePermissions} it attaches to a modal handler too.
1190
1135
  *
1136
+ * @param scope - The permission flag bits the bot must all hold.
1137
+ * @param options - Override each refusal, the outside-guild one with `notInGuild` and the missing-permissions one with `missing`.
1138
+ *
1139
+ * @see {@link Gated}
1140
+ *
1191
1141
  * @example
1192
1142
  * ```ts
1193
1143
  * import { PermissionFlagsBits } from 'discord.js';
@@ -1200,23 +1150,24 @@ function RequirePermissions(scope, options) {
1200
1150
  * }
1201
1151
  * }
1202
1152
  * ```
1203
- *
1204
- * @param scope - The permission flag bits the bot must all hold.
1205
- * @param options - Override the refusal Notice with a ctor that receives the where and the missing permission names.
1206
- *
1207
- * @see {@link Gated}
1208
1153
  */
1209
1154
  function RequireBotPermissions(scope, options) {
1210
1155
  return defineGate("RequireBotPermissions", (ctx) => {
1211
1156
  const botMember = ctx.guild?.members.me;
1212
- if (!ctx.guild || !botMember) throw new NotInGuild();
1213
- checkPermissions(botMember, ctx.guild, scope, false, options?.notice ? { missing: options.notice } : void 0);
1157
+ if (!ctx.guild || !botMember) throw pickNotice(options?.notInGuild, (message) => new NotInGuild(message));
1158
+ checkPermissions(botMember, ctx.guild, scope, false, options?.missing ? { missing: options.missing } : void 0);
1214
1159
  });
1215
1160
  }
1216
1161
  /**
1217
- * Requires the caller to have `roleId`, read from the member's role cache. Refuses with {@link MissingRole}.
1162
+ * Requires the caller to have `roleId`, read from the member's role cache. Refuses outside a guild first, then
1163
+ * when the role is missing.
1164
+ *
1165
+ * Interaction-only and excludes ModalSubmit.
1218
1166
  *
1219
- * Interaction-only and excludes ModalSubmit. Refuses outside a guild with {@link NotInGuild} first.
1167
+ * @param roleId - The role snowflake the caller must hold, read from the member's role cache.
1168
+ * @param options - Override each refusal, the outside-guild one with `notInGuild` and the missing-role one with `missingRole`.
1169
+ *
1170
+ * @see {@link Gated}
1220
1171
  *
1221
1172
  * @example
1222
1173
  * ```ts
@@ -1228,18 +1179,13 @@ function RequireBotPermissions(scope, options) {
1228
1179
  * }
1229
1180
  * }
1230
1181
  * ```
1231
- *
1232
- * @param roleId - The role snowflake the caller must hold, read from the member's role cache.
1233
- * @param options - Reword the refusal message or replace the {@link MissingRole} Notice entirely.
1234
- *
1235
- * @see {@link Gated}
1236
1182
  */
1237
1183
  function RequireRole(roleId, options) {
1238
1184
  return defineGate("RequireRole", (ctx) => {
1239
- if (!ctx.member || !ctx.guild) throw new NotInGuild();
1185
+ if (!ctx.member || !ctx.guild) throw pickNotice(options?.notInGuild, (message) => new NotInGuild(message));
1240
1186
  if (ctx.member.roles.cache.has(roleId)) return;
1241
1187
  const role = ctx.guild.roles.cache.get(roleId) ?? null;
1242
- throw pickNotice(options, (message) => new MissingRole(message, role));
1188
+ throw pickNotice(options?.missingRole, (message) => new MissingRole(message, role));
1243
1189
  });
1244
1190
  }
1245
1191
 
@@ -1335,25 +1281,11 @@ var EmojiInjector = class {
1335
1281
  //#endregion
1336
1282
  //#region src/bot/utilities/channels/fetchText.ts
1337
1283
  /**
1338
- * Error thrown when a channel could not be found or accessed.
1339
- */
1340
- var CouldNotFindChannel = class extends _seedcord_kit.Notice {
1341
- channelId;
1342
- constructor(message, channelId) {
1343
- super(message);
1344
- this.channelId = channelId;
1345
- }
1346
- render() {
1347
- return { components: [new _seedcord_kit_internal.NoticeCard(`Could not find channel with ID \`${this.channelId}\`. It could also be that the channel is not a text channel.`).component] };
1348
- }
1349
- };
1350
- /**
1351
- * Fetches and validates a text channel.
1284
+ * Fetches and validates a text channel. Refuses when the channel doesn't exist or isn't a text channel.
1352
1285
  *
1353
1286
  * @param client - The Discord client instance
1354
1287
  * @param channelId - Channel ID or TextChannel instance
1355
1288
  * @returns Promise resolving to the text channel
1356
- * @throws A {@link CouldNotFindChannel} When the channel doesn't exist or isn't text-based
1357
1289
  */
1358
1290
  async function fetchText(client, channelId) {
1359
1291
  if (channelId instanceof discord_js.TextChannel) return channelId;
@@ -1371,13 +1303,13 @@ async function fetchText(client, channelId) {
1371
1303
  //#endregion
1372
1304
  //#region src/bot/utilities/permissions/checkBotPermissions.ts
1373
1305
  /**
1374
- * Checks if the bot has required permissions in a {@link Guild} or {@link TextChannel}.
1306
+ * Checks if the bot has required permissions in a {@link Guild} or {@link TextChannel}. Refuses when a
1307
+ * required permission is missing.
1375
1308
  *
1376
1309
  * @param target - Guild or text channel to check in
1377
1310
  * @param scope - Permission bits to validate
1378
1311
  * @param inverse - Whether to check for absence of the given permissions
1379
1312
  * @param errors - Optional custom error constructors
1380
- * @throws A {@link MissingPermissions} error when required permissions are missing or when bot member is unavailable/uncached
1381
1313
  *
1382
1314
  * @example
1383
1315
  * ```ts
@@ -1417,7 +1349,7 @@ function checkBotPermissions(target, scope, inverse = false, errors) {
1417
1349
  *
1418
1350
  * @param guild - The guild to get the bot role from
1419
1351
  * @returns The bot's managed role in the guild
1420
- * @throws A {@link SeedcordError} if the client user is unavailable or if the bot role is missing
1352
+ * @throws A **SeedcordError** if the client user is unavailable or if the bot role is missing
1421
1353
  */
1422
1354
  function getBotRole(guild) {
1423
1355
  const botRole = guild.roles.botRoleFor(guild.client.user);
@@ -1428,12 +1360,9 @@ function getBotRole(guild) {
1428
1360
  //#endregion
1429
1361
  //#region src/bot/utilities/permissions/hasPermsToAssign.ts
1430
1362
  /**
1431
- * Validates if the bot can assign a target role. Checks for role hierarchy, managed status, and Manage Roles permission.
1363
+ * Validates if the bot can assign a target role. Refuses when the target role is above the bot's, is managed, or the bot lacks Manage Roles.
1432
1364
  *
1433
1365
  * @param roleOrOptions - Target role or complete options for the check
1434
- * @throws A {@link RoleHigherThanMe} When the target role is higher than the bot role
1435
- * @throws A {@link CannotAssignBotRole} When the target role is managed
1436
- * @throws A {@link MissingPermissions} When the bot lacks Manage Roles permission
1437
1366
  *
1438
1367
  * @example
1439
1368
  * ```ts
@@ -1469,19 +1398,6 @@ function hasPermsToAssign(roleOrOptions) {
1469
1398
 
1470
1399
  //#endregion
1471
1400
  //#region src/bot/utilities/roles/fetchRole.ts
1472
- /**
1473
- * Error thrown when a requested role does not exist.
1474
- */
1475
- var RoleDoesNotExist = class extends _seedcord_kit.Notice {
1476
- roleId;
1477
- constructor(message, roleId) {
1478
- super(message);
1479
- this.roleId = roleId;
1480
- }
1481
- render() {
1482
- return { components: [new _seedcord_kit_internal.NoticeCard(`The role with ID \`${this.roleId}\` does not exist.`).component] };
1483
- }
1484
- };
1485
1401
  function isUnknownRole(err) {
1486
1402
  return err instanceof discord_js.DiscordAPIError && err.code === discord_js.RESTJSONErrorCodes.UnknownRole;
1487
1403
  }
@@ -1508,12 +1424,11 @@ async function scanGuildsForRole(client, roleId) {
1508
1424
  return null;
1509
1425
  }
1510
1426
  /**
1511
- * Fetches a role by ID from a client or guild.
1427
+ * Fetches a role by ID from a client or guild. Refuses when the role doesn't exist.
1512
1428
  *
1513
1429
  * @param clientOrGuild - Discord client or guild instance
1514
1430
  * @param roleId - The role ID to fetch
1515
1431
  * @returns Promise resolving to the role
1516
- * @throws A {@link RoleDoesNotExist} When the role doesn't exist
1517
1432
  */
1518
1433
  async function fetchRole(clientOrGuild, roleId) {
1519
1434
  if (!roleId) throw new RoleDoesNotExist("Role ID is null or undefined", roleId);
@@ -1525,23 +1440,11 @@ async function fetchRole(clientOrGuild, roleId) {
1525
1440
  //#endregion
1526
1441
  //#region src/bot/utilities/users/fetchGuildMember.ts
1527
1442
  /**
1528
- * Error thrown when attempting to perform actions on a user not in the guild.
1529
- */
1530
- var UserNotInGuild = class extends _seedcord_kit.Notice {
1531
- constructor(message = "User is not in the guild.") {
1532
- super(message);
1533
- }
1534
- render() {
1535
- return { components: [new _seedcord_kit_internal.NoticeCard(this.message).component] };
1536
- }
1537
- };
1538
- /**
1539
- * Fetches a guild member by user ID with error handling.
1443
+ * Fetches a guild member by user ID. Refuses when the user is not in the guild.
1540
1444
  *
1541
1445
  * @param guild - The guild to fetch the member from
1542
1446
  * @param userId - The Discord user ID
1543
1447
  * @returns Promise resolving to the guild member
1544
- * @throws A {@link UserNotInGuild} When the user is not found in the guild
1545
1448
  */
1546
1449
  async function fetchGuildMember(guild, userId) {
1547
1450
  let user = guild.members.cache.get(userId);
@@ -1568,26 +1471,12 @@ async function fetchManyGuildMembers(guild, userIds) {
1568
1471
  //#endregion
1569
1472
  //#region src/bot/utilities/users/fetchUser.ts
1570
1473
  /**
1571
- * Error thrown when a requested user cannot be found.
1572
- */
1573
- var UserNotFound = class extends _seedcord_kit.Notice {
1574
- userArg;
1575
- constructor(userArg) {
1576
- super(`User not found: ${userArg}`);
1577
- this.userArg = userArg;
1578
- }
1579
- render() {
1580
- return { components: [new _seedcord_kit_internal.NoticeCard(`User probably doesn't exist or was deleted.\n**User Argument:** \`${this.userArg}\`\nPlease check the user ID and try again. Only pass valid user IDs as the argument.`, "User Not Found").component] };
1581
- }
1582
- };
1583
- /**
1584
- * Fetches a Discord user by ID with error handling.
1474
+ * Fetches a Discord user by ID. Refuses when the user doesn't exist, with `options.throwAs` or the default.
1585
1475
  *
1586
1476
  * @param client - The Discord client instance
1587
1477
  * @param userId - The Discord user ID
1588
- * @param options - Optional overrides, including the {@link FetchUserOptions.throwAs} denial
1478
+ * @param options - Optional overrides, including the {@link FetchUserOptions.throwAs} notice
1589
1479
  * @returns Promise resolving to the user
1590
- * @throws The {@link FetchUserOptions.throwAs} denial when the user doesn't exist
1591
1480
  */
1592
1481
  async function fetchUser(client, userId, options) {
1593
1482
  const Throw = options?.throwAs ?? UserNotFound;
@@ -1898,7 +1787,7 @@ var Pluggable = class Pluggable extends _seedcord_services.StrictEventEmitter {
1898
1787
  * @param startupPhase - When during startup to initialize this plugin ({@link StartupPhase})
1899
1788
  * @param args - Additional arguments to pass to the plugin constructor
1900
1789
  * @returns This instance with the plugin attached as a typed property
1901
- * @throws A {@link SeedcordError} When called after initialization or if key already exists
1790
+ * @throws A **SeedcordError** When called after initialization or if key already exists
1902
1791
  * @example
1903
1792
  * ```typescript
1904
1793
  * seedcord.attach('db', Mongo, StartupPhase.Configuration, { uri: 'mongodb://...', name: 'seedcord', dir: ... })
@@ -2318,14 +2207,14 @@ var ModalHandler = class extends ComponentHandler {};
2318
2207
  * so `this.event` and `this.event.values` are narrowed to that kind. Read `this.params` for a single
2319
2208
  * route or `this.match` for several.
2320
2209
  *
2321
- * @typeParam Kind - The select kind from {@link SelectMenuType}, e.g. `SelectMenuType.User`.
2210
+ * @typeParam Kind - The select kind from {@link SelectMenuKind}, e.g. `SelectMenuKind.User`.
2322
2211
  * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof AssignId]`.
2323
2212
  * @typeParam Cache - The interaction cache state, `'cached'` by default.
2324
2213
  *
2325
2214
  * @example
2326
2215
  * ```ts
2327
- * \@SelectMenuRoute(SelectMenuType.User, AssignId)
2328
- * class AssignSelect extends SelectHandler<SelectMenuType.User, [typeof AssignId]> {
2216
+ * \@SelectMenuRoute(SelectMenuKind.User, AssignId)
2217
+ * class AssignSelect extends SelectHandler<SelectMenuKind.User, [typeof AssignId]> {
2329
2218
  * async execute() {
2330
2219
  * const { roleId } = this.params;
2331
2220
  * await this.event.reply(`assigning ${this.event.values.length} member(s) to <@&${roleId}>`);
@@ -2459,12 +2348,6 @@ var Subscriber = class {
2459
2348
  //#endregion
2460
2349
  //#region src/subscribers/decorators/Subscribe.ts
2461
2350
  /**
2462
- * Metadata key used to store subscriber handler information
2463
- *
2464
- * @internal
2465
- */
2466
- const SubscribeMetadataKey = Symbol("subscribe:metadata");
2467
- /**
2468
2351
  * Registers a subscriber handler class with a specific subscriber event.
2469
2352
  *
2470
2353
  * Associates the decorated class with a subscriber event type for automatic
@@ -3168,7 +3051,7 @@ var EventDispatcher = class {
3168
3051
  }
3169
3052
  registerMiddleware(middlewareCtor, relativePath) {
3170
3053
  const metadata = Reflect.getMetadata(MiddlewareMetadataKey, middlewareCtor);
3171
- if (metadata?.type !== "middleware:event") return;
3054
+ if (metadata?.type !== "event") return;
3172
3055
  if (this.middlewares.some((entry) => entry.ctor === middlewareCtor)) return;
3173
3056
  this.middlewares.push({
3174
3057
  ctor: middlewareCtor,
@@ -3344,17 +3227,17 @@ var InteractionDispatcher = class {
3344
3227
  middlewares = [];
3345
3228
  hmrHandler;
3346
3229
  routeTypes = [
3347
- ["interaction:slash", this.slashMap],
3348
- ["interaction:button", this.buttonMap],
3349
- ["interaction:modal", this.modalMap],
3350
- ["interaction:stringMenu", this.stringSelectMap],
3351
- ["interaction:userMenu", this.userSelectMap],
3352
- ["interaction:roleMenu", this.roleSelectMap],
3353
- ["interaction:channelMenu", this.channelSelectMap],
3354
- ["interaction:mentionableMenu", this.mentionableSelectMap],
3355
- ["interaction:messageContextMenu", this.messageContextMenuMap],
3356
- ["interaction:userContextMenu", this.userContextMenuMap],
3357
- ["interaction:autocomplete", this.autocompleteMap]
3230
+ ["slash", this.slashMap],
3231
+ ["button", this.buttonMap],
3232
+ ["modal", this.modalMap],
3233
+ ["stringMenu", this.stringSelectMap],
3234
+ ["userMenu", this.userSelectMap],
3235
+ ["roleMenu", this.roleSelectMap],
3236
+ ["channelMenu", this.channelSelectMap],
3237
+ ["mentionableMenu", this.mentionableSelectMap],
3238
+ ["messageContextMenu", this.messageContextMenuMap],
3239
+ ["userContextMenu", this.userContextMenuMap],
3240
+ ["autocomplete", this.autocompleteMap]
3358
3241
  ];
3359
3242
  constructor(core) {
3360
3243
  this.core = core;
@@ -3400,7 +3283,7 @@ var InteractionDispatcher = class {
3400
3283
  getArtifacts(handlerClass) {
3401
3284
  const artifacts = [];
3402
3285
  for (const [routeType] of this.routeTypes) {
3403
- const meta = Reflect.getMetadata(routeType, handlerClass);
3286
+ const meta = Reflect.getMetadata(InteractionRouteKeys[routeType], handlerClass);
3404
3287
  if (areRoutes(meta)) artifacts.push({
3405
3288
  routeType,
3406
3289
  routes: meta
@@ -3457,7 +3340,7 @@ var InteractionDispatcher = class {
3457
3340
  }
3458
3341
  registerMiddleware(middlewareCtor, relativePath) {
3459
3342
  const metadata = Reflect.getMetadata(MiddlewareMetadataKey, middlewareCtor);
3460
- if (metadata?.type !== "middleware:interaction") return;
3343
+ if (metadata?.type !== "interaction") return;
3461
3344
  const existingIndex = this.middlewares.findIndex((entry) => entry.ctor.name === middlewareCtor.name);
3462
3345
  if (existingIndex !== -1) this.middlewares[existingIndex] = {
3463
3346
  ctor: middlewareCtor,
@@ -3480,7 +3363,7 @@ var InteractionDispatcher = class {
3480
3363
  }
3481
3364
  registerHandler(handlerClass, relativePath) {
3482
3365
  for (const [routeType, map] of this.routeTypes) {
3483
- const meta = Reflect.getMetadata(routeType, handlerClass);
3366
+ const meta = Reflect.getMetadata(InteractionRouteKeys[routeType], handlerClass);
3484
3367
  if (!areRoutes(meta)) continue;
3485
3368
  meta.forEach((route) => map.set(route, handlerClass));
3486
3369
  this.logger.utils.registration(handlerClass.name, (0, _seedcord_utils.formatFilePath)(relativePath));
@@ -3499,7 +3382,7 @@ var InteractionDispatcher = class {
3499
3382
  return;
3500
3383
  }
3501
3384
  for (const [routeType, map] of this.routeTypes) {
3502
- const meta = Reflect.getMetadata(routeType, handlerClass);
3385
+ const meta = Reflect.getMetadata(InteractionRouteKeys[routeType], handlerClass);
3503
3386
  if (!areRoutes(meta)) continue;
3504
3387
  meta.forEach((route) => map.delete(route));
3505
3388
  }
@@ -4095,7 +3978,7 @@ var Seedcord = class Seedcord extends Pluggable {
4095
3978
  * Creates a new Seedcord instance
4096
3979
  *
4097
3980
  * @param config - Bot configuration including paths and Discord client options
4098
- * @throws A {@link SeedcordError} When attempting to create multiple instances (singleton)
3981
+ * @throws A **SeedcordError** When attempting to create multiple instances (singleton)
4099
3982
  */
4100
3983
  constructor(config) {
4101
3984
  if (Seedcord.isInstantiated) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.CoreSingletonViolation);
@@ -4165,14 +4048,13 @@ var Seedcord = class Seedcord extends Pluggable {
4165
4048
  //#endregion
4166
4049
  //#region src/index.ts
4167
4050
  /** Package version */
4168
- const version = "0.13.0-next.0";
4051
+ const version = "0.13.0";
4169
4052
 
4170
4053
  //#endregion
4171
4054
  exports.AutocompleteHandler = AutocompleteHandler;
4172
4055
  exports.AutocompleteRoute = AutocompleteRoute;
4173
4056
  exports.ButtonHandler = ButtonHandler;
4174
4057
  exports.ButtonRoute = ButtonRoute;
4175
- exports.CannotAssignBotRole = CannotAssignBotRole;
4176
4058
  exports.ContextMenuHandler = ContextMenuHandler;
4177
4059
  exports.ContextMenuRoute = ContextMenuRoute;
4178
4060
  exports.Cooldown = Cooldown;
@@ -4180,26 +4062,17 @@ exports.DmOnly = DmOnly;
4180
4062
  exports.Emojis = Emojis;
4181
4063
  exports.EventHandler = EventHandler;
4182
4064
  exports.EventMiddleware = EventMiddleware;
4183
- exports.GateNotice = GateNotice;
4184
4065
  exports.Gated = Gated;
4185
4066
  exports.GuildOnly = GuildOnly;
4186
- exports.HasDangerousPermissions = HasDangerousPermissions;
4187
4067
  exports.HmrModuleHandler = HmrModuleHandler;
4188
4068
  exports.IgnoreBots = IgnoreBots;
4189
4069
  exports.InteractionHandler = InteractionHandler;
4190
4070
  exports.InteractionMiddleware = InteractionMiddleware;
4191
4071
  exports.Middleware = Middleware;
4192
4072
  exports.MiddlewareType = MiddlewareType;
4193
- exports.MissingPermissions = MissingPermissions;
4194
- exports.MissingRole = MissingRole;
4195
4073
  exports.ModalHandler = ModalHandler;
4196
4074
  exports.ModalRoute = ModalRoute;
4197
- exports.NotInDm = NotInDm;
4198
- exports.NotInGuild = NotInGuild;
4199
- exports.NotNsfw = NotNsfw;
4200
- exports.NotOwner = NotOwner;
4201
4075
  exports.Nsfw = Nsfw;
4202
- exports.OnCooldown = OnCooldown;
4203
4076
  exports.OwnerOnly = OwnerOnly;
4204
4077
  exports.Pluggable = Pluggable;
4205
4078
  exports.Plugin = Plugin;
@@ -4209,11 +4082,10 @@ exports.ReplySender = ReplySender;
4209
4082
  exports.RequireBotPermissions = RequireBotPermissions;
4210
4083
  exports.RequirePermissions = RequirePermissions;
4211
4084
  exports.RequireRole = RequireRole;
4212
- exports.RoleHigherThanMe = RoleHigherThanMe;
4213
4085
  exports.Seedcord = Seedcord;
4214
4086
  exports.SelectHandler = SelectHandler;
4087
+ exports.SelectMenuKind = SelectMenuKind;
4215
4088
  exports.SelectMenuRoute = SelectMenuRoute;
4216
- exports.SelectMenuType = SelectMenuType;
4217
4089
  exports.SlashHandler = SlashHandler;
4218
4090
  exports.SlashRoute = SlashRoute;
4219
4091
  exports.Subscribe = Subscribe;