@seedcord/core 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/decorators/Command.ts","../src/decorators/routes.ts","../src/dispatch/DispatchContext.ts","../src/handlers/BaseHandler.ts","../src/handlers/RepliableHandler.ts","../src/stops/Fault.ts","../src/gates/combinators.ts","../src/gates/catalog/access.ts","../src/gates/catalog/Cooldown.ts","../src/permissions/assert.ts","../src/permissions/mergeRoles.ts","../src/miscellaneous/emoji.ts","../src/pagination/paginate.ts","../src/pagination/controls.ts","../src/pagination/render.ts","../src/pagination/PaginatorBase.ts","../src/pagination/sources.ts","../src/index.ts"],"sourcesContent":["import 'reflect-metadata';\n\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { CommandMetadataKey } from '#src/metadataKeys';\n\nimport type { BuilderComponent } from '#components/Component';\nimport type { Constructor } from 'type-fest';\n\ntype CommandCtor = Constructor<BuilderComponent<'command' | 'context_menu'>>;\n\ninterface GlobalMeta {\n scope: 'global';\n}\n\ninterface GuildMeta {\n scope: 'guild';\n guilds: string[];\n}\n\nexport type CommandMeta = GlobalMeta | GuildMeta;\n\ntype CommandScope = CommandMeta['scope'];\n\n/**\n * Attaches deployment metadata so the framework registers this command globally or in specific guilds.\n *\n * @decorator\n * @example\n * ```typescript\n * \\@RegisterCommand('global')\n * class PingCommand extends BuilderComponent<'command'> {} // or BuilderComponent<'context_menu'>\n * ```\n */\nexport function RegisterCommand(scope: 'global'): (ctor: CommandCtor) => void;\n\n/**\n * Registers a command for specific guild deployment.\n *\n * @param guilds - Array of guild IDs where the command should be registered\n * @decorator\n * @example\n * ```typescript\n * \\@RegisterCommand('guild', ['123456789'])\n * class AdminCommand extends BuilderComponent<'command'> {} // or BuilderComponent<'context_menu'>\n * ```\n */\nexport function RegisterCommand(scope: 'guild', guilds: string[]): (ctor: CommandCtor) => void;\n\nexport function RegisterCommand(scope: CommandScope, guilds: string[] = []) {\n return (ctor: CommandCtor): void => {\n const existingMeta = Reflect.getOwnMetadata(CommandMetadataKey, ctor) as CommandMeta | undefined;\n if (existingMeta) {\n throw new SeedcordError(SeedcordErrorCode.DecoratorCommandAlreadyRegistered, [\n ctor.name,\n existingMeta.scope,\n scope\n ]);\n }\n\n if (scope === 'global' && guilds.length > 0) {\n throw new SeedcordError(SeedcordErrorCode.DecoratorCommandGlobalWithGuilds);\n }\n\n if (scope === 'guild' && (!Array.isArray(guilds) || guilds.length === 0)) {\n throw new SeedcordError(SeedcordErrorCode.DecoratorCommandGuildWithoutGuilds);\n }\n\n const meta: GlobalMeta | GuildMeta = scope === 'global' ? { scope } : { scope, guilds };\n Reflect.defineMetadata(CommandMetadataKey, meta, ctor);\n };\n}\n","import { ApplicationCommandType } from 'discord-api-types/v10';\n\nimport {\n contextMenuRouteOf,\n selectMenuRouteOf,\n storeComponentRoute,\n storeInteractionRoute\n} from '#decorators/interactionRoutes';\nimport { InteractionRoutes } from '#src/metadataKeys';\n\nimport type { AnyCustomId } from '#customId/CustomId';\nimport type { SelectMenuKind } from '#decorators/interactionRoutes';\nimport type { ContextMenuKind, NamesFor } from '#registries/ContextMenuRegistry';\nimport type { SlashRegistry } from '#registries/SlashRegistry';\nimport type {\n AutocompleteRouteBrand,\n ComponentDefsBrand,\n ComponentKindBrand,\n ContextMenuKindBrand,\n ContextMenuNamesBrand,\n SlashRouteBrand\n} from './brands';\nimport type { Constructor } from 'type-fest';\n\ntype ComponentBrand = 'button' | 'modal' | SelectMenuKind;\n\n// phantom brands keep the decorators off transport-specific classes\ntype AnyHandlerCtor = new (...args: any[]) => unknown;\n\ntype SlashRouteOf<TCtor extends AnyHandlerCtor> =\n InstanceType<TCtor> extends {\n [SlashRouteBrand]?: infer Route extends keyof SlashRegistry;\n }\n ? Route\n : never;\n\ntype AutocompleteRouteOf<TCtor extends AnyHandlerCtor> =\n InstanceType<TCtor> extends {\n [AutocompleteRouteBrand]?: infer Route extends keyof SlashRegistry;\n }\n ? Route\n : never;\n\ntype ContextMenuKindOf<TCtor extends AnyHandlerCtor> =\n InstanceType<TCtor> extends {\n [ContextMenuKindBrand]?: infer Kind extends ContextMenuKind;\n }\n ? Kind\n : never;\n\ntype ContextMenuNamesOf<TCtor extends AnyHandlerCtor> =\n InstanceType<TCtor> extends {\n [ContextMenuNamesBrand]?: infer Names;\n }\n ? Names\n : never;\n\ntype ComponentOf<TCtor extends AnyHandlerCtor> =\n InstanceType<TCtor> extends {\n [ComponentKindBrand]?: infer Brand extends ComponentBrand;\n }\n ? Brand\n : never;\n\ntype DefsOf<TCtor extends AnyHandlerCtor> =\n InstanceType<TCtor> extends {\n [ComponentDefsBrand]?: infer Defs extends readonly AnyCustomId[];\n }\n ? Defs\n : never;\n\ntype AssertSlashRoute<Route extends keyof SlashRegistry, TCtor extends AnyHandlerCtor> = [Route] extends [\n SlashRouteOf<TCtor>\n]\n ? [SlashRouteOf<TCtor>] extends [Route]\n ? TCtor\n : Constructor<['SlashHandler declares a route the SlashRoute decorator does not list', SlashRouteOf<TCtor>]>\n : Constructor<['SlashRoute does not match the SlashHandler generic', Route]>;\n\ntype AssertAutocompleteRoute<Route extends keyof SlashRegistry, TCtor extends AnyHandlerCtor> = [Route] extends [\n AutocompleteRouteOf<TCtor>\n]\n ? [AutocompleteRouteOf<TCtor>] extends [Route]\n ? TCtor\n : Constructor<\n [\n 'AutocompleteHandler declares a command the AutocompleteRoute decorator does not list',\n AutocompleteRouteOf<TCtor>\n ]\n >\n : Constructor<['AutocompleteRoute does not match the AutocompleteHandler generic', Route]>;\n\ntype AssertContextMenuRoute<\n Kind extends ContextMenuKind,\n Names extends NamesFor<Kind>,\n TCtor extends AnyHandlerCtor\n> = [Kind] extends [ContextMenuKindOf<TCtor>]\n ? [ContextMenuKindOf<TCtor>] extends [Kind]\n ? [Names] extends [ContextMenuNamesOf<TCtor>]\n ? [ContextMenuNamesOf<TCtor>] extends [Names]\n ? TCtor\n : Constructor<\n [\n 'ContextMenuHandler declares a name the ContextMenuRoute decorator does not list',\n ContextMenuNamesOf<TCtor>\n ]\n >\n : Constructor<['ContextMenuRoute lists a name the ContextMenuHandler generic does not declare', Names]>\n : Constructor<\n [\n 'ContextMenuHandler declares a kind the ContextMenuRoute decorator does not match',\n ContextMenuKindOf<TCtor>\n ]\n >\n : Constructor<['ContextMenuRoute does not match the ContextMenuHandler generic', Kind]>;\n\n// one assignability direction for the defs, a bidirectional check would reject const-inferred readonly tuples\ntype AssertComponentRoute<\n Brand extends ComponentBrand,\n Defs extends readonly AnyCustomId[],\n TCtor extends AnyHandlerCtor\n> = [ComponentOf<TCtor>] extends [Brand]\n ? [Brand] extends [ComponentOf<TCtor>]\n ? [DefsOf<TCtor>] extends [Defs]\n ? TCtor\n : Constructor<['the customId definitions do not match the handler generic', DefsOf<TCtor>]>\n : Constructor<['the decorator does not match the handler kind', ComponentOf<TCtor>]>\n : Constructor<['the decorator does not match the handler kind', ComponentOf<TCtor>]>;\n\n/**\n * Routes one or more slash commands to a `SlashHandler`.\n *\n * Pass the same route string(s) as the handler's generic. A single command reads `this.options`, several\n * commands branch with `this.match`. The decorator routes and the generic must match exactly. Listing\n * fewer or more than the handler declares is a compile error. Subcommands use a slash path.\n *\n * @param routes - The route string(s) this handler serves, e.g. `'ban'`, `'ban', 'kick'`, or `'demo/setup'`.\n * @decorator\n * @example\n * ```ts\n * \\@SlashRoute('ban')\n * class BanHandler extends SlashHandler<'ban'> {\n * async execute() {\n * const target = this.options.getUser('target');\n * }\n * }\n * ```\n */\nexport function SlashRoute<const Route extends keyof SlashRegistry>(...routes: Route[]) {\n return function <TCtor extends AnyHandlerCtor>(constructor: AssertSlashRoute<Route, TCtor>): void {\n storeInteractionRoute(InteractionRoutes.Slash, routes, constructor);\n };\n}\n\n/**\n * Routes one or more commands' autocomplete to an `AutocompleteHandler`.\n *\n * Pass the same command route string(s) as the handler's generic. One command branches with `this.match`\n * over its autocompletable fields, several commands share one handler whose arms span every command's\n * fields. Listing fewer or more commands than the handler declares is a compile error, since the\n * decorator routes and the generic must match exactly.\n *\n * @param routes - The command route string(s) this handler serves, e.g. `'search'` or `'search', 'find'`.\n * @decorator\n * @example\n * ```ts\n * \\@AutocompleteRoute('search')\n * class SearchAutocomplete extends AutocompleteHandler<'search'> {\n * async execute() {\n * await this.match({ query: (value, respond) => respond([{ name: value, value }]) });\n * }\n * }\n * ```\n */\nexport function AutocompleteRoute<const Route extends keyof SlashRegistry>(...routes: Route[]) {\n return function <TCtor extends AnyHandlerCtor>(constructor: AssertAutocompleteRoute<Route, TCtor>): void {\n storeInteractionRoute(InteractionRoutes.Autocomplete, routes, constructor);\n };\n}\n\n/**\n * Routes one or more user context-menu commands to a `UserContextMenuHandler`.\n *\n * Every name is checked against the user registry. The handler's generic must declare the same names,\n * checked in both directions.\n *\n * @param names - The command name(s) this handler serves.\n * @decorator\n * @example\n * ```ts\n * \\@UserContextMenuRoute('View Profile')\n * class ViewProfile extends UserContextMenuHandler<'View Profile'> {\n * async execute() {\n * const user = this.target;\n * }\n * }\n * ```\n */\nexport function UserContextMenuRoute<const Names extends NamesFor<ApplicationCommandType.User>>(...names: Names[]) {\n return function <TCtor extends AnyHandlerCtor>(\n constructor: AssertContextMenuRoute<ApplicationCommandType.User, Names, TCtor>\n ): void {\n storeInteractionRoute(contextMenuRouteOf(ApplicationCommandType.User), names, constructor);\n };\n}\n\n/**\n * Routes one or more message context-menu commands to a `MessageContextMenuHandler`.\n *\n * Every name is checked against the message registry. The handler's generic must declare the same names,\n * checked in both directions.\n *\n * @param names - The command name(s) this handler serves.\n * @decorator\n * @example\n * ```ts\n * \\@MessageContextMenuRoute('Report Message')\n * class ReportMessage extends MessageContextMenuHandler<'Report Message'> {\n * async execute() {\n * const message = this.target;\n * }\n * }\n * ```\n */\nexport function MessageContextMenuRoute<const Names extends NamesFor<ApplicationCommandType.Message>>(\n ...names: Names[]\n) {\n return function <TCtor extends AnyHandlerCtor>(\n constructor: AssertContextMenuRoute<ApplicationCommandType.Message, Names, TCtor>\n ): void {\n storeInteractionRoute(contextMenuRouteOf(ApplicationCommandType.Message), names, constructor);\n };\n}\n\n/**\n * Routes button interactions to handler classes.\n *\n * Pass the {@link CustomId} definition(s) this handler decodes and list the same ones in the handler's\n * generic. Passing different definitions to the decorator and the generic is a compile error. Routing\n * matches the stable prefix, so a wire minted from an older shape still reaches the handler, where\n * reading this.params throws StaleCustomId and the controller boundary turns it into a reply.\n *\n * @param defs - The customId definition(s) this handler decodes, one per route.\n * @decorator\n */\nexport function ButtonRoute<const Defs extends readonly AnyCustomId[]>(...defs: Defs) {\n return function <TCtor extends AnyHandlerCtor>(constructor: AssertComponentRoute<'button', Defs, TCtor>): void {\n storeComponentRoute(InteractionRoutes.Button, defs, constructor);\n };\n}\n\n/**\n * Routes modal submissions to handler classes.\n *\n * Pass the {@link CustomId} definition(s) this handler decodes and list the same ones in the handler's\n * generic. Passing different definitions to the decorator and the generic is a compile error. Routing\n * matches the stable prefix here too, throwing StaleCustomId on read the same as a button route.\n *\n * @param defs - The customId definition(s) this handler decodes, one per route.\n * @decorator\n */\nexport function ModalRoute<const Defs extends readonly AnyCustomId[]>(...defs: Defs) {\n return function <TCtor extends AnyHandlerCtor>(constructor: AssertComponentRoute<'modal', Defs, TCtor>): void {\n storeComponentRoute(InteractionRoutes.Modal, defs, constructor);\n };\n}\n\n/**\n * Routes select menu interactions to handler classes.\n *\n * Pass the select kind and the {@link CustomId} definition(s) this handler decodes. The handler's\n * generic must list the same kind and definitions, or it is a compile error.\n *\n * @param type - Select menu kind from {@link SelectMenuKind}.\n * @param defs - The customId definition(s) this handler decodes, one per route.\n * @decorator\n *\n * @example\n * ```typescript\n * \\@SelectMenuRoute(SelectMenuKind.User, AssignId)\n * class AssignSelect extends SelectMenuHandler<SelectMenuKind.User, [typeof AssignId]> {}\n * ```\n */\nexport function SelectMenuRoute<const Kind extends SelectMenuKind, const Defs extends readonly AnyCustomId[]>(\n type: Kind,\n ...defs: Defs\n) {\n return function <TCtor extends AnyHandlerCtor>(constructor: AssertComponentRoute<Kind, Defs, TCtor>): void {\n storeComponentRoute(selectMenuRouteOf(type), defs, constructor);\n };\n}\n","/**\n * The per-dispatch bag. Any code can merge fields into it by declaration merging, the\n * same pattern as {@link SlashRegistry}. It stays empty until one does, so `keyof DispatchState` is\n * `never` and the bag is inert.\n */\nexport interface DispatchState {}\n\n/**\n * A typed bag allocated once per interaction dispatch and handed to the handler. The middleware and i18n\n * work reads and writes it through {@link DispatchState}. Today the bag is empty, so it only carries the\n * dispatched `routeId`.\n */\nexport class DispatchContext {\n private readonly state: Partial<DispatchState> = {};\n\n constructor(public readonly routeId: string | null) {}\n\n set<Key extends keyof DispatchState>(key: Key, value: DispatchState[Key]): void {\n this.state[key] = value;\n }\n\n get<Key extends keyof DispatchState>(key: Key): DispatchState[Key] | undefined {\n return this.state[key];\n }\n}\n","import { Logger } from '@seedcord/logger';\n\nimport type { CoreBase } from '#interfaces/CoreBase';\nimport type { DispatchContext } from '#src/dispatch/DispatchContext';\nimport type { LoggerChannelId } from '@seedcord/types';\n\n/**\n * Base class every transport handler extends. Don't register handlers directly. Use the more specific\n * handler subclasses.\n *\n * @typeParam Event - The event or interaction this handler processes\n * @typeParam TCore - The transport's Core\n */\nexport abstract class BaseHandler<Event, TCore extends CoreBase> {\n protected readonly event: Event;\n protected readonly logger: Logger;\n // absent on event handlers\n protected readonly dispatch?: DispatchContext | undefined;\n\n protected constructor(\n event: Event,\n public readonly core: TCore,\n dispatch?: DispatchContext,\n channel?: LoggerChannelId\n ) {\n this.event = event;\n this.logger = new Logger(this.constructor.name, { channel });\n this.dispatch = dispatch;\n }\n\n /**\n * Holds the main logic of your handler. The dispatcher calls it after the handler's gates pass, so a\n * gate that refuses stops `execute()` from running.\n */\n abstract execute(): Promise<void>;\n}\n","import { BaseHandler } from './BaseHandler';\n\nimport type { CoreBase } from '#interfaces/CoreBase';\nimport type { BaseReplySender } from '#reply/BaseReplySender';\nimport type { DispatchContext } from '#src/dispatch/DispatchContext';\nimport type { DeferOpts, ReplyResponse, SendOpts } from '@seedcord/types';\n\n/**\n * Shared base the repliable handlers on both transports extend. Defines the reply members over the sender\n * the transport builds.\n *\n * @typeParam Event - The repliable interaction type this handler processes\n * @typeParam TCore - The transport's Core\n * @typeParam TMessage - The transport's created-message lens\n * @typeParam TNative - The transport's own file forms, beyond the portable `ReplyFile`\n * @typeParam TSender - The transport's reply sender\n */\nexport abstract class RepliableHandler<\n Event,\n TCore extends CoreBase,\n TMessage extends { id: string },\n TNative,\n TSender extends BaseReplySender<TMessage, TNative>\n> extends BaseHandler<Event, TCore> {\n protected readonly sender: TSender;\n protected readonly routeId: string;\n\n protected constructor(event: Event, core: TCore, dispatch?: DispatchContext) {\n super(event, core, dispatch, 'interactions');\n this.routeId = dispatch?.routeId ?? this.constructor.name;\n // the override runs before its own field initializers\n this.sender = this.buildSender(event, core, this.routeId);\n }\n\n protected abstract buildSender(event: Event, core: TCore, routeId: string): TSender;\n\n /** @internal The dispatcher's fault boundary replies through the live ack state. */\n public getSender(): TSender {\n return this.sender;\n }\n\n /** Send the initial response, exactly once. */\n protected reply(response: ReplyResponse<TNative> | string, opts?: SendOpts): Promise<TMessage> {\n return this.sender.reply(response, opts);\n }\n\n /** Show a \"thinking\" placeholder, then fill it later with {@link edit}. */\n protected defer(opts?: DeferOpts): Promise<void> {\n return this.sender.defer(opts);\n }\n\n /** Send a new message once the interaction is acknowledged. */\n protected followUp(response: ReplyResponse<TNative> | string, opts?: SendOpts): Promise<TMessage> {\n return this.sender.followUp(response, opts);\n }\n\n /** Rewrite the initial reply or deferred placeholder. The targeted form rewrites a message a prior send returned. */\n protected edit(response: ReplyResponse<TNative> | string): Promise<TMessage>;\n protected edit(target: TMessage, response: ReplyResponse<TNative> | string): Promise<TMessage>;\n protected edit(\n targetOrResponse: TMessage | ReplyResponse<TNative> | string,\n maybeResponse?: ReplyResponse<TNative> | string\n ): Promise<TMessage> {\n // justified: the overloads narrow targetOrResponse to a response once maybeResponse is absent\n if (maybeResponse === undefined) return this.sender.edit(targetOrResponse as ReplyResponse<TNative> | string);\n // justified: the overloads narrow targetOrResponse to a target message once maybeResponse is defined\n return this.sender.edit(targetOrResponse as TMessage, maybeResponse);\n }\n\n /** Routes to reply, followUp, or edit based on the current ack state. */\n protected send(response: ReplyResponse<TNative> | string, opts?: SendOpts): Promise<TMessage> {\n return this.sender.send(response, opts);\n }\n\n /** Delete the initial reply or deferred placeholder. Pass a target to delete a message a prior send returned. */\n protected delete(target?: TMessage): Promise<void> {\n return target === undefined ? this.sender.delete() : this.sender.delete(target);\n }\n}\n","import { Notice } from './Notice';\nimport { NoticeCard } from './NoticeCard';\n\nimport type { RenderContext, ReplyResponse } from '@seedcord/types';\n\n/**\n * A generic fault you throw after catching an error you do not have a specific message for.\n *\n * The user sees a fixed generic reply with the tracking uuid, never the cause. `report` defaults to\n * `true`, so the framework logs it and publishes it to the `handledException` bus. Pass `report: false`\n * to show the generic reply without the bus publish. The original error is stored as the standard\n * `cause`, so the real stack reaches the webhook. When the user should see a real message for a fault,\n * subclass {@link Notice} and write your own `render` instead.\n *\n * The framework also renders this for an unhandled throw, where it points the user at\n * `ctx.developerUsername`.\n *\n * @example\n * ```ts\n * import { Fault } from '@seedcord/gateway';\n *\n * try {\n * await db.write(record);\n * } catch (cause) {\n * // user sees the generic reply with the uuid, and the real error rides along as cause for the webhook\n * throw new Fault({ cause });\n *\n * // pass report: false to show the same reply without publishing to the handledException bus\n * // throw new Fault({ cause, report: false });\n * }\n * ```\n */\nexport class Fault extends Notice {\n public constructor(options?: { cause?: unknown; report?: boolean }) {\n super('A fault occurred', options?.cause === undefined ? undefined : { cause: options.cause });\n this.report = options?.report ?? true;\n }\n\n public render(ctx: RenderContext): ReplyResponse {\n const contact = ctx.developerUsername ?? 'the developer';\n const card = new NoticeCard(\n `Something went wrong. Please reach out to ${contact} with a way to reproduce the error and the following:\\n### UUID: \\`${ctx.uuid}\\``,\n 'Error'\n );\n return { components: [card.component] };\n }\n}\n","import { NeedsAny, NotAllowed } from '#notices/index';\nimport { Fault } from '#stops/Fault';\nimport { Notice } from '#stops/Notice';\n\nimport { markCommits, rollbackCommits, runCheck } from './effects';\nimport { defineGate } from './Gate';\n\nimport type { Gate, GateContextBase, RequiredOf } from './Gate';\nimport type { IntersectRequired, JoinNames, TwoOrMore } from './matching';\n\n/**\n * Runs each gate in order and refuses on the first refusal. Takes two or more arms. The required\n * context is the intersection of the arms, so an event-only and an interaction-only gate cannot be\n * combined.\n *\n * @typeParam Gates - The tuple of two or more gate arms, run left to right.\n * @param gates - The gate arms to run in order, all refused on the first refusal.\n *\n * @see {@link or}\n *\n * @example\n * ```ts\n * import { PermissionFlagsBits } from 'discord.js';\n *\n * \\@Gated(and(GuildOnly(), RequirePermissions([PermissionFlagsBits.BanMembers])))\n * \\@SlashRoute('ban')\n * class BanHandler extends SlashHandler<'ban'> {\n * async execute() {\n * // ...\n * }\n * }\n * ```\n */\nexport function and<Gates extends TwoOrMore<Gate<GateContextBase>>>(\n ...gates: Gates\n): Gate<IntersectRequired<Gates> & GateContextBase, JoinNames<Gates, ' & '>>;\nexport function and(...gates: readonly Gate<GateContextBase>[]): Gate<GateContextBase> {\n return defineGate(gates.map((gate) => gate.name).join(' & '), async (ctx) => {\n for (const gate of gates) {\n await runCheck(gate, ctx);\n }\n });\n}\n\n// the caller-supplied refusal when every or arm refuses, replacing the default list or neutral refusal\ninterface OrOptions {\n fail: Notice | ((ctx: GateContextBase) => Notice);\n}\n\n// a gate has a check method, but the options object does not\nfunction isOrOptions(arg: Gate<GateContextBase> | OrOptions): arg is OrOptions {\n return !('check' in arg);\n}\n\n/**\n * Runs each gate in order and passes on the first arm that passes. Takes two or more arms. The\n * required context is the union of the arms. A handler that matches any one arm fits. When every\n * arm refuses it throws the trailing {@link OrOptions} `fail` if given, else an auto list of the\n * arms derived from the summary field, else a default refusal. The trailing options object does not\n * count as an arm.\n *\n * @typeParam Gates - The tuple of two or more gate arms, tried left to right.\n * @param gates - The gate arms to try in order, with an optional trailing {@link OrOptions} object.\n *\n * @see {@link and}\n *\n * @example\n * ```ts\n * \\@Gated(or(OwnerOnly(), RequireRole('123456789012345678')))\n * \\@SlashRoute('admin')\n * class AdminHandler extends SlashHandler<'admin'> {\n * async execute() {\n * // ...\n * }\n * }\n * ```\n */\nexport function or<Gates extends TwoOrMore<Gate<GateContextBase>>>(\n ...gates: Gates\n): Gate<RequiredOf<Gates[number]>, JoinNames<Gates, ' | '>>;\n/**\n * @example\n * ```ts\n * // custom refusal when every arm refuses\n * or(OwnerOnly(), RequireRole('123456789012345678'), { fail: new NotAllowedNotice() });\n * ```\n */\nexport function or<Gates extends TwoOrMore<Gate<GateContextBase>>>(\n ...args: [...Gates, OrOptions]\n): Gate<RequiredOf<Gates[number]>, JoinNames<Gates, ' | '>>;\nexport function or(...args: readonly (Gate<GateContextBase> | OrOptions)[]): Gate<GateContextBase> {\n const last = args.at(-1);\n const options = last !== undefined && isOrOptions(last) ? last : undefined;\n const gates = (options ? args.slice(0, -1) : args) as readonly Gate<GateContextBase>[];\n\n return defineGate(gates.map((gate) => gate.name).join(' | '), async (ctx) => {\n const summaries: string[] = [];\n let everyArmHasSummary = true;\n for (const gate of gates) {\n const mark = markCommits(ctx);\n try {\n await runCheck(gate, ctx);\n return;\n } catch (error) {\n // only a refusal counts as an arm declining. a Fault (even report-false), a Silence, or a raw error stops everything\n if (error instanceof Notice && !(error instanceof Fault) && !error.report) {\n // the arm may have queued an effect's commit before refusing, so drop it before the winner carries it\n rollbackCommits(ctx, mark);\n if (error.summary === undefined) everyArmHasSummary = false;\n else summaries.push(error.summary);\n continue;\n }\n throw error;\n }\n }\n if (options) throw typeof options.fail === 'function' ? options.fail(ctx) : options.fail;\n // the auto-list shows only when every refusal gave a summary, because a partial list would mislead by omission\n if (everyArmHasSummary && summaries.length > 0) throw new NeedsAny(summaries);\n throw new NotAllowed();\n });\n}\n","import { defineGate } from '#gates/Gate';\nimport { NotInDm, NotInGuild, NotOwner } from '#notices/index';\n\nimport { pickNotice } from './options';\n\nimport type { Gate, GateContextBase } from '#gates/Gate';\nimport type { GateNoticeOptions } from './options';\n\n/**\n * Passes only for a user id listed in `config.ownerIds`, else refuses.\n *\n * Agnostic, so it attaches to interaction and event handlers alike. With no `ownerIds` configured it refuses every caller. Pass {@link GateNoticeOptions} to reword or replace the refusal.\n *\n * @param options - Reword the default refusal with `message`, or replace it with `notice`.\n *\n * @see the `@Gated` decorator from your transport package\n *\n * @example\n * ```ts\n * \\@Gated(OwnerOnly())\n * \\@SlashRoute('shutdown')\n * class ShutdownHandler extends SlashHandler<'shutdown'> {\n * async execute() {\n * // ...\n * }\n * }\n * ```\n *\n * @example\n * ```ts\n * // reword the refusal, keeping the embed styling\n * OwnerOnly({ message: 'Owners only.' });\n * ```\n */\nexport function OwnerOnly(options?: GateNoticeOptions): Gate<GateContextBase, 'OwnerOnly'> {\n return defineGate('OwnerOnly', (ctx) => {\n const owners = ctx.core.config.ownerIds ?? [];\n if (ctx.userId !== null && owners.includes(ctx.userId)) return;\n throw pickNotice(options, (message) => new NotOwner(message));\n });\n}\n\n/**\n * Passes inside a guild, else refuses.\n *\n * Agnostic. Attaches to any handler kind. Often paired with `RequirePermissions` in an {@link and}.\n *\n * @param options - Reword the default refusal with `message`, or replace it with `notice`.\n *\n * @see the `@Gated` decorator from your transport package\n *\n * @example\n * ```ts\n * import { PermissionFlagsBits } from 'discord.js';\n *\n * \\@Gated(GuildOnly(), RequirePermissions([PermissionFlagsBits.Administrator]))\n * \\@SlashRoute('maintenance')\n * class Maintenance extends SlashHandler<'maintenance'> {\n * async execute() {\n * // ...\n * }\n * }\n * ```\n */\nexport function GuildOnly(options?: GateNoticeOptions): Gate<GateContextBase, 'GuildOnly'> {\n return defineGate('GuildOnly', (ctx) => {\n if (ctx.guildId) return;\n throw pickNotice(options, (message) => new NotInGuild(message));\n });\n}\n\n/**\n * Passes in a direct message, else refuses.\n *\n * Agnostic, fitting any handler kind. The inverse of {@link GuildOnly}, so combining the two in an {@link and} can never pass.\n *\n * @param options - Reword the default refusal with `message`, or replace it with `notice`.\n *\n * @see the `@Gated` decorator from your transport package\n *\n * @example\n * ```ts\n * \\@Gated(DmOnly())\n * \\@SlashRoute('verify')\n * class VerifyHandler extends SlashHandler<'verify'> {\n * async execute() {\n * // ...\n * }\n * }\n * ```\n */\nexport function DmOnly(options?: GateNoticeOptions): Gate<GateContextBase, 'DmOnly'> {\n return defineGate('DmOnly', (ctx) => {\n if (!ctx.guildId) return;\n throw pickNotice(options, (message) => new NotInDm(message));\n });\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordTypeError } from '@seedcord/errors/internal';\nimport { parseDuration, type ValidDuration } from '@seedcord/utils';\n\nimport { defineEffectGate } from '#gates/Gate';\nimport { OnCooldown } from '#notices/index';\n\nimport type { EffectGate, GateContextBase } from '#gates/Gate';\nimport type { Notice } from '#stops/Notice';\nimport type { EpochMs } from '@seedcord/types';\n\n/**\n * Options for {@link Cooldown}. `per` sets the bucket the window applies to and `limit` the uses allowed per\n * window. `message` rewords the refusal and `notice` replaces it, both receiving the epoch ms the key frees\n * up so the refusal can show the retry time.\n *\n * @example\n * ```ts\n * // one use per channel, rewording the refusal with the retry time\n * Cooldown('10s', {\n * per: 'channel',\n * message: (resetAt) => `Slow down. Try again <t:${Math.round(resetAt / 1000)}:R>.`\n * });\n * ```\n *\n * @example\n * ```ts\n * // five uses per minute, per user\n * Cooldown('1m', { limit: 5 });\n * ```\n */\nexport interface CooldownOptions {\n /**\n * The bucket the cooldown window applies to. 'user' scopes by user ID (falls back to global when the source carries no user id). 'guild' scopes by guild ID (falls back to global if no guild), and 'channel' scopes by channel ID (falls back to global if no channel).\n *\n * @defaultValue `'user'`\n */\n per?: 'user' | 'guild' | 'channel';\n /**\n * Uses allowed inside one window before the gate refuses.\n *\n * @defaultValue `1`\n */\n limit?: number;\n /**\n * Reword the refusal, keeping the standard notice card. Receives the epoch ms the key frees up, to\n * show the retry time with `<t:${Math.round(resetAt / 1000)}:R>`.\n */\n message?: (resetAt: EpochMs) => string;\n /**\n * Replace the refusal Notice entirely, for full control or a translated copy. Receives the epoch ms the\n * key frees up.\n */\n notice?: (resetAt: EpochMs) => Notice;\n}\n\n// off-route ctxs have no route to key on. each Cooldown() call gets its own id to keep unrelated event handlers isolated\nlet anonSeq = 0;\n\nfunction scopeValue(ctx: GateContextBase, per: 'user' | 'guild' | 'channel'): string {\n if (per === 'guild') return ctx.guildId ?? 'global';\n if (per === 'channel') return ctx.channelId ?? 'global';\n return ctx.userId ?? 'global';\n}\n\n/**\n * Allows `limit` uses per window, scoped by `per`. A number `duration` is **seconds**, and a string is\n * a duration like `30m` or `24h`. An unparseable string throws a **SeedcordTypeError** at construction. The\n * slot is charged in commit, only after the whole gate set passes, so a later refusal never burns the cooldown.\n * That split also means two requests racing the same key can both pass before either charges the slot.\n * The key combines the handler's route with the window settings, keeping two different handlers on separate\n * windows. Reword the refusal with {@link CooldownOptions.message} or replace it with {@link CooldownOptions.notice}.\n *\n * @param duration - A number is seconds, and a string is a duration like `30m` or `24h`. An unparseable string throws a **SeedcordTypeError**.\n * @param options - Sets the scope with `per`, the uses per window with `limit`, and the refusal text with `message` or `notice`.\n *\n * @see the `@Gated` decorator from your transport package\n * @see {@link IRateLimiter}\n *\n * @example\n * ```ts\n * // a number argument is SECONDS\n * \\@Gated(Cooldown(5))\n * \\@SlashRoute('daily')\n * class DailyHandler extends SlashHandler<'daily'> {\n * async execute() {\n * // ...\n * }\n * }\n * ```\n *\n * @example\n * ```ts\n * // a string is a duration, here scoped per guild (the default is per user)\n * Cooldown('30m', { per: 'guild' });\n * ```\n *\n * @example\n * ```ts\n * // a burst, 3 uses per 10 minutes per user, then it refuses until a slot frees\n * \\@Gated(Cooldown('10m', { limit: 3 }))\n * \\@SlashRoute('report')\n * class ReportHandler extends SlashHandler<'report'> {\n * async execute() {\n * // ...\n * }\n * }\n * ```\n */\nexport function Cooldown(\n duration: number | ValidDuration,\n options?: CooldownOptions\n): EffectGate<GateContextBase, 'Cooldown'> {\n let windowMs: number;\n if (typeof duration === 'number') {\n windowMs = duration * 1000;\n } else {\n const parsed = parseDuration(duration);\n if (parsed === null) throw new SeedcordTypeError(SeedcordErrorCode.GateInvalidCooldownDuration, [duration]);\n windowMs = parsed;\n }\n // refused the same way as an unparseable string, because a zero or negative window would never limit\n if (windowMs <= 0) {\n throw new SeedcordTypeError(SeedcordErrorCode.GateInvalidCooldownDuration, [String(duration)]);\n }\n\n const per = options?.per ?? 'user';\n // exactOptionalPropertyTypes rejects an explicit undefined key\n const window = options?.limit === undefined ? { windowMs } : { windowMs, limit: options.limit };\n const anonId = anonSeq++;\n // routed keys stay stable across restarts, letting a durable store rebuild the same window\n const keyOf = (ctx: GateContextBase): string =>\n `cooldown:${ctx.routeId ?? `anon${anonId}`}:${per}:w${windowMs}:l${options?.limit ?? 1}:${scopeValue(ctx, per)}`;\n\n return defineEffectGate(\n 'Cooldown',\n async (ctx) => {\n const result = await ctx.core.rateLimiter.peek(keyOf(ctx), window);\n if (!result.limited) return;\n if (options?.notice) throw options.notice(result.resetAt);\n throw new OnCooldown(result.resetAt, options?.message?.(result.resetAt));\n },\n async (ctx) => {\n await ctx.core.rateLimiter.charge(keyOf(ctx), window);\n }\n );\n}\n","import { HasDangerousPermissions, MissingPermissions } from '#notices/index';\n\nimport { hasAll, missingNames, presentNames } from './bits';\n\nimport type { Notice } from '#stops/Notice';\nimport type { PermissionScope } from './bits';\n\n/** Replacement notices for the two refusals {@link assertPermissions} raises. */\nexport interface PermissionNoticeOverrides {\n missingNotice?: new (message: string | undefined, subject: string, missingPerms: readonly string[]) => Notice;\n dangerousNotice?: new (message: string | undefined, subject: string, dangerousPerms: readonly string[]) => Notice;\n}\n\n/** The check {@link assertPermissions} runs. */\nexport interface PermissionAssertion extends PermissionNoticeOverrides {\n /** Mention string for who is checked, used as the refusal's subject. */\n subject: string;\n /** The effective permission bitfield the subject holds, computed by the caller. */\n permissions: bigint;\n /** The permission flag bits to check. */\n scope: PermissionScope;\n /** Refuse when the subject holds a scope bit. */\n inverse?: boolean;\n}\n\n/**\n * Refuses when the subject is missing a permission in `scope`, or with `inverse` when it holds one.\n * The Administrator bit passes any scope, and on the inverse arm it reports every bit in `scope`.\n *\n * The caller computes `permissions`, gateway off a discord.js role, member, or channel, and HTTP off the\n * bitfield the interaction payload carries.\n *\n * @throws A {@link Notice} listing the missing or present permissions.\n *\n * @example\n * ```ts\n * import { assertPermissions } from '@seedcord/core';\n * import { PermissionFlagsBits } from 'discord-api-types/v10';\n *\n * // gateway, the bits come off a discord.js member\n * assertPermissions({\n * subject: `<@${member.id}>`,\n * permissions: member.permissions.bitfield,\n * scope: [PermissionFlagsBits.BanMembers]\n * });\n * ```\n *\n * @example\n * ```ts\n * // HTTP, the payload carries the member's bits as a string\n * assertPermissions({\n * subject: `<@${this.event.member.user.id}>`,\n * permissions: BigInt(this.event.member.permissions),\n * scope: [PermissionFlagsBits.ManageMessages]\n * });\n * ```\n *\n * @example\n * ```ts\n * // inverse, refuse a role that carries Administrator\n * assertPermissions({\n * subject: `<@&${role.id}>`,\n * permissions: role.permissions.bitfield,\n * scope: [PermissionFlagsBits.Administrator],\n * inverse: true\n * });\n * ```\n *\n * @example\n * ```ts\n * import { Notice } from '@seedcord/core';\n *\n * // a replacement notice takes the same three arguments the default one does\n * class CannotBan extends Notice {\n * public constructor(\n * message: string | undefined,\n * private readonly subject: string,\n * private readonly missing: readonly string[]\n * ) {\n * super(message ?? 'You cannot ban here.');\n * }\n *\n * public render(): ReplyResponse {\n * return { components: [new BanDenied(this.subject, this.missing).component] };\n * }\n * }\n *\n * assertPermissions({\n * subject: `<@${member.id}>`,\n * permissions: member.permissions.bitfield,\n * scope: [PermissionFlagsBits.BanMembers],\n * missingNotice: CannotBan\n * });\n * ```\n */\nexport function assertPermissions(check: PermissionAssertion): void {\n const { subject, permissions, scope, inverse = false, missingNotice, dangerousNotice } = check;\n\n if (inverse) {\n const present = presentNames(permissions, scope);\n if (present.length === 0) return;\n const Dangerous = dangerousNotice ?? HasDangerousPermissions;\n throw new Dangerous(undefined, subject, present);\n }\n\n if (hasAll(permissions, scope)) return;\n const Missing = missingNotice ?? MissingPermissions;\n throw new Missing(undefined, subject, missingNames(permissions, scope));\n}\n","/**\n * The role ids a member holds after adding and removing the given sets. Removals win over additions.\n *\n * This does not write the roles. Apply the result with `member.roles.set()` on gateway, or PATCH the member over HTTP.\n *\n * @param current - The role ids the member holds.\n * @param add - The role ids to add.\n * @param remove - The role ids to remove.\n * @returns The merged role ids.\n *\n * @example\n * ```ts\n * import { mergeRoles } from '@seedcord/core';\n *\n * // gateway\n * const ids = mergeRoles(member.roles.cache.map((role) => role.id), ['123'], ['456']);\n * await member.roles.set(ids);\n * ```\n */\nexport function mergeRoles(current: readonly string[], add: readonly string[], remove: readonly string[]): string[] {\n return [...new Set(current).union(new Set(add)).difference(new Set(remove))];\n}\n","import type { APIMessageComponentEmoji } from 'discord-api-types/v10';\n\n/**\n * A resolved custom emoji. Renders as `<:name:id>` in message content, or `<a:name:id>` when animated,\n * and passes straight to a builder's `setEmoji`.\n */\n// setEmoji in @discordjs/builders rejects any own key past these three, scanning with Object.entries\nexport class ResolvedEmoji implements APIMessageComponentEmoji {\n constructor(\n public readonly name: string,\n public readonly id: string,\n public readonly animated = false\n ) {}\n\n public toString(): string {\n return `<${this.animated ? 'a' : ''}:${this.name}:${this.id}>`;\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordRangeError } from '@seedcord/errors/internal';\n\nimport type { PageView } from './PageView';\n\n/**\n * Pure page math, usable headless. Clamps `page` into range and slices the window.\n *\n * @param items - The full list to page over.\n * @param page - The requested zero-based page, clamped into `[0, totalPages - 1]`.\n * @param perPage - The page size. Must be a positive integer.\n * @returns The sliced {@link PageView}, with `totalPages` always known.\n * @throws SeedcordRangeError when `perPage` is not a positive integer.\n */\nexport function paginate<Item>(items: readonly Item[], page: number, perPage: number): PageView<Item> {\n if (!Number.isSafeInteger(perPage) || perPage <= 0) {\n throw new SeedcordRangeError(SeedcordErrorCode.PaginationInvalidPerPage, [perPage]);\n }\n\n const totalPages = Math.max(1, Math.ceil(items.length / perPage));\n const clamped = Math.min(Math.max(Math.trunc(page), 0), totalPages - 1);\n const start = clamped * perPage;\n\n return {\n items: items.slice(start, start + perPage),\n page: clamped,\n perPage,\n totalPages,\n hasPrev: clamped > 0,\n hasNext: clamped < totalPages - 1\n };\n}\n","import { ActionRowBuilder, ButtonBuilder } from '@discordjs/builders';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordRangeError, SeedcordTypeError } from '@seedcord/errors/internal';\nimport { ButtonStyle } from 'discord-api-types/v10';\n\nimport { PAGE_MAX } from '#pagination/cursor';\n\nimport type { PageCursor } from '#pagination/cursor';\nimport type { PageView } from '#pagination/PageView';\nimport type { APIMessageComponentEmoji } from 'discord-api-types/v10';\n\n/** The five built-in nav controls. */\nexport type ControlKey = 'first' | 'prev' | 'indicator' | 'next' | 'last';\n\n// Link and Premium have no custom_id, which un-routes a control.\ntype CustomIdStyle = ButtonStyle.Primary | ButtonStyle.Secondary | ButtonStyle.Success | ButtonStyle.Danger;\n\n/** Cosmetic overrides for a control button. `style` excludes Link and Premium, which would un-route it. */\nexport interface ControlCosmetics {\n label?: string;\n style?: CustomIdStyle;\n emoji?: APIMessageComponentEmoji;\n}\n\n/** The controls passed to `render`. */\nexport interface PaginatorControls {\n /**\n * A fresh `ButtonBuilder` for one control, already stamped with its target page and disabled-at-bounds\n * state. Call only cosmetic setters on it, because a `setCustomId` or `setStyle(Link)` un-routes it.\n */\n button(key: ControlKey, cosmetics?: ControlCosmetics): ButtonBuilder;\n /**\n * Assemble an action row of the chosen controls, in order. Throws on an empty set, more than five\n * controls, or a duplicate.\n */\n row(...keys: ControlKey[]): ActionRowBuilder<ButtonBuilder>;\n}\n\nconst DEFAULT_LABEL: Record<ControlKey, string> = {\n first: 'First',\n prev: 'Prev',\n indicator: '',\n next: 'Next',\n last: 'Last'\n};\n\n// a distinct slot keeps two controls on the same page off the same id. 0-4 is the cursor's SLOT_MAX.\nconst CONTROL_SLOT: Record<ControlKey, number> = {\n first: 0,\n prev: 1,\n indicator: 2,\n next: 3,\n last: 4\n};\n\nconst MAX_BUTTONS_PER_ROW = 5;\n\nexport class Controls implements PaginatorControls {\n constructor(\n private readonly cursor: PageCursor<string>,\n private readonly view: PageView<unknown>\n ) {}\n\n button(key: ControlKey, cosmetics?: ControlCosmetics): ButtonBuilder {\n const { target, disabled } = this.navState(key);\n\n // an emoji with no explicit label makes an icon-only button. the indicator always shows its page text.\n const defaultLabel = key === 'indicator' ? this.indicatorText() : DEFAULT_LABEL[key];\n const label = cosmetics?.label ?? (key === 'indicator' || !cosmetics?.emoji ? defaultLabel : undefined);\n\n const button = new ButtonBuilder()\n // an enormous list could push last/next past PAGE_MAX, where encode throws.\n .setCustomId(this.cursor.encode({ page: Math.min(Math.max(0, target), PAGE_MAX), slot: CONTROL_SLOT[key] }))\n .setStyle(cosmetics?.style ?? ButtonStyle.Secondary)\n .setDisabled(disabled);\n if (label !== undefined) button.setLabel(label);\n if (cosmetics?.emoji) button.setEmoji(cosmetics.emoji);\n return button;\n }\n\n private navState(key: ControlKey): { target: number; disabled: boolean } {\n const { page, totalPages, hasPrev, hasNext } = this.view;\n const knownTotal = totalPages !== undefined;\n const target = {\n first: 0,\n prev: page - 1,\n indicator: page,\n next: page + 1,\n last: knownTotal ? totalPages - 1 : page\n }[key];\n // past PAGE_MAX the encoded page would clamp to a wrong one\n const disabled = {\n first: !hasPrev,\n prev: !hasPrev,\n indicator: true,\n next: !hasNext || page + 1 > PAGE_MAX,\n last: !hasNext || !knownTotal || totalPages - 1 > PAGE_MAX\n }[key];\n return { target, disabled };\n }\n\n row(...keys: ControlKey[]): ActionRowBuilder<ButtonBuilder> {\n if (keys.length === 0) {\n throw new SeedcordRangeError(SeedcordErrorCode.PaginationEmptyControls);\n }\n if (keys.length > MAX_BUTTONS_PER_ROW) {\n throw new SeedcordRangeError(SeedcordErrorCode.PaginationTooManyControls, [keys.length]);\n }\n // repeating a control reuses its slot and page, so the custom_ids collide and Discord rejects the message.\n const seen = new Set<ControlKey>();\n for (const key of keys) {\n if (seen.has(key)) throw new SeedcordTypeError(SeedcordErrorCode.PaginationDuplicateControls, [key]);\n seen.add(key);\n }\n return new ActionRowBuilder<ButtonBuilder>().addComponents(keys.map((key) => this.button(key)));\n }\n\n private indicatorText(): string {\n const { page, totalPages } = this.view;\n return totalPages === undefined ? `Page ${page + 1}` : `Page ${page + 1} of ${totalPages}`;\n }\n}\n","import { TextDisplayBuilder } from '@discordjs/builders';\n\nimport { BuilderComponent } from '#components/Component';\nimport { Controls } from '#pagination/controls';\n\nimport type { ControlKey, PaginatorControls } from '#pagination/controls';\nimport type { PageCursor } from '#pagination/cursor';\nimport type { PageView } from '#pagination/PageView';\nimport type { ButtonBuilder, ActionRowBuilder } from '@discordjs/builders';\nimport type { ReplyResponse, V2Component } from '@seedcord/types';\n\n/** What `renderItem` returns for one item, a text line or a section. */\nexport type ItemRender<Item> = (item: Item, index: number) => string | BuilderComponent<'section'>;\n\ntype Renderable = string | V2Component[] | ReplyResponse;\n\n/** A full-page render override. */\nexport type PageRender<Item> = (view: PageView<Item>, controls: PaginatorControls) => Renderable;\n\n// subclassed because BuilderComponent.instance is protected\nclass PageContainer<Item> extends BuilderComponent<'container'> {\n constructor(view: PageView<Item>, renderItem: ItemRender<Item>, controlRow: ActionRowBuilder<ButtonBuilder>) {\n super('container');\n const base = view.page * view.perPage;\n let lines: string[] = [];\n const flush = (): void => {\n if (lines.length === 0) return;\n this.instance.addTextDisplayComponents(new TextDisplayBuilder().setContent(lines.join('\\n')));\n lines = [];\n };\n view.items.forEach((item, offset) => {\n const rendered = renderItem(item, base + offset);\n if (typeof rendered === 'string') {\n lines.push(rendered);\n return;\n }\n flush();\n this.instance.addSectionComponents(rendered.component);\n });\n flush();\n this.instance.addActionRowComponents(controlRow);\n }\n}\n\nfunction toReplyResponse(renderable: Renderable): ReplyResponse {\n if (typeof renderable === 'string') return { components: [new TextDisplayBuilder().setContent(renderable)] };\n if (Array.isArray(renderable)) return { components: renderable };\n return renderable;\n}\n\nconst ARRAY_KEYS: ControlKey[] = ['first', 'prev', 'indicator', 'next', 'last'];\nconst CURSOR_KEYS: ControlKey[] = ['prev', 'indicator', 'next'];\n\n// a render override builds the whole tree, or the default container lists the items and appends\n// the controls (all five for a known total, prev/indicator/next for a cursor source)\nexport function renderPage<Item>(\n view: PageView<Item>,\n cursor: PageCursor<string>,\n config: { renderItem?: ItemRender<Item>; render?: PageRender<Item> }\n): ReplyResponse {\n const controls = new Controls(cursor, view);\n if (config.render) return toReplyResponse(config.render(view, controls));\n\n const renderItem = config.renderItem ?? String;\n const row = controls.row(...(view.totalPages === undefined ? CURSOR_KEYS : ARRAY_KEYS));\n return { components: [new PageContainer(view, renderItem, row).component] };\n}\n","import { pageCursor } from '#pagination/cursor';\nimport { renderPage } from '#pagination/render';\n\nimport type { PageCursor } from '#pagination/cursor';\nimport type { ItemRender, PageRender } from '#pagination/render';\nimport type { PageSource } from '#pagination/sources';\nimport type { ReplyResponse } from '@seedcord/types';\n\n/**\n * The transport-agnostic paginator configuration. Each transport adds its own handler wiring on top.\n *\n * @typeParam Item - The item type, inferred from the source.\n * @typeParam Prefix - The route prefix, inferred from `prefix`.\n * @typeParam Ctx - The transport's page context.\n */\nexport interface PaginatorConfig<Item, Prefix extends string, Ctx> {\n /** The route prefix used to build the page cursor on the nav buttons. */\n prefix: Prefix;\n source: PageSource<Item, Ctx>;\n /** Render one item, `index` is its absolute position across pages. Ignored when `render` is set. */\n renderItem?: ItemRender<Item>;\n /** Take over the whole page tree. Receives the page data and the controls factory. */\n render?: PageRender<Item>;\n /**\n * Whether the first page is ephemeral.\n *\n * @defaultValue `false`\n */\n ephemeral?: boolean;\n}\n\n/**\n * Holds a paginator's cursor and renders one page. Each transport extends this to add the nav handler and\n * the method that sends the first page.\n *\n * @typeParam Item - The item type, inferred from the source.\n * @typeParam Prefix - The route prefix, inferred from `config.prefix`.\n * @typeParam Ctx - The transport's page context.\n */\nexport abstract class PaginatorBase<Item, Prefix extends string, Ctx> {\n /** The page cursor, pass it to your `@ButtonRoute`. */\n public readonly cursor: PageCursor<Prefix>;\n\n protected readonly config: PaginatorConfig<Item, Prefix, Ctx>;\n\n protected constructor(config: PaginatorConfig<Item, Prefix, Ctx>) {\n this.cursor = pageCursor(config.prefix);\n this.config = config;\n }\n\n /** Render a page as a {@link ReplyResponse}. To post it elsewhere, add `flags: MessageFlags.IsComponentsV2`. */\n async page(ctx: Ctx, n: number): Promise<ReplyResponse> {\n const view = await this.config.source.page(ctx, n);\n return renderPage(view, this.cursor, this.config);\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordRangeError } from '@seedcord/errors/internal';\n\nimport { paginate } from '#pagination/paginate';\n\nimport type { PageView } from '#pagination/PageView';\nimport type { Promisable } from 'type-fest';\n\nconst DEFAULT_PER_PAGE = 10;\n\n/**\n * The one method a paginator calls on a source, once per click. Implement it to supply a custom source.\n *\n * @typeParam Item - The item type the source pages over.\n * @typeParam Ctx - The transport's page context, handed to the loader.\n */\nexport interface PageSource<Item, Ctx> {\n page(ctx: Ctx, n: number): Promise<PageView<Item>>;\n}\n\nfunction perPageOf(opts?: { perPage?: number }): number {\n const perPage = opts?.perPage ?? DEFAULT_PER_PAGE;\n if (!Number.isSafeInteger(perPage) || perPage <= 0) {\n throw new SeedcordRangeError(SeedcordErrorCode.PaginationInvalidPerPage, [perPage]);\n }\n return perPage;\n}\n\n/**\n * A source for a bounded list you can load whole. It loads the full list on every click, then slices the\n * page via {@link paginate}, so the real total is known (a real last button, \"Page X of Y\"). Cache inside\n * your loader if the load is expensive.\n *\n * @typeParam Item - The item type, inferred from the loader's return.\n * @typeParam Ctx - The transport's page context. Each transport exports a subclass that fixes it.\n */\nexport class ArraySource<Item, Ctx> implements PageSource<Item, Ctx> {\n private readonly perPage: number;\n\n constructor(\n private readonly load: (ctx: Ctx) => Promisable<readonly Item[]>,\n opts?: { perPage?: number }\n ) {\n this.perPage = perPageOf(opts);\n }\n\n async page(ctx: Ctx, n: number): Promise<PageView<Item>> {\n return paginate(await this.load(ctx), n, this.perPage);\n }\n}\n\n/**\n * A source for a large or unknown-length set you fetch one page at a time (SQL LIMIT/OFFSET, a paged API).\n * The fetcher receives the page index and the page size and reports whether a next page exists. A cursor\n * source has no cheap total, so `totalPages` is undefined, the last button is omitted, and the indicator\n * reads \"Page X\".\n *\n * @typeParam Item - The item type, inferred from the fetcher's slice.\n * @typeParam Ctx - The transport's page context. Each transport exports a subclass that fixes it.\n */\nexport class CursorSource<Item, Ctx> implements PageSource<Item, Ctx> {\n private readonly perPage: number;\n\n constructor(\n private readonly fetch: (\n ctx: Ctx,\n page: number,\n perPage: number\n ) => Promisable<{ items: readonly Item[]; hasNext: boolean }>,\n opts?: { perPage?: number }\n ) {\n this.perPage = perPageOf(opts);\n }\n\n async page(ctx: Ctx, n: number): Promise<PageView<Item>> {\n const page = Math.max(0, Math.trunc(n));\n const { items, hasNext } = await this.fetch(ctx, page, this.perPage);\n return { items: [...items], page, perPage: this.perPage, hasPrev: page > 0, hasNext };\n }\n}\n","import 'reflect-metadata';\n\nexport type { MessageContextMenuRegistry, UserContextMenuRegistry } from '#registries/ContextMenuRegistry';\nexport type { SlashRegistry } from '#registries/SlashRegistry';\n\nexport { Commands, ContextMenus } from '#src/commands/CommandInjector';\nexport type {\n CommandInfo,\n ContextMenuInfo,\n InjectedCommandMap,\n InjectedContextMenuMap\n} from '#src/commands/CommandInjector';\n\nexport { RegisterCommand } from '#decorators/Command';\nexport { SelectMenuKind } from '#decorators/interactionRoutes';\nexport {\n AutocompleteRoute,\n ButtonRoute,\n MessageContextMenuRoute,\n UserContextMenuRoute,\n ModalRoute,\n SelectMenuRoute,\n SlashRoute\n} from '#decorators/routes';\n\nexport { DispatchContext } from '#src/dispatch/DispatchContext';\nexport type { DispatchState } from '#src/dispatch/DispatchContext';\n\nexport { BaseHandler } from '#src/handlers/BaseHandler';\nexport { RepliableHandler } from '#src/handlers/RepliableHandler';\n\nexport type { CoreBase } from '#interfaces/CoreBase';\n\nexport type { Initializeable } from '#src/plugin/Plugin';\n\nexport { ShutdownPhase, StartupPhase } from '#src/lifecycle/phases';\n\nexport { defineEffectGate, defineGate } from '#gates/Gate';\nexport type { EffectGate, Gate, GateContextBase, GuildPermissionsContext, RequiredOf } from '#gates/Gate';\nexport { and, or } from '#gates/combinators';\nexport { DmOnly, GuildOnly, OwnerOnly } from '#gates/catalog/access';\nexport { Cooldown, type CooldownOptions } from '#gates/catalog/Cooldown';\nexport { RequireBotPermissions, RequirePermissions, RequireRole } from '#gates/catalog/permissions';\nexport type { PermissionScope, RequirePermissionsOptions, RequireRoleOptions } from '#gates/catalog/permissions';\nexport type { GateNoticeOptions } from '#gates/catalog/options';\n\nexport { assertPermissions } from '#src/permissions/assert';\nexport type { PermissionAssertion, PermissionNoticeOverrides } from '#src/permissions/assert';\nexport { mergeRoles } from '#src/permissions/mergeRoles';\n\nexport { BuilderComponent, RowComponent } from '#components/Component';\nexport { type RowType, type BuilderType } from '#components/builderTypes';\n\nexport { Notice } from '#stops/Notice';\nexport { Fault } from '#stops/Fault';\nexport { Silence } from '#stops/Silence';\n\nexport { CustomId } from '#customId/CustomId';\n\nexport { ResolvedEmoji } from '#src/miscellaneous/emoji';\n\nexport type { AutocompleteOptions } from '#inputs/AutocompleteOptions';\n\nexport { paginate } from '#pagination/paginate';\nexport { type PageView } from '#pagination/PageView';\nexport { PaginatorBase } from '#pagination/PaginatorBase';\nexport type { PaginatorConfig } from '#pagination/PaginatorBase';\nexport { ArraySource, CursorSource } from '#pagination/sources';\nexport type { PageSource } from '#pagination/sources';\nexport type { ControlCosmetics, ControlKey, PaginatorControls } from '#pagination/controls';\nexport type { ItemRender, PageRender } from '#pagination/render';\n\nexport { Bus, Subscribe, Subscriber, WebhookLog, WebhookUrl } from '#subscribers/index';\nexport type {\n DispatchOutcome,\n FaultSource,\n SubscribeOptions,\n Subscriptions,\n SubscriptionData,\n SubscriptionKey,\n WebhookFile,\n WebhookReport\n} from '#subscribers/index';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":";;;;;;;;;;;;;AAkDA,SAAgB,gBAAgB,OAAqB,SAAmB,CAAC,GAAG;CACxE,QAAQ,SAA4B;EAChC,MAAM,eAAe,QAAQ,eAAe,oBAAoB,IAAI;EACpE,IAAI,cACA,MAAM,IAAI,cAAc,kBAAkB,mCAAmC;GACzE,KAAK;GACL,aAAa;GACb;EACJ,CAAC;EAGL,IAAI,UAAU,YAAY,OAAO,SAAS,GACtC,MAAM,IAAI,cAAc,kBAAkB,gCAAgC;EAG9E,IAAI,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,IAClE,MAAM,IAAI,cAAc,kBAAkB,kCAAkC;EAGhF,MAAM,OAA+B,UAAU,WAAW,EAAE,MAAM,IAAI;GAAE;GAAO;EAAO;EACtF,QAAQ,eAAe,oBAAoB,MAAM,IAAI;CACzD;AACJ;;;;;;;;;;;;;;;;;;;;;;;AC4EA,SAAgB,WAAoD,GAAG,QAAiB;CACpF,OAAO,SAAwC,aAAmD;EAC9F,+BAA+C,QAAQ,WAAW;CACtE;AACJ;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,kBAA2D,GAAG,QAAiB;CAC3F,OAAO,SAAwC,aAA0D;EACrG,sCAAsD,QAAQ,WAAW;CAC7E;AACJ;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,qBAAgF,GAAG,OAAgB;CAC/G,OAAO,SACH,aACI;EACJ,sBAAsB,mBAAmB,uBAAuB,IAAI,GAAG,OAAO,WAAW;CAC7F;AACJ;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,wBACZ,GAAG,OACL;CACE,OAAO,SACH,aACI;EACJ,sBAAsB,mBAAmB,uBAAuB,OAAO,GAAG,OAAO,WAAW;CAChG;AACJ;;;;;;;;;;;;AAaA,SAAgB,YAAuD,GAAG,MAAY;CAClF,OAAO,SAAwC,aAAgE;EAC3G,8BAA8C,MAAM,WAAW;CACnE;AACJ;;;;;;;;;;;AAYA,SAAgB,WAAsD,GAAG,MAAY;CACjF,OAAO,SAAwC,aAA+D;EAC1G,6BAA6C,MAAM,WAAW;CAClE;AACJ;;;;;;;;;;;;;;;;;AAkBA,SAAgB,gBACZ,MACA,GAAG,MACL;CACE,OAAO,SAAwC,aAA4D;EACvG,oBAAoB,kBAAkB,IAAI,GAAG,MAAM,WAAW;CAClE;AACJ;;;;;;;;;ACtRA,IAAa,kBAAb,MAA6B;CAGG;CAF5B,AAAiB,QAAgC,CAAC;CAElD,YAAY,AAAgB,SAAwB;EAAxB;CAAyB;CAErD,IAAqC,KAAU,OAAiC;EAC5E,KAAK,MAAM,OAAO;CACtB;CAEA,IAAqC,KAA0C;EAC3E,OAAO,KAAK,MAAM;CACtB;AACJ;;;;;;;;;;;ACXA,IAAsB,cAAtB,MAAiE;CAQzC;CAPpB,AAAmB;CACnB,AAAmB;CAEnB,AAAmB;CAEnB,AAAU,YACN,OACA,AAAgB,MAChB,UACA,SACF;EAHkB;EAIhB,KAAK,QAAQ;EACb,KAAK,SAAS,IAAI,OAAO,KAAK,YAAY,MAAM,EAAE,QAAQ,CAAC;EAC3D,KAAK,WAAW;CACpB;AAOJ;;;;;;;;;;;;;;AClBA,IAAsB,mBAAtB,cAMU,YAA0B;CAChC,AAAmB;CACnB,AAAmB;CAEnB,AAAU,YAAY,OAAc,MAAa,UAA4B;EACzE,MAAM,OAAO,MAAM,UAAU,cAAc;EAC3C,KAAK,UAAU,UAAU,WAAW,KAAK,YAAY;EAErD,KAAK,SAAS,KAAK,YAAY,OAAO,MAAM,KAAK,OAAO;CAC5D;;CAKA,AAAO,YAAqB;EACxB,OAAO,KAAK;CAChB;;CAGA,AAAU,MAAM,UAA2C,MAAoC;EAC3F,OAAO,KAAK,OAAO,MAAM,UAAU,IAAI;CAC3C;;CAGA,AAAU,MAAM,MAAiC;EAC7C,OAAO,KAAK,OAAO,MAAM,IAAI;CACjC;;CAGA,AAAU,SAAS,UAA2C,MAAoC;EAC9F,OAAO,KAAK,OAAO,SAAS,UAAU,IAAI;CAC9C;CAKA,AAAU,KACN,kBACA,eACiB;EAEjB,IAAI,kBAAkB,QAAW,OAAO,KAAK,OAAO,KAAK,gBAAmD;EAE5G,OAAO,KAAK,OAAO,KAAK,kBAA8B,aAAa;CACvE;;CAGA,AAAU,KAAK,UAA2C,MAAoC;EAC1F,OAAO,KAAK,OAAO,KAAK,UAAU,IAAI;CAC1C;;CAGA,AAAU,OAAO,QAAkC;EAC/C,OAAO,WAAW,SAAY,KAAK,OAAO,OAAO,IAAI,KAAK,OAAO,OAAO,MAAM;CAClF;AACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC9CA,IAAa,QAAb,cAA2B,OAAO;CAC9B,AAAO,YAAY,SAAiD;EAChE,MAAM,oBAAoB,SAAS,UAAU,SAAY,SAAY,EAAE,OAAO,QAAQ,MAAM,CAAC;EAC7F,KAAK,SAAS,SAAS,UAAU;CACrC;CAEA,AAAO,OAAO,KAAmC;EAC7C,MAAM,UAAU,IAAI,qBAAqB;EAKzC,OAAO,EAAE,YAAY,CAAC,IAJL,WACb,6CAA6C,QAAQ,qEAAqE,IAAI,KAAK,KACnI,OAEqB,CAAC,CAAC,SAAS,EAAE;CAC1C;AACJ;;;;ACVA,SAAgB,IAAI,GAAG,OAAgE;CACnF,OAAO,WAAW,MAAM,KAAK,SAAS,KAAK,IAAI,CAAC,CAAC,KAAK,KAAK,GAAG,OAAO,QAAQ;EACzE,KAAK,MAAM,QAAQ,OACf,MAAM,SAAS,MAAM,GAAG;CAEhC,CAAC;AACL;AAQA,SAAS,YAAY,KAA0D;CAC3E,OAAO,EAAE,WAAW;AACxB;AAsCA,SAAgB,GAAG,GAAG,MAA6E;CAC/F,MAAM,OAAO,KAAK,GAAG,EAAE;CACvB,MAAM,UAAU,SAAS,UAAa,YAAY,IAAI,IAAI,OAAO;CACjE,MAAM,QAAS,UAAU,KAAK,MAAM,GAAG,EAAE,IAAI;CAE7C,OAAO,WAAW,MAAM,KAAK,SAAS,KAAK,IAAI,CAAC,CAAC,KAAK,KAAK,GAAG,OAAO,QAAQ;EACzE,MAAM,YAAsB,CAAC;EAC7B,IAAI,qBAAqB;EACzB,KAAK,MAAM,QAAQ,OAAO;GACtB,MAAM,OAAO,YAAY,GAAG;GAC5B,IAAI;IACA,MAAM,SAAS,MAAM,GAAG;IACxB;GACJ,SAAS,OAAO;IAEZ,IAAI,iBAAiB,UAAU,EAAE,iBAAiB,UAAU,CAAC,MAAM,QAAQ;KAEvE,gBAAgB,KAAK,IAAI;KACzB,IAAI,MAAM,YAAY,QAAW,qBAAqB;UACjD,UAAU,KAAK,MAAM,OAAO;KACjC;IACJ;IACA,MAAM;GACV;EACJ;EACA,IAAI,SAAS,MAAM,OAAO,QAAQ,SAAS,aAAa,QAAQ,KAAK,GAAG,IAAI,QAAQ;EAEpF,IAAI,sBAAsB,UAAU,SAAS,GAAG,MAAM,IAAI,SAAS,SAAS;EAC5E,MAAM,IAAI,WAAW;CACzB,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtFA,SAAgB,UAAU,SAAiE;CACvF,OAAO,WAAW,cAAc,QAAQ;EACpC,MAAM,SAAS,IAAI,KAAK,OAAO,YAAY,CAAC;EAC5C,IAAI,IAAI,WAAW,QAAQ,OAAO,SAAS,IAAI,MAAM,GAAG;EACxD,MAAM,WAAW,UAAU,YAAY,IAAI,SAAS,OAAO,CAAC;CAChE,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,UAAU,SAAiE;CACvF,OAAO,WAAW,cAAc,QAAQ;EACpC,IAAI,IAAI,SAAS;EACjB,MAAM,WAAW,UAAU,YAAY,IAAI,WAAW,OAAO,CAAC;CAClE,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,OAAO,SAA8D;CACjF,OAAO,WAAW,WAAW,QAAQ;EACjC,IAAI,CAAC,IAAI,SAAS;EAClB,MAAM,WAAW,UAAU,YAAY,IAAI,QAAQ,OAAO,CAAC;CAC/D,CAAC;AACL;;;;ACvCA,IAAI,UAAU;AAEd,SAAS,WAAW,KAAsB,KAA2C;CACjF,IAAI,QAAQ,SAAS,OAAO,IAAI,WAAW;CAC3C,IAAI,QAAQ,WAAW,OAAO,IAAI,aAAa;CAC/C,OAAO,IAAI,UAAU;AACzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,SAAgB,SACZ,UACA,SACuC;CACvC,IAAI;CACJ,IAAI,OAAO,aAAa,UACpB,WAAW,WAAW;MACnB;EACH,MAAM,SAAS,cAAc,QAAQ;EACrC,IAAI,WAAW,MAAM,MAAM,IAAI,kBAAkB,kBAAkB,6BAA6B,CAAC,QAAQ,CAAC;EAC1G,WAAW;CACf;CAEA,IAAI,YAAY,GACZ,MAAM,IAAI,kBAAkB,kBAAkB,6BAA6B,CAAC,OAAO,QAAQ,CAAC,CAAC;CAGjG,MAAM,MAAM,SAAS,OAAO;CAE5B,MAAM,SAAS,SAAS,UAAU,SAAY,EAAE,SAAS,IAAI;EAAE;EAAU,OAAO,QAAQ;CAAM;CAC9F,MAAM,SAAS;CAEf,MAAM,SAAS,QACX,YAAY,IAAI,WAAW,OAAO,SAAS,GAAG,IAAI,IAAI,SAAS,IAAI,SAAS,SAAS,EAAE,GAAG,WAAW,KAAK,GAAG;CAEjH,OAAO,iBACH,YACA,OAAO,QAAQ;EACX,MAAM,SAAS,MAAM,IAAI,KAAK,YAAY,KAAK,MAAM,GAAG,GAAG,MAAM;EACjE,IAAI,CAAC,OAAO,SAAS;EACrB,IAAI,SAAS,QAAQ,MAAM,QAAQ,OAAO,OAAO,OAAO;EACxD,MAAM,IAAI,WAAW,OAAO,SAAS,SAAS,UAAU,OAAO,OAAO,CAAC;CAC3E,GACA,OAAO,QAAQ;EACX,MAAM,IAAI,KAAK,YAAY,OAAO,MAAM,GAAG,GAAG,MAAM;CACxD,CACJ;AACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACnDA,SAAgB,kBAAkB,OAAkC;CAChE,MAAM,EAAE,SAAS,aAAa,OAAO,UAAU,OAAO,eAAe,oBAAoB;CAEzF,IAAI,SAAS;EACT,MAAM,UAAU,aAAa,aAAa,KAAK;EAC/C,IAAI,QAAQ,WAAW,GAAG;EAE1B,MAAM,KADY,mBAAmB,yBACjB,QAAW,SAAS,OAAO;CACnD;CAEA,IAAI,OAAO,aAAa,KAAK,GAAG;CAEhC,MAAM,KADU,iBAAiB,oBACf,QAAW,SAAS,aAAa,aAAa,KAAK,CAAC;AAC1E;;;;;;;;;;;;;;;;;;;;;;;ACzFA,SAAgB,WAAW,SAA4B,KAAwB,QAAqC;CAChH,OAAO,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,CAAC,MAAM,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,IAAI,MAAM,CAAC,CAAC;AAC/E;;;;;;;;ACdA,IAAa,gBAAb,MAA+D;CAEvC;CACA;CACA;CAHpB,YACI,AAAgB,MAChB,AAAgB,IAChB,AAAgB,WAAW,OAC7B;EAHkB;EACA;EACA;CACjB;CAEH,AAAO,WAAmB;EACtB,OAAO,IAAI,KAAK,WAAW,MAAM,GAAG,GAAG,KAAK,KAAK,GAAG,KAAK,GAAG;CAChE;AACJ;;;;;;;;;;;;;ACHA,SAAgB,SAAe,OAAwB,MAAc,SAAiC;CAClG,IAAI,CAAC,OAAO,cAAc,OAAO,KAAK,WAAW,GAC7C,MAAM,IAAI,mBAAmB,kBAAkB,0BAA0B,CAAC,OAAO,CAAC;CAGtF,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,KAAK,MAAM,SAAS,OAAO,CAAC;CAChE,MAAM,UAAU,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,GAAG,aAAa,CAAC;CACtE,MAAM,QAAQ,UAAU;CAExB,OAAO;EACH,OAAO,MAAM,MAAM,OAAO,QAAQ,OAAO;EACzC,MAAM;EACN;EACA;EACA,SAAS,UAAU;EACnB,SAAS,UAAU,aAAa;CACpC;AACJ;;;;ACOA,MAAM,gBAA4C;CAC9C,OAAO;CACP,MAAM;CACN,WAAW;CACX,MAAM;CACN,MAAM;AACV;AAGA,MAAM,eAA2C;CAC7C,OAAO;CACP,MAAM;CACN,WAAW;CACX,MAAM;CACN,MAAM;AACV;AAEA,MAAM,sBAAsB;AAE5B,IAAa,WAAb,MAAmD;CAE1B;CACA;CAFrB,YACI,AAAiB,QACjB,AAAiB,MACnB;EAFmB;EACA;CAClB;CAEH,OAAO,KAAiB,WAA6C;EACjE,MAAM,EAAE,QAAQ,aAAa,KAAK,SAAS,GAAG;EAG9C,MAAM,eAAe,QAAQ,cAAc,KAAK,cAAc,IAAI,cAAc;EAChF,MAAM,QAAQ,WAAW,UAAU,QAAQ,eAAe,CAAC,WAAW,QAAQ,eAAe;EAE7F,MAAM,SAAS,IAAI,cAAc,CAAC,CAE7B,YAAY,KAAK,OAAO,OAAO;GAAE,MAAM,KAAK,IAAI,KAAK,IAAI,GAAG,MAAM,GAAG,QAAQ;GAAG,MAAM,aAAa;EAAK,CAAC,CAAC,CAAC,CAC3G,SAAS,WAAW,SAAS,YAAY,SAAS,CAAC,CACnD,YAAY,QAAQ;EACzB,IAAI,UAAU,QAAW,OAAO,SAAS,KAAK;EAC9C,IAAI,WAAW,OAAO,OAAO,SAAS,UAAU,KAAK;EACrD,OAAO;CACX;CAEA,AAAQ,SAAS,KAAwD;EACrE,MAAM,EAAE,MAAM,YAAY,SAAS,YAAY,KAAK;EACpD,MAAM,aAAa,eAAe;EAgBlC,OAAO;GAAE,QAfM;IACX,OAAO;IACP,MAAM,OAAO;IACb,WAAW;IACX,MAAM,OAAO;IACb,MAAM,aAAa,aAAa,IAAI;GACxC,EAAE;GASe,UAPA;IACb,OAAO,CAAC;IACR,MAAM,CAAC;IACP,WAAW;IACX,MAAM,CAAC,WAAW,OAAO;IACzB,MAAM,CAAC,WAAW,CAAC,cAAc,aAAa;GAClD,EAAE;EACwB;CAC9B;CAEA,IAAI,GAAG,MAAqD;EACxD,IAAI,KAAK,WAAW,GAChB,MAAM,IAAI,mBAAmB,kBAAkB,uBAAuB;EAE1E,IAAI,KAAK,SAAS,qBACd,MAAM,IAAI,mBAAmB,kBAAkB,2BAA2B,CAAC,KAAK,MAAM,CAAC;EAG3F,MAAM,uBAAO,IAAI,IAAgB;EACjC,KAAK,MAAM,OAAO,MAAM;GACpB,IAAI,KAAK,IAAI,GAAG,GAAG,MAAM,IAAI,kBAAkB,kBAAkB,6BAA6B,CAAC,GAAG,CAAC;GACnG,KAAK,IAAI,GAAG;EAChB;EACA,OAAO,IAAI,iBAAgC,CAAC,CAAC,cAAc,KAAK,KAAK,QAAQ,KAAK,OAAO,GAAG,CAAC,CAAC;CAClG;CAEA,AAAQ,gBAAwB;EAC5B,MAAM,EAAE,MAAM,eAAe,KAAK;EAClC,OAAO,eAAe,SAAY,QAAQ,OAAO,MAAM,QAAQ,OAAO,EAAE,MAAM;CAClF;AACJ;;;;ACrGA,IAAM,gBAAN,cAAkC,iBAA8B;CAC5D,YAAY,MAAsB,YAA8B,YAA6C;EACzG,MAAM,WAAW;EACjB,MAAM,OAAO,KAAK,OAAO,KAAK;EAC9B,IAAI,QAAkB,CAAC;EACvB,MAAM,cAAoB;GACtB,IAAI,MAAM,WAAW,GAAG;GACxB,KAAK,SAAS,yBAAyB,IAAI,mBAAmB,CAAC,CAAC,WAAW,MAAM,KAAK,IAAI,CAAC,CAAC;GAC5F,QAAQ,CAAC;EACb;EACA,KAAK,MAAM,SAAS,MAAM,WAAW;GACjC,MAAM,WAAW,WAAW,MAAM,OAAO,MAAM;GAC/C,IAAI,OAAO,aAAa,UAAU;IAC9B,MAAM,KAAK,QAAQ;IACnB;GACJ;GACA,MAAM;GACN,KAAK,SAAS,qBAAqB,SAAS,SAAS;EACzD,CAAC;EACD,MAAM;EACN,KAAK,SAAS,uBAAuB,UAAU;CACnD;AACJ;AAEA,SAAS,gBAAgB,YAAuC;CAC5D,IAAI,OAAO,eAAe,UAAU,OAAO,EAAE,YAAY,CAAC,IAAI,mBAAmB,CAAC,CAAC,WAAW,UAAU,CAAC,EAAE;CAC3G,IAAI,MAAM,QAAQ,UAAU,GAAG,OAAO,EAAE,YAAY,WAAW;CAC/D,OAAO;AACX;AAEA,MAAM,aAA2B;CAAC;CAAS;CAAQ;CAAa;CAAQ;AAAM;AAC9E,MAAM,cAA4B;CAAC;CAAQ;CAAa;AAAM;AAI9D,SAAgB,WACZ,MACA,QACA,QACa;CACb,MAAM,WAAW,IAAI,SAAS,QAAQ,IAAI;CAC1C,IAAI,OAAO,QAAQ,OAAO,gBAAgB,OAAO,OAAO,MAAM,QAAQ,CAAC;CAEvE,MAAM,aAAa,OAAO,cAAc;CACxC,MAAM,MAAM,SAAS,IAAI,GAAI,KAAK,eAAe,SAAY,cAAc,UAAW;CACtF,OAAO,EAAE,YAAY,CAAC,IAAI,cAAc,MAAM,YAAY,GAAG,CAAC,CAAC,SAAS,EAAE;AAC9E;;;;;;;;;;;;AC3BA,IAAsB,gBAAtB,MAAsE;;CAElE,AAAgB;CAEhB,AAAmB;CAEnB,AAAU,YAAY,QAA4C;EAC9D,KAAK,SAAS,WAAW,OAAO,MAAM;EACtC,KAAK,SAAS;CAClB;;CAGA,MAAM,KAAK,KAAU,GAAmC;EACpD,MAAM,OAAO,MAAM,KAAK,OAAO,OAAO,KAAK,KAAK,CAAC;EACjD,OAAO,WAAW,MAAM,KAAK,QAAQ,KAAK,MAAM;CACpD;AACJ;;;;AC/CA,MAAM,mBAAmB;AAYzB,SAAS,UAAU,MAAqC;CACpD,MAAM,UAAU,MAAM,WAAW;CACjC,IAAI,CAAC,OAAO,cAAc,OAAO,KAAK,WAAW,GAC7C,MAAM,IAAI,mBAAmB,kBAAkB,0BAA0B,CAAC,OAAO,CAAC;CAEtF,OAAO;AACX;;;;;;;;;AAUA,IAAa,cAAb,MAAqE;CAI5C;CAHrB,AAAiB;CAEjB,YACI,AAAiB,MACjB,MACF;EAFmB;EAGjB,KAAK,UAAU,UAAU,IAAI;CACjC;CAEA,MAAM,KAAK,KAAU,GAAoC;EACrD,OAAO,SAAS,MAAM,KAAK,KAAK,GAAG,GAAG,GAAG,KAAK,OAAO;CACzD;AACJ;;;;;;;;;;AAWA,IAAa,eAAb,MAAsE;CAI7C;CAHrB,AAAiB;CAEjB,YACI,AAAiB,OAKjB,MACF;EANmB;EAOjB,KAAK,UAAU,UAAU,IAAI;CACjC;CAEA,MAAM,KAAK,KAAU,GAAoC;EACrD,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC;EACtC,MAAM,EAAE,OAAO,YAAY,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,OAAO;EACnE,OAAO;GAAE,OAAO,CAAC,GAAG,KAAK;GAAG;GAAM,SAAS,KAAK;GAAS,SAAS,OAAO;GAAG;EAAQ;CACxF;AACJ;;;;;ACMA,MAAa"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/decorators/Command.ts","../src/decorators/routes.ts","../src/dispatch/DispatchContext.ts","../src/handlers/BaseHandler.ts","../src/handlers/RepliableHandler.ts","../src/stops/Fault.ts","../src/gates/combinators.ts","../src/gates/catalog/access.ts","../src/gates/catalog/Cooldown.ts","../src/permissions/assert.ts","../src/permissions/mergeRoles.ts","../src/miscellaneous/emoji.ts","../src/pagination/paginate.ts","../src/pagination/controls.ts","../src/pagination/render.ts","../src/pagination/PaginatorBase.ts","../src/pagination/sources.ts","../src/index.ts"],"sourcesContent":["import 'reflect-metadata';\n\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { CommandMetadataKey } from '#src/metadataKeys';\n\nimport type { BuilderComponent } from '#components/Component';\nimport type { Constructor } from 'type-fest';\n\ntype CommandCtor = Constructor<BuilderComponent<'command' | 'context_menu'>>;\n\ninterface GlobalMeta {\n scope: 'global';\n}\n\ninterface GuildMeta {\n scope: 'guild';\n guilds: string[];\n}\n\nexport type CommandMeta = GlobalMeta | GuildMeta;\n\ntype CommandScope = CommandMeta['scope'];\n\n/**\n * Attaches deployment metadata so the framework registers this command globally or in specific guilds.\n *\n * @decorator\n * @example\n * ```typescript\n * \\@RegisterCommand('global')\n * class PingCommand extends BuilderComponent<'command'> {} // or BuilderComponent<'context_menu'>\n * ```\n */\nexport function RegisterCommand(scope: 'global'): (ctor: CommandCtor) => void;\n\n/**\n * Registers a command for specific guild deployment.\n *\n * @param guilds - Array of guild IDs where the command should be registered\n * @decorator\n * @example\n * ```typescript\n * \\@RegisterCommand('guild', ['123456789'])\n * class AdminCommand extends BuilderComponent<'command'> {} // or BuilderComponent<'context_menu'>\n * ```\n */\nexport function RegisterCommand(scope: 'guild', guilds: string[]): (ctor: CommandCtor) => void;\n\nexport function RegisterCommand(scope: CommandScope, guilds: string[] = []) {\n return (ctor: CommandCtor): void => {\n const existingMeta = Reflect.getOwnMetadata(CommandMetadataKey, ctor) as CommandMeta | undefined;\n if (existingMeta) {\n throw new SeedcordError(SeedcordErrorCode.DecoratorCommandAlreadyRegistered, [\n ctor.name,\n existingMeta.scope,\n scope\n ]);\n }\n\n if (scope === 'global' && guilds.length > 0) {\n throw new SeedcordError(SeedcordErrorCode.DecoratorCommandGlobalWithGuilds);\n }\n\n if (scope === 'guild' && (!Array.isArray(guilds) || guilds.length === 0)) {\n throw new SeedcordError(SeedcordErrorCode.DecoratorCommandGuildWithoutGuilds);\n }\n\n const meta: GlobalMeta | GuildMeta = scope === 'global' ? { scope } : { scope, guilds };\n Reflect.defineMetadata(CommandMetadataKey, meta, ctor);\n };\n}\n","import { ApplicationCommandType } from 'discord-api-types/v10';\n\nimport {\n contextMenuRouteOf,\n selectMenuRouteOf,\n storeComponentRoute,\n storeInteractionRoute\n} from '#decorators/interactionRoutes';\nimport { InteractionRoutes } from '#src/metadataKeys';\n\nimport type { AnyCustomId } from '#customId/CustomId';\nimport type { SelectMenuKind } from '#decorators/interactionRoutes';\nimport type { ContextMenuKind, NamesFor } from '#registries/ContextMenuRegistry';\nimport type { SlashRegistry } from '#registries/SlashRegistry';\nimport type {\n AutocompleteRouteBrand,\n ComponentDefsBrand,\n ComponentKindBrand,\n ContextMenuKindBrand,\n ContextMenuNamesBrand,\n SlashRouteBrand\n} from './brands';\nimport type { Constructor } from 'type-fest';\n\ntype ComponentBrand = 'button' | 'modal' | SelectMenuKind;\n\n// phantom brands keep the decorators off transport-specific classes\ntype AnyHandlerCtor = new (...args: any[]) => unknown;\n\ntype SlashRouteOf<TCtor extends AnyHandlerCtor> =\n InstanceType<TCtor> extends {\n [SlashRouteBrand]?: infer Route extends keyof SlashRegistry;\n }\n ? Route\n : never;\n\ntype AutocompleteRouteOf<TCtor extends AnyHandlerCtor> =\n InstanceType<TCtor> extends {\n [AutocompleteRouteBrand]?: infer Route extends keyof SlashRegistry;\n }\n ? Route\n : never;\n\ntype ContextMenuKindOf<TCtor extends AnyHandlerCtor> =\n InstanceType<TCtor> extends {\n [ContextMenuKindBrand]?: infer Kind extends ContextMenuKind;\n }\n ? Kind\n : never;\n\ntype ContextMenuNamesOf<TCtor extends AnyHandlerCtor> =\n InstanceType<TCtor> extends {\n [ContextMenuNamesBrand]?: infer Names;\n }\n ? Names\n : never;\n\ntype ComponentOf<TCtor extends AnyHandlerCtor> =\n InstanceType<TCtor> extends {\n [ComponentKindBrand]?: infer Brand extends ComponentBrand;\n }\n ? Brand\n : never;\n\ntype DefsOf<TCtor extends AnyHandlerCtor> =\n InstanceType<TCtor> extends {\n [ComponentDefsBrand]?: infer Defs extends readonly AnyCustomId[];\n }\n ? Defs\n : never;\n\ntype AssertSlashRoute<Route extends keyof SlashRegistry, TCtor extends AnyHandlerCtor> = [Route] extends [\n SlashRouteOf<TCtor>\n]\n ? [SlashRouteOf<TCtor>] extends [Route]\n ? TCtor\n : Constructor<['SlashHandler declares a route the SlashRoute decorator does not list', SlashRouteOf<TCtor>]>\n : Constructor<['SlashRoute does not match the SlashHandler generic', Route]>;\n\ntype AssertAutocompleteRoute<Route extends keyof SlashRegistry, TCtor extends AnyHandlerCtor> = [Route] extends [\n AutocompleteRouteOf<TCtor>\n]\n ? [AutocompleteRouteOf<TCtor>] extends [Route]\n ? TCtor\n : Constructor<\n [\n 'AutocompleteHandler declares a command the AutocompleteRoute decorator does not list',\n AutocompleteRouteOf<TCtor>\n ]\n >\n : Constructor<['AutocompleteRoute does not match the AutocompleteHandler generic', Route]>;\n\ntype AssertContextMenuRoute<\n Kind extends ContextMenuKind,\n Names extends NamesFor<Kind>,\n TCtor extends AnyHandlerCtor\n> = [Kind] extends [ContextMenuKindOf<TCtor>]\n ? [ContextMenuKindOf<TCtor>] extends [Kind]\n ? [Names] extends [ContextMenuNamesOf<TCtor>]\n ? [ContextMenuNamesOf<TCtor>] extends [Names]\n ? TCtor\n : Constructor<\n [\n 'ContextMenuHandler declares a name the ContextMenuRoute decorator does not list',\n ContextMenuNamesOf<TCtor>\n ]\n >\n : Constructor<['ContextMenuRoute lists a name the ContextMenuHandler generic does not declare', Names]>\n : Constructor<\n [\n 'ContextMenuHandler declares a kind the ContextMenuRoute decorator does not match',\n ContextMenuKindOf<TCtor>\n ]\n >\n : Constructor<['ContextMenuRoute does not match the ContextMenuHandler generic', Kind]>;\n\n// one assignability direction for the defs, a bidirectional check would reject const-inferred readonly tuples\ntype AssertComponentRoute<\n Brand extends ComponentBrand,\n Defs extends readonly AnyCustomId[],\n TCtor extends AnyHandlerCtor\n> = [ComponentOf<TCtor>] extends [Brand]\n ? [Brand] extends [ComponentOf<TCtor>]\n ? [DefsOf<TCtor>] extends [Defs]\n ? TCtor\n : Constructor<['the customId definitions do not match the handler generic', DefsOf<TCtor>]>\n : Constructor<['the decorator does not match the handler kind', ComponentOf<TCtor>]>\n : Constructor<['the decorator does not match the handler kind', ComponentOf<TCtor>]>;\n\n/**\n * Routes one or more slash commands to a `SlashHandler`.\n *\n * Pass the same route string(s) as the handler's generic. A single command reads `this.options`, several\n * commands branch with `this.match`. The decorator routes and the generic must match exactly. Listing\n * fewer or more than the handler declares is a compile error. Subcommands use a slash path.\n *\n * @param routes - The route string(s) this handler serves, e.g. `'ban'`, `'ban', 'kick'`, or `'demo/setup'`.\n * @decorator\n * @example\n * ```ts\n * \\@SlashRoute('ban')\n * class BanHandler extends SlashHandler<'ban'> {\n * async execute() {\n * const target = this.options.getUser('target');\n * }\n * }\n * ```\n */\nexport function SlashRoute<const Route extends keyof SlashRegistry>(...routes: Route[]) {\n return function <TCtor extends AnyHandlerCtor>(constructor: AssertSlashRoute<Route, TCtor>): void {\n storeInteractionRoute(InteractionRoutes.Slash, routes, constructor);\n };\n}\n\n/**\n * Routes one or more commands' autocomplete to an `AutocompleteHandler`.\n *\n * Pass the same command route string(s) as the handler's generic. One command branches with `this.match`\n * over its autocompletable fields, several commands share one handler whose arms span every command's\n * fields. Listing fewer or more commands than the handler declares is a compile error, since the\n * decorator routes and the generic must match exactly.\n *\n * @param routes - The command route string(s) this handler serves, e.g. `'search'` or `'search', 'find'`.\n * @decorator\n * @example\n * ```ts\n * \\@AutocompleteRoute('search')\n * class SearchAutocomplete extends AutocompleteHandler<'search'> {\n * async execute() {\n * await this.match({ query: (value, respond) => respond([{ name: value, value }]) });\n * }\n * }\n * ```\n */\nexport function AutocompleteRoute<const Route extends keyof SlashRegistry>(...routes: Route[]) {\n return function <TCtor extends AnyHandlerCtor>(constructor: AssertAutocompleteRoute<Route, TCtor>): void {\n storeInteractionRoute(InteractionRoutes.Autocomplete, routes, constructor);\n };\n}\n\n/**\n * Routes one or more user context-menu commands to a `UserContextMenuHandler`.\n *\n * Every name is checked against the user registry. The handler's generic must declare the same names,\n * checked in both directions.\n *\n * @param names - The command name(s) this handler serves.\n * @decorator\n * @example\n * ```ts\n * \\@UserContextMenuRoute('View Profile')\n * class ViewProfile extends UserContextMenuHandler<'View Profile'> {\n * async execute() {\n * const user = this.target;\n * }\n * }\n * ```\n */\nexport function UserContextMenuRoute<const Names extends NamesFor<ApplicationCommandType.User>>(...names: Names[]) {\n return function <TCtor extends AnyHandlerCtor>(\n constructor: AssertContextMenuRoute<ApplicationCommandType.User, Names, TCtor>\n ): void {\n storeInteractionRoute(contextMenuRouteOf(ApplicationCommandType.User), names, constructor);\n };\n}\n\n/**\n * Routes one or more message context-menu commands to a `MessageContextMenuHandler`.\n *\n * Every name is checked against the message registry. The handler's generic must declare the same names,\n * checked in both directions.\n *\n * @param names - The command name(s) this handler serves.\n * @decorator\n * @example\n * ```ts\n * \\@MessageContextMenuRoute('Report Message')\n * class ReportMessage extends MessageContextMenuHandler<'Report Message'> {\n * async execute() {\n * const message = this.target;\n * }\n * }\n * ```\n */\nexport function MessageContextMenuRoute<const Names extends NamesFor<ApplicationCommandType.Message>>(\n ...names: Names[]\n) {\n return function <TCtor extends AnyHandlerCtor>(\n constructor: AssertContextMenuRoute<ApplicationCommandType.Message, Names, TCtor>\n ): void {\n storeInteractionRoute(contextMenuRouteOf(ApplicationCommandType.Message), names, constructor);\n };\n}\n\n/**\n * Routes button interactions to handler classes.\n *\n * Pass the {@link CustomId} definition(s) this handler decodes and list the same ones in the handler's\n * generic. Passing different definitions to the decorator and the generic is a compile error. Routing\n * matches the stable prefix, so a wire minted from an older shape still reaches the handler, where\n * reading this.params throws StaleCustomId and the controller boundary turns it into a reply.\n *\n * @param defs - The customId definition(s) this handler decodes, one per route.\n * @decorator\n */\nexport function ButtonRoute<const Defs extends readonly AnyCustomId[]>(...defs: Defs) {\n return function <TCtor extends AnyHandlerCtor>(constructor: AssertComponentRoute<'button', Defs, TCtor>): void {\n storeComponentRoute(InteractionRoutes.Button, defs, constructor);\n };\n}\n\n/**\n * Routes modal submissions to handler classes.\n *\n * Pass the {@link CustomId} definition(s) this handler decodes and list the same ones in the handler's\n * generic. Passing different definitions to the decorator and the generic is a compile error. Routing\n * matches the stable prefix here too, throwing StaleCustomId on read the same as a button route.\n *\n * @param defs - The customId definition(s) this handler decodes, one per route.\n * @decorator\n */\nexport function ModalRoute<const Defs extends readonly AnyCustomId[]>(...defs: Defs) {\n return function <TCtor extends AnyHandlerCtor>(constructor: AssertComponentRoute<'modal', Defs, TCtor>): void {\n storeComponentRoute(InteractionRoutes.Modal, defs, constructor);\n };\n}\n\n/**\n * Routes select menu interactions to handler classes.\n *\n * Pass the select kind and the {@link CustomId} definition(s) this handler decodes. The handler's\n * generic must list the same kind and definitions, or it is a compile error.\n *\n * @param type - Select menu kind from {@link SelectMenuKind}.\n * @param defs - The customId definition(s) this handler decodes, one per route.\n * @decorator\n *\n * @example\n * ```typescript\n * \\@SelectMenuRoute(SelectMenuKind.User, AssignId)\n * class AssignSelect extends SelectMenuHandler<SelectMenuKind.User, [typeof AssignId]> {}\n * ```\n */\nexport function SelectMenuRoute<const Kind extends SelectMenuKind, const Defs extends readonly AnyCustomId[]>(\n type: Kind,\n ...defs: Defs\n) {\n return function <TCtor extends AnyHandlerCtor>(constructor: AssertComponentRoute<Kind, Defs, TCtor>): void {\n storeComponentRoute(selectMenuRouteOf(type), defs, constructor);\n };\n}\n","/**\n * The per-dispatch bag. Any code can merge fields into it by declaration merging, the\n * same pattern as {@link SlashRegistry}. It stays empty until one does, so `keyof DispatchState` is\n * `never` and the bag is inert.\n */\nexport interface DispatchState {}\n\n/**\n * A typed bag allocated once per interaction dispatch and handed to the handler. The middleware and i18n\n * work reads and writes it through {@link DispatchState}. Today the bag is empty, so it only carries the\n * dispatched `routeId`.\n */\nexport class DispatchContext {\n private readonly state: Partial<DispatchState> = {};\n\n constructor(public readonly routeId: string | null) {}\n\n set<Key extends keyof DispatchState>(key: Key, value: DispatchState[Key]): void {\n this.state[key] = value;\n }\n\n get<Key extends keyof DispatchState>(key: Key): DispatchState[Key] | undefined {\n return this.state[key];\n }\n}\n","import { Logger } from '@seedcord/logger';\n\nimport type { CoreBase } from '#interfaces/CoreBase';\nimport type { DispatchContext } from '#src/dispatch/DispatchContext';\nimport type { LoggerChannelId } from '@seedcord/types';\n\n/**\n * Base class every transport handler extends. Don't register handlers directly. Use the more specific\n * handler subclasses.\n *\n * @typeParam Event - The event or interaction this handler processes\n * @typeParam TCore - The transport's Core\n */\nexport abstract class BaseHandler<Event, TCore extends CoreBase> {\n protected readonly event: Event;\n protected readonly logger: Logger;\n // absent on event handlers\n protected readonly dispatch?: DispatchContext | undefined;\n\n protected constructor(\n event: Event,\n public readonly core: TCore,\n dispatch?: DispatchContext,\n channel?: LoggerChannelId\n ) {\n this.event = event;\n this.logger = new Logger(this.constructor.name, { channel });\n this.dispatch = dispatch;\n }\n\n /**\n * Holds the main logic of your handler. The dispatcher calls it after the handler's gates pass, so a\n * gate that refuses stops `execute()` from running.\n */\n abstract execute(): Promise<void>;\n}\n","import { BaseHandler } from './BaseHandler';\n\nimport type { CoreBase } from '#interfaces/CoreBase';\nimport type { BaseReplySender } from '#reply/BaseReplySender';\nimport type { DispatchContext } from '#src/dispatch/DispatchContext';\nimport type { DeferOpts, ReplyResponse, SendOpts } from '@seedcord/types';\n\n/**\n * Shared base the repliable handlers on both transports extend. Defines the reply members over the sender\n * the transport builds.\n *\n * @typeParam Event - The repliable interaction type this handler processes\n * @typeParam TCore - The transport's Core\n * @typeParam TMessage - The transport's created-message lens\n * @typeParam TNative - The transport's own file forms, beyond the portable `ReplyFile`\n * @typeParam TSender - The transport's reply sender\n */\nexport abstract class RepliableHandler<\n Event,\n TCore extends CoreBase,\n TMessage extends { id: string },\n TNative,\n TSender extends BaseReplySender<TMessage, TNative>\n> extends BaseHandler<Event, TCore> {\n protected readonly sender: TSender;\n protected readonly routeId: string;\n\n protected constructor(event: Event, core: TCore, dispatch?: DispatchContext) {\n super(event, core, dispatch, 'interactions');\n this.routeId = dispatch?.routeId ?? this.constructor.name;\n // the override runs before its own field initializers\n this.sender = this.buildSender(event, core, this.routeId);\n }\n\n protected abstract buildSender(event: Event, core: TCore, routeId: string): TSender;\n\n /** @internal The dispatcher's fault boundary replies through the live ack state. */\n public getSender(): TSender {\n return this.sender;\n }\n\n /** Send the initial response, exactly once. */\n protected reply(response: ReplyResponse<TNative> | string, opts?: SendOpts): Promise<TMessage> {\n return this.sender.reply(response, opts);\n }\n\n /** Show a \"thinking\" placeholder, then fill it later with {@link edit}. */\n protected defer(opts?: DeferOpts): Promise<void> {\n return this.sender.defer(opts);\n }\n\n /** Send a new message once the interaction is acknowledged. */\n protected followUp(response: ReplyResponse<TNative> | string, opts?: SendOpts): Promise<TMessage> {\n return this.sender.followUp(response, opts);\n }\n\n /** Rewrite the initial reply or deferred placeholder. The targeted form rewrites a message a prior send returned. */\n protected edit(response: ReplyResponse<TNative> | string): Promise<TMessage>;\n protected edit(target: TMessage, response: ReplyResponse<TNative> | string): Promise<TMessage>;\n protected edit(\n targetOrResponse: TMessage | ReplyResponse<TNative> | string,\n maybeResponse?: ReplyResponse<TNative> | string\n ): Promise<TMessage> {\n // justified: the overloads narrow targetOrResponse to a response once maybeResponse is absent\n if (maybeResponse === undefined) return this.sender.edit(targetOrResponse as ReplyResponse<TNative> | string);\n // justified: the overloads narrow targetOrResponse to a target message once maybeResponse is defined\n return this.sender.edit(targetOrResponse as TMessage, maybeResponse);\n }\n\n /** Routes to reply, followUp, or edit based on the current ack state. */\n protected send(response: ReplyResponse<TNative> | string, opts?: SendOpts): Promise<TMessage> {\n return this.sender.send(response, opts);\n }\n\n /** Delete the initial reply or deferred placeholder. Pass a target to delete a message a prior send returned. */\n protected delete(target?: TMessage): Promise<void> {\n return target === undefined ? this.sender.delete() : this.sender.delete(target);\n }\n}\n","import { Notice } from './Notice';\nimport { NoticeCard } from './NoticeCard';\n\nimport type { RenderContext, ReplyResponse } from '@seedcord/types';\n\n/**\n * A generic fault you throw after catching an error you do not have a specific message for.\n *\n * The user sees a fixed generic reply with the tracking uuid, never the cause. `report` defaults to\n * `true`, so the framework logs it and publishes it to the `handledException` bus. Pass `report: false`\n * to show the generic reply without the bus publish. The original error is stored as the standard\n * `cause`, so the real stack reaches the webhook. When the user should see a real message for a fault,\n * subclass {@link Notice} and write your own `render` instead.\n *\n * The framework also renders this for an unhandled throw, where it points the user at\n * `ctx.developerUsername`.\n *\n * @example\n * ```ts\n * import { Fault } from '@seedcord/gateway';\n *\n * try {\n * await db.write(record);\n * } catch (cause) {\n * // user sees the generic reply with the uuid, and the real error rides along as cause for the webhook\n * throw new Fault({ cause });\n *\n * // pass report: false to show the same reply without publishing to the handledException bus\n * // throw new Fault({ cause, report: false });\n * }\n * ```\n */\nexport class Fault extends Notice {\n public constructor(options?: { cause?: unknown; report?: boolean }) {\n super('A fault occurred', options?.cause === undefined ? undefined : { cause: options.cause });\n this.report = options?.report ?? true;\n }\n\n public render(ctx: RenderContext): ReplyResponse {\n const contact = ctx.developerUsername ?? 'the developer';\n const card = new NoticeCard(\n `Something went wrong. Please reach out to ${contact} with a way to reproduce the error and the following:\\n### UUID: \\`${ctx.uuid}\\``,\n 'Error'\n );\n return { components: [card.component] };\n }\n}\n","import { NeedsAny, NotAllowed } from '#notices/index';\nimport { Fault } from '#stops/Fault';\nimport { Notice } from '#stops/Notice';\n\nimport { markCommits, rollbackCommits, runCheck } from './effects';\nimport { defineGate } from './Gate';\n\nimport type { Gate, GateContextBase, RequiredOf } from './Gate';\nimport type { IntersectRequired, JoinNames, TwoOrMore } from './matching';\n\n/**\n * Runs each gate in order and refuses on the first refusal. Takes two or more arms. The required\n * context is the intersection of the arms, so an event-only and an interaction-only gate cannot be\n * combined.\n *\n * @typeParam Gates - The tuple of two or more gate arms, run left to right.\n * @param gates - The gate arms to run in order, all refused on the first refusal.\n *\n * @see {@link or}\n *\n * @example\n * ```ts\n * import { PermissionFlagsBits } from 'discord.js';\n *\n * \\@Gated(and(GuildOnly(), RequirePermissions([PermissionFlagsBits.BanMembers])))\n * \\@SlashRoute('ban')\n * class BanHandler extends SlashHandler<'ban'> {\n * async execute() {\n * // ...\n * }\n * }\n * ```\n */\nexport function and<Gates extends TwoOrMore<Gate<GateContextBase>>>(\n ...gates: Gates\n): Gate<IntersectRequired<Gates> & GateContextBase, JoinNames<Gates, ' & '>>;\nexport function and(...gates: readonly Gate<GateContextBase>[]): Gate<GateContextBase> {\n return defineGate(gates.map((gate) => gate.name).join(' & '), async (ctx) => {\n for (const gate of gates) {\n await runCheck(gate, ctx);\n }\n });\n}\n\n// the caller-supplied refusal when every or arm refuses, replacing the default list or neutral refusal\ninterface OrOptions {\n fail: Notice | ((ctx: GateContextBase) => Notice);\n}\n\n// a gate has a check method, but the options object does not\nfunction isOrOptions(arg: Gate<GateContextBase> | OrOptions): arg is OrOptions {\n return !('check' in arg);\n}\n\n/**\n * Runs each gate in order and passes on the first arm that passes. Takes two or more arms. The\n * required context is the union of the arms. A handler that matches any one arm fits. When every\n * arm refuses it throws the trailing {@link OrOptions} `fail` if given, else an auto list of the\n * arms derived from the summary field, else a default refusal. The trailing options object does not\n * count as an arm.\n *\n * @typeParam Gates - The tuple of two or more gate arms, tried left to right.\n * @param gates - The gate arms to try in order, with an optional trailing {@link OrOptions} object.\n *\n * @see {@link and}\n *\n * @example\n * ```ts\n * \\@Gated(or(OwnerOnly(), RequireRole('123456789012345678')))\n * \\@SlashRoute('admin')\n * class AdminHandler extends SlashHandler<'admin'> {\n * async execute() {\n * // ...\n * }\n * }\n * ```\n */\nexport function or<Gates extends TwoOrMore<Gate<GateContextBase>>>(\n ...gates: Gates\n): Gate<RequiredOf<Gates[number]>, JoinNames<Gates, ' | '>>;\n/**\n * @example\n * ```ts\n * // custom refusal when every arm refuses\n * or(OwnerOnly(), RequireRole('123456789012345678'), { fail: new NotAllowedNotice() });\n * ```\n */\nexport function or<Gates extends TwoOrMore<Gate<GateContextBase>>>(\n ...args: [...Gates, OrOptions]\n): Gate<RequiredOf<Gates[number]>, JoinNames<Gates, ' | '>>;\nexport function or(...args: readonly (Gate<GateContextBase> | OrOptions)[]): Gate<GateContextBase> {\n const last = args.at(-1);\n const options = last !== undefined && isOrOptions(last) ? last : undefined;\n const gates = (options ? args.slice(0, -1) : args) as readonly Gate<GateContextBase>[];\n\n return defineGate(gates.map((gate) => gate.name).join(' | '), async (ctx) => {\n const summaries: string[] = [];\n let everyArmHasSummary = true;\n for (const gate of gates) {\n const mark = markCommits(ctx);\n try {\n await runCheck(gate, ctx);\n return;\n } catch (error) {\n // only a refusal counts as an arm declining. a Fault (even report-false), a Silence, or a raw error stops everything\n if (error instanceof Notice && !(error instanceof Fault) && !error.report) {\n // the arm may have queued an effect's commit before refusing, so drop it before the winner carries it\n rollbackCommits(ctx, mark);\n if (error.summary === undefined) everyArmHasSummary = false;\n else summaries.push(error.summary);\n continue;\n }\n throw error;\n }\n }\n if (options) throw typeof options.fail === 'function' ? options.fail(ctx) : options.fail;\n // the auto-list shows only when every refusal gave a summary, because a partial list would mislead by omission\n if (everyArmHasSummary && summaries.length > 0) throw new NeedsAny(summaries);\n throw new NotAllowed();\n });\n}\n","import { defineGate } from '#gates/Gate';\nimport { NotInDm, NotInGuild, NotOwner } from '#notices/index';\n\nimport { pickNotice } from './options';\n\nimport type { Gate, GateContextBase } from '#gates/Gate';\nimport type { GateNoticeOptions } from './options';\n\n/**\n * Passes only for a user id listed in `config.ownerIds`, else refuses.\n *\n * Agnostic, so it attaches to interaction and event handlers alike. With no `ownerIds` configured it refuses every caller. Pass {@link GateNoticeOptions} to reword or replace the refusal.\n *\n * @param options - Reword the default refusal with `message`, or replace it with `notice`.\n *\n * @see the `@Gated` decorator from your transport package\n *\n * @example\n * ```ts\n * \\@Gated(OwnerOnly())\n * \\@SlashRoute('shutdown')\n * class ShutdownHandler extends SlashHandler<'shutdown'> {\n * async execute() {\n * // ...\n * }\n * }\n * ```\n *\n * @example\n * ```ts\n * // reword the refusal, keeping the embed styling\n * OwnerOnly({ message: 'Owners only.' });\n * ```\n */\nexport function OwnerOnly(options?: GateNoticeOptions): Gate<GateContextBase, 'OwnerOnly'> {\n return defineGate('OwnerOnly', (ctx) => {\n const owners = ctx.core.config.ownerIds ?? [];\n if (ctx.userId !== null && owners.includes(ctx.userId)) return;\n throw pickNotice(options, (message) => new NotOwner(message));\n });\n}\n\n/**\n * Passes inside a guild, else refuses.\n *\n * Agnostic. Attaches to any handler kind. Often paired with `RequirePermissions` in an {@link and}.\n *\n * @param options - Reword the default refusal with `message`, or replace it with `notice`.\n *\n * @see the `@Gated` decorator from your transport package\n *\n * @example\n * ```ts\n * import { PermissionFlagsBits } from 'discord.js';\n *\n * \\@Gated(GuildOnly(), RequirePermissions([PermissionFlagsBits.Administrator]))\n * \\@SlashRoute('maintenance')\n * class Maintenance extends SlashHandler<'maintenance'> {\n * async execute() {\n * // ...\n * }\n * }\n * ```\n */\nexport function GuildOnly(options?: GateNoticeOptions): Gate<GateContextBase, 'GuildOnly'> {\n return defineGate('GuildOnly', (ctx) => {\n if (ctx.guildId) return;\n throw pickNotice(options, (message) => new NotInGuild(message));\n });\n}\n\n/**\n * Passes in a direct message, else refuses.\n *\n * Agnostic, fitting any handler kind. The inverse of {@link GuildOnly}, so combining the two in an {@link and} can never pass.\n *\n * @param options - Reword the default refusal with `message`, or replace it with `notice`.\n *\n * @see the `@Gated` decorator from your transport package\n *\n * @example\n * ```ts\n * \\@Gated(DmOnly())\n * \\@SlashRoute('verify')\n * class VerifyHandler extends SlashHandler<'verify'> {\n * async execute() {\n * // ...\n * }\n * }\n * ```\n */\nexport function DmOnly(options?: GateNoticeOptions): Gate<GateContextBase, 'DmOnly'> {\n return defineGate('DmOnly', (ctx) => {\n if (!ctx.guildId) return;\n throw pickNotice(options, (message) => new NotInDm(message));\n });\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordTypeError } from '@seedcord/errors/internal';\nimport { parseDuration, type ValidDuration } from '@seedcord/utils';\n\nimport { defineEffectGate } from '#gates/Gate';\nimport { OnCooldown } from '#notices/index';\n\nimport type { EffectGate, GateContextBase } from '#gates/Gate';\nimport type { Notice } from '#stops/Notice';\nimport type { EpochMs } from '@seedcord/types';\n\n/**\n * Options for {@link Cooldown}. `per` sets the bucket the window applies to and `limit` the uses allowed per\n * window. `message` rewords the refusal and `notice` replaces it, both receiving the epoch ms the key frees\n * up so the refusal can show the retry time.\n *\n * @example\n * ```ts\n * // one use per channel, rewording the refusal with the retry time\n * Cooldown('10s', {\n * per: 'channel',\n * message: (resetAt) => `Slow down. Try again <t:${Math.round(resetAt / 1000)}:R>.`\n * });\n * ```\n *\n * @example\n * ```ts\n * // five uses per minute, per user\n * Cooldown('1m', { limit: 5 });\n * ```\n */\nexport interface CooldownOptions {\n /**\n * The bucket the cooldown window applies to. 'user' scopes by user ID (falls back to global when the source carries no user id). 'guild' scopes by guild ID (falls back to global if no guild), and 'channel' scopes by channel ID (falls back to global if no channel).\n *\n * @defaultValue `'user'`\n */\n per?: 'user' | 'guild' | 'channel';\n /**\n * Uses allowed inside one window before the gate refuses.\n *\n * @defaultValue `1`\n */\n limit?: number;\n /**\n * Reword the refusal, keeping the standard notice card. Receives the epoch ms the key frees up, to\n * show the retry time with `<t:${Math.round(resetAt / 1000)}:R>`.\n */\n message?: (resetAt: EpochMs) => string;\n /**\n * Replace the refusal Notice entirely, for full control or a translated copy. Receives the epoch ms the\n * key frees up.\n */\n notice?: (resetAt: EpochMs) => Notice;\n}\n\n// off-route ctxs have no route to key on. each Cooldown() call gets its own id to keep unrelated event handlers isolated\nlet anonSeq = 0;\n\nfunction scopeValue(ctx: GateContextBase, per: 'user' | 'guild' | 'channel'): string {\n if (per === 'guild') return ctx.guildId ?? 'global';\n if (per === 'channel') return ctx.channelId ?? 'global';\n return ctx.userId ?? 'global';\n}\n\n/**\n * Allows `limit` uses per window, scoped by `per`. A number `duration` is **seconds**, and a string is\n * a duration like `30m` or `24h`. An unparseable string throws a **SeedcordTypeError** at construction. The\n * slot is charged in commit, only after the whole gate set passes, so a later refusal never burns the cooldown.\n * That split also means two requests racing the same key can both pass before either charges the slot.\n * The key combines the handler's route with the window settings, keeping two different handlers on separate\n * windows. Reword the refusal with {@link CooldownOptions.message} or replace it with {@link CooldownOptions.notice}.\n *\n * @param duration - A number is seconds, and a string is a duration like `30m` or `24h`. An unparseable string throws a **SeedcordTypeError**.\n * @param options - Sets the scope with `per`, the uses per window with `limit`, and the refusal text with `message` or `notice`.\n *\n * @see the `@Gated` decorator from your transport package\n * @see {@link IRateLimiter}\n *\n * @example\n * ```ts\n * // a number argument is SECONDS\n * \\@Gated(Cooldown(5))\n * \\@SlashRoute('daily')\n * class DailyHandler extends SlashHandler<'daily'> {\n * async execute() {\n * // ...\n * }\n * }\n * ```\n *\n * @example\n * ```ts\n * // a string is a duration, here scoped per guild (the default is per user)\n * Cooldown('30m', { per: 'guild' });\n * ```\n *\n * @example\n * ```ts\n * // a burst, 3 uses per 10 minutes per user, then it refuses until a slot frees\n * \\@Gated(Cooldown('10m', { limit: 3 }))\n * \\@SlashRoute('report')\n * class ReportHandler extends SlashHandler<'report'> {\n * async execute() {\n * // ...\n * }\n * }\n * ```\n */\nexport function Cooldown(\n duration: number | ValidDuration,\n options?: CooldownOptions\n): EffectGate<GateContextBase, 'Cooldown'> {\n let windowMs: number;\n if (typeof duration === 'number') {\n windowMs = duration * 1000;\n } else {\n const parsed = parseDuration(duration);\n if (parsed === null) throw new SeedcordTypeError(SeedcordErrorCode.GateInvalidCooldownDuration, [duration]);\n windowMs = parsed;\n }\n // refused the same way as an unparseable string, because a zero or negative window would never limit\n if (windowMs <= 0) {\n throw new SeedcordTypeError(SeedcordErrorCode.GateInvalidCooldownDuration, [String(duration)]);\n }\n\n const per = options?.per ?? 'user';\n // exactOptionalPropertyTypes rejects an explicit undefined key\n const window = options?.limit === undefined ? { windowMs } : { windowMs, limit: options.limit };\n const anonId = anonSeq++;\n // routed keys stay stable across restarts, letting a durable store rebuild the same window\n const keyOf = (ctx: GateContextBase): string =>\n `cooldown:${ctx.routeId ?? `anon${anonId}`}:${per}:w${windowMs}:l${options?.limit ?? 1}:${scopeValue(ctx, per)}`;\n\n return defineEffectGate(\n 'Cooldown',\n async (ctx) => {\n const result = await ctx.core.rateLimiter.peek(keyOf(ctx), window);\n if (!result.limited) return;\n if (options?.notice) throw options.notice(result.resetAt);\n throw new OnCooldown(result.resetAt, options?.message?.(result.resetAt));\n },\n async (ctx) => {\n await ctx.core.rateLimiter.charge(keyOf(ctx), window);\n }\n );\n}\n","import { HasDangerousPermissions, MissingPermissions } from '#notices/index';\n\nimport { hasAll, missingNames, presentNames } from './bits';\n\nimport type { Notice } from '#stops/Notice';\nimport type { PermissionScope } from './bits';\n\n/** Replacement notices for the two refusals {@link assertPermissions} raises. */\nexport interface PermissionNoticeOverrides {\n missingNotice?: new (message: string | undefined, subject: string, missingPerms: readonly string[]) => Notice;\n dangerousNotice?: new (message: string | undefined, subject: string, dangerousPerms: readonly string[]) => Notice;\n}\n\n/** The check {@link assertPermissions} runs. */\nexport interface PermissionAssertion extends PermissionNoticeOverrides {\n /** Mention string for who is checked, used as the refusal's subject. */\n subject: string;\n /** The effective permission bitfield the subject holds, computed by the caller. */\n permissions: bigint;\n /** The permission flag bits to check. */\n scope: PermissionScope;\n /** Refuse when the subject holds a scope bit. */\n inverse?: boolean;\n}\n\n/**\n * Refuses when the subject is missing a permission in `scope`, or with `inverse` when it holds one.\n * The Administrator bit passes any scope, and on the inverse arm it reports every bit in `scope`.\n *\n * The caller computes `permissions`, gateway off a discord.js role, member, or channel, and HTTP off the\n * bitfield the interaction payload carries.\n *\n * @throws A {@link Notice} listing the missing or present permissions.\n *\n * @example\n * ```ts\n * import { assertPermissions } from '@seedcord/core';\n * import { PermissionFlagsBits } from 'discord-api-types/v10';\n *\n * // gateway, the bits come off a discord.js member\n * assertPermissions({\n * subject: `<@${member.id}>`,\n * permissions: member.permissions.bitfield,\n * scope: [PermissionFlagsBits.BanMembers]\n * });\n * ```\n *\n * @example\n * ```ts\n * // HTTP, the payload carries the member's bits as a string\n * assertPermissions({\n * subject: `<@${this.event.member.user.id}>`,\n * permissions: BigInt(this.event.member.permissions),\n * scope: [PermissionFlagsBits.ManageMessages]\n * });\n * ```\n *\n * @example\n * ```ts\n * // inverse, refuse a role that carries Administrator\n * assertPermissions({\n * subject: `<@&${role.id}>`,\n * permissions: role.permissions.bitfield,\n * scope: [PermissionFlagsBits.Administrator],\n * inverse: true\n * });\n * ```\n *\n * @example\n * ```ts\n * import { Notice } from '@seedcord/core';\n *\n * // a replacement notice takes the same three arguments the default one does\n * class CannotBan extends Notice {\n * public constructor(\n * message: string | undefined,\n * private readonly subject: string,\n * private readonly missing: readonly string[]\n * ) {\n * super(message ?? 'You cannot ban here.');\n * }\n *\n * public render(): ReplyResponse {\n * return { components: [new BanDenied(this.subject, this.missing).component] };\n * }\n * }\n *\n * assertPermissions({\n * subject: `<@${member.id}>`,\n * permissions: member.permissions.bitfield,\n * scope: [PermissionFlagsBits.BanMembers],\n * missingNotice: CannotBan\n * });\n * ```\n */\nexport function assertPermissions(check: PermissionAssertion): void {\n const { subject, permissions, scope, inverse = false, missingNotice, dangerousNotice } = check;\n\n if (inverse) {\n const present = presentNames(permissions, scope);\n if (present.length === 0) return;\n const Dangerous = dangerousNotice ?? HasDangerousPermissions;\n throw new Dangerous(undefined, subject, present);\n }\n\n if (hasAll(permissions, scope)) return;\n const Missing = missingNotice ?? MissingPermissions;\n throw new Missing(undefined, subject, missingNames(permissions, scope));\n}\n","/**\n * The role ids a member holds after adding and removing the given sets. Removals win over additions.\n *\n * This does not write the roles. Apply the result with `member.roles.set()` on gateway, or PATCH the member over HTTP.\n *\n * @param current - The role ids the member holds.\n * @param add - The role ids to add.\n * @param remove - The role ids to remove.\n * @returns The merged role ids.\n *\n * @example\n * ```ts\n * import { mergeRoles } from '@seedcord/core';\n *\n * // gateway\n * const ids = mergeRoles(member.roles.cache.map((role) => role.id), ['123'], ['456']);\n * await member.roles.set(ids);\n * ```\n */\nexport function mergeRoles(current: readonly string[], add: readonly string[], remove: readonly string[]): string[] {\n return [...new Set(current).union(new Set(add)).difference(new Set(remove))];\n}\n","import type { APIMessageComponentEmoji } from 'discord-api-types/v10';\n\n/**\n * A resolved custom emoji. Renders as `<:name:id>` in message content, or `<a:name:id>` when animated,\n * and passes straight to a builder's `setEmoji`.\n */\n// setEmoji in @discordjs/builders rejects any own key past these three, scanning with Object.entries\nexport class ResolvedEmoji implements APIMessageComponentEmoji {\n constructor(\n public readonly name: string,\n public readonly id: string,\n public readonly animated = false\n ) {}\n\n public toString(): string {\n return `<${this.animated ? 'a' : ''}:${this.name}:${this.id}>`;\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordRangeError } from '@seedcord/errors/internal';\n\nimport type { PageView } from './PageView';\n\n/**\n * Pure page math, usable headless. Clamps `page` into range and slices the window.\n *\n * @param items - The full list to page over.\n * @param page - The requested zero-based page, clamped into `[0, totalPages - 1]`.\n * @param perPage - The page size. Must be a positive integer.\n * @returns The sliced {@link PageView}, with `totalPages` always known.\n * @throws SeedcordRangeError when `perPage` is not a positive integer.\n */\nexport function paginate<Item>(items: readonly Item[], page: number, perPage: number): PageView<Item> {\n if (!Number.isSafeInteger(perPage) || perPage <= 0) {\n throw new SeedcordRangeError(SeedcordErrorCode.PaginationInvalidPerPage, [perPage]);\n }\n\n const totalPages = Math.max(1, Math.ceil(items.length / perPage));\n const clamped = Math.min(Math.max(Math.trunc(page), 0), totalPages - 1);\n const start = clamped * perPage;\n\n return {\n items: items.slice(start, start + perPage),\n page: clamped,\n perPage,\n totalPages,\n hasPrev: clamped > 0,\n hasNext: clamped < totalPages - 1\n };\n}\n","import { ActionRowBuilder, ButtonBuilder } from '@discordjs/builders';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordRangeError, SeedcordTypeError } from '@seedcord/errors/internal';\nimport { ButtonStyle } from 'discord-api-types/v10';\n\nimport { PAGE_MAX } from '#pagination/cursor';\n\nimport type { PageCursor } from '#pagination/cursor';\nimport type { PageView } from '#pagination/PageView';\nimport type { APIMessageComponentEmoji } from 'discord-api-types/v10';\n\n/** The five built-in nav controls. */\nexport type ControlKey = 'first' | 'prev' | 'indicator' | 'next' | 'last';\n\n// Link and Premium have no custom_id, which un-routes a control.\ntype CustomIdStyle = ButtonStyle.Primary | ButtonStyle.Secondary | ButtonStyle.Success | ButtonStyle.Danger;\n\n/** Cosmetic overrides for a control button. `style` excludes Link and Premium, which would un-route it. */\nexport interface ControlCosmetics {\n label?: string;\n style?: CustomIdStyle;\n emoji?: APIMessageComponentEmoji;\n}\n\n/** The controls passed to `render`. */\nexport interface PaginatorControls {\n /**\n * A fresh `ButtonBuilder` for one control, already stamped with its target page and disabled-at-bounds\n * state. Call only cosmetic setters on it, because a `setCustomId` or `setStyle(Link)` un-routes it.\n */\n button(key: ControlKey, cosmetics?: ControlCosmetics): ButtonBuilder;\n /**\n * Assemble an action row of the chosen controls, in order. Throws on an empty set, more than five\n * controls, or a duplicate.\n */\n row(...keys: ControlKey[]): ActionRowBuilder<ButtonBuilder>;\n}\n\nconst DEFAULT_LABEL: Record<ControlKey, string> = {\n first: 'First',\n prev: 'Prev',\n indicator: '',\n next: 'Next',\n last: 'Last'\n};\n\n// a distinct slot keeps two controls on the same page off the same id. 0-4 is the cursor's SLOT_MAX.\nconst CONTROL_SLOT: Record<ControlKey, number> = {\n first: 0,\n prev: 1,\n indicator: 2,\n next: 3,\n last: 4\n};\n\nconst MAX_BUTTONS_PER_ROW = 5;\n\nexport class Controls implements PaginatorControls {\n constructor(\n private readonly cursor: PageCursor<string>,\n private readonly view: PageView<unknown>\n ) {}\n\n button(key: ControlKey, cosmetics?: ControlCosmetics): ButtonBuilder {\n const { target, disabled } = this.navState(key);\n\n // an emoji with no explicit label makes an icon-only button. the indicator always shows its page text.\n const defaultLabel = key === 'indicator' ? this.indicatorText() : DEFAULT_LABEL[key];\n const label = cosmetics?.label ?? (key === 'indicator' || !cosmetics?.emoji ? defaultLabel : undefined);\n\n const button = new ButtonBuilder()\n // an enormous list could push last/next past PAGE_MAX, where encode throws.\n .setCustomId(this.cursor.encode({ page: Math.min(Math.max(0, target), PAGE_MAX), slot: CONTROL_SLOT[key] }))\n .setStyle(cosmetics?.style ?? ButtonStyle.Secondary)\n .setDisabled(disabled);\n if (label !== undefined) button.setLabel(label);\n if (cosmetics?.emoji) button.setEmoji(cosmetics.emoji);\n return button;\n }\n\n private navState(key: ControlKey): { target: number; disabled: boolean } {\n const { page, totalPages, hasPrev, hasNext } = this.view;\n const knownTotal = totalPages !== undefined;\n const target = {\n first: 0,\n prev: page - 1,\n indicator: page,\n next: page + 1,\n last: knownTotal ? totalPages - 1 : page\n }[key];\n // past PAGE_MAX the encoded page would clamp to a wrong one\n const disabled = {\n first: !hasPrev,\n prev: !hasPrev,\n indicator: true,\n next: !hasNext || page + 1 > PAGE_MAX,\n last: !hasNext || !knownTotal || totalPages - 1 > PAGE_MAX\n }[key];\n return { target, disabled };\n }\n\n row(...keys: ControlKey[]): ActionRowBuilder<ButtonBuilder> {\n if (keys.length === 0) {\n throw new SeedcordRangeError(SeedcordErrorCode.PaginationEmptyControls);\n }\n if (keys.length > MAX_BUTTONS_PER_ROW) {\n throw new SeedcordRangeError(SeedcordErrorCode.PaginationTooManyControls, [keys.length]);\n }\n // repeating a control reuses its slot and page, so the custom_ids collide and Discord rejects the message.\n const seen = new Set<ControlKey>();\n for (const key of keys) {\n if (seen.has(key)) throw new SeedcordTypeError(SeedcordErrorCode.PaginationDuplicateControls, [key]);\n seen.add(key);\n }\n return new ActionRowBuilder<ButtonBuilder>().addComponents(keys.map((key) => this.button(key)));\n }\n\n private indicatorText(): string {\n const { page, totalPages } = this.view;\n return totalPages === undefined ? `Page ${page + 1}` : `Page ${page + 1} of ${totalPages}`;\n }\n}\n","import { TextDisplayBuilder } from '@discordjs/builders';\n\nimport { BuilderComponent } from '#components/Component';\nimport { Controls } from '#pagination/controls';\n\nimport type { ControlKey, PaginatorControls } from '#pagination/controls';\nimport type { PageCursor } from '#pagination/cursor';\nimport type { PageView } from '#pagination/PageView';\nimport type { ButtonBuilder, ActionRowBuilder } from '@discordjs/builders';\nimport type { ReplyResponse, V2Component } from '@seedcord/types';\n\n/** What `renderItem` returns for one item, a text line or a section. */\nexport type ItemRender<Item> = (item: Item, index: number) => string | BuilderComponent<'section'>;\n\ntype Renderable = string | V2Component[] | ReplyResponse;\n\n/** A full-page render override. */\nexport type PageRender<Item> = (view: PageView<Item>, controls: PaginatorControls) => Renderable;\n\n// subclassed because BuilderComponent.instance is protected\nclass PageContainer<Item> extends BuilderComponent<'container'> {\n constructor(view: PageView<Item>, renderItem: ItemRender<Item>, controlRow: ActionRowBuilder<ButtonBuilder>) {\n super('container');\n const base = view.page * view.perPage;\n let lines: string[] = [];\n const flush = (): void => {\n if (lines.length === 0) return;\n this.instance.addTextDisplayComponents(new TextDisplayBuilder().setContent(lines.join('\\n')));\n lines = [];\n };\n view.items.forEach((item, offset) => {\n const rendered = renderItem(item, base + offset);\n if (typeof rendered === 'string') {\n lines.push(rendered);\n return;\n }\n flush();\n this.instance.addSectionComponents(rendered.component);\n });\n flush();\n this.instance.addActionRowComponents(controlRow);\n }\n}\n\nfunction toReplyResponse(renderable: Renderable): ReplyResponse {\n if (typeof renderable === 'string') return { components: [new TextDisplayBuilder().setContent(renderable)] };\n if (Array.isArray(renderable)) return { components: renderable };\n return renderable;\n}\n\nconst ARRAY_KEYS: ControlKey[] = ['first', 'prev', 'indicator', 'next', 'last'];\nconst CURSOR_KEYS: ControlKey[] = ['prev', 'indicator', 'next'];\n\n// a render override builds the whole tree, or the default container lists the items and appends\n// the controls (all five for a known total, prev/indicator/next for a cursor source)\nexport function renderPage<Item>(\n view: PageView<Item>,\n cursor: PageCursor<string>,\n config: { renderItem?: ItemRender<Item>; render?: PageRender<Item> }\n): ReplyResponse {\n const controls = new Controls(cursor, view);\n if (config.render) return toReplyResponse(config.render(view, controls));\n\n const renderItem = config.renderItem ?? String;\n const row = controls.row(...(view.totalPages === undefined ? CURSOR_KEYS : ARRAY_KEYS));\n return { components: [new PageContainer(view, renderItem, row).component] };\n}\n","import { pageCursor } from '#pagination/cursor';\nimport { renderPage } from '#pagination/render';\n\nimport type { PageCursor } from '#pagination/cursor';\nimport type { ItemRender, PageRender } from '#pagination/render';\nimport type { PageSource } from '#pagination/sources';\nimport type { ReplyResponse } from '@seedcord/types';\n\n/**\n * The transport-agnostic paginator configuration. Each transport adds its own handler wiring on top.\n *\n * @typeParam Item - The item type, inferred from the source.\n * @typeParam Prefix - The route prefix, inferred from `prefix`.\n * @typeParam Ctx - The transport's page context.\n */\nexport interface PaginatorConfig<Item, Prefix extends string, Ctx> {\n /** The route prefix used to build the page cursor on the nav buttons. */\n prefix: Prefix;\n source: PageSource<Item, Ctx>;\n /** Render one item, `index` is its absolute position across pages. Ignored when `render` is set. */\n renderItem?: ItemRender<Item>;\n /** Take over the whole page tree. Receives the page data and the controls factory. */\n render?: PageRender<Item>;\n /**\n * Whether the first page is ephemeral.\n *\n * @defaultValue `false`\n */\n ephemeral?: boolean;\n}\n\n/**\n * Holds a paginator's cursor and renders one page. Each transport extends this to add the nav handler and\n * the method that sends the first page.\n *\n * @typeParam Item - The item type, inferred from the source.\n * @typeParam Prefix - The route prefix, inferred from `config.prefix`.\n * @typeParam Ctx - The transport's page context.\n */\nexport abstract class PaginatorBase<Item, Prefix extends string, Ctx> {\n /** The page cursor, pass it to your `@ButtonRoute`. */\n public readonly cursor: PageCursor<Prefix>;\n\n protected readonly config: PaginatorConfig<Item, Prefix, Ctx>;\n\n protected constructor(config: PaginatorConfig<Item, Prefix, Ctx>) {\n this.cursor = pageCursor(config.prefix);\n this.config = config;\n }\n\n /** Render a page as a {@link ReplyResponse}. To post it elsewhere, add `flags: MessageFlags.IsComponentsV2`. */\n async page(ctx: Ctx, n: number): Promise<ReplyResponse> {\n const view = await this.config.source.page(ctx, n);\n return renderPage(view, this.cursor, this.config);\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordRangeError } from '@seedcord/errors/internal';\n\nimport { paginate } from '#pagination/paginate';\n\nimport type { PageView } from '#pagination/PageView';\nimport type { Promisable } from 'type-fest';\n\nconst DEFAULT_PER_PAGE = 10;\n\n/**\n * The one method a paginator calls on a source, once per click. Implement it to supply a custom source.\n *\n * @typeParam Item - The item type the source pages over.\n * @typeParam Ctx - The transport's page context, handed to the loader.\n */\nexport interface PageSource<Item, Ctx> {\n page(ctx: Ctx, n: number): Promise<PageView<Item>>;\n}\n\nfunction perPageOf(opts?: { perPage?: number }): number {\n const perPage = opts?.perPage ?? DEFAULT_PER_PAGE;\n if (!Number.isSafeInteger(perPage) || perPage <= 0) {\n throw new SeedcordRangeError(SeedcordErrorCode.PaginationInvalidPerPage, [perPage]);\n }\n return perPage;\n}\n\n/**\n * A source for a bounded list you can load whole. It loads the full list on every click, then slices the\n * page via {@link paginate}, so the real total is known (a real last button, \"Page X of Y\"). Cache inside\n * your loader if the load is expensive.\n *\n * @typeParam Item - The item type, inferred from the loader's return.\n * @typeParam Ctx - The transport's page context. Each transport exports a subclass that fixes it.\n */\nexport class ArraySource<Item, Ctx> implements PageSource<Item, Ctx> {\n private readonly perPage: number;\n\n constructor(\n private readonly load: (ctx: Ctx) => Promisable<readonly Item[]>,\n opts?: { perPage?: number }\n ) {\n this.perPage = perPageOf(opts);\n }\n\n async page(ctx: Ctx, n: number): Promise<PageView<Item>> {\n return paginate(await this.load(ctx), n, this.perPage);\n }\n}\n\n/**\n * A source for a large or unknown-length set you fetch one page at a time (SQL LIMIT/OFFSET, a paged API).\n * The fetcher receives the page index and the page size and reports whether a next page exists. A cursor\n * source has no cheap total, so `totalPages` is undefined, the last button is omitted, and the indicator\n * reads \"Page X\".\n *\n * @typeParam Item - The item type, inferred from the fetcher's slice.\n * @typeParam Ctx - The transport's page context. Each transport exports a subclass that fixes it.\n */\nexport class CursorSource<Item, Ctx> implements PageSource<Item, Ctx> {\n private readonly perPage: number;\n\n constructor(\n private readonly fetch: (\n ctx: Ctx,\n page: number,\n perPage: number\n ) => Promisable<{ items: readonly Item[]; hasNext: boolean }>,\n opts?: { perPage?: number }\n ) {\n this.perPage = perPageOf(opts);\n }\n\n async page(ctx: Ctx, n: number): Promise<PageView<Item>> {\n const page = Math.max(0, Math.trunc(n));\n const { items, hasNext } = await this.fetch(ctx, page, this.perPage);\n return { items: [...items], page, perPage: this.perPage, hasPrev: page > 0, hasNext };\n }\n}\n","import 'reflect-metadata';\n\nexport type { MessageContextMenuRegistry, UserContextMenuRegistry } from '#registries/ContextMenuRegistry';\nexport type { SlashRegistry } from '#registries/SlashRegistry';\n\nexport { Commands, ContextMenus } from '#src/commands/CommandInjector';\nexport type {\n CommandInfo,\n ContextMenuInfo,\n InjectedCommandMap,\n InjectedContextMenuMap\n} from '#src/commands/CommandInjector';\n\nexport { RegisterCommand } from '#decorators/Command';\nexport { SelectMenuKind } from '#decorators/interactionRoutes';\nexport {\n AutocompleteRoute,\n ButtonRoute,\n MessageContextMenuRoute,\n UserContextMenuRoute,\n ModalRoute,\n SelectMenuRoute,\n SlashRoute\n} from '#decorators/routes';\n\nexport { DispatchContext } from '#src/dispatch/DispatchContext';\nexport type { DispatchState } from '#src/dispatch/DispatchContext';\n\nexport { BaseHandler } from '#src/handlers/BaseHandler';\nexport { RepliableHandler } from '#src/handlers/RepliableHandler';\n\nexport type { CoreBase } from '#interfaces/CoreBase';\n\nexport { ShutdownPhase, StartupPhase } from '#src/lifecycle/phases';\n\nexport { defineEffectGate, defineGate } from '#gates/Gate';\nexport type { EffectGate, Gate, GateContextBase, GuildPermissionsContext, RequiredOf } from '#gates/Gate';\nexport { and, or } from '#gates/combinators';\nexport { DmOnly, GuildOnly, OwnerOnly } from '#gates/catalog/access';\nexport { Cooldown, type CooldownOptions } from '#gates/catalog/Cooldown';\nexport { RequireBotPermissions, RequirePermissions, RequireRole } from '#gates/catalog/permissions';\nexport type { PermissionScope, RequirePermissionsOptions, RequireRoleOptions } from '#gates/catalog/permissions';\nexport type { GateNoticeOptions } from '#gates/catalog/options';\n\nexport { assertPermissions } from '#src/permissions/assert';\nexport type { PermissionAssertion, PermissionNoticeOverrides } from '#src/permissions/assert';\nexport { mergeRoles } from '#src/permissions/mergeRoles';\n\nexport { BuilderComponent, RowComponent } from '#components/Component';\nexport { type RowType, type BuilderType } from '#components/builderTypes';\n\nexport { Notice } from '#stops/Notice';\nexport { Fault } from '#stops/Fault';\nexport { Silence } from '#stops/Silence';\n\nexport { CustomId } from '#customId/CustomId';\n\nexport { ResolvedEmoji } from '#src/miscellaneous/emoji';\n\nexport type { AutocompleteOptions } from '#inputs/AutocompleteOptions';\n\nexport { paginate } from '#pagination/paginate';\nexport { type PageView } from '#pagination/PageView';\nexport { PaginatorBase } from '#pagination/PaginatorBase';\nexport type { PaginatorConfig } from '#pagination/PaginatorBase';\nexport { ArraySource, CursorSource } from '#pagination/sources';\nexport type { PageSource } from '#pagination/sources';\nexport type { ControlCosmetics, ControlKey, PaginatorControls } from '#pagination/controls';\nexport type { ItemRender, PageRender } from '#pagination/render';\n\nexport { Bus, Subscribe, Subscriber, WebhookLog, WebhookUrl } from '#subscribers/index';\nexport type {\n DispatchOutcome,\n FaultSource,\n SubscribeOptions,\n Subscriptions,\n SubscriptionData,\n SubscriptionKey,\n WebhookFile,\n WebhookReport\n} from '#subscribers/index';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":";;;;;;;;;;;;;AAkDA,SAAgB,gBAAgB,OAAqB,SAAmB,CAAC,GAAG;CACxE,QAAQ,SAA4B;EAChC,MAAM,eAAe,QAAQ,eAAe,oBAAoB,IAAI;EACpE,IAAI,cACA,MAAM,IAAI,cAAc,kBAAkB,mCAAmC;GACzE,KAAK;GACL,aAAa;GACb;EACJ,CAAC;EAGL,IAAI,UAAU,YAAY,OAAO,SAAS,GACtC,MAAM,IAAI,cAAc,kBAAkB,gCAAgC;EAG9E,IAAI,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,IAClE,MAAM,IAAI,cAAc,kBAAkB,kCAAkC;EAGhF,MAAM,OAA+B,UAAU,WAAW,EAAE,MAAM,IAAI;GAAE;GAAO;EAAO;EACtF,QAAQ,eAAe,oBAAoB,MAAM,IAAI;CACzD;AACJ;;;;;;;;;;;;;;;;;;;;;;;AC4EA,SAAgB,WAAoD,GAAG,QAAiB;CACpF,OAAO,SAAwC,aAAmD;EAC9F,+BAA+C,QAAQ,WAAW;CACtE;AACJ;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,kBAA2D,GAAG,QAAiB;CAC3F,OAAO,SAAwC,aAA0D;EACrG,sCAAsD,QAAQ,WAAW;CAC7E;AACJ;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,qBAAgF,GAAG,OAAgB;CAC/G,OAAO,SACH,aACI;EACJ,sBAAsB,mBAAmB,uBAAuB,IAAI,GAAG,OAAO,WAAW;CAC7F;AACJ;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,wBACZ,GAAG,OACL;CACE,OAAO,SACH,aACI;EACJ,sBAAsB,mBAAmB,uBAAuB,OAAO,GAAG,OAAO,WAAW;CAChG;AACJ;;;;;;;;;;;;AAaA,SAAgB,YAAuD,GAAG,MAAY;CAClF,OAAO,SAAwC,aAAgE;EAC3G,8BAA8C,MAAM,WAAW;CACnE;AACJ;;;;;;;;;;;AAYA,SAAgB,WAAsD,GAAG,MAAY;CACjF,OAAO,SAAwC,aAA+D;EAC1G,6BAA6C,MAAM,WAAW;CAClE;AACJ;;;;;;;;;;;;;;;;;AAkBA,SAAgB,gBACZ,MACA,GAAG,MACL;CACE,OAAO,SAAwC,aAA4D;EACvG,oBAAoB,kBAAkB,IAAI,GAAG,MAAM,WAAW;CAClE;AACJ;;;;;;;;;ACtRA,IAAa,kBAAb,MAA6B;CAGG;CAF5B,AAAiB,QAAgC,CAAC;CAElD,YAAY,AAAgB,SAAwB;EAAxB;CAAyB;CAErD,IAAqC,KAAU,OAAiC;EAC5E,KAAK,MAAM,OAAO;CACtB;CAEA,IAAqC,KAA0C;EAC3E,OAAO,KAAK,MAAM;CACtB;AACJ;;;;;;;;;;;ACXA,IAAsB,cAAtB,MAAiE;CAQzC;CAPpB,AAAmB;CACnB,AAAmB;CAEnB,AAAmB;CAEnB,AAAU,YACN,OACA,AAAgB,MAChB,UACA,SACF;EAHkB;EAIhB,KAAK,QAAQ;EACb,KAAK,SAAS,IAAI,OAAO,KAAK,YAAY,MAAM,EAAE,QAAQ,CAAC;EAC3D,KAAK,WAAW;CACpB;AAOJ;;;;;;;;;;;;;;AClBA,IAAsB,mBAAtB,cAMU,YAA0B;CAChC,AAAmB;CACnB,AAAmB;CAEnB,AAAU,YAAY,OAAc,MAAa,UAA4B;EACzE,MAAM,OAAO,MAAM,UAAU,cAAc;EAC3C,KAAK,UAAU,UAAU,WAAW,KAAK,YAAY;EAErD,KAAK,SAAS,KAAK,YAAY,OAAO,MAAM,KAAK,OAAO;CAC5D;;CAKA,AAAO,YAAqB;EACxB,OAAO,KAAK;CAChB;;CAGA,AAAU,MAAM,UAA2C,MAAoC;EAC3F,OAAO,KAAK,OAAO,MAAM,UAAU,IAAI;CAC3C;;CAGA,AAAU,MAAM,MAAiC;EAC7C,OAAO,KAAK,OAAO,MAAM,IAAI;CACjC;;CAGA,AAAU,SAAS,UAA2C,MAAoC;EAC9F,OAAO,KAAK,OAAO,SAAS,UAAU,IAAI;CAC9C;CAKA,AAAU,KACN,kBACA,eACiB;EAEjB,IAAI,kBAAkB,QAAW,OAAO,KAAK,OAAO,KAAK,gBAAmD;EAE5G,OAAO,KAAK,OAAO,KAAK,kBAA8B,aAAa;CACvE;;CAGA,AAAU,KAAK,UAA2C,MAAoC;EAC1F,OAAO,KAAK,OAAO,KAAK,UAAU,IAAI;CAC1C;;CAGA,AAAU,OAAO,QAAkC;EAC/C,OAAO,WAAW,SAAY,KAAK,OAAO,OAAO,IAAI,KAAK,OAAO,OAAO,MAAM;CAClF;AACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC9CA,IAAa,QAAb,cAA2B,OAAO;CAC9B,AAAO,YAAY,SAAiD;EAChE,MAAM,oBAAoB,SAAS,UAAU,SAAY,SAAY,EAAE,OAAO,QAAQ,MAAM,CAAC;EAC7F,KAAK,SAAS,SAAS,UAAU;CACrC;CAEA,AAAO,OAAO,KAAmC;EAC7C,MAAM,UAAU,IAAI,qBAAqB;EAKzC,OAAO,EAAE,YAAY,CAAC,IAJL,WACb,6CAA6C,QAAQ,qEAAqE,IAAI,KAAK,KACnI,OAEqB,CAAC,CAAC,SAAS,EAAE;CAC1C;AACJ;;;;ACVA,SAAgB,IAAI,GAAG,OAAgE;CACnF,OAAO,WAAW,MAAM,KAAK,SAAS,KAAK,IAAI,CAAC,CAAC,KAAK,KAAK,GAAG,OAAO,QAAQ;EACzE,KAAK,MAAM,QAAQ,OACf,MAAM,SAAS,MAAM,GAAG;CAEhC,CAAC;AACL;AAQA,SAAS,YAAY,KAA0D;CAC3E,OAAO,EAAE,WAAW;AACxB;AAsCA,SAAgB,GAAG,GAAG,MAA6E;CAC/F,MAAM,OAAO,KAAK,GAAG,EAAE;CACvB,MAAM,UAAU,SAAS,UAAa,YAAY,IAAI,IAAI,OAAO;CACjE,MAAM,QAAS,UAAU,KAAK,MAAM,GAAG,EAAE,IAAI;CAE7C,OAAO,WAAW,MAAM,KAAK,SAAS,KAAK,IAAI,CAAC,CAAC,KAAK,KAAK,GAAG,OAAO,QAAQ;EACzE,MAAM,YAAsB,CAAC;EAC7B,IAAI,qBAAqB;EACzB,KAAK,MAAM,QAAQ,OAAO;GACtB,MAAM,OAAO,YAAY,GAAG;GAC5B,IAAI;IACA,MAAM,SAAS,MAAM,GAAG;IACxB;GACJ,SAAS,OAAO;IAEZ,IAAI,iBAAiB,UAAU,EAAE,iBAAiB,UAAU,CAAC,MAAM,QAAQ;KAEvE,gBAAgB,KAAK,IAAI;KACzB,IAAI,MAAM,YAAY,QAAW,qBAAqB;UACjD,UAAU,KAAK,MAAM,OAAO;KACjC;IACJ;IACA,MAAM;GACV;EACJ;EACA,IAAI,SAAS,MAAM,OAAO,QAAQ,SAAS,aAAa,QAAQ,KAAK,GAAG,IAAI,QAAQ;EAEpF,IAAI,sBAAsB,UAAU,SAAS,GAAG,MAAM,IAAI,SAAS,SAAS;EAC5E,MAAM,IAAI,WAAW;CACzB,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtFA,SAAgB,UAAU,SAAiE;CACvF,OAAO,WAAW,cAAc,QAAQ;EACpC,MAAM,SAAS,IAAI,KAAK,OAAO,YAAY,CAAC;EAC5C,IAAI,IAAI,WAAW,QAAQ,OAAO,SAAS,IAAI,MAAM,GAAG;EACxD,MAAM,WAAW,UAAU,YAAY,IAAI,SAAS,OAAO,CAAC;CAChE,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,UAAU,SAAiE;CACvF,OAAO,WAAW,cAAc,QAAQ;EACpC,IAAI,IAAI,SAAS;EACjB,MAAM,WAAW,UAAU,YAAY,IAAI,WAAW,OAAO,CAAC;CAClE,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,OAAO,SAA8D;CACjF,OAAO,WAAW,WAAW,QAAQ;EACjC,IAAI,CAAC,IAAI,SAAS;EAClB,MAAM,WAAW,UAAU,YAAY,IAAI,QAAQ,OAAO,CAAC;CAC/D,CAAC;AACL;;;;ACvCA,IAAI,UAAU;AAEd,SAAS,WAAW,KAAsB,KAA2C;CACjF,IAAI,QAAQ,SAAS,OAAO,IAAI,WAAW;CAC3C,IAAI,QAAQ,WAAW,OAAO,IAAI,aAAa;CAC/C,OAAO,IAAI,UAAU;AACzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,SAAgB,SACZ,UACA,SACuC;CACvC,IAAI;CACJ,IAAI,OAAO,aAAa,UACpB,WAAW,WAAW;MACnB;EACH,MAAM,SAAS,cAAc,QAAQ;EACrC,IAAI,WAAW,MAAM,MAAM,IAAI,kBAAkB,kBAAkB,6BAA6B,CAAC,QAAQ,CAAC;EAC1G,WAAW;CACf;CAEA,IAAI,YAAY,GACZ,MAAM,IAAI,kBAAkB,kBAAkB,6BAA6B,CAAC,OAAO,QAAQ,CAAC,CAAC;CAGjG,MAAM,MAAM,SAAS,OAAO;CAE5B,MAAM,SAAS,SAAS,UAAU,SAAY,EAAE,SAAS,IAAI;EAAE;EAAU,OAAO,QAAQ;CAAM;CAC9F,MAAM,SAAS;CAEf,MAAM,SAAS,QACX,YAAY,IAAI,WAAW,OAAO,SAAS,GAAG,IAAI,IAAI,SAAS,IAAI,SAAS,SAAS,EAAE,GAAG,WAAW,KAAK,GAAG;CAEjH,OAAO,iBACH,YACA,OAAO,QAAQ;EACX,MAAM,SAAS,MAAM,IAAI,KAAK,YAAY,KAAK,MAAM,GAAG,GAAG,MAAM;EACjE,IAAI,CAAC,OAAO,SAAS;EACrB,IAAI,SAAS,QAAQ,MAAM,QAAQ,OAAO,OAAO,OAAO;EACxD,MAAM,IAAI,WAAW,OAAO,SAAS,SAAS,UAAU,OAAO,OAAO,CAAC;CAC3E,GACA,OAAO,QAAQ;EACX,MAAM,IAAI,KAAK,YAAY,OAAO,MAAM,GAAG,GAAG,MAAM;CACxD,CACJ;AACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACnDA,SAAgB,kBAAkB,OAAkC;CAChE,MAAM,EAAE,SAAS,aAAa,OAAO,UAAU,OAAO,eAAe,oBAAoB;CAEzF,IAAI,SAAS;EACT,MAAM,UAAU,aAAa,aAAa,KAAK;EAC/C,IAAI,QAAQ,WAAW,GAAG;EAE1B,MAAM,KADY,mBAAmB,yBACjB,QAAW,SAAS,OAAO;CACnD;CAEA,IAAI,OAAO,aAAa,KAAK,GAAG;CAEhC,MAAM,KADU,iBAAiB,oBACf,QAAW,SAAS,aAAa,aAAa,KAAK,CAAC;AAC1E;;;;;;;;;;;;;;;;;;;;;;;ACzFA,SAAgB,WAAW,SAA4B,KAAwB,QAAqC;CAChH,OAAO,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,CAAC,MAAM,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,IAAI,MAAM,CAAC,CAAC;AAC/E;;;;;;;;ACdA,IAAa,gBAAb,MAA+D;CAEvC;CACA;CACA;CAHpB,YACI,AAAgB,MAChB,AAAgB,IAChB,AAAgB,WAAW,OAC7B;EAHkB;EACA;EACA;CACjB;CAEH,AAAO,WAAmB;EACtB,OAAO,IAAI,KAAK,WAAW,MAAM,GAAG,GAAG,KAAK,KAAK,GAAG,KAAK,GAAG;CAChE;AACJ;;;;;;;;;;;;;ACHA,SAAgB,SAAe,OAAwB,MAAc,SAAiC;CAClG,IAAI,CAAC,OAAO,cAAc,OAAO,KAAK,WAAW,GAC7C,MAAM,IAAI,mBAAmB,kBAAkB,0BAA0B,CAAC,OAAO,CAAC;CAGtF,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,KAAK,MAAM,SAAS,OAAO,CAAC;CAChE,MAAM,UAAU,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,GAAG,aAAa,CAAC;CACtE,MAAM,QAAQ,UAAU;CAExB,OAAO;EACH,OAAO,MAAM,MAAM,OAAO,QAAQ,OAAO;EACzC,MAAM;EACN;EACA;EACA,SAAS,UAAU;EACnB,SAAS,UAAU,aAAa;CACpC;AACJ;;;;ACOA,MAAM,gBAA4C;CAC9C,OAAO;CACP,MAAM;CACN,WAAW;CACX,MAAM;CACN,MAAM;AACV;AAGA,MAAM,eAA2C;CAC7C,OAAO;CACP,MAAM;CACN,WAAW;CACX,MAAM;CACN,MAAM;AACV;AAEA,MAAM,sBAAsB;AAE5B,IAAa,WAAb,MAAmD;CAE1B;CACA;CAFrB,YACI,AAAiB,QACjB,AAAiB,MACnB;EAFmB;EACA;CAClB;CAEH,OAAO,KAAiB,WAA6C;EACjE,MAAM,EAAE,QAAQ,aAAa,KAAK,SAAS,GAAG;EAG9C,MAAM,eAAe,QAAQ,cAAc,KAAK,cAAc,IAAI,cAAc;EAChF,MAAM,QAAQ,WAAW,UAAU,QAAQ,eAAe,CAAC,WAAW,QAAQ,eAAe;EAE7F,MAAM,SAAS,IAAI,cAAc,CAAC,CAE7B,YAAY,KAAK,OAAO,OAAO;GAAE,MAAM,KAAK,IAAI,KAAK,IAAI,GAAG,MAAM,GAAG,QAAQ;GAAG,MAAM,aAAa;EAAK,CAAC,CAAC,CAAC,CAC3G,SAAS,WAAW,SAAS,YAAY,SAAS,CAAC,CACnD,YAAY,QAAQ;EACzB,IAAI,UAAU,QAAW,OAAO,SAAS,KAAK;EAC9C,IAAI,WAAW,OAAO,OAAO,SAAS,UAAU,KAAK;EACrD,OAAO;CACX;CAEA,AAAQ,SAAS,KAAwD;EACrE,MAAM,EAAE,MAAM,YAAY,SAAS,YAAY,KAAK;EACpD,MAAM,aAAa,eAAe;EAgBlC,OAAO;GAAE,QAfM;IACX,OAAO;IACP,MAAM,OAAO;IACb,WAAW;IACX,MAAM,OAAO;IACb,MAAM,aAAa,aAAa,IAAI;GACxC,EAAE;GASe,UAPA;IACb,OAAO,CAAC;IACR,MAAM,CAAC;IACP,WAAW;IACX,MAAM,CAAC,WAAW,OAAO;IACzB,MAAM,CAAC,WAAW,CAAC,cAAc,aAAa;GAClD,EAAE;EACwB;CAC9B;CAEA,IAAI,GAAG,MAAqD;EACxD,IAAI,KAAK,WAAW,GAChB,MAAM,IAAI,mBAAmB,kBAAkB,uBAAuB;EAE1E,IAAI,KAAK,SAAS,qBACd,MAAM,IAAI,mBAAmB,kBAAkB,2BAA2B,CAAC,KAAK,MAAM,CAAC;EAG3F,MAAM,uBAAO,IAAI,IAAgB;EACjC,KAAK,MAAM,OAAO,MAAM;GACpB,IAAI,KAAK,IAAI,GAAG,GAAG,MAAM,IAAI,kBAAkB,kBAAkB,6BAA6B,CAAC,GAAG,CAAC;GACnG,KAAK,IAAI,GAAG;EAChB;EACA,OAAO,IAAI,iBAAgC,CAAC,CAAC,cAAc,KAAK,KAAK,QAAQ,KAAK,OAAO,GAAG,CAAC,CAAC;CAClG;CAEA,AAAQ,gBAAwB;EAC5B,MAAM,EAAE,MAAM,eAAe,KAAK;EAClC,OAAO,eAAe,SAAY,QAAQ,OAAO,MAAM,QAAQ,OAAO,EAAE,MAAM;CAClF;AACJ;;;;ACrGA,IAAM,gBAAN,cAAkC,iBAA8B;CAC5D,YAAY,MAAsB,YAA8B,YAA6C;EACzG,MAAM,WAAW;EACjB,MAAM,OAAO,KAAK,OAAO,KAAK;EAC9B,IAAI,QAAkB,CAAC;EACvB,MAAM,cAAoB;GACtB,IAAI,MAAM,WAAW,GAAG;GACxB,KAAK,SAAS,yBAAyB,IAAI,mBAAmB,CAAC,CAAC,WAAW,MAAM,KAAK,IAAI,CAAC,CAAC;GAC5F,QAAQ,CAAC;EACb;EACA,KAAK,MAAM,SAAS,MAAM,WAAW;GACjC,MAAM,WAAW,WAAW,MAAM,OAAO,MAAM;GAC/C,IAAI,OAAO,aAAa,UAAU;IAC9B,MAAM,KAAK,QAAQ;IACnB;GACJ;GACA,MAAM;GACN,KAAK,SAAS,qBAAqB,SAAS,SAAS;EACzD,CAAC;EACD,MAAM;EACN,KAAK,SAAS,uBAAuB,UAAU;CACnD;AACJ;AAEA,SAAS,gBAAgB,YAAuC;CAC5D,IAAI,OAAO,eAAe,UAAU,OAAO,EAAE,YAAY,CAAC,IAAI,mBAAmB,CAAC,CAAC,WAAW,UAAU,CAAC,EAAE;CAC3G,IAAI,MAAM,QAAQ,UAAU,GAAG,OAAO,EAAE,YAAY,WAAW;CAC/D,OAAO;AACX;AAEA,MAAM,aAA2B;CAAC;CAAS;CAAQ;CAAa;CAAQ;AAAM;AAC9E,MAAM,cAA4B;CAAC;CAAQ;CAAa;AAAM;AAI9D,SAAgB,WACZ,MACA,QACA,QACa;CACb,MAAM,WAAW,IAAI,SAAS,QAAQ,IAAI;CAC1C,IAAI,OAAO,QAAQ,OAAO,gBAAgB,OAAO,OAAO,MAAM,QAAQ,CAAC;CAEvE,MAAM,aAAa,OAAO,cAAc;CACxC,MAAM,MAAM,SAAS,IAAI,GAAI,KAAK,eAAe,SAAY,cAAc,UAAW;CACtF,OAAO,EAAE,YAAY,CAAC,IAAI,cAAc,MAAM,YAAY,GAAG,CAAC,CAAC,SAAS,EAAE;AAC9E;;;;;;;;;;;;AC3BA,IAAsB,gBAAtB,MAAsE;;CAElE,AAAgB;CAEhB,AAAmB;CAEnB,AAAU,YAAY,QAA4C;EAC9D,KAAK,SAAS,WAAW,OAAO,MAAM;EACtC,KAAK,SAAS;CAClB;;CAGA,MAAM,KAAK,KAAU,GAAmC;EACpD,MAAM,OAAO,MAAM,KAAK,OAAO,OAAO,KAAK,KAAK,CAAC;EACjD,OAAO,WAAW,MAAM,KAAK,QAAQ,KAAK,MAAM;CACpD;AACJ;;;;AC/CA,MAAM,mBAAmB;AAYzB,SAAS,UAAU,MAAqC;CACpD,MAAM,UAAU,MAAM,WAAW;CACjC,IAAI,CAAC,OAAO,cAAc,OAAO,KAAK,WAAW,GAC7C,MAAM,IAAI,mBAAmB,kBAAkB,0BAA0B,CAAC,OAAO,CAAC;CAEtF,OAAO;AACX;;;;;;;;;AAUA,IAAa,cAAb,MAAqE;CAI5C;CAHrB,AAAiB;CAEjB,YACI,AAAiB,MACjB,MACF;EAFmB;EAGjB,KAAK,UAAU,UAAU,IAAI;CACjC;CAEA,MAAM,KAAK,KAAU,GAAoC;EACrD,OAAO,SAAS,MAAM,KAAK,KAAK,GAAG,GAAG,GAAG,KAAK,OAAO;CACzD;AACJ;;;;;;;;;;AAWA,IAAa,eAAb,MAAsE;CAI7C;CAHrB,AAAiB;CAEjB,YACI,AAAiB,OAKjB,MACF;EANmB;EAOjB,KAAK,UAAU,UAAU,IAAI;CACjC;CAEA,MAAM,KAAK,KAAU,GAAoC;EACrD,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC;EACtC,MAAM,EAAE,OAAO,YAAY,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,OAAO;EACnE,OAAO;GAAE,OAAO,CAAC,GAAG,KAAK;GAAG;GAAM,SAAS,KAAK;GAAS,SAAS,OAAO;GAAG;EAAQ;CACxF;AACJ;;;;;ACIA,MAAa"}
@@ -1,6 +1,7 @@
1
- import { A as ModalLike, C as Gate, F as ComponentKindBrand, G as storeComponentRoute, H as contextMenuRouteOf, I as ContextMenuKindBrand, K as storeInteractionRoute, L as ContextMenuNamesBrand, M as serializeReply, N as AutocompleteRouteBrand, P as ComponentDefsBrand, Q as CommandMeta, R as SlashRouteBrand, St as NamesFor, U as interactionRoutesOf, V as areRoutes, W as selectMenuRouteOf, X as prefixOf, Y as decodeFor, Z as DecodedParams, _ as pickNotice, _t as SlashRegistry, bt as MenuCacheFor, c as FocusedField, et as Promisable, gt as SlashOption, ht as RouteCache, i as AutocompletableNames, j as SerializedReply, k as BaseReplySender, m as PermissionNames, mt as OptionKind, n as PageCursor, nt as BuilderComponent, o as ChoiceValueOf, pt as CacheFor, q as AnyCustomId, r as pageCursor, s as EntryFor, st as CommandInjector, t as PAGE_MAX, tt as IsNever, v as GateFitsWith, vt as SlashRouteEntry, w as GateContextBase, z as RoutableConstructor } from "./cursor-DKKKUa_7.mjs";
1
+ import { A as ModalLike, C as Gate, F as ComponentKindBrand, G as storeComponentRoute, H as contextMenuRouteOf, I as ContextMenuKindBrand, K as storeInteractionRoute, L as ContextMenuNamesBrand, M as serializeReply, N as AutocompleteRouteBrand, P as ComponentDefsBrand, Q as CommandMeta, R as SlashRouteBrand, U as interactionRoutesOf, V as areRoutes, W as selectMenuRouteOf, X as prefixOf, Y as decodeFor, Z as DecodedParams, _ as pickNotice, c as FocusedField, ct as RouteCache, dt as SlashRouteEntry, et as Promisable, ht as NamesFor, i as AutocompletableNames, j as SerializedReply, k as BaseReplySender, lt as SlashOption, m as PermissionNames, n as PageCursor, nt as BuilderComponent, o as ChoiceValueOf, ot as CacheFor, pt as MenuCacheFor, q as AnyCustomId, r as pageCursor, s as EntryFor, st as OptionKind, t as PAGE_MAX, tt as IsNever, ut as SlashRegistry, v as GateFitsWith, w as GateContextBase, z as RoutableConstructor } from "./cursor-BANzac53.mjs";
2
2
  import { n as CommandBuilder, r as DeployResult } from "./basic-Dng__Ena.mjs";
3
- import { A as reportedWrite, B as InteractionMetadataKey, C as DispatchOutcome, F as AckTrace, G as WebhookUrlMetadataKey, H as InteractionRoutes, I as PublishDefault, L as CommandMetadataKey, M as ReplyMethod, N as checkAckLegality, O as Notice, P as sendTarget, R as EventMetadataKey, S as DefaultSubscriptions, U as MiddlewareMetadataKey, V as InteractionRouteKeys, W as SubscribeMetadataKey, _ as Bus, a as PluginCtor, b as busLoggerOf, i as PluginArgs, j as AckState, k as ReplyTelemetry, v as StoredSubscriberCtor, y as SubscriberRegistration, z as GatedMetadataKey } from "./Plugin-B6iRzm3g.mjs";
3
+ import { A as MiddlewareMetadataKey, C as PublishDefault, D as InteractionMetadataKey, E as GatedMetadataKey, M as WebhookUrlMetadataKey, O as InteractionRouteKeys, S as AckTrace, T as EventMetadataKey, _ as reportedWrite, a as StoredSubscriberCtor, b as checkAckLegality, g as ReplyTelemetry, h as Notice, i as Bus, j as SubscribeMetadataKey, k as InteractionRoutes, l as DefaultSubscriptions, o as SubscriberRegistration, s as busLoggerOf, u as DispatchOutcome, v as AckState, w as CommandMetadataKey, x as sendTarget, y as ReplyMethod } from "./phases-hBxMCD2V.mjs";
4
+ import { a as PluginCtor, i as PluginArgs, n as Initializeable } from "./Plugin-CQ9IEj5X.mjs";
4
5
  import { n as contextMenuLeaves, t as ContextMenuLeaves } from "./contextMenuLeaves-COzDYa6c.mjs";
5
6
  import "reflect-metadata";
6
7
  import { ContextMenuCommandBuilder, SlashCommandBuilder } from "@discordjs/builders";
@@ -260,5 +261,5 @@ interface DispatchReport {
260
261
  declare function queuedMsFor(interactionId: string): number;
261
262
  declare function reportDispatch(bus: Bus, report: DispatchReport): void;
262
263
  //#endregion
263
- export { type AckState, AckTrace, type AnyCustomId, type AutocompletableNames, AutocompleteRouteBrand, BaseReplySender, type CacheFor, type ChoiceValueOf, type CommandBuilder, CommandInjector, type CommandMeta, CommandMetadataKey, ComponentDefsBrand, ComponentDefsKey, ComponentKindBrand, ContextMenuKindBrand, type ContextMenuLeaves, ContextMenuNamesBrand, type DecodedComponentRoute, type DecodedParams, type DefaultSubscriptions, type DeployResult, type EntryFor, EventMetadataKey, type FocusedField, type GateFitsWith, GateNotice, type GateObserver, GatedMetadataKey, type HasComponentDefs, HasDangerousPermissions, HmrManager, InteractionMetadataKey, InteractionRouteKeys, InteractionRoutes, type MatchArms, type MenuCacheFor, MiddlewareMetadataKey, MissingPermissions, MissingRole, type ModalLike, type NamesFor, NeedsAny, NotAllowed, NotInDm, NotInGuild, NotOwner, NoticeCard, OnCooldown, type OptionKind, type OptionLens, PAGE_MAX, type PageCursor, PermissionNames, type PluginArgs, type PluginCtor, PublishDefault, type ReplyMethod, type ReplyTelemetry, ReportThrottle, type RoutableConstructor, type RouteCache, type SerializedReply, type SingleParams, type SlashOption, type SlashOptions, SlashRouteBrand, type SlashRouteEntry, type SlowGateMonitor, type StoredSubscriberCtor, SubscribeMetadataKey, type SubscriberRegistration, WebhookUrlMetadataKey, accessorStore, areRoutes, asError, busLoggerOf, checkAckLegality, clearStore, contextMenuLeaves, contextMenuRouteOf, decodeComponentRoute, decodeFor, deferFlags, getDevChannel, guardedAccessor, interactionRoutesOf, isEmojiTuple, outcomeFor, pageCursor, pickNotice, prefixOf, queuedMsFor, reportDispatch, reportedWrite, routeIdOf, runGates, runHandlerGates, selectMenuRouteOf, sendFlags, sendTarget, serializeReply, setBotColor, setDevChannel, slashRouteLeaves, slowGateMonitor, storeComponentRoute, storeInteractionRoute, translateSerializationError, wrapHot };
264
+ export { type AckState, AckTrace, type AnyCustomId, type AutocompletableNames, AutocompleteRouteBrand, BaseReplySender, type CacheFor, type ChoiceValueOf, type CommandBuilder, type CommandMeta, CommandMetadataKey, ComponentDefsBrand, ComponentDefsKey, ComponentKindBrand, ContextMenuKindBrand, type ContextMenuLeaves, ContextMenuNamesBrand, type DecodedComponentRoute, type DecodedParams, type DefaultSubscriptions, type DeployResult, type EntryFor, EventMetadataKey, type FocusedField, type GateFitsWith, GateNotice, type GateObserver, GatedMetadataKey, type HasComponentDefs, HasDangerousPermissions, HmrManager, type Initializeable, InteractionMetadataKey, InteractionRouteKeys, InteractionRoutes, type MatchArms, type MenuCacheFor, MiddlewareMetadataKey, MissingPermissions, MissingRole, type ModalLike, type NamesFor, NeedsAny, NotAllowed, NotInDm, NotInGuild, NotOwner, NoticeCard, OnCooldown, type OptionKind, type OptionLens, PAGE_MAX, type PageCursor, PermissionNames, type PluginArgs, type PluginCtor, PublishDefault, type ReplyMethod, type ReplyTelemetry, ReportThrottle, type RoutableConstructor, type RouteCache, type SerializedReply, type SingleParams, type SlashOption, type SlashOptions, SlashRouteBrand, type SlashRouteEntry, type SlowGateMonitor, type StoredSubscriberCtor, SubscribeMetadataKey, type SubscriberRegistration, WebhookUrlMetadataKey, accessorStore, areRoutes, asError, busLoggerOf, checkAckLegality, clearStore, contextMenuLeaves, contextMenuRouteOf, decodeComponentRoute, decodeFor, deferFlags, getDevChannel, guardedAccessor, interactionRoutesOf, isEmojiTuple, outcomeFor, pageCursor, pickNotice, prefixOf, queuedMsFor, reportDispatch, reportedWrite, routeIdOf, runGates, runHandlerGates, selectMenuRouteOf, sendFlags, sendTarget, serializeReply, setBotColor, setDevChannel, slashRouteLeaves, slowGateMonitor, storeComponentRoute, storeInteractionRoute, translateSerializationError, wrapHot };
264
265
  //# sourceMappingURL=internal.index.d.mts.map
@@ -1,5 +1,5 @@
1
- import { C as WebhookUrlMetadataKey, S as SubscribeMetadataKey, _ as GatedMetadataKey, b as InteractionRoutes, d as sendFlags, g as EventMetadataKey, h as CommandMetadataKey, i as PublishDefault, l as ReportThrottle, m as setBotColor, n as busLoggerOf, u as deferFlags, v as InteractionMetadataKey, w as contextMenuLeaves, x as MiddlewareMetadataKey, y as InteractionRouteKeys } from "./Bus-DcuUtOEL.mjs";
2
- import { A as areRoutes, B as prefixOf, C as NotInDm, E as OnCooldown, F as storeInteractionRoute, H as Notice, I as ComponentDefsKey, J as guardedAccessor, K as accessorStore, L as decodeComponentRoute, M as interactionRoutesOf, N as selectMenuRouteOf, P as storeComponentRoute, S as NotAllowed, T as NotOwner, U as CommandInjector, V as NoticeCard, _ as GateNotice, b as MissingRole, d as pickNotice, f as discardCommits, g as runCommits, h as runCheck, j as contextMenuRouteOf, n as pageCursor, q as clearStore, r as Silence, s as PermissionNames, t as PAGE_MAX, v as HasDangerousPermissions, w as NotInGuild, x as NeedsAny, y as MissingPermissions, z as decodeFor } from "./cursor-Cv1Ui6gZ.mjs";
1
+ import { C as WebhookUrlMetadataKey, D as contextMenuLeaves, E as guardedAccessor, S as SubscribeMetadataKey, T as clearStore, _ as GatedMetadataKey, b as InteractionRoutes, d as sendFlags, g as EventMetadataKey, h as CommandMetadataKey, i as PublishDefault, l as ReportThrottle, m as setBotColor, n as busLoggerOf, u as deferFlags, v as InteractionMetadataKey, w as accessorStore, x as MiddlewareMetadataKey, y as InteractionRouteKeys } from "./Bus-BvF4VShN.mjs";
2
+ import { A as areRoutes, B as prefixOf, C as NotInDm, E as OnCooldown, F as storeInteractionRoute, H as Notice, I as ComponentDefsKey, L as decodeComponentRoute, M as interactionRoutesOf, N as selectMenuRouteOf, P as storeComponentRoute, S as NotAllowed, T as NotOwner, V as NoticeCard, _ as GateNotice, b as MissingRole, d as pickNotice, f as discardCommits, g as runCommits, h as runCheck, j as contextMenuRouteOf, n as pageCursor, r as Silence, s as PermissionNames, t as PAGE_MAX, v as HasDangerousPermissions, w as NotInGuild, x as NeedsAny, y as MissingPermissions, z as decodeFor } from "./cursor-CuoA9cRh.mjs";
3
3
  import { n as setDevChannel, t as getDevChannel } from "./devChannel-CQU7hq7Q.mjs";
4
4
  import { t as slashRouteLeaves } from "./slashRouteLeaves-De1SxqnX.mjs";
5
5
  import "reflect-metadata";
@@ -617,5 +617,5 @@ function reportDispatch(bus, report) {
617
617
  }
618
618
 
619
619
  //#endregion
620
- export { AckTrace, AutocompleteRouteBrand, BaseReplySender, CommandInjector, CommandMetadataKey, ComponentDefsBrand, ComponentDefsKey, ComponentKindBrand, ContextMenuKindBrand, ContextMenuNamesBrand, EventMetadataKey, GateNotice, GatedMetadataKey, HasDangerousPermissions, HmrManager, InteractionMetadataKey, InteractionRouteKeys, InteractionRoutes, MiddlewareMetadataKey, MissingPermissions, MissingRole, NeedsAny, NotAllowed, NotInDm, NotInGuild, NotOwner, NoticeCard, OnCooldown, PAGE_MAX, PermissionNames, PublishDefault, ReportThrottle, SlashRouteBrand, SubscribeMetadataKey, WebhookUrlMetadataKey, accessorStore, areRoutes, asError, busLoggerOf, checkAckLegality, clearStore, contextMenuLeaves, contextMenuRouteOf, decodeComponentRoute, decodeFor, deferFlags, getDevChannel, guardedAccessor, interactionRoutesOf, isEmojiTuple, outcomeFor, pageCursor, pickNotice, prefixOf, queuedMsFor, reportDispatch, reportedWrite, routeIdOf, runGates, runHandlerGates, selectMenuRouteOf, sendFlags, sendTarget, serializeReply, setBotColor, setDevChannel, slashRouteLeaves, slowGateMonitor, storeComponentRoute, storeInteractionRoute, translateSerializationError, wrapHot };
620
+ export { AckTrace, AutocompleteRouteBrand, BaseReplySender, CommandMetadataKey, ComponentDefsBrand, ComponentDefsKey, ComponentKindBrand, ContextMenuKindBrand, ContextMenuNamesBrand, EventMetadataKey, GateNotice, GatedMetadataKey, HasDangerousPermissions, HmrManager, InteractionMetadataKey, InteractionRouteKeys, InteractionRoutes, MiddlewareMetadataKey, MissingPermissions, MissingRole, NeedsAny, NotAllowed, NotInDm, NotInGuild, NotOwner, NoticeCard, OnCooldown, PAGE_MAX, PermissionNames, PublishDefault, ReportThrottle, SlashRouteBrand, SubscribeMetadataKey, WebhookUrlMetadataKey, accessorStore, areRoutes, asError, busLoggerOf, checkAckLegality, clearStore, contextMenuLeaves, contextMenuRouteOf, decodeComponentRoute, decodeFor, deferFlags, getDevChannel, guardedAccessor, interactionRoutesOf, isEmojiTuple, outcomeFor, pageCursor, pickNotice, prefixOf, queuedMsFor, reportDispatch, reportedWrite, routeIdOf, runGates, runHandlerGates, selectMenuRouteOf, sendFlags, sendTarget, serializeReply, setBotColor, setDevChannel, slashRouteLeaves, slowGateMonitor, storeComponentRoute, storeInteractionRoute, translateSerializationError, wrapHot };
621
621
  //# sourceMappingURL=internal.index.mjs.map
@@ -1,6 +1,7 @@
1
1
  import { n as CommandBuilder, r as DeployResult } from "./basic-Dng__Ena.mjs";
2
- import { _ as Bus, a as PluginCtor, d as Transport, f as TransportAssert, g as CoreBase, h as StartupPhase, i as PluginArgs, l as Runtime, m as ShutdownPhase, n as Initializeable, o as PluginLike, s as ChannelKeyAssert, t as CoreParamAssert, u as RuntimeAssert } from "./Plugin-B6iRzm3g.mjs";
3
- import "./index-uBwh8r7x.mjs";
2
+ import { i as Bus, n as StartupPhase, r as CoreBase, t as ShutdownPhase } from "./phases-hBxMCD2V.mjs";
3
+ import "./index-rt8rZg4d.mjs";
4
+ import { a as PluginCtor, d as Transport, f as TransportAssert, i as PluginArgs, l as Runtime, n as Initializeable, o as PluginLike, s as ChannelKeyAssert, t as CoreParamAssert, u as RuntimeAssert } from "./Plugin-CQ9IEj5X.mjs";
4
5
  import { t as ContextMenuLeaves } from "./contextMenuLeaves-COzDYa6c.mjs";
5
6
  import { Logger } from "@seedcord/logger";
6
7
  import { REST } from "@discordjs/rest";
@@ -97,12 +98,13 @@ declare class HealthResponder {
97
98
  /**
98
99
  * Base class for a plugin host, a transport `Seedcord` class.
99
100
  *
100
- * Plugins are attached during configuration and initialized during startup, sequentially in attach
101
- * order within each phase.
101
+ * You attach plugins while configuring the bot. Within one startup phase, their `init()` calls run
102
+ * one after another in attach order.
102
103
  */
103
104
  declare abstract class Pluggable<BotT extends Transport, BotRt extends Runtime> implements CoreBase {
104
105
  abstract readonly config: Config;
105
106
  abstract readonly rest: REST;
107
+ abstract readonly applicationId: string;
106
108
  abstract readonly rateLimiter: IRateLimiter;
107
109
  abstract readonly bus: Bus;
108
110
  /** @see {@link CoordinatedShutdown} */
@@ -127,26 +129,21 @@ declare abstract class Pluggable<BotT extends Transport, BotRt extends Runtime>
127
129
  /** @internal so the next host can construct */
128
130
  protected static reset(): void;
129
131
  /**
130
- * Attaches a plugin to this instance.
132
+ * Attaches a plugin under `key`. Read the instance back as `core[key]`. `seedcord codegen`
133
+ * writes the `Core` augmentation that types it there.
131
134
  *
132
- * The plugin initializes during startup, after every plugin attached before it, and becomes
133
- * available as a property on `core`. `seedcord codegen` writes the `Core` augmentation that
134
- * types it.
135
+ * Startup runs each plugin's `init()` in attach order within its phase.
135
136
  *
136
- * A plugin declaring a `transport` or `runtime` this host does not run is a compile error on
137
- * this call. An edge host takes no plugins at all. A plugin constructor narrowing its first
138
- * parameter past `CoreBase` is a compile error here too. Read the transport `Core` off `this.core`.
137
+ * Attaching a plugin whose `transport` or `runtime` differs from this host fails to compile.
138
+ * Your constructor takes `CoreBase` as its first parameter (you don't need to pass it though).
139
+ * A narrower one fails to compile here.
139
140
  *
140
- * @typeParam Key - The property name for accessing the plugin
141
- * @typeParam Ctor - The plugin constructor type
142
- * @param key - Property name to access the plugin instance
143
- * @param Plugin - Plugin constructor class
144
- * @param args - Additional arguments to pass to the plugin constructor
145
- * @returns This instance with the plugin attached as a typed property
146
- * @throws A **SeedcordError** When called after initialization or if key already exists or is reserved
141
+ * @param key - Also the channel the plugin logs on. Reserved channel names throw.
142
+ * @param args - Whatever your constructor takes after the host.
143
+ * @throws A **SeedcordError** if you attach after the bot has started. A taken or reserved key throws too.
147
144
  * @example
148
- * ```typescript
149
- * seedcord.attach('db', Mongoose, { uri: 'mongodb://...', name: 'seedcord', dir: ... })
145
+ * ```ts
146
+ * seedcord.attach('db', Mongoose, { uri: 'mongodb://...', name: 'seedcord', dir: ... });
150
147
  * ```
151
148
  */
152
149
  attach<Key extends string, Ctor extends PluginCtor>(this: this, key: ChannelKeyAssert<Key>, Plugin: Ctor & TransportAssert<InstanceType<Ctor>, BotT> & RuntimeAssert<InstanceType<Ctor>, BotRt> & CoreParamAssert<Ctor>, ...args: PluginArgs<Ctor>): this & Record<Key, InstanceType<Ctor>>;
@@ -181,21 +178,13 @@ declare class SubscriberLoader implements Initializeable, HmrAware {
181
178
  }
182
179
  //#endregion
183
180
  //#region src/node/commands/CommandRegistry.d.ts
184
- /** @internal */
185
- interface CommandRegistryOptions {
186
- readonly dir: string;
187
- readonly rest: REST;
188
- /** Called at deploy time, because the application id only resolves after login. */
189
- readonly applicationId: () => string;
190
- readonly onDeployed?: (result: DeployResult) => void;
191
- }
192
181
  /**
193
182
  * Scans the command directory, builds each command, and PUTs the global and guild scopes to Discord.
194
183
  *
195
184
  * @internal
196
185
  */
197
186
  declare class CommandRegistry implements Initializeable, HmrAware {
198
- private readonly options;
187
+ private readonly core;
199
188
  private readonly logger;
200
189
  private isInitialised;
201
190
  readonly globalCommands: CommandBuilder[];
@@ -205,7 +194,9 @@ declare class CommandRegistry implements Initializeable, HmrAware {
205
194
  private readonly loadedCommands;
206
195
  private readonly hmrHandler?;
207
196
  private readonly pendingEvents;
208
- constructor(options: CommandRegistryOptions);
197
+ private readonly injector;
198
+ private readonly dir;
199
+ constructor(core: CoreBase);
209
200
  private getArtifacts;
210
201
  /** @internal */
211
202
  init(): Promise<void>;
@@ -229,5 +220,5 @@ declare class CommandRegistry implements Initializeable, HmrAware {
229
220
  contextMenuLeaves(): ContextMenuLeaves;
230
221
  }
231
222
  //#endregion
232
- export { CommandRegistry, type CommandRegistryOptions, CoordinatedShutdown, CoordinatedStartup, HealthCheck, HealthResponder, type LifecycleTask, Pluggable, ShutdownPhase, StartupPhase, SubscriberLoader, settleWithin };
223
+ export { CommandRegistry, CoordinatedShutdown, CoordinatedStartup, HealthCheck, HealthResponder, type LifecycleTask, Pluggable, ShutdownPhase, StartupPhase, SubscriberLoader, settleWithin };
233
224
  //# sourceMappingURL=node-internal.index.d.mts.map