@minesa-org/mini-interaction 0.1.6 → 0.1.7

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/README.md CHANGED
@@ -1,7 +1,3 @@
1
1
  # Mini Interaction
2
2
 
3
3
  Mini interaction, connecting your app with Discord via HTTP-interaction (Vercel support).
4
-
5
- - Read the [Discord OAuth Linked Roles setup guide](docs/discord-oauth-setup.md)
6
- for a step-by-step walkthrough that uses `mini.discordOAuthCallback()` and a
7
- MongoDB connection configured via `MONGODB_URI`.
@@ -113,6 +113,23 @@ export type DiscordOAuthCallbackOptions = {
113
113
  successRedirect?: string | ((context: DiscordOAuthAuthorizeContext) => string | null | undefined);
114
114
  templates?: Partial<DiscordOAuthCallbackTemplates>;
115
115
  };
116
+ /** Options accepted by {@link MiniInteraction.discordOAuthVerificationPage}. */
117
+ export type DiscordOAuthVerificationPageOptions = {
118
+ oauth?: OAuthConfig;
119
+ scopes?: string[];
120
+ /**
121
+ * Path to the HTML file to load. Relative paths resolve from {@link process.cwd}.
122
+ *
123
+ * @defaultValue "index.html"
124
+ */
125
+ htmlFile?: string;
126
+ /**
127
+ * Placeholder token (with or without the `{{ }}` wrapper) that will be replaced with the generated OAuth URL.
128
+ *
129
+ * @defaultValue "OAUTH_URL"
130
+ */
131
+ placeholder?: string;
132
+ };
116
133
  /**
117
134
  * Minimal interface describing a function capable of verifying Discord interaction signatures.
118
135
  */
@@ -123,7 +140,6 @@ type VerifyKeyFunction = (message: string | Uint8Array, signature: string, times
123
140
  export declare class MiniInteraction {
124
141
  readonly applicationId: string;
125
142
  readonly publicKey: string;
126
- private readonly baseUrl;
127
143
  private readonly fetchImpl;
128
144
  private readonly verifyKeyImpl;
129
145
  private readonly commandsDirectory;
@@ -213,25 +229,26 @@ export declare class MiniInteraction {
213
229
  */
214
230
  handleRequest(request: MiniInteractionRequest): Promise<MiniInteractionHandlerResult>;
215
231
  /**
216
- * Creates a Node.js style request handler that validates and processes interactions.
232
+ * Creates a Node.js style request handler compatible with Express, Next.js API routes,
233
+ * Vercel serverless functions, and any runtime that expects a `(req, res)` listener.
217
234
  */
218
235
  createNodeHandler(): MiniInteractionNodeHandler;
219
236
  /**
220
- * Alias for {@link createNodeHandler} for frameworks expecting a listener function.
221
- */
222
- createNodeListener(): MiniInteractionNodeHandler;
223
- /**
224
- * Convenience alias for {@link createNodeHandler} tailored to Vercel serverless functions.
237
+ * Generates a lightweight verification handler that serves an HTML page with an embedded OAuth link.
238
+ *
239
+ * This is primarily used when Discord asks for a verification URL while setting up Linked Roles.
240
+ * Provide an HTML file that contains the `{{OAUTH_URL}}` placeholder (or a custom placeholder defined in options)
241
+ * and this helper will replace the token with a freshly generated OAuth link on every request.
225
242
  */
226
- createVercelHandler(): MiniInteractionNodeHandler;
243
+ discordOAuthVerificationPage(options?: DiscordOAuthVerificationPageOptions): MiniInteractionNodeHandler;
227
244
  /**
228
245
  * Loads an HTML file and returns a success template that replaces useful placeholders.
229
246
  *
230
247
  * The following placeholders are available in the HTML file:
231
248
  * - `{{username}}`, `{{discriminator}}`, `{{user_id}}`, `{{user_tag}}`
232
249
  * - `{{access_token}}`, `{{refresh_token}}`, `{{token_type}}`, `{{scope}}`, `{{expires_at}}`
233
- * - `{{state}}`
234
- */
250
+ * - `{{state}}`
251
+ */
235
252
  connectedOAuthPage(filePath: string): DiscordOAuthCallbackTemplates["success"];
236
253
  /**
237
254
  * Loads an HTML file and returns an error template that can be reused for all failure cases.
@@ -249,6 +266,10 @@ export declare class MiniInteraction {
249
266
  * Replaces placeholder tokens in a template with escaped HTML values.
250
267
  */
251
268
  private renderHtmlTemplate;
269
+ /**
270
+ * Normalizes placeholder tokens to the bare key the HTML renderer expects.
271
+ */
272
+ private normalizeTemplateKey;
252
273
  /**
253
274
  * Creates a minimal Discord OAuth callback handler that renders helpful HTML responses.
254
275
  *
@@ -9,7 +9,7 @@ import { createCommandInteraction } from "../utils/CommandInteractionOptions.js"
9
9
  import { createMessageComponentInteraction, } from "../utils/MessageComponentInteraction.js";
10
10
  import { createModalSubmitInteraction, } from "../utils/ModalSubmitInteraction.js";
11
11
  import { createUserContextMenuInteraction, createMessageContextMenuInteraction, } from "../utils/ContextMenuInteraction.js";
12
- import { getOAuthTokens, getDiscordUser, } from "../oauth/DiscordOAuth.js";
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([
15
15
  ".js",
@@ -25,7 +25,6 @@ const SUPPORTED_MODULE_EXTENSIONS = new Set([
25
25
  export class MiniInteraction {
26
26
  applicationId;
27
27
  publicKey;
28
- baseUrl;
29
28
  fetchImpl;
30
29
  verifyKeyImpl;
31
30
  commandsDirectory;
@@ -54,7 +53,6 @@ export class MiniInteraction {
54
53
  }
55
54
  this.applicationId = applicationId;
56
55
  this.publicKey = publicKey;
57
- this.baseUrl = DISCORD_BASE_URL;
58
56
  this.fetchImpl = fetchImpl;
59
57
  this.verifyKeyImpl = verifyKeyImplementation ?? verifyKey;
60
58
  this.commandsDirectory =
@@ -271,7 +269,7 @@ export class MiniInteraction {
271
269
  if (!Array.isArray(resolvedCommands) || resolvedCommands.length === 0) {
272
270
  throw new Error("[MiniInteraction] commands must be a non-empty array payload");
273
271
  }
274
- const url = `${this.baseUrl}/applications/${this.applicationId}/commands`;
272
+ const url = `${DISCORD_BASE_URL}/applications/${this.applicationId}/commands`;
275
273
  const response = await this.fetchImpl(url, {
276
274
  method: "PUT",
277
275
  headers: {
@@ -299,7 +297,7 @@ export class MiniInteraction {
299
297
  if (!Array.isArray(metadata) || metadata.length === 0) {
300
298
  throw new Error("[MiniInteraction] metadata must be a non-empty array payload");
301
299
  }
302
- const url = `${this.baseUrl}/applications/${this.applicationId}/role-connections/metadata`;
300
+ const url = `${DISCORD_BASE_URL}/applications/${this.applicationId}/role-connections/metadata`;
303
301
  const response = await this.fetchImpl(url, {
304
302
  method: "PUT",
305
303
  headers: {
@@ -372,7 +370,8 @@ export class MiniInteraction {
372
370
  };
373
371
  }
374
372
  /**
375
- * Creates a Node.js style request handler that validates and processes interactions.
373
+ * Creates a Node.js style request handler compatible with Express, Next.js API routes,
374
+ * Vercel serverless functions, and any runtime that expects a `(req, res)` listener.
376
375
  */
377
376
  createNodeHandler() {
378
377
  return (request, response) => {
@@ -426,16 +425,35 @@ export class MiniInteraction {
426
425
  };
427
426
  }
428
427
  /**
429
- * Alias for {@link createNodeHandler} for frameworks expecting a listener function.
430
- */
431
- createNodeListener() {
432
- return this.createNodeHandler();
433
- }
434
- /**
435
- * Convenience alias for {@link createNodeHandler} tailored to Vercel serverless functions.
428
+ * Generates a lightweight verification handler that serves an HTML page with an embedded OAuth link.
429
+ *
430
+ * This is primarily used when Discord asks for a verification URL while setting up Linked Roles.
431
+ * Provide an HTML file that contains the `{{OAUTH_URL}}` placeholder (or a custom placeholder defined in options)
432
+ * and this helper will replace the token with a freshly generated OAuth link on every request.
436
433
  */
437
- createVercelHandler() {
438
- return this.createNodeHandler();
434
+ discordOAuthVerificationPage(options = {}) {
435
+ const scopes = options.scopes ?? ["identify", "role_connections.write"];
436
+ const htmlFile = options.htmlFile ?? "index.html";
437
+ const placeholderKey = this.normalizeTemplateKey(options.placeholder ?? "OAUTH_URL");
438
+ const template = this.loadHtmlTemplate(htmlFile);
439
+ const oauthConfig = resolveOAuthConfig(options.oauth);
440
+ return (_request, response) => {
441
+ try {
442
+ const { url, state } = generateOAuthUrl(oauthConfig, scopes);
443
+ const html = this.renderHtmlTemplate(template, {
444
+ OAUTH_URL: url,
445
+ OAUTH_STATE: state,
446
+ ...(placeholderKey !== "OAUTH_URL"
447
+ ? { [placeholderKey]: url }
448
+ : {}),
449
+ });
450
+ sendHtml(response, html);
451
+ }
452
+ catch (error) {
453
+ console.error("[MiniInteraction] Failed to render OAuth verification page:", error);
454
+ sendHtml(response, DEFAULT_VERIFICATION_ERROR_HTML, 500);
455
+ }
456
+ };
439
457
  }
440
458
  /**
441
459
  * Loads an HTML file and returns a success template that replaces useful placeholders.
@@ -443,8 +461,8 @@ export class MiniInteraction {
443
461
  * The following placeholders are available in the HTML file:
444
462
  * - `{{username}}`, `{{discriminator}}`, `{{user_id}}`, `{{user_tag}}`
445
463
  * - `{{access_token}}`, `{{refresh_token}}`, `{{token_type}}`, `{{scope}}`, `{{expires_at}}`
446
- * - `{{state}}`
447
- */
464
+ * - `{{state}}`
465
+ */
448
466
  connectedOAuthPage(filePath) {
449
467
  const template = this.loadHtmlTemplate(filePath);
450
468
  return ({ user, tokens, state }) => {
@@ -511,6 +529,20 @@ export class MiniInteraction {
511
529
  return escapeHtml(String(value));
512
530
  });
513
531
  }
532
+ /**
533
+ * Normalizes placeholder tokens to the bare key the HTML renderer expects.
534
+ */
535
+ normalizeTemplateKey(token) {
536
+ if (!token) {
537
+ return "OAUTH_URL";
538
+ }
539
+ const trimmed = token.trim();
540
+ const match = trimmed.match(/^\{\{\s*(\w+)\s*\}\}$/);
541
+ if (match) {
542
+ return match[1];
543
+ }
544
+ return trimmed || "OAUTH_URL";
545
+ }
514
546
  /**
515
547
  * Creates a minimal Discord OAuth callback handler that renders helpful HTML responses.
516
548
  *
@@ -1224,6 +1256,22 @@ const DEFAULT_DISCORD_OAUTH_TEMPLATES = {
1224
1256
  </body>
1225
1257
  </html>`,
1226
1258
  };
1259
+ const DEFAULT_VERIFICATION_ERROR_HTML = `<!DOCTYPE html>
1260
+ <html>
1261
+ <head>
1262
+ <meta charset="utf-8" />
1263
+ <title>Server Error</title>
1264
+ <style>
1265
+ body { font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; text-align: center; }
1266
+ .error { color: #d32f2f; background: #ffebee; padding: 15px; border-radius: 5px; display: inline-block; }
1267
+ </style>
1268
+ </head>
1269
+ <body>
1270
+ <div class="error">
1271
+ <p>We were unable to load the Discord verification page. Please try again later.</p>
1272
+ </div>
1273
+ </body>
1274
+ </html>`;
1227
1275
  function sendHtml(response, body, statusCode = 200) {
1228
1276
  if (response.headersSent || response.writableEnded) {
1229
1277
  return;
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ export type { AttachmentOptionBuilder, ChannelOptionBuilder, MentionableOptionBu
7
7
  export { CommandInteractionOptionResolver, createCommandInteraction, } from "./utils/CommandInteractionOptions.js";
8
8
  export type { CommandInteraction, MentionableOption, ResolvedUserOption, } from "./utils/CommandInteractionOptions.js";
9
9
  export type { UserContextMenuInteraction, MessageContextMenuInteraction, } from "./utils/ContextMenuInteraction.js";
10
- export type { MiniInteractionFetchHandler, MiniInteractionNodeHandler, MiniInteractionHandlerResult, MiniInteractionRequest, MiniInteractionOptions, DiscordOAuthAuthorizeContext, DiscordOAuthCallbackOptions, DiscordOAuthCallbackTemplates, DiscordOAuthErrorTemplateContext, DiscordOAuthServerErrorTemplateContext, DiscordOAuthStateTemplateContext, DiscordOAuthSuccessTemplateContext, } from "./clients/MiniInteraction.js";
10
+ export type { MiniInteractionFetchHandler, MiniInteractionNodeHandler, MiniInteractionHandlerResult, MiniInteractionRequest, MiniInteractionOptions, DiscordOAuthAuthorizeContext, DiscordOAuthCallbackOptions, DiscordOAuthCallbackTemplates, DiscordOAuthErrorTemplateContext, DiscordOAuthServerErrorTemplateContext, DiscordOAuthStateTemplateContext, DiscordOAuthSuccessTemplateContext, DiscordOAuthVerificationPageOptions, } from "./clients/MiniInteraction.js";
11
11
  export type { MiniInteractionCommand, SlashCommandHandler, UserCommandHandler, MessageCommandHandler, 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";
@@ -1,16 +1,2 @@
1
- import "dotenv/config";
2
- /** Discord application's public key used for request signature verification. */
3
- declare const DISCORD_APP_PUBLIC_KEY: string;
4
- /** Discord application identifier used for REST requests. */
5
- declare const DISCORD_APPLICATION_ID: string;
6
- /** Bot token used when registering commands against Discord's API. */
7
- declare const DISCORD_BOT_TOKEN: string;
8
- /** Guild identifier used for guild-scoped command registration. */
9
- declare const DISCORD_GUILD_ID: string;
10
- /** Whether commands should be registered globally instead of per guild. */
11
- declare const DISCORD_GLOBAL: boolean;
12
- /** Local development port for the example interaction server. */
13
- declare const DISCORD_APP_PORT: string;
14
1
  /** Discord REST API base URL used for all network requests. */
15
- declare const DISCORD_BASE_URL = "https://discord.com/api/v10";
16
- export { DISCORD_APPLICATION_ID, DISCORD_APP_PORT, DISCORD_APP_PUBLIC_KEY, DISCORD_BASE_URL, DISCORD_BOT_TOKEN, DISCORD_GLOBAL, DISCORD_GUILD_ID, };
2
+ export declare const DISCORD_BASE_URL: "https://discord.com/api/v10";
@@ -1,16 +1,2 @@
1
- import "dotenv/config";
2
- /** Discord application's public key used for request signature verification. */
3
- const DISCORD_APP_PUBLIC_KEY = process.env.DISCORD_APP_PUBLIC_KEY;
4
- /** Discord application identifier used for REST requests. */
5
- const DISCORD_APPLICATION_ID = process.env.DISCORD_APPLICATION_ID;
6
- /** Bot token used when registering commands against Discord's API. */
7
- const DISCORD_BOT_TOKEN = process.env.DISCORD_BOT_TOKEN;
8
- /** Guild identifier used for guild-scoped command registration. */
9
- const DISCORD_GUILD_ID = process.env.DISCORD_GUILD_ID;
10
- /** Whether commands should be registered globally instead of per guild. */
11
- const DISCORD_GLOBAL = (process.env.DISCORD_GLOBAL ?? "false").toLowerCase() === "true";
12
- /** Local development port for the example interaction server. */
13
- const DISCORD_APP_PORT = process.env.DISCORD_PORT;
14
1
  /** Discord REST API base URL used for all network requests. */
15
- const DISCORD_BASE_URL = "https://discord.com/api/v10";
16
- export { DISCORD_APPLICATION_ID, DISCORD_APP_PORT, DISCORD_APP_PUBLIC_KEY, DISCORD_BASE_URL, DISCORD_BOT_TOKEN, DISCORD_GLOBAL, DISCORD_GUILD_ID, };
2
+ export const DISCORD_BASE_URL = "https://discord.com/api/v10";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minesa-org/mini-interaction",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
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",
@@ -41,8 +41,7 @@
41
41
  "homepage": "https://github.com/minesa-org/mini-interaction#readme",
42
42
  "dependencies": {
43
43
  "discord-api-types": "^0.38.32",
44
- "discord-interactions": "^4.4.0",
45
- "dotenv": "^17.2.3"
44
+ "discord-interactions": "^4.4.0"
46
45
  },
47
46
  "devDependencies": {
48
47
  "@types/node": "^24.10.0",