@minesa-org/mini-interaction 0.1.12 → 0.2.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,25 +1,23 @@
1
1
  import type { APIActionRowComponent } from "discord-api-types/v10";
2
2
  import type { JSONEncodable } from "./shared.js";
3
- import type { MiniComponentActionRow } from "../types/ComponentTypes.js";
3
+ import type { ActionRowComponent } from "../types/ComponentTypes.js";
4
4
  /** Values accepted when composing component action rows. */
5
- export type ActionRowComponentLike<T extends MiniComponentActionRow> = JSONEncodable<T> | T;
6
- /** Builder for Discord action row components. */
7
- export declare class ActionRowBuilder<T extends MiniComponentActionRow = MiniComponentActionRow> implements JSONEncodable<APIActionRowComponent<T>> {
5
+ export type ActionRowComponentLike<T extends ActionRowComponent> = JSONEncodable<T> | T;
6
+ /** Builder for creating Action Row components. */
7
+ export declare class ActionRowBuilder<T extends ActionRowComponent> implements JSONEncodable<APIActionRowComponent<T>> {
8
8
  private components;
9
+ constructor(data?: Partial<APIActionRowComponent<T>>);
9
10
  /**
10
- * Creates a new action row builder with optional seed components.
11
+ * Adds components to this action row.
12
+ *
13
+ * @param components - The components to add (can be builders or raw objects).
11
14
  */
12
- constructor(components?: Iterable<ActionRowComponentLike<T>>);
15
+ addComponents(...components: (T | JSONEncodable<T>)[]): this;
13
16
  /**
14
- * Appends additional components to the action row.
15
- */
16
- addComponents(...components: ActionRowComponentLike<T>[]): this;
17
- /**
18
- * Replaces the current action row contents.
19
- */
20
- setComponents(components: Iterable<ActionRowComponentLike<T>>): this;
21
- /**
22
- * Serialises the builder into an API compatible action row payload.
17
+ * Sets the components for this action row, replacing any existing ones.
18
+ *
19
+ * @param components - The new components to set.
23
20
  */
21
+ setComponents(...components: (T | JSONEncodable<T>)[]): this;
24
22
  toJSON(): APIActionRowComponent<T>;
25
23
  }
@@ -1,35 +1,33 @@
1
1
  import { ComponentType } from "discord-api-types/v10";
2
2
  import { resolveJSONEncodable } from "./shared.js";
3
- /** Builder for Discord action row components. */
3
+ /** Builder for creating Action Row components. */
4
4
  export class ActionRowBuilder {
5
- components;
6
- /**
7
- * Creates a new action row builder with optional seed components.
8
- */
9
- constructor(components = []) {
10
- this.components = Array.from(components);
5
+ components = [];
6
+ constructor(data = {}) {
7
+ this.components = [...(data.components ?? [])];
11
8
  }
12
9
  /**
13
- * Appends additional components to the action row.
10
+ * Adds components to this action row.
11
+ *
12
+ * @param components - The components to add (can be builders or raw objects).
14
13
  */
15
14
  addComponents(...components) {
16
- this.components.push(...components);
15
+ this.components.push(...components.map((c) => resolveJSONEncodable(c)));
17
16
  return this;
18
17
  }
19
18
  /**
20
- * Replaces the current action row contents.
19
+ * Sets the components for this action row, replacing any existing ones.
20
+ *
21
+ * @param components - The new components to set.
21
22
  */
22
- setComponents(components) {
23
- this.components = Array.from(components);
23
+ setComponents(...components) {
24
+ this.components = components.map((c) => resolveJSONEncodable(c));
24
25
  return this;
25
26
  }
26
- /**
27
- * Serialises the builder into an API compatible action row payload.
28
- */
29
27
  toJSON() {
30
28
  return {
31
29
  type: ComponentType.ActionRow,
32
- components: this.components.map((component) => resolveJSONEncodable(component)),
30
+ components: [...this.components],
33
31
  };
34
32
  }
35
33
  }
@@ -1,12 +1,12 @@
1
1
  import type { IncomingMessage, ServerResponse } from "node:http";
2
2
  import { APIInteractionResponse, RESTPostAPIChatInputApplicationCommandsJSONBody, RESTPostAPIContextMenuApplicationCommandsJSONBody, RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody } from "discord-api-types/v10";
3
- import type { MiniInteractionCommand } from "../types/Commands.js";
3
+ import type { InteractionCommand } from "../types/Commands.js";
4
4
  import { RoleConnectionMetadataTypes } from "../types/RoleConnectionMetadataTypes.js";
5
5
  import { type MessageComponentInteraction, type ButtonInteraction, type StringSelectInteraction, type RoleSelectInteraction, type UserSelectInteraction, type ChannelSelectInteraction, type MentionableSelectInteraction } from "../utils/MessageComponentInteraction.js";
6
6
  import { type ModalSubmitInteraction } from "../utils/ModalSubmitInteraction.js";
7
7
  import { type OAuthConfig, type OAuthTokens, type DiscordUser } from "../oauth/DiscordOAuth.js";
8
8
  /** Configuration parameters for the MiniInteraction client. */
9
- export type MiniInteractionOptions = {
9
+ export type InteractionClientOptions = {
10
10
  applicationId: string;
11
11
  publicKey: string;
12
12
  commandsDirectory?: string | false;
@@ -14,6 +14,7 @@ export type MiniInteractionOptions = {
14
14
  utilsDirectory?: string | false;
15
15
  fetchImplementation?: typeof fetch;
16
16
  verifyKeyImplementation?: VerifyKeyFunction;
17
+ timeoutConfig?: InteractionTimeoutConfig;
17
18
  };
18
19
  /** Payload structure for role connection metadata registration. */
19
20
  export type RoleConnectionMetadataField = {
@@ -25,36 +26,56 @@ export type RoleConnectionMetadataField = {
25
26
  /**
26
27
  * HTTP request information needed to validate and handle Discord interaction payloads.
27
28
  */
28
- export type MiniInteractionRequest = {
29
+ export type InteractionRequest = {
29
30
  body: string | Uint8Array;
30
31
  signature?: string;
31
32
  timestamp?: string;
32
33
  };
33
34
  /** Result payload returned by request handlers when processing an interaction. */
34
- export type MiniInteractionHandlerResult = {
35
+ export type InteractionHandlerResult = {
35
36
  status: number;
36
37
  body: APIInteractionResponse | {
37
38
  error: string;
38
39
  };
39
40
  };
41
+ /** Configuration for interaction timeout handling. */
42
+ export type InteractionTimeoutConfig = {
43
+ /** Maximum time in milliseconds to wait for initial response (default: 2800ms) */
44
+ initialResponseTimeout?: number;
45
+ /** Whether to enable timeout warnings (default: true) */
46
+ enableTimeoutWarnings?: boolean;
47
+ /** Whether to force deferReply for slow operations (default: true) */
48
+ autoDeferSlowOperations?: boolean;
49
+ /** Whether to enable debug logging for interaction responses (default: false) */
50
+ enableResponseDebugLogging?: boolean;
51
+ };
52
+ /** Enhanced timeout configuration with response acknowledgment settings. */
53
+ export type InteractionTimeoutConfigV2 = InteractionTimeoutConfig & {
54
+ /** Time to wait for Discord acknowledgment after sending response (default: 500ms) */
55
+ responseAcknowledgmentTimeout?: number;
56
+ /** Maximum retries for response acknowledgment (default: 2) */
57
+ responseAcknowledgmentRetries?: number;
58
+ /** Delay between acknowledgment retries (default: 100ms) */
59
+ responseAcknowledgmentRetryDelay?: number;
60
+ };
40
61
  /** Handler signature invoked for Discord button interactions. */
41
- export type MiniInteractionButtonHandler = (interaction: ButtonInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
62
+ export type ButtonComponentHandler = (interaction: ButtonInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
42
63
  /** Handler signature invoked for Discord string select menu interactions. */
43
- export type MiniInteractionStringSelectHandler = (interaction: StringSelectInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
64
+ export type StringSelectComponentHandler = (interaction: StringSelectInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
44
65
  /** Handler signature invoked for Discord role select menu interactions. */
45
- export type MiniInteractionRoleSelectHandler = (interaction: RoleSelectInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
66
+ export type RoleSelectComponentHandler = (interaction: RoleSelectInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
46
67
  /** Handler signature invoked for Discord user select menu interactions. */
47
- export type MiniInteractionUserSelectHandler = (interaction: UserSelectInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
68
+ export type UserSelectComponentHandler = (interaction: UserSelectInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
48
69
  /** Handler signature invoked for Discord channel select menu interactions. */
49
- export type MiniInteractionChannelSelectHandler = (interaction: ChannelSelectInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
70
+ export type ChannelSelectComponentHandler = (interaction: ChannelSelectInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
50
71
  /** Handler signature invoked for Discord mentionable select menu interactions. */
51
- export type MiniInteractionMentionableSelectHandler = (interaction: MentionableSelectInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
72
+ export type MentionableSelectComponentHandler = (interaction: MentionableSelectInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
52
73
  /** Handler signature invoked for Discord message component interactions (generic). */
53
- export type MiniInteractionComponentHandler = (interaction: MessageComponentInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
74
+ export type ComponentHandler = (interaction: MessageComponentInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
54
75
  /** Handler signature invoked for Discord modal submit interactions. */
55
- export type MiniInteractionModalHandler = (interaction: ModalSubmitInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
76
+ export type ModalHandler = (interaction: ModalSubmitInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
56
77
  /** Unified handler signature that accepts any component or modal interaction. */
57
- export type MiniInteractionHandler = MiniInteractionButtonHandler | MiniInteractionStringSelectHandler | MiniInteractionRoleSelectHandler | MiniInteractionUserSelectHandler | MiniInteractionChannelSelectHandler | MiniInteractionMentionableSelectHandler | MiniInteractionComponentHandler | MiniInteractionModalHandler;
78
+ export type InteractionHandler = ButtonComponentHandler | StringSelectComponentHandler | RoleSelectComponentHandler | UserSelectComponentHandler | ChannelSelectComponentHandler | MentionableSelectComponentHandler | ComponentHandler | ModalHandler;
58
79
  type CommandDataPayload = RESTPostAPIChatInputApplicationCommandsJSONBody | RESTPostAPIContextMenuApplicationCommandsJSONBody | RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody;
59
80
  /**
60
81
  * Structure describing a component or modal handler mapped to a custom id.
@@ -63,19 +84,19 @@ type CommandDataPayload = RESTPostAPIChatInputApplicationCommandsJSONBody | REST
63
84
  * - Other files are treated as component handlers
64
85
  * You can use this type for both - the system will figure out which one it is.
65
86
  */
66
- export type MiniInteractionComponent = {
87
+ export type ComponentCommand = {
67
88
  customId: string;
68
- handler: MiniInteractionHandler;
89
+ handler: InteractionHandler;
69
90
  };
70
91
  /** Structure describing a modal handler mapped to a custom id. */
71
- export type MiniInteractionModal = {
92
+ export type ModalCommand = {
72
93
  customId: string;
73
- handler: MiniInteractionModalHandler;
94
+ handler: ModalHandler;
74
95
  };
75
96
  /** Node.js HTTP handler compatible with frameworks like Express or Next.js API routes. */
76
- export type MiniInteractionNodeHandler = (request: IncomingMessage, response: ServerResponse) => void;
97
+ export type InteractionNodeHandler = (request: IncomingMessage, response: ServerResponse) => void;
77
98
  /** Web Fetch API compatible request handler for platforms such as Cloudflare Workers. */
78
- export type MiniInteractionFetchHandler = (request: Request) => Promise<Response>;
99
+ export type InteractionFetchHandler = (request: Request) => Promise<Response>;
79
100
  /** Context passed to OAuth success handlers and templates. */
80
101
  export type DiscordOAuthAuthorizeContext = {
81
102
  tokens: OAuthTokens;
@@ -147,10 +168,12 @@ export declare class MiniInteraction {
147
168
  private readonly commandsDirectory;
148
169
  private readonly componentsDirectory;
149
170
  readonly utilsDirectory: string | null;
171
+ private readonly timeoutConfig;
150
172
  private readonly commands;
151
173
  private readonly componentHandlers;
152
174
  private readonly modalHandlers;
153
175
  private readonly htmlTemplateCache;
176
+ private readonly interactionStates;
154
177
  private commandsLoaded;
155
178
  private loadCommandsPromise;
156
179
  private componentsLoaded;
@@ -160,7 +183,52 @@ export declare class MiniInteraction {
160
183
  /**
161
184
  * Creates a new MiniInteraction client with optional command auto-loading and custom runtime hooks.
162
185
  */
163
- constructor({ applicationId, publicKey, commandsDirectory, componentsDirectory, utilsDirectory, fetchImplementation, verifyKeyImplementation, }: MiniInteractionOptions);
186
+ constructor({ applicationId, publicKey, commandsDirectory, componentsDirectory, utilsDirectory, fetchImplementation, verifyKeyImplementation, timeoutConfig, }: InteractionClientOptions);
187
+ /**
188
+ * Tracks the state of an interaction to prevent race conditions and double responses.
189
+ */
190
+ private trackInteractionState;
191
+ /**
192
+ * Checks if an interaction can still respond (not expired and not already responded).
193
+ */
194
+ private canRespond;
195
+ /**
196
+ * Logs response timing and acknowledgment for debugging.
197
+ */
198
+ private logResponseTiming;
199
+ /**
200
+ * Simulates waiting for Discord acknowledgment to help debug timing issues.
201
+ * Note: This is a best-effort simulation since actual acknowledgment happens at the HTTP level.
202
+ */
203
+ private simulateAcknowledgmentWait;
204
+ /**
205
+ * Enables or disables debug logging for interaction responses and timing.
206
+ * Useful for troubleshooting "didn't respond in time" errors.
207
+ *
208
+ * @param enabled - Whether to enable debug logging
209
+ */
210
+ setResponseDebugLogging(enabled: boolean): void;
211
+ /**
212
+ * Gets the current state of an interaction.
213
+ */
214
+ private getInteractionState;
215
+ /**
216
+ * Gets the current state of an interaction for debugging purposes.
217
+ *
218
+ * @param interactionId - The interaction ID to check
219
+ * @returns The current interaction state or null if not found
220
+ */
221
+ getInteractionStateInfo(interactionId: string): {
222
+ state: "pending" | "deferred" | "responded" | "expired";
223
+ timestamp: number;
224
+ token: string;
225
+ responseCount: number;
226
+ } | null;
227
+ /**
228
+ * Clears expired interaction states to prevent memory leaks.
229
+ * Call this periodically to clean up old interaction data.
230
+ */
231
+ cleanupExpiredInteractions(): number;
164
232
  private normalizeCommandData;
165
233
  private registerCommand;
166
234
  /**
@@ -168,37 +236,37 @@ export declare class MiniInteraction {
168
236
  *
169
237
  * @param command - The command definition to register.
170
238
  */
171
- useCommand(command: MiniInteractionCommand): this;
239
+ useCommand(command: InteractionCommand): this;
172
240
  /**
173
241
  * Registers multiple command handlers with the client.
174
242
  *
175
243
  * @param commands - The command definitions to register.
176
244
  */
177
- useCommands(commands: MiniInteractionCommand[]): this;
245
+ useCommands(commands: InteractionCommand[]): this;
178
246
  /**
179
247
  * Registers a single component handler mapped to a custom identifier.
180
248
  *
181
249
  * @param component - The component definition to register.
182
250
  */
183
- useComponent(component: MiniInteractionComponent): this;
251
+ useComponent(component: ComponentCommand): this;
184
252
  /**
185
253
  * Registers multiple component handlers in a single call.
186
254
  *
187
255
  * @param components - The component definitions to register.
188
256
  */
189
- useComponents(components: MiniInteractionComponent[]): this;
257
+ useComponents(components: ComponentCommand[]): this;
190
258
  /**
191
259
  * Registers a single modal handler mapped to a custom identifier.
192
260
  *
193
261
  * @param modal - The modal definition to register.
194
262
  */
195
- useModal(modal: MiniInteractionModal): this;
263
+ useModal(modal: ModalCommand): this;
196
264
  /**
197
265
  * Registers multiple modal handlers in a single call.
198
266
  *
199
267
  * @param modals - The modal definitions to register.
200
268
  */
201
- useModals(modals: MiniInteractionModal[]): this;
269
+ useModals(modals: ModalCommand[]): this;
202
270
  /**
203
271
  * Recursively loads components from the configured components directory.
204
272
  *
@@ -234,12 +302,12 @@ export declare class MiniInteraction {
234
302
  *
235
303
  * @param request - The request payload containing headers and body data.
236
304
  */
237
- handleRequest(request: MiniInteractionRequest): Promise<MiniInteractionHandlerResult>;
305
+ handleRequest(request: InteractionRequest): Promise<InteractionHandlerResult>;
238
306
  /**
239
307
  * Creates a Node.js style request handler compatible with Express, Next.js API routes,
240
308
  * Vercel serverless functions, and any runtime that expects a `(req, res)` listener.
241
309
  */
242
- createNodeHandler(): MiniInteractionNodeHandler;
310
+ createNodeHandler(): InteractionNodeHandler;
243
311
  /**
244
312
  * Generates a lightweight verification handler that serves an HTML page with an embedded OAuth link.
245
313
  *
@@ -253,15 +321,15 @@ export declare class MiniInteraction {
253
321
  * - `{{OAUTH_STATE}}` - HTML-escaped OAuth state value.
254
322
  * - Custom placeholder name (from {@link DiscordOAuthVerificationPageOptions.placeholder}) and its `_RAW` variant.
255
323
  */
256
- discordOAuthVerificationPage(options?: DiscordOAuthVerificationPageOptions): MiniInteractionNodeHandler;
324
+ discordOAuthVerificationPage(options?: DiscordOAuthVerificationPageOptions): InteractionNodeHandler;
257
325
  /**
258
326
  * Loads an HTML file and returns a success template that replaces useful placeholders.
259
327
  *
260
328
  * The following placeholders are available in the HTML file:
261
329
  * - `{{username}}`, `{{discriminator}}`, `{{user_id}}`, `{{user_tag}}`
262
330
  * - `{{access_token}}`, `{{refresh_token}}`, `{{token_type}}`, `{{scope}}`, `{{expires_at}}`
263
- * - `{{state}}`
264
- */
331
+ * - `{{state}}`
332
+ */
265
333
  connectedOAuthPage(filePath: string): DiscordOAuthCallbackTemplates["success"];
266
334
  /**
267
335
  * Loads an HTML file and returns an error template that can be reused for all failure cases.
@@ -277,7 +345,7 @@ export declare class MiniInteraction {
277
345
  private loadHtmlTemplate;
278
346
  /**
279
347
  * Replaces placeholder tokens in a template with escaped HTML values.
280
- */
348
+ */
281
349
  private renderHtmlTemplate;
282
350
  /**
283
351
  * Normalizes placeholder tokens to the bare key the HTML renderer expects.
@@ -289,11 +357,11 @@ export declare class MiniInteraction {
289
357
  * This helper keeps the user-side implementation tiny while still exposing hooks for
290
358
  * storing metadata or validating the OAuth state value.
291
359
  */
292
- discordOAuthCallback(options: DiscordOAuthCallbackOptions): MiniInteractionNodeHandler;
360
+ discordOAuthCallback(options: DiscordOAuthCallbackOptions): InteractionNodeHandler;
293
361
  /**
294
362
  * Creates a Fetch API compatible handler for runtimes like Workers or Deno.
295
363
  */
296
- createFetchHandler(): MiniInteractionFetchHandler;
364
+ createFetchHandler(): InteractionFetchHandler;
297
365
  /**
298
366
  * Checks if the provided directory path exists on disk.
299
367
  */