@boringnode/queue 0.0.1-alpha.4 → 0.2.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.
- package/README.md +156 -32
- package/build/{chunk-5PDDRF5O.js → chunk-NPQKBCCY.js} +2 -2
- package/build/{chunk-CD45GT6E.js → chunk-RIXMQXYJ.js} +5 -4
- package/build/chunk-RIXMQXYJ.js.map +1 -0
- package/build/{chunk-HMGNQSSG.js → chunk-SMOKFZ46.js} +15 -1
- package/build/chunk-SMOKFZ46.js.map +1 -0
- package/build/{index-C3_tlebh.d.ts → index-C0Xg6F4E.d.ts} +433 -35
- package/build/index.d.ts +81 -4
- package/build/index.js +428 -30
- package/build/index.js.map +1 -1
- package/build/src/contracts/adapter.d.ts +1 -1
- package/build/src/drivers/knex_adapter.d.ts +8 -1
- package/build/src/drivers/knex_adapter.js +186 -34
- package/build/src/drivers/knex_adapter.js.map +1 -1
- package/build/src/drivers/redis_adapter.d.ts +7 -1
- package/build/src/drivers/redis_adapter.js +193 -2
- package/build/src/drivers/redis_adapter.js.map +1 -1
- package/build/src/drivers/sync_adapter.d.ts +7 -1
- package/build/src/drivers/sync_adapter.js +24 -5
- package/build/src/drivers/sync_adapter.js.map +1 -1
- package/build/src/types/index.d.ts +1 -1
- package/build/src/types/main.d.ts +1 -1
- package/package.json +3 -2
- package/build/chunk-CD45GT6E.js.map +0 -1
- package/build/chunk-HMGNQSSG.js.map +0 -1
- /package/build/{chunk-5PDDRF5O.js.map → chunk-NPQKBCCY.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @boringnode/queue
|
|
2
2
|
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
[![typescript-image]][typescript-url]
|
|
6
|
+
[![gh-workflow-image]][gh-workflow-url]
|
|
7
|
+
[![npm-image]][npm-url]
|
|
8
|
+
[![npm-download-image]][npm-download-url]
|
|
9
|
+
[![license-image]][license-url]
|
|
10
|
+
|
|
11
|
+
</div>
|
|
12
|
+
|
|
3
13
|
A simple and efficient queue system for Node.js applications. Built for simplicity and ease of use, `@boringnode/queue` allows you to dispatch background jobs and process them asynchronously with support for multiple queue adapters.
|
|
4
14
|
|
|
5
15
|
## Installation
|
|
@@ -19,6 +29,7 @@ npm install @boringnode/queue
|
|
|
19
29
|
- **Priority Queues**: Process high-priority jobs first
|
|
20
30
|
- **Retry with Backoff**: Automatic retries with exponential, linear, or fixed backoff strategies
|
|
21
31
|
- **Job Timeout**: Automatically fail or retry jobs that exceed a time limit
|
|
32
|
+
- **Scheduled Jobs**: Cron-based or interval-based job scheduling with pause/resume support
|
|
22
33
|
|
|
23
34
|
## Quick Start
|
|
24
35
|
|
|
@@ -28,29 +39,27 @@ Create a job by extending the `Job` class:
|
|
|
28
39
|
|
|
29
40
|
```typescript
|
|
30
41
|
import { Job } from '@boringnode/queue'
|
|
31
|
-
import type {
|
|
42
|
+
import type { JobOptions } from '@boringnode/queue/types'
|
|
32
43
|
|
|
33
44
|
interface SendEmailPayload {
|
|
34
45
|
to: string
|
|
35
46
|
}
|
|
36
47
|
|
|
37
48
|
export default class SendEmailJob extends Job<SendEmailPayload> {
|
|
38
|
-
static readonly jobName = 'SendEmailJob'
|
|
39
|
-
|
|
40
49
|
static options: JobOptions = {
|
|
41
50
|
queue: 'email',
|
|
42
51
|
}
|
|
43
52
|
|
|
44
|
-
constructor(payload: SendEmailPayload, context: JobContext) {
|
|
45
|
-
super(payload, context)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
53
|
async execute(): Promise<void> {
|
|
49
54
|
console.log(`[Attempt ${this.context.attempt}] Sending email to: ${this.payload.to}`)
|
|
50
55
|
}
|
|
51
56
|
}
|
|
52
57
|
```
|
|
53
58
|
|
|
59
|
+
> **Note**: The job name defaults to the class name (`SendEmailJob`). You can override it with `name: 'CustomName'` in options if needed.
|
|
60
|
+
>
|
|
61
|
+
> **Warning**: If you minify your code in production, class names may be mangled. In that case, always specify `name` explicitly in your job options.
|
|
62
|
+
|
|
54
63
|
### 2. Configure the Queue Manager
|
|
55
64
|
|
|
56
65
|
```typescript
|
|
@@ -125,7 +134,7 @@ interface QueueManagerConfig {
|
|
|
125
134
|
// Worker configuration
|
|
126
135
|
worker: {
|
|
127
136
|
concurrency: number
|
|
128
|
-
|
|
137
|
+
idleDelay: Duration
|
|
129
138
|
}
|
|
130
139
|
|
|
131
140
|
// Job discovery locations
|
|
@@ -243,8 +252,6 @@ Jobs with lower priority numbers are processed first:
|
|
|
243
252
|
|
|
244
253
|
```typescript
|
|
245
254
|
export default class UrgentJob extends Job<Payload> {
|
|
246
|
-
static readonly jobName = 'UrgentJob'
|
|
247
|
-
|
|
248
255
|
static options: JobOptions = {
|
|
249
256
|
priority: 1, // Processed before default priority (5)
|
|
250
257
|
}
|
|
@@ -263,8 +270,6 @@ Configure automatic retries with backoff strategies:
|
|
|
263
270
|
import { exponentialBackoff, linearBackoff, fixedBackoff } from '@boringnode/queue'
|
|
264
271
|
|
|
265
272
|
export default class ReliableJob extends Job<Payload> {
|
|
266
|
-
static readonly jobName = 'ReliableJob'
|
|
267
|
-
|
|
268
273
|
static options: JobOptions = {
|
|
269
274
|
maxRetries: 5,
|
|
270
275
|
retry: {
|
|
@@ -296,8 +301,6 @@ Set a maximum execution time for jobs:
|
|
|
296
301
|
|
|
297
302
|
```typescript
|
|
298
303
|
export default class LimitedJob extends Job<Payload> {
|
|
299
|
-
static readonly jobName = 'LimitedJob'
|
|
300
|
-
|
|
301
304
|
static options: JobOptions = {
|
|
302
305
|
timeout: '30s', // Maximum execution time
|
|
303
306
|
failOnTimeout: false, // Retry on timeout (default)
|
|
@@ -319,19 +322,42 @@ const config = {
|
|
|
319
322
|
}
|
|
320
323
|
```
|
|
321
324
|
|
|
325
|
+
### Handling Timeout Gracefully
|
|
326
|
+
|
|
327
|
+
Jobs have access to an abort signal via `this.signal` to handle timeouts gracefully:
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
export default class LongRunningJob extends Job<Payload> {
|
|
331
|
+
static options: JobOptions = {
|
|
332
|
+
timeout: '30s',
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async execute(): Promise<void> {
|
|
336
|
+
for (const item of this.payload.items) {
|
|
337
|
+
// Check if the job has been aborted
|
|
338
|
+
if (this.signal?.aborted) {
|
|
339
|
+
throw new Error('Job timed out')
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
await this.processItem(item)
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private async processItem(item: any): Promise<void> {
|
|
347
|
+
// Pass the signal to fetch or other async operations
|
|
348
|
+
await fetch(item.url, { signal: this.signal })
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
322
353
|
## Job Context
|
|
323
354
|
|
|
324
355
|
Every job has access to execution context via `this.context`. This provides metadata about the current job execution:
|
|
325
356
|
|
|
326
357
|
```typescript
|
|
327
358
|
import { Job } from '@boringnode/queue'
|
|
328
|
-
import type { JobContext } from '@boringnode/queue'
|
|
329
359
|
|
|
330
360
|
export default class MyJob extends Job<Payload> {
|
|
331
|
-
constructor(payload: Payload, context: JobContext) {
|
|
332
|
-
super(payload, context)
|
|
333
|
-
}
|
|
334
|
-
|
|
335
361
|
async execute(): Promise<void> {
|
|
336
362
|
console.log(`Job ID: ${this.context.jobId}`)
|
|
337
363
|
console.log(`Attempt: ${this.context.attempt}`) // 1, 2, 3...
|
|
@@ -349,7 +375,7 @@ export default class MyJob extends Job<Payload> {
|
|
|
349
375
|
### Context Properties
|
|
350
376
|
|
|
351
377
|
| Property | Type | Description |
|
|
352
|
-
|
|
378
|
+
|----------------|--------|-------------------------------------------------|
|
|
353
379
|
| `jobId` | string | Unique identifier for this job |
|
|
354
380
|
| `name` | string | Job class name |
|
|
355
381
|
| `attempt` | number | Current attempt number (1-based) |
|
|
@@ -360,7 +386,7 @@ export default class MyJob extends Job<Payload> {
|
|
|
360
386
|
|
|
361
387
|
## Dependency Injection
|
|
362
388
|
|
|
363
|
-
Use the `jobFactory` option to integrate with IoC containers for dependency injection.
|
|
389
|
+
Use the `jobFactory` option to integrate with IoC containers for dependency injection. The constructor is reserved for injecting dependencies - payload and context are provided separately by the worker.
|
|
364
390
|
|
|
365
391
|
```typescript
|
|
366
392
|
import { QueueManager } from '@boringnode/queue'
|
|
@@ -368,9 +394,9 @@ import { QueueManager } from '@boringnode/queue'
|
|
|
368
394
|
await QueueManager.init({
|
|
369
395
|
default: 'redis',
|
|
370
396
|
adapters: { redis: redis(connection) },
|
|
371
|
-
jobFactory: async (JobClass
|
|
397
|
+
jobFactory: async (JobClass) => {
|
|
372
398
|
// Use your IoC container to instantiate jobs
|
|
373
|
-
return app.container.make(JobClass
|
|
399
|
+
return app.container.make(JobClass)
|
|
374
400
|
},
|
|
375
401
|
})
|
|
376
402
|
```
|
|
@@ -379,7 +405,6 @@ Example with injected dependencies:
|
|
|
379
405
|
|
|
380
406
|
```typescript
|
|
381
407
|
import { Job } from '@boringnode/queue'
|
|
382
|
-
import type { JobContext } from '@boringnode/queue'
|
|
383
408
|
|
|
384
409
|
interface SendEmailPayload {
|
|
385
410
|
to: string
|
|
@@ -387,15 +412,11 @@ interface SendEmailPayload {
|
|
|
387
412
|
}
|
|
388
413
|
|
|
389
414
|
export default class SendEmailJob extends Job<SendEmailPayload> {
|
|
390
|
-
static readonly jobName = 'SendEmailJob'
|
|
391
|
-
|
|
392
415
|
constructor(
|
|
393
|
-
payload: SendEmailPayload,
|
|
394
|
-
context: JobContext,
|
|
395
416
|
private mailer: MailerService, // Injected by IoC container
|
|
396
417
|
private logger: Logger // Injected by IoC container
|
|
397
418
|
) {
|
|
398
|
-
super(
|
|
419
|
+
super()
|
|
399
420
|
}
|
|
400
421
|
|
|
401
422
|
async execute(): Promise<void> {
|
|
@@ -405,7 +426,98 @@ export default class SendEmailJob extends Job<SendEmailPayload> {
|
|
|
405
426
|
}
|
|
406
427
|
```
|
|
407
428
|
|
|
408
|
-
Without a `jobFactory`, jobs are instantiated with `new JobClass(
|
|
429
|
+
Without a `jobFactory`, jobs are instantiated with `new JobClass()`.
|
|
430
|
+
|
|
431
|
+
## Scheduled Jobs
|
|
432
|
+
|
|
433
|
+
Schedule jobs to run on a recurring basis using cron expressions or fixed intervals. Schedules are persisted and survive worker restarts.
|
|
434
|
+
|
|
435
|
+
### Creating a Schedule
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
import { Schedule } from '@boringnode/queue'
|
|
439
|
+
|
|
440
|
+
// Run every 10 seconds (uses job name as schedule ID by default)
|
|
441
|
+
const { scheduleId } = await MetricsJob.schedule({ endpoint: '/api/health' }).every('10s').run()
|
|
442
|
+
|
|
443
|
+
// Run on a cron schedule with custom ID
|
|
444
|
+
await CleanupJob.schedule({ days: 30 })
|
|
445
|
+
.id('daily-cleanup') // Custom ID (optional, defaults to job name)
|
|
446
|
+
.cron('0 * * * *') // Every hour at minute 0
|
|
447
|
+
.timezone('Europe/Paris') // Optional timezone (default: UTC)
|
|
448
|
+
.run()
|
|
449
|
+
|
|
450
|
+
// Schedule with constraints
|
|
451
|
+
await ReportJob.schedule({ type: 'weekly' })
|
|
452
|
+
.id('weekly-report')
|
|
453
|
+
.cron('0 9 * * MON') // Every Monday at 9am
|
|
454
|
+
.from(new Date('2024-01-01')) // Start date
|
|
455
|
+
.to(new Date('2024-12-31')) // End date
|
|
456
|
+
.limit(52) // Maximum 52 runs
|
|
457
|
+
.run()
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Managing Schedules
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
import { Schedule } from '@boringnode/queue'
|
|
464
|
+
|
|
465
|
+
// Find a schedule by ID
|
|
466
|
+
const schedule = await Schedule.find('health-check')
|
|
467
|
+
|
|
468
|
+
if (schedule) {
|
|
469
|
+
console.log(`Status: ${schedule.status}`) // 'active' or 'paused'
|
|
470
|
+
console.log(`Run count: ${schedule.runCount}`)
|
|
471
|
+
console.log(`Next run: ${schedule.nextRunAt}`)
|
|
472
|
+
console.log(`Last run: ${schedule.lastRunAt}`)
|
|
473
|
+
|
|
474
|
+
// Pause the schedule
|
|
475
|
+
await schedule.pause()
|
|
476
|
+
|
|
477
|
+
// Resume the schedule
|
|
478
|
+
await schedule.resume()
|
|
479
|
+
|
|
480
|
+
// Trigger an immediate run (outside of the normal schedule)
|
|
481
|
+
await schedule.trigger()
|
|
482
|
+
|
|
483
|
+
// Delete the schedule
|
|
484
|
+
await schedule.delete()
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### Listing Schedules
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
import { Schedule } from '@boringnode/queue'
|
|
492
|
+
|
|
493
|
+
// List all schedules
|
|
494
|
+
const all = await Schedule.list()
|
|
495
|
+
|
|
496
|
+
// Filter by status
|
|
497
|
+
const active = await Schedule.list({ status: 'active' })
|
|
498
|
+
const paused = await Schedule.list({ status: 'paused' })
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### Schedule Options
|
|
502
|
+
|
|
503
|
+
| Method | Description |
|
|
504
|
+
|----------------------|-------------------------------------------------|
|
|
505
|
+
| `.id(string)` | Unique identifier (defaults to job name) |
|
|
506
|
+
| `.every(duration)` | Run at fixed intervals ('5s', '1m', '1h', '1d') |
|
|
507
|
+
| `.cron(expression)` | Run on a cron schedule |
|
|
508
|
+
| `.timezone(tz)` | Timezone for cron expressions (default: 'UTC') |
|
|
509
|
+
| `.from(date)` | Don't run before this date |
|
|
510
|
+
| `.to(date)` | Don't run after this date |
|
|
511
|
+
| `.between(from, to)` | Shorthand for `.from().to()` |
|
|
512
|
+
| `.limit(n)` | Maximum number of runs |
|
|
513
|
+
|
|
514
|
+
### How Scheduling Works
|
|
515
|
+
|
|
516
|
+
- Schedules are **persisted** in the database (via the adapter)
|
|
517
|
+
- The **Worker** polls for due schedules and dispatches jobs automatically
|
|
518
|
+
- Each schedule run creates a **new job** with a unique ID
|
|
519
|
+
- Multiple workers can run concurrently - only one will claim each due schedule
|
|
520
|
+
- Failed jobs do **not** affect the schedule (the next run will still occur)
|
|
409
521
|
|
|
410
522
|
## Job Discovery
|
|
411
523
|
|
|
@@ -420,10 +532,11 @@ const config = {
|
|
|
420
532
|
Jobs must:
|
|
421
533
|
|
|
422
534
|
- Extend the `Job` class
|
|
423
|
-
- Have a static `jobName` property
|
|
424
535
|
- Implement the `execute` method
|
|
425
536
|
- Be exported as default
|
|
426
537
|
|
|
538
|
+
The job name is automatically derived from the class name, or can be explicitly set via `static options = { name: 'CustomName' }`.
|
|
539
|
+
|
|
427
540
|
## Logging
|
|
428
541
|
|
|
429
542
|
You can pass a logger to the queue manager for debugging or monitoring. The logger must be compatible with the [pino](https://github.com/pinojs/pino) interface.
|
|
@@ -449,7 +562,7 @@ By default, a simple console logger is used that only outputs warnings and error
|
|
|
449
562
|
Performance comparison with BullMQ using realistic jobs (5ms simulated work per job):
|
|
450
563
|
|
|
451
564
|
| Jobs | Concurrency | @boringnode/queue | BullMQ | Diff |
|
|
452
|
-
|
|
565
|
+
|------|-------------|-------------------|--------|-------------|
|
|
453
566
|
| 100 | 1 | 562ms | 596ms | 5.7% faster |
|
|
454
567
|
| 100 | 5 | 116ms | 117ms | ~same |
|
|
455
568
|
| 100 | 10 | 62ms | 62ms | ~same |
|
|
@@ -475,3 +588,14 @@ npm run benchmark
|
|
|
475
588
|
# Custom job duration
|
|
476
589
|
npm run benchmark -- --duration=10
|
|
477
590
|
```
|
|
591
|
+
|
|
592
|
+
[gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/boringnode/queue/checks.yml?branch=main&style=for-the-badge
|
|
593
|
+
[gh-workflow-url]: https://github.com/boringnode/queue/actions/workflows/checks.yml
|
|
594
|
+
[npm-image]: https://img.shields.io/npm/v/@boringnode/queue.svg?style=for-the-badge&logo=npm
|
|
595
|
+
[npm-url]: https://www.npmjs.com/package/@boringnode/queue
|
|
596
|
+
[npm-download-image]: https://img.shields.io/npm/dm/@boringnode/queue?style=for-the-badge
|
|
597
|
+
[npm-download-url]: https://www.npmjs.com/package/@boringnode/queue
|
|
598
|
+
[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
|
|
599
|
+
[typescript-url]: https://www.typescriptlang.org
|
|
600
|
+
[license-image]: https://img.shields.io/npm/l/@boringnode/queue?color=blueviolet&style=for-the-badge
|
|
601
|
+
[license-url]: LICENSE.md
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
E_INVALID_DURATION_EXPRESSION,
|
|
3
3
|
PRIORITY_SCORE_MULTIPLIER
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SMOKFZ46.js";
|
|
5
5
|
|
|
6
6
|
// src/utils.ts
|
|
7
7
|
import { parse as parseDuration } from "@lukeed/ms";
|
|
@@ -23,4 +23,4 @@ export {
|
|
|
23
23
|
parse,
|
|
24
24
|
calculateScore
|
|
25
25
|
};
|
|
26
|
-
//# sourceMappingURL=chunk-
|
|
26
|
+
//# sourceMappingURL=chunk-NPQKBCCY.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
E_CONFIGURATION_ERROR,
|
|
4
4
|
E_JOB_NOT_FOUND,
|
|
5
5
|
E_QUEUE_NOT_INITIALIZED
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SMOKFZ46.js";
|
|
7
7
|
|
|
8
8
|
// src/debug.ts
|
|
9
9
|
import { debuglog } from "util";
|
|
@@ -57,8 +57,9 @@ var LocatorSingleton = class {
|
|
|
57
57
|
const absolutePath = resolve(file);
|
|
58
58
|
const module = await import(`file://${absolutePath}`);
|
|
59
59
|
const JobClass = module.default;
|
|
60
|
-
if (JobClass && typeof JobClass === "function"
|
|
61
|
-
|
|
60
|
+
if (JobClass && typeof JobClass === "function") {
|
|
61
|
+
const jobName = JobClass.options?.name || JobClass.name;
|
|
62
|
+
this.register(jobName, JobClass);
|
|
62
63
|
registered++;
|
|
63
64
|
}
|
|
64
65
|
} catch (error) {
|
|
@@ -354,4 +355,4 @@ export {
|
|
|
354
355
|
Locator,
|
|
355
356
|
QueueManager
|
|
356
357
|
};
|
|
357
|
-
//# sourceMappingURL=chunk-
|
|
358
|
+
//# sourceMappingURL=chunk-RIXMQXYJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/debug.ts","../src/locator.ts","../src/logger.ts","../src/queue_manager.ts"],"sourcesContent":["import { debuglog } from 'node:util'\n\nexport default debuglog('boringnode:queue')\n","import { Job } from './job.js'\nimport * as errors from './exceptions.js'\nimport type { JobClass } from './types/main.js'\nimport debug from './debug.js'\nimport { glob } from 'node:fs/promises'\nimport { resolve } from 'node:path'\n\n/**\n * Job class registry.\n *\n * The Locator maintains a mapping of job names to their classes,\n * allowing the Worker to instantiate jobs by name when processing.\n *\n * Jobs are typically registered automatically via `QueueManager.init()`\n * using the `locations` config option, but can also be registered manually.\n *\n * @example\n * ```typescript\n * import { Locator } from '@boringnode/queue'\n * import SendEmailJob from './jobs/send_email_job.js'\n *\n * // Manual registration\n * Locator.register('SendEmailJob', SendEmailJob)\n *\n * // Auto-registration via glob (used by QueueManager.init)\n * await Locator.registerFromGlob(['./jobs/**\\/*.js'])\n *\n * // Retrieve a job class\n * const JobClass = Locator.getOrThrow('SendEmailJob')\n * ```\n */\nclass LocatorSingleton {\n #registry = new Map<string, JobClass>()\n\n /**\n * Register a job class with a given name.\n *\n * @param name - The job name (usually the class name)\n * @param JobClass - The job class constructor\n *\n * @example\n * ```typescript\n * Locator.register('SendEmailJob', SendEmailJob)\n * ```\n */\n register<T extends Job>(name: string, JobClass: JobClass<T>) {\n debug('registering job: %s', name)\n\n this.#registry.set(name, JobClass)\n }\n\n /**\n * Auto-register job classes from files matching glob patterns.\n *\n * Each file should have a default export that is a Job class.\n * The class name is used as the registration name.\n *\n * @param patterns - Glob patterns to match job files\n * @returns Number of jobs successfully registered\n *\n * @example\n * ```typescript\n * const count = await Locator.registerFromGlob([\n * './jobs/**\\/*.js',\n * './app/jobs/**\\/*.ts'\n * ])\n * console.log(`Registered ${count} jobs`)\n * ```\n */\n async registerFromGlob(patterns: string[]): Promise<number> {\n let registered = 0\n\n for (const pattern of patterns) {\n debug('registering jobs from glob pattern: %s', pattern)\n for await (const file of glob(pattern)) {\n debug('found job file: %s', file)\n\n try {\n const absolutePath = resolve(file)\n const module = await import(`file://${absolutePath}`)\n const JobClass = module.default as JobClass\n\n if (JobClass && typeof JobClass === 'function') {\n const jobName = JobClass.options?.name || JobClass.name\n this.register(jobName, JobClass)\n registered++\n }\n } catch (error) {\n console.warn(`Failed to load job from ${file}:`, error)\n }\n }\n }\n\n return registered\n }\n\n /**\n * Get a job class by name.\n *\n * @param name - The job name to look up\n * @returns The job class, or undefined if not found\n *\n * @example\n * ```typescript\n * const JobClass = Locator.get('SendEmailJob')\n * if (JobClass) {\n * const instance = new JobClass(payload)\n * }\n * ```\n */\n get<T extends Job = Job>(name: string): JobClass<T> | undefined {\n return this.#registry.get(name) as JobClass<T> | undefined\n }\n\n /**\n * Get a job class by name, throwing if not found.\n *\n * @param name - The job name to look up\n * @returns The job class\n * @throws {E_JOB_NOT_FOUND} If the job is not registered\n *\n * @example\n * ```typescript\n * const JobClass = Locator.getOrThrow('SendEmailJob')\n * const instance = new JobClass(payload)\n * ```\n */\n getOrThrow<T extends Job = Job>(name: string): JobClass<T> {\n const JobClass = this.get<T>(name)\n\n if (!JobClass) {\n throw new errors.E_JOB_NOT_FOUND([name])\n }\n\n return JobClass\n }\n\n /**\n * Remove all registered jobs.\n *\n * Primarily useful for testing.\n */\n clear(): void {\n this.#registry.clear()\n }\n}\n\n/** Global job class registry singleton */\nexport const Locator = new LocatorSingleton()\n","export interface LogObject {\n [key: string]: unknown\n}\n\nexport interface ErrorObject extends LogObject {\n err?: Error\n}\n\nexport interface Logger {\n trace(msg: string): void\n trace(obj: LogObject, msg: string): void\n\n debug(msg: string): void\n debug(obj: LogObject, msg: string): void\n\n info(msg: string): void\n info(obj: LogObject, msg: string): void\n\n warn(msg: string): void\n warn(obj: LogObject, msg: string): void\n\n error(msg: string): void\n error(obj: ErrorObject, msg: string): void\n\n child(obj: LogObject): Logger\n}\n\n/**\n * A simple logger that writes to console.\n */\nclass ConsoleLogger implements Logger {\n #prefix: string\n\n constructor(prefix: string = 'queue') {\n this.#prefix = prefix\n }\n\n #format(level: string, msgOrObj: string | LogObject, msg?: string): [string, LogObject?] {\n const prefix = `[${this.#prefix}] ${level}:`\n\n if (typeof msgOrObj === 'object') {\n return [`${prefix} ${msg}`, msgOrObj]\n }\n\n return [`${prefix} ${msgOrObj}`]\n }\n\n trace(msg: string): void\n trace(obj: LogObject, msg: string): void\n trace(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('TRACE', msgOrObj, msg)\n\n if (obj) {\n return console.log(message, obj)\n }\n\n console.log(message)\n }\n\n debug(msg: string): void\n debug(obj: LogObject, msg: string): void\n debug(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('DEBUG', msgOrObj, msg)\n\n if (obj) {\n return console.log(message, obj)\n }\n\n console.log(message)\n }\n\n info(msg: string): void\n info(obj: LogObject, msg: string): void\n info(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('INFO', msgOrObj, msg)\n\n if (obj) {\n return console.log(message, obj)\n }\n\n console.log(message)\n }\n\n warn(msg: string): void\n warn(obj: LogObject, msg: string): void\n warn(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('WARN', msgOrObj, msg)\n\n if (obj) {\n return console.warn(message, obj)\n }\n\n console.warn(message)\n }\n\n error(msg: string): void\n error(obj: ErrorObject, msg: string): void\n error(msgOrObj: string | ErrorObject, msg?: string): void {\n const [message, obj] = this.#format('ERROR', msgOrObj, msg)\n\n if (obj) {\n return console.error(message, obj)\n }\n\n console.error(message)\n }\n\n child(obj: LogObject): Logger {\n const childPrefix = obj.pkg ? String(obj.pkg) : this.#prefix\n return new ConsoleLogger(childPrefix)\n }\n}\n\nexport const consoleLogger = new ConsoleLogger()\n","import * as errors from './exceptions.js'\nimport debug from './debug.js'\nimport { Locator } from './locator.js'\nimport { consoleLogger, type Logger } from './logger.js'\nimport type { Adapter } from './contracts/adapter.js'\nimport type {\n AdapterFactory,\n JobFactory,\n QueueConfig,\n QueueManagerConfig,\n RetryConfig,\n} from './types/main.js'\n\n/**\n * Central configuration and adapter management for the queue system.\n *\n * The QueueManager is responsible for:\n * - Initializing adapters and job registration\n * - Providing adapter instances to workers and dispatchers\n * - Managing retry configuration across global, queue, and job levels\n *\n * @example\n * ```typescript\n * import { QueueManager, redis } from '@boringnode/queue'\n *\n * await QueueManager.init({\n * default: 'redis',\n * adapters: {\n * redis: redis({ host: 'localhost' }),\n * },\n * locations: ['./jobs/**\\/*.js'],\n * retry: {\n * maxRetries: 3,\n * backoff: exponentialBackoff(),\n * },\n * })\n *\n * // Get the default adapter\n * const adapter = QueueManager.use()\n *\n * // Clean up when done\n * await QueueManager.destroy()\n * ```\n */\nclass QueueManagerSingleton {\n #initialized = false\n #defaultAdapter!: string\n #adapters: Record<string, AdapterFactory> = {}\n #adapterInstances: Map<string, Adapter> = new Map()\n #globalRetryConfig?: RetryConfig\n #queueConfigs: Map<string, QueueConfig> = new Map()\n #logger: Logger = consoleLogger\n #jobFactory?: JobFactory\n\n /**\n * Initialize the queue system with the given configuration.\n *\n * This must be called before using the queue system. It:\n * - Validates the configuration\n * - Registers adapters\n * - Auto-discovers and registers job classes from `locations`\n *\n * @param config - The queue configuration\n * @returns This instance for chaining\n * @throws {E_CONFIGURATION_ERROR} If the configuration is invalid\n *\n * @example\n * ```typescript\n * await QueueManager.init({\n * default: 'redis',\n * adapters: {\n * redis: redis(),\n * postgres: knex(pgConfig),\n * },\n * locations: ['./jobs/**\\/*.js'],\n * })\n * ```\n */\n async init(config: QueueManagerConfig) {\n debug('initializing queue manager with config: %O', config)\n\n this.#validateConfig(config)\n\n this.#adapterInstances.clear()\n\n this.#defaultAdapter = config.default\n this.#adapters = config.adapters\n this.#globalRetryConfig = config.retry\n this.#logger = config.logger ?? consoleLogger\n this.#jobFactory = config.jobFactory\n\n if (config.queues) {\n for (const [queue, queueConfig] of Object.entries(config.queues)) {\n this.#queueConfigs.set(queue, queueConfig as QueueConfig)\n }\n }\n\n if (config.locations && config.locations.length > 0) {\n const registered = await Locator.registerFromGlob(config.locations)\n\n if (registered === 0) {\n this.#logger.warn(\n `No jobs found for locations: ${config.locations.join(', ')}. ` +\n 'Verify your glob patterns match your job files.'\n )\n }\n }\n\n this.#initialized = true\n\n return this\n }\n\n /**\n * Get an adapter instance by name.\n *\n * Adapter instances are cached and reused. If no name is provided,\n * the default adapter is returned.\n *\n * @param adapter - Adapter name (optional, defaults to the default adapter)\n * @returns The adapter instance\n * @throws {E_QUEUE_NOT_INITIALIZED} If `init()` hasn't been called\n * @throws {E_CONFIGURATION_ERROR} If the adapter is not registered\n * @throws {E_ADAPTER_INIT_ERROR} If the adapter factory throws\n *\n * @example\n * ```typescript\n * // Get default adapter\n * const adapter = QueueManager.use()\n *\n * // Get specific adapter\n * const redisAdapter = QueueManager.use('redis')\n * ```\n */\n use(adapter?: string): Adapter {\n if (!this.#initialized) {\n throw new errors.E_QUEUE_NOT_INITIALIZED()\n }\n\n if (!adapter) {\n adapter = this.#defaultAdapter\n }\n\n // Return cached instance if exists\n const cached = this.#adapterInstances.get(adapter)\n if (cached) {\n return cached\n }\n\n const adapterFactory = this.#adapters[adapter]\n\n if (!adapterFactory) {\n throw new errors.E_CONFIGURATION_ERROR([`Adapter \"${adapter}\" is not registered`])\n }\n\n debug('using adapter \"%s\"', adapter)\n\n try {\n const instance = adapterFactory()\n this.#adapterInstances.set(adapter, instance)\n return instance\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new errors.E_ADAPTER_INIT_ERROR([adapter, message])\n }\n }\n\n /**\n * Get the merged retry configuration for a job.\n *\n * Configuration is merged with priority: job > queue > global.\n * This allows specific jobs or queues to override global defaults.\n *\n * @param queue - The queue name\n * @param jobRetryConfig - Optional job-level retry config\n * @returns The merged retry configuration\n *\n * @example\n * ```typescript\n * // Global: maxRetries=3, Queue: maxRetries=5, Job: maxRetries=1\n * // Result: maxRetries=1 (job wins)\n * const config = QueueManager.getMergedRetryConfig('emails', { maxRetries: 1 })\n * ```\n */\n getMergedRetryConfig(queue: string, jobRetryConfig?: RetryConfig): RetryConfig {\n const queueConfig = this.#queueConfigs.get(queue)\n const queueRetryConfig = queueConfig?.retry || {}\n\n let maxRetries =\n jobRetryConfig?.maxRetries ||\n queueRetryConfig.maxRetries ||\n this.#globalRetryConfig?.maxRetries ||\n 0\n\n let backoff =\n jobRetryConfig?.backoff || queueRetryConfig.backoff || this.#globalRetryConfig?.backoff\n\n return { maxRetries, backoff }\n }\n\n /**\n * Get the configured job factory for custom instantiation.\n *\n * @returns The job factory function, or undefined if not configured\n */\n getJobFactory(): JobFactory | undefined {\n return this.#jobFactory\n }\n\n #validateConfig(config: QueueManagerConfig): void {\n if (!config.adapters || Object.keys(config.adapters).length === 0) {\n throw new errors.E_CONFIGURATION_ERROR(['At least one adapter must be configured'])\n }\n\n if (!config.default) {\n throw new errors.E_CONFIGURATION_ERROR(['Default adapter must be specified'])\n }\n\n if (!config.adapters[config.default]) {\n throw new errors.E_CONFIGURATION_ERROR([\n `Default adapter \"${config.default}\" not found in adapters configuration`,\n ])\n }\n\n for (const [name, factory] of Object.entries(config.adapters)) {\n if (typeof factory !== 'function') {\n throw new errors.E_CONFIGURATION_ERROR([`Adapter \"${name}\" must be a factory function`])\n }\n }\n }\n\n /**\n * Clean up all adapter instances and reset state.\n *\n * Call this when shutting down the application or when\n * you need to reinitialize with a new configuration.\n *\n * @example\n * ```typescript\n * // On application shutdown\n * await QueueManager.destroy()\n * ```\n */\n async destroy() {\n for (const [name, adapter] of this.#adapterInstances) {\n debug('destroying adapter \"%s\"', name)\n await adapter.destroy()\n }\n this.#adapterInstances.clear()\n this.#initialized = false\n }\n}\n\n/** Global queue manager singleton */\nexport const QueueManager = new QueueManagerSingleton()\n"],"mappings":"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","names":[]}
|
|
@@ -10,9 +10,11 @@ __export(exceptions_exports, {
|
|
|
10
10
|
E_ADAPTER_INIT_ERROR: () => E_ADAPTER_INIT_ERROR,
|
|
11
11
|
E_CONFIGURATION_ERROR: () => E_CONFIGURATION_ERROR,
|
|
12
12
|
E_INVALID_BASE_DELAY: () => E_INVALID_BASE_DELAY,
|
|
13
|
+
E_INVALID_CRON_EXPRESSION: () => E_INVALID_CRON_EXPRESSION,
|
|
13
14
|
E_INVALID_DURATION_EXPRESSION: () => E_INVALID_DURATION_EXPRESSION,
|
|
14
15
|
E_INVALID_MAX_DELAY: () => E_INVALID_MAX_DELAY,
|
|
15
16
|
E_INVALID_MULTIPLIER: () => E_INVALID_MULTIPLIER,
|
|
17
|
+
E_INVALID_SCHEDULE_CONFIG: () => E_INVALID_SCHEDULE_CONFIG,
|
|
16
18
|
E_JOB_MAX_ATTEMPTS_REACHED: () => E_JOB_MAX_ATTEMPTS_REACHED,
|
|
17
19
|
E_JOB_NOT_FOUND: () => E_JOB_NOT_FOUND,
|
|
18
20
|
E_JOB_TIMEOUT: () => E_JOB_TIMEOUT,
|
|
@@ -72,6 +74,16 @@ var E_NO_JOBS_FOUND = createError(
|
|
|
72
74
|
"E_NO_JOBS_FOUND",
|
|
73
75
|
500
|
|
74
76
|
);
|
|
77
|
+
var E_INVALID_CRON_EXPRESSION = createError(
|
|
78
|
+
'Invalid cron expression "%s": %s',
|
|
79
|
+
"E_INVALID_CRON_EXPRESSION",
|
|
80
|
+
500
|
|
81
|
+
);
|
|
82
|
+
var E_INVALID_SCHEDULE_CONFIG = createError(
|
|
83
|
+
"Invalid schedule configuration: %s",
|
|
84
|
+
"E_INVALID_SCHEDULE_CONFIG",
|
|
85
|
+
500
|
|
86
|
+
);
|
|
75
87
|
|
|
76
88
|
// src/constants.ts
|
|
77
89
|
var DEFAULT_PRIORITY = 5;
|
|
@@ -92,6 +104,8 @@ export {
|
|
|
92
104
|
E_JOB_TIMEOUT,
|
|
93
105
|
E_QUEUE_NOT_INITIALIZED,
|
|
94
106
|
E_ADAPTER_INIT_ERROR,
|
|
107
|
+
E_INVALID_CRON_EXPRESSION,
|
|
108
|
+
E_INVALID_SCHEDULE_CONFIG,
|
|
95
109
|
exceptions_exports,
|
|
96
110
|
DEFAULT_PRIORITY,
|
|
97
111
|
PRIORITY_SCORE_MULTIPLIER,
|
|
@@ -100,4 +114,4 @@ export {
|
|
|
100
114
|
DEFAULT_STALLED_THRESHOLD,
|
|
101
115
|
DEFAULT_ERROR_RETRY_DELAY
|
|
102
116
|
};
|
|
103
|
-
//# sourceMappingURL=chunk-
|
|
117
|
+
//# sourceMappingURL=chunk-SMOKFZ46.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/exceptions.ts","../src/constants.ts"],"sourcesContent":["import { createError } from '@poppinss/utils'\n\nexport const E_INVALID_DURATION_EXPRESSION = createError(\n 'Invalid duration expression: \"%s\"',\n 'E_INVALID_DURATION_EXPRESSION',\n 500\n)\n\nexport const E_INVALID_BASE_DELAY = createError<[reason: string]>(\n 'Invalid base delay. Reason: %s',\n 'E_INVALID_BASE_DELAY',\n 500\n)\n\nexport const E_INVALID_MAX_DELAY = createError<[reason: string]>(\n 'Invalid max delay. Reason: %s',\n 'E_INVALID_MAX_DELAY',\n 500\n)\n\nexport const E_INVALID_MULTIPLIER = createError<[reason: string]>(\n 'Invalid multiplier. Reason: %s',\n 'E_INVALID_MULTIPLIER',\n 500\n)\n\nexport const E_CONFIGURATION_ERROR = createError<[reason: string]>(\n 'Configuration error. Reason: %s',\n 'E_CONFIGURATION_ERROR',\n 500\n)\n\nexport const E_JOB_NOT_FOUND = createError<[jobName: string]>(\n 'Requested job \"%s\" is not registered',\n 'E_JOB_NOT_FOUND'\n)\n\nexport const E_JOB_MAX_ATTEMPTS_REACHED = createError<[jobName: string]>(\n 'The job \"%s\" has reached the maximum number of retry attempts',\n 'E_JOB_MAX_ATTEMPTS_REACHED'\n)\n\nexport const E_JOB_TIMEOUT = createError<[jobName: string, timeout: number]>(\n 'The job \"%s\" has exceeded the timeout of %dms',\n 'E_JOB_TIMEOUT'\n)\n\nexport const E_QUEUE_NOT_INITIALIZED = createError(\n 'QueueManager is not initialized. Call QueueManager.init() before using it.',\n 'E_QUEUE_NOT_INITIALIZED',\n 500\n)\n\nexport const E_ADAPTER_INIT_ERROR = createError<[adapterName: string, originalMessage: string]>(\n 'Failed to initialize adapter \"%s\". Reason: %s',\n 'E_ADAPTER_INIT_ERROR',\n 500\n)\n\nexport const E_NO_JOBS_FOUND = createError<[patterns: string]>(\n 'No jobs found for the specified locations: %s. Verify your glob patterns match your job files.',\n 'E_NO_JOBS_FOUND',\n 500\n)\n\nexport const E_INVALID_CRON_EXPRESSION = createError<[expression: string, reason: string]>(\n 'Invalid cron expression \"%s\": %s',\n 'E_INVALID_CRON_EXPRESSION',\n 500\n)\n\nexport const E_INVALID_SCHEDULE_CONFIG = createError<[reason: string]>(\n 'Invalid schedule configuration: %s',\n 'E_INVALID_SCHEDULE_CONFIG',\n 500\n)\n","/**\n * Default job priority (1-10 scale, lower = higher priority)\n */\nexport const DEFAULT_PRIORITY = 5\n\n/**\n * Multiplier used in score calculation: priority * multiplier + timestamp\n *\n * This ensures higher priority jobs are processed first,\n * while preserving FIFO order within the same priority.\n * The value (1e13) leaves room for ~300 years of millisecond timestamps.\n */\nexport const PRIORITY_SCORE_MULTIPLIER = 1e13\n\n/**\n * Default delay when the worker is idle (no jobs in queue)\n */\nexport const DEFAULT_IDLE_DELAY = '2s'\n\n/**\n * Default interval between stalled job checks\n */\nexport const DEFAULT_STALLED_INTERVAL = '30s'\n\n/**\n * Default threshold after which a job is considered stalled\n */\nexport const DEFAULT_STALLED_THRESHOLD = '30s'\n\n/**\n * Default delay before retrying after an error\n */\nexport const DEFAULT_ERROR_RETRY_DELAY = '5s'\n"],"mappings":";;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,mBAAmB;AAErB,IAAM,gCAAgC;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,uBAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,uBAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,wBAAwB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EACA;AACF;AAEO,IAAM,6BAA6B;AAAA,EACxC;AAAA,EACA;AACF;AAEO,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AACF;AAEO,IAAM,0BAA0B;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,uBAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,4BAA4B;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,4BAA4B;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AACF;;;ACxEO,IAAM,mBAAmB;AASzB,IAAM,4BAA4B;AAKlC,IAAM,qBAAqB;AAK3B,IAAM,2BAA2B;AAKjC,IAAM,4BAA4B;AAKlC,IAAM,4BAA4B;","names":[]}
|