@boringnode/queue 0.0.1-alpha.3 → 0.1.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 +255 -35
- package/build/chunk-NPQKBCCY.js +26 -0
- package/build/chunk-NPQKBCCY.js.map +1 -0
- package/build/chunk-SMOKFZ46.js +117 -0
- package/build/chunk-SMOKFZ46.js.map +1 -0
- package/build/chunk-US7THLSZ.js +357 -0
- package/build/chunk-US7THLSZ.js.map +1 -0
- package/build/index-2Ng_OpVK.d.ts +1013 -0
- package/build/index.d.ts +429 -4
- package/build/index.js +725 -118
- package/build/index.js.map +1 -1
- package/build/src/contracts/adapter.d.ts +1 -1
- package/build/src/drivers/knex_adapter.d.ts +9 -1
- package/build/src/drivers/knex_adapter.js +253 -42
- package/build/src/drivers/knex_adapter.js.map +1 -1
- package/build/src/drivers/redis_adapter.d.ts +8 -2
- package/build/src/drivers/redis_adapter.js +265 -27
- package/build/src/drivers/redis_adapter.js.map +1 -1
- package/build/src/drivers/sync_adapter.d.ts +13 -3
- package/build/src/drivers/sync_adapter.js +43 -8
- package/build/src/drivers/sync_adapter.js.map +1 -1
- package/build/src/types/index.d.ts +1 -0
- package/build/src/types/index.js +1 -0
- package/build/src/types/index.js.map +1 -0
- package/build/src/types/main.d.ts +1 -1
- package/package.json +16 -3
- package/build/chunk-Y6KR3UIR.js +0 -99
- package/build/chunk-Y6KR3UIR.js.map +0 -1
- package/build/job-Bd_c2lFK.d.ts +0 -149
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,7 +39,7 @@ Create a job by extending the `Job` class:
|
|
|
28
39
|
|
|
29
40
|
```typescript
|
|
30
41
|
import { Job } from '@boringnode/queue'
|
|
31
|
-
import type { JobOptions } from '@boringnode/queue/types
|
|
42
|
+
import type { JobContext, JobOptions } from '@boringnode/queue/types'
|
|
32
43
|
|
|
33
44
|
interface SendEmailPayload {
|
|
34
45
|
to: string
|
|
@@ -42,7 +53,7 @@ export default class SendEmailJob extends Job<SendEmailPayload> {
|
|
|
42
53
|
}
|
|
43
54
|
|
|
44
55
|
async execute(): Promise<void> {
|
|
45
|
-
console.log(`Sending email to: ${this.payload.to}`)
|
|
56
|
+
console.log(`[Attempt ${this.context.attempt}] Sending email to: ${this.payload.to}`)
|
|
46
57
|
}
|
|
47
58
|
}
|
|
48
59
|
```
|
|
@@ -72,7 +83,7 @@ const config = {
|
|
|
72
83
|
|
|
73
84
|
worker: {
|
|
74
85
|
concurrency: 5,
|
|
75
|
-
|
|
86
|
+
idleDelay: '2s',
|
|
76
87
|
},
|
|
77
88
|
|
|
78
89
|
locations: ['./app/jobs/**/*.ts'],
|
|
@@ -121,7 +132,7 @@ interface QueueManagerConfig {
|
|
|
121
132
|
// Worker configuration
|
|
122
133
|
worker: {
|
|
123
134
|
concurrency: number
|
|
124
|
-
|
|
135
|
+
idleDelay: Duration
|
|
125
136
|
}
|
|
126
137
|
|
|
127
138
|
// Job discovery locations
|
|
@@ -227,10 +238,10 @@ Schedule jobs to run in the future:
|
|
|
227
238
|
|
|
228
239
|
```typescript
|
|
229
240
|
// Various time formats
|
|
230
|
-
await SendEmailJob.dispatch(payload).in('30s')
|
|
231
|
-
await SendEmailJob.dispatch(payload).in('5m')
|
|
232
|
-
await SendEmailJob.dispatch(payload).in('2h')
|
|
233
|
-
await SendEmailJob.dispatch(payload).in('1d')
|
|
241
|
+
await SendEmailJob.dispatch(payload).in('30s') // 30 seconds
|
|
242
|
+
await SendEmailJob.dispatch(payload).in('5m') // 5 minutes
|
|
243
|
+
await SendEmailJob.dispatch(payload).in('2h') // 2 hours
|
|
244
|
+
await SendEmailJob.dispatch(payload).in('1d') // 1 day
|
|
234
245
|
```
|
|
235
246
|
|
|
236
247
|
## Priority
|
|
@@ -242,7 +253,7 @@ export default class UrgentJob extends Job<Payload> {
|
|
|
242
253
|
static readonly jobName = 'UrgentJob'
|
|
243
254
|
|
|
244
255
|
static options: JobOptions = {
|
|
245
|
-
priority: 1,
|
|
256
|
+
priority: 1, // Processed before default priority (5)
|
|
246
257
|
}
|
|
247
258
|
|
|
248
259
|
async execute(): Promise<void> {
|
|
@@ -264,12 +275,13 @@ export default class ReliableJob extends Job<Payload> {
|
|
|
264
275
|
static options: JobOptions = {
|
|
265
276
|
maxRetries: 5,
|
|
266
277
|
retry: {
|
|
267
|
-
backoff: () =>
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
278
|
+
backoff: () =>
|
|
279
|
+
exponentialBackoff({
|
|
280
|
+
baseDelay: '1s',
|
|
281
|
+
maxDelay: '1m',
|
|
282
|
+
multiplier: 2,
|
|
283
|
+
jitter: true,
|
|
284
|
+
}),
|
|
273
285
|
},
|
|
274
286
|
}
|
|
275
287
|
|
|
@@ -294,7 +306,7 @@ export default class LimitedJob extends Job<Payload> {
|
|
|
294
306
|
static readonly jobName = 'LimitedJob'
|
|
295
307
|
|
|
296
308
|
static options: JobOptions = {
|
|
297
|
-
timeout: '30s',
|
|
309
|
+
timeout: '30s', // Maximum execution time
|
|
298
310
|
failOnTimeout: false, // Retry on timeout (default)
|
|
299
311
|
}
|
|
300
312
|
|
|
@@ -309,48 +321,245 @@ You can also set a global timeout in the worker configuration:
|
|
|
309
321
|
```typescript
|
|
310
322
|
const config = {
|
|
311
323
|
worker: {
|
|
312
|
-
timeout: '1m',
|
|
324
|
+
timeout: '1m', // Default timeout for all jobs
|
|
313
325
|
},
|
|
314
326
|
}
|
|
315
327
|
```
|
|
316
328
|
|
|
329
|
+
## Job Context
|
|
330
|
+
|
|
331
|
+
Every job has access to execution context via `this.context`. This provides metadata about the current job execution:
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
import { Job } from '@boringnode/queue'
|
|
335
|
+
import type { JobContext } from '@boringnode/queue'
|
|
336
|
+
|
|
337
|
+
export default class MyJob extends Job<Payload> {
|
|
338
|
+
constructor(payload: Payload, context: JobContext) {
|
|
339
|
+
super(payload, context)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async execute(): Promise<void> {
|
|
343
|
+
console.log(`Job ID: ${this.context.jobId}`)
|
|
344
|
+
console.log(`Attempt: ${this.context.attempt}`) // 1, 2, 3...
|
|
345
|
+
console.log(`Queue: ${this.context.queue}`)
|
|
346
|
+
console.log(`Priority: ${this.context.priority}`)
|
|
347
|
+
console.log(`Acquired at: ${this.context.acquiredAt}`)
|
|
348
|
+
|
|
349
|
+
if (this.context.attempt > 1) {
|
|
350
|
+
console.log('This is a retry!')
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Context Properties
|
|
357
|
+
|
|
358
|
+
| Property | Type | Description |
|
|
359
|
+
|----------------|--------|-------------------------------------------------|
|
|
360
|
+
| `jobId` | string | Unique identifier for this job |
|
|
361
|
+
| `name` | string | Job class name |
|
|
362
|
+
| `attempt` | number | Current attempt number (1-based) |
|
|
363
|
+
| `queue` | string | Queue name this job is being processed from |
|
|
364
|
+
| `priority` | number | Job priority (lower = higher priority) |
|
|
365
|
+
| `acquiredAt` | Date | When this job was acquired by the worker |
|
|
366
|
+
| `stalledCount` | number | Times this job was recovered from stalled state |
|
|
367
|
+
|
|
368
|
+
## Dependency Injection
|
|
369
|
+
|
|
370
|
+
Use the `jobFactory` option to integrate with IoC containers for dependency injection. This allows your jobs to receive injected services in their constructor.
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
import { QueueManager } from '@boringnode/queue'
|
|
374
|
+
|
|
375
|
+
await QueueManager.init({
|
|
376
|
+
default: 'redis',
|
|
377
|
+
adapters: { redis: redis(connection) },
|
|
378
|
+
jobFactory: async (JobClass, payload, context) => {
|
|
379
|
+
// Use your IoC container to instantiate jobs
|
|
380
|
+
return app.container.make(JobClass, [payload, context])
|
|
381
|
+
},
|
|
382
|
+
})
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Example with injected dependencies:
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
import { Job } from '@boringnode/queue'
|
|
389
|
+
import type { JobContext } from '@boringnode/queue'
|
|
390
|
+
|
|
391
|
+
interface SendEmailPayload {
|
|
392
|
+
to: string
|
|
393
|
+
subject: string
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export default class SendEmailJob extends Job<SendEmailPayload> {
|
|
397
|
+
static readonly jobName = 'SendEmailJob'
|
|
398
|
+
|
|
399
|
+
constructor(
|
|
400
|
+
payload: SendEmailPayload,
|
|
401
|
+
context: JobContext,
|
|
402
|
+
private mailer: MailerService, // Injected by IoC container
|
|
403
|
+
private logger: Logger // Injected by IoC container
|
|
404
|
+
) {
|
|
405
|
+
super(payload, context)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
async execute(): Promise<void> {
|
|
409
|
+
this.logger.info(`[Attempt ${this.context.attempt}] Sending email to ${this.payload.to}`)
|
|
410
|
+
await this.mailer.send(this.payload)
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Without a `jobFactory`, jobs are instantiated with `new JobClass(payload, context)`.
|
|
416
|
+
|
|
417
|
+
## Scheduled Jobs
|
|
418
|
+
|
|
419
|
+
Schedule jobs to run on a recurring basis using cron expressions or fixed intervals. Schedules are persisted and survive worker restarts.
|
|
420
|
+
|
|
421
|
+
### Creating a Schedule
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
import { Schedule } from '@boringnode/queue'
|
|
425
|
+
|
|
426
|
+
// Run every 10 seconds (uses job name as schedule ID by default)
|
|
427
|
+
const { scheduleId } = await MetricsJob.schedule({ endpoint: '/api/health' }).every('10s').run()
|
|
428
|
+
|
|
429
|
+
// Run on a cron schedule with custom ID
|
|
430
|
+
await CleanupJob.schedule({ days: 30 })
|
|
431
|
+
.id('daily-cleanup') // Custom ID (optional, defaults to job name)
|
|
432
|
+
.cron('0 * * * *') // Every hour at minute 0
|
|
433
|
+
.timezone('Europe/Paris') // Optional timezone (default: UTC)
|
|
434
|
+
.run()
|
|
435
|
+
|
|
436
|
+
// Schedule with constraints
|
|
437
|
+
await ReportJob.schedule({ type: 'weekly' })
|
|
438
|
+
.id('weekly-report')
|
|
439
|
+
.cron('0 9 * * MON') // Every Monday at 9am
|
|
440
|
+
.from(new Date('2024-01-01')) // Start date
|
|
441
|
+
.to(new Date('2024-12-31')) // End date
|
|
442
|
+
.limit(52) // Maximum 52 runs
|
|
443
|
+
.run()
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Managing Schedules
|
|
447
|
+
|
|
448
|
+
```typescript
|
|
449
|
+
import { Schedule } from '@boringnode/queue'
|
|
450
|
+
|
|
451
|
+
// Find a schedule by ID
|
|
452
|
+
const schedule = await Schedule.find('health-check')
|
|
453
|
+
|
|
454
|
+
if (schedule) {
|
|
455
|
+
console.log(`Status: ${schedule.status}`) // 'active' or 'paused'
|
|
456
|
+
console.log(`Run count: ${schedule.runCount}`)
|
|
457
|
+
console.log(`Next run: ${schedule.nextRunAt}`)
|
|
458
|
+
console.log(`Last run: ${schedule.lastRunAt}`)
|
|
459
|
+
|
|
460
|
+
// Pause the schedule
|
|
461
|
+
await schedule.pause()
|
|
462
|
+
|
|
463
|
+
// Resume the schedule
|
|
464
|
+
await schedule.resume()
|
|
465
|
+
|
|
466
|
+
// Trigger an immediate run (outside of the normal schedule)
|
|
467
|
+
await schedule.trigger()
|
|
468
|
+
|
|
469
|
+
// Delete the schedule
|
|
470
|
+
await schedule.delete()
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Listing Schedules
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
import { Schedule } from '@boringnode/queue'
|
|
478
|
+
|
|
479
|
+
// List all schedules
|
|
480
|
+
const all = await Schedule.list()
|
|
481
|
+
|
|
482
|
+
// Filter by status
|
|
483
|
+
const active = await Schedule.list({ status: 'active' })
|
|
484
|
+
const paused = await Schedule.list({ status: 'paused' })
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Schedule Options
|
|
488
|
+
|
|
489
|
+
| Method | Description |
|
|
490
|
+
|----------------------|-------------------------------------------------|
|
|
491
|
+
| `.id(string)` | Unique identifier (defaults to job name) |
|
|
492
|
+
| `.every(duration)` | Run at fixed intervals ('5s', '1m', '1h', '1d') |
|
|
493
|
+
| `.cron(expression)` | Run on a cron schedule |
|
|
494
|
+
| `.timezone(tz)` | Timezone for cron expressions (default: 'UTC') |
|
|
495
|
+
| `.from(date)` | Don't run before this date |
|
|
496
|
+
| `.to(date)` | Don't run after this date |
|
|
497
|
+
| `.between(from, to)` | Shorthand for `.from().to()` |
|
|
498
|
+
| `.limit(n)` | Maximum number of runs |
|
|
499
|
+
|
|
500
|
+
### How Scheduling Works
|
|
501
|
+
|
|
502
|
+
- Schedules are **persisted** in the database (via the adapter)
|
|
503
|
+
- The **Worker** polls for due schedules and dispatches jobs automatically
|
|
504
|
+
- Each schedule run creates a **new job** with a unique ID
|
|
505
|
+
- Multiple workers can run concurrently - only one will claim each due schedule
|
|
506
|
+
- Failed jobs do **not** affect the schedule (the next run will still occur)
|
|
507
|
+
|
|
317
508
|
## Job Discovery
|
|
318
509
|
|
|
319
510
|
The queue manager automatically discovers and registers jobs from the specified locations:
|
|
320
511
|
|
|
321
512
|
```typescript
|
|
322
513
|
const config = {
|
|
323
|
-
locations: [
|
|
324
|
-
'./app/jobs/**/*.ts',
|
|
325
|
-
'./modules/**/jobs/**/*.ts',
|
|
326
|
-
],
|
|
514
|
+
locations: ['./app/jobs/**/*.ts', './modules/**/jobs/**/*.ts'],
|
|
327
515
|
}
|
|
328
516
|
```
|
|
329
517
|
|
|
330
518
|
Jobs must:
|
|
519
|
+
|
|
331
520
|
- Extend the `Job` class
|
|
332
521
|
- Have a static `jobName` property
|
|
333
522
|
- Implement the `execute` method
|
|
334
523
|
- Be exported as default
|
|
335
524
|
|
|
525
|
+
## Logging
|
|
526
|
+
|
|
527
|
+
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.
|
|
528
|
+
|
|
529
|
+
```typescript
|
|
530
|
+
import { pino } from 'pino'
|
|
531
|
+
|
|
532
|
+
const config = {
|
|
533
|
+
default: 'redis',
|
|
534
|
+
adapters: {
|
|
535
|
+
/* ... */
|
|
536
|
+
},
|
|
537
|
+
logger: pino(),
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
await QueueManager.init(config)
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
By default, a simple console logger is used that only outputs warnings and errors.
|
|
544
|
+
|
|
336
545
|
## Benchmarks
|
|
337
546
|
|
|
338
547
|
Performance comparison with BullMQ using realistic jobs (5ms simulated work per job):
|
|
339
548
|
|
|
340
|
-
| Jobs | Concurrency | @boringnode/queue | BullMQ | Diff
|
|
341
|
-
|
|
342
|
-
| 100 | 1 | 562ms | 596ms | 5.7% faster
|
|
343
|
-
| 100 | 5 | 116ms | 117ms | ~same
|
|
344
|
-
| 100 | 10 | 62ms | 62ms | ~same
|
|
345
|
-
| 500 | 1 | 2728ms | 2798ms | 2.5% faster
|
|
346
|
-
| 500 | 5 | 565ms | 565ms | ~same
|
|
347
|
-
| 500 | 10 | 287ms | 288ms | ~same
|
|
348
|
-
| 1000 | 1 | 5450ms | 5547ms | 1.7% faster
|
|
349
|
-
| 1000 | 5 | 1096ms | 1116ms | 1.8% faster
|
|
350
|
-
| 1000 | 10 | 565ms | 579ms | 2.4% faster
|
|
351
|
-
| 100K | 5 | 110.5s | 112.3s | 1.5% faster
|
|
352
|
-
| 100K | 10 | 56.2s | 57.5s | 2.1% faster
|
|
353
|
-
| 100K | 20 | 29.1s | 29.6s | 1.7% faster
|
|
549
|
+
| Jobs | Concurrency | @boringnode/queue | BullMQ | Diff |
|
|
550
|
+
|------|-------------|-------------------|--------|-------------|
|
|
551
|
+
| 100 | 1 | 562ms | 596ms | 5.7% faster |
|
|
552
|
+
| 100 | 5 | 116ms | 117ms | ~same |
|
|
553
|
+
| 100 | 10 | 62ms | 62ms | ~same |
|
|
554
|
+
| 500 | 1 | 2728ms | 2798ms | 2.5% faster |
|
|
555
|
+
| 500 | 5 | 565ms | 565ms | ~same |
|
|
556
|
+
| 500 | 10 | 287ms | 288ms | ~same |
|
|
557
|
+
| 1000 | 1 | 5450ms | 5547ms | 1.7% faster |
|
|
558
|
+
| 1000 | 5 | 1096ms | 1116ms | 1.8% faster |
|
|
559
|
+
| 1000 | 10 | 565ms | 579ms | 2.4% faster |
|
|
560
|
+
| 100K | 5 | 110.5s | 112.3s | 1.5% faster |
|
|
561
|
+
| 100K | 10 | 56.2s | 57.5s | 2.1% faster |
|
|
562
|
+
| 100K | 20 | 29.1s | 29.6s | 1.7% faster |
|
|
354
563
|
|
|
355
564
|
Run benchmarks yourself:
|
|
356
565
|
|
|
@@ -364,3 +573,14 @@ npm run benchmark
|
|
|
364
573
|
# Custom job duration
|
|
365
574
|
npm run benchmark -- --duration=10
|
|
366
575
|
```
|
|
576
|
+
|
|
577
|
+
[gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/boringnode/queue/checks.yml?branch=main&style=for-the-badge
|
|
578
|
+
[gh-workflow-url]: https://github.com/boringnode/queue/actions/workflows/checks.yml
|
|
579
|
+
[npm-image]: https://img.shields.io/npm/v/@boringnode/queue.svg?style=for-the-badge&logo=npm
|
|
580
|
+
[npm-url]: https://www.npmjs.com/package/@boringnode/queue
|
|
581
|
+
[npm-download-image]: https://img.shields.io/npm/dm/@boringnode/queue?style=for-the-badge
|
|
582
|
+
[npm-download-url]: https://www.npmjs.com/package/@boringnode/queue
|
|
583
|
+
[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
|
|
584
|
+
[typescript-url]: https://www.typescriptlang.org
|
|
585
|
+
[license-image]: https://img.shields.io/npm/l/@boringnode/queue?color=blueviolet&style=for-the-badge
|
|
586
|
+
[license-url]: LICENSE.md
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {
|
|
2
|
+
E_INVALID_DURATION_EXPRESSION,
|
|
3
|
+
PRIORITY_SCORE_MULTIPLIER
|
|
4
|
+
} from "./chunk-SMOKFZ46.js";
|
|
5
|
+
|
|
6
|
+
// src/utils.ts
|
|
7
|
+
import { parse as parseDuration } from "@lukeed/ms";
|
|
8
|
+
function parse(duration) {
|
|
9
|
+
if (typeof duration === "number") {
|
|
10
|
+
return duration;
|
|
11
|
+
}
|
|
12
|
+
const milliseconds = parseDuration(duration);
|
|
13
|
+
if (typeof milliseconds === "undefined") {
|
|
14
|
+
throw new E_INVALID_DURATION_EXPRESSION([duration]);
|
|
15
|
+
}
|
|
16
|
+
return milliseconds;
|
|
17
|
+
}
|
|
18
|
+
function calculateScore(priority, timestamp) {
|
|
19
|
+
return priority * PRIORITY_SCORE_MULTIPLIER + timestamp;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
parse,
|
|
24
|
+
calculateScore
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=chunk-NPQKBCCY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts"],"sourcesContent":["import { parse as parseDuration } from '@lukeed/ms'\nimport type { Duration } from './types/main.js'\nimport * as errors from './exceptions.js'\nimport { PRIORITY_SCORE_MULTIPLIER } from './constants.js'\n\nexport function parse(duration: Duration): number {\n if (typeof duration === 'number') {\n return duration\n }\n\n const milliseconds = parseDuration(duration)\n\n if (typeof milliseconds === 'undefined') {\n throw new errors.E_INVALID_DURATION_EXPRESSION([duration])\n }\n\n return milliseconds\n}\n\n/**\n * Calculate the score for job ordering in the queue.\n * Lower scores are processed first.\n *\n * @param priority - Job priority (1-10, lower = higher priority)\n * @param timestamp - Timestamp in milliseconds\n * @returns Score for queue ordering\n */\nexport function calculateScore(priority: number, timestamp: number): number {\n return priority * PRIORITY_SCORE_MULTIPLIER + timestamp\n}\n"],"mappings":";;;;;;AAAA,SAAS,SAAS,qBAAqB;AAKhC,SAAS,MAAM,UAA4B;AAChD,MAAI,OAAO,aAAa,UAAU;AAChC,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,cAAc,QAAQ;AAE3C,MAAI,OAAO,iBAAiB,aAAa;AACvC,UAAM,IAAW,8BAA8B,CAAC,QAAQ,CAAC;AAAA,EAC3D;AAEA,SAAO;AACT;AAUO,SAAS,eAAe,UAAkB,WAA2B;AAC1E,SAAO,WAAW,4BAA4B;AAChD;","names":[]}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/exceptions.ts
|
|
8
|
+
var exceptions_exports = {};
|
|
9
|
+
__export(exceptions_exports, {
|
|
10
|
+
E_ADAPTER_INIT_ERROR: () => E_ADAPTER_INIT_ERROR,
|
|
11
|
+
E_CONFIGURATION_ERROR: () => E_CONFIGURATION_ERROR,
|
|
12
|
+
E_INVALID_BASE_DELAY: () => E_INVALID_BASE_DELAY,
|
|
13
|
+
E_INVALID_CRON_EXPRESSION: () => E_INVALID_CRON_EXPRESSION,
|
|
14
|
+
E_INVALID_DURATION_EXPRESSION: () => E_INVALID_DURATION_EXPRESSION,
|
|
15
|
+
E_INVALID_MAX_DELAY: () => E_INVALID_MAX_DELAY,
|
|
16
|
+
E_INVALID_MULTIPLIER: () => E_INVALID_MULTIPLIER,
|
|
17
|
+
E_INVALID_SCHEDULE_CONFIG: () => E_INVALID_SCHEDULE_CONFIG,
|
|
18
|
+
E_JOB_MAX_ATTEMPTS_REACHED: () => E_JOB_MAX_ATTEMPTS_REACHED,
|
|
19
|
+
E_JOB_NOT_FOUND: () => E_JOB_NOT_FOUND,
|
|
20
|
+
E_JOB_TIMEOUT: () => E_JOB_TIMEOUT,
|
|
21
|
+
E_NO_JOBS_FOUND: () => E_NO_JOBS_FOUND,
|
|
22
|
+
E_QUEUE_NOT_INITIALIZED: () => E_QUEUE_NOT_INITIALIZED
|
|
23
|
+
});
|
|
24
|
+
import { createError } from "@poppinss/utils";
|
|
25
|
+
var E_INVALID_DURATION_EXPRESSION = createError(
|
|
26
|
+
'Invalid duration expression: "%s"',
|
|
27
|
+
"E_INVALID_DURATION_EXPRESSION",
|
|
28
|
+
500
|
|
29
|
+
);
|
|
30
|
+
var E_INVALID_BASE_DELAY = createError(
|
|
31
|
+
"Invalid base delay. Reason: %s",
|
|
32
|
+
"E_INVALID_BASE_DELAY",
|
|
33
|
+
500
|
|
34
|
+
);
|
|
35
|
+
var E_INVALID_MAX_DELAY = createError(
|
|
36
|
+
"Invalid max delay. Reason: %s",
|
|
37
|
+
"E_INVALID_MAX_DELAY",
|
|
38
|
+
500
|
|
39
|
+
);
|
|
40
|
+
var E_INVALID_MULTIPLIER = createError(
|
|
41
|
+
"Invalid multiplier. Reason: %s",
|
|
42
|
+
"E_INVALID_MULTIPLIER",
|
|
43
|
+
500
|
|
44
|
+
);
|
|
45
|
+
var E_CONFIGURATION_ERROR = createError(
|
|
46
|
+
"Configuration error. Reason: %s",
|
|
47
|
+
"E_CONFIGURATION_ERROR",
|
|
48
|
+
500
|
|
49
|
+
);
|
|
50
|
+
var E_JOB_NOT_FOUND = createError(
|
|
51
|
+
'Requested job "%s" is not registered',
|
|
52
|
+
"E_JOB_NOT_FOUND"
|
|
53
|
+
);
|
|
54
|
+
var E_JOB_MAX_ATTEMPTS_REACHED = createError(
|
|
55
|
+
'The job "%s" has reached the maximum number of retry attempts',
|
|
56
|
+
"E_JOB_MAX_ATTEMPTS_REACHED"
|
|
57
|
+
);
|
|
58
|
+
var E_JOB_TIMEOUT = createError(
|
|
59
|
+
'The job "%s" has exceeded the timeout of %dms',
|
|
60
|
+
"E_JOB_TIMEOUT"
|
|
61
|
+
);
|
|
62
|
+
var E_QUEUE_NOT_INITIALIZED = createError(
|
|
63
|
+
"QueueManager is not initialized. Call QueueManager.init() before using it.",
|
|
64
|
+
"E_QUEUE_NOT_INITIALIZED",
|
|
65
|
+
500
|
|
66
|
+
);
|
|
67
|
+
var E_ADAPTER_INIT_ERROR = createError(
|
|
68
|
+
'Failed to initialize adapter "%s". Reason: %s',
|
|
69
|
+
"E_ADAPTER_INIT_ERROR",
|
|
70
|
+
500
|
|
71
|
+
);
|
|
72
|
+
var E_NO_JOBS_FOUND = createError(
|
|
73
|
+
"No jobs found for the specified locations: %s. Verify your glob patterns match your job files.",
|
|
74
|
+
"E_NO_JOBS_FOUND",
|
|
75
|
+
500
|
|
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
|
+
);
|
|
87
|
+
|
|
88
|
+
// src/constants.ts
|
|
89
|
+
var DEFAULT_PRIORITY = 5;
|
|
90
|
+
var PRIORITY_SCORE_MULTIPLIER = 1e13;
|
|
91
|
+
var DEFAULT_IDLE_DELAY = "2s";
|
|
92
|
+
var DEFAULT_STALLED_INTERVAL = "30s";
|
|
93
|
+
var DEFAULT_STALLED_THRESHOLD = "30s";
|
|
94
|
+
var DEFAULT_ERROR_RETRY_DELAY = "5s";
|
|
95
|
+
|
|
96
|
+
export {
|
|
97
|
+
E_INVALID_DURATION_EXPRESSION,
|
|
98
|
+
E_INVALID_BASE_DELAY,
|
|
99
|
+
E_INVALID_MAX_DELAY,
|
|
100
|
+
E_INVALID_MULTIPLIER,
|
|
101
|
+
E_CONFIGURATION_ERROR,
|
|
102
|
+
E_JOB_NOT_FOUND,
|
|
103
|
+
E_JOB_MAX_ATTEMPTS_REACHED,
|
|
104
|
+
E_JOB_TIMEOUT,
|
|
105
|
+
E_QUEUE_NOT_INITIALIZED,
|
|
106
|
+
E_ADAPTER_INIT_ERROR,
|
|
107
|
+
E_INVALID_CRON_EXPRESSION,
|
|
108
|
+
E_INVALID_SCHEDULE_CONFIG,
|
|
109
|
+
exceptions_exports,
|
|
110
|
+
DEFAULT_PRIORITY,
|
|
111
|
+
PRIORITY_SCORE_MULTIPLIER,
|
|
112
|
+
DEFAULT_IDLE_DELAY,
|
|
113
|
+
DEFAULT_STALLED_INTERVAL,
|
|
114
|
+
DEFAULT_STALLED_THRESHOLD,
|
|
115
|
+
DEFAULT_ERROR_RETRY_DELAY
|
|
116
|
+
};
|
|
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":[]}
|