bakit 2.0.0-alpha.24 → 2.0.0-alpha.26

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/cli.js CHANGED
@@ -1,71 +1,61 @@
1
1
  import { config } from 'dotenv';
2
2
  import { program } from 'commander';
3
3
  import { fork } from 'child_process';
4
- import path, { resolve, relative, sep } from 'path';
4
+ import path, { resolve } from 'path';
5
5
  import chokidar from 'chokidar';
6
+ import { RPC } from 'bakit';
6
7
 
7
8
  // src/cli/bin.ts
8
- function getTopLevelDirectory(path2, entryDir) {
9
- return relative(entryDir, path2).split(sep)[0] ?? null;
10
- }
11
-
12
- // src/cli/process/DevProcessManager.ts
13
9
  var DevProcessManager = class {
14
10
  constructor(options) {
15
11
  this.options = options;
16
12
  }
17
- child = null;
13
+ rpc = null;
18
14
  restartTimer = null;
19
15
  start() {
20
16
  console.log("Starting bakit in dev mode..."), this.startChild(), this.startWatcher();
21
17
  }
22
18
  startChild() {
23
- if (this.child) return;
24
- let entry = path.resolve(this.options.entry);
25
- this.child = fork(entry, {
19
+ if (this.rpc)
20
+ return;
21
+ let entry = path.resolve(this.options.entry), child = fork(entry, {
26
22
  execArgv: ["--import", "bakit/register"],
27
23
  stdio: "inherit",
28
24
  env: {
29
25
  ...process.env,
30
26
  NODE_ENV: "development"
31
27
  }
32
- }), this.child.on("exit", () => {
33
- this.child = null;
34
28
  });
29
+ this.rpc = new RPC(child), this.rpc.on("restart", (fileUpdated) => this.scheduleRestart(fileUpdated));
35
30
  }
36
31
  restartChild() {
37
- if (!this.child)
32
+ if (!this.rpc)
38
33
  return this.startChild();
39
- let old = this.child;
40
- old.once("exit", () => {
41
- this.child = null, this.startChild();
42
- }), old.kill("SIGTERM");
34
+ let child = this.rpc.transport;
35
+ child.once("exit", () => {
36
+ this.rpc = null, this.startChild();
37
+ }), child.kill("SIGTERM");
43
38
  }
44
39
  startWatcher() {
45
- let { rootDir } = this.options;
46
- chokidar.watch(rootDir, {
40
+ let { rootDir } = this.options, watcher = chokidar.watch(rootDir, {
47
41
  ignoreInitial: true,
48
42
  awaitWriteFinish: {
49
43
  stabilityThreshold: 200,
50
44
  pollInterval: 50
51
45
  }
52
- }).on("change", (path2) => {
53
- this.onFileChanged(path2);
54
46
  });
47
+ watcher.on("change", (path2) => this.onFileUpdate("fileChange", path2)), watcher.on("unlink", (path2) => this.onFileUpdate("fileRemove", path2));
55
48
  }
56
- onFileChanged(path2) {
57
- if (!this.child)
58
- return;
59
- let top = getTopLevelDirectory(path2, this.options.rootDir);
60
- if (top && this.options.hotDirs.includes(top)) {
61
- this.child.connected && this.child.send({ type: `hmr:${top}`, path: resolve(path2) });
62
- return;
49
+ onFileUpdate(type, path2) {
50
+ try {
51
+ this.rpc?.send(type, resolve(path2));
52
+ } catch {
53
+ this.scheduleRestart(true);
63
54
  }
64
- this.scheduleRestart();
65
55
  }
66
- scheduleRestart() {
56
+ scheduleRestart(fileUpdated = false) {
67
57
  this.restartTimer && clearTimeout(this.restartTimer), this.restartTimer = setTimeout(() => {
68
- console.log("Detected changes, restarting..."), this.restartChild(), this.restartTimer = null;
58
+ fileUpdated && console.log("File changes detected, restarting..."), this.restartChild(), this.restartTimer = null;
69
59
  }, 150);
70
60
  }
71
61
  };
package/dist/hooks.js CHANGED
@@ -3,14 +3,15 @@ import { readFile } from 'fs/promises';
3
3
  import { Module } from 'module';
4
4
  import { dirname, resolve as resolve$1, basename } from 'path';
5
5
  import { fileURLToPath, pathToFileURL } from 'url';
6
+ import { RPC } from 'bakit';
6
7
 
7
8
  // src/lib/loader/hooks.ts
8
- var EXTENSIONS = [".js", ".ts"], parentPort, versions, esbuild;
9
+ var EXTENSIONS = [".js", ".ts"], rpc, versions, esbuild;
9
10
  function isDevelopment() {
10
11
  return process.env.NODE_ENV === "development";
11
12
  }
12
13
  async function initialize({ port }) {
13
- isDevelopment() && (parentPort = port, parentPort.on("message", onMessage), versions = /* @__PURE__ */ new Map(), esbuild = await import('esbuild'));
14
+ rpc = new RPC(port), versions = /* @__PURE__ */ new Map(), rpc.on("message", onUnload), isDevelopment() && (esbuild = await import('esbuild'));
14
15
  }
15
16
  async function resolve(specifier, context, nextResolve) {
16
17
  if (shouldSkip(specifier))
@@ -27,10 +28,9 @@ async function resolve(specifier, context, nextResolve) {
27
28
  let urlObj = new URL(url);
28
29
  if (isDevelopment()) {
29
30
  let filePath = fileURLToPath(urlObj), version = createVersion(filePath);
30
- urlObj.searchParams.set("hmr", version), parentURL && parentPort && parentPort.postMessage({
31
- type: "dependency",
32
- parent: parentURL,
33
- child: url
31
+ urlObj.searchParams.set("hmr", version), parentURL && rpc.send("dependencyAdd", {
32
+ parentURL,
33
+ url
34
34
  });
35
35
  }
36
36
  return {
@@ -62,8 +62,8 @@ async function load(url, context, nextLoad) {
62
62
  return nextLoad(url, context);
63
63
  }
64
64
  function createVersion(filename) {
65
- let version = versions?.get(filename);
66
- return version || (version = Date.now().toString(), versions?.set(filename, version)), version;
65
+ let version = versions.get(filename);
66
+ return version || (version = Date.now().toString(), versions.set(filename, version)), version;
67
67
  }
68
68
  function shouldSkip(specifier) {
69
69
  if (Module.isBuiltin(specifier) || specifier.includes("/node_modules/"))
@@ -74,19 +74,9 @@ function shouldSkip(specifier) {
74
74
  }
75
75
  return true;
76
76
  }
77
- function onMessage(message) {
78
- let { type, data, id } = message;
79
- if (type !== "unload") {
80
- parentPort?.postMessage({
81
- id,
82
- data: false
83
- });
84
- return;
85
- }
86
- versions?.delete(data), parentPort?.postMessage({
87
- id,
88
- data: true
89
- });
77
+ function onUnload(id, path) {
78
+ let deleted = versions.delete(resolve$1(path));
79
+ rpc.success(id, deleted);
90
80
  }
91
81
 
92
82
  export { initialize, load, resolve };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,9 @@ import * as discord_js from 'discord.js';
2
2
  import { ChatInputCommandInteraction, CacheType, Message, User, MessageCreateOptions, InteractionReplyOptions, Awaitable, Collection, Events, IntentsBitField, ClientEvents, Client, ClientOptions, GatewayIntentBits } from 'discord.js';
3
3
  import { inspect } from 'node:util';
4
4
  import z from 'zod';
5
+ import EventEmitter from 'node:events';
6
+ import { Serializable, ChildProcess } from 'node:child_process';
7
+ import { MessagePort as MessagePort$1 } from 'node:worker_threads';
5
8
 
6
9
  declare enum ParamUserType {
7
10
  Bot = "bot",
@@ -367,12 +370,20 @@ declare class ProjectCacheManager {
367
370
  declare class Instance {
368
371
  client: BakitClient;
369
372
  cache: ProjectCacheManager;
373
+ private rpc;
370
374
  constructor();
371
375
  start(): Promise<void>;
372
376
  private initProcess;
373
377
  private loadModules;
374
378
  private initIntents;
375
- private onProcessMessage;
379
+ private isInHotDirectory;
380
+ private restart;
381
+ shutdown(): Promise<void>;
382
+ private containsEntryPoint;
383
+ private containsHotModule;
384
+ private onFileRemove;
385
+ private onFileChange;
386
+ private unloadModule;
376
387
  }
377
388
  declare function useApp(): Instance;
378
389
 
@@ -449,20 +460,52 @@ declare function extractSnowflakeId(input: string): string | null;
449
460
 
450
461
  declare function getTopLevelDirectory(path: string, entryDir: string): string | null;
451
462
 
452
- interface PostMessage<Data> {
453
- id: number;
463
+ declare function $initLoader(): void;
464
+ declare function $unloadFile(path: string): Promise<boolean>;
465
+ declare function getImporters(path: string, createNew: true): Set<string>;
466
+ declare function getImporters(path: string, createNew?: false): Set<string> | undefined;
467
+ declare function getDependencyChain(path: string): string[];
468
+ declare function isImported(path: string): boolean;
469
+ declare function isImportedBy(path: string, matcher: string | RegExp | ((path: string) => boolean)): boolean;
470
+
471
+ declare const RPC_RESPONSE_MARK = "$DONE:";
472
+ declare const RPC_RESPONSE_TIMEOUT = 5000;
473
+ interface BaseRPCMessage {
474
+ id: string;
475
+ }
476
+ interface BaseRPCResponse {
477
+ id: `${typeof RPC_RESPONSE_MARK}${string}`;
478
+ }
479
+ interface RPCRequest<Data extends Serializable = Serializable> extends BaseRPCMessage {
454
480
  type: string;
455
481
  data: Data;
456
482
  }
457
- interface ResponseMessage<Data> {
458
- id: number;
459
- error?: string;
460
- data?: Data;
483
+ interface RPCSuccessResponse<Data extends Serializable = Serializable> extends BaseRPCResponse {
484
+ data: Data;
485
+ }
486
+ interface RPCErrorResponse extends BaseRPCResponse {
487
+ error: string;
488
+ }
489
+ type RPCResponse<Data extends Serializable = Serializable> = RPCSuccessResponse<Data> | RPCErrorResponse;
490
+ type RPCMessage<Data extends Serializable = Serializable> = RPCRequest<Data> | RPCResponse<Data>;
491
+ interface RPCPendingPromise {
492
+ resolve: (data: any) => void;
493
+ reject: (error: unknown) => void;
494
+ timeout: NodeJS.Timeout;
495
+ }
496
+ declare class RPC extends EventEmitter {
497
+ transport: MessagePort$1 | NodeJS.Process | ChildProcess;
498
+ requests: Map<string, RPCPendingPromise>;
499
+ constructor(transport: MessagePort$1 | NodeJS.Process | ChildProcess);
500
+ postMessage(message: Serializable): void;
501
+ private onMessage;
502
+ private handleResponseMessage;
503
+ private handleRequestMessage;
504
+ send<Data extends Serializable>(type: string, data: Data, id?: string): void;
505
+ success<Data extends Serializable>(id: string, data: Data): void;
506
+ error(id: string, error: string): void;
507
+ request<Data extends Serializable, Output extends Serializable>(type: string, data: Data, id?: string): Promise<Output>;
461
508
  }
462
- declare function $initLoader(): void;
463
- declare function $postLoaderMessage<Data>(type: string, data: Data, wait?: false): void;
464
- declare function $postLoaderMessage<Data, Output = unknown>(type: string, data: Data, wait: true): Promise<Output>;
465
- declare function $unloadFile(path: string): Promise<boolean>;
466
509
 
467
510
  declare const messageCommandHandler: Listener<Events.MessageCreate>;
468
511
  declare const chatInputCommandHandler: Listener<Events.InteractionCreate>;
@@ -494,4 +537,36 @@ declare function loadConfig(cwd?: string): Promise<ProjectConfig>;
494
537
  */
495
538
  declare function getConfig(): ProjectConfig;
496
539
 
497
- export { $initLoader, $postLoaderMessage, $unloadFile, 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 PostMessage, ProjectCacheManager, type ProjectConfig, type ProjectConfigInput, ProjectConfigSchema, type ResponseMessage, type StringOptions, StringParam, StringParamSchema, type UserOptions, UserParam, UserParamSchema, chatInputCommandHandler, defineCommand, defineConfig, defineListener, extractSnowflakeId, getConfig, getTopLevelDirectory, loadConfig, messageCommandHandler, registerCommandsHandler, tokenize, useApp, validateParamsOrder };
540
+ interface InitializeData {
541
+ port: MessagePort;
542
+ }
543
+
544
+ interface ResolveContext {
545
+ conditions: string[];
546
+ importAttributes: Record<string, string>;
547
+ parentURL?: string;
548
+ }
549
+
550
+ interface ResolveResult {
551
+ url: string;
552
+ shortCircuit?: boolean;
553
+ format?: string | null | undefined;
554
+ importAttributes?: Record<string, string>;
555
+ }
556
+
557
+ interface LoadContext {
558
+ conditions: string[];
559
+ format: string | null | undefined;
560
+ importAttributes: Record<string, string>;
561
+ }
562
+
563
+ interface LoadResult {
564
+ source: string | ArrayBuffer | Uint8Array;
565
+ format: string;
566
+ shortCircuit?: boolean;
567
+ }
568
+
569
+ type NextResolve = (specifier: string, context: ResolveContext) => Promise<ResolveResult>;
570
+ type NextLoad = (url: string, context: LoadContext) => Promise<LoadResult>;
571
+
572
+ export { $initLoader, $unloadFile, type AnyParam, ArgumentError, BakitClient, type BakitClientEvents, BakitError, BaseClientManager, BaseCommandContext, BaseParam, type BaseParamOptions, BaseParamSchema, type BaseRPCMessage, type BaseRPCResponse, 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, type InitializeData, Instance, LifecycleManager, Listener, ListenerManager, type ListenerOptions, ListenerOptionsSchema, type LoadContext, type LoadResult, type MainHookCallback, MessageContext, type MessageContextSendOptions, type NextLoad, type NextResolve, type NumberOptions, NumberParam, NumberParamSchema, type ParamResolvedOutputType, ParamUserType, Params, ProjectCacheManager, type ProjectConfig, type ProjectConfigInput, ProjectConfigSchema, RPC, type RPCErrorResponse, type RPCMessage, type RPCPendingPromise, type RPCRequest, type RPCResponse, type RPCSuccessResponse, RPC_RESPONSE_MARK, RPC_RESPONSE_TIMEOUT, type ResolveContext, type ResolveResult, type StringOptions, StringParam, StringParamSchema, type UserOptions, UserParam, UserParamSchema, chatInputCommandHandler, defineCommand, defineConfig, defineListener, extractSnowflakeId, getConfig, getDependencyChain, getImporters, getTopLevelDirectory, isImported, isImportedBy, loadConfig, messageCommandHandler, registerCommandsHandler, tokenize, useApp, validateParamsOrder };
package/dist/index.js CHANGED
@@ -1,14 +1,15 @@
1
1
  import { GatewayIntentBits, Events, Client, Collection, IntentsBitField, SlashCommandBuilder, SlashCommandStringOption, SlashCommandNumberOption, SlashCommandUserOption, ChatInputCommandInteraction, Message } from 'discord.js';
2
2
  import { inspect } from 'util';
3
- import { posix, resolve, relative, sep, join, dirname } from 'path';
3
+ import { pathToFileURL, fileURLToPath } from 'url';
4
+ import { resolve, posix, relative, sep, join, dirname } from 'path';
4
5
  import glob from 'tiny-glob';
5
6
  import z4 from 'zod';
6
7
  import { register } from 'module';
7
8
  import { MessageChannel } from 'worker_threads';
8
- import { pathToFileURL } from 'url';
9
+ import { randomUUID, createHash } from 'crypto';
10
+ import EventEmitter from 'events';
9
11
  import { existsSync, mkdirSync, rmSync } from 'fs';
10
12
  import { mkdir, writeFile, readFile, rm } from 'fs/promises';
11
- import { createHash } from 'crypto';
12
13
 
13
14
  // src/core/client/BakitClient.ts
14
15
  var ParamUserType = /* @__PURE__ */ ((ParamUserType2) => (ParamUserType2.Bot = "bot", ParamUserType2.Normal = "normal", ParamUserType2.Any = "any", ParamUserType2))(ParamUserType || {}), BaseParamSchema = z4.object({
@@ -164,34 +165,118 @@ function extractSnowflakeId(input) {
164
165
  function getTopLevelDirectory(path, entryDir) {
165
166
  return relative(entryDir, path).split(sep)[0] ?? null;
166
167
  }
167
- var port1, port2, messageId = 0, pending = /* @__PURE__ */ new Map();
168
+ var RPC_RESPONSE_MARK = "$DONE:", RPC_RESPONSE_TIMEOUT = 5e3, RPC = class extends EventEmitter {
169
+ constructor(transport) {
170
+ super();
171
+ this.transport = transport;
172
+ this.transport.on("message", (message) => this.onMessage(message));
173
+ }
174
+ requests = /* @__PURE__ */ new Map();
175
+ postMessage(message) {
176
+ let { transport } = this;
177
+ "send" in transport ? transport.send(message) : "postMessage" in transport ? transport.postMessage(message) : console.warn(`${transport.constructor.name} doesn't support IPC`);
178
+ }
179
+ onMessage(message) {
180
+ if (message.id.startsWith(RPC_RESPONSE_MARK)) {
181
+ this.handleResponseMessage(message);
182
+ return;
183
+ }
184
+ if ("type" in message) {
185
+ this.handleRequestMessage(message);
186
+ return;
187
+ }
188
+ }
189
+ handleResponseMessage(message) {
190
+ let id = message.id.slice(RPC_RESPONSE_MARK.length), request = this.requests.get(id);
191
+ if (!request)
192
+ return;
193
+ let { reject, resolve: resolve5, timeout } = request;
194
+ this.requests.delete(id), clearTimeout(timeout), "data" in message ? resolve5(message.data) : reject(new Error(message.error));
195
+ }
196
+ handleRequestMessage(message) {
197
+ this.emit("message", message), this.emit(message.type, message.id, message.data);
198
+ }
199
+ send(type, data, id = randomUUID()) {
200
+ let message = {
201
+ id,
202
+ type,
203
+ data
204
+ };
205
+ this.postMessage(message);
206
+ }
207
+ success(id, data) {
208
+ let message = {
209
+ id: `${RPC_RESPONSE_MARK}${id}`,
210
+ data
211
+ };
212
+ this.postMessage(message);
213
+ }
214
+ error(id, error) {
215
+ let message = {
216
+ id: `${RPC_RESPONSE_MARK}${id}`,
217
+ error
218
+ };
219
+ this.postMessage(message);
220
+ }
221
+ request(type, data, id = randomUUID()) {
222
+ return new Promise((resolve5, reject) => {
223
+ let timeout = setTimeout(() => {
224
+ this.requests.delete(id) && reject(new Error("Request timed out"));
225
+ }, RPC_RESPONSE_TIMEOUT);
226
+ this.requests.set(id, {
227
+ resolve: resolve5,
228
+ reject,
229
+ timeout
230
+ }), this.send(type, data, id);
231
+ });
232
+ }
233
+ };
234
+
235
+ // src/lib/loader/loader.ts
236
+ var rpc, reverseDependencyGraph = /* @__PURE__ */ new Map();
168
237
  function $initLoader() {
169
- let channel = new MessageChannel();
170
- port1 = channel.port1, port2 = channel.port2;
171
- let hookPath = new URL("./hooks.js", import.meta.url).href;
238
+ let { port1, port2 } = new MessageChannel(), hookPath = new URL("./hooks.js", import.meta.url).href;
172
239
  register(hookPath, import.meta.url, {
173
240
  data: { port: port1 },
174
241
  transferList: [port1]
175
- }), port2.on("message", onMessage), port2.unref();
242
+ }), rpc = new RPC(port2), rpc.on("dependencyAdd", (_id, data) => onDependencyAdd(data)), port2.unref();
176
243
  }
177
- function onMessage(message) {
178
- let { id, error, data } = message, request = pending.get(id);
179
- request && (pending.delete(id), error ? request.reject(new Error(error)) : request.resolve(data));
244
+ function $unloadFile(path) {
245
+ if (!rpc)
246
+ throw new Error("Loader isn't initialized");
247
+ return rpc.request("unload", resolve(path));
180
248
  }
181
- function $postLoaderMessage(type, data, wait = false) {
182
- let id = messageId++, message = { id, type, data };
183
- if (!wait) {
184
- port2?.postMessage(message);
185
- return;
249
+ function getImporters(path, createNew = false) {
250
+ path = resolve(path);
251
+ let entry = reverseDependencyGraph.get(path);
252
+ return createNew && !entry && (entry = /* @__PURE__ */ new Set(), reverseDependencyGraph.set(path, entry)), entry;
253
+ }
254
+ function getDependencyChain(path) {
255
+ path = resolve(path);
256
+ let queue = [path], visited = /* @__PURE__ */ new Set();
257
+ for (; queue.length > 0; ) {
258
+ let current = queue.shift();
259
+ if (!current || visited.has(current) || (visited.add(current), current.includes("/node_modules/")))
260
+ continue;
261
+ let parents = getImporters(current);
262
+ if (parents)
263
+ for (let parent of parents)
264
+ visited.has(parent) || queue.push(parent);
186
265
  }
187
- return new Promise((resolve4, reject) => {
188
- if (!port2)
189
- return reject(new Error("Loader is not initialized"));
190
- pending.set(id, { resolve: resolve4, reject }), port2.postMessage(message);
266
+ return Array.from(visited);
267
+ }
268
+ function isImported(path) {
269
+ return !!getImporters(path)?.size;
270
+ }
271
+ function isImportedBy(path, matcher) {
272
+ return getDependencyChain(path).slice(1).some((p) => {
273
+ let isMatch = false;
274
+ return typeof matcher == "string" && (isMatch = resolve(matcher) === p), typeof matcher == "function" && (isMatch = matcher(p)), matcher instanceof RegExp && (isMatch = matcher.test(p)), isMatch;
191
275
  });
192
276
  }
193
- function $unloadFile(path) {
194
- return $postLoaderMessage("unload", resolve(path), true);
277
+ function onDependencyAdd(data) {
278
+ let { url, parentURL } = data, path = fileURLToPath(url), parentPath = fileURLToPath(parentURL), entry = reverseDependencyGraph.get(path);
279
+ entry || (entry = /* @__PURE__ */ new Set(), reverseDependencyGraph.set(path, entry)), entry.add(parentPath);
195
280
  }
196
281
 
197
282
  // src/core/structures/param/Param.ts
@@ -475,6 +560,8 @@ var BaseClientManager = class {
475
560
  this.client = client;
476
561
  }
477
562
  };
563
+
564
+ // src/core/managers/CommandManager.ts
478
565
  var CommandManager = class extends BaseClientManager {
479
566
  commands = new Collection();
480
567
  entries = new Collection();
@@ -573,6 +660,8 @@ var Context = class {
573
660
  this.canceled = true;
574
661
  }
575
662
  };
663
+
664
+ // src/core/managers/ListenerManager.ts
576
665
  var ListenerManager = class extends BaseClientManager {
577
666
  listeners = [];
578
667
  entries = new Collection();
@@ -770,7 +859,7 @@ var BaseCommandContext = class extends Context {
770
859
  return await channel.send(options);
771
860
  }
772
861
  };
773
- var ProjectConfigSchema = z4.object({
862
+ var CONFIG_EXTENSIONS = ["ts", "js"], ProjectConfigSchema = z4.object({
774
863
  /**
775
864
  * The gateway intents to use for the Discord client.
776
865
  *
@@ -789,7 +878,13 @@ var ProjectConfigSchema = z4.object({
789
878
  * @see {@link https://discord.js.org/docs/packages/discord.js/main/ClientOptions:Interface}
790
879
  */
791
880
  clientOptions: z4.custom().optional(),
881
+ /**
882
+ * Your bot prefixes to trigger the commands.
883
+ */
792
884
  prefixes: z4.array(z4.string()).default([]),
885
+ /**
886
+ * Your Discord bot token.
887
+ */
793
888
  token: z4.string()
794
889
  });
795
890
  function defineConfig(config) {
@@ -799,7 +894,7 @@ var _config;
799
894
  async function loadConfig(cwd = process.cwd()) {
800
895
  if (_config)
801
896
  return console.warn("loadConfig() was called more than once. This shouldn't happen."), _config;
802
- let globPattern = `bakit.config.{${["ts", "js"].join(",")}}`, [configPath, other] = await glob(globPattern, {
897
+ let globPattern = `bakit.config.{${CONFIG_EXTENSIONS.join(",")}}`, [configPath, other] = await glob(globPattern, {
803
898
  cwd: cwd.replace(/\\/g, "/"),
804
899
  // ensure the path uses `/` instead of `\` on Windows
805
900
  absolute: true
@@ -815,6 +910,39 @@ function getConfig() {
815
910
  throw new Error("Project config is not loaded.");
816
911
  return _config;
817
912
  }
913
+ var ProjectCacheManager = class {
914
+ rootDir;
915
+ constructor(root = process.cwd()) {
916
+ this.rootDir = join(root, ".bakit"), this.ensureRoot();
917
+ }
918
+ ensureRoot() {
919
+ existsSync(this.rootDir) || mkdirSync(this.rootDir, { recursive: true });
920
+ }
921
+ getHash(data) {
922
+ return createHash("sha256").update(JSON.stringify(data)).digest("hex");
923
+ }
924
+ async write(path, data) {
925
+ let fullPath = join(this.rootDir, path), dir = dirname(fullPath);
926
+ await mkdir(dir, { recursive: true });
927
+ let content = typeof data == "string" ? data : JSON.stringify(data);
928
+ await writeFile(fullPath, content, "utf-8");
929
+ }
930
+ async read(path) {
931
+ let fullPath = join(this.rootDir, path);
932
+ try {
933
+ let content = await readFile(fullPath, "utf-8");
934
+ return JSON.parse(content);
935
+ } catch {
936
+ return null;
937
+ }
938
+ }
939
+ async clear() {
940
+ await rm(this.rootDir, { recursive: true, force: true });
941
+ }
942
+ clearSync() {
943
+ existsSync(this.rootDir) && rmSync(this.rootDir, { recursive: true, force: true });
944
+ }
945
+ };
818
946
  var messageCommandHandler = defineListener(Events.MessageCreate), chatInputCommandHandler = defineListener(Events.InteractionCreate), registerCommandsHandler = defineListener({
819
947
  name: Events.ClientReady,
820
948
  once: true
@@ -873,46 +1001,14 @@ chatInputCommandHandler.main(async (_, interaction) => {
873
1001
  }
874
1002
  await command.execute(context, ...resolvedArgs);
875
1003
  });
876
- var ProjectCacheManager = class {
877
- rootDir;
878
- constructor(root = process.cwd()) {
879
- this.rootDir = join(root, ".bakit"), this.ensureRoot();
880
- }
881
- ensureRoot() {
882
- existsSync(this.rootDir) || mkdirSync(this.rootDir, { recursive: true });
883
- }
884
- getHash(data) {
885
- return createHash("sha256").update(JSON.stringify(data)).digest("hex");
886
- }
887
- async write(path, data) {
888
- let fullPath = join(this.rootDir, path), dir = dirname(fullPath);
889
- await mkdir(dir, { recursive: true });
890
- let content = typeof data == "string" ? data : JSON.stringify(data);
891
- await writeFile(fullPath, content, "utf-8");
892
- }
893
- async read(path) {
894
- let fullPath = join(this.rootDir, path);
895
- try {
896
- let content = await readFile(fullPath, "utf-8");
897
- return JSON.parse(content);
898
- } catch {
899
- return null;
900
- }
901
- }
902
- async clear() {
903
- await rm(this.rootDir, { recursive: true, force: true });
904
- }
905
- clearSync() {
906
- existsSync(this.rootDir) && rmSync(this.rootDir, { recursive: true, force: true });
907
- }
908
- };
909
1004
 
910
1005
  // src/core/internal/Instance.ts
911
- var Instance = class {
1006
+ var HOT_DIRECTORIES = ["listeners", "commands"], SOURCE_ROOT = resolve(process.cwd(), "src"), APP_ENTRY_POINTS = /* @__PURE__ */ new Set([resolve(SOURCE_ROOT, "index.ts"), resolve(SOURCE_ROOT, "index.js")]), Instance = class {
912
1007
  client;
913
1008
  cache;
1009
+ rpc;
914
1010
  constructor() {
915
- this.cache = new ProjectCacheManager();
1011
+ this.cache = new ProjectCacheManager(), this.rpc = new RPC(process);
916
1012
  }
917
1013
  async start() {
918
1014
  await loadConfig();
@@ -926,7 +1022,7 @@ var Instance = class {
926
1022
  ), await this.loadModules(), this.initIntents(), await this.client.login(config.token), this.initProcess();
927
1023
  }
928
1024
  initProcess() {
929
- process.on("message", (msg) => this.onProcessMessage(msg));
1025
+ process.env.NODE_ENV === "development" && (this.rpc.on("fileRemove", (_id, path) => this.onFileRemove(path)), this.rpc.on("fileChange", (_id, path) => this.onFileChange(path))), process.on("SIGINT", () => this.shutdown()), process.on("SIGTERM", () => this.shutdown());
930
1026
  }
931
1027
  loadModules() {
932
1028
  let { managers } = this.client, { commands, listeners } = managers;
@@ -936,18 +1032,59 @@ var Instance = class {
936
1032
  let config = getConfig(), { options, managers } = this.client, { listeners } = managers, intents;
937
1033
  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;
938
1034
  }
939
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
940
- async onProcessMessage(message) {
941
- let { type, path } = message;
942
- if (!type.startsWith("hmr:"))
1035
+ isInHotDirectory(path) {
1036
+ if (!path.startsWith(SOURCE_ROOT))
1037
+ return false;
1038
+ let topLevelDir = getTopLevelDirectory(path, SOURCE_ROOT);
1039
+ return !!topLevelDir && HOT_DIRECTORIES.includes(topLevelDir);
1040
+ }
1041
+ restart() {
1042
+ this.rpc.send("restart", {});
1043
+ }
1044
+ async shutdown() {
1045
+ this.client && await this.client.destroy().catch(() => null), process.exit(0);
1046
+ }
1047
+ containsEntryPoint(chain) {
1048
+ return chain.some((x) => APP_ENTRY_POINTS.has(x));
1049
+ }
1050
+ containsHotModule(chain) {
1051
+ return chain.some((x) => this.isInHotDirectory(x));
1052
+ }
1053
+ async onFileRemove(path) {
1054
+ if (!isImported(path))
1055
+ return;
1056
+ let chain = getDependencyChain(path);
1057
+ if (this.containsEntryPoint(chain)) {
1058
+ this.restart();
943
1059
  return;
944
- let target = type.split(":")[1], { listeners, commands } = this.client.managers;
945
- switch (target) {
1060
+ }
1061
+ if (this.containsHotModule(chain))
1062
+ for (let path2 of chain.reverse())
1063
+ await this.unloadModule(path2);
1064
+ }
1065
+ async onFileChange(path) {
1066
+ if (!isImported(path))
1067
+ return;
1068
+ let chain = getDependencyChain(path);
1069
+ if (this.containsEntryPoint(chain)) {
1070
+ this.restart();
1071
+ return;
1072
+ }
1073
+ if (this.containsHotModule(chain))
1074
+ for (let path2 of chain.reverse())
1075
+ await this.unloadModule(path2, true);
1076
+ }
1077
+ async unloadModule(path, reload = false) {
1078
+ let { listeners, commands } = this.client.managers;
1079
+ switch (getTopLevelDirectory(path, SOURCE_ROOT)) {
946
1080
  case "listeners":
947
- await listeners.reload(path);
1081
+ await listeners[reload ? "reload" : "unload"](path);
948
1082
  break;
949
1083
  case "commands":
950
- await commands.reload(path);
1084
+ await commands[reload ? "reload" : "unload"](path);
1085
+ break;
1086
+ default:
1087
+ await $unloadFile(path);
951
1088
  break;
952
1089
  }
953
1090
  }
@@ -966,4 +1103,4 @@ var Params = {
966
1103
  user: createFactory(UserParam)
967
1104
  };
968
1105
 
969
- export { $initLoader, $postLoaderMessage, $unloadFile, ArgumentError, 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, ProjectCacheManager, ProjectConfigSchema, StringParam, StringParamSchema, UserParam, UserParamSchema, chatInputCommandHandler, defineCommand, defineConfig, defineListener, extractSnowflakeId, getConfig, getTopLevelDirectory, loadConfig, messageCommandHandler, registerCommandsHandler, tokenize, useApp, validateParamsOrder };
1106
+ export { $initLoader, $unloadFile, ArgumentError, 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, ProjectCacheManager, ProjectConfigSchema, RPC, RPC_RESPONSE_MARK, RPC_RESPONSE_TIMEOUT, StringParam, StringParamSchema, UserParam, UserParamSchema, chatInputCommandHandler, defineCommand, defineConfig, defineListener, extractSnowflakeId, getConfig, getDependencyChain, getImporters, getTopLevelDirectory, isImported, isImportedBy, loadConfig, messageCommandHandler, registerCommandsHandler, tokenize, useApp, validateParamsOrder };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bakit",
3
- "version": "2.0.0-alpha.24",
3
+ "version": "2.0.0-alpha.26",
4
4
  "description": "A framework for discord.js",
5
5
  "type": "module",
6
6
  "exports": {