meocord 2.1.1 → 3.1.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.
Files changed (34) hide show
  1. package/AUTHOR.md +2 -3
  2. package/README.md +289 -41
  3. package/dist/cjs/_shared/controller.decorator-MUHA_A3z.cjs +529 -0
  4. package/dist/cjs/core/index.cjs +288 -85
  5. package/dist/cjs/decorator/index.cjs +5 -1
  6. package/dist/cjs/enum/index.cjs +17 -4
  7. package/dist/cjs/testing/index.cjs +88 -4
  8. package/dist/esm/bin/builder-template/builder/primary-entry-point.builder.template +26 -0
  9. package/dist/esm/bin/builder-template/controller/autocomplete.controller.template +16 -0
  10. package/dist/esm/bin/builder-template/controller/channel-select-menu.controller.template +12 -0
  11. package/dist/esm/bin/builder-template/controller/context-menu.controller.template +2 -2
  12. package/dist/esm/bin/builder-template/controller/mentionable-select-menu.controller.template +12 -0
  13. package/dist/esm/bin/builder-template/controller/modal-submit.controller.template +1 -1
  14. package/dist/esm/bin/builder-template/controller/primary-entry-point.controller.template +12 -0
  15. package/dist/esm/bin/builder-template/controller/role-select-menu.controller.template +12 -0
  16. package/dist/esm/bin/builder-template/controller/slash.controller.template +1 -1
  17. package/dist/esm/bin/builder-template/controller/user-select-menu.controller.template +12 -0
  18. package/dist/esm/bin/generator.js +4 -9
  19. package/dist/esm/bin/helper/controller-generator.helper.js +24 -7
  20. package/dist/esm/core/meocord.app.js +289 -85
  21. package/dist/esm/decorator/controller.decorator.js +157 -24
  22. package/dist/esm/decorator/index.js +1 -1
  23. package/dist/esm/enum/controller.enum.js +23 -4
  24. package/dist/esm/testing/mock-interaction.js +89 -5
  25. package/dist/esm/util/interaction.util.js +174 -0
  26. package/dist/types/controller.enum-DYfhYaat.d.ts +36 -0
  27. package/dist/types/core/index.d.ts +94 -0
  28. package/dist/types/decorator/index.d.ts +74 -41
  29. package/dist/types/enum/index.d.ts +1 -1
  30. package/dist/types/interface/index.d.ts +87 -3
  31. package/dist/types/testing/index.d.ts +3 -21
  32. package/package.json +2 -2
  33. package/dist/cjs/_shared/controller.decorator-DX5lFlPZ.cjs +0 -216
  34. package/dist/types/controller.enum-QA-IuReF.d.ts +0 -18
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The kinds of interaction a `@Command` method can be bound to.
3
+ *
4
+ * Each member names one Discord interaction shape rather than a family of them, so a
5
+ * handler's parameter type follows from its command type alone. That is why the four
6
+ * entity select menus are separate members instead of one `SELECT_MENU`: Discord sends
7
+ * them as distinct component types (5-8) carrying different resolved data, and
8
+ * collapsing them would leave the handler with a union it has to re-narrow by hand.
9
+ */
10
+ declare enum CommandType {
11
+ /** Chat input command, or one subcommand of it. */
12
+ SLASH = "SLASH",
13
+ /** User or message context menu command. */
14
+ CONTEXT_MENU = "CONTEXT_MENU",
15
+ /** Activity launch command (`ApplicationCommandType.PrimaryEntryPoint`). */
16
+ PRIMARY_ENTRY_POINT = "PRIMARY_ENTRY_POINT",
17
+ BUTTON = "BUTTON",
18
+ /** String select menu — the one whose options the application defines itself. */
19
+ SELECT_MENU = "SELECT_MENU",
20
+ USER_SELECT_MENU = "USER_SELECT_MENU",
21
+ ROLE_SELECT_MENU = "ROLE_SELECT_MENU",
22
+ MENTIONABLE_SELECT_MENU = "MENTIONABLE_SELECT_MENU",
23
+ CHANNEL_SELECT_MENU = "CHANNEL_SELECT_MENU",
24
+ MODAL_SUBMIT = "MODAL_SUBMIT"
25
+ }
26
+ /**
27
+ * Enum representing actions that can be performed on a message reaction.
28
+ */
29
+ declare enum ReactionHandlerAction {
30
+ /** Reaction added to a message. */
31
+ ADD = "ADD",
32
+ /** Reaction removed from a message. */
33
+ REMOVE = "REMOVE"
34
+ }
35
+
36
+ export { CommandType as C, ReactionHandlerAction as R };
@@ -19,10 +19,104 @@ declare class MeoCordApp {
19
19
  private activityInterval;
20
20
  private controllerInstancesCache;
21
21
  constructor(controllerClasses: (new (...args: any[]) => any)[], container: Container, discordClient: Client, discordToken: string, activities?: ActivityOptions[] | undefined);
22
+ /**
23
+ * Runs an event handler so a failure inside it cannot take the process down.
24
+ *
25
+ * discord.js calls listeners without awaiting them, so a rejection escaping one has
26
+ * nothing left to settle it: Node reports an unhandled rejection, which terminates
27
+ * the process by default. Losing the whole bot because one reaction landed on a
28
+ * deleted message, or one controller could not be resolved, is a worse failure than
29
+ * the one that caused it -- every other user is served by the same process.
30
+ *
31
+ * Nothing is silenced. The error is logged against the event that produced it, so a
32
+ * genuine misconfiguration -- an unbound controller, a missing dependency -- shows
33
+ * up on the very first interaction rather than staying hidden.
34
+ *
35
+ * @param event - The gateway event being handled, named in the log.
36
+ * @param run - The handler to run.
37
+ */
38
+ private runListener;
39
+ /**
40
+ * Rotates the bot's activity.
41
+ *
42
+ * Guarded separately from {@link runListener}: this runs on a timer rather than an
43
+ * event, and a throw from a timer callback is an uncaught exception no listener
44
+ * wrapper can reach.
45
+ */
46
+ private updateActivity;
22
47
  private getInstance;
23
48
  start(): Promise<void>;
24
49
  registerCommands(): Promise<void>;
50
+ /**
51
+ * Every pattern-matched route, ordered most specific first.
52
+ *
53
+ * Built once and cached, so dispatch stays a single ordered walk with an early exit
54
+ * rather than paying to rank anything per interaction. Ordering here is what lets
55
+ * `gi-profile-summary-{ownerId}-{uid}` keep the ids it owns when
56
+ * `gi-profile-{uuid}-{uid}` would also match them — without it, the winner would be
57
+ * whichever controller happened to be registered first.
58
+ */
59
+ private componentRoutes?;
60
+ private getComponentRoutes;
61
+ /**
62
+ * Warns rather than throws: an app whose patterns overlap boots and works today, and
63
+ * refusing to start would turn a latent mis-route into an outage on upgrade.
64
+ */
65
+ private reportAmbiguousRoutes;
66
+ /**
67
+ * Every `@Autocomplete` handler, ordered so an option-specific handler is found
68
+ * before a command-wide one.
69
+ *
70
+ * Cached alongside the component table: autocomplete fires on every keystroke, and
71
+ * rebuilding the list per keystroke would put reflection on the hottest path the
72
+ * framework has.
73
+ */
74
+ private autocompleteRoutes?;
75
+ private getAutocompleteRoutes;
76
+ /**
77
+ * Dispatches an interaction, and makes sure a failure anywhere in that still reaches
78
+ * the person who triggered it.
79
+ *
80
+ * {@link executeCommand} already reports what a handler throws, but everything
81
+ * *before* the handler can fail too — resolving a controller through the container
82
+ * is the common case — and a component that fails there would otherwise look dead
83
+ * with nothing said to the user and nothing in the log.
84
+ */
25
85
  private handleInteraction;
86
+ private dispatchInteraction;
87
+ /**
88
+ * The names a command interaction can be handled under, most specific first.
89
+ *
90
+ * Empty for anything that is not a registered command, which is how a component
91
+ * whose customId matched no pattern falls through to the unmatched warning instead
92
+ * of being looked up under a name it does not have.
93
+ */
94
+ private resolveNameRoutes;
95
+ /**
96
+ * Answers an autocomplete interaction from the `@Autocomplete` handler that claims it.
97
+ *
98
+ * Discord closes the window after three seconds and shows a loading state until
99
+ * something arrives, so an unclaimed option is answered with an empty list rather
100
+ * than left to time out -- a visibly empty menu is a better failure than a stuck one,
101
+ * and the warning says which option is missing a handler.
102
+ */
103
+ private handleAutocomplete;
104
+ /** Closes an autocomplete window that nothing else answered. */
105
+ private respondEmpty;
106
+ /**
107
+ * Runs a resolved command, shared by both dispatch paths so a pattern-matched
108
+ * component and a named slash command behave identically once the route is chosen.
109
+ */
110
+ private executeCommand;
111
+ /**
112
+ * Tells the user something went wrong, if the interaction can still hear it.
113
+ *
114
+ * A handler that replies and *then* throws is the common shape of a failure, and
115
+ * replying twice throws in turn -- out of the catch block, where nothing is left to
116
+ * handle it. Whatever the interaction's state, reporting an error must not be able
117
+ * to become a second, worse one.
118
+ */
119
+ private replyWithError;
26
120
  private handleMessage;
27
121
  private handleReaction;
28
122
  private gracefulShutdown;
@@ -1,6 +1,6 @@
1
+ import { BuildableCommandType, CommandBuilderBase, CommandBuilderConstructor, CommandInteractionType, ReactionHandlerOptions, AutocompleteMetadata, CommandMetadata, GuardInterface } from '../interface/index.js';
2
+ import { AutocompleteInteraction, OmitPartialGroupDMChannel, Message, MessageReaction, PartialMessageReaction, ClientOptions, ActivityOptions } from 'discord.js';
1
3
  import { CommandType } from '../enum/index.js';
2
- import { SlashCommandBuilder, SlashCommandSubcommandsOnlyBuilder, ContextMenuCommandBuilder, ButtonInteraction, StringSelectMenuInteraction, ChatInputCommandInteraction, UserContextMenuCommandInteraction, MessageContextMenuCommandInteraction, ModalSubmitInteraction, OmitPartialGroupDMChannel, Message, MessageReaction, PartialMessageReaction, ClientOptions, ActivityOptions } from 'discord.js';
3
- import { ReactionHandlerOptions, GuardInterface } from '../interface/index.js';
4
4
  import { ServiceIdentifier } from 'inversify';
5
5
  import 'webpack';
6
6
 
@@ -29,37 +29,6 @@ import 'webpack';
29
29
  */
30
30
  declare function Service<T>(): (target: new (...args: any[]) => T) => void;
31
31
 
32
- /**
33
- * MeoCord Framework
34
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
35
- * SPDX-License-Identifier: MIT
36
- */
37
-
38
- /**
39
- * Base interface for a command builder.
40
- */
41
- interface CommandBuilderBase<T extends CommandType.SLASH | CommandType.CONTEXT_MENU = CommandType.SLASH | CommandType.CONTEXT_MENU> {
42
- /**
43
- * Builds the command structure using the specified command name.
44
- *
45
- * @param commandName - The name of the command.
46
- * @returns A SlashCommandBuilder or ContextMenuCommandBuilder instance.
47
- */
48
- build: (commandName: string) => T extends CommandType.SLASH ? SlashCommandBuilder | SlashCommandSubcommandsOnlyBuilder : T extends CommandType.CONTEXT_MENU ? ContextMenuCommandBuilder : never;
49
- }
50
- type CommandBuilderConstructor<T extends CommandType.SLASH | CommandType.CONTEXT_MENU> = new () => CommandBuilderBase<T>;
51
- /**
52
- * Command metadata describing a registered command method.
53
- */
54
- interface CommandMetadata<T extends string = string> {
55
- methodName: string;
56
- builder: ReturnType<CommandBuilderBase['build']> | undefined;
57
- type: CommandType;
58
- regex?: RegExp;
59
- dynamicParams?: T[];
60
- }
61
- type CommandInteractionType<CBC extends CommandType.SLASH | CommandType.CONTEXT_MENU, T extends CommandBuilderConstructor<CBC> | CommandType> = T extends CommandType.BUTTON ? ButtonInteraction : T extends CommandType.SELECT_MENU ? StringSelectMenuInteraction : T extends CommandBuilderConstructor<CommandType.SLASH> ? ChatInputCommandInteraction : T extends CommandBuilderConstructor<CommandType.CONTEXT_MENU> ? UserContextMenuCommandInteraction | MessageContextMenuCommandInteraction : T extends CommandType.MODAL_SUBMIT ? ModalSubmitInteraction : never;
62
-
63
32
  /**
64
33
  * MeoCord Framework
65
34
  * Copyright (c) 2025 Ukasyah Rahmatullah Zada
@@ -84,7 +53,7 @@ type CommandInteractionType<CBC extends CommandType.SLASH | CommandType.CONTEXT_
84
53
  * @returns A decorator function that makes the target class injectable
85
54
  * and assigns the `commandType` metadata.
86
55
  */
87
- declare function CommandBuilder<T extends CommandType.SLASH | CommandType.CONTEXT_MENU>(commandType: T): (target: new () => CommandBuilderBase<T>) => void;
56
+ declare function CommandBuilder<T extends BuildableCommandType>(commandType: T): (target: new () => CommandBuilderBase<T>) => void;
88
57
 
89
58
  /**
90
59
  * MeoCord Framework
@@ -150,11 +119,18 @@ declare function getMessageHandlers(controller: any): {
150
119
  keyword: string | undefined;
151
120
  method: string;
152
121
  }[];
122
+ /** The character a parameter will not cross, so one pattern segment maps to one value. */
123
+ declare const PARAM_SEPARATOR = "/";
153
124
  /**
154
125
  * Decorator to register command methods in a controller.
155
126
  *
156
- * @param commandName - The name or pattern of the command.
157
- * @param builderOrType - A command builder class or a command type from `CommandType`.
127
+ * @param commandName - What the command is addressed by. Commands registered with
128
+ * Discord use their name, and a subcommand its full path — `settings notify email`,
129
+ * parts separated by a space, the way Discord displays it. Components use a customId
130
+ * pattern, where `{name}` captures one `/`-separated segment.
131
+ * @param builderOrType - A command builder class, or a `CommandType` for a handler that
132
+ * registers nothing of its own: every component, and every subcommand of a command
133
+ * whose builder already describes it.
158
134
  *
159
135
  * @example
160
136
  * ```typescript
@@ -163,13 +139,23 @@ declare function getMessageHandlers(controller: any): {
163
139
  * await interaction.reply('This is the help command!')
164
140
  * }
165
141
  *
166
- * @Command('stats-{id}', CommandType.BUTTON)
167
- * public async handleStats(message: ButtonInteraction, { id }) {
168
- * await message.reply(`Fetching stats for ID: ${id}`);
142
+ * @Command('settings notify email', CommandType.SLASH)
143
+ * public async handleNotifyEmail(interaction: ChatInputCommandInteraction, { enabled }) {
144
+ * await interaction.reply(`Email notifications ${enabled ? 'on' : 'off'}`)
145
+ * }
146
+ *
147
+ * @Command('stats/{id}', CommandType.BUTTON)
148
+ * public async handleStats(interaction: ButtonInteraction, { id }) {
149
+ * await interaction.reply(`Fetching stats for ID: ${id}`);
150
+ * }
151
+ *
152
+ * @Command('assign/{taskId}', CommandType.USER_SELECT_MENU)
153
+ * public async handleAssign(interaction: UserSelectMenuInteraction, { taskId }) {
154
+ * await interaction.reply(`Assigned ${interaction.users.size} user(s) to ${taskId}`)
169
155
  * }
170
156
  * ```
171
157
  */
172
- declare function Command<CBC extends CommandType.SLASH | CommandType.CONTEXT_MENU, T extends CommandBuilderConstructor<CBC> | CommandType>(commandName: string, builderOrType: T): <P extends Record<string, any>, R extends Promise<void> | void>(target: object, propertyKey: string, _descriptor: TypedPropertyDescriptor<(interaction: CommandInteractionType<CBC, T>, params: P) => R> | TypedPropertyDescriptor<(interaction: CommandInteractionType<CBC, T>) => R>) => void;
158
+ declare function Command<CBC extends BuildableCommandType, T extends CommandBuilderConstructor<CBC> | CommandType>(commandName: string, builderOrType: T): <P extends Record<string, any>, R extends Promise<void> | void>(target: object, propertyKey: string, _descriptor: TypedPropertyDescriptor<(interaction: CommandInteractionType<CBC, T>, params: P) => R> | TypedPropertyDescriptor<(interaction: CommandInteractionType<CBC, T>) => R>) => void;
173
159
  /**
174
160
  * Retrieves the command map for a given controller.
175
161
  *
@@ -177,6 +163,41 @@ declare function Command<CBC extends CommandType.SLASH | CommandType.CONTEXT_MEN
177
163
  * @returns A record containing command metadata indexed by command names.
178
164
  */
179
165
  declare function getCommandMap<T extends string>(controller: any): Record<string, CommandMetadata<T>[]>;
166
+ /**
167
+ * Decorator to register an autocomplete handler for a chat input command's option.
168
+ *
169
+ * Autocomplete is a separate interaction from the command it belongs to, and Discord
170
+ * sends it while the user is still typing. It is not a `@Command`: nothing is
171
+ * registered for it — the option's own `setAutocomplete(true)` is what turns it on —
172
+ * and it is answered with `interaction.respond()` rather than a reply. Leaving it
173
+ * unhandled is not silent to the user: the client shows a loading state until the
174
+ * three-second window closes.
175
+ *
176
+ * @param commandPath - The command to complete, e.g. `settings` or `settings notify email`
177
+ * for a subcommand. Parts are separated by a single space, as Discord displays them.
178
+ * @param optionName - The option to complete. Omit to handle every option of the command,
179
+ * branching on `interaction.options.getFocused(true)`.
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * @Autocomplete('search', 'query')
184
+ * async completeQuery(interaction: AutocompleteInteraction) {
185
+ * const { value } = interaction.options.getFocused(true)
186
+ * await interaction.respond(this.search(value).map(name => ({ name, value: name })))
187
+ * }
188
+ * ```
189
+ */
190
+ declare function Autocomplete<R extends void | Promise<void>>(commandPath: string, optionName?: string): <P extends Record<string, any>>(target: object, propertyKey: string, _descriptor: TypedPropertyDescriptor<(interaction: AutocompleteInteraction, params: P) => R> | TypedPropertyDescriptor<(interaction: AutocompleteInteraction) => R>) => void;
191
+ /**
192
+ * Retrieves autocomplete handler metadata from a given controller.
193
+ *
194
+ * Handlers naming an option come first, so a command-wide handler acts as the fallback
195
+ * for options no specific handler claimed rather than shadowing them by declaration order.
196
+ *
197
+ * @param controller - The controller class instance.
198
+ * @returns The registered autocomplete handlers, most specific first.
199
+ */
200
+ declare function getAutocompleteHandlers(controller: any): AutocompleteMetadata[];
180
201
  /**
181
202
  * Decorator to mark a class as a controller that can later be registered to the App class `(app.ts)` using the `@MeoCord` decorator.
182
203
  *
@@ -195,6 +216,18 @@ declare function getCommandMap<T extends string>(controller: any): Record<string
195
216
  * ```
196
217
  */
197
218
  declare function Controller(): (target: any) => void;
219
+ /**
220
+ * Finds pattern pairs that can both match one customId.
221
+ *
222
+ * Patterns of different segment counts are disjoint, because a parameter cannot cross
223
+ * `/`. Within the same count, two patterns overlap unless some position holds literals
224
+ * that differ: `a/{x}/c` and `a/b/{y}` both take `a/b/c`, and neither is more literal
225
+ * than the other, so ranking cannot settle it either.
226
+ *
227
+ * @param patterns - The registered patterns.
228
+ * @returns Each ambiguous pair, once, in the order the patterns were given.
229
+ */
230
+ declare function findAmbiguousRoutes(patterns: string[]): [string, string][];
198
231
 
199
232
  /**
200
233
  * MeoCord Framework
@@ -334,4 +367,4 @@ declare function MeoCord(options: {
334
367
  services?: ServiceIdentifier[];
335
368
  }): (target: any) => void;
336
369
 
337
- export { Command, CommandBuilder, Controller, Guard, MeoCord, MessageHandler, ReactionHandler, Service, UseGuard, getCommandMap, getMessageHandlers, getReactionHandlers };
370
+ export { Autocomplete, Command, CommandBuilder, Controller, Guard, MeoCord, MessageHandler, PARAM_SEPARATOR, ReactionHandler, Service, UseGuard, findAmbiguousRoutes, getAutocompleteHandlers, getCommandMap, getMessageHandlers, getReactionHandlers };
@@ -1,4 +1,4 @@
1
- export { C as CommandType, R as ReactionHandlerAction } from '../controller.enum-QA-IuReF.js';
1
+ export { C as CommandType, R as ReactionHandlerAction } from '../controller.enum-DYfhYaat.js';
2
2
 
3
3
  /**
4
4
  * MeoCord Framework
@@ -1,6 +1,90 @@
1
- import { User, PartialUser, BaseInteraction, Message, MessageReaction } from 'discord.js';
1
+ import { SlashCommandBuilder, SlashCommandSubcommandsOnlyBuilder, ContextMenuCommandBuilder, RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody, ChatInputCommandInteraction, UserContextMenuCommandInteraction, MessageContextMenuCommandInteraction, PrimaryEntryPointCommandInteraction, ButtonInteraction, StringSelectMenuInteraction, UserSelectMenuInteraction, RoleSelectMenuInteraction, MentionableSelectMenuInteraction, ChannelSelectMenuInteraction, ModalSubmitInteraction, User, PartialUser, BaseInteraction, Message, MessageReaction } from 'discord.js';
2
2
  import { Configuration } from 'webpack';
3
- import { ReactionHandlerAction } from '../enum/index.js';
3
+ import { CommandType, ReactionHandlerAction } from '../enum/index.js';
4
+
5
+ /**
6
+ * MeoCord Framework
7
+ * Copyright (c) 2025 Ukasyah Rahmatullah Zada
8
+ * SPDX-License-Identifier: MIT
9
+ */
10
+
11
+ /**
12
+ * The command types that are registered with Discord, and so need a builder.
13
+ *
14
+ * Components are addressed by a customId the application makes up, so they have
15
+ * nothing to register; these three are published through `application.commands.set`.
16
+ */
17
+ type BuildableCommandType = CommandType.SLASH | CommandType.CONTEXT_MENU | CommandType.PRIMARY_ENTRY_POINT;
18
+ /**
19
+ * The body an entry point command is registered with.
20
+ *
21
+ * `description` is added back on top of the discord-api-types body: that type omits it
22
+ * from every non-chat-input command, but the API accepts one for entry point commands
23
+ * — Discord's own default activity command ships with it set.
24
+ */
25
+ type PrimaryEntryPointCommandData = RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody & {
26
+ description?: string;
27
+ };
28
+ /**
29
+ * The payload `build()` returns for a given command type.
30
+ *
31
+ * `PRIMARY_ENTRY_POINT` yields a raw REST body rather than a builder because
32
+ * `@discordjs/builders` ships none for it; `ApplicationCommandManager#set` accepts the
33
+ * JSON body directly, so nothing is lost.
34
+ */
35
+ type CommandBuildResult<T extends BuildableCommandType> = T extends CommandType.SLASH ? SlashCommandBuilder | SlashCommandSubcommandsOnlyBuilder : T extends CommandType.CONTEXT_MENU ? ContextMenuCommandBuilder : T extends CommandType.PRIMARY_ENTRY_POINT ? PrimaryEntryPointCommandData : never;
36
+ /**
37
+ * Base interface for a command builder.
38
+ */
39
+ interface CommandBuilderBase<T extends BuildableCommandType = BuildableCommandType> {
40
+ /**
41
+ * Builds the command structure using the specified command name.
42
+ *
43
+ * @param commandName - The name of the command.
44
+ * @returns A builder instance, or a raw command body for entry point commands.
45
+ */
46
+ build: (commandName: string) => CommandBuildResult<T>;
47
+ }
48
+ type CommandBuilderConstructor<T extends BuildableCommandType> = new () => CommandBuilderBase<T>;
49
+ /**
50
+ * Command metadata describing a registered command method.
51
+ */
52
+ interface CommandMetadata<T extends string = string> {
53
+ methodName: string;
54
+ builder: ReturnType<CommandBuilderBase['build']> | undefined;
55
+ type: CommandType;
56
+ regex?: RegExp;
57
+ dynamicParams?: T[];
58
+ /**
59
+ * How specific this pattern is; higher wins when more than one route matches the
60
+ * same customId. A greedy parameter can overlap a more literal sibling —
61
+ * `gi-profile-{uuid:*}-{uid}` also matches an id meant for
62
+ * `gi-profile-summary-{ownerId}-{uid}` — so dispatch cannot rely on declaration order.
63
+ */
64
+ specificity?: number;
65
+ }
66
+ /** Metadata describing one `@Autocomplete` handler. */
67
+ interface AutocompleteMetadata {
68
+ /** The command path the handler serves, e.g. `settings` or `settings notify email`. */
69
+ commandPath: string;
70
+ /** The option it completes, or `undefined` to complete every option of that command. */
71
+ optionName?: string;
72
+ methodName: string;
73
+ }
74
+ /** The interaction class each non-buildable command type hands to its handler. */
75
+ interface ComponentInteractionMap {
76
+ [CommandType.BUTTON]: ButtonInteraction;
77
+ [CommandType.SELECT_MENU]: StringSelectMenuInteraction;
78
+ [CommandType.USER_SELECT_MENU]: UserSelectMenuInteraction;
79
+ [CommandType.ROLE_SELECT_MENU]: RoleSelectMenuInteraction;
80
+ [CommandType.MENTIONABLE_SELECT_MENU]: MentionableSelectMenuInteraction;
81
+ [CommandType.CHANNEL_SELECT_MENU]: ChannelSelectMenuInteraction;
82
+ [CommandType.MODAL_SUBMIT]: ModalSubmitInteraction;
83
+ [CommandType.SLASH]: ChatInputCommandInteraction;
84
+ [CommandType.CONTEXT_MENU]: UserContextMenuCommandInteraction | MessageContextMenuCommandInteraction;
85
+ [CommandType.PRIMARY_ENTRY_POINT]: PrimaryEntryPointCommandInteraction;
86
+ }
87
+ type CommandInteractionType<CBC extends BuildableCommandType, T extends CommandBuilderConstructor<CBC> | CommandType> = T extends CommandBuilderConstructor<CommandType.SLASH> ? ChatInputCommandInteraction : T extends CommandBuilderConstructor<CommandType.CONTEXT_MENU> ? UserContextMenuCommandInteraction | MessageContextMenuCommandInteraction : T extends CommandBuilderConstructor<CommandType.PRIMARY_ENTRY_POINT> ? PrimaryEntryPointCommandInteraction : T extends keyof ComponentInteractionMap ? ComponentInteractionMap[T] : never;
4
88
 
5
89
  /**
6
90
  * MeoCord Framework
@@ -92,4 +176,4 @@ interface MeoCordConfig {
92
176
  webpack?: (config: MeoCordWebpackConfig) => MeoCordWebpackConfig | undefined;
93
177
  }
94
178
 
95
- export type { GuardInterface, MeoCordConfig, MeoCordWebpackConfig, ReactionHandlerOptions };
179
+ export type { AutocompleteMetadata, BuildableCommandType, CommandBuildResult, CommandBuilderBase, CommandBuilderConstructor, CommandInteractionType, CommandMetadata, GuardInterface, MeoCordConfig, MeoCordWebpackConfig, ReactionHandlerOptions };
@@ -2,7 +2,7 @@ import { ServiceIdentifier, Container } from 'inversify';
2
2
  import { GuardInterface } from '../interface/index.js';
3
3
  import { CacheType, CommandInteractionOptionResolver, Channel, Client, Guild, Message, User } from 'discord.js';
4
4
  import 'webpack';
5
- import '../controller.enum-QA-IuReF.js';
5
+ import '../controller.enum-DYfhYaat.js';
6
6
 
7
7
  /**
8
8
  * MeoCord Framework
@@ -277,30 +277,12 @@ declare function createMockMessage(): DeepMocked<Message>;
277
277
  interface ChatInputOptions {
278
278
  subcommandGroup?: string | null;
279
279
  subcommand?: string | null;
280
+ /** The option the user is currently typing, for autocomplete interactions. */
281
+ focused?: string | null;
280
282
  [name: string]: string | number | boolean | {
281
283
  id: string;
282
284
  } | null | undefined;
283
285
  }
284
- /**
285
- * Builds a typed options resolver from a plain record. Mirrors how the real
286
- * `CommandInteractionOptionResolver` works: declare what options the command
287
- * was invoked with, and the resolver finds them by name.
288
- *
289
- * All explicit methods are mock functions — override per test with `.mockReturnValue()`.
290
- * Methods not listed (e.g. `getAttachment`) are auto-stubbed by the Proxy.
291
- *
292
- * @example
293
- * ```ts
294
- * const interaction = createMockInteraction(ChatInputCommandInteraction)
295
- * interaction.options = createChatInputOptions({
296
- * subcommandGroup: 'daily',
297
- * subcommand: 'notes',
298
- * uid: 12345678,
299
- * })
300
- * interaction.options.getSubcommand() // → 'notes'
301
- * interaction.options.getNumber('uid') // → 12345678
302
- * ```
303
- */
304
286
  declare function createChatInputOptions<Cached extends CacheType = any>(opts?: ChatInputOptions): DeepMocked<CommandInteractionOptionResolver<Cached>>;
305
287
 
306
288
  export { MeoCordTestingModule, TestingModule, TestingModuleBuilder, createChatInputOptions, createMock, createMockChannel, createMockClient, createMockFn, createMockGuild, createMockInteraction, createMockMessage, createMockUser, isMockFunction };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "meocord",
3
3
  "description": "Decorator-based Discord bot framework built on discord.js. Brings NestJS-style controllers, dependency injection, guards, and testing utilities to bot development — with a full CLI and TypeScript-first design.",
4
- "version": "2.1.1",
4
+ "version": "3.1.0",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=22"
8
8
  },
9
9
  "scripts": {
10
- "lint": "eslint --fix . && tsc --noEmit && tsc --noEmit --project tsconfig.test.json",
10
+ "lint": "prettier --write \"**/*.md\" && eslint --fix . && tsc --noEmit && tsc --noEmit --project tsconfig.test.json",
11
11
  "build": "rm -rf ./dist && rollup -c",
12
12
  "prepare": "husky",
13
13
  "test": "vitest run",