spearkit 0.1.0

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.cjs ADDED
@@ -0,0 +1,1242 @@
1
+ 'use strict';
2
+
3
+ var discord_js = require('discord.js');
4
+ var promises = require('fs/promises');
5
+ var path = require('path');
6
+ var url = require('url');
7
+
8
+ // src/index.ts
9
+ function withEphemeralFlag(flags) {
10
+ if (flags == null) return discord_js.MessageFlags.Ephemeral;
11
+ if (typeof flags === "number" || typeof flags === "bigint") {
12
+ return Number(flags) | discord_js.MessageFlags.Ephemeral;
13
+ }
14
+ if (Array.isArray(flags)) return [...flags, discord_js.MessageFlags.Ephemeral];
15
+ return [flags, discord_js.MessageFlags.Ephemeral];
16
+ }
17
+ function normalizeReply(input) {
18
+ if (typeof input === "string") return { content: input };
19
+ const { ephemeral, ...rest } = input;
20
+ if (ephemeral) return { ...rest, flags: withEphemeralFlag(rest.flags) };
21
+ return rest;
22
+ }
23
+ function normalizeEdit(input) {
24
+ if (typeof input === "string") return { content: input };
25
+ const { ephemeral: _ephemeral, flags: _flags, ...rest } = input;
26
+ return rest;
27
+ }
28
+ function asEphemeral(input) {
29
+ if (typeof input === "string") return { content: input, ephemeral: true };
30
+ return { ...input, ephemeral: true };
31
+ }
32
+ var BaseContext = class {
33
+ constructor(interaction) {
34
+ this.interaction = interaction;
35
+ }
36
+ interaction;
37
+ get client() {
38
+ return this.interaction.client;
39
+ }
40
+ get user() {
41
+ return this.interaction.user;
42
+ }
43
+ get member() {
44
+ return this.interaction.member;
45
+ }
46
+ get guild() {
47
+ return this.interaction.guild;
48
+ }
49
+ get guildId() {
50
+ return this.interaction.guildId;
51
+ }
52
+ get channel() {
53
+ return this.interaction.channel;
54
+ }
55
+ get channelId() {
56
+ return this.interaction.channelId;
57
+ }
58
+ get locale() {
59
+ return this.interaction.locale;
60
+ }
61
+ /** Whether the interaction is already deferred. */
62
+ get deferred() {
63
+ return this.interaction.deferred;
64
+ }
65
+ /** Whether the interaction already received an initial response. */
66
+ get replied() {
67
+ return this.interaction.replied;
68
+ }
69
+ /** Send the initial response to the interaction. */
70
+ reply(input) {
71
+ return this.interaction.reply(normalizeReply(input));
72
+ }
73
+ /** Reply, but always hidden to everyone except the invoking user. */
74
+ replyEphemeral(input) {
75
+ return this.reply(asEphemeral(input));
76
+ }
77
+ /** Acknowledge now and respond later via {@link editReply}. */
78
+ defer(options = {}) {
79
+ return this.interaction.deferReply(
80
+ options.ephemeral ? { flags: discord_js.MessageFlags.Ephemeral } : {}
81
+ );
82
+ }
83
+ /** Edit the original (or deferred) response. */
84
+ editReply(input) {
85
+ return this.interaction.editReply(normalizeEdit(input));
86
+ }
87
+ /** Add an additional message after the initial response. */
88
+ followUp(input) {
89
+ return this.interaction.followUp(normalizeReply(input));
90
+ }
91
+ /**
92
+ * State-aware send: replies, edits a deferred response, or follows up —
93
+ * whichever is valid given the current interaction state. The single method
94
+ * most handlers ever need.
95
+ */
96
+ async send(input) {
97
+ if (this.interaction.deferred) {
98
+ await this.editReply(input);
99
+ } else if (this.interaction.replied) {
100
+ await this.followUp(input);
101
+ } else {
102
+ await this.reply(input);
103
+ }
104
+ }
105
+ /** State-aware ephemeral error message. */
106
+ error(message) {
107
+ return this.send(asEphemeral(message));
108
+ }
109
+ };
110
+ function makeOption(type, config) {
111
+ return { type, ...config, required: config.required ?? false };
112
+ }
113
+ var option = {
114
+ string(config) {
115
+ return makeOption(discord_js.ApplicationCommandOptionType.String, config);
116
+ },
117
+ integer(config) {
118
+ return makeOption(discord_js.ApplicationCommandOptionType.Integer, config);
119
+ },
120
+ number(config) {
121
+ return makeOption(discord_js.ApplicationCommandOptionType.Number, config);
122
+ },
123
+ boolean(config) {
124
+ return makeOption(discord_js.ApplicationCommandOptionType.Boolean, config);
125
+ },
126
+ user(config) {
127
+ return makeOption(discord_js.ApplicationCommandOptionType.User, config);
128
+ },
129
+ channel(config) {
130
+ return makeOption(discord_js.ApplicationCommandOptionType.Channel, config);
131
+ },
132
+ role(config) {
133
+ return makeOption(discord_js.ApplicationCommandOptionType.Role, config);
134
+ },
135
+ mentionable(config) {
136
+ return makeOption(discord_js.ApplicationCommandOptionType.Mentionable, config);
137
+ },
138
+ attachment(config) {
139
+ return makeOption(discord_js.ApplicationCommandOptionType.Attachment, config);
140
+ }
141
+ };
142
+ function mapChoices(choices) {
143
+ return choices?.map((c) => ({
144
+ name: c.name,
145
+ value: c.value,
146
+ name_localizations: c.nameLocalizations
147
+ }));
148
+ }
149
+ function toAPIOption(name, def) {
150
+ const shared = {
151
+ name,
152
+ description: def.description,
153
+ required: def.required,
154
+ name_localizations: def.nameLocalizations,
155
+ description_localizations: def.descriptionLocalizations
156
+ };
157
+ switch (def.type) {
158
+ case discord_js.ApplicationCommandOptionType.String: {
159
+ const base = { ...shared, min_length: def.minLength, max_length: def.maxLength };
160
+ return def.autocomplete !== void 0 ? { ...base, type: discord_js.ApplicationCommandOptionType.String, autocomplete: true } : { ...base, type: discord_js.ApplicationCommandOptionType.String, choices: mapChoices(def.choices) };
161
+ }
162
+ case discord_js.ApplicationCommandOptionType.Integer: {
163
+ const base = { ...shared, min_value: def.minValue, max_value: def.maxValue };
164
+ return def.autocomplete !== void 0 ? { ...base, type: discord_js.ApplicationCommandOptionType.Integer, autocomplete: true } : { ...base, type: discord_js.ApplicationCommandOptionType.Integer, choices: mapChoices(def.choices) };
165
+ }
166
+ case discord_js.ApplicationCommandOptionType.Number: {
167
+ const base = { ...shared, min_value: def.minValue, max_value: def.maxValue };
168
+ return def.autocomplete !== void 0 ? { ...base, type: discord_js.ApplicationCommandOptionType.Number, autocomplete: true } : { ...base, type: discord_js.ApplicationCommandOptionType.Number, choices: mapChoices(def.choices) };
169
+ }
170
+ case discord_js.ApplicationCommandOptionType.Channel:
171
+ return {
172
+ ...shared,
173
+ type: discord_js.ApplicationCommandOptionType.Channel,
174
+ channel_types: def.channelTypes ? [...def.channelTypes] : void 0
175
+ };
176
+ case discord_js.ApplicationCommandOptionType.User:
177
+ return { ...shared, type: discord_js.ApplicationCommandOptionType.User };
178
+ case discord_js.ApplicationCommandOptionType.Boolean:
179
+ return { ...shared, type: discord_js.ApplicationCommandOptionType.Boolean };
180
+ case discord_js.ApplicationCommandOptionType.Role:
181
+ return { ...shared, type: discord_js.ApplicationCommandOptionType.Role };
182
+ case discord_js.ApplicationCommandOptionType.Mentionable:
183
+ return { ...shared, type: discord_js.ApplicationCommandOptionType.Mentionable };
184
+ case discord_js.ApplicationCommandOptionType.Attachment:
185
+ return { ...shared, type: discord_js.ApplicationCommandOptionType.Attachment };
186
+ default:
187
+ return { ...shared, type: discord_js.ApplicationCommandOptionType.String };
188
+ }
189
+ }
190
+ function readOption(resolver, name, def) {
191
+ switch (def.type) {
192
+ case discord_js.ApplicationCommandOptionType.String:
193
+ return resolver.getString(name) ?? void 0;
194
+ case discord_js.ApplicationCommandOptionType.Integer:
195
+ return resolver.getInteger(name) ?? void 0;
196
+ case discord_js.ApplicationCommandOptionType.Number:
197
+ return resolver.getNumber(name) ?? void 0;
198
+ case discord_js.ApplicationCommandOptionType.Boolean:
199
+ return resolver.getBoolean(name) ?? void 0;
200
+ case discord_js.ApplicationCommandOptionType.User:
201
+ return resolver.getUser(name) ?? void 0;
202
+ case discord_js.ApplicationCommandOptionType.Channel:
203
+ return resolver.getChannel(name) ?? void 0;
204
+ case discord_js.ApplicationCommandOptionType.Role:
205
+ return resolver.getRole(name) ?? void 0;
206
+ case discord_js.ApplicationCommandOptionType.Mentionable:
207
+ return resolver.getMentionable(name) ?? void 0;
208
+ case discord_js.ApplicationCommandOptionType.Attachment:
209
+ return resolver.getAttachment(name) ?? void 0;
210
+ default:
211
+ return void 0;
212
+ }
213
+ }
214
+ function optionsHaveAutocomplete(options) {
215
+ for (const def of Object.values(options)) {
216
+ if (def.autocomplete !== void 0) return true;
217
+ }
218
+ return false;
219
+ }
220
+
221
+ // src/commands/context.ts
222
+ var CommandContext = class extends BaseContext {
223
+ constructor(interaction, options) {
224
+ super(interaction);
225
+ this.options = options;
226
+ }
227
+ options;
228
+ get commandName() {
229
+ return this.interaction.commandName;
230
+ }
231
+ /** The invoked subcommand name, if any. */
232
+ get subcommand() {
233
+ return this.interaction.options.getSubcommand(false);
234
+ }
235
+ /** Present a modal to the user in response to this command. */
236
+ async showModal(modal2) {
237
+ await this.interaction.showModal(modal2);
238
+ }
239
+ };
240
+ var AutocompleteContext = class {
241
+ constructor(interaction) {
242
+ this.interaction = interaction;
243
+ }
244
+ interaction;
245
+ get client() {
246
+ return this.interaction.client;
247
+ }
248
+ get user() {
249
+ return this.interaction.user;
250
+ }
251
+ get guild() {
252
+ return this.interaction.guild;
253
+ }
254
+ get guildId() {
255
+ return this.interaction.guildId;
256
+ }
257
+ get commandName() {
258
+ return this.interaction.commandName;
259
+ }
260
+ /** Name of the option currently being completed. */
261
+ get focusedName() {
262
+ return this.interaction.options.getFocused(true).name;
263
+ }
264
+ /** Current partial value typed by the user. */
265
+ get value() {
266
+ return this.interaction.options.getFocused();
267
+ }
268
+ /** Send autocomplete suggestions (capped at the discord limit of 25). */
269
+ respond(choices) {
270
+ return this.interaction.respond(
271
+ choices.slice(0, 25).map((c) => ({
272
+ name: c.name,
273
+ value: c.value,
274
+ name_localizations: c.nameLocalizations
275
+ }))
276
+ );
277
+ }
278
+ };
279
+
280
+ // src/commands/command.ts
281
+ var SlashCommand = class {
282
+ /** The top-level command name (used as the registry lookup key). */
283
+ name;
284
+ /** Whether any option declares an autocomplete handler. */
285
+ hasAutocomplete;
286
+ json;
287
+ executor;
288
+ autocompleter;
289
+ /** @internal */
290
+ constructor(spec) {
291
+ this.name = spec.name;
292
+ this.hasAutocomplete = spec.hasAutocomplete;
293
+ this.json = spec.json;
294
+ this.executor = spec.executor;
295
+ this.autocompleter = spec.autocompleter;
296
+ }
297
+ /** Serialise to the discord REST chat-input command payload. */
298
+ toJSON() {
299
+ return this.json;
300
+ }
301
+ /** Execute the command for an incoming chat-input interaction. */
302
+ execute(interaction) {
303
+ return this.executor(interaction);
304
+ }
305
+ /** Execute autocomplete for the focused option. */
306
+ autocomplete(interaction) {
307
+ return this.autocompleter(interaction);
308
+ }
309
+ };
310
+ function resolveOptions(interaction, options) {
311
+ const resolved = {};
312
+ for (const [name, def] of Object.entries(options)) {
313
+ resolved[name] = readOption(interaction.options, name, def);
314
+ }
315
+ return resolved;
316
+ }
317
+ function makeAutocompleter(options) {
318
+ return async (interaction) => {
319
+ const focused = interaction.options.getFocused(true);
320
+ const def = options[focused.name];
321
+ if (def?.autocomplete === void 0) {
322
+ if (!interaction.responded) await interaction.respond([]);
323
+ return;
324
+ }
325
+ const ctx = new AutocompleteContext(interaction);
326
+ const choices = await def.autocomplete(ctx);
327
+ if (!interaction.responded) await ctx.respond(choices);
328
+ };
329
+ }
330
+ function baseJSON(meta, options) {
331
+ return {
332
+ type: discord_js.ApplicationCommandType.ChatInput,
333
+ name: meta.name,
334
+ description: meta.description,
335
+ name_localizations: meta.nameLocalizations,
336
+ description_localizations: meta.descriptionLocalizations,
337
+ nsfw: meta.nsfw,
338
+ default_member_permissions: meta.defaultMemberPermissions == null ? meta.defaultMemberPermissions : new discord_js.PermissionsBitField(meta.defaultMemberPermissions).bitfield.toString(),
339
+ contexts: meta.guildOnly ? [discord_js.InteractionContextType.Guild] : void 0,
340
+ options
341
+ };
342
+ }
343
+ function leafOptionsJSON(options) {
344
+ return Object.entries(options).map(([name, def]) => toAPIOption(name, def));
345
+ }
346
+ function subcommandJSON(name, sub) {
347
+ return {
348
+ type: discord_js.ApplicationCommandOptionType.Subcommand,
349
+ name,
350
+ description: sub.description,
351
+ name_localizations: sub.nameLocalizations,
352
+ description_localizations: sub.descriptionLocalizations,
353
+ options: Object.entries(sub.options).map(([n, def]) => toAPIOption(n, def))
354
+ };
355
+ }
356
+ function routeSubcommand(groupName, subName, subcommands, groups) {
357
+ if (groupName !== null) return groups?.[groupName]?.subcommands[subName ?? ""];
358
+ if (subName !== null) return subcommands?.[subName];
359
+ return void 0;
360
+ }
361
+ function command(config) {
362
+ const options = config.options ?? {};
363
+ const { run } = config;
364
+ const executor = async (interaction) => {
365
+ const resolved = resolveOptions(interaction, options);
366
+ await run(new CommandContext(interaction, resolved));
367
+ };
368
+ return new SlashCommand({
369
+ name: config.name,
370
+ json: baseJSON(config, leafOptionsJSON(options)),
371
+ hasAutocomplete: optionsHaveAutocomplete(options),
372
+ executor,
373
+ autocompleter: makeAutocompleter(options)
374
+ });
375
+ }
376
+ function subcommand(config) {
377
+ const options = config.options ?? {};
378
+ const { run } = config;
379
+ const execute = async (interaction) => {
380
+ const resolved = resolveOptions(interaction, options);
381
+ await run(new CommandContext(interaction, resolved));
382
+ };
383
+ return {
384
+ kind: "subcommand",
385
+ description: config.description,
386
+ options,
387
+ nameLocalizations: config.nameLocalizations,
388
+ descriptionLocalizations: config.descriptionLocalizations,
389
+ hasAutocomplete: optionsHaveAutocomplete(options),
390
+ execute,
391
+ autocomplete: makeAutocompleter(options)
392
+ };
393
+ }
394
+ function subcommandGroup(config) {
395
+ return { kind: "group", ...config };
396
+ }
397
+ function commandGroup(config) {
398
+ const { subcommands, groups } = config;
399
+ const options = [];
400
+ for (const [name, sub] of Object.entries(subcommands ?? {})) {
401
+ options.push(subcommandJSON(name, sub));
402
+ }
403
+ for (const [name, group] of Object.entries(groups ?? {})) {
404
+ options.push({
405
+ type: discord_js.ApplicationCommandOptionType.SubcommandGroup,
406
+ name,
407
+ description: group.description,
408
+ name_localizations: group.nameLocalizations,
409
+ description_localizations: group.descriptionLocalizations,
410
+ options: Object.entries(group.subcommands).map(([n, s]) => subcommandJSON(n, s))
411
+ });
412
+ }
413
+ const hasAutocomplete = Object.values(subcommands ?? {}).some((s) => s.hasAutocomplete) || Object.values(groups ?? {}).some((g) => Object.values(g.subcommands).some((s) => s.hasAutocomplete));
414
+ const executor = async (interaction) => {
415
+ const target = routeSubcommand(
416
+ interaction.options.getSubcommandGroup(false),
417
+ interaction.options.getSubcommand(false),
418
+ subcommands,
419
+ groups
420
+ );
421
+ if (target === void 0) {
422
+ throw new Error(`spearkit: no subcommand handler for /${config.name}`);
423
+ }
424
+ await target.execute(interaction);
425
+ };
426
+ const autocompleter = async (interaction) => {
427
+ const target = routeSubcommand(
428
+ interaction.options.getSubcommandGroup(false),
429
+ interaction.options.getSubcommand(false),
430
+ subcommands,
431
+ groups
432
+ );
433
+ if (target === void 0) {
434
+ if (!interaction.responded) await interaction.respond([]);
435
+ return;
436
+ }
437
+ await target.autocomplete(interaction);
438
+ };
439
+ return new SlashCommand({
440
+ name: config.name,
441
+ json: baseJSON(config, options),
442
+ hasAutocomplete,
443
+ executor,
444
+ autocompleter
445
+ });
446
+ }
447
+ var CommandRegistry = class {
448
+ commands = /* @__PURE__ */ new Map();
449
+ errorHandler;
450
+ /** Register one or more commands. Later registrations override by name. */
451
+ add(...commands) {
452
+ for (const command2 of commands) this.commands.set(command2.name, command2);
453
+ return this;
454
+ }
455
+ /** Remove a command by name. */
456
+ remove(name) {
457
+ return this.commands.delete(name);
458
+ }
459
+ /** Look up a command by name. */
460
+ get(name) {
461
+ return this.commands.get(name);
462
+ }
463
+ /** All registered commands. */
464
+ all() {
465
+ return [...this.commands.values()];
466
+ }
467
+ /** All registered command names. */
468
+ get names() {
469
+ return [...this.commands.keys()];
470
+ }
471
+ /** Number of registered commands. */
472
+ get size() {
473
+ return this.commands.size;
474
+ }
475
+ /** Set the handler used when a command throws. */
476
+ onError(handler) {
477
+ this.errorHandler = handler;
478
+ return this;
479
+ }
480
+ /** Serialise every command to discord REST payloads. */
481
+ toJSON() {
482
+ return this.all().map((c) => c.toJSON());
483
+ }
484
+ /** Dispatch an incoming chat-input interaction to its command. */
485
+ async handle(interaction) {
486
+ const command2 = this.commands.get(interaction.commandName);
487
+ if (command2 === void 0) return;
488
+ try {
489
+ await command2.execute(interaction);
490
+ } catch (error) {
491
+ const err = error instanceof Error ? error : new Error(String(error));
492
+ if (this.errorHandler !== void 0) {
493
+ await this.errorHandler(err, interaction);
494
+ } else {
495
+ await this.defaultErrorReply(err, interaction);
496
+ }
497
+ }
498
+ }
499
+ /** Dispatch an autocomplete interaction to its command. */
500
+ async handleAutocomplete(interaction) {
501
+ const command2 = this.commands.get(interaction.commandName);
502
+ if (command2 === void 0) return;
503
+ try {
504
+ await command2.autocomplete(interaction);
505
+ } catch {
506
+ if (!interaction.responded) await interaction.respond([]).catch(() => void 0);
507
+ }
508
+ }
509
+ /**
510
+ * Push the registered commands to discord. Returns the API response.
511
+ *
512
+ * Guild deploys apply instantly and are ideal during development; global
513
+ * deploys can take up to an hour to propagate.
514
+ */
515
+ async deploy(options) {
516
+ let rest = options.rest;
517
+ if (rest === void 0) {
518
+ if (options.token === void 0) {
519
+ throw new Error("spearkit: deploy() requires a token or a pre-configured REST instance");
520
+ }
521
+ rest = new discord_js.REST().setToken(options.token);
522
+ }
523
+ const body = this.toJSON();
524
+ const route = options.guildId !== void 0 ? discord_js.Routes.applicationGuildCommands(options.applicationId, options.guildId) : discord_js.Routes.applicationCommands(options.applicationId);
525
+ return await rest.put(route, { body });
526
+ }
527
+ async defaultErrorReply(error, interaction) {
528
+ interaction.client.emit("error", error);
529
+ const content = "Something went wrong while running that command.";
530
+ try {
531
+ if (interaction.deferred) {
532
+ await interaction.editReply({ content });
533
+ } else if (interaction.replied) {
534
+ await interaction.followUp({ content, flags: discord_js.MessageFlags.Ephemeral });
535
+ } else {
536
+ await interaction.reply({ content, flags: discord_js.MessageFlags.Ephemeral });
537
+ }
538
+ } catch {
539
+ }
540
+ }
541
+ };
542
+
543
+ // src/events.ts
544
+ function toError(value) {
545
+ return value instanceof Error ? value : new Error(String(value));
546
+ }
547
+ function build(name, once, run) {
548
+ const listeners = /* @__PURE__ */ new WeakMap();
549
+ return {
550
+ name,
551
+ once,
552
+ attach(client) {
553
+ const listener = (...args) => {
554
+ try {
555
+ const result = run(...args);
556
+ if (result instanceof Promise) {
557
+ result.catch((error) => client.emit("error", toError(error)));
558
+ }
559
+ } catch (error) {
560
+ client.emit("error", toError(error));
561
+ }
562
+ };
563
+ listeners.set(client, listener);
564
+ if (once) client.once(name, listener);
565
+ else client.on(name, listener);
566
+ },
567
+ detach(client) {
568
+ const listener = listeners.get(client);
569
+ if (listener !== void 0) {
570
+ client.off(name, listener);
571
+ listeners.delete(client);
572
+ }
573
+ }
574
+ };
575
+ }
576
+ function event(nameOrConfig, run) {
577
+ if (typeof nameOrConfig === "object") {
578
+ return build(nameOrConfig.name, nameOrConfig.once ?? false, nameOrConfig.run);
579
+ }
580
+ if (run === void 0) {
581
+ throw new Error("spearkit: event(name, run) requires a handler");
582
+ }
583
+ return build(nameOrConfig, false, run);
584
+ }
585
+ var EventRegistry = class {
586
+ events = [];
587
+ attached = /* @__PURE__ */ new Set();
588
+ /** Register one or more event definitions. */
589
+ add(...defs) {
590
+ this.events.push(...defs);
591
+ for (const client of this.attached) {
592
+ for (const def of defs) def.attach(client);
593
+ }
594
+ return this;
595
+ }
596
+ /** Number of registered listeners. */
597
+ get size() {
598
+ return this.events.length;
599
+ }
600
+ /** Attach every registered listener to the client. */
601
+ attachAll(client) {
602
+ this.attached.add(client);
603
+ for (const def of this.events) def.attach(client);
604
+ }
605
+ /** Detach every registered listener from the client. */
606
+ detachAll(client) {
607
+ this.attached.delete(client);
608
+ for (const def of this.events) def.detach(client);
609
+ }
610
+ };
611
+
612
+ // src/components/customId.ts
613
+ var MAX_CUSTOM_ID_LENGTH = 100;
614
+ var PARAM_SEGMENT = /^\{([A-Za-z0-9_]+)\}$/;
615
+ function compilePattern(pattern) {
616
+ const segments = pattern.split(":");
617
+ const namespace = segments[0] ?? "";
618
+ if (namespace.length === 0 || /[{}]/.test(namespace)) {
619
+ throw new Error(
620
+ `spearkit: invalid custom-id pattern "${pattern}". Expected "namespace" or "namespace:{param}".`
621
+ );
622
+ }
623
+ const paramNames = [];
624
+ for (let i = 1; i < segments.length; i++) {
625
+ const match = PARAM_SEGMENT.exec(segments[i] ?? "");
626
+ if (match === null) {
627
+ throw new Error(
628
+ `spearkit: invalid custom-id pattern "${pattern}". Segment "${segments[i]}" must be "{param}".`
629
+ );
630
+ }
631
+ paramNames.push(match[1]);
632
+ }
633
+ return { pattern, namespace, paramNames };
634
+ }
635
+ function encodeValue(value) {
636
+ return value.replace(/%/g, "%25").replace(/:/g, "%3A");
637
+ }
638
+ function decodeValue(value) {
639
+ return value.replace(/%3A/g, ":").replace(/%25/g, "%");
640
+ }
641
+ function buildCustomId(compiled, params) {
642
+ const parts = [compiled.namespace];
643
+ for (const name of compiled.paramNames) {
644
+ const value = params[name];
645
+ if (value === void 0) {
646
+ throw new Error(`spearkit: missing param "${name}" for custom-id "${compiled.pattern}"`);
647
+ }
648
+ parts.push(encodeValue(value));
649
+ }
650
+ const id = parts.join(":");
651
+ if (id.length > MAX_CUSTOM_ID_LENGTH) {
652
+ throw new Error(
653
+ `spearkit: custom-id "${id}" exceeds the ${MAX_CUSTOM_ID_LENGTH}-character discord limit`
654
+ );
655
+ }
656
+ return id;
657
+ }
658
+ function parseCustomId(customId) {
659
+ const segments = customId.split(":");
660
+ const namespace = segments[0] ?? "";
661
+ const values = segments.slice(1).map(decodeValue);
662
+ return { namespace, values };
663
+ }
664
+ function paramsFromValues(paramNames, values) {
665
+ const params = {};
666
+ for (let i = 0; i < paramNames.length; i++) {
667
+ params[paramNames[i]] = values[i] ?? "";
668
+ }
669
+ return params;
670
+ }
671
+
672
+ // src/components/context.ts
673
+ var MessageComponentContext = class extends BaseContext {
674
+ constructor(interaction, params) {
675
+ super(interaction);
676
+ this.params = params;
677
+ }
678
+ params;
679
+ /** The raw custom-id that triggered this interaction. */
680
+ get customId() {
681
+ return this.interaction.customId;
682
+ }
683
+ /** The message the component lives on. */
684
+ get message() {
685
+ return this.interaction.message;
686
+ }
687
+ /** Edit the message this component belongs to. */
688
+ async update(input) {
689
+ await this.interaction.update(input);
690
+ }
691
+ /** Acknowledge the interaction without editing the message yet. */
692
+ async deferUpdate() {
693
+ await this.interaction.deferUpdate();
694
+ }
695
+ /** Open a modal in response to this component. */
696
+ async showModal(modal2) {
697
+ await this.interaction.showModal(modal2);
698
+ }
699
+ };
700
+ var ButtonContext = class extends MessageComponentContext {
701
+ };
702
+ var StringSelectContext = class extends MessageComponentContext {
703
+ /** All selected values. */
704
+ get values() {
705
+ return this.interaction.values;
706
+ }
707
+ /** The first selected value, or `undefined` if none. */
708
+ get value() {
709
+ return this.interaction.values[0];
710
+ }
711
+ };
712
+ var UserSelectContext = class extends MessageComponentContext {
713
+ get values() {
714
+ return this.interaction.values;
715
+ }
716
+ get users() {
717
+ return this.interaction.users;
718
+ }
719
+ get members() {
720
+ return this.interaction.members;
721
+ }
722
+ };
723
+ var RoleSelectContext = class extends MessageComponentContext {
724
+ get values() {
725
+ return this.interaction.values;
726
+ }
727
+ get roles() {
728
+ return this.interaction.roles;
729
+ }
730
+ };
731
+ var ChannelSelectContext = class extends MessageComponentContext {
732
+ get values() {
733
+ return this.interaction.values;
734
+ }
735
+ get channels() {
736
+ return this.interaction.channels;
737
+ }
738
+ };
739
+ var MentionableSelectContext = class extends MessageComponentContext {
740
+ get values() {
741
+ return this.interaction.values;
742
+ }
743
+ get users() {
744
+ return this.interaction.users;
745
+ }
746
+ get roles() {
747
+ return this.interaction.roles;
748
+ }
749
+ get members() {
750
+ return this.interaction.members;
751
+ }
752
+ };
753
+ var ModalContext = class extends BaseContext {
754
+ constructor(interaction, params, fields) {
755
+ super(interaction);
756
+ this.params = params;
757
+ this.fields = fields;
758
+ }
759
+ params;
760
+ fields;
761
+ /** The raw custom-id that triggered this modal submission. */
762
+ get customId() {
763
+ return this.interaction.customId;
764
+ }
765
+ };
766
+ function toError2(value) {
767
+ return value instanceof Error ? value : new Error(String(value));
768
+ }
769
+ var ComponentRegistry = class {
770
+ buttons = /* @__PURE__ */ new Map();
771
+ stringSelects = /* @__PURE__ */ new Map();
772
+ userSelects = /* @__PURE__ */ new Map();
773
+ roleSelects = /* @__PURE__ */ new Map();
774
+ channelSelects = /* @__PURE__ */ new Map();
775
+ mentionableSelects = /* @__PURE__ */ new Map();
776
+ modals = /* @__PURE__ */ new Map();
777
+ errorHandler;
778
+ /** Register one or more components. Later registrations override by namespace. */
779
+ add(...defs) {
780
+ for (const def of defs) {
781
+ switch (def.kind) {
782
+ case "button":
783
+ this.buttons.set(def.namespace, def);
784
+ break;
785
+ case "stringSelect":
786
+ this.stringSelects.set(def.namespace, def);
787
+ break;
788
+ case "userSelect":
789
+ this.userSelects.set(def.namespace, def);
790
+ break;
791
+ case "roleSelect":
792
+ this.roleSelects.set(def.namespace, def);
793
+ break;
794
+ case "channelSelect":
795
+ this.channelSelects.set(def.namespace, def);
796
+ break;
797
+ case "mentionableSelect":
798
+ this.mentionableSelects.set(def.namespace, def);
799
+ break;
800
+ case "modal":
801
+ this.modals.set(def.namespace, def);
802
+ break;
803
+ }
804
+ }
805
+ return this;
806
+ }
807
+ /** Set the handler used when a component throws. */
808
+ onError(handler) {
809
+ this.errorHandler = handler;
810
+ return this;
811
+ }
812
+ /** Total number of registered components. */
813
+ get size() {
814
+ return this.buttons.size + this.stringSelects.size + this.userSelects.size + this.roleSelects.size + this.channelSelects.size + this.mentionableSelects.size + this.modals.size;
815
+ }
816
+ /**
817
+ * Dispatch an interaction to its component handler. Returns `true` if a
818
+ * handler matched and ran, `false` otherwise.
819
+ */
820
+ async handle(interaction) {
821
+ if (interaction.isButton()) {
822
+ return this.exec(this.buttons.get(namespaceOf(interaction.customId)), interaction);
823
+ }
824
+ if (interaction.isStringSelectMenu()) {
825
+ return this.exec(this.stringSelects.get(namespaceOf(interaction.customId)), interaction);
826
+ }
827
+ if (interaction.isUserSelectMenu()) {
828
+ return this.exec(this.userSelects.get(namespaceOf(interaction.customId)), interaction);
829
+ }
830
+ if (interaction.isRoleSelectMenu()) {
831
+ return this.exec(this.roleSelects.get(namespaceOf(interaction.customId)), interaction);
832
+ }
833
+ if (interaction.isChannelSelectMenu()) {
834
+ return this.exec(this.channelSelects.get(namespaceOf(interaction.customId)), interaction);
835
+ }
836
+ if (interaction.isMentionableSelectMenu()) {
837
+ return this.exec(this.mentionableSelects.get(namespaceOf(interaction.customId)), interaction);
838
+ }
839
+ if (interaction.isModalSubmit()) {
840
+ return this.exec(this.modals.get(namespaceOf(interaction.customId)), interaction);
841
+ }
842
+ return false;
843
+ }
844
+ async exec(route, interaction) {
845
+ if (route === void 0) return false;
846
+ const { values } = parseCustomId(interaction.customId);
847
+ const params = paramsFromValues(route.paramNames, values);
848
+ try {
849
+ await route.handle(interaction, params);
850
+ } catch (error) {
851
+ const err = toError2(error);
852
+ if (this.errorHandler !== void 0) {
853
+ await this.errorHandler(err, interaction);
854
+ } else {
855
+ interaction.client.emit("error", err);
856
+ if (!interaction.replied && !interaction.deferred) {
857
+ await interaction.reply({ content: "Something went wrong.", flags: discord_js.MessageFlags.Ephemeral }).catch(() => void 0);
858
+ }
859
+ }
860
+ }
861
+ return true;
862
+ }
863
+ };
864
+ function namespaceOf(customId) {
865
+ return parseCustomId(customId).namespace;
866
+ }
867
+ function resolveButtonStyle(input) {
868
+ if (input === void 0) return discord_js.ButtonStyle.Secondary;
869
+ return typeof input === "number" ? input : discord_js.ButtonStyle[input];
870
+ }
871
+ function button(config) {
872
+ const compiled = compilePattern(config.id);
873
+ const style = resolveButtonStyle(config.style);
874
+ return {
875
+ kind: "button",
876
+ namespace: compiled.namespace,
877
+ paramNames: compiled.paramNames,
878
+ async handle(interaction, params) {
879
+ await config.run(new ButtonContext(interaction, params));
880
+ },
881
+ build(...args) {
882
+ const params = args[0] ?? {};
883
+ const builder = new discord_js.ButtonBuilder().setCustomId(buildCustomId(compiled, params)).setStyle(style);
884
+ if (config.label !== void 0) builder.setLabel(config.label);
885
+ if (config.emoji !== void 0) builder.setEmoji(config.emoji);
886
+ if (config.disabled !== void 0) builder.setDisabled(config.disabled);
887
+ return builder;
888
+ }
889
+ };
890
+ }
891
+ function linkButton(config) {
892
+ const builder = new discord_js.ButtonBuilder().setStyle(discord_js.ButtonStyle.Link).setURL(config.url);
893
+ if (config.label !== void 0) builder.setLabel(config.label);
894
+ if (config.emoji !== void 0) builder.setEmoji(config.emoji);
895
+ if (config.disabled !== void 0) builder.setDisabled(config.disabled);
896
+ return builder;
897
+ }
898
+ function applySelectBase(builder, config) {
899
+ if (config.placeholder !== void 0) builder.setPlaceholder(config.placeholder);
900
+ if (config.minValues !== void 0) builder.setMinValues(config.minValues);
901
+ if (config.maxValues !== void 0) builder.setMaxValues(config.maxValues);
902
+ if (config.disabled !== void 0) builder.setDisabled(config.disabled);
903
+ }
904
+ function stringSelect(config) {
905
+ const compiled = compilePattern(config.id);
906
+ return {
907
+ kind: "stringSelect",
908
+ namespace: compiled.namespace,
909
+ paramNames: compiled.paramNames,
910
+ async handle(interaction, params) {
911
+ await config.run(new StringSelectContext(interaction, params));
912
+ },
913
+ build(...args) {
914
+ const params = args[0] ?? {};
915
+ const builder = new discord_js.StringSelectMenuBuilder().setCustomId(buildCustomId(compiled, params)).addOptions(...config.options);
916
+ applySelectBase(builder, config);
917
+ return builder;
918
+ }
919
+ };
920
+ }
921
+ function userSelect(config) {
922
+ const compiled = compilePattern(config.id);
923
+ return {
924
+ kind: "userSelect",
925
+ namespace: compiled.namespace,
926
+ paramNames: compiled.paramNames,
927
+ async handle(interaction, params) {
928
+ await config.run(new UserSelectContext(interaction, params));
929
+ },
930
+ build(...args) {
931
+ const params = args[0] ?? {};
932
+ const builder = new discord_js.UserSelectMenuBuilder().setCustomId(buildCustomId(compiled, params));
933
+ applySelectBase(builder, config);
934
+ return builder;
935
+ }
936
+ };
937
+ }
938
+ function roleSelect(config) {
939
+ const compiled = compilePattern(config.id);
940
+ return {
941
+ kind: "roleSelect",
942
+ namespace: compiled.namespace,
943
+ paramNames: compiled.paramNames,
944
+ async handle(interaction, params) {
945
+ await config.run(new RoleSelectContext(interaction, params));
946
+ },
947
+ build(...args) {
948
+ const params = args[0] ?? {};
949
+ const builder = new discord_js.RoleSelectMenuBuilder().setCustomId(buildCustomId(compiled, params));
950
+ applySelectBase(builder, config);
951
+ return builder;
952
+ }
953
+ };
954
+ }
955
+ function channelSelect(config) {
956
+ const compiled = compilePattern(config.id);
957
+ return {
958
+ kind: "channelSelect",
959
+ namespace: compiled.namespace,
960
+ paramNames: compiled.paramNames,
961
+ async handle(interaction, params) {
962
+ await config.run(new ChannelSelectContext(interaction, params));
963
+ },
964
+ build(...args) {
965
+ const params = args[0] ?? {};
966
+ const builder = new discord_js.ChannelSelectMenuBuilder().setCustomId(buildCustomId(compiled, params));
967
+ if (config.channelTypes !== void 0) builder.setChannelTypes(...config.channelTypes);
968
+ applySelectBase(builder, config);
969
+ return builder;
970
+ }
971
+ };
972
+ }
973
+ function mentionableSelect(config) {
974
+ const compiled = compilePattern(config.id);
975
+ return {
976
+ kind: "mentionableSelect",
977
+ namespace: compiled.namespace,
978
+ paramNames: compiled.paramNames,
979
+ async handle(interaction, params) {
980
+ await config.run(new MentionableSelectContext(interaction, params));
981
+ },
982
+ build(...args) {
983
+ const params = args[0] ?? {};
984
+ const builder = new discord_js.MentionableSelectMenuBuilder().setCustomId(buildCustomId(compiled, params));
985
+ applySelectBase(builder, config);
986
+ return builder;
987
+ }
988
+ };
989
+ }
990
+ function resolveTextInputStyle(input) {
991
+ if (input === void 0) return discord_js.TextInputStyle.Short;
992
+ return typeof input === "number" ? input : discord_js.TextInputStyle[input];
993
+ }
994
+ function textInput(config) {
995
+ return {
996
+ label: config.label,
997
+ style: resolveTextInputStyle(config.style),
998
+ placeholder: config.placeholder,
999
+ required: config.required,
1000
+ minLength: config.minLength,
1001
+ maxLength: config.maxLength,
1002
+ value: config.value
1003
+ };
1004
+ }
1005
+ function buildTextInput(customId, def) {
1006
+ const input = new discord_js.TextInputBuilder().setCustomId(customId).setLabel(def.label).setStyle(def.style);
1007
+ if (def.placeholder !== void 0) input.setPlaceholder(def.placeholder);
1008
+ if (def.required !== void 0) input.setRequired(def.required);
1009
+ if (def.minLength !== void 0) input.setMinLength(def.minLength);
1010
+ if (def.maxLength !== void 0) input.setMaxLength(def.maxLength);
1011
+ if (def.value !== void 0) input.setValue(def.value);
1012
+ return input;
1013
+ }
1014
+ function modal(config) {
1015
+ const compiled = compilePattern(config.id);
1016
+ const fieldKeys = Object.keys(config.fields);
1017
+ return {
1018
+ kind: "modal",
1019
+ namespace: compiled.namespace,
1020
+ paramNames: compiled.paramNames,
1021
+ async handle(interaction, params) {
1022
+ const fields = {};
1023
+ for (const key of fieldKeys) {
1024
+ try {
1025
+ fields[key] = interaction.fields.getTextInputValue(key);
1026
+ } catch {
1027
+ fields[key] = "";
1028
+ }
1029
+ }
1030
+ await config.run(
1031
+ new ModalContext(interaction, params, fields)
1032
+ );
1033
+ },
1034
+ build(...args) {
1035
+ const params = args[0] ?? {};
1036
+ const builder = new discord_js.ModalBuilder().setCustomId(buildCustomId(compiled, params)).setTitle(config.title);
1037
+ for (const [key, def] of Object.entries(config.fields)) {
1038
+ builder.addComponents(
1039
+ new discord_js.ActionRowBuilder().addComponents(buildTextInput(key, def))
1040
+ );
1041
+ }
1042
+ return builder;
1043
+ }
1044
+ };
1045
+ }
1046
+ function row(...components) {
1047
+ return new discord_js.ActionRowBuilder().addComponents(...components);
1048
+ }
1049
+ var DEFAULT_EXTENSIONS = [".js", ".mjs", ".cjs"];
1050
+ function isRegisterable(value) {
1051
+ if (value instanceof SlashCommand) return true;
1052
+ if (typeof value !== "object" || value === null) return false;
1053
+ const record = value;
1054
+ if (typeof record["attach"] === "function" && typeof record["detach"] === "function") {
1055
+ return true;
1056
+ }
1057
+ if (typeof record["kind"] === "string" && typeof record["handle"] === "function") {
1058
+ return true;
1059
+ }
1060
+ return false;
1061
+ }
1062
+ async function collectModules(dir, options = {}) {
1063
+ const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
1064
+ const recursive = options.recursive ?? true;
1065
+ const found = [];
1066
+ const entries = await promises.readdir(dir, { withFileTypes: true });
1067
+ for (const entry of entries) {
1068
+ const fullPath = path.join(dir, entry.name);
1069
+ if (entry.isDirectory()) {
1070
+ if (recursive) found.push(...await collectModules(fullPath, options));
1071
+ continue;
1072
+ }
1073
+ if (!extensions.includes(path.extname(entry.name))) continue;
1074
+ const mod = await import(url.pathToFileURL(fullPath).href);
1075
+ for (const value of Object.values(mod)) {
1076
+ if (isRegisterable(value)) found.push(value);
1077
+ }
1078
+ }
1079
+ return found;
1080
+ }
1081
+ async function loadInto(client, dir, options) {
1082
+ const items = await collectModules(dir, options);
1083
+ client.register(...items);
1084
+ return items.length;
1085
+ }
1086
+
1087
+ // src/client.ts
1088
+ var allIntents = Object.values(discord_js.GatewayIntentBits).filter(
1089
+ (value) => typeof value === "number"
1090
+ );
1091
+ var Intents = {
1092
+ /** No intents. */
1093
+ none: [],
1094
+ /** Just `Guilds` — enough for slash commands and interactions. */
1095
+ default: [discord_js.GatewayIntentBits.Guilds],
1096
+ /** Guild + member gateway data. */
1097
+ guilds: [discord_js.GatewayIntentBits.Guilds, discord_js.GatewayIntentBits.GuildMembers],
1098
+ /** Read message content (privileged) alongside guild messages. */
1099
+ messages: [
1100
+ discord_js.GatewayIntentBits.Guilds,
1101
+ discord_js.GatewayIntentBits.GuildMessages,
1102
+ discord_js.GatewayIntentBits.MessageContent
1103
+ ],
1104
+ /** Every intent, including privileged ones. */
1105
+ all: allIntents
1106
+ };
1107
+ var SpearClient = class extends discord_js.Client {
1108
+ /** Slash command registry and dispatcher. */
1109
+ commands = new CommandRegistry();
1110
+ /** Event listener registry. */
1111
+ events = new EventRegistry();
1112
+ /** Button / select / modal registry and router. */
1113
+ components = new ComponentRegistry();
1114
+ constructor(options = {}) {
1115
+ const { intents, ...rest } = options;
1116
+ super({ ...rest, intents: intents ?? Intents.default });
1117
+ this.events.attachAll(this);
1118
+ this.on("interactionCreate", (interaction) => this.route(interaction));
1119
+ }
1120
+ /**
1121
+ * Register commands, events and components in one call. Each item is routed
1122
+ * to the matching registry based on its kind.
1123
+ */
1124
+ register(...items) {
1125
+ for (const item of items) {
1126
+ if (item instanceof SlashCommand) {
1127
+ this.commands.add(item);
1128
+ } else if ("attach" in item) {
1129
+ this.events.add(item);
1130
+ } else {
1131
+ this.components.add(item);
1132
+ }
1133
+ }
1134
+ return this;
1135
+ }
1136
+ /** Install one or more plugins, running each plugin's `setup`. */
1137
+ async use(...plugins) {
1138
+ for (const plugin of plugins) {
1139
+ await plugin.setup(this);
1140
+ }
1141
+ return this;
1142
+ }
1143
+ /**
1144
+ * Recursively load a directory and register every command, event and
1145
+ * component it exports. Returns the number of items registered.
1146
+ */
1147
+ load(dir, options) {
1148
+ return loadInto(this, dir, options);
1149
+ }
1150
+ /**
1151
+ * Log in. Falls back to the `DISCORD_TOKEN` environment variable when no
1152
+ * token is passed.
1153
+ */
1154
+ async start(token) {
1155
+ const resolved = token ?? process.env.DISCORD_TOKEN;
1156
+ if (resolved === void 0 || resolved.length === 0) {
1157
+ throw new Error("spearkit: start() needs a token (pass one or set DISCORD_TOKEN)");
1158
+ }
1159
+ await this.login(resolved);
1160
+ return this;
1161
+ }
1162
+ /**
1163
+ * Push the registered slash commands to discord using the client's own
1164
+ * authenticated REST connection. Call after the client is ready.
1165
+ */
1166
+ async deployCommands(options = {}) {
1167
+ const applicationId = this.application?.id ?? this.user?.id;
1168
+ if (applicationId == null) {
1169
+ throw new Error("spearkit: deployCommands() must run after the client is ready");
1170
+ }
1171
+ return this.commands.deploy({ rest: this.rest, applicationId, guildId: options.guildId });
1172
+ }
1173
+ async route(interaction) {
1174
+ if (interaction.isChatInputCommand()) {
1175
+ await this.commands.handle(interaction);
1176
+ } else if (interaction.isAutocomplete()) {
1177
+ await this.commands.handleAutocomplete(interaction);
1178
+ } else {
1179
+ await this.components.handle(interaction);
1180
+ }
1181
+ }
1182
+ };
1183
+
1184
+ // src/plugin.ts
1185
+ function definePlugin(plugin) {
1186
+ return plugin;
1187
+ }
1188
+
1189
+ exports.AutocompleteContext = AutocompleteContext;
1190
+ exports.BaseContext = BaseContext;
1191
+ exports.ButtonContext = ButtonContext;
1192
+ exports.ChannelSelectContext = ChannelSelectContext;
1193
+ exports.CommandContext = CommandContext;
1194
+ exports.CommandRegistry = CommandRegistry;
1195
+ exports.ComponentRegistry = ComponentRegistry;
1196
+ exports.EventRegistry = EventRegistry;
1197
+ exports.Intents = Intents;
1198
+ exports.MAX_CUSTOM_ID_LENGTH = MAX_CUSTOM_ID_LENGTH;
1199
+ exports.MentionableSelectContext = MentionableSelectContext;
1200
+ exports.MessageComponentContext = MessageComponentContext;
1201
+ exports.ModalContext = ModalContext;
1202
+ exports.RoleSelectContext = RoleSelectContext;
1203
+ exports.SlashCommand = SlashCommand;
1204
+ exports.SpearClient = SpearClient;
1205
+ exports.StringSelectContext = StringSelectContext;
1206
+ exports.UserSelectContext = UserSelectContext;
1207
+ exports.asEphemeral = asEphemeral;
1208
+ exports.buildCustomId = buildCustomId;
1209
+ exports.button = button;
1210
+ exports.channelSelect = channelSelect;
1211
+ exports.collectModules = collectModules;
1212
+ exports.command = command;
1213
+ exports.commandGroup = commandGroup;
1214
+ exports.compilePattern = compilePattern;
1215
+ exports.definePlugin = definePlugin;
1216
+ exports.event = event;
1217
+ exports.linkButton = linkButton;
1218
+ exports.loadInto = loadInto;
1219
+ exports.mentionableSelect = mentionableSelect;
1220
+ exports.modal = modal;
1221
+ exports.normalizeReply = normalizeReply;
1222
+ exports.option = option;
1223
+ exports.optionsHaveAutocomplete = optionsHaveAutocomplete;
1224
+ exports.paramsFromValues = paramsFromValues;
1225
+ exports.parseCustomId = parseCustomId;
1226
+ exports.readOption = readOption;
1227
+ exports.roleSelect = roleSelect;
1228
+ exports.row = row;
1229
+ exports.stringSelect = stringSelect;
1230
+ exports.subcommand = subcommand;
1231
+ exports.subcommandGroup = subcommandGroup;
1232
+ exports.textInput = textInput;
1233
+ exports.toAPIOption = toAPIOption;
1234
+ exports.userSelect = userSelect;
1235
+ Object.keys(discord_js).forEach(function (k) {
1236
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
1237
+ enumerable: true,
1238
+ get: function () { return discord_js[k]; }
1239
+ });
1240
+ });
1241
+ //# sourceMappingURL=index.cjs.map
1242
+ //# sourceMappingURL=index.cjs.map