commandkit 0.1.6-dev.20231124165615 → 0.1.6-dev.20231125121601

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/README.md CHANGED
@@ -19,7 +19,7 @@ CommandKit is a library that makes it easy to handle commands and events in your
19
19
  - Automatic command registration, edits, and deletion 🤖
20
20
  - Supports multiple development servers 🤝
21
21
  - Supports multiple users as bot developers 👥
22
- - Object oriented 💻
22
+ - User friendly CLI 🖥️
23
23
 
24
24
  ## Documentation
25
25
 
package/bin/build.mjs CHANGED
@@ -39,7 +39,7 @@ export async function bootstrapProductionBuild(config) {
39
39
  entry: [src, '!dist', '!.commandkit', `!${outDir}`],
40
40
  });
41
41
 
42
- if (antiCrash) await injectAntiCrash(outDir, main);
42
+ await injectShims(outDir, main, antiCrash);
43
43
 
44
44
  status.succeed(
45
45
  Colors.green(`Build completed in ${(performance.now() - start).toFixed(2)}ms!`),
@@ -55,27 +55,32 @@ export async function bootstrapProductionBuild(config) {
55
55
  }
56
56
  }
57
57
 
58
- function injectAntiCrash(outDir, main) {
58
+ async function injectShims(outDir, main, antiCrash) {
59
59
  const path = join(process.cwd(), outDir, main);
60
- const snippet = [
61
- '\n\n// --- CommandKit Anti-Crash Monitor ---',
62
- ';(()=>{',
63
- " 'use strict';",
64
- " // 'uncaughtException' event is supposed to be used to perform synchronous cleanup before shutting down the process",
65
- ' // instead of using it as a means to resume operation.',
66
- ' // But it exists here due to compatibility reasons with discord bot ecosystem.',
67
- " const p = (t) => `\\x1b[33m${t}\\x1b[0m`, b = '[CommandKit Anti-Crash Monitor]', l = console.log, e1 = 'uncaughtException', e2 = 'unhandledRejection';",
68
- ' if (!process.eventNames().includes(e1)) // skip if it is already handled',
69
- ' process.on(e1, (e, o) => {',
70
- ' l(p(`${b} Uncaught Exception`)); l(p(b), p(e.stack || e));',
71
- ' })',
72
- ' if (!process.eventNames().includes(e2)) // skip if it is already handled',
73
- ' process.on(e2, (r) => {',
74
- ' l(p(`${b} Unhandled promise rejection`)); l(p(`${b} ${r.stack || r}`));',
75
- ' });',
76
- '})();',
77
- '// --- CommandKit Anti-Crash Monitor ---\n',
78
- ].join('\n');
79
60
 
80
- return appendFile(path, snippet);
61
+ const antiCrashScript = antiCrash
62
+ ? [
63
+ '\n\n// --- CommandKit Anti-Crash Monitor ---',
64
+ ';(()=>{',
65
+ " 'use strict';",
66
+ " // 'uncaughtException' event is supposed to be used to perform synchronous cleanup before shutting down the process",
67
+ ' // instead of using it as a means to resume operation.',
68
+ ' // But it exists here due to compatibility reasons with discord bot ecosystem.',
69
+ " const p = (t) => `\\x1b[33m${t}\\x1b[0m`, b = '[CommandKit Anti-Crash Monitor]', l = console.log, e1 = 'uncaughtException', e2 = 'unhandledRejection';",
70
+ ' if (!process.eventNames().includes(e1)) // skip if it is already handled',
71
+ ' process.on(e1, (e) => {',
72
+ ' l(p(`${b} Uncaught Exception`)); l(p(b), p(e.stack || e));',
73
+ ' })',
74
+ ' if (!process.eventNames().includes(e2)) // skip if it is already handled',
75
+ ' process.on(e2, (r) => {',
76
+ ' l(p(`${b} Unhandled promise rejection`)); l(p(`${b} ${r.stack || r}`));',
77
+ ' });',
78
+ '})();',
79
+ '// --- CommandKit Anti-Crash Monitor ---\n',
80
+ ].join('\n')
81
+ : '';
82
+
83
+ const finalScript = [antiCrashScript].join('\n');
84
+
85
+ return appendFile(path, finalScript);
81
86
  }
package/bin/parse-env.mjs CHANGED
@@ -15,6 +15,15 @@ const VALUE_PREFIXES = {
15
15
  DATE: 'DATE::',
16
16
  };
17
17
 
18
+ function catcher(fn) {
19
+ try {
20
+ fn();
21
+ return true;
22
+ } catch {
23
+ return false;
24
+ }
25
+ }
26
+
18
27
  export function parseEnv(src) {
19
28
  for (const key in src) {
20
29
  const value = src[key];
@@ -22,7 +31,7 @@ export function parseEnv(src) {
22
31
  if (typeof value !== 'string') continue;
23
32
 
24
33
  if (value.startsWith(VALUE_PREFIXES.JSON)) {
25
- src[key] = JSON.parse(value.replace(VALUE_PREFIXES.JSON, ''));
34
+ catcher(() => (src[key] = JSON.parse(value.replace(VALUE_PREFIXES.JSON, ''))));
26
35
  continue;
27
36
  }
28
37
 
package/dist/index.d.mts CHANGED
@@ -1,202 +1,178 @@
1
- import { CommandInteraction, Client, ChatInputCommandInteraction, ContextMenuCommandInteraction, PermissionsString, APIApplicationCommandOption, ButtonInteraction, Awaitable, Message, InteractionCollectorOptions, ButtonBuilder } from 'discord.js';
1
+ import { Client, ChatInputCommandInteraction, ContextMenuCommandInteraction, UserContextMenuCommandInteraction, MessageContextMenuCommandInteraction, AutocompleteInteraction, PermissionsString, RESTPostAPIApplicationCommandsJSONBody, ButtonInteraction, Awaitable, Message, InteractionCollectorOptions, ButtonBuilder } from 'discord.js';
2
2
 
3
3
  /**
4
- * Base props for commands.
4
+ * Options for instantiating a CommandKit handler.
5
5
  */
6
- interface CommandProps {
6
+ interface CommandKitOptions {
7
7
  /**
8
- * Represents the command's interaction.
8
+ * The Discord.js client object to use with CommandKit.
9
9
  */
10
- interaction: CommandInteraction;
10
+ client: Client;
11
11
  /**
12
- * The client created in your main file.
12
+ * The path to your commands directory.
13
13
  */
14
- client: Client<true>;
14
+ commandsPath?: string;
15
15
  /**
16
- * The main CommandKit handler that instantiated this.
16
+ * The path to your events directory.
17
17
  */
18
- handler: CommandKit;
18
+ eventsPath?: string;
19
+ /**
20
+ * The path to the validations directory.
21
+ */
22
+ validationsPath?: string;
23
+ /**
24
+ * List of development guild IDs to restrict devOnly commands to.
25
+ */
26
+ devGuildIds?: string[];
27
+ /**
28
+ * List of developer user IDs to restrict devOnly commands to.
29
+ */
30
+ devUserIds?: string[];
31
+ /**
32
+ * List of developer role IDs to restrict devOnly commands to.
33
+ */
34
+ devRoleIds?: string[];
35
+ /**
36
+ * Skip CommandKit's built-in validations (for devOnly commands).
37
+ */
38
+ skipBuiltInValidations?: boolean;
39
+ /**
40
+ * Bulk register application commands instead of one-by-one.
41
+ */
42
+ bulkRegister?: boolean;
19
43
  }
20
44
  /**
21
- * Props for slash command run functions.
45
+ * A reload type for commands.
46
+ */
47
+ type ReloadOptions = 'dev' | 'global' | ReloadType;
48
+
49
+ /**
50
+ * Props for command run functions.
22
51
  */
23
- interface SlashCommandProps {
52
+ interface CommandProps {
24
53
  /**
25
- * Represents the command's interaction.
54
+ * The current command interaction object.
26
55
  */
27
- interaction: ChatInputCommandInteraction;
56
+ interaction: ChatInputCommandInteraction | ContextMenuCommandInteraction | UserContextMenuCommandInteraction | MessageContextMenuCommandInteraction | AutocompleteInteraction;
28
57
  /**
29
- * The client created in your main file.
58
+ * The Discord.js client object that CommandKit is handling.
30
59
  */
31
60
  client: Client<true>;
32
61
  /**
33
- * The CommandKit handler that instantiated this.
62
+ * The current CommandKit handler instance.
34
63
  */
35
64
  handler: CommandKit;
36
65
  }
37
66
  /**
38
- * Props for context menu command run functions.
67
+ * Props for autocomplete command run functions.
39
68
  */
40
- interface ContextMenuCommandProps {
69
+ interface AutocompleteCommandProps extends CommandProps {
41
70
  /**
42
- * Represents the command's interaction.
71
+ * The current autocomplete command interaction object.
43
72
  */
44
- interaction: ContextMenuCommandInteraction;
73
+ interaction: AutocompleteInteraction;
74
+ }
75
+ /**
76
+ * Props for slash (chat input) command run functions.
77
+ */
78
+ interface SlashCommandProps extends CommandProps {
45
79
  /**
46
- * The client created in your main file.
80
+ * The current slash (chat input) command interaction object.
47
81
  */
48
- client: Client<true>;
82
+ interaction: ChatInputCommandInteraction;
83
+ }
84
+ /**
85
+ * Props for context menu command run functions.
86
+ */
87
+ interface ContextMenuCommandProps extends CommandProps {
49
88
  /**
50
- * The CommandKit handler that instantiated this.
89
+ * The current context menu command interaction object.
51
90
  */
52
- handler: CommandKit;
91
+ interaction: ContextMenuCommandInteraction;
92
+ }
93
+ /**
94
+ * Props for user context menu command run functions.
95
+ */
96
+ interface UserContextMenuCommandProps extends CommandProps {
97
+ interaction: UserContextMenuCommandInteraction;
53
98
  }
54
99
  /**
55
- * Props for command validations.
100
+ * Props for message context menu command run functions.
101
+ */
102
+ interface MessageContextMenuCommandProps extends CommandProps {
103
+ interaction: MessageContextMenuCommandInteraction;
104
+ }
105
+ /**
106
+ * Props for command validation functions.
56
107
  */
57
108
  interface ValidationFunctionProps {
58
109
  /**
59
- * Represents the command's interaction.
60
- * Either a slash command or a context menu command interaction.
110
+ * The current command interaction object.
61
111
  */
62
112
  interaction: ChatInputCommandInteraction | ContextMenuCommandInteraction;
63
113
  /**
64
- * The client created in your main file.
65
- * Represented as a ready client (`Client<true>`).
114
+ * The Discord.js client object that CommandKit is handling.
66
115
  */
67
116
  client: Client<true>;
68
117
  /**
69
- * The command object that is being validated.
118
+ * The current (local) target command object.
70
119
  */
71
120
  commandObj: CommandObject;
72
121
  /**
73
- * The CommandKit handler that instantiated this.
122
+ * The current CommandKit handler instance.
74
123
  */
75
124
  handler: CommandKit;
76
125
  }
77
126
  /**
78
- * Configuration for commands.
127
+ * Additional command configuration options.
79
128
  */
80
129
  interface CommandOptions {
81
130
  /**
82
- * Boolean indicating whether the command is guild-only.
83
- * @deprecated - Use `dm_permission` in the command's data instead.
131
+ * A boolean indicating whether the command is guild-only.
132
+ * Used for built-in validation.
133
+ *
134
+ * @deprecated Use `dm_permission` in the command's `data` object instead.
84
135
  */
85
136
  guildOnly?: boolean;
86
137
  /**
87
- * Boolean indicating whether the command is developer only.
138
+ * A boolean indicating whether the command is developer-only.
139
+ * Used for registration and built-in validation.
88
140
  */
89
141
  devOnly?: boolean;
90
142
  /**
91
- * Boolean indicating whether the command is deleted and should not be registered.
143
+ * A boolean indicating whether the command is deleted/ignored on restart/reload.
92
144
  */
93
145
  deleted?: boolean;
94
146
  /**
95
- * An array of user permissions.
147
+ * A string or array of permissions that a user needs for the current command to be executed.
148
+ * Used for built-in validation.
96
149
  *
97
150
  * @example
98
- * userPermissions: ['BanMembers']
151
+ * userPermissions: 'BanMembers'
152
+ * or
153
+ * userPermissions: ['BanMembers', 'KickMembers']
99
154
  */
100
- userPermissions?: PermissionsString[];
155
+ userPermissions?: PermissionsString | PermissionsString[];
101
156
  /**
102
- * An array of bot permissions.
157
+ * A string or array of permissions that the bot needs to execute the current command.
158
+ * Used for built-in validation.
103
159
  *
104
160
  * @example
105
- * botPermissions: ['BanMembers']
161
+ * botPermissions: 'BanMembers'
162
+ * or
163
+ * botPermissions: ['BanMembers', 'KickMembers']
106
164
  */
107
- botPermissions?: PermissionsString[];
165
+ botPermissions?: PermissionsString | PermissionsString[];
108
166
  [key: string]: any;
109
167
  }
110
- declare enum CommandType {
111
- /**
112
- * Slash commands.
113
- */
114
- 'ChatInput' = 1,
115
- /**
116
- * Message context menu commands.
117
- */
118
- 'Message' = 3,
119
- /**
120
- * User context menu commands.
121
- */
122
- 'User' = 2
123
- }
124
- type LocaleString = 'id' | `en-${'GB' | 'US'}` | 'bg' | `zh-${'CN' | 'TW'}` | 'hr' | 'cs' | 'da' | 'nl' | 'fi' | 'fr' | 'de' | 'el' | 'hi' | 'hu' | 'it' | 'ja' | 'ko' | 'lt' | 'no' | 'pl' | 'pt-BR' | 'ro' | 'ru' | 'es-ES' | 'sv-SE' | 'th' | 'tr' | 'uk' | 'vi';
125
- /**
126
- * Common items for command data.
127
- */
128
- type BaseCommandData = {
129
- /**
130
- * The name of the command.
131
- */
132
- name: string;
133
- /**
134
- * The type of the command.
135
- */
136
- type?: CommandType;
137
- /**
138
- * i18n for the command name.
139
- */
140
- name_localizations?: Partial<Record<LocaleString, string | null>>;
141
- /**
142
- * Whether to allow this command in DMs.
143
- */
144
- dm_permission?: boolean;
145
- /**
146
- * Default permissions for members for the command.
147
- */
148
- default_member_permissions?: string;
149
- /**
150
- * Whether the command is age-restricted.
151
- */
152
- nsfw?: boolean;
153
- };
154
- /**
155
- * Data for chat input commands.
156
- */
157
- type ChatInputCommandData = BaseCommandData & {
158
- /**
159
- * The command's type.
160
- */
161
- type?: CommandType.ChatInput;
162
- /**
163
- * The description of the command.
164
- */
165
- description: string;
166
- /**
167
- * i18n for the command description.
168
- */
169
- description_localizations?: Partial<Record<LocaleString, string | null>>;
170
- /**
171
- * Chat input command options.
172
- */
173
- options?: Array<APIApplicationCommandOption>;
174
- };
175
- /**
176
- * Represents any context menu command data.
177
- */
178
- type UserOrMessageCommandData = BaseCommandData & {
179
- type: CommandType.User | CommandType.Message;
180
- };
181
- type CommandData = ChatInputCommandData | UserOrMessageCommandData;
168
+ type CommandData = RESTPostAPIApplicationCommandsJSONBody;
182
169
  type CommandObject = {
183
170
  /**
184
- * Command data which is a slash command builder or raw JSON data.
185
- * @example
186
- * {
187
- * name: 'ping',
188
- * description: 'Replies with Pong!'
189
- * }
171
+ * An object which defines the structure of the application command.
190
172
  */
191
173
  data: CommandData;
192
174
  /**
193
- * CommandKit command options.
194
- * @example
195
- * {
196
- * devOnly: true,
197
- * userPermissions: ['ManageGuild'],
198
- * botPermissions: ['ManageGuild'],
199
- * }
175
+ * Additional command configuration options.
200
176
  */
201
177
  options?: CommandOptions;
202
178
  /**
@@ -204,7 +180,13 @@ type CommandObject = {
204
180
  */
205
181
  filePath: string;
206
182
  /**
207
- * The command's category.
183
+ * The command's category. Determined based on the command folder.
184
+ *
185
+ * @example
186
+ * ```txt
187
+ * "/src/commands/ping.js" -> null
188
+ * "/src/commands/Misc/ping.js" -> "Misc"
189
+ * ```
208
190
  */
209
191
  category: string | null;
210
192
  [key: string]: any;
@@ -220,57 +202,12 @@ declare enum ReloadType {
220
202
  Global = "global"
221
203
  }
222
204
 
223
- /**
224
- * Options for instantiating a CommandKit handler.
225
- */
226
- interface CommandKitOptions {
227
- /**
228
- * The Discord.js client object to use with CommandKit.
229
- */
230
- client: Client;
231
- /**
232
- * The path to your commands directory.
233
- */
234
- commandsPath?: string;
235
- /**
236
- * The path to your events directory.
237
- */
238
- eventsPath?: string;
239
- /**
240
- * The path to the validations directory.
241
- */
242
- validationsPath?: string;
243
- /**
244
- * List of development guild IDs to restrict devOnly commands to.
245
- */
246
- devGuildIds?: string[];
247
- /**
248
- * List of developer user IDs to restrict devOnly commands to.
249
- */
250
- devUserIds?: string[];
251
- /**
252
- * List of developer role IDs to restrict devOnly commands to.
253
- */
254
- devRoleIds?: string[];
255
- /**
256
- * Skip CommandKit's built-in validations (for devOnly commands).
257
- */
258
- skipBuiltInValidations?: boolean;
259
- /**
260
- * Bulk register application commands instead of one-by-one.
261
- */
262
- bulkRegister?: boolean;
263
- }
264
- /**
265
- * A reload type for commands.
266
- */
267
- type ReloadOptions = 'dev' | 'global' | ReloadType;
268
-
269
205
  declare class CommandKit {
270
206
  #private;
271
207
  /**
272
- * Create a new handler with CommandKit.
273
- * @param options - Options to use (client, commandsPath, eventsPath, etc.)
208
+ * Create a new command and event handler with CommandKit.
209
+ *
210
+ * @param options - The default CommandKit configuration.
274
211
  * @see {@link https://commandkit.js.org/docs/commandkit-setup}
275
212
  */
276
213
  constructor(options: CommandKitOptions);
@@ -316,7 +253,11 @@ declare class CommandKit {
316
253
  get devRoleIds(): string[];
317
254
  }
318
255
 
319
- type CommandKitButtonBuilderInteractionCollectorDispatch = (interaction: ButtonInteraction) => Awaitable<void>;
256
+ /**
257
+ * The handler to run when a button is clicked. This handler is called with the interaction as the first argument.
258
+ * If the first argument is null, it means that the interaction collector has been destroyed.
259
+ */
260
+ type CommandKitButtonBuilderInteractionCollectorDispatch = (interaction: ButtonInteraction | null) => Awaitable<void>;
320
261
  type CommandKitButtonBuilderInteractionCollectorDispatchContextData = {
321
262
  /**
322
263
  * The message to listen for button interactions on.
@@ -430,4 +371,4 @@ declare function createSignal<T = unknown>(value?: CommandKitSignalInitializer<T
430
371
  */
431
372
  declare function createEffect(callback: CommandKitEffectCallback): void;
432
373
 
433
- export { ButtonKit, CommandData, CommandKit, CommandKitButtonBuilderInteractionCollectorDispatch, CommandKitButtonBuilderInteractionCollectorDispatchContextData, CommandKitConfig, CommandKitEffectCallback, CommandKitSignal, CommandKitSignalInitializer, CommandKitSignalUpdater, CommandObject, CommandOptions, CommandProps, CommandType, ContextMenuCommandProps, ReloadType, SlashCommandProps, ValidationFunctionProps, createEffect, createSignal, defineConfig, getConfig };
374
+ export { AutocompleteCommandProps, ButtonKit, CommandData, CommandKit, CommandKitButtonBuilderInteractionCollectorDispatch, CommandKitButtonBuilderInteractionCollectorDispatchContextData, CommandKitConfig, CommandKitEffectCallback, CommandKitSignal, CommandKitSignalInitializer, CommandKitSignalUpdater, CommandObject, CommandOptions, CommandProps, ContextMenuCommandProps, MessageContextMenuCommandProps, ReloadType, SlashCommandProps, UserContextMenuCommandProps, ValidationFunctionProps, createEffect, createSignal, defineConfig, getConfig };
package/dist/index.d.ts CHANGED
@@ -1,202 +1,178 @@
1
- import { CommandInteraction, Client, ChatInputCommandInteraction, ContextMenuCommandInteraction, PermissionsString, APIApplicationCommandOption, ButtonInteraction, Awaitable, Message, InteractionCollectorOptions, ButtonBuilder } from 'discord.js';
1
+ import { Client, ChatInputCommandInteraction, ContextMenuCommandInteraction, UserContextMenuCommandInteraction, MessageContextMenuCommandInteraction, AutocompleteInteraction, PermissionsString, RESTPostAPIApplicationCommandsJSONBody, ButtonInteraction, Awaitable, Message, InteractionCollectorOptions, ButtonBuilder } from 'discord.js';
2
2
 
3
3
  /**
4
- * Base props for commands.
4
+ * Options for instantiating a CommandKit handler.
5
5
  */
6
- interface CommandProps {
6
+ interface CommandKitOptions {
7
7
  /**
8
- * Represents the command's interaction.
8
+ * The Discord.js client object to use with CommandKit.
9
9
  */
10
- interaction: CommandInteraction;
10
+ client: Client;
11
11
  /**
12
- * The client created in your main file.
12
+ * The path to your commands directory.
13
13
  */
14
- client: Client<true>;
14
+ commandsPath?: string;
15
15
  /**
16
- * The main CommandKit handler that instantiated this.
16
+ * The path to your events directory.
17
17
  */
18
- handler: CommandKit;
18
+ eventsPath?: string;
19
+ /**
20
+ * The path to the validations directory.
21
+ */
22
+ validationsPath?: string;
23
+ /**
24
+ * List of development guild IDs to restrict devOnly commands to.
25
+ */
26
+ devGuildIds?: string[];
27
+ /**
28
+ * List of developer user IDs to restrict devOnly commands to.
29
+ */
30
+ devUserIds?: string[];
31
+ /**
32
+ * List of developer role IDs to restrict devOnly commands to.
33
+ */
34
+ devRoleIds?: string[];
35
+ /**
36
+ * Skip CommandKit's built-in validations (for devOnly commands).
37
+ */
38
+ skipBuiltInValidations?: boolean;
39
+ /**
40
+ * Bulk register application commands instead of one-by-one.
41
+ */
42
+ bulkRegister?: boolean;
19
43
  }
20
44
  /**
21
- * Props for slash command run functions.
45
+ * A reload type for commands.
46
+ */
47
+ type ReloadOptions = 'dev' | 'global' | ReloadType;
48
+
49
+ /**
50
+ * Props for command run functions.
22
51
  */
23
- interface SlashCommandProps {
52
+ interface CommandProps {
24
53
  /**
25
- * Represents the command's interaction.
54
+ * The current command interaction object.
26
55
  */
27
- interaction: ChatInputCommandInteraction;
56
+ interaction: ChatInputCommandInteraction | ContextMenuCommandInteraction | UserContextMenuCommandInteraction | MessageContextMenuCommandInteraction | AutocompleteInteraction;
28
57
  /**
29
- * The client created in your main file.
58
+ * The Discord.js client object that CommandKit is handling.
30
59
  */
31
60
  client: Client<true>;
32
61
  /**
33
- * The CommandKit handler that instantiated this.
62
+ * The current CommandKit handler instance.
34
63
  */
35
64
  handler: CommandKit;
36
65
  }
37
66
  /**
38
- * Props for context menu command run functions.
67
+ * Props for autocomplete command run functions.
39
68
  */
40
- interface ContextMenuCommandProps {
69
+ interface AutocompleteCommandProps extends CommandProps {
41
70
  /**
42
- * Represents the command's interaction.
71
+ * The current autocomplete command interaction object.
43
72
  */
44
- interaction: ContextMenuCommandInteraction;
73
+ interaction: AutocompleteInteraction;
74
+ }
75
+ /**
76
+ * Props for slash (chat input) command run functions.
77
+ */
78
+ interface SlashCommandProps extends CommandProps {
45
79
  /**
46
- * The client created in your main file.
80
+ * The current slash (chat input) command interaction object.
47
81
  */
48
- client: Client<true>;
82
+ interaction: ChatInputCommandInteraction;
83
+ }
84
+ /**
85
+ * Props for context menu command run functions.
86
+ */
87
+ interface ContextMenuCommandProps extends CommandProps {
49
88
  /**
50
- * The CommandKit handler that instantiated this.
89
+ * The current context menu command interaction object.
51
90
  */
52
- handler: CommandKit;
91
+ interaction: ContextMenuCommandInteraction;
92
+ }
93
+ /**
94
+ * Props for user context menu command run functions.
95
+ */
96
+ interface UserContextMenuCommandProps extends CommandProps {
97
+ interaction: UserContextMenuCommandInteraction;
53
98
  }
54
99
  /**
55
- * Props for command validations.
100
+ * Props for message context menu command run functions.
101
+ */
102
+ interface MessageContextMenuCommandProps extends CommandProps {
103
+ interaction: MessageContextMenuCommandInteraction;
104
+ }
105
+ /**
106
+ * Props for command validation functions.
56
107
  */
57
108
  interface ValidationFunctionProps {
58
109
  /**
59
- * Represents the command's interaction.
60
- * Either a slash command or a context menu command interaction.
110
+ * The current command interaction object.
61
111
  */
62
112
  interaction: ChatInputCommandInteraction | ContextMenuCommandInteraction;
63
113
  /**
64
- * The client created in your main file.
65
- * Represented as a ready client (`Client<true>`).
114
+ * The Discord.js client object that CommandKit is handling.
66
115
  */
67
116
  client: Client<true>;
68
117
  /**
69
- * The command object that is being validated.
118
+ * The current (local) target command object.
70
119
  */
71
120
  commandObj: CommandObject;
72
121
  /**
73
- * The CommandKit handler that instantiated this.
122
+ * The current CommandKit handler instance.
74
123
  */
75
124
  handler: CommandKit;
76
125
  }
77
126
  /**
78
- * Configuration for commands.
127
+ * Additional command configuration options.
79
128
  */
80
129
  interface CommandOptions {
81
130
  /**
82
- * Boolean indicating whether the command is guild-only.
83
- * @deprecated - Use `dm_permission` in the command's data instead.
131
+ * A boolean indicating whether the command is guild-only.
132
+ * Used for built-in validation.
133
+ *
134
+ * @deprecated Use `dm_permission` in the command's `data` object instead.
84
135
  */
85
136
  guildOnly?: boolean;
86
137
  /**
87
- * Boolean indicating whether the command is developer only.
138
+ * A boolean indicating whether the command is developer-only.
139
+ * Used for registration and built-in validation.
88
140
  */
89
141
  devOnly?: boolean;
90
142
  /**
91
- * Boolean indicating whether the command is deleted and should not be registered.
143
+ * A boolean indicating whether the command is deleted/ignored on restart/reload.
92
144
  */
93
145
  deleted?: boolean;
94
146
  /**
95
- * An array of user permissions.
147
+ * A string or array of permissions that a user needs for the current command to be executed.
148
+ * Used for built-in validation.
96
149
  *
97
150
  * @example
98
- * userPermissions: ['BanMembers']
151
+ * userPermissions: 'BanMembers'
152
+ * or
153
+ * userPermissions: ['BanMembers', 'KickMembers']
99
154
  */
100
- userPermissions?: PermissionsString[];
155
+ userPermissions?: PermissionsString | PermissionsString[];
101
156
  /**
102
- * An array of bot permissions.
157
+ * A string or array of permissions that the bot needs to execute the current command.
158
+ * Used for built-in validation.
103
159
  *
104
160
  * @example
105
- * botPermissions: ['BanMembers']
161
+ * botPermissions: 'BanMembers'
162
+ * or
163
+ * botPermissions: ['BanMembers', 'KickMembers']
106
164
  */
107
- botPermissions?: PermissionsString[];
165
+ botPermissions?: PermissionsString | PermissionsString[];
108
166
  [key: string]: any;
109
167
  }
110
- declare enum CommandType {
111
- /**
112
- * Slash commands.
113
- */
114
- 'ChatInput' = 1,
115
- /**
116
- * Message context menu commands.
117
- */
118
- 'Message' = 3,
119
- /**
120
- * User context menu commands.
121
- */
122
- 'User' = 2
123
- }
124
- type LocaleString = 'id' | `en-${'GB' | 'US'}` | 'bg' | `zh-${'CN' | 'TW'}` | 'hr' | 'cs' | 'da' | 'nl' | 'fi' | 'fr' | 'de' | 'el' | 'hi' | 'hu' | 'it' | 'ja' | 'ko' | 'lt' | 'no' | 'pl' | 'pt-BR' | 'ro' | 'ru' | 'es-ES' | 'sv-SE' | 'th' | 'tr' | 'uk' | 'vi';
125
- /**
126
- * Common items for command data.
127
- */
128
- type BaseCommandData = {
129
- /**
130
- * The name of the command.
131
- */
132
- name: string;
133
- /**
134
- * The type of the command.
135
- */
136
- type?: CommandType;
137
- /**
138
- * i18n for the command name.
139
- */
140
- name_localizations?: Partial<Record<LocaleString, string | null>>;
141
- /**
142
- * Whether to allow this command in DMs.
143
- */
144
- dm_permission?: boolean;
145
- /**
146
- * Default permissions for members for the command.
147
- */
148
- default_member_permissions?: string;
149
- /**
150
- * Whether the command is age-restricted.
151
- */
152
- nsfw?: boolean;
153
- };
154
- /**
155
- * Data for chat input commands.
156
- */
157
- type ChatInputCommandData = BaseCommandData & {
158
- /**
159
- * The command's type.
160
- */
161
- type?: CommandType.ChatInput;
162
- /**
163
- * The description of the command.
164
- */
165
- description: string;
166
- /**
167
- * i18n for the command description.
168
- */
169
- description_localizations?: Partial<Record<LocaleString, string | null>>;
170
- /**
171
- * Chat input command options.
172
- */
173
- options?: Array<APIApplicationCommandOption>;
174
- };
175
- /**
176
- * Represents any context menu command data.
177
- */
178
- type UserOrMessageCommandData = BaseCommandData & {
179
- type: CommandType.User | CommandType.Message;
180
- };
181
- type CommandData = ChatInputCommandData | UserOrMessageCommandData;
168
+ type CommandData = RESTPostAPIApplicationCommandsJSONBody;
182
169
  type CommandObject = {
183
170
  /**
184
- * Command data which is a slash command builder or raw JSON data.
185
- * @example
186
- * {
187
- * name: 'ping',
188
- * description: 'Replies with Pong!'
189
- * }
171
+ * An object which defines the structure of the application command.
190
172
  */
191
173
  data: CommandData;
192
174
  /**
193
- * CommandKit command options.
194
- * @example
195
- * {
196
- * devOnly: true,
197
- * userPermissions: ['ManageGuild'],
198
- * botPermissions: ['ManageGuild'],
199
- * }
175
+ * Additional command configuration options.
200
176
  */
201
177
  options?: CommandOptions;
202
178
  /**
@@ -204,7 +180,13 @@ type CommandObject = {
204
180
  */
205
181
  filePath: string;
206
182
  /**
207
- * The command's category.
183
+ * The command's category. Determined based on the command folder.
184
+ *
185
+ * @example
186
+ * ```txt
187
+ * "/src/commands/ping.js" -> null
188
+ * "/src/commands/Misc/ping.js" -> "Misc"
189
+ * ```
208
190
  */
209
191
  category: string | null;
210
192
  [key: string]: any;
@@ -220,57 +202,12 @@ declare enum ReloadType {
220
202
  Global = "global"
221
203
  }
222
204
 
223
- /**
224
- * Options for instantiating a CommandKit handler.
225
- */
226
- interface CommandKitOptions {
227
- /**
228
- * The Discord.js client object to use with CommandKit.
229
- */
230
- client: Client;
231
- /**
232
- * The path to your commands directory.
233
- */
234
- commandsPath?: string;
235
- /**
236
- * The path to your events directory.
237
- */
238
- eventsPath?: string;
239
- /**
240
- * The path to the validations directory.
241
- */
242
- validationsPath?: string;
243
- /**
244
- * List of development guild IDs to restrict devOnly commands to.
245
- */
246
- devGuildIds?: string[];
247
- /**
248
- * List of developer user IDs to restrict devOnly commands to.
249
- */
250
- devUserIds?: string[];
251
- /**
252
- * List of developer role IDs to restrict devOnly commands to.
253
- */
254
- devRoleIds?: string[];
255
- /**
256
- * Skip CommandKit's built-in validations (for devOnly commands).
257
- */
258
- skipBuiltInValidations?: boolean;
259
- /**
260
- * Bulk register application commands instead of one-by-one.
261
- */
262
- bulkRegister?: boolean;
263
- }
264
- /**
265
- * A reload type for commands.
266
- */
267
- type ReloadOptions = 'dev' | 'global' | ReloadType;
268
-
269
205
  declare class CommandKit {
270
206
  #private;
271
207
  /**
272
- * Create a new handler with CommandKit.
273
- * @param options - Options to use (client, commandsPath, eventsPath, etc.)
208
+ * Create a new command and event handler with CommandKit.
209
+ *
210
+ * @param options - The default CommandKit configuration.
274
211
  * @see {@link https://commandkit.js.org/docs/commandkit-setup}
275
212
  */
276
213
  constructor(options: CommandKitOptions);
@@ -316,7 +253,11 @@ declare class CommandKit {
316
253
  get devRoleIds(): string[];
317
254
  }
318
255
 
319
- type CommandKitButtonBuilderInteractionCollectorDispatch = (interaction: ButtonInteraction) => Awaitable<void>;
256
+ /**
257
+ * The handler to run when a button is clicked. This handler is called with the interaction as the first argument.
258
+ * If the first argument is null, it means that the interaction collector has been destroyed.
259
+ */
260
+ type CommandKitButtonBuilderInteractionCollectorDispatch = (interaction: ButtonInteraction | null) => Awaitable<void>;
320
261
  type CommandKitButtonBuilderInteractionCollectorDispatchContextData = {
321
262
  /**
322
263
  * The message to listen for button interactions on.
@@ -430,4 +371,4 @@ declare function createSignal<T = unknown>(value?: CommandKitSignalInitializer<T
430
371
  */
431
372
  declare function createEffect(callback: CommandKitEffectCallback): void;
432
373
 
433
- export { ButtonKit, CommandData, CommandKit, CommandKitButtonBuilderInteractionCollectorDispatch, CommandKitButtonBuilderInteractionCollectorDispatchContextData, CommandKitConfig, CommandKitEffectCallback, CommandKitSignal, CommandKitSignalInitializer, CommandKitSignalUpdater, CommandObject, CommandOptions, CommandProps, CommandType, ContextMenuCommandProps, ReloadType, SlashCommandProps, ValidationFunctionProps, createEffect, createSignal, defineConfig, getConfig };
374
+ export { AutocompleteCommandProps, ButtonKit, CommandData, CommandKit, CommandKitButtonBuilderInteractionCollectorDispatch, CommandKitButtonBuilderInteractionCollectorDispatchContextData, CommandKitConfig, CommandKitEffectCallback, CommandKitSignal, CommandKitSignalInitializer, CommandKitSignalUpdater, CommandObject, CommandOptions, CommandProps, ContextMenuCommandProps, MessageContextMenuCommandProps, ReloadType, SlashCommandProps, UserContextMenuCommandProps, ValidationFunctionProps, createEffect, createSignal, defineConfig, getConfig };
package/dist/index.js CHANGED
@@ -39,8 +39,15 @@ __export(src_exports, {
39
39
  });
40
40
  module.exports = __toCommonJS(src_exports);
41
41
 
42
- // src/utils/get-paths.ts
42
+ // src/utils/resolve-file-url.ts
43
43
  var import_path = __toESM(require("path"));
44
+ function toFileURL(filePath) {
45
+ const resolvedPath = import_path.default.resolve(filePath);
46
+ return "file://" + resolvedPath.replace(/\\\\|\\/g, "/");
47
+ }
48
+
49
+ // src/utils/get-paths.ts
50
+ var import_path2 = __toESM(require("path"));
44
51
  var import_promises = __toESM(require("fs/promises"));
45
52
  async function getFilePaths(directory, nesting) {
46
53
  let filePaths = [];
@@ -48,7 +55,7 @@ async function getFilePaths(directory, nesting) {
48
55
  return filePaths;
49
56
  const files = await import_promises.default.readdir(directory, { withFileTypes: true });
50
57
  for (const file of files) {
51
- const filePath = import_path.default.join(directory, file.name);
58
+ const filePath = import_path2.default.join(directory, file.name);
52
59
  if (file.isFile()) {
53
60
  filePaths.push(filePath);
54
61
  }
@@ -64,7 +71,7 @@ async function getFolderPaths(directory, nesting) {
64
71
  return folderPaths;
65
72
  const folders = await import_promises.default.readdir(directory, { withFileTypes: true });
66
73
  for (const folder of folders) {
67
- const folderPath = import_path.default.join(directory, folder.name);
74
+ const folderPath = import_path2.default.join(directory, folder.name);
68
75
  if (folder.isDirectory()) {
69
76
  folderPaths.push(folderPath);
70
77
  if (nesting) {
@@ -75,13 +82,6 @@ async function getFolderPaths(directory, nesting) {
75
82
  return folderPaths;
76
83
  }
77
84
 
78
- // src/utils/resolve-file-url.ts
79
- var import_path2 = __toESM(require("path"));
80
- function toFileURL(filePath) {
81
- const resolvedPath = import_path2.default.resolve(filePath);
82
- return "file://" + resolvedPath.replace(/\\\\|\\/g, "/");
83
- }
84
-
85
85
  // src/utils/colors.ts
86
86
  var resetColor = "\x1B[0m";
87
87
  var colors_default = {
@@ -366,6 +366,8 @@ async function registerDevCommands(client, commands, guildIds) {
366
366
 
367
367
  // src/handlers/command-handler/validations/devOnly.ts
368
368
  function devOnly_default({ interaction, targetCommand, handlerData }) {
369
+ if (interaction.isAutocomplete())
370
+ return;
369
371
  if (targetCommand.options?.devOnly) {
370
372
  if (interaction.inGuild() && !handlerData.devGuildIds.includes(interaction.guildId)) {
371
373
  interaction.reply({
@@ -393,26 +395,23 @@ function devOnly_default({ interaction, targetCommand, handlerData }) {
393
395
  }
394
396
  }
395
397
 
396
- // src/handlers/command-handler/validations/guildOnly.ts
397
- function guildOnly_default({ interaction, targetCommand }) {
398
- if (targetCommand.options?.guildOnly && !interaction.inGuild()) {
399
- interaction.reply({
400
- content: "\u274C This command can only be used inside a server.",
401
- ephemeral: true
402
- });
403
- return true;
404
- }
405
- }
406
-
407
398
  // src/handlers/command-handler/validations/permissions.ts
408
399
  var import_discord = require("discord.js");
409
400
  function permissions_default({ interaction, targetCommand }) {
401
+ if (interaction.isAutocomplete())
402
+ return;
410
403
  const userPermissions = interaction.memberPermissions;
411
404
  let userPermissionsRequired = targetCommand.options?.userPermissions;
412
405
  let missingUserPermissions = [];
406
+ if (typeof userPermissionsRequired === "string") {
407
+ userPermissionsRequired = [userPermissionsRequired];
408
+ }
413
409
  const botPermissions = interaction.guild?.members.me?.permissions;
414
410
  let botPermissionsRequired = targetCommand.options?.botPermissions;
415
411
  let missingBotPermissions = [];
412
+ if (typeof botPermissionsRequired === "string") {
413
+ botPermissionsRequired = [botPermissionsRequired];
414
+ }
416
415
  if (!userPermissionsRequired?.length && !botPermissionsRequired?.length) {
417
416
  return;
418
417
  }
@@ -463,11 +462,13 @@ function permissions_default({ interaction, targetCommand }) {
463
462
  }
464
463
 
465
464
  // src/handlers/command-handler/validations/index.ts
466
- var validations_default = [devOnly_default, guildOnly_default, permissions_default];
465
+ var validations_default = [devOnly_default, permissions_default];
467
466
 
468
- // src/handlers/command-handler/CommandHandler.ts
467
+ // src/utils/clone.ts
469
468
  var import_rfdc = __toESM(require("rfdc"));
470
469
  var clone = (0, import_rfdc.default)();
470
+
471
+ // src/handlers/command-handler/CommandHandler.ts
471
472
  var CommandHandler = class {
472
473
  #data;
473
474
  constructor({ ...options }) {
@@ -516,7 +517,7 @@ var CommandHandler = class {
516
517
  const commandFilePaths = paths.filter((path3) => allowedExtensions.test(path3));
517
518
  for (const commandFilePath of commandFilePaths) {
518
519
  const modulePath = toFileURL(commandFilePath);
519
- let importedObj = await import(`${modulePath}?t=${Date.now()}`);
520
+ const importedObj = await import(`${modulePath}?t=${Date.now()}`);
520
521
  let commandObj = clone(importedObj);
521
522
  if (typeof module !== "undefined" && typeof require !== "undefined") {
522
523
  delete require.cache[require.resolve(commandFilePath)];
@@ -578,14 +579,17 @@ var CommandHandler = class {
578
579
  }
579
580
  handleCommands() {
580
581
  this.#data.client.on("interactionCreate", async (interaction) => {
581
- if (!interaction.isChatInputCommand() && !interaction.isContextMenuCommand())
582
+ if (!interaction.isChatInputCommand() && !interaction.isContextMenuCommand() && !interaction.isAutocomplete())
582
583
  return;
584
+ const isAutocomplete = interaction.isAutocomplete();
583
585
  const targetCommand = this.#data.commands.find(
584
586
  (cmd) => cmd.data.name === interaction.commandName
585
587
  );
586
588
  if (!targetCommand)
587
589
  return;
588
- const { data, options, run, ...rest } = targetCommand;
590
+ const { data, options, run, autocompleteRun, ...rest } = targetCommand;
591
+ if (isAutocomplete && !autocompleteRun)
592
+ return;
589
593
  const commandObj = {
590
594
  data: targetCommand.data,
591
595
  options: targetCommand.options,
@@ -624,11 +628,12 @@ var CommandHandler = class {
624
628
  }
625
629
  if (!canRun)
626
630
  return;
627
- targetCommand.run({
631
+ const context2 = {
628
632
  interaction,
629
633
  client: this.#data.client,
630
634
  handler: this.#data.commandkitInstance
631
- });
635
+ };
636
+ await targetCommand[isAutocomplete ? "autocompleteRun" : "run"](context2);
632
637
  });
633
638
  }
634
639
  get commands() {
@@ -660,8 +665,6 @@ var CommandHandler = class {
660
665
  };
661
666
 
662
667
  // src/handlers/event-handler/EventHandler.ts
663
- var import_rfdc2 = __toESM(require("rfdc"));
664
- var clone2 = (0, import_rfdc2.default)();
665
668
  var EventHandler = class {
666
669
  #data;
667
670
  constructor({ ...options }) {
@@ -689,7 +692,7 @@ var EventHandler = class {
689
692
  for (const eventFilePath of eventFilePaths) {
690
693
  const modulePath = toFileURL(eventFilePath);
691
694
  let importedFunction = (await import(`${modulePath}?t=${Date.now()}`)).default;
692
- let eventFunction = clone2(importedFunction);
695
+ let eventFunction = clone(importedFunction);
693
696
  if (typeof module !== "undefined" && typeof require !== "undefined") {
694
697
  delete require.cache[require.resolve(eventFilePath)];
695
698
  }
@@ -736,8 +739,6 @@ var EventHandler = class {
736
739
  };
737
740
 
738
741
  // src/handlers/validation-handler/ValidationHandler.ts
739
- var import_rfdc3 = __toESM(require("rfdc"));
740
- var clone3 = (0, import_rfdc3.default)();
741
742
  var ValidationHandler = class {
742
743
  #data;
743
744
  constructor({ ...options }) {
@@ -756,7 +757,7 @@ var ValidationHandler = class {
756
757
  for (const validationFilePath of validationFilePaths) {
757
758
  const modulePath = toFileURL(validationFilePath);
758
759
  let importedFunction = (await import(`${modulePath}?t=${Date.now()}`)).default;
759
- let validationFunction = clone3(importedFunction);
760
+ let validationFunction = clone(importedFunction);
760
761
  if (typeof module !== "undefined" && typeof require !== "undefined") {
761
762
  delete require.cache[require.resolve(validationFilePath)];
762
763
  }
@@ -788,8 +789,9 @@ var ValidationHandler = class {
788
789
  var CommandKit = class {
789
790
  #data;
790
791
  /**
791
- * Create a new handler with CommandKit.
792
- * @param options - Options to use (client, commandsPath, eventsPath, etc.)
792
+ * Create a new command and event handler with CommandKit.
793
+ *
794
+ * @param options - The default CommandKit configuration.
793
795
  * @see {@link https://commandkit.js.org/docs/commandkit-setup}
794
796
  */
795
797
  constructor(options) {
@@ -971,6 +973,7 @@ var ButtonKit = class extends import_discord2.ButtonBuilder {
971
973
  });
972
974
  }
973
975
  #destroyCollector() {
976
+ this.#onClickHandler?.(null);
974
977
  this.#collector?.stop("end");
975
978
  this.#collector?.removeAllListeners();
976
979
  this.#collector = null;
package/dist/index.mjs CHANGED
@@ -6,8 +6,15 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
6
6
  throw Error('Dynamic require of "' + x + '" is not supported');
7
7
  });
8
8
 
9
- // src/utils/get-paths.ts
9
+ // src/utils/resolve-file-url.ts
10
10
  import path from "path";
11
+ function toFileURL(filePath) {
12
+ const resolvedPath = path.resolve(filePath);
13
+ return "file://" + resolvedPath.replace(/\\\\|\\/g, "/");
14
+ }
15
+
16
+ // src/utils/get-paths.ts
17
+ import path2 from "path";
11
18
  import fs from "fs/promises";
12
19
  async function getFilePaths(directory, nesting) {
13
20
  let filePaths = [];
@@ -15,7 +22,7 @@ async function getFilePaths(directory, nesting) {
15
22
  return filePaths;
16
23
  const files = await fs.readdir(directory, { withFileTypes: true });
17
24
  for (const file of files) {
18
- const filePath = path.join(directory, file.name);
25
+ const filePath = path2.join(directory, file.name);
19
26
  if (file.isFile()) {
20
27
  filePaths.push(filePath);
21
28
  }
@@ -31,7 +38,7 @@ async function getFolderPaths(directory, nesting) {
31
38
  return folderPaths;
32
39
  const folders = await fs.readdir(directory, { withFileTypes: true });
33
40
  for (const folder of folders) {
34
- const folderPath = path.join(directory, folder.name);
41
+ const folderPath = path2.join(directory, folder.name);
35
42
  if (folder.isDirectory()) {
36
43
  folderPaths.push(folderPath);
37
44
  if (nesting) {
@@ -42,13 +49,6 @@ async function getFolderPaths(directory, nesting) {
42
49
  return folderPaths;
43
50
  }
44
51
 
45
- // src/utils/resolve-file-url.ts
46
- import path2 from "path";
47
- function toFileURL(filePath) {
48
- const resolvedPath = path2.resolve(filePath);
49
- return "file://" + resolvedPath.replace(/\\\\|\\/g, "/");
50
- }
51
-
52
52
  // src/utils/colors.ts
53
53
  var resetColor = "\x1B[0m";
54
54
  var colors_default = {
@@ -333,6 +333,8 @@ async function registerDevCommands(client, commands, guildIds) {
333
333
 
334
334
  // src/handlers/command-handler/validations/devOnly.ts
335
335
  function devOnly_default({ interaction, targetCommand, handlerData }) {
336
+ if (interaction.isAutocomplete())
337
+ return;
336
338
  if (targetCommand.options?.devOnly) {
337
339
  if (interaction.inGuild() && !handlerData.devGuildIds.includes(interaction.guildId)) {
338
340
  interaction.reply({
@@ -360,26 +362,23 @@ function devOnly_default({ interaction, targetCommand, handlerData }) {
360
362
  }
361
363
  }
362
364
 
363
- // src/handlers/command-handler/validations/guildOnly.ts
364
- function guildOnly_default({ interaction, targetCommand }) {
365
- if (targetCommand.options?.guildOnly && !interaction.inGuild()) {
366
- interaction.reply({
367
- content: "\u274C This command can only be used inside a server.",
368
- ephemeral: true
369
- });
370
- return true;
371
- }
372
- }
373
-
374
365
  // src/handlers/command-handler/validations/permissions.ts
375
366
  import { EmbedBuilder } from "discord.js";
376
367
  function permissions_default({ interaction, targetCommand }) {
368
+ if (interaction.isAutocomplete())
369
+ return;
377
370
  const userPermissions = interaction.memberPermissions;
378
371
  let userPermissionsRequired = targetCommand.options?.userPermissions;
379
372
  let missingUserPermissions = [];
373
+ if (typeof userPermissionsRequired === "string") {
374
+ userPermissionsRequired = [userPermissionsRequired];
375
+ }
380
376
  const botPermissions = interaction.guild?.members.me?.permissions;
381
377
  let botPermissionsRequired = targetCommand.options?.botPermissions;
382
378
  let missingBotPermissions = [];
379
+ if (typeof botPermissionsRequired === "string") {
380
+ botPermissionsRequired = [botPermissionsRequired];
381
+ }
383
382
  if (!userPermissionsRequired?.length && !botPermissionsRequired?.length) {
384
383
  return;
385
384
  }
@@ -430,11 +429,13 @@ function permissions_default({ interaction, targetCommand }) {
430
429
  }
431
430
 
432
431
  // src/handlers/command-handler/validations/index.ts
433
- var validations_default = [devOnly_default, guildOnly_default, permissions_default];
432
+ var validations_default = [devOnly_default, permissions_default];
433
+
434
+ // src/utils/clone.ts
435
+ import rfdc from "rfdc";
436
+ var clone = rfdc();
434
437
 
435
438
  // src/handlers/command-handler/CommandHandler.ts
436
- import rdfc from "rfdc";
437
- var clone = rdfc();
438
439
  var CommandHandler = class {
439
440
  #data;
440
441
  constructor({ ...options }) {
@@ -483,7 +484,7 @@ var CommandHandler = class {
483
484
  const commandFilePaths = paths.filter((path3) => allowedExtensions.test(path3));
484
485
  for (const commandFilePath of commandFilePaths) {
485
486
  const modulePath = toFileURL(commandFilePath);
486
- let importedObj = await import(`${modulePath}?t=${Date.now()}`);
487
+ const importedObj = await import(`${modulePath}?t=${Date.now()}`);
487
488
  let commandObj = clone(importedObj);
488
489
  if (typeof module !== "undefined" && typeof __require !== "undefined") {
489
490
  delete __require.cache[__require.resolve(commandFilePath)];
@@ -545,14 +546,17 @@ var CommandHandler = class {
545
546
  }
546
547
  handleCommands() {
547
548
  this.#data.client.on("interactionCreate", async (interaction) => {
548
- if (!interaction.isChatInputCommand() && !interaction.isContextMenuCommand())
549
+ if (!interaction.isChatInputCommand() && !interaction.isContextMenuCommand() && !interaction.isAutocomplete())
549
550
  return;
551
+ const isAutocomplete = interaction.isAutocomplete();
550
552
  const targetCommand = this.#data.commands.find(
551
553
  (cmd) => cmd.data.name === interaction.commandName
552
554
  );
553
555
  if (!targetCommand)
554
556
  return;
555
- const { data, options, run, ...rest } = targetCommand;
557
+ const { data, options, run, autocompleteRun, ...rest } = targetCommand;
558
+ if (isAutocomplete && !autocompleteRun)
559
+ return;
556
560
  const commandObj = {
557
561
  data: targetCommand.data,
558
562
  options: targetCommand.options,
@@ -591,11 +595,12 @@ var CommandHandler = class {
591
595
  }
592
596
  if (!canRun)
593
597
  return;
594
- targetCommand.run({
598
+ const context2 = {
595
599
  interaction,
596
600
  client: this.#data.client,
597
601
  handler: this.#data.commandkitInstance
598
- });
602
+ };
603
+ await targetCommand[isAutocomplete ? "autocompleteRun" : "run"](context2);
599
604
  });
600
605
  }
601
606
  get commands() {
@@ -627,8 +632,6 @@ var CommandHandler = class {
627
632
  };
628
633
 
629
634
  // src/handlers/event-handler/EventHandler.ts
630
- import rdfc2 from "rfdc";
631
- var clone2 = rdfc2();
632
635
  var EventHandler = class {
633
636
  #data;
634
637
  constructor({ ...options }) {
@@ -656,7 +659,7 @@ var EventHandler = class {
656
659
  for (const eventFilePath of eventFilePaths) {
657
660
  const modulePath = toFileURL(eventFilePath);
658
661
  let importedFunction = (await import(`${modulePath}?t=${Date.now()}`)).default;
659
- let eventFunction = clone2(importedFunction);
662
+ let eventFunction = clone(importedFunction);
660
663
  if (typeof module !== "undefined" && typeof __require !== "undefined") {
661
664
  delete __require.cache[__require.resolve(eventFilePath)];
662
665
  }
@@ -703,8 +706,6 @@ var EventHandler = class {
703
706
  };
704
707
 
705
708
  // src/handlers/validation-handler/ValidationHandler.ts
706
- import rdfc3 from "rfdc";
707
- var clone3 = rdfc3();
708
709
  var ValidationHandler = class {
709
710
  #data;
710
711
  constructor({ ...options }) {
@@ -723,7 +724,7 @@ var ValidationHandler = class {
723
724
  for (const validationFilePath of validationFilePaths) {
724
725
  const modulePath = toFileURL(validationFilePath);
725
726
  let importedFunction = (await import(`${modulePath}?t=${Date.now()}`)).default;
726
- let validationFunction = clone3(importedFunction);
727
+ let validationFunction = clone(importedFunction);
727
728
  if (typeof module !== "undefined" && typeof __require !== "undefined") {
728
729
  delete __require.cache[__require.resolve(validationFilePath)];
729
730
  }
@@ -755,8 +756,9 @@ var ValidationHandler = class {
755
756
  var CommandKit = class {
756
757
  #data;
757
758
  /**
758
- * Create a new handler with CommandKit.
759
- * @param options - Options to use (client, commandsPath, eventsPath, etc.)
759
+ * Create a new command and event handler with CommandKit.
760
+ *
761
+ * @param options - The default CommandKit configuration.
760
762
  * @see {@link https://commandkit.js.org/docs/commandkit-setup}
761
763
  */
762
764
  constructor(options) {
@@ -942,6 +944,7 @@ var ButtonKit = class extends ButtonBuilder {
942
944
  });
943
945
  }
944
946
  #destroyCollector() {
947
+ this.#onClickHandler?.(null);
945
948
  this.#collector?.stop("end");
946
949
  this.#collector?.removeAllListeners();
947
950
  this.#collector = null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "commandkit",
3
3
  "description": "Beginner friendly command & event handler for Discord.js",
4
- "version": "0.1.6-dev.20231124165615",
4
+ "version": "0.1.6-dev.20231125121601",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",