@supergrowthai/tq 1.0.1-canary.03bf985

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.
Files changed (38) hide show
  1. package/README.md +538 -0
  2. package/dist/core/base/interfaces.d.ts +1 -0
  3. package/dist/core/base/interfaces.js +2 -0
  4. package/dist/core/base/interfaces.js.map +1 -0
  5. package/dist/core/base/interfaces.mjs +2 -0
  6. package/dist/core/base/interfaces.mjs.map +1 -0
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.js +1734 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/index.mjs +1734 -0
  11. package/dist/index.mjs.map +1 -0
  12. package/dist/src/adapters/IDatabaseAdapter.d.ts +61 -0
  13. package/dist/src/adapters/InMemoryAdapter.d.ts +24 -0
  14. package/dist/src/adapters/MongoDbAdapter.d.ts +34 -0
  15. package/dist/src/adapters/index.d.ts +4 -0
  16. package/dist/src/adapters/types.d.ts +9 -0
  17. package/dist/src/core/Actions.d.ts +33 -0
  18. package/dist/src/core/TaskHandler.d.ts +35 -0
  19. package/dist/src/core/TaskQueuesManager.d.ts +33 -0
  20. package/dist/src/core/TaskRunner.d.ts +26 -0
  21. package/dist/src/core/TaskStore.d.ts +63 -0
  22. package/dist/src/core/async/AsyncActions.d.ts +22 -0
  23. package/dist/src/core/async/AsyncTaskManager.d.ts +19 -0
  24. package/dist/src/core/async/async-task-manager.d.ts +25 -0
  25. package/dist/src/core/base/interfaces.d.ts +35 -0
  26. package/dist/src/core/environment.d.ts +6 -0
  27. package/dist/src/core/task-processor-types.d.ts +14 -0
  28. package/dist/src/index.d.ts +14 -0
  29. package/dist/src/task-registry.d.ts +4 -0
  30. package/dist/src/test/task-queue.test.d.ts +6 -0
  31. package/dist/src/types.d.ts +1 -0
  32. package/dist/src/utils/task-id-gen.d.ts +4 -0
  33. package/dist/types.d.ts +1 -0
  34. package/dist/types.js +2 -0
  35. package/dist/types.js.map +1 -0
  36. package/dist/types.mjs +2 -0
  37. package/dist/types.mjs.map +1 -0
  38. package/package.json +88 -0
package/README.md ADDED
@@ -0,0 +1,538 @@
1
+ # @supergrowthai/tq
2
+
3
+ A clean, dependency-injection based task queue management library with multiple executor types and async task handling.
4
+ Built on top of `@supergrowthai/mq` for flexible message queue backends.
5
+
6
+ ## Features
7
+
8
+ - **Clean Architecture**: Constructor-based dependency injection with no global state
9
+ - **Multiple Executor Types**: Single task (parallel/non-parallel) and multi-task executors
10
+ - **Async Task Management**: Handle long-running tasks with configurable timeouts
11
+ - **Type-Safe**: Full TypeScript support with generic task types
12
+ - **Queue Integration**: Works with any message queue backend via `@supergrowthai/mq`
13
+ - **Named Exports**: Tree-shakable, explicit imports
14
+ - **Fail-Fast Design**: Required dependencies enforce proper configuration
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @supergrowthai/tq @supergrowthai/mq
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import {TaskQueue, TaskHandler} from '@supergrowthai/tq';
26
+ import {InMemoryQueue, ITasksAdapter} from '@supergrowthai/mq';
27
+
28
+ // 1. Set up your adapters (see @supergrowthai/mq docs for details)
29
+ const tasksAdapter: ITasksAdapter = {
30
+ // Your implementation
31
+ findScheduledTasks: () => Promise.resolve([]),
32
+ generateTaskId: () => `task-${Date.now()}`,
33
+ insertTasks: () => Promise.resolve(),
34
+ markTasksAsExecuted: () => Promise.resolve(),
35
+ markTasksAsFailed: () => Promise.resolve(),
36
+ markTasksAsProcessing: () => Promise.resolve()
37
+ };
38
+
39
+ const databaseAdapter = /* your database adapter */;
40
+ const cacheAdapter = /* your cache adapter */;
41
+
42
+ // 2. Create instances with dependency injection
43
+ const messageQueue = new InMemoryQueue(tasksAdapter);
44
+ const taskQueue = new TaskQueue(messageQueue);
45
+ const taskHandler = new TaskHandler(
46
+ messageQueue,
47
+ taskQueue,
48
+ databaseAdapter,
49
+ cacheAdapter
50
+ // asyncTaskManager optional
51
+ );
52
+
53
+ // 3. Register task executors
54
+ taskQueue.register('email-queue', 'send-email', {
55
+ multiple: false,
56
+ parallel: false,
57
+ default_retries: 3,
58
+ store_on_failure: true,
59
+
60
+ async onTask(task, actions) {
61
+ try {
62
+ await sendEmail(task.payload.to, task.payload.subject);
63
+ actions.success(task);
64
+ } catch (error) {
65
+ console.error('Failed to send email:', error);
66
+ actions.fail(task);
67
+ }
68
+ }
69
+ });
70
+
71
+ // 4. Start processing
72
+ taskHandler.taskProcessServer();
73
+ ```
74
+
75
+ ## Core Components
76
+
77
+ ### TaskQueue
78
+
79
+ Manages task executor registration and retrieval:
80
+
81
+ ```typescript
82
+ import {TaskQueue} from '@supergrowthai/tq';
83
+ import {IMessageQueue} from '@supergrowthai/mq';
84
+
85
+ const taskQueue = new TaskQueue(messageQueue);
86
+
87
+ // Register executors
88
+ taskQueue.register('queue-name', 'task-type', executor);
89
+
90
+ // Get executor
91
+ const executor = taskQueue.getExecutor('queue-name', 'task-type');
92
+
93
+ // Get queue information
94
+ const queues = taskQueue.getQueues();
95
+ const taskTypes = taskQueue.getTasksForQueue('queue-name');
96
+ ```
97
+
98
+ ### TaskHandler
99
+
100
+ Manages task processing, retries, and queue consumption:
101
+
102
+ ```typescript
103
+ import {TaskHandler} from '@supergrowthai/tq';
104
+
105
+ const taskHandler = new TaskHandler(
106
+ messageQueue, // IMessageQueue
107
+ taskQueue, // TaskQueue
108
+ databaseAdapter, // IDatabaseAdapter
109
+ cacheAdapter, // BaseCacheProvider<any>
110
+ asyncTaskManager // IAsyncTaskManager (optional)
111
+ );
112
+
113
+ // Start processing all registered queues
114
+ taskHandler.taskProcessServer();
115
+
116
+ // Or process specific queues
117
+ taskHandler.startConsumingTasks('email-queue');
118
+
119
+ // Process mature tasks (scheduled for future execution)
120
+ taskHandler.processMatureTasks();
121
+ ```
122
+
123
+ ### TaskRunner
124
+
125
+ Handles task execution with locking and async support:
126
+
127
+ ```typescript
128
+ import {TaskRunner} from '@supergrowthai/tq';
129
+
130
+ const taskRunner = new TaskRunner(
131
+ messageQueue,
132
+ taskQueue,
133
+ taskStore,
134
+ cacheProvider
135
+ );
136
+
137
+ // Run tasks
138
+ const result = await taskRunner.run(
139
+ 'runner-id',
140
+ tasks,
141
+ asyncTaskManager // optional
142
+ );
143
+ ```
144
+
145
+ ## Task Executor Types
146
+
147
+ ### Single Task Non-Parallel Executor
148
+
149
+ For tasks that should be processed one at a time:
150
+
151
+ ```typescript
152
+ import {ISingleTaskNonParallel} from '@supergrowthai/tq';
153
+
154
+ interface EmailData {
155
+ to: string;
156
+ subject: string;
157
+ body: string;
158
+ }
159
+
160
+ const emailExecutor: ISingleTaskNonParallel<EmailData> = {
161
+ multiple: false,
162
+ parallel: false,
163
+ default_retries: 3,
164
+ store_on_failure: true,
165
+
166
+ async onTask(task, actions) {
167
+ try {
168
+ await sendEmail(task.payload);
169
+ actions.success(task);
170
+ } catch (error) {
171
+ console.error('Email sending failed:', error);
172
+ actions.fail(task);
173
+ }
174
+ }
175
+ };
176
+
177
+ taskQueue.register('email-queue', 'send-email', emailExecutor);
178
+ ```
179
+
180
+ ### Single Task Parallel Executor
181
+
182
+ For tasks that can be processed in parallel batches:
183
+
184
+ ```typescript
185
+ import {ISingleTaskParallel} from '@supergrowthai/tq';
186
+
187
+ const imageProcessorExecutor: ISingleTaskParallel<ImageData> = {
188
+ multiple: false,
189
+ parallel: true,
190
+ chunkSize: 5, // Process 5 images at a time
191
+ default_retries: 3,
192
+ store_on_failure: true,
193
+
194
+ async onTask(task, actions) {
195
+ try {
196
+ await processImage(task.payload.imageUrl, task.payload.filters);
197
+ actions.success(task);
198
+ } catch (error) {
199
+ console.error('Image processing failed:', error);
200
+ actions.fail(task);
201
+ }
202
+ }
203
+ };
204
+
205
+ taskQueue.register('image-queue', 'process-image', imageProcessorExecutor);
206
+ ```
207
+
208
+ ### Multi-Task Executor
209
+
210
+ For processing multiple tasks together as a batch:
211
+
212
+ ```typescript
213
+ import {IMultiTaskExecutor} from '@supergrowthai/tq';
214
+
215
+ const batchProcessorExecutor: IMultiTaskExecutor<BatchData> = {
216
+ multiple: true,
217
+ default_retries: 2,
218
+ store_on_failure: true,
219
+
220
+ async onTasks(tasks, actions) {
221
+ console.log(`Processing batch of ${tasks.length} tasks`);
222
+
223
+ for (const task of tasks) {
224
+ try {
225
+ await processBatchItem(task.payload);
226
+ actions.success(task);
227
+ } catch (error) {
228
+ console.error('Batch item failed:', error);
229
+ actions.fail(task);
230
+
231
+ // Optionally add retry tasks
232
+ if ((task.retries || 0) < 3) {
233
+ actions.addTasks([{
234
+ ...task,
235
+ retries: (task.retries || 0) + 1,
236
+ execute_at: new Date(Date.now() + 60000) // Retry in 1 minute
237
+ }]);
238
+ }
239
+ }
240
+ }
241
+ }
242
+ };
243
+
244
+ taskQueue.register('batch-queue', 'process-batch', batchProcessorExecutor);
245
+ ```
246
+
247
+ ## Async Task Management
248
+
249
+ For long-running tasks that might exceed normal timeouts:
250
+
251
+ ```typescript
252
+ import {AsyncTaskManager} from '@supergrowthai/tq';
253
+
254
+ // Set up async task manager
255
+ const asyncTaskManager = new AsyncTaskManager(maxConcurrent
256
+ :
257
+ 5
258
+ )
259
+ ;
260
+
261
+ const heavyProcessingExecutor: ISingleTaskNonParallel<ProcessingData> = {
262
+ multiple: false,
263
+ parallel: false,
264
+ default_retries: 1,
265
+ store_on_failure: true,
266
+
267
+ // Configure async handoff for tasks taking longer than 30 seconds
268
+ asyncConfig: {
269
+ handoffTimeout: 30000 // 30 seconds
270
+ },
271
+
272
+ async onTask(task, actions) {
273
+ try {
274
+ // This might take a very long time
275
+ const result = await performHeavyComputation(task.payload);
276
+ actions.success(task);
277
+ } catch (error) {
278
+ actions.fail(task);
279
+ }
280
+ }
281
+ };
282
+
283
+ // Pass async task manager to TaskHandler
284
+ const taskHandler = new TaskHandler(
285
+ messageQueue,
286
+ taskQueue,
287
+ databaseAdapter,
288
+ cacheAdapter,
289
+ asyncTaskManager // Now tasks can be handed off to async processing
290
+ );
291
+ ```
292
+
293
+ ## Error Handling and Retries
294
+
295
+ ```typescript
296
+ const resilientExecutor: ISingleTaskNonParallel<ApiCallData> = {
297
+ multiple: false,
298
+ parallel: false,
299
+ default_retries: 5,
300
+ store_on_failure: true,
301
+
302
+ async onTask(task, actions) {
303
+ try {
304
+ const response = await callExternalAPI(task.payload.endpoint, task.payload.data);
305
+
306
+ if (response.status === 200) {
307
+ actions.success(task);
308
+ } else {
309
+ throw new Error(`API returned status: ${response.status}`);
310
+ }
311
+ } catch (error) {
312
+ const currentRetries = task.retries || 0;
313
+ console.error(`API call failed (attempt ${currentRetries + 1}/${this.default_retries}):`, error);
314
+
315
+ if (currentRetries < this.default_retries) {
316
+ // Create retry task with exponential backoff
317
+ const retryDelay = Math.pow(2, currentRetries) * 1000; // 1s, 2s, 4s, 8s, 16s
318
+
319
+ actions.addTasks([{
320
+ ...task,
321
+ retries: currentRetries + 1,
322
+ execute_at: new Date(Date.now() + retryDelay)
323
+ }]);
324
+ } else {
325
+ actions.fail(task);
326
+ }
327
+ }
328
+ }
329
+ };
330
+ ```
331
+
332
+ ## Working with Different Queue Providers
333
+
334
+ ```typescript
335
+ import {MongoDBQueue, KinesisQueue, FileShardLockProvider} from '@supergrowthai/mq';
336
+ import {TaskQueue, TaskHandler} from '@supergrowthai/tq';
337
+
338
+ // MongoDB Queue Setup
339
+ const mongoQueue = new MongoDBQueue(cacheAdapter, tasksAdapter);
340
+ const mongoTaskQueue = new TaskQueue(mongoQueue);
341
+ const mongoTaskHandler = new TaskHandler(
342
+ mongoQueue,
343
+ mongoTaskQueue,
344
+ databaseAdapter,
345
+ cacheAdapter
346
+ );
347
+
348
+ // Kinesis Queue Setup
349
+ const shardLockProvider = new FileShardLockProvider();
350
+ const kinesisQueue = new KinesisQueue({
351
+ shardLockProvider,
352
+ instanceId: 'worker-1'
353
+ });
354
+ const kinesisTaskQueue = new TaskQueue(kinesisQueue);
355
+ const kinesisTaskHandler = new TaskHandler(
356
+ kinesisQueue,
357
+ kinesisTaskQueue,
358
+ databaseAdapter,
359
+ cacheAdapter
360
+ );
361
+
362
+ // Register different executors on different queues
363
+ mongoTaskQueue.register('heavy-processing', 'compute', heavyProcessingExecutor);
364
+ kinesisTaskQueue.register('real-time', 'notification', notificationExecutor);
365
+ ```
366
+
367
+ ## Architecture Benefits
368
+
369
+ ### No Global State
370
+
371
+ - All dependencies are injected via constructors
372
+ - No singletons or global variables
373
+ - Easy to test and mock
374
+
375
+ ### Fail-Fast Design
376
+
377
+ - Required dependencies are enforced at compile time
378
+ - No optional parameters with defaults
379
+ - Clear error messages when dependencies are missing
380
+
381
+ ### Clean Separation
382
+
383
+ - Queue management separate from task execution
384
+ - Pluggable storage and cache adapters
385
+ - Each component has a single responsibility
386
+
387
+ ## TypeScript Support
388
+
389
+ Full TypeScript definitions with generic task types:
390
+
391
+ ```typescript
392
+ import {TaskExecutor, ExecutorActions, CronTask} from '@supergrowthai/tq';
393
+
394
+ // Define your task data type
395
+ interface EmailTaskData {
396
+ to: string;
397
+ subject: string;
398
+ body: string;
399
+ attachments?: string[];
400
+ }
401
+
402
+ // Type-safe executor
403
+ const typedExecutor: ISingleTaskNonParallel<EmailTaskData> = {
404
+ multiple: false,
405
+ parallel: false,
406
+ default_retries: 3,
407
+ store_on_failure: true,
408
+
409
+ async onTask(
410
+ task: CronTask<EmailTaskData>,
411
+ actions: ExecutorActions<EmailTaskData>
412
+ ) {
413
+ // task.payload is properly typed as EmailTaskData
414
+ console.log(`Sending email to: ${task.payload.to}`);
415
+
416
+ try {
417
+ await sendEmail(task.payload);
418
+ actions.success(task);
419
+ } catch (error) {
420
+ actions.fail(task);
421
+ }
422
+ }
423
+ };
424
+ ```
425
+
426
+ ## Production Example
427
+
428
+ ```typescript
429
+ import {
430
+ TaskQueue,
431
+ TaskHandler,
432
+ TaskStore,
433
+ AsyncTaskManager
434
+ } from '@supergrowthai/tq';
435
+ import {MongoDBQueue, ITasksAdapter} from '@supergrowthai/mq';
436
+ import {RedisCacheProvider} from 'memoose-js';
437
+
438
+ // Production setup with all components
439
+ class ProductionTasksAdapter implements ITasksAdapter {
440
+ // Your database implementation
441
+ }
442
+
443
+ const cacheProvider = new RedisCacheProvider('redis', {
444
+ host: process.env.REDIS_HOST,
445
+ port: parseInt(process.env.REDIS_PORT || '6379')
446
+ });
447
+
448
+ const messageQueue = new MongoDBQueue(cacheProvider, new ProductionTasksAdapter());
449
+ const taskQueue = new TaskQueue(messageQueue);
450
+ const asyncTaskManager = new AsyncTaskManager(10); // 10 concurrent async tasks
451
+
452
+ const taskHandler = new TaskHandler(
453
+ messageQueue,
454
+ taskQueue,
455
+ new MongoDbAdapter(), // Your database adapter
456
+ cacheProvider,
457
+ cacheProvider,
458
+ asyncTaskManager
459
+ );
460
+
461
+ // Register all your executors
462
+ taskQueue.register('email', 'send', emailExecutor);
463
+ taskQueue.register('notifications', 'push', pushNotificationExecutor);
464
+ taskQueue.register('reports', 'generate', reportGeneratorExecutor);
465
+
466
+ // Start processing
467
+ taskHandler.taskProcessServer();
468
+ ```
469
+
470
+ ## Best Practices
471
+
472
+ ### 1. Task Idempotency
473
+
474
+ Ensure your tasks can be safely retried:
475
+
476
+ ```typescript
477
+ const idempotentExecutor: ISingleTaskNonParallel<UserUpdateData> = {
478
+ multiple: false,
479
+ parallel: false,
480
+ default_retries: 3,
481
+ store_on_failure: true,
482
+
483
+ async onTask(task, actions) {
484
+ try {
485
+ // Check if already processed
486
+ const user = await getUser(task.payload.userId);
487
+ if (user.lastUpdated >= task.payload.timestamp) {
488
+ console.log('Update already applied, skipping');
489
+ actions.success(task);
490
+ return;
491
+ }
492
+
493
+ await updateUser(task.payload.userId, task.payload.updates);
494
+ actions.success(task);
495
+ } catch (error) {
496
+ actions.fail(task);
497
+ }
498
+ }
499
+ };
500
+ ```
501
+
502
+ ### 2. Resource Management
503
+
504
+ Use parallel executors wisely to avoid overwhelming external services:
505
+
506
+ ```typescript
507
+ const apiExecutor: ISingleTaskParallel<ApiTaskData> = {
508
+ multiple: false,
509
+ parallel: true,
510
+ chunkSize: 3, // Limit concurrent API calls
511
+ default_retries: 3,
512
+ store_on_failure: true,
513
+
514
+ async onTask(task, actions) {
515
+ // Rate limiting logic here
516
+ await rateLimiter.acquire();
517
+
518
+ try {
519
+ const result = await callAPI(task.payload);
520
+ actions.success(task);
521
+ } catch (error) {
522
+ actions.fail(task);
523
+ } finally {
524
+ rateLimiter.release();
525
+ }
526
+ }
527
+ };
528
+ ```
529
+
530
+ ## Integration with @supergrowthai/mq
531
+
532
+ This library requires `@supergrowthai/mq` for message queue functionality. See
533
+ the [@supergrowthai/mq documentation](../mq/README.md) for details on configuring different queue providers and
534
+ adapters.
535
+
536
+ ## License
537
+
538
+ MIT
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=interfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interfaces.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=interfaces.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interfaces.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export * from './src/index'
2
+ export {}