Skip to content

Commit 5685f97

Browse files
authored
Merge pull request #517 from underctrl-io/shared-store
feat: add shared store within environment
2 parents ac03768 + 2fc2c2c commit 5685f97

File tree

8 files changed

+50
-23
lines changed

8 files changed

+50
-23
lines changed

apps/test-bot/src/app/commands/(general)/+ping.middleware.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,18 @@ export function beforeExecute(ctx: MiddlewareContext) {
1010
// `Command-scoped middleware: ${ctx.commandName} will be executed!`,
1111
// );
1212

13-
Logger.info(`Command-scoped middleware: ${ctx.commandName} will be stopped!`);
14-
Logger.info(
15-
'None of the other beforeExecute middlewares are supposed to be executed',
16-
);
13+
ctx.store.set('foo', 'bar');
14+
15+
// Logger.info(`Command-scoped middleware: ${ctx.commandName} will be stopped!`);
16+
// Logger.info(
17+
// 'None of the other beforeExecute middlewares are supposed to be executed',
18+
// );
1719

18-
after(() => {
19-
Logger.info(`after() has been called in command-scoped middleware: ping`);
20-
});
20+
// after(() => {
21+
// Logger.info(`after() has been called in command-scoped middleware: ping`);
22+
// });
2123

22-
stopMiddlewares();
24+
// stopMiddlewares();
2325
}
2426

2527
export function afterExecute(ctx: MiddlewareContext) {

apps/test-bot/src/app/commands/(general)/ping.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,15 @@ export async function autocomplete({
7676
interaction.respond(filtered);
7777
}
7878

79-
export async function message({ message }: MessageCommandContext) {
80-
message.reply('Pong!');
79+
export async function message(ctx: MessageCommandContext) {
80+
Logger.debug`Store data ${ctx.store.get('foo')} | ${ctx.store}`;
81+
ctx.message.reply('Pong!');
8182
}
8283

83-
export async function chatInput({
84-
interaction,
85-
client,
86-
}: ChatInputCommandContext) {
84+
export async function chatInput(ctx: ChatInputCommandContext) {
85+
const { interaction } = ctx;
86+
Logger.debug`Store data ${ctx.store.get('foo')} | ${ctx.store}`;
87+
8788
if (!interaction.channel) return;
8889

8990
const button = new ButtonKit()

packages/ai/src/context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { CommandKit } from 'commandkit';
2-
import { Client, Message } from 'discord.js';
2+
import { Client, Collection, Message } from 'discord.js';
33

44
/**
55
* Options for the AI context.
@@ -48,7 +48,7 @@ export class AiContext<
4848
/**
4949
* A key-value store to hold additional data.
5050
*/
51-
public store = new Map<string, any>();
51+
public store = new Collection<any, any>();
5252

5353
/**
5454
* Creates a new instance of AiContext.

packages/commandkit/hooks.cjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const {
22
useEnvironment,
3+
useStore,
34
getContext,
45
getCommandKit,
56
} = require('./dist/context/async-context.js');
@@ -51,6 +52,7 @@ function useEvent() {
5152

5253
module.exports = {
5354
useAnyEnvironment,
55+
useStore,
5456
useClient,
5557
useCommandKit,
5658
useCommand,

packages/commandkit/src/app/commands/Context.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
Awaitable,
44
ChatInputCommandInteraction,
55
Client,
6+
Collection,
67
Guild,
78
Interaction,
89
Locale,
@@ -64,7 +65,7 @@ export interface ContextParameters<
6465
message: T extends 'message' ? Message : never;
6566
forwarded?: boolean;
6667
messageCommandParser?: T extends 'message' ? MessageCommandParser : never;
67-
store?: Map<string, any>;
68+
store?: Collection<string, any>;
6869
customArgs?: Args;
6970
}
7071

@@ -244,7 +245,7 @@ export class Context<
244245
* @private
245246
* @internal
246247
*/
247-
#store: Map<string, any>;
248+
#store: Collection<any, any>;
248249

249250
/**
250251
* @private
@@ -265,7 +266,7 @@ export class Context<
265266
this.interaction = config.interaction;
266267
this.message = config.message;
267268
this.client = commandkit.client;
268-
this.#store = config.store ?? new Map();
269+
this.#store = config.environment?.store ?? config.store ?? new Collection();
269270
this.command = config.command;
270271

271272
if (config.interaction) {
@@ -292,7 +293,7 @@ export class Context<
292293
* This store is shared across all contexts in the same command execution, including the cloned contexts and middleware contexts.
293294
*/
294295
public get store() {
295-
return this.#store;
296+
return this.config.environment?.store ?? this.#store;
296297
}
297298

298299
/**

packages/commandkit/src/context/async-context.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { AsyncLocalStorage } from 'node:async_hooks';
22
import { CommandKitEnvironment } from './environment';
33
import { CommandKit } from '../commandkit';
44
import { isCommandKitError } from '../utils/error-codes';
5+
import { Collection } from 'discord.js';
56

67
const kCommandWorker = Symbol('commandkitCommandWorker');
78
const context = new AsyncLocalStorage<CommandKitEnvironment>();
@@ -132,3 +133,10 @@ export function useEnvironment(): CommandKitEnvironment {
132133

133134
return ctx;
134135
}
136+
137+
/**
138+
* Use the shared data store in the current environment. Throws an error if no context is found.
139+
*/
140+
export function useStore(): Collection<any, any> {
141+
return useEnvironment().store;
142+
}

packages/commandkit/src/context/environment.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { randomUUID } from 'node:crypto';
22
import { CommandKit } from '../commandkit';
3-
import { GenericFunction, getContext } from './async-context';
3+
import { GenericFunction, getContext, useEnvironment } from './async-context';
44
import type { Context } from '../app';
5+
import { Collection } from 'discord.js';
56

67
/**
78
* Represents the internal data structure for the CommandKit environment.
@@ -45,6 +46,10 @@ export interface CommandKitEnvironmentInternalData {
4546
* This can be used to access request-specific data or application state.
4647
*/
4748
context: Context | null;
49+
/**
50+
* Shared collection instance for arbitrary data storage
51+
*/
52+
store: Collection<any, any>;
4853
}
4954

5055
/**
@@ -60,6 +65,7 @@ export class CommandKitEnvironment {
6065
markStart: 0,
6166
markEnd: 0,
6267
context: null,
68+
store: new Collection(),
6369
};
6470

6571
/**
@@ -134,6 +140,13 @@ export class CommandKitEnvironment {
134140
return this.#data.variables;
135141
}
136142

143+
/**
144+
* The shared store for this environment
145+
*/
146+
public get store(): Collection<any, any> {
147+
return this.#data.store;
148+
}
149+
137150
/**
138151
* Register a deferred function.
139152
* @param fn - The deferred function to register.

packages/tasks/src/context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CommandKit, Client } from 'commandkit';
1+
import { type CommandKit, type Client, Collection } from 'commandkit';
22
import { Task } from './task';
33

44
/**
@@ -70,7 +70,7 @@ export class TaskContext<T extends Record<string, any> = Record<string, any>> {
7070
* });
7171
* ```
7272
*/
73-
public readonly store = new Map<string, any>();
73+
public readonly store = new Collection<any, any>();
7474

7575
/**
7676
* Creates a new task execution context.

0 commit comments

Comments
 (0)