@constatic/base 0.0.1-beta.1

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/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # template
2
+
3
+ To install dependencies:
4
+
5
+ ```bash
6
+ bun install
7
+ ```
8
+
9
+ To run:
10
+
11
+ ```bash
12
+ bun run ./src/index.ts
13
+ ```
14
+
15
+ This project was created using `bun init` in bun v1.3.0. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
package/dist/app.d.ts ADDED
@@ -0,0 +1,41 @@
1
+ import type { Client, CommandInteraction } from "discord.js";
2
+ import { CommandManager } from "./creators/commands/manager.js";
3
+ import type { EventPropData } from "./creators/events/event.js";
4
+ import { EventManager } from "./creators/events/manager.js";
5
+ import { ResponderManager, type GenericResponderInteraction } from "./creators/responders/manager.js";
6
+ export interface BaseCommandsConfig {
7
+ guilds?: string[];
8
+ verbose?: boolean;
9
+ middleware?(interaction: CommandInteraction, block: () => void): Promise<void>;
10
+ onNotFound?(interaction: CommandInteraction): void;
11
+ onError?(error: unknown, interaction: CommandInteraction): void;
12
+ }
13
+ export interface BaseRespondersConfig {
14
+ middleware?(interaction: GenericResponderInteraction, block: () => void, params: object): Promise<void>;
15
+ onNotFound?(interaction: GenericResponderInteraction): void;
16
+ onError?(error: unknown, interaction: GenericResponderInteraction, params: object): void;
17
+ }
18
+ export interface BaseEventsConfig {
19
+ middleware?(event: EventPropData, block: (...tags: string[]) => void): Promise<void>;
20
+ onError?(error: unknown, event: EventPropData): void;
21
+ }
22
+ export type BaseErrorHandler = (error: Error | unknown, client: Client) => void;
23
+ export interface BaseConfig {
24
+ commands: BaseCommandsConfig;
25
+ events: BaseEventsConfig;
26
+ responders: BaseRespondersConfig;
27
+ errorHandler: BaseErrorHandler;
28
+ }
29
+ export declare class ConstaticApp {
30
+ readonly commands: CommandManager;
31
+ readonly responders: ResponderManager;
32
+ readonly events: EventManager;
33
+ readonly config: BaseConfig;
34
+ private static "~instance";
35
+ static getInstance(): ConstaticApp;
36
+ private constructor();
37
+ static destroy(): void;
38
+ setErrorHandler(handler: BaseErrorHandler): void;
39
+ intro(): void;
40
+ printLogs(): void;
41
+ }
package/dist/app.js ADDED
@@ -0,0 +1,51 @@
1
+ // src/app.ts
2
+ import { version } from "#package";
3
+ import { version as djsVersion } from "discord.js";
4
+ import { styleText } from "node:util";
5
+ import { CommandManager } from "./creators/commands/manager.js";
6
+ import { EventManager } from "./creators/events/manager.js";
7
+ import { ResponderManager } from "./creators/responders/manager.js";
8
+ import { baseErrorHandler } from "./error.js";
9
+ var isBun = typeof Bun !== "undefined";
10
+
11
+ class ConstaticApp {
12
+ commands = new CommandManager(this);
13
+ responders = new ResponderManager(this);
14
+ events = new EventManager(this);
15
+ config;
16
+ static "~instance" = null;
17
+ static getInstance() {
18
+ return this["~instance"] ??= new ConstaticApp;
19
+ }
20
+ constructor() {
21
+ this.config = {
22
+ commands: {},
23
+ responders: {},
24
+ events: {},
25
+ errorHandler: baseErrorHandler
26
+ };
27
+ }
28
+ static destroy() {
29
+ this["~instance"] = null;
30
+ }
31
+ setErrorHandler(handler) {
32
+ this.config.errorHandler = handler;
33
+ }
34
+ intro() {
35
+ console.log();
36
+ console.log("%s %s", styleText("blue", "★ Constatic Base"), styleText("dim", version));
37
+ console.log("%s %s | %s %s", styleText("blueBright", "◌ discord.js"), styleText("dim", djsVersion), isBun ? "◌ Bun" : styleText("green", "⬢ Node.js"), styleText("dim", isBun ? Bun.version : process.versions.node));
38
+ console.log();
39
+ }
40
+ printLogs() {
41
+ console.log([
42
+ ...this.commands.logs,
43
+ ...this.responders.logs,
44
+ ...this.events.logs
45
+ ].join(`
46
+ `));
47
+ }
48
+ }
49
+ export {
50
+ ConstaticApp
51
+ };
@@ -0,0 +1,18 @@
1
+ import { type Client } from "discord.js";
2
+ import { type BaseErrorHandler } from "./app.js";
3
+ import { type CustomClientOptions } from "./client.js";
4
+ export interface BootstrapOptions extends CustomClientOptions {
5
+ token?: string;
6
+ meta: ImportMeta;
7
+ errorHandler?: BaseErrorHandler;
8
+ beforeLoad?(client: Client<boolean>): Promise<void>;
9
+ modules?: string[];
10
+ loadLogs?: boolean;
11
+ }
12
+ export declare function bootstrap(options: BootstrapOptions): Promise<{
13
+ client: Client<boolean>;
14
+ imports: {
15
+ module: any;
16
+ filepath: string;
17
+ }[];
18
+ }>;
@@ -0,0 +1,35 @@
1
+ // src/bootstrap.ts
2
+ import { ConstaticApp } from "./app.js";
3
+ import { createClient } from "./client.js";
4
+ import { ConstaticError } from "./error.js";
5
+ import { loadModules } from "./modules.js";
6
+ async function bootstrap(options) {
7
+ const token = options.token ?? process.env.BOT_TOKEN;
8
+ if (!token)
9
+ throw new ConstaticError("The application token was not provided!");
10
+ const app = ConstaticApp.getInstance();
11
+ if (options.errorHandler) {
12
+ app.setErrorHandler(options.errorHandler);
13
+ }
14
+ const client = createClient(token, options);
15
+ process.on("uncaughtException", (error) => app.config.errorHandler(error, client));
16
+ process.on("unhandledRejection", (error) => app.config.errorHandler(error, client));
17
+ if (options.beforeLoad) {
18
+ await options.beforeLoad(client);
19
+ }
20
+ const imports = await loadModules(options.meta, [
21
+ "./discord/**/*.{js,ts,jsx,tsx}",
22
+ "!./discord/index.{js,ts,jsx,tsx}",
23
+ ...options.modules ?? []
24
+ ]);
25
+ if (options.loadLogs ?? true) {
26
+ app.printLogs();
27
+ }
28
+ app.intro();
29
+ app.events.register(client);
30
+ client.login();
31
+ return { client, imports };
32
+ }
33
+ export {
34
+ bootstrap
35
+ };
@@ -0,0 +1,3 @@
1
+ import { Client, type ClientOptions } from "discord.js";
2
+ export type CustomClientOptions = Partial<ClientOptions>;
3
+ export declare function createClient(token: string, options: CustomClientOptions): Client<boolean>;
package/dist/client.js ADDED
@@ -0,0 +1,34 @@
1
+ // src/client.ts
2
+ import { Client } from "discord.js";
3
+ import { styleText } from "node:util";
4
+ import { ConstaticApp } from "./app.js";
5
+ function createClient(token, options) {
6
+ const app = ConstaticApp.getInstance();
7
+ const client = new Client({
8
+ ...options,
9
+ intents: options.intents ?? [],
10
+ partials: options.partials ?? [],
11
+ failIfNotExists: options.failIfNotExists ?? false
12
+ });
13
+ client.token = token;
14
+ client.once("clientReady", async (client2) => {
15
+ console.log("%s %s %s", styleText("green", "●"), styleText(["greenBright", "underline"], client2.user.username), styleText("green", "application is ready!"));
16
+ await app.commands.register(client2);
17
+ await app.events.runReady(client2);
18
+ });
19
+ client.on("interactionCreate", async (interaction) => {
20
+ if (interaction.isAutocomplete()) {
21
+ await app.commands.onAutocomplete(interaction);
22
+ return;
23
+ }
24
+ if (interaction.isCommand()) {
25
+ await app.commands.onCommand(interaction);
26
+ return;
27
+ }
28
+ await app.responders.onResponder(interaction);
29
+ });
30
+ return client;
31
+ }
32
+ export {
33
+ createClient
34
+ };
@@ -0,0 +1,92 @@
1
+ import { ApplicationCommandOptionType, ApplicationCommandType, AutocompleteInteraction, ChatInputCommandInteraction, MessageContextMenuCommandInteraction, UserContextMenuCommandInteraction, type ApplicationCommandOptionAllowedChannelTypes, type ApplicationCommandOptionChoiceData, type BaseApplicationCommandData, type CacheType, type LocalizationMap } from "discord.js";
2
+ export type CommandType = Exclude<ApplicationCommandType, ApplicationCommandType.PrimaryEntryPoint>;
3
+ type AutocompleData<Type> = Promise<readonly ApplicationCommandOptionChoiceData<Type extends (number | string) ? Type : (number | string)>[] | undefined | void>;
4
+ export type AutocompleteRun<Type, Perm> = (this: void, interaction: AutocompleteInteraction<CacheMode<Perm>>) => AutocompleData<Type>;
5
+ interface AutocompleteOptionData<Type, Perm> {
6
+ autocomplete?: true | AutocompleteRun<Type, Perm>;
7
+ }
8
+ interface BaseOptionData {
9
+ name: string;
10
+ nameLocalizations?: LocalizationMap;
11
+ description?: string;
12
+ descriptionLocalizations?: LocalizationMap;
13
+ required?: boolean;
14
+ }
15
+ interface StringOptionData<Perm> extends BaseOptionData, AutocompleteOptionData<string, Perm> {
16
+ type: ApplicationCommandOptionType.String;
17
+ choices?: readonly ApplicationCommandOptionChoiceData<string>[];
18
+ minLength?: number;
19
+ maxLength?: number;
20
+ }
21
+ interface NumberOptionData<P> extends BaseOptionData, AutocompleteOptionData<number, P> {
22
+ type: ApplicationCommandOptionType.Number | ApplicationCommandOptionType.Integer;
23
+ choices?: readonly ApplicationCommandOptionChoiceData<number>[];
24
+ minValue?: number;
25
+ maxValue?: number;
26
+ }
27
+ interface ChannelOptionData extends BaseOptionData {
28
+ type: ApplicationCommandOptionType.Channel;
29
+ channelTypes?: readonly ApplicationCommandOptionAllowedChannelTypes[];
30
+ }
31
+ interface CommonOptionData extends BaseOptionData {
32
+ type: ApplicationCommandOptionType.Attachment | ApplicationCommandOptionType.Boolean | ApplicationCommandOptionType.Mentionable | ApplicationCommandOptionType.Role | ApplicationCommandOptionType.User;
33
+ }
34
+ export type SlashCommandPrimitiveOptionData<Perm> = StringOptionData<Perm> | NumberOptionData<Perm> | CommonOptionData | ChannelOptionData;
35
+ export interface SubCommandOptionData<Perm> extends Omit<BaseOptionData, "required"> {
36
+ type: ApplicationCommandOptionType.Subcommand;
37
+ options?: SlashCommandPrimitiveOptionData<Perm>[];
38
+ }
39
+ export interface GroupOptionData<Perm> extends Omit<BaseOptionData, "required"> {
40
+ type: ApplicationCommandOptionType.SubcommandGroup;
41
+ options: SubCommandOptionData<Perm>[];
42
+ }
43
+ type CacheMode<Perm> = Perm extends false ? "cached" : CacheType;
44
+ interface CommandRunThis {
45
+ /**
46
+ * Blocks the flow of executions
47
+ */
48
+ block(): never;
49
+ }
50
+ type ResolveCommandModuleData<Return> = Return extends void ? undefined : Return;
51
+ export type SubCommandModuleData<Perm, Return> = Omit<BaseOptionData, "required"> & {
52
+ group?: string;
53
+ run(this: CommandRunThis, interaction: ChatInputCommandInteraction<CacheMode<Perm>>, data: ResolveCommandModuleData<Return>): Promise<void>;
54
+ options?: SlashCommandPrimitiveOptionData<Perm>[];
55
+ };
56
+ export type SubCommandGroupModuleData<Perm, Return, T> = Omit<BaseOptionData, "required"> & {
57
+ options?: Omit<SubCommandOptionData<Perm>, "type">[];
58
+ run?(this: CommandRunThis, interaction: ChatInputCommandInteraction<CacheMode<Perm>>, data: ResolveCommandModuleData<Return>): Promise<T>;
59
+ };
60
+ type RunInteraction<T, P> = T extends ApplicationCommandType.Message ? MessageContextMenuCommandInteraction<CacheMode<P>> : T extends ApplicationCommandType.User ? UserContextMenuCommandInteraction<CacheMode<P>> : ChatInputCommandInteraction<CacheMode<P>>;
61
+ type BaseAppCommandData = Omit<BaseApplicationCommandData, "dmPermission"> & Pick<BaseOptionData, "descriptionLocalizations">;
62
+ export interface CommandData<T, P, R> extends BaseAppCommandData {
63
+ name: string;
64
+ description?: string;
65
+ dmPermission?: P;
66
+ type?: T;
67
+ global?: boolean;
68
+ run?(this: CommandRunThis, interaction: RunInteraction<T, P>): Promise<R>;
69
+ autocomplete?: AutocompleteRun<string | number, P>;
70
+ options?: SlashCommandPrimitiveOptionData<P>[] | (GroupOptionData<P> | SubCommandOptionData<P>)[];
71
+ }
72
+ export type SlashCommandOptionData<Perm> = SlashCommandPrimitiveOptionData<Perm> | GroupOptionData<Perm> | SubCommandOptionData<Perm>;
73
+ export type CommandModule = (SubCommandGroupModuleData<unknown, unknown, unknown> & {
74
+ type: ApplicationCommandOptionType.SubcommandGroup;
75
+ }) | (SubCommandModuleData<unknown, unknown> & {
76
+ type: ApplicationCommandOptionType.Subcommand;
77
+ group?: string;
78
+ });
79
+ declare class GroupCommandModule<Type, Perm, Return, ModuleReturn> {
80
+ readonly command: Command<Type, Perm, Return>;
81
+ readonly data: SubCommandGroupModuleData<Perm, Return, ModuleReturn>;
82
+ constructor(command: Command<Type, Perm, Return>, data: SubCommandGroupModuleData<Perm, Return, ModuleReturn>);
83
+ subcommand(data: SubCommandModuleData<Perm, ModuleReturn>): this;
84
+ }
85
+ export declare class Command<Type, Perm, Return> {
86
+ readonly data: CommandData<Type, Perm, Return>;
87
+ readonly modules: CommandModule[];
88
+ constructor(data: CommandData<Type, Perm, Return>);
89
+ group<ModuleReturn>(data: SubCommandGroupModuleData<Perm, Return, ModuleReturn>): GroupCommandModule<Type, Perm, Return, ModuleReturn>;
90
+ subcommand<Return>(data: SubCommandModuleData<Perm, Return>): this;
91
+ }
92
+ export {};
@@ -0,0 +1,50 @@
1
+ // src/creators/commands/command.ts
2
+ import { ApplicationCommandOptionType, ApplicationCommandType } from "discord.js";
3
+
4
+ class GroupCommandModule {
5
+ command;
6
+ data;
7
+ constructor(command, data) {
8
+ this.command = command;
9
+ this.data = data;
10
+ }
11
+ subcommand(data) {
12
+ data.group ??= this.data.name;
13
+ this.command.subcommand(data);
14
+ return this;
15
+ }
16
+ }
17
+
18
+ class Command {
19
+ data;
20
+ modules = [];
21
+ constructor(data) {
22
+ this.data = data;
23
+ this.data.type ??= ApplicationCommandType.ChatInput;
24
+ this.data.dmPermission ??= false;
25
+ if (this.data.type === ApplicationCommandType.ChatInput) {
26
+ this.data.description ??= this.data.name;
27
+ this.data.name = this.data.name.toLowerCase().replaceAll(" ", "");
28
+ }
29
+ if (this.data.name.length > 32) {
30
+ this.data.name = this.data.name.slice(0, 32);
31
+ }
32
+ }
33
+ group(data) {
34
+ this.modules.push({
35
+ ...data,
36
+ type: ApplicationCommandOptionType.SubcommandGroup
37
+ });
38
+ return new GroupCommandModule(this, data);
39
+ }
40
+ subcommand(data) {
41
+ this.modules.push({
42
+ ...data,
43
+ type: ApplicationCommandOptionType.Subcommand
44
+ });
45
+ return this;
46
+ }
47
+ }
48
+ export {
49
+ Command
50
+ };
@@ -0,0 +1,22 @@
1
+ import { ApplicationCommandType, AutocompleteInteraction, Client, CommandInteraction, type ChatInputApplicationCommandData, type MessageApplicationCommandData, type UserApplicationCommandData } from "discord.js";
2
+ import { BaseManager } from "../manager.js";
3
+ import type { Command, CommandType } from "./command.js";
4
+ type Runner = Function | null | undefined;
5
+ type BuildedCommandData = (UserApplicationCommandData | MessageApplicationCommandData | ChatInputApplicationCommandData) & {
6
+ global?: boolean;
7
+ };
8
+ export declare class CommandManager extends BaseManager {
9
+ private get config();
10
+ private readonly collection;
11
+ readonly runners: Map<string, Runner[]>;
12
+ set<T, P, R>(command: Command<T, P, R>): void;
13
+ getTitle(type?: CommandType): string[];
14
+ build(): BuildedCommandData[];
15
+ private buildOptions;
16
+ private buildModules;
17
+ getHandler(type: ApplicationCommandType, ...path: (string | null)[]): Runner[] | undefined;
18
+ onAutocomplete(interaction: AutocompleteInteraction): Promise<void>;
19
+ onCommand(interaction: CommandInteraction): Promise<void>;
20
+ register(client: Client<true>): Promise<void>;
21
+ }
22
+ export {};
@@ -0,0 +1,221 @@
1
+ // src/creators/commands/manager.ts
2
+ import { ApplicationCommandOptionType, ApplicationCommandType } from "discord.js";
3
+ import { format, styleText } from "node:util";
4
+ import { RunBlockError } from "../../error.js";
5
+ import { BaseManager } from "../manager.js";
6
+
7
+ class CommandManager extends BaseManager {
8
+ get config() {
9
+ return this.app.config.commands;
10
+ }
11
+ collection = new Map;
12
+ runners = new Map;
13
+ set(command) {
14
+ this.collection.set(command.data.name, command);
15
+ const path = `/${command.data.type}/${command.data.name}`;
16
+ this.runners.set(path, [command.data.run]);
17
+ if (command.data.autocomplete) {
18
+ this.runners.set(`${path}/autocomplete`, [command.data.autocomplete]);
19
+ }
20
+ const [icon, label] = this.getTitle(command.data.type);
21
+ this.logs.push([
22
+ styleText("green", `${icon} ${label}`),
23
+ styleText("gray", `>`),
24
+ styleText(["blue", "underline"], command.data.name),
25
+ styleText("green", "✓")
26
+ ].join(" "));
27
+ }
28
+ getTitle(type) {
29
+ return {
30
+ [ApplicationCommandType.ChatInput]: ["{/}", "Slash command"],
31
+ [ApplicationCommandType.User]: ["{☰}", "User context menu"],
32
+ [ApplicationCommandType.Message]: ["{☰}", "Message context menu"]
33
+ }[type ?? ApplicationCommandType.ChatInput];
34
+ }
35
+ build() {
36
+ const commands = Array.from(this.collection.values());
37
+ return commands.map((command) => {
38
+ const {
39
+ options: rawOptions,
40
+ description,
41
+ descriptionLocalizations,
42
+ ...data
43
+ } = command.data;
44
+ const path = `/${data.type}/${data.name}`;
45
+ const modulesOptions = this.buildModules(command.modules, path, data.run);
46
+ const options = this.buildOptions([
47
+ ...rawOptions ?? [],
48
+ ...modulesOptions
49
+ ], path);
50
+ const slashData = data.type === ApplicationCommandType.ChatInput ? {
51
+ description,
52
+ descriptionLocalizations,
53
+ ...options.length >= 1 ? { options } : {}
54
+ } : {};
55
+ return { ...data, ...slashData };
56
+ });
57
+ }
58
+ buildOptions(options, path) {
59
+ const resolved = [];
60
+ for (const option of options) {
61
+ const description = option.description ?? option.name;
62
+ if ("autocomplete" in option && option.autocomplete && typeof option.autocomplete === "function") {
63
+ this.runners.set(`${path}/autocomplete/${option.name}`, [option.autocomplete]);
64
+ }
65
+ switch (option.type) {
66
+ case ApplicationCommandOptionType.SubcommandGroup: {
67
+ const { options: subcommands, ...data } = option;
68
+ resolved.push({
69
+ ...data,
70
+ description,
71
+ options: this.buildOptions(subcommands, `${path}/${data.name}`)
72
+ });
73
+ continue;
74
+ }
75
+ case ApplicationCommandOptionType.Subcommand: {
76
+ const { options: options2, ...data } = option;
77
+ resolved.push({
78
+ ...data,
79
+ description,
80
+ ...options2?.length ? {
81
+ options: this.buildOptions(options2, `${path}/${data.name}`)
82
+ } : {}
83
+ });
84
+ continue;
85
+ }
86
+ case ApplicationCommandOptionType.String:
87
+ case ApplicationCommandOptionType.Integer:
88
+ case ApplicationCommandOptionType.Number: {
89
+ const { choices, autocomplete, ...data } = option;
90
+ const validation = data.type === ApplicationCommandOptionType.String ? { minLength: data.minLength, maxLength: data.maxLength } : { minValue: data.minValue, maxValue: data.maxValue };
91
+ const extra = autocomplete ? { autocomplete: true, ...validation } : choices?.length ? { choices: choices.slice(0, 25) } : validation;
92
+ resolved.push(Object.assign({
93
+ ...data,
94
+ description,
95
+ ...extra
96
+ }));
97
+ continue;
98
+ }
99
+ default: {
100
+ resolved.push({ ...option, description });
101
+ }
102
+ }
103
+ }
104
+ return resolved;
105
+ }
106
+ buildModules(modules, path, run) {
107
+ const resolved = [];
108
+ if (!modules.length)
109
+ return resolved;
110
+ const groups = modules.filter((module) => module.type === ApplicationCommandOptionType.SubcommandGroup);
111
+ const subcommands = modules.filter((module) => module.type === ApplicationCommandOptionType.Subcommand);
112
+ if (groups.length >= 1) {
113
+ for (const group of groups) {
114
+ const data = [
115
+ ...(group.options ?? []).map((data2) => ({
116
+ ...data2,
117
+ type: ApplicationCommandOptionType.Subcommand
118
+ })),
119
+ ...subcommands.filter((sub) => sub.group === group.name)
120
+ ];
121
+ resolved.push({ ...group, options: data });
122
+ for (const subcommand of data) {
123
+ this.runners.set(`${path}/${group.name}/${subcommand.name}`, [run, group.run, subcommand.run]);
124
+ }
125
+ }
126
+ }
127
+ for (const subcommand of subcommands.filter((sub) => !sub.group)) {
128
+ this.runners.set(`${path}/${subcommand.name}`, [run, subcommand.run]);
129
+ resolved.push(subcommand);
130
+ }
131
+ return resolved;
132
+ }
133
+ getHandler(type, ...path) {
134
+ const commandName = path[0];
135
+ const resolved = `/${type}/${path.filter((p) => p !== null).join("/")}`;
136
+ return this.runners.get(resolved) ?? this.runners.get(`/${type}/${commandName}`);
137
+ }
138
+ async onAutocomplete(interaction) {
139
+ const options = interaction.options;
140
+ const handler = this.getHandler(interaction.commandType, interaction.commandName, "autocomplete", options.getSubcommandGroup(false), options.getSubcommand(false), options.getFocused(true).name);
141
+ if (!handler || !handler[0])
142
+ return;
143
+ const [run] = handler;
144
+ const choices = await run(interaction);
145
+ if (choices && Array.isArray(choices)) {
146
+ await interaction.respond(choices.slice(0, 25));
147
+ }
148
+ }
149
+ async onCommand(interaction) {
150
+ if (interaction.isPrimaryEntryPointCommand())
151
+ return;
152
+ const { middleware, onError, onNotFound } = this.config;
153
+ let isBlock = false;
154
+ const block = () => isBlock = true;
155
+ if (middleware)
156
+ await middleware(interaction, block);
157
+ if (isBlock)
158
+ return;
159
+ const path = [interaction.commandName];
160
+ if (interaction.isChatInputCommand()) {
161
+ path.push(interaction.options.getSubcommandGroup(false), interaction.options.getSubcommand(false));
162
+ }
163
+ const handler = this.getHandler(interaction.commandType, ...path);
164
+ if (!handler) {
165
+ onNotFound?.(interaction);
166
+ return;
167
+ }
168
+ try {
169
+ let result;
170
+ for (const run of handler.filter((h) => h !== null && h !== undefined)) {
171
+ result = await run.call({
172
+ block() {
173
+ throw new RunBlockError;
174
+ }
175
+ }, interaction, result);
176
+ }
177
+ } catch (err) {
178
+ if (err instanceof RunBlockError)
179
+ return;
180
+ if (onError) {
181
+ onError(err, interaction);
182
+ return;
183
+ }
184
+ throw err;
185
+ }
186
+ }
187
+ async register(client) {
188
+ const guildList = new Set(this.config.guilds);
189
+ const commands = this.build();
190
+ const promises = [];
191
+ const manager = client.application.commands;
192
+ const logs = [];
193
+ const setGlobal = (commands2) => {
194
+ promises.push(manager.set(commands2).then((data) => logs.push([
195
+ data.size,
196
+ `${client.user.username} application`
197
+ ])));
198
+ };
199
+ if (guildList.size >= 1) {
200
+ const guildCommands = commands.filter((c) => !c.global);
201
+ for (const id of guildList.values()) {
202
+ promises.push(manager.set(guildCommands, id).then((commands2) => logs.push([
203
+ commands2.size,
204
+ `${id} guild`
205
+ ])));
206
+ }
207
+ const globalCommands = commands.filter((c) => c.global);
208
+ if (globalCommands.length >= 1) {
209
+ setGlobal(globalCommands);
210
+ }
211
+ } else {
212
+ setGlobal(commands);
213
+ }
214
+ await Promise.all(promises);
215
+ console.log(logs.map(([size, location]) => format(styleText("green", "└ %s command%s registered in %s"), size, size === 1 ? "" : "s", styleText("greenBright", location))).join(`
216
+ `));
217
+ }
218
+ }
219
+ export {
220
+ CommandManager
221
+ };
@@ -0,0 +1,21 @@
1
+ import type { ClientEvents } from "discord.js";
2
+ export type ClientEventKey = keyof ClientEvents;
3
+ export type EventPropData = {
4
+ [Key in ClientEventKey]: {
5
+ name: Key;
6
+ args: ClientEvents[Key];
7
+ };
8
+ }[ClientEventKey];
9
+ export interface EventData<EventName extends ClientEventKey> {
10
+ name: string;
11
+ event: EventName;
12
+ once?: boolean;
13
+ tags?: string[];
14
+ run(this: void, ...args: ClientEvents[EventName]): Promise<void>;
15
+ }
16
+ export declare class Event<EventName extends ClientEventKey> {
17
+ readonly data: EventData<EventName>;
18
+ constructor(data: EventData<EventName>);
19
+ }
20
+ export type GenericEventArgs = ClientEvents[ClientEventKey];
21
+ export type EventsCollection = Map<string, Event<ClientEventKey>>;
@@ -0,0 +1,10 @@
1
+ // src/creators/events/event.ts
2
+ class Event {
3
+ data;
4
+ constructor(data) {
5
+ this.data = data;
6
+ }
7
+ }
8
+ export {
9
+ Event
10
+ };
@@ -0,0 +1,12 @@
1
+ import { Client, Collection } from "discord.js";
2
+ import { BaseManager } from "../manager.js";
3
+ import type { ClientEventKey, Event, EventsCollection, GenericEventArgs } from "./event.js";
4
+ export declare class EventManager extends BaseManager {
5
+ private get config();
6
+ readonly collection: Collection<keyof import("discord.js").ClientEvents, EventsCollection>;
7
+ getEvents(key: ClientEventKey): EventsCollection;
8
+ add(event: Event<ClientEventKey>): void;
9
+ onEvent(event: Event<ClientEventKey>, args: GenericEventArgs): Promise<void>;
10
+ register(client: Client): void;
11
+ runReady(client: Client<true>): Promise<void>;
12
+ }
@@ -0,0 +1,62 @@
1
+ // src/creators/events/manager.ts
2
+ import { Collection } from "discord.js";
3
+ import { BaseManager } from "../manager.js";
4
+
5
+ class EventManager extends BaseManager {
6
+ get config() {
7
+ return this.app.config.events;
8
+ }
9
+ collection = new Collection;
10
+ getEvents(key) {
11
+ if (!this.collection.has(key)) {
12
+ this.collection.set(key, new Map);
13
+ }
14
+ return this.collection.get(key);
15
+ }
16
+ add(event) {
17
+ const events = this.getEvents(event.data.event);
18
+ events.set(event.data.name, event);
19
+ }
20
+ async onEvent(event, args) {
21
+ const { middleware, onError } = this.config;
22
+ let isBlock = false;
23
+ const block = (...selected) => {
24
+ const tags = event.data.tags ?? [];
25
+ if (selected.length === 0) {
26
+ isBlock = tags.length === 0;
27
+ return;
28
+ }
29
+ isBlock = selected.some((tag) => tags.includes(tag));
30
+ };
31
+ const eventData = { name: event.data.name, args };
32
+ if (middleware)
33
+ await middleware(eventData, block);
34
+ if (isBlock)
35
+ return;
36
+ await event.data.run(...args).catch((err) => {
37
+ if (onError) {
38
+ onError(err, eventData);
39
+ return;
40
+ }
41
+ throw err;
42
+ });
43
+ if (event.data.once) {
44
+ this.getEvents(event.data.event)?.delete(event.data.name);
45
+ }
46
+ }
47
+ register(client) {
48
+ const collection = this.collection.filter((_, key) => key !== "clientReady");
49
+ for (const [key, events] of collection.entries()) {
50
+ client.on(key, (...args) => {
51
+ Promise.all(Array.from(events.values()).map((event) => this.onEvent(event, args)));
52
+ });
53
+ }
54
+ }
55
+ async runReady(client) {
56
+ const collection = this.getEvents("clientReady");
57
+ await Promise.all(Array.from(collection.values()).map((event) => this.onEvent(event, [client])));
58
+ }
59
+ }
60
+ export {
61
+ EventManager
62
+ };
@@ -0,0 +1,4 @@
1
+ export { ResponderType, type Responder, type ResponderData, type ResponderInteraction } from "./responders/responder.js";
2
+ export { type Command, type CommandData } from "./commands/command.js";
3
+ export { type Event, type EventData, type EventPropData } from "./events/event.js";
4
+ export * from "./setup.js";
@@ -0,0 +1,9 @@
1
+ // src/creators/index.ts
2
+ import {
3
+ ResponderType
4
+ } from "./responders/responder.js";
5
+
6
+ export * from "./setup.js";
7
+ export {
8
+ ResponderType
9
+ };
@@ -0,0 +1,6 @@
1
+ import type { ConstaticApp } from "../app.js";
2
+ export declare abstract class BaseManager {
3
+ readonly logs: string[];
4
+ readonly app: ConstaticApp;
5
+ constructor(app: ConstaticApp);
6
+ }
@@ -0,0 +1,12 @@
1
+ // src/creators/manager.ts
2
+ class BaseManager {
3
+ logs;
4
+ app;
5
+ constructor(app) {
6
+ this.app = app;
7
+ this.logs = [];
8
+ }
9
+ }
10
+ export {
11
+ BaseManager
12
+ };
@@ -0,0 +1,13 @@
1
+ import type { CacheType, MessageComponentInteraction, ModalSubmitInteraction } from "discord.js";
2
+ import { BaseManager } from "../manager.js";
3
+ import { ResponderType, type Responder } from "./responder.js";
4
+ export type GenericResponder = Responder<string, readonly ResponderType[], any, CacheType>;
5
+ export type GenericResponderInteraction = MessageComponentInteraction | ModalSubmitInteraction;
6
+ export declare class ResponderManager extends BaseManager {
7
+ private get config();
8
+ private readonly router;
9
+ set(responder: GenericResponder): void;
10
+ getHandler(type: ResponderType, customId: string): import("rou3").MatchedRoute<GenericResponder> | undefined;
11
+ private getType;
12
+ onResponder(interaction: GenericResponderInteraction): Promise<void>;
13
+ }
@@ -0,0 +1,61 @@
1
+ // src/creators/responders/manager.ts
2
+ import { styleText } from "node:util";
3
+ import { Router } from "../../utils/router.js";
4
+ import { BaseManager } from "../manager.js";
5
+ import { ResponderType } from "./responder.js";
6
+
7
+ class ResponderManager extends BaseManager {
8
+ get config() {
9
+ return this.app.config.responders;
10
+ }
11
+ router = new Router;
12
+ set(responder) {
13
+ const path = responder.data.customId;
14
+ for (const type of new Set(responder.data.types)) {
15
+ this.router.add(type, path, responder);
16
+ this.logs.push([
17
+ styleText("greenBright", `▸ ${type}`),
18
+ styleText("gray", `>`),
19
+ styleText(["blue", "underline"], path),
20
+ styleText("green", "✓")
21
+ ].join(" "));
22
+ }
23
+ }
24
+ getHandler(type, customId) {
25
+ return this.router.find(type, customId);
26
+ }
27
+ getType(interaction) {
28
+ return interaction.isButton() ? ResponderType.Button : interaction.isStringSelectMenu() ? ResponderType.StringSelect : interaction.isChannelSelectMenu() ? ResponderType.ChannelSelect : interaction.isRoleSelectMenu() ? ResponderType.RoleSelect : interaction.isUserSelectMenu() ? ResponderType.UserSelect : interaction.isMentionableSelectMenu() ? ResponderType.MentionableSelect : interaction.isModalSubmit() && interaction.isFromMessage() ? ResponderType.ModalComponent : interaction.isModalSubmit() ? ResponderType.Modal : undefined;
29
+ }
30
+ async onResponder(interaction) {
31
+ const { middleware, onError, onNotFound } = this.config;
32
+ const responderType = this.getType(interaction);
33
+ if (!responderType) {
34
+ onNotFound?.(interaction);
35
+ return;
36
+ }
37
+ const handler = this.getHandler(responderType, interaction.customId);
38
+ if (!handler) {
39
+ onNotFound?.(interaction);
40
+ return;
41
+ }
42
+ const data = handler.data.data;
43
+ const params = handler.params && data.parse ? data.parse(handler.params) : handler.params ?? {};
44
+ let isBlock = false;
45
+ const block = () => isBlock = true;
46
+ if (middleware)
47
+ await middleware(interaction, block, params);
48
+ if (isBlock)
49
+ return;
50
+ await data.run(interaction, params).catch((err) => {
51
+ if (onError) {
52
+ onError(err, interaction, params);
53
+ return;
54
+ }
55
+ throw err;
56
+ });
57
+ }
58
+ }
59
+ export {
60
+ ResponderManager
61
+ };
@@ -0,0 +1,41 @@
1
+ import type { ButtonInteraction, CacheType, ChannelSelectMenuInteraction, MentionableSelectMenuInteraction, ModalMessageModalSubmitInteraction, ModalSubmitInteraction, RoleSelectMenuInteraction, StringSelectMenuInteraction, UserSelectMenuInteraction } from "discord.js";
2
+ import { type InferRouteParams } from "rou3";
3
+ import type { Prettify } from "../../utils/types.js";
4
+ type NotEmptyArray<T> = T extends never[] ? never : T;
5
+ type UniqueArray<T> = T extends readonly [infer X, ...infer Rest] ? InArray<Rest, X> extends true ? ["Encountered value with duplicates:", X] : readonly [X, ...UniqueArray<Rest>] : T;
6
+ type InArray<T, X> = T extends readonly [X, ...infer _Rest] ? true : T extends readonly [X] ? true : T extends readonly [infer _, ...infer Rest] ? InArray<Rest, X> : false;
7
+ export declare enum ResponderType {
8
+ Button = "button",
9
+ StringSelect = "select.string",
10
+ UserSelect = "select.user",
11
+ RoleSelect = "select.role",
12
+ ChannelSelect = "select.channel",
13
+ MentionableSelect = "select.mentionable",
14
+ Modal = "modal",
15
+ ModalComponent = "modal.component"
16
+ }
17
+ export type ResponderInteraction<Type extends ResponderType, Cache extends CacheType> = {
18
+ [ResponderType.Button]: ButtonInteraction<Cache>;
19
+ [ResponderType.StringSelect]: StringSelectMenuInteraction<Cache>;
20
+ [ResponderType.UserSelect]: UserSelectMenuInteraction<Cache>;
21
+ [ResponderType.RoleSelect]: RoleSelectMenuInteraction<Cache>;
22
+ [ResponderType.ChannelSelect]: ChannelSelectMenuInteraction<Cache>;
23
+ [ResponderType.MentionableSelect]: MentionableSelectMenuInteraction<Cache>;
24
+ [ResponderType.Modal]: ModalSubmitInteraction<Cache>;
25
+ [ResponderType.ModalComponent]: ModalMessageModalSubmitInteraction<Cache>;
26
+ }[Type];
27
+ type ResolveParams<Path extends string, Parsed> = Prettify<Parsed extends {
28
+ [x: string | number | symbol]: any;
29
+ } ? Parsed : InferRouteParams<Path>>;
30
+ export interface ResponderData<Path extends string, Types extends readonly ResponderType[], out Parsed, Cache extends CacheType> {
31
+ customId: Path;
32
+ types: NotEmptyArray<UniqueArray<Types>>;
33
+ cache?: Cache;
34
+ parse?(this: void, params: InferRouteParams<Path>): Parsed;
35
+ run(this: void, interaction: ResponderInteraction<Types[number], Cache>, params: ResolveParams<Path, Parsed>): Promise<void>;
36
+ }
37
+ export declare class Responder<Path extends string, Types extends readonly ResponderType[], out Parsed, Cache extends CacheType> {
38
+ readonly data: ResponderData<Path, Types, Parsed, Cache>;
39
+ constructor(data: ResponderData<Path, Types, Parsed, Cache>);
40
+ }
41
+ export {};
@@ -0,0 +1,23 @@
1
+ // src/creators/responders/responder.ts
2
+ var ResponderType;
3
+ ((ResponderType2) => {
4
+ ResponderType2["Button"] = "button";
5
+ ResponderType2["StringSelect"] = "select.string";
6
+ ResponderType2["UserSelect"] = "select.user";
7
+ ResponderType2["RoleSelect"] = "select.role";
8
+ ResponderType2["ChannelSelect"] = "select.channel";
9
+ ResponderType2["MentionableSelect"] = "select.mentionable";
10
+ ResponderType2["Modal"] = "modal";
11
+ ResponderType2["ModalComponent"] = "modal.component";
12
+ })(ResponderType ||= {});
13
+
14
+ class Responder {
15
+ data;
16
+ constructor(data) {
17
+ this.data = data;
18
+ }
19
+ }
20
+ export {
21
+ ResponderType,
22
+ Responder
23
+ };
@@ -0,0 +1,18 @@
1
+ import type { ApplicationCommandType, CacheType, ClientEvents, PermissionResolvable } from "discord.js";
2
+ import { type BaseCommandsConfig, type BaseEventsConfig, type BaseRespondersConfig } from "../app.js";
3
+ import { Command, type CommandData, type CommandType } from "./commands/command.js";
4
+ import { Event, type EventData } from "./events/event.js";
5
+ import { Responder, type ResponderData, type ResponderType } from "./responders/responder.js";
6
+ interface SetupCreatorsOptions {
7
+ commands?: BaseCommandsConfig & {
8
+ defaultMemberPermissions?: PermissionResolvable[];
9
+ };
10
+ responders?: Partial<BaseRespondersConfig>;
11
+ events?: Partial<BaseEventsConfig>;
12
+ }
13
+ export declare function setupCreators(options?: SetupCreatorsOptions): {
14
+ createCommand<T extends CommandType = ApplicationCommandType.ChatInput, P extends boolean = false, R = void>(data: CommandData<T, P, R>): Command<T, P, R>;
15
+ createEvent: <EventName extends keyof ClientEvents>(data: EventData<EventName>) => Event<EventName>;
16
+ createResponder<Path extends string, const Types extends readonly ResponderType[], Parsed, Cache extends CacheType = CacheType>(data: ResponderData<Path, Types, Parsed, Cache>): Responder<Path, Types, Parsed, Cache>;
17
+ };
18
+ export {};
@@ -0,0 +1,39 @@
1
+ // src/creators/setup.ts
2
+ import { ConstaticApp } from "../app.js";
3
+ import { Command } from "./commands/command.js";
4
+ import { Event } from "./events/event.js";
5
+ import { Responder } from "./responders/responder.js";
6
+ function setupCreators(options = {}) {
7
+ const app = ConstaticApp.getInstance();
8
+ app.config.commands = { ...options.commands ??= {} };
9
+ app.config.commands.guilds ??= [];
10
+ if (process.env.GUILD_ID?.length) {
11
+ app.config.commands.guilds.push(process.env.GUILD_ID);
12
+ }
13
+ return {
14
+ createCommand(data) {
15
+ if (options.commands?.defaultMemberPermissions) {
16
+ data.defaultMemberPermissions ??= options.commands.defaultMemberPermissions;
17
+ }
18
+ const command = new Command(data);
19
+ app.commands.set(command);
20
+ return command;
21
+ },
22
+ createEvent: function(data) {
23
+ if (data.event === "clientReady") {
24
+ data.once = true;
25
+ }
26
+ const event = new Event(data);
27
+ app.events.add(event);
28
+ return event;
29
+ },
30
+ createResponder(data) {
31
+ const responder = new Responder(data);
32
+ app.responders.set(responder);
33
+ return responder;
34
+ }
35
+ };
36
+ }
37
+ export {
38
+ setupCreators
39
+ };
@@ -0,0 +1,7 @@
1
+ import { Client } from "discord.js";
2
+ export declare class ConstaticError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ export declare class RunBlockError {
6
+ }
7
+ export declare function baseErrorHandler(error: any, client?: Client): Promise<void>;
package/dist/error.js ADDED
@@ -0,0 +1,64 @@
1
+ // src/error.ts
2
+ import { codeBlock, TextDisplayBuilder, time, WebhookClient } from "discord.js";
3
+ import console from "node:console";
4
+ import { styleText } from "node:util";
5
+
6
+ class ConstaticError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ }
10
+ }
11
+
12
+ class RunBlockError {
13
+ }
14
+ async function baseErrorHandler(error, client) {
15
+ if (client?.user)
16
+ console.log(client.user.displayName);
17
+ const text = [];
18
+ const hightlight = (text2) => text2.replace(/\(([^)]+)\)/g, (_, match) => [
19
+ styleText("gray", "("),
20
+ styleText("cyan", match),
21
+ styleText("gray", ")")
22
+ ].join(""));
23
+ if ("message" in error)
24
+ text.push(styleText("red", `${error.message}`));
25
+ if ("stack" in error) {
26
+ const formated = `${error.stack}`.replaceAll(process.cwd(), ".").replaceAll("at ", styleText("gray", "at "));
27
+ text.push(hightlight(formated));
28
+ }
29
+ console.log(text.join(`
30
+ `));
31
+ const url = process.env.WEBHOOK_LOGS_URL;
32
+ if (!url)
33
+ return;
34
+ try {
35
+ const webhook = new WebhookClient({ url });
36
+ const username = client?.user?.username;
37
+ await webhook.send({
38
+ flags: ["IsComponentsV2"],
39
+ components: [
40
+ new TextDisplayBuilder({
41
+ content: [
42
+ codeBlock("ansi", text.join(`
43
+ `)),
44
+ time(new Date, "R")
45
+ ].join(`
46
+ `)
47
+ })
48
+ ],
49
+ withComponents: true,
50
+ avatarURL: client?.user?.displayAvatarURL({ size: 512 }),
51
+ username: username ? `${username} logs` : "Logs"
52
+ }).catch(console.error);
53
+ } catch {
54
+ console.log();
55
+ console.error("ENV VAR → %s Invalid webhook url", styleText(["bold", "underline"], "WEBHOOK_LOGS_URL"));
56
+ console.log();
57
+ console.warn("Unable to send logs to webhook because the url is invalid");
58
+ }
59
+ }
60
+ export {
61
+ baseErrorHandler,
62
+ RunBlockError,
63
+ ConstaticError
64
+ };
@@ -0,0 +1,3 @@
1
+ export declare const version: string;
2
+ export * from "./bootstrap.js";
3
+ export * from "./creators/index.js";
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ // src/index.ts
2
+ import pkg from "../package.json";
3
+
4
+ export * from "./bootstrap.js";
5
+ export * from "./creators/index.js";
6
+ var version = pkg.version;
7
+ export {
8
+ version
9
+ };
@@ -0,0 +1,4 @@
1
+ export declare function loadModules(meta: ImportMeta, modules?: string[]): Promise<{
2
+ module: any;
3
+ filepath: string;
4
+ }[]>;
@@ -0,0 +1,14 @@
1
+ // src/modules.ts
2
+ import { glob } from "node:fs/promises";
3
+ async function loadModules(meta, modules = []) {
4
+ const exclude = modules.filter((path) => path.startsWith("!")).map((path) => path.slice(1));
5
+ const filepaths = await Array.fromAsync(glob(modules, { cwd: meta.dirname, exclude }));
6
+ const promises = filepaths.map((filepath) => import(meta.resolve(filepath)).then((imported) => ({
7
+ module: imported,
8
+ filepath
9
+ })));
10
+ return Promise.all(promises);
11
+ }
12
+ export {
13
+ loadModules
14
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ // src/register.ts
2
+ import { ConstaticApp } from "./app.js";
3
+ async function registerCommands(client) {
4
+ const app = ConstaticApp.getInstance();
5
+ const guildList = new Set(app.config.commands.guilds);
6
+ const commands = app.commands.build();
7
+ const promises = [];
8
+ const manager = client.application.commands;
9
+ if (guildList.size >= 1) {
10
+ const globalCommands = commands.filter((c) => c.global);
11
+ const guildCommands = commands.filter((c) => !c.global);
12
+ for (const id of guildList.values()) {
13
+ promises.push(manager.set(guildCommands, id));
14
+ }
15
+ promises.push(manager.set(globalCommands));
16
+ }
17
+ await Promise.all(promises).then(console.log);
18
+ }
19
+ export {
20
+ registerCommands
21
+ };
@@ -0,0 +1,9 @@
1
+ import { type MatchedRoute } from "rou3";
2
+ export declare class Router<T> {
3
+ private router;
4
+ constructor();
5
+ add(method: string, path: string, data?: T): void;
6
+ find(method: string, path: string): MatchedRoute<T> | undefined;
7
+ remove(method: string, path: string): void;
8
+ private resolvePath;
9
+ }
@@ -0,0 +1,24 @@
1
+ // src/utils/router.ts
2
+ import { addRoute, createRouter, findRoute, removeRoute } from "rou3";
3
+
4
+ class Router {
5
+ router;
6
+ constructor() {
7
+ this.router = createRouter();
8
+ }
9
+ add(method, path, data) {
10
+ addRoute(this.router, method.toUpperCase(), this.resolvePath(path), data);
11
+ }
12
+ find(method, path) {
13
+ return findRoute(this.router, method.toUpperCase(), this.resolvePath(path));
14
+ }
15
+ remove(method, path) {
16
+ removeRoute(this.router, method.toUpperCase(), this.resolvePath(path));
17
+ }
18
+ resolvePath(path) {
19
+ return path.startsWith("/") ? path : `/${path}`;
20
+ }
21
+ }
22
+ export {
23
+ Router
24
+ };
@@ -0,0 +1,3 @@
1
+ export type Prettify<T> = {
2
+ [K in keyof T]: T[K];
3
+ } & {};
File without changes
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@constatic/base",
3
+ "version": "0.0.1-beta.1",
4
+ "description": "",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/rinckodev/constatic",
13
+ "directory": "packages/base"
14
+ },
15
+ "author": {
16
+ "url": "https://github.com/rinckodev",
17
+ "name": "Rincko dev"
18
+ },
19
+ "devDependencies": {
20
+ "@constatic/config": "*",
21
+ "typescript": "^5.9.3"
22
+ },
23
+ "exports": {
24
+ ".": {
25
+ "import": "./dist/index.js",
26
+ "types": "./dist/index.d.ts"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "homepage": "https://constatic-docs.vercel.app",
33
+ "imports": {
34
+ "#package": [
35
+ "./src/index.ts"
36
+ ]
37
+ },
38
+ "scripts": {
39
+ "typegen": "tsc",
40
+ "build": "bun run ./build.ts",
41
+ "dev": "bun run ./playground/index.ts"
42
+ },
43
+ "peerDependencies": {
44
+ "discord.js": "^14.24.2"
45
+ },
46
+ "dependencies": {
47
+ "@discordjs/core": "^2.3.0",
48
+ "rou3": "^0.7.10"
49
+ }
50
+ }