stoatx 0.7.7 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
9
  var __export = (target, all) => {
9
10
  for (var name in all)
10
11
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -31,15 +32,29 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
32
  // src/index.ts
32
33
  var index_exports = {};
33
34
  __export(index_exports, {
35
+ Arg: () => Arg,
34
36
  Client: () => Client,
37
+ CommandGroup: () => CommandGroup,
35
38
  CommandRegistry: () => CommandRegistry,
39
+ CommandValidationError: () => CommandValidationError,
36
40
  DefaultCooldownManager: () => DefaultCooldownManager,
41
+ FetchFailedError: () => FetchFailedError,
37
42
  Guard: () => Guard,
43
+ Injectable: () => Injectable,
44
+ InvalidMentionError: () => InvalidMentionError,
45
+ InvalidTypeError: () => InvalidTypeError,
38
46
  METADATA_KEYS: () => METADATA_KEYS,
47
+ MissingArgumentError: () => MissingArgumentError,
48
+ MissingOptionError: () => MissingOptionError,
49
+ NoServerContextError: () => NoServerContextError,
39
50
  On: () => On,
40
51
  Once: () => Once,
52
+ Option: () => Option,
53
+ PARAM_TYPE_MAP: () => PARAM_TYPE_MAP,
41
54
  SimpleCommand: () => SimpleCommand,
42
55
  Stoat: () => Stoat,
56
+ StoatxError: () => StoatxError,
57
+ SubCommand: () => SubCommand,
43
58
  buildSimpleCommandMetadata: () => buildSimpleCommandMetadata,
44
59
  getEventsMetadata: () => getEventsMetadata,
45
60
  getGuards: () => getGuards,
@@ -48,6 +63,35 @@ __export(index_exports, {
48
63
  });
49
64
  module.exports = __toCommonJS(index_exports);
50
65
 
66
+ // src/types.ts
67
+ var import_client = require("@stoatx/client");
68
+ var PARAM_TYPE_MAP = /* @__PURE__ */ new Map([
69
+ [
70
+ String,
71
+ "string"
72
+ ],
73
+ [
74
+ Number,
75
+ "number"
76
+ ],
77
+ [
78
+ Boolean,
79
+ "boolean"
80
+ ],
81
+ [
82
+ import_client.User,
83
+ "user"
84
+ ],
85
+ [
86
+ import_client.BaseChannel,
87
+ "channel"
88
+ ],
89
+ [
90
+ import_client.Role,
91
+ "role"
92
+ ]
93
+ ]);
94
+
51
95
  // src/decorators/Stoat.ts
52
96
  var import_reflect_metadata = require("reflect-metadata");
53
97
 
@@ -56,14 +100,22 @@ var METADATA_KEYS = {
56
100
  IS_STOAT_CLASS: /* @__PURE__ */ Symbol("stoatx:stoat:isClass"),
57
101
  SIMPLE_COMMANDS: /* @__PURE__ */ Symbol("stoatx:stoat:simpleCommands"),
58
102
  GUARDS: "stoatx:command:guards",
59
- EVENTS: /* @__PURE__ */ Symbol("stoatx:stoat:events")
103
+ EVENTS: /* @__PURE__ */ Symbol("stoatx:stoat:events"),
104
+ ARGS: /* @__PURE__ */ Symbol("stoatx:param:args"),
105
+ OPTIONS: /* @__PURE__ */ Symbol("stoatx:param:options"),
106
+ INJECTABLE: /* @__PURE__ */ Symbol("stoatx:injectable"),
107
+ SUBCOMMAND: /* @__PURE__ */ Symbol("stoatx:subcommand"),
108
+ COMMAND_GROUP: /* @__PURE__ */ Symbol("stoatx:commandGroup")
60
109
  };
61
110
 
62
111
  // src/decorators/store.ts
63
112
  var DecoratorStore = class _DecoratorStore {
113
+ static {
114
+ __name(this, "DecoratorStore");
115
+ }
64
116
  static instance;
65
117
  /** Stoat classes with their SimpleCommand methods */
66
- stoatClasses = /* @__PURE__ */ new Map();
118
+ stoatClasses = /* @__PURE__ */ new Set();
67
119
  /** Registered commands from @Stoat/@SimpleCommand decorators */
68
120
  commands = [];
69
121
  /** Whether the store has been initialized */
@@ -77,49 +129,46 @@ var DecoratorStore = class _DecoratorStore {
77
129
  return _DecoratorStore.instance;
78
130
  }
79
131
  /**
80
- * Register a @Stoat decorated class
81
- */
132
+ * Register a @Stoat decorated class
133
+ */
82
134
  registerStoatClass(classConstructor) {
83
- if (!this.stoatClasses.has(classConstructor)) {
84
- const instance = new classConstructor();
85
- this.stoatClasses.set(classConstructor, instance);
86
- }
135
+ this.stoatClasses.add(classConstructor);
87
136
  }
88
137
  /**
89
- * Get all registered Stoat classes with their instances
90
- */
138
+ * Get all registered Stoat classes with their instances
139
+ */
91
140
  getStoatClasses() {
92
141
  return this.stoatClasses;
93
142
  }
94
143
  /**
95
- * Add a registered command
96
- */
144
+ * Add a registered command
145
+ */
97
146
  addCommand(command) {
98
147
  this.commands.push(command);
99
148
  }
100
149
  /**
101
- * Get all registered commands
102
- */
150
+ * Get all registered commands
151
+ */
103
152
  getCommands() {
104
153
  return this.commands;
105
154
  }
106
155
  /**
107
- * Clear all registered classes (useful for testing)
108
- */
156
+ * Clear all registered classes (useful for testing)
157
+ */
109
158
  clear() {
110
159
  this.stoatClasses.clear();
111
160
  this.commands = [];
112
161
  this.initialized = false;
113
162
  }
114
163
  /**
115
- * Mark as initialized
116
- */
164
+ * Mark as initialized
165
+ */
117
166
  markInitialized() {
118
167
  this.initialized = true;
119
168
  }
120
169
  /**
121
- * Check if initialized
122
- */
170
+ * Check if initialized
171
+ */
123
172
  isInitialized() {
124
173
  return this.initialized;
125
174
  }
@@ -133,9 +182,11 @@ function Stoat() {
133
182
  decoratorStore.registerStoatClass(target);
134
183
  };
135
184
  }
185
+ __name(Stoat, "Stoat");
136
186
  function isStoatClass(target) {
137
187
  return Reflect.getMetadata(METADATA_KEYS.IS_STOAT_CLASS, target) === true;
138
188
  }
189
+ __name(isStoatClass, "isStoatClass");
139
190
 
140
191
  // src/decorators/SimpleCommand.ts
141
192
  var import_reflect_metadata2 = require("reflect-metadata");
@@ -151,22 +202,35 @@ function SimpleCommand(options = {}) {
151
202
  return descriptor;
152
203
  };
153
204
  }
205
+ __name(SimpleCommand, "SimpleCommand");
154
206
  function getSimpleCommands(target) {
155
207
  return Reflect.getMetadata(METADATA_KEYS.SIMPLE_COMMANDS, target) || [];
156
208
  }
209
+ __name(getSimpleCommands, "getSimpleCommands");
157
210
 
158
211
  // src/decorators/Guard.ts
159
212
  var import_reflect_metadata3 = require("reflect-metadata");
160
213
  function Guard(guardClass) {
161
- return (target) => {
162
- const existingGuards = Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];
163
- existingGuards.push(guardClass);
164
- Reflect.defineMetadata(METADATA_KEYS.GUARDS, existingGuards, target);
214
+ return (target, propertyKey) => {
215
+ if (propertyKey) {
216
+ const existingGuards = Reflect.getMetadata(METADATA_KEYS.GUARDS, target, propertyKey) || [];
217
+ existingGuards.push(guardClass);
218
+ Reflect.defineMetadata(METADATA_KEYS.GUARDS, existingGuards, target, propertyKey);
219
+ } else {
220
+ const existingGuards = Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];
221
+ existingGuards.push(guardClass);
222
+ Reflect.defineMetadata(METADATA_KEYS.GUARDS, existingGuards, target);
223
+ }
165
224
  };
166
225
  }
167
- function getGuards(target) {
226
+ __name(Guard, "Guard");
227
+ function getGuards(target, propertyKey) {
228
+ if (propertyKey) {
229
+ return Reflect.getMetadata(METADATA_KEYS.GUARDS, target.prototype, propertyKey) || [];
230
+ }
168
231
  return Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];
169
232
  }
233
+ __name(getGuards, "getGuards");
170
234
 
171
235
  // src/decorators/Events.ts
172
236
  var import_reflect_metadata4 = require("reflect-metadata");
@@ -183,18 +247,88 @@ function createEventDecorator(event, type) {
183
247
  return descriptor;
184
248
  };
185
249
  }
250
+ __name(createEventDecorator, "createEventDecorator");
186
251
  function On(event) {
187
252
  return createEventDecorator(event, "on");
188
253
  }
254
+ __name(On, "On");
189
255
  function Once(event) {
190
256
  return createEventDecorator(event, "once");
191
257
  }
258
+ __name(Once, "Once");
192
259
  function getEventsMetadata(target) {
193
260
  return Reflect.getMetadata(METADATA_KEYS.EVENTS, target) || [];
194
261
  }
262
+ __name(getEventsMetadata, "getEventsMetadata");
263
+
264
+ // src/decorators/Arg.ts
265
+ var import_reflect_metadata5 = require("reflect-metadata");
266
+ function Arg(options = {}) {
267
+ return (target, propertyKey, parameterIndex) => {
268
+ const existing = Reflect.getMetadata(METADATA_KEYS.ARGS, target, propertyKey) || [];
269
+ existing.push({
270
+ ...options,
271
+ index: parameterIndex
272
+ });
273
+ Reflect.defineMetadata(METADATA_KEYS.ARGS, existing, target, propertyKey);
274
+ };
275
+ }
276
+ __name(Arg, "Arg");
277
+ function getArgs(target, propertyKey) {
278
+ return Reflect.getMetadata(METADATA_KEYS.ARGS, target, propertyKey) || [];
279
+ }
280
+ __name(getArgs, "getArgs");
281
+
282
+ // src/decorators/Option.ts
283
+ var import_reflect_metadata6 = require("reflect-metadata");
284
+ function Option(options) {
285
+ return (target, propertyKey, parameterIndex) => {
286
+ const existing = Reflect.getMetadata(METADATA_KEYS.OPTIONS, target, propertyKey) || [];
287
+ existing.push({
288
+ ...options,
289
+ index: parameterIndex
290
+ });
291
+ Reflect.defineMetadata(METADATA_KEYS.OPTIONS, existing, target, propertyKey);
292
+ };
293
+ }
294
+ __name(Option, "Option");
295
+ function getOptions(target, propertyKey) {
296
+ return Reflect.getMetadata(METADATA_KEYS.OPTIONS, target, propertyKey) || [];
297
+ }
298
+ __name(getOptions, "getOptions");
299
+
300
+ // src/decorators/Injectable.ts
301
+ var import_reflect_metadata7 = require("reflect-metadata");
302
+ function Injectable() {
303
+ return (target) => {
304
+ Reflect.defineMetadata(METADATA_KEYS.INJECTABLE, true, target);
305
+ };
306
+ }
307
+ __name(Injectable, "Injectable");
308
+
309
+ // src/decorators/CommandGroup.ts
310
+ var import_reflect_metadata8 = require("reflect-metadata");
311
+ function CommandGroup(name) {
312
+ return (target) => {
313
+ Reflect.defineMetadata(METADATA_KEYS.COMMAND_GROUP, name, target);
314
+ };
315
+ }
316
+ __name(CommandGroup, "CommandGroup");
317
+
318
+ // src/decorators/SubCommand.ts
319
+ var import_reflect_metadata9 = require("reflect-metadata");
320
+ function SubCommand(options) {
321
+ const opts = typeof options === "string" ? {
322
+ name: options
323
+ } : options;
324
+ return (target, propertyKey) => {
325
+ Reflect.defineMetadata(METADATA_KEYS.SUBCOMMAND, opts, target, propertyKey);
326
+ };
327
+ }
328
+ __name(SubCommand, "SubCommand");
195
329
 
196
330
  // src/decorators/utils.ts
197
- function buildSimpleCommandMetadata(options, methodName, category) {
331
+ function buildSimpleCommandMetadata(options, methodName, category, params) {
198
332
  return {
199
333
  name: options.name ?? methodName.toLowerCase(),
200
334
  description: options.description ?? "No description provided",
@@ -202,11 +336,30 @@ function buildSimpleCommandMetadata(options, methodName, category) {
202
336
  permissions: options.permissions ?? [],
203
337
  category: options.category ?? category ?? "uncategorized",
204
338
  cooldown: options.cooldown ?? 0,
205
- ...options.cooldownStorage !== void 0 ? { cooldownStorage: options.cooldownStorage } : {},
339
+ ...options.cooldownStorage !== void 0 ? {
340
+ cooldownStorage: options.cooldownStorage
341
+ } : {},
206
342
  nsfw: options.nsfw ?? false,
207
- ownerOnly: options.ownerOnly ?? false
343
+ ownerOnly: options.ownerOnly ?? false,
344
+ params
208
345
  };
209
346
  }
347
+ __name(buildSimpleCommandMetadata, "buildSimpleCommandMetadata");
348
+ function getSubCommands(target) {
349
+ const methods = Object.getOwnPropertyNames(target.prototype);
350
+ const subCommands = [];
351
+ for (const method of methods) {
352
+ const options = Reflect.getMetadata(METADATA_KEYS.SUBCOMMAND, target.prototype, method);
353
+ if (options) {
354
+ subCommands.push({
355
+ methodName: method,
356
+ options
357
+ });
358
+ }
359
+ }
360
+ return subCommands;
361
+ }
362
+ __name(getSubCommands, "getSubCommands");
210
363
 
211
364
  // src/registry.ts
212
365
  var path = __toESM(require("path"));
@@ -214,6 +367,9 @@ var fs = __toESM(require("fs/promises"));
214
367
  var import_node_url = require("url");
215
368
  var import_tinyglobby = require("tinyglobby");
216
369
  var CommandRegistry = class _CommandRegistry {
370
+ static {
371
+ __name(this, "CommandRegistry");
372
+ }
217
373
  static DEFAULT_AUTO_DISCOVERY_IGNORES = [
218
374
  "**/node_modules/**",
219
375
  "**/.git/**",
@@ -226,23 +382,27 @@ var CommandRegistry = class _CommandRegistry {
226
382
  registeredEvents = [];
227
383
  extensions;
228
384
  processedStoatClasses = /* @__PURE__ */ new Set();
229
- constructor(extensions = [".js", ".mjs", ".cjs"]) {
385
+ container;
386
+ constructor(container, extensions = [
387
+ ".js",
388
+ ".mjs",
389
+ ".cjs"
390
+ ]) {
391
+ this.container = container;
230
392
  this.extensions = extensions;
231
393
  }
232
- /**
233
- * Get the number of registered commands
234
- */
235
394
  get size() {
236
395
  return this.commands.size;
237
396
  }
238
- /**
239
- * Load commands from a directory using glob pattern matching
240
- */
241
397
  async loadFromDirectory(directory) {
242
398
  const patterns = this.extensions.map((ext) => path.join(directory, "**", `*${ext}`).replace(/\\/g, "/"));
243
399
  for (const pattern of patterns) {
244
400
  const files = await (0, import_tinyglobby.glob)(pattern, {
245
- ignore: ["**/*.d.ts", "**/*.test.ts", "**/*.spec.ts"],
401
+ ignore: [
402
+ "**/*.d.ts",
403
+ "**/*.test.ts",
404
+ "**/*.spec.ts"
405
+ ],
246
406
  absolute: true
247
407
  });
248
408
  for (const file of files) {
@@ -251,26 +411,26 @@ var CommandRegistry = class _CommandRegistry {
251
411
  }
252
412
  console.log(`[Stoatx] Loaded ${this.commands.size} command(s) and ${this.registeredEvents.length} event(s)`);
253
413
  }
254
- /**
255
- * Auto-discover command files across one or more roots.
256
- */
257
414
  async autoDiscover(options = {}) {
258
- const roots = options.roots?.length ? options.roots : [process.cwd()];
415
+ const roots = options.roots?.length ? options.roots : [
416
+ process.cwd()
417
+ ];
259
418
  const includePatterns = options.include?.length ? options.include : this.getDefaultAutoDiscoveryPatterns();
260
- const patterns = roots.flatMap(
261
- (root) => includePatterns.map((pattern) => path.join(root, pattern).replace(/\\/g, "/"))
262
- );
419
+ const patterns = roots.flatMap((root) => includePatterns.map((pattern) => path.join(root, pattern).replace(/\\/g, "/")));
263
420
  const files = await (0, import_tinyglobby.glob)(patterns, {
264
- ignore: [..._CommandRegistry.DEFAULT_AUTO_DISCOVERY_IGNORES, ...options.ignore ?? []],
421
+ ignore: [
422
+ ..._CommandRegistry.DEFAULT_AUTO_DISCOVERY_IGNORES,
423
+ ...options.ignore ?? []
424
+ ],
265
425
  absolute: true
266
426
  });
267
- const uniqueFiles = [...new Set(files)];
268
- let candidateFiles = 0;
427
+ const uniqueFiles = [
428
+ ...new Set(files)
429
+ ];
269
430
  for (const file of uniqueFiles) {
270
431
  if (!await this.isLikelyCommandModule(file)) {
271
432
  continue;
272
433
  }
273
- candidateFiles++;
274
434
  const baseDir = roots.find((root) => {
275
435
  const relative2 = path.relative(root, file);
276
436
  return relative2 && !relative2.startsWith("..") && !path.isAbsolute(relative2);
@@ -290,17 +450,22 @@ var CommandRegistry = class _CommandRegistry {
290
450
  return true;
291
451
  }
292
452
  }
293
- /**
294
- * Register a command instance
295
- */
296
453
  register(instance, metadata, classConstructor, methodName) {
297
- const name = metadata.name.toLowerCase();
454
+ const groupOptions = Reflect.getMetadata(METADATA_KEYS.COMMAND_GROUP, classConstructor);
455
+ const subCommandOptions = Reflect.getMetadata(METADATA_KEYS.SUBCOMMAND, instance, methodName);
456
+ const subCommandName = subCommandOptions?.name;
457
+ const name = groupOptions && subCommandName ? `${groupOptions.name}:${subCommandName}`.toLowerCase() : metadata.name.toLowerCase();
298
458
  if (this.commands.has(name)) {
299
459
  console.warn(`[Stoatx] Duplicate command name: ${name}. Skipping...`);
300
460
  return;
301
461
  }
302
462
  this.validateGuards(classConstructor, metadata.name);
303
- this.commands.set(name, { instance, metadata, methodName, classConstructor });
463
+ this.commands.set(name, {
464
+ instance,
465
+ metadata,
466
+ methodName,
467
+ classConstructor
468
+ });
304
469
  for (const alias of metadata.aliases) {
305
470
  const aliasLower = alias.toLowerCase();
306
471
  if (this.aliases.has(aliasLower) || this.commands.has(aliasLower)) {
@@ -310,42 +475,24 @@ var CommandRegistry = class _CommandRegistry {
310
475
  this.aliases.set(aliasLower, name);
311
476
  }
312
477
  }
313
- /**
314
- * Get a command by name or alias
315
- */
316
478
  get(name) {
317
479
  const lowerName = name.toLowerCase();
318
480
  const resolvedName = this.aliases.get(lowerName) ?? lowerName;
319
481
  return this.commands.get(resolvedName);
320
482
  }
321
- /**
322
- * Check if a command exists
323
- */
324
483
  has(name) {
325
484
  const lowerName = name.toLowerCase();
326
485
  return this.commands.has(lowerName) || this.aliases.has(lowerName);
327
486
  }
328
- /**
329
- * Get all registered commands
330
- */
331
487
  getAll() {
332
488
  return Array.from(this.commands.values());
333
489
  }
334
- /**
335
- * Get all command metadata
336
- */
337
490
  getAllMetadata() {
338
491
  return this.getAll().map((c) => c.metadata);
339
492
  }
340
- /**
341
- * Get all registered events
342
- */
343
493
  getEvents() {
344
494
  return this.registeredEvents;
345
495
  }
346
- /**
347
- * Get commands grouped by category
348
- */
349
496
  getByCategory() {
350
497
  const categories = /* @__PURE__ */ new Map();
351
498
  for (const cmd of this.commands.values()) {
@@ -356,95 +503,70 @@ var CommandRegistry = class _CommandRegistry {
356
503
  }
357
504
  return categories;
358
505
  }
359
- /**
360
- * Clear all commands
361
- */
362
506
  clear() {
363
507
  this.commands.clear();
364
508
  this.aliases.clear();
365
509
  this.registeredEvents.length = 0;
366
510
  this.processedStoatClasses.clear();
367
511
  }
368
- /**
369
- * Iterate over commands
370
- */
371
512
  [Symbol.iterator]() {
372
513
  return this.commands.entries();
373
514
  }
374
- /**
375
- * Iterate over command values
376
- */
377
515
  values() {
378
516
  return this.commands.values();
379
517
  }
380
- /**
381
- * Iterate over command names
382
- */
383
518
  keys() {
384
519
  return this.commands.keys();
385
520
  }
386
- /**
387
- * Validate that all guards on a command implement the required methods
388
- * @param commandClass
389
- * @param commandName
390
- * @private
391
- */
392
521
  validateGuards(commandClass, commandName) {
393
- const guards = Reflect.getMetadata("stoatx:command:guards", commandClass) || [];
522
+ const guards = Reflect.getMetadata(METADATA_KEYS.GUARDS, commandClass) || [];
394
523
  for (const GuardClass of guards) {
395
- const guardInstance = new GuardClass();
524
+ const guardInstance = this.container.resolve(GuardClass);
396
525
  if (typeof guardInstance.run !== "function") {
397
- console.error(
398
- `[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a run() method.`
399
- );
526
+ console.error(`[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a run() method.`);
400
527
  process.exit(1);
401
528
  }
402
529
  if (typeof guardInstance.guardFail !== "function") {
403
- console.error(
404
- `[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a guardFail() method.`
405
- );
406
- console.error(`[Stoatx] All guards must implement guardFail() to handle failed checks.`);
530
+ console.error(`[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a guardFail() method.`);
407
531
  process.exit(1);
408
532
  }
409
533
  }
410
534
  }
411
- /**
412
- * Load commands from a single file
413
- */
414
535
  async loadFile(filePath, baseDir) {
415
536
  try {
416
537
  const knownStoatClasses = new Set(decoratorStore.getStoatClasses().keys());
417
538
  const fileUrl = (0, import_node_url.pathToFileURL)(filePath).href;
418
539
  await import(fileUrl);
419
540
  const allStoatClasses = decoratorStore.getStoatClasses();
420
- for (const [stoatClass, stoatInstance] of allStoatClasses.entries()) {
541
+ for (const [stoatClass] of allStoatClasses.entries()) {
421
542
  if (knownStoatClasses.has(stoatClass) || this.processedStoatClasses.has(stoatClass)) {
422
543
  continue;
423
544
  }
424
- this.registerStoatClassCommands(stoatClass, stoatInstance, filePath, baseDir);
545
+ this.registerStoatClassCommands(stoatClass, filePath, baseDir);
425
546
  }
426
547
  } catch (error) {
427
548
  console.error(`[Stoatx] Failed to load command file: ${filePath}`, error);
428
549
  }
429
550
  }
430
- registerStoatClassCommands(stoatClass, instance, filePath, baseDir) {
551
+ registerStoatClassCommands(stoatClass, filePath, baseDir) {
552
+ const instance = this.container.resolve(stoatClass);
431
553
  const simpleCommands = getSimpleCommands(stoatClass);
554
+ const subCommands = getSubCommands(stoatClass);
432
555
  const events = getEventsMetadata(stoatClass);
433
556
  const category = this.getCategoryFromPath(filePath, baseDir);
434
- if (simpleCommands.length === 0 && events.length === 0) {
435
- console.warn(
436
- `[Stoatx] Class ${stoatClass.name} is decorated with @Stoat but has no @SimpleCommand, @On or @Once methods. Skipping...`
437
- );
557
+ const allCommands = [
558
+ ...simpleCommands,
559
+ ...subCommands
560
+ ];
561
+ if (allCommands.length === 0 && events.length === 0) {
438
562
  this.processedStoatClasses.add(stoatClass);
439
563
  return;
440
564
  }
441
- for (const cmdDef of simpleCommands) {
565
+ for (const cmdDef of allCommands) {
442
566
  const method = instance[cmdDef.methodName];
443
- if (typeof method !== "function") {
444
- console.warn(`[Stoatx] Method ${cmdDef.methodName} not found on ${stoatClass.name}. Skipping...`);
445
- continue;
446
- }
447
- const metadata = buildSimpleCommandMetadata(cmdDef.options, cmdDef.methodName, category);
567
+ if (typeof method !== "function") continue;
568
+ const params = this.buildParamSchema(stoatClass.prototype, cmdDef.methodName);
569
+ const metadata = buildSimpleCommandMetadata(cmdDef.options, cmdDef.methodName, category, params);
448
570
  this.register(instance, metadata, stoatClass, cmdDef.methodName);
449
571
  }
450
572
  for (const eventDef of events) {
@@ -463,21 +585,180 @@ var CommandRegistry = class _CommandRegistry {
463
585
  this.processedStoatClasses.add(stoatClass);
464
586
  }
465
587
  /**
466
- * Derive category from file path relative to base directory
467
- */
588
+ * Build the parameter schema for a command method by combining
589
+ * reflect-metadata param types with @Arg/@Option decorator metadata.
590
+ */
591
+ buildParamSchema(prototype, methodName) {
592
+ const paramTypes = Reflect.getMetadata("design:paramtypes", prototype, methodName) ?? [];
593
+ const argDefs = getArgs(prototype, methodName);
594
+ const optionDefs = getOptions(prototype, methodName);
595
+ const argByIndex = new Map(argDefs.map((a) => [
596
+ a.index,
597
+ a
598
+ ]));
599
+ const optionByIndex = new Map(optionDefs.map((o) => [
600
+ o.index,
601
+ o
602
+ ]));
603
+ const params = [];
604
+ for (let i = 0; i < paramTypes.length; i++) {
605
+ const reflectedType = paramTypes[i];
606
+ if (optionByIndex.has(i)) {
607
+ const optDef = optionByIndex.get(i);
608
+ const resolvedType = reflectedType ? PARAM_TYPE_MAP.get(reflectedType) ?? "string" : "string";
609
+ params.push({
610
+ index: i,
611
+ kind: "option",
612
+ resolvedType,
613
+ name: optDef.name,
614
+ required: optDef.required
615
+ });
616
+ continue;
617
+ }
618
+ if (argByIndex.has(i)) {
619
+ const argDef = argByIndex.get(i);
620
+ const resolvedType = reflectedType ? PARAM_TYPE_MAP.get(reflectedType) ?? "string" : "string";
621
+ params.push({
622
+ index: i,
623
+ kind: "arg",
624
+ resolvedType,
625
+ required: argDef.required,
626
+ fetch: argDef.fetch
627
+ });
628
+ continue;
629
+ }
630
+ params.push({
631
+ index: i,
632
+ kind: "ctx",
633
+ resolvedType: "ctx"
634
+ });
635
+ }
636
+ return params;
637
+ }
468
638
  getCategoryFromPath(filePath, baseDir) {
469
639
  const relative2 = path.relative(baseDir, filePath);
470
640
  const parts = relative2.split(path.sep);
471
- if (parts.length > 1) {
472
- return parts[0];
641
+ return parts.length > 1 ? parts[0] : void 0;
642
+ }
643
+ };
644
+
645
+ // src/handler.ts
646
+ var import_reflect_metadata11 = require("reflect-metadata");
647
+
648
+ // src/error.ts
649
+ var StoatxError = class extends Error {
650
+ static {
651
+ __name(this, "StoatxError");
652
+ }
653
+ constructor(message) {
654
+ super(message);
655
+ this.name = "StoatxError";
656
+ }
657
+ };
658
+ var CommandValidationError = class extends StoatxError {
659
+ static {
660
+ __name(this, "CommandValidationError");
661
+ }
662
+ paramName;
663
+ paramKind;
664
+ constructor(paramName, paramKind, message) {
665
+ super(message), this.paramName = paramName, this.paramKind = paramKind;
666
+ this.name = "CommandValidationError";
667
+ }
668
+ };
669
+ var MissingArgumentError = class extends CommandValidationError {
670
+ static {
671
+ __name(this, "MissingArgumentError");
672
+ }
673
+ constructor(paramName) {
674
+ super(paramName, "arg", `Missing required argument: \`<${paramName}>\``);
675
+ this.name = "MissingArgumentError";
676
+ }
677
+ };
678
+ var MissingOptionError = class extends CommandValidationError {
679
+ static {
680
+ __name(this, "MissingOptionError");
681
+ }
682
+ constructor(paramName, flagPrefix) {
683
+ super(paramName, "option", `Missing required option: \`${flagPrefix.repeat(2)}${paramName}\``);
684
+ this.name = "MissingOptionError";
685
+ }
686
+ };
687
+ var InvalidTypeError = class extends CommandValidationError {
688
+ static {
689
+ __name(this, "InvalidTypeError");
690
+ }
691
+ expected;
692
+ received;
693
+ constructor(paramName, paramKind, expected, received) {
694
+ super(paramName, paramKind, `Invalid value for \`${paramName}\`. Expected ${expected}, got \`${received}\`.`), this.expected = expected, this.received = received;
695
+ this.name = "InvalidTypeError";
696
+ }
697
+ };
698
+ var InvalidMentionError = class extends CommandValidationError {
699
+ static {
700
+ __name(this, "InvalidMentionError");
701
+ }
702
+ mentionKind;
703
+ rawValue;
704
+ constructor(paramName, paramKind, mentionKind, rawValue) {
705
+ super(paramName, paramKind, `Invalid ${mentionKind} mention for \`${paramName}\`.`), this.mentionKind = mentionKind, this.rawValue = rawValue;
706
+ this.name = "InvalidMentionError";
707
+ }
708
+ };
709
+ var FetchFailedError = class extends CommandValidationError {
710
+ static {
711
+ __name(this, "FetchFailedError");
712
+ }
713
+ mentionKind;
714
+ resolvedId;
715
+ constructor(paramName, paramKind, mentionKind, resolvedId) {
716
+ super(paramName, paramKind, `Could not fetch ${mentionKind} \`${resolvedId}\` for \`${paramName}\`.`), this.mentionKind = mentionKind, this.resolvedId = resolvedId;
717
+ this.name = "FetchFailedError";
718
+ }
719
+ };
720
+ var NoServerContextError = class extends CommandValidationError {
721
+ static {
722
+ __name(this, "NoServerContextError");
723
+ }
724
+ constructor(paramName, paramKind) {
725
+ super(paramName, paramKind, `Cannot fetch role for \`${paramName}\` outside of a server.`);
726
+ this.name = "NoServerContextError";
727
+ }
728
+ };
729
+
730
+ // src/di/Container.ts
731
+ var import_reflect_metadata10 = require("reflect-metadata");
732
+ var StoatxContainer = class {
733
+ static {
734
+ __name(this, "StoatxContainer");
735
+ }
736
+ instances = /* @__PURE__ */ new Map();
737
+ /**
738
+ * Resolves a class instance, injecting any required dependencies.
739
+ */
740
+ resolve(target) {
741
+ if (this.instances.has(target)) {
742
+ return this.instances.get(target);
473
743
  }
474
- return void 0;
744
+ const paramTypes = Reflect.getMetadata("design:paramtypes", target) || [];
745
+ const injections = paramTypes.map((param) => {
746
+ if (!param) {
747
+ throw new Error(`[Stoatx DI] Cannot resolve dependency for ${target.name}. Ensure all injected services are decorated with @Injectable().`);
748
+ }
749
+ return this.resolve(param);
750
+ });
751
+ const instance = new target(...injections);
752
+ this.instances.set(target, instance);
753
+ return instance;
475
754
  }
476
755
  };
477
756
 
478
757
  // src/handler.ts
479
- var import_reflect_metadata5 = require("reflect-metadata");
480
758
  var DefaultCooldownManager = class {
759
+ static {
760
+ __name(this, "DefaultCooldownManager");
761
+ }
481
762
  cooldowns = /* @__PURE__ */ new Map();
482
763
  check(ctx, metadata) {
483
764
  if (metadata.cooldown <= 0) return true;
@@ -510,27 +791,33 @@ var DefaultCooldownManager = class {
510
791
  }
511
792
  };
512
793
  var StoatxHandler = class {
794
+ static {
795
+ __name(this, "StoatxHandler");
796
+ }
513
797
  commandsDir;
514
798
  discoveryOptions;
799
+ // After
515
800
  prefixResolver;
516
801
  owners;
517
802
  registry;
518
803
  cooldownManager;
519
804
  disableMentionPrefix;
520
805
  client;
806
+ flagPrefix;
807
+ globalGuards;
808
+ container = new StoatxContainer();
521
809
  constructor(options) {
522
810
  this.client = options.client;
523
811
  this.commandsDir = options.commandsDir;
524
812
  this.discoveryOptions = options.discovery;
525
813
  this.prefixResolver = options.prefix;
526
814
  this.owners = new Set(options.owners ?? []);
527
- this.registry = new CommandRegistry(options.extensions);
815
+ this.registry = new CommandRegistry(this.container, options.extensions);
528
816
  this.disableMentionPrefix = options.disableMentionPrefix ?? false;
529
817
  this.cooldownManager = options.cooldownManager ?? new DefaultCooldownManager();
818
+ this.flagPrefix = options.flagPrefix || "-";
819
+ this.globalGuards = options.globalGuards ?? [];
530
820
  }
531
- /**
532
- * Initialize the handler - load all commands
533
- */
534
821
  async init() {
535
822
  if (this.commandsDir) {
536
823
  await this.registry.loadFromDirectory(this.commandsDir);
@@ -539,22 +826,16 @@ var StoatxHandler = class {
539
826
  }
540
827
  this.attachEvents();
541
828
  }
542
- /**
543
- * Attach registered events to the client
544
- */
545
829
  attachEvents() {
546
830
  const events = this.registry.getEvents();
547
831
  for (const eventDef of events) {
548
- const handler = async (...args) => {
832
+ const handler = /* @__PURE__ */ __name(async (...args) => {
549
833
  try {
550
834
  await eventDef.instance[eventDef.methodName](...args, this.client);
551
835
  } catch (error) {
552
- console.error(
553
- `[Stoatx] Event Handler Error in @${eventDef.type === "on" ? "On" : "Once"}('${eventDef.event}'):`,
554
- error
555
- );
836
+ console.error(`[Stoatx] Event Handler Error in @${eventDef.type === "on" ? "On" : "Once"}('${eventDef.event}'):`, error);
556
837
  }
557
- };
838
+ }, "handler");
558
839
  const eventName = eventDef.event;
559
840
  if (eventDef.type === "once") {
560
841
  this.client.once(eventName, handler);
@@ -563,73 +844,86 @@ var StoatxHandler = class {
563
844
  }
564
845
  }
565
846
  }
566
- /**
567
- * Parse a raw message into command context
568
- */
847
+ parseRawInput(rawArgs) {
848
+ const args = [];
849
+ const flags = {};
850
+ for (let i = 0; i < rawArgs.length; i++) {
851
+ const arg = rawArgs[i];
852
+ if (arg === void 0) continue;
853
+ if (arg.startsWith(this.flagPrefix)) {
854
+ let key = arg;
855
+ while (key.startsWith(this.flagPrefix)) {
856
+ key = key.slice(this.flagPrefix.length);
857
+ }
858
+ const nextArg = rawArgs[i + 1];
859
+ if (nextArg !== void 0 && !nextArg.startsWith(this.flagPrefix)) {
860
+ flags[key] = nextArg;
861
+ i++;
862
+ } else {
863
+ flags[key] = true;
864
+ }
865
+ } else {
866
+ args.push(arg);
867
+ }
868
+ }
869
+ return {
870
+ args,
871
+ flags
872
+ };
873
+ }
569
874
  async parseMessage(rawContent, message, meta) {
570
- const prefix = await this.resolvePrefix(meta.serverId);
571
- let usedPrefix = prefix;
875
+ const prefixes = await this.resolvePrefix(meta.serverId);
876
+ let usedPrefix = "";
572
877
  let withoutPrefix = "";
573
- if (rawContent.startsWith(prefix)) {
574
- withoutPrefix = rawContent.slice(prefix.length).trim();
575
- usedPrefix = prefix;
576
- } else if (!this.disableMentionPrefix && rawContent.match(/^<@!?[\w]+>/)) {
577
- const mentionMatch = rawContent.match(/^<@!?([\w]+)>\s*/);
878
+ const matchedPrefix = prefixes.find((p) => rawContent.startsWith(p));
879
+ if (matchedPrefix !== void 0) {
880
+ usedPrefix = matchedPrefix;
881
+ withoutPrefix = rawContent.slice(matchedPrefix.length).trim();
882
+ } else if (!this.disableMentionPrefix && rawContent.match(/^<@!?\w+>/)) {
883
+ const mentionMatch = rawContent.match(/^<@!?(\w+)>\s*/);
578
884
  if (mentionMatch) {
579
885
  const mentionedId = mentionMatch[1];
580
886
  const botId = this.client.user?.id;
581
887
  if (botId && mentionedId === botId) {
582
888
  usedPrefix = mentionMatch[0];
583
889
  withoutPrefix = rawContent.slice(mentionMatch[0].length).trim();
584
- } else {
585
890
  }
586
891
  }
587
892
  }
588
- if (!withoutPrefix) {
589
- return null;
590
- }
591
- const [commandName, ...args] = withoutPrefix.split(/\s+/);
592
- if (!commandName) {
593
- return null;
893
+ if (!withoutPrefix) return null;
894
+ const parts = withoutPrefix.split(/\s+/);
895
+ const part1 = parts[0];
896
+ const part2 = parts[1];
897
+ if (!part1) return null;
898
+ let commandKey = part1.toLowerCase();
899
+ let remainingParts = parts.slice(1);
900
+ if (part2 && this.registry.has(`${part1}:${part2}`)) {
901
+ commandKey = `${part1}:${part2}`.toLowerCase();
902
+ remainingParts = parts.slice(2);
594
903
  }
904
+ const { args, flags } = this.parseRawInput(remainingParts);
595
905
  return {
596
906
  client: this.client,
597
907
  content: rawContent,
598
908
  authorId: meta.authorId,
599
909
  channelId: meta.channelId,
600
910
  serverId: meta.serverId,
601
- args,
602
911
  prefix: usedPrefix,
603
- commandName: commandName.toLowerCase(),
912
+ commandName: commandKey,
604
913
  reply: meta.reply,
605
- message
914
+ message,
915
+ _rawArgs: args,
916
+ _rawFlags: flags
606
917
  };
607
918
  }
608
- /**
609
- * Handle a message object using the configured message adapter
610
- *
611
- * @example
612
- * ```ts
613
- * // With message adapter configured
614
- * client.on('messageCreate', (message) => {
615
- * handler.handle(message);
616
- * });
617
- * ```
618
- */
619
919
  async handle(message) {
620
- if (!message.channel || !message.author || !message.content) {
621
- return false;
622
- }
623
- if (message.author.bot) {
624
- return false;
625
- }
920
+ if (!message.channel || !message.author || !message.content) return false;
921
+ if (message.author.bot) return false;
626
922
  const rawContent = message.content;
627
923
  const authorId = message.author.id;
628
924
  const channelId = message.channel.id;
629
925
  const serverId = message.server?.id;
630
- const reply = async (content) => {
631
- return await message.channel.send(content);
632
- };
926
+ const reply = /* @__PURE__ */ __name(async (content) => await message.channel.send(content), "reply");
633
927
  await this.handleMessage(rawContent, message, {
634
928
  authorId,
635
929
  channelId,
@@ -638,43 +932,20 @@ var StoatxHandler = class {
638
932
  });
639
933
  return true;
640
934
  }
641
- /**
642
- * Handle a raw message string with metadata
643
- *
644
- * @example
645
- * ```ts
646
- * // Manual usage without message adapter
647
- * client.on('messageCreate', (message) => {
648
- * handler.handleMessage(message.content, message, {
649
- * authorId: message.author.id,
650
- * channelId: message.channel.id,
651
- * serverId: message.server?.id,
652
- * reply: (content) => message.channel.sendMessage(content),
653
- * });
654
- * });
655
- * ```
656
- */
657
935
  async handleMessage(rawContent, message, meta) {
658
936
  const ctx = await this.parseMessage(rawContent, message, meta);
659
- if (!ctx) {
660
- return;
661
- }
937
+ if (!ctx) return;
662
938
  await this.execute(ctx);
663
939
  }
664
- /**
665
- * Execute a command with the given context
666
- */
667
940
  async execute(ctx) {
668
941
  const registered = this.registry.get(ctx.commandName);
669
- if (!registered) {
670
- return false;
671
- }
942
+ if (!registered) return false;
672
943
  const { instance, metadata, methodName, classConstructor } = registered;
673
944
  if (metadata.ownerOnly && !this.owners.has(ctx.authorId)) {
674
945
  await ctx.reply("This command is owner-only.");
675
946
  return false;
676
947
  }
677
- if (metadata.permissions) {
948
+ if (metadata.permissions.length > 0) {
678
949
  const server = ctx.message.server;
679
950
  const member = server ? await server.members.fetch(ctx.authorId) : null;
680
951
  if (!member || !member.permissions.has(metadata.permissions)) {
@@ -687,9 +958,16 @@ var StoatxHandler = class {
687
958
  return false;
688
959
  }
689
960
  }
690
- const guards = Reflect.getMetadata("stoatx:command:guards", classConstructor) || [];
691
- for (const guardClass of guards) {
692
- const guardInstance = new guardClass();
961
+ const globalGuards = this.globalGuards;
962
+ const classGuards = Reflect.getMetadata(METADATA_KEYS.GUARDS, classConstructor) || [];
963
+ const methodGuards = Reflect.getMetadata(METADATA_KEYS.GUARDS, instance, methodName) || [];
964
+ const allGuards = [
965
+ ...globalGuards,
966
+ ...classGuards,
967
+ ...methodGuards
968
+ ];
969
+ for (const guardClass of allGuards) {
970
+ const guardInstance = this.container.resolve(guardClass);
693
971
  if (typeof guardInstance.run === "function") {
694
972
  const guardResult = await guardInstance.run(ctx);
695
973
  if (!guardResult) {
@@ -711,42 +989,152 @@ var StoatxHandler = class {
711
989
  }
712
990
  return false;
713
991
  }
992
+ const resolvedParams = await this.resolveParams(metadata.params, ctx, instance);
993
+ if (resolvedParams === null) return false;
714
994
  try {
715
995
  if (metadata.cooldown > 0) {
716
996
  await this.cooldownManager.set(ctx, metadata);
717
997
  }
718
- await instance[methodName](ctx);
998
+ await instance[methodName](...resolvedParams);
719
999
  return true;
720
1000
  } catch (error) {
721
1001
  if (typeof instance.onError === "function") {
722
1002
  await instance.onError(ctx, error);
723
1003
  } else {
724
1004
  console.error(`[Stoatx] Error in command ${metadata.name}:`, error);
1005
+ await ctx.reply("Something went wrong. Please try again later.");
725
1006
  }
726
1007
  return false;
727
1008
  }
728
1009
  }
729
1010
  /**
730
- * Get the command registry
731
- */
1011
+ * Report a validation error to the instance via onValidationError → onError → default reply
1012
+ */
1013
+ async reportValidationError(instance, ctx, error) {
1014
+ if (typeof instance.onValidationError === "function") {
1015
+ await instance.onValidationError(ctx, error);
1016
+ } else if (typeof instance.onError === "function") {
1017
+ await instance.onError(ctx, error);
1018
+ } else {
1019
+ await ctx.reply(error.message);
1020
+ }
1021
+ return null;
1022
+ }
1023
+ async resolveParams(params, ctx, instance) {
1024
+ const resolved = new Array(params.length);
1025
+ let argCursor = 0;
1026
+ for (const param of params) {
1027
+ if (param.kind === "ctx") {
1028
+ resolved[param.index] = ctx;
1029
+ continue;
1030
+ }
1031
+ if (param.kind === "arg") {
1032
+ const rawValue = ctx._rawArgs[argCursor++];
1033
+ if (rawValue === void 0) {
1034
+ if (param.required) {
1035
+ const paramName = param.name ?? `arg[${param.index}]`;
1036
+ return this.reportValidationError(instance, ctx, new MissingArgumentError(paramName));
1037
+ }
1038
+ resolved[param.index] = void 0;
1039
+ continue;
1040
+ }
1041
+ const value = await this.resolveValue(rawValue, param, ctx, instance, "arg");
1042
+ if (value === null) return null;
1043
+ resolved[param.index] = value;
1044
+ continue;
1045
+ }
1046
+ if (param.kind === "option") {
1047
+ const rawValue = ctx._rawFlags[param.name];
1048
+ if (rawValue === void 0) {
1049
+ if (param.required) {
1050
+ return this.reportValidationError(instance, ctx, new MissingOptionError(param.name, this.flagPrefix));
1051
+ }
1052
+ resolved[param.index] = void 0;
1053
+ continue;
1054
+ }
1055
+ const value = await this.resolveValue(String(rawValue), param, ctx, instance, "option");
1056
+ if (value === null) return null;
1057
+ resolved[param.index] = value;
1058
+ }
1059
+ }
1060
+ return resolved;
1061
+ }
1062
+ async resolveValue(rawValue, param, ctx, instance, kind) {
1063
+ const paramName = kind === "arg" ? param.name ?? `arg[${param.index}]` : param.name;
1064
+ switch (param.resolvedType) {
1065
+ case "string":
1066
+ return String(rawValue);
1067
+ case "number": {
1068
+ const num = Number(rawValue);
1069
+ if (isNaN(num)) {
1070
+ return this.reportValidationError(instance, ctx, new InvalidTypeError(paramName, kind, "a number", rawValue));
1071
+ }
1072
+ return num;
1073
+ }
1074
+ case "boolean":
1075
+ return rawValue === "false" ? false : Boolean(rawValue);
1076
+ case "user": {
1077
+ const match = rawValue.match(/^(?:<@!?)?([0-7][0-9A-HJKMNP-TV-Z]{25})>?$/i);
1078
+ if (!match) {
1079
+ return this.reportValidationError(instance, ctx, new InvalidMentionError(paramName, kind, "user", rawValue));
1080
+ }
1081
+ const userId = match[1];
1082
+ if (param.fetch) {
1083
+ try {
1084
+ return await this.client.users.fetch(userId);
1085
+ } catch {
1086
+ return this.reportValidationError(instance, ctx, new FetchFailedError(paramName, kind, "user", userId));
1087
+ }
1088
+ }
1089
+ return this.client.users.cache.get(userId) ?? userId;
1090
+ }
1091
+ case "channel": {
1092
+ const match = rawValue.match(/^(?:<#)?([0-7][0-9A-HJKMNP-TV-Z]{25})>?$/i);
1093
+ if (!match) {
1094
+ return this.reportValidationError(instance, ctx, new InvalidMentionError(paramName, kind, "channel", rawValue));
1095
+ }
1096
+ const channelId = match[1];
1097
+ if (param.fetch) {
1098
+ try {
1099
+ return await this.client.channels.fetch(channelId);
1100
+ } catch {
1101
+ return this.reportValidationError(instance, ctx, new FetchFailedError(paramName, kind, "channel", channelId));
1102
+ }
1103
+ }
1104
+ return this.client.channels.cache.get(channelId) ?? channelId;
1105
+ }
1106
+ case "role": {
1107
+ const match = rawValue.match(/^(?:<@&)?([0-7][0-9A-HJKMNP-TV-Z]{25})>?$/i);
1108
+ if (!match) {
1109
+ return this.reportValidationError(instance, ctx, new InvalidMentionError(paramName, kind, "role", rawValue));
1110
+ }
1111
+ const roleId = match[1];
1112
+ if (param.fetch) {
1113
+ const server = ctx.message.server;
1114
+ if (!server) {
1115
+ return this.reportValidationError(instance, ctx, new NoServerContextError(paramName, kind));
1116
+ }
1117
+ try {
1118
+ return await server.roles.fetch(roleId);
1119
+ } catch {
1120
+ return this.reportValidationError(instance, ctx, new FetchFailedError(paramName, kind, "role", roleId));
1121
+ }
1122
+ }
1123
+ return ctx.message.server?.roles.cache.get(roleId) ?? roleId;
1124
+ }
1125
+ default:
1126
+ return rawValue;
1127
+ }
1128
+ }
732
1129
  getRegistry() {
733
1130
  return this.registry;
734
1131
  }
735
- /**
736
- * Get a command by name or alias
737
- */
738
1132
  getCommand(name) {
739
1133
  return this.registry.get(name);
740
1134
  }
741
- /**
742
- * Get all commands
743
- */
744
1135
  getCommands() {
745
1136
  return this.registry.getAll();
746
1137
  }
747
- /**
748
- * Reload all commands
749
- */
750
1138
  async reload() {
751
1139
  this.registry.clear();
752
1140
  if (this.cooldownManager.clear) {
@@ -758,48 +1146,45 @@ var StoatxHandler = class {
758
1146
  }
759
1147
  await this.registry.autoDiscover(this.discoveryOptions);
760
1148
  }
761
- /**
762
- * Check if a user is an owner
763
- */
764
1149
  isOwner(userId) {
765
1150
  return this.owners.has(userId);
766
1151
  }
767
- /**
768
- * Add an owner
769
- */
770
1152
  addOwner(userId) {
771
1153
  this.owners.add(userId);
772
1154
  }
773
- /**
774
- * Remove an owner
775
- */
776
1155
  removeOwner(userId) {
777
1156
  this.owners.delete(userId);
778
1157
  }
779
- /**
780
- * Resolve the prefix for a context
781
- */
782
1158
  async resolvePrefix(serverId) {
783
- if (typeof this.prefixResolver === "function") {
784
- return this.prefixResolver({ serverId });
785
- }
786
- return this.prefixResolver;
1159
+ const result = typeof this.prefixResolver === "function" ? await this.prefixResolver({
1160
+ serverId
1161
+ }) : this.prefixResolver;
1162
+ return Array.isArray(result) ? result : [
1163
+ result
1164
+ ];
787
1165
  }
788
1166
  };
789
1167
 
790
1168
  // src/client.ts
791
- var import_client = require("@stoatx/client");
792
- var Client = class extends import_client.Client {
1169
+ var import_client2 = require("@stoatx/client");
1170
+ var Client = class extends import_client2.Client {
1171
+ static {
1172
+ __name(this, "Client");
1173
+ }
793
1174
  handler;
794
1175
  constructor(options) {
795
- super();
796
- this.handler = new StoatxHandler({ ...options, client: this });
797
- this.on("messageCreate", async (message) => {
798
- await this.handler.handle(message);
1176
+ super(options);
1177
+ this.handler = new StoatxHandler({
1178
+ ...options,
1179
+ client: this
799
1180
  });
800
1181
  }
801
- async initCommands() {
1182
+ async login(token) {
802
1183
  await this.handler.init();
1184
+ return super.login(token);
1185
+ }
1186
+ async executeCommand(message) {
1187
+ await this.handler.handle(message);
803
1188
  }
804
1189
  };
805
1190
 
@@ -807,15 +1192,29 @@ var Client = class extends import_client.Client {
807
1192
  __reExport(index_exports, require("@stoatx/client"), module.exports);
808
1193
  // Annotate the CommonJS export names for ESM import in node:
809
1194
  0 && (module.exports = {
1195
+ Arg,
810
1196
  Client,
1197
+ CommandGroup,
811
1198
  CommandRegistry,
1199
+ CommandValidationError,
812
1200
  DefaultCooldownManager,
1201
+ FetchFailedError,
813
1202
  Guard,
1203
+ Injectable,
1204
+ InvalidMentionError,
1205
+ InvalidTypeError,
814
1206
  METADATA_KEYS,
1207
+ MissingArgumentError,
1208
+ MissingOptionError,
1209
+ NoServerContextError,
815
1210
  On,
816
1211
  Once,
1212
+ Option,
1213
+ PARAM_TYPE_MAP,
817
1214
  SimpleCommand,
818
1215
  Stoat,
1216
+ StoatxError,
1217
+ SubCommand,
819
1218
  buildSimpleCommandMetadata,
820
1219
  getEventsMetadata,
821
1220
  getGuards,