@commandkit/tasks 0.0.0-dev.20250724145623

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,11 @@
1
+ Copyright 2025 Avraj Sahota
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4
+ documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
5
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
6
+ persons to whom the Software is furnished to do so.
7
+
8
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
9
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
10
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
11
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,301 @@
1
+ # @commandkit/tasks
2
+
3
+ Task management plugin for CommandKit. Provides on-demand task creation and management with support for both static and dynamic tasks.
4
+
5
+ ## Features
6
+
7
+ - **Static Tasks**: Define tasks in your codebase that run on schedules
8
+ - **Dynamic Tasks**: Create tasks on-demand from commands or events
9
+ - **Multiple Drivers**: Support for in-memory, SQLite, and BullMQ persistence
10
+ - **HMR Support**: Hot reload tasks during development
11
+ - **Flexible Scheduling**: Support for cron expressions, dates, and dynamic schedules
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @commandkit/tasks
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ### 1. Add the plugin to your CommandKit configuration
22
+
23
+ ```ts
24
+ import { tasks } from '@commandkit/tasks';
25
+
26
+ export default {
27
+ plugins: [
28
+ tasks({
29
+ tasksPath: 'app/tasks', // optional, defaults to 'app/tasks'
30
+ enableHMR: true, // optional, defaults to true in development
31
+ }),
32
+ ],
33
+ };
34
+ ```
35
+
36
+ ### 2. Create static tasks
37
+
38
+ Create a file in `src/app/tasks/`:
39
+
40
+ ```ts
41
+ import { task } from '@commandkit/tasks';
42
+
43
+ export const refreshExchangeRate = task({
44
+ name: 'refresh-exchange-rate',
45
+ schedule: '0 0 * * *', // cron expression - daily at midnight
46
+ async execute(ctx) {
47
+ // Fetch latest exchange rates
48
+ const rates = await fetchExchangeRates();
49
+ await updateDatabase(rates);
50
+ },
51
+ });
52
+
53
+ export const cleanupOldData = task({
54
+ name: 'cleanup-old-data',
55
+ schedule: () => new Date(Date.now() + 24 * 60 * 60 * 1000), // tomorrow
56
+ async prepare(ctx) {
57
+ // Only run if there's old data to clean
58
+ return await hasOldData();
59
+ },
60
+ async execute(ctx) {
61
+ await cleanupOldRecords();
62
+ },
63
+ });
64
+ ```
65
+
66
+ ### 3. Create dynamic tasks from commands
67
+
68
+ ```ts
69
+ import { createTask } from '@commandkit/tasks';
70
+
71
+ export default {
72
+ name: 'remind-me',
73
+ description: 'Set a reminder',
74
+ async run(ctx) {
75
+ const time = ctx.interaction.options.getString('time');
76
+ const reason = ctx.interaction.options.getString('reason');
77
+
78
+ await createTask({
79
+ name: 'reminder',
80
+ schedule: new Date(Date.now() + ms(time)),
81
+ data: {
82
+ userId: ctx.interaction.user.id,
83
+ reason,
84
+ },
85
+ });
86
+
87
+ await ctx.interaction.reply('Reminder set!');
88
+ },
89
+ };
90
+ ```
91
+
92
+ ## API Reference
93
+
94
+ ### Plugin Options
95
+
96
+ ```ts
97
+ interface TasksPluginOptions {
98
+ tasksPath?: string; // Path to tasks directory, defaults to 'app/tasks'
99
+ enableHMR?: boolean; // Enable HMR for tasks, defaults to true in development
100
+ }
101
+ ```
102
+
103
+ ### Task Definition
104
+
105
+ ```ts
106
+ interface TaskDefinition {
107
+ name: string;
108
+ schedule?: ScheduleType;
109
+ prepare?: (ctx: TaskContext) => Promise<boolean> | boolean;
110
+ execute: (ctx: TaskContext) => Promise<void> | void;
111
+ }
112
+ ```
113
+
114
+ ### Schedule Types
115
+
116
+ ```ts
117
+ type ScheduleType =
118
+ | Date
119
+ | number // unix timestamp
120
+ | string // cron expression or date string
121
+ | (() => Date | number | string); // dynamic schedule
122
+ ```
123
+
124
+ **Cron Expressions**: The plugin supports standard cron expressions (e.g., `'0 0 * * *'` for daily at midnight). Cron parsing is handled by `cron-parser` for in-memory and SQLite drivers, while BullMQ uses its built-in cron support.
125
+
126
+ ### Task Context
127
+
128
+ ```ts
129
+ interface TaskContext {
130
+ task: TaskData;
131
+ commandkit: CommandKit;
132
+ client: Client;
133
+ }
134
+ ```
135
+
136
+ ### Functions
137
+
138
+ #### `task(definition: TaskDefinition)`
139
+
140
+ Creates a task definition.
141
+
142
+ ```ts
143
+ import { task } from '@commandkit/tasks';
144
+
145
+ export const myTask = task({
146
+ name: 'my-task',
147
+ schedule: '0 0 * * *',
148
+ async execute(ctx) {
149
+ // Task logic here
150
+ },
151
+ });
152
+ ```
153
+
154
+ #### `createTask(options: CreateTaskOptions)`
155
+
156
+ Creates a dynamic task.
157
+
158
+ ```ts
159
+ import { createTask } from '@commandkit/tasks';
160
+
161
+ await createTask({
162
+ name: 'reminder',
163
+ schedule: new Date(Date.now() + 60000), // 1 minute from now
164
+ data: { userId: '123', message: 'Hello!' },
165
+ });
166
+ ```
167
+
168
+ #### `executeTask(taskOrName: TaskDefinition | string)`
169
+
170
+ Executes a task immediately.
171
+
172
+ ```ts
173
+ import { executeTask } from '@commandkit/tasks';
174
+
175
+ await executeTask('my-task');
176
+ // or
177
+ await executeTask(myTask);
178
+ ```
179
+
180
+ #### `cancelTask(taskOrName: TaskDefinition | string)`
181
+
182
+ Cancels a scheduled task.
183
+
184
+ ```ts
185
+ import { cancelTask } from '@commandkit/tasks';
186
+
187
+ await cancelTask('my-task');
188
+ ```
189
+
190
+ #### `pauseTask(taskOrName: TaskDefinition | string)`
191
+
192
+ Pauses a task.
193
+
194
+ ```ts
195
+ import { pauseTask } from '@commandkit/tasks';
196
+
197
+ await pauseTask('my-task');
198
+ ```
199
+
200
+ #### `resumeTask(taskOrName: TaskDefinition | string)`
201
+
202
+ Resumes a paused task.
203
+
204
+ ```ts
205
+ import { resumeTask } from '@commandkit/tasks';
206
+
207
+ await resumeTask('my-task');
208
+ ```
209
+
210
+ ## Persistence Drivers
211
+
212
+ The drivers handle all scheduling and timing internally. When a task is due for execution, the driver calls the plugin's execution handler.
213
+
214
+ ### In-Memory Driver (Default)
215
+
216
+ ```ts
217
+ import { driver } from '@commandkit/tasks';
218
+ import { InMemoryDriver } from '@commandkit/tasks/drivers';
219
+
220
+ driver.use(new InMemoryDriver());
221
+ ```
222
+
223
+ ### SQLite Driver
224
+
225
+ ```ts
226
+ import { driver } from '@commandkit/tasks';
227
+ import { SQLiteDriver } from '@commandkit/tasks/drivers';
228
+
229
+ driver.use(new SQLiteDriver('./tasks.db'));
230
+ ```
231
+
232
+ **Note**: Requires `sqlite3`, `sqlite`, and `cron-parser` packages to be installed.
233
+
234
+ ### BullMQ Driver
235
+
236
+ ```ts
237
+ import { driver } from '@commandkit/tasks';
238
+ import { BullMQDriver } from '@commandkit/tasks/drivers';
239
+
240
+ driver.use(new BullMQDriver({
241
+ host: 'localhost',
242
+ port: 6379,
243
+ }));
244
+ ```
245
+
246
+ **Note**: Requires `bullmq` package to be installed. BullMQ has built-in cron support, so no additional cron parsing is needed.
247
+
248
+ ## Examples
249
+
250
+ ### Scheduled Database Backup
251
+
252
+ ```ts
253
+ import { task } from '@commandkit/tasks';
254
+
255
+ export const databaseBackup = task({
256
+ name: 'database-backup',
257
+ schedule: '0 2 * * *', // Daily at 2 AM
258
+ async execute(ctx) {
259
+ const backup = await createBackup();
260
+ await uploadToCloud(backup);
261
+ await ctx.client.channels.cache.get('backup-log')?.send('Backup completed!');
262
+ },
263
+ });
264
+ ```
265
+
266
+ ### User Reminder System
267
+
268
+ ```ts
269
+ import { task } from '@commandkit/tasks';
270
+
271
+ export const reminder = task({
272
+ name: 'reminder',
273
+ async execute(ctx) {
274
+ const { userId, message } = ctx.task.data;
275
+ const user = await ctx.client.users.fetch(userId);
276
+ await user.send(`Reminder: ${message}`);
277
+ },
278
+ });
279
+ ```
280
+
281
+ ### Conditional Task
282
+
283
+ ```ts
284
+ import { task } from '@commandkit/tasks';
285
+
286
+ export const maintenanceCheck = task({
287
+ name: 'maintenance-check',
288
+ schedule: '0 */6 * * *', // Every 6 hours
289
+ async prepare(ctx) {
290
+ // Only run if maintenance mode is not enabled
291
+ return !ctx.commandkit.store.get('maintenance-mode');
292
+ },
293
+ async execute(ctx) {
294
+ await performMaintenanceChecks();
295
+ },
296
+ });
297
+ ```
298
+
299
+ ## License
300
+
301
+ MIT
@@ -0,0 +1,103 @@
1
+ import type { CommandKit, Client } from 'commandkit';
2
+ import { Task } from './task';
3
+ /**
4
+ * Data structure for creating a task execution context.
5
+ *
6
+ * This interface contains all the necessary information to create a task context,
7
+ * including the task instance, custom data, and CommandKit instance.
8
+ */
9
+ export interface TaskContextData<T extends Record<string, any> = Record<string, any>> {
10
+ /** The task instance that is being executed */
11
+ task: Task;
12
+ /** Custom data passed to the task execution */
13
+ data: T;
14
+ /** The CommandKit instance for accessing bot functionality */
15
+ commandkit: CommandKit;
16
+ }
17
+ /**
18
+ * Execution context provided to task functions.
19
+ *
20
+ * This class provides access to the task instance, custom data, CommandKit instance,
21
+ * and a temporary store for sharing data between prepare and execute functions.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { task } from '@commandkit/tasks';
26
+ *
27
+ * export const reminderTask = task({
28
+ * name: 'reminder',
29
+ * async execute(ctx) {
30
+ * // Access custom data passed to the task
31
+ * const { userId, message } = ctx.data;
32
+ *
33
+ * // Access CommandKit and Discord.js client
34
+ * const user = await ctx.commandkit.client.users.fetch(userId);
35
+ * await user.send(`Reminder: ${message}`);
36
+ *
37
+ * // Use the store to share data between prepare and execute
38
+ * const processedCount = ctx.store.get('processedCount') || 0;
39
+ * ctx.store.set('processedCount', processedCount + 1);
40
+ * },
41
+ * });
42
+ * ```
43
+ */
44
+ export declare class TaskContext<T extends Record<string, any> = Record<string, any>> {
45
+ private _data;
46
+ /**
47
+ * Temporary key-value store for sharing data between prepare and execute functions.
48
+ *
49
+ * This store is useful for passing computed values or state between the prepare
50
+ * and execute phases of task execution.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * export const conditionalTask = task({
55
+ * name: 'conditional-task',
56
+ * async prepare(ctx) {
57
+ * const shouldRun = await checkConditions();
58
+ * ctx.store.set('shouldRun', shouldRun);
59
+ * return shouldRun;
60
+ * },
61
+ * async execute(ctx) {
62
+ * const shouldRun = ctx.store.get('shouldRun');
63
+ * if (shouldRun) {
64
+ * await performAction();
65
+ * }
66
+ * },
67
+ * });
68
+ * ```
69
+ */
70
+ readonly store: Map<string, any>;
71
+ /**
72
+ * Creates a new task execution context.
73
+ *
74
+ * @param _data - The task context data containing task, data, and CommandKit instance
75
+ */
76
+ constructor(_data: TaskContextData<T>);
77
+ /**
78
+ * Gets the task instance being executed.
79
+ *
80
+ * @returns The Task instance
81
+ */
82
+ get task(): Task;
83
+ /**
84
+ * Gets the Discord.js client.
85
+ * @returns The Discord.js client
86
+ */
87
+ get client(): Client;
88
+ /**
89
+ * Gets the custom data passed to the task execution.
90
+ *
91
+ * @returns The custom data object
92
+ */
93
+ get data(): T;
94
+ /**
95
+ * Gets the CommandKit instance for accessing bot functionality.
96
+ *
97
+ * This provides access to the Discord.js client, CommandKit store, and other
98
+ * bot-related functionality.
99
+ *
100
+ * @returns The CommandKit instance
101
+ */
102
+ get commandkit(): CommandKit;
103
+ }
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TaskContext = void 0;
4
+ /**
5
+ * Execution context provided to task functions.
6
+ *
7
+ * This class provides access to the task instance, custom data, CommandKit instance,
8
+ * and a temporary store for sharing data between prepare and execute functions.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { task } from '@commandkit/tasks';
13
+ *
14
+ * export const reminderTask = task({
15
+ * name: 'reminder',
16
+ * async execute(ctx) {
17
+ * // Access custom data passed to the task
18
+ * const { userId, message } = ctx.data;
19
+ *
20
+ * // Access CommandKit and Discord.js client
21
+ * const user = await ctx.commandkit.client.users.fetch(userId);
22
+ * await user.send(`Reminder: ${message}`);
23
+ *
24
+ * // Use the store to share data between prepare and execute
25
+ * const processedCount = ctx.store.get('processedCount') || 0;
26
+ * ctx.store.set('processedCount', processedCount + 1);
27
+ * },
28
+ * });
29
+ * ```
30
+ */
31
+ class TaskContext {
32
+ /**
33
+ * Creates a new task execution context.
34
+ *
35
+ * @param _data - The task context data containing task, data, and CommandKit instance
36
+ */
37
+ constructor(_data) {
38
+ this._data = _data;
39
+ /**
40
+ * Temporary key-value store for sharing data between prepare and execute functions.
41
+ *
42
+ * This store is useful for passing computed values or state between the prepare
43
+ * and execute phases of task execution.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * export const conditionalTask = task({
48
+ * name: 'conditional-task',
49
+ * async prepare(ctx) {
50
+ * const shouldRun = await checkConditions();
51
+ * ctx.store.set('shouldRun', shouldRun);
52
+ * return shouldRun;
53
+ * },
54
+ * async execute(ctx) {
55
+ * const shouldRun = ctx.store.get('shouldRun');
56
+ * if (shouldRun) {
57
+ * await performAction();
58
+ * }
59
+ * },
60
+ * });
61
+ * ```
62
+ */
63
+ this.store = new Map();
64
+ }
65
+ /**
66
+ * Gets the task instance being executed.
67
+ *
68
+ * @returns The Task instance
69
+ */
70
+ get task() {
71
+ return this._data.task;
72
+ }
73
+ /**
74
+ * Gets the Discord.js client.
75
+ * @returns The Discord.js client
76
+ */
77
+ get client() {
78
+ return this._data.commandkit.client;
79
+ }
80
+ /**
81
+ * Gets the custom data passed to the task execution.
82
+ *
83
+ * @returns The custom data object
84
+ */
85
+ get data() {
86
+ return this._data.data;
87
+ }
88
+ /**
89
+ * Gets the CommandKit instance for accessing bot functionality.
90
+ *
91
+ * This provides access to the Discord.js client, CommandKit store, and other
92
+ * bot-related functionality.
93
+ *
94
+ * @returns The CommandKit instance
95
+ */
96
+ get commandkit() {
97
+ return this._data.commandkit;
98
+ }
99
+ }
100
+ exports.TaskContext = TaskContext;
101
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udGV4dC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9jb250ZXh0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQW9CQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0EwQkc7QUFDSCxNQUFhLFdBQVc7SUEyQnRCOzs7O09BSUc7SUFDSCxZQUEyQixLQUF5QjtRQUF6QixVQUFLLEdBQUwsS0FBSyxDQUFvQjtRQS9CcEQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O1dBdUJHO1FBQ2EsVUFBSyxHQUFHLElBQUksR0FBRyxFQUFlLENBQUM7SUFPUSxDQUFDO0lBRXhEOzs7O09BSUc7SUFDSCxJQUFXLElBQUk7UUFDYixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDO0lBQ3pCLENBQUM7SUFFRDs7O09BR0c7SUFDSCxJQUFXLE1BQU07UUFDZixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQztJQUN0QyxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILElBQVcsSUFBSTtRQUNiLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUM7SUFDekIsQ0FBQztJQUVEOzs7Ozs7O09BT0c7SUFDSCxJQUFXLFVBQVU7UUFDbkIsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQztJQUMvQixDQUFDO0NBQ0Y7QUF2RUQsa0NBdUVDIn0=
@@ -0,0 +1,126 @@
1
+ import { TaskDriver, TaskRunner } from './driver';
2
+ import { TaskData } from './types';
3
+ /**
4
+ * Manages the active task driver and provides a unified interface for task operations.
5
+ *
6
+ * This class acts as a facade for the underlying task driver, providing methods
7
+ * to create, delete, and manage tasks. It ensures that a driver is set before
8
+ * any operations are performed.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { TaskDriverManager } from '@commandkit/tasks';
13
+ * import { HyperCronDriver } from '@commandkit/tasks/hypercron';
14
+ *
15
+ * const manager = new TaskDriverManager();
16
+ * manager.setDriver(new HyperCronDriver());
17
+ *
18
+ * // Now you can create and manage tasks
19
+ * const taskId = await manager.createTask({
20
+ * name: 'my-task',
21
+ * data: { userId: '123' },
22
+ * schedule: { type: 'date', value: Date.now() + 60000 },
23
+ * });
24
+ * ```
25
+ */
26
+ export declare class TaskDriverManager {
27
+ private driver;
28
+ /**
29
+ * Sets the active task driver.
30
+ *
31
+ * This method must be called before any task operations can be performed.
32
+ * The driver handles all scheduling, persistence, and execution timing.
33
+ *
34
+ * @param driver - The task driver to use for all operations
35
+ */
36
+ setDriver(driver: TaskDriver): void;
37
+ /**
38
+ * Creates a new scheduled task.
39
+ *
40
+ * This method delegates to the active driver to schedule the task according
41
+ * to its configuration. The task will be executed when its schedule is due.
42
+ *
43
+ * @param task - The task data containing name, schedule, and custom data
44
+ * @returns A unique identifier for the created task
45
+ * @throws {Error} If no driver has been set
46
+ */
47
+ createTask(task: TaskData): Promise<string>;
48
+ /**
49
+ * Deletes a scheduled task by its identifier.
50
+ *
51
+ * This method delegates to the active driver to remove the task from the
52
+ * scheduling system and cancel any pending executions.
53
+ *
54
+ * @param identifier - The unique identifier of the task to delete
55
+ * @throws {Error} If no driver has been set
56
+ */
57
+ deleteTask(identifier: string): Promise<void>;
58
+ /**
59
+ * Sets the task execution runner function.
60
+ *
61
+ * This method delegates to the active driver to set up the execution handler
62
+ * that will be called when tasks are due to run.
63
+ *
64
+ * @param runner - The function to call when a task should be executed
65
+ * @throws {Error} If no driver has been set
66
+ */
67
+ setTaskRunner(runner: TaskRunner): Promise<void>;
68
+ }
69
+ /**
70
+ * Global task driver manager instance.
71
+ *
72
+ * This is the default instance used by the tasks plugin for managing task operations.
73
+ * You can use this instance directly or create your own TaskDriverManager instance.
74
+ */
75
+ export declare const taskDriverManager: TaskDriverManager;
76
+ /**
77
+ * Sets the global task driver.
78
+ *
79
+ * This is a convenience function that sets the driver on the global task driver manager.
80
+ *
81
+ * @param driver - The task driver to use for all operations
82
+ *
83
+ * @example
84
+ * ```ts
85
+ * import { setDriver } from '@commandkit/tasks';
86
+ * import { HyperCronDriver } from '@commandkit/tasks/hypercron';
87
+ *
88
+ * setDriver(new HyperCronDriver());
89
+ * ```
90
+ */
91
+ export declare function setDriver(driver: TaskDriver): void;
92
+ /**
93
+ * Creates a new scheduled task using the global driver manager.
94
+ *
95
+ * This is a convenience function that creates a task using the global task driver manager.
96
+ *
97
+ * @param task - The task data containing name, schedule, and custom data
98
+ * @returns A unique identifier for the created task
99
+ *
100
+ * @example
101
+ * ```ts
102
+ * import { createTask } from '@commandkit/tasks';
103
+ *
104
+ * const taskId = await createTask({
105
+ * name: 'reminder',
106
+ * data: { userId: '123', message: 'Hello!' },
107
+ * schedule: { type: 'date', value: Date.now() + 60000 },
108
+ * });
109
+ * ```
110
+ */
111
+ export declare function createTask(task: TaskData): Promise<string>;
112
+ /**
113
+ * Deletes a scheduled task using the global driver manager.
114
+ *
115
+ * This is a convenience function that deletes a task using the global task driver manager.
116
+ *
117
+ * @param identifier - The unique identifier of the task to delete
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * import { deleteTask } from '@commandkit/tasks';
122
+ *
123
+ * await deleteTask('task-123');
124
+ * ```
125
+ */
126
+ export declare function deleteTask(identifier: string): Promise<void>;