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 facbe09a..d2049f2e 100644 --- a/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts +++ b/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts @@ -10,16 +10,18 @@ export function beforeExecute(ctx: MiddlewareContext) { // `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', - ); + ctx.store.set('foo', 'bar'); + + // Logger.info(`Command-scoped middleware: ${ctx.commandName} will be stopped!`); + // 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`); - }); + // after(() => { + // Logger.info(`after() has been called in command-scoped middleware: ping`); + // }); - stopMiddlewares(); + // 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 5cb7aca0..c332e0f5 100644 --- a/apps/test-bot/src/app/commands/(general)/ping.ts +++ b/apps/test-bot/src/app/commands/(general)/ping.ts @@ -76,14 +76,15 @@ export async function autocomplete({ interaction.respond(filtered); } -export async function message({ message }: MessageCommandContext) { - message.reply('Pong!'); +export async function message(ctx: MessageCommandContext) { + Logger.debug`Store data ${ctx.store.get('foo')} | ${ctx.store}`; + ctx.message.reply('Pong!'); } -export async function chatInput({ - interaction, - client, -}: ChatInputCommandContext) { +export async function chatInput(ctx: ChatInputCommandContext) { + const { interaction } = ctx; + Logger.debug`Store data ${ctx.store.get('foo')} | ${ctx.store}`; + if (!interaction.channel) return; const button = new ButtonKit() diff --git a/packages/ai/src/context.ts b/packages/ai/src/context.ts index f501c989..8c8d2f69 100644 --- a/packages/ai/src/context.ts +++ b/packages/ai/src/context.ts @@ -1,5 +1,5 @@ import type { CommandKit } from 'commandkit'; -import { Client, Message } from 'discord.js'; +import { Client, Collection, Message } from 'discord.js'; /** * Options for the AI context. @@ -48,7 +48,7 @@ export class AiContext< /** * A key-value store to hold additional data. */ - public store = new Map(); + public store = new Collection(); /** * Creates a new instance of AiContext. diff --git a/packages/commandkit/hooks.cjs b/packages/commandkit/hooks.cjs index 1fda59ea..00e9e3c6 100644 --- a/packages/commandkit/hooks.cjs +++ b/packages/commandkit/hooks.cjs @@ -1,5 +1,6 @@ const { useEnvironment, + useStore, getContext, getCommandKit, } = require('./dist/context/async-context.js'); @@ -51,6 +52,7 @@ function useEvent() { module.exports = { useAnyEnvironment, + useStore, useClient, useCommandKit, useCommand, diff --git a/packages/commandkit/src/app/commands/Context.ts b/packages/commandkit/src/app/commands/Context.ts index df5b1be0..5510b6b1 100644 --- a/packages/commandkit/src/app/commands/Context.ts +++ b/packages/commandkit/src/app/commands/Context.ts @@ -3,6 +3,7 @@ import { Awaitable, ChatInputCommandInteraction, Client, + Collection, Guild, Interaction, Locale, @@ -64,7 +65,7 @@ export interface ContextParameters< message: T extends 'message' ? Message : never; forwarded?: boolean; messageCommandParser?: T extends 'message' ? MessageCommandParser : never; - store?: Map; + store?: Collection; customArgs?: Args; } @@ -244,7 +245,7 @@ export class Context< * @private * @internal */ - #store: Map; + #store: Collection; /** * @private @@ -265,7 +266,7 @@ export class Context< this.interaction = config.interaction; this.message = config.message; this.client = commandkit.client; - this.#store = config.store ?? new Map(); + this.#store = config.environment?.store ?? config.store ?? new Collection(); this.command = config.command; if (config.interaction) { @@ -292,7 +293,7 @@ export class Context< * This store is shared across all contexts in the same command execution, including the cloned contexts and middleware contexts. */ public get store() { - return this.#store; + return this.config.environment?.store ?? this.#store; } /** diff --git a/packages/commandkit/src/context/async-context.ts b/packages/commandkit/src/context/async-context.ts index a7cc915a..7bc30e42 100644 --- a/packages/commandkit/src/context/async-context.ts +++ b/packages/commandkit/src/context/async-context.ts @@ -2,6 +2,7 @@ import { AsyncLocalStorage } from 'node:async_hooks'; import { CommandKitEnvironment } from './environment'; import { CommandKit } from '../commandkit'; import { isCommandKitError } from '../utils/error-codes'; +import { Collection } from 'discord.js'; const kCommandWorker = Symbol('commandkitCommandWorker'); const context = new AsyncLocalStorage(); @@ -132,3 +133,10 @@ export function useEnvironment(): CommandKitEnvironment { return ctx; } + +/** + * Use the shared data store in the current environment. Throws an error if no context is found. + */ +export function useStore(): Collection { + return useEnvironment().store; +} diff --git a/packages/commandkit/src/context/environment.ts b/packages/commandkit/src/context/environment.ts index b4155891..9447a1c6 100644 --- a/packages/commandkit/src/context/environment.ts +++ b/packages/commandkit/src/context/environment.ts @@ -1,7 +1,8 @@ import { randomUUID } from 'node:crypto'; import { CommandKit } from '../commandkit'; -import { GenericFunction, getContext } from './async-context'; +import { GenericFunction, getContext, useEnvironment } from './async-context'; import type { Context } from '../app'; +import { Collection } from 'discord.js'; /** * Represents the internal data structure for the CommandKit environment. @@ -45,6 +46,10 @@ export interface CommandKitEnvironmentInternalData { * This can be used to access request-specific data or application state. */ context: Context | null; + /** + * Shared collection instance for arbitrary data storage + */ + store: Collection; } /** @@ -60,6 +65,7 @@ export class CommandKitEnvironment { markStart: 0, markEnd: 0, context: null, + store: new Collection(), }; /** @@ -134,6 +140,13 @@ export class CommandKitEnvironment { return this.#data.variables; } + /** + * The shared store for this environment + */ + public get store(): Collection { + return this.#data.store; + } + /** * Register a deferred function. * @param fn - The deferred function to register. diff --git a/packages/tasks/src/context.ts b/packages/tasks/src/context.ts index 447cd370..c36e9ca2 100644 --- a/packages/tasks/src/context.ts +++ b/packages/tasks/src/context.ts @@ -1,4 +1,4 @@ -import type { CommandKit, Client } from 'commandkit'; +import { type CommandKit, type Client, Collection } from 'commandkit'; import { Task } from './task'; /** @@ -70,7 +70,7 @@ export class TaskContext = Record> { * }); * ``` */ - public readonly store = new Map(); + public readonly store = new Collection(); /** * Creates a new task execution context.