@imqueue/job
Version:
Simple job queue
233 lines • 7.69 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.JobQueueWorker = exports.JobQueuePublisher = exports.BaseJobQueue = void 0;
/*!
* Job Queue for @imqueue framework
*
* I'm Queue Software Project
* Copyright (C) 2025 imqueue.com <support@imqueue.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* If you want to use this code in a closed source (commercial) project, you can
* purchase a proprietary commercial license. Please contact us at
* <support@imqueue.com> to get commercial licensing options.
*/
const core_1 = require("@imqueue/core");
/**
* Abstract job queue, handles base implementations of AnyJobQueue interface.
*/
class BaseJobQueue {
constructor(options) {
this.options = options;
this.logger = options.logger || console;
}
/**
* Full name of this queue
*
* @type {string}
*/
get name() {
return this.options.name;
}
/**
* Starts processing job queue
*
* @return {Promise<T>} - this queue
*/
async start() {
await this.imq.start();
return this;
}
/**
* Stops processing job queue
*
* @return {Promise<T>} - this queue
*/
async stop() {
await this.imq.stop();
return this;
}
/**
* Destroys job queue
*
* @return {Promise<void>}
*/
async destroy() {
await this.imq.destroy();
}
}
exports.BaseJobQueue = BaseJobQueue;
/**
* Creates and returns IMQOptions derived from a given JobQueueOptions
*
* @param {JobQueueOptions} options
* @param {ILogger} logger
* @return {Partial<IMQOptions>}
* @private
*/
function toIMQOptions(options, logger) {
return {
cluster: options.cluster,
cleanup: false,
safeDelivery: typeof options.safe === 'undefined'
? true : options.safe,
safeDeliveryTtl: typeof options.safeLockTtl === 'undefined'
? 10000 : options.safeLockTtl,
prefix: options.prefix || 'imq-job',
logger,
};
}
// noinspection JSUnusedGlobalSymbols
/**
* Implements simple scheduled job queue publisher. Job queue publisher is only
* responsible for pushing queue messages.
*/
class JobQueuePublisher extends BaseJobQueue {
/**
* Constructor. Instantiates new JobQueue instance.
*
* @constructor
* @param {JobQueueOptions} options
*/
constructor(options) {
super(options);
this.imq = core_1.default.create(options.name, toIMQOptions(options, this.logger), core_1.IMQMode.PUBLISHER);
}
/**
* Pushes new job to this queue
*
* @param {T} job - job data itself of user defined type
* @param {PushOptions} options - push options, like delay and ttl for job
* @return {JobQueue<T>} - this queue
*/
push(job, options) {
options = options || {};
this.imq.send(this.name, Object.assign(Object.assign({ job: job }, (options.ttl ? { expire: Date.now() + options.ttl } : {})), (options.delay ? { delay: options.delay } : {})), options.delay).catch(err => this.logger.log('JobQueue push error:', err));
return this;
}
}
exports.JobQueuePublisher = JobQueuePublisher;
// noinspection JSUnusedGlobalSymbols
/**
* Implements simple scheduled job queue worker. Job queue worker is only
* responsible for processing queue messages.
*/
class JobQueueWorker extends BaseJobQueue {
/**
* Constructor. Instantiates new JobQueue instance.
*
* @constructor
* @param {JobQueueOptions} options
*/
constructor(options) {
super(options);
this.imq = core_1.default.create(options.name, toIMQOptions(options, this.logger), core_1.IMQMode.WORKER);
}
/**
* Sets up job handler, which is called when the job is popped from this
* queue.
*
* @param {JobQueuePopHandler<T>} handler - job pop handler
* @return {JobQueueWorker<T>} - this queue
*/
onPop(handler) {
this.handler = handler;
this.imq.removeAllListeners('message');
this.imq.on('message', async (message) => {
const { job, expire, delay } = message;
let rescheduleDelay;
try {
rescheduleDelay = this.handler(job);
if (rescheduleDelay &&
typeof rescheduleDelay === 'object' &&
rescheduleDelay &&
rescheduleDelay.then) {
// it's promise
rescheduleDelay = await rescheduleDelay;
}
}
catch (err) {
rescheduleDelay = delay;
this.logger.log('Error handling job:', err);
}
if (typeof expire === 'number' && expire <= Date.now()) {
return; // remove job from queue
}
if (typeof rescheduleDelay === 'number' && rescheduleDelay >= 0) {
await this.imq.send(this.name, message, rescheduleDelay);
}
});
return this;
}
}
exports.JobQueueWorker = JobQueueWorker;
// noinspection JSUnusedGlobalSymbols
/**
* Implements simple scheduled job queue. Job scheduling is optional. It may
* process jobs immediately or after specified delay for particular job.
* It also allows to define max lifetime of the job in a queue, after which
* the job is removed from a queue.
* Supports graceful shutdown, if TERM or SIGINT is sent to the process.
*/
class JobQueue extends BaseJobQueue {
/**
* Constructor. Instantiates new JobQueue instance.
*
* @constructor
* @param {JobQueueOptions} options
*/
constructor(options) {
super(options);
this.imq = core_1.default.create(options.name, toIMQOptions(options, this.logger));
}
/**
* Starts processing job queue. Throws if handler is not set before start.
*
* @throws {TypeError}
* @return {Promise<T>} - this queue
*/
async start() {
if (!this.handler) {
throw new TypeError('Message handler is not set, can not start job queue!');
}
return await super.start();
}
/**
* Pushes new job to this queue. Throws if handler is not set.
*
* @throws {TypeError}
* @param {T} job - job data itself of user defined type
* @param {PushOptions} options - push options, like delay and ttl for job
* @return {JobQueue<T>} - this queue
*/
push(job, options) {
if (!this.handler) {
throw new TypeError('Message handler is not set, can not enqueue data!');
}
return JobQueuePublisher.prototype.push.call(this, job, options);
}
/**
* Sets up job handler, which is called when the job is popped from this
* queue.
*
* @param {JobQueuePopHandler<T>} handler - job pop handler
* @return {JobQueue<T>} - this queue
*/
onPop(handler) {
return JobQueueWorker.prototype.onPop.call(this, handler);
}
}
exports.default = JobQueue;
//# sourceMappingURL=index.js.map