@minesa-org/mini-interaction 0.4.3 → 0.4.5

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.
@@ -0,0 +1,99 @@
1
+ import { type DiscordUser, type OAuthTokens } from "../oauth/DiscordOAuth.js";
2
+ type TimeoutConfig = {
3
+ initialResponseTimeout?: number;
4
+ autoDeferSlowOperations?: boolean;
5
+ enableTimeoutWarnings?: boolean;
6
+ enableResponseDebugLogging?: boolean;
7
+ };
8
+ export type MiniInteractionOptions = {
9
+ commandsDirectory?: string;
10
+ componentsDirectory?: string;
11
+ utilsDirectory?: string;
12
+ timeoutConfig?: TimeoutConfig;
13
+ debug?: boolean;
14
+ cwd?: string;
15
+ publicKey?: string;
16
+ applicationId?: string;
17
+ token?: string;
18
+ guildId?: string;
19
+ };
20
+ type OAuthPageTemplate = {
21
+ htmlFile: string;
22
+ };
23
+ type OAuthCallbackTemplates = {
24
+ success: OAuthPageTemplate;
25
+ missingCode: OAuthPageTemplate;
26
+ oauthError: OAuthPageTemplate;
27
+ invalidState: OAuthPageTemplate;
28
+ serverError: OAuthPageTemplate;
29
+ };
30
+ type NodeRequest = {
31
+ body?: unknown;
32
+ rawBody?: string | Uint8Array | Buffer;
33
+ headers: Record<string, string | string[] | undefined> | {
34
+ get(name: string): string | null;
35
+ };
36
+ method?: string;
37
+ url?: string;
38
+ [Symbol.asyncIterator]?: () => AsyncIterableIterator<Uint8Array>;
39
+ on?: (event: string, listener: (...args: unknown[]) => void) => void;
40
+ };
41
+ type NodeResponse = {
42
+ statusCode?: number;
43
+ setHeader?: (name: string, value: string) => void;
44
+ end: (body?: string) => void;
45
+ status?: (code: number) => NodeResponse;
46
+ json?: (body: unknown) => void;
47
+ };
48
+ export declare class MiniInteraction {
49
+ private readonly options;
50
+ private readonly projectRoot;
51
+ private readonly rest;
52
+ private readonly responseStates;
53
+ private loadedModulesPromise?;
54
+ constructor(options?: MiniInteractionOptions);
55
+ createNodeHandler(): (req: NodeRequest, res: NodeResponse) => Promise<void>;
56
+ registerCommands(tokenOverride?: string): Promise<unknown>;
57
+ discordOAuthVerificationPage(options: {
58
+ htmlFile: string;
59
+ scopes?: string[];
60
+ }): (req: NodeRequest, res: NodeResponse) => Promise<void>;
61
+ connectedOAuthPage(htmlFile: string): OAuthPageTemplate;
62
+ failedOAuthPage(htmlFile: string): OAuthPageTemplate;
63
+ discordOAuthCallback(options: {
64
+ templates: OAuthCallbackTemplates;
65
+ onAuthorize?: (payload: {
66
+ user: DiscordUser;
67
+ tokens: OAuthTokens;
68
+ req: NodeRequest;
69
+ res: NodeResponse;
70
+ }) => Promise<void> | void;
71
+ }): (req: NodeRequest, res: NodeResponse) => Promise<void>;
72
+ private dispatch;
73
+ private executeCommandHandler;
74
+ private executeComponentHandler;
75
+ private executeModalHandler;
76
+ private runWithResponseLifecycle;
77
+ private loadModules;
78
+ private discoverModules;
79
+ private loadDirectory;
80
+ private normalizeModuleExports;
81
+ private normalizeExportValue;
82
+ private walkFiles;
83
+ private isImportableModule;
84
+ private isInteractionCommand;
85
+ private getCommandName;
86
+ private resolveCommandPayload;
87
+ private isCustomIdHandler;
88
+ private looksLikeModalFile;
89
+ private readRawBody;
90
+ private getHeader;
91
+ private sendJson;
92
+ private loadHtmlFile;
93
+ private renderOAuthTemplate;
94
+ private sendHtml;
95
+ private getOAuthConfig;
96
+ private getCookie;
97
+ }
98
+ export declare const LegacyMiniInteractionAdapter: typeof MiniInteraction;
99
+ export {};
@@ -0,0 +1,467 @@
1
+ import { readFile, readdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import { ApplicationCommandType, InteractionResponseType, InteractionType, } from "discord-api-types/v10";
5
+ import { createCommandInteraction } from "../utils/CommandInteractionOptions.js";
6
+ import { createAppCommandInteraction, createMessageContextMenuInteraction, createUserContextMenuInteraction, } from "../utils/ContextMenuInteraction.js";
7
+ import { createMessageComponentInteraction } from "../utils/MessageComponentInteraction.js";
8
+ import { createModalSubmitInteraction } from "../utils/ModalSubmitInteraction.js";
9
+ import { DiscordRestClient } from "../core/http/DiscordRestClient.js";
10
+ import { verifyAndParseInteraction } from "../core/interactions/InteractionVerifier.js";
11
+ import { generateOAuthUrl, getDiscordUser, getOAuthTokens, } from "../oauth/DiscordOAuth.js";
12
+ export class MiniInteraction {
13
+ options;
14
+ projectRoot;
15
+ rest;
16
+ responseStates = new Map();
17
+ loadedModulesPromise;
18
+ constructor(options = {}) {
19
+ this.options = options;
20
+ this.projectRoot = path.resolve(options.cwd ?? process.cwd());
21
+ const applicationId = options.applicationId ??
22
+ process.env.DISCORD_APPLICATION_ID ??
23
+ process.env.DISCORD_APP_ID;
24
+ const token = options.token ??
25
+ process.env.DISCORD_BOT_TOKEN ??
26
+ process.env.DISCORD_TOKEN;
27
+ if (!applicationId || !token) {
28
+ throw new Error("[MiniInteraction] Missing Discord REST credentials. Set applicationId/token or DISCORD_APPLICATION_ID + DISCORD_BOT_TOKEN.");
29
+ }
30
+ this.rest = new DiscordRestClient({ applicationId, token });
31
+ }
32
+ createNodeHandler() {
33
+ return async (req, res) => {
34
+ try {
35
+ const body = await this.readRawBody(req);
36
+ const signature = this.getHeader(req.headers, "x-signature-ed25519");
37
+ const timestamp = this.getHeader(req.headers, "x-signature-timestamp");
38
+ const publicKey = this.options.publicKey ?? process.env.DISCORD_PUBLIC_KEY;
39
+ if (!publicKey) {
40
+ this.sendJson(res, 500, {
41
+ error: "[MiniInteraction] Missing DISCORD_PUBLIC_KEY.",
42
+ });
43
+ return;
44
+ }
45
+ if (!signature || !timestamp) {
46
+ this.sendJson(res, 401, {
47
+ error: "[MiniInteraction] Missing Discord signature headers.",
48
+ });
49
+ return;
50
+ }
51
+ const interaction = await verifyAndParseInteraction({
52
+ body,
53
+ signature,
54
+ timestamp,
55
+ publicKey,
56
+ });
57
+ if (interaction.type === InteractionType.Ping) {
58
+ this.sendJson(res, 200, { type: InteractionResponseType.Pong });
59
+ return;
60
+ }
61
+ const response = await this.dispatch(interaction);
62
+ this.sendJson(res, 200, response ?? {
63
+ type: InteractionResponseType.DeferredChannelMessageWithSource,
64
+ });
65
+ }
66
+ catch (error) {
67
+ const message = error instanceof Error ? error.message : "[MiniInteraction] Unknown error";
68
+ if (this.options.debug) {
69
+ console.error("[MiniInteraction] createNodeHandler failed", error);
70
+ }
71
+ this.sendJson(res, 500, { error: message });
72
+ }
73
+ };
74
+ }
75
+ async registerCommands(tokenOverride) {
76
+ const modules = await this.loadModules();
77
+ const payload = modules.commands.map((command) => this.resolveCommandPayload(command));
78
+ const applicationId = this.options.applicationId ??
79
+ process.env.DISCORD_APPLICATION_ID ??
80
+ process.env.DISCORD_APP_ID;
81
+ if (!applicationId) {
82
+ throw new Error("[MiniInteraction] Missing applicationId for command registration.");
83
+ }
84
+ const token = tokenOverride ??
85
+ this.options.token ??
86
+ process.env.DISCORD_BOT_TOKEN ??
87
+ process.env.DISCORD_TOKEN;
88
+ if (!token) {
89
+ throw new Error("[MiniInteraction] Missing bot token for command registration.");
90
+ }
91
+ const rest = new DiscordRestClient({ applicationId, token });
92
+ const guildId = this.options.guildId ?? process.env.DISCORD_GUILD_ID;
93
+ const route = guildId
94
+ ? `/applications/${applicationId}/guilds/${guildId}/commands`
95
+ : `/applications/${applicationId}/commands`;
96
+ if (this.options.debug) {
97
+ console.debug(`[MiniInteraction] Registering ${payload.length} command(s) on ${guildId ? `guild ${guildId}` : "global"} scope.`);
98
+ }
99
+ return rest.request(route, {
100
+ method: "PUT",
101
+ body: JSON.stringify(payload),
102
+ });
103
+ }
104
+ discordOAuthVerificationPage(options) {
105
+ return async (_req, res) => {
106
+ const oauthConfig = this.getOAuthConfig();
107
+ const { url, state } = generateOAuthUrl(oauthConfig, options.scopes ?? [
108
+ "applications.commands",
109
+ "identify",
110
+ "guilds",
111
+ "role_connections.write",
112
+ ]);
113
+ const html = await this.loadHtmlFile(options.htmlFile);
114
+ const rendered = html.replaceAll("{{OAUTH_URL_RAW}}", url);
115
+ res.setHeader?.("Set-Cookie", `mini_oauth_state=${encodeURIComponent(state)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=900`);
116
+ this.sendHtml(res, 200, rendered);
117
+ };
118
+ }
119
+ connectedOAuthPage(htmlFile) {
120
+ return { htmlFile };
121
+ }
122
+ failedOAuthPage(htmlFile) {
123
+ return { htmlFile };
124
+ }
125
+ discordOAuthCallback(options) {
126
+ return async (req, res) => {
127
+ try {
128
+ const requestUrl = new URL(req.url ?? "/", process.env.DISCORD_REDIRECT_URI ?? "http://localhost");
129
+ const error = requestUrl.searchParams.get("error");
130
+ const code = requestUrl.searchParams.get("code");
131
+ const state = requestUrl.searchParams.get("state");
132
+ const cookieState = this.getCookie(req, "mini_oauth_state");
133
+ if (error) {
134
+ await this.renderOAuthTemplate(res, options.templates.oauthError);
135
+ return;
136
+ }
137
+ if (!code) {
138
+ await this.renderOAuthTemplate(res, options.templates.missingCode);
139
+ return;
140
+ }
141
+ if (state && cookieState && state !== cookieState) {
142
+ await this.renderOAuthTemplate(res, options.templates.invalidState);
143
+ return;
144
+ }
145
+ const tokens = await getOAuthTokens(code, this.getOAuthConfig());
146
+ const user = await getDiscordUser(tokens.access_token);
147
+ await options.onAuthorize?.({ user, tokens, req, res });
148
+ res.setHeader?.("Set-Cookie", "mini_oauth_state=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0");
149
+ await this.renderOAuthTemplate(res, options.templates.success);
150
+ }
151
+ catch (error) {
152
+ if (this.options.debug) {
153
+ console.error("[MiniInteraction] discordOAuthCallback failed", error);
154
+ }
155
+ await this.renderOAuthTemplate(res, options.templates.serverError);
156
+ }
157
+ };
158
+ }
159
+ async dispatch(interaction) {
160
+ const modules = await this.loadModules();
161
+ if (interaction.type === InteractionType.ApplicationCommand) {
162
+ const command = modules.commands.find((candidate) => this.getCommandName(candidate) === interaction.data.name);
163
+ if (!command)
164
+ return undefined;
165
+ return this.executeCommandHandler(command.handler, interaction);
166
+ }
167
+ if (interaction.type === InteractionType.MessageComponent) {
168
+ const component = modules.components.find((candidate) => candidate.customId === interaction.data.custom_id);
169
+ if (!component)
170
+ return undefined;
171
+ return this.executeComponentHandler(component.handler, interaction);
172
+ }
173
+ if (interaction.type === InteractionType.ModalSubmit) {
174
+ const modal = modules.modals.find((candidate) => candidate.customId === interaction.data.custom_id);
175
+ if (!modal)
176
+ return undefined;
177
+ return this.executeModalHandler(modal.handler, interaction);
178
+ }
179
+ return undefined;
180
+ }
181
+ async executeCommandHandler(handler, interaction) {
182
+ return this.runWithResponseLifecycle(interaction, async (helpers) => {
183
+ switch (interaction.data.type) {
184
+ case ApplicationCommandType.ChatInput:
185
+ return handler(createCommandInteraction(interaction, helpers));
186
+ case ApplicationCommandType.User:
187
+ return handler(createUserContextMenuInteraction(interaction, helpers));
188
+ case ApplicationCommandType.Message:
189
+ return handler(createMessageContextMenuInteraction(interaction, helpers));
190
+ default:
191
+ if (interaction.data.type === ApplicationCommandType.PrimaryEntryPoint) {
192
+ return handler(createAppCommandInteraction(interaction, helpers));
193
+ }
194
+ throw new Error(`[MiniInteraction] Unsupported application command type: ${String(interaction.data.type)}`);
195
+ }
196
+ });
197
+ }
198
+ async executeComponentHandler(handler, interaction) {
199
+ return this.runWithResponseLifecycle(interaction, async (helpers) => handler(createMessageComponentInteraction(interaction, helpers)));
200
+ }
201
+ async executeModalHandler(handler, interaction) {
202
+ return this.runWithResponseLifecycle(interaction, async (helpers) => handler(createModalSubmitInteraction(interaction, helpers)));
203
+ }
204
+ async runWithResponseLifecycle(interaction, executor) {
205
+ let ackResponse;
206
+ const helpers = {
207
+ canRespond: (interactionId) => (this.responseStates.get(interactionId) ?? "pending") === "pending",
208
+ trackResponse: (interactionId, _token, state) => {
209
+ this.responseStates.set(interactionId, state);
210
+ },
211
+ onAck: (response) => {
212
+ ackResponse = response;
213
+ },
214
+ sendFollowUp: async (token, response, messageId) => {
215
+ const responseData = "data" in response ? response.data ?? {} : {};
216
+ if (messageId === "@original") {
217
+ await this.rest.editOriginal(token, responseData);
218
+ return;
219
+ }
220
+ await this.rest.createFollowup(token, responseData);
221
+ },
222
+ };
223
+ const autoDeferMs = Math.min(2500, this.options.timeoutConfig?.initialResponseTimeout ?? 2500);
224
+ const autoDeferTimer = this.options.timeoutConfig?.autoDeferSlowOperations === true
225
+ ? setTimeout(() => {
226
+ if (!helpers.canRespond(interaction.id))
227
+ return;
228
+ if (this.options.debug || this.options.timeoutConfig?.enableResponseDebugLogging) {
229
+ console.warn(`[MiniInteraction] Auto-deferred interaction ${interaction.id} after ${autoDeferMs}ms.`);
230
+ }
231
+ ackResponse = {
232
+ type: InteractionResponseType.DeferredChannelMessageWithSource,
233
+ };
234
+ helpers.trackResponse(interaction.id, interaction.token, "deferred");
235
+ }, autoDeferMs)
236
+ : undefined;
237
+ const timeoutWarningMs = this.options.timeoutConfig?.initialResponseTimeout;
238
+ const timeoutWarningTimer = this.options.timeoutConfig?.enableTimeoutWarnings && timeoutWarningMs
239
+ ? setTimeout(() => {
240
+ if (this.responseStates.get(interaction.id))
241
+ return;
242
+ console.warn(`[MiniInteraction] Interaction ${interaction.id} exceeded ${timeoutWarningMs}ms without a response.`);
243
+ }, timeoutWarningMs)
244
+ : undefined;
245
+ try {
246
+ const result = await executor(helpers);
247
+ if (this.options.debug || this.options.timeoutConfig?.enableResponseDebugLogging) {
248
+ console.debug(`[MiniInteraction] Interaction ${interaction.id} completed with ${result ? "explicit" : "fallback"} response.`);
249
+ }
250
+ return result ?? ackResponse;
251
+ }
252
+ finally {
253
+ if (autoDeferTimer)
254
+ clearTimeout(autoDeferTimer);
255
+ if (timeoutWarningTimer)
256
+ clearTimeout(timeoutWarningTimer);
257
+ }
258
+ }
259
+ async loadModules() {
260
+ if (!this.loadedModulesPromise) {
261
+ this.loadedModulesPromise = this.discoverModules();
262
+ }
263
+ return this.loadedModulesPromise;
264
+ }
265
+ async discoverModules() {
266
+ const commands = this.options.commandsDirectory
267
+ ? await this.loadDirectory(this.options.commandsDirectory)
268
+ : [];
269
+ const components = this.options.componentsDirectory
270
+ ? await this.loadDirectory(this.options.componentsDirectory)
271
+ : [];
272
+ const loaded = {
273
+ commands: [],
274
+ components: [],
275
+ modals: [],
276
+ };
277
+ for (const { filePath, value } of commands) {
278
+ if (this.isInteractionCommand(value)) {
279
+ loaded.commands.push(value);
280
+ }
281
+ else if (this.options.debug) {
282
+ console.warn(`[MiniInteraction] Ignored non-command module: ${filePath}`);
283
+ }
284
+ }
285
+ for (const { filePath, value } of components) {
286
+ if (!this.isCustomIdHandler(value)) {
287
+ if (this.options.debug) {
288
+ console.warn(`[MiniInteraction] Ignored non-component module: ${filePath}`);
289
+ }
290
+ continue;
291
+ }
292
+ if (this.looksLikeModalFile(filePath)) {
293
+ loaded.modals.push(value);
294
+ }
295
+ else {
296
+ loaded.components.push(value);
297
+ }
298
+ }
299
+ return loaded;
300
+ }
301
+ async loadDirectory(directory) {
302
+ const absoluteDirectory = path.resolve(this.projectRoot, directory);
303
+ const files = await this.walkFiles(absoluteDirectory);
304
+ const loaded = await Promise.all(files
305
+ .filter((filePath) => this.isImportableModule(filePath))
306
+ .map(async (filePath) => ({
307
+ filePath,
308
+ values: this.normalizeModuleExports(await import(pathToFileURL(filePath).href)),
309
+ })));
310
+ return loaded.flatMap(({ filePath, values }) => values.map((value) => ({ filePath, value })));
311
+ }
312
+ normalizeModuleExports(moduleValue) {
313
+ const values = [];
314
+ if ("default" in moduleValue) {
315
+ values.push(...this.normalizeExportValue(moduleValue.default));
316
+ }
317
+ for (const [key, value] of Object.entries(moduleValue)) {
318
+ if (key === "default")
319
+ continue;
320
+ values.push(...this.normalizeExportValue(value));
321
+ }
322
+ return values;
323
+ }
324
+ normalizeExportValue(value) {
325
+ if (Array.isArray(value))
326
+ return value;
327
+ return [value];
328
+ }
329
+ async walkFiles(directory) {
330
+ const entries = await readdir(directory, { withFileTypes: true });
331
+ const results = await Promise.all(entries.map(async (entry) => {
332
+ const resolvedPath = path.join(directory, entry.name);
333
+ if (entry.isDirectory()) {
334
+ return this.walkFiles(resolvedPath);
335
+ }
336
+ return [resolvedPath];
337
+ }));
338
+ return results.flat();
339
+ }
340
+ isImportableModule(filePath) {
341
+ if (filePath.endsWith(".d.ts"))
342
+ return false;
343
+ return /\.(ts|mts|js|mjs|cjs)$/i.test(filePath);
344
+ }
345
+ isInteractionCommand(value) {
346
+ return (typeof value === "object" &&
347
+ value !== null &&
348
+ "data" in value &&
349
+ "handler" in value &&
350
+ typeof value.handler === "function");
351
+ }
352
+ getCommandName(command) {
353
+ const data = command.data;
354
+ if (typeof data.toJSON === "function") {
355
+ return data.toJSON().name;
356
+ }
357
+ return data.name;
358
+ }
359
+ resolveCommandPayload(command) {
360
+ const data = command.data;
361
+ if (typeof data.toJSON === "function") {
362
+ return data.toJSON();
363
+ }
364
+ return command.data;
365
+ }
366
+ isCustomIdHandler(value) {
367
+ return (typeof value === "object" &&
368
+ value !== null &&
369
+ "customId" in value &&
370
+ "handler" in value &&
371
+ typeof value.customId === "string" &&
372
+ typeof value.handler === "function");
373
+ }
374
+ looksLikeModalFile(filePath) {
375
+ const normalized = filePath.toLowerCase();
376
+ return (normalized.includes(`${path.sep}modals${path.sep}`) ||
377
+ normalized.endsWith(".modal.ts") ||
378
+ normalized.endsWith(".modal.js") ||
379
+ normalized.includes("_modal.") ||
380
+ normalized.includes("-modal."));
381
+ }
382
+ async readRawBody(req) {
383
+ if (typeof req.rawBody === "string")
384
+ return req.rawBody;
385
+ if (req.rawBody instanceof Uint8Array) {
386
+ return Buffer.from(req.rawBody).toString("utf8");
387
+ }
388
+ if (typeof req.body === "string")
389
+ return req.body;
390
+ if (req.body instanceof Uint8Array) {
391
+ return Buffer.from(req.body).toString("utf8");
392
+ }
393
+ if (req.body && typeof req.body === "object") {
394
+ return JSON.stringify(req.body);
395
+ }
396
+ if (typeof req[Symbol.asyncIterator] === "function") {
397
+ const chunks = [];
398
+ for await (const chunk of req) {
399
+ chunks.push(Buffer.from(chunk));
400
+ }
401
+ return Buffer.concat(chunks).toString("utf8");
402
+ }
403
+ return "";
404
+ }
405
+ getHeader(headers, name) {
406
+ if (typeof headers.get === "function") {
407
+ return headers.get(name) ?? undefined;
408
+ }
409
+ const recordHeaders = headers;
410
+ const direct = recordHeaders[name] ??
411
+ recordHeaders[name.toLowerCase()] ??
412
+ recordHeaders[name.toUpperCase()];
413
+ if (Array.isArray(direct))
414
+ return direct[0];
415
+ return direct;
416
+ }
417
+ sendJson(res, statusCode, body) {
418
+ if (typeof res.status === "function" && typeof res.json === "function") {
419
+ const response = res.status(statusCode);
420
+ response.json?.(body);
421
+ return;
422
+ }
423
+ res.statusCode = statusCode;
424
+ res.setHeader?.("Content-Type", "application/json; charset=utf-8");
425
+ res.end(JSON.stringify(body));
426
+ }
427
+ async loadHtmlFile(htmlFile) {
428
+ const absolutePath = path.resolve(this.projectRoot, htmlFile);
429
+ return readFile(absolutePath, "utf8");
430
+ }
431
+ async renderOAuthTemplate(res, template) {
432
+ const html = await this.loadHtmlFile(template.htmlFile);
433
+ this.sendHtml(res, 200, html);
434
+ }
435
+ sendHtml(res, statusCode, html) {
436
+ if (typeof res.status === "function" && typeof res.end === "function") {
437
+ res.status(statusCode);
438
+ }
439
+ res.statusCode = statusCode;
440
+ res.setHeader?.("Content-Type", "text/html; charset=utf-8");
441
+ res.end(html);
442
+ }
443
+ getOAuthConfig() {
444
+ const appId = this.options.applicationId ??
445
+ process.env.DISCORD_APPLICATION_ID ??
446
+ process.env.DISCORD_APP_ID;
447
+ const appSecret = process.env.DISCORD_CLIENT_SECRET ?? process.env.DISCORD_APPLICATION_SECRET;
448
+ const redirectUri = process.env.DISCORD_REDIRECT_URI;
449
+ if (!appId || !appSecret || !redirectUri) {
450
+ throw new Error("[MiniInteraction] Missing OAuth config. Expected DISCORD_APPLICATION_ID, DISCORD_CLIENT_SECRET and DISCORD_REDIRECT_URI.");
451
+ }
452
+ return { appId, appSecret, redirectUri };
453
+ }
454
+ getCookie(req, name) {
455
+ const cookieHeader = this.getHeader(req.headers, "cookie");
456
+ if (!cookieHeader)
457
+ return undefined;
458
+ for (const rawPart of cookieHeader.split(";")) {
459
+ const [rawKey, ...rawValue] = rawPart.trim().split("=");
460
+ if (rawKey === name) {
461
+ return decodeURIComponent(rawValue.join("="));
462
+ }
463
+ }
464
+ return undefined;
465
+ }
466
+ }
467
+ export const LegacyMiniInteractionAdapter = MiniInteraction;
package/dist/index.d.ts CHANGED
@@ -30,6 +30,8 @@ export { InteractionContext } from "./core/interactions/InteractionContext.js";
30
30
  export type { InteractionContextOptions } from "./core/interactions/InteractionContext.js";
31
31
  export { verifyAndParseInteraction } from "./core/interactions/InteractionVerifier.js";
32
32
  export { InteractionRouter } from "./router/InteractionRouter.js";
33
+ export { MiniInteraction, LegacyMiniInteractionAdapter, } from "./compat/MiniInteraction.js";
34
+ export type { MiniInteractionOptions } from "./compat/MiniInteraction.js";
33
35
  export type { APIRadioComponent, APIRadioOption } from "./types/radio.js";
34
36
  export { RADIO_COMPONENT_TYPE } from "./types/radio.js";
35
37
  export type { APICheckboxComponent, APICheckboxOption } from "./types/checkbox.js";
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ export { DiscordRestClient } from "./core/http/DiscordRestClient.js";
23
23
  export { InteractionContext } from "./core/interactions/InteractionContext.js";
24
24
  export { verifyAndParseInteraction } from "./core/interactions/InteractionVerifier.js";
25
25
  export { InteractionRouter } from "./router/InteractionRouter.js";
26
+ export { MiniInteraction, LegacyMiniInteractionAdapter, } from "./compat/MiniInteraction.js";
26
27
  export { RADIO_COMPONENT_TYPE } from "./types/radio.js";
27
28
  export { CHECKBOX_COMPONENT_TYPE } from "./types/checkbox.js";
28
29
  export { ValidationError } from "./types/validation.js";
@@ -4,7 +4,7 @@ import type { APICheckboxComponent } from "./checkbox.js";
4
4
  /** Defines a component structure for use in ActionRow builders. */
5
5
  export type ActionRowComponent = APIComponentInActionRow | APIRadioComponent | APICheckboxComponent;
6
6
  /** Defines a message component structure for use in message builders. */
7
- export type MessageActionRowComponent = APIComponentInMessageActionRow | APIRadioComponent | APICheckboxComponent;
7
+ export type MessageActionRowComponent = APIComponentInMessageActionRow;
8
8
  /** Structure for an action row containing mini-interaction components. */
9
9
  export interface MiniActionRow<T extends ActionRowComponent = ActionRowComponent> {
10
10
  type: 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minesa-org/mini-interaction",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
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",