js-discord-modularcommand 3.0.0 → 3.2.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.
@@ -25,6 +25,12 @@ class ModularButton {
25
25
  constructor(customId, command) {
26
26
  /** Use other mechanisms to handle button interactions. */
27
27
  this.execute = async () => { };
28
+ if (!customId || typeof customId !== "string") {
29
+ throw new Error("Custom ID must be a non-empty string.");
30
+ }
31
+ if (!command.name || typeof command.name !== "string") {
32
+ throw new Error("ModularCommand must have a valid name.");
33
+ }
28
34
  this.buttonObject = new discord_js_1.ButtonBuilder()
29
35
  .setCustomId(`${command.name}_${customId}`);
30
36
  this.command = command;
@@ -60,7 +66,14 @@ class ModularButton {
60
66
  * @returns {ButtonBuilder} The built ButtonBuilder instance with localized label.
61
67
  */
62
68
  build(locale) {
63
- this.buttonObject.setLabel(locale[`${this.command.name}.${this.buttonId}`]);
69
+ if (!locale || typeof locale !== "object") {
70
+ throw new Error("Locale must be a valid object.");
71
+ }
72
+ const labelKey = `${this.command.name}.${this.buttonId}`;
73
+ if (!locale[labelKey]) {
74
+ throw new Error(`Locale key "${labelKey}" not found in the provided locale object.`);
75
+ }
76
+ this.buttonObject.setLabel(locale[labelKey]);
64
77
  return this.buttonObject;
65
78
  }
66
79
  }
@@ -65,6 +65,9 @@ class ModularCommand {
65
65
  * @returns {ModularCommand} The command instance for chaining.
66
66
  */
67
67
  setDescription(description) {
68
+ if (!description || typeof description !== "string") {
69
+ throw new Error("Description must be a non-empty string.");
70
+ }
68
71
  this.description = description;
69
72
  return this;
70
73
  }
@@ -75,6 +78,9 @@ class ModularCommand {
75
78
  * @returns {ModularCommand} The command instance for chaining.
76
79
  */
77
80
  setLocalizationsDescription(localizations) {
81
+ if (!localizations || typeof localizations !== "object") {
82
+ throw new Error("Localizations must be a valid object.");
83
+ }
78
84
  return this.setLocalizationDescription(localizations);
79
85
  }
80
86
  /**
@@ -83,6 +89,9 @@ class ModularCommand {
83
89
  * @returns {ModularCommand} The command instance for chaining.
84
90
  */
85
91
  setLocalizationDescription(localizations) {
92
+ if (!localizations || typeof localizations !== "object") {
93
+ throw new Error("Localizations must be a valid object.");
94
+ }
86
95
  this.description = localizations[discord_js_1.Locale.EnglishUS] || this.description;
87
96
  this.descriptionLocalizations = {
88
97
  ...this.descriptionLocalizations,
@@ -96,6 +105,9 @@ class ModularCommand {
96
105
  * @returns {ModularCommand} The command instance for chaining.
97
106
  */
98
107
  setLocalizationOptions(localizations) {
108
+ if (!localizations || typeof localizations !== "object") {
109
+ throw new Error("Localizations must be a valid object.");
110
+ }
99
111
  this.optionsLocalizations = {
100
112
  ...this.optionsLocalizations,
101
113
  ...localizations,
@@ -108,6 +120,9 @@ class ModularCommand {
108
120
  * @returns {ModularCommand} The command instance for chaining.
109
121
  */
110
122
  setLocalizationPhrases(localizationPhrases) {
123
+ if (!localizationPhrases || typeof localizationPhrases !== "object") {
124
+ throw new Error("Localization phrases must be a valid object.");
125
+ }
111
126
  this.localizationPhrases = {
112
127
  ...this.localizationPhrases,
113
128
  ...localizationPhrases,
@@ -120,6 +135,9 @@ class ModularCommand {
120
135
  * @returns {ModularCommand} The command instance for chaining.
121
136
  */
122
137
  setLocalizationSubCommands(localizations) {
138
+ if (!localizations || typeof localizations !== "object") {
139
+ throw new Error("Localizations must be a valid object.");
140
+ }
123
141
  this.subCommandLocalizations = {
124
142
  ...this.subCommandLocalizations,
125
143
  ...localizations,
@@ -132,6 +150,9 @@ class ModularCommand {
132
150
  * @returns {ModularCommand} The command instance for chaining.
133
151
  */
134
152
  setExecute(executeFunction) {
153
+ if (!executeFunction || typeof executeFunction !== "function") {
154
+ throw new Error("Execute function must be a valid function.");
155
+ }
135
156
  this.execute = executeFunction;
136
157
  return this;
137
158
  }
@@ -142,6 +163,12 @@ class ModularCommand {
142
163
  * @returns {ModularCommand} The command instance for chaining.
143
164
  */
144
165
  setComponentExecute(componentId, executeFunction) {
166
+ if (!componentId || typeof componentId !== "string") {
167
+ throw new Error("Component ID must be a non-empty string.");
168
+ }
169
+ if (!executeFunction || typeof executeFunction !== "function") {
170
+ throw new Error("Execute function must be a valid function.");
171
+ }
145
172
  this.componentId = componentId;
146
173
  this.componentExecute = executeFunction;
147
174
  return this;
@@ -152,6 +179,9 @@ class ModularCommand {
152
179
  * @returns {ModularCommand} The command instance for chaining.
153
180
  */
154
181
  setPermissionCheck(permissionCheckFunction) {
182
+ if (!permissionCheckFunction || typeof permissionCheckFunction !== "function") {
183
+ throw new Error("Permission check function must be a valid function.");
184
+ }
155
185
  this.permissionCheck = permissionCheckFunction;
156
186
  return this;
157
187
  }
@@ -161,6 +191,9 @@ class ModularCommand {
161
191
  * @returns {ModularCommand} The command instance for chaining.
162
192
  */
163
193
  setCooldown(cooldown) {
194
+ if (typeof cooldown !== "number" || cooldown < 0) {
195
+ throw new Error("Cooldown must be a non-negative number.");
196
+ }
164
197
  this.cooldown = cooldown;
165
198
  return this;
166
199
  }
@@ -177,6 +210,9 @@ class ModularCommand {
177
210
  * @returns {ModularCommand} The command instance for chaining.
178
211
  */
179
212
  addOption(option) {
213
+ if (!option || typeof option !== "object") {
214
+ throw new Error("Option must be a valid object.");
215
+ }
180
216
  this.options.push(option);
181
217
  return this;
182
218
  }
@@ -186,6 +222,9 @@ class ModularCommand {
186
222
  * @returns {ModularCommand} The command instance for chaining.
187
223
  */
188
224
  addSubCommand(subCommand) {
225
+ if (!subCommand || typeof subCommand !== "object") {
226
+ throw new Error("SubCommand must be a valid object.");
227
+ }
189
228
  this.subCommands.push(subCommand);
190
229
  return this;
191
230
  }
@@ -196,6 +235,12 @@ class ModularCommand {
196
235
  * @returns {ModularCommand} The command instance for chaining.
197
236
  */
198
237
  addCustomIDHandler(customId, handlerFunction) {
238
+ if (!customId || typeof customId !== "string") {
239
+ throw new Error("Custom ID must be a non-empty string.");
240
+ }
241
+ if (!handlerFunction || typeof handlerFunction !== "function") {
242
+ throw new Error("Handler function must be a valid function.");
243
+ }
199
244
  this.customIdHandlers[customId] = handlerFunction;
200
245
  return this;
201
246
  }
@@ -205,6 +250,9 @@ class ModularCommand {
205
250
  * @returns {ModularModal} The created modal instance.
206
251
  */
207
252
  addModal(modalId) {
253
+ if (!modalId || typeof modalId !== "string") {
254
+ throw new Error("Modal ID must be a non-empty string.");
255
+ }
208
256
  const modal = new modularmodal_js_1.default(modalId, this);
209
257
  this.modals.set(modalId, modal);
210
258
  return modal;
@@ -216,6 +264,12 @@ class ModularCommand {
216
264
  * @return {ModularButton} The created button instance.
217
265
  */
218
266
  addButton(customId, execute) {
267
+ if (!customId || typeof customId !== "string") {
268
+ throw new Error("Custom ID must be a non-empty string.");
269
+ }
270
+ if (!execute || typeof execute !== "function") {
271
+ throw new Error("Execute function must be a valid function.");
272
+ }
219
273
  const button = new modularbutton_js_1.default(customId, this);
220
274
  button.setExecute(execute);
221
275
  this.buttons.set(customId, button);
@@ -228,6 +282,9 @@ class ModularCommand {
228
282
  * @returns {ModularSelectMenu} The created select menu instance.
229
283
  */
230
284
  addSelectMenu(selectMenuId) {
285
+ if (!selectMenuId || typeof selectMenuId !== "string") {
286
+ throw new Error("Select Menu ID must be a non-empty string.");
287
+ }
231
288
  const menu = new modularselectmenu_js_1.default(selectMenuId, this);
232
289
  this.selectMenus.set(selectMenuId, menu);
233
290
  this.selectMenusArray.push(menu);
@@ -26,6 +26,12 @@ class ModularModal {
26
26
  constructor(modalId, command) {
27
27
  /** The function to execute when the modal is submitted. */
28
28
  this.execute = async () => { };
29
+ if (!modalId || typeof modalId !== "string") {
30
+ throw new Error("Modal ID must be a non-empty string.");
31
+ }
32
+ if (!command.name || typeof command.name !== "string") {
33
+ throw new Error("ModularCommand must have a valid name.");
34
+ }
29
35
  this.customId = `${command.name}_${modalId}`;
30
36
  this.modalId = modalId;
31
37
  this.command = command;
@@ -52,6 +58,9 @@ class ModularModal {
52
58
  * @returns {this} The current ModularModal instance for method chaining.
53
59
  */
54
60
  setExecute(executeFunction) {
61
+ if (!executeFunction || typeof executeFunction !== "function") {
62
+ throw new Error("Execute function must be a valid function.");
63
+ }
55
64
  this.execute = executeFunction;
56
65
  return this;
57
66
  }
@@ -61,6 +70,9 @@ class ModularModal {
61
70
  * @returns {TextInputBuilder} The created text input instance.
62
71
  */
63
72
  newTextInput(id) {
73
+ if (!id || typeof id !== "string") {
74
+ throw new Error("Text input ID must be a non-empty string.");
75
+ }
64
76
  const textInput = new discord_js_1.TextInputBuilder()
65
77
  .setCustomId(id);
66
78
  this.modalInputs.set(id, textInput);
@@ -74,16 +86,25 @@ class ModularModal {
74
86
  * @returns {ModalBuilder} The fully constructed modal object ready to be sent to a user.
75
87
  */
76
88
  build(locale) {
77
- this.modalObject.setTitle(locale[`${this.command.name}.${this.modalId}.title`]);
89
+ if (!locale || typeof locale !== "object") {
90
+ throw new Error("Locale must be a valid object.");
91
+ }
92
+ const titleKey = `${this.command.name}.${this.modalId}.title`;
93
+ if (!locale[titleKey]) {
94
+ throw new Error(`Missing locale entry for modal title: ${titleKey}`);
95
+ }
96
+ this.modalObject.setTitle(locale[titleKey]);
78
97
  this.modalInputs.forEach((input, id) => {
79
98
  const labelKey = `${this.command.name}.${id}.label`;
80
99
  const placeholderKey = `${this.command.name}.${id}.placeholder`;
81
- if (locale[labelKey]) {
82
- input.setLabel(locale[labelKey]);
100
+ if (!locale[labelKey]) {
101
+ throw new Error(`Missing locale entry for text input label: ${labelKey}`);
83
102
  }
84
- if (locale[placeholderKey]) {
85
- input.setPlaceholder(locale[placeholderKey]);
103
+ if (!locale[placeholderKey]) {
104
+ throw new Error(`Missing locale entry for text input placeholder: ${placeholderKey}`);
86
105
  }
106
+ input.setLabel(locale[labelKey]);
107
+ input.setPlaceholder(locale[placeholderKey]);
87
108
  });
88
109
  return this.modalObject;
89
110
  }
@@ -26,6 +26,12 @@ class ModularSelectMenu {
26
26
  constructor(selectMenuId, command) {
27
27
  /** The function to execute when the select menu is interacted with. */
28
28
  this.execute = async () => { };
29
+ if (!selectMenuId || typeof selectMenuId !== "string") {
30
+ throw new Error("Select Menu ID must be a non-empty string.");
31
+ }
32
+ if (!command.name || typeof command.name !== "string") {
33
+ throw new Error("ModularCommand must have a valid name.");
34
+ }
29
35
  this.selectMenuId = selectMenuId;
30
36
  this.command = command;
31
37
  this.customId = `${command.name}_${selectMenuId}`;
@@ -52,6 +58,9 @@ class ModularSelectMenu {
52
58
  * @returns {this} The current ModularSelectMenu instance for method chaining.
53
59
  */
54
60
  setExecute(executeFunction) {
61
+ if (!executeFunction || typeof executeFunction !== "function") {
62
+ throw new Error("Execute function must be a valid function.");
63
+ }
55
64
  this.execute = executeFunction;
56
65
  return this;
57
66
  }
@@ -62,6 +71,9 @@ class ModularSelectMenu {
62
71
  * @returns {StringSelectMenuOptionBuilder} The created option instance for further configuration (e.g., `setDefault`).
63
72
  */
64
73
  addOption(value) {
74
+ if (!value || typeof value !== "string") {
75
+ throw new Error("Option value must be a non-empty string.");
76
+ }
65
77
  const option = new discord_js_1.StringSelectMenuOptionBuilder().setValue(value);
66
78
  this.options.set(value, option);
67
79
  return option;
@@ -72,6 +84,9 @@ class ModularSelectMenu {
72
84
  * @returns {StringSelectMenuBuilder} The fully constructed select menu object ready to be sent to a user.
73
85
  */
74
86
  build(locale) {
87
+ if (!locale || typeof locale !== "object") {
88
+ throw new Error("Locale must be a valid object.");
89
+ }
75
90
  const placeholderKey = `${this.command.name}.${this.selectMenuId}.placeholder`;
76
91
  if (locale[placeholderKey]) {
77
92
  this.selectMenuObject.setPlaceholder(locale[placeholderKey]);
@@ -50,35 +50,64 @@ const cooldown_1 = __importStar(require("./cooldown"));
50
50
  const locales_1 = require("./locales");
51
51
  /**
52
52
  * @description Gets localized description for a subcommand.
53
+ * Supports multiple languages and falls back to default description if not found.
53
54
  * @param {ModularCommand} command The command instance.
54
55
  * @param {string} subCommandName The name of the subcommand.
55
56
  * @param {string} defaultDescription The default description.
57
+ * @param {string} locale The locale to use (defaults to EnglishUS).
56
58
  * @returns {string} The localized description.
57
59
  */
58
- function getLocalizedSubCommandDescription(command, subCommandName, defaultDescription) {
60
+ function getLocalizedSubCommandDescription(command, subCommandName, defaultDescription, locale = discord_js_1.Locale.EnglishUS) {
59
61
  if (!command.subCommandLocalizations)
60
62
  return defaultDescription;
61
63
  const localizations = command.subCommandLocalizations;
64
+ const key = `${subCommandName}.description`;
65
+ // Try to get the localization for the requested locale
66
+ const targetLocalizations = localizations[locale];
67
+ if (targetLocalizations?.[key]) {
68
+ return targetLocalizations[key];
69
+ }
70
+ // Fall back to English if the requested locale is not found
62
71
  const enLocalizations = localizations[discord_js_1.Locale.EnglishUS];
63
- return enLocalizations?.[`${subCommandName}.description`] || defaultDescription;
72
+ if (enLocalizations?.[key]) {
73
+ return enLocalizations[key];
74
+ }
75
+ else {
76
+ throw new Error(`Missing localization for subcommand '${subCommandName}' in command '${command.name}'`);
77
+ }
64
78
  }
65
79
  /**
66
80
  * @description Gets localized description for a subcommand option.
81
+ * Supports multiple languages and falls back to default description if not found.
67
82
  * @param {ModularCommand} command The command instance.
68
83
  * @param {string} subCommandName The name of the subcommand.
69
84
  * @param {string} optionName The name of the option.
70
85
  * @param {string} defaultDescription The default description.
86
+ * @param {string} locale The locale to use (defaults to EnglishUS).
71
87
  * @returns {string} The localized description.
72
88
  */
73
- function getLocalizedOptionDescription(command, subCommandName, optionName, defaultDescription) {
89
+ function getLocalizedOptionDescription(command, subCommandName, optionName, defaultDescription, locale = discord_js_1.Locale.EnglishUS) {
74
90
  if (!command.subCommandLocalizations)
75
91
  return defaultDescription;
76
92
  const localizations = command.subCommandLocalizations;
93
+ const key = `${subCommandName}.${optionName}.description`;
94
+ // Try to get the localization for the requested locale
95
+ const targetLocalizations = localizations[locale];
96
+ if (targetLocalizations?.[key]) {
97
+ return targetLocalizations[key];
98
+ }
99
+ // Fall back to English if the requested locale is not found
77
100
  const enLocalizations = localizations[discord_js_1.Locale.EnglishUS];
78
- return enLocalizations?.[`${subCommandName}.${optionName}.description`] || defaultDescription;
101
+ if (enLocalizations?.[key]) {
102
+ return enLocalizations[key];
103
+ }
104
+ else {
105
+ throw new Error(`Missing localization for option '${optionName}' in subcommand '${subCommandName}' for command '${command.name}'`);
106
+ }
79
107
  }
80
108
  /**
81
109
  * @description Creates an option builder function with common configuration.
110
+ * Supports multi-language descriptions through LocalizationMap.
82
111
  * @param {CommandOption} opt The option configuration.
83
112
  * @param {string} description The resolved description.
84
113
  * @returns {Function} The option builder function.
@@ -140,8 +169,14 @@ function processSubCommands(commandBuilder, command, options) {
140
169
  return;
141
170
  command.subCommands.forEach(subCmd => {
142
171
  commandBuilder.addSubcommand((subcommand) => {
172
+ if (typeof subCmd.name !== 'string' || subCmd.name.trim() === '') {
173
+ throw new Error("A subcommand is missing a name.");
174
+ }
143
175
  // Get localized description for subcommand
144
176
  const subCmdDescription = getLocalizedSubCommandDescription(command, subCmd.name, subCmd.description);
177
+ if (typeof subCmdDescription !== 'string' || subCmdDescription.trim() === '') {
178
+ throw new Error(`Subcommand '${subCmd.name}' is missing a description.`);
179
+ }
145
180
  subcommand
146
181
  .setName(subCmd.name)
147
182
  .setDescription(subCmdDescription);
@@ -378,10 +413,12 @@ function createSelectMenuExecutor(command) {
378
413
  function RegisterCommand(commands) {
379
414
  commands = Array.isArray(commands) ? commands : [commands];
380
415
  return commands.map(command => {
381
- if (command.name === undefined)
416
+ if (typeof command.name !== 'string' || command.name.trim() === '') {
382
417
  throw new Error("A command is missing a name.");
383
- if (command.description === undefined)
418
+ }
419
+ if (typeof command.description !== 'string' || command.description.trim() === '') {
384
420
  throw new Error(`Command "${command.name}" is missing a description.`);
421
+ }
385
422
  // Build SlashCommand Data
386
423
  const commandBuilder = new discord_js_1.SlashCommandBuilder()
387
424
  .setName(command.name)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-discord-modularcommand",
3
- "version": "3.0.0",
3
+ "version": "3.2.0",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "discord",
@@ -9,7 +9,7 @@
9
9
  ],
10
10
  "repository": {
11
11
  "type": "git",
12
- "url": "https://github.com/vicentefelipechile/js-discord-modularcommand.git"
12
+ "url": "git+https://github.com/vicentefelipechile/js-discord-modularcommand.git"
13
13
  },
14
14
  "license": "MIT",
15
15
  "author": "vicentefelipechile",