@seedcord/core 0.1.0-next.7 → 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.
Files changed (59) hide show
  1. package/dist/{Plugin-3rlH0uxH.d.mts → Plugin-BZit5HUI.d.mts} +52 -117
  2. package/dist/{Plugin-Cgp1a0uQ.cjs → Plugin-BlkhHNRm.cjs} +6 -34
  3. package/dist/Plugin-BlkhHNRm.cjs.map +1 -0
  4. package/dist/{Plugin-j9GYdroS.mjs → Plugin-CDAV7P9l.mjs} +7 -29
  5. package/dist/Plugin-CDAV7P9l.mjs.map +1 -0
  6. package/dist/basic-1hebYVcN.d.mts +22 -0
  7. package/dist/contextMenuLeaves-f2rbyo83.d.mts +19 -0
  8. package/dist/{Silence-DiP95iwD.cjs → cursor-BMkFAwO2.cjs} +293 -16
  9. package/dist/cursor-BMkFAwO2.cjs.map +1 -0
  10. package/dist/{Silence-dbC2slOB.mjs → cursor-BvSH8tgw.mjs} +222 -17
  11. package/dist/cursor-BvSH8tgw.mjs.map +1 -0
  12. package/dist/{AutocompleteOptions-DLp_B8as.d.mts → cursor-j5bRcn2O.d.mts} +166 -24
  13. package/dist/{index-Dueqztyy.d.mts → index-DafutfX8.d.mts} +8 -15
  14. package/dist/index.cjs +362 -40
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.mts +244 -44
  17. package/dist/index.mjs +322 -7
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/internal.index.cjs +50 -43
  20. package/dist/internal.index.cjs.map +1 -1
  21. package/dist/internal.index.d.mts +46 -41
  22. package/dist/internal.index.mjs +13 -14
  23. package/dist/internal.index.mjs.map +1 -1
  24. package/dist/node-internal.index.cjs +196 -29
  25. package/dist/node-internal.index.cjs.map +1 -1
  26. package/dist/node-internal.index.d.mts +59 -9
  27. package/dist/node-internal.index.mjs +196 -30
  28. package/dist/node-internal.index.mjs.map +1 -1
  29. package/dist/{phases-BfpAG6w3.mjs → phases-BauKJOTT.mjs} +2 -2
  30. package/dist/phases-BauKJOTT.mjs.map +1 -0
  31. package/dist/{phases-C_BGNyVo.cjs → phases-DcuDHoQl.cjs} +2 -2
  32. package/dist/phases-DcuDHoQl.cjs.map +1 -0
  33. package/dist/plugin.index.cjs +1 -1
  34. package/dist/plugin.index.d.mts +2 -2
  35. package/dist/plugin.index.mjs +1 -1
  36. package/dist/{publishDefault-l0NQ-SHo.cjs → publishDefault-CVlwYUS5.cjs} +32 -3
  37. package/dist/publishDefault-CVlwYUS5.cjs.map +1 -0
  38. package/dist/{publishDefault-CCf1azzW.mjs → publishDefault-CldnxcT2.mjs} +27 -4
  39. package/dist/publishDefault-CldnxcT2.mjs.map +1 -0
  40. package/dist/slashRouteLeaves-BANDtPFm.mjs +23 -0
  41. package/dist/slashRouteLeaves-BANDtPFm.mjs.map +1 -0
  42. package/dist/slashRouteLeaves-nfxOAM7Q.cjs +28 -0
  43. package/dist/slashRouteLeaves-nfxOAM7Q.cjs.map +1 -0
  44. package/dist/{subscribers-DYqFVP1X.mjs → subscribers-CmDKqC7y.mjs} +5 -5
  45. package/dist/subscribers-CmDKqC7y.mjs.map +1 -0
  46. package/dist/{subscribers-B1G6_LlK.cjs → subscribers-CojcCgj1.cjs} +5 -5
  47. package/dist/subscribers-CojcCgj1.cjs.map +1 -0
  48. package/package.json +8 -8
  49. package/dist/Plugin-Cgp1a0uQ.cjs.map +0 -1
  50. package/dist/Plugin-j9GYdroS.mjs.map +0 -1
  51. package/dist/Silence-DiP95iwD.cjs.map +0 -1
  52. package/dist/Silence-dbC2slOB.mjs.map +0 -1
  53. package/dist/basic-D7bDlgSP.d.mts +0 -10
  54. package/dist/phases-BfpAG6w3.mjs.map +0 -1
  55. package/dist/phases-C_BGNyVo.cjs.map +0 -1
  56. package/dist/publishDefault-CCf1azzW.mjs.map +0 -1
  57. package/dist/publishDefault-l0NQ-SHo.cjs.map +0 -1
  58. package/dist/subscribers-B1G6_LlK.cjs.map +0 -1
  59. package/dist/subscribers-DYqFVP1X.mjs.map +0 -1
@@ -1,8 +1,31 @@
1
+ let _discordjs_builders = require("@discordjs/builders");
2
+ let discord_api_types_v10 = require("discord-api-types/v10");
1
3
  let _seedcord_errors = require("@seedcord/errors");
2
4
  let _seedcord_errors_internal = require("@seedcord/errors/internal");
3
- let discord_api_types_v10 = require("discord-api-types/v10");
4
- let _discordjs_builders = require("@discordjs/builders");
5
5
 
6
+ //#region src/commands/contextMenuLeaves.ts
7
+ /**
8
+ * Collects the names of every registered context-menu command across a set of command builders, deduplicated
9
+ * because the same builder appears once per guild. A user and a message command can share a name, so the two
10
+ * kinds stay in separate sets.
11
+ *
12
+ * @internal
13
+ */
14
+ function contextMenuLeaves(commands) {
15
+ const user = /* @__PURE__ */ new Set();
16
+ const message = /* @__PURE__ */ new Set();
17
+ for (const command of commands) {
18
+ if (!(command instanceof _discordjs_builders.ContextMenuCommandBuilder)) continue;
19
+ if (command.type === discord_api_types_v10.ApplicationCommandType.User) user.add(command.name);
20
+ else message.add(command.name);
21
+ }
22
+ return {
23
+ user,
24
+ message
25
+ };
26
+ }
27
+
28
+ //#endregion
6
29
  //#region src/metadataKeys.ts
7
30
  let InteractionRoutes = /* @__PURE__ */ function(InteractionRoutes) {
8
31
  InteractionRoutes["Slash"] = "slash";
@@ -332,6 +355,12 @@ Object.defineProperty(exports, 'WebhookUrlMetadataKey', {
332
355
  return WebhookUrlMetadataKey;
333
356
  }
334
357
  });
358
+ Object.defineProperty(exports, 'contextMenuLeaves', {
359
+ enumerable: true,
360
+ get: function () {
361
+ return contextMenuLeaves;
362
+ }
363
+ });
335
364
  Object.defineProperty(exports, 'deferFlags', {
336
365
  enumerable: true,
337
366
  get: function () {
@@ -350,4 +379,4 @@ Object.defineProperty(exports, 'setBotColor', {
350
379
  return setBotColor;
351
380
  }
352
381
  });
353
- //# sourceMappingURL=publishDefault-l0NQ-SHo.cjs.map
382
+ //# sourceMappingURL=publishDefault-CVlwYUS5.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publishDefault-CVlwYUS5.cjs","names":["ContextMenuCommandBuilder","ApplicationCommandType","SlashCommandBuilder","ContextMenuCommandBuilder","SlashCommandSubcommandBuilder","SlashCommandSubcommandGroupBuilder","EmbedBuilder","ModalBuilder","LabelBuilder","TextInputBuilder","FileUploadBuilder","CheckboxBuilder","CheckboxGroupBuilder","CheckboxGroupOptionBuilder","RadioGroupBuilder","RadioGroupOptionBuilder","ButtonBuilder","StringSelectMenuBuilder","StringSelectMenuOptionBuilder","UserSelectMenuBuilder","ChannelSelectMenuBuilder","MentionableSelectMenuBuilder","RoleSelectMenuBuilder","ContainerBuilder","TextDisplayBuilder","FileBuilder","MediaGalleryBuilder","SectionBuilder","SeparatorBuilder","ActionRowBuilder","SeedcordTypeError","SeedcordErrorCode","SeedcordRangeError","SlashCommandBuilder","ContextMenuCommandBuilder","InteractionContextType","EmbedBuilder","ContainerBuilder","MessageFlags"],"sources":["../src/commands/contextMenuLeaves.ts","../src/metadataKeys.ts","../src/components/botColorHolder.ts","../src/components/builderTypes.ts","../src/components/colors.ts","../src/components/resolveColor.ts","../src/components/Component.ts","../src/reply/flags.ts","../src/subscribers/publishDefault.ts"],"sourcesContent":["import { ContextMenuCommandBuilder } from '@discordjs/builders';\nimport { ApplicationCommandType } from 'discord-api-types/v10';\n\nimport type { SlashCommandBuilder } from '@discordjs/builders';\n\n/** The registered context-menu command names, split by kind. @internal */\nexport interface ContextMenuLeaves {\n readonly user: Set<string>;\n readonly message: Set<string>;\n}\n\n/**\n * Collects the names of every registered context-menu command across a set of command builders, deduplicated\n * because the same builder appears once per guild. A user and a message command can share a name, so the two\n * kinds stay in separate sets.\n *\n * @internal\n */\nexport function contextMenuLeaves(\n commands: Iterable<SlashCommandBuilder | ContextMenuCommandBuilder>\n): ContextMenuLeaves {\n const user = new Set<string>();\n const message = new Set<string>();\n for (const command of commands) {\n if (!(command instanceof ContextMenuCommandBuilder)) continue;\n if (command.type === ApplicationCommandType.User) user.add(command.name);\n else message.add(command.name);\n }\n return { user, message };\n}\n","export enum InteractionRoutes {\n Slash = 'slash',\n Button = 'button',\n Modal = 'modal',\n StringMenu = 'stringMenu',\n UserMenu = 'userMenu',\n RoleMenu = 'roleMenu',\n ChannelMenu = 'channelMenu',\n MentionableMenu = 'mentionableMenu',\n MessageContextMenu = 'messageContextMenu',\n UserContextMenu = 'userContextMenu',\n Autocomplete = 'autocomplete'\n}\n\nexport const InteractionMetadataKey = Symbol('seedcord:interaction:metadata');\n\nexport const InteractionRouteKeys: Record<InteractionRoutes, symbol> = {\n [InteractionRoutes.Slash]: Symbol('seedcord:interaction:slash'),\n [InteractionRoutes.Button]: Symbol('seedcord:interaction:button'),\n [InteractionRoutes.Modal]: Symbol('seedcord:interaction:modal'),\n [InteractionRoutes.StringMenu]: Symbol('seedcord:interaction:stringMenu'),\n [InteractionRoutes.UserMenu]: Symbol('seedcord:interaction:userMenu'),\n [InteractionRoutes.RoleMenu]: Symbol('seedcord:interaction:roleMenu'),\n [InteractionRoutes.ChannelMenu]: Symbol('seedcord:interaction:channelMenu'),\n [InteractionRoutes.MentionableMenu]: Symbol('seedcord:interaction:mentionableMenu'),\n [InteractionRoutes.MessageContextMenu]: Symbol('seedcord:interaction:messageContextMenu'),\n [InteractionRoutes.UserContextMenu]: Symbol('seedcord:interaction:userContextMenu'),\n [InteractionRoutes.Autocomplete]: Symbol('seedcord:interaction:autocomplete')\n};\n\nexport const MiddlewareMetadataKey = Symbol('seedcord:middleware:metadata');\n\nexport const EventMetadataKey = Symbol('seedcord:event:metadata');\n\nexport const CommandMetadataKey = Symbol('seedcord:command:metadata');\n\nexport const GatedMetadataKey = Symbol('seedcord:gated:metadata');\n\nexport const SubscribeMetadataKey = Symbol('seedcord:subscribe:metadata');\n\nexport const WebhookUrlMetadataKey = Symbol('seedcord:webhookUrl:metadata');\n","import type { BotColor } from '@seedcord/types';\n\n// must be 'Default' so the container guard in applyBotColor leaves the accent unset\nconst DEFAULT_COLOR: BotColor = 'Default';\n\nlet current: BotColor = DEFAULT_COLOR;\n\nexport function setBotColor(color: BotColor | undefined): void {\n current = color ?? DEFAULT_COLOR;\n}\n\nexport function getBotColor(): BotColor {\n return current;\n}\n","import {\n ActionRowBuilder,\n ButtonBuilder,\n ChannelSelectMenuBuilder,\n CheckboxBuilder,\n CheckboxGroupBuilder,\n CheckboxGroupOptionBuilder,\n ContainerBuilder,\n ContextMenuCommandBuilder,\n EmbedBuilder,\n FileBuilder,\n FileUploadBuilder,\n LabelBuilder,\n MediaGalleryBuilder,\n MentionableSelectMenuBuilder,\n ModalBuilder,\n RadioGroupBuilder,\n RadioGroupOptionBuilder,\n RoleSelectMenuBuilder,\n SectionBuilder,\n SeparatorBuilder,\n SlashCommandBuilder,\n SlashCommandSubcommandBuilder,\n SlashCommandSubcommandGroupBuilder,\n StringSelectMenuBuilder,\n StringSelectMenuOptionBuilder,\n TextDisplayBuilder,\n TextInputBuilder,\n UserSelectMenuBuilder\n} from '@discordjs/builders';\n\n/**\n * Available Discord.js builder classes for use with BuilderComponent for commands, embeds, modals, etc.\n *\n * @internal\n */\nexport const BuilderTypes = {\n // Command Components\n command: SlashCommandBuilder,\n context_menu: ContextMenuCommandBuilder,\n subcommand: SlashCommandSubcommandBuilder,\n group: SlashCommandSubcommandGroupBuilder,\n\n // Embed Components\n embed: EmbedBuilder,\n\n // Modal Components\n modal: ModalBuilder,\n label: LabelBuilder,\n text_input: TextInputBuilder,\n file_upload: FileUploadBuilder,\n checkbox: CheckboxBuilder,\n checkbox_group: CheckboxGroupBuilder,\n checkbox_group_option: CheckboxGroupOptionBuilder,\n radio_group: RadioGroupBuilder,\n radio_group_option: RadioGroupOptionBuilder,\n\n // Action Row Components\n button: ButtonBuilder,\n menu_string: StringSelectMenuBuilder,\n menu_option_string: StringSelectMenuOptionBuilder,\n menu_user: UserSelectMenuBuilder,\n menu_channel: ChannelSelectMenuBuilder,\n menu_mentionable: MentionableSelectMenuBuilder,\n menu_role: RoleSelectMenuBuilder,\n\n // ComponentsV2\n container: ContainerBuilder,\n text_display: TextDisplayBuilder,\n file: FileBuilder,\n media: MediaGalleryBuilder,\n section: SectionBuilder,\n separator: SeparatorBuilder\n};\n\n/**\n * Available Discord.js action row classes for use with RowComponent for Select Menus and Buttons\n *\n * @internal\n */\nexport const RowTypes: {\n button: typeof ActionRowBuilder<ButtonBuilder>;\n menu_string: typeof ActionRowBuilder<StringSelectMenuBuilder>;\n menu_user: typeof ActionRowBuilder<UserSelectMenuBuilder>;\n menu_channel: typeof ActionRowBuilder<ChannelSelectMenuBuilder>;\n menu_mentionable: typeof ActionRowBuilder<MentionableSelectMenuBuilder>;\n menu_role: typeof ActionRowBuilder<RoleSelectMenuBuilder>;\n} = {\n button: ActionRowBuilder<ButtonBuilder>,\n menu_string: ActionRowBuilder<StringSelectMenuBuilder>,\n menu_user: ActionRowBuilder<UserSelectMenuBuilder>,\n menu_channel: ActionRowBuilder<ChannelSelectMenuBuilder>,\n menu_mentionable: ActionRowBuilder<MentionableSelectMenuBuilder>,\n menu_role: ActionRowBuilder<RoleSelectMenuBuilder>\n};\n\n/**\n * Available Discord.js builder types for use with BuilderComponent\n */\nexport type BuilderType = keyof typeof BuilderTypes;\n\n/**\n * @internal\n */\nexport type InstantiatedBuilder<BuilderKey extends BuilderType> = InstanceType<(typeof BuilderTypes)[BuilderKey]>;\n\n/**\n * Available Discord.js action row types for use with RowComponent\n */\nexport type RowType = keyof typeof RowTypes;\n\n/**\n * @internal\n */\nexport type InstantiatedActionRow<RowKey extends RowType> = InstanceType<(typeof RowTypes)[RowKey]>;\n","import type { ColorName, TypedOmit } from '@seedcord/types';\n\n// Ported verbatim from discord.js (Apache-2.0), src/util/Colors.js.\nexport const Colors: TypedOmit<Record<ColorName, number>, 'Random'> = {\n Aqua: 0x1a_bc_9c,\n Blue: 0x34_98_db,\n Blurple: 0x58_65_f2,\n DarkAqua: 0x11_80_6a,\n DarkBlue: 0x20_66_94,\n DarkButNotBlack: 0x2c_2f_33,\n DarkerGrey: 0x7f_8c_8d,\n DarkGold: 0xc2_7c_0e,\n DarkGreen: 0x1f_8b_4c,\n DarkGrey: 0x97_9c_9f,\n DarkNavy: 0x2c_3e_50,\n DarkOrange: 0xa8_43_00,\n DarkPurple: 0x71_36_8a,\n DarkRed: 0x99_2d_22,\n DarkVividPink: 0xad_14_57,\n Default: 0x00_00_00,\n Fuchsia: 0xeb_45_9e,\n Gold: 0xf1_c4_0f,\n Green: 0x57_f2_87,\n Grey: 0x95_a5_a6,\n Greyple: 0x99_aa_b5,\n LightGrey: 0xbc_c0_c0,\n LuminousVividPink: 0xe9_1e_63,\n Navy: 0x34_49_5e,\n NotQuiteBlack: 0x23_27_2a,\n Orange: 0xe6_7e_22,\n Purple: 0x9b_59_b6,\n Red: 0xed_42_45,\n White: 0xff_ff_ff,\n Yellow: 0xfe_e7_5c\n} as const;\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordRangeError, SeedcordTypeError } from '@seedcord/errors/internal';\n\nimport { Colors } from './colors';\n\nimport type { BotColor } from '@seedcord/types';\n\nconst MAX_COLOR = 0xff_ff_ff;\nconst RED_SHIFT = 16;\nconst GREEN_SHIFT = 8;\n\nconst palette: Record<string, number | undefined> = Colors;\n\n/**\n * Resolves a {@link BotColor} to the integer Discord expects.\n *\n * Ported from discord.js (Apache-2.0), `resolveColor` in src/util/Util.js.\n */\nexport function resolveColor(color: BotColor): number {\n let resolved: number | undefined;\n\n if (typeof color === 'string') {\n if (color === 'Random') return Math.floor(Math.random() * (MAX_COLOR + 1));\n if (color === 'Default') return 0;\n if (/^#?[\\da-f]{6}$/i.test(color)) return Number.parseInt(color.replace('#', ''), 16);\n resolved = palette[color];\n } else if (typeof color === 'number') {\n resolved = color;\n } else {\n resolved = (color[0] << RED_SHIFT) + (color[1] << GREEN_SHIFT) + color[2];\n }\n\n if (resolved === undefined || !Number.isSafeInteger(resolved)) {\n throw new SeedcordTypeError(SeedcordErrorCode.ColorUnresolvable, [JSON.stringify(color)]);\n }\n if (resolved < 0 || resolved > MAX_COLOR) {\n throw new SeedcordRangeError(SeedcordErrorCode.ColorOutOfRange);\n }\n\n return resolved;\n}\n","import { ContainerBuilder, ContextMenuCommandBuilder, EmbedBuilder, SlashCommandBuilder } from '@discordjs/builders';\nimport { InteractionContextType } from 'discord-api-types/v10';\n\nimport { getBotColor } from './botColorHolder';\nimport { BuilderTypes, RowTypes } from './builderTypes';\nimport { resolveColor } from './resolveColor';\n\nimport type { BuilderType, InstantiatedActionRow, InstantiatedBuilder, RowType } from './builderTypes';\n\n/**\n * Base class for Discord component wrappers.\n *\n * @typeParam TComponent - The Discord.js component type being wrapped\n *\n * @internal\n */\nabstract class BaseComponent<TComponent> {\n private readonly _component: TComponent;\n\n protected constructor(ComponentClass: new () => TComponent) {\n this._component = new ComponentClass();\n }\n\n /**\n * Returns the live builder, ready to send in a Discord message or nest in another component.\n *\n * Configure it through `this.instance`. Reading this can apply the bot color (see\n * {@link BuilderComponent}), so it has a side effect.\n * @example new SomeComponent().component\n */\n public abstract get component(): InstantiatedBuilder<BuilderType> | InstantiatedActionRow<RowType>;\n\n /**\n * The wrapped builder, for calling Discord.js methods like setTitle() and setDescription() inside a subclass.\n *\n * @example this.instance.setTitle('My Modal')\n */\n protected get instance(): TComponent {\n return this._component;\n }\n}\n\n/**\n * Base class for Discord.js builder components\n *\n * Wraps Discord.js builders (SlashCommandBuilder, EmbedBuilder, etc.) with\n * Seedcord-specific defaults and helper methods.\n *\n * @typeParam BuilderKey - The type of Discord.js builder being wrapped\n */\nexport abstract class BuilderComponent<BuilderKey extends BuilderType> extends BaseComponent<\n InstantiatedBuilder<BuilderKey>\n> {\n private colorApplied = false;\n\n protected constructor(public readonly type: BuilderKey) {\n const ComponentClass = BuilderTypes[type] as unknown;\n super(ComponentClass as new () => InstantiatedBuilder<BuilderKey>);\n\n if (this.instance instanceof SlashCommandBuilder || this.instance instanceof ContextMenuCommandBuilder) {\n this.instance.setContexts(InteractionContextType.Guild);\n }\n }\n\n get component(): InstantiatedBuilder<BuilderKey> {\n this.applyBotColor();\n return this.instance;\n }\n\n // Resolving in the constructor would capture the default for a component built before setBotColor()\n // ran. The unset check keeps a color the subclass set for itself.\n private applyBotColor(): void {\n if (this.colorApplied) return;\n this.colorApplied = true;\n\n const color = getBotColor();\n if (this.instance instanceof EmbedBuilder) {\n if (this.instance.data.color === undefined) this.instance.setColor(resolveColor(color));\n } else if (this.instance instanceof ContainerBuilder) {\n const accent = this.instance.data.accent_color;\n if (accent === null || accent === undefined) {\n this.instance.setAccentColor(color === 'Default' ? undefined : resolveColor(color));\n }\n }\n }\n}\n\n/**\n * Base class for Discord action row components\n *\n * Wraps Discord.js action row builder with Seedcord-specific defaults and helper methods.\n *\n * @typeParam RowKey - The Discord.js action row type being wrapped\n */\nexport abstract class RowComponent<RowKey extends RowType> extends BaseComponent<InstantiatedActionRow<RowKey>> {\n protected constructor(public readonly type: RowKey) {\n const ComponentClass = RowTypes[type] as unknown;\n super(ComponentClass as new () => InstantiatedActionRow<RowKey>);\n }\n\n get component(): InstantiatedActionRow<RowKey> {\n return this.instance;\n }\n}\n","import { MessageFlags } from 'discord-api-types/v10';\n\nimport type { DeferOpts, SendOpts } from '@seedcord/types';\n\n/** IsComponentsV2 is unconditional, every framework reply is v2. */\nexport function sendFlags(opts?: SendOpts): number {\n let flags = MessageFlags.IsComponentsV2;\n if (opts?.ephemeral ?? true) flags |= MessageFlags.Ephemeral;\n if (opts?.silent) flags |= MessageFlags.SuppressNotifications;\n return flags;\n}\n\n/** A deferral carries no content, so only Ephemeral applies. */\nexport function deferFlags(opts?: DeferOpts): number {\n return (opts?.ephemeral ?? true) ? MessageFlags.Ephemeral : 0;\n}\n","/**\n * The key of the Bus method that emits the framework's own subscriptions. `publish` excludes these keys\n * through `PublishableKey`, and this symbol is exported only from the internal entry.\n *\n * @internal\n */\nexport const PublishDefault = Symbol('seedcord.publishDefault');\n"],"mappings":"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"}
@@ -1,8 +1,31 @@
1
+ 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";
2
+ import { ApplicationCommandType, InteractionContextType, MessageFlags } from "discord-api-types/v10";
1
3
  import { SeedcordErrorCode } from "@seedcord/errors";
2
4
  import { SeedcordRangeError, SeedcordTypeError } from "@seedcord/errors/internal";
3
- import { InteractionContextType, MessageFlags } from "discord-api-types/v10";
4
- 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";
5
5
 
6
+ //#region src/commands/contextMenuLeaves.ts
7
+ /**
8
+ * Collects the names of every registered context-menu command across a set of command builders, deduplicated
9
+ * because the same builder appears once per guild. A user and a message command can share a name, so the two
10
+ * kinds stay in separate sets.
11
+ *
12
+ * @internal
13
+ */
14
+ function contextMenuLeaves(commands) {
15
+ const user = /* @__PURE__ */ new Set();
16
+ const message = /* @__PURE__ */ new Set();
17
+ for (const command of commands) {
18
+ if (!(command instanceof ContextMenuCommandBuilder)) continue;
19
+ if (command.type === ApplicationCommandType.User) user.add(command.name);
20
+ else message.add(command.name);
21
+ }
22
+ return {
23
+ user,
24
+ message
25
+ };
26
+ }
27
+
28
+ //#endregion
6
29
  //#region src/metadataKeys.ts
7
30
  let InteractionRoutes = /* @__PURE__ */ function(InteractionRoutes) {
8
31
  InteractionRoutes["Slash"] = "slash";
@@ -260,5 +283,5 @@ function deferFlags(opts) {
260
283
  const PublishDefault = Symbol("seedcord.publishDefault");
261
284
 
262
285
  //#endregion
263
- export { RowComponent as a, EventMetadataKey as c, InteractionRouteKeys as d, InteractionRoutes as f, WebhookUrlMetadataKey as h, BuilderComponent as i, GatedMetadataKey as l, SubscribeMetadataKey as m, deferFlags as n, setBotColor as o, MiddlewareMetadataKey as p, sendFlags as r, CommandMetadataKey as s, PublishDefault as t, InteractionMetadataKey as u };
264
- //# sourceMappingURL=publishDefault-CCf1azzW.mjs.map
286
+ export { RowComponent as a, EventMetadataKey as c, InteractionRouteKeys as d, InteractionRoutes as f, contextMenuLeaves as g, WebhookUrlMetadataKey as h, BuilderComponent as i, GatedMetadataKey as l, SubscribeMetadataKey as m, deferFlags as n, setBotColor as o, MiddlewareMetadataKey as p, sendFlags as r, CommandMetadataKey as s, PublishDefault as t, InteractionMetadataKey as u };
287
+ //# sourceMappingURL=publishDefault-CldnxcT2.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publishDefault-CldnxcT2.mjs","names":[],"sources":["../src/commands/contextMenuLeaves.ts","../src/metadataKeys.ts","../src/components/botColorHolder.ts","../src/components/builderTypes.ts","../src/components/colors.ts","../src/components/resolveColor.ts","../src/components/Component.ts","../src/reply/flags.ts","../src/subscribers/publishDefault.ts"],"sourcesContent":["import { ContextMenuCommandBuilder } from '@discordjs/builders';\nimport { ApplicationCommandType } from 'discord-api-types/v10';\n\nimport type { SlashCommandBuilder } from '@discordjs/builders';\n\n/** The registered context-menu command names, split by kind. @internal */\nexport interface ContextMenuLeaves {\n readonly user: Set<string>;\n readonly message: Set<string>;\n}\n\n/**\n * Collects the names of every registered context-menu command across a set of command builders, deduplicated\n * because the same builder appears once per guild. A user and a message command can share a name, so the two\n * kinds stay in separate sets.\n *\n * @internal\n */\nexport function contextMenuLeaves(\n commands: Iterable<SlashCommandBuilder | ContextMenuCommandBuilder>\n): ContextMenuLeaves {\n const user = new Set<string>();\n const message = new Set<string>();\n for (const command of commands) {\n if (!(command instanceof ContextMenuCommandBuilder)) continue;\n if (command.type === ApplicationCommandType.User) user.add(command.name);\n else message.add(command.name);\n }\n return { user, message };\n}\n","export enum InteractionRoutes {\n Slash = 'slash',\n Button = 'button',\n Modal = 'modal',\n StringMenu = 'stringMenu',\n UserMenu = 'userMenu',\n RoleMenu = 'roleMenu',\n ChannelMenu = 'channelMenu',\n MentionableMenu = 'mentionableMenu',\n MessageContextMenu = 'messageContextMenu',\n UserContextMenu = 'userContextMenu',\n Autocomplete = 'autocomplete'\n}\n\nexport const InteractionMetadataKey = Symbol('seedcord:interaction:metadata');\n\nexport const InteractionRouteKeys: Record<InteractionRoutes, symbol> = {\n [InteractionRoutes.Slash]: Symbol('seedcord:interaction:slash'),\n [InteractionRoutes.Button]: Symbol('seedcord:interaction:button'),\n [InteractionRoutes.Modal]: Symbol('seedcord:interaction:modal'),\n [InteractionRoutes.StringMenu]: Symbol('seedcord:interaction:stringMenu'),\n [InteractionRoutes.UserMenu]: Symbol('seedcord:interaction:userMenu'),\n [InteractionRoutes.RoleMenu]: Symbol('seedcord:interaction:roleMenu'),\n [InteractionRoutes.ChannelMenu]: Symbol('seedcord:interaction:channelMenu'),\n [InteractionRoutes.MentionableMenu]: Symbol('seedcord:interaction:mentionableMenu'),\n [InteractionRoutes.MessageContextMenu]: Symbol('seedcord:interaction:messageContextMenu'),\n [InteractionRoutes.UserContextMenu]: Symbol('seedcord:interaction:userContextMenu'),\n [InteractionRoutes.Autocomplete]: Symbol('seedcord:interaction:autocomplete')\n};\n\nexport const MiddlewareMetadataKey = Symbol('seedcord:middleware:metadata');\n\nexport const EventMetadataKey = Symbol('seedcord:event:metadata');\n\nexport const CommandMetadataKey = Symbol('seedcord:command:metadata');\n\nexport const GatedMetadataKey = Symbol('seedcord:gated:metadata');\n\nexport const SubscribeMetadataKey = Symbol('seedcord:subscribe:metadata');\n\nexport const WebhookUrlMetadataKey = Symbol('seedcord:webhookUrl:metadata');\n","import type { BotColor } from '@seedcord/types';\n\n// must be 'Default' so the container guard in applyBotColor leaves the accent unset\nconst DEFAULT_COLOR: BotColor = 'Default';\n\nlet current: BotColor = DEFAULT_COLOR;\n\nexport function setBotColor(color: BotColor | undefined): void {\n current = color ?? DEFAULT_COLOR;\n}\n\nexport function getBotColor(): BotColor {\n return current;\n}\n","import {\n ActionRowBuilder,\n ButtonBuilder,\n ChannelSelectMenuBuilder,\n CheckboxBuilder,\n CheckboxGroupBuilder,\n CheckboxGroupOptionBuilder,\n ContainerBuilder,\n ContextMenuCommandBuilder,\n EmbedBuilder,\n FileBuilder,\n FileUploadBuilder,\n LabelBuilder,\n MediaGalleryBuilder,\n MentionableSelectMenuBuilder,\n ModalBuilder,\n RadioGroupBuilder,\n RadioGroupOptionBuilder,\n RoleSelectMenuBuilder,\n SectionBuilder,\n SeparatorBuilder,\n SlashCommandBuilder,\n SlashCommandSubcommandBuilder,\n SlashCommandSubcommandGroupBuilder,\n StringSelectMenuBuilder,\n StringSelectMenuOptionBuilder,\n TextDisplayBuilder,\n TextInputBuilder,\n UserSelectMenuBuilder\n} from '@discordjs/builders';\n\n/**\n * Available Discord.js builder classes for use with BuilderComponent for commands, embeds, modals, etc.\n *\n * @internal\n */\nexport const BuilderTypes = {\n // Command Components\n command: SlashCommandBuilder,\n context_menu: ContextMenuCommandBuilder,\n subcommand: SlashCommandSubcommandBuilder,\n group: SlashCommandSubcommandGroupBuilder,\n\n // Embed Components\n embed: EmbedBuilder,\n\n // Modal Components\n modal: ModalBuilder,\n label: LabelBuilder,\n text_input: TextInputBuilder,\n file_upload: FileUploadBuilder,\n checkbox: CheckboxBuilder,\n checkbox_group: CheckboxGroupBuilder,\n checkbox_group_option: CheckboxGroupOptionBuilder,\n radio_group: RadioGroupBuilder,\n radio_group_option: RadioGroupOptionBuilder,\n\n // Action Row Components\n button: ButtonBuilder,\n menu_string: StringSelectMenuBuilder,\n menu_option_string: StringSelectMenuOptionBuilder,\n menu_user: UserSelectMenuBuilder,\n menu_channel: ChannelSelectMenuBuilder,\n menu_mentionable: MentionableSelectMenuBuilder,\n menu_role: RoleSelectMenuBuilder,\n\n // ComponentsV2\n container: ContainerBuilder,\n text_display: TextDisplayBuilder,\n file: FileBuilder,\n media: MediaGalleryBuilder,\n section: SectionBuilder,\n separator: SeparatorBuilder\n};\n\n/**\n * Available Discord.js action row classes for use with RowComponent for Select Menus and Buttons\n *\n * @internal\n */\nexport const RowTypes: {\n button: typeof ActionRowBuilder<ButtonBuilder>;\n menu_string: typeof ActionRowBuilder<StringSelectMenuBuilder>;\n menu_user: typeof ActionRowBuilder<UserSelectMenuBuilder>;\n menu_channel: typeof ActionRowBuilder<ChannelSelectMenuBuilder>;\n menu_mentionable: typeof ActionRowBuilder<MentionableSelectMenuBuilder>;\n menu_role: typeof ActionRowBuilder<RoleSelectMenuBuilder>;\n} = {\n button: ActionRowBuilder<ButtonBuilder>,\n menu_string: ActionRowBuilder<StringSelectMenuBuilder>,\n menu_user: ActionRowBuilder<UserSelectMenuBuilder>,\n menu_channel: ActionRowBuilder<ChannelSelectMenuBuilder>,\n menu_mentionable: ActionRowBuilder<MentionableSelectMenuBuilder>,\n menu_role: ActionRowBuilder<RoleSelectMenuBuilder>\n};\n\n/**\n * Available Discord.js builder types for use with BuilderComponent\n */\nexport type BuilderType = keyof typeof BuilderTypes;\n\n/**\n * @internal\n */\nexport type InstantiatedBuilder<BuilderKey extends BuilderType> = InstanceType<(typeof BuilderTypes)[BuilderKey]>;\n\n/**\n * Available Discord.js action row types for use with RowComponent\n */\nexport type RowType = keyof typeof RowTypes;\n\n/**\n * @internal\n */\nexport type InstantiatedActionRow<RowKey extends RowType> = InstanceType<(typeof RowTypes)[RowKey]>;\n","import type { ColorName, TypedOmit } from '@seedcord/types';\n\n// Ported verbatim from discord.js (Apache-2.0), src/util/Colors.js.\nexport const Colors: TypedOmit<Record<ColorName, number>, 'Random'> = {\n Aqua: 0x1a_bc_9c,\n Blue: 0x34_98_db,\n Blurple: 0x58_65_f2,\n DarkAqua: 0x11_80_6a,\n DarkBlue: 0x20_66_94,\n DarkButNotBlack: 0x2c_2f_33,\n DarkerGrey: 0x7f_8c_8d,\n DarkGold: 0xc2_7c_0e,\n DarkGreen: 0x1f_8b_4c,\n DarkGrey: 0x97_9c_9f,\n DarkNavy: 0x2c_3e_50,\n DarkOrange: 0xa8_43_00,\n DarkPurple: 0x71_36_8a,\n DarkRed: 0x99_2d_22,\n DarkVividPink: 0xad_14_57,\n Default: 0x00_00_00,\n Fuchsia: 0xeb_45_9e,\n Gold: 0xf1_c4_0f,\n Green: 0x57_f2_87,\n Grey: 0x95_a5_a6,\n Greyple: 0x99_aa_b5,\n LightGrey: 0xbc_c0_c0,\n LuminousVividPink: 0xe9_1e_63,\n Navy: 0x34_49_5e,\n NotQuiteBlack: 0x23_27_2a,\n Orange: 0xe6_7e_22,\n Purple: 0x9b_59_b6,\n Red: 0xed_42_45,\n White: 0xff_ff_ff,\n Yellow: 0xfe_e7_5c\n} as const;\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordRangeError, SeedcordTypeError } from '@seedcord/errors/internal';\n\nimport { Colors } from './colors';\n\nimport type { BotColor } from '@seedcord/types';\n\nconst MAX_COLOR = 0xff_ff_ff;\nconst RED_SHIFT = 16;\nconst GREEN_SHIFT = 8;\n\nconst palette: Record<string, number | undefined> = Colors;\n\n/**\n * Resolves a {@link BotColor} to the integer Discord expects.\n *\n * Ported from discord.js (Apache-2.0), `resolveColor` in src/util/Util.js.\n */\nexport function resolveColor(color: BotColor): number {\n let resolved: number | undefined;\n\n if (typeof color === 'string') {\n if (color === 'Random') return Math.floor(Math.random() * (MAX_COLOR + 1));\n if (color === 'Default') return 0;\n if (/^#?[\\da-f]{6}$/i.test(color)) return Number.parseInt(color.replace('#', ''), 16);\n resolved = palette[color];\n } else if (typeof color === 'number') {\n resolved = color;\n } else {\n resolved = (color[0] << RED_SHIFT) + (color[1] << GREEN_SHIFT) + color[2];\n }\n\n if (resolved === undefined || !Number.isSafeInteger(resolved)) {\n throw new SeedcordTypeError(SeedcordErrorCode.ColorUnresolvable, [JSON.stringify(color)]);\n }\n if (resolved < 0 || resolved > MAX_COLOR) {\n throw new SeedcordRangeError(SeedcordErrorCode.ColorOutOfRange);\n }\n\n return resolved;\n}\n","import { ContainerBuilder, ContextMenuCommandBuilder, EmbedBuilder, SlashCommandBuilder } from '@discordjs/builders';\nimport { InteractionContextType } from 'discord-api-types/v10';\n\nimport { getBotColor } from './botColorHolder';\nimport { BuilderTypes, RowTypes } from './builderTypes';\nimport { resolveColor } from './resolveColor';\n\nimport type { BuilderType, InstantiatedActionRow, InstantiatedBuilder, RowType } from './builderTypes';\n\n/**\n * Base class for Discord component wrappers.\n *\n * @typeParam TComponent - The Discord.js component type being wrapped\n *\n * @internal\n */\nabstract class BaseComponent<TComponent> {\n private readonly _component: TComponent;\n\n protected constructor(ComponentClass: new () => TComponent) {\n this._component = new ComponentClass();\n }\n\n /**\n * Returns the live builder, ready to send in a Discord message or nest in another component.\n *\n * Configure it through `this.instance`. Reading this can apply the bot color (see\n * {@link BuilderComponent}), so it has a side effect.\n * @example new SomeComponent().component\n */\n public abstract get component(): InstantiatedBuilder<BuilderType> | InstantiatedActionRow<RowType>;\n\n /**\n * The wrapped builder, for calling Discord.js methods like setTitle() and setDescription() inside a subclass.\n *\n * @example this.instance.setTitle('My Modal')\n */\n protected get instance(): TComponent {\n return this._component;\n }\n}\n\n/**\n * Base class for Discord.js builder components\n *\n * Wraps Discord.js builders (SlashCommandBuilder, EmbedBuilder, etc.) with\n * Seedcord-specific defaults and helper methods.\n *\n * @typeParam BuilderKey - The type of Discord.js builder being wrapped\n */\nexport abstract class BuilderComponent<BuilderKey extends BuilderType> extends BaseComponent<\n InstantiatedBuilder<BuilderKey>\n> {\n private colorApplied = false;\n\n protected constructor(public readonly type: BuilderKey) {\n const ComponentClass = BuilderTypes[type] as unknown;\n super(ComponentClass as new () => InstantiatedBuilder<BuilderKey>);\n\n if (this.instance instanceof SlashCommandBuilder || this.instance instanceof ContextMenuCommandBuilder) {\n this.instance.setContexts(InteractionContextType.Guild);\n }\n }\n\n get component(): InstantiatedBuilder<BuilderKey> {\n this.applyBotColor();\n return this.instance;\n }\n\n // Resolving in the constructor would capture the default for a component built before setBotColor()\n // ran. The unset check keeps a color the subclass set for itself.\n private applyBotColor(): void {\n if (this.colorApplied) return;\n this.colorApplied = true;\n\n const color = getBotColor();\n if (this.instance instanceof EmbedBuilder) {\n if (this.instance.data.color === undefined) this.instance.setColor(resolveColor(color));\n } else if (this.instance instanceof ContainerBuilder) {\n const accent = this.instance.data.accent_color;\n if (accent === null || accent === undefined) {\n this.instance.setAccentColor(color === 'Default' ? undefined : resolveColor(color));\n }\n }\n }\n}\n\n/**\n * Base class for Discord action row components\n *\n * Wraps Discord.js action row builder with Seedcord-specific defaults and helper methods.\n *\n * @typeParam RowKey - The Discord.js action row type being wrapped\n */\nexport abstract class RowComponent<RowKey extends RowType> extends BaseComponent<InstantiatedActionRow<RowKey>> {\n protected constructor(public readonly type: RowKey) {\n const ComponentClass = RowTypes[type] as unknown;\n super(ComponentClass as new () => InstantiatedActionRow<RowKey>);\n }\n\n get component(): InstantiatedActionRow<RowKey> {\n return this.instance;\n }\n}\n","import { MessageFlags } from 'discord-api-types/v10';\n\nimport type { DeferOpts, SendOpts } from '@seedcord/types';\n\n/** IsComponentsV2 is unconditional, every framework reply is v2. */\nexport function sendFlags(opts?: SendOpts): number {\n let flags = MessageFlags.IsComponentsV2;\n if (opts?.ephemeral ?? true) flags |= MessageFlags.Ephemeral;\n if (opts?.silent) flags |= MessageFlags.SuppressNotifications;\n return flags;\n}\n\n/** A deferral carries no content, so only Ephemeral applies. */\nexport function deferFlags(opts?: DeferOpts): number {\n return (opts?.ephemeral ?? true) ? MessageFlags.Ephemeral : 0;\n}\n","/**\n * The key of the Bus method that emits the framework's own subscriptions. `publish` excludes these keys\n * through `PublishableKey`, and this symbol is exported only from the internal entry.\n *\n * @internal\n */\nexport const PublishDefault = Symbol('seedcord.publishDefault');\n"],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { SlashCommandBuilder } from "@discordjs/builders";
2
+ import { routeLeavesOf } from "@seedcord/utils/internal";
3
+
4
+ //#region src/commands/slashRouteLeaves.ts
5
+ /**
6
+ * Collects the route keys of every executable slash leaf across a set of command builders, deduplicated
7
+ * because the same builder appears once per guild. The keys come from the same {@link routeLeavesOf} that
8
+ * `seedcord codegen` reads, so the boot-time check compares against the generated registry's keys.
9
+ *
10
+ * @internal
11
+ */
12
+ function slashRouteLeaves(commands) {
13
+ const leaves = /* @__PURE__ */ new Set();
14
+ for (const command of commands) {
15
+ if (!(command instanceof SlashCommandBuilder)) continue;
16
+ for (const leaf of routeLeavesOf(command.toJSON())) leaves.add(leaf.route);
17
+ }
18
+ return leaves;
19
+ }
20
+
21
+ //#endregion
22
+ export { slashRouteLeaves as t };
23
+ //# sourceMappingURL=slashRouteLeaves-BANDtPFm.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slashRouteLeaves-BANDtPFm.mjs","names":[],"sources":["../src/commands/slashRouteLeaves.ts"],"sourcesContent":["import { SlashCommandBuilder } from '@discordjs/builders';\nimport { routeLeavesOf } from '@seedcord/utils/internal';\n\nimport type { ContextMenuCommandBuilder } from '@discordjs/builders';\n\n/**\n * Collects the route keys of every executable slash leaf across a set of command builders, deduplicated\n * because the same builder appears once per guild. The keys come from the same {@link routeLeavesOf} that\n * `seedcord codegen` reads, so the boot-time check compares against the generated registry's keys.\n *\n * @internal\n */\nexport function slashRouteLeaves(commands: Iterable<SlashCommandBuilder | ContextMenuCommandBuilder>): Set<string> {\n const leaves = new Set<string>();\n for (const command of commands) {\n if (!(command instanceof SlashCommandBuilder)) continue;\n for (const leaf of routeLeavesOf(command.toJSON())) leaves.add(leaf.route);\n }\n return leaves;\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAgB,iBAAiB,UAAkF;CAC/G,MAAM,yBAAS,IAAI,IAAY;CAC/B,KAAK,MAAM,WAAW,UAAU;EAC5B,IAAI,EAAE,mBAAmB,sBAAsB;EAC/C,KAAK,MAAM,QAAQ,cAAc,QAAQ,OAAO,CAAC,GAAG,OAAO,IAAI,KAAK,KAAK;CAC7E;CACA,OAAO;AACX"}
@@ -0,0 +1,28 @@
1
+ let _discordjs_builders = require("@discordjs/builders");
2
+ let _seedcord_utils_internal = require("@seedcord/utils/internal");
3
+
4
+ //#region src/commands/slashRouteLeaves.ts
5
+ /**
6
+ * Collects the route keys of every executable slash leaf across a set of command builders, deduplicated
7
+ * because the same builder appears once per guild. The keys come from the same {@link routeLeavesOf} that
8
+ * `seedcord codegen` reads, so the boot-time check compares against the generated registry's keys.
9
+ *
10
+ * @internal
11
+ */
12
+ function slashRouteLeaves(commands) {
13
+ const leaves = /* @__PURE__ */ new Set();
14
+ for (const command of commands) {
15
+ if (!(command instanceof _discordjs_builders.SlashCommandBuilder)) continue;
16
+ for (const leaf of (0, _seedcord_utils_internal.routeLeavesOf)(command.toJSON())) leaves.add(leaf.route);
17
+ }
18
+ return leaves;
19
+ }
20
+
21
+ //#endregion
22
+ Object.defineProperty(exports, 'slashRouteLeaves', {
23
+ enumerable: true,
24
+ get: function () {
25
+ return slashRouteLeaves;
26
+ }
27
+ });
28
+ //# sourceMappingURL=slashRouteLeaves-nfxOAM7Q.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slashRouteLeaves-nfxOAM7Q.cjs","names":["SlashCommandBuilder"],"sources":["../src/commands/slashRouteLeaves.ts"],"sourcesContent":["import { SlashCommandBuilder } from '@discordjs/builders';\nimport { routeLeavesOf } from '@seedcord/utils/internal';\n\nimport type { ContextMenuCommandBuilder } from '@discordjs/builders';\n\n/**\n * Collects the route keys of every executable slash leaf across a set of command builders, deduplicated\n * because the same builder appears once per guild. The keys come from the same {@link routeLeavesOf} that\n * `seedcord codegen` reads, so the boot-time check compares against the generated registry's keys.\n *\n * @internal\n */\nexport function slashRouteLeaves(commands: Iterable<SlashCommandBuilder | ContextMenuCommandBuilder>): Set<string> {\n const leaves = new Set<string>();\n for (const command of commands) {\n if (!(command instanceof SlashCommandBuilder)) continue;\n for (const leaf of routeLeavesOf(command.toJSON())) leaves.add(leaf.route);\n }\n return leaves;\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAgB,iBAAiB,UAAkF;CAC/G,MAAM,yBAAS,IAAI,IAAY;CAC/B,KAAK,MAAM,WAAW,UAAU;EAC5B,IAAI,EAAE,mBAAmBA,0CAAsB;EAC/C,KAAK,MAAM,oDAAsB,QAAQ,OAAO,CAAC,GAAG,OAAO,IAAI,KAAK,KAAK;CAC7E;CACA,OAAO;AACX"}
@@ -1,9 +1,9 @@
1
- import { h as WebhookUrlMetadataKey, i as BuilderComponent, m as SubscribeMetadataKey, r as sendFlags, t as PublishDefault } from "./publishDefault-CCf1azzW.mjs";
1
+ import { h as WebhookUrlMetadataKey, i as BuilderComponent, m as SubscribeMetadataKey, r as sendFlags, t as PublishDefault } from "./publishDefault-CldnxcT2.mjs";
2
2
  import "reflect-metadata";
3
+ import { Logger, paint } from "@seedcord/logger";
4
+ import { Routes, SeparatorSpacingSize } from "discord-api-types/v10";
3
5
  import { SeedcordErrorCode } from "@seedcord/errors";
4
6
  import { SeedcordError } from "@seedcord/errors/internal";
5
- import { Routes, SeparatorSpacingSize } from "discord-api-types/v10";
6
- import { Logger, paint } from "@seedcord/logger";
7
7
  import { filterCirculars, stripAnsi, timestampFromSnowflake } from "@seedcord/utils";
8
8
  import { TypedEventEmitter } from "@seedcord/event-emitter";
9
9
  import chalk from "chalk";
@@ -48,7 +48,7 @@ function jsonAttachment(name, description, data) {
48
48
  return {
49
49
  name,
50
50
  description,
51
- data: JSON.stringify(content, void 0, 2)
51
+ data: new TextEncoder().encode(JSON.stringify(content, void 0, 2))
52
52
  };
53
53
  }
54
54
  function neutralizeFences(text) {
@@ -477,4 +477,4 @@ function registrationFor(ctor) {
477
477
 
478
478
  //#endregion
479
479
  export { WebhookLog as a, Subscribe as i, registrationFor as n, Subscriber as o, WebhookUrl as r, Bus as t };
480
- //# sourceMappingURL=subscribers-DYqFVP1X.mjs.map
480
+ //# sourceMappingURL=subscribers-CmDKqC7y.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscribers-CmDKqC7y.mjs","names":[],"sources":["../src/subscribers/Subscriber.ts","../src/subscribers/bases/webhookHelpers.ts","../src/subscribers/bases/WebhookSender.ts","../src/subscribers/bases/WebhookLog.ts","../src/subscribers/decorators/Subscribe.ts","../src/subscribers/decorators/WebhookUrl.ts","../src/subscribers/default/HandledException.ts","../src/subscribers/default/UnknownException.ts","../src/subscribers/Bus.ts"],"sourcesContent":["import { Logger } from '@seedcord/logger';\n\nimport type { SubscriptionKey, AllSubscriptions } from './types/Subscriptions';\nimport type { CoreBase } from '@interfaces/CoreBase';\n\n/**\n * Abstract base class for handling application subscribers.\n *\n * Provides type-safe access to subscribers data and the core framework instance.\n * Extend this class to create custom subscriber handlers.\n *\n * @typeParam KeyOfSubscribers - The specific subscriber type this handler processes\n * @typeParam TCore - The transport's Core\n */\nexport abstract class Subscriber<KeyOfSubscribers extends SubscriptionKey, TCore extends CoreBase> {\n protected readonly logger: Logger;\n\n /**\n * Instantiates the subscriber with event data and core.\n *\n * @param data - The subscriber event data\n * @param core - The core framework instance\n */\n constructor(\n protected readonly data: AllSubscriptions[KeyOfSubscribers],\n protected readonly core: TCore\n ) {\n this.logger = new Logger(this.constructor.name, { channel: 'subscribers' });\n }\n\n /**\n * Executes the subscriber handler logic.\n */\n abstract execute(): Promise<void>;\n}\n","import { filterCirculars, stripAnsi } from '@seedcord/utils';\nimport { SeparatorSpacingSize } from 'discord-api-types/v10';\n\nimport { BuilderComponent } from '@components/Component';\n\nimport type { WebhookFile } from './WebhookSender';\n\nconst DISCORD_WEBHOOK_REGEX = new RegExp(\n String.raw`^https?:\\/\\/(?:canary\\.|ptb\\.)?discord(?:app)?\\.com\\/api\\/webhooks\\/\\d+\\/[\\w$-]+$`\n);\n\nexport function isDiscordWebhookUrl(value: string): boolean {\n return URL.canParse(value) && DISCORD_WEBHOOK_REGEX.test(value);\n}\n\nexport function jsonAttachment(name: string, description: string, data: unknown): WebhookFile {\n const content = filterCirculars(data);\n return { name, description, data: new TextEncoder().encode(JSON.stringify(content, undefined, 2)) };\n}\n\n// the report renders inside a ``` fence, so break any triple-backtick run to keep it from closing early\nexport function neutralizeFences(text: string): string {\n return text.replaceAll('```', '`​`​`');\n}\n\n/** An error's stack with its direct cause appended (one level), ANSI-stripped for the webhook payload. */\nexport function errorReport(error: Error): string {\n let report = error.stack ?? `${error.name}: ${error.message}`;\n if (Error.isError(error.cause)) {\n const cause = error.cause.stack ?? `${error.cause.name}: ${error.cause.message}`;\n report += `\\n\\nCaused by:\\n${cause}`;\n }\n const stripped = neutralizeFences(stripAnsi(report));\n // eslint-disable-next-line no-magic-numbers\n return stripped.length > 1800 ? `${stripped.slice(0, 1799)}…` : stripped;\n}\n\nexport class WebhookSeparator extends BuilderComponent<'separator'> {\n constructor() {\n super('separator');\n this.instance.setSpacing(SeparatorSpacingSize.Small).setDivider(true);\n }\n}\n","import { DiscordAPIError, REST } from '@discordjs/rest';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Routes } from 'discord-api-types/v10';\n\nimport type { ReplyFile } from '@seedcord/types';\nimport type { APIMessageTopLevelComponent } from 'discord-api-types/v10';\n\nconst HTTP_NOT_FOUND = 404;\nconst HTTP_UNAUTHORIZED = 401;\n\n/** A file attached to a webhook report. */\nexport interface WebhookFile extends ReplyFile {\n readonly description: string;\n}\n\n/** The payload {@link WebhookSender.send} posts. */\nexport interface WebhookSendOptions {\n flags: number;\n username: string;\n components: readonly { toJSON(): APIMessageTopLevelComponent }[];\n files?: readonly WebhookFile[] | undefined;\n}\n\n/**\n * Sends component messages to a Discord webhook through `@discordjs/rest`.\n *\n * @throws A `SeedcordError` when the url does not carry a webhook id and token.\n */\nexport class WebhookSender {\n private readonly rest = new REST();\n private readonly route: `/${string}`;\n\n constructor(url: string) {\n const match = /\\/webhooks\\/(\\d+)\\/([\\w$-]+)$/.exec(url);\n // the url is a secret, so the error message never carries it\n if (!match?.[1] || !match[2]) throw new SeedcordError(SeedcordErrorCode.ConfigWebhookUrlInvalid, ['The url']);\n this.route = Routes.webhook(match[1], match[2]);\n }\n\n /**\n * Fetches the webhook object, sending no message. Returns `missing` on a 404 or 401 from\n * Discord and `unreachable` on any other failure.\n */\n async verify(): Promise<'ok' | 'missing' | 'unreachable'> {\n try {\n await this.rest.get(this.route, { auth: false });\n return 'ok';\n } catch (error) {\n const gone =\n error instanceof DiscordAPIError &&\n (error.status === HTTP_NOT_FOUND || error.status === HTTP_UNAUTHORIZED);\n return gone ? 'missing' : 'unreachable';\n }\n }\n\n async send(options: WebhookSendOptions): Promise<void> {\n const { flags, username, components, files } = options;\n await this.rest.post(this.route, {\n auth: false,\n // with_components=true makes discord accept a components-v2 payload. wait=true makes it\n // return payload errors in the response\n query: new URLSearchParams({ with_components: 'true', wait: 'true' }),\n body: {\n flags,\n username,\n components: components.map((component) => component.toJSON()),\n ...(files?.length && {\n attachments: files.map((file, id) => ({ id, filename: file.name, description: file.description }))\n })\n },\n ...(files?.length && { files: files.map((file) => ({ name: file.name, data: file.data })) })\n });\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport chalk from 'chalk';\nimport { Converters, Envapter } from 'envapt';\n\nimport { sendFlags } from '@reply/flags';\nimport { WebhookUrlMetadataKey } from '@src/metadataKeys';\n\nimport { Subscriber } from '../Subscriber';\nimport { isDiscordWebhookUrl } from './webhookHelpers';\nimport { WebhookSender } from './WebhookSender';\n\nimport type { WebhookFile } from './WebhookSender';\nimport type { SubscriptionKey } from '../types/Subscriptions';\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { APIMessageTopLevelComponent } from 'discord-api-types/v10';\n\n/**\n * The content a reporter returns from {@link WebhookLog.report}.\n */\nexport interface WebhookReport {\n /** Shown as the webhook message author. Defaults to the reporter's class name. */\n username?: string;\n components: readonly { toJSON(): APIMessageTopLevelComponent }[];\n files?: readonly WebhookFile[] | undefined;\n}\n\n/**\n * Base class for subscribers that deliver their event to a Discord webhook.\n *\n * Declare the url's environment variable with `@WebhookUrl` and implement {@link report}. Url\n * resolution, validation, sender reuse, and sending run in the base. When the variable is unset\n * the reporter is skipped at registration with a boot warning.\n *\n * @typeParam KeyOfSubscribers - The subscription key this reporter receives\n * @typeParam TCore - The transport's Core\n */\nexport abstract class WebhookLog<KeyOfSubscribers extends SubscriptionKey, TCore extends CoreBase> extends Subscriber<\n KeyOfSubscribers,\n TCore\n> {\n private static readonly senders = new Map<string, WebhookSender>();\n\n /** Builds the message for one published event. */\n abstract report(): WebhookReport | Promise<WebhookReport>;\n\n async execute(): Promise<void> {\n const url = WebhookLog.urlOf(WebhookLog.envKeyOf(this.constructor));\n if (url === null) {\n // a server host skips url-less reporters at registration, an edge host registers lazily and reaches this branch\n this.logger.warn(`${chalk.bold(this.constructor.name)} has no webhook url set, this reporter is disabled`);\n return;\n }\n\n const { username, components, files } = await this.report();\n try {\n await WebhookLog.senderFor(url).send({\n flags: sendFlags({ ephemeral: false }),\n username: username ?? this.constructor.name,\n components,\n files\n });\n } catch (error) {\n this.logger.error('Failed to send webhook report', error);\n }\n }\n\n /** @internal */\n static senderFor(url: string): WebhookSender {\n let sender = WebhookLog.senders.get(url);\n if (!sender) {\n sender = new WebhookSender(url);\n WebhookLog.senders.set(url, sender);\n }\n return sender;\n }\n\n /** @internal */\n static envKeyOf(ctor: NewableFunction): string {\n const envKey = Reflect.getMetadata(WebhookUrlMetadataKey, ctor) as string | undefined;\n if (!envKey) throw new SeedcordError(SeedcordErrorCode.DecoratorWebhookUrlMissing, [ctor.name]);\n return envKey;\n }\n\n /** @internal Returns null when the env var is unset, throws when set but malformed. */\n static urlOf(envKey: string): string | null {\n if (!Envapter.has(envKey)) return null;\n const raw = Envapter.getRequired(envKey, Converters.String).trim();\n if (raw === '') return null;\n if (!isDiscordWebhookUrl(raw)) throw new SeedcordError(SeedcordErrorCode.ConfigWebhookUrlInvalid, [envKey]);\n return raw;\n }\n}\n","// the default reporters are decorated at Bus module load, before any entry's own polyfill import runs\nimport 'reflect-metadata';\n\nimport { SubscribeMetadataKey } from '@src/metadataKeys';\n\nimport type { Subscriber } from '../Subscriber';\nimport type { SubscriptionKey } from '../types/Subscriptions';\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { EventFrequency } from '@seedcord/types';\nimport type { Constructor } from 'type-fest';\n\n/**\n * Options accepted by the `@Subscribe` decorator.\n */\nexport interface SubscribeOptions {\n /**\n * Frequency: `'once'` or `'on'`.\n *\n * @defaultValue `'on'`\n */\n readonly frequency?: EventFrequency | undefined;\n}\n\n/** @internal */\nexport interface SubscribeMetadataEntry {\n readonly subscriber: SubscriptionKey;\n readonly frequency?: EventFrequency | undefined;\n}\n\n/**\n * Registers a subscriber handler class with a specific subscriber event.\n *\n * Associates the decorated class with a subscriber event type for automatic\n * registration and execution when the subscriber is emitted.\n *\n * @typeParam TSubscriber - The subscriber event name to register for\n * @param subscriber - The subscriber event name to register for\n * @param options - Options to configure the subscriber handler registration.\n * @decorator\n * @example\n * ```typescript\n * \\@Subscribe('userJoin')\n * class WelcomeHandler extends Subscriber<'userJoin'> {\n * async execute() {\n * // Handle user join event\n * }\n * }\n * ```\n * @example\n * ```ts\n * \\@Subscribe('userJoin', { frequency: 'once' })\n * // or\n * \\@Subscribe('userJoin')\n * ```\n */\nexport function Subscribe<TSubscriber extends SubscriptionKey>(subscriber: TSubscriber, options?: SubscribeOptions) {\n return function <HandlerCtor extends Constructor<Subscriber<TSubscriber, CoreBase>>>(\n constructor: HandlerCtor\n ): void {\n const meta: SubscribeMetadataEntry = {\n subscriber,\n frequency: options?.frequency\n };\n\n Reflect.defineMetadata(SubscribeMetadataKey, meta, constructor);\n };\n}\n","import 'reflect-metadata';\n\nimport { WebhookUrlMetadataKey } from '@src/metadataKeys';\n\nimport type { WebhookLog } from '../bases/WebhookLog';\nimport type { SubscriptionKey } from '../types/Subscriptions';\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { Constructor } from 'type-fest';\n\n/**\n * Declares the environment variable a {@link WebhookLog} reporter reads its webhook url from.\n *\n * An unset variable disables the reporter with a boot warning. A set but malformed value throws\n * at boot.\n *\n * @param envKey - The environment variable name holding the webhook url\n * @decorator\n * @example\n * ```ts\n * \\@Subscribe('memberBanned')\n * \\@WebhookUrl('BAN_LOG_WEBHOOK_URL')\n * class BanLog extends WebhookLog<'memberBanned'> {\n * report() {\n * return { components: [new BanCard(this.data).component] };\n * }\n * }\n * ```\n */\nexport function WebhookUrl(envKey: string) {\n return function <HandlerCtor extends Constructor<WebhookLog<SubscriptionKey, CoreBase>>>(\n constructor: HandlerCtor\n ): void {\n Reflect.defineMetadata(WebhookUrlMetadataKey, envKey, constructor);\n };\n}\n","import { stripAnsi } from '@seedcord/utils';\n\nimport { BuilderComponent } from '@components/Component';\n\nimport { jsonAttachment, neutralizeFences, WebhookSeparator } from '../bases/webhookHelpers';\nimport { WebhookLog } from '../bases/WebhookLog';\nimport { Subscribe } from '../decorators/Subscribe';\nimport { WebhookUrl } from '../decorators/WebhookUrl';\n\nimport type { WebhookReport } from '../bases/WebhookLog';\nimport type { AllSubscriptions, FaultSource } from '../types/Subscriptions';\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { Notice } from '@stops/Notice';\n\n/**\n * Default subscriber that delivers a reported `Notice` (`report: true`) to a webhook with the\n * denial, the fault source, the threaded uuid, the cause stack, and the raw source as a JSON\n * attachment. The controller boundary throttles identical faults before they reach the bus.\n *\n * Reads its webhook url from HANDLED_EXCEPTION_WEBHOOK_URL. Unset, the reporter is disabled with\n * a boot warning.\n */\n@Subscribe('handledException')\n@WebhookUrl('HANDLED_EXCEPTION_WEBHOOK_URL')\nexport class HandledException extends WebhookLog<'handledException', CoreBase> {\n report(): WebhookReport {\n return {\n username: 'Handled Exception',\n components: [new HandledExceptionContainer(this.data).component],\n files: [jsonAttachment('source.json', 'The raw source the fault came from', this.data.source.raw)]\n };\n }\n}\n\nclass HandledExceptionContainer extends BuilderComponent<'container'> {\n constructor(data: AllSubscriptions['handledException']) {\n super('container');\n\n const { denial, uuid, source } = data;\n\n this.instance\n .addTextDisplayComponents((text) => text.setContent(faultSummary(denial, source)))\n .addSeparatorComponents(new WebhookSeparator().component)\n .addTextDisplayComponents((text) =>\n text.setContent(`### UUID \\`${uuid}\\`\\n\\`\\`\\`${neutralizeFences(causeStack(denial))}\\`\\`\\``)\n )\n .addSeparatorComponents(new WebhookSeparator().component)\n .addTextDisplayComponents((text) => text.setContent('### Source'))\n .addFileComponents((file) => file.setURL('attachment://source.json'));\n }\n}\n\nfunction faultSummary(denial: Notice, source: FaultSource): string {\n // name and message carry chalk, the webhook payload renders them raw\n const head = stripAnsi(`### A handled fault was reported: \\`${denial.name}\\`\\n**Message:** ${denial.message}\\n`);\n if (source.kind === 'event') {\n return (\n `${head}**Event:** ${source.eventName}\\n` +\n `**Handler:** ${source.handler}\\n` +\n `**User ID:** \\`${source.userId ?? 'n/a'}\\`\\n` +\n `**Guild ID:** \\`${source.guildId ?? 'n/a'}\\`\\n` +\n `**Channel ID:** \\`${source.channelId ?? 'n/a'}\\``\n );\n }\n return (\n `${head}**Kind:** ${source.interactionKind}\\n` +\n `**Command:** ${source.command ?? 'n/a'}\\n` +\n `**CustomId:** ${source.customId ?? 'n/a'}\\n` +\n `**User ID:** \\`${source.userId}\\`\\n` +\n `**Guild ID:** \\`${source.guildId ?? 'n/a'}\\`\\n` +\n `**Channel ID:** \\`${source.channelId ?? 'n/a'}\\`\\n` +\n `**Interaction ID:** \\`${source.interactionId}\\``\n );\n}\n\n// every cause shape returns a string, building the report never throws on a bigint or circular cause\nfunction causeStack(denial: Notice): string {\n const { cause } = denial;\n if (Error.isError(cause)) return stripAnsi(cause.stack ?? cause.message);\n if (typeof cause === 'string') return stripAnsi(cause);\n if (typeof cause === 'bigint' || typeof cause === 'symbol' || typeof cause === 'function') return cause.toString();\n if (cause !== undefined) {\n try {\n return JSON.stringify(cause);\n } catch {\n return '[unserializable cause]';\n }\n }\n return stripAnsi(denial.stack ?? 'No cause recorded.');\n}\n","import { DiscordAPIError } from '@discordjs/rest';\nimport { timestampFromSnowflake } from '@seedcord/utils';\n\nimport { BuilderComponent } from '@components/Component';\n\nimport { errorReport, jsonAttachment, WebhookSeparator } from '../bases/webhookHelpers';\nimport { WebhookLog } from '../bases/WebhookLog';\nimport { Subscribe } from '../decorators/Subscribe';\nimport { WebhookUrl } from '../decorators/WebhookUrl';\n\nimport type { WebhookReport } from '../bases/WebhookLog';\nimport type { AllSubscriptions } from '../types/Subscriptions';\nimport type { CoreBase } from '@interfaces/CoreBase';\n\n/**\n * Default subscriber that reports an unhandled exception to a webhook: the stack trace, the guild\n * and user, a tracking uuid, interaction timestamps when available, and the error metadata as a\n * JSON attachment.\n *\n * Reads its webhook url from UNKNOWN_EXCEPTION_WEBHOOK_URL. Unset, the reporter is disabled with\n * a boot warning.\n */\n@Subscribe('unknownException')\n@WebhookUrl('UNKNOWN_EXCEPTION_WEBHOOK_URL')\nexport class UnknownException extends WebhookLog<'unknownException', CoreBase> {\n report(): WebhookReport {\n const { metadata } = this.data;\n return {\n username: 'Unknown Exception',\n components: [new UnhandledErrorContainer(this.data).component],\n files: metadata\n ? [jsonAttachment('metadata.json', 'Metadata associated with the error', metadata)]\n : undefined\n };\n }\n}\n\nclass UnhandledErrorContainer extends BuilderComponent<'container'> {\n constructor(data: AllSubscriptions['unknownException']) {\n super('container');\n\n const { uuid, error, guild, user, metadata } = data;\n\n this.instance\n .addTextDisplayComponents((text) =>\n text.setContent(\n `### An unknown exception was thrown\\n` +\n `**Guild ID:** \\`${guild?.id ?? 'Not used in a guild'}\\`\\n` +\n `**Guild Name:** ${guild?.name ?? 'Not used in a guild'}\\n` +\n `**User ID:** \\`${user?.id ?? 'Missing user info'}\\`\\n` +\n `**Username:** ${user?.username ?? 'Missing user info'}\\n`\n )\n )\n .addSeparatorComponents(new WebhookSeparator().component)\n .addTextDisplayComponents((text) =>\n text.setContent(`### UUID \\`${uuid}\\`\\n\\`\\`\\`${errorReport(error)}\\`\\`\\``)\n );\n\n this.addTimestampsIfAvailable(error);\n this.addMetadataIfAvailable(metadata);\n }\n\n private addTimestampsIfAvailable(error: Error): void {\n if (!(error instanceof DiscordAPIError)) return;\n\n const now = Date.now();\n\n const snowflake = error.url.match(/\\/interactions\\/(\\d+)\\//)?.[1];\n if (!snowflake) return undefined;\n\n const interactionTs = timestampFromSnowflake(snowflake);\n\n const diff = now - interactionTs;\n const seconds = Math.floor(diff / 1000);\n const millis = diff % 1000;\n\n this.instance\n .addSeparatorComponents(new WebhookSeparator().component)\n .addTextDisplayComponents((text) =>\n text.setContent(\n `### Timestamps\\n` +\n `- **\\`Interaction sent\\` :** ${new Date(interactionTs).toISOString()} (${interactionTs})\\n` +\n `- **\\`Error logged \\` :** ${new Date(now).toISOString()} (${now})\\n` +\n `- **\\`Offset \\` :** ${seconds}s ${millis}ms`\n )\n );\n }\n\n private addMetadataIfAvailable(metadata: unknown): void {\n if (!metadata) return;\n\n this.instance\n .addSeparatorComponents(new WebhookSeparator().component)\n .addTextDisplayComponents((text) => text.setContent('### Metadata'))\n .addFileComponents((file) => file.setURL('attachment://metadata.json'));\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { TypedEventEmitter } from '@seedcord/event-emitter';\nimport { Logger, paint } from '@seedcord/logger';\nimport chalk from 'chalk';\n\nimport { SubscribeMetadataKey } from '@src/metadataKeys';\n\nimport { WebhookLog } from './bases/WebhookLog';\nimport { HandledException } from './default/HandledException';\nimport { UnknownException } from './default/UnknownException';\nimport { PublishDefault } from './publishDefault';\n\nimport type { SubscribeMetadataEntry } from './decorators/Subscribe';\nimport type { Subscriber } from './Subscriber';\nimport type {\n AllSubscriptions,\n DefaultSubscriptions,\n PublishableKey,\n SubscriptionKey,\n SubscriptionTuples\n} from './types/Subscriptions';\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { EventFrequency } from '@seedcord/types';\n\n/**\n * A subscriber class as the Bus stores it. Both parameters are `never` because construct-signature\n * parameters check contravariantly, and a concrete subscriber narrows each to its own payload and\n * transport `Core`. One cast at dispatch restores both.\n *\n * @internal\n */\nexport type StoredSubscriberCtor = new (data: never, core: never) => Subscriber<SubscriptionKey, CoreBase>;\n\n/**\n * One registration. `resolve` is lazy so an edge host registers from a manifest row without\n * importing the module until that key first publishes.\n *\n * @internal\n */\nexport interface SubscriberRegistration {\n readonly keys: readonly SubscriptionKey[];\n readonly frequency: EventFrequency;\n readonly resolve: () => StoredSubscriberCtor | Promise<StoredSubscriberCtor>;\n /** The resolved class. A server-host registration carries it from the start, a lazy one fills it on first resolve. */\n ctor?: StoredSubscriberCtor | undefined;\n}\n\n/**\n * Registers and dispatches subscribers. Subscribers run on a programmatic or framework publish.\n * Accessed via `core.bus`. Do not construct it directly.\n */\nexport class Bus extends TypedEventEmitter<SubscriptionTuples> {\n /** @internal */\n public readonly logger = new Logger('Subscribers', { channel: 'subscribers' });\n\n private readonly subscribersMap = new Map<SubscriptionKey, SubscriberRegistration[]>();\n private readonly executedOnce = new Set<SubscriberRegistration>();\n // url -> env keys, filled as reporters register and read once by verifyWebhooks\n private readonly webhookProbes = new Map<string, string[]>();\n\n constructor(protected core: CoreBase) {\n super();\n }\n\n /**\n * Registers the two shipped webhook reporters. The host calls this during startup, keeping the\n * url validation inside `start()` where a failure can still release the singleton guard.\n *\n * @internal\n */\n public registerDefaults(): void {\n this.register(registrationFor(UnknownException));\n this.register(registrationFor(HandledException));\n }\n\n /** @internal */\n public register(registration: SubscriberRegistration): void {\n // a url-less reporter never registers, so a publish on its key does not reach a disabled sender\n if (registration.ctor && !this.probeWebhook(registration.ctor)) return;\n\n for (const key of registration.keys) {\n let registrations = this.subscribersMap.get(key);\n if (!registrations) {\n registrations = [];\n this.subscribersMap.set(key, registrations);\n }\n registrations.push(registration);\n }\n }\n\n /** @internal */\n public unregister(ctor: StoredSubscriberCtor, keys?: readonly SubscriptionKey[]): void {\n for (const key of keys ?? [...this.subscribersMap.keys()]) {\n const registrations = this.subscribersMap.get(key);\n if (!registrations) continue;\n const index = registrations.findIndex((entry) => entry.ctor === ctor);\n if (index !== -1) registrations.splice(index, 1);\n }\n // registration identity carries the once state, so a restored subscriber runs again after a rollback\n }\n\n /** @internal */\n public get registeredCount(): number {\n return [...this.subscribersMap.values()].reduce((total, entries) => total + entries.length, 0);\n }\n\n /** @internal Only eagerly registered reporters are probed, which is every server-host one. */\n public async verifyWebhooks(): Promise<void> {\n const missing: string[] = [];\n await Promise.all(\n [...this.webhookProbes].map(async ([url, envKeys]) => {\n const result = await WebhookLog.senderFor(url).verify();\n if (result === 'missing') missing.push(...envKeys);\n else if (result === 'unreachable')\n this.logger.warn(`could not verify webhook at ${paint.sky.bold(envKeys.join(', '))}`);\n })\n );\n // initial boot reports all missing webhooks\n if (missing.length > 0) throw new SeedcordError(SeedcordErrorCode.ConfigWebhookNotFound, [missing.join(', ')]);\n }\n\n /** Returns whether the class should register. */\n private probeWebhook(ctor: StoredSubscriberCtor): boolean {\n if (!(ctor.prototype instanceof WebhookLog)) return true;\n\n const envKey = WebhookLog.envKeyOf(ctor);\n const url = WebhookLog.urlOf(envKey);\n if (url === null) {\n this.logger.warn(`${chalk.bold(ctor.name)} disabled, ${paint.sky.bold(envKey)} is not set`);\n return false;\n }\n const envKeys = this.webhookProbes.get(url) ?? [];\n if (!envKeys.includes(envKey)) envKeys.push(envKey);\n this.webhookProbes.set(url, envKeys);\n return true;\n }\n\n /**\n * Publishes an event to its subscribers and native listeners.\n *\n * Fire-and-forget, so subscriber handlers run asynchronously and this returns before they\n * complete, and callers must not assume side effects have completed. Errors thrown by a subscriber\n * or by an `on()` listener are caught and logged, never surfaced here. One throwing listener\n * does not stop the others. Subscribers on one key run concurrently and carry no ordering\n * guarantee. A `'once'` subscriber is marked as executed when it starts (even if it throws), so\n * it never runs twice.\n *\n * The framework's own keys are excluded. Subscribe to those and listen with `on`. The framework\n * is their only publisher.\n *\n * @param event - The subscription key to publish\n * @param data - Payload passed to each subscriber\n * @returns Whether the underlying emitter had native listeners for the event\n */\n public publish<KeyOfSubscribers extends PublishableKey>(\n event: KeyOfSubscribers,\n data: AllSubscriptions[KeyOfSubscribers]\n ): boolean {\n return this.dispatchKey(event, data);\n }\n\n /** @internal the framework's own publish path, keyed by a symbol no public entry exports */\n public [PublishDefault]<KeyOfSubscribers extends keyof DefaultSubscriptions>(\n event: KeyOfSubscribers,\n data: AllSubscriptions[KeyOfSubscribers]\n ): boolean {\n return this.dispatchKey(event, data);\n }\n\n private dispatchKey<KeyOfSubscribers extends SubscriptionKey>(\n event: KeyOfSubscribers,\n data: AllSubscriptions[KeyOfSubscribers]\n ): boolean {\n void this.processSubscriber(event, data);\n // justified: a generic key can't reduce SubscriptionTuples[K], but data is this event's payload by the signature\n return this.emitSafe(event, ...([data] as SubscriptionTuples[KeyOfSubscribers]));\n }\n\n protected override onListenerError(error: unknown, event: string | symbol): void {\n this.logger.error(`listener for ${String(event)} threw`, error);\n }\n\n private async processSubscriber<KeyOfSubscribers extends SubscriptionKey>(\n subscriberName: KeyOfSubscribers,\n data: AllSubscriptions[KeyOfSubscribers]\n ): Promise<void> {\n const registrations = this.subscribersMap.get(subscriberName);\n if (!registrations) return;\n\n const pending = registrations.reduce<Promise<void>[]>((acc, entry) => {\n if (entry.frequency === 'once') {\n if (this.executedOnce.has(entry)) return acc;\n // mark before awaiting, so a re-entrant publish can't run a 'once' subscriber twice mid-flight\n this.executedOnce.add(entry);\n }\n acc.push(this.runSubscriber(entry, subscriberName, data));\n return acc;\n }, []);\n\n await Promise.allSettled(pending);\n }\n\n private async runSubscriber<KeyOfSubscribers extends SubscriptionKey>(\n entry: SubscriberRegistration,\n subscriberName: KeyOfSubscribers,\n data: AllSubscriptions[KeyOfSubscribers]\n ): Promise<void> {\n // named outside the try so the catch can report which subscriber threw, several run per key\n let name = '<unresolved>';\n try {\n entry.ctor ??= await entry.resolve();\n // the registration keys each subscriber to its subscriptions, so data matches this ctor's payload arm\n const Ctor = entry.ctor as new (\n data: AllSubscriptions[KeyOfSubscribers],\n core: CoreBase\n ) => Subscriber<KeyOfSubscribers, CoreBase>;\n name = Ctor.name;\n await new Ctor(data, this.core).execute();\n } catch (err) {\n this.logger.error(`Error in subscriber ${String(subscriberName)} handler ${paint.sky.bold(name)}:`, err);\n }\n }\n}\n\n// callers gate on the Subscribe metadata, so it is present\n/** @internal */\nexport function registrationFor(ctor: StoredSubscriberCtor): SubscriberRegistration {\n const meta = Reflect.getMetadata(SubscribeMetadataKey, ctor) as SubscribeMetadataEntry;\n return { keys: [meta.subscriber], frequency: meta.frequency ?? 'on', resolve: () => ctor, ctor };\n}\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
1
  const require_rolldown_runtime = require('./rolldown-runtime-C_NdSu1c.cjs');
2
- const require_publishDefault = require('./publishDefault-l0NQ-SHo.cjs');
2
+ const require_publishDefault = require('./publishDefault-CVlwYUS5.cjs');
3
3
  require("reflect-metadata");
4
+ let _seedcord_logger = require("@seedcord/logger");
5
+ let discord_api_types_v10 = require("discord-api-types/v10");
4
6
  let _seedcord_errors = require("@seedcord/errors");
5
7
  let _seedcord_errors_internal = require("@seedcord/errors/internal");
6
- let discord_api_types_v10 = require("discord-api-types/v10");
7
- let _seedcord_logger = require("@seedcord/logger");
8
8
  let _seedcord_utils = require("@seedcord/utils");
9
9
  let _seedcord_event_emitter = require("@seedcord/event-emitter");
10
10
  let chalk = require("chalk");
@@ -50,7 +50,7 @@ function jsonAttachment(name, description, data) {
50
50
  return {
51
51
  name,
52
52
  description,
53
- data: JSON.stringify(content, void 0, 2)
53
+ data: new TextEncoder().encode(JSON.stringify(content, void 0, 2))
54
54
  };
55
55
  }
56
56
  function neutralizeFences(text) {
@@ -514,4 +514,4 @@ Object.defineProperty(exports, 'registrationFor', {
514
514
  return registrationFor;
515
515
  }
516
516
  });
517
- //# sourceMappingURL=subscribers-B1G6_LlK.cjs.map
517
+ //# sourceMappingURL=subscribers-CojcCgj1.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscribers-CojcCgj1.cjs","names":["Logger","BuilderComponent","SeparatorSpacingSize","REST","SeedcordError","SeedcordErrorCode","Routes","DiscordAPIError","sendFlags","WebhookUrlMetadataKey","SeedcordError","SeedcordErrorCode","Envapter","Converters","SubscribeMetadataKey","WebhookUrlMetadataKey","BuilderComponent","BuilderComponent","DiscordAPIError","TypedEventEmitter","Logger","paint","SeedcordError","SeedcordErrorCode","PublishDefault","SubscribeMetadataKey"],"sources":["../src/subscribers/Subscriber.ts","../src/subscribers/bases/webhookHelpers.ts","../src/subscribers/bases/WebhookSender.ts","../src/subscribers/bases/WebhookLog.ts","../src/subscribers/decorators/Subscribe.ts","../src/subscribers/decorators/WebhookUrl.ts","../src/subscribers/default/HandledException.ts","../src/subscribers/default/UnknownException.ts","../src/subscribers/Bus.ts"],"sourcesContent":["import { Logger } from '@seedcord/logger';\n\nimport type { SubscriptionKey, AllSubscriptions } from './types/Subscriptions';\nimport type { CoreBase } from '@interfaces/CoreBase';\n\n/**\n * Abstract base class for handling application subscribers.\n *\n * Provides type-safe access to subscribers data and the core framework instance.\n * Extend this class to create custom subscriber handlers.\n *\n * @typeParam KeyOfSubscribers - The specific subscriber type this handler processes\n * @typeParam TCore - The transport's Core\n */\nexport abstract class Subscriber<KeyOfSubscribers extends SubscriptionKey, TCore extends CoreBase> {\n protected readonly logger: Logger;\n\n /**\n * Instantiates the subscriber with event data and core.\n *\n * @param data - The subscriber event data\n * @param core - The core framework instance\n */\n constructor(\n protected readonly data: AllSubscriptions[KeyOfSubscribers],\n protected readonly core: TCore\n ) {\n this.logger = new Logger(this.constructor.name, { channel: 'subscribers' });\n }\n\n /**\n * Executes the subscriber handler logic.\n */\n abstract execute(): Promise<void>;\n}\n","import { filterCirculars, stripAnsi } from '@seedcord/utils';\nimport { SeparatorSpacingSize } from 'discord-api-types/v10';\n\nimport { BuilderComponent } from '@components/Component';\n\nimport type { WebhookFile } from './WebhookSender';\n\nconst DISCORD_WEBHOOK_REGEX = new RegExp(\n String.raw`^https?:\\/\\/(?:canary\\.|ptb\\.)?discord(?:app)?\\.com\\/api\\/webhooks\\/\\d+\\/[\\w$-]+$`\n);\n\nexport function isDiscordWebhookUrl(value: string): boolean {\n return URL.canParse(value) && DISCORD_WEBHOOK_REGEX.test(value);\n}\n\nexport function jsonAttachment(name: string, description: string, data: unknown): WebhookFile {\n const content = filterCirculars(data);\n return { name, description, data: new TextEncoder().encode(JSON.stringify(content, undefined, 2)) };\n}\n\n// the report renders inside a ``` fence, so break any triple-backtick run to keep it from closing early\nexport function neutralizeFences(text: string): string {\n return text.replaceAll('```', '`​`​`');\n}\n\n/** An error's stack with its direct cause appended (one level), ANSI-stripped for the webhook payload. */\nexport function errorReport(error: Error): string {\n let report = error.stack ?? `${error.name}: ${error.message}`;\n if (Error.isError(error.cause)) {\n const cause = error.cause.stack ?? `${error.cause.name}: ${error.cause.message}`;\n report += `\\n\\nCaused by:\\n${cause}`;\n }\n const stripped = neutralizeFences(stripAnsi(report));\n // eslint-disable-next-line no-magic-numbers\n return stripped.length > 1800 ? `${stripped.slice(0, 1799)}…` : stripped;\n}\n\nexport class WebhookSeparator extends BuilderComponent<'separator'> {\n constructor() {\n super('separator');\n this.instance.setSpacing(SeparatorSpacingSize.Small).setDivider(true);\n }\n}\n","import { DiscordAPIError, REST } from '@discordjs/rest';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Routes } from 'discord-api-types/v10';\n\nimport type { ReplyFile } from '@seedcord/types';\nimport type { APIMessageTopLevelComponent } from 'discord-api-types/v10';\n\nconst HTTP_NOT_FOUND = 404;\nconst HTTP_UNAUTHORIZED = 401;\n\n/** A file attached to a webhook report. */\nexport interface WebhookFile extends ReplyFile {\n readonly description: string;\n}\n\n/** The payload {@link WebhookSender.send} posts. */\nexport interface WebhookSendOptions {\n flags: number;\n username: string;\n components: readonly { toJSON(): APIMessageTopLevelComponent }[];\n files?: readonly WebhookFile[] | undefined;\n}\n\n/**\n * Sends component messages to a Discord webhook through `@discordjs/rest`.\n *\n * @throws A `SeedcordError` when the url does not carry a webhook id and token.\n */\nexport class WebhookSender {\n private readonly rest = new REST();\n private readonly route: `/${string}`;\n\n constructor(url: string) {\n const match = /\\/webhooks\\/(\\d+)\\/([\\w$-]+)$/.exec(url);\n // the url is a secret, so the error message never carries it\n if (!match?.[1] || !match[2]) throw new SeedcordError(SeedcordErrorCode.ConfigWebhookUrlInvalid, ['The url']);\n this.route = Routes.webhook(match[1], match[2]);\n }\n\n /**\n * Fetches the webhook object, sending no message. Returns `missing` on a 404 or 401 from\n * Discord and `unreachable` on any other failure.\n */\n async verify(): Promise<'ok' | 'missing' | 'unreachable'> {\n try {\n await this.rest.get(this.route, { auth: false });\n return 'ok';\n } catch (error) {\n const gone =\n error instanceof DiscordAPIError &&\n (error.status === HTTP_NOT_FOUND || error.status === HTTP_UNAUTHORIZED);\n return gone ? 'missing' : 'unreachable';\n }\n }\n\n async send(options: WebhookSendOptions): Promise<void> {\n const { flags, username, components, files } = options;\n await this.rest.post(this.route, {\n auth: false,\n // with_components=true makes discord accept a components-v2 payload. wait=true makes it\n // return payload errors in the response\n query: new URLSearchParams({ with_components: 'true', wait: 'true' }),\n body: {\n flags,\n username,\n components: components.map((component) => component.toJSON()),\n ...(files?.length && {\n attachments: files.map((file, id) => ({ id, filename: file.name, description: file.description }))\n })\n },\n ...(files?.length && { files: files.map((file) => ({ name: file.name, data: file.data })) })\n });\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport chalk from 'chalk';\nimport { Converters, Envapter } from 'envapt';\n\nimport { sendFlags } from '@reply/flags';\nimport { WebhookUrlMetadataKey } from '@src/metadataKeys';\n\nimport { Subscriber } from '../Subscriber';\nimport { isDiscordWebhookUrl } from './webhookHelpers';\nimport { WebhookSender } from './WebhookSender';\n\nimport type { WebhookFile } from './WebhookSender';\nimport type { SubscriptionKey } from '../types/Subscriptions';\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { APIMessageTopLevelComponent } from 'discord-api-types/v10';\n\n/**\n * The content a reporter returns from {@link WebhookLog.report}.\n */\nexport interface WebhookReport {\n /** Shown as the webhook message author. Defaults to the reporter's class name. */\n username?: string;\n components: readonly { toJSON(): APIMessageTopLevelComponent }[];\n files?: readonly WebhookFile[] | undefined;\n}\n\n/**\n * Base class for subscribers that deliver their event to a Discord webhook.\n *\n * Declare the url's environment variable with `@WebhookUrl` and implement {@link report}. Url\n * resolution, validation, sender reuse, and sending run in the base. When the variable is unset\n * the reporter is skipped at registration with a boot warning.\n *\n * @typeParam KeyOfSubscribers - The subscription key this reporter receives\n * @typeParam TCore - The transport's Core\n */\nexport abstract class WebhookLog<KeyOfSubscribers extends SubscriptionKey, TCore extends CoreBase> extends Subscriber<\n KeyOfSubscribers,\n TCore\n> {\n private static readonly senders = new Map<string, WebhookSender>();\n\n /** Builds the message for one published event. */\n abstract report(): WebhookReport | Promise<WebhookReport>;\n\n async execute(): Promise<void> {\n const url = WebhookLog.urlOf(WebhookLog.envKeyOf(this.constructor));\n if (url === null) {\n // a server host skips url-less reporters at registration, an edge host registers lazily and reaches this branch\n this.logger.warn(`${chalk.bold(this.constructor.name)} has no webhook url set, this reporter is disabled`);\n return;\n }\n\n const { username, components, files } = await this.report();\n try {\n await WebhookLog.senderFor(url).send({\n flags: sendFlags({ ephemeral: false }),\n username: username ?? this.constructor.name,\n components,\n files\n });\n } catch (error) {\n this.logger.error('Failed to send webhook report', error);\n }\n }\n\n /** @internal */\n static senderFor(url: string): WebhookSender {\n let sender = WebhookLog.senders.get(url);\n if (!sender) {\n sender = new WebhookSender(url);\n WebhookLog.senders.set(url, sender);\n }\n return sender;\n }\n\n /** @internal */\n static envKeyOf(ctor: NewableFunction): string {\n const envKey = Reflect.getMetadata(WebhookUrlMetadataKey, ctor) as string | undefined;\n if (!envKey) throw new SeedcordError(SeedcordErrorCode.DecoratorWebhookUrlMissing, [ctor.name]);\n return envKey;\n }\n\n /** @internal Returns null when the env var is unset, throws when set but malformed. */\n static urlOf(envKey: string): string | null {\n if (!Envapter.has(envKey)) return null;\n const raw = Envapter.getRequired(envKey, Converters.String).trim();\n if (raw === '') return null;\n if (!isDiscordWebhookUrl(raw)) throw new SeedcordError(SeedcordErrorCode.ConfigWebhookUrlInvalid, [envKey]);\n return raw;\n }\n}\n","// the default reporters are decorated at Bus module load, before any entry's own polyfill import runs\nimport 'reflect-metadata';\n\nimport { SubscribeMetadataKey } from '@src/metadataKeys';\n\nimport type { Subscriber } from '../Subscriber';\nimport type { SubscriptionKey } from '../types/Subscriptions';\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { EventFrequency } from '@seedcord/types';\nimport type { Constructor } from 'type-fest';\n\n/**\n * Options accepted by the `@Subscribe` decorator.\n */\nexport interface SubscribeOptions {\n /**\n * Frequency: `'once'` or `'on'`.\n *\n * @defaultValue `'on'`\n */\n readonly frequency?: EventFrequency | undefined;\n}\n\n/** @internal */\nexport interface SubscribeMetadataEntry {\n readonly subscriber: SubscriptionKey;\n readonly frequency?: EventFrequency | undefined;\n}\n\n/**\n * Registers a subscriber handler class with a specific subscriber event.\n *\n * Associates the decorated class with a subscriber event type for automatic\n * registration and execution when the subscriber is emitted.\n *\n * @typeParam TSubscriber - The subscriber event name to register for\n * @param subscriber - The subscriber event name to register for\n * @param options - Options to configure the subscriber handler registration.\n * @decorator\n * @example\n * ```typescript\n * \\@Subscribe('userJoin')\n * class WelcomeHandler extends Subscriber<'userJoin'> {\n * async execute() {\n * // Handle user join event\n * }\n * }\n * ```\n * @example\n * ```ts\n * \\@Subscribe('userJoin', { frequency: 'once' })\n * // or\n * \\@Subscribe('userJoin')\n * ```\n */\nexport function Subscribe<TSubscriber extends SubscriptionKey>(subscriber: TSubscriber, options?: SubscribeOptions) {\n return function <HandlerCtor extends Constructor<Subscriber<TSubscriber, CoreBase>>>(\n constructor: HandlerCtor\n ): void {\n const meta: SubscribeMetadataEntry = {\n subscriber,\n frequency: options?.frequency\n };\n\n Reflect.defineMetadata(SubscribeMetadataKey, meta, constructor);\n };\n}\n","import 'reflect-metadata';\n\nimport { WebhookUrlMetadataKey } from '@src/metadataKeys';\n\nimport type { WebhookLog } from '../bases/WebhookLog';\nimport type { SubscriptionKey } from '../types/Subscriptions';\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { Constructor } from 'type-fest';\n\n/**\n * Declares the environment variable a {@link WebhookLog} reporter reads its webhook url from.\n *\n * An unset variable disables the reporter with a boot warning. A set but malformed value throws\n * at boot.\n *\n * @param envKey - The environment variable name holding the webhook url\n * @decorator\n * @example\n * ```ts\n * \\@Subscribe('memberBanned')\n * \\@WebhookUrl('BAN_LOG_WEBHOOK_URL')\n * class BanLog extends WebhookLog<'memberBanned'> {\n * report() {\n * return { components: [new BanCard(this.data).component] };\n * }\n * }\n * ```\n */\nexport function WebhookUrl(envKey: string) {\n return function <HandlerCtor extends Constructor<WebhookLog<SubscriptionKey, CoreBase>>>(\n constructor: HandlerCtor\n ): void {\n Reflect.defineMetadata(WebhookUrlMetadataKey, envKey, constructor);\n };\n}\n","import { stripAnsi } from '@seedcord/utils';\n\nimport { BuilderComponent } from '@components/Component';\n\nimport { jsonAttachment, neutralizeFences, WebhookSeparator } from '../bases/webhookHelpers';\nimport { WebhookLog } from '../bases/WebhookLog';\nimport { Subscribe } from '../decorators/Subscribe';\nimport { WebhookUrl } from '../decorators/WebhookUrl';\n\nimport type { WebhookReport } from '../bases/WebhookLog';\nimport type { AllSubscriptions, FaultSource } from '../types/Subscriptions';\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { Notice } from '@stops/Notice';\n\n/**\n * Default subscriber that delivers a reported `Notice` (`report: true`) to a webhook with the\n * denial, the fault source, the threaded uuid, the cause stack, and the raw source as a JSON\n * attachment. The controller boundary throttles identical faults before they reach the bus.\n *\n * Reads its webhook url from HANDLED_EXCEPTION_WEBHOOK_URL. Unset, the reporter is disabled with\n * a boot warning.\n */\n@Subscribe('handledException')\n@WebhookUrl('HANDLED_EXCEPTION_WEBHOOK_URL')\nexport class HandledException extends WebhookLog<'handledException', CoreBase> {\n report(): WebhookReport {\n return {\n username: 'Handled Exception',\n components: [new HandledExceptionContainer(this.data).component],\n files: [jsonAttachment('source.json', 'The raw source the fault came from', this.data.source.raw)]\n };\n }\n}\n\nclass HandledExceptionContainer extends BuilderComponent<'container'> {\n constructor(data: AllSubscriptions['handledException']) {\n super('container');\n\n const { denial, uuid, source } = data;\n\n this.instance\n .addTextDisplayComponents((text) => text.setContent(faultSummary(denial, source)))\n .addSeparatorComponents(new WebhookSeparator().component)\n .addTextDisplayComponents((text) =>\n text.setContent(`### UUID \\`${uuid}\\`\\n\\`\\`\\`${neutralizeFences(causeStack(denial))}\\`\\`\\``)\n )\n .addSeparatorComponents(new WebhookSeparator().component)\n .addTextDisplayComponents((text) => text.setContent('### Source'))\n .addFileComponents((file) => file.setURL('attachment://source.json'));\n }\n}\n\nfunction faultSummary(denial: Notice, source: FaultSource): string {\n // name and message carry chalk, the webhook payload renders them raw\n const head = stripAnsi(`### A handled fault was reported: \\`${denial.name}\\`\\n**Message:** ${denial.message}\\n`);\n if (source.kind === 'event') {\n return (\n `${head}**Event:** ${source.eventName}\\n` +\n `**Handler:** ${source.handler}\\n` +\n `**User ID:** \\`${source.userId ?? 'n/a'}\\`\\n` +\n `**Guild ID:** \\`${source.guildId ?? 'n/a'}\\`\\n` +\n `**Channel ID:** \\`${source.channelId ?? 'n/a'}\\``\n );\n }\n return (\n `${head}**Kind:** ${source.interactionKind}\\n` +\n `**Command:** ${source.command ?? 'n/a'}\\n` +\n `**CustomId:** ${source.customId ?? 'n/a'}\\n` +\n `**User ID:** \\`${source.userId}\\`\\n` +\n `**Guild ID:** \\`${source.guildId ?? 'n/a'}\\`\\n` +\n `**Channel ID:** \\`${source.channelId ?? 'n/a'}\\`\\n` +\n `**Interaction ID:** \\`${source.interactionId}\\``\n );\n}\n\n// every cause shape returns a string, building the report never throws on a bigint or circular cause\nfunction causeStack(denial: Notice): string {\n const { cause } = denial;\n if (Error.isError(cause)) return stripAnsi(cause.stack ?? cause.message);\n if (typeof cause === 'string') return stripAnsi(cause);\n if (typeof cause === 'bigint' || typeof cause === 'symbol' || typeof cause === 'function') return cause.toString();\n if (cause !== undefined) {\n try {\n return JSON.stringify(cause);\n } catch {\n return '[unserializable cause]';\n }\n }\n return stripAnsi(denial.stack ?? 'No cause recorded.');\n}\n","import { DiscordAPIError } from '@discordjs/rest';\nimport { timestampFromSnowflake } from '@seedcord/utils';\n\nimport { BuilderComponent } from '@components/Component';\n\nimport { errorReport, jsonAttachment, WebhookSeparator } from '../bases/webhookHelpers';\nimport { WebhookLog } from '../bases/WebhookLog';\nimport { Subscribe } from '../decorators/Subscribe';\nimport { WebhookUrl } from '../decorators/WebhookUrl';\n\nimport type { WebhookReport } from '../bases/WebhookLog';\nimport type { AllSubscriptions } from '../types/Subscriptions';\nimport type { CoreBase } from '@interfaces/CoreBase';\n\n/**\n * Default subscriber that reports an unhandled exception to a webhook: the stack trace, the guild\n * and user, a tracking uuid, interaction timestamps when available, and the error metadata as a\n * JSON attachment.\n *\n * Reads its webhook url from UNKNOWN_EXCEPTION_WEBHOOK_URL. Unset, the reporter is disabled with\n * a boot warning.\n */\n@Subscribe('unknownException')\n@WebhookUrl('UNKNOWN_EXCEPTION_WEBHOOK_URL')\nexport class UnknownException extends WebhookLog<'unknownException', CoreBase> {\n report(): WebhookReport {\n const { metadata } = this.data;\n return {\n username: 'Unknown Exception',\n components: [new UnhandledErrorContainer(this.data).component],\n files: metadata\n ? [jsonAttachment('metadata.json', 'Metadata associated with the error', metadata)]\n : undefined\n };\n }\n}\n\nclass UnhandledErrorContainer extends BuilderComponent<'container'> {\n constructor(data: AllSubscriptions['unknownException']) {\n super('container');\n\n const { uuid, error, guild, user, metadata } = data;\n\n this.instance\n .addTextDisplayComponents((text) =>\n text.setContent(\n `### An unknown exception was thrown\\n` +\n `**Guild ID:** \\`${guild?.id ?? 'Not used in a guild'}\\`\\n` +\n `**Guild Name:** ${guild?.name ?? 'Not used in a guild'}\\n` +\n `**User ID:** \\`${user?.id ?? 'Missing user info'}\\`\\n` +\n `**Username:** ${user?.username ?? 'Missing user info'}\\n`\n )\n )\n .addSeparatorComponents(new WebhookSeparator().component)\n .addTextDisplayComponents((text) =>\n text.setContent(`### UUID \\`${uuid}\\`\\n\\`\\`\\`${errorReport(error)}\\`\\`\\``)\n );\n\n this.addTimestampsIfAvailable(error);\n this.addMetadataIfAvailable(metadata);\n }\n\n private addTimestampsIfAvailable(error: Error): void {\n if (!(error instanceof DiscordAPIError)) return;\n\n const now = Date.now();\n\n const snowflake = error.url.match(/\\/interactions\\/(\\d+)\\//)?.[1];\n if (!snowflake) return undefined;\n\n const interactionTs = timestampFromSnowflake(snowflake);\n\n const diff = now - interactionTs;\n const seconds = Math.floor(diff / 1000);\n const millis = diff % 1000;\n\n this.instance\n .addSeparatorComponents(new WebhookSeparator().component)\n .addTextDisplayComponents((text) =>\n text.setContent(\n `### Timestamps\\n` +\n `- **\\`Interaction sent\\` :** ${new Date(interactionTs).toISOString()} (${interactionTs})\\n` +\n `- **\\`Error logged \\` :** ${new Date(now).toISOString()} (${now})\\n` +\n `- **\\`Offset \\` :** ${seconds}s ${millis}ms`\n )\n );\n }\n\n private addMetadataIfAvailable(metadata: unknown): void {\n if (!metadata) return;\n\n this.instance\n .addSeparatorComponents(new WebhookSeparator().component)\n .addTextDisplayComponents((text) => text.setContent('### Metadata'))\n .addFileComponents((file) => file.setURL('attachment://metadata.json'));\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { TypedEventEmitter } from '@seedcord/event-emitter';\nimport { Logger, paint } from '@seedcord/logger';\nimport chalk from 'chalk';\n\nimport { SubscribeMetadataKey } from '@src/metadataKeys';\n\nimport { WebhookLog } from './bases/WebhookLog';\nimport { HandledException } from './default/HandledException';\nimport { UnknownException } from './default/UnknownException';\nimport { PublishDefault } from './publishDefault';\n\nimport type { SubscribeMetadataEntry } from './decorators/Subscribe';\nimport type { Subscriber } from './Subscriber';\nimport type {\n AllSubscriptions,\n DefaultSubscriptions,\n PublishableKey,\n SubscriptionKey,\n SubscriptionTuples\n} from './types/Subscriptions';\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { EventFrequency } from '@seedcord/types';\n\n/**\n * A subscriber class as the Bus stores it. Both parameters are `never` because construct-signature\n * parameters check contravariantly, and a concrete subscriber narrows each to its own payload and\n * transport `Core`. One cast at dispatch restores both.\n *\n * @internal\n */\nexport type StoredSubscriberCtor = new (data: never, core: never) => Subscriber<SubscriptionKey, CoreBase>;\n\n/**\n * One registration. `resolve` is lazy so an edge host registers from a manifest row without\n * importing the module until that key first publishes.\n *\n * @internal\n */\nexport interface SubscriberRegistration {\n readonly keys: readonly SubscriptionKey[];\n readonly frequency: EventFrequency;\n readonly resolve: () => StoredSubscriberCtor | Promise<StoredSubscriberCtor>;\n /** The resolved class. A server-host registration carries it from the start, a lazy one fills it on first resolve. */\n ctor?: StoredSubscriberCtor | undefined;\n}\n\n/**\n * Registers and dispatches subscribers. Subscribers run on a programmatic or framework publish.\n * Accessed via `core.bus`. Do not construct it directly.\n */\nexport class Bus extends TypedEventEmitter<SubscriptionTuples> {\n /** @internal */\n public readonly logger = new Logger('Subscribers', { channel: 'subscribers' });\n\n private readonly subscribersMap = new Map<SubscriptionKey, SubscriberRegistration[]>();\n private readonly executedOnce = new Set<SubscriberRegistration>();\n // url -> env keys, filled as reporters register and read once by verifyWebhooks\n private readonly webhookProbes = new Map<string, string[]>();\n\n constructor(protected core: CoreBase) {\n super();\n }\n\n /**\n * Registers the two shipped webhook reporters. The host calls this during startup, keeping the\n * url validation inside `start()` where a failure can still release the singleton guard.\n *\n * @internal\n */\n public registerDefaults(): void {\n this.register(registrationFor(UnknownException));\n this.register(registrationFor(HandledException));\n }\n\n /** @internal */\n public register(registration: SubscriberRegistration): void {\n // a url-less reporter never registers, so a publish on its key does not reach a disabled sender\n if (registration.ctor && !this.probeWebhook(registration.ctor)) return;\n\n for (const key of registration.keys) {\n let registrations = this.subscribersMap.get(key);\n if (!registrations) {\n registrations = [];\n this.subscribersMap.set(key, registrations);\n }\n registrations.push(registration);\n }\n }\n\n /** @internal */\n public unregister(ctor: StoredSubscriberCtor, keys?: readonly SubscriptionKey[]): void {\n for (const key of keys ?? [...this.subscribersMap.keys()]) {\n const registrations = this.subscribersMap.get(key);\n if (!registrations) continue;\n const index = registrations.findIndex((entry) => entry.ctor === ctor);\n if (index !== -1) registrations.splice(index, 1);\n }\n // registration identity carries the once state, so a restored subscriber runs again after a rollback\n }\n\n /** @internal */\n public get registeredCount(): number {\n return [...this.subscribersMap.values()].reduce((total, entries) => total + entries.length, 0);\n }\n\n /** @internal Only eagerly registered reporters are probed, which is every server-host one. */\n public async verifyWebhooks(): Promise<void> {\n const missing: string[] = [];\n await Promise.all(\n [...this.webhookProbes].map(async ([url, envKeys]) => {\n const result = await WebhookLog.senderFor(url).verify();\n if (result === 'missing') missing.push(...envKeys);\n else if (result === 'unreachable')\n this.logger.warn(`could not verify webhook at ${paint.sky.bold(envKeys.join(', '))}`);\n })\n );\n // initial boot reports all missing webhooks\n if (missing.length > 0) throw new SeedcordError(SeedcordErrorCode.ConfigWebhookNotFound, [missing.join(', ')]);\n }\n\n /** Returns whether the class should register. */\n private probeWebhook(ctor: StoredSubscriberCtor): boolean {\n if (!(ctor.prototype instanceof WebhookLog)) return true;\n\n const envKey = WebhookLog.envKeyOf(ctor);\n const url = WebhookLog.urlOf(envKey);\n if (url === null) {\n this.logger.warn(`${chalk.bold(ctor.name)} disabled, ${paint.sky.bold(envKey)} is not set`);\n return false;\n }\n const envKeys = this.webhookProbes.get(url) ?? [];\n if (!envKeys.includes(envKey)) envKeys.push(envKey);\n this.webhookProbes.set(url, envKeys);\n return true;\n }\n\n /**\n * Publishes an event to its subscribers and native listeners.\n *\n * Fire-and-forget, so subscriber handlers run asynchronously and this returns before they\n * complete, and callers must not assume side effects have completed. Errors thrown by a subscriber\n * or by an `on()` listener are caught and logged, never surfaced here. One throwing listener\n * does not stop the others. Subscribers on one key run concurrently and carry no ordering\n * guarantee. A `'once'` subscriber is marked as executed when it starts (even if it throws), so\n * it never runs twice.\n *\n * The framework's own keys are excluded. Subscribe to those and listen with `on`. The framework\n * is their only publisher.\n *\n * @param event - The subscription key to publish\n * @param data - Payload passed to each subscriber\n * @returns Whether the underlying emitter had native listeners for the event\n */\n public publish<KeyOfSubscribers extends PublishableKey>(\n event: KeyOfSubscribers,\n data: AllSubscriptions[KeyOfSubscribers]\n ): boolean {\n return this.dispatchKey(event, data);\n }\n\n /** @internal the framework's own publish path, keyed by a symbol no public entry exports */\n public [PublishDefault]<KeyOfSubscribers extends keyof DefaultSubscriptions>(\n event: KeyOfSubscribers,\n data: AllSubscriptions[KeyOfSubscribers]\n ): boolean {\n return this.dispatchKey(event, data);\n }\n\n private dispatchKey<KeyOfSubscribers extends SubscriptionKey>(\n event: KeyOfSubscribers,\n data: AllSubscriptions[KeyOfSubscribers]\n ): boolean {\n void this.processSubscriber(event, data);\n // justified: a generic key can't reduce SubscriptionTuples[K], but data is this event's payload by the signature\n return this.emitSafe(event, ...([data] as SubscriptionTuples[KeyOfSubscribers]));\n }\n\n protected override onListenerError(error: unknown, event: string | symbol): void {\n this.logger.error(`listener for ${String(event)} threw`, error);\n }\n\n private async processSubscriber<KeyOfSubscribers extends SubscriptionKey>(\n subscriberName: KeyOfSubscribers,\n data: AllSubscriptions[KeyOfSubscribers]\n ): Promise<void> {\n const registrations = this.subscribersMap.get(subscriberName);\n if (!registrations) return;\n\n const pending = registrations.reduce<Promise<void>[]>((acc, entry) => {\n if (entry.frequency === 'once') {\n if (this.executedOnce.has(entry)) return acc;\n // mark before awaiting, so a re-entrant publish can't run a 'once' subscriber twice mid-flight\n this.executedOnce.add(entry);\n }\n acc.push(this.runSubscriber(entry, subscriberName, data));\n return acc;\n }, []);\n\n await Promise.allSettled(pending);\n }\n\n private async runSubscriber<KeyOfSubscribers extends SubscriptionKey>(\n entry: SubscriberRegistration,\n subscriberName: KeyOfSubscribers,\n data: AllSubscriptions[KeyOfSubscribers]\n ): Promise<void> {\n // named outside the try so the catch can report which subscriber threw, several run per key\n let name = '<unresolved>';\n try {\n entry.ctor ??= await entry.resolve();\n // the registration keys each subscriber to its subscriptions, so data matches this ctor's payload arm\n const Ctor = entry.ctor as new (\n data: AllSubscriptions[KeyOfSubscribers],\n core: CoreBase\n ) => Subscriber<KeyOfSubscribers, CoreBase>;\n name = Ctor.name;\n await new Ctor(data, this.core).execute();\n } catch (err) {\n this.logger.error(`Error in subscriber ${String(subscriberName)} handler ${paint.sky.bold(name)}:`, err);\n }\n }\n}\n\n// callers gate on the Subscribe metadata, so it is present\n/** @internal */\nexport function registrationFor(ctor: StoredSubscriberCtor): SubscriberRegistration {\n const meta = Reflect.getMetadata(SubscribeMetadataKey, ctor) as SubscribeMetadataEntry;\n return { keys: [meta.subscriber], frequency: meta.frequency ?? 'on', resolve: () => ctor, ctor };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@seedcord/core",
3
3
  "type": "module",
4
- "version": "0.1.0-next.7",
4
+ "version": "0.1.0",
5
5
  "description": "Transport-agnostic core for Seedcord bots",
6
6
  "repository": {
7
7
  "type": "git",
@@ -77,18 +77,18 @@
77
77
  "chalk": "^5.6.2",
78
78
  "discord-api-types": "^0.38.51",
79
79
  "envapt": "^8.1.0",
80
- "@seedcord/errors": "0.3.0-next.6",
81
- "@seedcord/event-emitter": "0.1.0-next.0",
82
- "@seedcord/logger": "0.1.0-next.3",
83
- "@seedcord/types": "0.8.0-next.8",
84
- "@seedcord/utils": "0.8.0-next.8"
80
+ "@seedcord/errors": "0.3.0",
81
+ "@seedcord/event-emitter": "0.1.0",
82
+ "@seedcord/logger": "0.1.0",
83
+ "@seedcord/types": "0.8.0",
84
+ "@seedcord/utils": "0.8.0"
85
85
  },
86
86
  "devDependencies": {
87
87
  "reflect-metadata": "^0.2.2",
88
88
  "type-fest": "^5.7.0",
89
89
  "vite": "^8.0.16",
90
- "@seedcord/eslint-config": "1.5.0-next.4",
91
- "@seedcord/rate-limiter": "0.1.0-next.5",
90
+ "@seedcord/eslint-config": "1.5.0",
91
+ "@seedcord/rate-limiter": "0.1.0",
92
92
  "@seedcord/tsconfig": "2.0.1",
93
93
  "@seedcord/tsdown-config": "2.0.1",
94
94
  "@seedcord/vitest-config": "0.0.1"
@@ -1 +0,0 @@
1
- {"version":3,"file":"Plugin-Cgp1a0uQ.cjs","names":["SeedcordError","SeedcordErrorCode","SeedcordError","SeedcordErrorCode","Logger","SeedcordError","SeedcordErrorCode"],"sources":["../src/plugin/context.ts","../src/plugin/lifecycle.ts","../src/plugin/Plugin.ts"],"sourcesContent":["import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport type { PluginNeed } from './options';\nimport type { Config, Store } from '@seedcord/types';\n\n/**\n * The type each `ctx` capability resolves to. Empty in core, each transport augments it through\n * declaration merging.\n *\n * @example\n * ```typescript\n * declare module '@seedcord/core' {\n * interface PluginCapabilityTypes {\n * client: Client;\n * rest: REST;\n * }\n * }\n * ```\n */\nexport interface PluginCapabilityTypes {}\n\ntype CapabilityType<Key extends PluginNeed, Fallback> = Key extends keyof PluginCapabilityTypes\n ? PluginCapabilityTypes[Key]\n : Fallback;\n\ntype CapabilityValue<TNeeds extends PluginNeed, Key extends PluginNeed, Value> = Key extends TNeeds ? Value : never;\n\n/**\n * The framework surface a plugin reads through `this.ctx`, typed by its declared `needs`.\n *\n * Always available: `config` and `store`. Each capability resolves to its value only when\n * the plugin declared that key, otherwise `never`. The logger comes from `this.logger`.\n *\n * @typeParam TNeeds - The plugin's declared `needs` union.\n */\nexport interface PluginContext<TNeeds extends PluginNeed = never> {\n readonly config: Config;\n readonly store: Store<'charge'>;\n readonly client: CapabilityValue<TNeeds, 'client', CapabilityType<'client', unknown>>;\n readonly token: CapabilityValue<TNeeds, 'token', string>;\n /**\n * The REST client. On http, the client has no token until the Ready phase, so a call from\n * `init()` sends without a token and returns a 401.\n */\n readonly rest: CapabilityValue<TNeeds, 'rest', CapabilityType<'rest', unknown>>;\n}\n\n// the conditional capability types make PluginContext<A> and PluginContext<B> mutually unassignable\n/** @internal */\nexport type StoredPluginContext = Pick<PluginContext, 'config' | 'store'> & {\n client?: unknown;\n token?: string | undefined;\n rest?: unknown;\n};\n\n/**\n * The capability arms a transport supplies from its own Bot/client. `attach` merges them into each\n * plugin's ctx.\n *\n * @internal\n */\nexport interface PluginCapabilities {\n readonly client?: unknown;\n readonly token?: string | undefined;\n readonly rest?: unknown;\n}\n\n/** @internal unset until attach installs the built ctx */\nexport const pluginContextSlot = Symbol('seedcord.plugin.ctx');\n\ninterface ContextHolder {\n [pluginContextSlot]?: StoredPluginContext;\n}\n\n/** @internal */\nexport function readPluginContext<TNeeds extends PluginNeed>(holder: ContextHolder): PluginContext<TNeeds> {\n const ctx = holder[pluginContextSlot];\n if (!ctx) throw new SeedcordError(SeedcordErrorCode.PluginContextBeforeInit, [holder.constructor.name]);\n // safe, finalizePluginContext set the ctx typed for this plugin's declared needs\n return ctx as PluginContext<TNeeds>;\n}\n\n/** @internal */\nexport function finalizePluginContext<TNeeds extends PluginNeed>(\n holder: ContextHolder,\n ctx: PluginContext<TNeeds>\n): void {\n holder[pluginContextSlot] = ctx;\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { ShutdownPhase, StartupPhase } from '@src/lifecycle/phases';\n\n/**\n * The lifecycle a plugin declares through `super(host, spec)`. Unset fields resolve to the base\n * defaults. `ready` runs at a fixed `StartupPhase.Ready`, so it takes only a timeout.\n */\nexport interface PluginLifecycleSpec {\n init?: { phase?: StartupPhase; timeout?: number };\n ready?: { timeout?: number };\n dispose?: { phase?: ShutdownPhase; timeout?: number };\n}\n\n/** @internal */\nexport interface ResolvedPluginLifecycleSpec {\n readonly init: { readonly phase: StartupPhase; readonly timeout: number };\n readonly ready: { readonly timeout: number };\n readonly dispose: { readonly phase: ShutdownPhase; readonly timeout: number };\n}\n\nconst DEFAULT_INIT_TIMEOUT_MS = 15_000;\nconst DEFAULT_READY_TIMEOUT_MS = 15_000;\nconst DEFAULT_DISPOSE_TIMEOUT_MS = 10_000;\n\nfunction timeoutOf(\n field: 'init' | 'ready' | 'dispose',\n declared: number | undefined,\n fallback: number,\n pluginName: string\n): number {\n if (declared === undefined) return fallback;\n if (!Number.isFinite(declared) || declared <= 0) {\n throw new SeedcordError(SeedcordErrorCode.PluginOptionsRejected, [\n pluginName,\n `lifecycle ${field}.timeout must be a positive number of milliseconds`\n ]);\n }\n return declared;\n}\n\n/** @internal */\nexport function resolveLifecycleSpec(\n spec: PluginLifecycleSpec | undefined,\n pluginName: string\n): ResolvedPluginLifecycleSpec {\n return {\n init: {\n phase: spec?.init?.phase ?? StartupPhase.Configuration,\n timeout: timeoutOf('init', spec?.init?.timeout, DEFAULT_INIT_TIMEOUT_MS, pluginName)\n },\n ready: {\n timeout: timeoutOf('ready', spec?.ready?.timeout, DEFAULT_READY_TIMEOUT_MS, pluginName)\n },\n dispose: {\n phase: spec?.dispose?.phase ?? ShutdownPhase.Disconnect,\n timeout: timeoutOf('dispose', spec?.dispose?.timeout, DEFAULT_DISPOSE_TIMEOUT_MS, pluginName)\n }\n };\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\n\nimport { getDevChannel } from '@hmr/devChannel';\n\nimport { pluginContextSlot, readPluginContext } from './context';\nimport { resolveLifecycleSpec } from './lifecycle';\n\nimport type { PluginContext, StoredPluginContext } from './context';\nimport type { ResolvedPluginLifecycleSpec, PluginLifecycleSpec } from './lifecycle';\nimport type { TransportOf, NeedsOf, PluginOptions, RuntimeOf } from './options';\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { Tail, HmrAware, HmrUpdateEvent } from '@seedcord/types';\n\nexport interface Initializeable {\n init(): Promise<void>;\n}\n\n/** @internal */\nconst resolvedSpecSlot = Symbol('seedcord.plugin.spec');\n\n/**\n * Base class for Seedcord plugins. A subclass declares its options as the `Opts` type argument,\n * implements `init()`, and reads the framework surface through `this.ctx`.\n *\n * @typeParam Opts - The plugin's declared {@link PluginOptions}.\n * @typeParam TCore - The host type `this.core` resolves to. Each transport binds it to its own `Core`.\n */\nexport abstract class Plugin<Opts extends PluginOptions = {}, TCore extends CoreBase = CoreBase>\n implements Initializeable, HmrAware\n{\n /** @internal phantom, never set at runtime */\n declare readonly __transport?: TransportOf<Opts>;\n /** @internal phantom, never set at runtime */\n declare readonly __runtime?: RuntimeOf<Opts>;\n\n /** @internal */\n readonly [resolvedSpecSlot]: ResolvedPluginLifecycleSpec;\n /** @internal */\n [pluginContextSlot]?: StoredPluginContext;\n\n /** Logs under the plugin's class name, on the channel its attach key names. */\n public readonly logger: Logger;\n\n // CoreBase here keeps the augmented Core out of ConstructorParameters, which attach reads\n constructor(\n private readonly host: CoreBase,\n spec?: PluginLifecycleSpec\n ) {\n this.logger = new Logger(this.constructor.name);\n this[resolvedSpecSlot] = resolveLifecycleSpec(spec, this.constructor.name);\n }\n\n /** The host, typed to the transport whose `Plugin` base this class extends. */\n protected get core(): TCore {\n // justified: each transport binds TCore to the Core its own host satisfies\n return this.host as TCore;\n }\n\n /** The framework surface, available from `init()` onward. Throws if read before `attach` finalizes it. */\n protected get ctx(): PluginContext<NeedsOf<Opts>> {\n return readPluginContext<NeedsOf<Opts>>(this);\n }\n\n abstract init(): Promise<void>;\n\n /**\n * Runs in the startup Ready phase, after `init()`. The gateway client is logged in by this phase,\n * and the http host runs its server-bind and health tasks in the same phase.\n */\n ready?(): Promise<void>;\n\n /** Runs during teardown, only when `init()` resolved. */\n dispose?(): Promise<void>;\n\n /**\n * Rejects the plugin's constructor options with a reason. Throws a `SeedcordError` naming the\n * plugin class and the reason.\n *\n * @param reason - Why the options are invalid.\n */\n protected rejectOptions(reason: string): never {\n throw new SeedcordError(SeedcordErrorCode.PluginOptionsRejected, [this.constructor.name, reason]);\n }\n\n /** Override to reload plugin state on an HMR update. */\n public onHmr(_event: HmrUpdateEvent): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Registers critical file patterns that should trigger a full restart when changed in Dev HMR.\n * @param patterns - Glob patterns relative to the project root\n */\n protected registerCriticalFiles(patterns: string[]): void {\n getDevChannel()?.send('seedcord:register-critical-files', { patterns });\n }\n}\n\n/** @internal */\nexport function resolvedLifecycleSpecOf(plugin: PluginLike): ResolvedPluginLifecycleSpec {\n return plugin[resolvedSpecSlot];\n}\n\nexport type { PluginContext, PluginCapabilityTypes } from './context';\nexport type { PluginLifecycleSpec } from './lifecycle';\nexport type { PluginOptions } from './options';\n\n// a bound of Plugin<{}> rejects every plugin that declares an option, since Plugin<A> and Plugin<B>\n// are mutually unassignable. every member here must stay Opts-independent.\n/** @internal */\nexport type PluginLike = Pick<\n Plugin,\n 'init' | 'ready' | 'dispose' | 'logger' | 'onHmr' | typeof resolvedSpecSlot | typeof pluginContextSlot\n>;\n\n// a `CoreBase` first parameter rejects a narrowing ctor on its own, and it also collapses\n// `InstanceType<Ctor>` to `PluginLike` at every attach call. CoreParamAssert checks it instead.\n/** @internal */\nexport type PluginCtor<TPlugin extends PluginLike = PluginLike> = new (...args: any[]) => TPlugin;\n\n/** @internal */\nexport type PluginArgs<Ctor extends PluginCtor> = Tail<ConstructorParameters<Ctor>>;\n\ntype CoreParamTooNarrow = Record<\n 'this plugin constructor must take CoreBase as its first parameter and read the transport Core off this.core',\n never\n>;\n\n// a narrowed first parameter fails `CoreBase extends Param`\n/** @internal */\nexport type CoreParamAssert<Ctor extends PluginCtor> = CoreBase extends ConstructorParameters<Ctor>[0]\n ? unknown\n : CoreParamTooNarrow;\n\nexport { finalizePluginContext } from './context';\n"],"mappings":";;;;;;;;AAqEA,MAAa,oBAAoB,OAAO,qBAAqB;;AAO7D,SAAgB,kBAA6C,QAA8C;CACvG,MAAM,MAAM,OAAO;CACnB,IAAI,CAAC,KAAK,MAAM,IAAIA,wCAAcC,mCAAkB,yBAAyB,CAAC,OAAO,YAAY,IAAI,CAAC;CAEtG,OAAO;AACX;;AAGA,SAAgB,sBACZ,QACA,KACI;CACJ,OAAO,qBAAqB;AAChC;;;;ACnEA,MAAM,0BAA0B;AAChC,MAAM,2BAA2B;AACjC,MAAM,6BAA6B;AAEnC,SAAS,UACL,OACA,UACA,UACA,YACM;CACN,IAAI,aAAa,QAAW,OAAO;CACnC,IAAI,CAAC,OAAO,SAAS,QAAQ,KAAK,YAAY,GAC1C,MAAM,IAAIC,wCAAcC,mCAAkB,uBAAuB,CAC7D,YACA,aAAa,MAAM,mDACvB,CAAC;CAEL,OAAO;AACX;;AAGA,SAAgB,qBACZ,MACA,YAC2B;CAC3B,OAAO;EACH,MAAM;GACF,OAAO,MAAM,MAAM;GACnB,SAAS,UAAU,QAAQ,MAAM,MAAM,SAAS,yBAAyB,UAAU;EACvF;EACA,OAAO,EACH,SAAS,UAAU,SAAS,MAAM,OAAO,SAAS,0BAA0B,UAAU,EAC1F;EACA,SAAS;GACL,OAAO,MAAM,SAAS;GACtB,SAAS,UAAU,WAAW,MAAM,SAAS,SAAS,4BAA4B,UAAU;EAChG;CACJ;AACJ;;;;;ACxCA,MAAM,mBAAmB,OAAO,sBAAsB;;;;;;;;AAStD,IAAsB,SAAtB,MAEA;CAgByB;;CATrB,CAAU;;CAEV,CAAC;;CAGD,AAAgB;CAGhB,YACI,AAAiB,MACjB,MACF;EAFmB;EAGjB,KAAK,SAAS,IAAIC,wBAAO,KAAK,YAAY,IAAI;EAC9C,KAAK,oBAAoB,qBAAqB,MAAM,KAAK,YAAY,IAAI;CAC7E;;CAGA,IAAc,OAAc;EAExB,OAAO,KAAK;CAChB;;CAGA,IAAc,MAAoC;EAC9C,OAAO,kBAAiC,IAAI;CAChD;;;;;;;CAmBA,AAAU,cAAc,QAAuB;EAC3C,MAAM,IAAIC,wCAAcC,mCAAkB,uBAAuB,CAAC,KAAK,YAAY,MAAM,MAAM,CAAC;CACpG;;CAGA,AAAO,MAAM,QAAuC;EAChD,OAAO,QAAQ,QAAQ;CAC3B;;;;;CAMA,AAAU,sBAAsB,UAA0B;EACtD,iCAAc,CAAC,EAAE,KAAK,oCAAoC,EAAE,SAAS,CAAC;CAC1E;AACJ;;AAGA,SAAgB,wBAAwB,QAAiD;CACrF,OAAO,OAAO;AAClB"}