@seedcord/core 0.11.0 → 0.12.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +3 -3
  2. package/dist/Bus-VJw-QpoY.mjs +986 -0
  3. package/dist/Bus-VJw-QpoY.mjs.map +1 -0
  4. package/dist/{CoordinatedShutdown-H5YjZn8A.d.mts → CoordinatedShutdown-fPKLWjc5.d.mts} +13 -4
  5. package/dist/{CoordinatedStartup-CGnJrqcl.mjs → CoordinatedStartup-Zw_Lk7Zk.mjs} +22 -20
  6. package/dist/CoordinatedStartup-Zw_Lk7Zk.mjs.map +1 -0
  7. package/dist/Generated-ByC53_A2.d.mts +26 -0
  8. package/dist/HmrModuleHandler-Btgu8r96.mjs +165 -0
  9. package/dist/HmrModuleHandler-Btgu8r96.mjs.map +1 -0
  10. package/dist/{PluginHost-DB4j8unu.d.mts → PluginHost-DFnkIldj.d.mts} +43 -15
  11. package/dist/PluginHost-DTtxWRiL.mjs +552 -0
  12. package/dist/PluginHost-DTtxWRiL.mjs.map +1 -0
  13. package/dist/{PluginLifecycle-AhQ46NIr.mjs → PluginLifecycle-hx5zUc-P.mjs} +4 -4
  14. package/dist/PluginLifecycle-hx5zUc-P.mjs.map +1 -0
  15. package/dist/{ServerHost-DYeh0KTk.d.mts → ServerHost-B5o3ScCF.d.mts} +7 -6
  16. package/dist/{ServerHost-CtmAhEXn.mjs → ServerHost-CDaEDR4C.mjs} +17 -6
  17. package/dist/{ServerHost-CtmAhEXn.mjs.map → ServerHost-CDaEDR4C.mjs.map} +1 -1
  18. package/dist/bindViteEnv-NFzvdqlT.mjs +32 -0
  19. package/dist/{bindViteEnv-DWq5rftJ.mjs.map → bindViteEnv-NFzvdqlT.mjs.map} +1 -1
  20. package/dist/env-internal.index.mjs +1 -1
  21. package/dist/hmr.index.d.mts +20 -20
  22. package/dist/hmr.index.mjs +1 -1
  23. package/dist/{index-Du71Xx69.d.mts → index-DGHng1IN.d.mts} +115 -201
  24. package/dist/index.d.mts +321 -74
  25. package/dist/index.mjs +323 -102
  26. package/dist/index.mjs.map +1 -1
  27. package/dist/internal.index.d.mts +223 -83
  28. package/dist/internal.index.mjs +573 -74
  29. package/dist/internal.index.mjs.map +1 -1
  30. package/dist/node-internal.index.d.mts +8 -4
  31. package/dist/node-internal.index.mjs +19 -6
  32. package/dist/node-internal.index.mjs.map +1 -1
  33. package/dist/node.index.d.mts +3 -3
  34. package/dist/node.index.mjs +22 -28
  35. package/dist/node.index.mjs.map +1 -1
  36. package/dist/{phases-FTM69J3a.d.mts → phases-C2QCSqRu.d.mts} +164 -55
  37. package/dist/plugin.index.d.mts +3 -2
  38. package/dist/plugin.index.mjs +1 -1
  39. package/dist/subscribers-CGuVjbrg.mjs +982 -0
  40. package/dist/subscribers-CGuVjbrg.mjs.map +1 -0
  41. package/dist/{bindViteEnv-DWq5rftJ.mjs → usingCtx-Cf2cZi96.mjs} +1 -31
  42. package/package.json +14 -11
  43. package/dist/CoordinatedStartup-CGnJrqcl.mjs.map +0 -1
  44. package/dist/HmrModuleHandler-8ZC-UuZh.mjs +0 -162
  45. package/dist/HmrModuleHandler-8ZC-UuZh.mjs.map +0 -1
  46. package/dist/PluginHost-B8kxLOeb.mjs +0 -245
  47. package/dist/PluginHost-B8kxLOeb.mjs.map +0 -1
  48. package/dist/PluginLifecycle-AhQ46NIr.mjs.map +0 -1
  49. package/dist/phases-BauKJOTT.mjs +0 -31
  50. package/dist/phases-BauKJOTT.mjs.map +0 -1
  51. package/dist/publishDefault-D3dAyzZ9.mjs +0 -7
  52. package/dist/publishDefault-D3dAyzZ9.mjs.map +0 -1
  53. package/dist/subscribers-Ce3SWy23.mjs +0 -1913
  54. package/dist/subscribers-Ce3SWy23.mjs.map +0 -1
@@ -1,10 +1,11 @@
1
- import { K as Notice, P as DispatchContext, U as MiddlewareKind, l as Subscriber, r as CoreBase, y as SubscriptionKey } from "./phases-FTM69J3a.mjs";
1
+ import { I as DispatchContext, K as MiddlewareKind, c as AllSubscriptions, i as CoreBase, y as SubscriptionKey } from "./phases-C2QCSqRu.mjs";
2
2
  import "reflect-metadata";
3
3
  import { CustomId, CustomIdField } from "@seedcord/custom-id";
4
4
  import { ActionRowBuilder, ButtonBuilder, ChannelSelectMenuBuilder, CheckboxBuilder, CheckboxGroupBuilder, CheckboxGroupOptionBuilder, ContainerBuilder, ContextMenuCommandBuilder, EmbedBuilder, FileBuilder, FileUploadBuilder, LabelBuilder, MediaGalleryBuilder, MentionableSelectMenuBuilder, ModalBuilder, RadioGroupBuilder, RadioGroupOptionBuilder, RoleSelectMenuBuilder, SectionBuilder, SeparatorBuilder, SlashCommandBuilder, SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, TextDisplayBuilder, TextInputBuilder, UserSelectMenuBuilder } from "@discordjs/builders";
5
5
  import { APIApplicationCommand, APIMessageComponentEmoji, APIMessageTopLevelComponent, ApplicationCommandType, ChannelType, RESTPostAPIApplicationCommandsJSONBody, Snowflake } from "discord-api-types/v10";
6
+ import { Logger } from "@seedcord/logger";
6
7
  import { EventFrequency, ILogger, ReplyFile, ReplyResponse } from "@seedcord/types";
7
- import { Constructor, IsNever, LiteralUnion, NonEmptyTuple } from "type-fest";
8
+ import { Constructor, IsNever, IsUnion, LiteralUnion, NonEmptyTuple, Promisable, Simplify } from "type-fest";
8
9
  //#region src/registries/ContextMenuRegistry.d.ts
9
10
  /** The two context-menu command kinds Discord defines. */
10
11
  type ContextMenuKind = ApplicationCommandType.User | ApplicationCommandType.Message;
@@ -19,7 +20,7 @@ type ContextMenuKind = ApplicationCommandType.User | ApplicationCommandType.Mess
19
20
  *
20
21
  * @example
21
22
  * ```ts
22
- * // seedcord-gen.d.ts (emitted, committed)
23
+ * // seedcord-gen.ts (emitted, committed)
23
24
  * declare module '@seedcord/gateway' {
24
25
  * interface UserContextMenuRegistry {
25
26
  * 'View Profile': { cache: 'cached' };
@@ -77,7 +78,7 @@ interface SlashRouteEntry {
77
78
  *
78
79
  * @example
79
80
  * ```ts
80
- * // seedcord-gen.d.ts (emitted, committed)
81
+ * // seedcord-gen.ts (emitted, committed)
81
82
  * declare module '@seedcord/gateway' {
82
83
  * interface SlashRegistry {
83
84
  * ban: { options: { target: { kind: 'user'; required: true } }; cache: 'cached' };
@@ -471,7 +472,7 @@ declare function serializeReply<TNative>(response: ReplyResponse<TNative> | stri
471
472
  /**
472
473
  * The transport-agnostic gate context, the scalar identity of the acting user resolvable from any
473
474
  * transport's payload. Every field except `core` is nullable, since an event or DM may carry no
474
- * guild or member. Each transport extends it with its own arm. For example, the gateway adds the djs
475
+ * guild or member. Each transport extends it with its own context. For example, the gateway adds the djs
475
476
  * `user`/`guild`/`member` objects and the live interaction or event payload.
476
477
  *
477
478
  * @example
@@ -501,19 +502,9 @@ interface GateContextBase {
501
502
  * channel-scoped permissions the payload contains, and on a gateway event they're the member's guild-level ones.
502
503
  */
503
504
  readonly memberPermissions: bigint | null;
504
- /**
505
- * The bot's permissions in the invoked channel, the wire `app_permissions` value. Null on gateway events.
506
- */
507
- readonly appPermissions: bigint | null;
508
- /**
509
- * The handler's own route, as `kind:route` (`slash:daily`, `button:confirm`). A handler registered
510
- * on several reports the one this dispatch matched. Null when the handler declares none, which
511
- * covers a plain event handler, the unhandled default, and a gate run outside a handler.
512
- */
513
- readonly declaredRoute: string | null;
514
505
  /**
515
506
  * The bag for this dispatch, the same instance the handler and its middleware hold. Every middleware
516
- * runs before the first gate. A gate therefore reads what a middleware wrote.
507
+ * runs before the first gate. A gate can therefore read what a middleware wrote.
517
508
  */
518
509
  readonly dispatch: DispatchContext;
519
510
  }
@@ -535,10 +526,25 @@ interface GuildPermissionsContext extends GateContextBase {
535
526
  */
536
527
  readonly memberGuildPermissions: bigint | null;
537
528
  /**
538
- * The bot's base permission set from the transport's cache. Null when the cached member is unavailable.
529
+ * The bot's base permission set from the transport's cache. It's `0n` in a guild the bot isn't in. It's null
530
+ * when the guild is cached but the bot's member isn't.
539
531
  */
540
532
  readonly appGuildPermissions: bigint | null;
541
533
  }
534
+ /**
535
+ * The context an interaction provides on both transports, where Discord always sends the bot's channel
536
+ * permissions. A gate that requires it fits no event handler.
537
+ *
538
+ * @example
539
+ * ```ts
540
+ * // the default channel scope returns a Gate<AppPermissionsContext>
541
+ * \@Gated(RequireBotPermissions([PermissionFlagsBits.ManageMessages]))
542
+ * ```
543
+ */
544
+ interface AppPermissionsContext extends GateContextBase {
545
+ /** The bot's permissions in the invoked channel, the wire `app_permissions` value. */
546
+ readonly appPermissions: bigint;
547
+ }
542
548
  declare const GateBrand: unique symbol;
543
549
  /**
544
550
  * A precondition attached to a handler. It refuses by throwing a `Notice` (a reply) or a `Silence`
@@ -570,13 +576,12 @@ interface Gate<Ctx extends GateContextBase = GateContextBase, Name extends strin
570
576
  readonly [GateBrand]: true;
571
577
  }
572
578
  /**
573
- * A gate that carries a side effect, split so the effect fires only once the gate is the one that
574
- * let the request through. `check` peeks and refuses, while `commit` applies the effect after the whole
575
- * gate set passes. {@link Cooldown} is the catalog gate that returns one. Build one with
579
+ * A gate whose `check` method can refuse a request without applying its side effect. `commit` applies
580
+ * the effect after every gate passes. {@link Cooldown} is the catalog gate that returns one. Build one with
576
581
  * {@link defineEffectGate}.
577
582
  *
578
583
  * @typeParam Ctx - The context both `check` and `commit` require. A field missing from it fails to compile.
579
- * @typeParam Name - The gate's literal name, shown when a mismatch error names the gate.
584
+ * @typeParam Name - The literal name a mismatch error prints.
580
585
  *
581
586
  * @example
582
587
  * ```ts
@@ -633,11 +638,11 @@ interface EffectGate<Ctx extends GateContextBase = GateContextBase, Name extends
633
638
  * });
634
639
  * ```
635
640
  */
636
- declare function defineGate<const Name extends string, Ctx extends GateContextBase = GateContextBase>(name: Name, fn: (ctx: Ctx) => void | Promise<void>): Gate<Ctx, Name>;
641
+ declare function defineGate<const Name extends string, Ctx extends GateContextBase = GateContextBase>(name: Name, fn: (ctx: Ctx) => Promisable<void>): Gate<Ctx, Name>;
637
642
  /**
638
643
  * Builds an {@link EffectGate} from a `check` and a `commit`. `check` peeks and refuses by throwing, and
639
644
  * `commit` applies the side effect, running only once the whole gate set passes so a later refusal never
640
- * commits. In an `or`, a refusing arm's queued commit is rolled back. This is how {@link Cooldown} peeks
645
+ * commits. In an `or`, seedcord rolls back the commit a refusing gate queued. This is how {@link Cooldown} peeks
641
646
  * in `check` and charges the slot in `commit`.
642
647
  *
643
648
  * @typeParam Name - The gate's literal name, used to name a mismatch error.
@@ -669,7 +674,7 @@ declare function defineGate<const Name extends string, Ctx extends GateContextBa
669
674
  * }
670
675
  * ```
671
676
  */
672
- declare function defineEffectGate<const Name extends string, Ctx extends GateContextBase = GateContextBase>(name: Name, check: (ctx: Ctx) => void | Promise<void>, commit: (ctx: Ctx) => void | Promise<void>): EffectGate<Ctx, Name>;
677
+ declare function defineEffectGate<const Name extends string, Ctx extends GateContextBase = GateContextBase>(name: Name, check: (ctx: Ctx) => Promisable<void>, commit: (ctx: Ctx) => Promisable<void>): EffectGate<Ctx, Name>;
673
678
  /**
674
679
  * The context a {@link Gate} requires.
675
680
  *
@@ -688,164 +693,9 @@ type TwoOrMore<Item> = readonly [Item, Item, ...Item[]];
688
693
  type GateName<TGate> = TGate extends Gate<GateContextBase, infer Name> ? Name : string;
689
694
  type Grouped<Name extends string> = Name extends `${string} & ${string}` | `${string} | ${string}` ? `(${Name})` : Name;
690
695
  type JoinNames<Gates extends readonly Gate<GateContextBase>[], Sep extends string> = Gates extends readonly [infer Only extends Gate<GateContextBase>] ? Grouped<GateName<Only>> : Gates extends readonly [infer First extends Gate<GateContextBase>, ...infer Rest extends readonly Gate<GateContextBase>[]] ? `${Grouped<GateName<First>>}${Sep}${JoinNames<Rest, Sep>}` : string;
691
- type GateMismatch<Name extends string, Want extends string, Got extends string> = `gate '${Name}' requires a ${Want} handler, and this handler is ${Got}`;
696
+ type GateMismatch<Name extends string, Want extends string, Got extends string> = `gate '${Name}' needs a handler of kind ${Want}, but this handler's kind is ${Got}`;
692
697
  type GateFitsWith<Provided, ProvidedLabel extends string, TGate, ReqLabel extends string> = [Provided] extends [RequiredOf<TGate>] ? TGate : Constructor<[GateMismatch<GateName<TGate>, ReqLabel, ProvidedLabel>]>;
693
698
  //#endregion
694
- //#region src/gates/catalog/options.d.ts
695
- /**
696
- * The override a catalog gate accepts. A one-line `message` reword or a full `notice` replacement. Accepted by
697
- * the agnostic catalog gates and, nested per refusal, by {@link RequireRole}. {@link RequirePermissions} and
698
- * {@link RequireBotPermissions} take `RequirePermissionsOptions` instead.
699
- *
700
- * @example
701
- * ```ts
702
- * // reword the default refusal
703
- * OwnerOnly({ message: 'Bot owners only.' });
704
- * ```
705
- *
706
- * @example
707
- * ```ts
708
- * // replace the refusal entirely, for example a translated Notice
709
- * GuildOnly({ notice: new MyTranslatedNotice() });
710
- * ```
711
- */
712
- interface GateNoticeOptions {
713
- /** Reword the default refusal, keeping its embed styling. */
714
- message?: string;
715
- /** Replace the default refusal Notice entirely, for full control or a translated copy. */
716
- notice?: Notice;
717
- }
718
- declare function pickNotice(options: GateNoticeOptions | undefined, makeDefault: (message?: string) => Notice): Notice;
719
- //#endregion
720
- //#region src/permissions/bits.d.ts
721
- /**
722
- * The permission flag bits a caller or the bot must hold. Pass the `PermissionFlagsBits` values from
723
- * `discord-api-types/v10` (or `discord.js`).
724
- */
725
- type PermissionScope = readonly bigint[];
726
- declare const PermissionNames: Map<bigint, string>;
727
- //#endregion
728
- //#region src/gates/catalog/permissions.d.ts
729
- /**
730
- * Options for {@link RequirePermissions} and {@link RequireBotPermissions}.
731
- */
732
- interface RequirePermissionsOptions {
733
- /**
734
- * The set the check reads. `'channel'` reads the effective channel set the payload carries, what
735
- * Discord enforces in the invoked channel. `'guild'` reads the base set from roles, available on the
736
- * gateway transport only.
737
- *
738
- * @defaultValue `'channel'`
739
- */
740
- in?: 'channel' | 'guild';
741
- /** Reword or replace the refusal shown when the set is unavailable (outside a guild, or unresolved inside one). */
742
- notInGuild?: GateNoticeOptions;
743
- /** Reword or replace the refusal shown when a permission is missing. */
744
- missing?: GateNoticeOptions;
745
- }
746
- /**
747
- * Options for {@link RequireRole}.
748
- */
749
- interface RequireRoleOptions {
750
- /** Reword or replace the refusal shown outside a guild. */
751
- notInGuild?: GateNoticeOptions;
752
- /** Reword or replace the refusal shown when the caller lacks the role. */
753
- missingRole?: GateNoticeOptions;
754
- }
755
- /**
756
- * Requires the caller to hold every permission in `scope`. Refuses outside a guild first, then when a
757
- * permission is missing. The Administrator bit passes any scope.
758
- *
759
- * The default `'channel'` scope reads the effective channel set the payload carries, what Discord enforces
760
- * in the invoked channel. The `'guild'` scope reads the base set from roles and requires a
761
- * {@link GuildPermissionsContext}, so it fits a gateway handler only.
762
- *
763
- * @param scope - The permission flag bits the caller must all hold.
764
- * @param options - Pick the scope with `in`, and override each refusal with `notInGuild` or `missing`.
765
- *
766
- * @see the `@Gated` decorator from your transport package
767
- *
768
- * @example
769
- * ```ts
770
- * import { PermissionFlagsBits } from 'discord-api-types/v10';
771
- *
772
- * \@Gated(RequirePermissions([PermissionFlagsBits.BanMembers]))
773
- * \@SlashRoute('ban')
774
- * class BanHandler extends SlashHandler<'ban'> {
775
- * async execute() {
776
- * // ...
777
- * }
778
- * }
779
- * ```
780
- *
781
- * @example
782
- * ```ts
783
- * // the base set from roles, on a gateway handler
784
- * RequirePermissions([PermissionFlagsBits.BanMembers], { in: 'guild' });
785
- * ```
786
- */
787
- declare function RequirePermissions(scope: PermissionScope, options: RequirePermissionsOptions & {
788
- in: 'guild';
789
- }): Gate<GuildPermissionsContext, 'RequirePermissions'>;
790
- declare function RequirePermissions(scope: PermissionScope, options?: RequirePermissionsOptions & {
791
- in?: 'channel';
792
- }): Gate<GateContextBase, 'RequirePermissions'>;
793
- /**
794
- * Requires the bot to hold every permission in `scope`. Refuses outside a guild first, then when a
795
- * permission is missing. The Administrator bit passes any scope.
796
- *
797
- * The default `'channel'` scope reads the bot's effective channel set (wire `app_permissions`), what
798
- * Discord enforces in the invoked channel. The `'guild'` scope reads the bot's base set from roles and
799
- * requires a {@link GuildPermissionsContext}, fitting a gateway handler only. `app_permissions` is
800
- * absent on gateway events, the channel scope refuses there as unresolved, use `in: 'guild'` on an
801
- * event handler.
802
- *
803
- * @param scope - The permission flag bits the bot must all hold.
804
- * @param options - Pick the scope with `in`, and override each refusal with `notInGuild` or `missing`.
805
- *
806
- * @see the `@Gated` decorator from your transport package
807
- *
808
- * @example
809
- * ```ts
810
- * import { PermissionFlagsBits } from 'discord-api-types/v10';
811
- *
812
- * \@Gated(RequireBotPermissions([PermissionFlagsBits.ManageMessages]))
813
- * \@SlashRoute('purge')
814
- * class PurgeHandler extends SlashHandler<'purge'> {
815
- * async execute() {
816
- * // ...
817
- * }
818
- * }
819
- * ```
820
- */
821
- declare function RequireBotPermissions(scope: PermissionScope, options: RequirePermissionsOptions & {
822
- in: 'guild';
823
- }): Gate<GuildPermissionsContext, 'RequireBotPermissions'>;
824
- declare function RequireBotPermissions(scope: PermissionScope, options?: RequirePermissionsOptions & {
825
- in?: 'channel';
826
- }): Gate<GateContextBase, 'RequireBotPermissions'>;
827
- /**
828
- * Requires the caller to hold `roleId`, read from the member's role ids. Refuses outside a guild first,
829
- * then when the role is missing. Roles are guild-global, so there is no scope dimension.
830
- *
831
- * @param roleId - The role snowflake the caller must hold.
832
- * @param options - Override each refusal, the outside-guild one with `notInGuild` and the missing-role one with `missingRole`.
833
- *
834
- * @see the `@Gated` decorator from your transport package
835
- *
836
- * @example
837
- * ```ts
838
- * \@Gated(RequireRole('123456789012345678'))
839
- * \@SlashRoute('vip')
840
- * class VipHandler extends SlashHandler<'vip'> {
841
- * async execute() {
842
- * // ...
843
- * }
844
- * }
845
- * ```
846
- */
847
- declare function RequireRole(roleId: string, options?: RequireRoleOptions): Gate<GateContextBase, 'RequireRole'>;
848
- //#endregion
849
699
  //#region src/miscellaneous/ResolvedEmoji.d.ts
850
700
  /**
851
701
  * A resolved custom emoji. Renders as `<:name:id>` in message content, or `<a:name:id>` when animated,
@@ -871,10 +721,10 @@ type ChoiceValueOf<Entry> = Entry extends {
871
721
  kind: 'integer' | 'number';
872
722
  } ? number : never;
873
723
  /** the focused option. `value` is the raw partial discord delivers mid-type, always a string regardless of kind. */
874
- interface FocusedField<Route extends keyof SlashRegistry> {
724
+ type FocusedField<Route extends keyof SlashRegistry> = Simplify<{
875
725
  name: AutocompletableNames<Route>;
876
726
  value: string;
877
- }
727
+ }>;
878
728
  type NamesOfKind<Route extends keyof SlashRegistry, Kind extends OptionKind> = Route extends unknown ? { [Name in keyof Row<Route>]: Row<Route>[Name] extends {
879
729
  kind: Kind;
880
730
  } ? Name : never; }[keyof Row<Route>] : never;
@@ -908,6 +758,66 @@ type PageCursor<Prefix extends string> = CustomId<Prefix, {
908
758
  /** Build the page cursor for a paginator. */
909
759
  declare function pageCursor<Prefix extends string>(prefix: Prefix): PageCursor<Prefix>;
910
760
  //#endregion
761
+ //#region src/handlers/runCase.d.ts
762
+ type ResolvedReturn<Case> = Case extends ((...args: never[]) => infer Result) ? Awaited<Result> : never;
763
+ type CaseReturn<Ret> = [Ret] extends [never] ? unknown : Promisable<Ret>;
764
+ type MatchResult<Ret, Cases> = [Ret] extends [never] ? ResolvedReturn<Cases[keyof Cases]> : Ret;
765
+ type ShapeKey<Shape> = keyof Shape | `${Extract<keyof Shape, string>}`;
766
+ type OnlyCases<Cases, Shape> = Cases & Record<Exclude<keyof Cases, ShapeKey<Shape>>, never>;
767
+ declare function runCase<Result>(handler: object, cases: object, kind: string, value: string, ...args: unknown[]): Promise<Result>;
768
+ //#endregion
769
+ //#region src/subscribers/Subscriber.d.ts
770
+ type SubscriberMatchCases<Keys extends SubscriptionKey, Ret> = { [Key in Keys]: (data: AllSubscriptions[Key]) => CaseReturn<Ret>; };
771
+ type SingleKeyData<Keys extends SubscriptionKey> = IsUnion<Keys> extends true ? never : AllSubscriptions[Keys];
772
+ /**
773
+ * Abstract base class for handling application subscribers.
774
+ *
775
+ * Pass the subscription key(s) as the generic, the same one(s) as `@Subscribe`. On a single-key subscriber, read
776
+ * `this.data` directly. On a subscriber for several keys, branch with `this.match`, keyed by subscription key.
777
+ *
778
+ * @typeParam Keys - One or more subscription keys, like `'userJoin'` or `'userJoin' | 'userLeave'`.
779
+ * @typeParam TCore - The transport's Core
780
+ */
781
+ declare abstract class Subscriber<in out Keys extends SubscriptionKey, TCore extends CoreBase> {
782
+ #private;
783
+ protected readonly core: TCore;
784
+ protected readonly key: Keys;
785
+ protected readonly logger: Logger;
786
+ constructor(payload: AllSubscriptions[Keys], core: TCore, key: Keys);
787
+ /** The published payload. It's `never` on a subscriber for several keys, where `this.match` reads it. */
788
+ protected get data(): SingleKeyData<Keys>;
789
+ /**
790
+ * Executes the subscriber handler logic.
791
+ */
792
+ abstract execute(): Promise<void>;
793
+ /**
794
+ * Run the case for the published key. Use this only when the subscriber is registered for several
795
+ * keys. On a single-key subscriber, read `this.data` directly.
796
+ *
797
+ * Provide one case per registered key. Each case receives that key's own payload. A missing key or an unknown
798
+ * key is a compile error.
799
+ *
800
+ * @typeParam Ret - The type that every case must return. Leave it out to get the union of what the cases return.
801
+ * @throws A **SeedcordTypeError** with `MatchCaseMissing` when the published key doesn't have a case.
802
+ *
803
+ * @example
804
+ * ```ts
805
+ * import { Subscribe, Subscriber } from '@seedcord/gateway';
806
+ *
807
+ * \@Subscribe(['interactionDispatched'], ['eventDispatched'])
808
+ * class Metrics extends Subscriber<'interactionDispatched' | 'eventDispatched'> {
809
+ * async execute() {
810
+ * await this.match({
811
+ * interactionDispatched: (data) => this.logger.info(data.routeId),
812
+ * eventDispatched: (data) => this.logger.info(data.name)
813
+ * });
814
+ * }
815
+ * }
816
+ * ```
817
+ */
818
+ protected match<Ret = never, Cases extends SubscriberMatchCases<Keys, Ret> = SubscriberMatchCases<Keys, Ret>>(cases: OnlyCases<Cases, SubscriberMatchCases<Keys, Ret>>): Promise<MatchResult<Ret, Cases>>;
819
+ }
820
+ //#endregion
911
821
  //#region src/subscribers/decorators/Subscribe.d.ts
912
822
  /**
913
823
  * Options accepted by the `@Subscribe` decorator.
@@ -920,32 +830,36 @@ interface SubscribeOptions {
920
830
  */
921
831
  readonly frequency?: EventFrequency | undefined;
922
832
  }
833
+ type SubscribeSpec<Key extends SubscriptionKey> = readonly [key: Key, options?: SubscribeOptions];
923
834
  /**
924
- * Registers a subscriber handler class with a specific subscriber event.
835
+ * Registers a subscriber class with one or more subscription keys.
925
836
  *
926
- * Associates the decorated class with a subscriber event type for automatic
927
- * registration and execution when the subscriber is emitted.
837
+ * Supply one `[key, options?]` tuple per key. The subscriber's generic must list the same keys.
928
838
  *
929
- * @typeParam TSubscriber - The subscriber event name to register for
930
- * @param subscriber - The subscriber event name to register for
931
- * @param options - Options to configure the subscriber handler registration.
932
839
  * @decorator
933
840
  * @example
934
- * ```typescript
935
- * \@Subscribe('userJoin')
841
+ * ```ts
842
+ * \@Subscribe(['userJoin'])
936
843
  * class WelcomeHandler extends Subscriber<'userJoin'> {
937
- * async execute() {
938
- * }
844
+ * async execute() {
845
+ * this.logger.info(`${this.data.userId} joined`);
846
+ * }
939
847
  * }
940
848
  * ```
941
849
  * @example
942
850
  * ```ts
943
- * \@Subscribe('userJoin', { frequency: 'once' })
944
- * // or
945
- * \@Subscribe('userJoin')
851
+ * \@Subscribe(['userJoin', { frequency: 'once' }], ['userLeave'])
852
+ * class Presence extends Subscriber<'userJoin' | 'userLeave'> {
853
+ * async execute() {
854
+ * await this.match({
855
+ * userJoin: (data) => this.logger.info(`${data.userId} joined`),
856
+ * userLeave: (data) => this.logger.info(`${data.userId} left`)
857
+ * });
858
+ * }
859
+ * }
946
860
  * ```
947
861
  */
948
- declare function Subscribe<TSubscriber extends SubscriptionKey>(subscriber: TSubscriber, options?: SubscribeOptions): <HandlerCtor extends Constructor<Subscriber<TSubscriber, CoreBase>>>(constructor: HandlerCtor) => void;
862
+ declare function Subscribe<const Defs extends readonly SubscribeSpec<SubscriptionKey>[]>(...defs: Defs): <HandlerCtor extends Constructor<Subscriber<Defs[number][0], CoreBase>>>(constructor: HandlerCtor) => void;
949
863
  //#endregion
950
864
  //#region src/subscribers/bases/WebhookSender.d.ts
951
865
  /** A file attached to a webhook report. */
@@ -990,7 +904,7 @@ interface WebhookReport {
990
904
  * resolves the url, validates it, reuses one sender per url, and sends. Registration skips a reporter
991
905
  * whose variable is unset and warns at boot.
992
906
  *
993
- * @typeParam KeyOfSubscribers - The subscription key this reporter receives
907
+ * @typeParam KeyOfSubscribers - The subscription key or keys that this reporter receives, the same ones as `@Subscribe`
994
908
  * @typeParam TCore - The transport's Core
995
909
  */
996
910
  declare abstract class WebhookLog<KeyOfSubscribers extends SubscriptionKey, TCore extends CoreBase> extends Subscriber<KeyOfSubscribers, TCore> {
@@ -1025,7 +939,7 @@ declare abstract class WebhookLog<KeyOfSubscribers extends SubscriptionKey, TCor
1025
939
  * @decorator
1026
940
  * @example
1027
941
  * ```ts
1028
- * \@Subscribe('memberBanned')
942
+ * \@Subscribe(['memberBanned'])
1029
943
  * \@WebhookUrl('BAN_LOG_WEBHOOK_URL')
1030
944
  * class BanLog extends WebhookLog<'memberBanned'> {
1031
945
  * report() {
@@ -1034,7 +948,7 @@ declare abstract class WebhookLog<KeyOfSubscribers extends SubscriptionKey, TCor
1034
948
  * }
1035
949
  * ```
1036
950
  */
1037
- declare function WebhookUrl(envKey: string): <HandlerCtor extends Constructor<WebhookLog<SubscriptionKey, CoreBase>>>(constructor: HandlerCtor) => void;
951
+ declare function WebhookUrl(envKey: string): <Keys extends SubscriptionKey>(constructor: Constructor<WebhookLog<Keys, CoreBase>>) => void;
1038
952
  //#endregion
1039
- export { SlashRouteBrand as $, Gate as A, CacheFor as At, HandlerOutcome as B, UserContextMenuRegistry as Bt, GateNoticeOptions as C, CommandMeta as Ct, JoinNames as D, RowComponent as Dt, IntersectRequired as E, BuilderComponent as Et, defineGate as F, SlashRouteEntry as Ft, RegisterInteractionMiddleware as G, AnyMiddlewareCtor as H, SerializedReply as I, ContextMenuKind as It, ComponentDefsBrand as J, interactionMiddlewareMetaOf as K, serializeReply as L, MenuCacheFor as Lt, GuildPermissionsContext as M, RouteCache as Mt, RequiredOf as N, SlashOption as Nt, TwoOrMore as O, BuilderType as Ot, defineEffectGate as P, SlashRegistry as Pt, MiddlewareKindsBrand as Q, DispatchResult as R, MessageContextMenuRegistry as Rt, PermissionScope as S, resolveDeployTarget as St, GateFitsWith as T, RegisterCommand as Tt, InteractionMiddlewareMetadata as U, HandlerResult as V, InteractionMiddlewareOptions as W, ContextMenuKindBrand as X, ComponentKindBrand as Y, ContextMenuNamesBrand as Z, RequirePermissions as _, ScopeId as _t, Subscribe as a, InjectedContextMenuMap as at, RequireRoleOptions as b, scopeIdsOf as bt, PageCursor as c, CommandRef as ct, AutocompleteOptions as d, commandRefOf as dt, CommandInfo as et, ChoiceValueOf as f, indexById as ft, RequireBotPermissions as g, GLOBAL_ID as gt, ResolvedEmoji as h, DeployResult as ht, WebhookFile as i, InjectedCommandMap as it, GateContextBase as j, OptionKind as jt, EffectGate as k, RowType as kt, pageCursor as l, LinkedCommands as lt, FocusedField as m, CommandBuilder as mt, WebhookLog as n, ContextMenuInfo as nt, SubscribeOptions as o, installLinks as ot, EntryFor as p, linkCommands as pt, AutocompleteRouteBrand as q, WebhookReport as r, ContextMenus as rt, PAGE_MAX as s, warnLinks as st, WebhookUrl as t, Commands as tt, AutocompletableNames as u, commandKey as ut, RequirePermissionsOptions as v, commandsRoute as vt, pickNotice as w, GuildTarget as wt, PermissionNames as x, skippedNote as xt, RequireRole as y, globalFirst as yt, EventDispatchResult as z, NamesFor as zt };
1040
- //# sourceMappingURL=index-Du71Xx69.d.mts.map
953
+ export { ContextMenuInfo as $, RequiredOf as A, SlashOption as At, InteractionMiddlewareMetadata as B, JoinNames as C, BuilderComponent as Ct, Gate as D, CacheFor as Dt, EffectGate as E, RowType as Et, DispatchResult as F, MessageContextMenuRegistry as Ft, ComponentDefsBrand as G, RegisterInteractionMiddleware as H, EventDispatchResult as I, NamesFor as It, ContextMenuNamesBrand as J, ComponentKindBrand as K, HandlerOutcome as L, UserContextMenuRegistry as Lt, defineGate as M, SlashRouteEntry as Mt, SerializedReply as N, ContextMenuKind as Nt, GateContextBase as O, OptionKind as Ot, serializeReply as P, MenuCacheFor as Pt, Commands as Q, HandlerResult as R, IntersectRequired as S, RegisterCommand as St, AppPermissionsContext as T, BuilderType as Tt, interactionMiddlewareMetaOf as U, InteractionMiddlewareOptions as V, AutocompleteRouteBrand as W, SlashRouteBrand as X, MiddlewareKindsBrand as Y, CommandInfo as Z, ChoiceValueOf as _, scopeIdsOf as _t, Subscribe as a, CommandRef as at, ResolvedEmoji as b, CommandMeta as bt, CaseReturn as c, commandRefOf as ct, runCase as d, CommandBuilder as dt, ContextMenus as et, PAGE_MAX as f, DeployResult as ft, AutocompleteOptions as g, globalFirst as gt, AutocompletableNames as h, commandsRoute as ht, WebhookFile as i, warnLinks as it, defineEffectGate as j, SlashRegistry as jt, GuildPermissionsContext as k, RouteCache as kt, MatchResult as l, indexById as lt, pageCursor as m, ScopeId as mt, WebhookLog as n, InjectedContextMenuMap as nt, SubscribeOptions as o, LinkedCommands as ot, PageCursor as p, GLOBAL_ID as pt, ContextMenuKindBrand as q, WebhookReport as r, installLinks as rt, Subscriber as s, commandKey as st, WebhookUrl as t, InjectedCommandMap as tt, OnlyCases as u, linkCommands as ut, EntryFor as v, skippedNote as vt, TwoOrMore as w, RowComponent as wt, GateFitsWith as x, GuildTarget as xt, FocusedField as y, resolveDeployTarget as yt, AnyMiddlewareCtor as z };
954
+ //# sourceMappingURL=index-DGHng1IN.d.mts.map