@seedcord/core 0.9.2 → 0.9.3

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 (38) hide show
  1. package/README.md +2 -2
  2. package/dist/{Bus-Djm6688b.mjs → Bus-DUDafYDD.mjs} +15 -19
  3. package/dist/Bus-DUDafYDD.mjs.map +1 -0
  4. package/dist/{HmrModuleHandler-9AUfKXRl.mjs → HmrModuleHandler-DOAtah3j.mjs} +3 -5
  5. package/dist/HmrModuleHandler-DOAtah3j.mjs.map +1 -0
  6. package/dist/{Pluggable-D-xWcaIv.mjs → Pluggable-CXyUZIvT.mjs} +2 -8
  7. package/dist/{Pluggable-D-xWcaIv.mjs.map → Pluggable-CXyUZIvT.mjs.map} +1 -1
  8. package/dist/{Pluggable-aRzcpx4e.d.mts → Pluggable-CwKbLui3.d.mts} +3 -3
  9. package/dist/{Plugin-D01bBIFo.d.mts → Plugin-Dhx_ivqj.d.mts} +2 -2
  10. package/dist/{cursor-BPaPFTic.d.mts → cursor-BTU5bGoo.d.mts} +4 -151
  11. package/dist/{cursor-CFNKLVlk.mjs → cursor-DNFyo_BI.mjs} +2 -2
  12. package/dist/{cursor-CFNKLVlk.mjs.map → cursor-DNFyo_BI.mjs.map} +1 -1
  13. package/dist/hmr.index.d.mts +1 -2
  14. package/dist/hmr.index.mjs +1 -1
  15. package/dist/{index-BS1hH9Sh.d.mts → index-B_-EjyvS.d.mts} +6 -6
  16. package/dist/index.d.mts +40 -39
  17. package/dist/index.mjs +4 -4
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/internal.index.d.mts +52 -51
  20. package/dist/internal.index.mjs +7 -8
  21. package/dist/internal.index.mjs.map +1 -1
  22. package/dist/node-internal.index.d.mts +14 -14
  23. package/dist/node-internal.index.mjs +8 -9
  24. package/dist/node-internal.index.mjs.map +1 -1
  25. package/dist/node.index.d.mts +1 -1
  26. package/dist/node.index.mjs +4 -5
  27. package/dist/node.index.mjs.map +1 -1
  28. package/dist/{phases-DJf12l5T.d.mts → phases-D_zQBXga.d.mts} +15 -25
  29. package/dist/plugin.index.d.mts +1 -1
  30. package/dist/{slashRouteLeaves-DTFHzCKN.mjs → slashRouteLeaves-D__G9vsr.mjs} +2 -2
  31. package/dist/slashRouteLeaves-D__G9vsr.mjs.map +1 -0
  32. package/dist/{subscribers-kAQQt_YP.mjs → subscribers-Bxut8U6g.mjs} +2 -2
  33. package/dist/{subscribers-kAQQt_YP.mjs.map → subscribers-Bxut8U6g.mjs.map} +1 -1
  34. package/dist/{basic-Dng__Ena.d.mts → types-BG0BimVr.d.mts} +2 -10
  35. package/package.json +12 -12
  36. package/dist/Bus-Djm6688b.mjs.map +0 -1
  37. package/dist/HmrModuleHandler-9AUfKXRl.mjs.map +0 -1
  38. package/dist/slashRouteLeaves-DTFHzCKN.mjs.map +0 -1
package/README.md CHANGED
@@ -8,8 +8,8 @@
8
8
  <div align="center">
9
9
  <h3>The whole Discord bot, typed end to end</h3>
10
10
  <a href="https://seedcord.org">Website</a> ·
11
- <a href="https://guide.seedcord.org">Guide</a> ·
12
- <a href="https://docs.seedcord.org">Reference</a> ·
11
+ <a href="https://seedcord.org/guide">Guide</a> ·
12
+ <a href="https://seedcord.org/docs">Reference</a> ·
13
13
  <a href="https://discord.gg/DzFxY58WXf">Discord</a>
14
14
  </div>
15
15
 
@@ -474,9 +474,9 @@ var WebhookSender = class {
474
474
  /**
475
475
  * Base class for subscribers that deliver their event to a Discord webhook.
476
476
  *
477
- * Declare the url's environment variable with `@WebhookUrl` and implement {@link report}. Url
478
- * resolution, validation, sender reuse, and sending run in the base. When the variable is unset
479
- * the reporter is skipped at registration with a boot warning.
477
+ * Declare the url's environment variable with `@WebhookUrl` and implement {@link report}. The base
478
+ * resolves the url, validates it, reuses one sender per url, and sends. Registration skips a reporter
479
+ * whose variable is unset and warns at boot.
480
480
  *
481
481
  * @typeParam KeyOfSubscribers - The subscription key this reporter receives
482
482
  * @typeParam TCore - The transport's Core
@@ -490,11 +490,9 @@ var WebhookLog = class WebhookLog extends Subscriber {
490
490
  return null;
491
491
  }
492
492
  async execute() {
493
- const url = WebhookLog.urlOf(WebhookLog.envKeyOf(this.constructor));
494
- if (url === null) {
495
- this.logger.warn(`${paint.sky.bold(this.constructor.name)} has no webhook url set, this reporter is disabled`);
496
- return;
497
- }
493
+ const envKey = WebhookLog.envKeyOf(this.constructor);
494
+ const url = WebhookLog.urlOf(envKey);
495
+ if (url === null) throw new SeedcordError(SeedcordErrorCode.ConfigMissingEnv, [envKey]);
498
496
  const key = this.throttleKey();
499
497
  const throttle = ReportThrottle.for(this.core);
500
498
  const suppressed = key === null ? 0 : throttle.take(key);
@@ -606,7 +604,7 @@ function WebhookUrl(envKey) {
606
604
  }
607
605
 
608
606
  //#endregion
609
- //#region \0@oxc-project+runtime@0.142.0/helpers/esm/decorate.js
607
+ //#region \0@oxc-project+runtime@0.152.0/helpers/esm/decorate.js
610
608
  function __decorate(decorators, target, key, desc) {
611
609
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
612
610
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -742,7 +740,7 @@ var Bus = class extends TypedEventEmitter {
742
740
  }
743
741
  /** @internal */
744
742
  [RegisterSubscriber](registration) {
745
- if (registration.ctor && !this.probeWebhook(registration.ctor)) return;
743
+ if (!this.probeWebhook(registration.ctor)) return;
746
744
  for (const key of registration.keys) {
747
745
  let registrations = this.subscribersMap.get(key);
748
746
  if (!registrations) {
@@ -765,7 +763,7 @@ var Bus = class extends TypedEventEmitter {
765
763
  get [RegisteredCount]() {
766
764
  return [...this.subscribersMap.values()].reduce((total, entries) => total + entries.length, 0);
767
765
  }
768
- /** @internal a lazy edge registration never probes. every server-host reporter registers eagerly */
766
+ /** @internal registration already dropped every reporter with no url set */
769
767
  async [VerifyWebhooks]() {
770
768
  const missing = [];
771
769
  await Promise.all([...this.webhookProbes].map(async ([url, envKeys]) => {
@@ -791,11 +789,11 @@ var Bus = class extends TypedEventEmitter {
791
789
  /**
792
790
  * Publishes an event to its subscribers and native listeners.
793
791
  *
794
- * Fire-and-forget. Subscriber handlers run asynchronously and this returns before they finish. A
795
- * caller cannot assume a side effect has completed. Errors thrown by a subscriber or by an `on()`
796
- * listener are caught and logged, never surfaced here. One throwing listener does not stop the
797
- * others. Subscribers on one key run concurrently and carry no ordering guarantee. A `'once'`
798
- * subscriber is marked as executed when it starts, even if it throws. It never runs twice.
792
+ * Fire-and-forget. Subscriber handlers run asynchronously and this returns before they finish.
793
+ * Errors thrown by a subscriber or by an `on()` listener are caught and logged, never surfaced
794
+ * here. One throwing listener does not stop the others. Subscribers on one key run concurrently
795
+ * and carry no ordering guarantee. A `'once'` subscriber is marked as executed when it starts,
796
+ * even if it throws. It never runs twice.
799
797
  *
800
798
  * The framework's own keys are excluded. Subscribe to those and listen with `on`. The framework
801
799
  * is their only publisher.
@@ -841,7 +839,6 @@ var Bus = class extends TypedEventEmitter {
841
839
  async runSubscriber(entry, subscriberName, data) {
842
840
  let name = "<unresolved>";
843
841
  try {
844
- entry.ctor ??= await entry.resolve();
845
842
  const Ctor = entry.ctor;
846
843
  name = Ctor.name;
847
844
  await new Ctor(data, this.core).execute();
@@ -856,7 +853,6 @@ function registrationFor(ctor) {
856
853
  return {
857
854
  keys: [meta.subscriber],
858
855
  frequency: meta.frequency ?? "on",
859
- resolve: () => ctor,
860
856
  ctor
861
857
  };
862
858
  }
@@ -867,4 +863,4 @@ function busLoggerOf(bus) {
867
863
 
868
864
  //#endregion
869
865
  export { BuilderComponent as A, InteractionRouteKeys as C, clearStore as D, accessorStore as E, isBuilderComponentClass as M, setBotColor as N, guardedAccessor as O, InteractionMiddlewareMetadataKey as S, WebhookUrlMetadataKey as T, EventMetadataKey as _, RegisterSubscriber as a, InteractionKind as b, VerifyWebhooks as c, WebhookLog as d, Subscriber as f, CommandMetadataKey as g, sendFlags as h, RegisterDefaults as i, RowComponent as j, contextMenuLeaves as k, WebhookUrl as l, deferFlags as m, busLoggerOf as n, RegisteredCount as o, ReportThrottle as p, registrationFor as r, UnregisterSubscriber as s, Bus as t, Subscribe as u, EventMiddlewareMetadataKey as v, SubscribeMetadataKey as w, InteractionMetadataKey as x, GatedMetadataKey as y };
870
- //# sourceMappingURL=Bus-Djm6688b.mjs.map
866
+ //# sourceMappingURL=Bus-DUDafYDD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Bus-DUDafYDD.mjs","names":[],"sources":["../src/components/botColorHolder.ts","../src/components/builderTypes.ts","../src/components/colors.ts","../src/components/resolveColor.ts","../src/components/Component.ts","../src/commands/contextMenuLeaves.ts","../src/miscellaneous/guarded.ts","../src/metadataKeys.ts","../src/reply/flags.ts","../src/subscribers/ReportThrottle.ts","../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/slots.ts","../src/subscribers/Bus.ts"],"sourcesContent":["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/** @internal */\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 * Reading this can apply the bot color (see {@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// instanceof fails across the cli's separate module registry\nconst BUILDER_COMPONENT = Symbol.for('seedcord:core:builder-component');\n\nObject.defineProperty(BuilderComponent.prototype, BUILDER_COMPONENT, { value: true });\n\n/** @internal */\nexport function isBuilderComponentClass(value: unknown): value is new () => BuilderComponent<BuilderType> {\n if (typeof value !== 'function') return false;\n const proto: unknown = (value as { prototype?: unknown }).prototype;\n return typeof proto === 'object' && proto !== null && BUILDER_COMPONENT in proto;\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 { ContextMenuCommandBuilder } from '@discordjs/builders';\nimport { ApplicationCommandType } from 'discord-api-types/v10';\n\nimport type { SlashCommandBuilder } from '@discordjs/builders';\n\n/** @internal */\nexport interface ContextMenuLeaves {\n readonly user: Set<string>;\n readonly message: Set<string>;\n}\n\n/**\n * dedupes names across builders, since the same builder repeats once per guild.\n * a user and a message command can share a name.\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","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\n// null-prototype, on a plain object a key named __proto__ sets the store's prototype and adds no entry\nexport function accessorStore<Value>(): Record<string, Value> {\n return Object.create(null) as Record<string, Value>;\n}\n\n// empties a store in place, keeping the reference the accessor's Proxy wraps\nexport function clearStore(store: Record<string, unknown>): void {\n for (const key of Object.keys(store)) Reflect.deleteProperty(store, key);\n}\n\n// handler modules import before startup fills the record, and an unguarded read there returns undefined and renders as an empty string\nexport function guardedAccessor<Value>(accessor: string, storage: Record<string, Value>): Record<string, Value> {\n return new Proxy(storage, {\n get(target, key, receiver): unknown {\n // hasOwn, since `in` walks the prototype and would read a key like `constructor` as resolved\n if (typeof key !== 'string' || Object.hasOwn(target, key)) return Reflect.get(target, key, receiver);\n throw new SeedcordError(SeedcordErrorCode.CoreAccessorUnresolved, [accessor, key]);\n }\n });\n}\n","import type { TypedExclude } from '@seedcord/types';\n\n/**\n * Every interaction kind seedcord routes.\n */\nexport enum InteractionKind {\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\n/**\n * The interaction kinds a middleware can filter on. Autocomplete carries no reply target.\n */\nexport type MiddlewareKind = TypedExclude<InteractionKind, InteractionKind.Autocomplete>;\n\n// Symbol.for so a second copy of this module reads the same slots\nexport const CommandMetadataKey = Symbol.for('seedcord:command:metadata');\nexport const InteractionMetadataKey = Symbol.for('seedcord:interaction:metadata');\nexport const InteractionMiddlewareMetadataKey = Symbol.for('seedcord:middleware:interaction:metadata');\nexport const EventMiddlewareMetadataKey = Symbol.for('seedcord:middleware:event:metadata');\nexport const SubscribeMetadataKey = Symbol.for('seedcord:subscribe:metadata');\nexport const EventMetadataKey = Symbol.for('seedcord:event:metadata');\nexport const GatedMetadataKey = Symbol.for('seedcord:gated:metadata');\nexport const WebhookUrlMetadataKey = Symbol.for('seedcord:webhook-url:metadata');\n\nexport const InteractionRouteKeys: Record<InteractionKind, symbol> = {\n [InteractionKind.Slash]: Symbol.for('seedcord:interaction:slash'),\n [InteractionKind.Button]: Symbol.for('seedcord:interaction:button'),\n [InteractionKind.Modal]: Symbol.for('seedcord:interaction:modal'),\n [InteractionKind.StringMenu]: Symbol.for('seedcord:interaction:string-menu'),\n [InteractionKind.UserMenu]: Symbol.for('seedcord:interaction:user-menu'),\n [InteractionKind.RoleMenu]: Symbol.for('seedcord:interaction:role-menu'),\n [InteractionKind.ChannelMenu]: Symbol.for('seedcord:interaction:channel-menu'),\n [InteractionKind.MentionableMenu]: Symbol.for('seedcord:interaction:mentionable-menu'),\n [InteractionKind.MessageContextMenu]: Symbol.for('seedcord:interaction:message-context-menu'),\n [InteractionKind.UserContextMenu]: Symbol.for('seedcord:interaction:user-context-menu'),\n [InteractionKind.Autocomplete]: Symbol.for('seedcord:interaction:autocomplete')\n};\n","import { MessageFlags } from 'discord-api-types/v10';\n\nimport type { DeferOpts, SendOpts } from '@seedcord/types';\n\n// IsComponentsV2 is unconditional, since 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\nexport function deferFlags(opts?: DeferOpts): number {\n return (opts?.ephemeral ?? true) ? MessageFlags.Ephemeral : 0;\n}\n","interface ThrottleWindow {\n lastSentAt: number;\n suppressedSinceSend: number;\n}\n\nexport class ReportThrottle {\n private static readonly perCore = new WeakMap<object, ReportThrottle>();\n\n public static for(core: object): ReportThrottle {\n let throttle = ReportThrottle.perCore.get(core);\n if (!throttle) {\n throttle = new ReportThrottle();\n ReportThrottle.perCore.set(core, throttle);\n }\n return throttle;\n }\n\n private readonly windows = new Map<string, ThrottleWindow>();\n\n constructor(\n private readonly windowMs = 60_000,\n private readonly now: () => number = () => Date.now()\n ) {}\n\n // null while the window is open, otherwise how many were dropped since the last send\n public take(key: string): number | null {\n const window = this.windows.get(key);\n if (window && this.now() - window.lastSentAt < this.windowMs) {\n window.suppressedSinceSend += 1;\n return null;\n }\n\n const suppressed = window?.suppressedSinceSend ?? 0;\n this.windows.set(key, { lastSentAt: this.now(), suppressedSinceSend: 0 });\n return suppressed;\n }\n\n // gives a failed send's count to the open window, plus the one card that never went out\n public restore(key: string, suppressed: number): void {\n const window = this.windows.get(key);\n if (window) window.suppressedSinceSend += suppressed + 1;\n }\n}\n","import { Logger } from '@seedcord/logger';\n\nimport type { CoreBase } from '#interfaces/CoreBase';\nimport type { SubscriptionKey, AllSubscriptions } from './types/Subscriptions';\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 goes inside a ``` code block, so a stray ``` in the text would close it early\nexport function breakBackticks(text: string): string {\n return text.replaceAll('```', '`​`​`');\n}\n\n// discord caps a message at 2000 characters. the backticks and heading take the rest.\nconst REPORT_BUDGET = 1800;\n\n// breakBackticks grows the text by 2 chars per run\nfunction body(value: unknown): string {\n const raw = Error.isError(value) ? (value.stack ?? `${value.name}: ${value.message}`) : String(value);\n return breakBackticks(stripAnsi(raw));\n}\n\nfunction clamp(text: string, budget: number): string {\n if (text.length <= budget) return text;\n return budget > 0 ? `${text.slice(0, budget - 1)}…` : '';\n}\n\nfunction omittedLine(count: number): string {\n return `\\n\\nand ${count} more failure${count === 1 ? '' : 's'}`;\n}\n\nfunction memberReport(members: readonly unknown[], budget: number): string {\n // the dropped-count line has to fit even when every member is dropped\n const room = Math.max(budget - omittedLine(members.length).length, 0);\n const share = Math.floor(room / members.length);\n\n let report = '';\n let shown = 0;\n for (const [index, member] of members.entries()) {\n const heading = `\\n\\nFailure ${index + 1} of ${members.length}:\\n`;\n const entry = heading + clamp(body(member), Math.max(share - heading.length, 0));\n if (report.length + entry.length > room) break;\n report += entry;\n shown++;\n }\n\n const hidden = members.length - shown;\n return hidden > 0 ? report + omittedLine(hidden) : report;\n}\n\n// ansi codes are stripped because discord won't render them\nexport function errorReport(error: Error): string {\n let head = body(error);\n if (Error.isError(error.cause)) head += `\\n\\nCaused by:\\n${body(error.cause)}`;\n\n const members: unknown[] = error instanceof AggregateError ? error.errors : [];\n if (members.length === 0) return clamp(head, REPORT_BUDGET);\n\n // a long parent stack would otherwise push the dropped-count line off the end\n const report = clamp(head, REPORT_BUDGET - omittedLine(members.length).length);\n return report + memberReport(members, REPORT_BUDGET - report.length);\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 { SeedcordTypeError } 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\nexport interface WebhookSendOptions {\n flags: number;\n username: string;\n avatarUrl?: string | undefined;\n components: readonly { toJSON(): APIMessageTopLevelComponent }[];\n files?: readonly WebhookFile[] | undefined;\n}\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 if (!match?.[1] || !match[2])\n throw new SeedcordTypeError(SeedcordErrorCode.ConfigWebhookUrlInvalid, ['The url']);\n this.route = Routes.webhook(match[1], match[2]);\n }\n\n // missing means discord answered 404 or 401\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, avatarUrl, 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 ...(avatarUrl && { avatar_url: avatarUrl }),\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, paint } from '@seedcord/errors';\nimport { SeedcordError, SeedcordTypeError } from '@seedcord/errors/internal';\nimport { Converters, Envapter } from 'envapt';\n\nimport { sendFlags } from '#reply/flags';\nimport { WebhookUrlMetadataKey } from '#src/metadataKeys';\n\nimport { ReportThrottle } from '../ReportThrottle';\nimport { Subscriber } from '../Subscriber';\nimport { isDiscordWebhookUrl } from './webhookHelpers';\nimport { WebhookSender } from './WebhookSender';\n\nimport type { CoreBase } from '#interfaces/CoreBase';\nimport type { WebhookFile } from './WebhookSender';\nimport type { SubscriptionKey } from '../types/Subscriptions';\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 /** Shown as the webhook message avatar. Defaults to the webhook's own. */\n avatarUrl?: 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}. The base\n * resolves the url, validates it, reuses one sender per url, and sends. Registration skips a reporter\n * whose variable is unset and warns at boot.\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 /**\n * Builds the message for one published event.\n *\n * @param suppressed - How many events were dropped since the last card\n */\n abstract report(suppressed: number): WebhookReport | Promise<WebhookReport>;\n\n /**\n * Events sharing this key collapse into one card per minute. Return `null` to send every event.\n */\n protected throttleKey(): string | null {\n return null;\n }\n\n async execute(): Promise<void> {\n const envKey = WebhookLog.envKeyOf(this.constructor);\n const url = WebhookLog.urlOf(envKey);\n // Bus.probeWebhook dropped every url-less reporter at registration. reaching this means the env changed since.\n if (url === null) throw new SeedcordError(SeedcordErrorCode.ConfigMissingEnv, [envKey]);\n\n const key = this.throttleKey();\n const throttle = ReportThrottle.for(this.core);\n const suppressed = key === null ? 0 : throttle.take(key);\n if (suppressed === null) {\n this.logger.debug(`counted a repeat into the next ${paint.sky.bold(this.constructor.name)} card`);\n return;\n }\n\n try {\n const { username, avatarUrl, components, files } = await this.report(suppressed);\n await WebhookLog.senderFor(url).send({\n flags: sendFlags({ ephemeral: false }),\n username: username ?? this.constructor.name,\n avatarUrl,\n components,\n files\n });\n } catch (error) {\n if (key !== null) throttle.restore(key, suppressed);\n this.logger.error('Webhook report failed', 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 SeedcordTypeError(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 { CoreBase } from '#interfaces/CoreBase';\nimport type { Subscriber } from '../Subscriber';\nimport type { SubscriptionKey } from '../types/Subscriptions';\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 * }\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 { CoreBase } from '#interfaces/CoreBase';\nimport type { WebhookLog } from '../bases/WebhookLog';\nimport type { SubscriptionKey } from '../types/Subscriptions';\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 { breakBackticks, jsonAttachment, WebhookSeparator } from '../bases/webhookHelpers';\nimport { WebhookLog } from '../bases/WebhookLog';\nimport { Subscribe } from '../decorators/Subscribe';\nimport { WebhookUrl } from '../decorators/WebhookUrl';\n\nimport type { CoreBase } from '#interfaces/CoreBase';\nimport type { Notice } from '#stops/Notice';\nimport type { WebhookReport } from '../bases/WebhookLog';\nimport type { AllSubscriptions, FaultSource } from '../types/Subscriptions';\n\n// no HANDLED_EXCEPTION_WEBHOOK_URL disables the reporter with a boot warning\n@Subscribe('handledException')\n@WebhookUrl('HANDLED_EXCEPTION_WEBHOOK_URL')\nexport class HandledException extends WebhookLog<'handledException', CoreBase> {\n protected override throttleKey(): string {\n return `handledException:${this.data.origin}:${this.data.denial.name}`;\n }\n\n report(suppressed: number): WebhookReport {\n return {\n username: 'Handled Exception',\n components: [new HandledExceptionContainer(this.data, suppressed).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'], suppressed: number) {\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\\`\\`\\`${breakBackticks(causeStack(denial))}\\`\\`\\``)\n )\n .addSeparatorComponents(new WebhookSeparator().component)\n .addTextDisplayComponents((text) => text.setContent('### Source'))\n .addFileComponents((file) => file.setURL('attachment://source.json'));\n\n this.addSuppressedCount(suppressed);\n }\n\n private addSuppressedCount(suppressed: number): void {\n if (suppressed === 0) return;\n\n this.instance.addTextDisplayComponents((text) =>\n text.setContent(`-# ${suppressed} more of these since the last report`)\n );\n }\n}\n\nfunction faultSummary(denial: Notice, source: FaultSource): string {\n // name and message carry chalk codes\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// building the report must not throw on a bigint or a 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 { CoreBase } from '#interfaces/CoreBase';\nimport type { WebhookReport } from '../bases/WebhookLog';\nimport type { AllSubscriptions } from '../types/Subscriptions';\n\n// no UNKNOWN_EXCEPTION_WEBHOOK_URL disables the reporter with a boot warning\n@Subscribe('unknownException')\n@WebhookUrl('UNKNOWN_EXCEPTION_WEBHOOK_URL')\nexport class UnknownException extends WebhookLog<'unknownException', CoreBase> {\n protected override throttleKey(): string {\n return `unknownException:${this.data.origin}:${nameOf(this.data.error)}`;\n }\n\n report(suppressed: number): WebhookReport {\n const { metadata } = this.data;\n return {\n username: 'Unknown Exception',\n components: [new UnhandledErrorContainer(this.data, suppressed).component],\n files: metadata\n ? [jsonAttachment('metadata.json', 'Metadata associated with the error', metadata)]\n : undefined\n };\n }\n}\n\n// discord.js puts the api code in the name, as `DiscordAPIError[10062]`\nfunction nameOf(error: Error): string {\n return error.name === 'Error' ? error.constructor.name : error.name;\n}\n\nclass UnhandledErrorContainer extends BuilderComponent<'container'> {\n constructor(data: AllSubscriptions['unknownException'], suppressed: number) {\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 this.addSuppressedCount(suppressed);\n }\n\n private addSuppressedCount(suppressed: number): void {\n if (suppressed === 0) return;\n\n this.instance.addTextDisplayComponents((text) =>\n text.setContent(`-# ${suppressed} more of these since the last report`)\n );\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","// the framework's own Bus calls. a bot author reaches the Bus through publish() and a Subscriber subclass\nexport const RegisterDefaults = Symbol('seedcord:bus:register-defaults');\nexport const VerifyWebhooks = Symbol('seedcord:bus:verify-webhooks');\nexport const RegisteredCount = Symbol('seedcord:bus:registered-count');\nexport const RegisterSubscriber = Symbol('seedcord:bus:register');\nexport const UnregisterSubscriber = Symbol('seedcord:bus:unregister');\n","import { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError, SeedcordTypeError } from '@seedcord/errors/internal';\nimport { TypedEventEmitter } from '@seedcord/event-emitter';\nimport { Logger } from '@seedcord/logger';\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';\nimport { RegisterDefaults, RegisteredCount, RegisterSubscriber, UnregisterSubscriber, VerifyWebhooks } from './slots';\n\nimport type { CoreBase } from '#interfaces/CoreBase';\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 { EventFrequency, TypedConstructor } from '@seedcord/types';\nimport type { Constructor } from 'type-fest';\n\n// typed construct parameters would exclude every subscriber that narrows them\n/** @internal */\nexport type StoredSubscriberCtor = Constructor<Subscriber<SubscriptionKey, CoreBase>, never[]>;\n\n/** @internal */\nexport interface SubscriberRegistration {\n readonly keys: readonly SubscriptionKey[];\n readonly frequency: EventFrequency;\n readonly ctor: StoredSubscriberCtor;\n}\n\nconst loggerSlot = Symbol('seedcord:bus:logger');\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 private readonly logger = new Logger('Subscribers', { channel: 'subscribers' });\n\n /** @internal */\n readonly [loggerSlot]: Logger = this.logger;\n\n private readonly subscribersMap = new Map<SubscriptionKey, SubscriberRegistration[]>();\n private readonly executedOnce = new Set<SubscriberRegistration>();\n // url -> env keys\n private readonly webhookProbes = new Map<string, string[]>();\n\n constructor(protected core: CoreBase) {\n super();\n }\n\n /** @internal keep this inside start(). a bad webhook url thrown outside it never resets the singleton guard */\n public [RegisterDefaults](): void {\n this[RegisterSubscriber](registrationFor(UnknownException));\n this[RegisterSubscriber](registrationFor(HandledException));\n }\n\n /** @internal */\n public [RegisterSubscriber](registration: SubscriberRegistration): void {\n if (!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 [UnregisterSubscriber](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 // a re-registered subscriber runs again because executedOnce is keyed by registration identity\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 registration already dropped every reporter with no url set */\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(\n `could not verify webhook at ${envKeys.map((key) => paint.amber.bold(key)).join(', ')}`\n );\n })\n );\n // collected first so one throw names every missing webhook\n if (missing.length > 0) throw new SeedcordError(SeedcordErrorCode.ConfigWebhookNotFound, [missing.join(', ')]);\n }\n\n // true means the class registers\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(`${paint.sky.bold(ctor.name)} disabled, ${paint.amber.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. Subscriber handlers run asynchronously and this returns before they finish.\n * Errors thrown by a subscriber or by an `on()` listener are caught and logged, never surfaced\n * here. One throwing listener does not stop the others. Subscribers on one key run concurrently\n * and carry no ordering guarantee. A `'once'` subscriber is marked as executed when it starts,\n * even if it throws. 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 /**\n * @deprecated Always throws. Call {@link Bus.publish} to reach subscribers and listeners together.\n * @throws A **SeedcordTypeError** with `CoreBusEmitUnavailable`. `emit` reaches only the `on()` listeners.\n */\n public override emit<KeyOfSubscribers extends SubscriptionKey>(\n event: KeyOfSubscribers,\n ..._args: SubscriptionTuples[KeyOfSubscribers]\n ): never {\n throw new SeedcordTypeError(SeedcordErrorCode.CoreBusEmitUnavailable, [String(event)]);\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 say which subscriber threw\n let name = '<unresolved>';\n try {\n // data matches this ctor's payload arm because the registration keyed it to its subscriptions\n const Ctor = entry.ctor as TypedConstructor<typeof Subscriber>;\n name = Ctor.name;\n await new Ctor(data, this.core).execute();\n } catch (err) {\n this.logger.error(\n `Error in subscriber ${paint.mute(String(subscriberName))} handler ${paint.sky.bold(name)}:`,\n err\n );\n }\n }\n}\n\n// every caller already checked that the Subscribe metadata is there\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', ctor };\n}\n\n/** @internal */\nexport function busLoggerOf(bus: Bus): Logger {\n return bus[loggerSlot];\n}\n"],"mappings":"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"}
@@ -89,10 +89,8 @@ var HmrModuleHandler = class {
89
89
  this.store.fileToHandlers.set(file, set);
90
90
  }
91
91
  set.add(handler);
92
- if (this.options.getArtifacts) {
93
- const artifacts = this.options.getArtifacts(handler);
94
- this.store.handlerArtifacts.set(handler, artifacts);
95
- }
92
+ if (!this.options.getArtifacts) return;
93
+ this.store.handlerArtifacts.set(handler, this.options.getArtifacts(handler));
96
94
  }
97
95
  /** Links the middleware to its source file so a later update can unload it. */
98
96
  trackMiddleware(file, middleware) {
@@ -174,4 +172,4 @@ var HmrModuleHandler = class {
174
172
 
175
173
  //#endregion
176
174
  export { HmrModuleHandler as t };
177
- //# sourceMappingURL=HmrModuleHandler-9AUfKXRl.mjs.map
175
+ //# sourceMappingURL=HmrModuleHandler-DOAtah3j.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HmrModuleHandler-DOAtah3j.mjs","names":[],"sources":["../src/hmr/HmrModuleHandler.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { resolve, sep } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport { paint } from '@seedcord/errors';\nimport { formatFilePath } from '@seedcord/utils';\n\nimport type { Logger } from '@seedcord/logger';\nimport type { HmrUpdateEvent } from '@seedcord/types';\n\ninterface HmrStore<THandler, TMiddleware, TArtifacts> {\n fileToHandlers: Map<string, Set<THandler>>;\n fileToMiddlewares: Map<string, Set<TMiddleware>>;\n handlerArtifacts: Map<THandler, TArtifacts>;\n}\n\n// justified: units are caller-supplied classes, name is read for log labels only\nconst displayName = (unit: unknown, fallback: string): string => (unit as { name?: string }).name ?? fallback;\n\ninterface HmrModuleHandlerOptions<THandler, TMiddleware = void, TArtifacts = unknown> {\n handlersDir: string;\n middlewaresDir?: string;\n isHandler: (val: unknown) => val is THandler;\n isMiddleware?: (val: unknown) => val is TMiddleware;\n registerHandler: (handler: THandler, file: string) => void;\n registerMiddleware?: (middleware: TMiddleware, file: string) => void;\n unregisterHandler: (handler: THandler, artifacts?: TArtifacts) => void;\n unregisterMiddleware?: (middleware: TMiddleware) => void;\n /** Snapshotted per handler so a rollback re-registers with the same artifacts after the module re-imports. */\n getArtifacts?: (handler: THandler) => TArtifacts;\n logger: Logger;\n}\n\n/**\n * Reloads THandler and optional TMiddleware modules on HMR updates.\n *\n * Call `handle()` from your `HmrAware.onHmr()` for standard reload behavior, or write a fully custom `onHmr()` and skip this class.\n *\n * This is only useful during development.\n */\nexport class HmrModuleHandler<THandler, TMiddleware = void, TArtifacts = unknown> {\n private readonly store: HmrStore<THandler, TMiddleware, TArtifacts>;\n private readonly options: HmrModuleHandlerOptions<THandler, TMiddleware, TArtifacts>;\n\n constructor(options: HmrModuleHandlerOptions<THandler, TMiddleware, TArtifacts>) {\n this.options = { ...options, logger: options.logger.inChannel('hmr') };\n\n // no import.meta.hot.data stash needed because a leaf reload never reconstructs the singleton\n // holding these Maps\n this.store = {\n fileToHandlers: new Map(),\n fileToMiddlewares: new Map(),\n handlerArtifacts: new Map()\n };\n }\n\n /** Unloads or reloads the modules affected by an {@link HmrUpdateEvent}, scoped to the handler and middleware directories. */\n public async handle(event: HmrUpdateEvent): Promise<void> {\n const { file, affectedModules, type } = event;\n const { logger } = this.options;\n\n if (type === 'delete' || type === 'deleteDir') {\n this.report('Unloaded', this.unload(file), [file]);\n return;\n }\n\n // a directory is never importable. its files arrive as their own create events\n if (type === 'createDir') return;\n\n if (type === 'update' && !existsSync(file)) {\n if (this.isTracked(file)) {\n logger.debug(`Received update for non-existent file: ${formatFilePath(file)}, treating as delete`);\n this.report('Unloaded', this.unload(file), [file]);\n }\n return;\n }\n\n const rollbackEnabled = event.rollback ?? true;\n const filesToReload = affectedModules && affectedModules.length > 0 ? affectedModules : [file];\n\n const startedAt = Date.now();\n const names: string[] = [];\n const touched: string[] = [];\n\n for (const fileToReload of filesToReload) {\n if (!this.inScope(fileToReload)) continue;\n\n const reloaded = await this.reloadWithRollback(fileToReload, rollbackEnabled);\n if (reloaded.length === 0) continue;\n\n names.push(...reloaded);\n touched.push(fileToReload);\n }\n\n this.report(type === 'create' ? 'Registered' : 'Reloaded', names, touched, Date.now() - startedAt);\n }\n\n private inScope(file: string): boolean {\n const { handlersDir, middlewaresDir } = this.options;\n\n // the trailing separator keeps commands-extra out of commands\n if (file.startsWith(resolve(process.cwd(), handlersDir) + sep)) return true;\n return middlewaresDir ? file.startsWith(resolve(process.cwd(), middlewaresDir) + sep) : false;\n }\n\n // one line per save, since one edit can reload every module that imports the file\n private report(verb: string, names: string[], files: string[], elapsedMs?: number): void {\n if (names.length === 0) return;\n\n const from = files.length === 1 ? formatFilePath(files[0] ?? '') : `${String(files.length)} files`;\n const timing = elapsedMs === undefined ? '' : ` ${paint.mute('in')} ${String(elapsedMs)}ms`;\n this.options.logger.debug(`${verb} ${paint.sky.bold(names.join(', '))} from ${paint.mute(from)}${timing}`);\n }\n\n private async reloadWithRollback(file: string, rollbackEnabled: boolean): Promise<string[]> {\n const { logger } = this.options;\n const snapshot = this.snapshotUnits(file);\n this.unload(file);\n\n const reloaded = await this.reloadFile(file);\n if (reloaded !== null) return reloaded;\n\n // a failed reload can leave a partial registration\n this.unload(file);\n if (!rollbackEnabled) return [];\n if (snapshot.handlers.length === 0 && snapshot.middlewares.length === 0) return [];\n\n // a typo keeps the last-good units live until the next good save\n this.restoreUnits(file, snapshot);\n logger.warn(`${paint.amber.bold('Rolled back')} ${paint.mute(formatFilePath(file))} to the last-good version`);\n return [];\n }\n\n /** Links the handler to its source file so a later update can unload it. */\n public trackHandler(file: string, handler: THandler): void {\n let set = this.store.fileToHandlers.get(file);\n if (!set) {\n set = new Set();\n this.store.fileToHandlers.set(file, set);\n }\n set.add(handler);\n\n if (!this.options.getArtifacts) return;\n this.store.handlerArtifacts.set(handler, this.options.getArtifacts(handler));\n }\n\n /** Links the middleware to its source file so a later update can unload it. */\n public trackMiddleware(file: string, middleware: TMiddleware): void {\n let set = this.store.fileToMiddlewares.get(file);\n if (!set) {\n set = new Set();\n this.store.fileToMiddlewares.set(file, set);\n }\n set.add(middleware);\n }\n\n private isTracked(file: string): boolean {\n return this.store.fileToHandlers.has(file) || this.store.fileToMiddlewares.has(file);\n }\n\n private unload(file: string): string[] {\n const names: string[] = [];\n const handlers = this.store.fileToHandlers.get(file);\n if (handlers) {\n for (const handler of handlers) {\n const artifacts = this.store.handlerArtifacts.get(handler);\n this.options.unregisterHandler(handler, artifacts);\n this.store.handlerArtifacts.delete(handler);\n names.push(displayName(handler, 'Handler'));\n }\n this.store.fileToHandlers.delete(file);\n }\n\n const middlewares = this.store.fileToMiddlewares.get(file);\n if (middlewares) {\n for (const middleware of middlewares) {\n this.options.unregisterMiddleware?.(middleware);\n names.push(displayName(middleware, 'Middleware'));\n }\n this.store.fileToMiddlewares.delete(file);\n }\n\n return names;\n }\n\n private async reloadFile(file: string): Promise<string[] | null> {\n const { logger, isHandler, isMiddleware, registerHandler, registerMiddleware } = this.options;\n\n if (!existsSync(file)) {\n logger.debug(`File does not exist, skipping reload: ${formatFilePath(file)}`);\n return null;\n }\n\n try {\n let fileUrl = pathToFileURL(file).href;\n // vitest has no real vite server managing the module graph\n if (process.env.VITEST === 'true') fileUrl += `?update=${Date.now()}`;\n\n // justified: a dynamic import resolves to an untyped export map\n const imported = (await import(fileUrl)) as Record<string, unknown>;\n\n const names: string[] = [];\n for (const val of Object.values(imported)) {\n if (isHandler(val)) {\n registerHandler(val, file);\n this.trackHandler(file, val);\n names.push(displayName(val, 'Handler'));\n } else if (isMiddleware && registerMiddleware && isMiddleware(val)) {\n registerMiddleware(val, file);\n this.trackMiddleware(file, val);\n names.push(displayName(val, 'Middleware'));\n }\n }\n\n return names;\n } catch (error) {\n logger.error(`Failed to reload file: ${file}`, error);\n return null;\n }\n }\n\n // these are the same class objects, which is how a live db connection survives a rollback\n private snapshotUnits(file: string): { handlers: THandler[]; middlewares: TMiddleware[] } {\n return {\n handlers: [...(this.store.fileToHandlers.get(file) ?? [])],\n middlewares: [...(this.store.fileToMiddlewares.get(file) ?? [])]\n };\n }\n\n private restoreUnits(file: string, snapshot: { handlers: THandler[]; middlewares: TMiddleware[] }): void {\n for (const handler of snapshot.handlers) {\n this.options.registerHandler(handler, file);\n this.trackHandler(file, handler);\n }\n\n if (this.options.registerMiddleware) {\n for (const middleware of snapshot.middlewares) {\n this.options.registerMiddleware(middleware, file);\n this.trackMiddleware(file, middleware);\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -216,13 +216,7 @@ var Pluggable = class Pluggable {
216
216
  registerPluginTasks() {
217
217
  if (this.pluginTasksRegistered) return;
218
218
  this.pluginTasksRegistered = true;
219
- const groups = /* @__PURE__ */ new Map();
220
- for (const attachment of this.attachments) {
221
- const phase = resolvedLifecycleSpecOf(attachment.instance).init.phase;
222
- const group = groups.get(phase) ?? [];
223
- group.push(attachment);
224
- groups.set(phase, group);
225
- }
219
+ const groups = Map.groupBy(this.attachments, (attachment) => resolvedLifecycleSpecOf(attachment.instance).init.phase);
226
220
  for (const [phase, group] of groups) {
227
221
  if (phase === 3) continue;
228
222
  const budget = group.reduce((sum, a) => sum + resolvedLifecycleSpecOf(a.instance).init.timeout, 0);
@@ -313,4 +307,4 @@ function shutdownOf(host) {
313
307
 
314
308
  //#endregion
315
309
  export { withTimeout as a, settleWithin as i, shutdownOf as n, assertNodeVersion as r, Pluggable as t };
316
- //# sourceMappingURL=Pluggable-D-xWcaIv.mjs.map
310
+ //# sourceMappingURL=Pluggable-CXyUZIvT.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Pluggable-D-xWcaIv.mjs","names":[],"sources":["../src/node/Lifecycle/withTimeout.ts","../src/node/assertNodeVersion.ts","../src/node/processErrors.ts","../src/node/Pluggable.ts"],"sourcesContent":["import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\n// calling start() in here turns its synchronous throw into a rejection\nasync function raceTimer(\n start: () => Promise<unknown>,\n timeoutMs: number,\n onTimeout: (resolve: () => void, reject: (error: unknown) => void) => void\n): Promise<void> {\n let timer: NodeJS.Timeout | undefined;\n try {\n await Promise.race([\n start(),\n new Promise<void>((resolve, reject) => {\n // node's timer can go off before performance.now() reaches timeoutMs\n const expiresAt = performance.now() + timeoutMs;\n const tick = (): void => {\n const remaining = expiresAt - performance.now();\n if (remaining > 0) {\n timer = setTimeout(tick, remaining);\n return;\n }\n onTimeout(resolve, reject);\n };\n timer = setTimeout(tick, timeoutMs);\n })\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\n// rejects with a LifecycleTaskTimeout when run outlasts the bound\nexport function withTimeout(name: string, run: () => Promise<void>, timeoutMs: number): Promise<void> {\n return raceTimer(run, timeoutMs, (_, reject) => {\n reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [name, timeoutMs]));\n });\n}\n\n// the caller does not get work's rejection\nexport function settleWithin(work: Promise<unknown>, timeoutMs: number): Promise<void> {\n return raceTimer(\n () =>\n work.then(\n () => undefined,\n () => undefined\n ),\n timeoutMs,\n (resolve) => resolve()\n );\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\n// every seedcord package declares engines.node as `>=major` or `>=major.minor`\nconst RANGE = new RegExp(String.raw`^>=(\\d+)(?:\\.(\\d+))?`);\nconst VERSION = /^v?(\\d+)\\.(\\d+)/;\nconst SPACES = /\\s/g;\n\n/** @internal */\nexport function assertNodeVersion(range: string, running: string): void {\n const required = RANGE.exec(range.replaceAll(SPACES, ''));\n const current = VERSION.exec(running);\n if (!required || !current) return;\n\n const [, requiredMajor, requiredMinor = '0'] = required;\n const [, currentMajor, currentMinor] = current;\n\n const meetsRange =\n Number(currentMajor) > Number(requiredMajor) ||\n (Number(currentMajor) === Number(requiredMajor) && Number(currentMinor) >= Number(requiredMinor));\n if (meetsRange) return;\n\n throw new SeedcordError(SeedcordErrorCode.UnsupportedNodeVersion, [range, running]);\n}\n","import crypto from 'node:crypto';\n\nimport { Logger } from '@seedcord/logger';\n\nimport { asError } from '#stops/asError';\nimport { PublishDefault } from '#subscribers/publishDefault';\n\nimport type { CoreBase } from '#interfaces/CoreBase';\nimport type { CoordinatedShutdown } from '#node/Lifecycle/CoordinatedShutdown';\n\nconst logger = new Logger('Faults', { channel: 'errors' });\n\n/** @internal */\nexport function registerProcessErrors(core: CoreBase, shutdown: CoordinatedShutdown): () => void {\n function report(caught: unknown, origin: string): void {\n const error = asError(caught);\n const uuid = crypto.randomUUID();\n\n if (core.config.errors?.errorStack ?? false) logger.error(uuid, error);\n else logger.error(`${uuid} | ${error.message}`);\n\n core.bus[PublishDefault]('unknownException', { uuid, dispatchId: null, error, origin });\n }\n\n const onRejection = (reason: unknown): void => {\n report(reason, 'process:unhandledRejection');\n };\n\n const onException = (caught: unknown): void => {\n report(caught, 'process:uncaughtException');\n // node does not exit on its own once a listener is registered\n void shutdown.run(1);\n };\n\n process.on('unhandledRejection', onRejection);\n process.on('uncaughtException', onException);\n\n return () => {\n process.off('unhandledRejection', onRejection);\n process.off('uncaughtException', onException);\n };\n}\n","import { SeedcordErrorCode, isSeedcordError } from '@seedcord/errors';\nimport { SeedcordAggregateError, SeedcordError, SeedcordTypeError } from '@seedcord/errors/internal';\nimport { FRAMEWORK_CHANNELS, Logger } from '@seedcord/logger';\nimport { HostPluginKeys, HostShutdown, HostStartup } from '@seedcord/types/internal';\n\nimport { assertNodeVersion } from '#node/assertNodeVersion';\nimport { StartupPhase } from '#src/lifecycle/phases';\nimport { extendsThisCorePlugin, pluginLoggerOf, resolvedLifecycleSpecOf } from '#src/plugin/Plugin';\n\nimport { withTimeout } from './Lifecycle/withTimeout';\nimport { registerProcessErrors } from './processErrors';\n\nimport type { CoreBase } from '#interfaces/CoreBase';\nimport type { CoordinatedShutdown } from '#node/Lifecycle/CoordinatedShutdown';\nimport type { CoordinatedStartup } from '#node/Lifecycle/CoordinatedStartup';\nimport type { ShutdownPhase } from '#src/lifecycle/phases';\nimport type { Runtime, Transport } from '#src/plugin/options';\nimport type { AttachableCtor, Attached, AttachKeyAssert, PluginArgs, PluginCtor, PluginLike } from '#src/plugin/Plugin';\nimport type { Bus } from '#subscribers/Bus';\nimport type { REST } from '@discordjs/rest';\nimport type { Config, IRateLimiter } from '@seedcord/types';\n\ninterface Attachment {\n readonly key: string;\n readonly instance: PluginLike;\n}\n\nconst RESERVED_KEYS: ReadonlySet<string> = new Set(FRAMEWORK_CHANNELS);\n\n/**\n * Base class for a plugin host, a transport `Seedcord` class.\n *\n * You attach plugins while configuring the bot. Within one startup phase, their `init()` calls run\n * one after another in attach order.\n */\n// BotRt has no default because RuntimeAssert rejects every plugin once 'edge' is in the union\nexport abstract class Pluggable<BotT extends Transport, BotRt extends Runtime> implements CoreBase {\n public abstract readonly config: Config;\n public abstract readonly rest: REST;\n public abstract readonly applicationId: string;\n public abstract readonly rateLimiter: IRateLimiter;\n public abstract readonly bus: Bus;\n\n /** @internal */\n readonly [HostShutdown]: CoordinatedShutdown;\n /** @internal */\n readonly [HostStartup]: CoordinatedStartup;\n\n /** Add a task that runs while the bot shuts down. */\n public readonly shutdown: Pick<CoordinatedShutdown, 'addTask'>;\n\n /** Add a task that runs while the bot starts. */\n public readonly startup: Pick<CoordinatedStartup, 'addTask'>;\n\n protected isInitialized = false;\n protected startFailed = false;\n protected readonly plugins: PluginLike[] = [];\n private readonly groups = new Map<string, Record<string, PluginLike>>();\n\n private readonly pluginLogger = new Logger('Plugins', { channel: 'plugins' });\n\n private readonly attachments: Attachment[] = [];\n private readonly completedInits = new Set<Attachment>();\n private readonly disposePhases = new Set<ShutdownPhase>();\n private pluginTasksRegistered = false;\n private initPromise?: Promise<this> | undefined;\n\n private static isInstantiated = false;\n private static liveHost?: object | undefined;\n private static liveShutdown?: CoordinatedShutdown | undefined;\n private static liveProcessErrors?: (() => void) | undefined;\n\n constructor(shutdown: CoordinatedShutdown, startup: CoordinatedStartup) {\n // a `sideEffects: false` build would drop the same call in the node entry\n assertNodeVersion(process.env.PACKAGE_NODE_RANGE ?? '', process.version);\n\n if (Pluggable.isInstantiated) {\n // signal handlers from the caller's shutdown stay on the process unless dropped here\n shutdown.removeSignalHandlers();\n throw new SeedcordError(SeedcordErrorCode.CoreSingletonViolation);\n }\n\n Pluggable.isInstantiated = true;\n Pluggable.liveHost = this;\n Pluggable.liveShutdown = shutdown;\n this[HostShutdown] = shutdown;\n this[HostStartup] = startup;\n // a getter returning the slot would expose run() and the signal handlers too\n this.shutdown = { addTask: shutdown.addTask.bind(shutdown) };\n this.startup = { addTask: startup.addTask.bind(startup) };\n }\n\n /** @internal */\n protected init(): Promise<this> {\n // clearing the slot on a rejection means a later retry throws its own error\n this.initPromise ??= this.runInit().catch((caught: unknown) => {\n this.initPromise = undefined;\n throw caught;\n });\n return this.initPromise;\n }\n\n private async runInit(): Promise<this> {\n if (this.isInitialized) return this;\n // a rerun after a failed startup would re-init the rolled-back plugins\n if (this.startFailed) throw new SeedcordError(SeedcordErrorCode.LifecycleRestartAfterFailure);\n\n this.registerPluginTasks();\n\n if (this.config.errors?.catchProcessErrors ?? true) {\n Pluggable.liveProcessErrors = registerProcessErrors(this, this[HostShutdown]);\n }\n\n const startupSettled: PromiseWithResolvers<void> = Promise.withResolvers();\n this[HostShutdown].gateOnStartup(startupSettled.promise);\n\n try {\n await this[HostStartup].run();\n } catch (caught) {\n await this.rollback();\n throw caught;\n } finally {\n startupSettled.resolve();\n }\n\n this.isInitialized = true;\n return this;\n }\n\n /** @internal codegen reads this to emit the `Core` augmentation */\n public get [HostPluginKeys](): readonly string[] {\n return this.attachments.map((attachment) => attachment.key);\n }\n\n /** @internal */\n protected static reset(host?: object): boolean {\n if (host !== undefined && Pluggable.liveHost !== host) return false;\n\n Pluggable.liveShutdown?.removeSignalHandlers();\n Pluggable.liveShutdown = undefined;\n Pluggable.liveProcessErrors?.();\n Pluggable.liveProcessErrors = undefined;\n Pluggable.liveHost = undefined;\n Pluggable.isInstantiated = false;\n return true;\n }\n\n /**\n * Attaches a plugin under `key`. Read the instance back as `core[key]`. `seedcord codegen`\n * writes the `Core` augmentation that types it there.\n *\n * Put one dot in the key to nest the plugin under a group, so `'services.users'` reads back as\n * `core.services.users`. Each name holds one plugin or one group.\n *\n * Startup runs each plugin's `init()` in attach order within its phase.\n *\n * Attaching a plugin whose `transport` or `runtime` differs from this host fails to compile.\n * Your constructor takes `CoreBase` as its first parameter (you don't need to pass it though).\n * A narrower one fails to compile here.\n *\n * @param key - Also the channel the plugin logs on, dots included. Reserved channel names throw.\n * @param args - Whatever your constructor takes after the host.\n * @throws A **SeedcordError** if you attach after the bot has started. A taken or reserved key throws too.\n * So does a plugin that extends `Plugin` from another copy of `@seedcord/core`.\n * @example\n * ```ts\n * seedcord.attach('db', Mongoose, { uri: 'mongodb://...', name: 'seedcord', dir: ... });\n * ```\n */\n public attach<Key extends string, Ctor extends PluginCtor>(\n this: this,\n key: AttachKeyAssert<Key, this>,\n Plugin: AttachableCtor<Ctor, BotT, BotRt>,\n ...args: PluginArgs<Ctor>\n ): this & Attached<Key, InstanceType<Ctor>>;\n public attach<Key extends string, Ctor extends PluginCtor>(\n this: this,\n key: Key,\n Plugin: Ctor,\n ...args: PluginArgs<Ctor>\n ): this & Attached<Key, InstanceType<Ctor>> {\n if (this.isInitialized) {\n throw new SeedcordError(SeedcordErrorCode.CorePluginAfterInit);\n }\n\n const dot = key.indexOf('.');\n const head = dot === -1 ? key : key.slice(0, dot);\n\n // several reserved channels are also members on a host, which the next check would report first\n if (RESERVED_KEYS.has(head)) {\n throw new SeedcordTypeError(SeedcordErrorCode.CorePluginReservedChannel, [head]);\n }\n\n const leaf = dot === -1 ? undefined : key.slice(dot + 1);\n if (head === '' || leaf === '') {\n throw new SeedcordTypeError(SeedcordErrorCode.CorePluginKeyMalformed, [key, 'has an empty part.']);\n }\n if (leaf?.includes('.')) {\n throw new SeedcordTypeError(SeedcordErrorCode.CorePluginKeyMalformed, [key, 'has more than one dot.']);\n }\n this.assertFree(head, leaf, key);\n\n if (!extendsThisCorePlugin(Plugin)) {\n throw new SeedcordTypeError(SeedcordErrorCode.CorePluginFromOtherCore, [Plugin.name]);\n }\n\n const instance = new Plugin(this, ...args);\n pluginLoggerOf(instance).setChannel(key);\n this.plugins.push(instance);\n this.attachments.push({ key, instance });\n\n if (leaf === undefined) {\n return Object.assign(this, { [key]: instance }) as this & Attached<Key, InstanceType<Ctor>>;\n }\n\n this.groupFor(head)[leaf] = instance;\n return this as this & Attached<Key, InstanceType<Ctor>>;\n }\n\n private assertFree(head: string, leaf: string | undefined, key: string): void {\n if (leaf === undefined) {\n if (this.groups.has(key)) throw new SeedcordError(SeedcordErrorCode.CorePluginKeyHoldsGroup, [key]);\n if (key in this) throw new SeedcordError(SeedcordErrorCode.CorePluginKeyExists, [key]);\n return;\n }\n\n const group = this.groups.get(head);\n if (!group) {\n if (head in this) throw new SeedcordError(SeedcordErrorCode.CorePluginGroupTaken, [head, key]);\n return;\n }\n if (Object.hasOwn(group, leaf)) throw new SeedcordError(SeedcordErrorCode.CorePluginKeyExists, [key]);\n }\n\n private groupFor(head: string): Record<string, PluginLike> {\n const existing = this.groups.get(head);\n if (existing) return existing;\n\n // a null prototype makes a leaf called __proto__ or valueOf an ordinary key\n const group = Object.create(null) as Record<string, PluginLike>;\n this.groups.set(head, group);\n Object.assign(this, { [head]: group });\n return group;\n }\n\n // one combined task per phase keeps plugin inits sequential while the phase's other tasks run concurrently\n private registerPluginTasks(): void {\n if (this.pluginTasksRegistered) return;\n this.pluginTasksRegistered = true;\n\n const groups = new Map<StartupPhase, Attachment[]>();\n for (const attachment of this.attachments) {\n const phase = resolvedLifecycleSpecOf(attachment.instance).init.phase;\n const group = groups.get(phase) ?? [];\n group.push(attachment);\n groups.set(phase, group);\n }\n\n for (const [phase, group] of groups) {\n // Ready inits run in the combined Ready task, before the ready hooks\n if (phase === StartupPhase.Ready) continue;\n const budget = group.reduce((sum, a) => sum + resolvedLifecycleSpecOf(a.instance).init.timeout, 0);\n this.startup.addTask(phase, 'plugins-init', () => this.runInits(group), budget);\n }\n\n this.registerReadyTask(groups.get(StartupPhase.Ready) ?? []);\n }\n\n private async runInits(group: readonly Attachment[]): Promise<void> {\n for (const attachment of group) {\n const { key, instance } = attachment;\n const spec = resolvedLifecycleSpecOf(instance);\n\n pluginLoggerOf(instance).utils.initialization(key, 'start');\n const running = instance.init();\n try {\n await withTimeout(`Plugin (${key})`, () => running, spec.init.timeout);\n } catch (caught) {\n if (isSeedcordError(caught, undefined, SeedcordErrorCode.LifecycleTaskTimeout)) {\n this.disposeWhenInitResolves(attachment, running);\n }\n throw caught;\n }\n pluginLoggerOf(instance).utils.initialization(key, 'end');\n\n this.completedInits.add(attachment);\n if (instance.dispose) this.registerDisposeTask(spec.dispose.phase);\n }\n }\n\n // disposeCompleted skips a plugin missing from completedInits\n private disposeWhenInitResolves(attachment: Attachment, running: Promise<void>): void {\n const dispose = attachment.instance.dispose?.bind(attachment.instance);\n const spec = resolvedLifecycleSpecOf(attachment.instance);\n\n void running.then(\n async () => {\n if (!dispose) return;\n await withTimeout(`Plugin:${attachment.key}:dispose`, dispose, spec.dispose.timeout).catch(\n (caught: unknown) =>\n this.pluginLogger.warn(`${attachment.key} dispose failed after a timed-out init`, caught)\n );\n },\n (caught: unknown) => this.pluginLogger.warn(`${attachment.key} init failed after its timeout`, caught)\n );\n }\n\n private registerReadyTask(readyInits: readonly Attachment[]): void {\n const steps = this.attachments.reduce<{ key: string; run: () => Promise<void>; timeout: number }[]>(\n (acc, a) => {\n const ready = a.instance.ready?.bind(a.instance);\n if (ready) {\n acc.push({\n key: a.key,\n run: ready,\n timeout: resolvedLifecycleSpecOf(a.instance).ready.timeout\n });\n }\n return acc;\n },\n []\n );\n if (readyInits.length === 0 && steps.length === 0) return;\n\n const budget =\n readyInits.reduce((sum, a) => sum + resolvedLifecycleSpecOf(a.instance).init.timeout, 0) +\n steps.reduce((sum, step) => sum + step.timeout, 0);\n this.startup.addTask(\n StartupPhase.Ready,\n 'plugins-ready',\n async () => {\n await this.runInits(readyInits);\n for (const step of steps) {\n await withTimeout(`Plugin:${step.key}:ready`, step.run, step.timeout);\n }\n },\n budget\n );\n }\n\n private registerDisposeTask(phase: ShutdownPhase): void {\n if (this.disposePhases.has(phase)) return;\n this.disposePhases.add(phase);\n\n // the budget covers every dispose in this phase because they all run in this one task\n const budget = this.attachments.reduce((sum, a) => {\n const spec = resolvedLifecycleSpecOf(a.instance);\n return a.instance.dispose && spec.dispose.phase === phase ? sum + spec.dispose.timeout : sum;\n }, 0);\n this.shutdown.addTask(phase, 'plugins-dispose', () => this.runDisposals(phase), budget);\n }\n\n private async runDisposals(phase: ShutdownPhase): Promise<void> {\n const failures: unknown[] = [];\n await this.disposeCompleted(phase, (caught) => failures.push(caught));\n if (failures.length === 1) throw failures[0];\n if (failures.length > 1) {\n throw new SeedcordAggregateError(SeedcordErrorCode.PluginDisposeFailures, failures, [failures.length]);\n }\n }\n\n private async disposeCompleted(\n phase: ShutdownPhase | undefined,\n onError: (caught: unknown) => void\n ): Promise<void> {\n for (const attachment of [...this.attachments].reverse()) {\n if (!this.completedInits.has(attachment)) continue;\n const dispose = attachment.instance.dispose?.bind(attachment.instance);\n if (!dispose) continue;\n const spec = resolvedLifecycleSpecOf(attachment.instance);\n if (phase !== undefined && spec.dispose.phase !== phase) continue;\n\n try {\n await withTimeout(`Plugin:${attachment.key}:dispose`, dispose, spec.dispose.timeout);\n } catch (caught) {\n onError(caught);\n }\n }\n }\n\n private async rollback(): Promise<void> {\n this.startFailed = true;\n // log rollback dispose failures so they do not hide the startup error\n await this.disposeCompleted(undefined, (caught) => this.pluginLogger.warn('rollback dispose failed', caught));\n // prevents re-dispose when shutdown runs after rollback\n this.completedInits.clear();\n }\n}\n\n// the public `shutdown` field carries addTask alone\nexport function shutdownOf(host: Pick<Pluggable<Transport, Runtime>, typeof HostShutdown>): CoordinatedShutdown {\n return host[HostShutdown];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Pluggable-CXyUZIvT.mjs","names":[],"sources":["../src/node/Lifecycle/withTimeout.ts","../src/node/assertNodeVersion.ts","../src/node/processErrors.ts","../src/node/Pluggable.ts"],"sourcesContent":["import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\n// calling start() in here turns its synchronous throw into a rejection\nasync function raceTimer(\n start: () => Promise<unknown>,\n timeoutMs: number,\n onTimeout: (resolve: () => void, reject: (error: unknown) => void) => void\n): Promise<void> {\n let timer: NodeJS.Timeout | undefined;\n try {\n await Promise.race([\n start(),\n new Promise<void>((resolve, reject) => {\n // node's timer can go off before performance.now() reaches timeoutMs\n const expiresAt = performance.now() + timeoutMs;\n const tick = (): void => {\n const remaining = expiresAt - performance.now();\n if (remaining > 0) {\n timer = setTimeout(tick, remaining);\n return;\n }\n onTimeout(resolve, reject);\n };\n timer = setTimeout(tick, timeoutMs);\n })\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\n// rejects with a LifecycleTaskTimeout when run outlasts the bound\nexport function withTimeout(name: string, run: () => Promise<void>, timeoutMs: number): Promise<void> {\n return raceTimer(run, timeoutMs, (_, reject) => {\n reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [name, timeoutMs]));\n });\n}\n\n// the caller does not get work's rejection\nexport function settleWithin(work: Promise<unknown>, timeoutMs: number): Promise<void> {\n return raceTimer(\n () =>\n work.then(\n () => undefined,\n () => undefined\n ),\n timeoutMs,\n (resolve) => resolve()\n );\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\n// every seedcord package declares engines.node as `>=major` or `>=major.minor`\nconst RANGE = new RegExp(String.raw`^>=(\\d+)(?:\\.(\\d+))?`);\nconst VERSION = /^v?(\\d+)\\.(\\d+)/;\nconst SPACES = /\\s/g;\n\n/** @internal */\nexport function assertNodeVersion(range: string, running: string): void {\n const required = RANGE.exec(range.replaceAll(SPACES, ''));\n const current = VERSION.exec(running);\n if (!required || !current) return;\n\n const [, requiredMajor, requiredMinor = '0'] = required;\n const [, currentMajor, currentMinor] = current;\n\n const meetsRange =\n Number(currentMajor) > Number(requiredMajor) ||\n (Number(currentMajor) === Number(requiredMajor) && Number(currentMinor) >= Number(requiredMinor));\n if (meetsRange) return;\n\n throw new SeedcordError(SeedcordErrorCode.UnsupportedNodeVersion, [range, running]);\n}\n","import crypto from 'node:crypto';\n\nimport { Logger } from '@seedcord/logger';\n\nimport { asError } from '#stops/asError';\nimport { PublishDefault } from '#subscribers/publishDefault';\n\nimport type { CoreBase } from '#interfaces/CoreBase';\nimport type { CoordinatedShutdown } from '#node/Lifecycle/CoordinatedShutdown';\n\nconst logger = new Logger('Faults', { channel: 'errors' });\n\n/** @internal */\nexport function registerProcessErrors(core: CoreBase, shutdown: CoordinatedShutdown): () => void {\n function report(caught: unknown, origin: string): void {\n const error = asError(caught);\n const uuid = crypto.randomUUID();\n\n if (core.config.errors?.errorStack ?? false) logger.error(uuid, error);\n else logger.error(`${uuid} | ${error.message}`);\n\n core.bus[PublishDefault]('unknownException', { uuid, dispatchId: null, error, origin });\n }\n\n const onRejection = (reason: unknown): void => {\n report(reason, 'process:unhandledRejection');\n };\n\n const onException = (caught: unknown): void => {\n report(caught, 'process:uncaughtException');\n // node does not exit on its own once a listener is registered\n void shutdown.run(1);\n };\n\n process.on('unhandledRejection', onRejection);\n process.on('uncaughtException', onException);\n\n return () => {\n process.off('unhandledRejection', onRejection);\n process.off('uncaughtException', onException);\n };\n}\n","import { SeedcordErrorCode, isSeedcordError } from '@seedcord/errors';\nimport { SeedcordAggregateError, SeedcordError, SeedcordTypeError } from '@seedcord/errors/internal';\nimport { FRAMEWORK_CHANNELS, Logger } from '@seedcord/logger';\nimport { HostPluginKeys, HostShutdown, HostStartup } from '@seedcord/types/internal';\n\nimport { assertNodeVersion } from '#node/assertNodeVersion';\nimport { StartupPhase } from '#src/lifecycle/phases';\nimport { extendsThisCorePlugin, pluginLoggerOf, resolvedLifecycleSpecOf } from '#src/plugin/Plugin';\n\nimport { withTimeout } from './Lifecycle/withTimeout';\nimport { registerProcessErrors } from './processErrors';\n\nimport type { CoreBase } from '#interfaces/CoreBase';\nimport type { CoordinatedShutdown } from '#node/Lifecycle/CoordinatedShutdown';\nimport type { CoordinatedStartup } from '#node/Lifecycle/CoordinatedStartup';\nimport type { ShutdownPhase } from '#src/lifecycle/phases';\nimport type { Runtime, Transport } from '#src/plugin/options';\nimport type { AttachableCtor, Attached, AttachKeyAssert, PluginArgs, PluginCtor, PluginLike } from '#src/plugin/Plugin';\nimport type { Bus } from '#subscribers/Bus';\nimport type { REST } from '@discordjs/rest';\nimport type { Config, IRateLimiter } from '@seedcord/types';\n\ninterface Attachment {\n readonly key: string;\n readonly instance: PluginLike;\n}\n\nconst RESERVED_KEYS: ReadonlySet<string> = new Set(FRAMEWORK_CHANNELS);\n\n/**\n * Base class for a plugin host, a transport `Seedcord` class.\n *\n * You attach plugins while configuring the bot. Within one startup phase, their `init()` calls run\n * one after another in attach order.\n */\n// BotRt has no default because RuntimeAssert rejects every plugin once 'edge' is in the union\nexport abstract class Pluggable<BotT extends Transport, BotRt extends Runtime> implements CoreBase {\n public abstract readonly config: Config;\n public abstract readonly rest: REST;\n public abstract readonly applicationId: string;\n public abstract readonly rateLimiter: IRateLimiter;\n public abstract readonly bus: Bus;\n\n /** @internal */\n readonly [HostShutdown]: CoordinatedShutdown;\n /** @internal */\n readonly [HostStartup]: CoordinatedStartup;\n\n /** Add a task that runs while the bot shuts down. */\n public readonly shutdown: Pick<CoordinatedShutdown, 'addTask'>;\n\n /** Add a task that runs while the bot starts. */\n public readonly startup: Pick<CoordinatedStartup, 'addTask'>;\n\n protected isInitialized = false;\n protected startFailed = false;\n protected readonly plugins: PluginLike[] = [];\n private readonly groups = new Map<string, Record<string, PluginLike>>();\n\n private readonly pluginLogger = new Logger('Plugins', { channel: 'plugins' });\n\n private readonly attachments: Attachment[] = [];\n private readonly completedInits = new Set<Attachment>();\n private readonly disposePhases = new Set<ShutdownPhase>();\n private pluginTasksRegistered = false;\n private initPromise?: Promise<this> | undefined;\n\n private static isInstantiated = false;\n private static liveHost?: object | undefined;\n private static liveShutdown?: CoordinatedShutdown | undefined;\n private static liveProcessErrors?: (() => void) | undefined;\n\n constructor(shutdown: CoordinatedShutdown, startup: CoordinatedStartup) {\n // a `sideEffects: false` build would drop the same call in the node entry\n assertNodeVersion(process.env.PACKAGE_NODE_RANGE ?? '', process.version);\n\n if (Pluggable.isInstantiated) {\n // signal handlers from the caller's shutdown stay on the process unless dropped here\n shutdown.removeSignalHandlers();\n throw new SeedcordError(SeedcordErrorCode.CoreSingletonViolation);\n }\n\n Pluggable.isInstantiated = true;\n Pluggable.liveHost = this;\n Pluggable.liveShutdown = shutdown;\n this[HostShutdown] = shutdown;\n this[HostStartup] = startup;\n // a getter returning the slot would expose run() and the signal handlers too\n this.shutdown = { addTask: shutdown.addTask.bind(shutdown) };\n this.startup = { addTask: startup.addTask.bind(startup) };\n }\n\n /** @internal */\n protected init(): Promise<this> {\n // clearing the slot on a rejection means a later retry throws its own error\n this.initPromise ??= this.runInit().catch((caught: unknown) => {\n this.initPromise = undefined;\n throw caught;\n });\n return this.initPromise;\n }\n\n private async runInit(): Promise<this> {\n if (this.isInitialized) return this;\n // a rerun after a failed startup would re-init the rolled-back plugins\n if (this.startFailed) throw new SeedcordError(SeedcordErrorCode.LifecycleRestartAfterFailure);\n\n this.registerPluginTasks();\n\n if (this.config.errors?.catchProcessErrors ?? true) {\n Pluggable.liveProcessErrors = registerProcessErrors(this, this[HostShutdown]);\n }\n\n const startupSettled: PromiseWithResolvers<void> = Promise.withResolvers();\n this[HostShutdown].gateOnStartup(startupSettled.promise);\n\n try {\n await this[HostStartup].run();\n } catch (caught) {\n await this.rollback();\n throw caught;\n } finally {\n startupSettled.resolve();\n }\n\n this.isInitialized = true;\n return this;\n }\n\n /** @internal codegen reads this to emit the `Core` augmentation */\n public get [HostPluginKeys](): readonly string[] {\n return this.attachments.map((attachment) => attachment.key);\n }\n\n /** @internal */\n protected static reset(host?: object): boolean {\n if (host !== undefined && Pluggable.liveHost !== host) return false;\n\n Pluggable.liveShutdown?.removeSignalHandlers();\n Pluggable.liveShutdown = undefined;\n Pluggable.liveProcessErrors?.();\n Pluggable.liveProcessErrors = undefined;\n Pluggable.liveHost = undefined;\n Pluggable.isInstantiated = false;\n return true;\n }\n\n /**\n * Attaches a plugin under `key`. Read the instance back as `core[key]`. `seedcord codegen`\n * writes the `Core` augmentation that types it there.\n *\n * Put one dot in the key to nest the plugin under a group, so `'services.users'` reads back as\n * `core.services.users`. Each name holds one plugin or one group.\n *\n * Startup runs each plugin's `init()` in attach order within its phase.\n *\n * Attaching a plugin whose `transport` or `runtime` differs from this host fails to compile.\n * Your constructor takes `CoreBase` as its first parameter (you don't need to pass it though).\n * A narrower one fails to compile here.\n *\n * @param key - Also the channel the plugin logs on, dots included. Reserved channel names throw.\n * @param args - Whatever your constructor takes after the host.\n * @throws A **SeedcordError** if you attach after the bot has started. A taken or reserved key throws too.\n * So does a plugin that extends `Plugin` from another copy of `@seedcord/core`.\n * @example\n * ```ts\n * seedcord.attach('db', Mongoose, { uri: 'mongodb://...', name: 'seedcord', dir: ... });\n * ```\n */\n public attach<Key extends string, Ctor extends PluginCtor>(\n this: this,\n key: AttachKeyAssert<Key, this>,\n Plugin: AttachableCtor<Ctor, BotT, BotRt>,\n ...args: PluginArgs<Ctor>\n ): this & Attached<Key, InstanceType<Ctor>>;\n public attach<Key extends string, Ctor extends PluginCtor>(\n this: this,\n key: Key,\n Plugin: Ctor,\n ...args: PluginArgs<Ctor>\n ): this & Attached<Key, InstanceType<Ctor>> {\n if (this.isInitialized) {\n throw new SeedcordError(SeedcordErrorCode.CorePluginAfterInit);\n }\n\n const dot = key.indexOf('.');\n const head = dot === -1 ? key : key.slice(0, dot);\n\n // several reserved channels are also members on a host, which the next check would report first\n if (RESERVED_KEYS.has(head)) {\n throw new SeedcordTypeError(SeedcordErrorCode.CorePluginReservedChannel, [head]);\n }\n\n const leaf = dot === -1 ? undefined : key.slice(dot + 1);\n if (head === '' || leaf === '') {\n throw new SeedcordTypeError(SeedcordErrorCode.CorePluginKeyMalformed, [key, 'has an empty part.']);\n }\n if (leaf?.includes('.')) {\n throw new SeedcordTypeError(SeedcordErrorCode.CorePluginKeyMalformed, [key, 'has more than one dot.']);\n }\n this.assertFree(head, leaf, key);\n\n if (!extendsThisCorePlugin(Plugin)) {\n throw new SeedcordTypeError(SeedcordErrorCode.CorePluginFromOtherCore, [Plugin.name]);\n }\n\n const instance = new Plugin(this, ...args);\n pluginLoggerOf(instance).setChannel(key);\n this.plugins.push(instance);\n this.attachments.push({ key, instance });\n\n if (leaf === undefined) {\n return Object.assign(this, { [key]: instance }) as this & Attached<Key, InstanceType<Ctor>>;\n }\n\n this.groupFor(head)[leaf] = instance;\n return this as this & Attached<Key, InstanceType<Ctor>>;\n }\n\n private assertFree(head: string, leaf: string | undefined, key: string): void {\n if (leaf === undefined) {\n if (this.groups.has(key)) throw new SeedcordError(SeedcordErrorCode.CorePluginKeyHoldsGroup, [key]);\n if (key in this) throw new SeedcordError(SeedcordErrorCode.CorePluginKeyExists, [key]);\n return;\n }\n\n const group = this.groups.get(head);\n if (!group) {\n if (head in this) throw new SeedcordError(SeedcordErrorCode.CorePluginGroupTaken, [head, key]);\n return;\n }\n if (Object.hasOwn(group, leaf)) throw new SeedcordError(SeedcordErrorCode.CorePluginKeyExists, [key]);\n }\n\n private groupFor(head: string): Record<string, PluginLike> {\n const existing = this.groups.get(head);\n if (existing) return existing;\n\n // a null prototype makes a leaf called __proto__ or valueOf an ordinary key\n const group = Object.create(null) as Record<string, PluginLike>;\n this.groups.set(head, group);\n Object.assign(this, { [head]: group });\n return group;\n }\n\n // one combined task per phase keeps plugin inits sequential while the phase's other tasks run concurrently\n private registerPluginTasks(): void {\n if (this.pluginTasksRegistered) return;\n this.pluginTasksRegistered = true;\n\n const groups = Map.groupBy(\n this.attachments,\n (attachment) => resolvedLifecycleSpecOf(attachment.instance).init.phase\n );\n\n for (const [phase, group] of groups) {\n // Ready inits run in the combined Ready task, before the ready hooks\n if (phase === StartupPhase.Ready) continue;\n const budget = group.reduce((sum, a) => sum + resolvedLifecycleSpecOf(a.instance).init.timeout, 0);\n this.startup.addTask(phase, 'plugins-init', () => this.runInits(group), budget);\n }\n\n this.registerReadyTask(groups.get(StartupPhase.Ready) ?? []);\n }\n\n private async runInits(group: readonly Attachment[]): Promise<void> {\n for (const attachment of group) {\n const { key, instance } = attachment;\n const spec = resolvedLifecycleSpecOf(instance);\n\n pluginLoggerOf(instance).utils.initialization(key, 'start');\n const running = instance.init();\n try {\n await withTimeout(`Plugin (${key})`, () => running, spec.init.timeout);\n } catch (caught) {\n if (isSeedcordError(caught, undefined, SeedcordErrorCode.LifecycleTaskTimeout)) {\n this.disposeWhenInitResolves(attachment, running);\n }\n throw caught;\n }\n pluginLoggerOf(instance).utils.initialization(key, 'end');\n\n this.completedInits.add(attachment);\n if (instance.dispose) this.registerDisposeTask(spec.dispose.phase);\n }\n }\n\n // disposeCompleted skips a plugin missing from completedInits\n private disposeWhenInitResolves(attachment: Attachment, running: Promise<void>): void {\n const dispose = attachment.instance.dispose?.bind(attachment.instance);\n const spec = resolvedLifecycleSpecOf(attachment.instance);\n\n void running.then(\n async () => {\n if (!dispose) return;\n await withTimeout(`Plugin:${attachment.key}:dispose`, dispose, spec.dispose.timeout).catch(\n (caught: unknown) =>\n this.pluginLogger.warn(`${attachment.key} dispose failed after a timed-out init`, caught)\n );\n },\n (caught: unknown) => this.pluginLogger.warn(`${attachment.key} init failed after its timeout`, caught)\n );\n }\n\n private registerReadyTask(readyInits: readonly Attachment[]): void {\n const steps = this.attachments.reduce<{ key: string; run: () => Promise<void>; timeout: number }[]>(\n (acc, a) => {\n const ready = a.instance.ready?.bind(a.instance);\n if (ready) {\n acc.push({\n key: a.key,\n run: ready,\n timeout: resolvedLifecycleSpecOf(a.instance).ready.timeout\n });\n }\n return acc;\n },\n []\n );\n if (readyInits.length === 0 && steps.length === 0) return;\n\n const budget =\n readyInits.reduce((sum, a) => sum + resolvedLifecycleSpecOf(a.instance).init.timeout, 0) +\n steps.reduce((sum, step) => sum + step.timeout, 0);\n this.startup.addTask(\n StartupPhase.Ready,\n 'plugins-ready',\n async () => {\n await this.runInits(readyInits);\n for (const step of steps) {\n await withTimeout(`Plugin:${step.key}:ready`, step.run, step.timeout);\n }\n },\n budget\n );\n }\n\n private registerDisposeTask(phase: ShutdownPhase): void {\n if (this.disposePhases.has(phase)) return;\n this.disposePhases.add(phase);\n\n // the budget covers every dispose in this phase because they all run in this one task\n const budget = this.attachments.reduce((sum, a) => {\n const spec = resolvedLifecycleSpecOf(a.instance);\n return a.instance.dispose && spec.dispose.phase === phase ? sum + spec.dispose.timeout : sum;\n }, 0);\n this.shutdown.addTask(phase, 'plugins-dispose', () => this.runDisposals(phase), budget);\n }\n\n private async runDisposals(phase: ShutdownPhase): Promise<void> {\n const failures: unknown[] = [];\n await this.disposeCompleted(phase, (caught) => failures.push(caught));\n if (failures.length === 1) throw failures[0];\n if (failures.length > 1) {\n throw new SeedcordAggregateError(SeedcordErrorCode.PluginDisposeFailures, failures, [failures.length]);\n }\n }\n\n private async disposeCompleted(\n phase: ShutdownPhase | undefined,\n onError: (caught: unknown) => void\n ): Promise<void> {\n for (const attachment of [...this.attachments].reverse()) {\n if (!this.completedInits.has(attachment)) continue;\n const dispose = attachment.instance.dispose?.bind(attachment.instance);\n if (!dispose) continue;\n const spec = resolvedLifecycleSpecOf(attachment.instance);\n if (phase !== undefined && spec.dispose.phase !== phase) continue;\n\n try {\n await withTimeout(`Plugin:${attachment.key}:dispose`, dispose, spec.dispose.timeout);\n } catch (caught) {\n onError(caught);\n }\n }\n }\n\n private async rollback(): Promise<void> {\n this.startFailed = true;\n // log rollback dispose failures so they do not hide the startup error\n await this.disposeCompleted(undefined, (caught) => this.pluginLogger.warn('rollback dispose failed', caught));\n // prevents re-dispose when shutdown runs after rollback\n this.completedInits.clear();\n }\n}\n\n// the public `shutdown` field carries addTask alone\nexport function shutdownOf(host: Pick<Pluggable<Transport, Runtime>, typeof HostShutdown>): CoordinatedShutdown {\n return host[HostShutdown];\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { i as Bus, n as StartupPhase, r as CoreBase, t as ShutdownPhase } from "./phases-DJf12l5T.mjs";
2
- import { c as PluginLike, d as Transport, n as AttachableCtor, o as PluginArgs, r as Attached, s as PluginCtor, t as AttachKeyAssert, u as Runtime } from "./Plugin-D01bBIFo.mjs";
1
+ import { i as Bus, n as StartupPhase, r as CoreBase, t as ShutdownPhase } from "./phases-D_zQBXga.mjs";
2
+ import { c as PluginLike, d as Transport, n as AttachableCtor, o as PluginArgs, r as Attached, s as PluginCtor, t as AttachKeyAssert, u as Runtime } from "./Plugin-Dhx_ivqj.mjs";
3
3
  import { Logger } from "@seedcord/logger";
4
4
  import { REST } from "@discordjs/rest";
5
5
  import { HostPluginKeys, HostShutdown, HostStartup } from "@seedcord/types/internal";
@@ -159,4 +159,4 @@ declare abstract class Pluggable<BotT extends Transport, BotRt extends Runtime>
159
159
  declare function shutdownOf(host: Pick<Pluggable<Transport, Runtime>, typeof HostShutdown>): CoordinatedShutdown;
160
160
  //#endregion
161
161
  export { LifecycleTask as a, CoordinatedShutdown as i, shutdownOf as n, CoordinatedStartup as r, Pluggable as t };
162
- //# sourceMappingURL=Pluggable-aRzcpx4e.d.mts.map
162
+ //# sourceMappingURL=Pluggable-CwKbLui3.d.mts.map
@@ -1,4 +1,4 @@
1
- import { n as StartupPhase, r as CoreBase, t as ShutdownPhase } from "./phases-DJf12l5T.mjs";
1
+ import { n as StartupPhase, r as CoreBase, t as ShutdownPhase } from "./phases-D_zQBXga.mjs";
2
2
  import { Logger } from "@seedcord/logger";
3
3
  import { FrameworkChannel, HmrAware, HmrUpdateEvent, Tail, TypedExclude } from "@seedcord/types";
4
4
  //#region src/plugin/brands.d.ts
@@ -201,4 +201,4 @@ type AttachAsserts<Ctor extends PluginCtor, BotT extends Transport, BotRt extend
201
201
  type AttachableCtor<Ctor extends PluginCtor, BotT extends Transport, BotRt extends Runtime> = unknown extends AttachAsserts<Ctor, BotT, BotRt> ? Ctor : AttachAsserts<Ctor, BotT, BotRt>;
202
202
  //#endregion
203
203
  export { Plugin as a, PluginLike as c, Transport as d, PluginLifecycleSpec as f, Initializeable as i, PluginOptions as l, AttachableCtor as n, PluginArgs as o, Attached as r, PluginCtor as s, AttachKeyAssert as t, Runtime as u };
204
- //# sourceMappingURL=Plugin-D01bBIFo.d.mts.map
204
+ //# sourceMappingURL=Plugin-Dhx_ivqj.d.mts.map