@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.
- package/dist/index.cjs +1023 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +504 -1
- package/dist/index.mjs +1009 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +75 -60
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,509 @@
|
|
|
1
|
+
import { APIApplicationCommandAutocompleteInteraction, APIApplicationCommandOptionChoice, APIAttachment, APIChatInputApplicationCommandInteraction, APIContextMenuInteraction, APIInteractionDataResolvedChannelBase, APIInteractionDataResolvedGuildMember, APIInteractionGuildMember, APIMessage, APIMessageApplicationCommandInteraction, APIMessageChannelSelectInteractionData, APIMessageComponentButtonInteraction, APIMessageComponentInteraction, APIMessageComponentSelectMenuInteraction, APIMessageMentionableSelectInteractionData, APIMessageRoleSelectInteractionData, APIMessageStringSelectInteractionData, APIMessageUserSelectInteractionData, APIModalInteractionResponseCallbackData, APIModalSubmitInteraction, APIRole, APIThreadChannel, APIUser, APIUserApplicationCommandInteraction, ApplicationCommandType, ChannelType, ThreadChannelType } from "discord-api-types/v10";
|
|
2
|
+
import { REST } from "@discordjs/rest";
|
|
3
|
+
import { AutocompleteOptions, BaseHandler, CoreBase, DispatchContext, Gate, GateContextBase, GuildPermissionsContext, RepliableHandler as RepliableHandler$1, RequiredOf, SelectMenuKind, SlashOptionRegistry } from "@seedcord/core";
|
|
4
|
+
import { AnyCustomId, AutocompletableNames, BaseReplySender, ChoiceValueOf, EntryFor, FocusedField, GateFitsWith, HasComponentDefs, MatchArms, ModalLike, SingleParams, SlashOptions as SlashOptions$1 } from "@seedcord/core/internal";
|
|
5
|
+
import { Config, DeferOpts, ReplyResponse, SendOpts } from "@seedcord/types";
|
|
6
|
+
import { SeedcordInstance } from "@seedcord/types/internal";
|
|
7
|
+
import { NonEmptyTuple, Promisable } from "type-fest";
|
|
8
|
+
export * from "@seedcord/core";
|
|
9
|
+
|
|
10
|
+
//#region src/manifest/RouteManifest.d.ts
|
|
11
|
+
/**
|
|
12
|
+
* A slash or context-menu command row. `type` picks the per-kind map, `name` is the lookup key. For a
|
|
13
|
+
* chat-input command with subcommands, `name` is the full route path (`'config/set'`), the same key
|
|
14
|
+
* `@SlashRoute` registers.
|
|
15
|
+
*/
|
|
16
|
+
interface CommandRoute {
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly type: ApplicationCommandType;
|
|
19
|
+
/** Lazy import of the handler module, evaluated on first hit. */
|
|
20
|
+
readonly load: () => Promise<unknown>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A button, select, or modal row. `kind` picks the per-kind map, `prefix` is the stable customId prefix
|
|
24
|
+
* the receiver routes by (the routeKey minus its layout hash).
|
|
25
|
+
*/
|
|
26
|
+
interface ComponentRoute {
|
|
27
|
+
readonly kind: 'button' | 'stringSelect' | 'userSelect' | 'roleSelect' | 'channelSelect' | 'mentionableSelect' | 'modal';
|
|
28
|
+
readonly prefix: string;
|
|
29
|
+
/** Lazy import of the handler module, evaluated on first hit. */
|
|
30
|
+
readonly load: () => Promise<unknown>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* An autocomplete row, its own route keyed by the command's route path, separate from the slash row.
|
|
34
|
+
*/
|
|
35
|
+
interface AutocompleteRoute {
|
|
36
|
+
readonly name: string;
|
|
37
|
+
/** Lazy import of the handler module, evaluated on first hit. */
|
|
38
|
+
readonly load: () => Promise<unknown>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A bus subscriber row. The Bus routes off it without importing the subscriber module. The HTTP engine
|
|
42
|
+
* ignores these rows until the Bus ships on this transport.
|
|
43
|
+
*/
|
|
44
|
+
interface SubscriberRoute {
|
|
45
|
+
/** The subscription keys this subscriber registers for. */
|
|
46
|
+
readonly keys: readonly string[];
|
|
47
|
+
/** Mirrors the `@Subscribe` frequency metadata. */
|
|
48
|
+
readonly frequency: 'on' | 'once';
|
|
49
|
+
/** Lazy import of the subscriber module. */
|
|
50
|
+
readonly load: () => Promise<unknown>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The static route table the HTTP engine dispatches through, replacing the filesystem handler scan that
|
|
54
|
+
* cannot run in a bundled isolate. `seedcord build` emits it, the mount path authors it by hand.
|
|
55
|
+
*/
|
|
56
|
+
interface RouteManifest {
|
|
57
|
+
readonly commands: readonly CommandRoute[];
|
|
58
|
+
readonly components: readonly ComponentRoute[];
|
|
59
|
+
readonly autocomplete: readonly AutocompleteRoute[];
|
|
60
|
+
readonly subscribers: readonly SubscriberRoute[];
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/createSeedcord.d.ts
|
|
64
|
+
/** Structural match for the Workers `ExecutionContext`. Pass it from the edge entry so post-202 work runs under `waitUntil`. */
|
|
65
|
+
interface EngineContext {
|
|
66
|
+
waitUntil(promise: Promise<unknown>): void;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Builds the HTTP-interactions engine, a `(request, ctx?) => Promise<Response>` handler.
|
|
70
|
+
*
|
|
71
|
+
* The handler verifies the Ed25519 signature over the raw request bytes, rejects stale or replayed
|
|
72
|
+
* requests, answers a PING with an in-body PONG, and acks every other interaction with an empty 202.
|
|
73
|
+
* The engine dispatches a matched interaction through the manifest before the 202 goes out. Gates run
|
|
74
|
+
* first and the sender posts any refusal over the REST callback. `execute()` continues past the 202 under
|
|
75
|
+
* `ctx.waitUntil` when the caller passes one (the edge entry), and an engine-held in-flight set tracks
|
|
76
|
+
* it otherwise (node paths). It reads only the method, headers, and body. Mount it at any path.
|
|
77
|
+
*
|
|
78
|
+
* Reads `DISCORD_PUBLIC_KEY` and `DISCORD_BOT_TOKEN` from the environment through envapt and throws a
|
|
79
|
+
* `SeedcordError` when either is missing or malformed.
|
|
80
|
+
*/
|
|
81
|
+
declare function createSeedcord(config: Config, manifest: RouteManifest): (request: Request, ctx?: EngineContext) => Promise<Response>;
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/interfaces/Core.d.ts
|
|
84
|
+
/**
|
|
85
|
+
* Main interface for Seedcord core functionality on the HTTP transport.
|
|
86
|
+
*
|
|
87
|
+
* This interface can be augmented via declaration merging to add type-safe plugin definitions when
|
|
88
|
+
* using `this.core` in handlers.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* declare module '@seedcord/http' {
|
|
93
|
+
* interface Core {
|
|
94
|
+
* db: Mongo;
|
|
95
|
+
* }
|
|
96
|
+
* }
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
interface Core extends CoreBase, SeedcordInstance {
|
|
100
|
+
/** Workerd-compatible Discord REST client. */
|
|
101
|
+
readonly rest: REST;
|
|
102
|
+
readonly config: Config;
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/inputs/SlashOptions.d.ts
|
|
106
|
+
type ResolvedChannelOf<Type extends ChannelType> = APIInteractionDataResolvedChannelBase<Type> & (Type extends ThreadChannelType ? Pick<APIThreadChannel, 'parent_id' | 'thread_metadata'> : unknown);
|
|
107
|
+
type AnyResolvedChannel = { [Type in ChannelType]: ResolvedChannelOf<Type> }[ChannelType];
|
|
108
|
+
interface HttpLens {
|
|
109
|
+
user: APIUser;
|
|
110
|
+
member: APIInteractionDataResolvedGuildMember;
|
|
111
|
+
channel: AnyResolvedChannel;
|
|
112
|
+
role: APIRole;
|
|
113
|
+
mentionable: APIUser | APIInteractionDataResolvedGuildMember | APIRole;
|
|
114
|
+
attachment: APIAttachment;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The typed view over a chat-input command's options for one route on the HTTP transport. Each getter
|
|
118
|
+
* restricts its `name` to that route's options of the matching kind, drops the null on required options,
|
|
119
|
+
* narrows `choices` to their literal union, and only appears when the route has an option of that kind.
|
|
120
|
+
* The rich kinds return `discord-api-types` payloads resolved from the interaction's `resolved` data.
|
|
121
|
+
*
|
|
122
|
+
* A channel option declared with `addChannelTypes` narrows `getChannel` to the matching resolved-channel
|
|
123
|
+
* subtype.
|
|
124
|
+
*
|
|
125
|
+
* @typeParam Route - A route key from the generated {@link SlashOptionRegistry}.
|
|
126
|
+
*/
|
|
127
|
+
type SlashOptions<Route extends keyof SlashOptionRegistry> = SlashOptions$1<Route, HttpLens>;
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/reply/ReplySender.d.ts
|
|
130
|
+
/** The interaction payload fields the sender writes its wire from. */
|
|
131
|
+
interface InteractionRef {
|
|
132
|
+
readonly application_id: string;
|
|
133
|
+
readonly id: string;
|
|
134
|
+
readonly token: string;
|
|
135
|
+
}
|
|
136
|
+
/** The created message an http reply resolves to (the http `SentMessage` lens). */
|
|
137
|
+
type SentMessage = APIMessage;
|
|
138
|
+
/**
|
|
139
|
+
* Writes interaction responses over the Discord REST callback and webhook endpoints. Construction is
|
|
140
|
+
* internal to the repliable handler bases and the dispatcher.
|
|
141
|
+
*/
|
|
142
|
+
declare class ReplySender extends BaseReplySender<SentMessage> {
|
|
143
|
+
private readonly ref;
|
|
144
|
+
private readonly rest;
|
|
145
|
+
constructor(ref: InteractionRef, rest: REST, routeId: string);
|
|
146
|
+
protected writeReply(response: ReplyResponse | string, opts?: SendOpts): Promise<SentMessage | undefined>;
|
|
147
|
+
protected writeDefer(opts?: DeferOpts): Promise<void>;
|
|
148
|
+
protected writeDeferUpdate(): Promise<void>;
|
|
149
|
+
protected writeUpdate(response: ReplyResponse | string): Promise<SentMessage | undefined>;
|
|
150
|
+
protected writeFollowUp(response: ReplyResponse | string, opts?: SendOpts): Promise<SentMessage>;
|
|
151
|
+
protected writeEditOriginal(response: ReplyResponse | string): Promise<SentMessage>;
|
|
152
|
+
protected writeEditTarget(targetId: string, response: ReplyResponse | string): Promise<SentMessage>;
|
|
153
|
+
protected writeDeleteOriginal(): Promise<void>;
|
|
154
|
+
protected writeDeleteTarget(targetId: string): Promise<void>;
|
|
155
|
+
protected writeModal(data: APIModalInteractionResponseCallbackData): Promise<void>;
|
|
156
|
+
private callback;
|
|
157
|
+
private editMessage;
|
|
158
|
+
private rawFiles;
|
|
159
|
+
private createdMessage;
|
|
160
|
+
}
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region src/handlers/interactionTypes.d.ts
|
|
163
|
+
type ValidInteractionTypes = APIChatInputApplicationCommandInteraction | APIContextMenuInteraction | APIMessageComponentInteraction | APIModalSubmitInteraction | APIApplicationCommandAutocompleteInteraction;
|
|
164
|
+
type Repliables = Exclude<ValidInteractionTypes, APIApplicationCommandAutocompleteInteraction>;
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/handlers/RepliableHandler.d.ts
|
|
167
|
+
/**
|
|
168
|
+
* Shared base the repliable HTTP interaction handlers extend.
|
|
169
|
+
*
|
|
170
|
+
* Not a public entry point. Extend {@link SlashHandler}, {@link ButtonHandler}, {@link ModalHandler},
|
|
171
|
+
* {@link SelectMenuHandler}, or {@link ContextMenuHandler} instead. This class binds the core reply base
|
|
172
|
+
* to the http sender.
|
|
173
|
+
*
|
|
174
|
+
* @typeParam Event - The repliable interaction type this handler processes
|
|
175
|
+
*/
|
|
176
|
+
declare abstract class RepliableHandler<Event extends Repliables> extends RepliableHandler$1<Event, Core, SentMessage, ReplySender> {
|
|
177
|
+
protected buildSender(event: Event, core: Core, routeId: string): ReplySender;
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/handlers/interaction/InteractionHandler.d.ts
|
|
181
|
+
/**
|
|
182
|
+
* Shared base the repliable interaction handlers extend.
|
|
183
|
+
*
|
|
184
|
+
* Not a public entry point. Extend {@link SlashHandler}, {@link ContextMenuHandler}, {@link ButtonHandler},
|
|
185
|
+
* {@link SelectMenuHandler}, or {@link ModalHandler} instead. This class adds `showModal` on top of the
|
|
186
|
+
* reply members.
|
|
187
|
+
*
|
|
188
|
+
* @typeParam Event - The repliable interaction type this handler processes
|
|
189
|
+
*/
|
|
190
|
+
declare abstract class InteractionHandler<Event extends Repliables> extends RepliableHandler<Event> {
|
|
191
|
+
constructor(event: Event, core: Core, dispatch?: DispatchContext);
|
|
192
|
+
/**
|
|
193
|
+
* Open a modal. Must be the initial response to this interaction. The modal kind rejects this call at
|
|
194
|
+
* compile time (Discord forbids a modal in response to a modal).
|
|
195
|
+
*/
|
|
196
|
+
protected showModal(this: InteractionHandler<Exclude<Repliables, APIModalSubmitInteraction>>, modal: ModalLike): Promise<void>;
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/handlers/interaction/SlashHandler.d.ts
|
|
200
|
+
type SlashMatchArms<Route extends keyof SlashOptionRegistry, Ret> = { [Key in Route]: (options: SlashOptions<Key>) => Promisable<Ret> };
|
|
201
|
+
/**
|
|
202
|
+
* Base class for a chat-input (slash) command handler on the HTTP transport.
|
|
203
|
+
*
|
|
204
|
+
* Pass the route from the generated registry as the generic, then read `this.options` for a single
|
|
205
|
+
* command or `this.match` for several. Command authoring stays plain discord.js, `seedcord codegen`
|
|
206
|
+
* reads its `toJSON()` to type these options.
|
|
207
|
+
*
|
|
208
|
+
* @typeParam Route - One or more route keys from {@link SlashOptionRegistry}, e.g. `'ban'` or `'ban' | 'kick'`.
|
|
209
|
+
*/
|
|
210
|
+
declare abstract class SlashHandler<Route extends keyof SlashOptionRegistry> extends InteractionHandler<APIChatInputApplicationCommandInteraction> {
|
|
211
|
+
/** @internal */
|
|
212
|
+
readonly __slashRoute?: Route;
|
|
213
|
+
private resolver?;
|
|
214
|
+
/**
|
|
215
|
+
* The typed options for this command's route. Required options drop the null, choices narrow to their
|
|
216
|
+
* literal union, and only the getters for kinds this command actually uses appear. Rich kinds resolve
|
|
217
|
+
* from the interaction's `resolved` data.
|
|
218
|
+
*/
|
|
219
|
+
protected get options(): SlashOptions<Route>;
|
|
220
|
+
/**
|
|
221
|
+
* Run the arm for whichever command fired. Use this only when the handler is registered for several
|
|
222
|
+
* routes.
|
|
223
|
+
*
|
|
224
|
+
* Provide one arm per registered route, keyed by its route string, and each arm receives that route's
|
|
225
|
+
* own narrowed options. The arms must cover every route in the generic, a missing route or an unknown
|
|
226
|
+
* key is a compile error.
|
|
227
|
+
*
|
|
228
|
+
* @param arms - One handler per registered route, keyed by route string.
|
|
229
|
+
* @returns The result of the arm that ran.
|
|
230
|
+
*/
|
|
231
|
+
protected match<Ret>(arms: SlashMatchArms<Route, Ret>): Promise<Ret>;
|
|
232
|
+
}
|
|
233
|
+
//#endregion
|
|
234
|
+
//#region src/handlers/interaction/ContextMenuHandler.d.ts
|
|
235
|
+
type ContextMenuKind = ApplicationCommandType.User | ApplicationCommandType.Message;
|
|
236
|
+
type InteractionFor<Kind extends ContextMenuKind> = Kind extends ApplicationCommandType.User ? APIUserApplicationCommandInteraction : APIMessageApplicationCommandInteraction;
|
|
237
|
+
type TargetFor<Kind extends ContextMenuKind> = Kind extends ApplicationCommandType.User ? APIUser : APIMessage;
|
|
238
|
+
type TargetMemberFor<Kind extends ContextMenuKind> = Kind extends ApplicationCommandType.User ? APIInteractionDataResolvedGuildMember | null : never;
|
|
239
|
+
/**
|
|
240
|
+
* Base class for a context-menu command handler on the HTTP transport (right-click a user or a message).
|
|
241
|
+
*
|
|
242
|
+
* Pass the kind from {@link ApplicationCommandType} as the generic. Read the right-clicked entity from
|
|
243
|
+
* `this.target`, an `APIUser` for a user menu and an `APIMessage` for a message menu. Context menus carry
|
|
244
|
+
* no options, so a handler registered for several names reads `this.target` uniformly with no per-name
|
|
245
|
+
* branch.
|
|
246
|
+
*
|
|
247
|
+
* @typeParam Kind - `ApplicationCommandType.User` or `ApplicationCommandType.Message`.
|
|
248
|
+
*/
|
|
249
|
+
declare abstract class ContextMenuHandler<Kind extends ContextMenuKind> extends InteractionHandler<InteractionFor<Kind>> {
|
|
250
|
+
/** @internal */
|
|
251
|
+
readonly __ctxKind?: Kind;
|
|
252
|
+
protected get target(): TargetFor<Kind>;
|
|
253
|
+
/**
|
|
254
|
+
* The right-clicked user's guild member, resolved only on user menus fired in a guild. Reading it on a
|
|
255
|
+
* message menu is a compile error.
|
|
256
|
+
*/
|
|
257
|
+
protected get targetMember(): TargetMemberFor<Kind>;
|
|
258
|
+
}
|
|
259
|
+
//#endregion
|
|
260
|
+
//#region src/handlers/interaction/AutocompleteHandler.d.ts
|
|
261
|
+
type FocusedArms<Route extends keyof SlashOptionRegistry, Ret> = { [Name in AutocompletableNames<Route>]: (value: string, respond: (choices: readonly APIApplicationCommandOptionChoice<ChoiceValueOf<EntryFor<Route, Name>>>[]) => Promise<void>) => Promisable<Ret> };
|
|
262
|
+
/**
|
|
263
|
+
* Base class for an autocomplete handler on the HTTP transport.
|
|
264
|
+
*
|
|
265
|
+
* Pass the command route(s) from the generated registry as the generic. Branch on the focused field with
|
|
266
|
+
* `this.match`, read already-entered sibling options with `this.options`, and find which command fired
|
|
267
|
+
* with `this.route`.
|
|
268
|
+
*
|
|
269
|
+
* @typeParam Route - One or more route keys from {@link SlashOptionRegistry}, e.g. `'search'`.
|
|
270
|
+
*/
|
|
271
|
+
declare abstract class AutocompleteHandler<Route extends keyof SlashOptionRegistry> extends BaseHandler<APIApplicationCommandAutocompleteInteraction, Core> {
|
|
272
|
+
constructor(event: APIApplicationCommandAutocompleteInteraction, core: Core, dispatch?: DispatchContext);
|
|
273
|
+
/** @internal */
|
|
274
|
+
readonly __autocompleteRoute?: Route;
|
|
275
|
+
private resolver?;
|
|
276
|
+
private get reader();
|
|
277
|
+
/**
|
|
278
|
+
* The focused option for this interaction. `value` is always the raw partial string the user is typing,
|
|
279
|
+
* even for an integer or number option, so coerce it yourself when you need a number.
|
|
280
|
+
*/
|
|
281
|
+
protected get focused(): FocusedField<Route>;
|
|
282
|
+
/**
|
|
283
|
+
* Run the arm for the focused field. An autocomplete always dispatches by which field is focused, so
|
|
284
|
+
* match is how you read it.
|
|
285
|
+
*
|
|
286
|
+
* Provide one arm per autocompletable field across the registered commands, keyed by field name. Each
|
|
287
|
+
* arm receives the focused partial value and a `respond` typed to that field's choice value. The
|
|
288
|
+
* arms must cover every autocompletable field, a missing field or an unknown key is a compile error. A
|
|
289
|
+
* focused field with no arm, only reachable from a stale-deployed command, throws at runtime.
|
|
290
|
+
*
|
|
291
|
+
* @param arms - One handler per autocompletable field, keyed by field name.
|
|
292
|
+
* @returns The result of the arm that ran.
|
|
293
|
+
*/
|
|
294
|
+
protected match<Ret>(arms: FocusedArms<Route, Ret>): Promise<Ret>;
|
|
295
|
+
/** Send autocomplete suggestions, callback type 8. Prefer {@link match}, which restricts each field's choices to its declared type. */
|
|
296
|
+
protected respond(choices: readonly APIApplicationCommandOptionChoice[]): Promise<void>;
|
|
297
|
+
/** The firing command route, for a field whose completion differs per registered command. */
|
|
298
|
+
protected get route(): Route;
|
|
299
|
+
/**
|
|
300
|
+
* The already-entered options on this command, restricted to the kinds Discord resolves during autocomplete
|
|
301
|
+
* (string, integer, number, boolean) with every read nullable, since a sibling is partial while the user
|
|
302
|
+
* types the focused field. The focused field is on `this.focused`.
|
|
303
|
+
*/
|
|
304
|
+
protected get options(): AutocompleteOptions<Route>;
|
|
305
|
+
}
|
|
306
|
+
//#endregion
|
|
307
|
+
//#region src/handlers/interaction/components/ComponentHandler.d.ts
|
|
308
|
+
/**
|
|
309
|
+
* Shared base the customId-routed component handlers extend on the HTTP transport.
|
|
310
|
+
*
|
|
311
|
+
* Not a public entry point. Extend {@link ButtonHandler}, {@link SelectMenuHandler}, or {@link ModalHandler}
|
|
312
|
+
* instead. This class defines the customId decode, the route matching, and the source-message verbs
|
|
313
|
+
* `update` and `deferUpdate`.
|
|
314
|
+
*
|
|
315
|
+
* @typeParam Event - The component or modal interaction type this handler processes
|
|
316
|
+
* @typeParam Defs - The customId route definitions registered on the concrete handler
|
|
317
|
+
*/
|
|
318
|
+
declare abstract class ComponentHandler<Event extends APIMessageComponentInteraction | APIModalSubmitInteraction, Defs extends readonly AnyCustomId[]> extends InteractionHandler<Event> implements HasComponentDefs<Defs> {
|
|
319
|
+
/** @internal */
|
|
320
|
+
readonly __componentDefs?: Defs;
|
|
321
|
+
/** Rewrite the source message this component interaction came from. */
|
|
322
|
+
protected update(response: ReplyResponse | string): Promise<SentMessage>;
|
|
323
|
+
/** Acknowledge the component without changing the source message. */
|
|
324
|
+
protected deferUpdate(): Promise<void>;
|
|
325
|
+
private decoded?;
|
|
326
|
+
private get route();
|
|
327
|
+
/**
|
|
328
|
+
* The decoded params of the single route this handler is registered for.
|
|
329
|
+
*
|
|
330
|
+
* Reading this decodes `this.event.data.custom_id` once (cached after the first read) and throws
|
|
331
|
+
* `StaleCustomId` or `InvalidCustomId` when the wire no longer matches the current shape, which the
|
|
332
|
+
* dispatcher boundary turns into a reply. On a handler registered for several routes this is
|
|
333
|
+
* `never`, so use {@link match} instead.
|
|
334
|
+
*/
|
|
335
|
+
protected get params(): SingleParams<Defs>;
|
|
336
|
+
/**
|
|
337
|
+
* Run the arm for whichever route the component was minted from. Use this only when the handler is
|
|
338
|
+
* registered for several routes. A single-route handler reads `this.params` directly. On a multi-route
|
|
339
|
+
* handler `this.params` is `never`, so match is the only way to read the decoded params.
|
|
340
|
+
*
|
|
341
|
+
* Provide one arm per registered route, keyed by its prefix, and each arm receives that route's own
|
|
342
|
+
* decoded params. The arms cover every registered route prefix, checked at compile time, and a prefix
|
|
343
|
+
* unmatched at runtime throws `CustomIdMatchArmMissing`. Decoding runs before any arm, so a stale or
|
|
344
|
+
* corrupt wire throws before an arm body executes.
|
|
345
|
+
*
|
|
346
|
+
* @param arms - One callback per registered route, keyed by prefix.
|
|
347
|
+
* @returns The result of the arm that ran.
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* ```ts
|
|
351
|
+
* \@ButtonRoute(Approve, Reject)
|
|
352
|
+
* class ReviewButtons extends ButtonHandler<[typeof Approve, typeof Reject]> {
|
|
353
|
+
* async execute() {
|
|
354
|
+
* await this.match({
|
|
355
|
+
* approve: ({ userId }) => this.reply(`approved <@${userId}>`),
|
|
356
|
+
* reject: ({ userId }) => this.reply(`rejected <@${userId}>`)
|
|
357
|
+
* });
|
|
358
|
+
* }
|
|
359
|
+
* }
|
|
360
|
+
* ```
|
|
361
|
+
*/
|
|
362
|
+
protected match<Ret>(arms: MatchArms<Defs, Ret>): Promise<Ret>;
|
|
363
|
+
}
|
|
364
|
+
//#endregion
|
|
365
|
+
//#region src/handlers/interaction/components/ButtonHandler.d.ts
|
|
366
|
+
/**
|
|
367
|
+
* Base class for a button interaction handler on the HTTP transport.
|
|
368
|
+
*
|
|
369
|
+
* Register the customId definitions this handler decodes with `@ButtonRoute`, list the same ones in the
|
|
370
|
+
* generic, then read `this.params` for a single route or `this.match` for several. Reply through the
|
|
371
|
+
* handler members, or rewrite the source message with `this.update`. Passing different definitions to the
|
|
372
|
+
* decorator and the generic is a compile error.
|
|
373
|
+
*
|
|
374
|
+
* @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof ApproveId]`.
|
|
375
|
+
*/
|
|
376
|
+
declare abstract class ButtonHandler<Defs extends readonly AnyCustomId[]> extends ComponentHandler<APIMessageComponentButtonInteraction, Defs> {
|
|
377
|
+
/** @internal */
|
|
378
|
+
readonly __component?: 'button';
|
|
379
|
+
}
|
|
380
|
+
//#endregion
|
|
381
|
+
//#region src/handlers/interaction/components/SelectMenuHandler.d.ts
|
|
382
|
+
type SelectMenuInteractionFor<Kind extends SelectMenuKind> = APIMessageComponentSelectMenuInteraction & {
|
|
383
|
+
data: Kind extends SelectMenuKind.String ? APIMessageStringSelectInteractionData : Kind extends SelectMenuKind.User ? APIMessageUserSelectInteractionData : Kind extends SelectMenuKind.Role ? APIMessageRoleSelectInteractionData : Kind extends SelectMenuKind.Channel ? APIMessageChannelSelectInteractionData : Kind extends SelectMenuKind.Mentionable ? APIMessageMentionableSelectInteractionData : never;
|
|
384
|
+
};
|
|
385
|
+
/**
|
|
386
|
+
* Base class for a select menu handler on the HTTP transport.
|
|
387
|
+
*
|
|
388
|
+
* Pass the select kind first and the customId definitions second, the same order as `@SelectMenuRoute`,
|
|
389
|
+
* so `this.event.data` and its `values`/`resolved` are narrowed to that kind. Read `this.params` for a
|
|
390
|
+
* single route or `this.match` for several, and reply through the handler members or rewrite the source
|
|
391
|
+
* message with `this.update`.
|
|
392
|
+
*
|
|
393
|
+
* @typeParam Kind - The select kind from {@link SelectMenuKind}, e.g. `SelectMenuKind.User`.
|
|
394
|
+
* @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof AssignId]`.
|
|
395
|
+
*/
|
|
396
|
+
declare abstract class SelectMenuHandler<Kind extends SelectMenuKind, Defs extends readonly AnyCustomId[]> extends ComponentHandler<SelectMenuInteractionFor<Kind>, Defs> {
|
|
397
|
+
/** @internal */
|
|
398
|
+
readonly __component?: Kind;
|
|
399
|
+
}
|
|
400
|
+
//#endregion
|
|
401
|
+
//#region src/handlers/interaction/components/ModalHandler.d.ts
|
|
402
|
+
/**
|
|
403
|
+
* Base class for a modal submit handler on the HTTP transport.
|
|
404
|
+
*
|
|
405
|
+
* Register the customId definitions this handler decodes with `@ModalRoute` and list the same ones in the
|
|
406
|
+
* generic. Read the submitted inputs off `this.event.data.components`. Reply through the handler members.
|
|
407
|
+
* Discord forbids opening a modal in response to a modal, so `showModal` fails compilation on this kind.
|
|
408
|
+
*
|
|
409
|
+
* @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof ConfigId]`.
|
|
410
|
+
*/
|
|
411
|
+
declare abstract class ModalHandler<Defs extends readonly AnyCustomId[]> extends ComponentHandler<APIModalSubmitInteraction, Defs> {
|
|
412
|
+
/** @internal */
|
|
413
|
+
readonly __component?: 'modal';
|
|
414
|
+
/** Rewrite the message this modal was opened from (only when a component opened it). */
|
|
415
|
+
protected update(response: ReplyResponse | string): Promise<SentMessage>;
|
|
416
|
+
/** Silently acknowledge, leaving the source message untouched. */
|
|
417
|
+
protected deferUpdate(): Promise<void>;
|
|
418
|
+
private ensureSourceMessage;
|
|
419
|
+
}
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region src/gates/Gate.d.ts
|
|
422
|
+
/**
|
|
423
|
+
* The interaction gate context on the HTTP transport. It extends the scalar {@link GateContextBase} with the
|
|
424
|
+
* raw interaction payload and the user and member the payload carries. `Repliable` narrows which handlers a
|
|
425
|
+
* gate attaches to, inferred from the `ctx` annotation, so a gate reading button fields is rejected on a
|
|
426
|
+
* slash handler.
|
|
427
|
+
*
|
|
428
|
+
* The http transport delivers no role-derived permission sets, so `memberGuildPermissions` and
|
|
429
|
+
* `appGuildPermissions` are absent. A gate requiring `GuildPermissionsContext` (a permission gate with
|
|
430
|
+
* `in: 'guild'`) is a compile error on an http handler.
|
|
431
|
+
*
|
|
432
|
+
* @typeParam Repliable - The interaction type the gate reads, narrowing which handlers accept it.
|
|
433
|
+
*
|
|
434
|
+
* @example
|
|
435
|
+
* ```ts
|
|
436
|
+
* import type { APIMessageComponentButtonInteraction } from 'discord-api-types/v10';
|
|
437
|
+
*
|
|
438
|
+
* // a button-only gate: the Repliable generic narrows which handlers accept it
|
|
439
|
+
* const ButtonGate = defineGate('btn', (ctx: InteractionGateContext<APIMessageComponentButtonInteraction>) => {
|
|
440
|
+
* void ctx.interaction; // APIMessageComponentButtonInteraction
|
|
441
|
+
* });
|
|
442
|
+
* ```
|
|
443
|
+
*/
|
|
444
|
+
interface InteractionGateContext<Repliable extends Repliables = Repliables> extends GateContextBase {
|
|
445
|
+
/** Narrow on this before reading `interaction`. */
|
|
446
|
+
readonly kind: 'interaction';
|
|
447
|
+
readonly core: Core;
|
|
448
|
+
/** The raw interaction payload, which is the reply target. */
|
|
449
|
+
readonly interaction: Repliable;
|
|
450
|
+
/** Every repliable interaction contains a user, so this is never null. */
|
|
451
|
+
readonly user: APIUser;
|
|
452
|
+
/** The invoking member, or null in a DM. */
|
|
453
|
+
readonly member: APIInteractionGuildMember | null;
|
|
454
|
+
}
|
|
455
|
+
//#endregion
|
|
456
|
+
//#region src/gates/matching.d.ts
|
|
457
|
+
type AnyHandlerCtor = abstract new (...args: never[]) => {
|
|
458
|
+
execute(): Promise<void>;
|
|
459
|
+
};
|
|
460
|
+
declare const UngateableBrand: unique symbol;
|
|
461
|
+
interface Ungateable {
|
|
462
|
+
readonly [UngateableBrand]: true;
|
|
463
|
+
}
|
|
464
|
+
type ProvidedContext<TCtor extends AnyHandlerCtor> = InstanceType<TCtor> extends RepliableHandler<infer Event> ? InteractionGateContext<Event> : Ungateable;
|
|
465
|
+
type SelectData<Repliable> = Repliable extends APIMessageComponentSelectMenuInteraction ? Repliable['data'] : never;
|
|
466
|
+
type KindName<Ctx> = Ctx extends Ungateable ? 'autocomplete' : Ctx extends GuildPermissionsContext ? 'gateway (guild permissions)' : Ctx extends InteractionGateContext<infer Repliable> ? Repliable extends APIChatInputApplicationCommandInteraction ? 'Slash' : Repliable extends APIMessageApplicationCommandInteraction ? 'MessageMenu' : Repliable extends APIUserApplicationCommandInteraction ? 'UserMenu' : SelectData<Repliable> extends APIMessageStringSelectInteractionData ? 'StringSelect' : SelectData<Repliable> extends APIMessageUserSelectInteractionData ? 'UserSelect' : SelectData<Repliable> extends APIMessageRoleSelectInteractionData ? 'RoleSelect' : SelectData<Repliable> extends APIMessageChannelSelectInteractionData ? 'ChannelSelect' : SelectData<Repliable> extends APIMessageMentionableSelectInteractionData ? 'MentionableSelect' : Repliable extends APIMessageComponentButtonInteraction ? 'Button' : Repliable extends APIModalSubmitInteraction ? 'Modal' : 'an interaction' : 'an agnostic';
|
|
467
|
+
type FitAll<TCtor extends AnyHandlerCtor, Gates extends readonly Gate<GateContextBase>[]> = { [Index in keyof Gates]: GateFitsWith<ProvidedContext<TCtor>, KindName<ProvidedContext<TCtor>>, Gates[Index], KindName<RequiredOf<Gates[Index]>>> };
|
|
468
|
+
//#endregion
|
|
469
|
+
//#region src/gates/Gated.d.ts
|
|
470
|
+
/**
|
|
471
|
+
* Attaches gates to an HTTP interaction handler. The gates run before `execute`, and a gate refusing stops
|
|
472
|
+
* the handler with the reply or drop the gate threw. Multiple gates are ANDed, so each must pass. A gate
|
|
473
|
+
* that requires a context the handler does not provide (a button gate on a slash handler, a gate on an
|
|
474
|
+
* autocomplete handler, or a `RequirePermissions([...], { in: 'guild' })` gate the http transport cannot
|
|
475
|
+
* satisfy) is a compile error at this line, and the error names the gate and the handler kind. Place it
|
|
476
|
+
* above the route decorator.
|
|
477
|
+
*
|
|
478
|
+
* Combine gates with {@link and} or {@link or} to build a single gate from multiple arms.
|
|
479
|
+
*
|
|
480
|
+
* @example
|
|
481
|
+
* ```ts
|
|
482
|
+
* import { PermissionFlagsBits } from 'discord-api-types/v10';
|
|
483
|
+
*
|
|
484
|
+
* \@Gated(GuildOnly(), RequirePermissions([PermissionFlagsBits.ManageMessages]))
|
|
485
|
+
* \@SlashRoute('purge')
|
|
486
|
+
* class Purge extends SlashHandler<'purge'> {
|
|
487
|
+
* async execute() {
|
|
488
|
+
* // both gates passed, so this runs in a guild with a caller who can moderate this channel
|
|
489
|
+
* }
|
|
490
|
+
* }
|
|
491
|
+
* ```
|
|
492
|
+
*
|
|
493
|
+
* @typeParam Gates - The non-empty tuple of gates to run, in order, against the handler's context.
|
|
494
|
+
* @typeParam TCtor - The decorated handler's constructor, checked against every gate's required context.
|
|
495
|
+
* @param gates - One or more gates to attach, each ANDed so the handler runs only when all pass.
|
|
496
|
+
* @param ctor - The handler class being decorated, rejected at compile time when a gate requires a context it lacks.
|
|
497
|
+
*
|
|
498
|
+
* @see {@link defineGate}
|
|
499
|
+
* @see {@link defineEffectGate}
|
|
500
|
+
* @decorator
|
|
501
|
+
*/
|
|
502
|
+
declare function Gated<const Gates extends NonEmptyTuple<Gate<GateContextBase>>>(...gates: Gates): <TCtor extends AnyHandlerCtor>(ctor: FitAll<TCtor, Gates> extends Gates ? TCtor : FitAll<TCtor, Gates>) => void;
|
|
503
|
+
//#endregion
|
|
1
504
|
//#region src/index.d.ts
|
|
2
505
|
/** Package version */
|
|
3
506
|
declare const version: string;
|
|
4
507
|
//#endregion
|
|
5
|
-
export { version };
|
|
508
|
+
export { AutocompleteHandler, ButtonHandler, ComponentHandler, ContextMenuHandler, type Core, type EngineContext, Gated, type InteractionGateContext, InteractionHandler, ModalHandler, RepliableHandler, type RouteManifest, SelectMenuHandler, SlashHandler, type SlashOptions, createSeedcord, version };
|
|
6
509
|
//# sourceMappingURL=index.d.mts.map
|