@seedcord/http 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{edge.index-D9PlLJ9b.mjs → edge.index-DK-Y14ge.mjs} +11 -20
- package/dist/edge.index-DK-Y14ge.mjs.map +1 -0
- package/dist/{edge.index-Ce1r0bL4.d.mts → edge.index-QUU3_n9k.d.mts} +2 -2
- package/dist/edge.index.d.mts +1 -1
- package/dist/edge.index.mjs +1 -1
- package/dist/index.d.mts +4 -5
- package/dist/index.mjs +12 -28
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
- package/dist/edge.index-D9PlLJ9b.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
|
-
import { SeedcordError, validateDiscordToken } from "@seedcord/errors/internal";
|
|
2
|
+
import { SeedcordError, applicationIdFromToken, validateDiscordToken } from "@seedcord/errors/internal";
|
|
3
3
|
import { Converters, Envapter } from "envapt";
|
|
4
4
|
import { DiscordAPIError, REST } from "@discordjs/rest";
|
|
5
5
|
import { ArraySource, BaseHandler, Bus, CursorSource, DispatchContext, Fault, Notice, PaginatorBase, RepliableHandler, ResolvedEmoji, Silence, Subscriber, WebhookLog } from "@seedcord/core";
|
|
@@ -274,7 +274,10 @@ function createCore(config, token) {
|
|
|
274
274
|
const draft = {
|
|
275
275
|
config,
|
|
276
276
|
rateLimiter: config.store ?? new MemoryRateLimiter(),
|
|
277
|
-
rest: new REST().setToken(token)
|
|
277
|
+
rest: new REST().setToken(token),
|
|
278
|
+
get applicationId() {
|
|
279
|
+
return applicationIdFromToken(token);
|
|
280
|
+
}
|
|
278
281
|
};
|
|
279
282
|
draft.bus = new Bus(draft);
|
|
280
283
|
return draft;
|
|
@@ -988,16 +991,6 @@ function createSeedcord(config, manifest) {
|
|
|
988
991
|
return buildEngine(core, buildRouteMaps(manifest)).handle;
|
|
989
992
|
}
|
|
990
993
|
|
|
991
|
-
//#endregion
|
|
992
|
-
//#region src/applicationId.ts
|
|
993
|
-
async function fetchApplicationId(rest) {
|
|
994
|
-
try {
|
|
995
|
-
return (await rest.get(Routes.currentApplication())).id;
|
|
996
|
-
} catch (error) {
|
|
997
|
-
throw new SeedcordError(SeedcordErrorCode.CoreApplicationUnavailable, { cause: error });
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
994
|
//#endregion
|
|
1002
995
|
//#region src/emojis/EmojiInjector.ts
|
|
1003
996
|
const emojiStorage = accessorStore();
|
|
@@ -1011,16 +1004,14 @@ function byName(list) {
|
|
|
1011
1004
|
}
|
|
1012
1005
|
/**
|
|
1013
1006
|
* The bot's resolved emojis, keyed by {@link EmojiMap}. Filled by {@link EmojiInjector} during startup.
|
|
1014
|
-
*
|
|
1007
|
+
* Throws if you read a key the injector did not fill.
|
|
1015
1008
|
*/
|
|
1016
1009
|
const Emojis = guardedAccessor("Emojis", emojiStorage);
|
|
1017
1010
|
var EmojiInjector = class {
|
|
1018
1011
|
core;
|
|
1019
|
-
applicationId;
|
|
1020
1012
|
logger = new Logger("Emojis", { channel: "bot" });
|
|
1021
|
-
constructor(core
|
|
1013
|
+
constructor(core) {
|
|
1022
1014
|
this.core = core;
|
|
1023
|
-
this.applicationId = applicationId;
|
|
1024
1015
|
}
|
|
1025
1016
|
async init() {
|
|
1026
1017
|
clearStore(emojiStorage);
|
|
@@ -1042,7 +1033,7 @@ var EmojiInjector = class {
|
|
|
1042
1033
|
}
|
|
1043
1034
|
async applicationEmojis(failures) {
|
|
1044
1035
|
try {
|
|
1045
|
-
const appId =
|
|
1036
|
+
const appId = this.core.applicationId;
|
|
1046
1037
|
return byName((await this.core.rest.get(Routes.applicationEmojis(appId))).items);
|
|
1047
1038
|
} catch (error) {
|
|
1048
1039
|
failures.push(` - the application emojis could not be read (${reasonOf(error)})`);
|
|
@@ -1412,8 +1403,8 @@ function Gated(...gates) {
|
|
|
1412
1403
|
|
|
1413
1404
|
//#endregion
|
|
1414
1405
|
//#region src/version.ts
|
|
1415
|
-
const version = "0.4.
|
|
1406
|
+
const version = "0.4.1";
|
|
1416
1407
|
|
|
1417
1408
|
//#endregion
|
|
1418
|
-
export { RepliableHandler$1 as
|
|
1419
|
-
//# sourceMappingURL=edge.index-
|
|
1409
|
+
export { RepliableHandler$1 as S, createSeedcord as _, Paginator as a, InteractionHandler as b, SelectMenuHandler as c, ComponentHandler as d, MessageContextMenuHandler as f, Emojis as g, EmojiInjector as h, CursorSource$1 as i, ModalHandler as l, SlashHandler as m, Gated as n, Subscriber$1 as o, UserContextMenuHandler as p, ArraySource$1 as r, WebhookLog$1 as s, version as t, ButtonHandler as u, buildEngine as v, AutocompleteHandler as x, buildRouteMaps as y };
|
|
1410
|
+
//# sourceMappingURL=edge.index-DK-Y14ge.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge.index-DK-Y14ge.mjs","names":["RepliableHandler","CoreRepliableHandler","RepliableHandler","RepliableHandler","Subscriber","CoreSubscriber","WebhookLog","CoreWebhookLog","ArraySource","BaseArraySource","CursorSource","BaseCursorSource"],"sources":["../src/reply/ReplySender.ts","../src/api.ts","../src/handlers/RepliableHandler.ts","../src/gates/context.ts","../src/dispatch/slashRouteOf.ts","../src/dispatch/reportFault.ts","../src/dispatch/dispatchInteraction.ts","../src/dispatch/registerSubscribers.ts","../src/inputs/HttpSlashOptions.ts","../src/handlers/interaction/AutocompleteHandler.ts","../src/handlers/defaults/UnhandledAutocomplete.ts","../src/handlers/interaction/InteractionHandler.ts","../src/handlers/defaults/UnhandledRepliable.ts","../src/dispatch/resolve.ts","../src/receiver/Ed25519Verifier.ts","../src/receiver/ReplayGuard.ts","../src/engine.ts","../src/createSeedcord.ts","../src/emojis/EmojiInjector.ts","../src/handlers/interaction/SlashHandler.ts","../src/handlers/interaction/ContextMenuHandler.ts","../src/handlers/interaction/components/ComponentHandler.ts","../src/handlers/interaction/components/ButtonHandler.ts","../src/handlers/interaction/components/ModalHandler.ts","../src/handlers/interaction/components/SelectMenuHandler.ts","../src/subscribers/index.ts","../src/pagination/Paginator.ts","../src/pagination/sources.ts","../src/gates/Gated.ts","../src/version.ts"],"sourcesContent":["import { BaseReplySender, deferFlags, sendFlags } from '@seedcord/core/internal';\nimport { InteractionResponseType, MessageFlags, Routes } from 'discord-api-types/v10';\n\nimport type { REST, RawFile } from '@discordjs/rest';\nimport type { Bus } from '@seedcord/core';\nimport type { SerializedReply } from '@seedcord/core/internal';\nimport type { DeferOpts, ReplyResponse, SendOpts, TypedOmit } from '@seedcord/types';\nimport type { APIMessage, APIModalInteractionResponseCallbackData } from 'discord-api-types/v10';\n\n/** The interaction payload fields the sender writes its wire from. */\nexport interface InteractionRef {\n readonly application_id: string;\n readonly id: string;\n readonly token: string;\n}\n\n/** The created message an http reply resolves to (the http `SentMessage` lens). */\nexport type SentMessage = APIMessage;\n\n// the framework's allowedMentions keys are camelCase, and the wire reads replied_user\ntype WireAllowedMentions = TypedOmit<NonNullable<ReplyResponse['allowedMentions']>, 'repliedUser'> & {\n replied_user?: boolean;\n};\n\ninterface WireAttachment {\n id: number;\n filename: string;\n description?: string;\n title?: string;\n}\n\ninterface MessageBody {\n flags: number;\n components: SerializedReply['components'];\n allowed_mentions?: WireAllowedMentions;\n attachments?: WireAttachment[];\n}\n\nfunction wireAllowedMentions(mentions: NonNullable<ReplyResponse['allowedMentions']>): WireAllowedMentions {\n const { repliedUser, ...wire } = mentions;\n return { ...wire, ...(repliedUser !== undefined && { replied_user: repliedUser }) };\n}\n\n// RawFile has no description field\nfunction wireAttachments(files: SerializedReply['files']): WireAttachment[] | null {\n if (!files?.some((file) => file.description ?? file.title)) return null;\n return files.map((file, index) => ({\n id: index,\n filename: file.name,\n ...(file.description && { description: file.description }),\n ...(file.title && { title: file.title })\n }));\n}\n\nfunction callbackData(reply: SerializedReply, flags: number): MessageBody {\n const attachments = wireAttachments(reply.files);\n return {\n flags,\n components: reply.components,\n ...(reply.allowedMentions && { allowed_mentions: wireAllowedMentions(reply.allowedMentions) }),\n ...(attachments && { attachments })\n };\n}\n\n/**\n * Writes interaction responses over the Discord REST callback and webhook endpoints. Construction is\n * internal to the repliable handler bases and the dispatcher.\n */\nexport class ReplySender extends BaseReplySender<SentMessage> {\n public constructor(\n private readonly ref: InteractionRef,\n private readonly rest: REST,\n routeId: string,\n bus: Bus\n ) {\n super(routeId, { bus, interactionId: ref.id });\n }\n\n protected async writeReply(response: ReplyResponse | string, opts?: SendOpts): Promise<SentMessage | undefined> {\n const reply = this.serialize(response);\n const result = await this.callback(\n InteractionResponseType.ChannelMessageWithSource,\n callbackData(reply, sendFlags(opts)),\n reply.files\n );\n return this.createdMessage(result);\n }\n\n protected async writeDefer(opts?: DeferOpts): Promise<void> {\n await this.callback(InteractionResponseType.DeferredChannelMessageWithSource, { flags: deferFlags(opts) });\n }\n\n protected async writeDeferUpdate(): Promise<void> {\n await this.callback(InteractionResponseType.DeferredMessageUpdate);\n }\n\n protected async writeUpdate(response: ReplyResponse | string): Promise<SentMessage | undefined> {\n const reply = this.serialize(response);\n const result = await this.callback(\n InteractionResponseType.UpdateMessage,\n callbackData(reply, MessageFlags.IsComponentsV2),\n reply.files\n );\n return this.createdMessage(result);\n }\n\n protected async writeFollowUp(response: ReplyResponse | string, opts?: SendOpts): Promise<SentMessage> {\n const reply = this.serialize(response);\n // discord returns the created message for interaction-token followups either way. wait=true pins\n // that contract and matches the djs webhook client\n const result = await this.rest.post(Routes.webhook(this.ref.application_id, this.ref.token), {\n body: callbackData(reply, sendFlags(opts)),\n query: new URLSearchParams({ wait: 'true' }),\n ...(reply.files && { files: this.rawFiles(reply.files) })\n });\n // justified: a webhook POST returns the created message\n return result as SentMessage;\n }\n\n protected writeEditOriginal(response: ReplyResponse | string): Promise<SentMessage> {\n return this.editMessage(\n Routes.webhookMessage(this.ref.application_id, this.ref.token),\n this.serialize(response)\n );\n }\n\n protected writeEditTarget(targetId: string, response: ReplyResponse | string): Promise<SentMessage> {\n const route = Routes.webhookMessage(this.ref.application_id, this.ref.token, targetId);\n return this.editMessage(route, this.serialize(response));\n }\n\n protected async writeDeleteOriginal(): Promise<void> {\n await this.rest.delete(Routes.webhookMessage(this.ref.application_id, this.ref.token));\n }\n\n protected async writeDeleteTarget(targetId: string): Promise<void> {\n await this.rest.delete(Routes.webhookMessage(this.ref.application_id, this.ref.token, targetId));\n }\n\n protected async writeModal(data: APIModalInteractionResponseCallbackData): Promise<void> {\n await this.callback(InteractionResponseType.Modal, data);\n }\n\n private async callback(\n type: InteractionResponseType,\n data?: unknown,\n files?: SerializedReply['files']\n ): Promise<unknown> {\n const withResponse =\n type === InteractionResponseType.ChannelMessageWithSource || type === InteractionResponseType.UpdateMessage;\n return await this.rest.post(Routes.interactionCallback(this.ref.id, this.ref.token), {\n body: { type, ...(data !== undefined && { data }) },\n ...(withResponse && { query: new URLSearchParams({ with_response: 'true' }) }),\n ...(files && { files: this.rawFiles(files) })\n });\n }\n\n private async editMessage(route: `/${string}`, reply: SerializedReply): Promise<SentMessage> {\n const result = await this.rest.patch(route, {\n body: callbackData(reply, MessageFlags.IsComponentsV2),\n ...(reply.files && { files: this.rawFiles(reply.files) })\n });\n // justified: a webhook message PATCH returns the edited message\n return result as SentMessage;\n }\n\n private rawFiles(files: NonNullable<SerializedReply['files']>): RawFile[] {\n return files.map((file) => ({ name: file.name, data: file.data }));\n }\n\n private createdMessage(result: unknown): SentMessage | undefined {\n // justified: the with_response callback wire shape\n return (result as { resource?: { message?: SentMessage } }).resource?.message;\n }\n}\n","import { API } from '@discordjs/core/http-only';\n\nimport type { REST } from '@discordjs/rest';\n\nconst apis = new WeakMap<REST, API>();\n\nexport function apiFor(rest: REST): API {\n let api = apis.get(rest);\n if (!api) {\n api = new API(rest);\n apis.set(rest, api);\n }\n return api;\n}\n","import { RepliableHandler as CoreRepliableHandler } from '@seedcord/core';\n\nimport { ReplySender } from '#reply/ReplySender';\nimport { apiFor } from '#src/api';\n\nimport type { Core } from '#interfaces/Core';\nimport type { SentMessage } from '#reply/ReplySender';\nimport type { Repliables } from './interactionTypes';\nimport type { API } from '@discordjs/core/http-only';\n\n/**\n * Shared base the repliable HTTP interaction handlers extend.\n *\n * Not a public entry point. Extend {@link SlashHandler}, {@link ButtonHandler}, {@link ModalHandler},\n * {@link SelectMenuHandler}, or {@link ContextMenuHandler} instead. This class binds the core reply base\n * to the http sender.\n *\n * @typeParam Event - The repliable interaction type this handler processes\n */\nexport abstract class RepliableHandler<Event extends Repliables> extends CoreRepliableHandler<\n Event,\n Core,\n SentMessage,\n never,\n ReplySender\n> {\n // core's base keeps event protected, and Paginator.start reads it from outside the class\n /** @internal */\n public getEvent(): Event {\n return this.event;\n }\n\n protected buildSender(event: Event, core: Core, routeId: string): ReplySender {\n const ref = { application_id: event.application_id, id: event.id, token: event.token };\n return new ReplySender(ref, core.rest, routeId, core.bus);\n }\n\n /**\n * Typed Discord REST API (`@discordjs/core/http-only`) over `core.rest`, one instance per core.\n * The interaction callbacks on `api.interactions` bypass the reply surface. The\n * `no-raw-interaction-acks` rule flags them in handler classes.\n */\n protected get api(): API {\n return apiFor(this.core.rest);\n }\n}\n","import type { Core } from '#interfaces/Core';\nimport type { Repliables } from '#src/handlers/interactionTypes';\nimport type { InteractionGateContext } from './Gate';\nimport type { APIUser } from 'discord-api-types/v10';\n\n// autocomplete is skipped before gates run, so the payload here always carries a reply target\nexport function interactionGateContext(\n payload: Repliables,\n core: Core,\n routeId: string | null\n): InteractionGateContext {\n const member = payload.member ?? null;\n // the wrapper types leave member and user both optional. Discord sends member.user in a guild and the\n // top-level user in a DM\n const resolved = member?.user ?? payload.user;\n return {\n kind: 'interaction',\n core,\n interaction: payload,\n // justified: every repliable interaction Discord delivers carries a user, the wrapper types don't say so\n user: resolved as APIUser,\n member,\n userId: resolved?.id ?? null,\n guildId: payload.guild_id ?? null,\n channelId: payload.channel?.id ?? null,\n memberRoleIds: member?.roles ?? [],\n memberPermissions: member ? BigInt(member.permissions) : null,\n // app_permissions is present on every interaction Discord delivers, including DMs\n appPermissions: BigInt(payload.app_permissions),\n routeId\n };\n}\n","import { buildSlashRoute } from '@seedcord/utils/internal';\nimport { ApplicationCommandOptionType } from 'discord-api-types/v10';\n\nimport type {\n APIApplicationCommandAutocompleteInteraction,\n APIChatInputApplicationCommandInteraction\n} from 'discord-api-types/v10';\n\nexport type SlashLikeData = (\n APIChatInputApplicationCommandInteraction | APIApplicationCommandAutocompleteInteraction\n)['data'];\n\n// the raw-payload port of the gateway's slashRouteOf\n// both assemble the key through buildSlashRoute, so a dispatched interaction always matches its generated registry key\nexport function slashRouteOf(data: SlashLikeData): string {\n const first = data.options?.[0];\n if (first?.type === ApplicationCommandOptionType.SubcommandGroup) {\n return buildSlashRoute(data.name, first.options[0]?.name, first.name);\n }\n if (first?.type === ApplicationCommandOptionType.Subcommand) {\n return buildSlashRoute(data.name, first.name);\n }\n return data.name;\n}\n","import { Notice } from '@seedcord/core';\nimport { PublishDefault } from '@seedcord/core/internal';\nimport { ApplicationCommandType, ComponentType, InteractionType } from 'discord-api-types/v10';\n\nimport { slashRouteOf } from './slashRouteOf';\n\nimport type { ValidInteractionTypes } from '#handlers/interactionTypes';\nimport type { Core } from '#interfaces/Core';\nimport type { FaultSource, SubscriptionData } from '@seedcord/core';\nimport type { RenderContext } from '@seedcord/types';\n\ntype InteractionFaultSource = Extract<FaultSource, { kind: 'interaction' }>;\n\n// the raw-payload twin of gateway's extractErrorResponse\nexport function reportFault(\n error: Error,\n uuid: RenderContext['uuid'],\n routeId: string,\n payload: ValidInteractionTypes,\n core: Core\n): void {\n const source = interactionSource(payload);\n if (error instanceof Notice && source)\n core.bus[PublishDefault]('handledException', { denial: error, uuid, routeId, source });\n else core.bus[PublishDefault]('unknownException', { uuid, error, routeId, ...actors(payload), metadata: payload });\n}\n\n// the raw payload has guild_id and no guild name\nfunction actors(payload: ValidInteractionTypes): Pick<SubscriptionData<'unknownException'>, 'guild' | 'user'> {\n const user = payload.member?.user ?? payload.user;\n return { guild: undefined, user: user ? { id: user.id, username: user.username } : undefined };\n}\n\nfunction interactionSource(payload: ValidInteractionTypes): InteractionFaultSource | null {\n // discord sends member.user in a guild and user in a dm\n const user = payload.member?.user ?? payload.user;\n if (!user) return null;\n\n const common = {\n kind: 'interaction',\n userId: user.id,\n guildId: payload.guild_id ?? null,\n channelId: payload.channel?.id ?? null,\n interactionId: payload.id,\n raw: payload\n } as const;\n\n if (payload.type === InteractionType.ApplicationCommand) {\n const { data } = payload;\n const isSlash = data.type === ApplicationCommandType.ChatInput;\n return {\n ...common,\n interactionKind: isSlash ? 'slash' : 'context-menu',\n command: isSlash ? slashRouteOf(data) : data.name,\n customId: null\n };\n }\n if (payload.type === InteractionType.ModalSubmit) {\n return { ...common, interactionKind: 'modal', command: null, customId: payload.data.custom_id };\n }\n if (payload.type === InteractionType.MessageComponent) {\n const isButton = payload.data.component_type === ComponentType.Button;\n return {\n ...common,\n interactionKind: isButton ? 'button' : 'select',\n command: null,\n customId: payload.data.custom_id\n };\n }\n return null;\n}\n","import { DiscordAPIError, REST } from '@discordjs/rest';\nimport { BaseHandler, Bus, DispatchContext, Fault, Notice, Silence } from '@seedcord/core';\nimport {\n asError,\n outcomeFor,\n queuedMsFor,\n reportDispatch,\n reportedWrite,\n runHandlerGates,\n slowGateMonitor\n} from '@seedcord/core/internal';\nimport { paint } from '@seedcord/errors';\nimport { applicationIdFromToken } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\nimport { MemoryRateLimiter } from '@seedcord/rate-limiter';\nimport { InteractionResponseType, InteractionType, RESTJSONErrorCodes, Routes } from 'discord-api-types/v10';\n\nimport { RepliableHandler } from '#handlers/RepliableHandler';\nimport { ReplySender } from '#reply/ReplySender';\nimport { interactionGateContext } from '#src/gates/context';\n\nimport { reportFault } from './reportFault';\n\nimport type { ValidInteractionTypes } from '#handlers/interactionTypes';\nimport type { HttpConfig } from '#interfaces/Config';\nimport type { Core } from '#interfaces/Core';\nimport type { ResolvedRoute } from './resolve';\nimport type { DispatchOutcome } from '@seedcord/core';\nimport type { IRateLimiter, RenderContext, TypedOmit } from '@seedcord/types';\n\n// lazy, env binds after this module loads\nlet dispatchLogger: Logger | undefined;\nfunction logger(): Logger {\n dispatchLogger ??= new Logger('Dispatcher', { channel: 'interactions' });\n return dispatchLogger;\n}\n\ninterface HttpHandler {\n execute(): Promise<void>;\n}\n\ntype HandlerCtor = new (event: ValidInteractionTypes, core: Core, dispatch?: DispatchContext) => HttpHandler;\n\ntype CoreDraft = TypedOmit<Core, 'bus'> & { bus: Bus };\n\nexport function createCore(config: HttpConfig, token: string): Core {\n const rateLimiter: IRateLimiter = config.store ?? new MemoryRateLimiter();\n // justified: bus completes the shape on the next line, and the Bus reads core at dispatch\n const draft = {\n config,\n rateLimiter,\n rest: new REST().setToken(token),\n get applicationId(): string {\n return applicationIdFromToken(token);\n }\n } as CoreDraft;\n draft.bus = new Bus(draft);\n return draft;\n}\n\nfunction isHandlerCtor(value: unknown): value is HandlerCtor {\n return typeof value === 'function' && value.prototype instanceof BaseHandler;\n}\n\ninterface FaultScope {\n readonly core: Core;\n readonly payload: ValidInteractionTypes;\n readonly routeId: string;\n // null on autocomplete, whose only legal refusal is an empty type 8\n readonly sender: ReplySender | null;\n}\n\n// each one means the interaction is already gone\nconst HARMLESS_API_CODES: ReadonlySet<number | string> = new Set([\n RESTJSONErrorCodes.UnknownInteraction,\n RESTJSONErrorCodes.InteractionHasAlreadyBeenAcknowledged,\n RESTJSONErrorCodes.UnknownMessage\n]);\n\nasync function sendGuarded(routeId: string, send: () => Promise<unknown>): Promise<void> {\n try {\n await send();\n } catch (error) {\n if (error instanceof DiscordAPIError && HARMLESS_API_CODES.has(error.code)) {\n logger().debug(`boundary send hit harmless code ${paint.amber(String(error.code))}`);\n return;\n }\n logger().error(`boundary send failed for route ${paint.sky.bold(routeId)}`, error);\n }\n}\n\n// a message reply is illegal on autocomplete, so empty choices are the only way to clear the pending state\nasync function respondEmptyChoices(scope: FaultScope): Promise<void> {\n const telemetry = { bus: scope.core.bus, interactionId: scope.payload.id };\n await reportedWrite(telemetry, scope.routeId, 'respond', () =>\n scope.core.rest.post(Routes.interactionCallback(scope.payload.id, scope.payload.token), {\n body: { type: InteractionResponseType.ApplicationCommandAutocompleteResult, data: { choices: [] } }\n })\n );\n}\n\nfunction renderContext(core: Core, uuid: RenderContext['uuid']): RenderContext {\n const developerUsername = core.config.notifications?.developerUsername;\n return developerUsername === undefined ? { uuid } : { uuid, developerUsername };\n}\n\nasync function handleNotice(notice: Notice, uuid: RenderContext['uuid'], scope: FaultScope): Promise<void> {\n if (notice.report) {\n logger().error(`${notice.name}: ${paint.mute(uuid)}`, notice);\n reportFault(notice, uuid, scope.routeId, scope.payload, scope.core);\n }\n const { sender } = scope;\n if (!sender) {\n await sendGuarded(scope.routeId, () => respondEmptyChoices(scope));\n return;\n }\n const response = notice.render(renderContext(scope.core, uuid));\n await sendGuarded(scope.routeId, () => sender.send(response, { ephemeral: notice.ephemeral }));\n}\n\nasync function handleRawFault(error: Error, uuid: RenderContext['uuid'], scope: FaultScope): Promise<void> {\n const { core, sender } = scope;\n\n if (core.config.errors?.errorStack ?? false) logger().error(paint.mute(uuid), error);\n else logger().error(`${paint.mute(uuid)} | ${error.message}`);\n\n reportFault(error, uuid, scope.routeId, scope.payload, core);\n\n if (!sender) {\n await sendGuarded(scope.routeId, () => respondEmptyChoices(scope));\n return;\n }\n\n const override = core.config.errors?.defaultError;\n const card = override ? new override(uuid) : new Fault();\n const response = card.render(renderContext(core, uuid));\n await sendGuarded(scope.routeId, () => sender.send(response, { ephemeral: true }));\n}\n\nasync function handleFault(caught: unknown, scope: FaultScope): Promise<void> {\n if (caught instanceof Silence) {\n if (caught.reason !== undefined && (scope.core.config.errors?.logSilences ?? true)) {\n logger().debug(`Silence: ${caught.reason}`);\n }\n return;\n }\n\n const uuid = crypto.randomUUID();\n if (caught instanceof Notice) {\n await handleNotice(caught, uuid, scope);\n return;\n }\n\n const error = asError(caught);\n\n const ignore = new Set<number | string>(scope.core.config.errors?.ignoreApiCodes ?? []);\n if (error instanceof DiscordAPIError && ignore.has(error.code)) {\n logger().debug(`swallowed api code ${paint.amber(String(error.code))}`);\n return;\n }\n\n await handleRawFault(error, uuid, scope);\n}\n\ninterface DispatchArgs {\n readonly match: ResolvedRoute;\n readonly payload: ValidInteractionTypes;\n readonly core: Core;\n}\n\nfunction unhandledRouteId(match: ResolvedRoute): string {\n if (match.routeId) return match.routeId;\n // an empty key means a customId seedcord never minted, since a minted routeKey always outlives its 3-char hash\n const key = match.attemptedKey ?? '';\n return `${match.kind}:${key.length > 0 ? key : 'unrouted'}`;\n}\n\n// nothing is acked yet here, so a fresh sender can reply the card\nfunction freshScope(match: ResolvedRoute, payload: ValidInteractionTypes, core: Core): FaultScope {\n const ref = { application_id: payload.application_id, id: payload.id, token: payload.token };\n const routeId = unhandledRouteId(match);\n return {\n core,\n payload,\n routeId,\n sender: match.kind === 'autocomplete' ? null : new ReplySender(ref, core.rest, routeId, core.bus)\n };\n}\n\nfunction dispatchReporter(\n match: ResolvedRoute,\n payload: ValidInteractionTypes,\n core: Core\n): (outcome: DispatchOutcome) => void {\n const startedAt = performance.now();\n // reading this at publish time would count the handler run into the queue too\n const queuedMs = queuedMsFor(payload.id);\n return (outcome) => {\n reportDispatch(core.bus, {\n routeId: unhandledRouteId(match),\n interactionId: payload.id,\n kind: match.kind,\n outcome,\n fallback: match.routeId === null,\n startedAt,\n queuedMs\n });\n };\n}\n\nasync function answer(\n caught: unknown,\n scope: FaultScope,\n report: (outcome: DispatchOutcome) => void,\n outcome: DispatchOutcome = outcomeFor(caught)\n): Promise<void> {\n try {\n await handleFault(caught, scope);\n } finally {\n report(outcome);\n }\n}\n\nasync function loadHandlerCtor(\n match: ResolvedRoute,\n payload: ValidInteractionTypes,\n core: Core,\n report: (outcome: DispatchOutcome) => void\n): Promise<HandlerCtor | null> {\n const routeId = unhandledRouteId(match);\n let exported: unknown;\n try {\n exported = await match.load();\n } catch (caught) {\n logger().error(`Route ${paint.sky.bold(routeId)} failed to load its handler.`, caught);\n await answer(caught, freshScope(match, payload, core), report, 'failed');\n return null;\n }\n\n if (isHandlerCtor(exported)) return exported;\n\n logger().error(`Route ${paint.sky.bold(routeId)} loaded an export that is not a handler class.`);\n const wrong = new Error(`route ${routeId} loaded an export that is not a handler class`);\n await answer(wrong, freshScope(match, payload, core), report, 'failed');\n return null;\n}\n\n// a null return means the refusal is already sent\nexport async function dispatchInteraction(args: DispatchArgs): Promise<(() => Promise<void>) | null> {\n const { match, payload, core } = args;\n const report = dispatchReporter(match, payload, core);\n\n const ctor = await loadHandlerCtor(match, payload, core, report);\n if (!ctor) return null;\n\n const routeId = unhandledRouteId(match);\n logger().debug(`Processing ${paint.sky.bold(routeId)} with ${paint.mute(ctor.name)}`);\n\n const dispatch = new DispatchContext(routeId);\n let handler: HttpHandler;\n try {\n handler = new ctor(payload, core, dispatch);\n } catch (caught) {\n await answer(caught, freshScope(match, payload, core), report);\n return null;\n }\n const scope: FaultScope = {\n core,\n payload,\n routeId,\n sender: handler instanceof RepliableHandler ? handler.getSender() : null\n };\n\n const refusal = await gateRefusal(ctor, match, payload, core);\n if (refusal) {\n await answer(refusal.caught, scope, report);\n return null;\n }\n\n return async () => {\n try {\n await handler.execute();\n report('handled');\n } catch (caught) {\n await answer(caught, scope, report);\n }\n };\n}\n\n// autocomplete has no reply target. @Gated rejects it at compile time and this is the runtime backstop\nasync function gateRefusal(\n ctor: HandlerCtor,\n match: ResolvedRoute,\n payload: ValidInteractionTypes,\n core: Core\n): Promise<{ caught: unknown } | null> {\n // match.kind comes from payload.type in the router. the second clause narrows the union to\n // Repliables for interactionGateContext\n if (match.kind === 'autocomplete' || payload.type === InteractionType.ApplicationCommandAutocomplete) return null;\n const monitor = slowGateMonitor();\n try {\n await runHandlerGates(\n ctor,\n interactionGateContext(payload, core, match.routeId),\n match.routeId ?? undefined,\n monitor?.observe\n );\n return null;\n } catch (caught) {\n return { caught };\n } finally {\n // a refusing gate spent budget too\n monitor?.report(match.routeId);\n }\n}\n","import { Subscriber } from '@seedcord/core';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport type { RouteManifest } from '#src/manifest/RouteManifest';\nimport type { Bus } from '@seedcord/core';\nimport type { StoredSubscriberCtor, SubscriberRegistration } from '@seedcord/core/internal';\n\n// an idle isolate that never publishes pays nothing for this\nexport function registerSubscribers(bus: Bus, manifest: RouteManifest): void {\n for (const row of manifest.subscriberRoutes) {\n bus.register({\n keys: row.keys,\n frequency: row.frequency,\n resolve: async () => {\n const keys = row.keys.join(', ');\n let module: Awaited<ReturnType<typeof row.load>>;\n try {\n module = await row.load();\n } catch (caught) {\n throw new SeedcordError(SeedcordErrorCode.RouteModuleLoadFailed, [keys, row.from], {\n cause: caught\n });\n }\n if (!Object.hasOwn(module, row.exportName)) {\n throw new SeedcordError(SeedcordErrorCode.InteractionRouteExportMissing, [\n keys,\n row.exportName,\n row.from\n ]);\n }\n\n // a hand-authored manifest reaches here too, so the check cannot rest on codegen\n const exported = module[row.exportName];\n if (typeof exported !== 'function' || !(exported.prototype instanceof Subscriber)) {\n throw new SeedcordError(SeedcordErrorCode.SubscriberRouteNotASubscriber, [\n keys,\n row.exportName,\n row.from\n ]);\n }\n // justified: the two checks above prove it is a Subscriber subclass\n return exported as StoredSubscriberCtor;\n }\n } satisfies SubscriberRegistration);\n }\n}\n","import { ApplicationCommandOptionType } from 'discord-api-types/v10';\n\nimport type { SlashLikeData } from '#src/dispatch/slashRouteOf';\nimport type {\n APIApplicationCommandInteractionDataBasicOption,\n APIApplicationCommandInteractionDataOption,\n APIAttachment,\n APIInteractionDataResolved,\n APIInteractionDataResolvedChannel,\n APIInteractionDataResolvedGuildMember,\n APIRole,\n APIUser,\n InteractionType\n} from 'discord-api-types/v10';\n\ntype AnyOption = APIApplicationCommandInteractionDataOption<InteractionType>;\ntype Leaf = APIApplicationCommandInteractionDataBasicOption<InteractionType>;\n\ninterface FocusedOption {\n name: string;\n value: string;\n}\n\n// the raw-payload option reader for the typed SlashOptions/AutocompleteOptions views.\nexport class HttpSlashOptions {\n private readonly leaves = new Map<string, Leaf>();\n private readonly resolved: APIInteractionDataResolved | undefined;\n\n constructor(data: SlashLikeData) {\n this.resolved = data.resolved;\n let options: readonly AnyOption[] = data.options ?? [];\n const first = options[0];\n if (first?.type === ApplicationCommandOptionType.SubcommandGroup) options = first.options;\n const second = options[0];\n if (second?.type === ApplicationCommandOptionType.Subcommand) options = second.options ?? [];\n for (const option of options) {\n if (\n option.type !== ApplicationCommandOptionType.Subcommand &&\n option.type !== ApplicationCommandOptionType.SubcommandGroup\n ) {\n this.leaves.set(option.name, option);\n }\n }\n }\n\n getString(name: string): string | null {\n const leaf = this.leaves.get(name);\n return leaf?.type === ApplicationCommandOptionType.String ? leaf.value : null;\n }\n\n getInteger(name: string): number | null {\n const leaf = this.leaves.get(name);\n return leaf?.type === ApplicationCommandOptionType.Integer ? numeric(leaf.value) : null;\n }\n\n getNumber(name: string): number | null {\n const leaf = this.leaves.get(name);\n return leaf?.type === ApplicationCommandOptionType.Number ? numeric(leaf.value) : null;\n }\n\n getBoolean(name: string): boolean | null {\n const leaf = this.leaves.get(name);\n return leaf?.type === ApplicationCommandOptionType.Boolean ? leaf.value : null;\n }\n\n getUser(name: string): APIUser | null {\n const leaf = this.leaves.get(name);\n if (leaf?.type !== ApplicationCommandOptionType.User) return null;\n return this.resolved?.users?.[leaf.value] ?? null;\n }\n\n getMember(name: string): APIInteractionDataResolvedGuildMember | null {\n const leaf = this.leaves.get(name);\n if (leaf?.type !== ApplicationCommandOptionType.User) return null;\n return this.resolved?.members?.[leaf.value] ?? null;\n }\n\n getChannel(name: string): APIInteractionDataResolvedChannel | null {\n const leaf = this.leaves.get(name);\n if (leaf?.type !== ApplicationCommandOptionType.Channel) return null;\n return this.resolved?.channels?.[leaf.value] ?? null;\n }\n\n getRole(name: string): APIRole | null {\n const leaf = this.leaves.get(name);\n if (leaf?.type !== ApplicationCommandOptionType.Role) return null;\n return this.resolved?.roles?.[leaf.value] ?? null;\n }\n\n getAttachment(name: string): APIAttachment | null {\n const leaf = this.leaves.get(name);\n if (leaf?.type !== ApplicationCommandOptionType.Attachment) return null;\n return this.resolved?.attachments?.[leaf.value] ?? null;\n }\n\n getMentionable(name: string): APIUser | APIInteractionDataResolvedGuildMember | APIRole | null {\n const leaf = this.leaves.get(name);\n if (leaf?.type !== ApplicationCommandOptionType.Mentionable) return null;\n const resolved = this.resolved;\n return (\n resolved?.members?.[leaf.value] ?? resolved?.users?.[leaf.value] ?? resolved?.roles?.[leaf.value] ?? null\n );\n }\n\n getFocused(): FocusedOption | null {\n for (const leaf of this.leaves.values()) {\n if ('focused' in leaf && leaf.focused === true) return { name: leaf.name, value: String(leaf.value) };\n }\n return null;\n }\n}\n\n// the autocomplete wire carries integer/number values as strings\nfunction numeric(value: number | string): number | null {\n if (typeof value === 'number') return value;\n if (value.trim() === '') return null;\n const parsed = Number(value);\n return Number.isNaN(parsed) ? null : parsed;\n}\n","import { BaseHandler } from '@seedcord/core';\nimport { AutocompleteRouteBrand, reportedWrite } from '@seedcord/core/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { InteractionResponseType, Routes } from 'discord-api-types/v10';\n\nimport { HttpSlashOptions } from '#inputs/HttpSlashOptions';\nimport { apiFor } from '#src/api';\nimport { slashRouteOf } from '#src/dispatch/slashRouteOf';\n\nimport type { Core } from '#interfaces/Core';\nimport type { API } from '@discordjs/core/http-only';\nimport type { AutocompleteOptions, DispatchContext, SlashRegistry } from '@seedcord/core';\nimport type { AutocompletableNames, ChoiceValueOf, EntryFor, FocusedField } from '@seedcord/core/internal';\nimport type {\n APIApplicationCommandOptionChoice,\n APIApplicationCommandAutocompleteInteraction\n} from 'discord-api-types/v10';\nimport type { Promisable } from 'type-fest';\n\ntype FocusedArms<Route extends keyof SlashRegistry, Ret> = {\n [Name in AutocompletableNames<Route>]: (\n value: string,\n respond: (\n choices: readonly APIApplicationCommandOptionChoice<ChoiceValueOf<EntryFor<Route, Name>>>[]\n ) => Promise<void>\n ) => Promisable<Ret>;\n};\n\n/**\n * Base class for an autocomplete handler on the HTTP transport.\n *\n * Pass the command route(s) from the generated registry as the generic. Branch on the focused field with\n * `this.match`, read already-entered sibling options with `this.options`, and find which command fired\n * with `this.route`.\n *\n * @typeParam Route - One or more route keys from {@link SlashRegistry}, e.g. `'search'`.\n */\nexport abstract class AutocompleteHandler<Route extends keyof SlashRegistry> extends BaseHandler<\n APIApplicationCommandAutocompleteInteraction,\n Core\n> {\n // keep this ctor. it gives typeof AutocompleteHandler a public construct signature the dispatcher\n // needs, and dropping it (inheriting BaseHandler's protected ctor) collapses the handler ctor type to never.\n constructor(event: APIApplicationCommandAutocompleteInteraction, core: Core, dispatch?: DispatchContext) {\n super(event, core, dispatch, 'interactions');\n }\n\n // phantom, never set at runtime.\n /** @internal */\n declare readonly [AutocompleteRouteBrand]?: Route;\n\n /**\n * Typed Discord REST API (`@discordjs/core/http-only`) over `core.rest`, one instance per core.\n */\n protected get api(): API {\n return apiFor(this.core.rest);\n }\n\n private resolver?: HttpSlashOptions;\n\n private get reader(): HttpSlashOptions {\n this.resolver ??= new HttpSlashOptions(this.event.data);\n return this.resolver;\n }\n\n /**\n * The focused option for this interaction. `value` is always the raw partial string the user is typing,\n * even for an integer or number option, so coerce it yourself when you need a number.\n */\n protected get focused(): FocusedField<Route> {\n const focused = this.reader.getFocused();\n if (!focused) throw new SeedcordError(SeedcordErrorCode.AutocompleteNoFocusedOption);\n // the registry constrains the names. a focused field outside it has no arm\n return focused as FocusedField<Route>;\n }\n\n /**\n * Run the arm for the focused field. An autocomplete always dispatches by which field is focused.\n * Match is how you read it.\n *\n * Provide one arm per autocompletable field across the registered commands, keyed by field name. Each\n * arm receives the focused partial value and a `respond` typed to that field's choice value. The\n * arms must cover every autocompletable field, a missing field or an unknown key is a compile error. A\n * focused field with no arm, only reachable from a stale-deployed command, throws at runtime.\n *\n * @param arms - One handler per autocompletable field, keyed by field name.\n * @returns The result of the arm that ran.\n */\n protected async match<Ret>(arms: FocusedArms<Route, Ret>): Promise<Ret> {\n const { name, value } = this.focused;\n const respond = (choices: readonly APIApplicationCommandOptionChoice[]): Promise<void> => this.respond(choices);\n // justified: FocusedArms is keyed by field literals, the Record cast indexes it with the runtime field name.\n type Arm = (\n value: string,\n respond: (choices: readonly APIApplicationCommandOptionChoice[]) => Promise<void>\n ) => Promisable<Ret>;\n const arm = Object.hasOwn(arms, name) ? (arms as Record<string, Arm>)[name] : undefined;\n if (!arm) throw new SeedcordError(SeedcordErrorCode.AutocompleteMatchArmMissing, [name]);\n return await arm(value, respond);\n }\n\n /** Send autocomplete suggestions, callback type 8. Prefer {@link match}, which restricts each field's choices to its declared type. */\n protected async respond(choices: readonly APIApplicationCommandOptionChoice[]): Promise<void> {\n // a hand-built handler outside a dispatch carries no route id\n const routeId = this.dispatch?.routeId ?? 'autocomplete';\n await reportedWrite({ bus: this.core.bus, interactionId: this.event.id }, routeId, 'respond', () =>\n this.core.rest.post(Routes.interactionCallback(this.event.id, this.event.token), {\n body: { type: InteractionResponseType.ApplicationCommandAutocompleteResult, data: { choices } }\n })\n );\n }\n\n /** The firing command route, for a field whose completion differs per registered command. */\n protected get route(): Route {\n return slashRouteOf(this.event.data) as Route;\n }\n\n /**\n * The already-entered options on this command, restricted to the kinds Discord resolves during autocomplete\n * (string, integer, number, boolean) with every read nullable, since a sibling is partial while the user\n * types the focused field. The focused field is on `this.focused`.\n */\n protected get options(): AutocompleteOptions<Route> {\n // AutocompleteOptions is just the registry-typed view over the getters the reader already exposes\n return this.reader as AutocompleteOptions<Route>;\n }\n}\n","import { AutocompleteHandler } from '#handlers/interaction/AutocompleteHandler';\n\n// dispatch falls back here when no manifest row matches\n// discord requires empty choices here, which clears the pending autocomplete state\nexport class UnhandledAutocomplete extends AutocompleteHandler<never> {\n async execute(): Promise<void> {\n await this.respond([]);\n }\n}\n","import { RepliableHandler } from '#handlers/RepliableHandler';\n\nimport type { Repliables } from '#handlers/interactionTypes';\nimport type { Core } from '#interfaces/Core';\nimport type { DispatchContext } from '@seedcord/core';\nimport type { ModalLike } from '@seedcord/core/internal';\nimport type { APIModalSubmitInteraction } from 'discord-api-types/v10';\n\n/**\n * Shared base the repliable interaction handlers extend.\n *\n * Not a public entry point. Extend {@link SlashHandler}, {@link ContextMenuHandler}, {@link ButtonHandler},\n * {@link SelectMenuHandler}, or {@link ModalHandler} instead. This class adds `showModal` on top of the\n * reply members.\n *\n * @typeParam Event - The repliable interaction type this handler processes\n */\nexport abstract class InteractionHandler<Event extends Repliables> extends RepliableHandler<Event> {\n // the dispatcher needs a public construct signature, but RepliableHandler's ctor is protected\n constructor(event: Event, core: Core, dispatch?: DispatchContext) {\n super(event, core, dispatch);\n }\n\n /**\n * Open a modal. Must be the initial response to this interaction. The modal kind rejects this call at\n * compile time (Discord forbids a modal in response to a modal).\n */\n protected showModal(\n this: InteractionHandler<Exclude<Repliables, APIModalSubmitInteraction>>,\n modal: ModalLike\n ): Promise<void> {\n return this.sender.showModal(modal);\n }\n}\n","import { InteractionHandler } from '#handlers/interaction/InteractionHandler';\n\nimport type { Repliables } from '#handlers/interactionTypes';\n\n// dispatch falls back here when no manifest row matches\n// a reply failure here reaches the dispatch fault boundary directly\nexport class UnhandledRepliable extends InteractionHandler<Repliables> {\n async execute(): Promise<void> {\n await this.reply('Feature not implemented yet.');\n }\n}\n","import { prefixOf, type InteractionRoutes } from '@seedcord/core/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { ApplicationCommandType, ComponentType, InteractionType } from 'discord-api-types/v10';\n\nimport { UnhandledAutocomplete } from '#handlers/defaults/UnhandledAutocomplete';\nimport { UnhandledRepliable } from '#handlers/defaults/UnhandledRepliable';\n\nimport { slashRouteOf } from './slashRouteOf';\n\nimport type { ComponentRoute, RouteManifest, RouteModule } from '#src/manifest/RouteManifest';\nimport type { APIInteraction } from 'discord-api-types/v10';\n\ntype ResolvedKind = `${InteractionRoutes}`;\n\n/** A manifest row matched to an incoming interaction, keyed the way the gateway dispatcher keys. */\nexport interface ResolvedRoute {\n readonly kind: ResolvedKind;\n /**\n * The stable dispatch id, `kind:key` (`slash:ban`), the shape core's `routeIdOf` builds. Null for the\n * unhandled default, which matches no row.\n */\n readonly routeId: string | null;\n /** The key the router looked up. */\n readonly attemptedKey?: string;\n /** Resolves the one export the row registers. */\n readonly load: () => Promise<unknown>;\n}\n\n/** @internal */\nexport type RouteMap = Map<string, ResolvedRoute>;\n\ntype ComponentMapKey = ComponentRoute['kind'];\n\nexport interface RouteMaps {\n readonly slash: RouteMap;\n readonly userContextMenu: RouteMap;\n readonly messageContextMenu: RouteMap;\n readonly autocomplete: RouteMap;\n readonly components: Readonly<Record<ComponentMapKey, RouteMap>>;\n}\n\n// the manifest names selects `xSelect`, the resolved kinds name them `xMenu`\ntype ResolvedKindOf<Kind extends ComponentMapKey> = Kind extends `${infer Base}Select` ? `${Base}Menu` : Kind;\n\nconst COMPONENT_KIND = {\n button: 'button',\n stringSelect: 'stringMenu',\n userSelect: 'userMenu',\n roleSelect: 'roleMenu',\n channelSelect: 'channelMenu',\n mentionableSelect: 'mentionableMenu',\n modal: 'modal'\n} satisfies { [Kind in ComponentMapKey]: ResolvedKindOf<Kind> & ResolvedKind };\n\ntype CommandMapKey = Exclude<keyof RouteMaps, 'components' | 'autocomplete'>;\n\nfunction commandKind(type: ApplicationCommandType): CommandMapKey | null {\n switch (type) {\n case ApplicationCommandType.ChatInput: {\n return 'slash';\n }\n case ApplicationCommandType.User: {\n return 'userContextMenu';\n }\n case ApplicationCommandType.Message: {\n return 'messageContextMenu';\n }\n default: {\n return null;\n }\n }\n}\n\nfunction componentMapKey(type: ComponentType): Exclude<ComponentMapKey, 'modal'> | null {\n switch (type) {\n case ComponentType.Button: {\n return 'button';\n }\n case ComponentType.StringSelect: {\n return 'stringSelect';\n }\n case ComponentType.UserSelect: {\n return 'userSelect';\n }\n case ComponentType.RoleSelect: {\n return 'roleSelect';\n }\n case ComponentType.ChannelSelect: {\n return 'channelSelect';\n }\n case ComponentType.MentionableSelect: {\n return 'mentionableSelect';\n }\n default: {\n return null;\n }\n }\n}\n\nfunction namedExport(routeId: string, row: RouteModule): () => Promise<unknown> {\n return async () => {\n let moduleExports: Awaited<ReturnType<typeof row.load>>;\n try {\n moduleExports = await row.load();\n } catch (caught) {\n throw new SeedcordError(SeedcordErrorCode.RouteModuleLoadFailed, [routeId, row.from], { cause: caught });\n }\n if (!Object.hasOwn(moduleExports, row.exportName)) {\n throw new SeedcordError(SeedcordErrorCode.InteractionRouteExportMissing, [\n routeId,\n row.exportName,\n row.from\n ]);\n }\n return moduleExports[row.exportName];\n };\n}\n\nfunction describeRow(row: RouteModule): string {\n return `${row.exportName} (${row.from})`;\n}\n\n/**\n * Builds the per-kind lookup maps.\n *\n * @throws A **SeedcordError** when two rows resolve to the same route.\n */\nexport function buildRouteMaps(manifest: RouteManifest): RouteMaps {\n const maps: RouteMaps = {\n slash: new Map(),\n userContextMenu: new Map(),\n messageContextMenu: new Map(),\n autocomplete: new Map(),\n components: {\n button: new Map(),\n stringSelect: new Map(),\n userSelect: new Map(),\n roleSelect: new Map(),\n channelSelect: new Map(),\n mentionableSelect: new Map(),\n modal: new Map()\n }\n };\n const owners = new Map<string, RouteModule>();\n\n function set(map: RouteMap, kind: ResolvedKind, key: string, row: RouteModule): void {\n const routeId = `${kind}:${key}`;\n const owner = owners.get(routeId);\n if (owner) {\n // a bare map.set would let a later row shadow an earlier one\n throw new SeedcordError(SeedcordErrorCode.InteractionDuplicateRoute, [\n routeId,\n describeRow(owner),\n describeRow(row)\n ]);\n }\n owners.set(routeId, row);\n map.set(key, { kind, routeId, load: namedExport(routeId, row) });\n }\n\n for (const row of manifest.commandRoutes) {\n const kind = commandKind(row.type);\n if (kind) set(maps[kind], kind, row.name, row);\n }\n for (const row of manifest.autocompleteRoutes) {\n set(maps.autocomplete, 'autocomplete', row.name, row);\n }\n for (const row of manifest.componentRoutes) {\n set(maps.components[row.kind], COMPONENT_KIND[row.kind], row.prefix, row);\n }\n return maps;\n}\n\n// dispatched through the normal pipeline like the gateway's unhandled default\nfunction unhandled(kind: ResolvedKind, attemptedKey: string): ResolvedRoute {\n return {\n kind,\n routeId: null,\n attemptedKey,\n load: () => Promise.resolve(kind === 'autocomplete' ? UnhandledAutocomplete : UnhandledRepliable)\n };\n}\n\n/**\n * Matches a verified non-PING interaction to its manifest row. A known kind with no row resolves to the\n * unhandled default, whose handler replies \"Feature not implemented yet.\" (empty choices on autocomplete).\n * Null is an unrecognized payload shape, which the engine acks with a 202 without dispatching.\n * Components and modals route by the stable customId prefix, so a wire whose layout hash drifted still\n * routes to its handler, where decode refuses with `StaleCustomId`.\n */\nexport function resolve(maps: RouteMaps, interaction: APIInteraction): ResolvedRoute | null {\n switch (interaction.type) {\n case InteractionType.ApplicationCommand: {\n if (interaction.data.type === ApplicationCommandType.ChatInput) {\n const route = slashRouteOf(interaction.data);\n return maps.slash.get(route) ?? unhandled('slash', route);\n }\n const kind = commandKind(interaction.data.type);\n return kind ? (maps[kind].get(interaction.data.name) ?? unhandled(kind, interaction.data.name)) : null;\n }\n case InteractionType.ApplicationCommandAutocomplete: {\n const route = slashRouteOf(interaction.data);\n return maps.autocomplete.get(route) ?? unhandled('autocomplete', route);\n }\n case InteractionType.MessageComponent: {\n const key = componentMapKey(interaction.data.component_type);\n if (!key) return null;\n const prefix = prefixOf(interaction.data.custom_id);\n return maps.components[key].get(prefix) ?? unhandled(COMPONENT_KIND[key], prefix);\n }\n case InteractionType.ModalSubmit: {\n const prefix = prefixOf(interaction.data.custom_id);\n return maps.components.modal.get(prefix) ?? unhandled('modal', prefix);\n }\n default: {\n return null;\n }\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nconst PUBLIC_KEY_BYTES = 32;\nconst SIGNATURE_BYTES = 64;\nconst HEX_RADIX = 16;\nconst HEX_PATTERN = /^[0-9a-f]+$/i;\n\nfunction hexToBytes(hex: string, byteLength: number): Uint8Array<ArrayBuffer> | null {\n if (hex.length !== byteLength * 2 || !HEX_PATTERN.test(hex)) return null;\n\n const bytes = new Uint8Array(byteLength);\n for (let index = 0; index < byteLength; index++) {\n bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), HEX_RADIX);\n }\n return bytes;\n}\n\nconst encoder = new TextEncoder();\n\nexport class Ed25519Verifier {\n private readonly key: Promise<CryptoKey>;\n\n constructor(publicKeyHex: string) {\n const publicKey = hexToBytes(publicKeyHex, PUBLIC_KEY_BYTES);\n if (!publicKey)\n throw new SeedcordError(SeedcordErrorCode.ConfigInvalidEnv, ['DISCORD_PUBLIC_KEY', '64 hex characters']);\n\n this.key = crypto.subtle.importKey('raw', publicKey, 'Ed25519', false, ['verify']);\n }\n\n async verify(signatureHex: string, timestamp: string, body: Uint8Array): Promise<boolean> {\n const signature = hexToBytes(signatureHex, SIGNATURE_BYTES);\n if (!signature) return false;\n\n const timestampBytes = encoder.encode(timestamp);\n const message = new Uint8Array(timestampBytes.length + body.length);\n message.set(timestampBytes, 0);\n message.set(body, timestampBytes.length);\n\n return crypto.subtle.verify('Ed25519', await this.key, signature, message);\n }\n}\n","const REPLAY_WINDOW_MS = 300_000; // compared in both directions, clock skew stays inside the window\nconst SWEEP_GAP_MS = 60_000;\nconst MS_PER_SECOND = 1000;\nconst TIMESTAMP_PATTERN = /^\\d+$/;\n\n// the seen map is per-isolate on edge, so a fresh isolate rejects a replay on freshness alone\nexport class ReplayGuard {\n private readonly seen = new Map<string, number>();\n private lastSweep = Date.now();\n\n // the parsed value feeds unseen(), which only ever sees timestamps this already accepted\n public fresh(timestampHeader: string): number | null {\n if (!TIMESTAMP_PATTERN.test(timestampHeader)) return null;\n const timestampMs = Number(timestampHeader) * MS_PER_SECOND;\n return Math.abs(Date.now() - timestampMs) <= REPLAY_WINDOW_MS ? timestampMs : null;\n }\n\n public unseen(signatureHex: string, timestampMs: number): boolean {\n const now = Date.now();\n this.maybeSweep(now);\n\n // >= because a timestamp at the exact stale edge is accepted and its entry expires at `now`\n const rejectUntil = this.seen.get(signatureHex);\n if (rejectUntil !== undefined && rejectUntil >= now) return false;\n\n // expires when the freshness check starts rejecting this timestamp\n this.seen.set(signatureHex, timestampMs + REPLAY_WINDOW_MS);\n return true;\n }\n\n // swept on access, a construction-time timer throws in workers global scope\n private maybeSweep(now: number): void {\n if (now - this.lastSweep < SWEEP_GAP_MS) return;\n this.lastSweep = now;\n // strict, the sweep runs before the dedup lookup and must keep an entry expiring exactly at `now`\n for (const [signature, rejectUntil] of this.seen) {\n if (rejectUntil < now) this.seen.delete(signature);\n }\n }\n}\n","import { asError, PublishDefault } from '@seedcord/core/internal';\nimport { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\nimport { InteractionResponseType, InteractionType } from 'discord-api-types/v10';\nimport { Converters, Envapter } from 'envapt';\n\nimport { dispatchInteraction } from './dispatch/dispatchInteraction';\nimport { resolve } from './dispatch/resolve';\nimport { Ed25519Verifier } from './receiver/Ed25519Verifier';\nimport { ReplayGuard } from './receiver/ReplayGuard';\n\nimport type { ValidInteractionTypes } from '#handlers/interactionTypes';\nimport type { Core } from '#interfaces/Core';\nimport type { RouteMaps } from './dispatch/resolve';\nimport type { APIInteraction } from 'discord-api-types/v10';\n\nconst SIGNATURE_HEADER = 'x-signature-ed25519';\nconst TIMESTAMP_HEADER = 'x-signature-timestamp';\n\nconst ACCEPTED = 202;\nconst BAD_REQUEST = 400;\nconst UNAUTHORIZED = 401;\nconst METHOD_NOT_ALLOWED = 405;\n\n// widened so the raw payload's number field compares without an unsafe enum comparison\nconst PING: number = InteractionType.Ping;\n\nconst decoder = new TextDecoder();\n\ntype Outcome = 'not-post' | 'unsigned' | 'stale' | 'bad-signature' | 'bad-body' | 'ping' | 'replay' | 'dispatched';\ntype Answer = readonly [Response, Outcome];\n\nfunction hasInteractionType(payload: unknown): payload is { type: number } {\n return typeof payload === 'object' && payload !== null && 'type' in payload && typeof payload.type === 'number';\n}\n\n/** Structural match for the Workers `ExecutionContext`. Pass it from the edge entry so post-202 work runs under `waitUntil`. */\nexport interface EngineContext {\n waitUntil(promise: Promise<unknown>): void;\n}\n\nexport interface EngineParts {\n readonly handle: (request: Request, ctx?: EngineContext) => Promise<Response>;\n // node paths only, drained at shutdown\n readonly inFlight: ReadonlySet<Promise<void>>;\n}\n\nexport function buildEngine(core: Core, maps: RouteMaps): EngineParts {\n if (!Envapter.has('DISCORD_PUBLIC_KEY'))\n throw new SeedcordError(SeedcordErrorCode.ConfigMissingEnv, ['DISCORD_PUBLIC_KEY']);\n const publicKey = Envapter.getRequired('DISCORD_PUBLIC_KEY', Converters.String);\n\n const verifier = new Ed25519Verifier(publicKey);\n const replays = new ReplayGuard();\n const inFlight = new Set<Promise<void>>();\n // eager because env binds before this factory runs\n const logger = new Logger('Engine', { channel: 'bot' });\n\n async function dispatchMatched(\n match: NonNullable<ReturnType<typeof resolve>>,\n payload: ValidInteractionTypes,\n ctx: EngineContext | undefined\n ): Promise<void> {\n const start = await dispatchInteraction({ match, payload, core });\n if (!start) return;\n\n // this catch is all that stands between a throw and an unhandled rejection\n const work = start().catch(rootFault);\n if (ctx) {\n ctx.waitUntil(work);\n } else {\n inFlight.add(work);\n void work.finally(() => inFlight.delete(work));\n }\n }\n\n function rootFault(caught: unknown): void {\n const error = asError(caught);\n logger.error('dispatch failed past the boundary', error);\n core.bus[PublishDefault]('unhandledInteractionError', { error });\n }\n\n const handle = async (request: Request, ctx?: EngineContext): Promise<Response> => {\n const [response, outcome] = await respond(request, ctx);\n logger.debug(`${paint.sky.bold(request.method)} ${outcome} ${paint.amber(String(response.status))}`);\n return response;\n };\n\n const respond = async (request: Request, ctx?: EngineContext): Promise<Answer> => {\n if (request.method !== 'POST') {\n return [new Response(null, { status: METHOD_NOT_ALLOWED, headers: { allow: 'POST' } }), 'not-post'];\n }\n\n const signature = request.headers.get(SIGNATURE_HEADER);\n const timestamp = request.headers.get(TIMESTAMP_HEADER);\n if (signature === null || timestamp === null) return [new Response(null, { status: UNAUTHORIZED }), 'unsigned'];\n\n const timestampMs = replays.fresh(timestamp);\n if (timestampMs === null) return [new Response(null, { status: UNAUTHORIZED }), 'stale'];\n\n const body = new Uint8Array(await request.arrayBuffer());\n if (!(await verifier.verify(signature, timestamp, body))) {\n return [new Response(null, { status: UNAUTHORIZED }), 'bad-signature'];\n }\n\n let payload: unknown;\n try {\n payload = JSON.parse(decoder.decode(body));\n } catch {\n return [new Response(null, { status: BAD_REQUEST }), 'bad-body'];\n }\n if (!hasInteractionType(payload)) return [new Response(null, { status: BAD_REQUEST }), 'bad-body'];\n\n // discord resends the same signed ping while verifying an endpoint, and a 401 there reads as a broken endpoint\n if (payload.type === PING) {\n return [Response.json({ type: InteractionResponseType.Pong }), 'ping'];\n }\n\n if (!replays.unseen(signature, timestampMs)) return [new Response(null, { status: UNAUTHORIZED }), 'replay'];\n\n try {\n // justified: the payload is signed by Discord, and the type field anchors the discriminated union\n core.bus[PublishDefault]('anyInteraction', { interaction: payload as APIInteraction });\n const match = resolve(maps, payload as APIInteraction);\n if (match) await dispatchMatched(match, payload as ValidInteractionTypes, ctx);\n } catch (caught) {\n // the 202 has to go out either way or discord drops the interaction\n rootFault(caught);\n }\n\n return [new Response(null, { status: ACCEPTED }), 'dispatched'];\n };\n\n return { handle, inFlight };\n}\n","import { validateDiscordToken } from '@seedcord/errors/internal';\nimport { Envapter } from 'envapt';\n\nimport { createCore } from './dispatch/dispatchInteraction';\nimport { registerSubscribers } from './dispatch/registerSubscribers';\nimport { buildRouteMaps } from './dispatch/resolve';\nimport { buildEngine } from './engine';\n\nimport type { HttpConfig } from '#interfaces/Config';\nimport type { RouteManifest } from '#src/manifest/RouteManifest';\nimport type { EngineContext } from './engine';\n\nexport type { EngineContext } from './engine';\n\n/**\n * Builds the HTTP-interactions engine, a `(request, ctx?) => Promise<Response>` handler.\n *\n * The handler verifies the Ed25519 signature over the raw request bytes, rejects stale or replayed\n * requests, answers a PING with an in-body PONG, and acks every other interaction with an empty 202.\n * The engine dispatches a matched interaction through the manifest before the 202 goes out. Gates run\n * first and the sender posts any refusal over the REST callback. `execute()` continues past the 202 under\n * `ctx.waitUntil` when the caller passes one (the edge entry), and an engine-held in-flight set tracks\n * it otherwise (node paths). It reads only the method, headers, and body. Mount it at any path.\n *\n * Reads `DISCORD_PUBLIC_KEY` and `DISCORD_BOT_TOKEN` from the environment through envapt and throws a\n * `SeedcordError` when either is missing or malformed.\n */\nexport function createSeedcord(\n config: HttpConfig,\n manifest: RouteManifest\n): (request: Request, ctx?: EngineContext) => Promise<Response> {\n const token = validateDiscordToken(Envapter.get('DISCORD_BOT_TOKEN'));\n const core = createCore(config, token);\n core.bus.registerDefaults();\n registerSubscribers(core.bus, manifest);\n return buildEngine(core, buildRouteMaps(manifest)).handle;\n}\n","import { ResolvedEmoji } from '@seedcord/core';\nimport { accessorStore, clearStore, guardedAccessor, isEmojiTuple } from '@seedcord/core/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\nimport { Routes } from 'discord-api-types/v10';\n\nimport type { Core } from '#interfaces/Core';\nimport type { EmojiMap } from '@seedcord/types';\nimport type { APIEmoji } from 'discord-api-types/v10';\n\nconst emojiStorage = accessorStore<ResolvedEmoji>();\n\nfunction reasonOf(error: unknown): string {\n return Error.isError(error) ? error.message : String(error);\n}\n\nfunction byName(list: readonly APIEmoji[]): Map<string, APIEmoji> {\n const map = new Map<string, APIEmoji>();\n for (const entry of list) if (entry.name) map.set(entry.name, entry);\n return map;\n}\n\n/** The global {@link Emojis} accessor type. */\nexport type InjectedEmojiMap = {\n [K in keyof EmojiMap]: ResolvedEmoji;\n};\n\n/**\n * The bot's resolved emojis, keyed by {@link EmojiMap}. Filled by {@link EmojiInjector} during startup.\n * Throws if you read a key the injector did not fill.\n */\nexport const Emojis = guardedAccessor('Emojis', emojiStorage) as InjectedEmojiMap;\n\n// resolves the configured emojis over REST at startup\nexport class EmojiInjector {\n private readonly logger = new Logger('Emojis', { channel: 'bot' });\n\n constructor(private readonly core: Core) {}\n\n public async init(): Promise<void> {\n clearStore(emojiStorage);\n\n const configured = this.core.config.bot.emojis;\n if (!configured || Object.keys(configured).length === 0) {\n this.logger.debug('No emojis configured, skipping emoji injection.');\n return;\n }\n\n const failures: string[] = [];\n const guilds = new Map<string, Map<string, APIEmoji>>();\n let application: Map<string, APIEmoji> | undefined;\n\n for (const [key, value] of Object.entries(configured)) {\n if (isEmojiTuple(value)) await this.resolveTuple(key, value, guilds, failures);\n else if (typeof value === 'string') {\n application ??= await this.applicationEmojis(failures);\n this.resolve(key, value, application, failures, 'the application');\n } else failures.push(` - \"${key}\" has an invalid value (expected a name or [name, guildId])`);\n }\n\n // surface every unresolved emoji at once so the user fixes the whole config in one pass\n if (failures.length > 0) {\n throw new SeedcordError(SeedcordErrorCode.ConfigEmojiUnresolved, [failures.length, failures.join('\\n')]);\n }\n\n this.logger.utils.summary('Loaded emojis', { emojis: Object.keys(emojiStorage).length });\n }\n\n private async applicationEmojis(failures: string[]): Promise<Map<string, APIEmoji>> {\n try {\n const appId = this.core.applicationId;\n // justified: the discord api contract for this route\n const listed = (await this.core.rest.get(Routes.applicationEmojis(appId))) as {\n items: APIEmoji[];\n };\n return byName(listed.items);\n } catch (error) {\n failures.push(` - the application emojis could not be read (${reasonOf(error)})`);\n return new Map();\n }\n }\n\n private async resolveTuple(\n key: string,\n [name, guildId]: readonly [string, string],\n guilds: Map<string, Map<string, APIEmoji>>,\n failures: string[]\n ): Promise<void> {\n let guild = guilds.get(guildId);\n if (!guild) {\n try {\n // justified: the discord api contract for this route\n guild = byName((await this.core.rest.get(Routes.guildEmojis(guildId))) as APIEmoji[]);\n } catch (error) {\n failures.push(\n ` - \"${name}\" for \"${key}\" targets guild ${guildId}, which could not be read (${reasonOf(error)})`\n );\n return;\n }\n guilds.set(guildId, guild);\n }\n\n this.resolve(key, name, guild, failures, `guild ${guildId}`);\n }\n\n private resolve(key: string, name: string, source: Map<string, APIEmoji>, failures: string[], where: string): void {\n const found = source.get(name);\n if (!found?.id) {\n failures.push(` - \"${name}\" for \"${key}\" was not found in ${where}`);\n return;\n }\n\n emojiStorage[key] = new ResolvedEmoji(name, found.id, found.animated ?? false);\n }\n}\n","import { SlashRouteBrand } from '@seedcord/core/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { InteractionHandler } from '#handlers/interaction/InteractionHandler';\nimport { HttpSlashOptions } from '#inputs/HttpSlashOptions';\nimport { slashRouteOf } from '#src/dispatch/slashRouteOf';\n\nimport type { SlashOptions } from '#inputs/SlashOptions';\nimport type { SlashRegistry } from '@seedcord/core';\nimport type { APIChatInputApplicationCommandInteraction } from 'discord-api-types/v10';\nimport type { Promisable } from 'type-fest';\n\ntype SlashMatchArms<Route extends keyof SlashRegistry, Ret> = {\n [Key in Route]: (options: SlashOptions<Key>) => Promisable<Ret>;\n};\n\n/**\n * Base class for a chat-input (slash) command handler on the HTTP transport.\n *\n * Pass the route from the generated registry as the generic, then read `this.options` for a single\n * command or `this.match` for several. Command authoring stays plain discord.js, `seedcord codegen`\n * reads its `toJSON()` to type these options.\n *\n * @typeParam Route - One or more route keys from {@link SlashRegistry}, e.g. `'ban'` or `'ban' | 'kick'`.\n */\nexport abstract class SlashHandler<\n Route extends keyof SlashRegistry\n> extends InteractionHandler<APIChatInputApplicationCommandInteraction> {\n // phantom, never set at runtime.\n /** @internal */\n declare readonly [SlashRouteBrand]?: Route;\n\n private resolver?: HttpSlashOptions;\n\n /**\n * The typed options for this command's route. Required options drop the null, choices narrow to their\n * literal union, and only the getters for kinds this command actually uses appear. Rich kinds resolve\n * from the interaction's `resolved` data.\n */\n protected get options(): SlashOptions<Route> {\n this.resolver ??= new HttpSlashOptions(this.event.data);\n // SlashOptions is just the registry-typed view over the getters the reader already exposes\n return this.resolver as SlashOptions<Route>;\n }\n\n /**\n * Run the arm for whichever command fired. Use this only when the handler is registered for several\n * routes.\n *\n * Provide one arm per registered route, keyed by its route string, and each arm receives that route's\n * own narrowed options. The arms must cover every route in the generic, a missing route or an unknown\n * key is a compile error.\n *\n * @param arms - One handler per registered route, keyed by route string.\n * @returns The result of the arm that ran.\n */\n protected async match<Ret>(arms: SlashMatchArms<Route, Ret>): Promise<Ret> {\n const route = slashRouteOf(this.event.data);\n // justified: SlashMatchArms is keyed by Route literals, the Record cast indexes it with the runtime route string.\n const arm = Object.hasOwn(arms, route)\n ? (arms as Record<string, (options: SlashOptions<Route>) => Promisable<Ret>>)[route]\n : undefined;\n if (!arm) throw new SeedcordError(SeedcordErrorCode.SlashMatchArmMissing, [route]);\n return await arm(this.options);\n }\n}\n","import { ContextMenuKindBrand, ContextMenuNamesBrand } from '@seedcord/core/internal';\n\nimport { InteractionHandler } from '#handlers/interaction/InteractionHandler';\n\nimport type { NamesFor } from '@seedcord/core/internal';\nimport type {\n APIInteractionDataResolvedGuildMember,\n APIMessage,\n APIMessageApplicationCommandInteraction,\n APIUser,\n APIUserApplicationCommandInteraction,\n ApplicationCommandType\n} from 'discord-api-types/v10';\n\n/**\n * Base class for a user context-menu command handler on the HTTP transport (right-click a user).\n *\n * Pass the command name(s) as the generic, the same as `@UserContextMenuRoute`. Read the right-clicked user\n * from `this.target` and that same user's guild member from `this.targetMember`.\n *\n * @typeParam Names - One or more command names from the generated user registry.\n */\nexport abstract class UserContextMenuHandler<\n Names extends NamesFor<ApplicationCommandType.User>\n> extends InteractionHandler<APIUserApplicationCommandInteraction> {\n // phantom, nothing reads them. they keep the kind and the names on the instance type\n /** @internal */\n declare readonly [ContextMenuKindBrand]?: ApplicationCommandType.User;\n /** @internal */\n declare readonly [ContextMenuNamesBrand]?: Names;\n\n protected get target(): APIUser {\n const { target_id: targetId, resolved } = this.event.data;\n // justified: discord resolves the target it delivered target_id for\n return resolved.users[targetId] as APIUser;\n }\n\n /** The right-clicked user's guild member, null outside a guild. */\n protected get targetMember(): APIInteractionDataResolvedGuildMember | null {\n const { target_id: targetId, resolved } = this.event.data;\n return resolved.members?.[targetId] ?? null;\n }\n}\n\n/**\n * Base class for a message context-menu command handler on the HTTP transport (right-click a message).\n *\n * Pass the command name(s) as the generic, the same as `@MessageContextMenuRoute`. Read the right-clicked\n * message from `this.target`.\n *\n * @typeParam Names - One or more command names from the generated message registry.\n */\nexport abstract class MessageContextMenuHandler<\n Names extends NamesFor<ApplicationCommandType.Message>\n> extends InteractionHandler<APIMessageApplicationCommandInteraction> {\n /** @internal */\n declare readonly [ContextMenuKindBrand]?: ApplicationCommandType.Message;\n /** @internal */\n declare readonly [ContextMenuNamesBrand]?: Names;\n\n protected get target(): APIMessage {\n const { target_id: targetId, resolved } = this.event.data;\n // justified: discord resolves the target it delivered target_id for\n return resolved.messages[targetId] as APIMessage;\n }\n}\n","import { ComponentDefsBrand, decodeComponentRoute } from '@seedcord/core/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { InteractionHandler } from '#handlers/interaction/InteractionHandler';\n\nimport type { SentMessage } from '#reply/ReplySender';\nimport type {\n AnyCustomId,\n DecodedComponentRoute,\n HasComponentDefs,\n MatchArms,\n SingleParams\n} from '@seedcord/core/internal';\nimport type { ReplyResponse } from '@seedcord/types';\nimport type { APIMessageComponentInteraction, APIModalSubmitInteraction } from 'discord-api-types/v10';\nimport type { Promisable } from 'type-fest';\n\n/**\n * Shared base the customId-routed component handlers extend on the HTTP transport.\n *\n * Not a public entry point. Extend {@link ButtonHandler}, {@link SelectMenuHandler}, or {@link ModalHandler}\n * instead. This class defines the customId decode, the route matching, and the source-message verbs\n * `update` and `deferUpdate`.\n *\n * @typeParam Event - The component or modal interaction type this handler processes\n * @typeParam Defs - The customId route definitions registered on the concrete handler\n */\nexport abstract class ComponentHandler<\n Event extends APIMessageComponentInteraction | APIModalSubmitInteraction,\n Defs extends readonly AnyCustomId[]\n>\n extends InteractionHandler<Event>\n implements HasComponentDefs<Defs>\n{\n // phantom, never set at runtime.\n /** @internal */\n declare readonly [ComponentDefsBrand]?: Defs;\n\n /** Rewrite the source message this component interaction came from. */\n protected update(response: ReplyResponse | string): Promise<SentMessage> {\n return this.sender.update(response);\n }\n\n /** Acknowledge the component without changing the source message. */\n protected deferUpdate(): Promise<void> {\n return this.sender.deferUpdate();\n }\n\n private decoded?: DecodedComponentRoute;\n\n private get route(): DecodedComponentRoute {\n this.decoded ??= decodeComponentRoute(this.constructor, this.event.data.custom_id);\n return this.decoded;\n }\n\n /**\n * The decoded params of the single route this handler is registered for.\n *\n * Reading this decodes `this.event.data.custom_id` once (cached after the first read) and throws\n * `StaleCustomId` or `InvalidCustomId` when the wire no longer matches the current shape, which the\n * dispatcher boundary turns into a reply. On a handler registered for several routes this is\n * `never`, so use {@link match} instead.\n */\n protected get params(): SingleParams<Defs> {\n return this.route.params as SingleParams<Defs>;\n }\n\n /**\n * Run the arm for whichever route the component was minted from. Use this only when the handler is\n * registered for several routes. A single-route handler reads `this.params` directly. On a multi-route\n * handler `this.params` is `never`, so match is the only way to read the decoded params.\n *\n * Provide one arm per registered route, keyed by its prefix, and each arm receives that route's own\n * decoded params. The arms cover every registered route prefix, checked at compile time, and a prefix\n * unmatched at runtime throws `CustomIdMatchArmMissing`. Decoding runs before any arm, so a stale or\n * corrupt wire throws before an arm body executes.\n *\n * @param arms - One callback per registered route, keyed by prefix.\n * @returns The result of the arm that ran.\n *\n * @example\n * ```ts\n * \\@ButtonRoute(Approve, Reject)\n * class ReviewButtons extends ButtonHandler<[typeof Approve, typeof Reject]> {\n * async execute() {\n * await this.match({\n * approve: ({ userId }) => this.reply(`approved <@${userId}>`),\n * reject: ({ userId }) => this.reply(`rejected <@${userId}>`)\n * });\n * }\n * }\n * ```\n */\n protected async match<Ret>(arms: MatchArms<Defs, Ret>): Promise<Ret> {\n const { prefix, params } = this.route;\n // justified: MatchArms is keyed by prefix literals, the Record cast indexes it with the runtime prefix.\n const arm = Object.hasOwn(arms, prefix)\n ? (arms as Record<string, (params: Record<string, unknown>) => Promisable<Ret>>)[prefix]\n : undefined;\n if (!arm) throw new SeedcordError(SeedcordErrorCode.CustomIdMatchArmMissing, [prefix]);\n return await arm(params);\n }\n}\n","import { ComponentKindBrand } from '@seedcord/core/internal';\n\nimport { ComponentHandler } from './ComponentHandler';\n\nimport type { AnyCustomId } from '@seedcord/core/internal';\nimport type { APIMessageComponentButtonInteraction } from 'discord-api-types/v10';\n\n/**\n * Base class for a button interaction handler on the HTTP transport.\n *\n * Register the customId definitions this handler decodes with `@ButtonRoute`, list the same ones in the\n * generic, then read `this.params` for a single route or `this.match` for several. Reply through the\n * handler members, or rewrite the source message with `this.update`. Passing different definitions to the\n * decorator and the generic is a compile error.\n *\n * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof ApproveId]`.\n */\nexport abstract class ButtonHandler<Defs extends readonly AnyCustomId[]> extends ComponentHandler<\n APIMessageComponentButtonInteraction,\n Defs\n> {\n // phantom, never set at runtime.\n /** @internal */\n declare readonly [ComponentKindBrand]?: 'button';\n}\n","import { ComponentKindBrand } from '@seedcord/core/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { ComponentHandler } from './ComponentHandler';\n\nimport type { SentMessage } from '#reply/ReplySender';\nimport type { AnyCustomId } from '@seedcord/core/internal';\nimport type { ReplyResponse } from '@seedcord/types';\nimport type { APIModalSubmitInteraction } from 'discord-api-types/v10';\n\n/**\n * Base class for a modal submit handler on the HTTP transport.\n *\n * Register the customId definitions this handler decodes with `@ModalRoute` and list the same ones in the\n * generic. Read the submitted inputs off `this.event.data.components`. Reply through the handler members.\n * Discord forbids opening a modal in response to a modal, so `showModal` fails compilation on this kind.\n *\n * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof ConfigId]`.\n */\nexport abstract class ModalHandler<Defs extends readonly AnyCustomId[]> extends ComponentHandler<\n APIModalSubmitInteraction,\n Defs\n> {\n // phantom, never set at runtime.\n /** @internal */\n declare readonly [ComponentKindBrand]?: 'modal';\n\n /** Rewrite the message this modal was opened from. Throws when a command opened the modal. */\n protected override update(response: ReplyResponse | string): Promise<SentMessage> {\n this.ensureSourceMessage('update');\n return super.update(response);\n }\n\n /** Acknowledge the submit without changing the source message. Throws when a command opened the modal. */\n protected override deferUpdate(): Promise<void> {\n this.ensureSourceMessage('deferUpdate');\n return super.deferUpdate();\n }\n\n // discord omits the message when a command opened the modal, so update and deferUpdate have no target\n private ensureSourceMessage(method: string): void {\n if (this.event.message) return;\n throw new SeedcordError(SeedcordErrorCode.ReplyUpdateWithoutSource, [method, this.routeId]);\n }\n}\n","import { ComponentKindBrand } from '@seedcord/core/internal';\n\nimport { ComponentHandler } from './ComponentHandler';\n\nimport type { SelectMenuKind } from '@seedcord/core';\nimport type { AnyCustomId } from '@seedcord/core/internal';\nimport type {\n APIMessageChannelSelectInteractionData,\n APIMessageComponentSelectMenuInteraction,\n APIMessageMentionableSelectInteractionData,\n APIMessageRoleSelectInteractionData,\n APIMessageStringSelectInteractionData,\n APIMessageUserSelectInteractionData\n} from 'discord-api-types/v10';\n\ntype SelectMenuInteractionFor<Kind extends SelectMenuKind> = APIMessageComponentSelectMenuInteraction & {\n data: Kind extends SelectMenuKind.String\n ? APIMessageStringSelectInteractionData\n : Kind extends SelectMenuKind.User\n ? APIMessageUserSelectInteractionData\n : Kind extends SelectMenuKind.Role\n ? APIMessageRoleSelectInteractionData\n : Kind extends SelectMenuKind.Channel\n ? APIMessageChannelSelectInteractionData\n : Kind extends SelectMenuKind.Mentionable\n ? APIMessageMentionableSelectInteractionData\n : never;\n};\n\n/**\n * Base class for a select menu handler on the HTTP transport.\n *\n * Pass the select kind first and the customId definitions second, the same order as `@SelectMenuRoute`,\n * so `this.event.data` and its `values`/`resolved` are narrowed to that kind. Read `this.params` for a\n * single route or `this.match` for several, and reply through the handler members or rewrite the source\n * message with `this.update`.\n *\n * @typeParam Kind - The select kind from {@link SelectMenuKind}, e.g. `SelectMenuKind.User`.\n * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof AssignId]`.\n */\nexport abstract class SelectMenuHandler<\n Kind extends SelectMenuKind,\n Defs extends readonly AnyCustomId[]\n> extends ComponentHandler<SelectMenuInteractionFor<Kind>, Defs> {\n // phantom, never set at runtime.\n /** @internal */\n declare readonly [ComponentKindBrand]?: Kind;\n}\n","import { Subscriber as CoreSubscriber, WebhookLog as CoreWebhookLog } from '@seedcord/core';\n\nimport type { Core } from '#interfaces/Core';\nimport type { SubscriptionKey } from '@seedcord/core';\n\n/**\n * Base class for an HTTP subscriber, binding `this.core` to the HTTP {@link Core}.\n *\n * @typeParam KeyOfSubscribers - The subscription key this handler receives\n */\nexport abstract class Subscriber<KeyOfSubscribers extends SubscriptionKey> extends CoreSubscriber<\n KeyOfSubscribers,\n Core\n> {}\n\n/**\n * Base class for an HTTP subscriber that delivers its event to a Discord webhook, binding\n * `this.core` to the HTTP {@link Core}.\n *\n * @typeParam KeyOfSubscribers - The subscription key this reporter receives\n */\nexport abstract class WebhookLog<KeyOfSubscribers extends SubscriptionKey> extends CoreWebhookLog<\n KeyOfSubscribers,\n Core\n> {}\n","import { PaginatorBase } from '@seedcord/core';\n\nimport { ButtonHandler } from '#handlers/interaction/components';\n\nimport type { RepliableHandler } from '#handlers/RepliableHandler';\nimport type { Core } from '#interfaces/Core';\nimport type { SentMessage } from '#reply/ReplySender';\nimport type { Repliables } from '#src/handlers/interactionTypes';\nimport type { PageContext } from './PageContext';\nimport type { PaginatorConfig } from '@seedcord/core';\nimport type { PageCursor } from '@seedcord/core/internal';\nimport type { ReplyResponse } from '@seedcord/types';\nimport type { APIMessageComponentButtonInteraction, APIUser } from 'discord-api-types/v10';\n\n// `& { execute }` concretizes the abstract execute so the empty `extends Bans.Handler {}` stays concrete\n// (no TS2515) and a concrete Nav assigns with no cast.\ntype PaginatorHandlerCtor<Prefix extends string> = new (\n event: APIMessageComponentButtonInteraction,\n core: Core\n) => ButtonHandler<[PageCursor<Prefix>]> & { execute(): Promise<void> };\n\nfunction contextOf(interaction: Repliables, core: Core): PageContext {\n // discord sends member.user in a guild and the top-level user in a dm\n const resolved = interaction.member?.user ?? interaction.user;\n return {\n interaction,\n // justified: every repliable interaction Discord delivers carries a user, and the wrapper types don't say so\n user: resolved as APIUser,\n guildId: interaction.guild_id ?? null,\n core\n };\n}\n\n/**\n * A restart-proof paginator. Each nav button's customId encodes its full target page, so clicks are\n * idempotent and survive a restart. A persistent `@ButtonRoute` on `Handler` dispatches the clicks.\n *\n * @typeParam Item - The item type, inferred from the source.\n * @typeParam Prefix - The route prefix, inferred from `config.prefix`.\n *\n * @example\n * ```ts\n * export const Bans = new Paginator({\n * prefix: 'bans',\n * source: new ArraySource((ctx) => loadBans(ctx.guildId), { perPage: 10 }),\n * renderItem: (ban) => ban.user.username\n * });\n *\n * \\@ButtonRoute(Bans.cursor)\n * export class BansNav extends Bans.Handler {}\n * ```\n */\nexport class Paginator<Item, const Prefix extends string> extends PaginatorBase<Item, Prefix, PageContext> {\n /** The nav handler base. Extend it with an empty body and decorate it, `@ButtonRoute(p.cursor)`. */\n public readonly Handler: PaginatorHandlerCtor<Prefix>;\n\n constructor(config: PaginatorConfig<Item, Prefix, PageContext>) {\n super(config);\n\n // an arrow captures the paginator lexically so Nav.execute can reach it without aliasing `this`.\n const loadPage = (ctx: PageContext, n: number): Promise<ReplyResponse> => this.page(ctx, n);\n this.Handler = class Nav extends ButtonHandler<[PageCursor<Prefix>]> {\n async execute(): Promise<void> {\n await this.deferUpdate();\n const response = await loadPage(contextOf(this.event, this.core), this.params.page);\n // deferUpdate seeds the sender deferred-update, so update PATCHes @original\n await this.update(response);\n }\n };\n }\n\n /**\n * Render page 0 and send it through the handler's sender, which picks reply, edit, or followUp from its\n * ack state.\n *\n * @param handler - The handler starting the paginator, normally `this`.\n */\n async start(handler: RepliableHandler<Repliables>): Promise<SentMessage> {\n const interaction = handler.getEvent();\n const response = await this.page(contextOf(interaction, handler.core), 0);\n return handler.getSender().send(response, { ephemeral: this.config.ephemeral ?? false });\n }\n}\n","import { ArraySource as BaseArraySource, CursorSource as BaseCursorSource } from '@seedcord/core';\n\nimport type { PageContext } from './PageContext';\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, so the real total is known (a real last button, \"Page X of Y\"). Cache inside your loader if the\n * load is expensive.\n *\n * @typeParam Item - The item type, inferred from the loader's return.\n */\nexport class ArraySource<Item> extends BaseArraySource<Item, PageContext> {}\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 */\nexport class CursorSource<Item> extends BaseCursorSource<Item, PageContext> {}\n","import { GatedMetadataKey } from '@seedcord/core/internal';\n\nimport type { AnyHandlerCtor, FitAll } from './matching';\nimport type { Gate, GateContextBase } from '@seedcord/core';\nimport type { NonEmptyTuple } from 'type-fest';\n\n/**\n * Attaches gates to an HTTP interaction handler. The gates run before `execute`, and a gate refusing stops\n * the handler with the reply or drop the gate threw. Multiple gates are ANDed. A gate\n * that requires a context the handler does not provide (a button gate on a slash handler, a gate on an\n * autocomplete handler, or a `RequirePermissions([...], { in: 'guild' })` gate the http transport cannot\n * satisfy) is a compile error at this line, and the error names the gate and the handler kind. Place it\n * above the route decorator.\n *\n * Combine gates with {@link and} or {@link or} to build a single gate from multiple arms.\n *\n * @example\n * ```ts\n * import { PermissionFlagsBits } from 'discord-api-types/v10';\n *\n * \\@Gated(GuildOnly(), RequirePermissions([PermissionFlagsBits.ManageMessages]))\n * \\@SlashRoute('purge')\n * class Purge extends SlashHandler<'purge'> {\n * async execute() {\n * // both gates passed, so this runs in a guild with a caller who can moderate this channel\n * }\n * }\n * ```\n *\n * @typeParam Gates - The non-empty tuple of gates to run, in order, against the handler's context.\n * @typeParam TCtor - The decorated handler's constructor, checked against every gate's required context.\n * @param gates - One or more gates to attach, each ANDed so the handler runs only when all pass.\n * @param ctor - The handler class being decorated, rejected at compile time when a gate requires a context it lacks.\n *\n * @see {@link defineGate}\n * @see {@link defineEffectGate}\n * @decorator\n */\nexport function Gated<const Gates extends NonEmptyTuple<Gate<GateContextBase>>>(...gates: Gates) {\n // the class fails to assign and TS names the gate\n return function <TCtor extends AnyHandlerCtor>(\n ctor: FitAll<TCtor, Gates> extends Gates ? TCtor : FitAll<TCtor, Gates>\n ): void {\n const existing = (Reflect.getMetadata(GatedMetadataKey, ctor) as readonly Gate[] | undefined) ?? [];\n Reflect.defineMetadata(GatedMetadataKey, [...existing, ...gates], ctor);\n };\n}\n","export const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":"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"}
|
|
@@ -114,7 +114,7 @@ type SlashOptions$1<Route extends keyof SlashRegistry> = SlashOptions<Route, Htt
|
|
|
114
114
|
type InjectedEmojiMap = { [K in keyof EmojiMap]: ResolvedEmoji; };
|
|
115
115
|
/**
|
|
116
116
|
* The bot's resolved emojis, keyed by {@link EmojiMap}. Filled by {@link EmojiInjector} during startup.
|
|
117
|
-
*
|
|
117
|
+
* Throws if you read a key the injector did not fill.
|
|
118
118
|
*/
|
|
119
119
|
declare const Emojis: InjectedEmojiMap;
|
|
120
120
|
//#endregion
|
|
@@ -602,4 +602,4 @@ declare function Gated<const Gates extends NonEmptyTuple<Gate<GateContextBase>>>
|
|
|
602
602
|
declare const version: string;
|
|
603
603
|
//#endregion
|
|
604
604
|
export { createSeedcord as C, HttpEdgeConfig as D, HttpConfig as E, HttpServerConfig as O, SlashOptions$1 as S, Core as T, SlashHandler as _, CursorSource$1 as a, Emojis as b, Subscriber$1 as c, ModalHandler as d, ButtonHandler as f, UserContextMenuHandler as g, MessageContextMenuHandler as h, ArraySource$1 as i, WebhookLog$1 as l, AutocompleteHandler as m, Gated as n, Paginator as o, ComponentHandler as p, InteractionGateContext as r, PageContext as s, version as t, SelectMenuHandler as u, InteractionHandler as v, EngineContext as w, InjectedEmojiMap as x, RepliableHandler$1 as y };
|
|
605
|
-
//# sourceMappingURL=edge.index-
|
|
605
|
+
//# sourceMappingURL=edge.index-QUU3_n9k.d.mts.map
|
package/dist/edge.index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as createSeedcord, D as HttpEdgeConfig, E as HttpConfig, O as HttpServerConfig, S as SlashOptions, T as Core, _ as SlashHandler, a as CursorSource, b as Emojis, c as Subscriber, d as ModalHandler, f as ButtonHandler, g as UserContextMenuHandler, h as MessageContextMenuHandler, i as ArraySource, l as WebhookLog, m as AutocompleteHandler, n as Gated, o as Paginator, p as ComponentHandler, r as InteractionGateContext, s as PageContext, t as version, u as SelectMenuHandler, v as InteractionHandler, w as EngineContext, x as InjectedEmojiMap, y as RepliableHandler } from "./edge.index-
|
|
1
|
+
import { C as createSeedcord, D as HttpEdgeConfig, E as HttpConfig, O as HttpServerConfig, S as SlashOptions, T as Core, _ as SlashHandler, a as CursorSource, b as Emojis, c as Subscriber, d as ModalHandler, f as ButtonHandler, g as UserContextMenuHandler, h as MessageContextMenuHandler, i as ArraySource, l as WebhookLog, m as AutocompleteHandler, n as Gated, o as Paginator, p as ComponentHandler, r as InteractionGateContext, s as PageContext, t as version, u as SelectMenuHandler, v as InteractionHandler, w as EngineContext, x as InjectedEmojiMap, y as RepliableHandler } from "./edge.index-QUU3_n9k.mjs";
|
|
2
2
|
import { t as RouteManifest } from "./RouteManifest-DTxI4fS0.mjs";
|
|
3
3
|
export * from "@seedcord/core";
|
|
4
4
|
export * from "@seedcord/errors";
|
package/dist/edge.index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as RepliableHandler, _ as createSeedcord, a as Paginator, b as InteractionHandler, c as SelectMenuHandler, d as ComponentHandler, f as MessageContextMenuHandler, g as Emojis, i as CursorSource, l as ModalHandler, m as SlashHandler, n as Gated, o as Subscriber, p as UserContextMenuHandler, r as ArraySource, s as WebhookLog, t as version, u as ButtonHandler, x as AutocompleteHandler } from "./edge.index-DK-Y14ge.mjs";
|
|
2
2
|
|
|
3
3
|
export * from "@seedcord/core"
|
|
4
4
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as createSeedcord, D as HttpEdgeConfig, E as HttpConfig, O as HttpServerConfig, S as SlashOptions, T as Core, _ as SlashHandler, a as CursorSource, b as Emojis, c as Subscriber, d as ModalHandler, f as ButtonHandler, g as UserContextMenuHandler, h as MessageContextMenuHandler, i as ArraySource, l as WebhookLog, m as AutocompleteHandler, n as Gated, o as Paginator, p as ComponentHandler, r as InteractionGateContext, s as PageContext, t as version, u as SelectMenuHandler, v as InteractionHandler, w as EngineContext, x as InjectedEmojiMap, y as RepliableHandler } from "./edge.index-
|
|
1
|
+
import { C as createSeedcord, D as HttpEdgeConfig, E as HttpConfig, O as HttpServerConfig, S as SlashOptions, T as Core, _ as SlashHandler, a as CursorSource, b as Emojis, c as Subscriber, d as ModalHandler, f as ButtonHandler, g as UserContextMenuHandler, h as MessageContextMenuHandler, i as ArraySource, l as WebhookLog, m as AutocompleteHandler, n as Gated, o as Paginator, p as ComponentHandler, r as InteractionGateContext, s as PageContext, t as version, u as SelectMenuHandler, v as InteractionHandler, w as EngineContext, x as InjectedEmojiMap, y as RepliableHandler } from "./edge.index-QUU3_n9k.mjs";
|
|
2
2
|
import { t as RouteManifest } from "./RouteManifest-DTxI4fS0.mjs";
|
|
3
3
|
import "reflect-metadata";
|
|
4
4
|
import { REST } from "@discordjs/rest";
|
|
@@ -42,11 +42,10 @@ declare class Seedcord<Cfg extends HttpConfig = HttpConfig> extends Pluggable<'h
|
|
|
42
42
|
private readonly subscribers;
|
|
43
43
|
private readonly interactions?;
|
|
44
44
|
private readonly commandRegistry?;
|
|
45
|
-
private appId?;
|
|
46
|
-
private appIdPromise?;
|
|
47
45
|
private readonly emojiInjector;
|
|
48
46
|
private readonly hmrManager;
|
|
49
47
|
private readonly logger;
|
|
48
|
+
private token?;
|
|
50
49
|
private server?;
|
|
51
50
|
private boundPort?;
|
|
52
51
|
private fetchedUsername?;
|
|
@@ -63,8 +62,8 @@ declare class Seedcord<Cfg extends HttpConfig = HttpConfig> extends Pluggable<'h
|
|
|
63
62
|
private registerStartupTasks;
|
|
64
63
|
private registerHmrAwareModules;
|
|
65
64
|
private authenticate;
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
/** The bot's Discord application id. Throws if you read it before the Configuration phase. */
|
|
66
|
+
get applicationId(): string;
|
|
68
67
|
private listen;
|
|
69
68
|
private fetchUsername;
|
|
70
69
|
private stopServer;
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as RepliableHandler, _ as createSeedcord, a as Paginator, b as InteractionHandler, c as SelectMenuHandler, d as ComponentHandler, f as MessageContextMenuHandler, g as Emojis, h as EmojiInjector, i as CursorSource, l as ModalHandler, m as SlashHandler, n as Gated, o as Subscriber, p as UserContextMenuHandler, r as ArraySource, s as WebhookLog, t as version, u as ButtonHandler, v as buildEngine, x as AutocompleteHandler, y as buildRouteMaps } from "./edge.index-DK-Y14ge.mjs";
|
|
2
2
|
import "reflect-metadata";
|
|
3
|
-
import { SeedcordError, validateDiscordToken } from "@seedcord/errors/internal";
|
|
3
|
+
import { SeedcordError, applicationIdFromToken, validateDiscordToken } from "@seedcord/errors/internal";
|
|
4
4
|
import { Envapter } from "envapt";
|
|
5
5
|
import { REST } from "@discordjs/rest";
|
|
6
6
|
import { Bus } from "@seedcord/core";
|
|
7
|
-
import {
|
|
7
|
+
import { HmrManager, InteractionMetadataKey, InteractionRoutes, busLoggerOf, getDevChannel, interactionRoutesOf, setBotColor } from "@seedcord/core/internal";
|
|
8
8
|
import { SeedcordErrorCode, paint } from "@seedcord/errors";
|
|
9
9
|
import { Logger, LoggerChannelRegistry } from "@seedcord/logger";
|
|
10
10
|
import { MemoryRateLimiter } from "@seedcord/rate-limiter";
|
|
@@ -270,11 +270,10 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
270
270
|
subscribers;
|
|
271
271
|
interactions;
|
|
272
272
|
commandRegistry;
|
|
273
|
-
|
|
274
|
-
appIdPromise;
|
|
275
|
-
emojiInjector = new EmojiInjector(this, () => this.applicationId());
|
|
273
|
+
emojiInjector = new EmojiInjector(this);
|
|
276
274
|
hmrManager;
|
|
277
275
|
logger = new Logger("Server", { channel: "bot" });
|
|
276
|
+
token;
|
|
278
277
|
server;
|
|
279
278
|
boundPort;
|
|
280
279
|
fetchedUsername;
|
|
@@ -286,19 +285,7 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
286
285
|
this.hmrManager = new HmrManager();
|
|
287
286
|
this.hmrManager.init();
|
|
288
287
|
if (this.config.bot.interactions.path) this.interactions = new InteractionDispatcher(this.config.bot.interactions.path);
|
|
289
|
-
|
|
290
|
-
if (commandsDir) {
|
|
291
|
-
const injector = new CommandInjector();
|
|
292
|
-
const registry = new CommandRegistry({
|
|
293
|
-
dir: commandsDir,
|
|
294
|
-
rest: this.rest,
|
|
295
|
-
applicationId: () => this.requireApplicationId(),
|
|
296
|
-
onDeployed: (result) => {
|
|
297
|
-
injector.inject(result, registry.allCommands());
|
|
298
|
-
}
|
|
299
|
-
});
|
|
300
|
-
this.commandRegistry = registry;
|
|
301
|
-
}
|
|
288
|
+
if (this.config.bot.commands.path) this.commandRegistry = new CommandRegistry(this);
|
|
302
289
|
this.rateLimiter = config.store ?? new MemoryRateLimiter();
|
|
303
290
|
this.bus = new Bus(this);
|
|
304
291
|
this.subscribers = new SubscriberLoader(this.bus, config.subscribers.path);
|
|
@@ -350,7 +337,6 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
350
337
|
const { commandRegistry } = this;
|
|
351
338
|
if (commandRegistry) this.startup.addTask(StartupPhase.Login, "command-deploy", async () => {
|
|
352
339
|
await commandRegistry.init();
|
|
353
|
-
this.appId = await this.applicationId();
|
|
354
340
|
await commandRegistry.setCommands();
|
|
355
341
|
interactions?.warnUnhandledRoutes(commandRegistry.routeLeaves());
|
|
356
342
|
interactions?.warnUnhandledContextMenuRoutes(commandRegistry.contextMenuLeaves());
|
|
@@ -368,15 +354,13 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
368
354
|
});
|
|
369
355
|
}
|
|
370
356
|
authenticate() {
|
|
371
|
-
this.
|
|
372
|
-
|
|
373
|
-
applicationId() {
|
|
374
|
-
this.appIdPromise ??= fetchApplicationId(this.rest);
|
|
375
|
-
return this.appIdPromise;
|
|
357
|
+
this.token = validateDiscordToken(Envapter.get("DISCORD_BOT_TOKEN"));
|
|
358
|
+
this.rest.setToken(this.token);
|
|
376
359
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
360
|
+
/** The bot's Discord application id. Throws if you read it before the Configuration phase. */
|
|
361
|
+
get applicationId() {
|
|
362
|
+
if (!this.token) throw new SeedcordError(SeedcordErrorCode.CoreApplicationUnavailable);
|
|
363
|
+
return applicationIdFromToken(this.token);
|
|
380
364
|
}
|
|
381
365
|
async listen() {
|
|
382
366
|
const maps = this.interactions?.maps ?? buildRouteMaps(EMPTY_MANIFEST);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["packageVersion","CorePlugin"],"sources":["../src/manifest/RouteManifest.ts","../src/node/InteractionDispatcher.ts","../src/node/webBridge.ts","../src/node/Seedcord.ts","../src/plugin.ts"],"sourcesContent":["import type { SelectMenuKind, SubscriptionKey } from '@seedcord/core';\nimport type { EventFrequency } from '@seedcord/types';\nimport type { ApplicationCommandType } from 'discord-api-types/v10';\n\n/** The command kinds a manifest row registers, the wire enum's other members have no handler base. */\ntype RegistrableCommandType =\n ApplicationCommandType.ChatInput | ApplicationCommandType.User | ApplicationCommandType.Message;\n\n/** One file may export several handlers. */\nexport interface RouteModule {\n /** The exported binding inside the loaded module. */\n readonly exportName: string;\n /** The source file the row came from, reported when the export is missing. */\n readonly from: string;\n /** Lazy import of the module, evaluated on first hit. */\n readonly load: () => Promise<Record<string, unknown>>;\n}\n\n/**\n * A slash or context-menu command row. `type` picks the per-kind map, `name` is the lookup key. For a\n * chat-input command with subcommands, `name` is the full route path (`'config/set'`), the same key\n * `@SlashRoute` registers.\n */\ninterface CommandRoute extends RouteModule {\n readonly name: string;\n readonly type: RegistrableCommandType;\n}\n\n/**\n * A button, select, or modal row. `kind` picks the per-kind map, `prefix` is the stable customId prefix\n * the receiver routes by (the routeKey minus its layout hash).\n */\nexport interface ComponentRoute extends RouteModule {\n readonly kind: 'button' | `${SelectMenuKind}Select` | 'modal';\n readonly prefix: string;\n}\n\n/**\n * An autocomplete row, its own route keyed by the command's route path, separate from the slash row.\n */\ninterface AutocompleteRoute extends RouteModule {\n readonly name: string;\n}\n\n/**\n * A bus subscriber row. The Bus registers it without importing the subscriber module, and resolves the\n * class on the first publish of one of its keys.\n */\ninterface SubscriberRoute extends RouteModule {\n /** The subscription keys this subscriber registers for. */\n readonly keys: readonly SubscriptionKey[];\n /** Mirrors the `@Subscribe` frequency metadata. */\n readonly frequency: EventFrequency;\n}\n\n/** A middleware row. Middleware runs on every dispatch. */\ntype MiddlewareRoute = RouteModule;\n\n/**\n * The static route table the HTTP engine dispatches through, replacing the filesystem handler scan that\n * cannot run in a bundled isolate. `seedcord build` emits it, the mount path authors it by hand.\n */\nexport interface RouteManifest {\n readonly commandRoutes: readonly CommandRoute[];\n readonly componentRoutes: readonly ComponentRoute[];\n readonly autocompleteRoutes: readonly AutocompleteRoute[];\n readonly subscriberRoutes: readonly SubscriberRoute[];\n readonly middlewareRoutes: readonly MiddlewareRoute[];\n}\n\n/** @internal */\nexport const EMPTY_MANIFEST: RouteManifest = {\n commandRoutes: [],\n componentRoutes: [],\n autocompleteRoutes: [],\n subscriberRoutes: [],\n middlewareRoutes: []\n};\n","import { HmrModuleHandler } from '@seedcord/core/hmr';\nimport { interactionRoutesOf, InteractionMetadataKey, InteractionRoutes } from '@seedcord/core/internal';\nimport { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\nimport { formatFilePath } from '@seedcord/utils';\nimport { traverseDirectory } from '@seedcord/utils/node';\nimport { Envapter } from 'envapt';\n\nimport { AutocompleteHandler } from '#handlers/interaction/AutocompleteHandler';\nimport { InteractionHandler } from '#handlers/interaction/InteractionHandler';\nimport { buildRouteMaps } from '#src/dispatch/resolve';\nimport { EMPTY_MANIFEST } from '#src/manifest/RouteManifest';\n\nimport type { HandlerConstructor } from '#handlers/constructors';\nimport type { ResolvedRoute, RouteMap, RouteMaps } from '#src/dispatch/resolve';\nimport type { Initializeable } from '@seedcord/core';\nimport type { ContextMenuLeaves } from '@seedcord/core/internal';\nimport type { HmrAware, HmrUpdateEvent } from '@seedcord/types';\n\ninterface RouteTarget {\n readonly map: RouteMap;\n readonly kind: ResolvedRoute['kind'];\n}\n\n// hmr swaps entries live and resolve() reads per request, but edge builds from manifest instead\nexport class InteractionDispatcher implements Initializeable, HmrAware {\n public readonly maps: RouteMaps;\n private readonly targets: Record<InteractionRoutes, RouteTarget>;\n\n /** @internal */\n public readonly logger = new Logger('Interactions', { channel: 'interactions' });\n\n private isInitialized = false;\n private readonly hmrHandler?: HmrModuleHandler<HandlerConstructor, void, string[]>;\n // routeId -> owner row, the duplicate guard and the hmr unregister index\n private readonly rowOwners = new Map<\n string,\n { ctor: HandlerConstructor; target: RouteTarget; key: string; from: string }\n >();\n\n private loading = false;\n private readonly loadedHandlers: { name: string; from: string }[] = [];\n\n constructor(private readonly handlersDir: string) {\n this.maps = buildRouteMaps(EMPTY_MANIFEST);\n this.targets = this.buildTargets();\n\n if (!Envapter.isDevelopment && !Envapter.isTest) return;\n this.hmrHandler = new HmrModuleHandler({\n handlersDir,\n isHandler: this.isHandler.bind(this),\n registerHandler: this.registerHandler.bind(this),\n unregisterHandler: this.unregisterHandler.bind(this),\n getArtifacts: this.getArtifacts.bind(this),\n logger: this.logger\n });\n }\n\n /** @internal */\n public async init(): Promise<void> {\n if (this.isInitialized) return;\n this.isInitialized = true;\n\n this.loading = true;\n this.loadedHandlers.length = 0;\n try {\n await traverseDirectory(this.handlersDir, (fullPath, relativePath, imported) => {\n for (const value of Object.values(imported)) {\n if (!this.isHandler(value)) continue;\n this.registerHandler(value, relativePath);\n this.hmrHandler?.trackHandler(fullPath, value);\n }\n });\n } finally {\n this.loading = false;\n }\n\n this.logger.utils.block(\n 'Loaded handlers',\n [\n ...this.logger.utils.entries(this.loadedHandlers),\n ...this.logger.utils.counts({ routes: this.rowOwners.size })\n ],\n 'debug'\n );\n }\n\n /** @internal */\n public async onHmr(event: HmrUpdateEvent): Promise<void> {\n await this.hmrHandler?.handle(event);\n }\n\n public warnUnhandledRoutes(commandLeaves: Iterable<string>): void {\n this.warnMissing(commandLeaves, this.maps.slash, 'Slash route', '@SlashRoute');\n }\n\n public warnUnhandledContextMenuRoutes(leaves: ContextMenuLeaves): void {\n this.warnMissing(leaves.user, this.maps.userContextMenu, 'User context menu', '@ContextMenuRoute');\n this.warnMissing(leaves.message, this.maps.messageContextMenu, 'Message context menu', '@ContextMenuRoute');\n }\n\n private warnMissing(names: Iterable<string>, map: RouteMap, label: string, decorator: string): void {\n for (const name of names) {\n if (map.has(name)) continue;\n this.logger.warn(`${label} ${paint.sky.bold(name)} has no registered ${paint.bold(decorator)} handler.`);\n }\n }\n\n private isHandler(value: unknown): value is HandlerConstructor {\n if (typeof value !== 'function') return false;\n // this package's own family bases, so a gateway handler in the same dir stays unregistered\n return (\n (value.prototype instanceof InteractionHandler || value.prototype instanceof AutocompleteHandler) &&\n Reflect.hasMetadata(InteractionMetadataKey, value)\n );\n }\n\n private getArtifacts(ctor: HandlerConstructor): string[] {\n const routeIds: string[] = [];\n for (const [routeId, owner] of this.rowOwners) {\n if (owner.ctor === ctor) routeIds.push(routeId);\n }\n return routeIds;\n }\n\n private targetFor(route: InteractionRoutes): RouteTarget {\n return this.targets[route];\n }\n\n private buildTargets(): Record<InteractionRoutes, RouteTarget> {\n return {\n [InteractionRoutes.Slash]: { map: this.maps.slash, kind: 'slash' },\n [InteractionRoutes.UserContextMenu]: { map: this.maps.userContextMenu, kind: 'userContextMenu' },\n [InteractionRoutes.MessageContextMenu]: { map: this.maps.messageContextMenu, kind: 'messageContextMenu' },\n [InteractionRoutes.Autocomplete]: { map: this.maps.autocomplete, kind: 'autocomplete' },\n [InteractionRoutes.Button]: { map: this.maps.components.button, kind: 'button' },\n [InteractionRoutes.StringMenu]: { map: this.maps.components.stringSelect, kind: 'stringMenu' },\n [InteractionRoutes.UserMenu]: { map: this.maps.components.userSelect, kind: 'userMenu' },\n [InteractionRoutes.RoleMenu]: { map: this.maps.components.roleSelect, kind: 'roleMenu' },\n [InteractionRoutes.ChannelMenu]: { map: this.maps.components.channelSelect, kind: 'channelMenu' },\n [InteractionRoutes.MentionableMenu]: {\n map: this.maps.components.mentionableSelect,\n kind: 'mentionableMenu'\n },\n [InteractionRoutes.Modal]: { map: this.maps.components.modal, kind: 'modal' }\n };\n }\n\n private registerHandler(ctor: HandlerConstructor, relativePath: string): void {\n const from = formatFilePath(relativePath);\n // a partial registration would orphan routes and break hmr rollback\n const writes: [RouteTarget, string][] = [];\n\n for (const [route, keys] of interactionRoutesOf(ctor)) {\n const target = this.targetFor(route);\n for (const key of keys) {\n const routeId = `${target.kind}:${key}`;\n const existing = this.rowOwners.get(routeId);\n // a different class on the same route silently shadows the existing one (last write wins)\n if (existing && existing.ctor !== ctor) {\n throw new SeedcordError(SeedcordErrorCode.InteractionDuplicateRoute, [\n routeId,\n `${existing.ctor.name} (${existing.from})`,\n `${ctor.name} (${from})`\n ]);\n }\n writes.push([target, key]);\n }\n }\n\n if (writes.length === 0) return;\n for (const [target, key] of writes) {\n const routeId = `${target.kind}:${key}`;\n target.map.set(key, { kind: target.kind, routeId, load: () => Promise.resolve(ctor) });\n this.rowOwners.set(routeId, { ctor, target, key, from });\n }\n\n if (this.loading) this.loadedHandlers.push({ name: ctor.name, from });\n }\n\n private unregisterHandler(ctor: HandlerConstructor, artifacts?: string[]): void {\n const routeIds = artifacts ?? this.getArtifacts(ctor);\n for (const routeId of routeIds) {\n const owner = this.rowOwners.get(routeId);\n if (owner?.ctor !== ctor) continue;\n this.rowOwners.delete(routeId);\n owner.target.map.delete(owner.key);\n }\n }\n}\n","import type { IncomingHttpHeaders, IncomingMessage, ServerResponse } from 'node:http';\n\nfunction toHeaders(raw: IncomingHttpHeaders): Headers {\n const headers = new Headers();\n for (const [name, value] of Object.entries(raw)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const item of value) headers.append(name, item);\n } else {\n headers.set(name, value);\n }\n }\n return headers;\n}\n\n// buffers the raw body before any parse so ed25519 verifies the exact signed bytes\nexport async function toWebRequest(request: IncomingMessage): Promise<Request> {\n const chunks: Buffer[] = [];\n // justified: the async iterator is typed any and IncomingMessage yields Buffer chunks\n for await (const chunk of request) chunks.push(chunk as Buffer);\n\n const method = request.method ?? 'GET';\n const body = method === 'GET' || method === 'HEAD' ? undefined : new Uint8Array(Buffer.concat(chunks));\n\n return new Request(`http://${request.headers.host ?? 'localhost'}${request.url ?? '/'}`, {\n method,\n headers: toHeaders(request.headers),\n ...(body !== undefined && { body })\n });\n}\n\nexport async function writeWebResponse(response: Response, out: ServerResponse): Promise<void> {\n const headers: Record<string, string | string[]> = Object.fromEntries(response.headers);\n // iterating Headers comma-joins same-name values, which corrupts Set-Cookie (a per-cookie header)\n const cookies = response.headers.getSetCookie();\n if (cookies.length > 0) headers['set-cookie'] = cookies;\n\n out.writeHead(response.status, headers);\n out.end(response.body === null ? undefined : Buffer.from(await response.arrayBuffer()));\n}\n","import { once } from 'node:events';\nimport { createServer } from 'node:http';\n\nimport { REST } from '@discordjs/rest';\nimport { Bus } from '@seedcord/core';\nimport { busLoggerOf, CommandInjector, getDevChannel, HmrManager, setBotColor } from '@seedcord/core/internal';\nimport {\n CommandRegistry,\n CoordinatedShutdown,\n CoordinatedStartup,\n Pluggable,\n ShutdownPhase,\n StartupPhase,\n SubscriberLoader\n} from '@seedcord/core/node/internal';\nimport { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError, validateDiscordToken } from '@seedcord/errors/internal';\nimport { Logger, LoggerChannelRegistry } from '@seedcord/logger';\nimport { installNodeDefaults } from '@seedcord/logger/node';\nimport { MemoryRateLimiter } from '@seedcord/rate-limiter';\nimport { SeedcordBrand } from '@seedcord/types/internal';\nimport { Routes } from 'discord-api-types/v10';\nimport { Envapter } from 'envapt';\n\nimport { fetchApplicationId } from '#src/applicationId';\nimport { buildRouteMaps } from '#src/dispatch/resolve';\nimport { EmojiInjector } from '#src/emojis/EmojiInjector';\nimport { buildEngine } from '#src/engine';\nimport { EMPTY_MANIFEST } from '#src/manifest/RouteManifest';\n\nimport { InteractionDispatcher } from './InteractionDispatcher';\nimport { toWebRequest, writeWebResponse } from './webBridge';\nimport { version as packageVersion } from '../version';\n\nimport type { HttpConfig } from '#interfaces/Config';\nimport type { Core } from '#interfaces/Core';\nimport type { IRateLimiter } from '@seedcord/types';\nimport type { SeedcordInstance } from '@seedcord/types/internal';\nimport type { Server } from 'node:http';\nimport type { AddressInfo } from 'node:net';\n\nconst DEFAULT_PORT = 3000;\nconst SERVER_SHUTDOWN_TIMEOUT_MS = 5000;\nconst DRAIN_TIMEOUT_MS = 10_000;\n\ntype RuntimeOfConfig<Cfg extends HttpConfig> = Cfg extends { runtime: 'edge' } ? 'edge' : 'server';\n\n/**\n * The HTTP-interactions bot host, a long-running node server around the engine.\n *\n * Discovers handlers from `config.bot.interactions.path`, verifies and dispatches interactions on\n * `start()`, and runs coordinated shutdown with an in-flight drain. The edge deploy path calls\n * `createSeedcord` from a generated entry.\n */\nexport class Seedcord<Cfg extends HttpConfig = HttpConfig>\n extends Pluggable<'http', RuntimeOfConfig<Cfg>>\n implements Core, SeedcordInstance\n{\n // the CLI reads these to detect and augment the instance\n /** @internal */\n public readonly [SeedcordBrand] = true;\n /** @internal */\n public readonly augmentTarget = '@seedcord/http';\n /** @internal */\n public readonly version: string = packageVersion;\n\n /** Workerd-compatible Discord REST client. `start()` sets the token. */\n public readonly rest = new REST();\n\n /** @see {@link IRateLimiter} */\n public readonly rateLimiter: IRateLimiter;\n\n /** @see {@link Bus} */\n public readonly bus: Bus;\n\n private readonly subscribers: SubscriberLoader;\n\n private readonly interactions?: InteractionDispatcher;\n private readonly commandRegistry?: CommandRegistry;\n private appId?: string;\n private appIdPromise?: Promise<string>;\n private readonly emojiInjector = new EmojiInjector(this, () => this.applicationId());\n private readonly hmrManager: HmrManager;\n private readonly logger = new Logger('Server', { channel: 'bot' });\n\n private server?: Server;\n private boundPort?: number;\n private fetchedUsername?: string | undefined;\n\n constructor(public readonly config: Cfg) {\n super(new CoordinatedShutdown(), new CoordinatedStartup());\n\n installNodeDefaults(config.logger);\n setBotColor(config.botColor);\n\n this.hmrManager = new HmrManager();\n this.hmrManager.init();\n\n if (this.config.bot.interactions.path) {\n this.interactions = new InteractionDispatcher(this.config.bot.interactions.path);\n }\n\n const commandsDir = this.config.bot.commands.path;\n if (commandsDir) {\n const injector = new CommandInjector();\n // onDeployed only fires at deploy time, long after registry is assigned\n const registry: CommandRegistry = new CommandRegistry({\n dir: commandsDir,\n rest: this.rest,\n applicationId: () => this.requireApplicationId(),\n onDeployed: (result) => {\n injector.inject(result, registry.allCommands());\n }\n });\n this.commandRegistry = registry;\n }\n\n this.rateLimiter = config.store ?? new MemoryRateLimiter();\n this.bus = new Bus(this);\n this.subscribers = new SubscriberLoader(this.bus, config.subscribers.path);\n\n this.registerStartupTasks();\n }\n\n /** The bot's discord username, populated by the ready fetch. */\n public get username(): string | undefined {\n return this.fetchedUsername;\n }\n\n /** The bound server port, populated once `start()` is listening. */\n public get port(): number | undefined {\n return this.boundPort;\n }\n\n /**\n * Starts the host and runs the startup tasks.\n */\n public async start(): Promise<this> {\n try {\n await super.init();\n } catch (caught) {\n await this.shutdown.run(1, false);\n Seedcord.reset();\n throw caught;\n }\n return this;\n }\n\n protected static override reset(): void {\n super.reset();\n // super.reset() drops the dev TUI's log sink\n LoggerChannelRegistry.instance.configure({});\n }\n\n private registerStartupTasks(): void {\n if (Envapter.isDevelopment || Envapter.isTest) this.registerHmrAwareModules();\n\n this.startup.addTask(StartupPhase.Configuration, 'bus-initialization', async () => {\n busLoggerOf(this.bus).utils.initialization('Subscribers', 'start');\n await this.subscribers.init();\n busLoggerOf(this.bus).utils.initialization('Subscribers', 'end');\n });\n\n const { interactions } = this;\n if (interactions) {\n this.startup.addTask(StartupPhase.Configuration, 'interactions-initialization', async () => {\n interactions.logger.utils.initialization('Interactions', 'start');\n await interactions.init();\n interactions.logger.utils.initialization('Interactions', 'end');\n });\n }\n\n this.startup.addTask(StartupPhase.Configuration, 'authenticate', () => {\n this.authenticate();\n return Promise.resolve();\n });\n\n // needs the token from Configuration, and must finish before Ready opens the server to interactions\n this.startup.addTask(StartupPhase.Login, 'emoji-injection', () => this.emojiInjector.init());\n\n const { commandRegistry } = this;\n if (commandRegistry) {\n // one task because tasks in a phase run concurrently and the deploy reads the id\n this.startup.addTask(StartupPhase.Login, 'command-deploy', async () => {\n await commandRegistry.init();\n this.appId = await this.applicationId();\n await commandRegistry.setCommands();\n interactions?.warnUnhandledRoutes(commandRegistry.routeLeaves());\n interactions?.warnUnhandledContextMenuRoutes(commandRegistry.contextMenuLeaves());\n });\n }\n\n this.startup.addTask(StartupPhase.Ready, 'http-server', () => this.listen());\n\n if (!Envapter.isTest) {\n this.startup.addTask(StartupPhase.Ready, 'identity', () => this.fetchUsername());\n }\n }\n\n private registerHmrAwareModules(): void {\n this.startup.addTask(StartupPhase.Configuration, 'hmr-registration', async () => {\n if (this.interactions) this.hmrManager.register(this.interactions);\n if (this.commandRegistry) this.hmrManager.register(this.commandRegistry);\n this.hmrManager.register(this.subscribers);\n for (const plugin of this.plugins) {\n this.hmrManager.register(plugin);\n }\n await Promise.resolve();\n });\n }\n\n private authenticate(): void {\n this.rest.setToken(validateDiscordToken(Envapter.get('DISCORD_BOT_TOKEN')));\n }\n\n private applicationId(): Promise<string> {\n this.appIdPromise ??= fetchApplicationId(this.rest);\n return this.appIdPromise;\n }\n\n // the registry redeploys on a hot reload, after the startup task resolved this\n private requireApplicationId(): string {\n if (!this.appId) throw new SeedcordError(SeedcordErrorCode.CoreApplicationUnavailable);\n return this.appId;\n }\n\n private async listen(): Promise<void> {\n const maps = this.interactions?.maps ?? buildRouteMaps(EMPTY_MANIFEST);\n const { handle, inFlight } = buildEngine(this, maps);\n\n const server = createServer((incoming, outgoing) => {\n void (async () => {\n const response = await handle(await toWebRequest(incoming));\n await writeWebResponse(response, outgoing);\n })().catch((error: unknown) => {\n // a swallowed throw would hang the client with no cause\n outgoing.destroy(Error.isError(error) ? error : new Error(String(error)));\n });\n });\n this.server = server;\n\n server.listen(this.config.port ?? DEFAULT_PORT);\n await once(server, 'listening');\n // justified: address() is AddressInfo once a TCP server is listening\n this.boundPort = (server.address() as AddressInfo).port;\n this.logger.info(`Interactions server listening on port ${paint.sky.bold(String(this.boundPort))}`);\n getDevChannel()?.send('seedcord:server-listening', { port: this.boundPort });\n\n this.shutdown.addTask(\n ShutdownPhase.Unbind,\n 'stop-http-server',\n () => this.stopServer(),\n SERVER_SHUTDOWN_TIMEOUT_MS\n );\n // Unbind already ran, so every accepted request is in the in-flight set here\n this.shutdown.addTask(\n ShutdownPhase.Drain,\n 'drain-inflight',\n async () => {\n await Promise.allSettled(inFlight);\n },\n DRAIN_TIMEOUT_MS\n );\n }\n\n private async fetchUsername(): Promise<void> {\n try {\n // justified: the @me payload carries username per the discord api contract\n const me = (await this.rest.get(Routes.user('@me'))) as { username?: string };\n this.fetchedUsername = me.username;\n if (me.username) this.logger.info(`Running as ${paint.sky.bold(me.username)}`);\n } catch (caught) {\n // a bad token errors on the first real send anyway\n this.logger.warn('could not fetch the bot identity', caught);\n }\n }\n\n private stopServer(): Promise<void> {\n const server = this.server;\n if (!server?.listening) return Promise.resolve();\n\n return new Promise((resolveClose, rejectClose) => {\n server.close((err) => {\n if (err) {\n rejectClose(err);\n return;\n }\n this.logger.info(paint.coral.bold('Interactions server stopped'));\n resolveClose();\n });\n // close() waits on idle keep-alive sockets. active responses still flush, and the task\n // timeout bounds a hung one\n server.closeIdleConnections();\n });\n }\n}\n","import { Plugin as CorePlugin } from '@seedcord/core/plugin';\n\nimport type { Core } from '#interfaces/Core';\nimport type { PluginOptions } from '@seedcord/core/plugin';\nimport type { TypedOmit } from '@seedcord/types';\n\n/** The options an HTTP plugin declares, narrowed to the transports this base serves. */\nexport type HttpPluginOptions = TypedOmit<PluginOptions, 'transport'> & { transport?: 'http' };\n\n/**\n * Base class for an HTTP plugin, binding `this.core` to the HTTP {@link Core}.\n *\n * A plugin that runs on either transport extends the base from `@seedcord/core/plugin`, whose\n * `this.core` is typed to the shared members only.\n *\n * @typeParam Opts - The transport and runtime the plugin declares.\n */\nexport abstract class Plugin<Opts extends HttpPluginOptions = { transport: 'http' }> extends CorePlugin<Opts, Core> {}\n\nexport type { PluginLifecycleSpec, PluginOptions } from '@seedcord/core/plugin';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["packageVersion","CorePlugin"],"sources":["../src/manifest/RouteManifest.ts","../src/node/InteractionDispatcher.ts","../src/node/webBridge.ts","../src/node/Seedcord.ts","../src/plugin.ts"],"sourcesContent":["import type { SelectMenuKind, SubscriptionKey } from '@seedcord/core';\nimport type { EventFrequency } from '@seedcord/types';\nimport type { ApplicationCommandType } from 'discord-api-types/v10';\n\n/** The command kinds a manifest row registers, the wire enum's other members have no handler base. */\ntype RegistrableCommandType =\n ApplicationCommandType.ChatInput | ApplicationCommandType.User | ApplicationCommandType.Message;\n\n/** One file may export several handlers. */\nexport interface RouteModule {\n /** The exported binding inside the loaded module. */\n readonly exportName: string;\n /** The source file the row came from, reported when the export is missing. */\n readonly from: string;\n /** Lazy import of the module, evaluated on first hit. */\n readonly load: () => Promise<Record<string, unknown>>;\n}\n\n/**\n * A slash or context-menu command row. `type` picks the per-kind map, `name` is the lookup key. For a\n * chat-input command with subcommands, `name` is the full route path (`'config/set'`), the same key\n * `@SlashRoute` registers.\n */\ninterface CommandRoute extends RouteModule {\n readonly name: string;\n readonly type: RegistrableCommandType;\n}\n\n/**\n * A button, select, or modal row. `kind` picks the per-kind map, `prefix` is the stable customId prefix\n * the receiver routes by (the routeKey minus its layout hash).\n */\nexport interface ComponentRoute extends RouteModule {\n readonly kind: 'button' | `${SelectMenuKind}Select` | 'modal';\n readonly prefix: string;\n}\n\n/**\n * An autocomplete row, its own route keyed by the command's route path, separate from the slash row.\n */\ninterface AutocompleteRoute extends RouteModule {\n readonly name: string;\n}\n\n/**\n * A bus subscriber row. The Bus registers it without importing the subscriber module, and resolves the\n * class on the first publish of one of its keys.\n */\ninterface SubscriberRoute extends RouteModule {\n /** The subscription keys this subscriber registers for. */\n readonly keys: readonly SubscriptionKey[];\n /** Mirrors the `@Subscribe` frequency metadata. */\n readonly frequency: EventFrequency;\n}\n\n/** A middleware row. Middleware runs on every dispatch. */\ntype MiddlewareRoute = RouteModule;\n\n/**\n * The static route table the HTTP engine dispatches through, replacing the filesystem handler scan that\n * cannot run in a bundled isolate. `seedcord build` emits it, the mount path authors it by hand.\n */\nexport interface RouteManifest {\n readonly commandRoutes: readonly CommandRoute[];\n readonly componentRoutes: readonly ComponentRoute[];\n readonly autocompleteRoutes: readonly AutocompleteRoute[];\n readonly subscriberRoutes: readonly SubscriberRoute[];\n readonly middlewareRoutes: readonly MiddlewareRoute[];\n}\n\n/** @internal */\nexport const EMPTY_MANIFEST: RouteManifest = {\n commandRoutes: [],\n componentRoutes: [],\n autocompleteRoutes: [],\n subscriberRoutes: [],\n middlewareRoutes: []\n};\n","import { HmrModuleHandler } from '@seedcord/core/hmr';\nimport { interactionRoutesOf, InteractionMetadataKey, InteractionRoutes } from '@seedcord/core/internal';\nimport { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\nimport { formatFilePath } from '@seedcord/utils';\nimport { traverseDirectory } from '@seedcord/utils/node';\nimport { Envapter } from 'envapt';\n\nimport { AutocompleteHandler } from '#handlers/interaction/AutocompleteHandler';\nimport { InteractionHandler } from '#handlers/interaction/InteractionHandler';\nimport { buildRouteMaps } from '#src/dispatch/resolve';\nimport { EMPTY_MANIFEST } from '#src/manifest/RouteManifest';\n\nimport type { HandlerConstructor } from '#handlers/constructors';\nimport type { ResolvedRoute, RouteMap, RouteMaps } from '#src/dispatch/resolve';\nimport type { Initializeable, ContextMenuLeaves } from '@seedcord/core/internal';\nimport type { HmrAware, HmrUpdateEvent } from '@seedcord/types';\n\ninterface RouteTarget {\n readonly map: RouteMap;\n readonly kind: ResolvedRoute['kind'];\n}\n\n// hmr swaps entries live and resolve() reads per request, but edge builds from manifest instead\nexport class InteractionDispatcher implements Initializeable, HmrAware {\n public readonly maps: RouteMaps;\n private readonly targets: Record<InteractionRoutes, RouteTarget>;\n\n /** @internal */\n public readonly logger = new Logger('Interactions', { channel: 'interactions' });\n\n private isInitialized = false;\n private readonly hmrHandler?: HmrModuleHandler<HandlerConstructor, void, string[]>;\n // routeId -> owner row, the duplicate guard and the hmr unregister index\n private readonly rowOwners = new Map<\n string,\n { ctor: HandlerConstructor; target: RouteTarget; key: string; from: string }\n >();\n\n private loading = false;\n private readonly loadedHandlers: { name: string; from: string }[] = [];\n\n constructor(private readonly handlersDir: string) {\n this.maps = buildRouteMaps(EMPTY_MANIFEST);\n this.targets = this.buildTargets();\n\n if (!Envapter.isDevelopment && !Envapter.isTest) return;\n this.hmrHandler = new HmrModuleHandler({\n handlersDir,\n isHandler: this.isHandler.bind(this),\n registerHandler: this.registerHandler.bind(this),\n unregisterHandler: this.unregisterHandler.bind(this),\n getArtifacts: this.getArtifacts.bind(this),\n logger: this.logger\n });\n }\n\n /** @internal */\n public async init(): Promise<void> {\n if (this.isInitialized) return;\n this.isInitialized = true;\n\n this.loading = true;\n this.loadedHandlers.length = 0;\n try {\n await traverseDirectory(this.handlersDir, (fullPath, relativePath, imported) => {\n for (const value of Object.values(imported)) {\n if (!this.isHandler(value)) continue;\n this.registerHandler(value, relativePath);\n this.hmrHandler?.trackHandler(fullPath, value);\n }\n });\n } finally {\n this.loading = false;\n }\n\n this.logger.utils.block(\n 'Loaded handlers',\n [\n ...this.logger.utils.entries(this.loadedHandlers),\n ...this.logger.utils.counts({ routes: this.rowOwners.size })\n ],\n 'debug'\n );\n }\n\n /** @internal */\n public async onHmr(event: HmrUpdateEvent): Promise<void> {\n await this.hmrHandler?.handle(event);\n }\n\n public warnUnhandledRoutes(commandLeaves: Iterable<string>): void {\n this.warnMissing(commandLeaves, this.maps.slash, 'Slash route', '@SlashRoute');\n }\n\n public warnUnhandledContextMenuRoutes(leaves: ContextMenuLeaves): void {\n this.warnMissing(leaves.user, this.maps.userContextMenu, 'User context menu', '@ContextMenuRoute');\n this.warnMissing(leaves.message, this.maps.messageContextMenu, 'Message context menu', '@ContextMenuRoute');\n }\n\n private warnMissing(names: Iterable<string>, map: RouteMap, label: string, decorator: string): void {\n for (const name of names) {\n if (map.has(name)) continue;\n this.logger.warn(`${label} ${paint.sky.bold(name)} has no registered ${paint.bold(decorator)} handler.`);\n }\n }\n\n private isHandler(value: unknown): value is HandlerConstructor {\n if (typeof value !== 'function') return false;\n // this package's own family bases, so a gateway handler in the same dir stays unregistered\n return (\n (value.prototype instanceof InteractionHandler || value.prototype instanceof AutocompleteHandler) &&\n Reflect.hasMetadata(InteractionMetadataKey, value)\n );\n }\n\n private getArtifacts(ctor: HandlerConstructor): string[] {\n const routeIds: string[] = [];\n for (const [routeId, owner] of this.rowOwners) {\n if (owner.ctor === ctor) routeIds.push(routeId);\n }\n return routeIds;\n }\n\n private targetFor(route: InteractionRoutes): RouteTarget {\n return this.targets[route];\n }\n\n private buildTargets(): Record<InteractionRoutes, RouteTarget> {\n return {\n [InteractionRoutes.Slash]: { map: this.maps.slash, kind: 'slash' },\n [InteractionRoutes.UserContextMenu]: { map: this.maps.userContextMenu, kind: 'userContextMenu' },\n [InteractionRoutes.MessageContextMenu]: { map: this.maps.messageContextMenu, kind: 'messageContextMenu' },\n [InteractionRoutes.Autocomplete]: { map: this.maps.autocomplete, kind: 'autocomplete' },\n [InteractionRoutes.Button]: { map: this.maps.components.button, kind: 'button' },\n [InteractionRoutes.StringMenu]: { map: this.maps.components.stringSelect, kind: 'stringMenu' },\n [InteractionRoutes.UserMenu]: { map: this.maps.components.userSelect, kind: 'userMenu' },\n [InteractionRoutes.RoleMenu]: { map: this.maps.components.roleSelect, kind: 'roleMenu' },\n [InteractionRoutes.ChannelMenu]: { map: this.maps.components.channelSelect, kind: 'channelMenu' },\n [InteractionRoutes.MentionableMenu]: {\n map: this.maps.components.mentionableSelect,\n kind: 'mentionableMenu'\n },\n [InteractionRoutes.Modal]: { map: this.maps.components.modal, kind: 'modal' }\n };\n }\n\n private registerHandler(ctor: HandlerConstructor, relativePath: string): void {\n const from = formatFilePath(relativePath);\n // a partial registration would orphan routes and break hmr rollback\n const writes: [RouteTarget, string][] = [];\n\n for (const [route, keys] of interactionRoutesOf(ctor)) {\n const target = this.targetFor(route);\n for (const key of keys) {\n const routeId = `${target.kind}:${key}`;\n const existing = this.rowOwners.get(routeId);\n // a different class on the same route silently shadows the existing one (last write wins)\n if (existing && existing.ctor !== ctor) {\n throw new SeedcordError(SeedcordErrorCode.InteractionDuplicateRoute, [\n routeId,\n `${existing.ctor.name} (${existing.from})`,\n `${ctor.name} (${from})`\n ]);\n }\n writes.push([target, key]);\n }\n }\n\n if (writes.length === 0) return;\n for (const [target, key] of writes) {\n const routeId = `${target.kind}:${key}`;\n target.map.set(key, { kind: target.kind, routeId, load: () => Promise.resolve(ctor) });\n this.rowOwners.set(routeId, { ctor, target, key, from });\n }\n\n if (this.loading) this.loadedHandlers.push({ name: ctor.name, from });\n }\n\n private unregisterHandler(ctor: HandlerConstructor, artifacts?: string[]): void {\n const routeIds = artifacts ?? this.getArtifacts(ctor);\n for (const routeId of routeIds) {\n const owner = this.rowOwners.get(routeId);\n if (owner?.ctor !== ctor) continue;\n this.rowOwners.delete(routeId);\n owner.target.map.delete(owner.key);\n }\n }\n}\n","import type { IncomingHttpHeaders, IncomingMessage, ServerResponse } from 'node:http';\n\nfunction toHeaders(raw: IncomingHttpHeaders): Headers {\n const headers = new Headers();\n for (const [name, value] of Object.entries(raw)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const item of value) headers.append(name, item);\n } else {\n headers.set(name, value);\n }\n }\n return headers;\n}\n\n// buffers the raw body before any parse so ed25519 verifies the exact signed bytes\nexport async function toWebRequest(request: IncomingMessage): Promise<Request> {\n const chunks: Buffer[] = [];\n // justified: the async iterator is typed any and IncomingMessage yields Buffer chunks\n for await (const chunk of request) chunks.push(chunk as Buffer);\n\n const method = request.method ?? 'GET';\n const body = method === 'GET' || method === 'HEAD' ? undefined : new Uint8Array(Buffer.concat(chunks));\n\n return new Request(`http://${request.headers.host ?? 'localhost'}${request.url ?? '/'}`, {\n method,\n headers: toHeaders(request.headers),\n ...(body !== undefined && { body })\n });\n}\n\nexport async function writeWebResponse(response: Response, out: ServerResponse): Promise<void> {\n const headers: Record<string, string | string[]> = Object.fromEntries(response.headers);\n // iterating Headers comma-joins same-name values, which corrupts Set-Cookie (a per-cookie header)\n const cookies = response.headers.getSetCookie();\n if (cookies.length > 0) headers['set-cookie'] = cookies;\n\n out.writeHead(response.status, headers);\n out.end(response.body === null ? undefined : Buffer.from(await response.arrayBuffer()));\n}\n","import { once } from 'node:events';\nimport { createServer } from 'node:http';\n\nimport { REST } from '@discordjs/rest';\nimport { Bus } from '@seedcord/core';\nimport { busLoggerOf, getDevChannel, HmrManager, setBotColor } from '@seedcord/core/internal';\nimport {\n CommandRegistry,\n CoordinatedShutdown,\n CoordinatedStartup,\n Pluggable,\n ShutdownPhase,\n StartupPhase,\n SubscriberLoader\n} from '@seedcord/core/node/internal';\nimport { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { applicationIdFromToken, SeedcordError, validateDiscordToken } from '@seedcord/errors/internal';\nimport { Logger, LoggerChannelRegistry } from '@seedcord/logger';\nimport { installNodeDefaults } from '@seedcord/logger/node';\nimport { MemoryRateLimiter } from '@seedcord/rate-limiter';\nimport { SeedcordBrand } from '@seedcord/types/internal';\nimport { Routes } from 'discord-api-types/v10';\nimport { Envapter } from 'envapt';\n\nimport { buildRouteMaps } from '#src/dispatch/resolve';\nimport { EmojiInjector } from '#src/emojis/EmojiInjector';\nimport { buildEngine } from '#src/engine';\nimport { EMPTY_MANIFEST } from '#src/manifest/RouteManifest';\n\nimport { InteractionDispatcher } from './InteractionDispatcher';\nimport { toWebRequest, writeWebResponse } from './webBridge';\nimport { version as packageVersion } from '../version';\n\nimport type { HttpConfig } from '#interfaces/Config';\nimport type { Core } from '#interfaces/Core';\nimport type { IRateLimiter } from '@seedcord/types';\nimport type { SeedcordInstance } from '@seedcord/types/internal';\nimport type { Server } from 'node:http';\nimport type { AddressInfo } from 'node:net';\n\nconst DEFAULT_PORT = 3000;\nconst SERVER_SHUTDOWN_TIMEOUT_MS = 5000;\nconst DRAIN_TIMEOUT_MS = 10_000;\n\ntype RuntimeOfConfig<Cfg extends HttpConfig> = Cfg extends { runtime: 'edge' } ? 'edge' : 'server';\n\n/**\n * The HTTP-interactions bot host, a long-running node server around the engine.\n *\n * Discovers handlers from `config.bot.interactions.path`, verifies and dispatches interactions on\n * `start()`, and runs coordinated shutdown with an in-flight drain. The edge deploy path calls\n * `createSeedcord` from a generated entry.\n */\nexport class Seedcord<Cfg extends HttpConfig = HttpConfig>\n extends Pluggable<'http', RuntimeOfConfig<Cfg>>\n implements Core, SeedcordInstance\n{\n // the CLI reads these to detect and augment the instance\n /** @internal */\n public readonly [SeedcordBrand] = true;\n /** @internal */\n public readonly augmentTarget = '@seedcord/http';\n /** @internal */\n public readonly version: string = packageVersion;\n\n /** Workerd-compatible Discord REST client. `start()` sets the token. */\n public readonly rest = new REST();\n\n /** @see {@link IRateLimiter} */\n public readonly rateLimiter: IRateLimiter;\n\n /** @see {@link Bus} */\n public readonly bus: Bus;\n\n private readonly subscribers: SubscriberLoader;\n\n private readonly interactions?: InteractionDispatcher;\n private readonly commandRegistry?: CommandRegistry;\n private readonly emojiInjector = new EmojiInjector(this);\n private readonly hmrManager: HmrManager;\n private readonly logger = new Logger('Server', { channel: 'bot' });\n\n private token?: string;\n private server?: Server;\n private boundPort?: number;\n private fetchedUsername?: string | undefined;\n\n constructor(public readonly config: Cfg) {\n super(new CoordinatedShutdown(), new CoordinatedStartup());\n\n installNodeDefaults(config.logger);\n setBotColor(config.botColor);\n\n this.hmrManager = new HmrManager();\n this.hmrManager.init();\n\n if (this.config.bot.interactions.path) {\n this.interactions = new InteractionDispatcher(this.config.bot.interactions.path);\n }\n\n if (this.config.bot.commands.path) this.commandRegistry = new CommandRegistry(this);\n\n this.rateLimiter = config.store ?? new MemoryRateLimiter();\n this.bus = new Bus(this);\n this.subscribers = new SubscriberLoader(this.bus, config.subscribers.path);\n\n this.registerStartupTasks();\n }\n\n /** The bot's discord username, populated by the ready fetch. */\n public get username(): string | undefined {\n return this.fetchedUsername;\n }\n\n /** The bound server port, populated once `start()` is listening. */\n public get port(): number | undefined {\n return this.boundPort;\n }\n\n /**\n * Starts the host and runs the startup tasks.\n */\n public async start(): Promise<this> {\n try {\n await super.init();\n } catch (caught) {\n await this.shutdown.run(1, false);\n Seedcord.reset();\n throw caught;\n }\n return this;\n }\n\n protected static override reset(): void {\n super.reset();\n // super.reset() drops the dev TUI's log sink\n LoggerChannelRegistry.instance.configure({});\n }\n\n private registerStartupTasks(): void {\n if (Envapter.isDevelopment || Envapter.isTest) this.registerHmrAwareModules();\n\n this.startup.addTask(StartupPhase.Configuration, 'bus-initialization', async () => {\n busLoggerOf(this.bus).utils.initialization('Subscribers', 'start');\n await this.subscribers.init();\n busLoggerOf(this.bus).utils.initialization('Subscribers', 'end');\n });\n\n const { interactions } = this;\n if (interactions) {\n this.startup.addTask(StartupPhase.Configuration, 'interactions-initialization', async () => {\n interactions.logger.utils.initialization('Interactions', 'start');\n await interactions.init();\n interactions.logger.utils.initialization('Interactions', 'end');\n });\n }\n\n this.startup.addTask(StartupPhase.Configuration, 'authenticate', () => {\n this.authenticate();\n return Promise.resolve();\n });\n\n // needs the token from Configuration, and must finish before Ready opens the server to interactions\n this.startup.addTask(StartupPhase.Login, 'emoji-injection', () => this.emojiInjector.init());\n\n const { commandRegistry } = this;\n if (commandRegistry) {\n // one task because tasks in a phase run concurrently and the deploy reads the id\n this.startup.addTask(StartupPhase.Login, 'command-deploy', async () => {\n await commandRegistry.init();\n await commandRegistry.setCommands();\n interactions?.warnUnhandledRoutes(commandRegistry.routeLeaves());\n interactions?.warnUnhandledContextMenuRoutes(commandRegistry.contextMenuLeaves());\n });\n }\n\n this.startup.addTask(StartupPhase.Ready, 'http-server', () => this.listen());\n\n if (!Envapter.isTest) {\n this.startup.addTask(StartupPhase.Ready, 'identity', () => this.fetchUsername());\n }\n }\n\n private registerHmrAwareModules(): void {\n this.startup.addTask(StartupPhase.Configuration, 'hmr-registration', async () => {\n if (this.interactions) this.hmrManager.register(this.interactions);\n if (this.commandRegistry) this.hmrManager.register(this.commandRegistry);\n this.hmrManager.register(this.subscribers);\n for (const plugin of this.plugins) {\n this.hmrManager.register(plugin);\n }\n await Promise.resolve();\n });\n }\n\n private authenticate(): void {\n this.token = validateDiscordToken(Envapter.get('DISCORD_BOT_TOKEN'));\n this.rest.setToken(this.token);\n }\n\n /** The bot's Discord application id. Throws if you read it before the Configuration phase. */\n public get applicationId(): string {\n if (!this.token) throw new SeedcordError(SeedcordErrorCode.CoreApplicationUnavailable);\n return applicationIdFromToken(this.token);\n }\n\n private async listen(): Promise<void> {\n const maps = this.interactions?.maps ?? buildRouteMaps(EMPTY_MANIFEST);\n const { handle, inFlight } = buildEngine(this, maps);\n\n const server = createServer((incoming, outgoing) => {\n void (async () => {\n const response = await handle(await toWebRequest(incoming));\n await writeWebResponse(response, outgoing);\n })().catch((error: unknown) => {\n // a swallowed throw would hang the client with no cause\n outgoing.destroy(Error.isError(error) ? error : new Error(String(error)));\n });\n });\n this.server = server;\n\n server.listen(this.config.port ?? DEFAULT_PORT);\n await once(server, 'listening');\n // justified: address() is AddressInfo once a TCP server is listening\n this.boundPort = (server.address() as AddressInfo).port;\n this.logger.info(`Interactions server listening on port ${paint.sky.bold(String(this.boundPort))}`);\n getDevChannel()?.send('seedcord:server-listening', { port: this.boundPort });\n\n this.shutdown.addTask(\n ShutdownPhase.Unbind,\n 'stop-http-server',\n () => this.stopServer(),\n SERVER_SHUTDOWN_TIMEOUT_MS\n );\n // Unbind already ran, so every accepted request is in the in-flight set here\n this.shutdown.addTask(\n ShutdownPhase.Drain,\n 'drain-inflight',\n async () => {\n await Promise.allSettled(inFlight);\n },\n DRAIN_TIMEOUT_MS\n );\n }\n\n private async fetchUsername(): Promise<void> {\n try {\n // justified: the @me payload carries username per the discord api contract\n const me = (await this.rest.get(Routes.user('@me'))) as { username?: string };\n this.fetchedUsername = me.username;\n if (me.username) this.logger.info(`Running as ${paint.sky.bold(me.username)}`);\n } catch (caught) {\n // a bad token errors on the first real send anyway\n this.logger.warn('could not fetch the bot identity', caught);\n }\n }\n\n private stopServer(): Promise<void> {\n const server = this.server;\n if (!server?.listening) return Promise.resolve();\n\n return new Promise((resolveClose, rejectClose) => {\n server.close((err) => {\n if (err) {\n rejectClose(err);\n return;\n }\n this.logger.info(paint.coral.bold('Interactions server stopped'));\n resolveClose();\n });\n // close() waits on idle keep-alive sockets. active responses still flush, and the task\n // timeout bounds a hung one\n server.closeIdleConnections();\n });\n }\n}\n","import { Plugin as CorePlugin } from '@seedcord/core/plugin';\n\nimport type { Core } from '#interfaces/Core';\nimport type { PluginOptions } from '@seedcord/core/plugin';\nimport type { TypedOmit } from '@seedcord/types';\n\n/** The options an HTTP plugin declares, narrowed to the transports this base serves. */\nexport type HttpPluginOptions = TypedOmit<PluginOptions, 'transport'> & { transport?: 'http' };\n\n/**\n * Base class for an HTTP plugin, binding `this.core` to the HTTP {@link Core}.\n *\n * A plugin that runs on either transport extends the base from `@seedcord/core/plugin`, whose\n * `this.core` is typed to the shared members only.\n *\n * @typeParam Opts - The transport and runtime the plugin declares.\n */\nexport abstract class Plugin<Opts extends HttpPluginOptions = { transport: 'http' }> extends CorePlugin<Opts, Core> {}\n\nexport type { PluginLifecycleSpec, PluginOptions } from '@seedcord/core/plugin';\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seedcord/http",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.1",
|
|
5
5
|
"description": "HTTP interactions transport for seedcord bots",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"discord",
|
|
@@ -57,13 +57,13 @@
|
|
|
57
57
|
"discord-api-types": "^0.38.52",
|
|
58
58
|
"reflect-metadata": "^0.2.2",
|
|
59
59
|
"type-fest": "^5.8.0",
|
|
60
|
-
"@seedcord/core": "^0.3.
|
|
61
|
-
"@seedcord/errors": "^0.4.
|
|
60
|
+
"@seedcord/core": "^0.3.1",
|
|
61
|
+
"@seedcord/errors": "^0.4.3",
|
|
62
62
|
"@seedcord/event-emitter": "^0.1.4",
|
|
63
|
+
"@seedcord/logger": "^0.2.1",
|
|
63
64
|
"@seedcord/rate-limiter": "^0.1.4",
|
|
64
65
|
"@seedcord/types": "^0.9.1",
|
|
65
|
-
"@seedcord/utils": "^0.8.
|
|
66
|
-
"@seedcord/logger": "^0.2.1"
|
|
66
|
+
"@seedcord/utils": "^0.8.7"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
69
|
"envapt": "^8.1.1",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"envapt": "^8.1.1",
|
|
77
77
|
"esbuild": "^0.28.1",
|
|
78
78
|
"vitest": "^4.1.10",
|
|
79
|
-
"@seedcord/eslint-config": "^
|
|
79
|
+
"@seedcord/eslint-config": "^2.0.0",
|
|
80
80
|
"@seedcord/tsconfig": "^2.0.3",
|
|
81
81
|
"@seedcord/tsdown-config": "^2.0.1",
|
|
82
82
|
"@seedcord/vitest-config": "^0.0.1"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"edge.index-D9PlLJ9b.mjs","names":["RepliableHandler","CoreRepliableHandler","RepliableHandler","RepliableHandler","Subscriber","CoreSubscriber","WebhookLog","CoreWebhookLog","ArraySource","BaseArraySource","CursorSource","BaseCursorSource"],"sources":["../src/reply/ReplySender.ts","../src/api.ts","../src/handlers/RepliableHandler.ts","../src/gates/context.ts","../src/dispatch/slashRouteOf.ts","../src/dispatch/reportFault.ts","../src/dispatch/dispatchInteraction.ts","../src/dispatch/registerSubscribers.ts","../src/inputs/HttpSlashOptions.ts","../src/handlers/interaction/AutocompleteHandler.ts","../src/handlers/defaults/UnhandledAutocomplete.ts","../src/handlers/interaction/InteractionHandler.ts","../src/handlers/defaults/UnhandledRepliable.ts","../src/dispatch/resolve.ts","../src/receiver/Ed25519Verifier.ts","../src/receiver/ReplayGuard.ts","../src/engine.ts","../src/createSeedcord.ts","../src/applicationId.ts","../src/emojis/EmojiInjector.ts","../src/handlers/interaction/SlashHandler.ts","../src/handlers/interaction/ContextMenuHandler.ts","../src/handlers/interaction/components/ComponentHandler.ts","../src/handlers/interaction/components/ButtonHandler.ts","../src/handlers/interaction/components/ModalHandler.ts","../src/handlers/interaction/components/SelectMenuHandler.ts","../src/subscribers/index.ts","../src/pagination/Paginator.ts","../src/pagination/sources.ts","../src/gates/Gated.ts","../src/version.ts"],"sourcesContent":["import { BaseReplySender, deferFlags, sendFlags } from '@seedcord/core/internal';\nimport { InteractionResponseType, MessageFlags, Routes } from 'discord-api-types/v10';\n\nimport type { REST, RawFile } from '@discordjs/rest';\nimport type { Bus } from '@seedcord/core';\nimport type { SerializedReply } from '@seedcord/core/internal';\nimport type { DeferOpts, ReplyResponse, SendOpts, TypedOmit } from '@seedcord/types';\nimport type { APIMessage, APIModalInteractionResponseCallbackData } from 'discord-api-types/v10';\n\n/** The interaction payload fields the sender writes its wire from. */\nexport interface InteractionRef {\n readonly application_id: string;\n readonly id: string;\n readonly token: string;\n}\n\n/** The created message an http reply resolves to (the http `SentMessage` lens). */\nexport type SentMessage = APIMessage;\n\n// the framework's allowedMentions keys are camelCase, and the wire reads replied_user\ntype WireAllowedMentions = TypedOmit<NonNullable<ReplyResponse['allowedMentions']>, 'repliedUser'> & {\n replied_user?: boolean;\n};\n\ninterface WireAttachment {\n id: number;\n filename: string;\n description?: string;\n title?: string;\n}\n\ninterface MessageBody {\n flags: number;\n components: SerializedReply['components'];\n allowed_mentions?: WireAllowedMentions;\n attachments?: WireAttachment[];\n}\n\nfunction wireAllowedMentions(mentions: NonNullable<ReplyResponse['allowedMentions']>): WireAllowedMentions {\n const { repliedUser, ...wire } = mentions;\n return { ...wire, ...(repliedUser !== undefined && { replied_user: repliedUser }) };\n}\n\n// RawFile has no description field\nfunction wireAttachments(files: SerializedReply['files']): WireAttachment[] | null {\n if (!files?.some((file) => file.description ?? file.title)) return null;\n return files.map((file, index) => ({\n id: index,\n filename: file.name,\n ...(file.description && { description: file.description }),\n ...(file.title && { title: file.title })\n }));\n}\n\nfunction callbackData(reply: SerializedReply, flags: number): MessageBody {\n const attachments = wireAttachments(reply.files);\n return {\n flags,\n components: reply.components,\n ...(reply.allowedMentions && { allowed_mentions: wireAllowedMentions(reply.allowedMentions) }),\n ...(attachments && { attachments })\n };\n}\n\n/**\n * Writes interaction responses over the Discord REST callback and webhook endpoints. Construction is\n * internal to the repliable handler bases and the dispatcher.\n */\nexport class ReplySender extends BaseReplySender<SentMessage> {\n public constructor(\n private readonly ref: InteractionRef,\n private readonly rest: REST,\n routeId: string,\n bus: Bus\n ) {\n super(routeId, { bus, interactionId: ref.id });\n }\n\n protected async writeReply(response: ReplyResponse | string, opts?: SendOpts): Promise<SentMessage | undefined> {\n const reply = this.serialize(response);\n const result = await this.callback(\n InteractionResponseType.ChannelMessageWithSource,\n callbackData(reply, sendFlags(opts)),\n reply.files\n );\n return this.createdMessage(result);\n }\n\n protected async writeDefer(opts?: DeferOpts): Promise<void> {\n await this.callback(InteractionResponseType.DeferredChannelMessageWithSource, { flags: deferFlags(opts) });\n }\n\n protected async writeDeferUpdate(): Promise<void> {\n await this.callback(InteractionResponseType.DeferredMessageUpdate);\n }\n\n protected async writeUpdate(response: ReplyResponse | string): Promise<SentMessage | undefined> {\n const reply = this.serialize(response);\n const result = await this.callback(\n InteractionResponseType.UpdateMessage,\n callbackData(reply, MessageFlags.IsComponentsV2),\n reply.files\n );\n return this.createdMessage(result);\n }\n\n protected async writeFollowUp(response: ReplyResponse | string, opts?: SendOpts): Promise<SentMessage> {\n const reply = this.serialize(response);\n // discord returns the created message for interaction-token followups either way. wait=true pins\n // that contract and matches the djs webhook client\n const result = await this.rest.post(Routes.webhook(this.ref.application_id, this.ref.token), {\n body: callbackData(reply, sendFlags(opts)),\n query: new URLSearchParams({ wait: 'true' }),\n ...(reply.files && { files: this.rawFiles(reply.files) })\n });\n // justified: a webhook POST returns the created message\n return result as SentMessage;\n }\n\n protected writeEditOriginal(response: ReplyResponse | string): Promise<SentMessage> {\n return this.editMessage(\n Routes.webhookMessage(this.ref.application_id, this.ref.token),\n this.serialize(response)\n );\n }\n\n protected writeEditTarget(targetId: string, response: ReplyResponse | string): Promise<SentMessage> {\n const route = Routes.webhookMessage(this.ref.application_id, this.ref.token, targetId);\n return this.editMessage(route, this.serialize(response));\n }\n\n protected async writeDeleteOriginal(): Promise<void> {\n await this.rest.delete(Routes.webhookMessage(this.ref.application_id, this.ref.token));\n }\n\n protected async writeDeleteTarget(targetId: string): Promise<void> {\n await this.rest.delete(Routes.webhookMessage(this.ref.application_id, this.ref.token, targetId));\n }\n\n protected async writeModal(data: APIModalInteractionResponseCallbackData): Promise<void> {\n await this.callback(InteractionResponseType.Modal, data);\n }\n\n private async callback(\n type: InteractionResponseType,\n data?: unknown,\n files?: SerializedReply['files']\n ): Promise<unknown> {\n const withResponse =\n type === InteractionResponseType.ChannelMessageWithSource || type === InteractionResponseType.UpdateMessage;\n return await this.rest.post(Routes.interactionCallback(this.ref.id, this.ref.token), {\n body: { type, ...(data !== undefined && { data }) },\n ...(withResponse && { query: new URLSearchParams({ with_response: 'true' }) }),\n ...(files && { files: this.rawFiles(files) })\n });\n }\n\n private async editMessage(route: `/${string}`, reply: SerializedReply): Promise<SentMessage> {\n const result = await this.rest.patch(route, {\n body: callbackData(reply, MessageFlags.IsComponentsV2),\n ...(reply.files && { files: this.rawFiles(reply.files) })\n });\n // justified: a webhook message PATCH returns the edited message\n return result as SentMessage;\n }\n\n private rawFiles(files: NonNullable<SerializedReply['files']>): RawFile[] {\n return files.map((file) => ({ name: file.name, data: file.data }));\n }\n\n private createdMessage(result: unknown): SentMessage | undefined {\n // justified: the with_response callback wire shape\n return (result as { resource?: { message?: SentMessage } }).resource?.message;\n }\n}\n","import { API } from '@discordjs/core/http-only';\n\nimport type { REST } from '@discordjs/rest';\n\nconst apis = new WeakMap<REST, API>();\n\nexport function apiFor(rest: REST): API {\n let api = apis.get(rest);\n if (!api) {\n api = new API(rest);\n apis.set(rest, api);\n }\n return api;\n}\n","import { RepliableHandler as CoreRepliableHandler } from '@seedcord/core';\n\nimport { ReplySender } from '#reply/ReplySender';\nimport { apiFor } from '#src/api';\n\nimport type { Core } from '#interfaces/Core';\nimport type { SentMessage } from '#reply/ReplySender';\nimport type { Repliables } from './interactionTypes';\nimport type { API } from '@discordjs/core/http-only';\n\n/**\n * Shared base the repliable HTTP interaction handlers extend.\n *\n * Not a public entry point. Extend {@link SlashHandler}, {@link ButtonHandler}, {@link ModalHandler},\n * {@link SelectMenuHandler}, or {@link ContextMenuHandler} instead. This class binds the core reply base\n * to the http sender.\n *\n * @typeParam Event - The repliable interaction type this handler processes\n */\nexport abstract class RepliableHandler<Event extends Repliables> extends CoreRepliableHandler<\n Event,\n Core,\n SentMessage,\n never,\n ReplySender\n> {\n // core's base keeps event protected, and Paginator.start reads it from outside the class\n /** @internal */\n public getEvent(): Event {\n return this.event;\n }\n\n protected buildSender(event: Event, core: Core, routeId: string): ReplySender {\n const ref = { application_id: event.application_id, id: event.id, token: event.token };\n return new ReplySender(ref, core.rest, routeId, core.bus);\n }\n\n /**\n * Typed Discord REST API (`@discordjs/core/http-only`) over `core.rest`, one instance per core.\n * The interaction callbacks on `api.interactions` bypass the reply surface. The\n * `no-raw-interaction-acks` rule flags them in handler classes.\n */\n protected get api(): API {\n return apiFor(this.core.rest);\n }\n}\n","import type { Core } from '#interfaces/Core';\nimport type { Repliables } from '#src/handlers/interactionTypes';\nimport type { InteractionGateContext } from './Gate';\nimport type { APIUser } from 'discord-api-types/v10';\n\n// autocomplete is skipped before gates run, so the payload here always carries a reply target\nexport function interactionGateContext(\n payload: Repliables,\n core: Core,\n routeId: string | null\n): InteractionGateContext {\n const member = payload.member ?? null;\n // the wrapper types leave member and user both optional. Discord sends member.user in a guild and the\n // top-level user in a DM\n const resolved = member?.user ?? payload.user;\n return {\n kind: 'interaction',\n core,\n interaction: payload,\n // justified: every repliable interaction Discord delivers carries a user, the wrapper types don't say so\n user: resolved as APIUser,\n member,\n userId: resolved?.id ?? null,\n guildId: payload.guild_id ?? null,\n channelId: payload.channel?.id ?? null,\n memberRoleIds: member?.roles ?? [],\n memberPermissions: member ? BigInt(member.permissions) : null,\n // app_permissions is present on every interaction Discord delivers, including DMs\n appPermissions: BigInt(payload.app_permissions),\n routeId\n };\n}\n","import { buildSlashRoute } from '@seedcord/utils/internal';\nimport { ApplicationCommandOptionType } from 'discord-api-types/v10';\n\nimport type {\n APIApplicationCommandAutocompleteInteraction,\n APIChatInputApplicationCommandInteraction\n} from 'discord-api-types/v10';\n\nexport type SlashLikeData = (\n APIChatInputApplicationCommandInteraction | APIApplicationCommandAutocompleteInteraction\n)['data'];\n\n// the raw-payload port of the gateway's slashRouteOf\n// both assemble the key through buildSlashRoute, so a dispatched interaction always matches its generated registry key\nexport function slashRouteOf(data: SlashLikeData): string {\n const first = data.options?.[0];\n if (first?.type === ApplicationCommandOptionType.SubcommandGroup) {\n return buildSlashRoute(data.name, first.options[0]?.name, first.name);\n }\n if (first?.type === ApplicationCommandOptionType.Subcommand) {\n return buildSlashRoute(data.name, first.name);\n }\n return data.name;\n}\n","import { Notice } from '@seedcord/core';\nimport { PublishDefault } from '@seedcord/core/internal';\nimport { ApplicationCommandType, ComponentType, InteractionType } from 'discord-api-types/v10';\n\nimport { slashRouteOf } from './slashRouteOf';\n\nimport type { ValidInteractionTypes } from '#handlers/interactionTypes';\nimport type { Core } from '#interfaces/Core';\nimport type { FaultSource, SubscriptionData } from '@seedcord/core';\nimport type { RenderContext } from '@seedcord/types';\n\ntype InteractionFaultSource = Extract<FaultSource, { kind: 'interaction' }>;\n\n// the raw-payload twin of gateway's extractErrorResponse\nexport function reportFault(\n error: Error,\n uuid: RenderContext['uuid'],\n routeId: string,\n payload: ValidInteractionTypes,\n core: Core\n): void {\n const source = interactionSource(payload);\n if (error instanceof Notice && source)\n core.bus[PublishDefault]('handledException', { denial: error, uuid, routeId, source });\n else core.bus[PublishDefault]('unknownException', { uuid, error, routeId, ...actors(payload), metadata: payload });\n}\n\n// the raw payload has guild_id and no guild name\nfunction actors(payload: ValidInteractionTypes): Pick<SubscriptionData<'unknownException'>, 'guild' | 'user'> {\n const user = payload.member?.user ?? payload.user;\n return { guild: undefined, user: user ? { id: user.id, username: user.username } : undefined };\n}\n\nfunction interactionSource(payload: ValidInteractionTypes): InteractionFaultSource | null {\n // discord sends member.user in a guild and user in a dm\n const user = payload.member?.user ?? payload.user;\n if (!user) return null;\n\n const common = {\n kind: 'interaction',\n userId: user.id,\n guildId: payload.guild_id ?? null,\n channelId: payload.channel?.id ?? null,\n interactionId: payload.id,\n raw: payload\n } as const;\n\n if (payload.type === InteractionType.ApplicationCommand) {\n const { data } = payload;\n const isSlash = data.type === ApplicationCommandType.ChatInput;\n return {\n ...common,\n interactionKind: isSlash ? 'slash' : 'context-menu',\n command: isSlash ? slashRouteOf(data) : data.name,\n customId: null\n };\n }\n if (payload.type === InteractionType.ModalSubmit) {\n return { ...common, interactionKind: 'modal', command: null, customId: payload.data.custom_id };\n }\n if (payload.type === InteractionType.MessageComponent) {\n const isButton = payload.data.component_type === ComponentType.Button;\n return {\n ...common,\n interactionKind: isButton ? 'button' : 'select',\n command: null,\n customId: payload.data.custom_id\n };\n }\n return null;\n}\n","import { DiscordAPIError, REST } from '@discordjs/rest';\nimport { BaseHandler, Bus, DispatchContext, Fault, Notice, Silence } from '@seedcord/core';\nimport {\n asError,\n outcomeFor,\n queuedMsFor,\n reportDispatch,\n reportedWrite,\n runHandlerGates,\n slowGateMonitor\n} from '@seedcord/core/internal';\nimport { paint } from '@seedcord/errors';\nimport { Logger } from '@seedcord/logger';\nimport { MemoryRateLimiter } from '@seedcord/rate-limiter';\nimport { InteractionResponseType, InteractionType, RESTJSONErrorCodes, Routes } from 'discord-api-types/v10';\n\nimport { RepliableHandler } from '#handlers/RepliableHandler';\nimport { ReplySender } from '#reply/ReplySender';\nimport { interactionGateContext } from '#src/gates/context';\n\nimport { reportFault } from './reportFault';\n\nimport type { ValidInteractionTypes } from '#handlers/interactionTypes';\nimport type { HttpConfig } from '#interfaces/Config';\nimport type { Core } from '#interfaces/Core';\nimport type { ResolvedRoute } from './resolve';\nimport type { DispatchOutcome } from '@seedcord/core';\nimport type { IRateLimiter, RenderContext, TypedOmit } from '@seedcord/types';\n\n// lazy, env binds after this module loads\nlet dispatchLogger: Logger | undefined;\nfunction logger(): Logger {\n dispatchLogger ??= new Logger('Dispatcher', { channel: 'interactions' });\n return dispatchLogger;\n}\n\ninterface HttpHandler {\n execute(): Promise<void>;\n}\n\ntype HandlerCtor = new (event: ValidInteractionTypes, core: Core, dispatch?: DispatchContext) => HttpHandler;\n\ntype CoreDraft = TypedOmit<Core, 'bus'> & { bus: Bus };\n\nexport function createCore(config: HttpConfig, token: string): Core {\n const rateLimiter: IRateLimiter = config.store ?? new MemoryRateLimiter();\n // justified: bus completes the shape on the next line, and the Bus reads core at dispatch\n const draft = { config, rateLimiter, rest: new REST().setToken(token) } as CoreDraft;\n draft.bus = new Bus(draft);\n return draft;\n}\n\nfunction isHandlerCtor(value: unknown): value is HandlerCtor {\n return typeof value === 'function' && value.prototype instanceof BaseHandler;\n}\n\ninterface FaultScope {\n readonly core: Core;\n readonly payload: ValidInteractionTypes;\n readonly routeId: string;\n // null on autocomplete, whose only legal refusal is an empty type 8\n readonly sender: ReplySender | null;\n}\n\n// each one means the interaction is already gone\nconst HARMLESS_API_CODES: ReadonlySet<number | string> = new Set([\n RESTJSONErrorCodes.UnknownInteraction,\n RESTJSONErrorCodes.InteractionHasAlreadyBeenAcknowledged,\n RESTJSONErrorCodes.UnknownMessage\n]);\n\nasync function sendGuarded(routeId: string, send: () => Promise<unknown>): Promise<void> {\n try {\n await send();\n } catch (error) {\n if (error instanceof DiscordAPIError && HARMLESS_API_CODES.has(error.code)) {\n logger().debug(`boundary send hit harmless code ${paint.amber(String(error.code))}`);\n return;\n }\n logger().error(`boundary send failed for route ${paint.sky.bold(routeId)}`, error);\n }\n}\n\n// a message reply is illegal on autocomplete, so empty choices are the only way to clear the pending state\nasync function respondEmptyChoices(scope: FaultScope): Promise<void> {\n const telemetry = { bus: scope.core.bus, interactionId: scope.payload.id };\n await reportedWrite(telemetry, scope.routeId, 'respond', () =>\n scope.core.rest.post(Routes.interactionCallback(scope.payload.id, scope.payload.token), {\n body: { type: InteractionResponseType.ApplicationCommandAutocompleteResult, data: { choices: [] } }\n })\n );\n}\n\nfunction renderContext(core: Core, uuid: RenderContext['uuid']): RenderContext {\n const developerUsername = core.config.notifications?.developerUsername;\n return developerUsername === undefined ? { uuid } : { uuid, developerUsername };\n}\n\nasync function handleNotice(notice: Notice, uuid: RenderContext['uuid'], scope: FaultScope): Promise<void> {\n if (notice.report) {\n logger().error(`${notice.name}: ${paint.mute(uuid)}`, notice);\n reportFault(notice, uuid, scope.routeId, scope.payload, scope.core);\n }\n const { sender } = scope;\n if (!sender) {\n await sendGuarded(scope.routeId, () => respondEmptyChoices(scope));\n return;\n }\n const response = notice.render(renderContext(scope.core, uuid));\n await sendGuarded(scope.routeId, () => sender.send(response, { ephemeral: notice.ephemeral }));\n}\n\nasync function handleRawFault(error: Error, uuid: RenderContext['uuid'], scope: FaultScope): Promise<void> {\n const { core, sender } = scope;\n\n if (core.config.errors?.errorStack ?? false) logger().error(paint.mute(uuid), error);\n else logger().error(`${paint.mute(uuid)} | ${error.message}`);\n\n reportFault(error, uuid, scope.routeId, scope.payload, core);\n\n if (!sender) {\n await sendGuarded(scope.routeId, () => respondEmptyChoices(scope));\n return;\n }\n\n const override = core.config.errors?.defaultError;\n const card = override ? new override(uuid) : new Fault();\n const response = card.render(renderContext(core, uuid));\n await sendGuarded(scope.routeId, () => sender.send(response, { ephemeral: true }));\n}\n\nasync function handleFault(caught: unknown, scope: FaultScope): Promise<void> {\n if (caught instanceof Silence) {\n if (caught.reason !== undefined && (scope.core.config.errors?.logSilences ?? true)) {\n logger().debug(`Silence: ${caught.reason}`);\n }\n return;\n }\n\n const uuid = crypto.randomUUID();\n if (caught instanceof Notice) {\n await handleNotice(caught, uuid, scope);\n return;\n }\n\n const error = asError(caught);\n\n const ignore = new Set<number | string>(scope.core.config.errors?.ignoreApiCodes ?? []);\n if (error instanceof DiscordAPIError && ignore.has(error.code)) {\n logger().debug(`swallowed api code ${paint.amber(String(error.code))}`);\n return;\n }\n\n await handleRawFault(error, uuid, scope);\n}\n\ninterface DispatchArgs {\n readonly match: ResolvedRoute;\n readonly payload: ValidInteractionTypes;\n readonly core: Core;\n}\n\nfunction unhandledRouteId(match: ResolvedRoute): string {\n if (match.routeId) return match.routeId;\n // an empty key means a customId seedcord never minted, since a minted routeKey always outlives its 3-char hash\n const key = match.attemptedKey ?? '';\n return `${match.kind}:${key.length > 0 ? key : 'unrouted'}`;\n}\n\n// nothing is acked yet here, so a fresh sender can reply the card\nfunction freshScope(match: ResolvedRoute, payload: ValidInteractionTypes, core: Core): FaultScope {\n const ref = { application_id: payload.application_id, id: payload.id, token: payload.token };\n const routeId = unhandledRouteId(match);\n return {\n core,\n payload,\n routeId,\n sender: match.kind === 'autocomplete' ? null : new ReplySender(ref, core.rest, routeId, core.bus)\n };\n}\n\nfunction dispatchReporter(\n match: ResolvedRoute,\n payload: ValidInteractionTypes,\n core: Core\n): (outcome: DispatchOutcome) => void {\n const startedAt = performance.now();\n // reading this at publish time would count the handler run into the queue too\n const queuedMs = queuedMsFor(payload.id);\n return (outcome) => {\n reportDispatch(core.bus, {\n routeId: unhandledRouteId(match),\n interactionId: payload.id,\n kind: match.kind,\n outcome,\n fallback: match.routeId === null,\n startedAt,\n queuedMs\n });\n };\n}\n\nasync function answer(\n caught: unknown,\n scope: FaultScope,\n report: (outcome: DispatchOutcome) => void,\n outcome: DispatchOutcome = outcomeFor(caught)\n): Promise<void> {\n try {\n await handleFault(caught, scope);\n } finally {\n report(outcome);\n }\n}\n\nasync function loadHandlerCtor(\n match: ResolvedRoute,\n payload: ValidInteractionTypes,\n core: Core,\n report: (outcome: DispatchOutcome) => void\n): Promise<HandlerCtor | null> {\n const routeId = unhandledRouteId(match);\n let exported: unknown;\n try {\n exported = await match.load();\n } catch (caught) {\n logger().error(`Route ${paint.sky.bold(routeId)} failed to load its handler.`, caught);\n await answer(caught, freshScope(match, payload, core), report, 'failed');\n return null;\n }\n\n if (isHandlerCtor(exported)) return exported;\n\n logger().error(`Route ${paint.sky.bold(routeId)} loaded an export that is not a handler class.`);\n const wrong = new Error(`route ${routeId} loaded an export that is not a handler class`);\n await answer(wrong, freshScope(match, payload, core), report, 'failed');\n return null;\n}\n\n// a null return means the refusal is already sent\nexport async function dispatchInteraction(args: DispatchArgs): Promise<(() => Promise<void>) | null> {\n const { match, payload, core } = args;\n const report = dispatchReporter(match, payload, core);\n\n const ctor = await loadHandlerCtor(match, payload, core, report);\n if (!ctor) return null;\n\n const routeId = unhandledRouteId(match);\n logger().debug(`Processing ${paint.sky.bold(routeId)} with ${paint.mute(ctor.name)}`);\n\n const dispatch = new DispatchContext(routeId);\n let handler: HttpHandler;\n try {\n handler = new ctor(payload, core, dispatch);\n } catch (caught) {\n await answer(caught, freshScope(match, payload, core), report);\n return null;\n }\n const scope: FaultScope = {\n core,\n payload,\n routeId,\n sender: handler instanceof RepliableHandler ? handler.getSender() : null\n };\n\n const refusal = await gateRefusal(ctor, match, payload, core);\n if (refusal) {\n await answer(refusal.caught, scope, report);\n return null;\n }\n\n return async () => {\n try {\n await handler.execute();\n report('handled');\n } catch (caught) {\n await answer(caught, scope, report);\n }\n };\n}\n\n// autocomplete has no reply target. @Gated rejects it at compile time and this is the runtime backstop\nasync function gateRefusal(\n ctor: HandlerCtor,\n match: ResolvedRoute,\n payload: ValidInteractionTypes,\n core: Core\n): Promise<{ caught: unknown } | null> {\n // match.kind comes from payload.type in the router. the second clause narrows the union to\n // Repliables for interactionGateContext\n if (match.kind === 'autocomplete' || payload.type === InteractionType.ApplicationCommandAutocomplete) return null;\n const monitor = slowGateMonitor();\n try {\n await runHandlerGates(\n ctor,\n interactionGateContext(payload, core, match.routeId),\n match.routeId ?? undefined,\n monitor?.observe\n );\n return null;\n } catch (caught) {\n return { caught };\n } finally {\n // a refusing gate spent budget too\n monitor?.report(match.routeId);\n }\n}\n","import { Subscriber } from '@seedcord/core';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport type { RouteManifest } from '#src/manifest/RouteManifest';\nimport type { Bus } from '@seedcord/core';\nimport type { StoredSubscriberCtor, SubscriberRegistration } from '@seedcord/core/internal';\n\n// an idle isolate that never publishes pays nothing for this\nexport function registerSubscribers(bus: Bus, manifest: RouteManifest): void {\n for (const row of manifest.subscriberRoutes) {\n bus.register({\n keys: row.keys,\n frequency: row.frequency,\n resolve: async () => {\n const keys = row.keys.join(', ');\n let module: Awaited<ReturnType<typeof row.load>>;\n try {\n module = await row.load();\n } catch (caught) {\n throw new SeedcordError(SeedcordErrorCode.RouteModuleLoadFailed, [keys, row.from], {\n cause: caught\n });\n }\n if (!Object.hasOwn(module, row.exportName)) {\n throw new SeedcordError(SeedcordErrorCode.InteractionRouteExportMissing, [\n keys,\n row.exportName,\n row.from\n ]);\n }\n\n // a hand-authored manifest reaches here too, so the check cannot rest on codegen\n const exported = module[row.exportName];\n if (typeof exported !== 'function' || !(exported.prototype instanceof Subscriber)) {\n throw new SeedcordError(SeedcordErrorCode.SubscriberRouteNotASubscriber, [\n keys,\n row.exportName,\n row.from\n ]);\n }\n // justified: the two checks above prove it is a Subscriber subclass\n return exported as StoredSubscriberCtor;\n }\n } satisfies SubscriberRegistration);\n }\n}\n","import { ApplicationCommandOptionType } from 'discord-api-types/v10';\n\nimport type { SlashLikeData } from '#src/dispatch/slashRouteOf';\nimport type {\n APIApplicationCommandInteractionDataBasicOption,\n APIApplicationCommandInteractionDataOption,\n APIAttachment,\n APIInteractionDataResolved,\n APIInteractionDataResolvedChannel,\n APIInteractionDataResolvedGuildMember,\n APIRole,\n APIUser,\n InteractionType\n} from 'discord-api-types/v10';\n\ntype AnyOption = APIApplicationCommandInteractionDataOption<InteractionType>;\ntype Leaf = APIApplicationCommandInteractionDataBasicOption<InteractionType>;\n\ninterface FocusedOption {\n name: string;\n value: string;\n}\n\n// the raw-payload option reader for the typed SlashOptions/AutocompleteOptions views.\nexport class HttpSlashOptions {\n private readonly leaves = new Map<string, Leaf>();\n private readonly resolved: APIInteractionDataResolved | undefined;\n\n constructor(data: SlashLikeData) {\n this.resolved = data.resolved;\n let options: readonly AnyOption[] = data.options ?? [];\n const first = options[0];\n if (first?.type === ApplicationCommandOptionType.SubcommandGroup) options = first.options;\n const second = options[0];\n if (second?.type === ApplicationCommandOptionType.Subcommand) options = second.options ?? [];\n for (const option of options) {\n if (\n option.type !== ApplicationCommandOptionType.Subcommand &&\n option.type !== ApplicationCommandOptionType.SubcommandGroup\n ) {\n this.leaves.set(option.name, option);\n }\n }\n }\n\n getString(name: string): string | null {\n const leaf = this.leaves.get(name);\n return leaf?.type === ApplicationCommandOptionType.String ? leaf.value : null;\n }\n\n getInteger(name: string): number | null {\n const leaf = this.leaves.get(name);\n return leaf?.type === ApplicationCommandOptionType.Integer ? numeric(leaf.value) : null;\n }\n\n getNumber(name: string): number | null {\n const leaf = this.leaves.get(name);\n return leaf?.type === ApplicationCommandOptionType.Number ? numeric(leaf.value) : null;\n }\n\n getBoolean(name: string): boolean | null {\n const leaf = this.leaves.get(name);\n return leaf?.type === ApplicationCommandOptionType.Boolean ? leaf.value : null;\n }\n\n getUser(name: string): APIUser | null {\n const leaf = this.leaves.get(name);\n if (leaf?.type !== ApplicationCommandOptionType.User) return null;\n return this.resolved?.users?.[leaf.value] ?? null;\n }\n\n getMember(name: string): APIInteractionDataResolvedGuildMember | null {\n const leaf = this.leaves.get(name);\n if (leaf?.type !== ApplicationCommandOptionType.User) return null;\n return this.resolved?.members?.[leaf.value] ?? null;\n }\n\n getChannel(name: string): APIInteractionDataResolvedChannel | null {\n const leaf = this.leaves.get(name);\n if (leaf?.type !== ApplicationCommandOptionType.Channel) return null;\n return this.resolved?.channels?.[leaf.value] ?? null;\n }\n\n getRole(name: string): APIRole | null {\n const leaf = this.leaves.get(name);\n if (leaf?.type !== ApplicationCommandOptionType.Role) return null;\n return this.resolved?.roles?.[leaf.value] ?? null;\n }\n\n getAttachment(name: string): APIAttachment | null {\n const leaf = this.leaves.get(name);\n if (leaf?.type !== ApplicationCommandOptionType.Attachment) return null;\n return this.resolved?.attachments?.[leaf.value] ?? null;\n }\n\n getMentionable(name: string): APIUser | APIInteractionDataResolvedGuildMember | APIRole | null {\n const leaf = this.leaves.get(name);\n if (leaf?.type !== ApplicationCommandOptionType.Mentionable) return null;\n const resolved = this.resolved;\n return (\n resolved?.members?.[leaf.value] ?? resolved?.users?.[leaf.value] ?? resolved?.roles?.[leaf.value] ?? null\n );\n }\n\n getFocused(): FocusedOption | null {\n for (const leaf of this.leaves.values()) {\n if ('focused' in leaf && leaf.focused === true) return { name: leaf.name, value: String(leaf.value) };\n }\n return null;\n }\n}\n\n// the autocomplete wire carries integer/number values as strings\nfunction numeric(value: number | string): number | null {\n if (typeof value === 'number') return value;\n if (value.trim() === '') return null;\n const parsed = Number(value);\n return Number.isNaN(parsed) ? null : parsed;\n}\n","import { BaseHandler } from '@seedcord/core';\nimport { AutocompleteRouteBrand, reportedWrite } from '@seedcord/core/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { InteractionResponseType, Routes } from 'discord-api-types/v10';\n\nimport { HttpSlashOptions } from '#inputs/HttpSlashOptions';\nimport { apiFor } from '#src/api';\nimport { slashRouteOf } from '#src/dispatch/slashRouteOf';\n\nimport type { Core } from '#interfaces/Core';\nimport type { API } from '@discordjs/core/http-only';\nimport type { AutocompleteOptions, DispatchContext, SlashRegistry } from '@seedcord/core';\nimport type { AutocompletableNames, ChoiceValueOf, EntryFor, FocusedField } from '@seedcord/core/internal';\nimport type {\n APIApplicationCommandOptionChoice,\n APIApplicationCommandAutocompleteInteraction\n} from 'discord-api-types/v10';\nimport type { Promisable } from 'type-fest';\n\ntype FocusedArms<Route extends keyof SlashRegistry, Ret> = {\n [Name in AutocompletableNames<Route>]: (\n value: string,\n respond: (\n choices: readonly APIApplicationCommandOptionChoice<ChoiceValueOf<EntryFor<Route, Name>>>[]\n ) => Promise<void>\n ) => Promisable<Ret>;\n};\n\n/**\n * Base class for an autocomplete handler on the HTTP transport.\n *\n * Pass the command route(s) from the generated registry as the generic. Branch on the focused field with\n * `this.match`, read already-entered sibling options with `this.options`, and find which command fired\n * with `this.route`.\n *\n * @typeParam Route - One or more route keys from {@link SlashRegistry}, e.g. `'search'`.\n */\nexport abstract class AutocompleteHandler<Route extends keyof SlashRegistry> extends BaseHandler<\n APIApplicationCommandAutocompleteInteraction,\n Core\n> {\n // keep this ctor. it gives typeof AutocompleteHandler a public construct signature the dispatcher\n // needs, and dropping it (inheriting BaseHandler's protected ctor) collapses the handler ctor type to never.\n constructor(event: APIApplicationCommandAutocompleteInteraction, core: Core, dispatch?: DispatchContext) {\n super(event, core, dispatch, 'interactions');\n }\n\n // phantom, never set at runtime.\n /** @internal */\n declare readonly [AutocompleteRouteBrand]?: Route;\n\n /**\n * Typed Discord REST API (`@discordjs/core/http-only`) over `core.rest`, one instance per core.\n */\n protected get api(): API {\n return apiFor(this.core.rest);\n }\n\n private resolver?: HttpSlashOptions;\n\n private get reader(): HttpSlashOptions {\n this.resolver ??= new HttpSlashOptions(this.event.data);\n return this.resolver;\n }\n\n /**\n * The focused option for this interaction. `value` is always the raw partial string the user is typing,\n * even for an integer or number option, so coerce it yourself when you need a number.\n */\n protected get focused(): FocusedField<Route> {\n const focused = this.reader.getFocused();\n if (!focused) throw new SeedcordError(SeedcordErrorCode.AutocompleteNoFocusedOption);\n // the registry constrains the names. a focused field outside it has no arm\n return focused as FocusedField<Route>;\n }\n\n /**\n * Run the arm for the focused field. An autocomplete always dispatches by which field is focused.\n * Match is how you read it.\n *\n * Provide one arm per autocompletable field across the registered commands, keyed by field name. Each\n * arm receives the focused partial value and a `respond` typed to that field's choice value. The\n * arms must cover every autocompletable field, a missing field or an unknown key is a compile error. A\n * focused field with no arm, only reachable from a stale-deployed command, throws at runtime.\n *\n * @param arms - One handler per autocompletable field, keyed by field name.\n * @returns The result of the arm that ran.\n */\n protected async match<Ret>(arms: FocusedArms<Route, Ret>): Promise<Ret> {\n const { name, value } = this.focused;\n const respond = (choices: readonly APIApplicationCommandOptionChoice[]): Promise<void> => this.respond(choices);\n // justified: FocusedArms is keyed by field literals, the Record cast indexes it with the runtime field name.\n type Arm = (\n value: string,\n respond: (choices: readonly APIApplicationCommandOptionChoice[]) => Promise<void>\n ) => Promisable<Ret>;\n const arm = Object.hasOwn(arms, name) ? (arms as Record<string, Arm>)[name] : undefined;\n if (!arm) throw new SeedcordError(SeedcordErrorCode.AutocompleteMatchArmMissing, [name]);\n return await arm(value, respond);\n }\n\n /** Send autocomplete suggestions, callback type 8. Prefer {@link match}, which restricts each field's choices to its declared type. */\n protected async respond(choices: readonly APIApplicationCommandOptionChoice[]): Promise<void> {\n // a hand-built handler outside a dispatch carries no route id\n const routeId = this.dispatch?.routeId ?? 'autocomplete';\n await reportedWrite({ bus: this.core.bus, interactionId: this.event.id }, routeId, 'respond', () =>\n this.core.rest.post(Routes.interactionCallback(this.event.id, this.event.token), {\n body: { type: InteractionResponseType.ApplicationCommandAutocompleteResult, data: { choices } }\n })\n );\n }\n\n /** The firing command route, for a field whose completion differs per registered command. */\n protected get route(): Route {\n return slashRouteOf(this.event.data) as Route;\n }\n\n /**\n * The already-entered options on this command, restricted to the kinds Discord resolves during autocomplete\n * (string, integer, number, boolean) with every read nullable, since a sibling is partial while the user\n * types the focused field. The focused field is on `this.focused`.\n */\n protected get options(): AutocompleteOptions<Route> {\n // AutocompleteOptions is just the registry-typed view over the getters the reader already exposes\n return this.reader as AutocompleteOptions<Route>;\n }\n}\n","import { AutocompleteHandler } from '#handlers/interaction/AutocompleteHandler';\n\n// dispatch falls back here when no manifest row matches\n// discord requires empty choices here, which clears the pending autocomplete state\nexport class UnhandledAutocomplete extends AutocompleteHandler<never> {\n async execute(): Promise<void> {\n await this.respond([]);\n }\n}\n","import { RepliableHandler } from '#handlers/RepliableHandler';\n\nimport type { Repliables } from '#handlers/interactionTypes';\nimport type { Core } from '#interfaces/Core';\nimport type { DispatchContext } from '@seedcord/core';\nimport type { ModalLike } from '@seedcord/core/internal';\nimport type { APIModalSubmitInteraction } from 'discord-api-types/v10';\n\n/**\n * Shared base the repliable interaction handlers extend.\n *\n * Not a public entry point. Extend {@link SlashHandler}, {@link ContextMenuHandler}, {@link ButtonHandler},\n * {@link SelectMenuHandler}, or {@link ModalHandler} instead. This class adds `showModal` on top of the\n * reply members.\n *\n * @typeParam Event - The repliable interaction type this handler processes\n */\nexport abstract class InteractionHandler<Event extends Repliables> extends RepliableHandler<Event> {\n // the dispatcher needs a public construct signature, but RepliableHandler's ctor is protected\n constructor(event: Event, core: Core, dispatch?: DispatchContext) {\n super(event, core, dispatch);\n }\n\n /**\n * Open a modal. Must be the initial response to this interaction. The modal kind rejects this call at\n * compile time (Discord forbids a modal in response to a modal).\n */\n protected showModal(\n this: InteractionHandler<Exclude<Repliables, APIModalSubmitInteraction>>,\n modal: ModalLike\n ): Promise<void> {\n return this.sender.showModal(modal);\n }\n}\n","import { InteractionHandler } from '#handlers/interaction/InteractionHandler';\n\nimport type { Repliables } from '#handlers/interactionTypes';\n\n// dispatch falls back here when no manifest row matches\n// a reply failure here reaches the dispatch fault boundary directly\nexport class UnhandledRepliable extends InteractionHandler<Repliables> {\n async execute(): Promise<void> {\n await this.reply('Feature not implemented yet.');\n }\n}\n","import { prefixOf, type InteractionRoutes } from '@seedcord/core/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { ApplicationCommandType, ComponentType, InteractionType } from 'discord-api-types/v10';\n\nimport { UnhandledAutocomplete } from '#handlers/defaults/UnhandledAutocomplete';\nimport { UnhandledRepliable } from '#handlers/defaults/UnhandledRepliable';\n\nimport { slashRouteOf } from './slashRouteOf';\n\nimport type { ComponentRoute, RouteManifest, RouteModule } from '#src/manifest/RouteManifest';\nimport type { APIInteraction } from 'discord-api-types/v10';\n\ntype ResolvedKind = `${InteractionRoutes}`;\n\n/** A manifest row matched to an incoming interaction, keyed the way the gateway dispatcher keys. */\nexport interface ResolvedRoute {\n readonly kind: ResolvedKind;\n /**\n * The stable dispatch id, `kind:key` (`slash:ban`), the shape core's `routeIdOf` builds. Null for the\n * unhandled default, which matches no row.\n */\n readonly routeId: string | null;\n /** The key the router looked up. */\n readonly attemptedKey?: string;\n /** Resolves the one export the row registers. */\n readonly load: () => Promise<unknown>;\n}\n\n/** @internal */\nexport type RouteMap = Map<string, ResolvedRoute>;\n\ntype ComponentMapKey = ComponentRoute['kind'];\n\nexport interface RouteMaps {\n readonly slash: RouteMap;\n readonly userContextMenu: RouteMap;\n readonly messageContextMenu: RouteMap;\n readonly autocomplete: RouteMap;\n readonly components: Readonly<Record<ComponentMapKey, RouteMap>>;\n}\n\n// the manifest names selects `xSelect`, the resolved kinds name them `xMenu`\ntype ResolvedKindOf<Kind extends ComponentMapKey> = Kind extends `${infer Base}Select` ? `${Base}Menu` : Kind;\n\nconst COMPONENT_KIND = {\n button: 'button',\n stringSelect: 'stringMenu',\n userSelect: 'userMenu',\n roleSelect: 'roleMenu',\n channelSelect: 'channelMenu',\n mentionableSelect: 'mentionableMenu',\n modal: 'modal'\n} satisfies { [Kind in ComponentMapKey]: ResolvedKindOf<Kind> & ResolvedKind };\n\ntype CommandMapKey = Exclude<keyof RouteMaps, 'components' | 'autocomplete'>;\n\nfunction commandKind(type: ApplicationCommandType): CommandMapKey | null {\n switch (type) {\n case ApplicationCommandType.ChatInput: {\n return 'slash';\n }\n case ApplicationCommandType.User: {\n return 'userContextMenu';\n }\n case ApplicationCommandType.Message: {\n return 'messageContextMenu';\n }\n default: {\n return null;\n }\n }\n}\n\nfunction componentMapKey(type: ComponentType): Exclude<ComponentMapKey, 'modal'> | null {\n switch (type) {\n case ComponentType.Button: {\n return 'button';\n }\n case ComponentType.StringSelect: {\n return 'stringSelect';\n }\n case ComponentType.UserSelect: {\n return 'userSelect';\n }\n case ComponentType.RoleSelect: {\n return 'roleSelect';\n }\n case ComponentType.ChannelSelect: {\n return 'channelSelect';\n }\n case ComponentType.MentionableSelect: {\n return 'mentionableSelect';\n }\n default: {\n return null;\n }\n }\n}\n\nfunction namedExport(routeId: string, row: RouteModule): () => Promise<unknown> {\n return async () => {\n let moduleExports: Awaited<ReturnType<typeof row.load>>;\n try {\n moduleExports = await row.load();\n } catch (caught) {\n throw new SeedcordError(SeedcordErrorCode.RouteModuleLoadFailed, [routeId, row.from], { cause: caught });\n }\n if (!Object.hasOwn(moduleExports, row.exportName)) {\n throw new SeedcordError(SeedcordErrorCode.InteractionRouteExportMissing, [\n routeId,\n row.exportName,\n row.from\n ]);\n }\n return moduleExports[row.exportName];\n };\n}\n\nfunction describeRow(row: RouteModule): string {\n return `${row.exportName} (${row.from})`;\n}\n\n/**\n * Builds the per-kind lookup maps.\n *\n * @throws A **SeedcordError** when two rows resolve to the same route.\n */\nexport function buildRouteMaps(manifest: RouteManifest): RouteMaps {\n const maps: RouteMaps = {\n slash: new Map(),\n userContextMenu: new Map(),\n messageContextMenu: new Map(),\n autocomplete: new Map(),\n components: {\n button: new Map(),\n stringSelect: new Map(),\n userSelect: new Map(),\n roleSelect: new Map(),\n channelSelect: new Map(),\n mentionableSelect: new Map(),\n modal: new Map()\n }\n };\n const owners = new Map<string, RouteModule>();\n\n function set(map: RouteMap, kind: ResolvedKind, key: string, row: RouteModule): void {\n const routeId = `${kind}:${key}`;\n const owner = owners.get(routeId);\n if (owner) {\n // a bare map.set would let a later row shadow an earlier one\n throw new SeedcordError(SeedcordErrorCode.InteractionDuplicateRoute, [\n routeId,\n describeRow(owner),\n describeRow(row)\n ]);\n }\n owners.set(routeId, row);\n map.set(key, { kind, routeId, load: namedExport(routeId, row) });\n }\n\n for (const row of manifest.commandRoutes) {\n const kind = commandKind(row.type);\n if (kind) set(maps[kind], kind, row.name, row);\n }\n for (const row of manifest.autocompleteRoutes) {\n set(maps.autocomplete, 'autocomplete', row.name, row);\n }\n for (const row of manifest.componentRoutes) {\n set(maps.components[row.kind], COMPONENT_KIND[row.kind], row.prefix, row);\n }\n return maps;\n}\n\n// dispatched through the normal pipeline like the gateway's unhandled default\nfunction unhandled(kind: ResolvedKind, attemptedKey: string): ResolvedRoute {\n return {\n kind,\n routeId: null,\n attemptedKey,\n load: () => Promise.resolve(kind === 'autocomplete' ? UnhandledAutocomplete : UnhandledRepliable)\n };\n}\n\n/**\n * Matches a verified non-PING interaction to its manifest row. A known kind with no row resolves to the\n * unhandled default, whose handler replies \"Feature not implemented yet.\" (empty choices on autocomplete).\n * Null is an unrecognized payload shape, which the engine acks with a 202 without dispatching.\n * Components and modals route by the stable customId prefix, so a wire whose layout hash drifted still\n * routes to its handler, where decode refuses with `StaleCustomId`.\n */\nexport function resolve(maps: RouteMaps, interaction: APIInteraction): ResolvedRoute | null {\n switch (interaction.type) {\n case InteractionType.ApplicationCommand: {\n if (interaction.data.type === ApplicationCommandType.ChatInput) {\n const route = slashRouteOf(interaction.data);\n return maps.slash.get(route) ?? unhandled('slash', route);\n }\n const kind = commandKind(interaction.data.type);\n return kind ? (maps[kind].get(interaction.data.name) ?? unhandled(kind, interaction.data.name)) : null;\n }\n case InteractionType.ApplicationCommandAutocomplete: {\n const route = slashRouteOf(interaction.data);\n return maps.autocomplete.get(route) ?? unhandled('autocomplete', route);\n }\n case InteractionType.MessageComponent: {\n const key = componentMapKey(interaction.data.component_type);\n if (!key) return null;\n const prefix = prefixOf(interaction.data.custom_id);\n return maps.components[key].get(prefix) ?? unhandled(COMPONENT_KIND[key], prefix);\n }\n case InteractionType.ModalSubmit: {\n const prefix = prefixOf(interaction.data.custom_id);\n return maps.components.modal.get(prefix) ?? unhandled('modal', prefix);\n }\n default: {\n return null;\n }\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nconst PUBLIC_KEY_BYTES = 32;\nconst SIGNATURE_BYTES = 64;\nconst HEX_RADIX = 16;\nconst HEX_PATTERN = /^[0-9a-f]+$/i;\n\nfunction hexToBytes(hex: string, byteLength: number): Uint8Array<ArrayBuffer> | null {\n if (hex.length !== byteLength * 2 || !HEX_PATTERN.test(hex)) return null;\n\n const bytes = new Uint8Array(byteLength);\n for (let index = 0; index < byteLength; index++) {\n bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), HEX_RADIX);\n }\n return bytes;\n}\n\nconst encoder = new TextEncoder();\n\nexport class Ed25519Verifier {\n private readonly key: Promise<CryptoKey>;\n\n constructor(publicKeyHex: string) {\n const publicKey = hexToBytes(publicKeyHex, PUBLIC_KEY_BYTES);\n if (!publicKey)\n throw new SeedcordError(SeedcordErrorCode.ConfigInvalidEnv, ['DISCORD_PUBLIC_KEY', '64 hex characters']);\n\n this.key = crypto.subtle.importKey('raw', publicKey, 'Ed25519', false, ['verify']);\n }\n\n async verify(signatureHex: string, timestamp: string, body: Uint8Array): Promise<boolean> {\n const signature = hexToBytes(signatureHex, SIGNATURE_BYTES);\n if (!signature) return false;\n\n const timestampBytes = encoder.encode(timestamp);\n const message = new Uint8Array(timestampBytes.length + body.length);\n message.set(timestampBytes, 0);\n message.set(body, timestampBytes.length);\n\n return crypto.subtle.verify('Ed25519', await this.key, signature, message);\n }\n}\n","const REPLAY_WINDOW_MS = 300_000; // compared in both directions, clock skew stays inside the window\nconst SWEEP_GAP_MS = 60_000;\nconst MS_PER_SECOND = 1000;\nconst TIMESTAMP_PATTERN = /^\\d+$/;\n\n// the seen map is per-isolate on edge, so a fresh isolate rejects a replay on freshness alone\nexport class ReplayGuard {\n private readonly seen = new Map<string, number>();\n private lastSweep = Date.now();\n\n // the parsed value feeds unseen(), which only ever sees timestamps this already accepted\n public fresh(timestampHeader: string): number | null {\n if (!TIMESTAMP_PATTERN.test(timestampHeader)) return null;\n const timestampMs = Number(timestampHeader) * MS_PER_SECOND;\n return Math.abs(Date.now() - timestampMs) <= REPLAY_WINDOW_MS ? timestampMs : null;\n }\n\n public unseen(signatureHex: string, timestampMs: number): boolean {\n const now = Date.now();\n this.maybeSweep(now);\n\n // >= because a timestamp at the exact stale edge is accepted and its entry expires at `now`\n const rejectUntil = this.seen.get(signatureHex);\n if (rejectUntil !== undefined && rejectUntil >= now) return false;\n\n // expires when the freshness check starts rejecting this timestamp\n this.seen.set(signatureHex, timestampMs + REPLAY_WINDOW_MS);\n return true;\n }\n\n // swept on access, a construction-time timer throws in workers global scope\n private maybeSweep(now: number): void {\n if (now - this.lastSweep < SWEEP_GAP_MS) return;\n this.lastSweep = now;\n // strict, the sweep runs before the dedup lookup and must keep an entry expiring exactly at `now`\n for (const [signature, rejectUntil] of this.seen) {\n if (rejectUntil < now) this.seen.delete(signature);\n }\n }\n}\n","import { asError, PublishDefault } from '@seedcord/core/internal';\nimport { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\nimport { InteractionResponseType, InteractionType } from 'discord-api-types/v10';\nimport { Converters, Envapter } from 'envapt';\n\nimport { dispatchInteraction } from './dispatch/dispatchInteraction';\nimport { resolve } from './dispatch/resolve';\nimport { Ed25519Verifier } from './receiver/Ed25519Verifier';\nimport { ReplayGuard } from './receiver/ReplayGuard';\n\nimport type { ValidInteractionTypes } from '#handlers/interactionTypes';\nimport type { Core } from '#interfaces/Core';\nimport type { RouteMaps } from './dispatch/resolve';\nimport type { APIInteraction } from 'discord-api-types/v10';\n\nconst SIGNATURE_HEADER = 'x-signature-ed25519';\nconst TIMESTAMP_HEADER = 'x-signature-timestamp';\n\nconst ACCEPTED = 202;\nconst BAD_REQUEST = 400;\nconst UNAUTHORIZED = 401;\nconst METHOD_NOT_ALLOWED = 405;\n\n// widened so the raw payload's number field compares without an unsafe enum comparison\nconst PING: number = InteractionType.Ping;\n\nconst decoder = new TextDecoder();\n\ntype Outcome = 'not-post' | 'unsigned' | 'stale' | 'bad-signature' | 'bad-body' | 'ping' | 'replay' | 'dispatched';\ntype Answer = readonly [Response, Outcome];\n\nfunction hasInteractionType(payload: unknown): payload is { type: number } {\n return typeof payload === 'object' && payload !== null && 'type' in payload && typeof payload.type === 'number';\n}\n\n/** Structural match for the Workers `ExecutionContext`. Pass it from the edge entry so post-202 work runs under `waitUntil`. */\nexport interface EngineContext {\n waitUntil(promise: Promise<unknown>): void;\n}\n\nexport interface EngineParts {\n readonly handle: (request: Request, ctx?: EngineContext) => Promise<Response>;\n // node paths only, drained at shutdown\n readonly inFlight: ReadonlySet<Promise<void>>;\n}\n\nexport function buildEngine(core: Core, maps: RouteMaps): EngineParts {\n if (!Envapter.has('DISCORD_PUBLIC_KEY'))\n throw new SeedcordError(SeedcordErrorCode.ConfigMissingEnv, ['DISCORD_PUBLIC_KEY']);\n const publicKey = Envapter.getRequired('DISCORD_PUBLIC_KEY', Converters.String);\n\n const verifier = new Ed25519Verifier(publicKey);\n const replays = new ReplayGuard();\n const inFlight = new Set<Promise<void>>();\n // eager because env binds before this factory runs\n const logger = new Logger('Engine', { channel: 'bot' });\n\n async function dispatchMatched(\n match: NonNullable<ReturnType<typeof resolve>>,\n payload: ValidInteractionTypes,\n ctx: EngineContext | undefined\n ): Promise<void> {\n const start = await dispatchInteraction({ match, payload, core });\n if (!start) return;\n\n // this catch is all that stands between a throw and an unhandled rejection\n const work = start().catch(rootFault);\n if (ctx) {\n ctx.waitUntil(work);\n } else {\n inFlight.add(work);\n void work.finally(() => inFlight.delete(work));\n }\n }\n\n function rootFault(caught: unknown): void {\n const error = asError(caught);\n logger.error('dispatch failed past the boundary', error);\n core.bus[PublishDefault]('unhandledInteractionError', { error });\n }\n\n const handle = async (request: Request, ctx?: EngineContext): Promise<Response> => {\n const [response, outcome] = await respond(request, ctx);\n logger.debug(`${paint.sky.bold(request.method)} ${outcome} ${paint.amber(String(response.status))}`);\n return response;\n };\n\n const respond = async (request: Request, ctx?: EngineContext): Promise<Answer> => {\n if (request.method !== 'POST') {\n return [new Response(null, { status: METHOD_NOT_ALLOWED, headers: { allow: 'POST' } }), 'not-post'];\n }\n\n const signature = request.headers.get(SIGNATURE_HEADER);\n const timestamp = request.headers.get(TIMESTAMP_HEADER);\n if (signature === null || timestamp === null) return [new Response(null, { status: UNAUTHORIZED }), 'unsigned'];\n\n const timestampMs = replays.fresh(timestamp);\n if (timestampMs === null) return [new Response(null, { status: UNAUTHORIZED }), 'stale'];\n\n const body = new Uint8Array(await request.arrayBuffer());\n if (!(await verifier.verify(signature, timestamp, body))) {\n return [new Response(null, { status: UNAUTHORIZED }), 'bad-signature'];\n }\n\n let payload: unknown;\n try {\n payload = JSON.parse(decoder.decode(body));\n } catch {\n return [new Response(null, { status: BAD_REQUEST }), 'bad-body'];\n }\n if (!hasInteractionType(payload)) return [new Response(null, { status: BAD_REQUEST }), 'bad-body'];\n\n // discord resends the same signed ping while verifying an endpoint, and a 401 there reads as a broken endpoint\n if (payload.type === PING) {\n return [Response.json({ type: InteractionResponseType.Pong }), 'ping'];\n }\n\n if (!replays.unseen(signature, timestampMs)) return [new Response(null, { status: UNAUTHORIZED }), 'replay'];\n\n try {\n // justified: the payload is signed by Discord, and the type field anchors the discriminated union\n core.bus[PublishDefault]('anyInteraction', { interaction: payload as APIInteraction });\n const match = resolve(maps, payload as APIInteraction);\n if (match) await dispatchMatched(match, payload as ValidInteractionTypes, ctx);\n } catch (caught) {\n // the 202 has to go out either way or discord drops the interaction\n rootFault(caught);\n }\n\n return [new Response(null, { status: ACCEPTED }), 'dispatched'];\n };\n\n return { handle, inFlight };\n}\n","import { validateDiscordToken } from '@seedcord/errors/internal';\nimport { Envapter } from 'envapt';\n\nimport { createCore } from './dispatch/dispatchInteraction';\nimport { registerSubscribers } from './dispatch/registerSubscribers';\nimport { buildRouteMaps } from './dispatch/resolve';\nimport { buildEngine } from './engine';\n\nimport type { HttpConfig } from '#interfaces/Config';\nimport type { RouteManifest } from '#src/manifest/RouteManifest';\nimport type { EngineContext } from './engine';\n\nexport type { EngineContext } from './engine';\n\n/**\n * Builds the HTTP-interactions engine, a `(request, ctx?) => Promise<Response>` handler.\n *\n * The handler verifies the Ed25519 signature over the raw request bytes, rejects stale or replayed\n * requests, answers a PING with an in-body PONG, and acks every other interaction with an empty 202.\n * The engine dispatches a matched interaction through the manifest before the 202 goes out. Gates run\n * first and the sender posts any refusal over the REST callback. `execute()` continues past the 202 under\n * `ctx.waitUntil` when the caller passes one (the edge entry), and an engine-held in-flight set tracks\n * it otherwise (node paths). It reads only the method, headers, and body. Mount it at any path.\n *\n * Reads `DISCORD_PUBLIC_KEY` and `DISCORD_BOT_TOKEN` from the environment through envapt and throws a\n * `SeedcordError` when either is missing or malformed.\n */\nexport function createSeedcord(\n config: HttpConfig,\n manifest: RouteManifest\n): (request: Request, ctx?: EngineContext) => Promise<Response> {\n const token = validateDiscordToken(Envapter.get('DISCORD_BOT_TOKEN'));\n const core = createCore(config, token);\n core.bus.registerDefaults();\n registerSubscribers(core.bus, manifest);\n return buildEngine(core, buildRouteMaps(manifest)).handle;\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Routes } from 'discord-api-types/v10';\n\nimport type { REST } from '@discordjs/rest';\nimport type { APIApplication } from 'discord-api-types/v10';\n\n// a gateway host reads its id off the client session, this host has none so it fetches over REST\nexport async function fetchApplicationId(rest: REST): Promise<string> {\n try {\n // justified: the discord api contract for this route\n const application = (await rest.get(Routes.currentApplication())) as APIApplication;\n return application.id;\n } catch (error) {\n throw new SeedcordError(SeedcordErrorCode.CoreApplicationUnavailable, { cause: error });\n }\n}\n","import { ResolvedEmoji } from '@seedcord/core';\nimport { accessorStore, clearStore, guardedAccessor, isEmojiTuple } from '@seedcord/core/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\nimport { Routes } from 'discord-api-types/v10';\n\nimport { fetchApplicationId } from '#src/applicationId';\n\nimport type { Core } from '#interfaces/Core';\nimport type { EmojiMap } from '@seedcord/types';\nimport type { APIEmoji } from 'discord-api-types/v10';\n\nconst emojiStorage = accessorStore<ResolvedEmoji>();\n\nfunction reasonOf(error: unknown): string {\n return Error.isError(error) ? error.message : String(error);\n}\n\nfunction byName(list: readonly APIEmoji[]): Map<string, APIEmoji> {\n const map = new Map<string, APIEmoji>();\n for (const entry of list) if (entry.name) map.set(entry.name, entry);\n return map;\n}\n\n/** The global {@link Emojis} accessor type. */\nexport type InjectedEmojiMap = {\n [K in keyof EmojiMap]: ResolvedEmoji;\n};\n\n/**\n * The bot's resolved emojis, keyed by {@link EmojiMap}. Filled by {@link EmojiInjector} during startup.\n * A read before that throws.\n */\nexport const Emojis = guardedAccessor('Emojis', emojiStorage) as InjectedEmojiMap;\n\n// resolves the configured emojis over REST at startup\nexport class EmojiInjector {\n private readonly logger = new Logger('Emojis', { channel: 'bot' });\n\n constructor(\n private readonly core: Core,\n // the host passes its memoized one so a boot that also deploys commands resolves the id once\n private readonly applicationId: () => Promise<string> = () => fetchApplicationId(core.rest)\n ) {}\n\n public async init(): Promise<void> {\n clearStore(emojiStorage);\n\n const configured = this.core.config.bot.emojis;\n if (!configured || Object.keys(configured).length === 0) {\n this.logger.debug('No emojis configured, skipping emoji injection.');\n return;\n }\n\n const failures: string[] = [];\n const guilds = new Map<string, Map<string, APIEmoji>>();\n let application: Map<string, APIEmoji> | undefined;\n\n for (const [key, value] of Object.entries(configured)) {\n if (isEmojiTuple(value)) await this.resolveTuple(key, value, guilds, failures);\n else if (typeof value === 'string') {\n application ??= await this.applicationEmojis(failures);\n this.resolve(key, value, application, failures, 'the application');\n } else failures.push(` - \"${key}\" has an invalid value (expected a name or [name, guildId])`);\n }\n\n // surface every unresolved emoji at once so the user fixes the whole config in one pass\n if (failures.length > 0) {\n throw new SeedcordError(SeedcordErrorCode.ConfigEmojiUnresolved, [failures.length, failures.join('\\n')]);\n }\n\n this.logger.utils.summary('Loaded emojis', { emojis: Object.keys(emojiStorage).length });\n }\n\n private async applicationEmojis(failures: string[]): Promise<Map<string, APIEmoji>> {\n try {\n const appId = await this.applicationId();\n // justified: the discord api contract for this route\n const listed = (await this.core.rest.get(Routes.applicationEmojis(appId))) as {\n items: APIEmoji[];\n };\n return byName(listed.items);\n } catch (error) {\n failures.push(` - the application emojis could not be read (${reasonOf(error)})`);\n return new Map();\n }\n }\n\n private async resolveTuple(\n key: string,\n [name, guildId]: readonly [string, string],\n guilds: Map<string, Map<string, APIEmoji>>,\n failures: string[]\n ): Promise<void> {\n let guild = guilds.get(guildId);\n if (!guild) {\n try {\n // justified: the discord api contract for this route\n guild = byName((await this.core.rest.get(Routes.guildEmojis(guildId))) as APIEmoji[]);\n } catch (error) {\n failures.push(\n ` - \"${name}\" for \"${key}\" targets guild ${guildId}, which could not be read (${reasonOf(error)})`\n );\n return;\n }\n guilds.set(guildId, guild);\n }\n\n this.resolve(key, name, guild, failures, `guild ${guildId}`);\n }\n\n private resolve(key: string, name: string, source: Map<string, APIEmoji>, failures: string[], where: string): void {\n const found = source.get(name);\n if (!found?.id) {\n failures.push(` - \"${name}\" for \"${key}\" was not found in ${where}`);\n return;\n }\n\n emojiStorage[key] = new ResolvedEmoji(name, found.id, found.animated ?? false);\n }\n}\n","import { SlashRouteBrand } from '@seedcord/core/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { InteractionHandler } from '#handlers/interaction/InteractionHandler';\nimport { HttpSlashOptions } from '#inputs/HttpSlashOptions';\nimport { slashRouteOf } from '#src/dispatch/slashRouteOf';\n\nimport type { SlashOptions } from '#inputs/SlashOptions';\nimport type { SlashRegistry } from '@seedcord/core';\nimport type { APIChatInputApplicationCommandInteraction } from 'discord-api-types/v10';\nimport type { Promisable } from 'type-fest';\n\ntype SlashMatchArms<Route extends keyof SlashRegistry, Ret> = {\n [Key in Route]: (options: SlashOptions<Key>) => Promisable<Ret>;\n};\n\n/**\n * Base class for a chat-input (slash) command handler on the HTTP transport.\n *\n * Pass the route from the generated registry as the generic, then read `this.options` for a single\n * command or `this.match` for several. Command authoring stays plain discord.js, `seedcord codegen`\n * reads its `toJSON()` to type these options.\n *\n * @typeParam Route - One or more route keys from {@link SlashRegistry}, e.g. `'ban'` or `'ban' | 'kick'`.\n */\nexport abstract class SlashHandler<\n Route extends keyof SlashRegistry\n> extends InteractionHandler<APIChatInputApplicationCommandInteraction> {\n // phantom, never set at runtime.\n /** @internal */\n declare readonly [SlashRouteBrand]?: Route;\n\n private resolver?: HttpSlashOptions;\n\n /**\n * The typed options for this command's route. Required options drop the null, choices narrow to their\n * literal union, and only the getters for kinds this command actually uses appear. Rich kinds resolve\n * from the interaction's `resolved` data.\n */\n protected get options(): SlashOptions<Route> {\n this.resolver ??= new HttpSlashOptions(this.event.data);\n // SlashOptions is just the registry-typed view over the getters the reader already exposes\n return this.resolver as SlashOptions<Route>;\n }\n\n /**\n * Run the arm for whichever command fired. Use this only when the handler is registered for several\n * routes.\n *\n * Provide one arm per registered route, keyed by its route string, and each arm receives that route's\n * own narrowed options. The arms must cover every route in the generic, a missing route or an unknown\n * key is a compile error.\n *\n * @param arms - One handler per registered route, keyed by route string.\n * @returns The result of the arm that ran.\n */\n protected async match<Ret>(arms: SlashMatchArms<Route, Ret>): Promise<Ret> {\n const route = slashRouteOf(this.event.data);\n // justified: SlashMatchArms is keyed by Route literals, the Record cast indexes it with the runtime route string.\n const arm = Object.hasOwn(arms, route)\n ? (arms as Record<string, (options: SlashOptions<Route>) => Promisable<Ret>>)[route]\n : undefined;\n if (!arm) throw new SeedcordError(SeedcordErrorCode.SlashMatchArmMissing, [route]);\n return await arm(this.options);\n }\n}\n","import { ContextMenuKindBrand, ContextMenuNamesBrand } from '@seedcord/core/internal';\n\nimport { InteractionHandler } from '#handlers/interaction/InteractionHandler';\n\nimport type { NamesFor } from '@seedcord/core/internal';\nimport type {\n APIInteractionDataResolvedGuildMember,\n APIMessage,\n APIMessageApplicationCommandInteraction,\n APIUser,\n APIUserApplicationCommandInteraction,\n ApplicationCommandType\n} from 'discord-api-types/v10';\n\n/**\n * Base class for a user context-menu command handler on the HTTP transport (right-click a user).\n *\n * Pass the command name(s) as the generic, the same as `@UserContextMenuRoute`. Read the right-clicked user\n * from `this.target` and that same user's guild member from `this.targetMember`.\n *\n * @typeParam Names - One or more command names from the generated user registry.\n */\nexport abstract class UserContextMenuHandler<\n Names extends NamesFor<ApplicationCommandType.User>\n> extends InteractionHandler<APIUserApplicationCommandInteraction> {\n // phantom, nothing reads them. they keep the kind and the names on the instance type\n /** @internal */\n declare readonly [ContextMenuKindBrand]?: ApplicationCommandType.User;\n /** @internal */\n declare readonly [ContextMenuNamesBrand]?: Names;\n\n protected get target(): APIUser {\n const { target_id: targetId, resolved } = this.event.data;\n // justified: discord resolves the target it delivered target_id for\n return resolved.users[targetId] as APIUser;\n }\n\n /** The right-clicked user's guild member, null outside a guild. */\n protected get targetMember(): APIInteractionDataResolvedGuildMember | null {\n const { target_id: targetId, resolved } = this.event.data;\n return resolved.members?.[targetId] ?? null;\n }\n}\n\n/**\n * Base class for a message context-menu command handler on the HTTP transport (right-click a message).\n *\n * Pass the command name(s) as the generic, the same as `@MessageContextMenuRoute`. Read the right-clicked\n * message from `this.target`.\n *\n * @typeParam Names - One or more command names from the generated message registry.\n */\nexport abstract class MessageContextMenuHandler<\n Names extends NamesFor<ApplicationCommandType.Message>\n> extends InteractionHandler<APIMessageApplicationCommandInteraction> {\n /** @internal */\n declare readonly [ContextMenuKindBrand]?: ApplicationCommandType.Message;\n /** @internal */\n declare readonly [ContextMenuNamesBrand]?: Names;\n\n protected get target(): APIMessage {\n const { target_id: targetId, resolved } = this.event.data;\n // justified: discord resolves the target it delivered target_id for\n return resolved.messages[targetId] as APIMessage;\n }\n}\n","import { ComponentDefsBrand, decodeComponentRoute } from '@seedcord/core/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { InteractionHandler } from '#handlers/interaction/InteractionHandler';\n\nimport type { SentMessage } from '#reply/ReplySender';\nimport type {\n AnyCustomId,\n DecodedComponentRoute,\n HasComponentDefs,\n MatchArms,\n SingleParams\n} from '@seedcord/core/internal';\nimport type { ReplyResponse } from '@seedcord/types';\nimport type { APIMessageComponentInteraction, APIModalSubmitInteraction } from 'discord-api-types/v10';\nimport type { Promisable } from 'type-fest';\n\n/**\n * Shared base the customId-routed component handlers extend on the HTTP transport.\n *\n * Not a public entry point. Extend {@link ButtonHandler}, {@link SelectMenuHandler}, or {@link ModalHandler}\n * instead. This class defines the customId decode, the route matching, and the source-message verbs\n * `update` and `deferUpdate`.\n *\n * @typeParam Event - The component or modal interaction type this handler processes\n * @typeParam Defs - The customId route definitions registered on the concrete handler\n */\nexport abstract class ComponentHandler<\n Event extends APIMessageComponentInteraction | APIModalSubmitInteraction,\n Defs extends readonly AnyCustomId[]\n>\n extends InteractionHandler<Event>\n implements HasComponentDefs<Defs>\n{\n // phantom, never set at runtime.\n /** @internal */\n declare readonly [ComponentDefsBrand]?: Defs;\n\n /** Rewrite the source message this component interaction came from. */\n protected update(response: ReplyResponse | string): Promise<SentMessage> {\n return this.sender.update(response);\n }\n\n /** Acknowledge the component without changing the source message. */\n protected deferUpdate(): Promise<void> {\n return this.sender.deferUpdate();\n }\n\n private decoded?: DecodedComponentRoute;\n\n private get route(): DecodedComponentRoute {\n this.decoded ??= decodeComponentRoute(this.constructor, this.event.data.custom_id);\n return this.decoded;\n }\n\n /**\n * The decoded params of the single route this handler is registered for.\n *\n * Reading this decodes `this.event.data.custom_id` once (cached after the first read) and throws\n * `StaleCustomId` or `InvalidCustomId` when the wire no longer matches the current shape, which the\n * dispatcher boundary turns into a reply. On a handler registered for several routes this is\n * `never`, so use {@link match} instead.\n */\n protected get params(): SingleParams<Defs> {\n return this.route.params as SingleParams<Defs>;\n }\n\n /**\n * Run the arm for whichever route the component was minted from. Use this only when the handler is\n * registered for several routes. A single-route handler reads `this.params` directly. On a multi-route\n * handler `this.params` is `never`, so match is the only way to read the decoded params.\n *\n * Provide one arm per registered route, keyed by its prefix, and each arm receives that route's own\n * decoded params. The arms cover every registered route prefix, checked at compile time, and a prefix\n * unmatched at runtime throws `CustomIdMatchArmMissing`. Decoding runs before any arm, so a stale or\n * corrupt wire throws before an arm body executes.\n *\n * @param arms - One callback per registered route, keyed by prefix.\n * @returns The result of the arm that ran.\n *\n * @example\n * ```ts\n * \\@ButtonRoute(Approve, Reject)\n * class ReviewButtons extends ButtonHandler<[typeof Approve, typeof Reject]> {\n * async execute() {\n * await this.match({\n * approve: ({ userId }) => this.reply(`approved <@${userId}>`),\n * reject: ({ userId }) => this.reply(`rejected <@${userId}>`)\n * });\n * }\n * }\n * ```\n */\n protected async match<Ret>(arms: MatchArms<Defs, Ret>): Promise<Ret> {\n const { prefix, params } = this.route;\n // justified: MatchArms is keyed by prefix literals, the Record cast indexes it with the runtime prefix.\n const arm = Object.hasOwn(arms, prefix)\n ? (arms as Record<string, (params: Record<string, unknown>) => Promisable<Ret>>)[prefix]\n : undefined;\n if (!arm) throw new SeedcordError(SeedcordErrorCode.CustomIdMatchArmMissing, [prefix]);\n return await arm(params);\n }\n}\n","import { ComponentKindBrand } from '@seedcord/core/internal';\n\nimport { ComponentHandler } from './ComponentHandler';\n\nimport type { AnyCustomId } from '@seedcord/core/internal';\nimport type { APIMessageComponentButtonInteraction } from 'discord-api-types/v10';\n\n/**\n * Base class for a button interaction handler on the HTTP transport.\n *\n * Register the customId definitions this handler decodes with `@ButtonRoute`, list the same ones in the\n * generic, then read `this.params` for a single route or `this.match` for several. Reply through the\n * handler members, or rewrite the source message with `this.update`. Passing different definitions to the\n * decorator and the generic is a compile error.\n *\n * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof ApproveId]`.\n */\nexport abstract class ButtonHandler<Defs extends readonly AnyCustomId[]> extends ComponentHandler<\n APIMessageComponentButtonInteraction,\n Defs\n> {\n // phantom, never set at runtime.\n /** @internal */\n declare readonly [ComponentKindBrand]?: 'button';\n}\n","import { ComponentKindBrand } from '@seedcord/core/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { ComponentHandler } from './ComponentHandler';\n\nimport type { SentMessage } from '#reply/ReplySender';\nimport type { AnyCustomId } from '@seedcord/core/internal';\nimport type { ReplyResponse } from '@seedcord/types';\nimport type { APIModalSubmitInteraction } from 'discord-api-types/v10';\n\n/**\n * Base class for a modal submit handler on the HTTP transport.\n *\n * Register the customId definitions this handler decodes with `@ModalRoute` and list the same ones in the\n * generic. Read the submitted inputs off `this.event.data.components`. Reply through the handler members.\n * Discord forbids opening a modal in response to a modal, so `showModal` fails compilation on this kind.\n *\n * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof ConfigId]`.\n */\nexport abstract class ModalHandler<Defs extends readonly AnyCustomId[]> extends ComponentHandler<\n APIModalSubmitInteraction,\n Defs\n> {\n // phantom, never set at runtime.\n /** @internal */\n declare readonly [ComponentKindBrand]?: 'modal';\n\n /** Rewrite the message this modal was opened from. Throws when a command opened the modal. */\n protected override update(response: ReplyResponse | string): Promise<SentMessage> {\n this.ensureSourceMessage('update');\n return super.update(response);\n }\n\n /** Acknowledge the submit without changing the source message. Throws when a command opened the modal. */\n protected override deferUpdate(): Promise<void> {\n this.ensureSourceMessage('deferUpdate');\n return super.deferUpdate();\n }\n\n // discord omits the message when a command opened the modal, so update and deferUpdate have no target\n private ensureSourceMessage(method: string): void {\n if (this.event.message) return;\n throw new SeedcordError(SeedcordErrorCode.ReplyUpdateWithoutSource, [method, this.routeId]);\n }\n}\n","import { ComponentKindBrand } from '@seedcord/core/internal';\n\nimport { ComponentHandler } from './ComponentHandler';\n\nimport type { SelectMenuKind } from '@seedcord/core';\nimport type { AnyCustomId } from '@seedcord/core/internal';\nimport type {\n APIMessageChannelSelectInteractionData,\n APIMessageComponentSelectMenuInteraction,\n APIMessageMentionableSelectInteractionData,\n APIMessageRoleSelectInteractionData,\n APIMessageStringSelectInteractionData,\n APIMessageUserSelectInteractionData\n} from 'discord-api-types/v10';\n\ntype SelectMenuInteractionFor<Kind extends SelectMenuKind> = APIMessageComponentSelectMenuInteraction & {\n data: Kind extends SelectMenuKind.String\n ? APIMessageStringSelectInteractionData\n : Kind extends SelectMenuKind.User\n ? APIMessageUserSelectInteractionData\n : Kind extends SelectMenuKind.Role\n ? APIMessageRoleSelectInteractionData\n : Kind extends SelectMenuKind.Channel\n ? APIMessageChannelSelectInteractionData\n : Kind extends SelectMenuKind.Mentionable\n ? APIMessageMentionableSelectInteractionData\n : never;\n};\n\n/**\n * Base class for a select menu handler on the HTTP transport.\n *\n * Pass the select kind first and the customId definitions second, the same order as `@SelectMenuRoute`,\n * so `this.event.data` and its `values`/`resolved` are narrowed to that kind. Read `this.params` for a\n * single route or `this.match` for several, and reply through the handler members or rewrite the source\n * message with `this.update`.\n *\n * @typeParam Kind - The select kind from {@link SelectMenuKind}, e.g. `SelectMenuKind.User`.\n * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof AssignId]`.\n */\nexport abstract class SelectMenuHandler<\n Kind extends SelectMenuKind,\n Defs extends readonly AnyCustomId[]\n> extends ComponentHandler<SelectMenuInteractionFor<Kind>, Defs> {\n // phantom, never set at runtime.\n /** @internal */\n declare readonly [ComponentKindBrand]?: Kind;\n}\n","import { Subscriber as CoreSubscriber, WebhookLog as CoreWebhookLog } from '@seedcord/core';\n\nimport type { Core } from '#interfaces/Core';\nimport type { SubscriptionKey } from '@seedcord/core';\n\n/**\n * Base class for an HTTP subscriber, binding `this.core` to the HTTP {@link Core}.\n *\n * @typeParam KeyOfSubscribers - The subscription key this handler receives\n */\nexport abstract class Subscriber<KeyOfSubscribers extends SubscriptionKey> extends CoreSubscriber<\n KeyOfSubscribers,\n Core\n> {}\n\n/**\n * Base class for an HTTP subscriber that delivers its event to a Discord webhook, binding\n * `this.core` to the HTTP {@link Core}.\n *\n * @typeParam KeyOfSubscribers - The subscription key this reporter receives\n */\nexport abstract class WebhookLog<KeyOfSubscribers extends SubscriptionKey> extends CoreWebhookLog<\n KeyOfSubscribers,\n Core\n> {}\n","import { PaginatorBase } from '@seedcord/core';\n\nimport { ButtonHandler } from '#handlers/interaction/components';\n\nimport type { RepliableHandler } from '#handlers/RepliableHandler';\nimport type { Core } from '#interfaces/Core';\nimport type { SentMessage } from '#reply/ReplySender';\nimport type { Repliables } from '#src/handlers/interactionTypes';\nimport type { PageContext } from './PageContext';\nimport type { PaginatorConfig } from '@seedcord/core';\nimport type { PageCursor } from '@seedcord/core/internal';\nimport type { ReplyResponse } from '@seedcord/types';\nimport type { APIMessageComponentButtonInteraction, APIUser } from 'discord-api-types/v10';\n\n// `& { execute }` concretizes the abstract execute so the empty `extends Bans.Handler {}` stays concrete\n// (no TS2515) and a concrete Nav assigns with no cast.\ntype PaginatorHandlerCtor<Prefix extends string> = new (\n event: APIMessageComponentButtonInteraction,\n core: Core\n) => ButtonHandler<[PageCursor<Prefix>]> & { execute(): Promise<void> };\n\nfunction contextOf(interaction: Repliables, core: Core): PageContext {\n // discord sends member.user in a guild and the top-level user in a dm\n const resolved = interaction.member?.user ?? interaction.user;\n return {\n interaction,\n // justified: every repliable interaction Discord delivers carries a user, and the wrapper types don't say so\n user: resolved as APIUser,\n guildId: interaction.guild_id ?? null,\n core\n };\n}\n\n/**\n * A restart-proof paginator. Each nav button's customId encodes its full target page, so clicks are\n * idempotent and survive a restart. A persistent `@ButtonRoute` on `Handler` dispatches the clicks.\n *\n * @typeParam Item - The item type, inferred from the source.\n * @typeParam Prefix - The route prefix, inferred from `config.prefix`.\n *\n * @example\n * ```ts\n * export const Bans = new Paginator({\n * prefix: 'bans',\n * source: new ArraySource((ctx) => loadBans(ctx.guildId), { perPage: 10 }),\n * renderItem: (ban) => ban.user.username\n * });\n *\n * \\@ButtonRoute(Bans.cursor)\n * export class BansNav extends Bans.Handler {}\n * ```\n */\nexport class Paginator<Item, const Prefix extends string> extends PaginatorBase<Item, Prefix, PageContext> {\n /** The nav handler base. Extend it with an empty body and decorate it, `@ButtonRoute(p.cursor)`. */\n public readonly Handler: PaginatorHandlerCtor<Prefix>;\n\n constructor(config: PaginatorConfig<Item, Prefix, PageContext>) {\n super(config);\n\n // an arrow captures the paginator lexically so Nav.execute can reach it without aliasing `this`.\n const loadPage = (ctx: PageContext, n: number): Promise<ReplyResponse> => this.page(ctx, n);\n this.Handler = class Nav extends ButtonHandler<[PageCursor<Prefix>]> {\n async execute(): Promise<void> {\n await this.deferUpdate();\n const response = await loadPage(contextOf(this.event, this.core), this.params.page);\n // deferUpdate seeds the sender deferred-update, so update PATCHes @original\n await this.update(response);\n }\n };\n }\n\n /**\n * Render page 0 and send it through the handler's sender, which picks reply, edit, or followUp from its\n * ack state.\n *\n * @param handler - The handler starting the paginator, normally `this`.\n */\n async start(handler: RepliableHandler<Repliables>): Promise<SentMessage> {\n const interaction = handler.getEvent();\n const response = await this.page(contextOf(interaction, handler.core), 0);\n return handler.getSender().send(response, { ephemeral: this.config.ephemeral ?? false });\n }\n}\n","import { ArraySource as BaseArraySource, CursorSource as BaseCursorSource } from '@seedcord/core';\n\nimport type { PageContext } from './PageContext';\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, so the real total is known (a real last button, \"Page X of Y\"). Cache inside your loader if the\n * load is expensive.\n *\n * @typeParam Item - The item type, inferred from the loader's return.\n */\nexport class ArraySource<Item> extends BaseArraySource<Item, PageContext> {}\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 */\nexport class CursorSource<Item> extends BaseCursorSource<Item, PageContext> {}\n","import { GatedMetadataKey } from '@seedcord/core/internal';\n\nimport type { AnyHandlerCtor, FitAll } from './matching';\nimport type { Gate, GateContextBase } from '@seedcord/core';\nimport type { NonEmptyTuple } from 'type-fest';\n\n/**\n * Attaches gates to an HTTP interaction handler. The gates run before `execute`, and a gate refusing stops\n * the handler with the reply or drop the gate threw. Multiple gates are ANDed. A gate\n * that requires a context the handler does not provide (a button gate on a slash handler, a gate on an\n * autocomplete handler, or a `RequirePermissions([...], { in: 'guild' })` gate the http transport cannot\n * satisfy) is a compile error at this line, and the error names the gate and the handler kind. Place it\n * above the route decorator.\n *\n * Combine gates with {@link and} or {@link or} to build a single gate from multiple arms.\n *\n * @example\n * ```ts\n * import { PermissionFlagsBits } from 'discord-api-types/v10';\n *\n * \\@Gated(GuildOnly(), RequirePermissions([PermissionFlagsBits.ManageMessages]))\n * \\@SlashRoute('purge')\n * class Purge extends SlashHandler<'purge'> {\n * async execute() {\n * // both gates passed, so this runs in a guild with a caller who can moderate this channel\n * }\n * }\n * ```\n *\n * @typeParam Gates - The non-empty tuple of gates to run, in order, against the handler's context.\n * @typeParam TCtor - The decorated handler's constructor, checked against every gate's required context.\n * @param gates - One or more gates to attach, each ANDed so the handler runs only when all pass.\n * @param ctor - The handler class being decorated, rejected at compile time when a gate requires a context it lacks.\n *\n * @see {@link defineGate}\n * @see {@link defineEffectGate}\n * @decorator\n */\nexport function Gated<const Gates extends NonEmptyTuple<Gate<GateContextBase>>>(...gates: Gates) {\n // the class fails to assign and TS names the gate\n return function <TCtor extends AnyHandlerCtor>(\n ctor: FitAll<TCtor, Gates> extends Gates ? TCtor : FitAll<TCtor, Gates>\n ): void {\n const existing = (Reflect.getMetadata(GatedMetadataKey, ctor) as readonly Gate[] | undefined) ?? [];\n Reflect.defineMetadata(GatedMetadataKey, [...existing, ...gates], ctor);\n };\n}\n","export const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":"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"}
|