From 1ede5603afc21f86f6099d01f2d45e6ba8aa92b1 Mon Sep 17 00:00:00 2001 From: Avraj Date: Fri, 22 Aug 2025 16:33:46 +0300 Subject: [PATCH 01/19] update middleware behavior --- .../app/commands/(developer)/+middleware.ts | 4 +- .../src/app/commands/(general)/+middleware.ts | 3 +- .../commands/(general)/+ping.middleware.ts | 6 +- .../src/app/commands/(general)/ping.ts | 7 +- .../commands/(interactions)/+middleware.ts | 4 +- .../src/app/commands/+global-middleware.ts | 4 +- .../events/{ready => clientReady}/logger.ts | 0 apps/test-bot/src/events/ready/test.ts | 2 +- .../02-setup-commandkit.mdx | 2 +- .../02-commands/06-category-directory.mdx | 2 +- .../docs/guide/02-commands/07-middlewares.mdx | 6 +- .../guide/03-events/01-discordjs-events.mdx | 19 +-- .../01-setup-commandkit-manually.mdx | 16 +-- .../08-advanced/04-migrating-from-v0.mdx | 8 +- .../guide/05-event-file-setup.mdx | 10 +- packages/commandkit/package.json | 111 +++++++++--------- .../src/app/commands/AppCommandRunner.ts | 82 ++++++++----- .../commandkit/src/app/commands/Context.ts | 57 ++++----- .../commandkit/src/app/interrupt/signals.ts | 10 +- .../src/app/middlewares/permissions.ts | 5 +- packages/commandkit/src/utils/error-codes.ts | 2 +- .../JavaScript/src/app/events/ready/log.js | 2 +- .../TypeScript/src/app/events/ready/log.ts | 2 +- packages/legacy/src/plugin.ts | 3 +- pnpm-workspace.yaml | 2 +- 25 files changed, 202 insertions(+), 167 deletions(-) rename apps/test-bot/src/app/events/{ready => clientReady}/logger.ts (100%) diff --git a/apps/test-bot/src/app/commands/(developer)/+middleware.ts b/apps/test-bot/src/app/commands/(developer)/+middleware.ts index ebd06ced..59d060d2 100644 --- a/apps/test-bot/src/app/commands/(developer)/+middleware.ts +++ b/apps/test-bot/src/app/commands/(developer)/+middleware.ts @@ -1,4 +1,4 @@ -import { Logger, MiddlewareContext } from 'commandkit'; +import { Logger, MiddlewareContext, stopMiddlewares } from 'commandkit'; import { MessageFlags } from 'discord.js'; export function beforeExecute(ctx: MiddlewareContext) { @@ -16,7 +16,7 @@ export function beforeExecute(ctx: MiddlewareContext) { ctx.message.reply('You are not allowed to use this command.'); } - ctx.cancel(); + stopMiddlewares(); } } diff --git a/apps/test-bot/src/app/commands/(general)/+middleware.ts b/apps/test-bot/src/app/commands/(general)/+middleware.ts index 089016cb..2e086989 100644 --- a/apps/test-bot/src/app/commands/(general)/+middleware.ts +++ b/apps/test-bot/src/app/commands/(general)/+middleware.ts @@ -1,9 +1,10 @@ -import { Logger, MiddlewareContext } from 'commandkit'; +import { Logger, MiddlewareContext, stopMiddlewares } from 'commandkit'; export function beforeExecute(ctx: MiddlewareContext) { Logger.info( `Directory-scoped middleware: ${ctx.commandName} will be executed!`, ); + // stopMiddlewares(); } export function afterExecute(ctx: MiddlewareContext) { diff --git a/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts b/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts index 7f5573c4..c1cdc30f 100644 --- a/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts +++ b/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts @@ -1,9 +1,11 @@ -import { Logger, MiddlewareContext } from 'commandkit'; +import { Logger, type MiddlewareContext, stopMiddlewares } from 'commandkit'; export function beforeExecute(ctx: MiddlewareContext) { + Logger.info(`Command-scoped middleware: ${ctx.commandName} will be stopped!`); Logger.info( - `Command-scoped middleware: ${ctx.commandName} will be executed!`, + 'None of the other beforeExecute middlewares are supposed to be executed', ); + // stopMiddlewares(); } export function afterExecute(ctx: MiddlewareContext) { diff --git a/apps/test-bot/src/app/commands/(general)/ping.ts b/apps/test-bot/src/app/commands/(general)/ping.ts index b2814a15..fb44ffee 100644 --- a/apps/test-bot/src/app/commands/(general)/ping.ts +++ b/apps/test-bot/src/app/commands/(general)/ping.ts @@ -11,6 +11,7 @@ import { AutocompleteCommandContext, CommandMetadata, MessageCommandContext, + stopMiddlewares, } from 'commandkit'; export const command: CommandData = { @@ -33,8 +34,8 @@ export const command: CommandData = { }; export const metadata: CommandMetadata = { - userPermissions: 'Administrator', - botPermissions: 'KickMembers', + // userPermissions: 'Administrator', + // botPermissions: 'KickMembers', // guilds: ['1314834483660455938'], }; @@ -98,4 +99,6 @@ export async function chatInput({ button.setDisabled(true); message.edit({ components: [row] }); }); + + stopMiddlewares(); } diff --git a/apps/test-bot/src/app/commands/(interactions)/+middleware.ts b/apps/test-bot/src/app/commands/(interactions)/+middleware.ts index 5521f325..f12a33d9 100644 --- a/apps/test-bot/src/app/commands/(interactions)/+middleware.ts +++ b/apps/test-bot/src/app/commands/(interactions)/+middleware.ts @@ -1,4 +1,4 @@ -import { Logger, MiddlewareContext } from 'commandkit'; +import { Logger, MiddlewareContext, stopMiddlewares } from 'commandkit'; import { MessageFlags } from 'discord.js'; export function beforeExecute(ctx: MiddlewareContext) { @@ -18,7 +18,7 @@ export function beforeExecute(ctx: MiddlewareContext) { ctx.message.reply('You are not allowed to use this command.'); } - ctx.cancel(); + stopMiddlewares(); } } diff --git a/apps/test-bot/src/app/commands/+global-middleware.ts b/apps/test-bot/src/app/commands/+global-middleware.ts index f0e02933..04b04573 100644 --- a/apps/test-bot/src/app/commands/+global-middleware.ts +++ b/apps/test-bot/src/app/commands/+global-middleware.ts @@ -1,7 +1,9 @@ -import { Logger, MiddlewareContext } from 'commandkit'; +import { Logger, MiddlewareContext, stopMiddlewares } from 'commandkit'; export function beforeExecute(ctx: MiddlewareContext) { Logger.info(`Global middleware: ${ctx.commandName} will be executed!`); + // ctx.message.reply('Global middleware: This command will not be executed!'); + // stopMiddlewares(); } export function afterExecute(ctx: MiddlewareContext) { diff --git a/apps/test-bot/src/app/events/ready/logger.ts b/apps/test-bot/src/app/events/clientReady/logger.ts similarity index 100% rename from apps/test-bot/src/app/events/ready/logger.ts rename to apps/test-bot/src/app/events/clientReady/logger.ts diff --git a/apps/test-bot/src/events/ready/test.ts b/apps/test-bot/src/events/ready/test.ts index fcae89e0..bb6971ae 100644 --- a/apps/test-bot/src/events/ready/test.ts +++ b/apps/test-bot/src/events/ready/test.ts @@ -2,7 +2,7 @@ import { type EventHandler, Logger } from 'commandkit'; export const once = true; -const handler: EventHandler<'ready'> = (client, c, commandkit) => { +const handler: EventHandler<'clientReady'> = (client, c, commandkit) => { Logger.log(`Ready from legacy event handler: ${client.user.username}`); }; diff --git a/apps/website/docs/guide/01-getting-started/02-setup-commandkit.mdx b/apps/website/docs/guide/01-getting-started/02-setup-commandkit.mdx index 61385b82..f98e10f8 100644 --- a/apps/website/docs/guide/01-getting-started/02-setup-commandkit.mdx +++ b/apps/website/docs/guide/01-getting-started/02-setup-commandkit.mdx @@ -37,7 +37,7 @@ that looks something like this: │ │ ├── commands/ │ │ │ └── ping.ts │ │ └── events/ -│ │ └── ready/ +│ │ └── clientReady/ │ │ └── log.ts │ └── app.ts ├── .env diff --git a/apps/website/docs/guide/02-commands/06-category-directory.mdx b/apps/website/docs/guide/02-commands/06-category-directory.mdx index e727384c..587f84ed 100644 --- a/apps/website/docs/guide/02-commands/06-category-directory.mdx +++ b/apps/website/docs/guide/02-commands/06-category-directory.mdx @@ -31,7 +31,7 @@ plugins, or even other commands through your `commandkit` instance. │ │ │ ├── kick.ts │ │ │ └── ban.ts │ │ └── events/ -│ │ └── ready/ +│ │ └── clientReady/ │ │ └── log.ts │ └── app.ts ├── .env diff --git a/apps/website/docs/guide/02-commands/07-middlewares.mdx b/apps/website/docs/guide/02-commands/07-middlewares.mdx index 6c22bb8a..bdac05be 100644 --- a/apps/website/docs/guide/02-commands/07-middlewares.mdx +++ b/apps/website/docs/guide/02-commands/07-middlewares.mdx @@ -36,8 +36,8 @@ export function afterExecute(ctx: MiddlewareContext) { ## Stop command execution -You can stop a command from running by returning `ctx.cancel()` in the -`beforeExecute` function. +You can stop a command from running by calling `stopMiddlewares()` in +the `beforeExecute` function. ```ts title="src/app/commands/+middleware.ts" import type { MiddlewareContext } from 'commandkit'; @@ -46,7 +46,7 @@ export function beforeExecute(ctx: MiddlewareContext) { if (ctx.interaction.user.id !== '1234567890') { // Conditionally stop command execution console.log(`${ctx.commandName} will not be executed!`); - return ctx.cancel(); + stopMiddlewares(); } // Continue with command execution diff --git a/apps/website/docs/guide/03-events/01-discordjs-events.mdx b/apps/website/docs/guide/03-events/01-discordjs-events.mdx index 8bb486e2..91352f39 100644 --- a/apps/website/docs/guide/03-events/01-discordjs-events.mdx +++ b/apps/website/docs/guide/03-events/01-discordjs-events.mdx @@ -19,7 +19,7 @@ Inside each event directory, you can create multiple handler files that will all execute when that event is triggered. Here's how your project structure might look with the -`"messageCreate"` and `"ready"` events: +`"messageCreate"` and `"clientReady"` events: ```title="" {4-9} . @@ -29,7 +29,7 @@ Here's how your project structure might look with the │ │ ├── messageCreate/ │ │ │ ├── give-xp.ts │ │ │ └── log-message.ts -│ │ └── ready/ +│ │ └── clientReady/ │ │ └── log.ts │ └── app.ts ├── .env @@ -50,12 +50,12 @@ You can see what events are available in Discord.js by checking the To create an event handler, create a folder inside the `src/app/events` directory which should match the event name from -Discord.js. This example will use the `"ready"` event. +Discord.js. This example will use the `"clientReady"` event. -```ts title="src/app/events/ready/log.ts" +```ts title="src/app/events/clientReady/log.ts" import type { EventHandler } from 'commandkit'; -const handler: EventHandler<'ready'> = (client) => { +const handler: EventHandler<'clientReady'> = (client) => { console.log(`🤖 ${client.user.displayName} is online!`); }; @@ -63,11 +63,12 @@ export default handler; ``` That's it! CommandKit will automatically detect this file and register -it as one of the handler functions for the `"ready"` event. +it as one of the handler functions for the `"clientReady"` event. Just like the Discord.js `client.on()` method, the parameters you receive in your event file will be based on what event you're trying -to handle. In the example above, the `"ready"` event should give you a +to handle. In the example above, the `"clientReady"` event should give +you a [`Client`](https://discord.js.org/docs/packages/discord.js/main/Client:Class) instance. @@ -86,12 +87,12 @@ definition. You may want to have some events to only get called once. For this, you can export a variable called `once` from your event function file. -```ts title="src/app/events/ready/log.ts" +```ts title="src/app/events/clientReady/log.ts" import type { EventHandler } from 'commandkit'; export const once = true; -const handler: EventHandler<'ready'> = (client) => { +const handler: EventHandler<'clientReady'> = (client) => { console.log(`🤖 ${client.user.displayName} is online!`); }; diff --git a/apps/website/docs/guide/08-advanced/01-setup-commandkit-manually.mdx b/apps/website/docs/guide/08-advanced/01-setup-commandkit-manually.mdx index 3951d40b..dfe319f2 100644 --- a/apps/website/docs/guide/08-advanced/01-setup-commandkit-manually.mdx +++ b/apps/website/docs/guide/08-advanced/01-setup-commandkit-manually.mdx @@ -114,16 +114,16 @@ Learn more To register and handle events emitted by your discord.js client, create a folder inside the `src/app` directory called `events` and create a folder with the name of the discord.js event you'd like to -handle (e.g. ready, messageCreate, etc). This example will use the -`ready` event. +handle (e.g. clientReady, messageCreate, etc). This example will use +the `clientReady` event. -In the `src/app/events/ready` directory, you can create files which -will export default functions that will be called when the respective -event is emitted by discord.js. Following the `ready` event example -mentioned above, you may want to log when your bot comes online. The -function for that will look like so: +In the `src/app/events/clientReady` directory, you can create files +which will export default functions that will be called when the +respective event is emitted by discord.js. Following the `clientReady` +event example mentioned above, you may want to log when your bot comes +online. The function for that will look like so: -```ts title="src/app/events/log.ts" +```ts title="src/app/events/clientReady/log.ts" import type { Client } from 'discord.js'; export default function (client: Client) { diff --git a/apps/website/docs/guide/08-advanced/04-migrating-from-v0.mdx b/apps/website/docs/guide/08-advanced/04-migrating-from-v0.mdx index 425f9980..a99bb0d2 100644 --- a/apps/website/docs/guide/08-advanced/04-migrating-from-v0.mdx +++ b/apps/website/docs/guide/08-advanced/04-migrating-from-v0.mdx @@ -58,7 +58,7 @@ enables advanced features like automatic route discovery. │ │ ├── commands/ │ │ │ └── ping.ts │ │ └── events/ - │ │ └── ready/ + │ │ └── clientReady/ │ │ └── log.ts │ └── app.ts ├── .env @@ -75,7 +75,7 @@ enables advanced features like automatic route discovery. │ ├── commands/ │ │ └── ping.ts │ ├── events/ - │ │ └── ready/ + │ │ └── clientReady/ │ │ └── log.ts │ └── index.ts ├── .env @@ -241,7 +241,7 @@ control over command execution. ```ts title='src/app/commands/+global-middleware.ts' - import type { MiddlewareContext } from 'commandkit'; + import type { MiddlewareContext, stopMiddlewares } from 'commandkit'; export function beforeExecute(ctx: MiddlewareContext) { // Example: Block command execution based on conditions @@ -249,7 +249,7 @@ control over command execution. ctx.interaction.reply('Access denied: Command execution blocked.'); } - ctx.cancel(); // Prevents command execution + stopMiddlewares(); // Prevents command execution } ``` diff --git a/apps/website/versioned_docs/version-0.1.10/guide/05-event-file-setup.mdx b/apps/website/versioned_docs/version-0.1.10/guide/05-event-file-setup.mdx index de584991..1cd8761b 100644 --- a/apps/website/versioned_docs/version-0.1.10/guide/05-event-file-setup.mdx +++ b/apps/website/versioned_docs/version-0.1.10/guide/05-event-file-setup.mdx @@ -8,25 +8,25 @@ import TabItem from '@theme/TabItem'; # Events Setup -This is a simple overview of how to set up a simple function that is called when the "ready" event is triggered. All this code does is log to the console when the bot is ready. +This is a simple overview of how to set up a simple function that is called when the "clientReady" event is triggered. All this code does is log to the console when the bot is ready. - ```js title="src/events/ready/console-log.js" + ```js title="src/events/clientReady/console-log.js" module.exports = (c, client, handler) => { console.log(`${c.user.username} is ready!`); }; ``` - ```js title="src/events/ready/console-log.js" + ```js title="src/events/clientReady/console-log.js" export default function (c, client, handler) { console.log(`${c.user.username} is ready!`); }; ``` - ```ts title="src/events/ready/console-log.ts" + ```ts title="src/events/clientReady/console-log.ts" import type { Client } from 'discord.js'; import type { CommandKit } from 'commandkit'; @@ -41,7 +41,7 @@ This is a simple overview of how to set up a simple function that is called when ## Parameters explained -The parameters might look a bit confusing at first, but they're actually quite simple. The first parameter `c` is the client object that was returned as a parameter when the "ready" event was triggered. The second parameter `client` is the Discord.js client that was instantiated in your main entry point file. Finally, the `handler` parameter is the current CommandKit instance. +The parameters might look a bit confusing at first, but they're actually quite simple. The first parameter `c` is the client object that was returned as a parameter when the "clientReady" event was triggered. The second parameter `client` is the Discord.js client that was instantiated in your main entry point file. Finally, the `handler` parameter is the current CommandKit instance. To better understand how the parameters work, here's another example but with the "messageCreate" event listener. diff --git a/packages/commandkit/package.json b/packages/commandkit/package.json index c113f02e..9ceefe07 100644 --- a/packages/commandkit/package.json +++ b/packages/commandkit/package.json @@ -54,75 +54,85 @@ "import": "./dist/index.js", "types": "./dist/index.d.ts" }, - "./jsx-runtime": { - "require": "./jsx-runtime.cjs", - "import": "./jsx-runtime.cjs", - "types": "./jsx-runtime.d.ts" + "./ai": { + "require": "./ai.cjs", + "import": "./ai.cjs", + "types": "./ai.d.ts" }, - "./hooks": { - "require": "./hooks.cjs", - "import": "./hooks.cjs", - "types": "./hooks.d.ts" + "./analytics": { + "require": "./analytics.cjs", + "import": "./analytics.cjs", + "types": "./analytics.d.ts" }, - "./plugin": { - "require": "./plugin.cjs", - "import": "./plugin.cjs", - "types": "./plugin.d.ts" + "./async-queue": { + "require": "./async-queue.cjs", + "import": "./async-queue.cjs", + "types": "./async-queue.d.ts" }, - "./config": { - "require": "./config.cjs", - "import": "./config.cjs", - "types": "./config.d.ts" + "./cache": { + "require": "./cache.cjs", + "import": "./cache.cjs", + "types": "./cache.d.ts" }, "./components": { "require": "./components.cjs", "import": "./components.cjs", "types": "./components.d.ts" }, + "./config": { + "require": "./config.cjs", + "import": "./config.cjs", + "types": "./config.d.ts" + }, + "./env": { + "require": "./env.cjs", + "import": "./env.cjs", + "types": "./env.d.ts" + }, + "./events": { + "require": "./events.cjs", + "import": "./events.cjs", + "types": "./events.d.ts" + }, "./flag": { "require": "./flag.cjs", "import": "./flag.cjs", "types": "./flag.d.ts" }, - "./cache": { - "require": "./cache.cjs", - "import": "./cache.cjs", - "types": "./cache.d.ts" + "./hooks": { + "require": "./hooks.cjs", + "import": "./hooks.cjs", + "types": "./hooks.d.ts" }, "./i18n": { "require": "./i18n.cjs", "import": "./i18n.cjs", "types": "./i18n.d.ts" }, + "./jsx-runtime": { + "require": "./jsx-runtime.cjs", + "import": "./jsx-runtime.cjs", + "types": "./jsx-runtime.d.ts" + }, + "./kv": { + "require": "./kv.cjs", + "import": "./kv.cjs", + "types": "./kv.d.ts" + }, "./logger": { "require": "./logger.cjs", "import": "./logger.cjs", "types": "./logger.d.ts" }, - "./events": { - "require": "./events.cjs", - "import": "./events.cjs", - "types": "./events.d.ts" - }, - "./analytics": { - "require": "./analytics.cjs", - "import": "./analytics.cjs", - "types": "./analytics.d.ts" - }, - "./ai": { - "require": "./ai.cjs", - "import": "./ai.cjs", - "types": "./ai.d.ts" - }, - "./env": { - "require": "./env.cjs", - "import": "./env.cjs", - "types": "./env.d.ts" + "./mutex": { + "require": "./mutex.cjs", + "import": "./mutex.cjs", + "types": "./mutex.d.ts" }, - "./async-queue": { - "require": "./async-queue.cjs", - "import": "./async-queue.cjs", - "types": "./async-queue.d.ts" + "./plugin": { + "require": "./plugin.cjs", + "import": "./plugin.cjs", + "types": "./plugin.d.ts" }, "./ratelimit": { "require": "./ratelimit.cjs", @@ -133,21 +143,10 @@ "require": "./semaphore.cjs", "import": "./semaphore.cjs", "types": "./semaphore.d.ts" - }, - "./mutex": { - "require": "./mutex.cjs", - "import": "./mutex.cjs", - "types": "./mutex.d.ts" - }, - "./kv": { - "require": "./kv.cjs", - "import": "./kv.cjs", - "types": "./kv.d.ts" } }, "scripts": { "check-types": "tsc --noEmit", - "dev": "pnpm run --filter=test-bot dev", "build": "tsdown", "test": "vitest" }, @@ -160,7 +159,9 @@ "keywords": [ "discord.js", "command handler", - "event handler" + "event handler", + "framework", + "discord.js 14" ], "dependencies": { "@rollup/plugin-json": "^6.1.0", diff --git a/packages/commandkit/src/app/commands/AppCommandRunner.ts b/packages/commandkit/src/app/commands/AppCommandRunner.ts index 784566f7..d44f3e7e 100644 --- a/packages/commandkit/src/app/commands/AppCommandRunner.ts +++ b/packages/commandkit/src/app/commands/AppCommandRunner.ts @@ -1,22 +1,22 @@ import { ChatInputCommandInteraction, Interaction, Message } from 'discord.js'; +import { AnalyticsEvents } from '../../analytics/constants'; +import { + makeContextAwareFunction, + provideContext, + useEnvironment, +} from '../../context/async-context'; import { CommandKitEnvironment, CommandKitEnvironmentType, } from '../../context/environment'; import { Logger } from '../../logger/Logger'; +import { CommandKitErrorCodes, isErrorType } from '../../utils/error-codes'; import { AppCommandHandler, PreparedAppCommandExecution, RunCommand, } from '../handlers/AppCommandHandler'; import { CommandExecutionMode, MiddlewareContext } from './Context'; -import { - makeContextAwareFunction, - provideContext, - useEnvironment, -} from '../../context/async-context'; -import { CommandKitErrorCodes, isErrorType } from '../../utils/error-codes'; -import { AnalyticsEvents } from '../../analytics/constants'; /** * Options for running a command in CommandKit. @@ -53,6 +53,7 @@ export class AppCommandRunner { * Handles the complete command lifecycle including before/after middleware execution. * @param prepared - The prepared command execution data * @param source - The source interaction or message that triggered the command + * @param options - The options for running the command */ public async runCommand( prepared: PreparedAppCommandExecution, @@ -74,7 +75,7 @@ export class AppCommandRunner { env.variables.set('execHandlerKind', executionMode); env.variables.set('customHandler', options?.handler ?? null); - const ctx = new MiddlewareContext(commandkit, { + const middlewareCtx = new MiddlewareContext(commandkit, { command: prepared.command, environment: env, executionMode, @@ -91,19 +92,25 @@ export class AppCommandRunner { messageCommandParser: prepared.messageCommandParser, }); - let middlewaresCanceled = false; + const beforeMiddlewares = prepared.middlewares.filter( + (m) => m.data.beforeExecute, + ); + + let beforeMiddlewaresStopped = false; // Run middleware before command execution - if (prepared.middlewares.length) { + if (beforeMiddlewares.length) { await provideContext(env, async () => { - for (const middleware of prepared.middlewares) { - if (!middleware.data.beforeExecute) continue; + for (const middleware of beforeMiddlewares) { try { - await middleware.data.beforeExecute(ctx); + await middleware.data.beforeExecute(middlewareCtx); } catch (e) { - if (isErrorType(e, CommandKitErrorCodes.ExitMiddleware)) { - middlewaresCanceled = true; - return; + if (isErrorType(e, CommandKitErrorCodes.StopMiddlewares)) { + beforeMiddlewaresStopped = true; + Logger.debug( + `Middleware propagation stopped for command "${middlewareCtx.commandName}". stopMiddlewares() was called inside a beforeExecute function at "${middleware.middleware.relativePath}"`, + ); + break; // Stop the middleware loop if `stopMiddlewares()` is called. } if ( @@ -123,8 +130,10 @@ export class AppCommandRunner { let result: any; - if (!ctx.cancelled) { - // Determine which function to run based on whether we're executing a command or subcommand + let stopMiddlewaresCalledInCmd = false; + + // If no `stopMiddlewares()` was called in a `beforeExecute` middleware, try to run the command + if (!beforeMiddlewaresStopped) { const targetData = prepared.command.data; const fn = targetData[options?.handler || executionMode]; @@ -190,7 +199,7 @@ export class AppCommandRunner { }); }); - return fn(ctx.clone()); + return fn(middlewareCtx.clone()); }, this.#finalizer.bind(this), ); @@ -216,14 +225,17 @@ export class AppCommandRunner { result = await executeCommand(); } } catch (e) { - if (isErrorType(e, CommandKitErrorCodes.ExitMiddleware)) { - middlewaresCanceled = true; + if (isErrorType(e, CommandKitErrorCodes.StopMiddlewares)) { + stopMiddlewaresCalledInCmd = true; + Logger.debug( + `Middleware propagation stopped for command "${middlewareCtx.commandName}". stopMiddlewares() was called by the command itself`, + ); } if ( !isErrorType(e, [ CommandKitErrorCodes.ForwardedCommand, - CommandKitErrorCodes.ExitMiddleware, + CommandKitErrorCodes.StopMiddlewares, ]) ) { if (shouldThrowOnError) { @@ -241,18 +253,28 @@ export class AppCommandRunner { }; } - // Run middleware after command execution - if (!middlewaresCanceled && prepared.middlewares.length) { + const afterMiddlewares = prepared.middlewares.filter( + (m) => m.data.afterExecute, + ); + + // Run middleware after command execution only if `stopMiddlewares()` wasn't + // called in either `beforeExecute` middleware or in the command itself + if ( + !beforeMiddlewaresStopped && + !stopMiddlewaresCalledInCmd && + afterMiddlewares.length + ) { await provideContext(env, async () => { - for (const middleware of prepared.middlewares) { - if (!middleware.data.afterExecute) continue; + for (const middleware of afterMiddlewares) { try { - await middleware.data.afterExecute(ctx); + await middleware.data.afterExecute(middlewareCtx); } catch (e) { - if (isErrorType(e, CommandKitErrorCodes.ExitMiddleware)) { - return; + if (isErrorType(e, CommandKitErrorCodes.StopMiddlewares)) { + Logger.debug( + `Middleware propagation stopped for command "${middlewareCtx.commandName}". stopMiddlewares() was called inside an afterExecute function at "${middleware.middleware.relativePath}"`, + ); + break; // Stop the afterExecute middleware loop if `stopMiddlewares()` is called. } - throw e; } } diff --git a/packages/commandkit/src/app/commands/Context.ts b/packages/commandkit/src/app/commands/Context.ts index 6d75744a..3ecabe5d 100644 --- a/packages/commandkit/src/app/commands/Context.ts +++ b/packages/commandkit/src/app/commands/Context.ts @@ -18,12 +18,13 @@ import { } from './MessageCommandParser'; import { CommandKitEnvironment } from '../../context/environment'; import { GenericFunction, getContext } from '../../context/async-context'; -import { exitMiddleware, redirect } from '../interrupt/signals'; +import { stopMiddlewares, redirect } from '../interrupt/signals'; import { LoadedCommand, ResolvableCommand, RunCommand, } from '../handlers/AppCommandHandler'; +import { CommandKitErrorCodes } from '../../utils/error-codes'; /** * Enumeration of different command execution modes supported by CommandKit. @@ -551,15 +552,15 @@ export class Context< return []; } - /** - * Stops upcoming middleware or current command execution. - * If this is called inside pre-stage middleware, the next run will be the actual command, skipping all other pre-stage middlewares. - * If this is called inside a command itself, it will skip all post-stage middlewares. - * If this is called inside post-stage middleware, it will skip all other post-stage middlewares. - */ - public exit(): never { - exitMiddleware(); - } + // /** + // * Stops upcoming middleware or current command execution. + // * If this is called inside pre-stage middleware, the next run will be the actual command, skipping all other pre-stage middlewares. + // * If this is called inside a command itself, it will skip all post-stage middlewares. + // * If this is called inside post-stage middleware, it will skip all other post-stage middlewares. + // */ + // public exit(): never { + // stopMiddlewares(); + // } } /** @@ -568,25 +569,27 @@ export class Context< export class MiddlewareContext< T extends CommandExecutionMode = CommandExecutionMode, > extends Context { - /** - * @private - * @internal - */ - #cancel = false; + // /** + // * @private + // * @internal + // */ + // #cancel = false; - /** - * Whether the command execution was cancelled. - */ - public get cancelled(): boolean { - return this.#cancel; - } + // /** + // * Whether the command execution was cancelled. + // */ + // public get cancelled(): boolean { + // return this.#cancel; + // } - /** - * Cancels the command execution. - */ - public cancel(): void { - this.#cancel = true; - } + // /** + // * Cancels upcoming middleware and command execution. + // * This will **not** stop any `after()` callbacks inside the command. + // */ + // public cancel(): void { + // this.#cancel = true; + // stopMiddlewares(); + // } /** * Sets command runner function to wrap the command execution. diff --git a/packages/commandkit/src/app/interrupt/signals.ts b/packages/commandkit/src/app/interrupt/signals.ts index f12200ab..8a9df5bd 100644 --- a/packages/commandkit/src/app/interrupt/signals.ts +++ b/packages/commandkit/src/app/interrupt/signals.ts @@ -6,13 +6,11 @@ import { import { eventWorkerContext } from '../events/EventWorkerContext'; /** - * Cancel upcoming middleware execution. - * If this is called inside pre-stage middleware, the next run will be the actual command, skipping all other pre-stage middlewares. - * If this is called inside a command itself, it will skip all post-stage middlewares. - * If this is called inside post-stage middleware, it will skip all other post-stage middlewares. + * Stop upcoming middleware and command execution. + * This will **not** stop any `after()` callbacks inside the command. */ -export function exitMiddleware(): never { - throw createCommandKitError(CommandKitErrorCodes.ExitMiddleware); +export function stopMiddlewares(): never { + throw createCommandKitError(CommandKitErrorCodes.StopMiddlewares); } /** diff --git a/packages/commandkit/src/app/middlewares/permissions.ts b/packages/commandkit/src/app/middlewares/permissions.ts index 668224fc..940894dd 100644 --- a/packages/commandkit/src/app/middlewares/permissions.ts +++ b/packages/commandkit/src/app/middlewares/permissions.ts @@ -2,6 +2,7 @@ import { EmbedBuilder, MessageFlags } from 'discord.js'; import { getConfig } from '../../config/config'; import { Logger } from '../../logger/Logger'; import { MiddlewareContext } from '../commands/Context'; +import { stopMiddlewares } from '../interrupt/signals'; export const middlewareId = crypto.randomUUID(); @@ -49,7 +50,7 @@ export async function beforeExecute(ctx: MiddlewareContext) { ); } - return ctx.cancel(); // Stop the command from executing + stopMiddlewares(); // Stop the command from executing } const userPermissions = @@ -159,5 +160,5 @@ export async function beforeExecute(ctx: MiddlewareContext) { ); } - return ctx.cancel(); // Stop the command from executing + stopMiddlewares(); // Stop the command from executing } diff --git a/packages/commandkit/src/utils/error-codes.ts b/packages/commandkit/src/utils/error-codes.ts index 1c823273..a3ffcaff 100644 --- a/packages/commandkit/src/utils/error-codes.ts +++ b/packages/commandkit/src/utils/error-codes.ts @@ -5,7 +5,7 @@ export const CommandKitErrorCodes = { /** * Error code for exiting middleware. */ - ExitMiddleware: Symbol('kExitMiddleware'), + StopMiddlewares: Symbol('kStopMiddlewares'), /** * Error code for forwarded commands. */ diff --git a/packages/create-commandkit/templates/JavaScript/src/app/events/ready/log.js b/packages/create-commandkit/templates/JavaScript/src/app/events/ready/log.js index 44f2cf81..abb2fe51 100644 --- a/packages/create-commandkit/templates/JavaScript/src/app/events/ready/log.js +++ b/packages/create-commandkit/templates/JavaScript/src/app/events/ready/log.js @@ -1,7 +1,7 @@ import { Logger } from 'commandkit/logger'; /** - * @type {import('commandkit').EventHandler<'ready'>} + * @type {import('commandkit').EventHandler<'clientReady'>} */ const handler = async (client) => { Logger.info(`Logged in as ${client.user.username}!`); diff --git a/packages/create-commandkit/templates/TypeScript/src/app/events/ready/log.ts b/packages/create-commandkit/templates/TypeScript/src/app/events/ready/log.ts index cde182ab..e1ff76c0 100644 --- a/packages/create-commandkit/templates/TypeScript/src/app/events/ready/log.ts +++ b/packages/create-commandkit/templates/TypeScript/src/app/events/ready/log.ts @@ -1,7 +1,7 @@ import type { EventHandler } from 'commandkit'; import { Logger } from 'commandkit/logger'; -const handler: EventHandler<'ready'> = async (client) => { +const handler: EventHandler<'clientReady'> = async (client) => { Logger.info(`Logged in as ${client.user.username}!`); }; diff --git a/packages/legacy/src/plugin.ts b/packages/legacy/src/plugin.ts index 1ec13e00..b92ecdbb 100644 --- a/packages/legacy/src/plugin.ts +++ b/packages/legacy/src/plugin.ts @@ -13,6 +13,7 @@ import { HMREventType, getSourceDirectories, CommandKitEventDispatch, + stopMiddlewares, } from 'commandkit'; import { join, resolve } from 'node:path'; import { loadLegacyValidations } from './loadLegacyValidations.js'; @@ -254,7 +255,7 @@ export class LegacyHandlerPlugin extends RuntimePlugin Date: Fri, 22 Aug 2025 16:59:14 +0300 Subject: [PATCH 02/19] fix: context not throwing ckit errors --- .../app/commands/(general)/+ping.middleware.ts | 8 ++++++-- apps/test-bot/src/app/commands/(general)/ping.ts | 6 ++++-- .../src/app/commands/AppCommandRunner.ts | 15 ++++----------- packages/commandkit/src/context/async-context.ts | 3 +++ 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts b/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts index c1cdc30f..8db7ade7 100644 --- a/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts +++ b/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts @@ -1,10 +1,14 @@ import { Logger, type MiddlewareContext, stopMiddlewares } from 'commandkit'; export function beforeExecute(ctx: MiddlewareContext) { - Logger.info(`Command-scoped middleware: ${ctx.commandName} will be stopped!`); Logger.info( - 'None of the other beforeExecute middlewares are supposed to be executed', + `Command-scoped middleware: ${ctx.commandName} will be executed!`, ); + + // Logger.info(`Command-scoped middleware: ${ctx.commandName} will be stopped!`); + // Logger.info( + // 'None of the other beforeExecute middlewares are supposed to be executed', + // ); // stopMiddlewares(); } diff --git a/apps/test-bot/src/app/commands/(general)/ping.ts b/apps/test-bot/src/app/commands/(general)/ping.ts index fb44ffee..ca46e560 100644 --- a/apps/test-bot/src/app/commands/(general)/ping.ts +++ b/apps/test-bot/src/app/commands/(general)/ping.ts @@ -12,6 +12,7 @@ import { CommandMetadata, MessageCommandContext, stopMiddlewares, + Logger, } from 'commandkit'; export const command: CommandData = { @@ -66,6 +67,9 @@ export async function chatInput({ interaction, client, }: ChatInputCommandContext) { + Logger.debug('calling stopMiddlewares'); + stopMiddlewares(); + if (!interaction.channel) return; const button = new ButtonKit() @@ -99,6 +103,4 @@ export async function chatInput({ button.setDisabled(true); message.edit({ components: [row] }); }); - - stopMiddlewares(); } diff --git a/packages/commandkit/src/app/commands/AppCommandRunner.ts b/packages/commandkit/src/app/commands/AppCommandRunner.ts index d44f3e7e..6405b43b 100644 --- a/packages/commandkit/src/app/commands/AppCommandRunner.ts +++ b/packages/commandkit/src/app/commands/AppCommandRunner.ts @@ -230,18 +230,10 @@ export class AppCommandRunner { Logger.debug( `Middleware propagation stopped for command "${middlewareCtx.commandName}". stopMiddlewares() was called by the command itself`, ); - } - - if ( - !isErrorType(e, [ - CommandKitErrorCodes.ForwardedCommand, - CommandKitErrorCodes.StopMiddlewares, - ]) - ) { + } else if (!isErrorType(e, CommandKitErrorCodes.ForwardedCommand)) { if (shouldThrowOnError) { throw e; } - Logger.error(e); } } @@ -249,7 +241,8 @@ export class AppCommandRunner { } else { result = { error: true, - message: 'Command execution was cancelled by the middleware.', + message: + 'Command execution was cancelled by a beforeExecute middleware.', }; } @@ -258,7 +251,7 @@ export class AppCommandRunner { ); // Run middleware after command execution only if `stopMiddlewares()` wasn't - // called in either `beforeExecute` middleware or in the command itself + // called in either `beforeExecute` middleware or in the command itself. if ( !beforeMiddlewaresStopped && !stopMiddlewaresCalledInCmd && diff --git a/packages/commandkit/src/context/async-context.ts b/packages/commandkit/src/context/async-context.ts index 3d8d113f..a7cc915a 100644 --- a/packages/commandkit/src/context/async-context.ts +++ b/packages/commandkit/src/context/async-context.ts @@ -70,6 +70,9 @@ export function makeContextAwareFunction< } catch (e) { if (!isCommandKitError(e)) { env.setExecutionError(e as Error); + } else { + // rethrow commandkit errors so they can be handled by the caller + throw e; } } finally { if (typeof finalizer === 'function') { From 351564a9eab731c3e1ee00062d11e91d7fb223ae Mon Sep 17 00:00:00 2001 From: Avraj Date: Fri, 22 Aug 2025 17:02:32 +0300 Subject: [PATCH 03/19] update legacy ready event to clientReady + prettier format --- apps/test-bot/src/app/commands/(general)/ping.ts | 6 +++--- apps/test-bot/src/events/{ready => clientReady}/test.ts | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename apps/test-bot/src/events/{ready => clientReady}/test.ts (100%) diff --git a/apps/test-bot/src/app/commands/(general)/ping.ts b/apps/test-bot/src/app/commands/(general)/ping.ts index ca46e560..a6710ffa 100644 --- a/apps/test-bot/src/app/commands/(general)/ping.ts +++ b/apps/test-bot/src/app/commands/(general)/ping.ts @@ -67,9 +67,6 @@ export async function chatInput({ interaction, client, }: ChatInputCommandContext) { - Logger.debug('calling stopMiddlewares'); - stopMiddlewares(); - if (!interaction.channel) return; const button = new ButtonKit() @@ -103,4 +100,7 @@ export async function chatInput({ button.setDisabled(true); message.edit({ components: [row] }); }); + + Logger.debug('calling stopMiddlewares'); + stopMiddlewares(); } diff --git a/apps/test-bot/src/events/ready/test.ts b/apps/test-bot/src/events/clientReady/test.ts similarity index 100% rename from apps/test-bot/src/events/ready/test.ts rename to apps/test-bot/src/events/clientReady/test.ts From c08aa9bce26568afc246627080fb6cc4a4b51fb3 Mon Sep 17 00:00:00 2001 From: Avraj Date: Fri, 22 Aug 2025 17:54:45 +0300 Subject: [PATCH 04/19] cleanup imports --- .../commands/(general)/+ping.middleware.ts | 14 +++++------ .../src/app/commands/(general)/ping.ts | 6 +++-- .../commandkit/src/app/commands/Context.ts | 25 +++++++++---------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts b/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts index 8db7ade7..f28ba287 100644 --- a/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts +++ b/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts @@ -1,15 +1,15 @@ import { Logger, type MiddlewareContext, stopMiddlewares } from 'commandkit'; export function beforeExecute(ctx: MiddlewareContext) { - Logger.info( - `Command-scoped middleware: ${ctx.commandName} will be executed!`, - ); - - // Logger.info(`Command-scoped middleware: ${ctx.commandName} will be stopped!`); // Logger.info( - // 'None of the other beforeExecute middlewares are supposed to be executed', + // `Command-scoped middleware: ${ctx.commandName} will be executed!`, // ); - // stopMiddlewares(); + + Logger.info(`Command-scoped middleware: ${ctx.commandName} will be stopped!`); + Logger.info( + 'None of the other beforeExecute middlewares are supposed to be executed', + ); + stopMiddlewares(); } export function afterExecute(ctx: MiddlewareContext) { diff --git a/apps/test-bot/src/app/commands/(general)/ping.ts b/apps/test-bot/src/app/commands/(general)/ping.ts index a6710ffa..6f8b56a6 100644 --- a/apps/test-bot/src/app/commands/(general)/ping.ts +++ b/apps/test-bot/src/app/commands/(general)/ping.ts @@ -13,6 +13,7 @@ import { MessageCommandContext, stopMiddlewares, Logger, + after, } from 'commandkit'; export const command: CommandData = { @@ -101,6 +102,7 @@ export async function chatInput({ message.edit({ components: [row] }); }); - Logger.debug('calling stopMiddlewares'); - stopMiddlewares(); + after(() => { + Logger.debug('after called in ping'); + }); } diff --git a/packages/commandkit/src/app/commands/Context.ts b/packages/commandkit/src/app/commands/Context.ts index 3ecabe5d..d5ccb831 100644 --- a/packages/commandkit/src/app/commands/Context.ts +++ b/packages/commandkit/src/app/commands/Context.ts @@ -1,30 +1,29 @@ import { AutocompleteInteraction, + Awaitable, ChatInputCommandInteraction, - MessageContextMenuCommandInteraction, - Message, - Locale, - Interaction, - UserContextMenuCommandInteraction, Client, - Awaitable, Guild, + Interaction, + Locale, + Message, + MessageContextMenuCommandInteraction, TextBasedChannel, + UserContextMenuCommandInteraction, } from 'discord.js'; import { CommandKit } from '../../commandkit'; -import { - MessageCommandOptions, - MessageCommandParser, -} from './MessageCommandParser'; -import { CommandKitEnvironment } from '../../context/environment'; import { GenericFunction, getContext } from '../../context/async-context'; -import { stopMiddlewares, redirect } from '../interrupt/signals'; +import { CommandKitEnvironment } from '../../context/environment'; import { LoadedCommand, ResolvableCommand, RunCommand, } from '../handlers/AppCommandHandler'; -import { CommandKitErrorCodes } from '../../utils/error-codes'; +import { redirect } from '../interrupt/signals'; +import { + MessageCommandOptions, + MessageCommandParser, +} from './MessageCommandParser'; /** * Enumeration of different command execution modes supported by CommandKit. From 64e6f6eb8161146fa6aa91124e1873843267e385 Mon Sep 17 00:00:00 2001 From: twlite <46562212+twlite@users.noreply.github.com> Date: Sun, 24 Aug 2025 10:51:57 +0545 Subject: [PATCH 05/19] chore: deps --- pnpm-lock.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9e93912b..16813286 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,7 +11,7 @@ catalogs: version: 5.9.2 discordjs: discord.js: - specifier: ^14.21.0 + specifier: ^14.22.1 version: 14.22.1 overrides: @@ -13983,7 +13983,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 24.3.0 + '@types/node': 22.17.2 '@types/ws@8.5.13': dependencies: From a25d29f94ac39c3bbf3690d6c0542b6a1ad0f2db Mon Sep 17 00:00:00 2001 From: twlite <46562212+twlite@users.noreply.github.com> Date: Sun, 24 Aug 2025 10:56:02 +0545 Subject: [PATCH 06/19] feat: force run env finalizer during runCommand phase --- .../src/app/commands/AppCommandRunner.ts | 340 +++++++++--------- 1 file changed, 174 insertions(+), 166 deletions(-) diff --git a/packages/commandkit/src/app/commands/AppCommandRunner.ts b/packages/commandkit/src/app/commands/AppCommandRunner.ts index 6405b43b..3246a20d 100644 --- a/packages/commandkit/src/app/commands/AppCommandRunner.ts +++ b/packages/commandkit/src/app/commands/AppCommandRunner.ts @@ -75,90 +75,113 @@ export class AppCommandRunner { env.variables.set('execHandlerKind', executionMode); env.variables.set('customHandler', options?.handler ?? null); - const middlewareCtx = new MiddlewareContext(commandkit, { - command: prepared.command, - environment: env, - executionMode, - interaction: !(source instanceof Message) - ? (source as ChatInputCommandInteraction) - : (null as never), - message: source instanceof Message ? source : (null as never), - forwarded: false, - customArgs: { - setCommandRunner: (fn: RunCommand) => { - runCommand = fn; + try { + const middlewareCtx = new MiddlewareContext(commandkit, { + command: prepared.command, + environment: env, + executionMode, + interaction: !(source instanceof Message) + ? (source as ChatInputCommandInteraction) + : (null as never), + message: source instanceof Message ? source : (null as never), + forwarded: false, + customArgs: { + setCommandRunner: (fn: RunCommand) => { + runCommand = fn; + }, }, - }, - messageCommandParser: prepared.messageCommandParser, - }); - - const beforeMiddlewares = prepared.middlewares.filter( - (m) => m.data.beforeExecute, - ); - - let beforeMiddlewaresStopped = false; + messageCommandParser: prepared.messageCommandParser, + }); - // Run middleware before command execution - if (beforeMiddlewares.length) { - await provideContext(env, async () => { - for (const middleware of beforeMiddlewares) { - try { - await middleware.data.beforeExecute(middlewareCtx); - } catch (e) { - if (isErrorType(e, CommandKitErrorCodes.StopMiddlewares)) { - beforeMiddlewaresStopped = true; - Logger.debug( - `Middleware propagation stopped for command "${middlewareCtx.commandName}". stopMiddlewares() was called inside a beforeExecute function at "${middleware.middleware.relativePath}"`, - ); - break; // Stop the middleware loop if `stopMiddlewares()` is called. - } + const beforeMiddlewares = prepared.middlewares.filter( + (m) => m.data.beforeExecute, + ); + + let beforeMiddlewaresStopped = false; + + // Run middleware before command execution + if (beforeMiddlewares.length) { + await provideContext(env, async () => { + for (const middleware of beforeMiddlewares) { + try { + await middleware.data.beforeExecute(middlewareCtx); + } catch (e) { + if (isErrorType(e, CommandKitErrorCodes.StopMiddlewares)) { + beforeMiddlewaresStopped = true; + Logger.debug( + `Middleware propagation stopped for command "${middlewareCtx.commandName}". stopMiddlewares() was called inside a beforeExecute function at "${middleware.middleware.relativePath}"`, + ); + break; // Stop the middleware loop if `stopMiddlewares()` is called. + } + + if ( + isErrorType(e, [ + CommandKitErrorCodes.ForwardedCommand, + CommandKitErrorCodes.InvalidCommandPrefix, + ]) + ) { + continue; + } - if ( - isErrorType(e, [ - CommandKitErrorCodes.ForwardedCommand, - CommandKitErrorCodes.InvalidCommandPrefix, - ]) - ) { - continue; + throw e; } - - throw e; } - } - }); - } + }); + } - let result: any; + let result: any; - let stopMiddlewaresCalledInCmd = false; + let stopMiddlewaresCalledInCmd = false; - // If no `stopMiddlewares()` was called in a `beforeExecute` middleware, try to run the command - if (!beforeMiddlewaresStopped) { - const targetData = prepared.command.data; - const fn = targetData[options?.handler || executionMode]; + // If no `stopMiddlewares()` was called in a `beforeExecute` middleware, try to run the command + if (!beforeMiddlewaresStopped) { + const targetData = prepared.command.data; + const fn = targetData[options?.handler || executionMode]; - if (!fn) { - Logger.warn( - `Command ${prepared.command.command.name} has no handler for ${executionMode}`, - ); - } + if (!fn) { + Logger.warn( + `Command ${prepared.command.command.name} has no handler for ${executionMode}`, + ); + } + + const analytics = commandkit.analytics; - const analytics = commandkit.analytics; - - if (fn) { - try { - const _executeCommand = makeContextAwareFunction( - env, - async () => { - env.registerDeferredFunction(async (env) => { - env.markEnd(); - const error = env.getExecutionError(); - const marker = env.getMarker(); - const time = `${env.getExecutionTime().toFixed(2)}ms`; - - if (error) { - Logger.error( - `[${marker} - ${time}] Error executing command: ${error.stack || error}`, + if (fn) { + try { + const _executeCommand = makeContextAwareFunction( + env, + async () => { + env.registerDeferredFunction(async (env) => { + env.markEnd(); + const error = env.getExecutionError(); + const marker = env.getMarker(); + const time = `${env.getExecutionTime().toFixed(2)}ms`; + + if (error) { + Logger.error( + `[${marker} - ${time}] Error executing command: ${error.stack || error}`, + ); + + const commandName = + prepared.command?.data?.command?.name ?? + prepared.command.command.name; + + await analytics.track({ + name: AnalyticsEvents.COMMAND_EXECUTION, + id: commandName, + data: { + error: true, + executionTime: env.getExecutionTime().toFixed(2), + type: executionMode, + command: commandName, + }, + }); + + return; + } + + Logger.info( + `[${marker} - ${time}] Command executed successfully`, ); const commandName = @@ -169,112 +192,95 @@ export class AppCommandRunner { name: AnalyticsEvents.COMMAND_EXECUTION, id: commandName, data: { - error: true, + error: false, executionTime: env.getExecutionTime().toFixed(2), type: executionMode, command: commandName, }, }); - - return; - } - - Logger.info( - `[${marker} - ${time}] Command executed successfully`, - ); - - const commandName = - prepared.command?.data?.command?.name ?? - prepared.command.command.name; - - await analytics.track({ - name: AnalyticsEvents.COMMAND_EXECUTION, - id: commandName, - data: { - error: false, - executionTime: env.getExecutionTime().toFixed(2), - type: executionMode, - command: commandName, - }, }); - }); - return fn(middlewareCtx.clone()); - }, - this.#finalizer.bind(this), - ); - - const executeCommand = - runCommand != null - ? (runCommand as RunCommand)(_executeCommand) - : _executeCommand; - - env.markStart(prepared.command.data.command.name); - - const res = await commandkit.plugins.execute(async (ctx, plugin) => { - return plugin.executeCommand( - ctx, - env, - source, - prepared, - executeCommand, + return fn(middlewareCtx.clone()); + }, + this.#finalizer.bind(this), ); - }); - if (!res) { - result = await executeCommand(); - } - } catch (e) { - if (isErrorType(e, CommandKitErrorCodes.StopMiddlewares)) { - stopMiddlewaresCalledInCmd = true; - Logger.debug( - `Middleware propagation stopped for command "${middlewareCtx.commandName}". stopMiddlewares() was called by the command itself`, + const executeCommand = + runCommand != null + ? (runCommand as RunCommand)(_executeCommand) + : _executeCommand; + + env.markStart(prepared.command.data.command.name); + + const res = await commandkit.plugins.execute( + async (ctx, plugin) => { + return plugin.executeCommand( + ctx, + env, + source, + prepared, + executeCommand, + ); + }, ); - } else if (!isErrorType(e, CommandKitErrorCodes.ForwardedCommand)) { - if (shouldThrowOnError) { - throw e; - } - Logger.error(e); - } - } - } - } else { - result = { - error: true, - message: - 'Command execution was cancelled by a beforeExecute middleware.', - }; - } - const afterMiddlewares = prepared.middlewares.filter( - (m) => m.data.afterExecute, - ); - - // Run middleware after command execution only if `stopMiddlewares()` wasn't - // called in either `beforeExecute` middleware or in the command itself. - if ( - !beforeMiddlewaresStopped && - !stopMiddlewaresCalledInCmd && - afterMiddlewares.length - ) { - await provideContext(env, async () => { - for (const middleware of afterMiddlewares) { - try { - await middleware.data.afterExecute(middlewareCtx); + if (!res) { + result = await executeCommand(); + } } catch (e) { if (isErrorType(e, CommandKitErrorCodes.StopMiddlewares)) { + stopMiddlewaresCalledInCmd = true; Logger.debug( - `Middleware propagation stopped for command "${middlewareCtx.commandName}". stopMiddlewares() was called inside an afterExecute function at "${middleware.middleware.relativePath}"`, + `Middleware propagation stopped for command "${middlewareCtx.commandName}". stopMiddlewares() was called by the command itself`, ); - break; // Stop the afterExecute middleware loop if `stopMiddlewares()` is called. + } else if (!isErrorType(e, CommandKitErrorCodes.ForwardedCommand)) { + if (shouldThrowOnError) { + throw e; + } + Logger.error(e); } - throw e; } } - }); - } + } else { + result = { + error: true, + message: + 'Command execution was cancelled by a beforeExecute middleware.', + }; + } - return result; + const afterMiddlewares = prepared.middlewares.filter( + (m) => m.data.afterExecute, + ); + + // Run middleware after command execution only if `stopMiddlewares()` wasn't + // called in either `beforeExecute` middleware or in the command itself. + if ( + !beforeMiddlewaresStopped && + !stopMiddlewaresCalledInCmd && + afterMiddlewares.length + ) { + await provideContext(env, async () => { + for (const middleware of afterMiddlewares) { + try { + await middleware.data.afterExecute(middlewareCtx); + } catch (e) { + if (isErrorType(e, CommandKitErrorCodes.StopMiddlewares)) { + Logger.debug( + `Middleware propagation stopped for command "${middlewareCtx.commandName}". stopMiddlewares() was called inside an afterExecute function at "${middleware.middleware.relativePath}"`, + ); + break; // Stop the afterExecute middleware loop if `stopMiddlewares()` is called. + } + throw e; + } + } + }); + } + + return result; + } finally { + await this.#finalizer(env, false); + } } /** @@ -282,17 +288,19 @@ export class AppCommandRunner { * @internal * Finalizes command execution by running deferred functions and plugin cleanup. */ - async #finalizer() { - const env = useEnvironment(); + async #finalizer(env?: CommandKitEnvironment, runPlugins = true) { + env ??= useEnvironment(); await env.runDeferredFunctions(); env.clearAllDeferredFunctions(); // plugins may have their own deferred function, useful for cleanup or post-command analytics - await this.handler.commandkit.plugins.execute(async (ctx, plugin) => { - await plugin.onAfterCommand(ctx, env); - }); + if (runPlugins) { + await this.handler.commandkit.plugins.execute(async (ctx, plugin) => { + await plugin.onAfterCommand(ctx, env); + }); + } } /** From 80b4bf4adea9f4f4076df8329723f03602096070 Mon Sep 17 00:00:00 2001 From: twlite <46562212+twlite@users.noreply.github.com> Date: Sun, 24 Aug 2025 11:21:10 +0545 Subject: [PATCH 07/19] chore: prettier --- .../commandkit/classes/context.mdx | 9 ------- .../commandkit/classes/middleware-context.mdx | 12 --------- .../commandkit/functions/exit-middleware.mdx | 25 ------------------- .../commandkit/functions/get-command-kit.mdx | 2 +- .../commandkit/functions/redirect.mdx | 2 +- .../commandkit/functions/rethrow.mdx | 2 +- .../commandkit/functions/stop-events.mdx | 2 +- .../commandkit/functions/stop-middlewares.mdx | 23 +++++++++++++++++ .../commandkit/functions/use-environment.mdx | 2 +- .../commandkit/variables/middleware-id.mdx | 2 +- .../legacy/classes/legacy-handler-plugin.mdx | 2 +- .../legacy-handler-plugin-options.mdx | 2 +- 12 files changed, 31 insertions(+), 54 deletions(-) delete mode 100644 apps/website/docs/api-reference/commandkit/functions/exit-middleware.mdx create mode 100644 apps/website/docs/api-reference/commandkit/functions/stop-middlewares.mdx diff --git a/apps/website/docs/api-reference/commandkit/classes/context.mdx b/apps/website/docs/api-reference/commandkit/classes/context.mdx index b379d969..99947654 100644 --- a/apps/website/docs/api-reference/commandkit/classes/context.mdx +++ b/apps/website/docs/api-reference/commandkit/classes/context.mdx @@ -49,7 +49,6 @@ class Context>) => Context; isMiddleware() => this is MiddlewareContext; args() => string[]; - exit() => never; } ``` @@ -201,14 +200,6 @@ Checks if this context is a middleware context. string[]`} /> Gets the command arguments (only available for message commands). -### exit - - never`} /> - -Stops upcoming middleware or current command execution. -If this is called inside pre-stage middleware, the next run will be the actual command, skipping all other pre-stage middlewares. -If this is called inside a command itself, it will skip all post-stage middlewares. -If this is called inside post-stage middleware, it will skip all other post-stage middlewares. diff --git a/apps/website/docs/api-reference/commandkit/classes/middleware-context.mdx b/apps/website/docs/api-reference/commandkit/classes/middleware-context.mdx index 6e0fb33a..c9b08166 100644 --- a/apps/website/docs/api-reference/commandkit/classes/middleware-context.mdx +++ b/apps/website/docs/api-reference/commandkit/classes/middleware-context.mdx @@ -19,8 +19,6 @@ Extended context class for middleware execution with additional control methods. ```ts title="Signature" class MiddlewareContext extends Context { - cancelled: boolean - cancel() => void; setCommandRunner(fn: RunCommand) => void; } ``` @@ -30,16 +28,6 @@ class MiddlewareContext e
-### cancelled - - - -Whether the command execution was cancelled. -### cancel - - void`} /> - -Cancels the command execution. ### setCommandRunner RunCommand) => void`} /> diff --git a/apps/website/docs/api-reference/commandkit/functions/exit-middleware.mdx b/apps/website/docs/api-reference/commandkit/functions/exit-middleware.mdx deleted file mode 100644 index 65882d98..00000000 --- a/apps/website/docs/api-reference/commandkit/functions/exit-middleware.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: "ExitMiddleware" -isDefaultIndex: false -generated: true ---- - -import MemberInfo from '@site/src/components/MemberInfo'; -import GenerationInfo from '@site/src/components/GenerationInfo'; -import MemberDescription from '@site/src/components/MemberDescription'; - - - - -## exitMiddleware - - - -Cancel upcoming middleware execution. -If this is called inside pre-stage middleware, the next run will be the actual command, skipping all other pre-stage middlewares. -If this is called inside a command itself, it will skip all post-stage middlewares. -If this is called inside post-stage middleware, it will skip all other post-stage middlewares. - -```ts title="Signature" -function exitMiddleware(): never -``` diff --git a/apps/website/docs/api-reference/commandkit/functions/get-command-kit.mdx b/apps/website/docs/api-reference/commandkit/functions/get-command-kit.mdx index 6e147f6b..9823dad1 100644 --- a/apps/website/docs/api-reference/commandkit/functions/get-command-kit.mdx +++ b/apps/website/docs/api-reference/commandkit/functions/get-command-kit.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## getCommandKit - + diff --git a/apps/website/docs/api-reference/commandkit/functions/redirect.mdx b/apps/website/docs/api-reference/commandkit/functions/redirect.mdx index b7a91a51..6e1605d0 100644 --- a/apps/website/docs/api-reference/commandkit/functions/redirect.mdx +++ b/apps/website/docs/api-reference/commandkit/functions/redirect.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## redirect - + Stops current command assuming it has been redirected to another command. diff --git a/apps/website/docs/api-reference/commandkit/functions/rethrow.mdx b/apps/website/docs/api-reference/commandkit/functions/rethrow.mdx index 60ab12d1..451a0993 100644 --- a/apps/website/docs/api-reference/commandkit/functions/rethrow.mdx +++ b/apps/website/docs/api-reference/commandkit/functions/rethrow.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## rethrow - + Rethrow the error if it is a CommandKit error. diff --git a/apps/website/docs/api-reference/commandkit/functions/stop-events.mdx b/apps/website/docs/api-reference/commandkit/functions/stop-events.mdx index 2e7994c8..69109bd4 100644 --- a/apps/website/docs/api-reference/commandkit/functions/stop-events.mdx +++ b/apps/website/docs/api-reference/commandkit/functions/stop-events.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## stopEvents - + Stops event propagation. This function should be called inside an event handler to prevent further event handling. diff --git a/apps/website/docs/api-reference/commandkit/functions/stop-middlewares.mdx b/apps/website/docs/api-reference/commandkit/functions/stop-middlewares.mdx new file mode 100644 index 00000000..07863a7e --- /dev/null +++ b/apps/website/docs/api-reference/commandkit/functions/stop-middlewares.mdx @@ -0,0 +1,23 @@ +--- +title: "StopMiddlewares" +isDefaultIndex: false +generated: true +--- + +import MemberInfo from '@site/src/components/MemberInfo'; +import GenerationInfo from '@site/src/components/GenerationInfo'; +import MemberDescription from '@site/src/components/MemberDescription'; + + + + +## stopMiddlewares + + + +Stop upcoming middleware and command execution. +This will **not** stop any `after()` callbacks inside the command. + +```ts title="Signature" +function stopMiddlewares(): never +``` diff --git a/apps/website/docs/api-reference/commandkit/functions/use-environment.mdx b/apps/website/docs/api-reference/commandkit/functions/use-environment.mdx index ce6df1b0..c052206e 100644 --- a/apps/website/docs/api-reference/commandkit/functions/use-environment.mdx +++ b/apps/website/docs/api-reference/commandkit/functions/use-environment.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## useEnvironment - + Use current commandkit context. Throws an error if no context is found. diff --git a/apps/website/docs/api-reference/commandkit/variables/middleware-id.mdx b/apps/website/docs/api-reference/commandkit/variables/middleware-id.mdx index 90b18e61..1873c9ea 100644 --- a/apps/website/docs/api-reference/commandkit/variables/middleware-id.mdx +++ b/apps/website/docs/api-reference/commandkit/variables/middleware-id.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## middlewareId - + diff --git a/apps/website/docs/api-reference/legacy/classes/legacy-handler-plugin.mdx b/apps/website/docs/api-reference/legacy/classes/legacy-handler-plugin.mdx index c2a6f4c8..fffff894 100644 --- a/apps/website/docs/api-reference/legacy/classes/legacy-handler-plugin.mdx +++ b/apps/website/docs/api-reference/legacy/classes/legacy-handler-plugin.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## LegacyHandlerPlugin - + diff --git a/apps/website/docs/api-reference/legacy/interfaces/legacy-handler-plugin-options.mdx b/apps/website/docs/api-reference/legacy/interfaces/legacy-handler-plugin-options.mdx index f52777ac..04505ff7 100644 --- a/apps/website/docs/api-reference/legacy/interfaces/legacy-handler-plugin-options.mdx +++ b/apps/website/docs/api-reference/legacy/interfaces/legacy-handler-plugin-options.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## LegacyHandlerPluginOptions - + Options for the LegacyHandlerPlugin. From 35e7a143444ed1ddd2ab02a78adca486d7c719a2 Mon Sep 17 00:00:00 2001 From: twlite <46562212+twlite@users.noreply.github.com> Date: Sun, 24 Aug 2025 11:33:02 +0545 Subject: [PATCH 08/19] fix: logging enhancements --- packages/ai/src/configure.ts | 4 ++-- .../src/analytics/analytics-engine.ts | 10 ++------- .../src/app/commands/AppCommandRunner.ts | 6 ++--- .../src/app/handlers/AppCommandHandler.ts | 10 ++++----- .../src/app/handlers/AppEventsHandler.ts | 22 ++++++------------- .../src/app/middlewares/permissions.ts | 10 ++------- .../src/app/register/CommandRegistrar.ts | 4 ++-- packages/commandkit/src/commandkit.ts | 6 ++--- .../commandkit/src/flags/feature-flags.ts | 4 +--- .../commandkit/src/logger/DefaultLogger.ts | 21 +++++++++++++----- .../plugin-runtime/CommandKitPluginRuntime.ts | 2 +- .../plugin-runtime/CompilerPluginRuntime.ts | 8 ++----- packages/devtools/src/server/app.ts | 4 ++-- 13 files changed, 45 insertions(+), 66 deletions(-) diff --git a/packages/ai/src/configure.ts b/packages/ai/src/configure.ts index 4673d81b..533b1b57 100644 --- a/packages/ai/src/configure.ts +++ b/packages/ai/src/configure.ts @@ -168,7 +168,7 @@ const AIConfig: Required = { } }, onError: async (_ctx, message, error) => { - Logger.error(`Error processing AI message: ${error}`); + Logger.error`Error processing AI message: ${error}`; const channel = message.channel as TextChannel; if (channel.isSendable()) { @@ -177,7 +177,7 @@ const AIConfig: Required = { content: 'An error occurred while processing your request.', allowedMentions: { parse: [] }, }) - .catch((e) => Logger.error(`Failed to send error message: ${e}`)); + .catch((e) => Logger.error`Failed to send error message: ${e}`); } }, }; diff --git a/packages/commandkit/src/analytics/analytics-engine.ts b/packages/commandkit/src/analytics/analytics-engine.ts index dc5de5f0..13c292aa 100644 --- a/packages/commandkit/src/analytics/analytics-engine.ts +++ b/packages/commandkit/src/analytics/analytics-engine.ts @@ -83,10 +83,7 @@ export class AnalyticsEngine { try { await this.#provider!.identify?.(this, event); } catch (error) { - Logger.error( - `Error identifying with provider ${this.#provider!.name}`, - error, - ); + Logger.error`Error identifying with provider ${this.#provider!.name} ${error}`; } } @@ -102,10 +99,7 @@ export class AnalyticsEngine { if (await this.#doNotTrack(event)) return; await this.#provider!.track(this, event); } catch (error) { - Logger.error( - `Error tracking ${event.name} event with provider ${this.#provider!.name}`, - error, - ); + Logger.error`Error tracking ${event.name} event with provider ${this.#provider!.name} ${error}`; } } diff --git a/packages/commandkit/src/app/commands/AppCommandRunner.ts b/packages/commandkit/src/app/commands/AppCommandRunner.ts index 3246a20d..4490ae04 100644 --- a/packages/commandkit/src/app/commands/AppCommandRunner.ts +++ b/packages/commandkit/src/app/commands/AppCommandRunner.ts @@ -158,9 +158,7 @@ export class AppCommandRunner { const time = `${env.getExecutionTime().toFixed(2)}ms`; if (error) { - Logger.error( - `[${marker} - ${time}] Error executing command: ${error.stack || error}`, - ); + Logger.error`[${marker} - ${time}] Error executing command: ${error}`; const commandName = prepared.command?.data?.command?.name ?? @@ -237,7 +235,7 @@ export class AppCommandRunner { if (shouldThrowOnError) { throw e; } - Logger.error(e); + Logger.error`${e}`; } } } diff --git a/packages/commandkit/src/app/handlers/AppCommandHandler.ts b/packages/commandkit/src/app/handlers/AppCommandHandler.ts index 2e17211d..a54b0656 100644 --- a/packages/commandkit/src/app/handlers/AppCommandHandler.ts +++ b/packages/commandkit/src/app/handlers/AppCommandHandler.ts @@ -462,9 +462,7 @@ export class AppCommandHandler { COMMANDKIT_IS_DEV && this.commandkit.config.showUnknownPrefixCommandsWarning ) { - Logger.error( - `Prefix command "${command}" was not found.\nNote: This warning is only shown in development mode as an alert to help you find the command. If you wish to remove this warning, set \`showUnknownPrefixCommandsWarning\` to \`false\` in your commandkit config.`, - ); + Logger.error`Prefix command "${command}" was not found.\nNote: This warning is only shown in development mode as an alert to help you find the command. If you wish to remove this warning, set \`showUnknownPrefixCommandsWarning\` to \`false\` in your commandkit config.`; } return null; } @@ -502,7 +500,7 @@ export class AppCommandHandler { if (isErrorType(e, CommandKitErrorCodes.InvalidCommandPrefix)) { return null; } - Logger.error(e); + Logger.error`${e}`; return null; } } else { @@ -729,7 +727,7 @@ export class AppCommandHandler { this.loadedMiddlewares.set(id, { middleware, data }); } catch (error) { - Logger.error(`Failed to load middleware ${id}`, error); + Logger.error`Failed to load middleware ${id}\n${error}`; } } @@ -865,7 +863,7 @@ export class AppCommandHandler { }, }); } catch (error) { - Logger.error(`Failed to load command ${command.name} (${id})`, error); + Logger.error`Failed to load command ${command.name} (${id})\n${error}`; } } diff --git a/packages/commandkit/src/app/handlers/AppEventsHandler.ts b/packages/commandkit/src/app/handlers/AppEventsHandler.ts index 9a99e511..b6b0ef61 100644 --- a/packages/commandkit/src/app/handlers/AppEventsHandler.ts +++ b/packages/commandkit/src/app/handlers/AppEventsHandler.ts @@ -105,9 +105,7 @@ export class AppEventsHandler { const handler = await import(toFileURL(listener, true)); if (!handler.default || typeof handler.default !== 'function') { - Logger.error( - `Event handler for ${event.event}${event.namespace ? ` of namespace ${event.namespace}` : ''} does not have a default export or is not a function`, - ); + Logger.error`Event handler for ${event.event}${event.namespace ? ` of namespace ${event.namespace}` : ''} does not have a default export or is not a function`; } listeners.push({ @@ -217,12 +215,9 @@ export class AppEventsHandler { } // Otherwise log the error as usual - Logger.error( - `Error handling event ${name}${ - namespace ? ` of namespace ${namespace}` : '' - }`, - e, - ); + Logger.error`Error handling event ${name}${ + namespace ? ` of namespace ${namespace}` : '' + } ${e}`; } } }, @@ -284,12 +279,9 @@ export class AppEventsHandler { } // Otherwise log the error as usual - Logger.error( - `Error handling event ${name}${ - namespace ? ` of namespace ${namespace}` : '' - }`, - e, - ); + Logger.error`Error handling event ${name}${ + namespace ? ` of namespace ${namespace}` : '' + } ${e}`; } }, ); diff --git a/packages/commandkit/src/app/middlewares/permissions.ts b/packages/commandkit/src/app/middlewares/permissions.ts index 940894dd..78a95e88 100644 --- a/packages/commandkit/src/app/middlewares/permissions.ts +++ b/packages/commandkit/src/app/middlewares/permissions.ts @@ -44,10 +44,7 @@ export async function beforeExecute(ctx: MiddlewareContext) { } } } catch (error) { - Logger.error( - `Could not send 'Server-only command' DM to user ${interaction?.user.id ?? message?.author.id} for command ${command.command.name}.`, - error, - ); + Logger.error`Could not send 'Server-only command' DM to user ${interaction?.user.id ?? message?.author.id} for command ${command.command.name}. ${error}`; } stopMiddlewares(); // Stop the command from executing @@ -154,10 +151,7 @@ export async function beforeExecute(ctx: MiddlewareContext) { }); } } catch (error) { - Logger.error( - `Could not send 'Not enough permissions' reply to user ${interaction?.user.id ?? message?.author.id} for command ${command.command.name}.`, - error, - ); + Logger.error`Could not send 'Not enough permissions' reply to user ${interaction?.user.id ?? message?.author.id} for command ${command.command.name}. ${error}`; } stopMiddlewares(); // Stop the command from executing diff --git a/packages/commandkit/src/app/register/CommandRegistrar.ts b/packages/commandkit/src/app/register/CommandRegistrar.ts index 10349ce0..dccf015a 100644 --- a/packages/commandkit/src/app/register/CommandRegistrar.ts +++ b/packages/commandkit/src/app/register/CommandRegistrar.ts @@ -172,7 +172,7 @@ export class CommandRegistrar { `✨ Refreshed ${data.length} global application (/) commands`, ); } catch (e) { - Logger.error('Failed to update global application (/) commands', e); + Logger.error`Failed to update global application (/) commands ${e}`; } } @@ -253,7 +253,7 @@ export class CommandRegistrar { Logger.info(`✨ Refreshed ${count} guild application (/) commands`); } catch (e) { - Logger.error('Failed to update guild application (/) commands', e); + Logger.error`Failed to update guild application (/) commands ${e}`; } } } diff --git a/packages/commandkit/src/commandkit.ts b/packages/commandkit/src/commandkit.ts index 509e1e2a..a4a699ff 100644 --- a/packages/commandkit/src/commandkit.ts +++ b/packages/commandkit/src/commandkit.ts @@ -207,7 +207,7 @@ export class CommandKit extends EventEmitter { try { await hook(this); } catch (e) { - Logger.error('Error while executing bootstrap hook: ', e); + Logger.error`Error while executing bootstrap hook: ${e}`; } finally { bootstrapHooks.delete(hook); } @@ -222,7 +222,7 @@ export class CommandKit extends EventEmitter { try { await hook(this); } catch (e) { - Logger.error('Error while executing application bootstrap hook: ', e); + Logger.error`Error while executing application bootstrap hook: ${e}`; } finally { onApplicationBootstrapHooks.delete(hook); } @@ -254,7 +254,7 @@ export class CommandKit extends EventEmitter { } catch (e) { // ignore if (process.env.COMMANDKIT_DEBUG_TYPEGEN) { - Logger.error(e); + Logger.error`${e}`; } } } diff --git a/packages/commandkit/src/flags/feature-flags.ts b/packages/commandkit/src/flags/feature-flags.ts index 3291fee5..a94f1f13 100644 --- a/packages/commandkit/src/flags/feature-flags.ts +++ b/packages/commandkit/src/flags/feature-flags.ts @@ -352,9 +352,7 @@ export class FeatureFlag { } } } catch (error) { - Logger.error( - `Error fetching flag provider configuration for "${this.options.key}": ${error}`, - ); + Logger.error`Error fetching flag provider configuration for "${this.options.key}": ${error}`; // continue with local decision if provider fails } } diff --git a/packages/commandkit/src/logger/DefaultLogger.ts b/packages/commandkit/src/logger/DefaultLogger.ts index c791573d..8ce74a74 100644 --- a/packages/commandkit/src/logger/DefaultLogger.ts +++ b/packages/commandkit/src/logger/DefaultLogger.ts @@ -139,15 +139,20 @@ export class DefaultLogger implements ILogger { const context = this._getContext(); const colorFn = TextColorMap[level]; + let processedMessage = message; + if (message instanceof Error) { + processedMessage = `${message.message}\n${message.stack}`; + } + if (context) { this.logger.log( `${prefix}\n${context} ${colors.dim(BoxChars.corner)}`, - colorFn(message), + colorFn(processedMessage), ); } else { this.logger.log( `${prefix} ${colors.dim(BoxChars.corner)}`, - colorFn(message), + colorFn(processedMessage), ); } } @@ -165,10 +170,14 @@ export class DefaultLogger implements ILogger { for (let i = 0; i < strings.length; i++) { result += strings[i]; if (i < values.length) { - result += inspect(values[i], { - colors: COMMANDKIT_IS_DEV, - depth: 2, - }); + const value = values[i]; + if (value instanceof Error) { + result += `${value.message}\n${value.stack}`; + } else if (value !== null && typeof value === 'object') { + result += inspect(value, { colors: true, depth: 2 }); + } else { + result += value; + } } } diff --git a/packages/commandkit/src/plugins/plugin-runtime/CommandKitPluginRuntime.ts b/packages/commandkit/src/plugins/plugin-runtime/CommandKitPluginRuntime.ts index 34d315ff..be968fee 100644 --- a/packages/commandkit/src/plugins/plugin-runtime/CommandKitPluginRuntime.ts +++ b/packages/commandkit/src/plugins/plugin-runtime/CommandKitPluginRuntime.ts @@ -144,7 +144,7 @@ export class CommandKitPluginRuntime { return true; } - Logger.error(`Plugin "${plugin.name}" failed`, e?.stack || e); + Logger.error`Plugin "${plugin.name}" failed ${e}`; } } diff --git a/packages/commandkit/src/plugins/plugin-runtime/CompilerPluginRuntime.ts b/packages/commandkit/src/plugins/plugin-runtime/CompilerPluginRuntime.ts index a24a34af..b6a25b53 100644 --- a/packages/commandkit/src/plugins/plugin-runtime/CompilerPluginRuntime.ts +++ b/packages/commandkit/src/plugins/plugin-runtime/CompilerPluginRuntime.ts @@ -209,9 +209,7 @@ export class CompilerPluginRuntime { }, ); } catch (e: any) { - console.error( - `Plugin ${plugin.name} failed to activate with ${e?.stack || e}`, - ); + Logger.error`Plugin ${plugin.name} failed to activate with ${e}`; } } @@ -237,9 +235,7 @@ export class CompilerPluginRuntime { }, ); } catch (e: any) { - console.error( - `Plugin ${plugin.name} failed to deactivate with ${e?.stack || e}`, - ); + Logger.error`Plugin ${plugin.name} failed to deactivate with ${e}`; } } diff --git a/packages/devtools/src/server/app.ts b/packages/devtools/src/server/app.ts index 2389713d..78a8d396 100644 --- a/packages/devtools/src/server/app.ts +++ b/packages/devtools/src/server/app.ts @@ -48,12 +48,12 @@ export async function startServer( attemptToListen(); } else { // For other errors or if we've exceeded max retries - Logger.error('Server failed to start:', error); + Logger.error`Server failed to start: ${error}`; reject(error); } }); } catch (error) { - Logger.error('Unexpected error starting server:', error); + Logger.error`Unexpected error starting server: ${error}`; reject(error); } } From 6e040120462937dd3c27632fa1962bfb27d83620 Mon Sep 17 00:00:00 2001 From: twlite <46562212+twlite@users.noreply.github.com> Date: Sun, 24 Aug 2025 11:38:55 +0545 Subject: [PATCH 09/19] chore: add after() logging in command-scoped middleware --- .../src/app/commands/(general)/+ping.middleware.ts | 12 +++++++++++- packages/cache/src/use-cache-directive.ts | 2 -- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts b/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts index f28ba287..b59f2f26 100644 --- a/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts +++ b/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts @@ -1,4 +1,9 @@ -import { Logger, type MiddlewareContext, stopMiddlewares } from 'commandkit'; +import { + after, + Logger, + type MiddlewareContext, + stopMiddlewares, +} from 'commandkit'; export function beforeExecute(ctx: MiddlewareContext) { // Logger.info( @@ -9,6 +14,11 @@ export function beforeExecute(ctx: MiddlewareContext) { Logger.info( 'None of the other beforeExecute middlewares are supposed to be executed', ); + + after(() => { + Logger.info(`after() has been called in command-scoped middleware: ping`); + }); + stopMiddlewares(); } diff --git a/packages/cache/src/use-cache-directive.ts b/packages/cache/src/use-cache-directive.ts index c0910148..328795ca 100644 --- a/packages/cache/src/use-cache-directive.ts +++ b/packages/cache/src/use-cache-directive.ts @@ -2,7 +2,6 @@ import { CommonDirectiveTransformer, CommonDirectiveTransformerOptions, CompilerPluginRuntime, - Logger, } from 'commandkit'; /** @@ -26,6 +25,5 @@ export class UseCacheDirectivePlugin extends CommonDirectiveTransformer { public async activate(ctx: CompilerPluginRuntime): Promise { super.activate(ctx); - Logger.info('"use cache" directive compiler plugin activated'); } } From 1ee01fcf1273a9a6a795f4bdba7305e763ea7347 Mon Sep 17 00:00:00 2001 From: Avraj Date: Mon, 25 Aug 2025 13:10:44 +0300 Subject: [PATCH 10/19] mark `after` as unstable --- apps/test-bot/package.json | 2 +- .../src/app/commands/(developer)/run-after.ts | 6 +- .../commands/(general)/+ping.middleware.ts | 2 +- .../src/app/commands/(general)/ping.ts | 2 +- .../commands/(interactions)/confirmation.tsx | 4 +- .../src/app/commands/(leveling)/xp.ts | 2 +- .../commandkit/src/app/commands/Context.ts | 22 -- .../commandkit/src/app/interrupt/signals.ts | 3 +- packages/commandkit/src/index.ts | 8 +- pnpm-lock.yaml | 189 +++++++++--------- 10 files changed, 116 insertions(+), 124 deletions(-) diff --git a/apps/test-bot/package.json b/apps/test-bot/package.json index 8ee571c8..11710273 100644 --- a/apps/test-bot/package.json +++ b/apps/test-bot/package.json @@ -21,7 +21,7 @@ "discord.js": "catalog:discordjs", "dotenv": "^16.4.7", "ms": "^2.1.3", - "zod": "^3.25.56" + "zod": "^4.1.1" }, "devDependencies": { "@types/ms": "^2.1.0", diff --git a/apps/test-bot/src/app/commands/(developer)/run-after.ts b/apps/test-bot/src/app/commands/(developer)/run-after.ts index 81705cb6..30ff3703 100644 --- a/apps/test-bot/src/app/commands/(developer)/run-after.ts +++ b/apps/test-bot/src/app/commands/(developer)/run-after.ts @@ -1,4 +1,8 @@ -import { CommandData, after, ChatInputCommand } from 'commandkit'; +import { + CommandData, + unstable_after as after, + ChatInputCommand, +} from 'commandkit'; export const command: CommandData = { name: 'run-after', diff --git a/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts b/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts index b59f2f26..facbe09a 100644 --- a/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts +++ b/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts @@ -1,5 +1,5 @@ import { - after, + unstable_after as after, Logger, type MiddlewareContext, stopMiddlewares, diff --git a/apps/test-bot/src/app/commands/(general)/ping.ts b/apps/test-bot/src/app/commands/(general)/ping.ts index 6f8b56a6..20d4a965 100644 --- a/apps/test-bot/src/app/commands/(general)/ping.ts +++ b/apps/test-bot/src/app/commands/(general)/ping.ts @@ -13,7 +13,7 @@ import { MessageCommandContext, stopMiddlewares, Logger, - after, + unstable_after as after, } from 'commandkit'; export const command: CommandData = { diff --git a/apps/test-bot/src/app/commands/(interactions)/confirmation.tsx b/apps/test-bot/src/app/commands/(interactions)/confirmation.tsx index 34988285..d19abbd2 100644 --- a/apps/test-bot/src/app/commands/(interactions)/confirmation.tsx +++ b/apps/test-bot/src/app/commands/(interactions)/confirmation.tsx @@ -6,7 +6,7 @@ import { OnButtonKitClick, } from 'commandkit'; import { ButtonStyle, MessageFlags } from 'discord.js'; -import { AiConfig, AiCommand } from '@commandkit/ai'; +import { AiConfig, AiCommand, ToolParameterType } from '@commandkit/ai'; import { z } from 'zod'; export const command: CommandData = { @@ -15,7 +15,7 @@ export const command: CommandData = { }; export const aiConfig = { - parameters: z.object({ + inputSchema: z.object({ message: z .string() .describe('The message to be shown in the confirmation.'), diff --git a/apps/test-bot/src/app/commands/(leveling)/xp.ts b/apps/test-bot/src/app/commands/(leveling)/xp.ts index 441e03c1..8d0d19e5 100644 --- a/apps/test-bot/src/app/commands/(leveling)/xp.ts +++ b/apps/test-bot/src/app/commands/(leveling)/xp.ts @@ -11,7 +11,7 @@ export const command: CommandData = { export const aiConfig = { description: 'Get the XP of a user in a guild.', - parameters: z.object({ + inputSchema: z.object({ guildId: z.string().describe('The ID of the guild.'), userId: z.string().describe('The ID of the user.'), }), diff --git a/packages/commandkit/src/app/commands/Context.ts b/packages/commandkit/src/app/commands/Context.ts index d5ccb831..df5b1be0 100644 --- a/packages/commandkit/src/app/commands/Context.ts +++ b/packages/commandkit/src/app/commands/Context.ts @@ -568,28 +568,6 @@ export class Context< export class MiddlewareContext< T extends CommandExecutionMode = CommandExecutionMode, > extends Context { - // /** - // * @private - // * @internal - // */ - // #cancel = false; - - // /** - // * Whether the command execution was cancelled. - // */ - // public get cancelled(): boolean { - // return this.#cancel; - // } - - // /** - // * Cancels upcoming middleware and command execution. - // * This will **not** stop any `after()` callbacks inside the command. - // */ - // public cancel(): void { - // this.#cancel = true; - // stopMiddlewares(); - // } - /** * Sets command runner function to wrap the command execution. * @param fn The function to set. diff --git a/packages/commandkit/src/app/interrupt/signals.ts b/packages/commandkit/src/app/interrupt/signals.ts index 8a9df5bd..eba195c5 100644 --- a/packages/commandkit/src/app/interrupt/signals.ts +++ b/packages/commandkit/src/app/interrupt/signals.ts @@ -6,8 +6,7 @@ import { import { eventWorkerContext } from '../events/EventWorkerContext'; /** - * Stop upcoming middleware and command execution. - * This will **not** stop any `after()` callbacks inside the command. + * Stop upcoming middlewares and command execution. */ export function stopMiddlewares(): never { throw createCommandKitError(CommandKitErrorCodes.StopMiddlewares); diff --git a/packages/commandkit/src/index.ts b/packages/commandkit/src/index.ts index b7165166..8c668d0f 100644 --- a/packages/commandkit/src/index.ts +++ b/packages/commandkit/src/index.ts @@ -6,7 +6,13 @@ export * from './commandkit'; export * from './components'; export * from './config/config'; export * from './context/async-context'; -export * from './context/environment'; +export { + type CommandKitEnvironmentInternalData, + CommandKitEnvironment, + CommandKitEnvironmentType, + cancelAfter, + after as unstable_after, +} from './context/environment'; export * from './app/index'; export * from './logger/DefaultLogger'; export * from './logger/ILogger'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 16813286..da6c833e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -77,7 +77,7 @@ importers: dependencies: '@ai-sdk/google': specifier: ^2.0.8 - version: 2.0.8(zod@3.25.76) + version: 2.0.8(zod@4.1.1) '@commandkit/ai': specifier: workspace:* version: link:../../packages/ai @@ -109,8 +109,8 @@ importers: specifier: ^2.1.3 version: 2.1.3 zod: - specifier: ^3.25.56 - version: 3.25.76 + specifier: ^4.1.1 + version: 4.1.1 devDependencies: '@types/ms': specifier: ^2.1.0 @@ -2315,12 +2315,12 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} - '@oxc-project/runtime@0.82.2': - resolution: {integrity: sha512-cYxcj5CPn/vo5QSpCZcYzBiLidU5+GlFSqIeNaMgBDtcVRBsBJHZg3pHw999W6nHamFQ1EHuPPByB26tjaJiJw==} + '@oxc-project/runtime@0.82.3': + resolution: {integrity: sha512-LNh5GlJvYHAnMurO+EyA8jJwN1rki7l3PSHuosDh2I7h00T6/u9rCkUjg/SvPmT1CZzvhuW0y+gf7jcqUy/Usg==} engines: {node: '>=6.9.0'} - '@oxc-project/types@0.82.2': - resolution: {integrity: sha512-WMGSwd9FsNBs/WfqIOH0h3k1LBdjZJQGYjGnC+vla/fh6HUsu5HzGPerRljiq1hgMQ6gs031YJR12VyP57b/hQ==} + '@oxc-project/types@0.82.3': + resolution: {integrity: sha512-6nCUxBnGX0c6qfZW5MaF6/fmu5dHJDMiMPaioKHKs5mi5+8/FHQ7WGjgQIz1zxpmceMYfdIXkOaLYE+ejbuOtA==} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} @@ -2826,81 +2826,81 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - '@rolldown/binding-android-arm64@1.0.0-beta.33': - resolution: {integrity: sha512-xhDQXKftRkEULIxCddrKMR8y0YO/Y+6BKk/XrQP2B29YjV2wr8DByoEz+AHX9BfLHb2srfpdN46UquBW2QXWpQ==} + '@rolldown/binding-android-arm64@1.0.0-beta.34': + resolution: {integrity: sha512-jf5GNe5jP3Sr1Tih0WKvg2bzvh5T/1TA0fn1u32xSH7ca/p5t+/QRr4VRFCV/na5vjwKEhwWrChsL2AWlY+eoA==} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-beta.33': - resolution: {integrity: sha512-7lhhY08v5ZtRq8JJQaJ49fnJombAPnqllKKCDLU/UvaqNAOEyTGC8J1WVOLC4EA4zbXO5U3CCRgVGyAFNH2VtQ==} + '@rolldown/binding-darwin-arm64@1.0.0-beta.34': + resolution: {integrity: sha512-2F/TqH4QuJQ34tgWxqBjFL3XV1gMzeQgUO8YRtCPGBSP0GhxtoFzsp7KqmQEothsxztlv+KhhT9Dbg3HHwHViQ==} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-beta.33': - resolution: {integrity: sha512-U2iGjcDV7NWyYyhap8YuY0nwrLX6TvX/9i7gBtdEMPm9z3wIUVGNMVdGlA43uqg7xDpRGpEqGnxbeDgiEwYdnA==} + '@rolldown/binding-darwin-x64@1.0.0-beta.34': + resolution: {integrity: sha512-E1QuFslgLWbHQ8Qli/AqUKdfg0pockQPwRxVbhNQ74SciZEZpzLaujkdmOLSccMlSXDfFCF8RPnMoRAzQ9JV8Q==} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-beta.33': - resolution: {integrity: sha512-gd6ASromVHFLlzrjJWMG5CXHkS7/36DEZ8HhvGt2NN8eZALCIuyEx8HMMLqvKA7z4EAztVkdToVrdxpGMsKZxw==} + '@rolldown/binding-freebsd-x64@1.0.0-beta.34': + resolution: {integrity: sha512-VS8VInNCwnkpI9WeQaWu3kVBq9ty6g7KrHdLxYMzeqz24+w9hg712TcWdqzdY6sn+24lUoMD9jTZrZ/qfVpk0g==} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.33': - resolution: {integrity: sha512-xmeLfkfGthuynO1EpCdyTVr0r4G+wqvnKCuyR6rXOet+hLrq5HNAC2XtP/jU2TB4Bc6aiLYxl868B8CGtFDhcw==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.34': + resolution: {integrity: sha512-4St4emjcnULnxJYb/5ZDrH/kK/j6PcUgc3eAqH5STmTrcF+I9m/X2xvSF2a2bWv1DOQhxBewThu0KkwGHdgu5w==} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.33': - resolution: {integrity: sha512-cHGp8yfHL4pes6uaLbO5L58ceFkUK4efd8iE86jClD1QPPDLKiqEXJCFYeuK3OfODuF5EBOmf0SlcUZNEYGdmw==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.34': + resolution: {integrity: sha512-a737FTqhFUoWfnebS2SnQ2BS50p0JdukdkUBwy2J06j4hZ6Eej0zEB8vTfAqoCjn8BQKkXBy+3Sx0IRkgwz1gA==} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.33': - resolution: {integrity: sha512-wZ1t7JAvVeFgskH1L9y7c47ITitPytpL0s8FmAT8pVfXcaTmS58ZyoXT+y6cz8uCkQnETjrX3YezTGI18u3ecg==} + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.34': + resolution: {integrity: sha512-NH+FeQWKyuw0k+PbXqpFWNfvD8RPvfJk766B/njdaWz4TmiEcSB0Nb6guNw1rBpM1FmltQYb3fFnTumtC6pRfA==} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.33': - resolution: {integrity: sha512-cDndWo3VEYbm7yeujOV6Ie2XHz0K8YX/R/vbNmMo03m1QwtBKKvbYNSyJb3B9+8igltDjd8zNM9mpiNNrq/ekQ==} + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.34': + resolution: {integrity: sha512-Q3RSCivp8pNadYK8ke3hLnQk08BkpZX9BmMjgwae2FWzdxhxxUiUzd9By7kneUL0vRQ4uRnhD9VkFQ+Haeqdvw==} cpu: [x64] os: [linux] - '@rolldown/binding-linux-x64-musl@1.0.0-beta.33': - resolution: {integrity: sha512-bl7uzi6es/l6LT++NZcBpiX43ldLyKXCPwEZGY1rZJ99HQ7m1g3KxWwYCcGxtKjlb2ExVvDZicF6k+96vxOJKg==} + '@rolldown/binding-linux-x64-musl@1.0.0-beta.34': + resolution: {integrity: sha512-wDd/HrNcVoBhWWBUW3evJHoo7GJE/RofssBy3Dsiip05YUBmokQVrYAyrboOY4dzs/lJ7HYeBtWQ9hj8wlyF0A==} cpu: [x64] os: [linux] - '@rolldown/binding-openharmony-arm64@1.0.0-beta.33': - resolution: {integrity: sha512-TrgzQanpLgcmmzolCbYA9BPZgF1gYxkIGZhU/HROnJPsq67gcyaYw/JBLioqQLjIwMipETkn25YY799D2OZzJA==} + '@rolldown/binding-openharmony-arm64@1.0.0-beta.34': + resolution: {integrity: sha512-dH3FTEV6KTNWpYSgjSXZzeX7vLty9oBYn6R3laEdhwZftQwq030LKL+5wyQdlbX5pnbh4h127hpv3Hl1+sj8dg==} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-beta.33': - resolution: {integrity: sha512-z0LltdUfvoKak9SuaLz/M9AVSg+RTOZjFksbZXzC6Svl1odyW4ai21VHhZy3m2Faeeb/rl/9efVLayj+qYEGxw==} + '@rolldown/binding-wasm32-wasi@1.0.0-beta.34': + resolution: {integrity: sha512-y5BUf+QtO0JsIDKA51FcGwvhJmv89BYjUl8AmN7jqD6k/eU55mH6RJYnxwCsODq5m7KSSTigVb6O7/GqB8wbPw==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.33': - resolution: {integrity: sha512-CpvOHyqDNOYx9riD4giyXQDIu72bWRU2Dwt1xFSPlBudk6NumK0OJl6Ch+LPnkp5podQHcQg0mMauAXPVKct7g==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.34': + resolution: {integrity: sha512-ga5hFhdTwpaNxEiuxZHWnD3ed0GBAzbgzS5tRHpe0ObptxM1a9Xrq6TVfNQirBLwb5Y7T/FJmJi3pmdLy95ljg==} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.33': - resolution: {integrity: sha512-/tNTvZTWHz6HiVuwpR3zR0kGIyCNb+/tFhnJmti+Aw2fAXs3l7Aj0DcXd0646eFKMX8L2w5hOW9H08FXTUkN0g==} + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.34': + resolution: {integrity: sha512-4/MBp9T9eRnZskxWr8EXD/xHvLhdjWaeX/qY9LPRG1JdCGV3DphkLTy5AWwIQ5jhAy2ZNJR5z2fYRlpWU0sIyQ==} cpu: [ia32] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.33': - resolution: {integrity: sha512-Bb2qK3z7g2mf4zaKRvkohHzweaP1lLbaoBmXZFkY6jJWMm0Z8Pfnh8cOoRlH1IVM1Ufbo8ZZ1WXp1LbOpRMtXw==} + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.34': + resolution: {integrity: sha512-7O5iUBX6HSBKlQU4WykpUoEmb0wQmonb6ziKFr3dJTHud2kzDnWMqk344T0qm3uGv9Ddq6Re/94pInxo1G2d4w==} cpu: [x64] os: [win32] '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} - '@rolldown/pluginutils@1.0.0-beta.33': - resolution: {integrity: sha512-she25NCG6NoEPC/SEB4pHs5STcnfI4VBFOzjeI63maSPrWME5J2XC8ogrBgp8NaE/xzj28/kbpSaebiMvFRj+w==} + '@rolldown/pluginutils@1.0.0-beta.34': + resolution: {integrity: sha512-LyAREkZHP5pMom7c24meKmJCdhf2hEyvam2q0unr3or9ydwDL+DJ8chTF6Av/RFPb3rH8UFBdMzO5MxTZW97oA==} '@rollup/plugin-json@6.1.0': resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} @@ -8108,8 +8108,8 @@ packages: vue-tsc: optional: true - rolldown@1.0.0-beta.33: - resolution: {integrity: sha512-mgu118ZuRguC8unhPCbdZbyRbjQfEMiWqlojBA5aRIncBelRaBomnHNpGKYkYWeK7twRz5Cql30xgqqrA3Xelw==} + rolldown@1.0.0-beta.34: + resolution: {integrity: sha512-Wwh7EwalMzzX3Yy3VN58VEajeR2Si8+HDNMf706jPLIqU7CxneRW+dQVfznf5O0TWTnJyu4npelwg2bzTXB1Nw==} hasBin: true rollup@4.45.1: @@ -9313,6 +9313,9 @@ packages: zod@4.1.0: resolution: {integrity: sha512-UWxluYj2IDX9MHRXTMbB/2eeWrAMmmMSESM+MfT9MQw8U1qo9q5ASW08anoJh6AJ7pkt099fLdNFmfI+4aChHg==} + zod@4.1.1: + resolution: {integrity: sha512-SgMZK/h8Tigt9nnKkfJMvB/mKjiJXaX26xegP4sa+0wHIFVFWVlsQGdhklDmuargBD3Hsi3rsQRIzwJIhTPJHA==} + zustand@5.0.8: resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} engines: {node: '>=12.20.0'} @@ -9342,27 +9345,27 @@ snapshots: '@ai-sdk/provider-utils': 3.0.5(zod@4.1.0) zod: 4.1.0 - '@ai-sdk/google@2.0.8(zod@3.25.76)': + '@ai-sdk/google@2.0.8(zod@4.1.1)': dependencies: '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.5(zod@3.25.76) - zod: 3.25.76 + '@ai-sdk/provider-utils': 3.0.5(zod@4.1.1) + zod: 4.1.1 - '@ai-sdk/provider-utils@3.0.5(zod@3.25.76)': + '@ai-sdk/provider-utils@3.0.5(zod@4.1.0)': dependencies: '@ai-sdk/provider': 2.0.0 '@standard-schema/spec': 1.0.0 eventsource-parser: 3.0.5 - zod: 3.25.76 - zod-to-json-schema: 3.24.6(zod@3.25.76) + zod: 4.1.0 + zod-to-json-schema: 3.24.6(zod@4.1.0) - '@ai-sdk/provider-utils@3.0.5(zod@4.1.0)': + '@ai-sdk/provider-utils@3.0.5(zod@4.1.1)': dependencies: '@ai-sdk/provider': 2.0.0 '@standard-schema/spec': 1.0.0 eventsource-parser: 3.0.5 - zod: 4.1.0 - zod-to-json-schema: 3.24.6(zod@4.1.0) + zod: 4.1.1 + zod-to-json-schema: 3.24.6(zod@4.1.1) '@ai-sdk/provider@2.0.0': dependencies: @@ -12480,9 +12483,9 @@ snapshots: '@opentelemetry/api@1.9.0': {} - '@oxc-project/runtime@0.82.2': {} + '@oxc-project/runtime@0.82.3': {} - '@oxc-project/types@0.82.2': {} + '@oxc-project/types@0.82.3': {} '@pkgjs/parseargs@0.11.0': optional: true @@ -12990,53 +12993,53 @@ snapshots: '@radix-ui/rect@1.1.1': {} - '@rolldown/binding-android-arm64@1.0.0-beta.33': + '@rolldown/binding-android-arm64@1.0.0-beta.34': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-beta.33': + '@rolldown/binding-darwin-arm64@1.0.0-beta.34': optional: true - '@rolldown/binding-darwin-x64@1.0.0-beta.33': + '@rolldown/binding-darwin-x64@1.0.0-beta.34': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-beta.33': + '@rolldown/binding-freebsd-x64@1.0.0-beta.34': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.33': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.34': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.33': + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.34': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.33': + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.34': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.33': + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.34': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-beta.33': + '@rolldown/binding-linux-x64-musl@1.0.0-beta.34': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-beta.33': + '@rolldown/binding-openharmony-arm64@1.0.0-beta.34': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.33': + '@rolldown/binding-wasm32-wasi@1.0.0-beta.34': dependencies: '@napi-rs/wasm-runtime': 1.0.3 optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.33': + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.34': optional: true - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.33': + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.34': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.33': + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.34': optional: true '@rolldown/pluginutils@1.0.0-beta.27': {} - '@rolldown/pluginutils@1.0.0-beta.33': {} + '@rolldown/pluginutils@1.0.0-beta.34': {} '@rollup/plugin-json@6.1.0(rollup@4.46.3)': dependencies: @@ -13929,7 +13932,7 @@ snapshots: '@types/react-router@5.1.20': dependencies: '@types/history': 4.7.11 - '@types/react': 19.1.8 + '@types/react': 19.1.11 '@types/react@19.1.10': dependencies: @@ -19069,7 +19072,7 @@ snapshots: robust-predicates@3.0.2: {} - rolldown-plugin-dts@0.15.6(rolldown@1.0.0-beta.33)(typescript@5.9.2): + rolldown-plugin-dts@0.15.6(rolldown@1.0.0-beta.34)(typescript@5.9.2): dependencies: '@babel/generator': 7.28.0 '@babel/parser': 7.28.0 @@ -19079,34 +19082,34 @@ snapshots: debug: 4.4.1 dts-resolver: 2.1.1 get-tsconfig: 4.10.1 - rolldown: 1.0.0-beta.33 + rolldown: 1.0.0-beta.34 optionalDependencies: typescript: 5.9.2 transitivePeerDependencies: - oxc-resolver - supports-color - rolldown@1.0.0-beta.33: + rolldown@1.0.0-beta.34: dependencies: - '@oxc-project/runtime': 0.82.2 - '@oxc-project/types': 0.82.2 - '@rolldown/pluginutils': 1.0.0-beta.33 + '@oxc-project/runtime': 0.82.3 + '@oxc-project/types': 0.82.3 + '@rolldown/pluginutils': 1.0.0-beta.34 ansis: 4.1.0 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-beta.33 - '@rolldown/binding-darwin-arm64': 1.0.0-beta.33 - '@rolldown/binding-darwin-x64': 1.0.0-beta.33 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.33 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.33 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.33 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.33 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.33 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.33 - '@rolldown/binding-openharmony-arm64': 1.0.0-beta.33 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.33 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.33 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.33 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.33 + '@rolldown/binding-android-arm64': 1.0.0-beta.34 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.34 + '@rolldown/binding-darwin-x64': 1.0.0-beta.34 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.34 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.34 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.34 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.34 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.34 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.34 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.34 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.34 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.34 + '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.34 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.34 rollup@4.45.1: dependencies: @@ -19791,8 +19794,8 @@ snapshots: diff: 8.0.2 empathic: 2.0.0 hookable: 5.5.3 - rolldown: 1.0.0-beta.33 - rolldown-plugin-dts: 0.15.6(rolldown@1.0.0-beta.33)(typescript@5.9.2) + rolldown: 1.0.0-beta.34 + rolldown-plugin-dts: 0.15.6(rolldown@1.0.0-beta.34)(typescript@5.9.2) semver: 7.7.2 tinyexec: 1.0.1 tinyglobby: 0.2.14 @@ -20442,18 +20445,20 @@ snapshots: yocto-queue@1.2.1: {} - zod-to-json-schema@3.24.6(zod@3.25.76): - dependencies: - zod: 3.25.76 - zod-to-json-schema@3.24.6(zod@4.1.0): dependencies: zod: 4.1.0 + zod-to-json-schema@3.24.6(zod@4.1.1): + dependencies: + zod: 4.1.1 + zod@3.25.76: {} zod@4.1.0: {} + zod@4.1.1: {} + zustand@5.0.8(@types/react@19.1.11)(immer@9.0.21)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)): optionalDependencies: '@types/react': 19.1.11 From 7eab656c1e8a993e946c88726b5e40ce883cf07a Mon Sep 17 00:00:00 2001 From: Avraj Date: Mon, 25 Aug 2025 13:28:17 +0300 Subject: [PATCH 11/19] fix lockfile + add zod v3 support to ai tool param --- packages/ai/src/tools/common/index.ts | 8 ++++--- pnpm-lock.yaml | 34 ++++++++------------------- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/packages/ai/src/tools/common/index.ts b/packages/ai/src/tools/common/index.ts index e54108be..80b31db7 100644 --- a/packages/ai/src/tools/common/index.ts +++ b/packages/ai/src/tools/common/index.ts @@ -2,6 +2,7 @@ import { Schema, tool } from 'ai'; import { AiContext } from '../../context'; import { getAiWorkerContext } from '../../ai-context-worker'; import { z } from 'zod'; +import { z as z3 } from 'zod/v3'; /** * Utility type that represents a value that can be either synchronous or asynchronous. @@ -14,7 +15,7 @@ type Awaitable = T | Promise; * Type representing the parameters schema for AI tools. * Extracted from the first parameter of the `tool` function from the 'ai' library. */ -export type ToolParameterType = z.ZodType | Schema; +export type ToolParameterType = z.ZodType | z3.ZodType | Schema; /** * Utility type that infers the TypeScript type from a tool parameter schema. @@ -26,8 +27,9 @@ export type InferParameters = ? T['_type'] : T extends z.ZodTypeAny ? z.infer - : never; - + : T extends z3.ZodTypeAny + ? z3.infer + : never; /** * Configuration options for creating an AI tool. * @template T - The parameter schema type for the tool diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 356397b3..b388db52 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -190,7 +190,7 @@ importers: dependencies: ai: specifier: ^5.0.22 - version: 5.0.23(zod@4.1.1) + version: 5.0.22(zod@4.1.1) zod: specifier: ^4.1.0 version: 4.1.1 @@ -643,8 +643,8 @@ importers: packages: - '@ai-sdk/gateway@1.0.12': - resolution: {integrity: sha512-IH7bBrirbzUDUhPzl5fsXLaMKxbezXnoTTZEAtbRrL6AiYycH//B9U1u+FHuteISndbDArp0b5ujpW2mhHWceA==} + '@ai-sdk/gateway@1.0.11': + resolution: {integrity: sha512-ErwWS3sPOuWy42eE3AVxlKkTa1XjjKBEtNCOylVKMO5KNyz5qie8QVlLYbULOG56dtxX4zTKX3rQNJudplhcmQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4 @@ -4034,8 +4034,8 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} - ai@5.0.23: - resolution: {integrity: sha512-1zUF0o1zRI7UmSd8u5CKc2iHNhv21tM95Oka81c0CF77GnTbq5RvrAqVuLI+gMyKcIgs99yxA+xc5hJXvh6V+w==} + ai@5.0.22: + resolution: {integrity: sha512-RZiYhj7Ux7hrLtXkHPcxzdiSZt4NOiC69O5AkNfMCsz3twwz/KRkl9ASptosoOsg833s5yRcTSdIu5z53Sl6Pw==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4 @@ -9336,7 +9336,7 @@ packages: snapshots: - '@ai-sdk/gateway@1.0.12(zod@4.1.1)': + '@ai-sdk/gateway@1.0.11(zod@4.1.1)': dependencies: '@ai-sdk/provider': 2.0.0 '@ai-sdk/provider-utils': 3.0.5(zod@4.1.1) @@ -9348,14 +9348,6 @@ snapshots: '@ai-sdk/provider-utils': 3.0.5(zod@4.1.1) zod: 4.1.1 - '@ai-sdk/provider-utils@3.0.5(zod@3.25.76)': - dependencies: - '@ai-sdk/provider': 2.0.0 - '@standard-schema/spec': 1.0.0 - eventsource-parser: 3.0.5 - zod: 3.25.76 - zod-to-json-schema: 3.24.6(zod@3.25.76) - '@ai-sdk/provider-utils@3.0.5(zod@4.1.1)': dependencies: '@ai-sdk/provider': 2.0.0 @@ -11848,7 +11840,7 @@ snapshots: '@docusaurus/react-loadable@6.0.0(react@19.1.1)': dependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.11 react: 19.1.1 '@docusaurus/remark-plugin-npm2yarn@3.8.1': @@ -13983,7 +13975,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 22.17.2 + '@types/node': 24.3.0 '@types/ws@8.5.13': dependencies: @@ -14253,9 +14245,9 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - ai@5.0.23(zod@4.1.1): + ai@5.0.22(zod@4.1.1): dependencies: - '@ai-sdk/gateway': 1.0.12(zod@4.1.1) + '@ai-sdk/gateway': 1.0.11(zod@4.1.1) '@ai-sdk/provider': 2.0.0 '@ai-sdk/provider-utils': 3.0.5(zod@4.1.1) '@opentelemetry/api': 1.9.0 @@ -20442,10 +20434,6 @@ snapshots: yocto-queue@1.2.1: {} - zod-to-json-schema@3.24.6(zod@3.25.76): - dependencies: - zod: 3.25.76 - zod-to-json-schema@3.24.6(zod@4.1.1): dependencies: zod: 4.1.1 @@ -20454,8 +20442,6 @@ snapshots: zod@4.1.1: {} - zod@4.1.1: {} - zustand@5.0.8(@types/react@19.1.11)(immer@9.0.21)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)): optionalDependencies: '@types/react': 19.1.11 From 3b5f1eb9d7d88dc02a8d6f1aa5fba7622d4c67a1 Mon Sep 17 00:00:00 2001 From: Avraj Date: Mon, 25 Aug 2025 13:40:08 +0300 Subject: [PATCH 12/19] docs: mark `after` as unstable + improve middleware docs --- .../guide/02-commands/05-after-function.mdx | 14 ++++- .../docs/guide/02-commands/07-middlewares.mdx | 51 ++++++++++++++++++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/apps/website/docs/guide/02-commands/05-after-function.mdx b/apps/website/docs/guide/02-commands/05-after-function.mdx index 71571101..2c0b7cc1 100644 --- a/apps/website/docs/guide/02-commands/05-after-function.mdx +++ b/apps/website/docs/guide/02-commands/05-after-function.mdx @@ -2,6 +2,13 @@ title: after Function --- +:::warning UNSTABLE + +The `after()` function is currently marked as unstable and may change +in the future. + +::: + The `after()` function allows you to execute a callback after a command has been executed. This is useful for performing actions that should occur after the command has completed, such as logging or @@ -10,13 +17,18 @@ cleanup tasks. ## Usage ```ts title="src/app/commands/ping.ts" -import { type CommandData, type ChatInputCommand, after } from 'commandkit'; +import { + type CommandData, + type ChatInputCommand, + unstable_after as after, +} from 'commandkit'; export const command: CommandData = {}; export const chatInput: ChatInputCommand = async (ctx) => { after(() => { // This code will be executed after the command has been executed + // Perform any cleanup here console.log('Command has been executed'); }); diff --git a/apps/website/docs/guide/02-commands/07-middlewares.mdx b/apps/website/docs/guide/02-commands/07-middlewares.mdx index b9b19f50..5a2b6ec3 100644 --- a/apps/website/docs/guide/02-commands/07-middlewares.mdx +++ b/apps/website/docs/guide/02-commands/07-middlewares.mdx @@ -21,7 +21,9 @@ import { MiddlewareContext } from 'commandkit'; export function beforeExecute(ctx: MiddlewareContext) { // This function will be executed before the command is executed - console.log(`User ${ctx.interaction.user.id} is about to execute a command`); + console.log( + `User ${ctx.interaction.user.id} is about to execute a command`, + ); } export function afterExecute(ctx: MiddlewareContext) { @@ -52,6 +54,49 @@ export function beforeExecute(ctx: MiddlewareContext) { } ``` +:::tip + +You can also use `stopMiddlewares()` inside a command function to stop +any `afterExecute` middlewares from running. + +In addition, you can also use `stopMiddlewares()` inside any +`afterExecute` middleware function to stop any remaining middlewares +from running. + +::: + +:::warning + +Calling `stopMiddlewares()` in a try/catch block may lead to +unexpected behavior. + +If you still want to use `stopMiddlewares()` in a try/catch block, you +can use the `isErrorType` function to check if the error is an +instance of the `CommandKitErrorCodes.StopMiddlewares` error. + +```ts title="src/app/commands/+middleware.ts" +import type { MiddlewareContext } from 'commandkit'; + +export function beforeExecute(ctx: MiddlewareContext) { + try { + // code that may throw an error + + stopMiddlewares(); // conditionally stop the middleware chain + } catch (error) { + if (isErrorType(error, CommandKitErrorCodes.StopMiddlewares)) { + // if stopMiddlewares() is called in the try block, throw it so CommandKit can stop the middleware chain + throw error; + } + + // this means that the code threw the error, and stopMiddlewares() was not called + // the rest of the middlewares will be executed as normal + console.error(error); + } +} +``` + +::: + ## Middleware types ### Directory-scoped middleware @@ -66,7 +111,9 @@ import type { MiddlewareContext } from 'commandkit'; export function beforeExecute(ctx: MiddlewareContext) { // This middleware will run before any moderation command if (!ctx.interaction.member.permissions.has('KickMembers')) { - throw new Error('You need moderation permissions to use this command'); + throw new Error( + 'You need moderation permissions to use this command', + ); } } ``` From 1c98367b7ab8f7da6e503727ee234b9ec0d96379 Mon Sep 17 00:00:00 2001 From: Avraj Date: Mon, 25 Aug 2025 13:41:41 +0300 Subject: [PATCH 13/19] docs: update parameters to inputSchema --- .../05-official-plugins/01-commandkit-ai.mdx | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/apps/website/docs/guide/05-official-plugins/01-commandkit-ai.mdx b/apps/website/docs/guide/05-official-plugins/01-commandkit-ai.mdx index 90e6a814..b056eb6d 100644 --- a/apps/website/docs/guide/05-official-plugins/01-commandkit-ai.mdx +++ b/apps/website/docs/guide/05-official-plugins/01-commandkit-ai.mdx @@ -124,9 +124,12 @@ export const command: CommandData = { }; export const aiConfig: AiConfig = { - parameters: z.object({ + inputSchema: z.object({ username: z.string().describe('The username to greet'), - message: z.string().optional().describe('Optional custom greeting message'), + message: z + .string() + .optional() + .describe('Optional custom greeting message'), }), }; @@ -282,8 +285,10 @@ import { z } from 'zod'; export const getWeather = createTool({ name: 'getWeather', description: 'Get current weather information for a location', - parameters: z.object({ - location: z.string().describe('The city or location to get weather for'), + inputSchema: z.object({ + location: z + .string() + .describe('The city or location to get weather for'), units: z.enum(['celsius', 'fahrenheit']).default('celsius'), }), async execute(ctx, params) { @@ -422,7 +427,9 @@ export const ai: AiCommand = async (ctx) => { // Validate AI-generated parameters if (action === 'ban' && !isModeratorRole(ctx.message.member)) { - await ctx.message.reply('❌ Only moderators can perform ban actions.'); + await ctx.message.reply( + '❌ Only moderators can perform ban actions.', + ); return; } @@ -528,7 +535,9 @@ configureAI({ await message.reply(`Debug Error: ${error.message}`); } else { // Generic error in production - await message.reply('An error occurred while processing your request.'); + await message.reply( + 'An error occurred while processing your request.', + ); } }, }); From 7e56da4c50dfb9ae3dc20f90095f677216e8b39e Mon Sep 17 00:00:00 2001 From: Avraj Date: Mon, 25 Aug 2025 14:49:39 +0300 Subject: [PATCH 14/19] docs: add command metadata --- .../src/app/commands/(general)/ping.ts | 24 ++++- .../ai/functions/create-tool.mdx | 2 +- .../ai/interfaces/configure-ai.mdx | 26 ++--- .../ai/interfaces/create-tool-options.mdx | 2 +- .../ai/types/infer-parameters.mdx | 6 +- .../ai/types/tool-execute-function.mdx | 2 +- .../ai/types/tool-parameter-type.mdx | 4 +- .../classes/use-cache-directive-plugin.mdx | 2 +- .../commandkit/classes/analytics-engine.mdx | 2 +- .../commandkit/functions/flag.mdx | 2 +- .../commandkit/functions/redirect.mdx | 2 +- .../commandkit/functions/rethrow.mdx | 2 +- .../commandkit/functions/stop-events.mdx | 2 +- .../commandkit/functions/stop-middlewares.mdx | 5 +- .../interfaces/command-flag-context.mdx | 66 ++++++------- .../interfaces/context-parameters.mdx | 18 ++-- .../interfaces/event-flag-context.mdx | 68 ++++++------- .../commandkit/types/filter-function.mdx | 2 +- .../02-commands/01-chat-input-commands.mdx | 16 --- .../guide/02-commands/05-command-metadata.mdx | 98 +++++++++++++++++++ ...ter-function.mdx => 06-after-function.mdx} | 0 ...irectory.mdx => 07-category-directory.mdx} | 0 ...{07-middlewares.mdx => 08-middlewares.mdx} | 8 +- .../05-official-plugins/01-commandkit-ai.mdx | 17 +--- 24 files changed, 231 insertions(+), 145 deletions(-) create mode 100644 apps/website/docs/guide/02-commands/05-command-metadata.mdx rename apps/website/docs/guide/02-commands/{05-after-function.mdx => 06-after-function.mdx} (100%) rename apps/website/docs/guide/02-commands/{06-category-directory.mdx => 07-category-directory.mdx} (100%) rename apps/website/docs/guide/02-commands/{07-middlewares.mdx => 08-middlewares.mdx} (96%) diff --git a/apps/test-bot/src/app/commands/(general)/ping.ts b/apps/test-bot/src/app/commands/(general)/ping.ts index 20d4a965..6c7fcd88 100644 --- a/apps/test-bot/src/app/commands/(general)/ping.ts +++ b/apps/test-bot/src/app/commands/(general)/ping.ts @@ -14,6 +14,7 @@ import { stopMiddlewares, Logger, unstable_after as after, + CommandMetadataFunction, } from 'commandkit'; export const command: CommandData = { @@ -35,10 +36,25 @@ export const command: CommandData = { // guilds: ['1314834483660455938'], }; -export const metadata: CommandMetadata = { - // userPermissions: 'Administrator', - // botPermissions: 'KickMembers', - // guilds: ['1314834483660455938'], +// export const metadata: CommandMetadata = { +// // userPermissions: 'Administrator', +// // botPermissions: 'KickMembers', +// // guilds: ['1314834483660455938'], +// aliases: [''], +// userPermissions: 'Administrator', +// botPermissions: 'KickMembers', +// guilds: ['1314834483660455938'], +// }; + +export const generateMetadata: CommandMetadataFunction = async () => { + // Dynamically determine the metadata for the command + + return { + userPermissions: 'Administrator', + botPermissions: ['KickMembers', 'BanMembers'], + guilds: ['1234567890', '1234567891'], + aliases: ['p', 'pong'], + }; }; const tests = Array.from({ length: 10 }, (_, i) => ({ diff --git a/apps/website/docs/api-reference/ai/functions/create-tool.mdx b/apps/website/docs/api-reference/ai/functions/create-tool.mdx index b853c1bd..e3d0f88d 100644 --- a/apps/website/docs/api-reference/ai/functions/create-tool.mdx +++ b/apps/website/docs/api-reference/ai/functions/create-tool.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## createTool - + Creates a new AI tool with the specified configuration. This function wraps the underlying AI library's tool creation with additional diff --git a/apps/website/docs/api-reference/ai/interfaces/configure-ai.mdx b/apps/website/docs/api-reference/ai/interfaces/configure-ai.mdx index 1f31e2d9..23d77e27 100644 --- a/apps/website/docs/api-reference/ai/interfaces/configure-ai.mdx +++ b/apps/website/docs/api-reference/ai/interfaces/configure-ai.mdx @@ -23,16 +23,16 @@ interface ConfigureAI { messageFilter?: MessageFilter; selectAiModel: SelectAiModel; prepareSystemPrompt?: (ctx: AiContext, message: Message) => Promise; - preparePrompt?: ( - ctx: AiContext, - message: Message, + preparePrompt?: ( + ctx: AiContext, + message: Message, ) => Promise; onProcessingStart?: (ctx: AiContext, message: Message) => Promise; onProcessingFinish?: (ctx: AiContext, message: Message) => Promise; - onResult?: ( - ctx: AiContext, - message: Message, - result: AIGenerateResult, + onResult?: ( + ctx: AiContext, + message: Message, + result: AIGenerateResult, ) => Promise; onError?: (ctx: AiContext, message: Message, error: Error) => Promise; } @@ -49,24 +49,24 @@ Whether to disable the built-in tools. Default is false. MessageFilter`} /> -A filter function that determines whether a message should be processed by the AI. +A filter function that determines whether a message should be processed by the AI. CommandKit invokes this function before processing the message. ### selectAiModel SelectAiModel`} /> -A function that selects the AI model to use based on the message. +A function that selects the AI model to use based on the message. This function should return a promise that resolves to an object containing the model and options. ### prepareSystemPrompt AiContext, message: Message) => Promise<string>`} /> -A function that generates a system prompt based on the message. -This function should return a promise that resolves to a string containing the system prompt. +A function that generates a system prompt based on the message. +This function should return a promise that resolves to a string containing the system prompt. If not provided, a default system prompt will be used. ### preparePrompt -AiContext, message: Message, ) => Promise<string | AiMessage>`} /> +AiContext, message: Message, ) => Promise<string | AiMessage>`} /> A function that prepares the prompt for the AI model. ### onProcessingStart @@ -81,7 +81,7 @@ A function that gets called when the AI starts processing a message. A function that gets called when the AI finishes processing a message. ### onResult -AiContext, message: Message, result: AIGenerateResult, ) => Promise<void>`} /> +AiContext, message: Message, result: AIGenerateResult, ) => Promise<void>`} /> A function that gets called upon receiving the result from the AI model. ### onError diff --git a/apps/website/docs/api-reference/ai/interfaces/create-tool-options.mdx b/apps/website/docs/api-reference/ai/interfaces/create-tool-options.mdx index 95530697..8b6a6b4a 100644 --- a/apps/website/docs/api-reference/ai/interfaces/create-tool-options.mdx +++ b/apps/website/docs/api-reference/ai/interfaces/create-tool-options.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## CreateToolOptions - + Configuration options for creating an AI tool. diff --git a/apps/website/docs/api-reference/ai/types/infer-parameters.mdx b/apps/website/docs/api-reference/ai/types/infer-parameters.mdx index 245777c3..f2b4fa43 100644 --- a/apps/website/docs/api-reference/ai/types/infer-parameters.mdx +++ b/apps/website/docs/api-reference/ai/types/infer-parameters.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## InferParameters - + Utility type that infers the TypeScript type from a tool parameter schema. Supports both Zod schemas and AI library schemas. @@ -23,5 +23,7 @@ type InferParameters = T extends Schema ? T['_type'] : T extends z.ZodTypeAny ? z.infer - : never + : T extends z3.ZodTypeAny + ? z3.infer + : never ``` diff --git a/apps/website/docs/api-reference/ai/types/tool-execute-function.mdx b/apps/website/docs/api-reference/ai/types/tool-execute-function.mdx index 18fbe1eb..2b716410 100644 --- a/apps/website/docs/api-reference/ai/types/tool-execute-function.mdx +++ b/apps/website/docs/api-reference/ai/types/tool-execute-function.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## ToolExecuteFunction - + Type definition for a tool's execute function. diff --git a/apps/website/docs/api-reference/ai/types/tool-parameter-type.mdx b/apps/website/docs/api-reference/ai/types/tool-parameter-type.mdx index 3668869c..f19b3ea0 100644 --- a/apps/website/docs/api-reference/ai/types/tool-parameter-type.mdx +++ b/apps/website/docs/api-reference/ai/types/tool-parameter-type.mdx @@ -13,11 +13,11 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## ToolParameterType - + Type representing the parameters schema for AI tools. Extracted from the first parameter of the `tool` function from the 'ai' library. ```ts title="Signature" -type ToolParameterType = z.ZodType | Schema +type ToolParameterType = z.ZodType | z3.ZodType | Schema ``` diff --git a/apps/website/docs/api-reference/cache/classes/use-cache-directive-plugin.mdx b/apps/website/docs/api-reference/cache/classes/use-cache-directive-plugin.mdx index 742d0a44..66fad48f 100644 --- a/apps/website/docs/api-reference/cache/classes/use-cache-directive-plugin.mdx +++ b/apps/website/docs/api-reference/cache/classes/use-cache-directive-plugin.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## UseCacheDirectivePlugin - + Compiler plugin for the "use cache" directive. This plugin transforms the "use cache" directive into a runtime cache operation. diff --git a/apps/website/docs/api-reference/commandkit/classes/analytics-engine.mdx b/apps/website/docs/api-reference/commandkit/classes/analytics-engine.mdx index 8ac66f46..fad5b6b7 100644 --- a/apps/website/docs/api-reference/commandkit/classes/analytics-engine.mdx +++ b/apps/website/docs/api-reference/commandkit/classes/analytics-engine.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## AnalyticsEngine - + AnalyticsEngine class for managing analytics providers and tracking events. This class allows you to register a provider, set a filter for events, and track or identify events. diff --git a/apps/website/docs/api-reference/commandkit/functions/flag.mdx b/apps/website/docs/api-reference/commandkit/functions/flag.mdx index f9bc3858..e5b436d7 100644 --- a/apps/website/docs/api-reference/commandkit/functions/flag.mdx +++ b/apps/website/docs/api-reference/commandkit/functions/flag.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## flag - + Create a new feature flag. diff --git a/apps/website/docs/api-reference/commandkit/functions/redirect.mdx b/apps/website/docs/api-reference/commandkit/functions/redirect.mdx index 6e1605d0..64fdd3d6 100644 --- a/apps/website/docs/api-reference/commandkit/functions/redirect.mdx +++ b/apps/website/docs/api-reference/commandkit/functions/redirect.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## redirect - + Stops current command assuming it has been redirected to another command. diff --git a/apps/website/docs/api-reference/commandkit/functions/rethrow.mdx b/apps/website/docs/api-reference/commandkit/functions/rethrow.mdx index 451a0993..8993ab6f 100644 --- a/apps/website/docs/api-reference/commandkit/functions/rethrow.mdx +++ b/apps/website/docs/api-reference/commandkit/functions/rethrow.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## rethrow - + Rethrow the error if it is a CommandKit error. diff --git a/apps/website/docs/api-reference/commandkit/functions/stop-events.mdx b/apps/website/docs/api-reference/commandkit/functions/stop-events.mdx index 69109bd4..0d0d58b9 100644 --- a/apps/website/docs/api-reference/commandkit/functions/stop-events.mdx +++ b/apps/website/docs/api-reference/commandkit/functions/stop-events.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## stopEvents - + Stops event propagation. This function should be called inside an event handler to prevent further event handling. diff --git a/apps/website/docs/api-reference/commandkit/functions/stop-middlewares.mdx b/apps/website/docs/api-reference/commandkit/functions/stop-middlewares.mdx index 07863a7e..759c5b95 100644 --- a/apps/website/docs/api-reference/commandkit/functions/stop-middlewares.mdx +++ b/apps/website/docs/api-reference/commandkit/functions/stop-middlewares.mdx @@ -13,10 +13,9 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## stopMiddlewares - + -Stop upcoming middleware and command execution. -This will **not** stop any `after()` callbacks inside the command. +Stop upcoming middlewares and command execution. ```ts title="Signature" function stopMiddlewares(): never diff --git a/apps/website/docs/api-reference/commandkit/interfaces/command-flag-context.mdx b/apps/website/docs/api-reference/commandkit/interfaces/command-flag-context.mdx index 2fec6a23..5af9d70a 100644 --- a/apps/website/docs/api-reference/commandkit/interfaces/command-flag-context.mdx +++ b/apps/website/docs/api-reference/commandkit/interfaces/command-flag-context.mdx @@ -21,34 +21,34 @@ Context for evaluating command flags in CommandKit. interface CommandFlagContext { client: Client; commandkit: CommandKit; - command: { - /** - * The interaction object if the command was invoked via an interaction. - * This can be a ChatInputCommandInteraction, AutocompleteInteraction, or ContextMenuCommandInteraction. - */ - interaction?: - | ChatInputCommandInteraction - | AutocompleteInteraction - | ContextMenuCommandInteraction; - /** - * The message object if the command was invoked via a message. - */ - message?: Message; - /** - * The guild where the command was invoked, if applicable. - * This will be null for commands invoked in DMs. - */ - guild: Guild | null; - /** - * The channel where the command was invoked. - * This can be a text channel, DM channel, or any other type of text-based channel. - */ - channel: TextBasedChannel | null; - /** - * The loaded command instance that is being executed. - * This contains the command's metadata and logic. - */ - command: LoadedCommand; + command: { + /** + * The interaction object if the command was invoked via an interaction. + * This can be a ChatInputCommandInteraction, AutocompleteInteraction, or ContextMenuCommandInteraction. + */ + interaction?: + | ChatInputCommandInteraction + | AutocompleteInteraction + | ContextMenuCommandInteraction; + /** + * The message object if the command was invoked via a message. + */ + message?: Message; + /** + * The guild where the command was invoked, if applicable. + * This will be null for commands invoked in DMs. + */ + guild: Guild | null; + /** + * The channel where the command was invoked. + * This can be a text channel, DM channel, or any other type of text-based channel. + */ + channel: TextBasedChannel | null; + /** + * The loaded command instance that is being executed. + * This contains the command's metadata and logic. + */ + command: LoadedCommand; }; event: null; } @@ -60,25 +60,25 @@ interface CommandFlagContext { -The Discord client instance. +The Discord client instance. This is the main entry point for interacting with the Discord API. ### commandkit CommandKit`} /> -The CommandKit instance, which provides access to the command framework. +The CommandKit instance, which provides access to the command framework. This includes commands, events, and other features of CommandKit. ### command -LoadedCommand; }`} /> +LoadedCommand; }`} /> -The command context, which includes information about the command being executed. +The command context, which includes information about the command being executed. This can include the interaction, message, guild, channel, and the loaded command. ### event -The event context is null for command flags, as they are not tied to a specific event. +The event context is null for command flags, as they are not tied to a specific event. This is used to differentiate between command and event flags. diff --git a/apps/website/docs/api-reference/commandkit/interfaces/context-parameters.mdx b/apps/website/docs/api-reference/commandkit/interfaces/context-parameters.mdx index 055f9c5a..d5c2939f 100644 --- a/apps/website/docs/api-reference/commandkit/interfaces/context-parameters.mdx +++ b/apps/website/docs/api-reference/commandkit/interfaces/context-parameters.mdx @@ -22,14 +22,14 @@ interface ContextParameters + ### message diff --git a/apps/website/docs/api-reference/commandkit/interfaces/event-flag-context.mdx b/apps/website/docs/api-reference/commandkit/interfaces/event-flag-context.mdx index be20fb31..11b19d7e 100644 --- a/apps/website/docs/api-reference/commandkit/interfaces/event-flag-context.mdx +++ b/apps/website/docs/api-reference/commandkit/interfaces/event-flag-context.mdx @@ -21,35 +21,35 @@ Context for evaluating event flags in CommandKit. interface EventFlagContext { client: Client; commandkit: CommandKit; - event: { - /** - * The parsed event data, which contains the raw data from the event. - * This can include information like user IDs, channel IDs, and other relevant data. - */ - data: ParsedEvent; - /** - * The name of the event being processed. - * This is the string identifier for the event, such as 'messageCreate' or 'guildMemberAdd'. - */ - event: string; - /** - * The namespace of the event, if applicable. - * This can be used to group related events or commands together. - * It is null if the event does not belong to a specific namespace. - */ - namespace: string | null; - /** - * The arguments passed to the event handler. - * This is an array of arguments that were passed when the event was triggered. - * It can be used to access specific data related to the event. - */ - arguments: any[]; - /** - * A function to retrieve the arguments for a specific event type. - * This allows for type-safe access to the arguments based on the event name. - * @param event - The name of the event to retrieve arguments for. - */ - argumentsAs(event: E): ClientEvents[E]; + event: { + /** + * The parsed event data, which contains the raw data from the event. + * This can include information like user IDs, channel IDs, and other relevant data. + */ + data: ParsedEvent; + /** + * The name of the event being processed. + * This is the string identifier for the event, such as 'messageCreate' or 'guildMemberAdd'. + */ + event: string; + /** + * The namespace of the event, if applicable. + * This can be used to group related events or commands together. + * It is null if the event does not belong to a specific namespace. + */ + namespace: string | null; + /** + * The arguments passed to the event handler. + * This is an array of arguments that were passed when the event was triggered. + * It can be used to access specific data related to the event. + */ + arguments: any[]; + /** + * A function to retrieve the arguments for a specific event type. + * This allows for type-safe access to the arguments based on the event name. + * @param event - The name of the event to retrieve arguments for. + */ + argumentsAs(event: E): ClientEvents[E]; }; command: null; } @@ -61,25 +61,25 @@ interface EventFlagContext { -The Discord client instance. +The Discord client instance. This is the main entry point for interacting with the Discord API. ### commandkit CommandKit`} /> -The CommandKit instance, which provides access to the command framework. +The CommandKit instance, which provides access to the command framework. This includes commands, events, and other features of CommandKit. ### event -ParsedEvent; /** * The name of the event being processed. * This is the string identifier for the event, such as 'messageCreate' or 'guildMemberAdd'. */ event: string; /** * The namespace of the event, if applicable. * This can be used to group related events or commands together. * It is null if the event does not belong to a specific namespace. */ namespace: string | null; /** * The arguments passed to the event handler. * This is an array of arguments that were passed when the event was triggered. * It can be used to access specific data related to the event. */ arguments: any[]; /** * A function to retrieve the arguments for a specific event type. * This allows for type-safe access to the arguments based on the event name. * @param event - The name of the event to retrieve arguments for. */ argumentsAs<E extends keyof ClientEvents>(event: E): ClientEvents[E]; }`} /> +ParsedEvent; /** * The name of the event being processed. * This is the string identifier for the event, such as 'messageCreate' or 'guildMemberAdd'. */ event: string; /** * The namespace of the event, if applicable. * This can be used to group related events or commands together. * It is null if the event does not belong to a specific namespace. */ namespace: string | null; /** * The arguments passed to the event handler. * This is an array of arguments that were passed when the event was triggered. * It can be used to access specific data related to the event. */ arguments: any[]; /** * A function to retrieve the arguments for a specific event type. * This allows for type-safe access to the arguments based on the event name. * @param event - The name of the event to retrieve arguments for. */ argumentsAs<E extends keyof ClientEvents>(event: E): ClientEvents[E]; }`} /> -The event context, which includes information about the event being processed. +The event context, which includes information about the event being processed. This can include the parsed event data, the event name, and the namespace if applicable. ### command -The command context is null for event flags, as they are not tied to a specific command. +The command context is null for event flags, as they are not tied to a specific command. This is used to differentiate between command and event flags. diff --git a/apps/website/docs/api-reference/commandkit/types/filter-function.mdx b/apps/website/docs/api-reference/commandkit/types/filter-function.mdx index 17409dc9..0b7aae9f 100644 --- a/apps/website/docs/api-reference/commandkit/types/filter-function.mdx +++ b/apps/website/docs/api-reference/commandkit/types/filter-function.mdx @@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription'; ## FilterFunction - + Filter function type for analytics events. diff --git a/apps/website/docs/guide/02-commands/01-chat-input-commands.mdx b/apps/website/docs/guide/02-commands/01-chat-input-commands.mdx index 674a984b..7f4bb3ae 100644 --- a/apps/website/docs/guide/02-commands/01-chat-input-commands.mdx +++ b/apps/website/docs/guide/02-commands/01-chat-input-commands.mdx @@ -49,19 +49,3 @@ supports. Learn more about [context menu commands](./03-context-menu-commands.mdx). ::: - -## Guild-based commands - -You can register your chat input command to specific guilds by using -the `guilds` property in the `command` object which accepts an array -of guild IDs. - -```ts title="src/app/commands/ping.ts" {6} -import type { CommandData } from 'commandkit'; - -export const command: CommandData = { - name: 'ping', - description: 'Replies with Pong!', - guilds: ['1055188344188973066'], -}; -``` diff --git a/apps/website/docs/guide/02-commands/05-command-metadata.mdx b/apps/website/docs/guide/02-commands/05-command-metadata.mdx new file mode 100644 index 00000000..b941c5db --- /dev/null +++ b/apps/website/docs/guide/02-commands/05-command-metadata.mdx @@ -0,0 +1,98 @@ +--- +title: Command Metadata +--- + +Command metadata is a way to add additional information to your +commands that CommandKit will handle. + +To get started, you can export a `metadata` object from your command: + +```ts title="src/app/commands/ping.ts" {8-10} +import type { CommandData, CommandMetadata } from 'commandkit'; + +export const command: CommandData = { + name: 'ping', + description: 'Replies with Pong!', +}; + +export const metadata: CommandMetadata = { + // Add your metadata here +}; +``` + +## Metadata properties + +### `userPermissions` + +This is a string, or array of user permission strings that will be +required by the person executing the command. + +```ts title="src/app/commands/ping.ts" +export const metadata: CommandMetadata = { + // If the user does not have the Administrator permission, CommandKit will let them know + userPermissions: 'Administrator', +}; +``` + +### `botPermissions` + +This is a string, or array of bot permission strings that will be +required by your bot to execute the command. This is useful for +commands where your bot needs to have certain permissions in a guild +e.g. moderation commands. + +```ts title="src/app/commands/ping.ts" +export const metadata: CommandMetadata = { + // If the bot does not have these permissions, CommandKit will let them know + botPermissions: ['KickMembers', 'BanMembers'], +}; +``` + +### `guilds` + +This is an array of guild IDs that the command will be registered in, +or be available to be executed (message commands). + +```ts title="src/app/commands/ping.ts" +export const metadata: CommandMetadata = { + guilds: ['1234567890', '1234567891'], +}; +``` + +### `aliases` + +This is an array of alternative command names that will be available +for users to use to execute the command. + +:::warning + +This only works for [message commands](./04-message-commands.mdx). + +::: + +```ts title="src/app/commands/ping.ts" +export const metadata: CommandMetadata = { + aliases: ['p', 'pong'], +}; +``` + +## Generated metadata + +If you'd like to generate metadata dynamically, you can export a +`generateMetadata` function from your command file that should return +a `CommandMetadata` object. + +```ts title="src/app/commands/ping.ts" +import type { CommandMetadataFunction } from 'commandkit'; + +export const generateMetadata: CommandMetadataFunction = async () => { + // Dynamically determine the metadata for the command + + return { + userPermissions: 'Administrator', + botPermissions: ['KickMembers', 'BanMembers'], + guilds: ['1234567890', '1234567891'], + aliases: ['p', 'pong'], + }; +}; +``` diff --git a/apps/website/docs/guide/02-commands/05-after-function.mdx b/apps/website/docs/guide/02-commands/06-after-function.mdx similarity index 100% rename from apps/website/docs/guide/02-commands/05-after-function.mdx rename to apps/website/docs/guide/02-commands/06-after-function.mdx diff --git a/apps/website/docs/guide/02-commands/06-category-directory.mdx b/apps/website/docs/guide/02-commands/07-category-directory.mdx similarity index 100% rename from apps/website/docs/guide/02-commands/06-category-directory.mdx rename to apps/website/docs/guide/02-commands/07-category-directory.mdx diff --git a/apps/website/docs/guide/02-commands/07-middlewares.mdx b/apps/website/docs/guide/02-commands/08-middlewares.mdx similarity index 96% rename from apps/website/docs/guide/02-commands/07-middlewares.mdx rename to apps/website/docs/guide/02-commands/08-middlewares.mdx index 5a2b6ec3..c67bbe3a 100644 --- a/apps/website/docs/guide/02-commands/07-middlewares.mdx +++ b/apps/website/docs/guide/02-commands/08-middlewares.mdx @@ -21,9 +21,7 @@ import { MiddlewareContext } from 'commandkit'; export function beforeExecute(ctx: MiddlewareContext) { // This function will be executed before the command is executed - console.log( - `User ${ctx.interaction.user.id} is about to execute a command`, - ); + console.log(`User ${ctx.interaction.user.id} is about to execute a command`); } export function afterExecute(ctx: MiddlewareContext) { @@ -111,9 +109,7 @@ import type { MiddlewareContext } from 'commandkit'; export function beforeExecute(ctx: MiddlewareContext) { // This middleware will run before any moderation command if (!ctx.interaction.member.permissions.has('KickMembers')) { - throw new Error( - 'You need moderation permissions to use this command', - ); + throw new Error('You need moderation permissions to use this command'); } } ``` diff --git a/apps/website/docs/guide/05-official-plugins/01-commandkit-ai.mdx b/apps/website/docs/guide/05-official-plugins/01-commandkit-ai.mdx index b056eb6d..02f7ac17 100644 --- a/apps/website/docs/guide/05-official-plugins/01-commandkit-ai.mdx +++ b/apps/website/docs/guide/05-official-plugins/01-commandkit-ai.mdx @@ -126,10 +126,7 @@ export const command: CommandData = { export const aiConfig: AiConfig = { inputSchema: z.object({ username: z.string().describe('The username to greet'), - message: z - .string() - .optional() - .describe('Optional custom greeting message'), + message: z.string().optional().describe('Optional custom greeting message'), }), }; @@ -286,9 +283,7 @@ export const getWeather = createTool({ name: 'getWeather', description: 'Get current weather information for a location', inputSchema: z.object({ - location: z - .string() - .describe('The city or location to get weather for'), + location: z.string().describe('The city or location to get weather for'), units: z.enum(['celsius', 'fahrenheit']).default('celsius'), }), async execute(ctx, params) { @@ -427,9 +422,7 @@ export const ai: AiCommand = async (ctx) => { // Validate AI-generated parameters if (action === 'ban' && !isModeratorRole(ctx.message.member)) { - await ctx.message.reply( - '❌ Only moderators can perform ban actions.', - ); + await ctx.message.reply('❌ Only moderators can perform ban actions.'); return; } @@ -535,9 +528,7 @@ configureAI({ await message.reply(`Debug Error: ${error.message}`); } else { // Generic error in production - await message.reply( - 'An error occurred while processing your request.', - ); + await message.reply('An error occurred while processing your request.'); } }, }); From 18df6ab3209baeb9efd6a5f027adf54b7798bd65 Mon Sep 17 00:00:00 2001 From: Avraj Date: Mon, 25 Aug 2025 15:01:01 +0300 Subject: [PATCH 15/19] document cancelAfter + mark cancelAfter as unstable --- .../guide/02-commands/06-after-function.mdx | 24 +++++++++++++++++++ packages/commandkit/src/index.ts | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/apps/website/docs/guide/02-commands/06-after-function.mdx b/apps/website/docs/guide/02-commands/06-after-function.mdx index 2c0b7cc1..2411c9c7 100644 --- a/apps/website/docs/guide/02-commands/06-after-function.mdx +++ b/apps/website/docs/guide/02-commands/06-after-function.mdx @@ -45,3 +45,27 @@ The `after()` function will always be called, regardless of whether the command execution was successful or not. ::: + +## Cancelling the after function + +You can cancel the after function by calling the `cancelAfter` +function with the ID of the after function. + +```ts title="src/app/commands/ping.ts" +import { + unstable_after as after, + unstable_cancelAfter as cancelAfter, +} from 'commandkit'; + +export const chatInput: ChatInputCommand = async (ctx) => { + const id = after(() => { + console.log( + 'This will run after the command has finished executing.', + ); + }); + + if (something) { + cancelAfter(id); + } +}; +``` diff --git a/packages/commandkit/src/index.ts b/packages/commandkit/src/index.ts index 8c668d0f..2a672f8d 100644 --- a/packages/commandkit/src/index.ts +++ b/packages/commandkit/src/index.ts @@ -10,7 +10,7 @@ export { type CommandKitEnvironmentInternalData, CommandKitEnvironment, CommandKitEnvironmentType, - cancelAfter, + cancelAfter as unstable_cancelAfter, after as unstable_after, } from './context/environment'; export * from './app/index'; From 020f050ec6cab33aa4af4c821890b7a5f889e726 Mon Sep 17 00:00:00 2001 From: Avraj Date: Mon, 25 Aug 2025 15:01:29 +0300 Subject: [PATCH 16/19] increase hmr debounce to 700ms --- packages/commandkit/src/cli/development.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/commandkit/src/cli/development.ts b/packages/commandkit/src/cli/development.ts index c273f658..1c27060e 100644 --- a/packages/commandkit/src/cli/development.ts +++ b/packages/commandkit/src/cli/development.ts @@ -164,7 +164,7 @@ export async function bootstrapDevelopmentServer(configPath?: string) { } return false; - }, 300); + }, 700); const isConfigUpdate = (path: string) => { const isConfig = configPaths.some((configPath) => path === configPath); From 2fa7d83f58de50130a95a74220c50dd23a3827b6 Mon Sep 17 00:00:00 2001 From: Avraj Date: Mon, 25 Aug 2025 15:02:48 +0300 Subject: [PATCH 17/19] prettier format --- apps/website/docs/guide/02-commands/06-after-function.mdx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/website/docs/guide/02-commands/06-after-function.mdx b/apps/website/docs/guide/02-commands/06-after-function.mdx index 2411c9c7..210a872d 100644 --- a/apps/website/docs/guide/02-commands/06-after-function.mdx +++ b/apps/website/docs/guide/02-commands/06-after-function.mdx @@ -59,9 +59,7 @@ import { export const chatInput: ChatInputCommand = async (ctx) => { const id = after(() => { - console.log( - 'This will run after the command has finished executing.', - ); + console.log('This will run after the command has finished executing.'); }); if (something) { From c69dbfcd07b60c7ccce7aff7032807081ebf8898 Mon Sep 17 00:00:00 2001 From: Avraj Date: Mon, 25 Aug 2025 16:50:18 +0300 Subject: [PATCH 18/19] update tsdown --- .../src/app/commands/(general)/ping.ts | 2 +- .../src/app/commands/(leveling)/xp.ts | 2 +- packages/commandkit/package.json | 2 +- packages/commandkit/src/cli/build.ts | 17 +++--- pnpm-lock.yaml | 57 ++++++++++--------- 5 files changed, 43 insertions(+), 37 deletions(-) diff --git a/apps/test-bot/src/app/commands/(general)/ping.ts b/apps/test-bot/src/app/commands/(general)/ping.ts index 6c7fcd88..5cb7aca0 100644 --- a/apps/test-bot/src/app/commands/(general)/ping.ts +++ b/apps/test-bot/src/app/commands/(general)/ping.ts @@ -52,7 +52,7 @@ export const generateMetadata: CommandMetadataFunction = async () => { return { userPermissions: 'Administrator', botPermissions: ['KickMembers', 'BanMembers'], - guilds: ['1234567890', '1234567891'], + // guilds: ['1234567890', '1234567891'], aliases: ['p', 'pong'], }; }; diff --git a/apps/test-bot/src/app/commands/(leveling)/xp.ts b/apps/test-bot/src/app/commands/(leveling)/xp.ts index 8d0d19e5..09556930 100644 --- a/apps/test-bot/src/app/commands/(leveling)/xp.ts +++ b/apps/test-bot/src/app/commands/(leveling)/xp.ts @@ -1,5 +1,5 @@ import { ChatInputCommandContext, CommandData } from 'commandkit'; -import { database } from '@/database/store.ts'; +import { database } from '@/database/store'; import { cacheTag } from '@commandkit/cache'; import { AiCommand, AiConfig } from '@commandkit/ai'; import { z } from 'zod'; diff --git a/packages/commandkit/package.json b/packages/commandkit/package.json index 9ceefe07..fb06a395 100644 --- a/packages/commandkit/package.json +++ b/packages/commandkit/package.json @@ -174,7 +174,7 @@ "picocolors": "^1.1.1", "rfdc": "^1.3.1", "rimraf": "^6.0.0", - "tsdown": "^0.14.0", + "tsdown": "^0.14.2", "use-macro": "^1.1.0" }, "devDependencies": { diff --git a/packages/commandkit/src/cli/build.ts b/packages/commandkit/src/cli/build.ts index ac61b774..eeffad9f 100644 --- a/packages/commandkit/src/cli/build.ts +++ b/packages/commandkit/src/cli/build.ts @@ -1,16 +1,17 @@ -import { build, Options } from 'tsdown'; -import { CompilerPlugin, CompilerPluginRuntime } from '../plugins'; -import { loadConfigFile } from '../config/loader'; +import { existsSync } from 'node:fs'; import { writeFile } from 'node:fs/promises'; import { join } from 'node:path'; -import { DevEnv, devEnvFileArgs, ProdEnv, prodEnvFileArgs } from './env'; import { rimraf } from 'rimraf'; -import { performTypeCheck } from './type-checker'; -import { copyLocaleFiles } from './common'; +import { build, Options } from 'tsdown'; + import { MaybeArray } from '../components'; -import { COMMANDKIT_CWD } from '../utils/constants'; +import { loadConfigFile } from '../config/loader'; import { mergeDeep } from '../config/utils'; -import { existsSync } from 'node:fs'; +import { CompilerPlugin, CompilerPluginRuntime } from '../plugins'; +import { COMMANDKIT_CWD } from '../utils/constants'; +import { copyLocaleFiles } from './common'; +import { devEnvFileArgs, prodEnvFileArgs } from './env'; +import { performTypeCheck } from './type-checker'; /** * @private diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b388db52..234e0f2c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -281,8 +281,8 @@ importers: specifier: ^6.0.0 version: 6.0.1 tsdown: - specifier: ^0.14.0 - version: 0.14.1(typescript@5.9.2) + specifier: ^0.14.2 + version: 0.14.2(typescript@5.9.2) use-macro: specifier: ^1.1.0 version: 1.1.0 @@ -2341,8 +2341,8 @@ packages: '@polka/url@1.0.0-next.28': resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} - '@quansync/fs@0.1.4': - resolution: {integrity: sha512-vy/41FCdnIalPTQCb2Wl0ic1caMdzGus4ktDp+gpZesQNydXcx8nhh8qB3qMPbGkictOTaXgXEUUfQEm8DQYoA==} + '@quansync/fs@0.1.5': + resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==} '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} @@ -5164,8 +5164,8 @@ packages: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} - dts-resolver@2.1.1: - resolution: {integrity: sha512-3BiGFhB6mj5Kv+W2vdJseQUYW+SKVzAFJL6YNP6ursbrwy1fXHRotfHi3xLNxe4wZl/K8qbAFeCDjZLjzqxxRw==} + dts-resolver@2.1.2: + resolution: {integrity: sha512-xeXHBQkn2ISSXxbJWD828PFjtyg+/UrMDo7W4Ffcs7+YWCquxU8YjV1KoxuiL+eJ5pg3ll+bC6flVv61L3LKZg==} engines: {node: '>=20.18.0'} peerDependencies: oxc-resolver: '>=11.0.0' @@ -7761,6 +7761,9 @@ packages: quansync@0.2.10: resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -8092,8 +8095,8 @@ packages: robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} - rolldown-plugin-dts@0.15.6: - resolution: {integrity: sha512-AxQlyx3Nszob5QLmVUjz/VnC5BevtUo0h8tliuE0egddss7IbtCBU7GOe7biRU0fJNRQJmQjPKXFcc7K98j3+w==} + rolldown-plugin-dts@0.15.9: + resolution: {integrity: sha512-S2pPcC8h0C8a0ZLDdUTqqtTR9jlryThF3SmH8eZw97FQwgY+hd0x07Zm5algBkmj25S4nvvOusliR1YpImK3LA==} engines: {node: '>=20.18.0'} peerDependencies: '@typescript/native-preview': '>=7.0.0-dev.20250601.1' @@ -8675,8 +8678,8 @@ packages: '@swc/wasm': optional: true - tsdown@0.14.1: - resolution: {integrity: sha512-/nBuFDKZeYln9hAxwWG5Cm55/823sNIVI693iVi0xRFHzf9OVUq4b/lx9PH1TErFr/IQ0kd2hutFbJIPM0XQWA==} + tsdown@0.14.2: + resolution: {integrity: sha512-6ThtxVZoTlR5YJov5rYvH8N1+/S/rD/pGfehdCLGznGgbxz+73EASV1tsIIZkLw2n+SXcERqHhcB/OkyxdKv3A==} engines: {node: '>=20.19.0'} hasBin: true peerDependencies: @@ -8792,8 +8795,8 @@ packages: engines: {node: '>=0.8.0'} hasBin: true - unconfig@7.3.2: - resolution: {integrity: sha512-nqG5NNL2wFVGZ0NA/aCFw0oJ2pxSf1lwg4Z5ill8wd7K4KX/rQbHlwbh+bjctXL5Ly1xtzHenHGOK0b+lG6JVg==} + unconfig@7.3.3: + resolution: {integrity: sha512-QCkQoOnJF8L107gxfHL0uavn7WD9b3dpBcFX6HtfQYmjw2YzWxGuFQ0N0J6tE9oguCBJn9KOvfqYDCMPHIZrBA==} undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -12493,9 +12496,9 @@ snapshots: '@polka/url@1.0.0-next.28': {} - '@quansync/fs@0.1.4': + '@quansync/fs@0.1.5': dependencies: - quansync: 0.2.10 + quansync: 0.2.11 '@radix-ui/number@1.1.1': {} @@ -14356,7 +14359,7 @@ snapshots: ast-kit@2.1.2: dependencies: - '@babel/parser': 7.28.0 + '@babel/parser': 7.28.3 pathe: 2.0.3 ast-types@0.13.4: @@ -15526,7 +15529,7 @@ snapshots: dotenv@16.6.1: {} - dts-resolver@2.1.1: {} + dts-resolver@2.1.2: {} dunder-proto@1.0.1: dependencies: @@ -18643,6 +18646,8 @@ snapshots: quansync@0.2.10: {} + quansync@0.2.11: {} + queue-microtask@1.2.3: {} quick-lru@5.1.1: {} @@ -19061,15 +19066,15 @@ snapshots: robust-predicates@3.0.2: {} - rolldown-plugin-dts@0.15.6(rolldown@1.0.0-beta.34)(typescript@5.9.2): + rolldown-plugin-dts@0.15.9(rolldown@1.0.0-beta.34)(typescript@5.9.2): dependencies: - '@babel/generator': 7.28.0 - '@babel/parser': 7.28.0 + '@babel/generator': 7.28.3 + '@babel/parser': 7.28.3 '@babel/types': 7.28.2 ast-kit: 2.1.2 birpc: 2.5.0 debug: 4.4.1 - dts-resolver: 2.1.1 + dts-resolver: 2.1.2 get-tsconfig: 4.10.1 rolldown: 1.0.0-beta.34 optionalDependencies: @@ -19774,7 +19779,7 @@ snapshots: optionalDependencies: '@swc/core': 1.13.0 - tsdown@0.14.1(typescript@5.9.2): + tsdown@0.14.2(typescript@5.9.2): dependencies: ansis: 4.1.0 cac: 6.7.14 @@ -19784,12 +19789,12 @@ snapshots: empathic: 2.0.0 hookable: 5.5.3 rolldown: 1.0.0-beta.34 - rolldown-plugin-dts: 0.15.6(rolldown@1.0.0-beta.34)(typescript@5.9.2) + rolldown-plugin-dts: 0.15.9(rolldown@1.0.0-beta.34)(typescript@5.9.2) semver: 7.7.2 tinyexec: 1.0.1 tinyglobby: 0.2.14 tree-kill: 1.2.2 - unconfig: 7.3.2 + unconfig: 7.3.3 optionalDependencies: typescript: 5.9.2 transitivePeerDependencies: @@ -19881,12 +19886,12 @@ snapshots: uglify-js@3.19.3: optional: true - unconfig@7.3.2: + unconfig@7.3.3: dependencies: - '@quansync/fs': 0.1.4 + '@quansync/fs': 0.1.5 defu: 6.1.4 jiti: 2.5.1 - quansync: 0.2.10 + quansync: 0.2.11 undici-types@6.21.0: {} From 0935830ddda8351b44e94a310087b528be1f5b86 Mon Sep 17 00:00:00 2001 From: Avraj Date: Mon, 25 Aug 2025 17:12:04 +0300 Subject: [PATCH 19/19] fix lockfile --- pnpm-lock.yaml | 53 +++++++++++++------------------------------------- 1 file changed, 14 insertions(+), 39 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3594cabf..ee8ae656 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -190,7 +190,7 @@ importers: dependencies: ai: specifier: ^5.0.22 - version: 5.0.22(zod@4.1.1) + version: 5.0.23(zod@4.1.1) zod: specifier: ^4.1.0 version: 4.1.1 @@ -643,8 +643,8 @@ importers: packages: - '@ai-sdk/gateway@1.0.11': - resolution: {integrity: sha512-ErwWS3sPOuWy42eE3AVxlKkTa1XjjKBEtNCOylVKMO5KNyz5qie8QVlLYbULOG56dtxX4zTKX3rQNJudplhcmQ==} + '@ai-sdk/gateway@1.0.12': + resolution: {integrity: sha512-IH7bBrirbzUDUhPzl5fsXLaMKxbezXnoTTZEAtbRrL6AiYycH//B9U1u+FHuteISndbDArp0b5ujpW2mhHWceA==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4 @@ -2341,8 +2341,6 @@ packages: '@polka/url@1.0.0-next.28': resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} - '@quansync/fs@0.1.5': - resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==} '@quansync/fs@0.1.5': resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==} @@ -4036,8 +4034,8 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} - ai@5.0.22: - resolution: {integrity: sha512-RZiYhj7Ux7hrLtXkHPcxzdiSZt4NOiC69O5AkNfMCsz3twwz/KRkl9ASptosoOsg833s5yRcTSdIu5z53Sl6Pw==} + ai@5.0.23: + resolution: {integrity: sha512-1zUF0o1zRI7UmSd8u5CKc2iHNhv21tM95Oka81c0CF77GnTbq5RvrAqVuLI+gMyKcIgs99yxA+xc5hJXvh6V+w==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4 @@ -5166,8 +5164,6 @@ packages: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} - dts-resolver@2.1.2: - resolution: {integrity: sha512-xeXHBQkn2ISSXxbJWD828PFjtyg+/UrMDo7W4Ffcs7+YWCquxU8YjV1KoxuiL+eJ5pg3ll+bC6flVv61L3LKZg==} dts-resolver@2.1.2: resolution: {integrity: sha512-xeXHBQkn2ISSXxbJWD828PFjtyg+/UrMDo7W4Ffcs7+YWCquxU8YjV1KoxuiL+eJ5pg3ll+bC6flVv61L3LKZg==} engines: {node: '>=20.18.0'} @@ -7762,9 +7758,6 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} - quansync@0.2.10: - resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} - quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} @@ -8099,8 +8092,8 @@ packages: robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} - rolldown-plugin-dts@0.15.9: - resolution: {integrity: sha512-S2pPcC8h0C8a0ZLDdUTqqtTR9jlryThF3SmH8eZw97FQwgY+hd0x07Zm5algBkmj25S4nvvOusliR1YpImK3LA==} + rolldown-plugin-dts@0.15.8: + resolution: {integrity: sha512-/+MVL9Rja3+6EgbyOijfvd/dZnCQd9W6z3pPNVwJyVXhZP8TLjmWh79YcjRMzuJou0PE1+ViwDgsD3lEdUJhig==} engines: {node: '>=20.18.0'} peerDependencies: '@typescript/native-preview': '>=7.0.0-dev.20250601.1' @@ -8682,8 +8675,6 @@ packages: '@swc/wasm': optional: true - tsdown@0.14.2: - resolution: {integrity: sha512-6ThtxVZoTlR5YJov5rYvH8N1+/S/rD/pGfehdCLGznGgbxz+73EASV1tsIIZkLw2n+SXcERqHhcB/OkyxdKv3A==} tsdown@0.14.2: resolution: {integrity: sha512-6ThtxVZoTlR5YJov5rYvH8N1+/S/rD/pGfehdCLGznGgbxz+73EASV1tsIIZkLw2n+SXcERqHhcB/OkyxdKv3A==} engines: {node: '>=20.19.0'} @@ -8801,8 +8792,6 @@ packages: engines: {node: '>=0.8.0'} hasBin: true - unconfig@7.3.3: - resolution: {integrity: sha512-QCkQoOnJF8L107gxfHL0uavn7WD9b3dpBcFX6HtfQYmjw2YzWxGuFQ0N0J6tE9oguCBJn9KOvfqYDCMPHIZrBA==} unconfig@7.3.3: resolution: {integrity: sha512-QCkQoOnJF8L107gxfHL0uavn7WD9b3dpBcFX6HtfQYmjw2YzWxGuFQ0N0J6tE9oguCBJn9KOvfqYDCMPHIZrBA==} @@ -9347,7 +9336,7 @@ packages: snapshots: - '@ai-sdk/gateway@1.0.11(zod@4.1.1)': + '@ai-sdk/gateway@1.0.12(zod@4.1.1)': dependencies: '@ai-sdk/provider': 2.0.0 '@ai-sdk/provider-utils': 3.0.5(zod@4.1.1) @@ -12504,11 +12493,9 @@ snapshots: '@polka/url@1.0.0-next.28': {} - '@quansync/fs@0.1.5': '@quansync/fs@0.1.5': dependencies: quansync: 0.2.11 - quansync: 0.2.11 '@radix-ui/number@1.1.1': {} @@ -13934,7 +13921,7 @@ snapshots: '@types/react-router@5.1.20': dependencies: '@types/history': 4.7.11 - '@types/react': 19.1.11 + '@types/react': 19.1.8 '@types/react@19.1.10': dependencies: @@ -13988,7 +13975,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 24.3.0 + '@types/node': 22.18.0 '@types/ws@8.5.13': dependencies: @@ -14258,9 +14245,9 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - ai@5.0.22(zod@4.1.1): + ai@5.0.23(zod@4.1.1): dependencies: - '@ai-sdk/gateway': 1.0.11(zod@4.1.1) + '@ai-sdk/gateway': 1.0.12(zod@4.1.1) '@ai-sdk/provider': 2.0.0 '@ai-sdk/provider-utils': 3.0.5(zod@4.1.1) '@opentelemetry/api': 1.9.0 @@ -14369,7 +14356,6 @@ snapshots: ast-kit@2.1.2: dependencies: - '@babel/parser': 7.28.3 '@babel/parser': 7.28.3 pathe: 2.0.3 @@ -15540,7 +15526,6 @@ snapshots: dotenv@16.6.1: {} - dts-resolver@2.1.2: {} dts-resolver@2.1.2: {} dunder-proto@1.0.1: @@ -18656,8 +18641,6 @@ snapshots: dependencies: side-channel: 1.1.0 - quansync@0.2.10: {} - quansync@0.2.11: {} queue-microtask@1.2.3: {} @@ -19078,10 +19061,8 @@ snapshots: robust-predicates@3.0.2: {} - rolldown-plugin-dts@0.15.9(rolldown@1.0.0-beta.34)(typescript@5.9.2): + rolldown-plugin-dts@0.15.8(rolldown@1.0.0-beta.34)(typescript@5.9.2): dependencies: - '@babel/generator': 7.28.3 - '@babel/parser': 7.28.3 '@babel/generator': 7.28.3 '@babel/parser': 7.28.3 '@babel/types': 7.28.2 @@ -19089,7 +19070,6 @@ snapshots: birpc: 2.5.0 debug: 4.4.1 dts-resolver: 2.1.2 - dts-resolver: 2.1.2 get-tsconfig: 4.10.1 rolldown: 1.0.0-beta.34 optionalDependencies: @@ -19794,7 +19774,6 @@ snapshots: optionalDependencies: '@swc/core': 1.13.0 - tsdown@0.14.2(typescript@5.9.2): tsdown@0.14.2(typescript@5.9.2): dependencies: ansis: 4.1.0 @@ -19805,13 +19784,12 @@ snapshots: empathic: 2.0.0 hookable: 5.5.3 rolldown: 1.0.0-beta.34 - rolldown-plugin-dts: 0.15.9(rolldown@1.0.0-beta.34)(typescript@5.9.2) + rolldown-plugin-dts: 0.15.8(rolldown@1.0.0-beta.34)(typescript@5.9.2) semver: 7.7.2 tinyexec: 1.0.1 tinyglobby: 0.2.14 tree-kill: 1.2.2 unconfig: 7.3.3 - unconfig: 7.3.3 optionalDependencies: typescript: 5.9.2 transitivePeerDependencies: @@ -19903,15 +19881,12 @@ snapshots: uglify-js@3.19.3: optional: true - unconfig@7.3.3: unconfig@7.3.3: dependencies: - '@quansync/fs': 0.1.5 '@quansync/fs': 0.1.5 defu: 6.1.4 jiti: 2.5.1 quansync: 0.2.11 - quansync: 0.2.11 undici-types@6.21.0: {}