nodejs-task-scheduler 1.0.0

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 (50) hide show
  1. package/.claude/settings.local.json +20 -0
  2. package/.github/workflows/ci.yml +266 -0
  3. package/.github/workflows/release.yml +117 -0
  4. package/CHANGELOG.md +43 -0
  5. package/README.md +653 -0
  6. package/dist/__tests__/setup.d.ts +2 -0
  7. package/dist/__tests__/setup.d.ts.map +1 -0
  8. package/dist/__tests__/setup.js +24 -0
  9. package/dist/__tests__/setup.js.map +1 -0
  10. package/dist/decorators/index.d.ts +80 -0
  11. package/dist/decorators/index.d.ts.map +1 -0
  12. package/dist/decorators/index.js +171 -0
  13. package/dist/decorators/index.js.map +1 -0
  14. package/dist/decorators/metadata.d.ts +59 -0
  15. package/dist/decorators/metadata.d.ts.map +1 -0
  16. package/dist/decorators/metadata.js +68 -0
  17. package/dist/decorators/metadata.js.map +1 -0
  18. package/dist/decorators/registry.d.ts +42 -0
  19. package/dist/decorators/registry.d.ts.map +1 -0
  20. package/dist/decorators/registry.js +182 -0
  21. package/dist/decorators/registry.js.map +1 -0
  22. package/dist/index.d.ts +74 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +198 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/queue/index.d.ts +19 -0
  27. package/dist/queue/index.d.ts.map +1 -0
  28. package/dist/queue/index.js +89 -0
  29. package/dist/queue/index.js.map +1 -0
  30. package/dist/scheduler/index.d.ts +13 -0
  31. package/dist/scheduler/index.d.ts.map +1 -0
  32. package/dist/scheduler/index.js +102 -0
  33. package/dist/scheduler/index.js.map +1 -0
  34. package/dist/types/index.d.ts +63 -0
  35. package/dist/types/index.d.ts.map +1 -0
  36. package/dist/types/index.js +12 -0
  37. package/dist/types/index.js.map +1 -0
  38. package/dist/utils/load-balancer.d.ts +36 -0
  39. package/dist/utils/load-balancer.d.ts.map +1 -0
  40. package/dist/utils/load-balancer.js +158 -0
  41. package/dist/utils/load-balancer.js.map +1 -0
  42. package/dist/utils/rabbitmq.d.ts +18 -0
  43. package/dist/utils/rabbitmq.d.ts.map +1 -0
  44. package/dist/utils/rabbitmq.js +114 -0
  45. package/dist/utils/rabbitmq.js.map +1 -0
  46. package/dist/worker/index.d.ts +20 -0
  47. package/dist/worker/index.d.ts.map +1 -0
  48. package/dist/worker/index.js +138 -0
  49. package/dist/worker/index.js.map +1 -0
  50. package/package.json +63 -0
package/README.md ADDED
@@ -0,0 +1,653 @@
1
+ # Node.js Task Scheduler
2
+
3
+ [![CI/CD](https://github.com/your-username/nodejs-task-scheduler/actions/workflows/ci.yml/badge.svg)](https://github.com/your-username/nodejs-task-scheduler/actions/workflows/ci.yml)
4
+ [![npm version](https://badge.fury.io/js/nodejs-task-scheduler.svg)](https://badge.fury.io/js/nodejs-task-scheduler)
5
+
6
+ A distributed task scheduler for Node.js using RabbitMQ with support for cron jobs, direct job execution, and load balancing across multiple nodes.
7
+
8
+ ## Features
9
+
10
+ - **Distributed Job Processing**: Jobs are distributed across multiple worker nodes using RabbitMQ
11
+ - **Cron Job Support**: Schedule recurring jobs using cron expressions
12
+ - **Direct Job Execution**: Execute jobs immediately on available nodes
13
+ - **Load Balancing**: Automatically selects the least loaded available node
14
+ - **Singleton & Multi-instance Workers**: Support for both single-threaded and concurrent job processing
15
+ - **Retry Logic**: Configurable retry attempts with exponential backoff
16
+ - **Dead Letter Queue**: Failed jobs are moved to a dead letter queue for inspection
17
+ - **TypeScript Support**: Full TypeScript support with type definitions
18
+ - **Auto-reconnection**: Automatic reconnection to RabbitMQ on connection loss
19
+ - **Health Monitoring**: Built-in heartbeat system for node health monitoring
20
+
21
+ ## Installation
22
+
23
+ ### From npm (when published)
24
+ ```bash
25
+ npm install nodejs-task-scheduler
26
+ ```
27
+
28
+ ### From GitHub Packages
29
+ ```bash
30
+ npm install @theduchruben/nodejs-task-scheduler
31
+ ```
32
+
33
+ ### Development Setup
34
+ ```bash
35
+ git clone https://github.com/your-username/nodejs-task-scheduler.git
36
+ cd nodejs-task-scheduler
37
+ npm install
38
+ npm run build
39
+ ```
40
+
41
+ ## Prerequisites
42
+
43
+ - **Node.js**: Version 16 or higher
44
+ - **RabbitMQ**: Running RabbitMQ server (local or remote)
45
+ - **TypeScript**: For development (automatically handled by npm scripts)
46
+
47
+ ## Quick Start
48
+
49
+ ```typescript
50
+ import { TaskScheduler } from './src';
51
+
52
+ // Initialize the scheduler
53
+ const scheduler = new TaskScheduler({
54
+ url: 'amqp://localhost:5672'
55
+ });
56
+
57
+ await scheduler.initialize();
58
+
59
+ // Create a worker
60
+ await scheduler.createWorker({
61
+ name: 'email-worker',
62
+ concurrency: 1, // Singleton worker
63
+ queues: ['email-jobs'],
64
+ handlers: {
65
+ 'send-email': async (data) => {
66
+ console.log('Sending email to:', data.email);
67
+ // Email sending logic here
68
+ return { success: true };
69
+ }
70
+ }
71
+ });
72
+
73
+ // Schedule a direct job
74
+ await scheduler.scheduleJob({
75
+ id: 'job-1',
76
+ name: 'Send Welcome Email',
77
+ handler: 'send-email',
78
+ data: { email: 'user@example.com' }
79
+ });
80
+
81
+ // Schedule a cron job
82
+ await scheduler.scheduleCronJob({
83
+ id: 'daily-report',
84
+ name: 'Daily Report',
85
+ handler: 'generate-report',
86
+ schedule: '0 9 * * *', // Every day at 9 AM
87
+ data: { reportType: 'daily' }
88
+ });
89
+ ```
90
+
91
+ ## Configuration
92
+
93
+ ### Connection Configuration
94
+
95
+ ```typescript
96
+ const connectionConfig = {
97
+ url: 'amqp://localhost:5672',
98
+ options: {
99
+ heartbeat: 60,
100
+ // Other amqplib connection options
101
+ }
102
+ };
103
+ ```
104
+
105
+ ### Worker Configuration
106
+
107
+ ```typescript
108
+ const workerConfig = {
109
+ name: 'my-worker',
110
+ concurrency: 5, // Max concurrent jobs (1 for singleton)
111
+ queues: ['queue1', 'queue2'],
112
+ handlers: {
113
+ 'job-type-1': async (data) => {
114
+ // Job handler logic
115
+ return { success: true, data: result };
116
+ },
117
+ 'job-type-2': async (data) => {
118
+ // Another job handler
119
+ return { success: false, error: 'Something went wrong' };
120
+ }
121
+ }
122
+ };
123
+ ```
124
+
125
+ ### Job Configuration
126
+
127
+ ```typescript
128
+ // Direct job
129
+ const jobConfig = {
130
+ id: 'unique-job-id',
131
+ name: 'Job Name',
132
+ handler: 'job-type-1',
133
+ data: { key: 'value' },
134
+ priority: 5, // Higher number = higher priority
135
+ attempts: 3, // Max retry attempts
136
+ backoff: {
137
+ type: 'exponential',
138
+ delay: 1000 // Initial delay in ms
139
+ }
140
+ };
141
+
142
+ // Cron job
143
+ const cronJobConfig = {
144
+ ...jobConfig,
145
+ schedule: '0 */6 * * *', // Every 6 hours
146
+ timezone: 'America/New_York'
147
+ };
148
+ ```
149
+
150
+ ## API Reference
151
+
152
+ ### TaskScheduler
153
+
154
+ #### Methods
155
+
156
+ - `initialize()`: Initialize the scheduler and connect to RabbitMQ
157
+ - `shutdown()`: Gracefully shutdown all workers and connections
158
+ - `createWorker(config)`: Create and start a new worker
159
+ - `scheduleJob(config)`: Schedule a job for immediate execution
160
+ - `scheduleCronJob(config)`: Schedule a recurring cron job
161
+ - `cancelCronJob(jobId)`: Cancel a scheduled cron job
162
+ - `register(instance)`: Register a class instance with decorators
163
+ - `executeJobMethod(className, methodName, data)`: Execute a job method from registered class
164
+ - `getNodeInfo()`: Get information about the current node and active nodes
165
+ - `getWorkerStatus(workerName)`: Get status of a specific worker
166
+ - `getCronJobs()`: Get list of active cron jobs
167
+ - `getRegisteredClasses()`: Get information about registered decorator classes
168
+
169
+ ### Decorators
170
+
171
+ #### Job Decorators
172
+ - `@Job(options?)`: Mark a method as a job handler
173
+ - `@CronJob(options)`: Mark a method as a cron job handler
174
+ - `@SingletonJob(options?)`: Mark a method as singleton job (concurrency: 1)
175
+ - `@HighPriorityJob(options?)`: Mark a method as high priority job
176
+ - `@LowPriorityJob(options?)`: Mark a method as low priority job
177
+ - `@Retry(attempts, type, delay)`: Add retry configuration to a job
178
+
179
+ #### Class Decorators
180
+ - `@Worker(options?)`: Define worker configuration for a class
181
+ - `@Queue(options)`: Define queue configuration for a class
182
+
183
+ ### Job Handlers
184
+
185
+ Job handlers are async functions that process job data:
186
+
187
+ ```typescript
188
+ const handler = async (data: any): Promise<JobResult> => {
189
+ try {
190
+ // Process job data
191
+ const result = await processData(data);
192
+
193
+ return {
194
+ success: true,
195
+ data: result
196
+ };
197
+ } catch (error) {
198
+ return {
199
+ success: false,
200
+ error: error.message
201
+ };
202
+ }
203
+ };
204
+ ```
205
+
206
+ ## Load Balancing
207
+
208
+ The scheduler automatically distributes jobs to the least loaded available nodes. Nodes communicate via heartbeat messages to track their status and current load.
209
+
210
+ ## Error Handling
211
+
212
+ - Failed jobs are automatically retried based on the `attempts` configuration
213
+ - Backoff strategies include fixed delay and exponential backoff
214
+ - Jobs that exceed max attempts are moved to a dead letter queue
215
+ - Connection failures trigger automatic reconnection attempts
216
+
217
+ ## Docker Setup
218
+
219
+ ### Using Docker Compose (Recommended for Testing)
220
+
221
+ Create a `docker-compose.yml` file:
222
+
223
+ ```yaml
224
+ version: '3.8'
225
+ services:
226
+ rabbitmq:
227
+ image: rabbitmq:3-management
228
+ container_name: rabbitmq
229
+ ports:
230
+ - "5672:5672"
231
+ - "15672:15672"
232
+ environment:
233
+ - RABBITMQ_DEFAULT_USER=admin
234
+ - RABBITMQ_DEFAULT_PASS=password
235
+ volumes:
236
+ - rabbitmq_data:/var/lib/rabbitmq
237
+
238
+ scheduler-node-1:
239
+ build: .
240
+ depends_on:
241
+ - rabbitmq
242
+ environment:
243
+ - RABBITMQ_URL=amqp://admin:password@rabbitmq:5672
244
+ - NODE_NAME=scheduler-1
245
+ volumes:
246
+ - ./examples:/app/examples
247
+
248
+ scheduler-node-2:
249
+ build: .
250
+ depends_on:
251
+ - rabbitmq
252
+ environment:
253
+ - RABBITMQ_URL=amqp://admin:password@rabbitmq:5672
254
+ - NODE_NAME=scheduler-2
255
+ volumes:
256
+ - ./examples:/app/examples
257
+
258
+ volumes:
259
+ rabbitmq_data:
260
+ ```
261
+
262
+ Start the services:
263
+ ```bash
264
+ docker-compose up -d
265
+ ```
266
+
267
+ ### RabbitMQ Management UI
268
+ Access the RabbitMQ management interface at http://localhost:15672
269
+ - Username: admin
270
+ - Password: password
271
+
272
+ ## Complete Usage Examples
273
+
274
+ ### Basic Email Service Example
275
+
276
+ ```typescript
277
+ import { TaskScheduler } from 'nodejs-task-scheduler';
278
+
279
+ const scheduler = new TaskScheduler({
280
+ url: process.env.RABBITMQ_URL || 'amqp://localhost:5672'
281
+ });
282
+
283
+ async function setupEmailService() {
284
+ await scheduler.initialize();
285
+
286
+ // Create email worker with singleton processing
287
+ await scheduler.createWorker({
288
+ name: 'email-worker',
289
+ concurrency: 1, // Process one email at a time
290
+ queues: ['email-jobs'],
291
+ handlers: {
292
+ 'send-welcome-email': async (data) => {
293
+ console.log(`Sending welcome email to: ${data.email}`);
294
+
295
+ // Simulate email sending
296
+ await new Promise(resolve => setTimeout(resolve, 1000));
297
+
298
+ if (Math.random() > 0.9) {
299
+ return { success: false, error: 'Email service temporarily unavailable' };
300
+ }
301
+
302
+ return {
303
+ success: true,
304
+ data: { messageId: `msg_${Date.now()}` }
305
+ };
306
+ },
307
+
308
+ 'send-notification': async (data) => {
309
+ console.log(`Sending notification: ${data.message}`);
310
+ return { success: true };
311
+ }
312
+ }
313
+ });
314
+
315
+ // Schedule immediate jobs
316
+ await scheduler.scheduleJob({
317
+ id: 'welcome-123',
318
+ name: 'Welcome Email',
319
+ handler: 'send-welcome-email',
320
+ data: {
321
+ email: 'user@example.com',
322
+ name: 'John Doe'
323
+ },
324
+ attempts: 3,
325
+ backoff: {
326
+ type: 'exponential',
327
+ delay: 2000
328
+ }
329
+ });
330
+
331
+ // Schedule daily email digest
332
+ await scheduler.scheduleCronJob({
333
+ id: 'daily-digest',
334
+ name: 'Daily Email Digest',
335
+ handler: 'send-notification',
336
+ schedule: '0 9 * * *', // 9 AM every day
337
+ timezone: 'America/New_York',
338
+ data: {
339
+ message: 'Your daily digest is ready!'
340
+ }
341
+ });
342
+ }
343
+
344
+ setupEmailService().catch(console.error);
345
+ ```
346
+
347
+ ### Decorator-Based Architecture (Recommended)
348
+
349
+ ```typescript
350
+ import 'reflect-metadata';
351
+ import {
352
+ TaskScheduler,
353
+ Job,
354
+ CronJob,
355
+ Worker,
356
+ Queue,
357
+ SingletonJob,
358
+ HighPriorityJob,
359
+ Retry
360
+ } from 'nodejs-task-scheduler';
361
+
362
+ @Worker({ name: 'email-service', concurrency: 1 })
363
+ @Queue({ name: 'email-queue', durable: true })
364
+ class EmailService {
365
+
366
+ @SingletonJob({ name: 'send-email' })
367
+ @Retry(3, 'exponential', 2000)
368
+ async sendEmail(data: { to: string; subject: string; body: string }) {
369
+ console.log(`📧 Sending email to: ${data.to}`);
370
+
371
+ // Simulate email sending
372
+ await new Promise(resolve => setTimeout(resolve, 1000));
373
+
374
+ return { messageId: `msg_${Date.now()}`, sentAt: new Date() };
375
+ }
376
+
377
+ @CronJob({
378
+ schedule: '0 9 * * *', // 9 AM every day
379
+ name: 'daily-digest',
380
+ timezone: 'America/New_York'
381
+ })
382
+ async sendDailyDigest() {
383
+ console.log('📰 Sending daily digest emails...');
384
+ // Implementation here
385
+ return { digestSent: true };
386
+ }
387
+ }
388
+
389
+ @Worker({ name: 'data-processor', concurrency: 4 })
390
+ class DataProcessingService {
391
+
392
+ @Job({ name: 'process-user-data', priority: 6 })
393
+ @Retry(2, 'exponential', 1000)
394
+ async processUserData(data: { userId: string; records: any[] }) {
395
+ console.log(`🔄 Processing data for user: ${data.userId}`);
396
+ // Processing logic here
397
+ return { processed: true, recordCount: data.records.length };
398
+ }
399
+
400
+ @HighPriorityJob({ name: 'process-urgent-data' })
401
+ async processUrgentData(data: { alertId: string }) {
402
+ console.log(`🚨 Processing urgent data: ${data.alertId}`);
403
+ // Urgent processing logic
404
+ return { processed: true, urgent: true };
405
+ }
406
+
407
+ @CronJob({ schedule: '0 2 * * *', name: 'daily-cleanup' })
408
+ async dailyCleanup() {
409
+ console.log('🌙 Running daily cleanup...');
410
+ return { cleanedUp: true };
411
+ }
412
+ }
413
+
414
+ // Usage
415
+ async function main() {
416
+ const scheduler = new TaskScheduler({
417
+ url: process.env.RABBITMQ_URL || 'amqp://localhost:5672'
418
+ });
419
+
420
+ await scheduler.initialize();
421
+
422
+ // Register services - workers and cron jobs are automatically created
423
+ await scheduler.register(new EmailService());
424
+ await scheduler.register(new DataProcessingService());
425
+
426
+ // Execute jobs directly
427
+ await scheduler.executeJobMethod('EmailService', 'sendEmail', {
428
+ to: 'user@example.com',
429
+ subject: 'Welcome!',
430
+ body: 'Thanks for joining!'
431
+ });
432
+
433
+ await scheduler.executeJobMethod('DataProcessingService', 'processUserData', {
434
+ userId: 'user123',
435
+ records: [{ id: 1, data: 'sample' }]
436
+ });
437
+ }
438
+ ```
439
+
440
+ ### Multi-Service Architecture Example (Traditional)
441
+
442
+ ```typescript
443
+ import { TaskScheduler } from 'nodejs-task-scheduler';
444
+
445
+ class DataProcessingService {
446
+ private scheduler: TaskScheduler;
447
+
448
+ constructor() {
449
+ this.scheduler = new TaskScheduler({
450
+ url: process.env.RABBITMQ_URL || 'amqp://localhost:5672'
451
+ });
452
+ }
453
+
454
+ async start() {
455
+ await this.scheduler.initialize();
456
+
457
+ // High-throughput data processing worker
458
+ await this.scheduler.createWorker({
459
+ name: 'data-processor',
460
+ concurrency: 5, // Process 5 jobs concurrently
461
+ queues: ['data-processing', 'analytics'],
462
+ handlers: {
463
+ 'process-user-data': this.processUserData.bind(this),
464
+ 'generate-analytics': this.generateAnalytics.bind(this),
465
+ 'cleanup-old-data': this.cleanupOldData.bind(this)
466
+ }
467
+ });
468
+
469
+ // Critical operations worker (singleton)
470
+ await this.scheduler.createWorker({
471
+ name: 'critical-ops',
472
+ concurrency: 1,
473
+ queues: ['critical-operations'],
474
+ handlers: {
475
+ 'backup-database': this.backupDatabase.bind(this),
476
+ 'update-system-config': this.updateSystemConfig.bind(this)
477
+ }
478
+ });
479
+
480
+ // Schedule recurring maintenance tasks
481
+ await this.scheduleMaintenance();
482
+ }
483
+
484
+ private async processUserData(data: any) {
485
+ console.log(`Processing data for user: ${data.userId}`);
486
+
487
+ // Simulate data processing
488
+ await new Promise(resolve => setTimeout(resolve, 2000));
489
+
490
+ return {
491
+ success: true,
492
+ data: { processedRecords: data.records?.length || 0 }
493
+ };
494
+ }
495
+
496
+ private async generateAnalytics(data: any) {
497
+ console.log(`Generating analytics report: ${data.reportType}`);
498
+
499
+ // Simulate analytics generation
500
+ await new Promise(resolve => setTimeout(resolve, 5000));
501
+
502
+ return {
503
+ success: true,
504
+ data: { reportUrl: `https://reports.example.com/${data.reportType}` }
505
+ };
506
+ }
507
+
508
+ private async cleanupOldData(data: any) {
509
+ console.log(`Cleaning up data older than: ${data.days} days`);
510
+
511
+ // Simulate cleanup
512
+ await new Promise(resolve => setTimeout(resolve, 1000));
513
+
514
+ return {
515
+ success: true,
516
+ data: { deletedRecords: Math.floor(Math.random() * 1000) }
517
+ };
518
+ }
519
+
520
+ private async backupDatabase(data: any) {
521
+ console.log('Starting database backup...');
522
+
523
+ // Simulate backup
524
+ await new Promise(resolve => setTimeout(resolve, 10000));
525
+
526
+ return {
527
+ success: true,
528
+ data: { backupSize: '2.5GB', location: '/backups/2024-01-01.sql' }
529
+ };
530
+ }
531
+
532
+ private async updateSystemConfig(data: any) {
533
+ console.log(`Updating system config: ${data.configKey}`);
534
+
535
+ return { success: true };
536
+ }
537
+
538
+ private async scheduleMaintenance() {
539
+ // Daily cleanup at 2 AM
540
+ await this.scheduler.scheduleCronJob({
541
+ id: 'daily-cleanup',
542
+ name: 'Daily Data Cleanup',
543
+ handler: 'cleanup-old-data',
544
+ schedule: '0 2 * * *',
545
+ data: { days: 30 }
546
+ });
547
+
548
+ // Weekly database backup on Sundays at 3 AM
549
+ await this.scheduler.scheduleCronJob({
550
+ id: 'weekly-backup',
551
+ name: 'Weekly Database Backup',
552
+ handler: 'backup-database',
553
+ schedule: '0 3 * * 0',
554
+ data: { type: 'full' }
555
+ });
556
+
557
+ // Generate daily analytics report at 6 AM
558
+ await this.scheduler.scheduleCronJob({
559
+ id: 'daily-analytics',
560
+ name: 'Daily Analytics Report',
561
+ handler: 'generate-analytics',
562
+ schedule: '0 6 * * *',
563
+ data: { reportType: 'daily' }
564
+ });
565
+ }
566
+
567
+ async processUserSignup(userId: string, userData: any) {
568
+ // Schedule user data processing
569
+ return await this.scheduler.scheduleJob({
570
+ id: `user-signup-${userId}`,
571
+ name: 'Process User Signup',
572
+ handler: 'process-user-data',
573
+ data: { userId, records: userData },
574
+ priority: 5
575
+ });
576
+ }
577
+
578
+ async shutdown() {
579
+ await this.scheduler.shutdown();
580
+ }
581
+ }
582
+
583
+ // Usage
584
+ const service = new DataProcessingService();
585
+ service.start().then(() => {
586
+ console.log('Data processing service started');
587
+
588
+ // Example: Process a new user signup
589
+ service.processUserSignup('user123', { name: 'John', email: 'john@example.com' });
590
+ });
591
+
592
+ // Graceful shutdown
593
+ process.on('SIGINT', async () => {
594
+ console.log('Shutting down gracefully...');
595
+ await service.shutdown();
596
+ process.exit(0);
597
+ });
598
+ ```
599
+
600
+ ## Examples
601
+
602
+ ```typescript
603
+ // Node 1 - Scheduler + Worker
604
+ const scheduler1 = new TaskScheduler({ url: 'amqp://localhost:5672' });
605
+ await scheduler1.initialize();
606
+
607
+ await scheduler1.createWorker({
608
+ name: 'worker-1',
609
+ concurrency: 3,
610
+ queues: ['tasks'],
611
+ handlers: { 'process': processHandler }
612
+ });
613
+
614
+ // Node 2 - Worker Only
615
+ const scheduler2 = new TaskScheduler({ url: 'amqp://localhost:5672' });
616
+ await scheduler2.initialize();
617
+
618
+ await scheduler2.createWorker({
619
+ name: 'worker-2',
620
+ concurrency: 5,
621
+ queues: ['tasks'],
622
+ handlers: { 'process': processHandler }
623
+ });
624
+
625
+ // Jobs scheduled on Node 1 will be distributed between both workers
626
+ ```
627
+
628
+ ### Singleton Worker
629
+
630
+ ```typescript
631
+ // Ensures only one job runs at a time
632
+ await scheduler.createWorker({
633
+ name: 'singleton-worker',
634
+ concurrency: 1,
635
+ queues: ['critical-tasks'],
636
+ handlers: {
637
+ 'critical-job': async (data) => {
638
+ // Only one instance of this job runs at a time
639
+ return { success: true };
640
+ }
641
+ }
642
+ });
643
+ ```
644
+
645
+ ## Requirements
646
+
647
+ - Node.js 16+
648
+ - RabbitMQ server
649
+ - TypeScript (for development)
650
+
651
+ ## License
652
+
653
+ MIT
@@ -0,0 +1,2 @@
1
+ import 'reflect-metadata';
2
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/__tests__/setup.ts"],"names":[],"mappings":"AACA,OAAO,kBAAkB,CAAC"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ // Test setup file
4
+ require("reflect-metadata");
5
+ // Suppress console logs during tests unless debugging
6
+ if (!process.env.DEBUG_TESTS) {
7
+ global.console = {
8
+ ...console,
9
+ log: jest.fn(),
10
+ warn: jest.fn(),
11
+ error: jest.fn(),
12
+ };
13
+ }
14
+ // Global test timeout
15
+ jest.setTimeout(10000);
16
+ // Handle unhandled promise rejections in tests
17
+ process.on('unhandledRejection', (reason, promise) => {
18
+ // Ignore unhandled rejections during tests
19
+ });
20
+ // Clean up any lingering timers after each test
21
+ afterEach(() => {
22
+ jest.clearAllTimers();
23
+ });
24
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/__tests__/setup.ts"],"names":[],"mappings":";;AAAA,kBAAkB;AAClB,4BAA0B;AAE1B,sDAAsD;AACtD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;IAC7B,MAAM,CAAC,OAAO,GAAG;QACf,GAAG,OAAO;QACV,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE;QACd,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;QACf,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE;KACjB,CAAC;AACJ,CAAC;AAED,sBAAsB;AACtB,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AAEvB,+CAA+C;AAC/C,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE;IACnD,2CAA2C;AAC7C,CAAC,CAAC,CAAC;AAEH,gDAAgD;AAChD,SAAS,CAAC,GAAG,EAAE;IACb,IAAI,CAAC,cAAc,EAAE,CAAC;AACxB,CAAC,CAAC,CAAC"}