bakit 2.0.0-alpha.1 → 2.0.0-alpha.2

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.d.ts CHANGED
@@ -1,9 +1,12 @@
1
- import { GatewayIntentBits, ClientOptions, Message, Awaitable, ClientEvents, Client } from 'discord.js';
2
- import { z } from 'zod';
1
+ import * as discord_js from 'discord.js';
2
+ import { GatewayIntentBits, ClientOptions, ChatInputCommandInteraction, CacheType, Message, User, MessageCreateOptions, InteractionReplyOptions, Awaitable, Collection, ClientEvents, Events, Client } from 'discord.js';
3
+ import z$1, { z } from 'zod';
4
+ import { inspect } from 'node:util';
3
5
 
4
6
  declare const ProjectConfigSchema: z.ZodObject<{
5
7
  intents: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<"auto">, z.ZodBigInt, z.ZodArray<z.ZodEnum<typeof GatewayIntentBits>>]>>;
6
8
  clientOptions: z.ZodOptional<z.ZodCustom<Omit<ClientOptions, "intents">, Omit<ClientOptions, "intents">>>;
9
+ entryDir: z.ZodDefault<z.ZodString>;
7
10
  }, z.core.$strip>;
8
11
  type ProjectConfigInput = z.input<typeof ProjectConfigSchema>;
9
12
  type ProjectConfig = z.output<typeof ProjectConfigSchema>;
@@ -25,13 +28,276 @@ declare function loadConfig(cwd?: string): Promise<ProjectConfig>;
25
28
  */
26
29
  declare function getConfig(): ProjectConfig;
27
30
 
31
+ declare class Context {
32
+ canceled: boolean;
33
+ cancel(): void;
34
+ }
35
+
36
+ type ChatInputContextSendOptions = string | InteractionReplyOptions;
37
+ type MessageContextSendOptions = string | MessageCreateOptions;
38
+ type ContextSendOptions = ChatInputContextSendOptions | MessageContextSendOptions;
39
+ declare abstract class BaseCommandContext<Cached extends boolean, InGuild extends boolean> extends Context {
40
+ source: ChatInputCommandInteraction<Cached extends true ? "cached" : CacheType> | Message<InGuild>;
41
+ constructor(source: ChatInputCommandInteraction<Cached extends true ? "cached" : CacheType> | Message<InGuild>);
42
+ get client(): BakitClient<true>;
43
+ get channel(): discord_js.CacheTypeReducer<Cached extends true ? "cached" : CacheType, discord_js.GuildTextBasedChannel | null, discord_js.GuildTextBasedChannel | null, discord_js.GuildTextBasedChannel | null, discord_js.TextBasedChannel | null> | discord_js.If<InGuild, discord_js.GuildTextBasedChannel, discord_js.TextBasedChannel>;
44
+ get channelId(): string;
45
+ get guild(): discord_js.CacheTypeReducer<Cached extends true ? "cached" : CacheType, discord_js.Guild, null, discord_js.Guild | null, discord_js.Guild | null> | discord_js.If<InGuild, discord_js.Guild, null>;
46
+ get guildId(): discord_js.CacheTypeReducer<Cached extends true ? "cached" : CacheType, string, string, string, string | null> | discord_js.If<InGuild, string, null>;
47
+ get member(): discord_js.GuildMember | discord_js.CacheTypeReducer<Cached extends true ? "cached" : CacheType, discord_js.GuildMember, discord_js.APIInteractionGuildMember, discord_js.GuildMember | discord_js.APIInteractionGuildMember, discord_js.GuildMember | discord_js.APIInteractionGuildMember | null> | null;
48
+ inGuild(): this is CommandContext<Cached, true>;
49
+ inCachedGuild(): this is CommandContext<true, true>;
50
+ get user(): User;
51
+ isChatInput(): this is ChatInputContext;
52
+ isMessage(): this is MessageContext;
53
+ abstract send(options: ContextSendOptions): Promise<Message<InGuild>>;
54
+ }
55
+ declare class ChatInputContext<Cached extends boolean = boolean, InGuild extends boolean = boolean> extends BaseCommandContext<Cached, InGuild> {
56
+ source: ChatInputCommandInteraction<Cached extends true ? "cached" : CacheType>;
57
+ send(options: ContextSendOptions): Promise<Message<InGuild>>;
58
+ }
59
+ declare class MessageContext<Cached extends boolean = boolean, InGuild extends boolean = boolean> extends BaseCommandContext<Cached, InGuild> {
60
+ source: Message<InGuild>;
61
+ send(options: string | MessageCreateOptions): Promise<Message<InGuild>>;
62
+ }
63
+ type CommandContext<Cached extends boolean = boolean, InGuild extends boolean = boolean> = ChatInputContext<Cached, InGuild> | MessageContext<Cached, InGuild>;
64
+
65
+ declare enum HookState {
66
+ Pre = "PRE",
67
+ Main = "MAIN",
68
+ Post = "POST",
69
+ Error = "ERROR"
70
+ }
71
+ declare enum HookOrder {
72
+ First = 0,
73
+ Last = 1
74
+ }
75
+ type MainHookCallback<C extends Context, Args extends unknown[]> = (context: C, ...args: Args) => Awaitable<void>;
76
+ type ErrorHookCallback<C extends Context, Args extends unknown[]> = (context: C, error: unknown, ...args: Args) => Awaitable<void>;
77
+ declare class LifecycleManager<C extends Context, Args extends unknown[]> {
78
+ id: string;
79
+ private readonly hooks;
80
+ constructor(id: string);
81
+ getName(name: string): string;
82
+ setHook(name: string, state: HookState.Post, callback: MainHookCallback<C, Args>, order?: HookOrder): this;
83
+ setHook(name: string, state: HookState.Main, callback: MainHookCallback<C, Args>, order?: HookOrder): this;
84
+ setHook(name: string, state: HookState.Pre, callback: MainHookCallback<C, Args>, order?: HookOrder): this;
85
+ setHook(name: string, state: HookState.Error, callback: ErrorHookCallback<C, Args>, order?: HookOrder): this;
86
+ main(callback: MainHookCallback<C, Args>): this;
87
+ pre(callback: MainHookCallback<C, Args>): this;
88
+ post(callback: MainHookCallback<C, Args>): this;
89
+ error(callback: ErrorHookCallback<C, Args>): this;
90
+ execute(context: C, ...args: Args): Promise<void>;
91
+ }
92
+
93
+ declare enum ParamUserType {
94
+ Bot = "bot",
95
+ Normal = "normal",
96
+ Any = "any"
97
+ }
98
+ declare const BaseParamSchema: z.ZodObject<{
99
+ name: z.ZodString;
100
+ description: z.ZodOptional<z.ZodString>;
101
+ required: z.ZodDefault<z.ZodBoolean>;
102
+ }, z.core.$strip>;
103
+ declare const StringParamSchema: z.ZodObject<{
104
+ name: z.ZodString;
105
+ description: z.ZodOptional<z.ZodString>;
106
+ required: z.ZodDefault<z.ZodBoolean>;
107
+ maxLength: z.ZodOptional<z.ZodNumber>;
108
+ minLength: z.ZodOptional<z.ZodNumber>;
109
+ }, z.core.$strip>;
110
+ declare const NumberParamSchema: z.ZodObject<{
111
+ name: z.ZodString;
112
+ description: z.ZodOptional<z.ZodString>;
113
+ required: z.ZodDefault<z.ZodBoolean>;
114
+ maxValue: z.ZodOptional<z.ZodNumber>;
115
+ minValue: z.ZodOptional<z.ZodNumber>;
116
+ }, z.core.$strip>;
117
+ type BaseParamOptions = z.input<typeof BaseParamSchema>;
118
+ type StringOptions = z.input<typeof StringParamSchema>;
119
+ type NumberOptions = z.input<typeof NumberParamSchema>;
120
+
121
+ declare abstract class BaseParam<Options extends BaseParamOptions, OutputType, Required extends boolean = true> {
122
+ options: Options & {
123
+ required: Required;
124
+ };
125
+ /**
126
+ * **Internal Phantom Type**
127
+ *
128
+ * Used strictly for TypeScript type inference to determine the runtime value
129
+ * of this parameter. This property does not exist at runtime.
130
+ *
131
+ * @internal
132
+ */
133
+ readonly _type: Required extends true ? OutputType : OutputType | null;
134
+ constructor(options: Options);
135
+ protected setOption(key: keyof Options, value: any): this;
136
+ name(value: string): this;
137
+ description(value: string): this;
138
+ required<V extends boolean>(value: V): BaseParam<Options, OutputType, V>;
139
+ /**
140
+ * Helper to normalize string inputs into an options object.
141
+ */
142
+ protected static getOptions<Options>(options: Options | string): Options;
143
+ }
144
+ declare class StringParam<Required extends boolean = true> extends BaseParam<StringOptions, string, Required> {
145
+ constructor(options: string | StringOptions);
146
+ required<V extends boolean>(value: V): StringParam<V>;
147
+ /**
148
+ * Sets the minimum allowed length for this string.
149
+ * Pass `null` to remove this constraint.
150
+ */
151
+ min(length: number | null): this;
152
+ /**
153
+ * Sets the maximum allowed length for this string.
154
+ * Pass `null` to remove this constraint.
155
+ */
156
+ max(length: number | null): this;
157
+ }
158
+ declare class NumberParam<Required extends boolean = true> extends BaseParam<NumberOptions, number, Required> {
159
+ constructor(options: string | NumberOptions);
160
+ required<V extends boolean>(value: V): NumberParam<V>;
161
+ /**
162
+ * Sets the minimum allowed value for this number.
163
+ * Pass `null` to remove this constraint.
164
+ */
165
+ min(value: number | null): this;
166
+ /**
167
+ * Sets the maximum allowed value for this number.
168
+ * Pass `null` to remove this constraint.
169
+ */
170
+ max(value: number | null): this;
171
+ }
172
+ type AnyParam<Required extends boolean = true> = BaseParam<any, any, Required>;
173
+ /**
174
+ * Helper type to extract the runtime value of a Param instance.
175
+ *
176
+ * @example
177
+ * const p = new StringParam("name").required(false);
178
+ * type T = InferParamValue<typeof p>; // string | null
179
+ */
180
+ type InferParamValue<P extends AnyParam<any>> = P["_type"];
181
+ type InferParamTuple<T extends readonly BaseParam<any, any, any>[]> = {
182
+ [K in keyof T]: T[K] extends AnyParam<any> ? InferParamValue<T[K]> : never;
183
+ };
184
+
185
+ declare const CommandOptionsSchema: z.ZodPipe<z.ZodObject<{
186
+ name: z.ZodString;
187
+ description: z.ZodOptional<z.ZodString>;
188
+ params: z.ZodDefault<z.ZodArray<z.ZodCustom<BaseParam<{
189
+ name: string;
190
+ description?: string | undefined;
191
+ required?: boolean | undefined;
192
+ }, unknown, boolean>, BaseParam<{
193
+ name: string;
194
+ description?: string | undefined;
195
+ required?: boolean | undefined;
196
+ }, unknown, boolean>>>>;
197
+ }, z.core.$strip>, z.ZodTransform<{
198
+ description: string;
199
+ name: string;
200
+ params: BaseParam<{
201
+ name: string;
202
+ description?: string | undefined;
203
+ required?: boolean | undefined;
204
+ }, unknown, boolean>[];
205
+ }, {
206
+ name: string;
207
+ params: BaseParam<{
208
+ name: string;
209
+ description?: string | undefined;
210
+ required?: boolean | undefined;
211
+ }, unknown, boolean>[];
212
+ description?: string | undefined;
213
+ }>>;
214
+ type CommandOptionsInput = z.input<typeof CommandOptionsSchema>;
215
+ type CommandOptions = z.output<typeof CommandOptionsSchema>;
216
+ /**
217
+ * The command entry, used for registering command.
218
+ */
219
+ declare class Command<ParamsList extends readonly AnyParam<any>[] = []> extends LifecycleManager<CommandContext, [
220
+ ...args: InferParamTuple<ParamsList>
221
+ ]> {
222
+ options: CommandOptions;
223
+ constructor(options: (Omit<CommandOptionsInput, "params"> & {
224
+ params?: ParamsList;
225
+ }) | string);
226
+ }
227
+ /**
228
+ * Define command entry, usually for modules.
229
+ * @param options The command options.
230
+ * @returns The entry of the command to deploy or register hooks.
231
+ * @example
232
+ * ```ts
233
+ * import { defineCommand } from "bakit";
234
+ *
235
+ * const command = defineCommand({
236
+ * name: "ping",
237
+ * description: "Displays bot's latency.",
238
+ * });
239
+ *
240
+ * command.main(async (context) => {
241
+ * await context.send(`Pong! ${context.client.ws.ping}ms!`);
242
+ * });
243
+ *
244
+ * export default command;
245
+ * ```
246
+ */
247
+ declare function defineCommand<const ParamsList extends readonly AnyParam<any>[] = []>(options: (Omit<CommandOptionsInput, "params"> & {
248
+ params?: ParamsList;
249
+ }) | string): Command<ParamsList>;
250
+
251
+ declare class BaseClientManager {
252
+ client: BakitClient;
253
+ constructor(client: BakitClient);
254
+ }
255
+
256
+ declare class CommandManager extends BaseClientManager {
257
+ commands: Collection<string, Command<[]>>;
258
+ loadModules(): Promise<Command[]>;
259
+ add(command: Command): void;
260
+ remove(target: string | Command): Command | undefined;
261
+ get(name: string): Command<[]> | undefined;
262
+ }
263
+
264
+ declare const ListenerOptionsSchema: z$1.ZodObject<{
265
+ name: z$1.ZodEnum<typeof Events>;
266
+ once: z$1.ZodDefault<z$1.ZodBoolean>;
267
+ }, z$1.z.core.$strip>;
268
+ type ListenerOptions<K extends EventKey = EventKey> = Omit<z$1.input<typeof ListenerOptionsSchema>, "name"> & {
269
+ name: K;
270
+ };
271
+ type EventKey = keyof ClientEvents;
272
+ declare class Listener<K extends EventKey = EventKey> extends LifecycleManager<Context, [...args: ClientEvents[K]]> {
273
+ options: ListenerOptions<K>;
274
+ constructor(options: K | ListenerOptions<K>);
275
+ }
276
+ declare function defineListener<const K extends EventKey = EventKey>(options: K | ListenerOptions<K>): Listener<K>;
277
+
278
+ declare class ListenerManager extends BaseClientManager {
279
+ listeners: Listener[];
280
+ private executors;
281
+ loadModules(): Promise<Listener[]>;
282
+ add(listener: Listener): void;
283
+ remove(target: string | Listener): Listener[];
284
+ }
285
+
28
286
  type GetPrefixFunction = (message: Message) => Awaitable<string[] | string>;
29
287
  interface BakitClientEvents extends ClientEvents {
30
288
  ready: [BakitClient<true>];
31
289
  clientReady: [BakitClient<true>];
32
290
  }
33
291
  declare class BakitClient<Ready extends boolean = boolean> extends Client<Ready> {
292
+ managers: {
293
+ commands: CommandManager;
294
+ listeners: ListenerManager;
295
+ };
34
296
  constructor(options: ClientOptions);
297
+ start(token?: string): Promise<string>;
298
+ /**
299
+ * Check if the client is connected to gateway successfully and finished initialization.
300
+ */
35
301
  isReady(): this is BakitClient<true>;
36
302
  on<K extends keyof BakitClientEvents>(event: K, listener: (...args: BakitClientEvents[K]) => void): this;
37
303
  once<K extends keyof BakitClientEvents>(event: K, listener: (...args: BakitClientEvents[K]) => void): this;
@@ -39,6 +305,28 @@ declare class BakitClient<Ready extends boolean = boolean> extends Client<Ready>
39
305
  removeAllListeners(event?: keyof BakitClientEvents): this;
40
306
  removeListener<K extends keyof BakitClientEvents>(event: K, listener: (...args: BakitClientEvents[K]) => void): this;
41
307
  emit<K extends keyof BakitClientEvents>(event: K, ...args: BakitClientEvents[K]): boolean;
308
+ /**
309
+ * Override BakitClient output when using logger for security concern.
310
+ * @returns `BakitClient {}`
311
+ */
312
+ [inspect.custom](): string;
42
313
  }
43
314
 
44
- export { BakitClient, type BakitClientEvents, type GetPrefixFunction, type ProjectConfig, type ProjectConfigInput, defineConfig, getConfig, loadConfig };
315
+ declare const Params: {
316
+ readonly string: <Required extends boolean = true>(options: string | {
317
+ name: string;
318
+ description?: string | undefined;
319
+ required?: boolean | undefined;
320
+ maxLength?: number | undefined;
321
+ minLength?: number | undefined;
322
+ }) => StringParam<Required>;
323
+ readonly number: <Required extends boolean = true>(options: string | {
324
+ name: string;
325
+ description?: string | undefined;
326
+ required?: boolean | undefined;
327
+ maxValue?: number | undefined;
328
+ minValue?: number | undefined;
329
+ }) => NumberParam<Required>;
330
+ };
331
+
332
+ export { type AnyParam, BakitClient, type BakitClientEvents, BaseCommandContext, BaseParam, type BaseParamOptions, BaseParamSchema, ChatInputContext, type ChatInputContextSendOptions, Command, type CommandContext, CommandManager, type CommandOptions, type CommandOptionsInput, CommandOptionsSchema, type ContextSendOptions, type GetPrefixFunction, type InferParamTuple, type InferParamValue, Listener, ListenerManager, type ListenerOptions, ListenerOptionsSchema, MessageContext, type MessageContextSendOptions, type NumberOptions, NumberParam, NumberParamSchema, ParamUserType, Params, type ProjectConfig, type ProjectConfigInput, ProjectConfigSchema, type StringOptions, StringParam, StringParamSchema, defineCommand, defineConfig, defineListener, getConfig, loadConfig };
package/dist/index.js CHANGED
@@ -1,12 +1,36 @@
1
- import { GatewayIntentBits, Client } from 'discord.js';
2
- import { z } from 'zod';
1
+ import { GatewayIntentBits, Events, Client, Collection, ChatInputCommandInteraction, Message } from 'discord.js';
2
+ import z3, { z } from 'zod';
3
3
  import { pathToFileURL } from 'url';
4
4
  import glob from 'tiny-glob';
5
+ import { inspect } from 'util';
6
+ import { posix } from 'path';
5
7
 
6
8
  // src/config.ts
7
9
  var ProjectConfigSchema = z.object({
10
+ /**
11
+ * The gateway intents to use for the Discord client.
12
+ *
13
+ * - `auto` — automatically determine the required intents.
14
+ * - bigint — a raw bitfield value representing the combined intents.
15
+ * - array — a list of individual intent flags from `GatewayIntentBits`.
16
+ *
17
+ * @defaultvalue `auto`
18
+ */
8
19
  intents: z.union([z.literal("auto"), z.bigint(), z.array(z.enum(GatewayIntentBits))]).default("auto"),
9
- clientOptions: z.custom().optional()
20
+ /**
21
+ * Optional custom client options for Discord.js (excluding `intents`).
22
+ *
23
+ * These are passed directly to the `Client` constructor when initializing the bot.
24
+ *
25
+ * @see {@link https://discord.js.org/docs/packages/discord.js/main/ClientOptions:Interface}
26
+ */
27
+ clientOptions: z.custom().optional(),
28
+ /**
29
+ * The path to the main project source directory.
30
+ *
31
+ * @defaultvalue `src`
32
+ */
33
+ entryDir: z.string().default("src")
10
34
  });
11
35
  function defineConfig(config) {
12
36
  return config;
@@ -31,10 +55,365 @@ function getConfig() {
31
55
  throw new Error("Project config is not loaded.");
32
56
  return _config;
33
57
  }
34
- var BakitClient = class extends Client {
58
+
59
+ // src/base/lifecycle/Context.ts
60
+ var Context = class {
61
+ canceled = false;
62
+ cancel() {
63
+ this.canceled = true;
64
+ }
65
+ };
66
+
67
+ // src/command/CommandContext.ts
68
+ var BaseCommandContext = class extends Context {
69
+ constructor(source) {
70
+ super();
71
+ this.source = source;
72
+ }
73
+ get client() {
74
+ return this.source.client;
75
+ }
76
+ get channel() {
77
+ return this.source.channel;
78
+ }
79
+ get channelId() {
80
+ return this.source.channelId;
81
+ }
82
+ get guild() {
83
+ return this.source.guild;
84
+ }
85
+ get guildId() {
86
+ return this.source.guildId;
87
+ }
88
+ get member() {
89
+ return this.source.member;
90
+ }
91
+ inGuild() {
92
+ return !!this.guildId;
93
+ }
94
+ inCachedGuild() {
95
+ if (this.isChatInput())
96
+ return this.source.inCachedGuild();
97
+ if (this.isMessage())
98
+ return this.source.inGuild();
99
+ throw new Error("Invalid source");
100
+ }
101
+ get user() {
102
+ if (this.isChatInput())
103
+ return this.source.user;
104
+ if (this.isMessage())
105
+ return this.source.author;
106
+ throw new Error("Invalid source");
107
+ }
108
+ isChatInput() {
109
+ return this.source instanceof ChatInputCommandInteraction;
110
+ }
111
+ isMessage() {
112
+ return this.source instanceof Message;
113
+ }
114
+ }, ChatInputContext = class extends BaseCommandContext {
115
+ async send(options) {
116
+ typeof options == "string" && (options = { content: options });
117
+ let sendOptions = {
118
+ ...options,
119
+ withResponse: true
120
+ };
121
+ return this.source.deferred || this.source.replied ? await this.source.followUp(sendOptions) : (await this.source.reply(sendOptions)).resource?.message;
122
+ }
123
+ }, MessageContext = class extends BaseCommandContext {
124
+ async send(options) {
125
+ let { channel } = this;
126
+ if (!channel?.isSendable())
127
+ throw new Error("Invalid channel or channel is not sendable");
128
+ return await channel.send(options);
129
+ }
130
+ };
131
+ var LifecycleManager = class {
132
+ constructor(id) {
133
+ this.id = id;
134
+ }
135
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
136
+ hooks = {
137
+ MAIN: new Collection(),
138
+ PRE: new Collection(),
139
+ POST: new Collection(),
140
+ ERROR: new Collection()
141
+ };
142
+ getName(name) {
143
+ return `${this.id}:${name}`;
144
+ }
145
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
146
+ setHook(name, state, callback, order = 1 /* Last */) {
147
+ let currentHooks = this.hooks[state], key = this.getName(name);
148
+ if (currentHooks.has(key) && console.warn(`Overriding duplicate hook '${key}' for state '${state}'`), order === 1 /* Last */)
149
+ currentHooks.set(key, callback);
150
+ else {
151
+ let existingEntries = [...currentHooks.entries()].filter(([k]) => k !== key);
152
+ currentHooks.clear(), currentHooks.set(key, callback);
153
+ for (let [k, v] of existingEntries)
154
+ currentHooks.set(k, v);
155
+ }
156
+ return this;
157
+ }
158
+ main(callback) {
159
+ return this.setHook("main", "MAIN" /* Main */, callback);
160
+ }
161
+ pre(callback) {
162
+ return this.setHook("pre", "PRE" /* Pre */, callback);
163
+ }
164
+ post(callback) {
165
+ return this.setHook("post", "POST" /* Post */, callback);
166
+ }
167
+ error(callback) {
168
+ return this.setHook("error", "ERROR" /* Error */, callback);
169
+ }
170
+ async execute(context, ...args) {
171
+ let pipeline = [
172
+ ...this.hooks.PRE.values(),
173
+ ...this.hooks.MAIN.values(),
174
+ ...this.hooks.POST.values()
175
+ ], error;
176
+ for (let hook of pipeline) {
177
+ if (context.canceled)
178
+ break;
179
+ try {
180
+ await hook(context, ...args);
181
+ } catch (e) {
182
+ error = e;
183
+ break;
184
+ }
185
+ }
186
+ if (!error)
187
+ return;
188
+ if (!this.hooks.ERROR.size)
189
+ throw error;
190
+ for (let [key, callback] of this.hooks.ERROR.entries()) {
191
+ if (context.canceled)
192
+ break;
193
+ try {
194
+ await callback(context, error, ...args);
195
+ } catch (innerError) {
196
+ console.error(`[Lifecycle] Error handler for '${key}' failed:`, innerError);
197
+ }
198
+ }
199
+ }
200
+ };
201
+
202
+ // src/command/param/Param.ts
203
+ var BaseParam = class {
204
+ options;
35
205
  constructor(options) {
36
- super(options);
206
+ this.options = { ...options, required: options.required ?? true };
207
+ }
208
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
209
+ setOption(key, value) {
210
+ return value === null ? delete this.options[key] : this.options[key] = value, this;
211
+ }
212
+ name(value) {
213
+ return this.setOption("name", value);
214
+ }
215
+ description(value) {
216
+ return this.setOption("description", value);
217
+ }
218
+ required(value) {
219
+ return this.setOption("required", value);
220
+ }
221
+ /**
222
+ * Helper to normalize string inputs into an options object.
223
+ */
224
+ static getOptions(options) {
225
+ return typeof options == "string" ? { name: options } : options;
226
+ }
227
+ }, StringParam = class extends BaseParam {
228
+ constructor(options) {
229
+ super(BaseParam.getOptions(options));
230
+ }
231
+ required(value) {
232
+ return super.required(value);
233
+ }
234
+ /**
235
+ * Sets the minimum allowed length for this string.
236
+ * Pass `null` to remove this constraint.
237
+ */
238
+ min(length) {
239
+ return this.setOption("minLength", length);
240
+ }
241
+ /**
242
+ * Sets the maximum allowed length for this string.
243
+ * Pass `null` to remove this constraint.
244
+ */
245
+ max(length) {
246
+ return this.setOption("maxLength", length);
37
247
  }
248
+ }, NumberParam = class extends BaseParam {
249
+ constructor(options) {
250
+ super(BaseParam.getOptions(options));
251
+ }
252
+ required(value) {
253
+ return super.required(value);
254
+ }
255
+ /**
256
+ * Sets the minimum allowed value for this number.
257
+ * Pass `null` to remove this constraint.
258
+ */
259
+ min(value) {
260
+ return this.setOption("minValue", value);
261
+ }
262
+ /**
263
+ * Sets the maximum allowed value for this number.
264
+ * Pass `null` to remove this constraint.
265
+ */
266
+ max(value) {
267
+ return this.setOption("maxValue", value);
268
+ }
269
+ };
270
+
271
+ // src/command/Command.ts
272
+ var CommandOptionsSchema = z.object({
273
+ name: z.string(),
274
+ description: z.string().min(1).max(100).optional(),
275
+ params: z.array(z.instanceof(BaseParam)).default([])
276
+ }).transform((data) => ({
277
+ ...data,
278
+ description: data.description ?? `Command ${data.name}`
279
+ })), Command = class extends LifecycleManager {
280
+ constructor(options) {
281
+ let _options = CommandOptionsSchema.parse(typeof options == "string" ? { name: options } : options);
282
+ super(`command:${_options.name}`), this.options = _options;
283
+ }
284
+ };
285
+ function defineCommand(options) {
286
+ return new Command(options);
287
+ }
288
+
289
+ // src/base/BaseClientManager.ts
290
+ var BaseClientManager = class {
291
+ constructor(client) {
292
+ this.client = client;
293
+ }
294
+ };
295
+
296
+ // src/command/CommandManager.ts
297
+ var CommandManager = class extends BaseClientManager {
298
+ commands = new Collection();
299
+ async loadModules() {
300
+ let entryDir = posix.resolve(getConfig().entryDir), pattern = posix.join(entryDir, "commands", "**/*.{ts,js}"), loads = (await glob(pattern, {
301
+ cwd: process.cwd()
302
+ })).map(async (file) => {
303
+ try {
304
+ let { default: command } = await import(pathToFileURL(file).toString());
305
+ if (!command) {
306
+ console.warn(`[Loader] File has no default export: ${file}`);
307
+ return;
308
+ }
309
+ if (!(command instanceof Command)) {
310
+ console.warn(`[Loader] Default export is not a Command: ${file}`);
311
+ return;
312
+ }
313
+ return this.add(command), command;
314
+ } catch (error) {
315
+ console.error(`An error occurred while trying to add command for '${file}':`, error);
316
+ }
317
+ }), loaded = (await Promise.all(loads)).filter((x) => x !== void 0);
318
+ return console.log(`Loaded ${loaded.length} command(s).`), loaded;
319
+ }
320
+ add(command) {
321
+ if (!(command instanceof Command))
322
+ throw new Error("Invalid command provided");
323
+ let { name } = command.options;
324
+ if (this.commands.has(name)) {
325
+ console.warn(`[Loader] Duplicate command registered: '${name}'`);
326
+ return;
327
+ }
328
+ this.commands.set(name, command);
329
+ }
330
+ remove(target) {
331
+ let name = typeof target == "string" ? target : target.options.name, existing = this.commands.get(name);
332
+ if (existing)
333
+ return this.commands.delete(name), existing;
334
+ }
335
+ get(name) {
336
+ return this.commands.get(name);
337
+ }
338
+ };
339
+ var ListenerOptionsSchema = z3.object({
340
+ name: z3.enum(Events),
341
+ once: z3.boolean().default(false)
342
+ }), Listener = class extends LifecycleManager {
343
+ options;
344
+ constructor(options) {
345
+ let _options = ListenerOptionsSchema.parse(typeof options == "string" ? { name: options } : options);
346
+ super(`listener:${_options.name}`), this.options = options;
347
+ }
348
+ };
349
+ function defineListener(options) {
350
+ return new Listener(options);
351
+ }
352
+
353
+ // src/listener/ListenerManager.ts
354
+ var ListenerManager = class extends BaseClientManager {
355
+ listeners = [];
356
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
357
+ executors = /* @__PURE__ */ new WeakMap();
358
+ async loadModules() {
359
+ let entryDir = posix.resolve(getConfig().entryDir), pattern = posix.join(entryDir, "listeners", "**/*.{ts,js}"), loads = (await glob(pattern, {
360
+ cwd: process.cwd()
361
+ })).map(async (file) => {
362
+ try {
363
+ let { default: listener } = await import(pathToFileURL(file).toString());
364
+ if (!listener) {
365
+ console.warn(`[Loader] File has no default export: ${file}`);
366
+ return;
367
+ }
368
+ if (!(listener instanceof Listener)) {
369
+ console.warn(`[Loader] Default export is not a Listener: ${file}`);
370
+ return;
371
+ }
372
+ return this.add(listener), listener;
373
+ } catch (error) {
374
+ console.error(`An error occurred while trying to add listener for '${file}':`, error);
375
+ }
376
+ }), loaded = (await Promise.all(loads)).filter((x) => x !== void 0);
377
+ return console.log(`Loaded ${loaded.length} listener(s).`), loaded;
378
+ }
379
+ add(listener) {
380
+ if (!(listener instanceof Listener))
381
+ throw new Error("Invalid listener provided");
382
+ let execute = (...args) => {
383
+ listener.execute(new Context(), ...args);
384
+ };
385
+ this.listeners.push(listener), this.executors.set(listener, execute);
386
+ let { once, name } = listener.options;
387
+ this.client[once ? "once" : "on"](name, execute);
388
+ }
389
+ remove(target) {
390
+ let isMatched = (listener) => typeof target == "string" ? listener.options.name === target : listener === target, removed = [];
391
+ return this.listeners = this.listeners.filter((listener) => {
392
+ if (!isMatched(listener))
393
+ return true;
394
+ removed.push(listener);
395
+ let execute = this.executors.get(listener);
396
+ return execute && (this.client.removeListener(listener.options.name, execute), this.executors.delete(listener)), false;
397
+ }), removed;
398
+ }
399
+ };
400
+
401
+ // src/BakitClient.ts
402
+ var BakitClient3 = class extends Client {
403
+ managers;
404
+ constructor(options) {
405
+ super(options), this.managers = {
406
+ commands: new CommandManager(this),
407
+ listeners: new ListenerManager(this)
408
+ };
409
+ }
410
+ async start(token) {
411
+ let { commands, listeners } = this.managers;
412
+ return await Promise.all([commands.loadModules(), listeners.loadModules()]), await this.login(token);
413
+ }
414
+ /**
415
+ * Check if the client is connected to gateway successfully and finished initialization.
416
+ */
38
417
  isReady() {
39
418
  return super.isReady();
40
419
  }
@@ -56,6 +435,33 @@ var BakitClient = class extends Client {
56
435
  emit(event, ...args) {
57
436
  return super.emit(event, ...args);
58
437
  }
438
+ /**
439
+ * Override BakitClient output when using logger for security concern.
440
+ * @returns `BakitClient {}`
441
+ */
442
+ [inspect.custom]() {
443
+ return `${this.constructor.name} {}`;
444
+ }
445
+ };
446
+ var ParamUserType = /* @__PURE__ */ ((ParamUserType2) => (ParamUserType2.Bot = "bot", ParamUserType2.Normal = "normal", ParamUserType2.Any = "any", ParamUserType2))(ParamUserType || {}), BaseParamSchema = z.object({
447
+ name: z.string(),
448
+ description: z.string().optional(),
449
+ required: z.boolean().default(true)
450
+ }), StringParamSchema = BaseParamSchema.extend({
451
+ maxLength: z.number().min(1).optional(),
452
+ minLength: z.number().min(1).optional()
453
+ }), NumberParamSchema = BaseParamSchema.extend({
454
+ maxValue: z.number().optional(),
455
+ minValue: z.number().optional()
456
+ });
457
+
458
+ // src/command/param/Params.ts
459
+ function createFactory(ctor) {
460
+ return (...args) => new ctor(...args);
461
+ }
462
+ var Params = {
463
+ string: createFactory(StringParam),
464
+ number: createFactory(NumberParam)
59
465
  };
60
466
 
61
- export { BakitClient, defineConfig, getConfig, loadConfig };
467
+ export { BakitClient3 as BakitClient, BaseCommandContext, BaseParam, BaseParamSchema, ChatInputContext, Command, CommandManager, CommandOptionsSchema, Listener, ListenerManager, ListenerOptionsSchema, MessageContext, NumberParam, NumberParamSchema, ParamUserType, Params, ProjectConfigSchema, StringParam, StringParamSchema, defineCommand, defineConfig, defineListener, getConfig, loadConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bakit",
3
- "version": "2.0.0-alpha.1",
3
+ "version": "2.0.0-alpha.2",
4
4
  "description": "A framework for discord.js",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",