bakit 2.0.0-alpha.11 → 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;
@@ -406,6 +471,24 @@ declare const Params: {
406
471
  }) => UserParam<Required>;
407
472
  };
408
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
+
409
492
  declare class BakitError extends Error {
410
493
  constructor(message: string);
411
494
  }
@@ -416,4 +499,4 @@ declare class ArgumentError extends BakitError {
416
499
  constructor(target: string, reason: string);
417
500
  }
418
501
 
419
- 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;
@@ -569,31 +583,55 @@ function defineCommand(options) {
569
583
  return new Command(options);
570
584
  }
571
585
 
586
+ // src/base/client/BaseClientManager.ts
587
+ var BaseClientManager = class {
588
+ constructor(client) {
589
+ this.client = client;
590
+ }
591
+ };
592
+
572
593
  // src/base/command/CommandManager.ts
573
594
  var CommandManager = class extends BaseClientManager {
574
595
  commands = new Collection();
575
- async loadModules() {
576
- 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, {
577
599
  cwd: process.cwd(),
578
600
  absolute: true
579
- })).map(async (file) => {
580
- try {
581
- let command = await $jiti.import(file, { default: !0 });
582
- if (!command) {
583
- console.warn(`[Loader] File has no default export: ${file}`);
584
- return;
585
- }
586
- if (!(command instanceof Command)) {
587
- console.warn(`[Loader] Default export is not a Command: ${file}`);
588
- return;
589
- }
590
- return this.add(command), command;
591
- } catch (error) {
592
- console.error(`An error occurred while trying to add command for '${file}':`, error);
593
- }
594
- }), loaded = (await Promise.all(loads)).filter((x) => x !== void 0);
595
- 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;
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;
596
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
+ */
597
635
  add(command) {
598
636
  if (!(command instanceof Command))
599
637
  throw new Error("Invalid command provided");
@@ -604,11 +642,23 @@ var CommandManager = class extends BaseClientManager {
604
642
  }
605
643
  this.commands.set(name, command);
606
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
+ */
607
650
  remove(target) {
651
+ if (typeof target != "string" && !(target instanceof Command))
652
+ return;
608
653
  let name = typeof target == "string" ? target : target.options.name, existing = this.commands.get(name);
609
654
  if (existing)
610
655
  return this.commands.delete(name), existing;
611
656
  }
657
+ /**
658
+ * Get a command using its name.
659
+ * @param name The command to get.
660
+ * @returns The command object.
661
+ */
612
662
  get(name) {
613
663
  return this.commands.get(name);
614
664
  }
@@ -628,40 +678,59 @@ function defineListener(options) {
628
678
  }
629
679
  var ListenerManager = class extends BaseClientManager {
630
680
  listeners = [];
631
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
681
+ entries = new Collection();
632
682
  executors = /* @__PURE__ */ new WeakMap();
633
- async loadModules() {
634
- 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, {
635
685
  cwd: process.cwd(),
636
686
  absolute: true
637
- })).map(async (file) => {
638
- try {
639
- let listener = await $jiti.import(file, { default: !0 });
640
- if (!listener) {
641
- console.warn(`[Loader] File has no default export: ${file}`);
642
- return;
643
- }
644
- if (!(listener instanceof Listener)) {
645
- console.warn(`[Loader] Default export is not a Listener: ${file}`);
646
- return;
647
- }
648
- return this.add(listener), listener;
649
- } catch (error) {
650
- console.error(`An error occurred while trying to add listener for '${file}':`, error);
651
- }
652
- }), loaded = (await Promise.all(loads)).filter((x) => x !== void 0);
653
- 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;
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];
654
716
  }
717
+ /**
718
+ * Add a listener to the registry and create a listener for client.
719
+ * @param listener Listener to add.
720
+ */
655
721
  add(listener) {
656
722
  if (!(listener instanceof Listener))
657
723
  throw new Error("Invalid listener provided");
658
- let execute = (...args) => {
724
+ let { once, name } = listener.options, execute = (...args) => {
659
725
  listener.execute(new Context(), ...args);
660
726
  };
661
- this.listeners.push(listener), this.executors.set(listener, execute);
662
- let { once, name } = listener.options;
663
- this.client[once ? "once" : "on"](name, execute);
727
+ this.listeners.push(listener), this.executors.set(listener, execute), this.client[once ? "once" : "on"](name, execute);
664
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
+ */
665
734
  remove(target) {
666
735
  let isMatched = (listener) => typeof target == "string" ? listener.options.name === target : listener === target, removed = [];
667
736
  return this.listeners = this.listeners.filter((listener) => {
@@ -672,9 +741,17 @@ var ListenerManager = class extends BaseClientManager {
672
741
  return execute && (this.client.removeListener(listener.options.name, execute), this.executors.delete(listener)), false;
673
742
  }), removed;
674
743
  }
744
+ /**
745
+ * Get a list of required intents for Bakit to run correctly.
746
+ * @returns Used intents.
747
+ */
675
748
  getBaseIntents() {
676
749
  return new IntentsBitField([GatewayIntentBits.Guilds]);
677
750
  }
751
+ /**
752
+ * Get a list of needed intents based on registered listeners to receive needed events.
753
+ * @returns Used intents.
754
+ */
678
755
  getNeededIntents() {
679
756
  let result = this.getBaseIntents();
680
757
  for (let listener of this.listeners) {
@@ -685,7 +762,7 @@ var ListenerManager = class extends BaseClientManager {
685
762
  }
686
763
  };
687
764
 
688
- // src/base/BakitClient.ts
765
+ // src/base/client/BakitClient.ts
689
766
  var BakitClient2 = class extends Client {
690
767
  constructor(options, instance) {
691
768
  super(options);
@@ -738,8 +815,63 @@ var Params = {
738
815
  number: createFactory(NumberParam),
739
816
  user: createFactory(UserParam)
740
817
  };
741
-
742
- // src/defaults/command.ts
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
+ }
874
+ };
743
875
  var messageCommandHandler = defineListener(Events.MessageCreate), chatInputCommandHandler = defineListener(Events.InteractionCreate), registerCommandsHandler = defineListener({
744
876
  name: Events.ClientReady,
745
877
  once: true
@@ -809,14 +941,14 @@ var ProjectCacheManager = class {
809
941
  getHash(data) {
810
942
  return createHash("sha256").update(JSON.stringify(data)).digest("hex");
811
943
  }
812
- async write(path, data) {
813
- let fullPath = join(this.rootDir, path), dir = dirname(fullPath);
944
+ async write(path2, data) {
945
+ let fullPath = join(this.rootDir, path2), dir = dirname(fullPath);
814
946
  await mkdir(dir, { recursive: true });
815
947
  let content = typeof data == "string" ? data : JSON.stringify(data);
816
948
  await writeFile(fullPath, content, "utf-8");
817
949
  }
818
- async read(path) {
819
- let fullPath = join(this.rootDir, path);
950
+ async read(path2) {
951
+ let fullPath = join(this.rootDir, path2);
820
952
  try {
821
953
  let content = await readFile(fullPath, "utf-8");
822
954
  return JSON.parse(content);
@@ -832,7 +964,7 @@ var ProjectCacheManager = class {
832
964
  }
833
965
  };
834
966
 
835
- // src/base/Instance.ts
967
+ // src/base/process/Instance.ts
836
968
  var Instance = class {
837
969
  client;
838
970
  cache;
@@ -848,19 +980,37 @@ var Instance = class {
848
980
  ...config.clientOptions
849
981
  },
850
982
  this
851
- ), 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));
852
987
  }
853
988
  loadModules() {
854
989
  let { managers } = this.client, { commands, listeners } = managers;
855
- 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")]);
856
991
  }
857
992
  initIntents() {
858
993
  let config = getConfig(), { options, managers } = this.client, { listeners } = managers, intents;
859
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;
860
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
+ }
861
1011
  };
862
1012
  function useApp() {
863
1013
  return new Instance();
864
1014
  }
865
1015
 
866
- 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.11",
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();