@minesa-org/mini-interaction 0.1.9 → 0.1.11

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,5 +1,5 @@
1
1
  import type { IncomingMessage, ServerResponse } from "node:http";
2
- import { APIInteractionResponse, RESTPostAPIChatInputApplicationCommandsJSONBody, RESTPostAPIContextMenuApplicationCommandsJSONBody } from "discord-api-types/v10";
2
+ import { APIInteractionResponse, RESTPostAPIChatInputApplicationCommandsJSONBody, RESTPostAPIContextMenuApplicationCommandsJSONBody, RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody } from "discord-api-types/v10";
3
3
  import type { MiniInteractionCommand } 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";
@@ -11,6 +11,7 @@ export type MiniInteractionOptions = {
11
11
  publicKey: string;
12
12
  commandsDirectory?: string | false;
13
13
  componentsDirectory?: string | false;
14
+ utilsDirectory?: string | false;
14
15
  fetchImplementation?: typeof fetch;
15
16
  verifyKeyImplementation?: VerifyKeyFunction;
16
17
  };
@@ -54,6 +55,7 @@ export type MiniInteractionComponentHandler = (interaction: MessageComponentInte
54
55
  export type MiniInteractionModalHandler = (interaction: ModalSubmitInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
55
56
  /** Unified handler signature that accepts any component or modal interaction. */
56
57
  export type MiniInteractionHandler = MiniInteractionButtonHandler | MiniInteractionStringSelectHandler | MiniInteractionRoleSelectHandler | MiniInteractionUserSelectHandler | MiniInteractionChannelSelectHandler | MiniInteractionMentionableSelectHandler | MiniInteractionComponentHandler | MiniInteractionModalHandler;
58
+ type CommandDataPayload = RESTPostAPIChatInputApplicationCommandsJSONBody | RESTPostAPIContextMenuApplicationCommandsJSONBody | RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody;
57
59
  /**
58
60
  * Structure describing a component or modal handler mapped to a custom id.
59
61
  * When auto-loading from the components directory:
@@ -144,6 +146,7 @@ export declare class MiniInteraction {
144
146
  private readonly verifyKeyImpl;
145
147
  private readonly commandsDirectory;
146
148
  private readonly componentsDirectory;
149
+ readonly utilsDirectory: string | null;
147
150
  private readonly commands;
148
151
  private readonly componentHandlers;
149
152
  private readonly modalHandlers;
@@ -152,10 +155,14 @@ export declare class MiniInteraction {
152
155
  private loadCommandsPromise;
153
156
  private componentsLoaded;
154
157
  private loadComponentsPromise;
158
+ private registerCommandsPromise;
159
+ private registerCommandsSignature;
155
160
  /**
156
161
  * Creates a new MiniInteraction client with optional command auto-loading and custom runtime hooks.
157
162
  */
158
- constructor({ applicationId, publicKey, commandsDirectory, componentsDirectory, fetchImplementation, verifyKeyImplementation, }: MiniInteractionOptions);
163
+ constructor({ applicationId, publicKey, commandsDirectory, componentsDirectory, utilsDirectory, fetchImplementation, verifyKeyImplementation, }: MiniInteractionOptions);
164
+ private normalizeCommandData;
165
+ private registerCommand;
159
166
  /**
160
167
  * Registers a single command handler with the client.
161
168
  *
@@ -207,14 +214,14 @@ export declare class MiniInteraction {
207
214
  /**
208
215
  * Lists the raw command data payloads for registration with Discord.
209
216
  */
210
- listCommandData(): (RESTPostAPIChatInputApplicationCommandsJSONBody | RESTPostAPIContextMenuApplicationCommandsJSONBody)[];
217
+ listCommandData(): CommandDataPayload[];
211
218
  /**
212
219
  * Registers commands with Discord's REST API.
213
220
  *
214
221
  * @param botToken - The bot token authorising the registration request.
215
222
  * @param commands - Optional command list to register instead of auto-loaded commands.
216
223
  */
217
- registerCommands(botToken: string, commands?: (RESTPostAPIChatInputApplicationCommandsJSONBody | RESTPostAPIContextMenuApplicationCommandsJSONBody)[]): Promise<unknown>;
224
+ registerCommands(botToken: string, commands?: CommandDataPayload[]): Promise<unknown>;
218
225
  /**
219
226
  * Registers role connection metadata with Discord's REST API.
220
227
  *
@@ -339,6 +346,10 @@ export declare class MiniInteraction {
339
346
  * Resolves the absolute components directory path from configuration.
340
347
  */
341
348
  private resolveComponentsDirectory;
349
+ /**
350
+ * Resolves the absolute utilities directory path from configuration.
351
+ */
352
+ private resolveUtilsDirectory;
342
353
  /**
343
354
  * Resolves a directory relative to the project "src" or "dist" folders with optional overrides.
344
355
  */
@@ -8,7 +8,7 @@ import { DISCORD_BASE_URL } from "../utils/constants.js";
8
8
  import { createCommandInteraction } from "../utils/CommandInteractionOptions.js";
9
9
  import { createMessageComponentInteraction, } from "../utils/MessageComponentInteraction.js";
10
10
  import { createModalSubmitInteraction, } from "../utils/ModalSubmitInteraction.js";
11
- import { createUserContextMenuInteraction, createMessageContextMenuInteraction, } from "../utils/ContextMenuInteraction.js";
11
+ import { createUserContextMenuInteraction, createMessageContextMenuInteraction, createAppCommandInteraction, } from "../utils/ContextMenuInteraction.js";
12
12
  import { generateOAuthUrl, getOAuthTokens, getDiscordUser, } from "../oauth/DiscordOAuth.js";
13
13
  /** File extensions that are treated as loadable modules when auto-loading. */
14
14
  const SUPPORTED_MODULE_EXTENSIONS = new Set([
@@ -29,6 +29,7 @@ export class MiniInteraction {
29
29
  verifyKeyImpl;
30
30
  commandsDirectory;
31
31
  componentsDirectory;
32
+ utilsDirectory;
32
33
  commands = new Map();
33
34
  componentHandlers = new Map();
34
35
  modalHandlers = new Map();
@@ -37,10 +38,12 @@ export class MiniInteraction {
37
38
  loadCommandsPromise = null;
38
39
  componentsLoaded = false;
39
40
  loadComponentsPromise = null;
41
+ registerCommandsPromise = null;
42
+ registerCommandsSignature = null;
40
43
  /**
41
44
  * Creates a new MiniInteraction client with optional command auto-loading and custom runtime hooks.
42
45
  */
43
- constructor({ applicationId, publicKey, commandsDirectory, componentsDirectory, fetchImplementation, verifyKeyImplementation, }) {
46
+ constructor({ applicationId, publicKey, commandsDirectory, componentsDirectory, utilsDirectory, fetchImplementation, verifyKeyImplementation, }) {
44
47
  if (!applicationId) {
45
48
  throw new Error("[MiniInteraction] applicationId is required");
46
49
  }
@@ -63,33 +66,52 @@ export class MiniInteraction {
63
66
  componentsDirectory === false
64
67
  ? null
65
68
  : this.resolveComponentsDirectory(componentsDirectory);
69
+ this.utilsDirectory =
70
+ utilsDirectory === false
71
+ ? null
72
+ : this.resolveUtilsDirectory(utilsDirectory);
66
73
  }
67
- /**
68
- * Registers a single command handler with the client.
69
- *
70
- * @param command - The command definition to register.
71
- */
72
- useCommand(command) {
73
- const commandName = command?.data?.name;
74
+ normalizeCommandData(data) {
75
+ if (typeof data === "object" && data !== null) {
76
+ const toJSON = data.toJSON;
77
+ if (typeof toJSON === "function") {
78
+ return toJSON.call(data);
79
+ }
80
+ }
81
+ return data;
82
+ }
83
+ registerCommand(command) {
84
+ const normalizedData = this.normalizeCommandData(command.data);
85
+ const commandName = normalizedData?.name;
74
86
  if (!commandName) {
75
87
  throw new Error("[MiniInteraction] command.data.name is required");
76
88
  }
77
89
  if (this.commands.has(commandName)) {
78
90
  console.warn(`[MiniInteraction] Command "${commandName}" already exists and will be overwritten.`);
79
91
  }
80
- this.commands.set(commandName, command);
81
- // Register components exported with the command
82
- if (command.components && Array.isArray(command.components)) {
83
- for (const component of command.components) {
92
+ const normalizedCommand = {
93
+ ...command,
94
+ data: normalizedData,
95
+ };
96
+ this.commands.set(commandName, normalizedCommand);
97
+ if (normalizedCommand.components && Array.isArray(normalizedCommand.components)) {
98
+ for (const component of normalizedCommand.components) {
84
99
  this.useComponent(component);
85
100
  }
86
101
  }
87
- // Register modals exported with the command
88
- if (command.modals && Array.isArray(command.modals)) {
89
- for (const modal of command.modals) {
102
+ if (normalizedCommand.modals && Array.isArray(normalizedCommand.modals)) {
103
+ for (const modal of normalizedCommand.modals) {
90
104
  this.useModal(modal);
91
105
  }
92
106
  }
107
+ }
108
+ /**
109
+ * Registers a single command handler with the client.
110
+ *
111
+ * @param command - The command definition to register.
112
+ */
113
+ useCommand(command) {
114
+ this.registerCommand(command);
93
115
  return this;
94
116
  }
95
117
  /**
@@ -228,19 +250,7 @@ export class MiniInteraction {
228
250
  if (!command) {
229
251
  continue;
230
252
  }
231
- this.commands.set(command.data.name, command);
232
- // Register components exported from the command file
233
- if (command.components && Array.isArray(command.components)) {
234
- for (const component of command.components) {
235
- this.useComponent(component);
236
- }
237
- }
238
- // Register modals exported from the command file
239
- if (command.modals && Array.isArray(command.modals)) {
240
- for (const modal of command.modals) {
241
- this.useModal(modal);
242
- }
243
- }
253
+ this.registerCommand(command);
244
254
  }
245
255
  this.commandsLoaded = true;
246
256
  return this;
@@ -269,20 +279,46 @@ export class MiniInteraction {
269
279
  if (!Array.isArray(resolvedCommands) || resolvedCommands.length === 0) {
270
280
  throw new Error("[MiniInteraction] commands must be a non-empty array payload");
271
281
  }
282
+ const signature = JSON.stringify(resolvedCommands);
283
+ if (this.registerCommandsPromise) {
284
+ if (this.registerCommandsSignature === signature) {
285
+ console.warn("[MiniInteraction] Command registration already in progress. Reusing the in-flight request.");
286
+ return this.registerCommandsPromise;
287
+ }
288
+ console.warn("[MiniInteraction] Command registration already in progress. Waiting for it to finish before continuing.");
289
+ await this.registerCommandsPromise.catch(() => undefined);
290
+ }
272
291
  const url = `${DISCORD_BASE_URL}/applications/${this.applicationId}/commands`;
273
- const response = await this.fetchImpl(url, {
274
- method: "PUT",
275
- headers: {
276
- Authorization: `Bot ${botToken}`,
277
- "Content-Type": "application/json",
278
- },
279
- body: JSON.stringify(resolvedCommands),
292
+ const requestPromise = (async () => {
293
+ try {
294
+ const response = await this.fetchImpl(url, {
295
+ method: "PUT",
296
+ headers: {
297
+ Authorization: `Bot ${botToken}`,
298
+ "Content-Type": "application/json",
299
+ },
300
+ body: JSON.stringify(resolvedCommands),
301
+ });
302
+ if (!response.ok) {
303
+ const errorBody = await response.text();
304
+ throw new Error(`[MiniInteraction] Failed to register commands: [${response.status}] ${errorBody}`);
305
+ }
306
+ return response.json();
307
+ }
308
+ catch (error) {
309
+ const message = error instanceof Error ? error.message : String(error);
310
+ if (message.startsWith("[MiniInteraction]")) {
311
+ throw error;
312
+ }
313
+ throw new Error(`[MiniInteraction] Failed to register commands: ${message}`);
314
+ }
315
+ })();
316
+ this.registerCommandsSignature = signature;
317
+ this.registerCommandsPromise = requestPromise.finally(() => {
318
+ this.registerCommandsPromise = null;
319
+ this.registerCommandsSignature = null;
280
320
  });
281
- if (!response.ok) {
282
- const errorBody = await response.text();
283
- throw new Error(`[MiniInteraction] Failed to register commands: [${response.status}] ${errorBody}`);
284
- }
285
- return response.json();
321
+ return this.registerCommandsPromise;
286
322
  }
287
323
  /**
288
324
  * Registers role connection metadata with Discord's REST API.
@@ -775,8 +811,9 @@ export class MiniInteraction {
775
811
  console.warn(`[MiniInteraction] Command module "${absolutePath}" does not export a command object. Skipping.`);
776
812
  return null;
777
813
  }
778
- const { data, handler } = candidate;
779
- if (!data || typeof data.name !== "string") {
814
+ const { data, handler, components, modals } = candidate;
815
+ const normalizedData = this.normalizeCommandData(data);
816
+ if (!normalizedData || typeof normalizedData.name !== "string") {
780
817
  console.warn(`[MiniInteraction] Command module "${absolutePath}" is missing "data.name". Skipping.`);
781
818
  return null;
782
819
  }
@@ -784,7 +821,7 @@ export class MiniInteraction {
784
821
  console.warn(`[MiniInteraction] Command module "${absolutePath}" is missing a "handler" function. Skipping.`);
785
822
  return null;
786
823
  }
787
- return { data, handler };
824
+ return { data: normalizedData, handler, components, modals };
788
825
  }
789
826
  catch (error) {
790
827
  console.error(`[MiniInteraction] Failed to load command module "${absolutePath}":`, error);
@@ -931,6 +968,12 @@ export class MiniInteraction {
931
968
  resolveComponentsDirectory(componentsDirectory) {
932
969
  return this.resolveDirectory("components", componentsDirectory);
933
970
  }
971
+ /**
972
+ * Resolves the absolute utilities directory path from configuration.
973
+ */
974
+ resolveUtilsDirectory(utilsDirectory) {
975
+ return this.resolveDirectory("utils", utilsDirectory);
976
+ }
934
977
  /**
935
978
  * Resolves a directory relative to the project "src" or "dist" folders with optional overrides.
936
979
  */
@@ -1131,6 +1174,13 @@ export class MiniInteraction {
1131
1174
  resolvedResponse =
1132
1175
  response ?? interactionWithHelpers.getResponse();
1133
1176
  }
1177
+ else if (commandInteraction.data.type ===
1178
+ ApplicationCommandType.PrimaryEntryPoint) {
1179
+ const interactionWithHelpers = createAppCommandInteraction(commandInteraction);
1180
+ response = await command.handler(interactionWithHelpers);
1181
+ resolvedResponse =
1182
+ response ?? interactionWithHelpers.getResponse();
1183
+ }
1134
1184
  else if (commandInteraction.data.type === ApplicationCommandType.Message) {
1135
1185
  // Message context menu command
1136
1186
  const interactionWithHelpers = createMessageContextMenuInteraction(commandInteraction);
@@ -1,18 +1,23 @@
1
- import { ApplicationCommandType, type LocalizationMap, type Permissions, type RESTPostAPIContextMenuApplicationCommandsJSONBody } from "discord-api-types/v10";
1
+ import { ApplicationCommandType, type LocalizationMap, type Permissions, type RESTPostAPIContextMenuApplicationCommandsJSONBody, type RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody } from "discord-api-types/v10";
2
2
  import { CommandContext, IntegrationType } from "./CommandBuilder.js";
3
3
  /**
4
- * Base data structure for context menu commands.
4
+ * Supported non-chat command types.
5
5
  */
6
- type ContextMenuCommandData = Partial<Omit<RESTPostAPIContextMenuApplicationCommandsJSONBody, "contexts" | "integration_types" | "type">> & {
7
- type: ApplicationCommandType.User | ApplicationCommandType.Message;
6
+ type NonChatCommandType = ApplicationCommandType.User | ApplicationCommandType.Message | ApplicationCommandType.PrimaryEntryPoint;
7
+ type NonChatCommandPayload<T extends NonChatCommandType> = T extends ApplicationCommandType.PrimaryEntryPoint ? RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody : RESTPostAPIContextMenuApplicationCommandsJSONBody;
8
+ /**
9
+ * Base data structure for context menu and primary entry point commands.
10
+ */
11
+ type ContextMenuCommandData<T extends NonChatCommandType> = Partial<Omit<NonChatCommandPayload<T>, "contexts" | "integration_types" | "type">> & {
12
+ type: T;
8
13
  contexts?: CommandContext[] | null;
9
14
  integration_types?: IntegrationType[];
10
15
  };
11
16
  /**
12
17
  * Base builder for context menu commands (User and Message commands).
13
18
  */
14
- declare abstract class BaseContextMenuCommandBuilder<T extends ApplicationCommandType.User | ApplicationCommandType.Message> {
15
- protected readonly data: ContextMenuCommandData;
19
+ declare abstract class BaseContextMenuCommandBuilder<T extends NonChatCommandType> {
20
+ protected readonly data: ContextMenuCommandData<T>;
16
21
  constructor(type: T);
17
22
  /**
18
23
  * Sets the command name.
@@ -46,11 +51,11 @@ declare abstract class BaseContextMenuCommandBuilder<T extends ApplicationComman
46
51
  /**
47
52
  * Produces the final REST payload for the configured command.
48
53
  */
49
- toJSON(): RESTPostAPIContextMenuApplicationCommandsJSONBody;
54
+ toJSON(): NonChatCommandPayload<T>;
50
55
  /**
51
56
  * Allows the builder to be coerced into its JSON payload automatically.
52
57
  */
53
- valueOf(): RESTPostAPIContextMenuApplicationCommandsJSONBody;
58
+ valueOf(): NonChatCommandPayload<T>;
54
59
  }
55
60
  /**
56
61
  * Builder for User context menu commands.
@@ -80,4 +85,10 @@ export declare class UserCommandBuilder extends BaseContextMenuCommandBuilder<Ap
80
85
  export declare class MessageCommandBuilder extends BaseContextMenuCommandBuilder<ApplicationCommandType.Message> {
81
86
  constructor();
82
87
  }
88
+ /**
89
+ * Builder for Primary Entry Point commands.
90
+ */
91
+ export declare class AppCommandBuilder extends BaseContextMenuCommandBuilder<ApplicationCommandType.PrimaryEntryPoint> {
92
+ constructor();
93
+ }
83
94
  export {};
@@ -135,3 +135,11 @@ export class MessageCommandBuilder extends BaseContextMenuCommandBuilder {
135
135
  super(ApplicationCommandType.Message);
136
136
  }
137
137
  }
138
+ /**
139
+ * Builder for Primary Entry Point commands.
140
+ */
141
+ export class AppCommandBuilder extends BaseContextMenuCommandBuilder {
142
+ constructor() {
143
+ super(ApplicationCommandType.PrimaryEntryPoint);
144
+ }
145
+ }
package/dist/index.d.ts CHANGED
@@ -2,13 +2,13 @@
2
2
  export { MiniInteraction } from "./clients/MiniInteraction.js";
3
3
  export type { RoleConnectionMetadataField } from "./clients/MiniInteraction.js";
4
4
  export { CommandBuilder, CommandContext, IntegrationType, } from "./commands/CommandBuilder.js";
5
- export { UserCommandBuilder, MessageCommandBuilder, } from "./commands/ContextMenuCommandBuilder.js";
5
+ export { UserCommandBuilder, MessageCommandBuilder, AppCommandBuilder, } from "./commands/ContextMenuCommandBuilder.js";
6
6
  export type { AttachmentOptionBuilder, ChannelOptionBuilder, MentionableOptionBuilder, NumberOptionBuilder, RoleOptionBuilder, StringOptionBuilder, SubcommandBuilder, SubcommandGroupBuilder, UserOptionBuilder, } from "./commands/CommandBuilder.js";
7
7
  export { CommandInteractionOptionResolver, createCommandInteraction, } from "./utils/CommandInteractionOptions.js";
8
8
  export type { CommandInteraction, MentionableOption, ResolvedUserOption, } from "./utils/CommandInteractionOptions.js";
9
- export type { UserContextMenuInteraction, MessageContextMenuInteraction, } from "./utils/ContextMenuInteraction.js";
9
+ export type { UserContextMenuInteraction, MessageContextMenuInteraction, AppCommandInteraction, } from "./utils/ContextMenuInteraction.js";
10
10
  export type { MiniInteractionFetchHandler, MiniInteractionNodeHandler, MiniInteractionHandlerResult, MiniInteractionRequest, MiniInteractionOptions, DiscordOAuthAuthorizeContext, DiscordOAuthCallbackOptions, DiscordOAuthCallbackTemplates, DiscordOAuthErrorTemplateContext, DiscordOAuthServerErrorTemplateContext, DiscordOAuthStateTemplateContext, DiscordOAuthSuccessTemplateContext, DiscordOAuthVerificationPageOptions, } from "./clients/MiniInteraction.js";
11
- export type { MiniInteractionCommand, SlashCommandHandler, UserCommandHandler, MessageCommandHandler, CommandHandler, } from "./types/Commands.js";
11
+ export type { MiniInteractionCommand, SlashCommandHandler, UserCommandHandler, MessageCommandHandler, AppCommandHandler, CommandHandler, } from "./types/Commands.js";
12
12
  export type { MiniInteractionComponent, MiniInteractionButtonHandler, MiniInteractionStringSelectHandler, MiniInteractionRoleSelectHandler, MiniInteractionUserSelectHandler, MiniInteractionChannelSelectHandler, MiniInteractionMentionableSelectHandler, MiniInteractionComponentHandler, MiniInteractionModal, MiniInteractionModalHandler, MiniInteractionHandler, } from "./clients/MiniInteraction.js";
13
13
  export type { MessageComponentInteraction, ButtonInteraction, StringSelectInteraction, RoleSelectInteraction, UserSelectInteraction, ChannelSelectInteraction, MentionableSelectInteraction, ResolvedUserOption as ComponentResolvedUserOption, ResolvedMentionableOption as ComponentResolvedMentionableOption, } from "./utils/MessageComponentInteraction.js";
14
14
  export type { ModalSubmitInteraction } from "./utils/ModalSubmitInteraction.js";
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /** Entry point re-exporting MiniInteraction helpers, builders, and shared types. */
2
2
  export { MiniInteraction } from "./clients/MiniInteraction.js";
3
3
  export { CommandBuilder, CommandContext, IntegrationType, } from "./commands/CommandBuilder.js";
4
- export { UserCommandBuilder, MessageCommandBuilder, } from "./commands/ContextMenuCommandBuilder.js";
4
+ export { UserCommandBuilder, MessageCommandBuilder, AppCommandBuilder, } from "./commands/ContextMenuCommandBuilder.js";
5
5
  export { CommandInteractionOptionResolver, createCommandInteraction, } from "./utils/CommandInteractionOptions.js";
6
6
  export { RoleConnectionMetadataTypes } from "./types/RoleConnectionMetadataTypes.js";
7
7
  export { ChannelType } from "./types/ChannelType.js";
@@ -1,23 +1,27 @@
1
- import type { APIInteractionResponse, RESTPostAPIChatInputApplicationCommandsJSONBody, RESTPostAPIContextMenuApplicationCommandsJSONBody } from "discord-api-types/v10";
1
+ import type { APIInteractionResponse, RESTPostAPIChatInputApplicationCommandsJSONBody, RESTPostAPIContextMenuApplicationCommandsJSONBody, RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody } from "discord-api-types/v10";
2
2
  import type { CommandInteraction } from "../utils/CommandInteractionOptions.js";
3
- import type { UserContextMenuInteraction, MessageContextMenuInteraction } from "../utils/ContextMenuInteraction.js";
3
+ import type { UserContextMenuInteraction, MessageContextMenuInteraction, AppCommandInteraction } from "../utils/ContextMenuInteraction.js";
4
4
  import type { MiniInteractionComponent, MiniInteractionModal } from "../clients/MiniInteraction.js";
5
+ import type { CommandBuilder } from "../commands/CommandBuilder.js";
6
+ import type { MessageCommandBuilder, UserCommandBuilder, AppCommandBuilder } from "../commands/ContextMenuCommandBuilder.js";
5
7
  /** Handler signature for slash command executions within MiniInteraction. */
6
8
  export type SlashCommandHandler = (interaction: CommandInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
7
9
  /** Handler signature for user context menu command executions within MiniInteraction. */
8
10
  export type UserCommandHandler = (interaction: UserContextMenuInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
9
11
  /** Handler signature for message context menu command executions within MiniInteraction. */
10
12
  export type MessageCommandHandler = (interaction: MessageContextMenuInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
13
+ /** Handler signature for primary entry point command executions within MiniInteraction. */
14
+ export type AppCommandHandler = (interaction: AppCommandInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
11
15
  /** Union of all command handler types. */
12
- export type CommandHandler = SlashCommandHandler | UserCommandHandler | MessageCommandHandler;
16
+ export type CommandHandler = SlashCommandHandler | UserCommandHandler | MessageCommandHandler | AppCommandHandler;
13
17
  /** Structure representing a slash command definition and its runtime handler. */
14
18
  export type MiniInteractionCommand = {
15
- data: RESTPostAPIChatInputApplicationCommandsJSONBody | RESTPostAPIContextMenuApplicationCommandsJSONBody;
19
+ data: RESTPostAPIChatInputApplicationCommandsJSONBody | RESTPostAPIContextMenuApplicationCommandsJSONBody | RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody | CommandBuilder | UserCommandBuilder | MessageCommandBuilder | AppCommandBuilder;
16
20
  handler: CommandHandler;
17
21
  /**
18
22
  * Optional array of component handlers related to this command.
19
23
  * These will be automatically registered when the command is loaded.
20
- */
24
+ */
21
25
  components?: MiniInteractionComponent[];
22
26
  /**
23
27
  * Optional array of modal handlers related to this command.
@@ -1,4 +1,4 @@
1
- import { type APIInteractionResponse, type APIInteractionResponseChannelMessageWithSource, type APIInteractionResponseDeferredChannelMessageWithSource, type APIMessageApplicationCommandInteraction, type APIModalInteractionResponse, type APIModalInteractionResponseCallbackData, type APIUserApplicationCommandInteraction } from "discord-api-types/v10";
1
+ import { type APIInteractionResponse, type APIInteractionResponseChannelMessageWithSource, type APIInteractionResponseDeferredChannelMessageWithSource, type APIInteractionResponseUpdateMessage, type APIMessage, type APIMessageApplicationCommandInteraction, type APIModalInteractionResponse, type APIModalInteractionResponseCallbackData, type APIPrimaryEntryPointCommandInteraction, type APIUser, type APIUserApplicationCommandInteraction } from "discord-api-types/v10";
2
2
  import { DeferReplyOptions, InteractionMessageData } from "./interactionMessageHelpers.js";
3
3
  /**
4
4
  * Base helper methods for context menu interactions.
@@ -6,6 +6,8 @@ import { DeferReplyOptions, InteractionMessageData } from "./interactionMessageH
6
6
  type ContextMenuInteractionHelpers = {
7
7
  getResponse: () => APIInteractionResponse | null;
8
8
  reply: (data: InteractionMessageData) => APIInteractionResponseChannelMessageWithSource;
9
+ followUp: (data: InteractionMessageData) => APIInteractionResponseChannelMessageWithSource;
10
+ editReply: (data?: InteractionMessageData) => APIInteractionResponseUpdateMessage;
9
11
  deferReply: (options?: DeferReplyOptions) => APIInteractionResponseDeferredChannelMessageWithSource;
10
12
  showModal: (data: APIModalInteractionResponseCallbackData | {
11
13
  toJSON(): APIModalInteractionResponseCallbackData;
@@ -14,11 +16,21 @@ type ContextMenuInteractionHelpers = {
14
16
  /**
15
17
  * User context menu interaction with helper methods.
16
18
  */
17
- export type UserContextMenuInteraction = APIUserApplicationCommandInteraction & ContextMenuInteractionHelpers;
19
+ export type UserContextMenuInteraction = APIUserApplicationCommandInteraction & ContextMenuInteractionHelpers & {
20
+ /** Resolved user targeted by this user context menu command. */
21
+ targetUser?: APIUser;
22
+ };
18
23
  /**
19
24
  * Message context menu interaction with helper methods.
20
25
  */
21
- export type MessageContextMenuInteraction = APIMessageApplicationCommandInteraction & ContextMenuInteractionHelpers;
26
+ export type MessageContextMenuInteraction = APIMessageApplicationCommandInteraction & ContextMenuInteractionHelpers & {
27
+ /** Resolved message targeted by this message context menu command. */
28
+ targetMessage?: APIMessage;
29
+ };
30
+ /**
31
+ * Primary entry point interaction with helper methods.
32
+ */
33
+ export type AppCommandInteraction = APIPrimaryEntryPointCommandInteraction & ContextMenuInteractionHelpers;
22
34
  /**
23
35
  * Wraps a raw user context menu interaction with helper methods.
24
36
  *
@@ -33,4 +45,11 @@ export declare function createUserContextMenuInteraction(interaction: APIUserApp
33
45
  * @returns A helper-augmented interaction object.
34
46
  */
35
47
  export declare function createMessageContextMenuInteraction(interaction: APIMessageApplicationCommandInteraction): MessageContextMenuInteraction;
48
+ /**
49
+ * Wraps a raw primary entry point interaction with helper methods.
50
+ *
51
+ * @param interaction - The raw primary entry point interaction payload from Discord.
52
+ * @returns A helper-augmented interaction object.
53
+ */
54
+ export declare function createAppCommandInteraction(interaction: APIPrimaryEntryPointCommandInteraction): AppCommandInteraction;
36
55
  export {};
@@ -1,49 +1,63 @@
1
1
  import { InteractionResponseType, } from "discord-api-types/v10";
2
2
  import { normaliseInteractionMessageData, normaliseMessageFlags, } from "./interactionMessageHelpers.js";
3
- /**
4
- * Wraps a raw user context menu interaction with helper methods.
5
- *
6
- * @param interaction - The raw user context menu interaction payload from Discord.
7
- * @returns A helper-augmented interaction object.
8
- */
9
- export function createUserContextMenuInteraction(interaction) {
3
+ function createContextMenuInteractionHelpers() {
10
4
  let capturedResponse = null;
11
- const reply = (data) => {
12
- const normalised = normaliseInteractionMessageData(data);
13
- if (!normalised) {
14
- throw new Error("[MiniInteraction] Channel message responses require data to be provided.");
15
- }
16
- const response = {
17
- type: InteractionResponseType.ChannelMessageWithSource,
18
- data: normalised,
19
- };
5
+ const captureResponse = (response) => {
20
6
  capturedResponse = response;
21
7
  return response;
22
8
  };
9
+ function createMessageResponse(type, data) {
10
+ const normalised = normaliseInteractionMessageData(data);
11
+ if (type === InteractionResponseType.ChannelMessageWithSource) {
12
+ if (!normalised) {
13
+ throw new Error("[MiniInteraction] Channel message responses require data to be provided.");
14
+ }
15
+ return captureResponse({
16
+ type,
17
+ data: normalised,
18
+ });
19
+ }
20
+ if (normalised) {
21
+ return captureResponse({ type, data: normalised });
22
+ }
23
+ return captureResponse({ type });
24
+ }
25
+ const reply = (data) => createMessageResponse(InteractionResponseType.ChannelMessageWithSource, data);
26
+ const followUp = (data) => createMessageResponse(InteractionResponseType.ChannelMessageWithSource, data);
27
+ const editReply = (data) => createMessageResponse(InteractionResponseType.UpdateMessage, data);
23
28
  const deferReply = (options = {}) => {
24
29
  const flags = normaliseMessageFlags(options.flags);
25
- const response = {
30
+ return captureResponse({
26
31
  type: InteractionResponseType.DeferredChannelMessageWithSource,
27
32
  data: flags ? { flags } : undefined,
28
- };
29
- capturedResponse = response;
30
- return response;
33
+ });
31
34
  };
32
35
  const showModal = (data) => {
33
36
  const modalData = typeof data === "object" && "toJSON" in data ? data.toJSON() : data;
34
- const response = {
37
+ return captureResponse({
35
38
  type: InteractionResponseType.Modal,
36
39
  data: modalData,
37
- };
38
- capturedResponse = response;
39
- return response;
40
+ });
40
41
  };
41
42
  const getResponse = () => capturedResponse;
42
- return Object.assign(interaction, {
43
+ return {
44
+ getResponse,
43
45
  reply,
46
+ followUp,
47
+ editReply,
44
48
  deferReply,
45
49
  showModal,
46
- getResponse,
50
+ };
51
+ }
52
+ /**
53
+ * Wraps a raw user context menu interaction with helper methods.
54
+ *
55
+ * @param interaction - The raw user context menu interaction payload from Discord.
56
+ * @returns A helper-augmented interaction object.
57
+ */
58
+ export function createUserContextMenuInteraction(interaction) {
59
+ return Object.assign(interaction, createContextMenuInteractionHelpers(), {
60
+ targetUser: resolveTargetUser(interaction),
47
61
  });
48
62
  }
49
63
  /**
@@ -53,42 +67,30 @@ export function createUserContextMenuInteraction(interaction) {
53
67
  * @returns A helper-augmented interaction object.
54
68
  */
55
69
  export function createMessageContextMenuInteraction(interaction) {
56
- let capturedResponse = null;
57
- const reply = (data) => {
58
- const normalised = normaliseInteractionMessageData(data);
59
- if (!normalised) {
60
- throw new Error("[MiniInteraction] Channel message responses require data to be provided.");
61
- }
62
- const response = {
63
- type: InteractionResponseType.ChannelMessageWithSource,
64
- data: normalised,
65
- };
66
- capturedResponse = response;
67
- return response;
68
- };
69
- const deferReply = (options = {}) => {
70
- const flags = normaliseMessageFlags(options.flags);
71
- const response = {
72
- type: InteractionResponseType.DeferredChannelMessageWithSource,
73
- data: flags ? { flags } : undefined,
74
- };
75
- capturedResponse = response;
76
- return response;
77
- };
78
- const showModal = (data) => {
79
- const modalData = typeof data === "object" && "toJSON" in data ? data.toJSON() : data;
80
- const response = {
81
- type: InteractionResponseType.Modal,
82
- data: modalData,
83
- };
84
- capturedResponse = response;
85
- return response;
86
- };
87
- const getResponse = () => capturedResponse;
88
- return Object.assign(interaction, {
89
- reply,
90
- deferReply,
91
- showModal,
92
- getResponse,
70
+ return Object.assign(interaction, createContextMenuInteractionHelpers(), {
71
+ targetMessage: resolveTargetMessage(interaction),
93
72
  });
94
73
  }
74
+ /**
75
+ * Wraps a raw primary entry point interaction with helper methods.
76
+ *
77
+ * @param interaction - The raw primary entry point interaction payload from Discord.
78
+ * @returns A helper-augmented interaction object.
79
+ */
80
+ export function createAppCommandInteraction(interaction) {
81
+ return Object.assign(interaction, createContextMenuInteractionHelpers());
82
+ }
83
+ function resolveTargetMessage(interaction) {
84
+ const targetId = interaction.data?.target_id;
85
+ if (!targetId) {
86
+ return undefined;
87
+ }
88
+ return interaction.data?.resolved?.messages?.[targetId];
89
+ }
90
+ function resolveTargetUser(interaction) {
91
+ const targetId = interaction.data?.target_id;
92
+ if (!targetId) {
93
+ return undefined;
94
+ }
95
+ return interaction.data?.resolved?.users?.[targetId];
96
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minesa-org/mini-interaction",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Mini interaction, connecting your app with Discord via HTTP-interaction (Vercel support).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",