@commandkit/tasks 0.0.0-dev.20250725125550 → 0.0.0-dev.20250726103524

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,15 +25,21 @@ import { tasks } from '@commandkit/tasks';
25
25
 
26
26
  export default {
27
27
  plugins: [
28
- tasks({
29
- tasksPath: 'app/tasks', // optional, defaults to 'app/tasks'
30
- enableHMR: true, // optional, defaults to true in development
31
- }),
28
+ tasks(),
32
29
  ],
33
30
  };
34
31
  ```
35
32
 
36
- ### 2. Create static tasks
33
+ ### 2. Set up a driver
34
+
35
+ ```ts
36
+ import { setDriver } from '@commandkit/tasks';
37
+ import { SQLiteDriver } from '@commandkit/tasks/sqlite';
38
+
39
+ setDriver(new SQLiteDriver('./tasks.db'));
40
+ ```
41
+
42
+ ### 3. Create static tasks
37
43
 
38
44
  Create a file in `src/app/tasks/`:
39
45
 
@@ -42,95 +48,86 @@ import { task } from '@commandkit/tasks';
42
48
 
43
49
  export const refreshExchangeRate = task({
44
50
  name: 'refresh-exchange-rate',
45
- schedule: '0 0 * * *', // cron expression - daily at midnight
51
+ schedule: { type: 'cron', value: '0 0 * * *' }, // daily at midnight
46
52
  async execute(ctx) {
47
53
  // Fetch latest exchange rates
48
54
  const rates = await fetchExchangeRates();
49
55
  await updateDatabase(rates);
50
56
  },
51
57
  });
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
58
  ```
65
59
 
66
- ### 3. Create dynamic tasks from commands
60
+ ### 4. Create dynamic tasks from commands
67
61
 
68
62
  ```ts
63
+ import type { CommandData, ChatInputCommand } from 'commandkit';
64
+ import { ApplicationCommandOptionType } from 'discord.js';
65
+ import ms from 'ms';
69
66
  import { createTask } from '@commandkit/tasks';
70
67
 
71
- export default {
68
+ export const command: CommandData = {
72
69
  name: 'remind-me',
73
70
  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
- },
71
+ options: [
72
+ {
73
+ name: 'time',
74
+ description: 'The time to remind after. Eg: 6h, 10m, 1d',
75
+ type: ApplicationCommandOptionType.String,
76
+ required: true,
77
+ },
78
+ {
79
+ name: 'message',
80
+ description: 'The message to remind about.',
81
+ type: ApplicationCommandOptionType.String,
82
+ required: true,
83
+ },
84
+ ],
85
+ };
86
+
87
+ export const chatInput: ChatInputCommand = async (ctx) => {
88
+ const time = ctx.options.getString('time', true);
89
+ const message = ctx.options.getString('message', true);
90
+ const timeMs = Date.now() + ms(time as `${number}`);
91
+
92
+ await createTask({
93
+ name: 'reminder',
94
+ data: {
95
+ userId: ctx.interaction.user.id,
96
+ message,
97
+ channelId: ctx.interaction.channelId,
98
+ setAt: Date.now(),
99
+ },
100
+ schedule: {
101
+ type: 'date',
102
+ value: timeMs,
103
+ },
104
+ });
105
+
106
+ await ctx.interaction.reply(
107
+ `I will remind you <t:${Math.floor(timeMs / 1000)}:R> for \`${message}\``,
108
+ );
89
109
  };
90
110
  ```
91
111
 
92
112
  ## API Reference
93
113
 
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
114
  ### Task Definition
104
115
 
105
116
  ```ts
106
117
  interface TaskDefinition {
107
118
  name: string;
108
- schedule?: ScheduleType;
109
- prepare?: (ctx: TaskContext) => Promise<boolean> | boolean;
110
- execute: (ctx: TaskContext) => Promise<void> | void;
119
+ schedule?: TaskSchedule;
120
+ prepare?: (ctx: TaskContext) => Promise<boolean>;
121
+ execute: (ctx: TaskContext) => Promise<void>;
111
122
  }
112
123
  ```
113
124
 
114
125
  ### Schedule Types
115
126
 
116
127
  ```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
- }
128
+ type TaskSchedule =
129
+ | { type: 'cron'; value: string; timezone?: string }
130
+ | { type: 'date'; value: Date | number; timezone?: string };
134
131
  ```
135
132
 
136
133
  ### Functions
@@ -139,163 +136,44 @@ interface TaskContext {
139
136
 
140
137
  Creates a task definition.
141
138
 
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)`
139
+ #### `createTask(task: TaskData)`
155
140
 
156
141
  Creates a dynamic task.
157
142
 
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
- ```
143
+ #### `deleteTask(identifier: string)`
209
144
 
210
- ## Persistence Drivers
145
+ Deletes a scheduled task.
211
146
 
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
- ```
147
+ ## Drivers
222
148
 
223
149
  ### SQLite Driver
224
150
 
151
+ Persistent job queue with recovery on restart.
152
+
225
153
  ```ts
226
- import { driver } from '@commandkit/tasks';
227
- import { SQLiteDriver } from '@commandkit/tasks/drivers';
154
+ import { SQLiteDriver } from '@commandkit/tasks/sqlite';
228
155
 
229
- driver.use(new SQLiteDriver('./tasks.db'));
156
+ setDriver(new SQLiteDriver('./tasks.db'));
230
157
  ```
231
158
 
232
- **Note**: Requires `sqlite3`, `sqlite`, and `cron-parser` packages to be installed.
159
+ **Features:**
160
+ - Jobs recoverable on restart
161
+ - Persistent job data
162
+ - Cron expression support via cron-parser
233
163
 
234
164
  ### BullMQ Driver
235
165
 
166
+ Distributed task scheduling with Redis.
167
+
236
168
  ```ts
237
- import { driver } from '@commandkit/tasks';
238
- import { BullMQDriver } from '@commandkit/tasks/drivers';
169
+ import { BullMQDriver } from '@commandkit/tasks/bullmq';
239
170
 
240
- driver.use(new BullMQDriver({
171
+ setDriver(new BullMQDriver({
241
172
  host: 'localhost',
242
173
  port: 6379,
243
174
  }));
244
175
  ```
245
176
 
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
177
  ## License
300
178
 
301
179
  MIT
@@ -10,10 +10,9 @@ import { TaskData } from './types';
10
10
  * @example
11
11
  * ```ts
12
12
  * import { TaskDriverManager } from '@commandkit/tasks';
13
- * import { HyperCronDriver } from '@commandkit/tasks/hypercron';
14
13
  *
15
14
  * const manager = new TaskDriverManager();
16
- * manager.setDriver(new HyperCronDriver());
15
+ * // Set your preferred driver here
17
16
  *
18
17
  * // Now you can create and manage tasks
19
18
  * const taskId = await manager.createTask({
@@ -83,9 +82,9 @@ export declare const taskDriverManager: TaskDriverManager;
83
82
  * @example
84
83
  * ```ts
85
84
  * import { setDriver } from '@commandkit/tasks';
86
- * import { HyperCronDriver } from '@commandkit/tasks/hypercron';
85
+ * import { SQLiteDriver } from '@commandkit/tasks/sqlite';
87
86
  *
88
- * setDriver(new HyperCronDriver());
87
+ * setDriver(new SQLiteDriver('./tasks.db'));
89
88
  * ```
90
89
  */
91
90
  export declare function setDriver(driver: TaskDriver): void;
@@ -14,10 +14,9 @@ exports.deleteTask = deleteTask;
14
14
  * @example
15
15
  * ```ts
16
16
  * import { TaskDriverManager } from '@commandkit/tasks';
17
- * import { HyperCronDriver } from '@commandkit/tasks/hypercron';
18
17
  *
19
18
  * const manager = new TaskDriverManager();
20
- * manager.setDriver(new HyperCronDriver());
19
+ * // Set your preferred driver here
21
20
  *
22
21
  * // Now you can create and manage tasks
23
22
  * const taskId = await manager.createTask({
@@ -104,9 +103,9 @@ exports.taskDriverManager = new TaskDriverManager();
104
103
  * @example
105
104
  * ```ts
106
105
  * import { setDriver } from '@commandkit/tasks';
107
- * import { HyperCronDriver } from '@commandkit/tasks/hypercron';
106
+ * import { SQLiteDriver } from '@commandkit/tasks/sqlite';
108
107
  *
109
- * setDriver(new HyperCronDriver());
108
+ * setDriver(new SQLiteDriver('./tasks.db'));
110
109
  * ```
111
110
  */
112
111
  function setDriver(driver) {
@@ -151,4 +150,4 @@ function createTask(task) {
151
150
  function deleteTask(identifier) {
152
151
  return exports.taskDriverManager.deleteTask(identifier);
153
152
  }
154
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJpdmVyLW1hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZHJpdmVyLW1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBK0dBLDhCQUVDO0FBcUJELGdDQUVDO0FBZ0JELGdDQUVDO0FBdkpEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBc0JHO0FBQ0gsTUFBYSxpQkFBaUI7SUFBOUI7UUFDVSxXQUFNLEdBQXNCLElBQUksQ0FBQztJQTJEM0MsQ0FBQztJQXpEQzs7Ozs7OztPQU9HO0lBQ0ksU0FBUyxDQUFDLE1BQWtCO1FBQ2pDLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO0lBQ3ZCLENBQUM7SUFFRDs7Ozs7Ozs7O09BU0c7SUFDSSxLQUFLLENBQUMsVUFBVSxDQUFDLElBQWM7UUFDcEMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNO1lBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO1FBRWxFLE9BQU8sTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN4QyxDQUFDO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSSxLQUFLLENBQUMsVUFBVSxDQUFDLFVBQWtCO1FBQ3hDLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTTtZQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsOEJBQThCLENBQUMsQ0FBQztRQUVsRSxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ3ZDLENBQUM7SUFFRDs7Ozs7Ozs7T0FRRztJQUNJLEtBQUssQ0FBQyxhQUFhLENBQUMsTUFBa0I7UUFDM0MsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNO1lBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO1FBRWxFLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDMUMsQ0FBQztDQUNGO0FBNURELDhDQTREQztBQUVEOzs7OztHQUtHO0FBQ1UsUUFBQSxpQkFBaUIsR0FBRyxJQUFJLGlCQUFpQixFQUFFLENBQUM7QUFFekQ7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFDSCxTQUFnQixTQUFTLENBQUMsTUFBa0I7SUFDMUMseUJBQWlCLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0FBQ3RDLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0JHO0FBQ0gsU0FBZ0IsVUFBVSxDQUFDLElBQWM7SUFDdkMsT0FBTyx5QkFBaUIsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUM7QUFDNUMsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSCxTQUFnQixVQUFVLENBQUMsVUFBa0I7SUFDM0MsT0FBTyx5QkFBaUIsQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLENBQUM7QUFDbEQsQ0FBQyJ9
153
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJpdmVyLW1hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZHJpdmVyLW1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBOEdBLDhCQUVDO0FBcUJELGdDQUVDO0FBZ0JELGdDQUVDO0FBdEpEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FxQkc7QUFDSCxNQUFhLGlCQUFpQjtJQUE5QjtRQUNVLFdBQU0sR0FBc0IsSUFBSSxDQUFDO0lBMkQzQyxDQUFDO0lBekRDOzs7Ozs7O09BT0c7SUFDSSxTQUFTLENBQUMsTUFBa0I7UUFDakMsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7SUFDdkIsQ0FBQztJQUVEOzs7Ozs7Ozs7T0FTRztJQUNJLEtBQUssQ0FBQyxVQUFVLENBQUMsSUFBYztRQUNwQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU07WUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixDQUFDLENBQUM7UUFFbEUsT0FBTyxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3hDLENBQUM7SUFFRDs7Ozs7Ozs7T0FRRztJQUNJLEtBQUssQ0FBQyxVQUFVLENBQUMsVUFBa0I7UUFDeEMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNO1lBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO1FBRWxFLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDdkMsQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0ksS0FBSyxDQUFDLGFBQWEsQ0FBQyxNQUFrQjtRQUMzQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU07WUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixDQUFDLENBQUM7UUFFbEUsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUMxQyxDQUFDO0NBQ0Y7QUE1REQsOENBNERDO0FBRUQ7Ozs7O0dBS0c7QUFDVSxRQUFBLGlCQUFpQixHQUFHLElBQUksaUJBQWlCLEVBQUUsQ0FBQztBQUV6RDs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUNILFNBQWdCLFNBQVMsQ0FBQyxNQUFrQjtJQUMxQyx5QkFBaUIsQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUM7QUFDdEMsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FrQkc7QUFDSCxTQUFnQixVQUFVLENBQUMsSUFBYztJQUN2QyxPQUFPLHlCQUFpQixDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILFNBQWdCLFVBQVUsQ0FBQyxVQUFrQjtJQUMzQyxPQUFPLHlCQUFpQixDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsQ0FBQztBQUNsRCxDQUFDIn0=
@@ -69,18 +69,18 @@ class BullMQDriver {
69
69
  const jobId = crypto.randomUUID();
70
70
  const job = await this.queue.add(task.name, task.data, {
71
71
  jobId,
72
- ...(task.schedule.type === 'cron'
72
+ ...(typeof task.schedule === 'string'
73
73
  ? {
74
74
  repeat: {
75
- pattern: task.schedule.value,
76
- tz: task.schedule.timezone,
75
+ pattern: task.schedule,
76
+ tz: task.timezone,
77
77
  immediately: !!task.immediate,
78
78
  },
79
79
  }
80
80
  : {
81
- delay: (task.schedule.value instanceof Date
82
- ? task.schedule.value.getTime()
83
- : task.schedule.value) - Date.now(),
81
+ delay: (task.schedule instanceof Date
82
+ ? task.schedule.getTime()
83
+ : task.schedule) - Date.now(),
84
84
  }),
85
85
  });
86
86
  return job.id ?? jobId;
@@ -107,4 +107,4 @@ class BullMQDriver {
107
107
  }
108
108
  }
109
109
  exports.BullMQDriver = BullMQDriver;
110
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVsbG1xLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2RyaXZlcnMvYnVsbG1xLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLG1DQUEwRDtBQUcxRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQ0c7QUFDSCxNQUFhLFlBQVk7SUFTdkI7Ozs7O09BS0c7SUFDSCxZQUNFLFVBQTZCLEVBQ1osWUFBb0Isa0JBQWtCO1FBQXRDLGNBQVMsR0FBVCxTQUFTLENBQTZCO1FBaEJqRCxXQUFNLEdBQXNCLElBQUksQ0FBQztRQWtCdkMsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLGNBQUssQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLEVBQUUsVUFBVSxFQUFFLENBQUMsQ0FBQztRQUN2RCxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksZUFBTSxDQUN0QixJQUFJLENBQUMsU0FBUyxFQUNkLEtBQUssRUFBRSxHQUFHLEVBQUUsRUFBRTtZQUNaLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTTtnQkFBRSxNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixDQUFDLENBQUM7WUFFbEUsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDO2dCQUNoQixJQUFJLEVBQUUsR0FBRyxDQUFDLElBQUk7Z0JBQ2QsSUFBSSxFQUFFLEdBQUcsQ0FBQyxJQUFJO2dCQUNkLFNBQVMsRUFBRSxHQUFHLENBQUMsU0FBUzthQUN6QixDQUFDLENBQUM7UUFDTCxDQUFDLEVBQ0QsRUFBRSxVQUFVLEVBQUUsQ0FDZixDQUFDO0lBQ0osQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0ksS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUFjO1FBQ2hDLE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNsQyxNQUFNLEdBQUcsR0FBRyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksRUFBRTtZQUNyRCxLQUFLO1lBQ0wsR0FBRyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxLQUFLLE1BQU07Z0JBQy9CLENBQUMsQ0FBQztvQkFDRSxNQUFNLEVBQUU7d0JBQ04sT0FBTyxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSzt3QkFDNUIsRUFBRSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUTt3QkFDMUIsV0FBVyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsU0FBUztxQkFDOUI7aUJBQ0Y7Z0JBQ0gsQ0FBQyxDQUFDO29CQUNFLEtBQUssRUFDSCxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxZQUFZLElBQUk7d0JBQ2xDLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUU7d0JBQy9CLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUU7aUJBQ3hDLENBQUM7U0FDUCxDQUFDLENBQUM7UUFFSCxPQUFPLEdBQUcsQ0FBQyxFQUFFLElBQUksS0FBSyxDQUFDO0lBQ3pCLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSSxLQUFLLENBQUMsTUFBTSxDQUFDLFVBQWtCO1FBQ3BDLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsVUFBVSxFQUFFLEVBQUUsY0FBYyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFDaEUsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNJLEtBQUssQ0FBQyxhQUFhLENBQUMsTUFBa0I7UUFDM0MsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7SUFDdkIsQ0FBQztDQUNGO0FBeEZELG9DQXdGQyJ9
110
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVsbG1xLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2RyaXZlcnMvYnVsbG1xLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLG1DQUEwRDtBQUcxRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQ0c7QUFDSCxNQUFhLFlBQVk7SUFTdkI7Ozs7O09BS0c7SUFDSCxZQUNFLFVBQTZCLEVBQ1osWUFBb0Isa0JBQWtCO1FBQXRDLGNBQVMsR0FBVCxTQUFTLENBQTZCO1FBaEJqRCxXQUFNLEdBQXNCLElBQUksQ0FBQztRQWtCdkMsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLGNBQUssQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLEVBQUUsVUFBVSxFQUFFLENBQUMsQ0FBQztRQUN2RCxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksZUFBTSxDQUN0QixJQUFJLENBQUMsU0FBUyxFQUNkLEtBQUssRUFBRSxHQUFHLEVBQUUsRUFBRTtZQUNaLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTTtnQkFBRSxNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixDQUFDLENBQUM7WUFFbEUsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDO2dCQUNoQixJQUFJLEVBQUUsR0FBRyxDQUFDLElBQUk7Z0JBQ2QsSUFBSSxFQUFFLEdBQUcsQ0FBQyxJQUFJO2dCQUNkLFNBQVMsRUFBRSxHQUFHLENBQUMsU0FBUzthQUN6QixDQUFDLENBQUM7UUFDTCxDQUFDLEVBQ0QsRUFBRSxVQUFVLEVBQUUsQ0FDZixDQUFDO0lBQ0osQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0ksS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUFjO1FBQ2hDLE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNsQyxNQUFNLEdBQUcsR0FBRyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksRUFBRTtZQUNyRCxLQUFLO1lBQ0wsR0FBRyxDQUFDLE9BQU8sSUFBSSxDQUFDLFFBQVEsS0FBSyxRQUFRO2dCQUNuQyxDQUFDLENBQUM7b0JBQ0UsTUFBTSxFQUFFO3dCQUNOLE9BQU8sRUFBRSxJQUFJLENBQUMsUUFBUTt3QkFDdEIsRUFBRSxFQUFFLElBQUksQ0FBQyxRQUFRO3dCQUNqQixXQUFXLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTO3FCQUM5QjtpQkFDRjtnQkFDSCxDQUFDLENBQUM7b0JBQ0UsS0FBSyxFQUNILENBQUMsSUFBSSxDQUFDLFFBQVEsWUFBWSxJQUFJO3dCQUM1QixDQUFDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLEVBQUU7d0JBQ3pCLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRTtpQkFDbEMsQ0FBQztTQUNQLENBQUMsQ0FBQztRQUVILE9BQU8sR0FBRyxDQUFDLEVBQUUsSUFBSSxLQUFLLENBQUM7SUFDekIsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNJLEtBQUssQ0FBQyxNQUFNLENBQUMsVUFBa0I7UUFDcEMsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUUsRUFBRSxjQUFjLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUNoRSxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksS0FBSyxDQUFDLGFBQWEsQ0FBQyxNQUFrQjtRQUMzQyxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztJQUN2QixDQUFDO0NBQ0Y7QUF4RkQsb0NBd0ZDIn0=
@@ -0,0 +1,63 @@
1
+ import { TaskDriver, TaskRunner } from '../driver';
2
+ import { TaskData } from '../types';
3
+ /**
4
+ * SQLite-based persistent job queue manager for CommandKit tasks.
5
+ *
6
+ * - Jobs are recoverable on restart (pending jobs from the past are run on startup)
7
+ * - Job data is persisted in SQLite
8
+ * - Supports both cron and date-based schedules (uses cron-parser for cron)
9
+ *
10
+ * @example
11
+ * import { SQLiteDriver } from '@commandkit/tasks/sqlite';
12
+ * import { setDriver } from '@commandkit/tasks';
13
+ *
14
+ * const driver = new SQLiteDriver('./tasks.db');
15
+ * setDriver(driver);
16
+ */
17
+ export declare class SQLiteDriver implements TaskDriver {
18
+ private runner;
19
+ private db;
20
+ private interval;
21
+ /**
22
+ * Create a new SQLiteDriver instance.
23
+ * @param dbPath Path to the SQLite database file (default: './commandkit-tasks.db'). Use `:memory:` for an in-memory database.
24
+ */
25
+ constructor(dbPath?: string);
26
+ /**
27
+ * Destroy the SQLite driver and stop the polling loop.
28
+ */
29
+ destroy(): void;
30
+ /**
31
+ * Initialize the jobs table and start the polling loop.
32
+ */
33
+ private init;
34
+ /**
35
+ * Schedule a new job.
36
+ * @param task TaskData to schedule
37
+ * @returns The job ID as a string
38
+ */
39
+ create(task: TaskData): Promise<string>;
40
+ /**
41
+ * Delete a scheduled job by its ID.
42
+ * @param identifier Job ID
43
+ */
44
+ delete(identifier: string): Promise<void>;
45
+ /**
46
+ * Set the task runner function to be called when a job is due.
47
+ * @param runner TaskRunner function
48
+ */
49
+ setTaskRunner(runner: TaskRunner): Promise<void>;
50
+ /**
51
+ * Poll the database for due jobs and execute them.
52
+ * Handles recovery of missed jobs on restart.
53
+ */
54
+ private startPolling;
55
+ /**
56
+ * Poll for jobs that are due and execute them.
57
+ */
58
+ private pollJobs;
59
+ /**
60
+ * Execute a job and reschedule or remove as needed.
61
+ */
62
+ private executeJob;
63
+ }
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SQLiteDriver = void 0;
7
+ const node_sqlite_1 = require("node:sqlite");
8
+ const cron_parser_1 = __importDefault(require("cron-parser"));
9
+ /**
10
+ * SQLite-based persistent job queue manager for CommandKit tasks.
11
+ *
12
+ * - Jobs are recoverable on restart (pending jobs from the past are run on startup)
13
+ * - Job data is persisted in SQLite
14
+ * - Supports both cron and date-based schedules (uses cron-parser for cron)
15
+ *
16
+ * @example
17
+ * import { SQLiteDriver } from '@commandkit/tasks/sqlite';
18
+ * import { setDriver } from '@commandkit/tasks';
19
+ *
20
+ * const driver = new SQLiteDriver('./tasks.db');
21
+ * setDriver(driver);
22
+ */
23
+ class SQLiteDriver {
24
+ /**
25
+ * Create a new SQLiteDriver instance.
26
+ * @param dbPath Path to the SQLite database file (default: './commandkit-tasks.db'). Use `:memory:` for an in-memory database.
27
+ */
28
+ constructor(dbPath = './commandkit-tasks.db') {
29
+ this.runner = null;
30
+ this.interval = null;
31
+ this.db = new node_sqlite_1.DatabaseSync(dbPath, { open: true });
32
+ this.init();
33
+ }
34
+ /**
35
+ * Destroy the SQLite driver and stop the polling loop.
36
+ */
37
+ destroy() {
38
+ this.db.close();
39
+ this.interval && clearInterval(this.interval);
40
+ }
41
+ /**
42
+ * Initialize the jobs table and start the polling loop.
43
+ */
44
+ init() {
45
+ this.db.exec(`CREATE TABLE IF NOT EXISTS jobs (
46
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
47
+ name TEXT NOT NULL,
48
+ data TEXT,
49
+ schedule_type TEXT NOT NULL,
50
+ schedule_value TEXT NOT NULL,
51
+ timezone TEXT,
52
+ next_run INTEGER,
53
+ status TEXT NOT NULL DEFAULT 'pending',
54
+ created_at INTEGER NOT NULL,
55
+ last_run INTEGER
56
+ )`);
57
+ this.startPolling();
58
+ }
59
+ /**
60
+ * Schedule a new job.
61
+ * @param task TaskData to schedule
62
+ * @returns The job ID as a string
63
+ */
64
+ async create(task) {
65
+ const { name, data, schedule, timezone } = task;
66
+ let nextRun;
67
+ let scheduleType;
68
+ let scheduleValue;
69
+ if (typeof schedule === 'string') {
70
+ scheduleType = 'cron';
71
+ scheduleValue = schedule;
72
+ const interval = cron_parser_1.default.parseExpression(schedule, {
73
+ tz: timezone,
74
+ });
75
+ nextRun = interval.next().getTime();
76
+ }
77
+ else {
78
+ scheduleType = 'date';
79
+ scheduleValue = String(schedule);
80
+ nextRun = typeof schedule === 'number' ? schedule : schedule.getTime();
81
+ }
82
+ const stmt = this.db.prepare(`INSERT INTO jobs (name, data, schedule_type, schedule_value, timezone, next_run, status, created_at) VALUES (?, ?, ?, ?, ?, ?, 'pending', ?)`);
83
+ const result = stmt.run(name, JSON.stringify(data ?? {}), scheduleType, scheduleValue, timezone ?? null, nextRun, Date.now());
84
+ if (task.immediate) {
85
+ await this.runner?.({
86
+ name,
87
+ data,
88
+ timestamp: Date.now(),
89
+ });
90
+ }
91
+ return result.lastInsertRowid.toString();
92
+ }
93
+ /**
94
+ * Delete a scheduled job by its ID.
95
+ * @param identifier Job ID
96
+ */
97
+ async delete(identifier) {
98
+ const stmt = this.db.prepare(`DELETE FROM jobs WHERE id = ?`);
99
+ stmt.run(identifier);
100
+ }
101
+ /**
102
+ * Set the task runner function to be called when a job is due.
103
+ * @param runner TaskRunner function
104
+ */
105
+ async setTaskRunner(runner) {
106
+ this.runner = runner;
107
+ }
108
+ /**
109
+ * Poll the database for due jobs and execute them.
110
+ * Handles recovery of missed jobs on restart.
111
+ */
112
+ startPolling() {
113
+ if (this.interval)
114
+ clearInterval(this.interval);
115
+ this.interval = setInterval(() => this.pollJobs(), 1000);
116
+ // Run immediately on startup
117
+ this.pollJobs();
118
+ }
119
+ /**
120
+ * Poll for jobs that are due and execute them.
121
+ */
122
+ pollJobs() {
123
+ if (!this.runner)
124
+ return;
125
+ const now = Date.now();
126
+ const stmt = this.db.prepare(`SELECT * FROM jobs WHERE status = 'pending' AND next_run <= ?`);
127
+ const rows = stmt.all(now);
128
+ for (const job of rows) {
129
+ this.executeJob(job);
130
+ }
131
+ }
132
+ /**
133
+ * Execute a job and reschedule or remove as needed.
134
+ */
135
+ async executeJob(job) {
136
+ if (!this.runner)
137
+ return;
138
+ const data = JSON.parse(job.data ?? '{}');
139
+ await this.runner({
140
+ name: job.name,
141
+ data,
142
+ timestamp: Date.now(),
143
+ });
144
+ const now = Date.now();
145
+ if (job.schedule_type === 'cron') {
146
+ let nextRun = null;
147
+ try {
148
+ const interval = cron_parser_1.default.parseExpression(job.schedule_value, {
149
+ tz: job.timezone || undefined,
150
+ });
151
+ // Find the next run after now
152
+ while (true) {
153
+ const candidate = interval.next().getTime();
154
+ if (candidate > now) {
155
+ nextRun = candidate;
156
+ break;
157
+ }
158
+ }
159
+ }
160
+ catch {
161
+ nextRun = null;
162
+ }
163
+ if (nextRun) {
164
+ const stmt = this.db.prepare(`UPDATE jobs SET next_run = ?, last_run = ? WHERE id = ?`);
165
+ stmt.run(nextRun, now, job.id);
166
+ }
167
+ else {
168
+ const stmt = this.db.prepare(`UPDATE jobs SET status = 'completed', last_run = ? WHERE id = ?`);
169
+ stmt.run(now, job.id);
170
+ }
171
+ }
172
+ else {
173
+ const stmt = this.db.prepare(`UPDATE jobs SET status = 'completed', last_run = ? WHERE id = ?`);
174
+ stmt.run(now, job.id);
175
+ }
176
+ }
177
+ }
178
+ exports.SQLiteDriver = SQLiteDriver;
179
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3FsaXRlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2RyaXZlcnMvc3FsaXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUVBLDZDQUEyQztBQUMzQyw4REFBcUM7QUFFckM7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILE1BQWEsWUFBWTtJQUt2Qjs7O09BR0c7SUFDSCxZQUFZLE1BQU0sR0FBRyx1QkFBdUI7UUFScEMsV0FBTSxHQUFzQixJQUFJLENBQUM7UUFFakMsYUFBUSxHQUEwQixJQUFJLENBQUM7UUFPN0MsSUFBSSxDQUFDLEVBQUUsR0FBRyxJQUFJLDBCQUFZLENBQUMsTUFBTSxFQUFFLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7UUFDbkQsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ2QsQ0FBQztJQUVEOztPQUVHO0lBQ0ksT0FBTztRQUNaLElBQUksQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDaEIsSUFBSSxDQUFDLFFBQVEsSUFBSSxhQUFhLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ2hELENBQUM7SUFFRDs7T0FFRztJQUNLLElBQUk7UUFDVixJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQzs7Ozs7Ozs7Ozs7TUFXWCxDQUFDLENBQUM7UUFDSixJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7SUFDdEIsQ0FBQztJQUVEOzs7O09BSUc7SUFDSCxLQUFLLENBQUMsTUFBTSxDQUFDLElBQWM7UUFDekIsTUFBTSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxHQUFHLElBQUksQ0FBQztRQUNoRCxJQUFJLE9BQWUsQ0FBQztRQUNwQixJQUFJLFlBQW9CLENBQUM7UUFDekIsSUFBSSxhQUFxQixDQUFDO1FBRTFCLElBQUksT0FBTyxRQUFRLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDakMsWUFBWSxHQUFHLE1BQU0s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
package/dist/task.d.ts CHANGED
@@ -60,6 +60,11 @@ export declare class Task<T extends Record<string, any> = Record<string, any>> {
60
60
  * @returns true if the task has a date schedule
61
61
  */
62
62
  isDate(): boolean;
63
+ /**
64
+ * The timezone for the task schedule.
65
+ * Returns undefined if no timezone is defined.
66
+ */
67
+ get timezone(): string | undefined;
63
68
  /**
64
69
  * Determines if the task is ready to be executed.
65
70
  *
package/dist/task.js CHANGED
@@ -63,7 +63,7 @@ class Task {
63
63
  * @returns true if the task has a cron schedule
64
64
  */
65
65
  isCron() {
66
- return this.schedule?.type === 'cron';
66
+ return typeof this.schedule === 'string';
67
67
  }
68
68
  /**
69
69
  * Checks if this task uses date-based scheduling.
@@ -71,7 +71,16 @@ class Task {
71
71
  * @returns true if the task has a date schedule
72
72
  */
73
73
  isDate() {
74
- return this.schedule?.type === 'date';
74
+ if (this.schedule == null)
75
+ return false;
76
+ return this.schedule instanceof Date || typeof this.schedule === 'number';
77
+ }
78
+ /**
79
+ * The timezone for the task schedule.
80
+ * Returns undefined if no timezone is defined.
81
+ */
82
+ get timezone() {
83
+ return this.data.timezone ?? undefined;
75
84
  }
76
85
  /**
77
86
  * Determines if the task is ready to be executed.
@@ -126,4 +135,4 @@ exports.Task = Task;
126
135
  function task(data) {
127
136
  return new Task(data);
128
137
  }
129
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFzay5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy90YXNrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQWlJQSxvQkFJQztBQWxJRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXlCRztBQUNILE1BQWEsSUFBSTtJQUNmOzs7O09BSUc7SUFDSCxZQUEyQixJQUF1QjtRQUF2QixTQUFJLEdBQUosSUFBSSxDQUFtQjtJQUFHLENBQUM7SUFFdEQ7OztPQUdHO0lBQ0gsSUFBVyxTQUFTO1FBQ2xCLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLElBQUksS0FBSyxDQUFDO0lBQ3RDLENBQUM7SUFFRDs7T0FFRztJQUNILElBQVcsSUFBSTtRQUNiLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7SUFDeEIsQ0FBQztJQUVEOzs7T0FHRztJQUNILElBQVcsUUFBUTtRQUNqQixPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxJQUFJLElBQUksQ0FBQztJQUNwQyxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLE1BQU07UUFDWCxPQUFPLElBQUksQ0FBQyxRQUFRLEVBQUUsSUFBSSxLQUFLLE1BQU0sQ0FBQztJQUN4QyxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLE1BQU07UUFDWCxPQUFPLElBQUksQ0FBQyxRQUFRLEVBQUUsSUFBSSxLQUFLLE1BQU0sQ0FBQztJQUN4QyxDQUFDO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSSxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQW1CO1FBQ3RDLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxJQUFJLENBQUM7SUFDMUMsQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0ksS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFtQjtRQUN0QyxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQy9CLENBQUM7Q0FDRjtBQTFFRCxvQkEwRUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F1Qkc7QUFDSCxTQUFnQixJQUFJLENBQ2xCLElBQXVCO0lBRXZCLE9BQU8sSUFBSSxJQUFJLENBQUksSUFBSSxDQUFDLENBQUM7QUFDM0IsQ0FBQyJ9
138
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFzay5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy90YXNrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQTBJQSxvQkFJQztBQTNJRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXlCRztBQUNILE1BQWEsSUFBSTtJQUNmOzs7O09BSUc7SUFDSCxZQUEyQixJQUF1QjtRQUF2QixTQUFJLEdBQUosSUFBSSxDQUFtQjtJQUFHLENBQUM7SUFFdEQ7OztPQUdHO0lBQ0gsSUFBVyxTQUFTO1FBQ2xCLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLElBQUksS0FBSyxDQUFDO0lBQ3RDLENBQUM7SUFFRDs7T0FFRztJQUNILElBQVcsSUFBSTtRQUNiLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7SUFDeEIsQ0FBQztJQUVEOzs7T0FHRztJQUNILElBQVcsUUFBUTtRQUNqQixPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxJQUFJLElBQUksQ0FBQztJQUNwQyxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLE1BQU07UUFDWCxPQUFPLE9BQU8sSUFBSSxDQUFDLFFBQVEsS0FBSyxRQUFRLENBQUM7SUFDM0MsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxNQUFNO1FBQ1gsSUFBSSxJQUFJLENBQUMsUUFBUSxJQUFJLElBQUk7WUFBRSxPQUFPLEtBQUssQ0FBQztRQUN4QyxPQUFPLElBQUksQ0FBQyxRQUFRLFlBQVksSUFBSSxJQUFJLE9BQU8sSUFBSSxDQUFDLFFBQVEsS0FBSyxRQUFRLENBQUM7SUFDNUUsQ0FBQztJQUVEOzs7T0FHRztJQUNILElBQVcsUUFBUTtRQUNqQixPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxJQUFJLFNBQVMsQ0FBQztJQUN6QyxDQUFDO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSSxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQW1CO1FBQ3RDLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxJQUFJLENBQUM7SUFDMUMsQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0ksS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFtQjtRQUN0QyxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQy9CLENBQUM7Q0FDRjtBQW5GRCxvQkFtRkM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F1Qkc7QUFDSCxTQUFnQixJQUFJLENBQ2xCLElBQXVCO0lBRXZCLE9BQU8sSUFBSSxJQUFJLENBQUksSUFBSSxDQUFDLENBQUM7QUFDM0IsQ0FBQyJ9
package/dist/types.d.ts CHANGED
@@ -5,21 +5,7 @@ import { TaskContext } from './context';
5
5
  * Tasks can be scheduled using either cron expressions or specific dates/timestamps.
6
6
  * The timezone is optional and defaults to the system timezone.
7
7
  */
8
- export type TaskSchedule = {
9
- /** Schedule type using cron expressions */
10
- type: 'cron';
11
- /** Optional timezone for the cron schedule (e.g., 'UTC', 'America/New_York') */
12
- timezone?: string;
13
- /** Cron expression (e.g., '0 0 * * *' for daily at midnight) */
14
- value: string;
15
- } | {
16
- /** Schedule type using a specific date or timestamp */
17
- type: 'date';
18
- /** Optional timezone for the date schedule */
19
- timezone?: string;
20
- /** Date object or Unix timestamp in milliseconds */
21
- value: Date | number;
22
- };
8
+ export type TaskSchedule = string | Date | number;
23
9
  /**
24
10
  * Defines a task with its execution logic and scheduling.
25
11
  *
@@ -31,6 +17,8 @@ export interface TaskDefinition<T extends Record<string, any> = Record<string, a
31
17
  name: string;
32
18
  /** Optional schedule configuration for recurring or delayed execution */
33
19
  schedule?: TaskSchedule;
20
+ /** Optional timezone for the schedule */
21
+ timezone?: string;
34
22
  /** Whether the task should run immediately when created (only for cron tasks) */
35
23
  immediate?: boolean;
36
24
  /**
@@ -56,6 +44,8 @@ export interface TaskData<T extends Record<string, any> = Record<string, any>> {
56
44
  data: T;
57
45
  /** Schedule configuration for when the task should run */
58
46
  schedule: TaskSchedule;
47
+ /** Optional timezone for the schedule */
48
+ timezone?: string;
59
49
  /** Whether the task should run immediately when created */
60
50
  immediate?: boolean;
61
51
  }
@@ -71,7 +61,7 @@ export type PartialTaskData<T extends Record<string, any> = Record<string, any>>
71
61
  * This includes the task metadata and execution timestamp, but excludes
72
62
  * scheduling information since the task is already being executed.
73
63
  */
74
- export type TaskExecutionData = Omit<TaskData, 'schedule' | 'immediate'> & {
64
+ export type TaskExecutionData = Omit<TaskData, 'schedule' | 'immediate' | 'timezone'> & {
75
65
  /** Unix timestamp when the task execution started */
76
66
  timestamp: number;
77
67
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commandkit/tasks",
3
- "version": "0.0.0-dev.20250725125550",
3
+ "version": "0.0.0-dev.20250726103524",
4
4
  "description": "Task management plugin for CommandKit",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -15,10 +15,10 @@
15
15
  "import": "./dist/drivers/bullmq.js",
16
16
  "require": "./dist/drivers/bullmq.js"
17
17
  },
18
- "./hypercron": {
19
- "types": "./dist/drivers/hypercron.d.ts",
20
- "import": "./dist/drivers/hypercron.js",
21
- "require": "./dist/drivers/hypercron.js"
18
+ "./sqlite": {
19
+ "types": "./dist/drivers/sqlite.d.ts",
20
+ "import": "./dist/drivers/sqlite.js",
21
+ "require": "./dist/drivers/sqlite.js"
22
22
  }
23
23
  },
24
24
  "files": [
@@ -44,10 +44,9 @@
44
44
  "homepage": "https://github.com/underctrl-io/commandkit#readme",
45
45
  "devDependencies": {
46
46
  "bullmq": "^5.56.5",
47
- "hypercron": "^0.1.0",
48
47
  "typescript": "^5.8.3",
49
- "tsconfig": "0.0.0-dev.20250725125550",
50
- "commandkit": "1.0.0-dev.20250725125550"
48
+ "tsconfig": "0.0.0-dev.20250726103524",
49
+ "commandkit": "1.0.0-dev.20250726103524"
51
50
  },
52
51
  "dependencies": {
53
52
  "cron-parser": "^4.9.0"
@@ -1,69 +0,0 @@
1
- import { TaskDriver, TaskRunner } from '../driver';
2
- import { TaskData } from '../types';
3
- /**
4
- * HyperCron-based task driver for lightweight task scheduling.
5
- *
6
- * This driver uses HyperCron to provide simple, in-memory task scheduling.
7
- * It's ideal for single-instance applications or development environments
8
- * where you don't need distributed task scheduling.
9
- *
10
- * **Requirements**: Requires the `hypercron` package to be installed.
11
- *
12
- * @example
13
- * ```ts
14
- * import { HyperCronDriver } from '@commandkit/tasks/hypercron';
15
- * import { setDriver } from '@commandkit/tasks';
16
- *
17
- * const driver = new HyperCronDriver();
18
- * setDriver(driver);
19
- * ```
20
- *
21
- * @example
22
- * ```ts
23
- * // With custom cron service
24
- * import { cronService } from 'hypercron';
25
- *
26
- * const customService = cronService.create({
27
- * // Custom configuration
28
- * });
29
- *
30
- * const driver = new HyperCronDriver(customService);
31
- * setDriver(driver);
32
- * ```
33
- */
34
- export declare class HyperCronDriver implements TaskDriver {
35
- private readonly service;
36
- private runner;
37
- /**
38
- * Creates a new HyperCron driver instance.
39
- *
40
- * @param service - Optional custom cron service instance, defaults to the global cronService
41
- */
42
- constructor(service?: import("hypercron").CronService);
43
- /**
44
- * Creates a new scheduled task in HyperCron.
45
- *
46
- * This schedules the task using the HyperCron service and starts the service
47
- * if it's not already running.
48
- *
49
- * @param task - The task data containing name, schedule, and custom data
50
- * @returns A unique schedule identifier
51
- */
52
- create(task: TaskData): Promise<string>;
53
- /**
54
- * Deletes a scheduled task from HyperCron.
55
- *
56
- * This cancels the scheduled task and removes it from the cron service.
57
- *
58
- * @param identifier - The schedule identifier to delete
59
- */
60
- delete(identifier: string): Promise<void>;
61
- /**
62
- * Sets the task execution runner function.
63
- *
64
- * This function will be called by the HyperCron service when a task is due for execution.
65
- *
66
- * @param runner - The function to call when a task should be executed
67
- */
68
- setTaskRunner(runner: TaskRunner): Promise<void>;
69
- }
@@ -1,90 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HyperCronDriver = void 0;
4
- const hypercron_1 = require("hypercron");
5
- /**
6
- * HyperCron-based task driver for lightweight task scheduling.
7
- *
8
- * This driver uses HyperCron to provide simple, in-memory task scheduling.
9
- * It's ideal for single-instance applications or development environments
10
- * where you don't need distributed task scheduling.
11
- *
12
- * **Requirements**: Requires the `hypercron` package to be installed.
13
- *
14
- * @example
15
- * ```ts
16
- * import { HyperCronDriver } from '@commandkit/tasks/hypercron';
17
- * import { setDriver } from '@commandkit/tasks';
18
- *
19
- * const driver = new HyperCronDriver();
20
- * setDriver(driver);
21
- * ```
22
- *
23
- * @example
24
- * ```ts
25
- * // With custom cron service
26
- * import { cronService } from 'hypercron';
27
- *
28
- * const customService = cronService.create({
29
- * // Custom configuration
30
- * });
31
- *
32
- * const driver = new HyperCronDriver(customService);
33
- * setDriver(driver);
34
- * ```
35
- */
36
- class HyperCronDriver {
37
- /**
38
- * Creates a new HyperCron driver instance.
39
- *
40
- * @param service - Optional custom cron service instance, defaults to the global cronService
41
- */
42
- constructor(service = hypercron_1.cronService) {
43
- this.service = service;
44
- this.runner = null;
45
- }
46
- /**
47
- * Creates a new scheduled task in HyperCron.
48
- *
49
- * This schedules the task using the HyperCron service and starts the service
50
- * if it's not already running.
51
- *
52
- * @param task - The task data containing name, schedule, and custom data
53
- * @returns A unique schedule identifier
54
- */
55
- async create(task) {
56
- const id = await this.service.schedule(task.schedule.value, task.name, async () => {
57
- if (!this.runner)
58
- throw new Error('Task runner has not been set');
59
- await this.runner({
60
- name: task.name,
61
- data: task.data,
62
- timestamp: Date.now(),
63
- });
64
- });
65
- await this.service.start();
66
- return id;
67
- }
68
- /**
69
- * Deletes a scheduled task from HyperCron.
70
- *
71
- * This cancels the scheduled task and removes it from the cron service.
72
- *
73
- * @param identifier - The schedule identifier to delete
74
- */
75
- async delete(identifier) {
76
- await this.service.cancel(identifier);
77
- }
78
- /**
79
- * Sets the task execution runner function.
80
- *
81
- * This function will be called by the HyperCron service when a task is due for execution.
82
- *
83
- * @param runner - The function to call when a task should be executed
84
- */
85
- async setTaskRunner(runner) {
86
- this.runner = runner;
87
- }
88
- }
89
- exports.HyperCronDriver = HyperCronDriver;
90
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHlwZXJjcm9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2RyaXZlcnMvaHlwZXJjcm9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHlDQUF3QztBQUl4Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBOEJHO0FBQ0gsTUFBYSxlQUFlO0lBRzFCOzs7O09BSUc7SUFDSCxZQUFvQyxVQUFVLHVCQUFXO1FBQXJCLFlBQU8sR0FBUCxPQUFPLENBQWM7UUFQakQsV0FBTSxHQUFzQixJQUFJLENBQUM7SUFPbUIsQ0FBQztJQUU3RDs7Ozs7Ozs7T0FRRztJQUNJLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBYztRQUNoQyxNQUFNLEVBQUUsR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUNwQyxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssRUFDbkIsSUFBSSxDQUFDLElBQUksRUFDVCxLQUFLLElBQUksRUFBRTtZQUNULElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTTtnQkFBRSxNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixDQUFDLENBQUM7WUFFbEUsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDO2dCQUNoQixJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUk7Z0JBQ2YsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJO2dCQUNmLFNBQVMsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFO2FBQ3RCLENBQUMsQ0FBQztRQUNMLENBQUMsQ0FDRixDQUFDO1FBRUYsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxDQUFDO1FBRTNCLE9BQU8sRUFBRSxDQUFDO0lBQ1osQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNJLEtBQUssQ0FBQyxNQUFNLENBQUMsVUFBa0I7UUFDcEMsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUN4QyxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksS0FBSyxDQUFDLGFBQWEsQ0FBQyxNQUFrQjtRQUMzQyxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztJQUN2QixDQUFDO0NBQ0Y7QUE1REQsMENBNERDIn0=