bakit 1.0.0-beta.5 → 1.0.0-beta.8

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
@@ -1,3 +1,6 @@
1
+ // src/index.ts
2
+ import "reflect-metadata";
3
+
1
4
  // src/BakitClient.ts
2
5
  import {
3
6
  Client,
@@ -18,6 +21,125 @@ import { pathToFileURL } from "url";
18
21
  // src/command/CommandEntry.ts
19
22
  import { Collection } from "discord.js";
20
23
 
24
+ // src/base/BaseEntry.ts
25
+ var BaseEntry = class {
26
+ target;
27
+ hooks = {
28
+ ["MAIN" /* Main */]: void 0,
29
+ ["ERROR" /* Error */]: void 0,
30
+ ["POST" /* Post */]: void 0,
31
+ ["PRE" /* Pre */]: void 0
32
+ };
33
+ main;
34
+ pre;
35
+ post;
36
+ error;
37
+ constructor() {
38
+ this.main = this.createMainHookDecorator("MAIN" /* Main */);
39
+ this.pre = this.createMainHookDecorator("PRE" /* Pre */);
40
+ this.post = this.createMainHookDecorator("POST" /* Post */);
41
+ this.error = this.createMainHookDecorator("ERROR" /* Error */);
42
+ }
43
+ setTarget(target) {
44
+ this.target = target;
45
+ }
46
+ createMainHookDecorator(state) {
47
+ return (target, _key, descriptor) => {
48
+ this.addHook(state, target, descriptor);
49
+ };
50
+ }
51
+ createErrorHookDecorator(state) {
52
+ return (target, _key, descriptor) => {
53
+ this.addHook(state, target, descriptor);
54
+ };
55
+ }
56
+ addHook(state, target, descriptor) {
57
+ if (target.constructor !== this.target) {
58
+ throw new Error("Hook is used at unknown constructor.");
59
+ }
60
+ const { value: method } = descriptor;
61
+ if (typeof method !== "function") {
62
+ throw new Error("Invalid target method for hook.");
63
+ }
64
+ const hook = {
65
+ state,
66
+ method,
67
+ entry: this
68
+ };
69
+ this.hooks[state] = hook;
70
+ }
71
+ };
72
+
73
+ // src/command/CommandEntry.ts
74
+ var BaseCommandEntry = class extends BaseEntry {
75
+ constructor(options) {
76
+ super();
77
+ this.options = options;
78
+ }
79
+ };
80
+ var BaseCommandGroupEntry = class extends BaseCommandEntry {
81
+ children = new Collection();
82
+ subcommand(options) {
83
+ const fullOptions = typeof options === "string" ? { name: options, description: `${options} command` } : { description: `${options.name} command`, ...options };
84
+ if (this.children.has(fullOptions.name)) {
85
+ throw new Error(`Entry "${fullOptions.name}" is already existed.`);
86
+ }
87
+ const subcommand = new SubcommandEntry(fullOptions, this);
88
+ this.children.set(fullOptions.name, subcommand);
89
+ return subcommand;
90
+ }
91
+ };
92
+ var RootCommandEntry = class extends BaseCommandGroupEntry {
93
+ group(options) {
94
+ const fullOptions = typeof options === "string" ? { name: options, description: `${options} command` } : { description: `${options.name} command`, ...options };
95
+ if (this.children.has(fullOptions.name)) {
96
+ throw new Error(`Entry "${fullOptions.name}" is already existed.`);
97
+ }
98
+ const group = new CommandGroupEntry(fullOptions, this);
99
+ this.children.set(fullOptions.name, group);
100
+ return group;
101
+ }
102
+ };
103
+ var CommandGroupEntry = class extends BaseCommandGroupEntry {
104
+ constructor(options, parent) {
105
+ super(options);
106
+ this.parent = parent;
107
+ }
108
+ };
109
+ var SubcommandEntry = class extends BaseCommandEntry {
110
+ constructor(options, parent) {
111
+ super(options);
112
+ this.parent = parent;
113
+ }
114
+ };
115
+
116
+ // src/command/Command.ts
117
+ var CommandAPI;
118
+ ((CommandAPI2) => {
119
+ const rootEntries = /* @__PURE__ */ new WeakMap();
120
+ function use(root) {
121
+ return (target) => {
122
+ root.setTarget(target);
123
+ rootEntries.set(target, root);
124
+ };
125
+ }
126
+ CommandAPI2.use = use;
127
+ function getRoot(constructor) {
128
+ return rootEntries.get(constructor);
129
+ }
130
+ CommandAPI2.getRoot = getRoot;
131
+ })(CommandAPI || (CommandAPI = {}));
132
+ function CommandFactory(options) {
133
+ if (typeof options === "string") {
134
+ options = { name: options };
135
+ }
136
+ if (!options.description) {
137
+ options.description = options.name;
138
+ }
139
+ return new RootCommandEntry(options);
140
+ }
141
+ var Command = Object.assign(CommandFactory, CommandAPI);
142
+
21
143
  // src/command/argument/Argument.ts
22
144
  var ArgumentType = /* @__PURE__ */ ((ArgumentType2) => {
23
145
  ArgumentType2["String"] = "string";
@@ -121,157 +243,14 @@ var Arg = {
121
243
  member
122
244
  };
123
245
 
124
- // src/command/CommandEntry.ts
125
- var CommandHookExecutionState = /* @__PURE__ */ ((CommandHookExecutionState2) => {
126
- CommandHookExecutionState2["Main"] = "main";
127
- CommandHookExecutionState2["Pre"] = "pre";
128
- CommandHookExecutionState2["Post"] = "post";
129
- CommandHookExecutionState2["Error"] = "error";
130
- return CommandHookExecutionState2;
131
- })(CommandHookExecutionState || {});
132
- var HOOKS_KEY = Symbol("hooks");
133
- var BaseCommandEntry = class _BaseCommandEntry {
134
- constructor(options) {
135
- this.options = options;
136
- }
137
- static cache = /* @__PURE__ */ new WeakMap();
138
- main = _BaseCommandEntry.createMainHookDecorator("main" /* Main */, this);
139
- pre = _BaseCommandEntry.createMainHookDecorator("pre" /* Pre */, this);
140
- post = _BaseCommandEntry.createMainHookDecorator("post" /* Post */, this);
141
- error = _BaseCommandEntry.createErrorHookDecorator("error" /* Error */, this);
142
- static getHooks(constructor, init = false) {
143
- let hooks = this.cache.get(constructor) ?? Reflect.getMetadata(HOOKS_KEY, constructor);
144
- if (!hooks) {
145
- hooks = [];
146
- if (init) {
147
- Reflect.defineMetadata(HOOKS_KEY, hooks, constructor);
148
- this.cache.set(constructor, hooks);
149
- }
150
- }
151
- return init ? hooks : Object.freeze([...hooks]);
152
- }
153
- static createMainHookDecorator(state, entry) {
154
- return (target, _key, descriptor) => {
155
- this.addHook(target, state, descriptor.value, entry);
156
- };
157
- }
158
- static createErrorHookDecorator(state, entry) {
159
- return (target, _key, descriptor) => {
160
- this.addHook(target, state, descriptor.value, entry);
161
- };
162
- }
163
- static addHook(target, state, method, entry) {
164
- const { constructor } = target;
165
- const hooks = _BaseCommandEntry.getHooks(constructor, true);
166
- if (typeof method !== "function") {
167
- throw new Error("CommandEntry decorator must be used with a class method.");
168
- }
169
- if (hooks.some((hook) => hook.state === state && hook.entry === entry)) {
170
- throw new Error(`Hook "${state}" is already defined for entry "${entry.options.name}".`);
171
- }
172
- if ("parent" in entry) {
173
- const parentHook = hooks.find(
174
- (hook) => hook.entry === entry.parent && hook.state === "main" /* Main */
175
- );
176
- if (parentHook) {
177
- const parentArgs = Arg.getMethodArguments(parentHook.method);
178
- if (parentArgs.at(-1)?.tuple) {
179
- throw new Error(
180
- `Cannot add hook "${state}" to entry "${entry.options.name}" because its parent "${entry.parent.options.name}" has a tuple argument.`
181
- );
182
- }
183
- }
184
- }
185
- hooks.push({
186
- state,
187
- entry,
188
- method
189
- });
190
- }
191
- };
192
- var BaseCommandGroupEntry = class extends BaseCommandEntry {
193
- children = new Collection();
194
- subcommand(options) {
195
- if (typeof options === "string") {
196
- options = { name: options };
197
- }
198
- if (!options.description) {
199
- options.description = options.name;
200
- }
201
- if (this.children.has(options.name)) {
202
- throw new Error(`Entry with name "${options.name}" already exists.`);
203
- }
204
- if (!(this instanceof CommandGroupEntry) && !(this instanceof RootCommandEntry)) {
205
- throw new Error(`Invalid parent "${this.constructor.name}"`);
206
- }
207
- const entry = new SubcommandEntry(options, this);
208
- this.children.set(entry.options.name, entry);
209
- return entry;
210
- }
211
- };
212
- var CommandGroupEntry = class extends BaseCommandGroupEntry {
213
- constructor(options, parent) {
214
- super(options);
215
- this.options = options;
216
- this.parent = parent;
217
- }
218
- };
219
- var RootCommandEntry = class extends BaseCommandGroupEntry {
220
- constructor(options) {
221
- super(options);
222
- this.options = options;
223
- }
224
- group(options) {
225
- if (typeof options === "string") {
226
- options = { name: options };
227
- }
228
- if (!options.description) {
229
- options.description = options.name;
230
- }
231
- if (this.children.has(options.name)) {
232
- throw new Error(`Entry with name "${options.name}" already exists.`);
233
- }
234
- const entry = new CommandGroupEntry(options, this);
235
- this.children.set(entry.options.name, entry);
236
- return entry;
237
- }
238
- };
239
- var SubcommandEntry = class extends BaseCommandEntry {
240
- constructor(options, parent) {
241
- super(options);
242
- this.options = options;
243
- this.parent = parent;
244
- }
245
- };
246
-
247
- // src/command/Command.ts
248
- var ROOT_KEY = Symbol("root");
249
- function use(command) {
250
- return (target) => {
251
- Reflect.defineMetadata(ROOT_KEY, command, target);
252
- };
253
- }
254
- function getRoot(constructor) {
255
- return Reflect.getMetadata(ROOT_KEY, constructor);
256
- }
257
- function CommandFactory(options) {
258
- if (typeof options === "string") {
259
- options = { name: options };
260
- }
261
- if (!options.description) {
262
- options.description = options.name;
263
- }
264
- return new RootCommandEntry(options);
265
- }
266
- var Command = Object.assign(CommandFactory, {
267
- use,
268
- getRoot
269
- });
270
-
271
246
  // src/command/CommandRegistry.ts
272
247
  var CommandRegistry = class _CommandRegistry {
273
248
  static constructors = new Collection2();
274
249
  static instances = new Collection2();
250
+ /**
251
+ * Add a command to the registry.
252
+ * @param constructor The command class you want to add.
253
+ */
275
254
  static add(constructor) {
276
255
  const root = Command.getRoot(constructor);
277
256
  if (!root) {
@@ -281,20 +260,13 @@ var CommandRegistry = class _CommandRegistry {
281
260
  this.constructors.set(options.name, constructor);
282
261
  this.instances.set(options.name, new constructor());
283
262
  }
284
- static buildSlashCommand(constructor) {
285
- const builder = new SlashCommandBuilder();
286
- const root = Command.getRoot(constructor);
287
- if (!root) {
288
- throw new Error(`No root found for "${constructor.name}"`);
289
- }
290
- const { options } = root;
291
- builder.setName(options.name);
292
- builder.setDescription(options.description || "");
293
- builder.setNSFW(Boolean(options.nsfw));
294
- this.buildSlashCommandOptions(builder, constructor);
295
- return builder.toJSON();
296
- }
297
- static async loadDirectory(pattern, parallel = true) {
263
+ /**
264
+ * Load and add all commands which matched provided glob pattern to the registry.
265
+ * @param pattern glob pattern to load.
266
+ * @param parallel load all matched results in parallel, enabled by default.
267
+ * @returns All loaded command constructors.
268
+ */
269
+ static async load(pattern, parallel = true) {
298
270
  const files = await glob(pattern);
299
271
  const loaders = files.map(async (file) => {
300
272
  const fileURL = pathToFileURL(file).toString();
@@ -311,72 +283,102 @@ var CommandRegistry = class _CommandRegistry {
311
283
  }
312
284
  return result;
313
285
  }
314
- static buildSlashCommandOptions(builder, constructor) {
286
+ /**
287
+ * Build a command into application command data.
288
+ * @param constructor The command class you want to build.
289
+ * @returns a REST JSON version of the application command data.
290
+ */
291
+ static buildSlashCommand(constructor) {
315
292
  const root = Command.getRoot(constructor);
316
293
  if (!root) {
317
294
  throw new Error(`No root found for "${constructor.name}"`);
318
295
  }
319
- const hooks = new Collection2(
320
- BaseCommandEntry.getHooks(constructor).filter((hook) => hook.state === "main" /* Main */).map((hook) => [hook.entry, hook])
321
- );
322
- const rootHook = hooks.get(root);
323
- if (!root.children.size && hooks.size) {
324
- if (!rootHook) {
325
- return;
326
- }
327
- for (const arg of Arg.getMethodArguments(rootHook.method)) {
328
- this.buildSlashCommandOption(builder, arg);
329
- }
330
- return;
296
+ const { options } = root;
297
+ const builder = new SlashCommandBuilder().setName(options.name).setDescription(options.description).setNSFW(Boolean(options.nsfw));
298
+ const args = this.getMainHookArguments(root);
299
+ if (root.children.size) {
300
+ this.buildSlashCommandSubcommands(builder, root, args);
301
+ } else {
302
+ this.buildSlashCommandOptions(builder, args);
331
303
  }
332
- for (const child of root.children.values()) {
333
- const hook = hooks.get(child);
334
- const inheritedArgs = [];
335
- if (rootHook) {
336
- inheritedArgs.push(...Arg.getMethodArguments(rootHook.method));
337
- }
338
- if (child instanceof SubcommandEntry) {
339
- this.buildSubcommand(builder, child, hook, inheritedArgs);
340
- } else if (child instanceof CommandGroupEntry) {
341
- if (hook) {
342
- inheritedArgs.push(...Arg.getMethodArguments(hook.method));
343
- }
304
+ return builder.toJSON();
305
+ }
306
+ static getMainHookArguments(entry) {
307
+ const { hooks } = entry;
308
+ const mainHook = hooks["MAIN" /* Main */];
309
+ return mainHook ? Arg.getMethodArguments(mainHook.method) : [];
310
+ }
311
+ static buildSlashCommandSubcommands(parent, entry, inheritedArgs) {
312
+ const { children } = entry;
313
+ for (const child of children.values()) {
314
+ if (child instanceof CommandGroupEntry && parent instanceof SlashCommandBuilder) {
344
315
  const { options } = child;
345
316
  const group = new SlashCommandSubcommandGroupBuilder().setName(options.name).setDescription(options.description);
346
- for (const subChild of child.children.values()) {
347
- this.buildSubcommand(group, subChild, hooks.get(subChild), inheritedArgs);
348
- }
349
- builder.addSubcommandGroup(group);
317
+ this.buildSlashCommandSubcommands(group, child, [
318
+ ...inheritedArgs,
319
+ ...this.getMainHookArguments(child)
320
+ ]);
321
+ parent.addSubcommandGroup(group);
322
+ } else if (child instanceof SubcommandEntry) {
323
+ const { options } = child;
324
+ const subcommand = new SlashCommandSubcommandBuilder().setName(options.name).setDescription(options.description);
325
+ this.buildSlashCommandOptions(subcommand, [
326
+ ...inheritedArgs,
327
+ ...this.getMainHookArguments(child)
328
+ ]);
329
+ parent.addSubcommand(subcommand);
350
330
  }
351
331
  }
352
332
  }
353
- static buildSubcommand(parent, entry, hook, inheritedArgs) {
354
- const { options } = entry;
355
- const subcommand = new SlashCommandSubcommandBuilder().setName(options.name).setDescription(options.description);
356
- const args = [...inheritedArgs];
357
- if (hook) {
358
- args.push(...Arg.getMethodArguments(hook.method));
333
+ static buildSlashCommandOptions(builder, args) {
334
+ const argGroup = Object.groupBy(args, ({ required }) => required ? "required" : "optional");
335
+ const orderedArgs = [...argGroup.required || [], ...argGroup.optional || []];
336
+ for (const arg of orderedArgs) {
337
+ this.attachSlashCommandOption(builder, arg);
359
338
  }
360
- for (const arg of args) {
361
- this.buildSlashCommandOption(subcommand, arg);
362
- }
363
- parent.addSubcommand(subcommand);
364
339
  }
365
- static buildSlashCommandOption(builder, arg) {
340
+ static attachSlashCommandOption(builder, arg) {
366
341
  const setupOption = (option) => {
367
342
  return option.setName(arg.name).setDescription(arg.description || arg.name).setRequired(Boolean(arg.required));
368
343
  };
369
344
  switch (arg.type) {
370
345
  case "string" /* String */: {
371
- builder.addStringOption((option) => setupOption(option));
346
+ builder.addStringOption((data) => {
347
+ const option = setupOption(data);
348
+ if (arg.maxLength) {
349
+ option.setMaxLength(arg.maxLength);
350
+ }
351
+ if (arg.minLength) {
352
+ option.setMinLength(arg.minLength);
353
+ }
354
+ return option;
355
+ });
372
356
  break;
373
357
  }
374
358
  case "integer" /* Integer */: {
375
- builder.addIntegerOption((option) => setupOption(option));
359
+ builder.addIntegerOption((data) => {
360
+ const option = setupOption(data);
361
+ if (arg.maxValue) {
362
+ option.setMaxValue(arg.maxValue);
363
+ }
364
+ if (arg.minValue) {
365
+ option.setMinValue(arg.minValue);
366
+ }
367
+ return option;
368
+ });
376
369
  break;
377
370
  }
378
371
  case "number" /* Number */: {
379
- builder.addNumberOption((option) => setupOption(option));
372
+ builder.addNumberOption((data) => {
373
+ const option = setupOption(data);
374
+ if (arg.maxValue) {
375
+ option.setMaxValue(arg.maxValue);
376
+ }
377
+ if (arg.minValue) {
378
+ option.setMinValue(arg.minValue);
379
+ }
380
+ return option;
381
+ });
380
382
  break;
381
383
  }
382
384
  case "user" /* User */:
@@ -388,9 +390,6 @@ var CommandRegistry = class _CommandRegistry {
388
390
  }
389
391
  };
390
392
 
391
- // src/command/index.ts
392
- import "reflect-metadata";
393
-
394
393
  // src/command/Context.ts
395
394
  import {
396
395
  ChatInputCommandInteraction,
@@ -564,13 +563,13 @@ var ArgumentResolver = class _ArgumentResolver {
564
563
  return this.options.message.client;
565
564
  }
566
565
  static create(message) {
567
- const client2 = message.client;
568
- const { enableMentionPrefix } = client2.options;
566
+ const client = message.client;
567
+ const { enableMentionPrefix } = client.options;
569
568
  const prefixes = [
570
569
  // Custom prefixes specified in options
571
- ...client2.options.prefixes ?? [],
570
+ ...client.options.prefixes ?? [],
572
571
  // Use bot mention as prefix if enabled
573
- ...enableMentionPrefix ? [client2.user.toString()] : []
572
+ ...enableMentionPrefix ? [client.user.toString()] : []
574
573
  ];
575
574
  const prefix = prefixes.find((p) => message.content.startsWith(p)) ?? null;
576
575
  if (!prefix) {
@@ -813,191 +812,171 @@ var StateBox = class _StateBox {
813
812
  };
814
813
 
815
814
  // src/listener/ListenerEntry.ts
816
- var ListenerHookExecutionState = /* @__PURE__ */ ((ListenerHookExecutionState2) => {
817
- ListenerHookExecutionState2["Main"] = "main";
818
- ListenerHookExecutionState2["Pre"] = "pre";
819
- ListenerHookExecutionState2["Post"] = "post";
820
- ListenerHookExecutionState2["Error"] = "error";
821
- return ListenerHookExecutionState2;
822
- })(ListenerHookExecutionState || {});
823
- var ListenerEntry = class _ListenerEntry {
815
+ var ListenerEntry = class extends BaseEntry {
824
816
  constructor(options) {
817
+ super();
825
818
  this.options = options;
826
819
  }
827
- static hooksKey = Symbol("hooks");
828
- static cache = /* @__PURE__ */ new WeakMap();
829
- main = _ListenerEntry.createMainHookDecorator("main" /* Main */, this);
830
- pre = _ListenerEntry.createMainHookDecorator("pre" /* Pre */, this);
831
- post = _ListenerEntry.createMainHookDecorator("post" /* Post */, this);
832
- error = _ListenerEntry.createErrorHookDecorator("error" /* Error */, this);
833
- static getHooks(constructor, init = false) {
834
- const { cache: cache2 } = this;
835
- let hooks = cache2.get(constructor) ?? Reflect.getMetadata(this.hooksKey, constructor);
836
- if (!hooks) {
837
- hooks = [];
838
- if (init) {
839
- Reflect.defineMetadata(this.hooksKey, hooks, constructor);
840
- this.cache.set(
841
- constructor,
842
- hooks
843
- );
844
- }
845
- }
846
- return init ? hooks : [...hooks];
847
- }
848
- static createMainHookDecorator(state, entry) {
849
- return (target, _key, descriptor) => {
850
- this.addHook(target, state, descriptor.value, entry);
851
- };
852
- }
853
- static createErrorHookDecorator(state, entry) {
854
- return (target, _key, descriptor) => {
855
- this.addHook(target, state, descriptor.value, entry);
856
- };
857
- }
858
- static addHook(target, state, method, entry) {
859
- const { constructor } = target;
860
- const hooks = this.getHooks(constructor, true);
861
- if (typeof method !== "function") {
862
- throw new Error("CommandEntry decorator must be used with a class method.");
863
- }
864
- if (hooks.some((hook2) => hook2.state === state && hook2.entry === entry)) {
865
- throw new Error(
866
- `Hook "${state}" is already defined for entry "${String(entry.options.name)}".`
867
- );
868
- }
869
- const hook = {
870
- state,
871
- entry,
872
- method
873
- };
874
- hooks.push(hook);
875
- }
876
820
  };
877
821
 
878
822
  // src/listener/Listener.ts
823
+ var ListenerAPI;
824
+ ((ListenerAPI2) => {
825
+ ListenerAPI2.ENTRY_KEY = Symbol("entry");
826
+ function use(entry) {
827
+ return (target) => {
828
+ Reflect.defineMetadata(ListenerAPI2.ENTRY_KEY, entry, target);
829
+ };
830
+ }
831
+ ListenerAPI2.use = use;
832
+ function getEntry(target) {
833
+ return Reflect.getMetadata(ListenerAPI2.ENTRY_KEY, target);
834
+ }
835
+ ListenerAPI2.getEntry = getEntry;
836
+ })(ListenerAPI || (ListenerAPI = {}));
879
837
  function ListenerFactory(options) {
880
- const normalizedOptions = typeof options === "object" ? { once: false, ...options } : { name: options, once: false };
881
- return new ListenerEntry(normalizedOptions);
838
+ const fullOptions = typeof options !== "object" ? { name: options, once: false } : { once: false, ...options };
839
+ return new ListenerEntry(fullOptions);
882
840
  }
883
- var LISTENER_ENTRY_KEY = Symbol("entry");
884
- function use2(listener) {
885
- return (target) => {
886
- Reflect.defineMetadata(LISTENER_ENTRY_KEY, listener, target);
887
- };
888
- }
889
- function getEntry(constructor) {
890
- return Reflect.getMetadata(LISTENER_ENTRY_KEY, constructor);
891
- }
892
- var Listener = Object.assign(ListenerFactory, {
893
- use: use2,
894
- getEntry
895
- });
841
+ var Listener = Object.assign(ListenerFactory, ListenerAPI);
896
842
 
897
843
  // src/listener/ListenerRegistry.ts
898
- var client;
899
- var constructors = /* @__PURE__ */ new Set();
900
- var instances = /* @__PURE__ */ new WeakMap();
901
- var executors = /* @__PURE__ */ new WeakMap();
902
- function add(constructor) {
903
- const entry = Listener.getEntry(constructor);
904
- if (!entry) {
905
- throw new Error(`No entry found for "${constructor.name}"`);
906
- }
907
- const { options } = entry;
908
- if (!options.emitter) {
909
- if (!client) {
910
- throw new Error("Client is not ready.");
911
- }
912
- options.emitter = client;
913
- }
914
- const instance = new constructor();
915
- constructors.add(constructor);
916
- instances.set(constructor, instance);
917
- const executor = createExecutor(constructor, instance);
918
- executors.set(instance, executor);
919
- options.emitter[options.once ? "once" : "on"](options.name, (...args) => {
920
- void executor(...args);
921
- });
922
- }
923
- function remove(constructor) {
924
- const entry = Listener.getEntry(constructor);
925
- if (!entry) {
926
- return;
927
- }
928
- constructors.delete(constructor);
929
- const instance = instances.get(constructor);
930
- if (!instance) {
931
- return;
932
- }
933
- instances.delete(constructor);
934
- const executor = executors.get(instance);
935
- if (!executor) {
936
- return;
937
- }
938
- const { name, emitter } = entry.options;
939
- emitter?.removeListener(name, executor);
940
- executors.delete(instance);
941
- }
942
- function removeAll() {
943
- for (const constructor of constructors) {
944
- remove(constructor);
945
- }
946
- }
947
- function setClient(newClient) {
948
- client = newClient;
949
- }
950
- function createExecutor(constructor, instance) {
951
- const entry = Listener.getEntry(constructor);
952
- if (!entry) {
953
- throw new Error("Missing listener entry");
954
- }
955
- const hooks = ListenerEntry.getHooks(constructor).filter((hook) => hook.entry === entry);
956
- const hookGroup = makeHookGroup(hooks);
957
- return async function(...args) {
958
- if (!hookGroup.main) {
959
- return;
844
+ import glob2 from "tiny-glob";
845
+ import { pathToFileURL as pathToFileURL2 } from "url";
846
+ var ListenerRegistry = class {
847
+ static client;
848
+ static constructors = /* @__PURE__ */ new Set();
849
+ static instances = /* @__PURE__ */ new WeakMap();
850
+ static executors = /* @__PURE__ */ new WeakMap();
851
+ /**
852
+ * Add and register a listener to the registry.
853
+ * If `options.emitter` is not provided, the registry will use the base `client` by default.
854
+ * @param constructor The listener class you want to add.
855
+ */
856
+ static add(constructor) {
857
+ const entry = Listener.getEntry(constructor);
858
+ if (!entry) {
859
+ throw new Error(`No entry found for "${constructor.name}"`);
960
860
  }
961
- try {
962
- if (hookGroup.pre) {
963
- const preMethod = hookGroup.pre.method;
964
- await preMethod.call(instance, ...args);
861
+ const { options } = entry;
862
+ if (!options.emitter) {
863
+ if (!this.client) {
864
+ throw new Error("Client is not ready.");
965
865
  }
966
- await hookGroup.main.method.call(instance, ...args);
967
- if (hookGroup.post) {
968
- const postMethod = hookGroup.post.method;
969
- await postMethod.call(instance, ...args);
866
+ options.emitter = this.client;
867
+ }
868
+ const instance = new constructor();
869
+ const executor = this.createExecutor(constructor, instance);
870
+ this.constructors.add(constructor);
871
+ this.instances.set(constructor, instance);
872
+ this.executors.set(instance, executor);
873
+ options.emitter[options.once ? "once" : "on"](options.name, (...args) => {
874
+ void executor(...args);
875
+ });
876
+ }
877
+ /**
878
+ * Remove and unregister a listener from the registry.
879
+ * @param constructor The listener class you want to remove.
880
+ * @returns `boolean`, returns `true` if the listener is removed successfully.
881
+ */
882
+ static remove(constructor) {
883
+ const entry = Listener.getEntry(constructor);
884
+ if (!entry) {
885
+ return false;
886
+ }
887
+ this.constructors.delete(constructor);
888
+ const instance = this.instances.get(constructor);
889
+ if (!instance) {
890
+ return false;
891
+ }
892
+ this.instances.delete(constructor);
893
+ const executor = this.executors.get(instance);
894
+ if (!executor) {
895
+ return false;
896
+ }
897
+ const { name, emitter } = entry.options;
898
+ emitter?.removeListener(name, executor);
899
+ this.executors.delete(instance);
900
+ return true;
901
+ }
902
+ /**
903
+ * Remove and unregister all listeners from the registry.
904
+ * @returns Amount of removed listeners.
905
+ */
906
+ static removeAll() {
907
+ let removedAmount = 0;
908
+ for (const constructor of this.constructors) {
909
+ if (this.remove(constructor)) {
910
+ removedAmount++;
970
911
  }
971
- } catch (error) {
972
- if (hookGroup.error) {
973
- const errorMethod = hookGroup.error.method;
974
- await errorMethod.call(instance, error, ...args);
975
- } else {
976
- throw error;
912
+ }
913
+ return removedAmount;
914
+ }
915
+ /**
916
+ * Set base client for the registry to fallback as default emitter. This should be used only by BakitClient and stay untouched.
917
+ * @param newClient base client to set for the registry.
918
+ */
919
+ static setClient(newClient) {
920
+ this.client = newClient;
921
+ }
922
+ static createExecutor(constructor, instance) {
923
+ const entry = Listener.getEntry(constructor);
924
+ if (!entry) {
925
+ throw new Error("Missing listener entry");
926
+ }
927
+ const { hooks } = entry;
928
+ return async function(...args) {
929
+ const mainHook = hooks["MAIN" /* Main */];
930
+ const preHook = hooks["PRE" /* Pre */];
931
+ const postHook = hooks["POST" /* Post */];
932
+ const errorHook = hooks["ERROR" /* Error */];
933
+ if (!mainHook) {
934
+ return;
977
935
  }
936
+ try {
937
+ if (preHook) {
938
+ await preHook.method.call(instance, ...args);
939
+ }
940
+ await mainHook.method.call(instance, ...args);
941
+ if (postHook) {
942
+ await postHook.method.call(instance, ...args);
943
+ }
944
+ } catch (error) {
945
+ if (errorHook) {
946
+ await errorHook.method.call(
947
+ instance,
948
+ error,
949
+ ...args
950
+ );
951
+ } else {
952
+ throw error;
953
+ }
954
+ }
955
+ };
956
+ }
957
+ /**
958
+ * Load and add all listeners which matched provided glob pattern to the registry.
959
+ * @param pattern glob pattern to load.
960
+ * @param parallel load all matched results in parallel, enabled by default.
961
+ * @returns All loaded listener constructors.
962
+ */
963
+ static async load(pattern, parallel = true) {
964
+ const files = await glob2(pattern);
965
+ const loaders = files.map(async (file) => {
966
+ const fileURL = pathToFileURL2(file).toString();
967
+ const { default: constructor } = await import(fileURL);
968
+ this.add(constructor);
969
+ return constructor;
970
+ });
971
+ if (parallel) {
972
+ return Promise.all(loaders);
978
973
  }
979
- };
980
- }
981
- function makeHookGroup(hooks) {
982
- const hooksByType = {
983
- ["pre" /* Pre */]: void 0,
984
- ["main" /* Main */]: void 0,
985
- ["post" /* Post */]: void 0,
986
- ["error" /* Error */]: void 0
987
- };
988
- for (const hook of hooks) {
989
- hooksByType[hook.state] = hook;
974
+ const result = [];
975
+ for (const loader of loaders) {
976
+ result.push(await loader);
977
+ }
978
+ return result;
990
979
  }
991
- return hooksByType;
992
- }
993
- var ListenerRegistry = {
994
- constructors,
995
- instances,
996
- executors,
997
- add,
998
- setClient,
999
- remove,
1000
- removeAll
1001
980
  };
1002
981
 
1003
982
  // src/BakitClient.ts
@@ -1007,10 +986,10 @@ var BakitClient = class _BakitClient extends Client {
1007
986
  options.getSyntaxErrorMessage = _BakitClient.getSyntaxErrorMessage;
1008
987
  }
1009
988
  super(options);
1010
- ListenerRegistry.setClient(this);
989
+ ListenerRegistry["setClient"](this);
1011
990
  this.once(
1012
991
  Events.ClientReady,
1013
- (client2) => void this.registerApplicationCommands(client2)
992
+ (client) => void this.registerApplicationCommands(client)
1014
993
  );
1015
994
  this.on(Events.InteractionCreate, (interaction) => void this.handleInteraction(interaction));
1016
995
  this.on(Events.MessageCreate, (message) => void this.handleMessage(message));
@@ -1030,14 +1009,15 @@ var BakitClient = class _BakitClient extends Client {
1030
1009
  content
1031
1010
  };
1032
1011
  };
1033
- async registerApplicationCommands(client2) {
1012
+ async registerApplicationCommands(client) {
1034
1013
  const commands = CommandRegistry.constructors.map((c) => CommandRegistry.buildSlashCommand(c));
1035
- await client2.application.commands.set(commands);
1014
+ await client.application.commands.set(commands);
1036
1015
  }
1037
1016
  async handleMessage(message) {
1038
1017
  if (message.author.bot) {
1039
1018
  return;
1040
1019
  }
1020
+ const context = new MessageContext(message);
1041
1021
  const resolver = ArgumentResolver.create(message);
1042
1022
  if (!resolver) {
1043
1023
  return;
@@ -1047,9 +1027,7 @@ var BakitClient = class _BakitClient extends Client {
1047
1027
  if (!command) {
1048
1028
  return;
1049
1029
  }
1050
- const hooks = BaseCommandEntry.getHooks(command.constructor);
1051
- const context = new MessageContext(message);
1052
- await StateBox.run(() => this.handleMessageHooks(context, hooks, command, resolver));
1030
+ await StateBox.run(() => this.handleMessageHooks(context, command, resolver));
1053
1031
  }
1054
1032
  async handleInteraction(interaction) {
1055
1033
  if (!interaction.isChatInputCommand()) {
@@ -1060,54 +1038,57 @@ var BakitClient = class _BakitClient extends Client {
1060
1038
  if (!command) {
1061
1039
  return;
1062
1040
  }
1063
- const hooks = BaseCommandEntry.getHooks(command.constructor);
1064
1041
  const context = new ChatInputContext(interaction);
1065
- await StateBox.run(() => this.handleChatInputHooks(context, hooks, command));
1042
+ await StateBox.run(() => this.handleChatInputHooks(context, command));
1066
1043
  }
1067
- async handleChatInputHooks(context, hooks, instance) {
1068
- const targetHooks = this.getChatInputTargetHooks(context.source, hooks);
1044
+ async handleChatInputHooks(context, instance) {
1045
+ const targetHooks = this.getChatInputTargetHooks(context.source, instance);
1069
1046
  let inheritedArgs = [];
1070
- for (const record of [targetHooks.root, targetHooks.group, targetHooks.subcommand]) {
1071
- const newArgs = await this.runChatInputHooks(context, instance, record, inheritedArgs);
1047
+ for (const hooks of [targetHooks.root, targetHooks.group, targetHooks.subcommand]) {
1048
+ if (!hooks) {
1049
+ continue;
1050
+ }
1051
+ const newArgs = await this.runChatInputHooks(context, instance, hooks, inheritedArgs);
1072
1052
  if (newArgs) {
1073
1053
  inheritedArgs = newArgs;
1074
1054
  }
1075
1055
  }
1076
1056
  }
1077
- async handleMessageHooks(context, hooks, instance, resolver) {
1057
+ async handleMessageHooks(context, instance, resolver) {
1078
1058
  if (!resolver) {
1079
1059
  return;
1080
1060
  }
1081
- const rootEntry = Command.getRoot(instance.constructor);
1082
- if (!rootEntry) return;
1083
- const rootRecord = this.createHookRecord(hooks.filter((hook) => hook.entry === rootEntry));
1084
- resolver = await this.runMessageHooks(context, instance, rootRecord, resolver);
1061
+ const root = Command.getRoot(instance.constructor);
1062
+ if (!root) {
1063
+ return;
1064
+ }
1065
+ resolver = await this.runMessageHooks(context, instance, root.hooks, resolver);
1085
1066
  if (!resolver) {
1086
1067
  return;
1087
1068
  }
1088
- const usedValues = resolver.parsedValues.length;
1089
- const nextTrigger = resolver.values[usedValues + 1];
1090
- const nextHook = hooks.find((hook) => hook.entry.options.name === nextTrigger);
1091
- const nextRecord = this.createHookRecord(nextHook ? [nextHook] : []);
1092
- resolver = await this.runMessageHooks(context, instance, nextRecord, resolver);
1069
+ await this.handleChildMessageHooks(context, root, instance, resolver);
1070
+ }
1071
+ async handleChildMessageHooks(context, parent, instance, resolver, skip = 1) {
1093
1072
  if (!resolver) {
1094
1073
  return;
1095
1074
  }
1096
- if (nextRecord.main?.entry instanceof CommandGroupEntry) {
1097
- const groupEntry = nextRecord.main.entry;
1098
- const subTrigger = resolver.values[usedValues + 2];
1099
- const subHook = hooks.find(
1100
- (h) => h.entry instanceof SubcommandEntry && h.entry.options.name === subTrigger && h.entry.parent === groupEntry
1101
- );
1102
- const subRecord = this.createHookRecord(subHook ? [subHook] : []);
1103
- resolver = await this.runMessageHooks(context, instance, subRecord, resolver);
1075
+ const usedValues = resolver.parsedValues.length;
1076
+ const nextTrigger = resolver.values[usedValues + skip];
1077
+ const child = parent.children.get(nextTrigger);
1078
+ if (!child) {
1079
+ return;
1080
+ }
1081
+ resolver = await this.runMessageHooks(context, instance, child.hooks, resolver);
1082
+ if (child instanceof CommandGroupEntry) {
1083
+ await this.handleChildMessageHooks(context, child, instance, resolver, skip + 1);
1104
1084
  }
1105
1085
  }
1106
- async runMessageHooks(context, instance, record, resolver) {
1107
- if (!record.main) {
1086
+ async runMessageHooks(context, instance, hooks, resolver) {
1087
+ const mainHook = hooks["MAIN" /* Main */];
1088
+ if (!mainHook) {
1108
1089
  return resolver;
1109
1090
  }
1110
- const args = Arg.getMethodArguments(record.main.method);
1091
+ const args = Arg.getMethodArguments(mainHook.method);
1111
1092
  try {
1112
1093
  resolver = await resolver.resolve(args);
1113
1094
  } catch (error) {
@@ -1126,75 +1107,78 @@ var BakitClient = class _BakitClient extends Client {
1126
1107
  }
1127
1108
  throw error;
1128
1109
  }
1129
- try {
1130
- await record.pre?.method.call(instance, context, ...resolver.parsedValues);
1131
- await record.main.method.call(instance, context, ...resolver.parsedValues);
1132
- await record.post?.method.call(instance, context, ...resolver.parsedValues);
1133
- } catch (error) {
1134
- if (record.error) {
1135
- await record.error.method.call(instance, context, error, ...resolver.parsedValues);
1136
- } else {
1137
- throw error;
1138
- }
1139
- }
1110
+ await this.runHooks(context, instance, hooks, resolver.parsedValues);
1140
1111
  return resolver;
1141
1112
  }
1142
- async runChatInputHooks(context, instance, record, inheritedArgs) {
1143
- if (!record.main) {
1113
+ async runChatInputHooks(context, instance, hooks, inheritedArgs) {
1114
+ const mainHook = hooks["MAIN" /* Main */];
1115
+ if (!mainHook) {
1144
1116
  return;
1145
1117
  }
1146
- const newArgs = Arg.getMethodArguments(record.main.method).map(
1118
+ const newArgs = Arg.getMethodArguments(mainHook.method).map(
1147
1119
  (arg) => ArgumentResolver.resolveChatInput(context.source, arg)
1148
1120
  );
1149
1121
  const argValues = [...inheritedArgs, ...newArgs];
1122
+ await this.runHooks(context, instance, hooks, argValues);
1123
+ return argValues;
1124
+ }
1125
+ async runHooks(context, instance, hooks, args) {
1126
+ const mainHook = hooks["MAIN" /* Main */];
1127
+ const preHook = hooks["PRE" /* Pre */];
1128
+ const postHook = hooks["POST" /* Post */];
1129
+ const errorHook = hooks["ERROR" /* Error */];
1130
+ if (!mainHook) {
1131
+ return;
1132
+ }
1133
+ const execute = async (hook, error) => {
1134
+ if (!hook) {
1135
+ return;
1136
+ }
1137
+ if (hook.state === "ERROR" /* Error */) {
1138
+ await hook.method.call(instance, error, context, ...args);
1139
+ } else {
1140
+ await hook.method.call(instance, context, ...args);
1141
+ }
1142
+ };
1150
1143
  try {
1151
- await record.pre?.method.call(instance, context, ...argValues);
1152
- await record.main.method.call(instance, context, ...argValues);
1153
- await record.post?.method.call(instance, context, ...argValues);
1144
+ await execute(preHook);
1145
+ await execute(mainHook);
1146
+ await execute(postHook);
1154
1147
  } catch (error) {
1155
- if (record.error) {
1156
- await record.error.method.call(instance, context, error, ...argValues);
1148
+ if (errorHook) {
1149
+ await execute(errorHook, error);
1157
1150
  } else {
1158
1151
  throw error;
1159
1152
  }
1160
1153
  }
1161
- return argValues;
1162
1154
  }
1163
- getChatInputTargetHooks(interaction, hooks) {
1164
- const subcommand = interaction.options.getSubcommand(false);
1165
- const subcommandGroup = interaction.options.getSubcommandGroup(false);
1166
- const root = this.createHookRecord(
1167
- hooks.filter((hook) => hook.entry instanceof RootCommandEntry)
1168
- );
1169
- const group = this.createHookRecord(
1170
- hooks.filter(({ entry }) => {
1171
- return entry instanceof CommandGroupEntry && entry.options.name === subcommandGroup;
1172
- })
1173
- );
1174
- const sub = this.createHookRecord(
1175
- hooks.filter(({ entry }) => {
1176
- if (!(entry instanceof SubcommandEntry) || !(entry.options.name === subcommand)) {
1177
- return false;
1178
- }
1179
- if (subcommandGroup) {
1180
- const { parent } = entry;
1181
- if (!(parent instanceof CommandGroupEntry) || parent.options.name !== subcommandGroup) {
1182
- return false;
1183
- }
1184
- }
1185
- return true;
1186
- })
1187
- );
1188
- return { root, group, subcommand: sub };
1189
- }
1190
- createHookRecord(hooks) {
1191
- return Object.values(CommandHookExecutionState).reduce(
1192
- (acc, state) => {
1193
- acc[state] = hooks.find((h) => h.state === state);
1194
- return acc;
1195
- },
1196
- {}
1197
- );
1155
+ getChatInputTargetHooks(interaction, instance) {
1156
+ const subcommandName = interaction.options.getSubcommand(false);
1157
+ const groupName = interaction.options.getSubcommandGroup(false);
1158
+ const root = Command.getRoot(instance.constructor);
1159
+ if (!root) {
1160
+ throw new Error("No root found.");
1161
+ }
1162
+ let group;
1163
+ if (groupName) {
1164
+ const child = root.children.get(groupName);
1165
+ if (child instanceof CommandGroupEntry) {
1166
+ group = child;
1167
+ }
1168
+ }
1169
+ let subcommand;
1170
+ if (subcommandName) {
1171
+ const parent = group || root;
1172
+ const child = parent.children.get(subcommandName);
1173
+ if (child instanceof SubcommandEntry) {
1174
+ subcommand = child;
1175
+ }
1176
+ }
1177
+ return {
1178
+ root: root.hooks,
1179
+ group: group?.hooks,
1180
+ subcommand: subcommand?.hooks
1181
+ };
1198
1182
  }
1199
1183
  };
1200
1184
  export {
@@ -1206,18 +1190,16 @@ export {
1206
1190
  BaseContext,
1207
1191
  ChatInputContext,
1208
1192
  Command,
1193
+ CommandAPI,
1209
1194
  CommandFactory,
1210
1195
  CommandGroupEntry,
1211
- CommandHookExecutionState,
1212
1196
  CommandRegistry,
1213
1197
  CommandSyntaxError,
1214
1198
  CommandSyntaxErrorType,
1215
- HOOKS_KEY,
1216
- LISTENER_ENTRY_KEY,
1217
1199
  Listener,
1200
+ ListenerAPI,
1218
1201
  ListenerEntry,
1219
1202
  ListenerFactory,
1220
- ListenerHookExecutionState,
1221
1203
  ListenerRegistry,
1222
1204
  MessageContext,
1223
1205
  RootCommandEntry,