nodejs-cloud-taskmq
Version:
Node.js TypeScript library for integrating Google Cloud Tasks with MongoDB/Redis/Memory/Custom for a BullMQ-like queue system. Compatible with NestJS but framework-agnostic.
238 lines (237 loc) • 8.08 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CloudTaskMQ = void 0;
const producer_service_1 = require("./services/producer.service");
const consumer_service_1 = require("./services/consumer.service");
const rate_limiter_service_1 = require("./services/rate-limiter.service");
const memory_storage_adapter_1 = require("./adapters/memory-storage.adapter");
const redis_storage_adapter_1 = require("./adapters/redis-storage.adapter");
const mongo_storage_adapter_1 = require("./adapters/mongo-storage.adapter");
const events_1 = require("events");
/**
* Main CloudTaskMQ class
*/
class CloudTaskMQ extends events_1.EventEmitter {
constructor(config) {
super();
this.config = config;
this.initialized = false;
this.storageAdapter = this.createStorageAdapter();
this.producerService = new producer_service_1.ProducerService(config, this.storageAdapter);
this.consumerService = new consumer_service_1.ConsumerService(config, this.storageAdapter);
this.rateLimiterService = new rate_limiter_service_1.RateLimiterService(this.storageAdapter);
// Forward events
this.producerService.on('taskAdded', (event) => this.emit('taskAdded', event));
this.consumerService.on('taskActive', (event) => this.emit('taskActive', event));
this.consumerService.on('taskCompleted', (event) => this.emit('taskCompleted', event));
this.consumerService.on('taskFailed', (event) => this.emit('taskFailed', event));
this.consumerService.on('taskProgress', (event) => this.emit('taskProgress', event));
}
/**
* Initialize CloudTaskMQ
*/
async initialize() {
if (this.initialized) {
return;
}
try {
await this.storageAdapter.initialize();
await this.producerService.initialize();
await this.consumerService.initialize();
this.initialized = true;
this.emit('initialized');
}
catch (error) {
this.emit('error', error);
throw error;
}
}
/**
* Get producer service
*/
getProducer() {
return this.producerService;
}
/**
* Get consumer service
*/
getConsumer() {
return this.consumerService;
}
/**
* Get rate limiter service
*/
getRateLimiter() {
return this.rateLimiterService;
}
/**
* Get storage adapter
*/
getStorageAdapter() {
return this.storageAdapter;
}
/**
* Register a processor
*/
registerProcessor(processor) {
if (!this.initialized) {
throw new Error('CloudTaskMQ must be initialized before registering processors');
}
this.consumerService.registerProcessor(processor);
}
/**
* Process a task (called by HTTP endpoints)
*/
async processTask(payload) {
if (!this.initialized) {
throw new Error('CloudTaskMQ must be initialized before processing tasks');
}
return await this.consumerService.processTask(payload);
}
/**
* Add a task to a queue
*/
async addTask(queueName, data, options) {
if (!this.initialized) {
throw new Error('CloudTaskMQ must be initialized before adding tasks');
}
return await this.producerService.addTask(queueName, data, options);
}
/**
* Add a chain of tasks
*/
async addChain(queueName, tasks, chainOptions) {
if (!this.initialized) {
throw new Error('CloudTaskMQ must be initialized before adding task chains');
}
return await this.producerService.addChain(queueName, tasks, chainOptions);
}
/**
* Get task by ID
*/
async getTask(taskId) {
if (!this.initialized) {
throw new Error('CloudTaskMQ must be initialized before getting tasks');
}
return await this.storageAdapter.getTask(taskId);
}
/**
* Get tasks with filtering options
*/
async getTasks(options) {
if (!this.initialized) {
throw new Error('CloudTaskMQ must be initialized before getting tasks');
}
return await this.storageAdapter.getTasks(options);
}
/**
* Get task count
*/
async getTaskCount(options) {
if (!this.initialized) {
throw new Error('CloudTaskMQ must be initialized before getting task count');
}
return await this.storageAdapter.getTaskCount(options);
}
/**
* Update task progress
*/
async updateTaskProgress(taskId, progress) {
if (!this.initialized) {
throw new Error('CloudTaskMQ must be initialized before updating task progress');
}
return await this.consumerService.updateTaskProgress(taskId, progress);
}
/**
* Clean up old tasks
*/
async cleanup(options) {
if (!this.initialized) {
throw new Error('CloudTaskMQ must be initialized before cleanup');
}
return await this.storageAdapter.cleanup(options);
}
/**
* Check rate limit
*/
async checkRateLimit(key, options) {
if (!this.initialized) {
throw new Error('CloudTaskMQ must be initialized before checking rate limits');
}
return await this.rateLimiterService.checkRateLimit(key, options);
}
/**
* Get configuration
*/
getConfig() {
return { ...this.config };
}
/**
* Check if initialized
*/
isInitialized() {
return this.initialized;
}
/**
* Close CloudTaskMQ and clean up resources
*/
async close() {
if (!this.initialized)
return;
try {
await this.producerService.close();
await this.consumerService.close();
await this.storageAdapter.close();
this.initialized = false;
this.removeAllListeners();
this.emit('closed');
}
catch (error) {
// Try to clean up what we can even if some parts fail
this.initialized = false;
this.removeAllListeners();
// Only throw if it's not a connection-related error during shutdown
if (error instanceof Error && !error.message.includes('Connection is closed')) {
this.emit('error', error);
throw error;
}
}
}
/**
* Create storage adapter based on configuration
*/
createStorageAdapter() {
const { storageAdapter, storageOptions = {} } = this.config;
switch (storageAdapter) {
case 'memory':
return new memory_storage_adapter_1.MemoryStorageAdapter();
case 'redis':
if (!storageOptions.redis) {
throw new Error('Redis storage options are required when using Redis adapter');
}
return new redis_storage_adapter_1.RedisStorageAdapter(storageOptions.redis);
case 'mongo':
if (!storageOptions.mongo) {
throw new Error('MongoDB storage options are required when using MongoDB adapter');
}
return new mongo_storage_adapter_1.MongoStorageAdapter(storageOptions.mongo);
case 'custom':
if (!storageOptions.customAdapter) {
throw new Error('Custom storage adapter instance is required when using custom adapter');
}
return storageOptions.customAdapter;
default:
throw new Error(`Unsupported storage adapter: ${storageAdapter}`);
}
}
/**
* Create CloudTaskMQ instance with async configuration
*/
static async create(configFactory) {
const config = await configFactory();
const instance = new CloudTaskMQ(config);
await instance.initialize();
return instance;
}
}
exports.CloudTaskMQ = CloudTaskMQ;