@seedcord/http 0.0.1 → 0.1.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":";;AACA,MAAa"}
1
+ {"version":3,"file":"index.mjs","names":["CoreRepliableHandler"],"sources":["../src/reply/ReplySender.ts","../src/handlers/RepliableHandler.ts","../src/gates/context.ts","../src/dispatch/dispatchInteraction.ts","../src/inputs/HttpSlashOptions.ts","../src/dispatch/slashRouteOf.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/createSeedcord.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/SelectMenuHandler.ts","../src/handlers/interaction/components/ModalHandler.ts","../src/gates/Gated.ts","../src/index.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 { 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, 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}\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, so alt text goes on the wire attachments entries\nfunction wireAttachments(files: SerializedReply['files']): WireAttachment[] | null {\n if (!files?.some((file) => file.description)) return null;\n return files.map((file, index) => ({\n id: index,\n ...(file.name && { filename: file.name }),\n ...(file.description && { description: file.description })\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 ) {\n super(routeId);\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\n // pins that contract explicitly 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, index) => ({ name: file.name ?? `file-${index}`, data: file.attachment }));\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 { RepliableHandler as CoreRepliableHandler } from '@seedcord/core';\n\nimport { ReplySender } from '@reply/ReplySender';\n\nimport type { Repliables } from './interactionTypes';\nimport type { Core } from '@interfaces/Core';\nimport type { SentMessage } from '@reply/ReplySender';\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 ReplySender\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);\n }\n}\n","import type { InteractionGateContext } from './Gate';\nimport type { Core } from '@interfaces/Core';\nimport type { Repliables } from '@src/handlers/interactionTypes';\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, so it is never null here\n appPermissions: BigInt(payload.app_permissions),\n routeId\n };\n}\n","import { DiscordAPIError, REST } from '@discordjs/rest';\nimport { BaseHandler, DispatchContext, Fault, Notice, Silence } from '@seedcord/core';\nimport { runHandlerGates, slowGateMonitor } from '@seedcord/core/internal';\nimport { Logger, paint } 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 type { ResolvedRoute } from './resolve';\nimport type { ValidInteractionTypes } from '@handlers/interactionTypes';\nimport type { Core } from '@interfaces/Core';\nimport type { Config, IRateLimiter, RenderContext, TypedOmit } from '@seedcord/types';\n\n// lazy because the logger reads the environment, which binds after this module loads\nlet dispatchLogger: Logger | undefined;\nfunction logger(): Logger {\n dispatchLogger ??= new Logger('Dispatcher');\n return dispatchLogger;\n}\n\ninterface HttpHandler {\n execute(): Promise<void>;\n}\n\ntype HandlerCtor = new (event: ValidInteractionTypes, core: Core, dispatch?: DispatchContext) => HttpHandler;\n\nexport function createCore(config: Config, token: string): Core {\n const rateLimiter: IRateLimiter = config.store ?? new MemoryRateLimiter();\n // the omit type makes TS check every present field, and the cast below only adds the three named absences\n const core: TypedOmit<Core, 'start' | 'shutdown' | 'startup'> = {\n config,\n rateLimiter,\n rest: new REST().setToken(token),\n version: process.env.PACKAGE_VERSION ?? '0.0.0',\n username: undefined,\n augmentTarget: '@seedcord/http'\n };\n // justified: start, shutdown, and startup get added later, dispatch reads none of them\n return core as Core;\n}\n\nfunction isHandlerCtor(value: unknown): value is HandlerCtor {\n return typeof value === 'function' && value.prototype instanceof BaseHandler;\n}\n\nfunction handlerCtorOf(moduleExports: unknown): HandlerCtor | null {\n if (typeof moduleExports !== 'object' || moduleExports === null) return null;\n for (const value of Object.values(moduleExports)) {\n if (isHandlerCtor(value)) return value;\n }\n return null;\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// these codes from the boundary's own send mean the interaction is gone\nconst HARMLESS_API_CODES: ReadonlySet<number | string> = new Set([\n RESTJSONErrorCodes.UnknownInteraction,\n RESTJSONErrorCodes.InteractionHasAlreadyBeenAcknowledged,\n RESTJSONErrorCodes.UnknownMessage\n]);\n\n// never rethrows\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 Notice is illegal on autocomplete, so empty choices clear the pending state\nasync function respondEmptyChoices(scope: FaultScope): Promise<void> {\n await scope.core.rest.post(Routes.interactionCallback(scope.payload.id, scope.payload.token), {\n body: { type: InteractionResponseType.ApplicationCommandAutocompleteResult, data: { choices: [] } }\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) logger().error(`${notice.name}: ${paint.mute(uuid)}`, notice);\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 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) logger().debug(`Silence: ${caught.reason}`);\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 = Error.isError(caught) ? caught : new Error(String(caught));\n\n // empty by default, so every api code from the handler's own work reports\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\n// nothing is acked in the pre-handler failure paths, 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 = match.routeId ?? 'unhandled';\n return {\n core,\n payload,\n routeId,\n sender: match.kind === 'autocomplete' ? null : new ReplySender(ref, core.rest, routeId)\n };\n}\n\n/**\n * Runs the pre-ack phase for a matched interaction. Loads the route module, constructs the handler, and\n * runs its gates, and the sender posts any refusal. Returns the post-ack execute continuation, or null\n * when a gate refuses or nothing can run.\n */\nexport async function dispatchInteraction(args: DispatchArgs): Promise<(() => Promise<void>) | null> {\n const { match, payload, core } = args;\n\n let moduleExports: unknown;\n try {\n moduleExports = await match.load();\n } catch (caught) {\n logger().error(`Route ${paint.sky.bold(match.routeId ?? 'unhandled')} failed to load its module.`, caught);\n await handleFault(caught, freshScope(match, payload, core));\n return null;\n }\n\n const ctor = handlerCtorOf(moduleExports);\n if (!ctor) {\n const routeId = match.routeId ?? 'unhandled';\n logger().error(`Route ${paint.sky.bold(routeId)} loaded a module with no handler class.`);\n await handleFault(\n new Error(`route ${routeId} loaded a module with no handler class`),\n freshScope(match, payload, core)\n );\n return null;\n }\n\n const dispatch = new DispatchContext(match.routeId);\n let handler: HttpHandler;\n try {\n handler = new ctor(payload, core, dispatch);\n } catch (caught) {\n await handleFault(caught, freshScope(match, payload, core));\n return null;\n }\n const scope: FaultScope = {\n core,\n payload,\n routeId: match.routeId ?? handler.constructor.name,\n sender: handler instanceof RepliableHandler ? handler.getSender() : null\n };\n\n if (!(await passedGates(ctor, match, payload, core, scope))) return null;\n\n return async () => {\n try {\n await handler.execute();\n } catch (caught) {\n await handleFault(caught, scope);\n }\n };\n}\n\n// autocomplete has no reply target, @Gated rejects it at compile time, this is the runtime backstop\nasync function passedGates(\n ctor: HandlerCtor,\n match: ResolvedRoute,\n payload: ValidInteractionTypes,\n core: Core,\n scope: FaultScope\n): Promise<boolean> {\n // the router derives match.kind from payload.type, so the two would be the same. the payload.type clause narrows\n // the union to Repliables for interactionGateContext below\n if (match.kind === 'autocomplete' || payload.type === InteractionType.ApplicationCommandAutocomplete) return true;\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 true;\n } catch (caught) {\n await handleFault(caught, scope);\n return false;\n } finally {\n // a refusing gate ate budget too, report either way\n monitor?.report(match.routeId);\n }\n}\n","import { ApplicationCommandOptionType } from 'discord-api-types/v10';\n\nimport type {\n APIApplicationCommandInteractionDataBasicOption,\n APIApplicationCommandInteractionDataOption,\n APIAttachment,\n APIAutocompleteApplicationCommandInteractionData,\n APIChatInputApplicationCommandInteractionData,\n APIInteractionDataResolved,\n APIInteractionDataResolvedChannel,\n APIInteractionDataResolvedGuildMember,\n APIRole,\n APIUser,\n InteractionType\n} from 'discord-api-types/v10';\n\ntype SlashLikeData = APIChatInputApplicationCommandInteractionData | APIAutocompleteApplicationCommandInteractionData;\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 { 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\ntype SlashLikeData = (APIChatInputApplicationCommandInteraction | APIApplicationCommandAutocompleteInteraction)['data'];\n\n/**\n * Computes the dispatch route for a chat-input or autocomplete payload, the raw-payload port of the\n * gateway's `slashRouteOf`. Both assemble keys through `buildSlashRoute`, so a dispatched interaction\n * and a generated registry key always match.\n */\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 { BaseHandler } from '@seedcord/core';\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 { slashRouteOf } from '@src/dispatch/slashRouteOf';\n\nimport type { Core } from '@interfaces/Core';\nimport type { AutocompleteOptions, DispatchContext, SlashOptionRegistry } 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 SlashOptionRegistry, 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 SlashOptionRegistry}, e.g. `'search'`.\n */\nexport abstract class AutocompleteHandler<Route extends keyof SlashOptionRegistry> 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);\n }\n\n // phantom, never set at runtime.\n /** @internal */\n declare readonly __autocompleteRoute?: Route;\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 the set 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, so\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 await this.core.rest.post(Routes.interactionCallback(this.event.id, this.event.token), {\n body: { type: InteractionResponseType.ApplicationCommandAutocompleteResult, data: { choices } }\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 // the reader exposes these getters, AutocompleteOptions is the registry-typed view over it.\n return this.reader as AutocompleteOptions<Route>;\n }\n}\n","import { AutocompleteHandler } from '@handlers/interaction/AutocompleteHandler';\n\n/**\n * Default handler for autocomplete no manifest row matches.\n * Empty choices are the only legal answer, they clear the pending state.\n *\n * @internal\n */\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, 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/**\n * Default handler for interactions no manifest row matches. A reply failure here\n * goes directly to the dispatch fault boundary, so it needs no guard of its own.\n *\n * @internal\n */\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 { 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 } 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 readonly load: () => Promise<unknown>;\n}\n\ntype 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\nconst COMPONENT_KIND: Record<ComponentMapKey, ResolvedKind> = {\n button: 'button',\n stringSelect: 'stringMenu',\n userSelect: 'userMenu',\n roleSelect: 'roleMenu',\n channelSelect: 'channelMenu',\n mentionableSelect: 'mentionableMenu',\n modal: 'modal'\n};\n\nfunction commandKind(type: ApplicationCommandType): 'slash' | 'userContextMenu' | 'messageContextMenu' | 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 set(map: RouteMap, kind: ResolvedKind, key: string, load: () => Promise<unknown>): void {\n map.set(key, { kind, routeId: `${kind}:${key}`, load });\n}\n\n/** Builds the per-kind lookup maps. */\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 for (const row of manifest.commands) {\n const kind = commandKind(row.type);\n if (kind) set(maps[kind], kind, row.name, row.load);\n }\n for (const row of manifest.autocomplete) {\n set(maps.autocomplete, 'autocomplete', row.name, row.load);\n }\n for (const row of manifest.components) {\n set(maps.components[row.kind], COMPONENT_KIND[row.kind], row.prefix, row.load);\n }\n return maps;\n}\n\n// dispatched through the normal pipeline like the gateway's unhandled default\nfunction unhandled(kind: ResolvedKind): ResolvedRoute {\n return {\n kind,\n routeId: null,\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 return maps.slash.get(slashRouteOf(interaction.data)) ?? unhandled('slash');\n }\n const kind = commandKind(interaction.data.type);\n return kind ? (maps[kind].get(interaction.data.name) ?? unhandled(kind)) : null;\n }\n case InteractionType.ApplicationCommandAutocomplete: {\n return maps.autocomplete.get(slashRouteOf(interaction.data)) ?? unhandled('autocomplete');\n }\n case InteractionType.MessageComponent: {\n const key = componentMapKey(interaction.data.component_type);\n if (!key) return null;\n return maps.components[key].get(prefixOf(interaction.data.custom_id)) ?? unhandled(COMPONENT_KIND[key]);\n }\n case InteractionType.ModalSubmit: {\n return maps.components.modal.get(prefixOf(interaction.data.custom_id)) ?? unhandled('modal');\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) throw new SeedcordError(SeedcordErrorCode.ConfigIncorrectPublicKey);\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-signature map is per-isolate on edge, so only the freshness check rejects a replay\n// against a fresh isolate\nexport class ReplayGuard {\n private readonly seen = new Map<string, number>();\n private lastSweep = Date.now();\n\n // call after signature verification, so the map stores only discord-signed signatures\n public accepts(signatureHex: string, timestampHeader: string): boolean {\n const now = Date.now();\n this.maybeSweep(now);\n\n if (!TIMESTAMP_PATTERN.test(timestampHeader)) return false;\n const timestampMs = Number(timestampHeader) * MS_PER_SECOND;\n if (Math.abs(now - timestampMs) > REPLAY_WINDOW_MS) return false;\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 { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError, validateDiscordToken } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\nimport { InteractionResponseType, InteractionType } from 'discord-api-types/v10';\nimport { Converters, Envapter } from 'envapt';\n\nimport { createCore, dispatchInteraction } from './dispatch/dispatchInteraction';\nimport { buildRouteMaps, resolve } from './dispatch/resolve';\nimport { Ed25519Verifier } from './receiver/Ed25519Verifier';\nimport { ReplayGuard } from './receiver/ReplayGuard';\n\nimport type { ValidInteractionTypes } from '@handlers/interactionTypes';\nimport type { Config } from '@seedcord/types';\nimport type { RouteManifest } from '@src/manifest/RouteManifest';\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\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\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: Config,\n manifest: RouteManifest\n): (request: Request, ctx?: EngineContext) => Promise<Response> {\n if (!Envapter.has('DISCORD_PUBLIC_KEY')) throw new SeedcordError(SeedcordErrorCode.ConfigMissingPublicKey);\n const publicKey = Envapter.getRequired('DISCORD_PUBLIC_KEY', Converters.String);\n const token = validateDiscordToken(Envapter.get('DISCORD_BOT_TOKEN'));\n\n const verifier = new Ed25519Verifier(publicKey);\n const replays = new ReplayGuard();\n const maps = buildRouteMaps(manifest);\n const inFlight = new Set<Promise<void>>();\n const core = createCore(config, token);\n // constructed here because the logger reads the environment, which binds before this factory runs\n const logger = new Logger('Engine');\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 const work = start();\n if (ctx) {\n ctx.waitUntil(work);\n } else {\n inFlight.add(work);\n void work.finally(() => inFlight.delete(work));\n }\n }\n\n return async (request, ctx) => {\n if (request.method !== 'POST') {\n return new Response(null, { status: METHOD_NOT_ALLOWED, headers: { allow: '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 });\n\n const body = new Uint8Array(await request.arrayBuffer());\n if (!(await verifier.verify(signature, timestamp, body))) {\n return new Response(null, { status: UNAUTHORIZED });\n }\n if (!replays.accepts(signature, timestamp)) return new Response(null, { status: UNAUTHORIZED });\n\n let payload: unknown;\n try {\n payload = JSON.parse(decoder.decode(body));\n } catch {\n return new Response(null, { status: BAD_REQUEST });\n }\n if (!hasInteractionType(payload)) return new Response(null, { status: BAD_REQUEST });\n\n if (payload.type === PING) {\n return Response.json({ type: InteractionResponseType.Pong });\n }\n\n try {\n // justified: the payload is signed by Discord, the type field anchors the discriminated union\n const match = resolve(maps, payload as APIInteraction);\n if (match) await dispatchMatched(match, payload as ValidInteractionTypes, ctx);\n } catch (caught) {\n // a throw here must never eat the ack\n logger.error('dispatch failed, acking anyway', caught);\n }\n\n return new Response(null, { status: ACCEPTED });\n };\n}\n","import { 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 { SlashOptionRegistry } from '@seedcord/core';\nimport type { APIChatInputApplicationCommandInteraction } from 'discord-api-types/v10';\nimport type { Promisable } from 'type-fest';\n\ntype SlashMatchArms<Route extends keyof SlashOptionRegistry, 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 SlashOptionRegistry}, e.g. `'ban'` or `'ban' | 'kick'`.\n */\nexport abstract class SlashHandler<\n Route extends keyof SlashOptionRegistry\n> extends InteractionHandler<APIChatInputApplicationCommandInteraction> {\n // phantom, never set at runtime.\n /** @internal */\n declare readonly __slashRoute?: 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 // the reader exposes every getter, SlashOptions is the registry-typed view over it.\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 { ApplicationCommandType } from 'discord-api-types/v10';\n\nimport { InteractionHandler } from '@handlers/interaction/InteractionHandler';\n\nimport type {\n APIInteractionDataResolvedGuildMember,\n APIMessage,\n APIMessageApplicationCommandInteraction,\n APIUser,\n APIUserApplicationCommandInteraction\n} from 'discord-api-types/v10';\n\ntype ContextMenuKind = ApplicationCommandType.User | ApplicationCommandType.Message;\n\ntype InteractionFor<Kind extends ContextMenuKind> = Kind extends ApplicationCommandType.User\n ? APIUserApplicationCommandInteraction\n : APIMessageApplicationCommandInteraction;\n\ntype TargetFor<Kind extends ContextMenuKind> = Kind extends ApplicationCommandType.User ? APIUser : APIMessage;\n\ntype TargetMemberFor<Kind extends ContextMenuKind> = Kind extends ApplicationCommandType.User\n ? APIInteractionDataResolvedGuildMember | null\n : never;\n\n/**\n * Base class for a context-menu command handler on the HTTP transport (right-click a user or a message).\n *\n * Pass the kind from {@link ApplicationCommandType} as the generic. Read the right-clicked entity from\n * `this.target`, an `APIUser` for a user menu and an `APIMessage` for a message menu. Context menus carry\n * no options, so a handler registered for several names reads `this.target` uniformly with no per-name\n * branch.\n *\n * @typeParam Kind - `ApplicationCommandType.User` or `ApplicationCommandType.Message`.\n */\nexport abstract class ContextMenuHandler<Kind extends ContextMenuKind> extends InteractionHandler<\n InteractionFor<Kind>\n> {\n // phantom, never set at runtime.\n /** @internal */\n declare readonly __ctxKind?: Kind;\n\n protected get target(): TargetFor<Kind> {\n const data = this.event.data;\n const target =\n data.type === ApplicationCommandType.User\n ? data.resolved.users[data.target_id]\n : data.resolved.messages[data.target_id];\n // justified: Discord resolves the target it delivered target_id for, the Kind generic picks the arm.\n return target as TargetFor<Kind>;\n }\n\n /**\n * The right-clicked user's guild member, resolved only on user menus fired in a guild. Reading it on a\n * message menu is a compile error.\n */\n protected get targetMember(): TargetMemberFor<Kind> {\n const data = this.event.data;\n const member =\n data.type === ApplicationCommandType.User ? (data.resolved.members?.[data.target_id] ?? null) : null;\n // justified: only the User-kind event includes resolved members, the Message-kind read is unreachable per the type.\n return member as TargetMemberFor<Kind>;\n }\n}\n","import { 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 __componentDefs?: 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 { 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 __component?: 'button';\n}\n","import { 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 __component?: Kind;\n}\n","import { 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 __component?: 'modal';\n\n /** Rewrite the message this modal was opened from (only when a component opened it). */\n protected override update(response: ReplyResponse | string): Promise<SentMessage> {\n this.ensureSourceMessage('update');\n return super.update(response);\n }\n\n /** Silently acknowledge, leaving the source message untouched. */\n protected override deferUpdate(): Promise<void> {\n this.ensureSourceMessage('deferUpdate');\n return super.deferUpdate();\n }\n\n // discord omits message when a command opened the modal, the source verbs have no target then\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 { 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, so each must pass. 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 // mismatch resolves to the error tuple, so 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 { createSeedcord } from './createSeedcord';\nexport type { EngineContext } from './createSeedcord';\n\nexport type { RouteManifest } from './manifest/RouteManifest';\n\nexport type { Core } from '@interfaces/Core';\n\nexport type { SlashOptions } from '@inputs/SlashOptions';\n\nexport * from '@seedcord/core';\n\nexport * from './handlers';\n// the core and ./handlers `export *` lines both have a RepliableHandler, and es modules drop a name\n// exported by two stars, so this explicit export keeps the http subclass on the barrel\nexport { RepliableHandler } from '@handlers/RepliableHandler';\n\nexport { Gated } from '@src/gates/Gated';\nexport type { InteractionGateContext } from '@src/gates/Gate';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,62 +1,77 @@
1
1
  {
2
- "name": "@seedcord/http",
3
- "type": "module",
4
- "version": "0.0.1",
5
- "description": "HTTP-interactions transport for Seedcord bots",
6
- "engines": {
7
- "node": ">=24.3"
8
- },
9
- "repository": {
10
- "type": "git",
11
- "url": "https://github.com/seedcord/seedcord.git",
12
- "directory": "packages/http"
13
- },
14
- "types": "./dist/index.d.mts",
15
- "exports": {
16
- ".": {
17
- "import": {
18
- "types": "./dist/index.d.mts",
19
- "default": "./dist/index.mjs"
20
- },
21
- "require": {
22
- "types": "./dist/index.d.cts",
23
- "default": "./dist/index.cjs"
24
- }
25
- }
26
- },
27
- "files": [
28
- "dist",
29
- "package.json",
30
- "README.md",
31
- "LICENSE"
32
- ],
33
- "scripts": {
34
- "build": "tsdown",
35
- "clean": "rm -rf dist",
36
- "lint": "eslint 'src/**/*.{ts,tsx}' 'tests/**/*.{ts,tsx}' --cache",
37
- "lint:fix": "eslint 'src/**/*.{ts,tsx}' 'tests/**/*.{ts,tsx}' --fix --cache",
38
- "fmt": "prettier --write 'src/**/*.{ts,tsx,json,md}' 'tests/**/*.{ts,tsx,json,md}' --cache",
39
- "fmt:check": "prettier --check 'src/**/*.{ts,tsx,json,md}' 'tests/**/*.{ts,tsx,json,md}' --cache",
40
- "tc": "tsgo --noEmit",
41
- "test": "vitest run",
42
- "test:watch": "vitest dev",
43
- "coverage": "vitest run --coverage"
44
- },
45
- "dependencies": {
46
- "@seedcord/errors": "workspace:*"
47
- },
48
- "peerDependencies": {
49
- "typescript": "catalog:peer"
50
- },
51
- "devDependencies": {
52
- "@cloudflare/vitest-pool-workers": "^0.18.4",
53
- "@vitest/coverage-istanbul": "^4.1.9",
54
- "@seedcord/eslint-config": "workspace:*",
55
- "@seedcord/tsconfig": "workspace:*",
56
- "@seedcord/tsdown-config": "workspace:*"
57
- },
58
- "publishConfig": {
59
- "access": "public",
60
- "provenance": true
2
+ "name": "@seedcord/http",
3
+ "type": "module",
4
+ "version": "0.1.0-next.0",
5
+ "description": "HTTP-interactions transport for Seedcord bots",
6
+ "engines": {
7
+ "node": ">=24.3"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/seedcord/seedcord.git",
12
+ "directory": "packages/http"
13
+ },
14
+ "types": "./dist/index.d.mts",
15
+ "exports": {
16
+ ".": {
17
+ "import": {
18
+ "types": "./dist/index.d.mts",
19
+ "default": "./dist/index.mjs"
20
+ },
21
+ "require": {
22
+ "types": "./dist/index.d.cts",
23
+ "default": "./dist/index.cjs"
24
+ }
61
25
  }
62
- }
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "package.json",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "dependencies": {
34
+ "@discordjs/rest": "^2.6.2",
35
+ "discord-api-types": "^0.38.50",
36
+ "envapt": "^8.1.0",
37
+ "type-fest": "^5.7.0",
38
+ "@seedcord/core": "0.1.0-next.5",
39
+ "@seedcord/errors": "0.3.0-next.4",
40
+ "@seedcord/logger": "0.1.0-next.1",
41
+ "@seedcord/rate-limiter": "0.1.0-next.3",
42
+ "@seedcord/types": "0.8.0-next.6",
43
+ "@seedcord/utils": "0.8.0-next.6"
44
+ },
45
+ "peerDependencies": {
46
+ "reflect-metadata": "^0.2.2",
47
+ "typescript": "^6.0.3"
48
+ },
49
+ "devDependencies": {
50
+ "@cloudflare/vitest-pool-workers": "^0.18.4",
51
+ "@discordjs/builders": "^1.14.1",
52
+ "@vitest/coverage-istanbul": "^4.1.10",
53
+ "esbuild": "^0.28.1",
54
+ "reflect-metadata": "^0.2.2",
55
+ "vitest": "^4.1.10",
56
+ "@seedcord/eslint-config": "1.5.0-next.4",
57
+ "@seedcord/tsconfig": "2.0.1",
58
+ "@seedcord/tsdown-config": "2.0.1"
59
+ },
60
+ "publishConfig": {
61
+ "access": "public",
62
+ "provenance": true
63
+ },
64
+ "scripts": {
65
+ "build": "tsdown",
66
+ "clean": "rm -rf dist",
67
+ "lint": "eslint 'src/**/*.{ts,tsx}' 'tests/**/*.{ts,tsx}' --cache",
68
+ "lint:fix": "eslint 'src/**/*.{ts,tsx}' 'tests/**/*.{ts,tsx}' --fix --cache",
69
+ "fmt": "prettier --write 'src/**/*.{ts,tsx,json,md}' 'tests/**/*.{ts,tsx,json,md}' --cache",
70
+ "fmt:check": "prettier --check 'src/**/*.{ts,tsx,json,md}' 'tests/**/*.{ts,tsx,json,md}' --cache",
71
+ "tc": "tsgo --noEmit",
72
+ "test": "vitest run",
73
+ "test:watch": "vitest dev",
74
+ "coverage": "vitest run --coverage"
75
+ },
76
+ "readme": "<p align=\"center\">\n <picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://cdn.seedcord.org/assets/wordmark-dark.webp\" />\n <img src=\"https://cdn.seedcord.org/assets/wordmark-light.webp\" alt=\"seedcord\" width=\"440\" />\n </picture>\n</p>\n\n# @seedcord/http\n\nHTTP-interactions transport for Seedcord bots\n\nPart of the [seedcord](https://github.com/seedcord/seedcord) framework. Until a major v1.0.0 release, expect breaking changes in minor versions.\n"
77
+ }