@seedcord/core 0.1.0-next.6 → 0.1.0-next.8

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.
Files changed (64) hide show
  1. package/dist/HmrModuleHandler-3sNgKHdf.mjs +163 -0
  2. package/dist/HmrModuleHandler-3sNgKHdf.mjs.map +1 -0
  3. package/dist/HmrModuleHandler-CTBng7wV.cjs +170 -0
  4. package/dist/HmrModuleHandler-CTBng7wV.cjs.map +1 -0
  5. package/dist/Plugin-BZit5HUI.d.mts +555 -0
  6. package/dist/{Plugin-C2-VHrsX.cjs → Plugin-BlkhHNRm.cjs} +15 -34
  7. package/dist/Plugin-BlkhHNRm.cjs.map +1 -0
  8. package/dist/{Plugin-PcdPjAzc.mjs → Plugin-CDAV7P9l.mjs} +16 -29
  9. package/dist/Plugin-CDAV7P9l.mjs.map +1 -0
  10. package/dist/basic-1hebYVcN.d.mts +22 -0
  11. package/dist/contextMenuLeaves-f2rbyo83.d.mts +19 -0
  12. package/dist/{permissions-DSW_EZfN.cjs → cursor-BMkFAwO2.cjs} +308 -300
  13. package/dist/cursor-BMkFAwO2.cjs.map +1 -0
  14. package/dist/{permissions-RVCBhL2n.mjs → cursor-BvSH8tgw.mjs} +249 -247
  15. package/dist/cursor-BvSH8tgw.mjs.map +1 -0
  16. package/dist/{AutocompleteOptions-eKhIe6Qt.d.mts → cursor-D6nBjwaE.d.mts} +207 -116
  17. package/dist/hmr.index.cjs +2 -164
  18. package/dist/hmr.index.mjs +2 -162
  19. package/dist/index-Cr4-VV4u.d.mts +142 -0
  20. package/dist/index.cjs +362 -63
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.mts +239 -38
  23. package/dist/index.mjs +313 -26
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/internal.index.cjs +278 -83
  26. package/dist/internal.index.cjs.map +1 -1
  27. package/dist/internal.index.d.mts +111 -42
  28. package/dist/internal.index.mjs +223 -43
  29. package/dist/internal.index.mjs.map +1 -1
  30. package/dist/node-internal.index.cjs +314 -76
  31. package/dist/node-internal.index.cjs.map +1 -1
  32. package/dist/node-internal.index.d.mts +123 -2164
  33. package/dist/node-internal.index.mjs +313 -77
  34. package/dist/node-internal.index.mjs.map +1 -1
  35. package/dist/{phases-BfpAG6w3.mjs → phases-BauKJOTT.mjs} +2 -2
  36. package/dist/phases-BauKJOTT.mjs.map +1 -0
  37. package/dist/{phases-C_BGNyVo.cjs → phases-DcuDHoQl.cjs} +2 -2
  38. package/dist/phases-DcuDHoQl.cjs.map +1 -0
  39. package/dist/plugin.index.cjs +1 -1
  40. package/dist/plugin.index.d.mts +2 -2
  41. package/dist/plugin.index.mjs +1 -1
  42. package/dist/publishDefault-CVlwYUS5.cjs +382 -0
  43. package/dist/publishDefault-CVlwYUS5.cjs.map +1 -0
  44. package/dist/publishDefault-CldnxcT2.mjs +287 -0
  45. package/dist/publishDefault-CldnxcT2.mjs.map +1 -0
  46. package/dist/slashRouteLeaves-BANDtPFm.mjs +23 -0
  47. package/dist/slashRouteLeaves-BANDtPFm.mjs.map +1 -0
  48. package/dist/slashRouteLeaves-nfxOAM7Q.cjs +28 -0
  49. package/dist/slashRouteLeaves-nfxOAM7Q.cjs.map +1 -0
  50. package/dist/subscribers-BSJoyKNN.cjs +517 -0
  51. package/dist/subscribers-BSJoyKNN.cjs.map +1 -0
  52. package/dist/subscribers-DuxdM2cS.mjs +480 -0
  53. package/dist/subscribers-DuxdM2cS.mjs.map +1 -0
  54. package/package.json +7 -6
  55. package/dist/Plugin-C2-VHrsX.cjs.map +0 -1
  56. package/dist/Plugin-CNyRKDHT.d.mts +0 -226
  57. package/dist/Plugin-PcdPjAzc.mjs.map +0 -1
  58. package/dist/hmr.index.cjs.map +0 -1
  59. package/dist/hmr.index.mjs.map +0 -1
  60. package/dist/is-never-Ddaba1Su.d.mts +0 -58
  61. package/dist/permissions-DSW_EZfN.cjs.map +0 -1
  62. package/dist/permissions-RVCBhL2n.mjs.map +0 -1
  63. package/dist/phases-BfpAG6w3.mjs.map +0 -1
  64. package/dist/phases-C_BGNyVo.cjs.map +0 -1
@@ -1,45 +1,173 @@
1
+ import { d as InteractionRouteKeys, f as InteractionRoutes, g as contextMenuLeaves, i as BuilderComponent, u as InteractionMetadataKey } from "./publishDefault-CldnxcT2.mjs";
1
2
  import "reflect-metadata";
3
+ import { SlashCommandBuilder, TextDisplayBuilder } from "@discordjs/builders";
4
+ import { Logger, paint } from "@seedcord/logger";
5
+ import { routeLeavesOf } from "@seedcord/utils/internal";
6
+ import { ApplicationCommandType, PermissionFlagsBits } from "discord-api-types/v10";
2
7
  import { SeedcordErrorCode } from "@seedcord/errors";
3
- import { SeedcordError, SeedcordRangeError, SeedcordTypeError } from "@seedcord/errors/internal";
4
- import { ApplicationCommandType, InteractionContextType, PermissionFlagsBits } from "discord-api-types/v10";
5
- import { ActionRowBuilder, ButtonBuilder, ChannelSelectMenuBuilder, CheckboxBuilder, CheckboxGroupBuilder, CheckboxGroupOptionBuilder, ContainerBuilder, ContextMenuCommandBuilder, EmbedBuilder, FileBuilder, FileUploadBuilder, LabelBuilder, MediaGalleryBuilder, MentionableSelectMenuBuilder, ModalBuilder, RadioGroupBuilder, RadioGroupOptionBuilder, RoleSelectMenuBuilder, SectionBuilder, SeparatorBuilder, SlashCommandBuilder, SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, TextDisplayBuilder, TextInputBuilder, UserSelectMenuBuilder } from "@discordjs/builders";
8
+ import { SeedcordError, SeedcordRangeError } from "@seedcord/errors/internal";
6
9
  import { prettify, toEpochSeconds } from "@seedcord/utils";
7
10
 
8
- //#region src/metadataKeys.ts
9
- let InteractionRoutes = /* @__PURE__ */ function(InteractionRoutes) {
10
- InteractionRoutes["Slash"] = "slash";
11
- InteractionRoutes["Button"] = "button";
12
- InteractionRoutes["Modal"] = "modal";
13
- InteractionRoutes["StringMenu"] = "stringMenu";
14
- InteractionRoutes["UserMenu"] = "userMenu";
15
- InteractionRoutes["RoleMenu"] = "roleMenu";
16
- InteractionRoutes["ChannelMenu"] = "channelMenu";
17
- InteractionRoutes["MentionableMenu"] = "mentionableMenu";
18
- InteractionRoutes["MessageContextMenu"] = "messageContextMenu";
19
- InteractionRoutes["UserContextMenu"] = "userContextMenu";
20
- InteractionRoutes["Autocomplete"] = "autocomplete";
21
- return InteractionRoutes;
22
- }({});
23
- const InteractionMetadataKey = Symbol("seedcord:interaction:metadata");
24
- const InteractionRouteKeys = {
25
- ["slash"]: Symbol("seedcord:interaction:slash"),
26
- ["button"]: Symbol("seedcord:interaction:button"),
27
- ["modal"]: Symbol("seedcord:interaction:modal"),
28
- ["stringMenu"]: Symbol("seedcord:interaction:stringMenu"),
29
- ["userMenu"]: Symbol("seedcord:interaction:userMenu"),
30
- ["roleMenu"]: Symbol("seedcord:interaction:roleMenu"),
31
- ["channelMenu"]: Symbol("seedcord:interaction:channelMenu"),
32
- ["mentionableMenu"]: Symbol("seedcord:interaction:mentionableMenu"),
33
- ["messageContextMenu"]: Symbol("seedcord:interaction:messageContextMenu"),
34
- ["userContextMenu"]: Symbol("seedcord:interaction:userContextMenu"),
35
- ["autocomplete"]: Symbol("seedcord:interaction:autocomplete")
11
+ //#region src/miscellaneous/guarded.ts
12
+ /**
13
+ * A backing record for a generated accessor. Null-prototype, on a plain object a key named `__proto__` sets
14
+ * the store's prototype and adds no entry.
15
+ *
16
+ * @internal
17
+ */
18
+ function accessorStore() {
19
+ return Object.create(null);
20
+ }
21
+ /** Empties a store in place, keeping the reference the accessor's Proxy wraps. @internal */
22
+ function clearStore(store) {
23
+ for (const key of Object.keys(store)) Reflect.deleteProperty(store, key);
24
+ }
25
+ /**
26
+ * Wraps a generated accessor's backing record so a read of an absent key throws. Handler modules import before
27
+ * startup fills the record, and an unguarded read there returns `undefined` and renders as an empty string.
28
+ *
29
+ * @internal
30
+ */
31
+ function guardedAccessor(accessor, storage) {
32
+ return new Proxy(storage, { get(target, key, receiver) {
33
+ if (typeof key !== "string" || Object.hasOwn(target, key)) return Reflect.get(target, key, receiver);
34
+ throw new SeedcordError(SeedcordErrorCode.CoreAccessorUnresolved, [accessor, key]);
35
+ } });
36
+ }
37
+
38
+ //#endregion
39
+ //#region src/commands/CommandInjector.ts
40
+ const commandStorage = accessorStore();
41
+ const userMenuStorage = accessorStore();
42
+ const messageMenuStorage = accessorStore();
43
+ /**
44
+ * The bot's deployed slash commands, keyed by {@link SlashOptionRegistry}. Filled by
45
+ * {@link CommandInjector} after each deploy, and a read before that throws.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * import { Commands } from '@seedcord/gateway';
50
+ *
51
+ * await this.reply(`Try ${Commands['config/set'].mention}`);
52
+ *
53
+ * // a help listing
54
+ * const lines = Object.values(Commands).map((c) => `${c.mention} ${c.description}`);
55
+ * ```
56
+ */
57
+ const Commands = guardedAccessor("Commands", commandStorage);
58
+ /**
59
+ * The bot's deployed context-menu commands. Filled by {@link CommandInjector} after each deploy, and a read
60
+ * before that throws.
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * import { ContextMenus } from '@seedcord/gateway';
65
+ *
66
+ * const id = ContextMenus.user['View Profile'].id;
67
+ * ```
68
+ */
69
+ const ContextMenus = {
70
+ user: guardedAccessor("ContextMenus.user", userMenuStorage),
71
+ message: guardedAccessor("ContextMenus.message", messageMenuStorage)
72
+ };
73
+ const MULTI_GUILD = Symbol("seedcord:commands:multi-guild");
74
+ function commandKey(type, name) {
75
+ return `${type}:${name}`;
76
+ }
77
+ function slashBuildersOf(all) {
78
+ const slash = all.filter((command) => command instanceof SlashCommandBuilder);
79
+ return [...new Set(slash)];
80
+ }
81
+ /**
82
+ * Records each deployed command on {@link Commands} and {@link ContextMenus}.
83
+ *
84
+ * @internal
85
+ */
86
+ var CommandInjector = class {
87
+ logger = new Logger("Commands", { channel: "bot" });
88
+ inject(deploy, all) {
89
+ this.clear();
90
+ const ids = {
91
+ global: this.indexByKey(deploy.global),
92
+ guild: this.indexGuilds(deploy.guilds),
93
+ warned: /* @__PURE__ */ new Set()
94
+ };
95
+ let clickable = 0;
96
+ for (const command of slashBuildersOf(all)) {
97
+ const id = this.resolveId(ApplicationCommandType.ChatInput, command.name, ids, "falling back to plain text (not clickable)");
98
+ for (const leaf of routeLeavesOf(command.toJSON())) {
99
+ commandStorage[leaf.route] = {
100
+ mention: id ? this.toMention(leaf.route, id) : this.toPlain(leaf.route),
101
+ ...id && { id },
102
+ route: leaf.route,
103
+ description: leaf.description
104
+ };
105
+ if (id) clickable++;
106
+ }
107
+ }
108
+ const menus = contextMenuLeaves(all);
109
+ this.injectMenus(menus.user, ApplicationCommandType.User, userMenuStorage, ids);
110
+ this.injectMenus(menus.message, ApplicationCommandType.Message, messageMenuStorage, ids);
111
+ this.logger.utils.summary("Linked commands", {
112
+ clickable,
113
+ plain: Object.keys(commandStorage).length - clickable,
114
+ "context menus": menus.user.size + menus.message.size
115
+ });
116
+ }
117
+ injectMenus(names, kind, storage, ids) {
118
+ for (const name of names) {
119
+ const id = this.resolveId(kind, name, ids, "leaving its id unset");
120
+ storage[name] = {
121
+ ...id && { id },
122
+ name,
123
+ kind
124
+ };
125
+ }
126
+ }
127
+ indexByKey(collection) {
128
+ const map = /* @__PURE__ */ new Map();
129
+ for (const command of collection.values()) map.set(commandKey(command.type, command.name), command.id);
130
+ return map;
131
+ }
132
+ indexGuilds(guilds) {
133
+ const map = /* @__PURE__ */ new Map();
134
+ for (const collection of guilds.values()) for (const command of collection.values()) {
135
+ const key = commandKey(command.type, command.name);
136
+ const existing = map.get(key);
137
+ if (existing === void 0) map.set(key, command.id);
138
+ else if (existing !== command.id) map.set(key, MULTI_GUILD);
139
+ }
140
+ return map;
141
+ }
142
+ resolveId(type, name, ids, consequence) {
143
+ const key = commandKey(type, name);
144
+ const global = ids.global.get(key);
145
+ if (global) return global;
146
+ const guild = ids.guild.get(key);
147
+ if (guild === void 0) return void 0;
148
+ if (guild === MULTI_GUILD) {
149
+ if (!ids.warned.has(key)) {
150
+ this.logger.warn(`${paint.sky.bold(name)} is deployed to multiple guilds, ${consequence}.`);
151
+ ids.warned.add(key);
152
+ }
153
+ return;
154
+ }
155
+ return guild;
156
+ }
157
+ toMention(route, id) {
158
+ return `</${route.replaceAll("/", " ")}:${id}>`;
159
+ }
160
+ toPlain(route) {
161
+ return `/${route.replaceAll("/", " ")}`;
162
+ }
163
+ clear() {
164
+ for (const store of [
165
+ commandStorage,
166
+ userMenuStorage,
167
+ messageMenuStorage
168
+ ]) clearStore(store);
169
+ }
36
170
  };
37
- const MiddlewareMetadataKey = Symbol("seedcord:middleware:metadata");
38
- const EventMetadataKey = Symbol("seedcord:event:metadata");
39
- const CommandMetadataKey = Symbol("seedcord:command:metadata");
40
- const GatedMetadataKey = Symbol("seedcord:gated:metadata");
41
- const SubscribeMetadataKey = Symbol("seedcord:subscribe:metadata");
42
- const WebhookUrlMetadataKey = Symbol("seedcord:webhookUrl:metadata");
43
171
 
44
172
  //#endregion
45
173
  //#region src/stops/Notice.ts
@@ -107,202 +235,6 @@ var Notice = class extends Error {
107
235
  }
108
236
  };
109
237
 
110
- //#endregion
111
- //#region src/components/botColorHolder.ts
112
- const DEFAULT_COLOR = "Default";
113
- let current = DEFAULT_COLOR;
114
- function setBotColor(color) {
115
- current = color ?? DEFAULT_COLOR;
116
- }
117
- function getBotColor() {
118
- return current;
119
- }
120
-
121
- //#endregion
122
- //#region src/components/builderTypes.ts
123
- /**
124
- * Available Discord.js builder classes for use with BuilderComponent for commands, embeds, modals, etc.
125
- *
126
- * @internal
127
- */
128
- const BuilderTypes = {
129
- command: SlashCommandBuilder,
130
- context_menu: ContextMenuCommandBuilder,
131
- subcommand: SlashCommandSubcommandBuilder,
132
- group: SlashCommandSubcommandGroupBuilder,
133
- embed: EmbedBuilder,
134
- modal: ModalBuilder,
135
- label: LabelBuilder,
136
- text_input: TextInputBuilder,
137
- file_upload: FileUploadBuilder,
138
- checkbox: CheckboxBuilder,
139
- checkbox_group: CheckboxGroupBuilder,
140
- checkbox_group_option: CheckboxGroupOptionBuilder,
141
- radio_group: RadioGroupBuilder,
142
- radio_group_option: RadioGroupOptionBuilder,
143
- button: ButtonBuilder,
144
- menu_string: StringSelectMenuBuilder,
145
- menu_option_string: StringSelectMenuOptionBuilder,
146
- menu_user: UserSelectMenuBuilder,
147
- menu_channel: ChannelSelectMenuBuilder,
148
- menu_mentionable: MentionableSelectMenuBuilder,
149
- menu_role: RoleSelectMenuBuilder,
150
- container: ContainerBuilder,
151
- text_display: TextDisplayBuilder,
152
- file: FileBuilder,
153
- media: MediaGalleryBuilder,
154
- section: SectionBuilder,
155
- separator: SeparatorBuilder
156
- };
157
- /**
158
- * Available Discord.js action row classes for use with RowComponent for Select Menus and Buttons
159
- *
160
- * @internal
161
- */
162
- const RowTypes = {
163
- button: ActionRowBuilder,
164
- menu_string: ActionRowBuilder,
165
- menu_user: ActionRowBuilder,
166
- menu_channel: ActionRowBuilder,
167
- menu_mentionable: ActionRowBuilder,
168
- menu_role: ActionRowBuilder
169
- };
170
-
171
- //#endregion
172
- //#region src/components/colors.ts
173
- const Colors = {
174
- Aqua: 1752220,
175
- Blue: 3447003,
176
- Blurple: 5793266,
177
- DarkAqua: 1146986,
178
- DarkBlue: 2123412,
179
- DarkButNotBlack: 2895667,
180
- DarkerGrey: 8359053,
181
- DarkGold: 12745742,
182
- DarkGreen: 2067276,
183
- DarkGrey: 9936031,
184
- DarkNavy: 2899536,
185
- DarkOrange: 11027200,
186
- DarkPurple: 7419530,
187
- DarkRed: 10038562,
188
- DarkVividPink: 11342935,
189
- Default: 0,
190
- Fuchsia: 15418782,
191
- Gold: 15844367,
192
- Green: 5763719,
193
- Grey: 9807270,
194
- Greyple: 10070709,
195
- LightGrey: 12370112,
196
- LuminousVividPink: 15277667,
197
- Navy: 3426654,
198
- NotQuiteBlack: 2303786,
199
- Orange: 15105570,
200
- Purple: 10181046,
201
- Red: 15548997,
202
- White: 16777215,
203
- Yellow: 16705372
204
- };
205
-
206
- //#endregion
207
- //#region src/components/resolveColor.ts
208
- const MAX_COLOR = 16777215;
209
- const RED_SHIFT = 16;
210
- const GREEN_SHIFT = 8;
211
- const palette = Colors;
212
- /**
213
- * Resolves a {@link BotColor} to the integer Discord expects.
214
- *
215
- * Ported from discord.js (Apache-2.0), `resolveColor` in src/util/Util.js.
216
- */
217
- function resolveColor(color) {
218
- let resolved;
219
- if (typeof color === "string") {
220
- if (color === "Random") return Math.floor(Math.random() * 16777216);
221
- if (color === "Default") return 0;
222
- if (/^#?[\da-f]{6}$/i.test(color)) return Number.parseInt(color.replace("#", ""), 16);
223
- resolved = palette[color];
224
- } else if (typeof color === "number") resolved = color;
225
- else resolved = (color[0] << RED_SHIFT) + (color[1] << GREEN_SHIFT) + color[2];
226
- if (resolved === void 0 || !Number.isSafeInteger(resolved)) throw new SeedcordTypeError(SeedcordErrorCode.ColorUnresolvable, [JSON.stringify(color)]);
227
- if (resolved < 0 || resolved > MAX_COLOR) throw new SeedcordRangeError(SeedcordErrorCode.ColorOutOfRange);
228
- return resolved;
229
- }
230
-
231
- //#endregion
232
- //#region src/components/Component.ts
233
- /**
234
- * Base class for Discord component wrappers.
235
- *
236
- * @typeParam TComponent - The Discord.js component type being wrapped
237
- *
238
- * @internal
239
- */
240
- var BaseComponent = class {
241
- _component;
242
- constructor(ComponentClass) {
243
- this._component = new ComponentClass();
244
- }
245
- /**
246
- * The wrapped builder, for calling Discord.js methods like setTitle() and setDescription() inside a subclass.
247
- *
248
- * @example this.instance.setTitle('My Modal')
249
- */
250
- get instance() {
251
- return this._component;
252
- }
253
- };
254
- /**
255
- * Base class for Discord.js builder components
256
- *
257
- * Wraps Discord.js builders (SlashCommandBuilder, EmbedBuilder, etc.) with
258
- * Seedcord-specific defaults and helper methods.
259
- *
260
- * @typeParam BuilderKey - The type of Discord.js builder being wrapped
261
- */
262
- var BuilderComponent = class extends BaseComponent {
263
- type;
264
- colorApplied = false;
265
- constructor(type) {
266
- const ComponentClass = BuilderTypes[type];
267
- super(ComponentClass);
268
- this.type = type;
269
- if (this.instance instanceof SlashCommandBuilder || this.instance instanceof ContextMenuCommandBuilder) this.instance.setContexts(InteractionContextType.Guild);
270
- }
271
- get component() {
272
- this.applyBotColor();
273
- return this.instance;
274
- }
275
- applyBotColor() {
276
- if (this.colorApplied) return;
277
- this.colorApplied = true;
278
- const color = getBotColor();
279
- if (this.instance instanceof EmbedBuilder) {
280
- if (this.instance.data.color === void 0) this.instance.setColor(resolveColor(color));
281
- } else if (this.instance instanceof ContainerBuilder) {
282
- const accent = this.instance.data.accent_color;
283
- if (accent === null || accent === void 0) this.instance.setAccentColor(color === "Default" ? void 0 : resolveColor(color));
284
- }
285
- }
286
- };
287
- /**
288
- * Base class for Discord action row components
289
- *
290
- * Wraps Discord.js action row builder with Seedcord-specific defaults and helper methods.
291
- *
292
- * @typeParam RowKey - The Discord.js action row type being wrapped
293
- */
294
- var RowComponent = class extends BaseComponent {
295
- type;
296
- constructor(type) {
297
- const ComponentClass = RowTypes[type];
298
- super(ComponentClass);
299
- this.type = type;
300
- }
301
- get component() {
302
- return this.instance;
303
- }
304
- };
305
-
306
238
  //#endregion
307
239
  //#region src/stops/NoticeCard.ts
308
240
  /**
@@ -955,6 +887,21 @@ var MissingPermissions = class extends Notice {
955
887
  return { components: [new NoticeCard(`${this.customLead ?? `${this.subject === null ? "You are" : `${this.subject} is`} missing the following permission entries:`}\n\n${bullets}`).component] };
956
888
  }
957
889
  };
890
+ var HasDangerousPermissions = class extends Notice {
891
+ subject;
892
+ dangerousPerms;
893
+ customLead;
894
+ constructor(message, subject, dangerousPerms) {
895
+ super(message ?? "A dangerous permission is enabled.");
896
+ this.subject = subject;
897
+ this.dangerousPerms = dangerousPerms;
898
+ this.customLead = message;
899
+ }
900
+ render() {
901
+ const bullets = this.dangerousPerms.map((perm) => `• ${perm}`).join("\n");
902
+ return { components: [new NoticeCard(`${this.customLead ?? `${this.subject} has the following permission entries that must not be enabled:`}\n\n${bullets}`).component] };
903
+ }
904
+ };
958
905
 
959
906
  //#endregion
960
907
  //#region src/gates/effects.ts
@@ -995,10 +942,37 @@ function pickNotice(options, makeDefault) {
995
942
  }
996
943
 
997
944
  //#endregion
998
- //#region src/gates/catalog/permissions.ts
999
- /** Permission bit to its prettified human-readable name, for the missing-permission refusal. */
945
+ //#region src/permissions/bits.ts
946
+ /** Permission bit to its prettified human-readable name. */
1000
947
  const PermissionNames = new Map(Object.entries(PermissionFlagsBits).map(([key, bit]) => [bit, prettify(key)]));
1001
948
  const ADMINISTRATOR = PermissionFlagsBits.Administrator;
949
+ function nameOf(bit) {
950
+ return PermissionNames.get(bit) ?? String(bit);
951
+ }
952
+ function holds(held, bit) {
953
+ return (held & ADMINISTRATOR) === ADMINISTRATOR || (held & bit) === bit;
954
+ }
955
+ /** @internal */
956
+ function hasAll(held, scope) {
957
+ return scope.every((bit) => holds(held, bit));
958
+ }
959
+ /** @internal only reached once hasAll failed, so the administrator bit is absent */
960
+ function missingNames(held, scope) {
961
+ return scope.reduce((names, bit) => {
962
+ if ((held & bit) !== bit) names.push(nameOf(bit));
963
+ return names;
964
+ }, []);
965
+ }
966
+ /** @internal */
967
+ function presentNames(held, scope) {
968
+ return scope.reduce((names, bit) => {
969
+ if (holds(held, bit)) names.push(nameOf(bit));
970
+ return names;
971
+ }, []);
972
+ }
973
+
974
+ //#endregion
975
+ //#region src/gates/catalog/permissions.ts
1002
976
  const MEMBER_CHECK = {
1003
977
  unresolved: "Your server member data could not be resolved. Try again.",
1004
978
  subject: null
@@ -1007,16 +981,6 @@ const BOT_CHECK = {
1007
981
  unresolved: "The bot's permissions could not be resolved. Try again.",
1008
982
  subject: "The bot"
1009
983
  };
1010
- function hasAll(held, scope) {
1011
- if ((held & ADMINISTRATOR) === ADMINISTRATOR) return true;
1012
- return scope.every((bit) => (held & bit) === bit);
1013
- }
1014
- function missingNames(held, scope) {
1015
- return scope.reduce((names, bit) => {
1016
- if ((held & bit) !== bit) names.push(PermissionNames.get(bit) ?? String(bit));
1017
- return names;
1018
- }, []);
1019
- }
1020
984
  function refusePermissions(held, scope, options, guildId, kind) {
1021
985
  if (held === null) throw pickNotice(options?.notInGuild, (message) => new NotInGuild(message ?? (guildId ? kind.unresolved : void 0)));
1022
986
  if (hasAll(held, scope)) return;
@@ -1067,5 +1031,43 @@ function RequireRole(roleId, options) {
1067
1031
  }
1068
1032
 
1069
1033
  //#endregion
1070
- export { decodeComponentRoute as A, EventMetadataKey as B, areRoutes as C, storeComponentRoute as D, selectMenuRouteOf as E, BuilderComponent as F, MiddlewareMetadataKey as G, InteractionMetadataKey as H, RowComponent as I, SubscribeMetadataKey as K, setBotColor as L, decodeFor as M, prefixOf as N, storeInteractionRoute as O, NoticeCard as P, Notice as R, SelectMenuKind as S, interactionRoutesOf as T, InteractionRouteKeys as U, GatedMetadataKey as V, InteractionRoutes as W, NotInGuild as _, pickNotice as a, defineEffectGate as b, rollbackCommits as c, GateNotice as d, MissingPermissions as f, NotInDm as g, NotAllowed as h, RequireRole as i, CustomId as j, ComponentDefsKey as k, runCheck as l, NeedsAny as m, RequireBotPermissions as n, discardCommits as o, MissingRole as p, WebhookUrlMetadataKey as q, RequirePermissions as r, markCommits as s, PermissionNames as t, runCommits as u, NotOwner as v, contextMenuRouteOf as w, defineGate as x, OnCooldown as y, CommandMetadataKey as z };
1071
- //# sourceMappingURL=permissions-RVCBhL2n.mjs.map
1034
+ //#region src/stops/Silence.ts
1035
+ /**
1036
+ * Throw to stop a handler with no reply and no report.
1037
+ *
1038
+ * The framework boundary catches `Silence` before {@link Notice}, makes zero Discord calls, and stops.
1039
+ * Ideally you'd only throw this in `EventHandlers` (or `Gates` for those), because it doesn't make sense to
1040
+ * leave the user with no reply for an interaction.
1041
+ *
1042
+ * @example
1043
+ * ```ts
1044
+ * import { Silence } from '@seedcord/gateway';
1045
+ *
1046
+ * // before any reply or defer, drop the interaction with no reply and no report
1047
+ * if (await isBlacklisted(interaction.user.id)) throw new Silence('blacklisted user');
1048
+ * ```
1049
+ */
1050
+ var Silence = class extends Error {
1051
+ reason;
1052
+ /**
1053
+ * @param reason - Optional note written only to a debug log, never shown to the user or reported.
1054
+ */
1055
+ constructor(reason) {
1056
+ super(reason ?? "Silence");
1057
+ this.reason = reason;
1058
+ Error.captureStackTrace(this, this.constructor);
1059
+ }
1060
+ };
1061
+
1062
+ //#endregion
1063
+ //#region src/pagination/cursor.ts
1064
+ const PAGE_MAX = 1e6;
1065
+ const SLOT_MAX = 4;
1066
+ /** Build the page cursor for a paginator. */
1067
+ function pageCursor(prefix) {
1068
+ return new CustomId(prefix).int("page", 0, PAGE_MAX).int("slot", 0, SLOT_MAX);
1069
+ }
1070
+
1071
+ //#endregion
1072
+ export { areRoutes as A, prefixOf as B, NotInDm as C, defineEffectGate as D, OnCooldown as E, storeInteractionRoute as F, ContextMenus as G, Notice as H, ComponentDefsKey as I, guardedAccessor as J, accessorStore as K, decodeComponentRoute as L, interactionRoutesOf as M, selectMenuRouteOf as N, defineGate as O, storeComponentRoute as P, CustomId as R, NotAllowed as S, NotOwner as T, CommandInjector as U, NoticeCard as V, Commands as W, GateNotice as _, RequirePermissions as a, MissingRole as b, hasAll as c, pickNotice as d, discardCommits as f, runCommits as g, runCheck as h, RequireBotPermissions as i, contextMenuRouteOf as j, SelectMenuKind as k, missingNames as l, rollbackCommits as m, pageCursor as n, RequireRole as o, markCommits as p, clearStore as q, Silence as r, PermissionNames as s, PAGE_MAX as t, presentNames as u, HasDangerousPermissions as v, NotInGuild as w, NeedsAny as x, MissingPermissions as y, decodeFor as z };
1073
+ //# sourceMappingURL=cursor-BvSH8tgw.mjs.map