bakit 2.0.0-alpha.10 → 2.0.0-alpha.12

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,5 +1,5 @@
1
1
  import * as discord_js from 'discord.js';
2
- import { GatewayIntentBits, ClientOptions, ChatInputCommandInteraction, CacheType, Message, User, MessageCreateOptions, InteractionReplyOptions, Awaitable, Collection, Events, IntentsBitField, Client, ClientEvents } from 'discord.js';
2
+ import { GatewayIntentBits, ClientOptions, ChatInputCommandInteraction, CacheType, Message, User, MessageCreateOptions, InteractionReplyOptions, Awaitable, Collection, Events, IntentsBitField, ClientEvents, Client } from 'discord.js';
3
3
  import z$1, { z } from 'zod';
4
4
  import * as jiti from 'jiti';
5
5
  import { inspect } from 'node:util';
@@ -7,7 +7,6 @@ import { inspect } from 'node:util';
7
7
  declare const ProjectConfigSchema: z.ZodObject<{
8
8
  intents: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<"auto">, z.ZodBigInt, z.ZodArray<z.ZodEnum<typeof GatewayIntentBits>>]>>;
9
9
  clientOptions: z.ZodOptional<z.ZodCustom<Omit<ClientOptions, "intents">, Omit<ClientOptions, "intents">>>;
10
- entryDir: z.ZodDefault<z.ZodString>;
11
10
  prefixes: z.ZodDefault<z.ZodArray<z.ZodString>>;
12
11
  token: z.ZodString;
13
12
  }, z.core.$strip>;
@@ -41,7 +40,14 @@ declare function tokenize(content: string): string[];
41
40
  */
42
41
  declare function extractSnowflakeId(input: string): string | null;
43
42
 
44
- declare const $jiti: jiti.Jiti;
43
+ declare class Module {
44
+ static jiti: jiti.Jiti;
45
+ static import<T>(module: string, defaultImport?: boolean): Promise<T | null>;
46
+ static isLoaded(module: string): boolean;
47
+ static unload(module: string): boolean;
48
+ static resolve(module: string): string | null;
49
+ static getTopLevel(path: string, entryDir: string): string | null;
50
+ }
45
51
 
46
52
  declare class Context {
47
53
  canceled: boolean;
@@ -295,11 +301,43 @@ declare function defineCommand<const ParamsList extends readonly AnyParam<any>[]
295
301
  params?: ParamsList;
296
302
  }) | string): Command<ParamsList>;
297
303
 
304
+ declare class BaseClientManager {
305
+ client: BakitClient;
306
+ constructor(client: BakitClient);
307
+ }
308
+
298
309
  declare class CommandManager extends BaseClientManager {
299
310
  commands: Collection<string, Command<any[]>>;
300
- loadModules(): Promise<Command[]>;
311
+ entries: Collection<string, Command<any[]>>;
312
+ loadModules(entryDir: string): Promise<Command[]>;
313
+ /**
314
+ * Load the file and add the command to the registry.
315
+ * @param path The path to the command file.
316
+ * @returns The command object if added successfully.
317
+ */
318
+ load(path: string): Promise<Command | undefined>;
319
+ /**
320
+ * Unload the file and remove the command from the registry.
321
+ * @param path The path to the command file.
322
+ * @returns The command object if unloaded successfully.
323
+ */
324
+ unload(path: string): Promise<Command | undefined>;
325
+ /**
326
+ * Add a command to the registry.
327
+ * @param command Command to add.
328
+ */
301
329
  add(command: Command): void;
330
+ /**
331
+ * Remove a command from the registry.
332
+ * @param target Command name or object to remove.
333
+ * @returns The command object if removed successfully.
334
+ */
302
335
  remove(target: string | Command): Command | undefined;
336
+ /**
337
+ * Get a command using its name.
338
+ * @param name The command to get.
339
+ * @returns The command object.
340
+ */
303
341
  get(name: string): Command<any[]> | undefined;
304
342
  }
305
343
 
@@ -321,11 +359,41 @@ declare function defineListener<const K extends EventKey = EventKey>(options: K
321
359
 
322
360
  declare class ListenerManager extends BaseClientManager {
323
361
  listeners: Listener[];
362
+ entries: Collection<string, Listener<keyof BakitClientEvents>>;
324
363
  private executors;
325
- loadModules(): Promise<Listener[]>;
364
+ loadModules(entryDir: string): Promise<Listener[]>;
365
+ /**
366
+ * Load the file and add the listener to the registry.
367
+ * @param path The path to the listener file.
368
+ * @returns The listener object if added successfully.
369
+ */
370
+ load(path: string): Promise<Listener | undefined>;
371
+ /**
372
+ * Unload the file and remove the listener from the registry.
373
+ * @param path The path to the listener file.
374
+ * @returns The listener object if unloaded successfully.
375
+ */
376
+ unload(path: string): Promise<Listener | undefined>;
377
+ /**
378
+ * Add a listener to the registry and create a listener for client.
379
+ * @param listener Listener to add.
380
+ */
326
381
  add(listener: Listener): void;
382
+ /**
383
+ * Remove a listener from the registry and client.
384
+ * @param target Listener name or object to remove.
385
+ * @returns The list of listener objects if removed successfully.
386
+ */
327
387
  remove(target: string | Listener): Listener[];
388
+ /**
389
+ * Get a list of required intents for Bakit to run correctly.
390
+ * @returns Used intents.
391
+ */
328
392
  getBaseIntents(): IntentsBitField;
393
+ /**
394
+ * Get a list of needed intents based on registered listeners to receive needed events.
395
+ * @returns Used intents.
396
+ */
329
397
  getNeededIntents(): IntentsBitField;
330
398
  }
331
399
 
@@ -345,8 +413,10 @@ declare class Instance {
345
413
  cache: ProjectCacheManager;
346
414
  constructor();
347
415
  start(): Promise<void>;
416
+ private initProcess;
348
417
  private loadModules;
349
418
  private initIntents;
419
+ private onProcessMessage;
350
420
  }
351
421
  declare function useApp(): Instance;
352
422
 
@@ -379,11 +449,6 @@ declare class BakitClient<Ready extends boolean = boolean> extends Client<Ready>
379
449
  [inspect.custom](): string;
380
450
  }
381
451
 
382
- declare class BaseClientManager {
383
- client: BakitClient;
384
- constructor(client: BakitClient);
385
- }
386
-
387
452
  declare const Params: {
388
453
  readonly string: <Required extends boolean = true>(options: string | {
389
454
  name: string;
@@ -399,8 +464,31 @@ declare const Params: {
399
464
  maxValue?: number | undefined;
400
465
  minValue?: number | undefined;
401
466
  }) => NumberParam<Required>;
467
+ readonly user: <Required extends boolean = true>(options: string | {
468
+ name: string;
469
+ description?: string | undefined;
470
+ required?: boolean | undefined;
471
+ }) => UserParam<Required>;
402
472
  };
403
473
 
474
+ interface DevManagerOptions {
475
+ rootDir: string;
476
+ entry: string;
477
+ hotDirs: string[];
478
+ }
479
+ declare class DevProcessManager {
480
+ private options;
481
+ private child;
482
+ private restartTimer;
483
+ constructor(options: DevManagerOptions);
484
+ start(): void;
485
+ private startChild;
486
+ private restartChild;
487
+ private startWatcher;
488
+ private onFileChanged;
489
+ private scheduleRestart;
490
+ }
491
+
404
492
  declare class BakitError extends Error {
405
493
  constructor(message: string);
406
494
  }
@@ -411,4 +499,4 @@ declare class ArgumentError extends BakitError {
411
499
  constructor(target: string, reason: string);
412
500
  }
413
501
 
414
- export { $jiti, type AnyParam, ArgumentError, BakitClient, type BakitClientEvents, BakitError, BaseClientManager, BaseCommandContext, BaseParam, type BaseParamOptions, BaseParamSchema, ChatInputContext, type ChatInputContextSendOptions, Command, type CommandContext, CommandManager, type CommandOptions, type CommandOptionsInput, CommandOptionsSchema, Context, type ContextSendOptions, EVENT_INTENT_MAPPING, type ErrorHookCallback, type GetPrefixFunction, HookOrder, HookState, type InferParamTuple, type InferParamValue, Instance, LifecycleManager, Listener, ListenerManager, type ListenerOptions, ListenerOptionsSchema, type MainHookCallback, MessageContext, type MessageContextSendOptions, type NumberOptions, NumberParam, NumberParamSchema, type ParamResolvedOutputType, ParamUserType, Params, type ProjectConfig, type ProjectConfigInput, ProjectConfigSchema, type StringOptions, StringParam, StringParamSchema, type UserOptions, UserParam, UserParamSchema, defineCommand, defineConfig, defineListener, extractSnowflakeId, getConfig, loadConfig, tokenize, useApp, validateParamsOrder };
502
+ export { type AnyParam, ArgumentError, BakitClient, type BakitClientEvents, BakitError, BaseClientManager, BaseCommandContext, BaseParam, type BaseParamOptions, BaseParamSchema, ChatInputContext, type ChatInputContextSendOptions, Command, type CommandContext, CommandManager, type CommandOptions, type CommandOptionsInput, CommandOptionsSchema, Context, type ContextSendOptions, DevProcessManager, EVENT_INTENT_MAPPING, type ErrorHookCallback, type GetPrefixFunction, HookOrder, HookState, type InferParamTuple, type InferParamValue, Instance, LifecycleManager, Listener, ListenerManager, type ListenerOptions, ListenerOptionsSchema, type MainHookCallback, MessageContext, type MessageContextSendOptions, Module, type NumberOptions, NumberParam, NumberParamSchema, type ParamResolvedOutputType, ParamUserType, Params, ProjectCacheManager, type ProjectConfig, type ProjectConfigInput, ProjectConfigSchema, type StringOptions, StringParam, StringParamSchema, type UserOptions, UserParam, UserParamSchema, defineCommand, defineConfig, defineListener, extractSnowflakeId, getConfig, loadConfig, tokenize, useApp, validateParamsOrder };
package/dist/index.js CHANGED
@@ -1,9 +1,12 @@
1
- import { GatewayIntentBits, Events, Client, IntentsBitField, Collection, SlashCommandBuilder, SlashCommandStringOption, SlashCommandNumberOption, SlashCommandUserOption, ChatInputCommandInteraction, Message } from 'discord.js';
1
+ import { GatewayIntentBits, Events, Client, Collection, IntentsBitField, SlashCommandBuilder, SlashCommandStringOption, SlashCommandNumberOption, SlashCommandUserOption, ChatInputCommandInteraction, Message } from 'discord.js';
2
2
  import z4, { z } from 'zod';
3
3
  import glob from 'tiny-glob';
4
4
  import { createJiti } from 'jiti';
5
+ import { fileURLToPath } from 'url';
6
+ import path, { relative, sep, posix, join, dirname } from 'path';
5
7
  import { inspect } from 'util';
6
- import { posix, join, dirname } from 'path';
8
+ import { fork } from 'child_process';
9
+ import chokidar from 'chokidar';
7
10
  import { existsSync, mkdirSync, rmSync } from 'fs';
8
11
  import { mkdir, writeFile, readFile, rm } from 'fs/promises';
9
12
  import { createHash } from 'crypto';
@@ -132,9 +135,38 @@ function extractSnowflakeId(input) {
132
135
  let idMatch = /^(\d{17,20})$/.exec(input);
133
136
  return idMatch?.[1] ? idMatch[1] : null;
134
137
  }
135
-
136
- // src/utils/index.ts
137
- var $jiti = createJiti(import.meta.url);
138
+ var Module = class {
139
+ static jiti = createJiti(process.cwd());
140
+ static async import(module, defaultImport = false) {
141
+ let path2 = this.resolve(module);
142
+ if (!path2)
143
+ return null;
144
+ try {
145
+ return await this.jiti.import(path2, { default: defaultImport });
146
+ } catch (error) {
147
+ return console.error(`[Module] Import failed for ${path2}:`, error), null;
148
+ }
149
+ }
150
+ static isLoaded(module) {
151
+ let path2 = this.resolve(module);
152
+ return !!path2 && !!this.jiti.cache[path2];
153
+ }
154
+ static unload(module) {
155
+ let path2 = this.resolve(module);
156
+ return !path2 || !this.jiti.cache[path2] ? false : (delete this.jiti.cache[path2], true);
157
+ }
158
+ static resolve(module) {
159
+ try {
160
+ let url = this.jiti.esmResolve(module);
161
+ return fileURLToPath(url);
162
+ } catch {
163
+ return null;
164
+ }
165
+ }
166
+ static getTopLevel(path2, entryDir) {
167
+ return relative(entryDir, path2).split(sep)[0] ?? null;
168
+ }
169
+ };
138
170
 
139
171
  // src/config.ts
140
172
  var ProjectConfigSchema = z.object({
@@ -156,22 +188,13 @@ var ProjectConfigSchema = z.object({
156
188
  * @see {@link https://discord.js.org/docs/packages/discord.js/main/ClientOptions:Interface}
157
189
  */
158
190
  clientOptions: z.custom().optional(),
159
- /**
160
- * The path to the main project source directory.
161
- *
162
- * @defaultvalue `src`
163
- */
164
- entryDir: z.string().default("src"),
165
191
  prefixes: z.array(z.string()).default([]),
166
192
  token: z.string()
167
193
  });
168
194
  function defineConfig(config) {
169
195
  return config;
170
196
  }
171
- var _config;
172
197
  async function loadConfig(cwd = process.cwd()) {
173
- if (_config)
174
- return console.warn("loadConfig() was called more than once. This shouldn't happen."), _config;
175
198
  let globPattern = `bakit.config.{${["ts", "js"].join(",")}}`, [configPath, other] = await glob(globPattern, {
176
199
  cwd: cwd.replace(/\\/g, "/"),
177
200
  // ensure the path uses `/` instead of `\` on Windows
@@ -180,22 +203,13 @@ async function loadConfig(cwd = process.cwd()) {
180
203
  if (!configPath)
181
204
  throw new Error("Missing config file");
182
205
  other && console.warn(`Multiple config files found in ${cwd}. Using ${configPath}.`);
183
- let config = await $jiti.import(configPath, { default: true });
184
- return _config = Object.freeze(await ProjectConfigSchema.parseAsync(config)), _config;
206
+ let config = await Module.import(configPath, true);
207
+ return Object.freeze(await ProjectConfigSchema.parseAsync(config));
185
208
  }
186
209
  function getConfig() {
187
- if (!_config)
188
- throw new Error("Project config is not loaded.");
189
- return _config;
210
+ throw new Error("Project config is not loaded.");
190
211
  }
191
212
 
192
- // src/base/BaseClientManager.ts
193
- var BaseClientManager = class {
194
- constructor(client) {
195
- this.client = client;
196
- }
197
- };
198
-
199
213
  // src/base/lifecycle/Context.ts
200
214
  var Context = class {
201
215
  canceled = false;
@@ -397,16 +411,17 @@ var BaseParam = class {
397
411
  return this.setOption("required", value);
398
412
  }
399
413
  async resolve(context, value) {
400
- let { required, name } = this.options;
401
- if (value === void 0) {
402
- if (required)
403
- throw new ArgumentError(name, "is required");
404
- return null;
405
- }
406
414
  if (context.isChatInput())
407
415
  return await this.resolveChatInput(context);
408
- if (context.isMessage())
416
+ if (context.isMessage()) {
417
+ let { required, name } = this.options;
418
+ if (value === void 0) {
419
+ if (required)
420
+ throw new ArgumentError(name, "is required");
421
+ return null;
422
+ }
409
423
  return await this.resolveMessage(context, value);
424
+ }
410
425
  throw new Error("Invalid context type provided");
411
426
  }
412
427
  /**
@@ -568,31 +583,55 @@ function defineCommand(options) {
568
583
  return new Command(options);
569
584
  }
570
585
 
586
+ // src/base/client/BaseClientManager.ts
587
+ var BaseClientManager = class {
588
+ constructor(client) {
589
+ this.client = client;
590
+ }
591
+ };
592
+
571
593
  // src/base/command/CommandManager.ts
572
594
  var CommandManager = class extends BaseClientManager {
573
595
  commands = new Collection();
574
- async loadModules() {
575
- let entryDir = posix.resolve(getConfig().entryDir), pattern = posix.join(entryDir, "commands", "**/*.{ts,js}"), loads = (await glob(pattern, {
596
+ entries = new Collection();
597
+ async loadModules(entryDir) {
598
+ let pattern = posix.join(posix.resolve(entryDir), "commands", "**/*.{ts,js}"), files = await glob(pattern, {
576
599
  cwd: process.cwd(),
577
600
  absolute: true
578
- })).map(async (file) => {
579
- try {
580
- let command = await $jiti.import(file, { default: !0 });
581
- if (!command) {
582
- console.warn(`[Loader] File has no default export: ${file}`);
583
- return;
584
- }
585
- if (!(command instanceof Command)) {
586
- console.warn(`[Loader] Default export is not a Command: ${file}`);
587
- return;
588
- }
589
- return this.add(command), command;
590
- } catch (error) {
591
- console.error(`An error occurred while trying to add command for '${file}':`, error);
592
- }
593
- }), loaded = (await Promise.all(loads)).filter((x) => x !== void 0);
594
- return console.log(`Loaded ${loaded.length} command(s).`), loaded;
601
+ }), filtered = (await Promise.all(files.map((file) => this.load(file)))).filter((c) => !!c);
602
+ return console.log(`[Loader] Loaded ${filtered.length}/${files.length}`), filtered;
595
603
  }
604
+ /**
605
+ * Load the file and add the command to the registry.
606
+ * @param path The path to the command file.
607
+ * @returns The command object if added successfully.
608
+ */
609
+ async load(path2) {
610
+ let command = await Module.import(path2, true);
611
+ if (!command) {
612
+ console.warn(`[Loader] File has no default export: ${path2}`);
613
+ return;
614
+ }
615
+ if (!(command instanceof Command)) {
616
+ console.warn(`[Loader] Default export is not a Command: ${path2}`);
617
+ return;
618
+ }
619
+ return this.add(command), this.entries.set(path2, command), command;
620
+ }
621
+ /**
622
+ * Unload the file and remove the command from the registry.
623
+ * @param path The path to the command file.
624
+ * @returns The command object if unloaded successfully.
625
+ */
626
+ async unload(path2) {
627
+ let command = this.entries.get(path2);
628
+ if (Module.isLoaded(path2) && (command ??= await Module.import(path2, true), Module.unload(path2)), this.entries.delete(path2), !!command)
629
+ return this.remove(command);
630
+ }
631
+ /**
632
+ * Add a command to the registry.
633
+ * @param command Command to add.
634
+ */
596
635
  add(command) {
597
636
  if (!(command instanceof Command))
598
637
  throw new Error("Invalid command provided");
@@ -603,11 +642,23 @@ var CommandManager = class extends BaseClientManager {
603
642
  }
604
643
  this.commands.set(name, command);
605
644
  }
645
+ /**
646
+ * Remove a command from the registry.
647
+ * @param target Command name or object to remove.
648
+ * @returns The command object if removed successfully.
649
+ */
606
650
  remove(target) {
651
+ if (typeof target != "string" && !(target instanceof Command))
652
+ return;
607
653
  let name = typeof target == "string" ? target : target.options.name, existing = this.commands.get(name);
608
654
  if (existing)
609
655
  return this.commands.delete(name), existing;
610
656
  }
657
+ /**
658
+ * Get a command using its name.
659
+ * @param name The command to get.
660
+ * @returns The command object.
661
+ */
611
662
  get(name) {
612
663
  return this.commands.get(name);
613
664
  }
@@ -627,40 +678,59 @@ function defineListener(options) {
627
678
  }
628
679
  var ListenerManager = class extends BaseClientManager {
629
680
  listeners = [];
630
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
681
+ entries = new Collection();
631
682
  executors = /* @__PURE__ */ new WeakMap();
632
- async loadModules() {
633
- let entryDir = posix.resolve(getConfig().entryDir), pattern = posix.join(entryDir, "listeners", "**/*.{ts,js}"), loads = (await glob(pattern, {
683
+ async loadModules(entryDir) {
684
+ let pattern = posix.join(posix.resolve(entryDir), "listeners", "**/*.{ts,js}"), files = await glob(pattern, {
634
685
  cwd: process.cwd(),
635
686
  absolute: true
636
- })).map(async (file) => {
637
- try {
638
- let listener = await $jiti.import(file, { default: !0 });
639
- if (!listener) {
640
- console.warn(`[Loader] File has no default export: ${file}`);
641
- return;
642
- }
643
- if (!(listener instanceof Listener)) {
644
- console.warn(`[Loader] Default export is not a Listener: ${file}`);
645
- return;
646
- }
647
- return this.add(listener), listener;
648
- } catch (error) {
649
- console.error(`An error occurred while trying to add listener for '${file}':`, error);
650
- }
651
- }), loaded = (await Promise.all(loads)).filter((x) => x !== void 0);
652
- return console.log(`Loaded ${loaded.length} listener(s).`), loaded;
687
+ }), filtered = (await Promise.all(files.map((file) => this.load(file)))).filter((l) => !!l);
688
+ return console.log(`[Loader] Loaded ${filtered.length}/${files.length}`), filtered;
653
689
  }
690
+ /**
691
+ * Load the file and add the listener to the registry.
692
+ * @param path The path to the listener file.
693
+ * @returns The listener object if added successfully.
694
+ */
695
+ async load(path2) {
696
+ let listener = await Module.import(path2, true);
697
+ if (!listener) {
698
+ console.warn(`[Loader] File has no default export: ${path2}`);
699
+ return;
700
+ }
701
+ if (!(listener instanceof Listener)) {
702
+ console.warn(`[Loader] Default export is not a Listener: ${path2}`);
703
+ return;
704
+ }
705
+ return this.add(listener), this.entries.set(path2, listener), listener;
706
+ }
707
+ /**
708
+ * Unload the file and remove the listener from the registry.
709
+ * @param path The path to the listener file.
710
+ * @returns The listener object if unloaded successfully.
711
+ */
712
+ async unload(path2) {
713
+ let listener = this.entries.get(path2);
714
+ if (Module.isLoaded(path2) && (listener ??= await Module.import(path2, true), Module.unload(path2)), this.entries.delete(path2), !!listener)
715
+ return this.remove(listener)?.[0];
716
+ }
717
+ /**
718
+ * Add a listener to the registry and create a listener for client.
719
+ * @param listener Listener to add.
720
+ */
654
721
  add(listener) {
655
722
  if (!(listener instanceof Listener))
656
723
  throw new Error("Invalid listener provided");
657
- let execute = (...args) => {
724
+ let { once, name } = listener.options, execute = (...args) => {
658
725
  listener.execute(new Context(), ...args);
659
726
  };
660
- this.listeners.push(listener), this.executors.set(listener, execute);
661
- let { once, name } = listener.options;
662
- this.client[once ? "once" : "on"](name, execute);
727
+ this.listeners.push(listener), this.executors.set(listener, execute), this.client[once ? "once" : "on"](name, execute);
663
728
  }
729
+ /**
730
+ * Remove a listener from the registry and client.
731
+ * @param target Listener name or object to remove.
732
+ * @returns The list of listener objects if removed successfully.
733
+ */
664
734
  remove(target) {
665
735
  let isMatched = (listener) => typeof target == "string" ? listener.options.name === target : listener === target, removed = [];
666
736
  return this.listeners = this.listeners.filter((listener) => {
@@ -671,9 +741,17 @@ var ListenerManager = class extends BaseClientManager {
671
741
  return execute && (this.client.removeListener(listener.options.name, execute), this.executors.delete(listener)), false;
672
742
  }), removed;
673
743
  }
744
+ /**
745
+ * Get a list of required intents for Bakit to run correctly.
746
+ * @returns Used intents.
747
+ */
674
748
  getBaseIntents() {
675
749
  return new IntentsBitField([GatewayIntentBits.Guilds]);
676
750
  }
751
+ /**
752
+ * Get a list of needed intents based on registered listeners to receive needed events.
753
+ * @returns Used intents.
754
+ */
677
755
  getNeededIntents() {
678
756
  let result = this.getBaseIntents();
679
757
  for (let listener of this.listeners) {
@@ -684,7 +762,7 @@ var ListenerManager = class extends BaseClientManager {
684
762
  }
685
763
  };
686
764
 
687
- // src/base/BakitClient.ts
765
+ // src/base/client/BakitClient.ts
688
766
  var BakitClient2 = class extends Client {
689
767
  constructor(options, instance) {
690
768
  super(options);
@@ -734,10 +812,66 @@ function createFactory(ctor) {
734
812
  }
735
813
  var Params = {
736
814
  string: createFactory(StringParam),
737
- number: createFactory(NumberParam)
815
+ number: createFactory(NumberParam),
816
+ user: createFactory(UserParam)
817
+ };
818
+ var DevProcessManager = class {
819
+ constructor(options) {
820
+ this.options = options;
821
+ }
822
+ child = null;
823
+ restartTimer = null;
824
+ start() {
825
+ console.log("Starting bakit in dev mode..."), this.startChild(), this.startWatcher();
826
+ }
827
+ startChild() {
828
+ if (this.child) return;
829
+ let entry = path.resolve(this.options.entry);
830
+ this.child = fork(entry, {
831
+ execArgv: ["--import", "tsx"],
832
+ stdio: "inherit",
833
+ env: {
834
+ ...process.env,
835
+ NODE_ENV: "development"
836
+ }
837
+ }), this.child.on("exit", () => {
838
+ this.child = null;
839
+ });
840
+ }
841
+ restartChild() {
842
+ if (!this.child)
843
+ return this.startChild();
844
+ let old = this.child;
845
+ old.once("exit", () => {
846
+ this.child = null, this.startChild();
847
+ }), old.kill("SIGTERM");
848
+ }
849
+ startWatcher() {
850
+ let { rootDir } = this.options;
851
+ chokidar.watch(rootDir, {
852
+ ignoreInitial: true,
853
+ awaitWriteFinish: {
854
+ stabilityThreshold: 200,
855
+ pollInterval: 50
856
+ }
857
+ }).on("change", (file) => this.onFileChanged(file));
858
+ }
859
+ onFileChanged(file) {
860
+ if (!this.child)
861
+ return;
862
+ let top = Module.getTopLevel(file, this.options.rootDir);
863
+ if (top && this.options.hotDirs.includes(top)) {
864
+ this.child.connected && this.child.send({ type: `hmr:${top}`, file });
865
+ return;
866
+ }
867
+ this.scheduleRestart();
868
+ }
869
+ scheduleRestart() {
870
+ this.restartTimer && clearTimeout(this.restartTimer), this.restartTimer = setTimeout(() => {
871
+ console.log("Detected changes, restarting..."), this.restartChild(), this.restartTimer = null;
872
+ }, 150);
873
+ }
738
874
  };
739
-
740
- // src/defaults/command.ts
741
875
  var messageCommandHandler = defineListener(Events.MessageCreate), chatInputCommandHandler = defineListener(Events.InteractionCreate), registerCommandsHandler = defineListener({
742
876
  name: Events.ClientReady,
743
877
  once: true
@@ -807,14 +941,14 @@ var ProjectCacheManager = class {
807
941
  getHash(data) {
808
942
  return createHash("sha256").update(JSON.stringify(data)).digest("hex");
809
943
  }
810
- async write(path, data) {
811
- let fullPath = join(this.rootDir, path), dir = dirname(fullPath);
944
+ async write(path2, data) {
945
+ let fullPath = join(this.rootDir, path2), dir = dirname(fullPath);
812
946
  await mkdir(dir, { recursive: true });
813
947
  let content = typeof data == "string" ? data : JSON.stringify(data);
814
948
  await writeFile(fullPath, content, "utf-8");
815
949
  }
816
- async read(path) {
817
- let fullPath = join(this.rootDir, path);
950
+ async read(path2) {
951
+ let fullPath = join(this.rootDir, path2);
818
952
  try {
819
953
  let content = await readFile(fullPath, "utf-8");
820
954
  return JSON.parse(content);
@@ -830,7 +964,7 @@ var ProjectCacheManager = class {
830
964
  }
831
965
  };
832
966
 
833
- // src/base/Instance.ts
967
+ // src/base/process/Instance.ts
834
968
  var Instance = class {
835
969
  client;
836
970
  cache;
@@ -846,19 +980,37 @@ var Instance = class {
846
980
  ...config.clientOptions
847
981
  },
848
982
  this
849
- ), await this.loadModules(), this.initIntents(), await this.client.login(config.token);
983
+ ), await this.loadModules(), this.initIntents(), await this.client.login(config.token), this.initProcess();
984
+ }
985
+ initProcess() {
986
+ process.on("message", (msg) => this.onProcessMessage(msg));
850
987
  }
851
988
  loadModules() {
852
989
  let { managers } = this.client, { commands, listeners } = managers;
853
- return listeners.add(chatInputCommandHandler), listeners.add(messageCommandHandler), listeners.add(registerCommandsHandler), Promise.all([commands.loadModules(), listeners.loadModules()]);
990
+ return listeners.add(chatInputCommandHandler), listeners.add(messageCommandHandler), listeners.add(registerCommandsHandler), Promise.all([commands.loadModules("src"), listeners.loadModules("src")]);
854
991
  }
855
992
  initIntents() {
856
993
  let config = getConfig(), { options, managers } = this.client, { listeners } = managers, intents;
857
994
  config.intents === "auto" ? intents = listeners.getNeededIntents() : (intents = listeners.getBaseIntents(), typeof config.intents == "bigint" ? intents.bitfield = Number(config.intents) : intents.add(...config.intents)), options.intents = intents;
858
995
  }
996
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
997
+ async onProcessMessage(message) {
998
+ let { type, file } = message;
999
+ if (!type.startsWith("hmr:"))
1000
+ return;
1001
+ let target = type.split(":")[1], { listeners, commands } = this.client.managers;
1002
+ switch (target) {
1003
+ case "listeners":
1004
+ listeners.unload(file), await listeners.load(file);
1005
+ break;
1006
+ case "commands":
1007
+ commands.unload(file), await listeners.load(file);
1008
+ break;
1009
+ }
1010
+ }
859
1011
  };
860
1012
  function useApp() {
861
1013
  return new Instance();
862
1014
  }
863
1015
 
864
- export { $jiti, ArgumentError, BakitClient2 as BakitClient, BakitError, BaseClientManager, BaseCommandContext, BaseParam, BaseParamSchema, ChatInputContext, Command, CommandManager, CommandOptionsSchema, Context, EVENT_INTENT_MAPPING, HookOrder, HookState, Instance, LifecycleManager, Listener, ListenerManager, ListenerOptionsSchema, MessageContext, NumberParam, NumberParamSchema, ParamUserType, Params, ProjectConfigSchema, StringParam, StringParamSchema, UserParam, UserParamSchema, defineCommand, defineConfig, defineListener, extractSnowflakeId, getConfig, loadConfig, tokenize, useApp, validateParamsOrder };
1016
+ export { ArgumentError, BakitClient2 as BakitClient, BakitError, BaseClientManager, BaseCommandContext, BaseParam, BaseParamSchema, ChatInputContext, Command, CommandManager, CommandOptionsSchema, Context, DevProcessManager, EVENT_INTENT_MAPPING, HookOrder, HookState, Instance, LifecycleManager, Listener, ListenerManager, ListenerOptionsSchema, MessageContext, Module, NumberParam, NumberParamSchema, ParamUserType, Params, ProjectCacheManager, ProjectConfigSchema, StringParam, StringParamSchema, UserParam, UserParamSchema, defineCommand, defineConfig, defineListener, extractSnowflakeId, getConfig, loadConfig, tokenize, useApp, validateParamsOrder };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bakit",
3
- "version": "2.0.0-alpha.10",
3
+ "version": "2.0.0-alpha.12",
4
4
  "description": "A framework for discord.js",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -11,7 +11,7 @@
11
11
  "test": "vitest run --pass-with-no-tests"
12
12
  },
13
13
  "bin": {
14
- "bakit": "./bin/bakit.js"
14
+ "bakit": "./dist/bin/bakit.js"
15
15
  },
16
16
  "files": [
17
17
  "dist"
@@ -37,6 +37,7 @@
37
37
  "discord.js": "^14.0.0"
38
38
  },
39
39
  "dependencies": {
40
+ "chokidar": "^5.0.0",
40
41
  "commander": "^14.0.2",
41
42
  "dotenv": "^17.2.1",
42
43
  "jiti": "^2.6.1",
package/bin/bakit.js DELETED
@@ -1,39 +0,0 @@
1
- // @ts-check
2
- import { config as useEnv } from "dotenv";
3
- import { program } from "commander";
4
-
5
- program.name("bakit");
6
-
7
- program.command("dev").action(async () => {
8
- useEnv({
9
- path: [".env.local", ".env"],
10
- quiet: true,
11
- });
12
- const { default: nodemon } = await import("nodemon");
13
-
14
- nodemon({
15
- script: "src/index.ts",
16
- exec: `${process.execPath} --import tsx`,
17
- ext: "ts,js",
18
- watch: ["src"],
19
- env: {
20
- ...process.env,
21
- FORCE_COLOR: "1",
22
- NODE_ENV: "development",
23
- },
24
- });
25
-
26
- nodemon.on("start", () => {
27
- console.log("Starting bakit app...");
28
- });
29
-
30
- nodemon.on("restart", () => {
31
- console.log("Bakit detected changes! Restarting...");
32
- });
33
-
34
- nodemon.on("quit", () => {
35
- process.exit();
36
- });
37
- });
38
-
39
- program.parse();