@seedcord/core 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{Bus-DcuUtOEL.mjs → Bus-BVwmKc-A.mjs} +51 -17
- package/dist/Bus-BVwmKc-A.mjs.map +1 -0
- package/dist/Pluggable-Cf5-WWmW.mjs +280 -0
- package/dist/Pluggable-Cf5-WWmW.mjs.map +1 -0
- package/dist/Pluggable-DFNUIlJM.d.mts +148 -0
- package/dist/{Plugin-Bkeqcjy6.mjs → Plugin-C6oMqWf8.mjs} +32 -7
- package/dist/Plugin-C6oMqWf8.mjs.map +1 -0
- package/dist/Plugin-CG9pWbPF.d.mts +193 -0
- package/dist/{cursor-DKKKUa_7.d.mts → cursor-BcRfmTSo.d.mts} +27 -140
- package/dist/{cursor-Cv1Ui6gZ.mjs → cursor-GBUYz3jk.mjs} +377 -176
- package/dist/cursor-GBUYz3jk.mjs.map +1 -0
- package/dist/{index-uBwh8r7x.d.mts → index-ZzdQafFv.d.mts} +2 -2
- package/dist/index.d.mts +132 -19
- package/dist/index.mjs +186 -19
- package/dist/index.mjs.map +1 -1
- package/dist/internal.index.d.mts +11 -4
- package/dist/internal.index.mjs +8 -455
- package/dist/internal.index.mjs.map +1 -1
- package/dist/node-internal.index.d.mts +10 -146
- package/dist/node-internal.index.mjs +43 -470
- package/dist/node-internal.index.mjs.map +1 -1
- package/dist/node.index.d.mts +2 -0
- package/dist/node.index.mjs +256 -0
- package/dist/node.index.mjs.map +1 -0
- package/dist/{Plugin-B6iRzm3g.d.mts → phases-F8yvPd60.d.mts} +111 -207
- package/dist/plugin.index.d.mts +2 -2
- package/dist/plugin.index.mjs +1 -1
- package/dist/publishDefault-D826XXDQ.mjs +14 -0
- package/dist/publishDefault-D826XXDQ.mjs.map +1 -0
- package/dist/{slashRouteLeaves-De1SxqnX.mjs → slashRouteLeaves-CbYRwqMY.mjs} +10 -2
- package/dist/slashRouteLeaves-CbYRwqMY.mjs.map +1 -0
- package/dist/subscribers-CGGQkUfM.mjs +139 -0
- package/dist/subscribers-CGGQkUfM.mjs.map +1 -0
- package/package.json +14 -8
- package/dist/Bus-DcuUtOEL.mjs.map +0 -1
- package/dist/Plugin-Bkeqcjy6.mjs.map +0 -1
- package/dist/cursor-Cv1Ui6gZ.mjs.map +0 -1
- package/dist/slashRouteLeaves-De1SxqnX.mjs.map +0 -1
- package/dist/subscribers-DuHg-bGT.mjs +0 -3
package/dist/index.mjs.map
CHANGED
|
@@ -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":"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"}
|
|
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/reply/BaseReplySender.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, SeedcordTypeError } 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 SeedcordTypeError(SeedcordErrorCode.DecoratorCommandGlobalWithGuilds);\n }\n\n if (scope === 'guild' && (!Array.isArray(guilds) || guilds.length === 0)) {\n throw new SeedcordTypeError(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 /** The reply surface for this interaction. Pass the handler to reply from outside its class. */\n public 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 /** 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 { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { checkAckLegality, sendTarget } from './ackLegality';\nimport { AckTrace } from './AckTrace';\nimport { attemptWrite, publishResponse } from './responseReport';\nimport { serializeReply } from './serializeReply';\nimport { translateSerializationError } from './translateSerialization';\n\nimport type { AckState, ReplyMethod } from './ackLegality';\nimport type { ReplyTelemetry } from './responseReport';\nimport type { SerializedReply } from './serializeReply';\nimport type { DeferOpts, ReplyResponse, SendOpts } from '@seedcord/types';\nimport type { APIModalInteractionResponseCallbackData } from 'discord-api-types/v10';\n\n/** The shape djs modal builders emit from `toJSON()`. */\nexport interface ModalLike {\n toJSON(): APIModalInteractionResponseCallbackData;\n}\n\n/**\n * Tracks acknowledgement state and throws a translated `SeedcordError` on an illegal verb before any\n * transport call. Every verb runs the legality check, calls the transport's wire writer, then updates\n * the state and the ack trace. Each transport binds `TMessage` to its own created-message type and\n * supplies the writers.\n */\nexport abstract class BaseReplySender<TMessage extends { id: string }, TNative = never> {\n private state: AckState;\n // the only ids a targeted edit accepts\n private readonly sent = new Set<string>();\n // the cause carries the first ack's stack so a double-ack reports both lines\n private ackedBy?: AckTrace;\n\n protected constructor(\n protected readonly routeId: string,\n private readonly telemetry: ReplyTelemetry,\n initialState: AckState = 'unacked'\n ) {\n this.state = initialState;\n }\n\n // anything that threw already reported through attemptWrite\n private report(method: ReplyMethod, startedAt: number, messageId: string | null): void {\n publishResponse(this.telemetry, { routeId: this.routeId, method, startedAt, outcome: 'sent', messageId });\n }\n\n private async attempt<Result>(\n method: ReplyMethod,\n startedAt: number,\n write: () => Promise<Result>\n ): Promise<Result> {\n return await attemptWrite(this.telemetry, this.routeId, method, startedAt, write);\n }\n\n public async reply(response: ReplyResponse<TNative> | string, opts?: SendOpts): Promise<TMessage> {\n this.checkLegality('reply');\n const startedAt = performance.now();\n const created = await this.attempt('reply', startedAt, () => this.writeReply(response, opts));\n // the callback already acked discord, so record replied before the message guard can throw\n this.transition('reply', 'replied');\n const message = this.remember(this.requireMessage(created, 'reply'));\n this.report('reply', startedAt, message.id);\n return message;\n }\n\n public async defer(opts?: DeferOpts): Promise<void> {\n this.checkLegality('defer');\n const startedAt = performance.now();\n await this.attempt('defer', startedAt, () => this.writeDefer(opts));\n this.transition('defer', 'deferred-reply');\n this.report('defer', startedAt, null);\n }\n\n public async deferUpdate(): Promise<void> {\n this.checkLegality('deferUpdate');\n const startedAt = performance.now();\n await this.attempt('deferUpdate', startedAt, () => this.writeDeferUpdate());\n this.transition('deferUpdate', 'deferred-update');\n this.report('deferUpdate', startedAt, null);\n }\n\n /**\n * After a deferUpdate the state stays deferred-update. Calling this again rewrites. The returned message\n * is the source message, editable only through a bare `update` or `edit`. A targeted `edit` or `delete`\n * of it throws the foreign-target error.\n */\n public async update(response: ReplyResponse<TNative> | string): Promise<TMessage> {\n this.checkLegality('update');\n const startedAt = performance.now();\n // in deferred-update the source message is @original and the ack-legality check above already passed\n if (this.state === 'deferred-update') {\n const edited = await this.attempt('update', startedAt, () => this.editOriginal(response));\n this.report('update', startedAt, edited.id);\n return edited;\n }\n const created = await this.attempt('update', startedAt, () => this.writeUpdate(response));\n this.transition('update', 'replied');\n const message = this.remember(this.requireMessage(created, 'update'));\n this.report('update', startedAt, message.id);\n return message;\n }\n\n public async followUp(response: ReplyResponse<TNative> | string, opts?: SendOpts): Promise<TMessage> {\n this.checkLegality('followUp');\n const startedAt = performance.now();\n const created = await this.attempt('followUp', startedAt, () => this.writeFollowUp(response, opts));\n const message = this.remember(created);\n this.report('followUp', startedAt, message.id);\n return message;\n }\n\n public edit(response: ReplyResponse<TNative> | string): Promise<TMessage>;\n public edit(target: TMessage, response: ReplyResponse<TNative> | string): Promise<TMessage>;\n public async edit(\n targetOrResponse: TMessage | ReplyResponse<TNative> | string,\n maybeResponse?: ReplyResponse<TNative> | string\n ): Promise<TMessage> {\n this.checkLegality('edit');\n const startedAt = performance.now();\n if (maybeResponse === undefined) {\n // justified: the overloads narrow targetOrResponse to a response once maybeResponse is absent\n const edited = await this.attempt('edit', startedAt, () =>\n this.editOriginal(targetOrResponse as ReplyResponse<TNative> | string)\n );\n this.report('edit', startedAt, edited.id);\n return edited;\n }\n // justified: the overloads narrow targetOrResponse to a target message once maybeResponse is defined\n const target = targetOrResponse as TMessage;\n if (!this.sent.has(target.id)) {\n throw new SeedcordError(SeedcordErrorCode.ReplyForeignEditTarget, ['edit', target.id, this.routeId]);\n }\n const written = await this.attempt('edit', startedAt, () => this.writeEditTarget(target.id, maybeResponse));\n const message = this.remember(written);\n this.report('edit', startedAt, message.id);\n return message;\n }\n\n public delete(): Promise<void>;\n public delete(target: TMessage): Promise<void>;\n public async delete(target?: TMessage): Promise<void> {\n this.checkLegality('delete');\n const startedAt = performance.now();\n if (target === undefined) {\n await this.attempt('delete', startedAt, () => this.writeDeleteOriginal());\n this.report('delete', startedAt, null);\n return;\n }\n if (!this.sent.has(target.id)) {\n throw new SeedcordError(SeedcordErrorCode.ReplyForeignEditTarget, ['delete', target.id, this.routeId]);\n }\n await this.attempt('delete', startedAt, () => this.writeDeleteTarget(target.id));\n // evict so a later targeted edit of this id throws foreign\n this.sent.delete(target.id);\n this.report('delete', startedAt, null);\n }\n\n /** Routes to the verb the current ack state permits. Every state has a route, so the illegal-ack throw is unreachable. */\n public async send(response: ReplyResponse<TNative> | string, opts?: SendOpts): Promise<TMessage> {\n const target = sendTarget(this.state);\n if (target === 'reply') return await this.reply(response, opts);\n if (target === 'edit') return await this.edit(response);\n return await this.followUp(response, opts);\n }\n\n /** Must be the initial response. */\n public async showModal(modal: ModalLike): Promise<void> {\n this.checkLegality('showModal');\n this.guardModalSource();\n let data: APIModalInteractionResponseCallbackData;\n try {\n data = modal.toJSON();\n } catch (error) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- a null-prototype modal has no constructor at runtime\n const name = modal.constructor?.name;\n throw translateSerializationError(error, !name || name === 'Object' ? 'modal' : name, 0, this.routeId);\n }\n const startedAt = performance.now();\n await this.attempt('showModal', startedAt, () => this.writeModal(data));\n this.transition('showModal', 'replied');\n this.report('showModal', startedAt, null);\n }\n\n protected serialize(response: ReplyResponse<TNative> | string): SerializedReply<TNative> {\n return serializeReply(response, this.routeId);\n }\n\n protected remember(created: TMessage): TMessage {\n this.sent.add(created.id);\n return created;\n }\n\n // a with_response callback always returns the created message. this only trips if the wire says otherwise\n private requireMessage(created: TMessage | undefined, method: 'reply' | 'update'): TMessage {\n if (!created) throw new SeedcordError(SeedcordErrorCode.ReplyCallbackMissingMessage, [method, this.routeId]);\n return created;\n }\n\n private async editOriginal(response: ReplyResponse<TNative> | string): Promise<TMessage> {\n const edited = await this.writeEditOriginal(response);\n // after a deferUpdate the source message is @original, which this interaction did not send\n if (this.state !== 'deferred-update') this.remember(edited);\n return edited;\n }\n\n private checkLegality(method: ReplyMethod): void {\n checkAckLegality(method, this.state, this.routeId, this.ackedBy);\n }\n\n private transition(method: ReplyMethod, state: AckState): void {\n this.state = state;\n this.ackedBy = new AckTrace(method);\n }\n\n // gateway overrides this to reject a modal-submit source\n protected guardModalSource(): void {}\n\n protected abstract writeReply(\n response: ReplyResponse<TNative> | string,\n opts?: SendOpts\n ): Promise<TMessage | undefined>;\n protected abstract writeDefer(opts?: DeferOpts): Promise<void>;\n protected abstract writeDeferUpdate(): Promise<void>;\n protected abstract writeUpdate(response: ReplyResponse<TNative> | string): Promise<TMessage | undefined>;\n protected abstract writeFollowUp(response: ReplyResponse<TNative> | string, opts?: SendOpts): Promise<TMessage>;\n protected abstract writeEditOriginal(response: ReplyResponse<TNative> | string): Promise<TMessage>;\n protected abstract writeEditTarget(targetId: string, response: ReplyResponse<TNative> | string): Promise<TMessage>;\n protected abstract writeDeleteOriginal(): Promise<void>;\n protected abstract writeDeleteTarget(targetId: string): Promise<void>;\n protected abstract writeModal(data: APIModalInteractionResponseCallbackData): Promise<void>;\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// keep this in step with Grouped in matching.ts\nconst ALREADY_JOINED = / [&|] /;\n\nfunction joinNames(gates: readonly Gate<GateContextBase>[], separator: string): string {\n return gates.map((gate) => (ALREADY_JOINED.test(gate.name) ? `(${gate.name})` : gate.name)).join(separator);\n}\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(joinNames(gates, ' & '), async (ctx) => {\n for (const gate of gates) {\n await runCheck(gate, ctx);\n }\n });\n}\n\ninterface OrOptions {\n fail: Notice | ((ctx: GateContextBase) => Notice);\n}\n\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 a list built from each\n * refusal's `summary`, else a default refusal. The trailing options object does not 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(joinNames(gates, ' | '), 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 it refused\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 // 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 // NaN comes back out of Math.trunc, Math.max, and Math.min unchanged.\n const requested = Number.isNaN(page) ? 0 : Math.trunc(page);\n const clamped = Math.min(Math.max(requested, 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 { PageSourceBase } 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: PageSourceBase<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 /** Build one page as a {@link ReplyResponse}. Each transport wraps this in a `page()` taking its handler. */\n protected async buildPage(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 PageSourceBase<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 ArraySourceBase<Item, Ctx> implements PageSourceBase<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 CursorSourceBase<Item, Ctx> implements PageSourceBase<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 // a cursor source has no total to clamp against. Infinity and NaN both fall back to 0.\n const page = Number.isFinite(n) ? Math.max(0, Math.trunc(n)) : 0;\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 { BaseReplySender } from '#reply/BaseReplySender';\nexport type { ModalLike } from '#reply/BaseReplySender';\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';\nexport type { FieldOptions } 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 { ArraySourceBase, CursorSourceBase } from '#pagination/sources';\nexport type { PageSourceBase } 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 AttemptedWrite,\n DispatchOutcome,\n FaultSource,\n ResponseAttempt,\n ResponseFailed,\n ResponseOutcome,\n ResponseSent,\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":"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"}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { $ as Promisable, A as serializeReply, B as contextMenuRouteOf, C as Gate, F as ContextMenuNamesBrand, G as AnyCustomId, H as selectMenuRouteOf, I as SlashRouteBrand, J as decodeFor, L as RoutableConstructor, M as ComponentDefsBrand, N as ComponentKindBrand, P as ContextMenuKindBrand, U as storeComponentRoute, V as interactionRoutesOf, W as storeInteractionRoute, X as DecodedParams, Y as prefixOf, Z as CommandMeta, _ as pickNotice, at as CacheFor, c as FocusedField, ct as SlashOption, et as IsNever, ft as MenuCacheFor, i as AutocompletableNames, j as AutocompleteRouteBrand, k as SerializedReply, lt as SlashRegistry, m as PermissionNames, mt as NamesFor, n as PageCursor, o as ChoiceValueOf, ot as OptionKind, r as pageCursor, s as EntryFor, st as RouteCache, t as PAGE_MAX, tt as BuilderComponent, ut as SlashRouteEntry, v as GateFitsWith, w as GateContextBase, z as areRoutes } from "./cursor-BcRfmTSo.mjs";
|
|
2
2
|
import { n as CommandBuilder, r as DeployResult } from "./basic-Dng__Ena.mjs";
|
|
3
|
-
import { A as
|
|
3
|
+
import { A as RegisteredCount, B as MiddlewareMetadataKey, C as AckState, D as AckTrace, E as sendTarget, F as EventMetadataKey, H as WebhookUrlMetadataKey, I as GatedMetadataKey, L as InteractionMetadataKey, M as VerifyWebhooks, N as PublishDefault, O as RegisterDefaults, P as CommandMetadataKey, R as InteractionRouteKeys, S as reportedWrite, T as checkAckLegality, V as SubscribeMetadataKey, a as StoredSubscriberCtor, b as Notice, d as DispatchOutcome, i as Bus, j as UnregisterSubscriber, k as RegisterSubscriber, o as SubscriberRegistration, s as busLoggerOf, u as DefaultSubscriptions, w as ReplyMethod, x as ReplyTelemetry, z as InteractionRoutes } from "./phases-F8yvPd60.mjs";
|
|
4
|
+
import { a as PluginCtor, i as PluginArgs, n as Initializeable } from "./Plugin-CG9pWbPF.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";
|
|
7
8
|
import { SeedcordErrorCode } from "@seedcord/errors";
|
|
8
9
|
import { ChannelType } from "discord-api-types/v10";
|
|
9
10
|
import { SeedcordError } from "@seedcord/errors/internal";
|
|
10
|
-
import { BotColor, DeferOpts, EpochMs, HmrAware, ReplyResponse, SendOpts } from "@seedcord/types";
|
|
11
11
|
import { DevChannel, SeedcordCliEvents, SeedcordFrameworkEvents } from "@seedcord/types/internal";
|
|
12
|
+
import { BotColor, DeferOpts, EpochMs, HmrAware, ReplyResponse, SendOpts } from "@seedcord/types";
|
|
12
13
|
//#region src/components/botColorHolder.d.ts
|
|
13
14
|
declare function setBotColor(color: BotColor | undefined): void;
|
|
14
15
|
//#endregion
|
|
@@ -37,6 +38,12 @@ declare function guardedAccessor<Value>(accessor: string, storage: Record<string
|
|
|
37
38
|
//#region src/miscellaneous/emojiConfig.d.ts
|
|
38
39
|
declare function isEmojiTuple(value: unknown): value is readonly [string, string];
|
|
39
40
|
//#endregion
|
|
41
|
+
//#region src/commands/isCommandClass.d.ts
|
|
42
|
+
/** @internal */
|
|
43
|
+
type CommandCtor = new () => BuilderComponent<'command' | 'context_menu'>;
|
|
44
|
+
/** @internal */
|
|
45
|
+
declare function isCommandClass(value: unknown): value is CommandCtor;
|
|
46
|
+
//#endregion
|
|
40
47
|
//#region src/commands/slashRouteLeaves.d.ts
|
|
41
48
|
/**
|
|
42
49
|
* dedupes route keys since the same builder appears once per guild.
|
|
@@ -260,5 +267,5 @@ interface DispatchReport {
|
|
|
260
267
|
declare function queuedMsFor(interactionId: string): number;
|
|
261
268
|
declare function reportDispatch(bus: Bus, report: DispatchReport): void;
|
|
262
269
|
//#endregion
|
|
263
|
-
export { type AckState, AckTrace, type AnyCustomId, type AutocompletableNames, AutocompleteRouteBrand,
|
|
270
|
+
export { type AckState, AckTrace, type AnyCustomId, type AutocompletableNames, AutocompleteRouteBrand, type CacheFor, type ChoiceValueOf, type CommandBuilder, type CommandCtor, 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 NamesFor, NeedsAny, NotAllowed, NotInDm, NotInGuild, NotOwner, NoticeCard, OnCooldown, type OptionKind, type OptionLens, PAGE_MAX, type PageCursor, PermissionNames, type PluginArgs, type PluginCtor, PublishDefault, RegisterDefaults, RegisterSubscriber, RegisteredCount, 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, UnregisterSubscriber, VerifyWebhooks, WebhookUrlMetadataKey, accessorStore, areRoutes, asError, busLoggerOf, checkAckLegality, clearStore, contextMenuLeaves, contextMenuRouteOf, decodeComponentRoute, decodeFor, deferFlags, getDevChannel, guardedAccessor, interactionRoutesOf, isCommandClass, isEmojiTuple, outcomeFor, pageCursor, pickNotice, prefixOf, queuedMsFor, reportDispatch, reportedWrite, routeIdOf, runGates, runHandlerGates, selectMenuRouteOf, sendFlags, sendTarget, serializeReply, setBotColor, setDevChannel, slashRouteLeaves, slowGateMonitor, storeComponentRoute, storeInteractionRoute, translateSerializationError, wrapHot };
|
|
264
271
|
//# sourceMappingURL=internal.index.d.mts.map
|