@boringnode/queue 0.1.0 → 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 +43 -28
- package/build/{chunk-US7THLSZ.js → chunk-RIXMQXYJ.js} +4 -3
- package/build/chunk-RIXMQXYJ.js.map +1 -0
- package/build/{index-2Ng_OpVK.d.ts → index-C0Xg6F4E.d.ts} +194 -33
- package/build/index.d.ts +3 -3
- package/build/index.js +96 -36
- package/build/index.js.map +1 -1
- package/build/src/contracts/adapter.d.ts +1 -1
- package/build/src/drivers/knex_adapter.d.ts +1 -1
- package/build/src/drivers/knex_adapter.js +4 -4
- package/build/src/drivers/knex_adapter.js.map +1 -1
- package/build/src/drivers/redis_adapter.d.ts +1 -1
- package/build/src/drivers/redis_adapter.js +2 -2
- package/build/src/drivers/redis_adapter.js.map +1 -1
- package/build/src/drivers/sync_adapter.d.ts +1 -1
- package/build/src/drivers/sync_adapter.js +5 -4
- 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 +1 -1
- package/build/chunk-US7THLSZ.js.map +0 -1
package/README.md
CHANGED
|
@@ -39,15 +39,13 @@ Create a job by extending the `Job` class:
|
|
|
39
39
|
|
|
40
40
|
```typescript
|
|
41
41
|
import { Job } from '@boringnode/queue'
|
|
42
|
-
import type {
|
|
42
|
+
import type { JobOptions } from '@boringnode/queue/types'
|
|
43
43
|
|
|
44
44
|
interface SendEmailPayload {
|
|
45
45
|
to: string
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export default class SendEmailJob extends Job<SendEmailPayload> {
|
|
49
|
-
static readonly jobName = 'SendEmailJob'
|
|
50
|
-
|
|
51
49
|
static options: JobOptions = {
|
|
52
50
|
queue: 'email',
|
|
53
51
|
}
|
|
@@ -58,6 +56,10 @@ export default class SendEmailJob extends Job<SendEmailPayload> {
|
|
|
58
56
|
}
|
|
59
57
|
```
|
|
60
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
|
+
|
|
61
63
|
### 2. Configure the Queue Manager
|
|
62
64
|
|
|
63
65
|
```typescript
|
|
@@ -239,9 +241,9 @@ Schedule jobs to run in the future:
|
|
|
239
241
|
```typescript
|
|
240
242
|
// Various time formats
|
|
241
243
|
await SendEmailJob.dispatch(payload).in('30s') // 30 seconds
|
|
242
|
-
await SendEmailJob.dispatch(payload).in('5m')
|
|
243
|
-
await SendEmailJob.dispatch(payload).in('2h')
|
|
244
|
-
await SendEmailJob.dispatch(payload).in('1d')
|
|
244
|
+
await SendEmailJob.dispatch(payload).in('5m') // 5 minutes
|
|
245
|
+
await SendEmailJob.dispatch(payload).in('2h') // 2 hours
|
|
246
|
+
await SendEmailJob.dispatch(payload).in('1d') // 1 day
|
|
245
247
|
```
|
|
246
248
|
|
|
247
249
|
## Priority
|
|
@@ -250,8 +252,6 @@ Jobs with lower priority numbers are processed first:
|
|
|
250
252
|
|
|
251
253
|
```typescript
|
|
252
254
|
export default class UrgentJob extends Job<Payload> {
|
|
253
|
-
static readonly jobName = 'UrgentJob'
|
|
254
|
-
|
|
255
255
|
static options: JobOptions = {
|
|
256
256
|
priority: 1, // Processed before default priority (5)
|
|
257
257
|
}
|
|
@@ -270,8 +270,6 @@ Configure automatic retries with backoff strategies:
|
|
|
270
270
|
import { exponentialBackoff, linearBackoff, fixedBackoff } from '@boringnode/queue'
|
|
271
271
|
|
|
272
272
|
export default class ReliableJob extends Job<Payload> {
|
|
273
|
-
static readonly jobName = 'ReliableJob'
|
|
274
|
-
|
|
275
273
|
static options: JobOptions = {
|
|
276
274
|
maxRetries: 5,
|
|
277
275
|
retry: {
|
|
@@ -303,8 +301,6 @@ Set a maximum execution time for jobs:
|
|
|
303
301
|
|
|
304
302
|
```typescript
|
|
305
303
|
export default class LimitedJob extends Job<Payload> {
|
|
306
|
-
static readonly jobName = 'LimitedJob'
|
|
307
|
-
|
|
308
304
|
static options: JobOptions = {
|
|
309
305
|
timeout: '30s', // Maximum execution time
|
|
310
306
|
failOnTimeout: false, // Retry on timeout (default)
|
|
@@ -326,19 +322,42 @@ const config = {
|
|
|
326
322
|
}
|
|
327
323
|
```
|
|
328
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
|
+
|
|
329
353
|
## Job Context
|
|
330
354
|
|
|
331
355
|
Every job has access to execution context via `this.context`. This provides metadata about the current job execution:
|
|
332
356
|
|
|
333
357
|
```typescript
|
|
334
358
|
import { Job } from '@boringnode/queue'
|
|
335
|
-
import type { JobContext } from '@boringnode/queue'
|
|
336
359
|
|
|
337
360
|
export default class MyJob extends Job<Payload> {
|
|
338
|
-
constructor(payload: Payload, context: JobContext) {
|
|
339
|
-
super(payload, context)
|
|
340
|
-
}
|
|
341
|
-
|
|
342
361
|
async execute(): Promise<void> {
|
|
343
362
|
console.log(`Job ID: ${this.context.jobId}`)
|
|
344
363
|
console.log(`Attempt: ${this.context.attempt}`) // 1, 2, 3...
|
|
@@ -367,7 +386,7 @@ export default class MyJob extends Job<Payload> {
|
|
|
367
386
|
|
|
368
387
|
## Dependency Injection
|
|
369
388
|
|
|
370
|
-
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.
|
|
371
390
|
|
|
372
391
|
```typescript
|
|
373
392
|
import { QueueManager } from '@boringnode/queue'
|
|
@@ -375,9 +394,9 @@ import { QueueManager } from '@boringnode/queue'
|
|
|
375
394
|
await QueueManager.init({
|
|
376
395
|
default: 'redis',
|
|
377
396
|
adapters: { redis: redis(connection) },
|
|
378
|
-
jobFactory: async (JobClass
|
|
397
|
+
jobFactory: async (JobClass) => {
|
|
379
398
|
// Use your IoC container to instantiate jobs
|
|
380
|
-
return app.container.make(JobClass
|
|
399
|
+
return app.container.make(JobClass)
|
|
381
400
|
},
|
|
382
401
|
})
|
|
383
402
|
```
|
|
@@ -386,7 +405,6 @@ Example with injected dependencies:
|
|
|
386
405
|
|
|
387
406
|
```typescript
|
|
388
407
|
import { Job } from '@boringnode/queue'
|
|
389
|
-
import type { JobContext } from '@boringnode/queue'
|
|
390
408
|
|
|
391
409
|
interface SendEmailPayload {
|
|
392
410
|
to: string
|
|
@@ -394,15 +412,11 @@ interface SendEmailPayload {
|
|
|
394
412
|
}
|
|
395
413
|
|
|
396
414
|
export default class SendEmailJob extends Job<SendEmailPayload> {
|
|
397
|
-
static readonly jobName = 'SendEmailJob'
|
|
398
|
-
|
|
399
415
|
constructor(
|
|
400
|
-
payload: SendEmailPayload,
|
|
401
|
-
context: JobContext,
|
|
402
416
|
private mailer: MailerService, // Injected by IoC container
|
|
403
417
|
private logger: Logger // Injected by IoC container
|
|
404
418
|
) {
|
|
405
|
-
super(
|
|
419
|
+
super()
|
|
406
420
|
}
|
|
407
421
|
|
|
408
422
|
async execute(): Promise<void> {
|
|
@@ -412,7 +426,7 @@ export default class SendEmailJob extends Job<SendEmailPayload> {
|
|
|
412
426
|
}
|
|
413
427
|
```
|
|
414
428
|
|
|
415
|
-
Without a `jobFactory`, jobs are instantiated with `new JobClass(
|
|
429
|
+
Without a `jobFactory`, jobs are instantiated with `new JobClass()`.
|
|
416
430
|
|
|
417
431
|
## Scheduled Jobs
|
|
418
432
|
|
|
@@ -518,10 +532,11 @@ const config = {
|
|
|
518
532
|
Jobs must:
|
|
519
533
|
|
|
520
534
|
- Extend the `Job` class
|
|
521
|
-
- Have a static `jobName` property
|
|
522
535
|
- Implement the `execute` method
|
|
523
536
|
- Be exported as default
|
|
524
537
|
|
|
538
|
+
The job name is automatically derived from the class name, or can be explicitly set via `static options = { name: 'CustomName' }`.
|
|
539
|
+
|
|
525
540
|
## Logging
|
|
526
541
|
|
|
527
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.
|
|
@@ -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":[]}
|
|
@@ -185,6 +185,18 @@ interface Logger {
|
|
|
185
185
|
child(obj: LogObject): Logger;
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Duration can be specified as milliseconds (number) or as a human-readable string.
|
|
190
|
+
*
|
|
191
|
+
* Supported string formats: '1s', '5m', '2h', '1d', etc.
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```typescript
|
|
195
|
+
* const timeout: Duration = '30s' // 30 seconds
|
|
196
|
+
* const delay: Duration = 5000 // 5000 milliseconds
|
|
197
|
+
* const interval: Duration = '5m' // 5 minutes
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
188
200
|
type Duration = number | string;
|
|
189
201
|
/**
|
|
190
202
|
* Result returned when dispatching a job.
|
|
@@ -199,22 +211,108 @@ interface DispatchResult {
|
|
|
199
211
|
/** Unique identifier for this specific job instance */
|
|
200
212
|
jobId: string;
|
|
201
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Internal representation of a job in the queue.
|
|
216
|
+
*
|
|
217
|
+
* This is used by adapters to store and retrieve job data.
|
|
218
|
+
* Not typically used directly by application code.
|
|
219
|
+
*/
|
|
202
220
|
interface JobData {
|
|
221
|
+
/**
|
|
222
|
+
* Unique identifier for this job.
|
|
223
|
+
*/
|
|
203
224
|
id: string;
|
|
225
|
+
/**
|
|
226
|
+
* Job class name.
|
|
227
|
+
*/
|
|
204
228
|
name: string;
|
|
229
|
+
/**
|
|
230
|
+
* Serialized job payload.
|
|
231
|
+
*/
|
|
205
232
|
payload: any;
|
|
233
|
+
/**
|
|
234
|
+
* Number of execution attempts so far.
|
|
235
|
+
*/
|
|
206
236
|
attempts: number;
|
|
237
|
+
/**
|
|
238
|
+
* Job priority (lower = higher priority).
|
|
239
|
+
*
|
|
240
|
+
* @default 0
|
|
241
|
+
*/
|
|
207
242
|
priority?: number;
|
|
243
|
+
/**
|
|
244
|
+
* When to retry this job next (for failed jobs).
|
|
245
|
+
*/
|
|
208
246
|
nextRetryAt?: Date;
|
|
247
|
+
/**
|
|
248
|
+
* Number of times this job was recovered from stalled state.
|
|
249
|
+
*/
|
|
209
250
|
stalledCount?: number;
|
|
210
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* Static options for a Job class.
|
|
254
|
+
*
|
|
255
|
+
* Define these as a static property on your Job class to configure
|
|
256
|
+
* default behavior for all instances.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```typescript
|
|
260
|
+
* class SendEmailJob extends Job<EmailPayload> {
|
|
261
|
+
* static options: JobOptions = {
|
|
262
|
+
* name: 'SendEmailJob',
|
|
263
|
+
* queue: 'emails',
|
|
264
|
+
* maxRetries: 3,
|
|
265
|
+
* timeout: '30s',
|
|
266
|
+
* }
|
|
267
|
+
* }
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
211
270
|
interface JobOptions {
|
|
271
|
+
/**
|
|
272
|
+
* Unique name for this job class.
|
|
273
|
+
*
|
|
274
|
+
* Used to identify the job when dispatching and processing.
|
|
275
|
+
*
|
|
276
|
+
* @default constructor.name
|
|
277
|
+
*/
|
|
278
|
+
name?: string;
|
|
279
|
+
/**
|
|
280
|
+
* Queue name for this job.
|
|
281
|
+
*
|
|
282
|
+
* @default 'default'
|
|
283
|
+
*/
|
|
212
284
|
queue?: string;
|
|
285
|
+
/**
|
|
286
|
+
* Adapter name or factory to use for this job.
|
|
287
|
+
*/
|
|
213
288
|
adapter?: string | (() => Adapter);
|
|
289
|
+
/**
|
|
290
|
+
* Maximum retry attempts before permanent failure.
|
|
291
|
+
*
|
|
292
|
+
* @default 3
|
|
293
|
+
*/
|
|
214
294
|
maxRetries?: number;
|
|
295
|
+
/**
|
|
296
|
+
* Job priority (lower = higher priority).
|
|
297
|
+
*
|
|
298
|
+
* @default 0
|
|
299
|
+
*/
|
|
215
300
|
priority?: number;
|
|
301
|
+
/**
|
|
302
|
+
* Retry configuration (backoff strategy, delays, etc.).
|
|
303
|
+
*/
|
|
216
304
|
retry?: RetryConfig;
|
|
305
|
+
/**
|
|
306
|
+
* Maximum execution time before timeout.
|
|
307
|
+
*
|
|
308
|
+
* @default undefined (no timeout)
|
|
309
|
+
*/
|
|
217
310
|
timeout?: Duration;
|
|
311
|
+
/**
|
|
312
|
+
* Whether to mark job as failed on timeout.
|
|
313
|
+
*
|
|
314
|
+
* @default true
|
|
315
|
+
*/
|
|
218
316
|
failOnTimeout?: boolean;
|
|
219
317
|
}
|
|
220
318
|
/**
|
|
@@ -249,34 +347,39 @@ interface JobContext {
|
|
|
249
347
|
/** Number of times this job has been recovered from stalled state */
|
|
250
348
|
stalledCount: number;
|
|
251
349
|
}
|
|
252
|
-
|
|
350
|
+
/**
|
|
351
|
+
* Type representing a Job class constructor.
|
|
352
|
+
*
|
|
353
|
+
* The constructor accepts any arguments for dependency injection.
|
|
354
|
+
* Payload and context are provided separately via `$hydrate()`.
|
|
355
|
+
*/
|
|
356
|
+
type JobClass<T extends Job = Job> = (new (...args: any[]) => T) & {
|
|
253
357
|
options?: JobOptions;
|
|
254
358
|
};
|
|
255
359
|
/**
|
|
256
360
|
* Factory function for custom job instantiation.
|
|
257
361
|
*
|
|
258
362
|
* Use this to integrate with IoC containers for dependency injection.
|
|
259
|
-
* The factory receives the job class
|
|
260
|
-
*
|
|
363
|
+
* The factory receives only the job class and should return an instance
|
|
364
|
+
* with all dependencies injected. The worker will call `$hydrate()` separately
|
|
365
|
+
* to provide payload, context, and signal.
|
|
261
366
|
*
|
|
262
367
|
* @param JobClass - The job class to instantiate
|
|
263
|
-
* @param payload - The payload data for the job
|
|
264
|
-
* @param context - The job execution context (jobId, attempt, queue, etc.)
|
|
265
368
|
* @returns The job instance, or a Promise resolving to the instance
|
|
266
369
|
*
|
|
267
370
|
* @example
|
|
268
371
|
* ```typescript
|
|
269
372
|
* // With AdonisJS IoC container
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
373
|
+
* await QueueManager.init({
|
|
374
|
+
* default: 'redis',
|
|
375
|
+
* adapters: { redis: redis() },
|
|
376
|
+
* jobFactory: async (JobClass) => {
|
|
377
|
+
* return app.container.make(JobClass)
|
|
275
378
|
* }
|
|
276
379
|
* })
|
|
277
380
|
* ```
|
|
278
381
|
*/
|
|
279
|
-
type JobFactory = (JobClass: JobClass
|
|
382
|
+
type JobFactory = (JobClass: JobClass) => Job | Promise<Job>;
|
|
280
383
|
interface RetryConfig {
|
|
281
384
|
maxRetries?: number;
|
|
282
385
|
backoff?: () => BackoffStrategy$1;
|
|
@@ -370,7 +473,7 @@ interface ScheduleConfig {
|
|
|
370
473
|
/** Optional ID for the schedule (UUID if not set). Used for upsert. */
|
|
371
474
|
id?: string;
|
|
372
475
|
/** Job class name */
|
|
373
|
-
|
|
476
|
+
name: string;
|
|
374
477
|
/** Job payload */
|
|
375
478
|
payload: any;
|
|
376
479
|
/** Cron expression (mutually exclusive with everyMs) */
|
|
@@ -394,7 +497,7 @@ interface ScheduleData {
|
|
|
394
497
|
/** Unique identifier */
|
|
395
498
|
id: string;
|
|
396
499
|
/** Job class name */
|
|
397
|
-
|
|
500
|
+
name: string;
|
|
398
501
|
/** Job payload */
|
|
399
502
|
payload: any;
|
|
400
503
|
/** Cron expression (null if using interval) */
|
|
@@ -446,15 +549,17 @@ interface QueueManagerConfig {
|
|
|
446
549
|
* Custom factory function for job instantiation.
|
|
447
550
|
*
|
|
448
551
|
* Use this to integrate with IoC containers for dependency injection.
|
|
449
|
-
* When provided, this factory is called instead of `new JobClass(
|
|
552
|
+
* When provided, this factory is called instead of `new JobClass()`.
|
|
553
|
+
* The worker will call `$hydrate()` on the returned instance to provide
|
|
554
|
+
* payload, context, and signal.
|
|
450
555
|
*
|
|
451
556
|
* @example
|
|
452
557
|
* ```typescript
|
|
453
558
|
* await QueueManager.init({
|
|
454
559
|
* default: 'redis',
|
|
455
560
|
* adapters: { redis: redis() },
|
|
456
|
-
* jobFactory: async (JobClass
|
|
457
|
-
* return app.container.make(JobClass
|
|
561
|
+
* jobFactory: async (JobClass) => {
|
|
562
|
+
* return app.container.make(JobClass)
|
|
458
563
|
* }
|
|
459
564
|
* })
|
|
460
565
|
* ```
|
|
@@ -797,7 +902,7 @@ declare class JobDispatcher<T> {
|
|
|
797
902
|
*/
|
|
798
903
|
declare class ScheduleBuilder implements PromiseLike<ScheduleResult> {
|
|
799
904
|
#private;
|
|
800
|
-
constructor(
|
|
905
|
+
constructor(name: string, payload: any);
|
|
801
906
|
/**
|
|
802
907
|
* Set a custom schedule ID.
|
|
803
908
|
* If not specified, defaults to the job name.
|
|
@@ -854,12 +959,14 @@ declare class ScheduleBuilder implements PromiseLike<ScheduleResult> {
|
|
|
854
959
|
* Extend this class to create your own jobs. Each job must implement
|
|
855
960
|
* the `execute()` method which contains the job's business logic.
|
|
856
961
|
*
|
|
962
|
+
* The constructor is reserved for dependency injection. Payload and context
|
|
963
|
+
* are provided separately via the `$hydrate()` method (called by the worker).
|
|
964
|
+
*
|
|
857
965
|
* @typeParam Payload - The type of data this job receives
|
|
858
966
|
*
|
|
859
967
|
* @example
|
|
860
968
|
* ```typescript
|
|
861
969
|
* import { Job } from '@boringnode/queue'
|
|
862
|
-
* import type { JobContext } from '@boringnode/queue'
|
|
863
970
|
*
|
|
864
971
|
* interface SendEmailPayload {
|
|
865
972
|
* to: string
|
|
@@ -873,13 +980,14 @@ declare class ScheduleBuilder implements PromiseLike<ScheduleResult> {
|
|
|
873
980
|
* maxRetries: 3,
|
|
874
981
|
* }
|
|
875
982
|
*
|
|
876
|
-
*
|
|
877
|
-
*
|
|
983
|
+
* // Constructor is for dependency injection only
|
|
984
|
+
* constructor(private mailer: MailerService) {
|
|
985
|
+
* super()
|
|
878
986
|
* }
|
|
879
987
|
*
|
|
880
988
|
* async execute() {
|
|
881
989
|
* console.log(`Attempt ${this.context.attempt} for job ${this.context.jobId}`)
|
|
882
|
-
* await
|
|
990
|
+
* await this.mailer.send(this.payload.to, this.payload.subject, this.payload.body)
|
|
883
991
|
* }
|
|
884
992
|
*
|
|
885
993
|
* async failed(error: Error) {
|
|
@@ -890,9 +998,38 @@ declare class ScheduleBuilder implements PromiseLike<ScheduleResult> {
|
|
|
890
998
|
*/
|
|
891
999
|
declare abstract class Job<Payload = any> {
|
|
892
1000
|
#private;
|
|
893
|
-
/**
|
|
1001
|
+
/**
|
|
1002
|
+
* Static options for this job class.
|
|
1003
|
+
*
|
|
1004
|
+
* Override this property in subclasses to configure job behavior
|
|
1005
|
+
* such as queue name, retry policy, timeout, and more.
|
|
1006
|
+
*
|
|
1007
|
+
* @example
|
|
1008
|
+
* ```typescript
|
|
1009
|
+
* class SendEmailJob extends Job<SendEmailPayload> {
|
|
1010
|
+
* static options = {
|
|
1011
|
+
* queue: 'emails',
|
|
1012
|
+
* maxRetries: 3,
|
|
1013
|
+
* timeout: '30s',
|
|
1014
|
+
* }
|
|
1015
|
+
* }
|
|
1016
|
+
* ```
|
|
1017
|
+
*/
|
|
894
1018
|
static options: JobOptions;
|
|
895
|
-
/**
|
|
1019
|
+
/**
|
|
1020
|
+
* The payload data passed to this job instance.
|
|
1021
|
+
*
|
|
1022
|
+
* Contains the data provided when the job was dispatched.
|
|
1023
|
+
* Available after the job has been hydrated by the worker.
|
|
1024
|
+
*
|
|
1025
|
+
* @example
|
|
1026
|
+
* ```typescript
|
|
1027
|
+
* async execute() {
|
|
1028
|
+
* const { to, subject, body } = this.payload
|
|
1029
|
+
* await sendEmail(to, subject, body)
|
|
1030
|
+
* }
|
|
1031
|
+
* ```
|
|
1032
|
+
*/
|
|
896
1033
|
get payload(): Payload;
|
|
897
1034
|
/**
|
|
898
1035
|
* Context information for the current job execution.
|
|
@@ -912,12 +1049,36 @@ declare abstract class Job<Payload = any> {
|
|
|
912
1049
|
*/
|
|
913
1050
|
get context(): JobContext;
|
|
914
1051
|
/**
|
|
915
|
-
*
|
|
1052
|
+
* The abort signal for timeout handling.
|
|
1053
|
+
*
|
|
1054
|
+
* Check `signal.aborted` in long-running operations to handle timeouts gracefully.
|
|
1055
|
+
*
|
|
1056
|
+
* @example
|
|
1057
|
+
* ```typescript
|
|
1058
|
+
* async execute() {
|
|
1059
|
+
* for (const item of this.payload.items) {
|
|
1060
|
+
* if (this.signal?.aborted) {
|
|
1061
|
+
* throw new Error('Job timed out')
|
|
1062
|
+
* }
|
|
1063
|
+
* await processItem(item)
|
|
1064
|
+
* }
|
|
1065
|
+
* }
|
|
1066
|
+
* ```
|
|
1067
|
+
*/
|
|
1068
|
+
get signal(): AbortSignal | undefined;
|
|
1069
|
+
/**
|
|
1070
|
+
* Hydrate the job with payload, context, and optional abort signal.
|
|
1071
|
+
*
|
|
1072
|
+
* This method is called by the worker after instantiation to provide
|
|
1073
|
+
* the job's runtime data. It should not be called directly by user code.
|
|
916
1074
|
*
|
|
917
1075
|
* @param payload - The data to be processed by this job
|
|
918
|
-
* @param context - The job execution context
|
|
1076
|
+
* @param context - The job execution context
|
|
1077
|
+
* @param signal - Optional abort signal for timeout handling
|
|
1078
|
+
*
|
|
1079
|
+
* @internal
|
|
919
1080
|
*/
|
|
920
|
-
|
|
1081
|
+
$hydrate(payload: Payload, context: JobContext, signal?: AbortSignal): void;
|
|
921
1082
|
/**
|
|
922
1083
|
* Dispatch this job to the queue.
|
|
923
1084
|
*
|
|
@@ -941,7 +1102,7 @@ declare abstract class Job<Payload = any> {
|
|
|
941
1102
|
* .run()
|
|
942
1103
|
* ```
|
|
943
1104
|
*/
|
|
944
|
-
static dispatch<T extends Job>(this: new (
|
|
1105
|
+
static dispatch<T extends Job>(this: new (...args: any[]) => T, payload: T extends Job<infer P> ? P : never): JobDispatcher<T extends Job<infer P> ? P : never>;
|
|
945
1106
|
/**
|
|
946
1107
|
* Create a schedule for this job.
|
|
947
1108
|
*
|
|
@@ -967,22 +1128,22 @@ declare abstract class Job<Payload = any> {
|
|
|
967
1128
|
* .run()
|
|
968
1129
|
* ```
|
|
969
1130
|
*/
|
|
970
|
-
static schedule<T extends Job>(this: new (
|
|
1131
|
+
static schedule<T extends Job>(this: new (...args: any[]) => T, payload: T extends Job<infer P> ? P : never): ScheduleBuilder;
|
|
971
1132
|
/**
|
|
972
1133
|
* Execute the job's business logic.
|
|
973
1134
|
*
|
|
974
1135
|
* This method is called by the worker when processing the job.
|
|
975
1136
|
* Implement your job's logic here.
|
|
976
1137
|
*
|
|
977
|
-
*
|
|
978
|
-
*
|
|
1138
|
+
* For timeout handling, use `this.signal` which is available after hydration.
|
|
1139
|
+
*
|
|
979
1140
|
* @throws Any error thrown will trigger retry logic (if configured)
|
|
980
1141
|
*
|
|
981
1142
|
* @example
|
|
982
1143
|
* ```typescript
|
|
983
|
-
* async execute(
|
|
1144
|
+
* async execute() {
|
|
984
1145
|
* for (const item of this.payload.items) {
|
|
985
|
-
* if (signal?.aborted) {
|
|
1146
|
+
* if (this.signal?.aborted) {
|
|
986
1147
|
* throw new Error('Job timed out')
|
|
987
1148
|
* }
|
|
988
1149
|
* await processItem(item)
|
|
@@ -990,7 +1151,7 @@ declare abstract class Job<Payload = any> {
|
|
|
990
1151
|
* }
|
|
991
1152
|
* ```
|
|
992
1153
|
*/
|
|
993
|
-
abstract execute(
|
|
1154
|
+
abstract execute(): Promise<void>;
|
|
994
1155
|
/**
|
|
995
1156
|
* Called when the job has permanently failed (after all retries exhausted).
|
|
996
1157
|
*
|
package/build/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Q as QueueManagerConfig, W as WorkerCycle, A as Adapter, R as RetryConfig, d as JobFactory, e as Job, f as JobClass, b as ScheduleData, g as ScheduleStatus, c as ScheduleListOptions } from './index-
|
|
2
|
-
export { h as ScheduleBuilder, i as customBackoff, j as exponentialBackoff, k as fixedBackoff, l as linearBackoff } from './index-
|
|
1
|
+
import { Q as QueueManagerConfig, W as WorkerCycle, A as Adapter, R as RetryConfig, d as JobFactory, e as Job, f as JobClass, b as ScheduleData, g as ScheduleStatus, c as ScheduleListOptions } from './index-C0Xg6F4E.js';
|
|
2
|
+
export { h as ScheduleBuilder, i as customBackoff, j as exponentialBackoff, k as fixedBackoff, l as linearBackoff } from './index-C0Xg6F4E.js';
|
|
3
3
|
import * as _poppinss_utils from '@poppinss/utils';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -368,7 +368,7 @@ declare class Schedule {
|
|
|
368
368
|
#private;
|
|
369
369
|
constructor(data: ScheduleData);
|
|
370
370
|
get id(): string;
|
|
371
|
-
get
|
|
371
|
+
get name(): string;
|
|
372
372
|
get payload(): any;
|
|
373
373
|
get cronExpression(): string | null;
|
|
374
374
|
get everyMs(): number | null;
|