@forinda/kickjs-queue 0.6.0 → 1.1.1

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/dist/index.d.ts CHANGED
@@ -17,6 +17,42 @@ interface QueueAdapterOptions {
17
17
  }
18
18
  /** DI token for resolving the QueueService from the container */
19
19
  declare const QUEUE_MANAGER: unique symbol;
20
+ /**
21
+ * Abstract interface for queue providers.
22
+ * Implement this to use a different queue backend (RabbitMQ, SQS, Kafka, etc.)
23
+ * while keeping the @Job/@Process decorators working.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * class RabbitMQProvider implements QueueProvider {
28
+ * async addJob(queue, name, data) { ... }
29
+ * async createWorker(queue, processor) { ... }
30
+ * async shutdown() { ... }
31
+ * }
32
+ *
33
+ * new QueueAdapter({ provider: new RabbitMQProvider(amqpUrl) })
34
+ * ```
35
+ */
36
+ interface QueueProvider {
37
+ /** Add a job to a queue */
38
+ addJob(queue: string, name: string, data: any, opts?: any): Promise<any>;
39
+ /** Add multiple jobs to a queue */
40
+ addBulk?(queue: string, jobs: Array<{
41
+ name: string;
42
+ data: any;
43
+ opts?: any;
44
+ }>): Promise<any[]>;
45
+ /** Create a worker that processes jobs from a queue */
46
+ createWorker(queue: string, processor: (job: {
47
+ name: string;
48
+ data: any;
49
+ id?: string;
50
+ }) => Promise<void>, concurrency?: number): any;
51
+ /** Get or create a queue by name */
52
+ getQueue?(name: string): any;
53
+ /** Graceful shutdown — close all workers and queues */
54
+ shutdown(): Promise<void>;
55
+ }
20
56
 
21
57
  /**
22
58
  * BullMQ adapter for KickJS — creates queues and workers, wires @Job/@Process
@@ -45,6 +81,10 @@ declare class QueueAdapter implements AppAdapter {
45
81
  private queueService;
46
82
  constructor(options: QueueAdapterOptions);
47
83
  beforeStart(_app: any, container: Container): void;
84
+ /** Get all registered queue names (used by DevTools) */
85
+ getQueueNames(): string[];
86
+ /** Get stats for a specific queue (used by DevTools) */
87
+ getQueueStats(name: string): Promise<Record<string, any>>;
48
88
  /** Gracefully close all workers and queues */
49
89
  shutdown(): Promise<void>;
50
90
  }
@@ -135,4 +175,149 @@ declare function Job(queueName: string): ClassDecorator;
135
175
  */
136
176
  declare function Process(jobName?: string): MethodDecorator;
137
177
 
138
- export { type BulkJobEntry, Job, Process, QUEUE_MANAGER, QueueAdapter, type QueueAdapterOptions, QueueService };
178
+ /**
179
+ * BullMQ queue provider — uses Redis via BullMQ.
180
+ * Requires: `pnpm add bullmq ioredis`
181
+ */
182
+ declare class BullMQProvider implements QueueProvider {
183
+ private redis;
184
+ private defaultConcurrency;
185
+ private queues;
186
+ private workers;
187
+ constructor(redis: {
188
+ host: string;
189
+ port: number;
190
+ password?: string;
191
+ }, defaultConcurrency?: number);
192
+ addJob(queue: string, name: string, data: any, opts?: any): Promise<any>;
193
+ addBulk(queue: string, jobs: Array<{
194
+ name: string;
195
+ data: any;
196
+ opts?: any;
197
+ }>): Promise<any>;
198
+ createWorker(queue: string, processor: (job: {
199
+ name: string;
200
+ data: any;
201
+ id?: string;
202
+ }) => Promise<void>, concurrency?: number): any;
203
+ getQueue(name: string): any;
204
+ shutdown(): Promise<void>;
205
+ private getOrCreateQueue;
206
+ }
207
+
208
+ /**
209
+ * RabbitMQ queue provider — uses AMQP via amqplib.
210
+ * Requires: `pnpm add amqplib @types/amqplib`
211
+ */
212
+ declare class RabbitMQProvider implements QueueProvider {
213
+ private url;
214
+ private connection;
215
+ private channel;
216
+ private consumers;
217
+ constructor(url: string);
218
+ private ensureChannel;
219
+ addJob(queue: string, name: string, data: any, opts?: any): Promise<{
220
+ queue: string;
221
+ name: string;
222
+ }>;
223
+ addBulk(queue: string, jobs: Array<{
224
+ name: string;
225
+ data: any;
226
+ opts?: any;
227
+ }>): Promise<{
228
+ queue: string;
229
+ name: string;
230
+ }[]>;
231
+ createWorker(queue: string, processor: (job: {
232
+ name: string;
233
+ data: any;
234
+ id?: string;
235
+ }) => Promise<void>, _concurrency?: number): void;
236
+ getQueue(name: string): {
237
+ name: string;
238
+ type: string;
239
+ };
240
+ shutdown(): Promise<void>;
241
+ }
242
+
243
+ /**
244
+ * Kafka queue provider — uses KafkaJS.
245
+ * Requires: `pnpm add kafkajs`
246
+ */
247
+ declare class KafkaProvider implements QueueProvider {
248
+ private config;
249
+ private kafka;
250
+ private producer;
251
+ private consumers;
252
+ constructor(config: {
253
+ brokers: string[];
254
+ clientId?: string;
255
+ groupId?: string;
256
+ });
257
+ private ensureProducer;
258
+ addJob(queue: string, name: string, data: any, _opts?: any): Promise<{
259
+ topic: string;
260
+ name: string;
261
+ }>;
262
+ addBulk(queue: string, jobs: Array<{
263
+ name: string;
264
+ data: any;
265
+ opts?: any;
266
+ }>): Promise<{
267
+ topic: string;
268
+ name: string;
269
+ }[]>;
270
+ createWorker(queue: string, processor: (job: {
271
+ name: string;
272
+ data: any;
273
+ id?: string;
274
+ }) => Promise<void>, _concurrency?: number): void;
275
+ getQueue(name: string): {
276
+ name: string;
277
+ type: string;
278
+ };
279
+ shutdown(): Promise<void>;
280
+ }
281
+
282
+ /**
283
+ * Redis Pub/Sub provider — lightweight message passing without BullMQ.
284
+ * No job persistence, retries, or delayed jobs — just fire-and-forget pub/sub.
285
+ * Good for event broadcasting, cache invalidation, and real-time notifications.
286
+ * Requires: `pnpm add ioredis`
287
+ */
288
+ declare class RedisPubSubProvider implements QueueProvider {
289
+ private redis;
290
+ private publisher;
291
+ private subscriber;
292
+ private subscriptions;
293
+ constructor(redis: {
294
+ host: string;
295
+ port: number;
296
+ password?: string;
297
+ });
298
+ private ensurePublisher;
299
+ private ensureSubscriber;
300
+ addJob(queue: string, name: string, data: any): Promise<{
301
+ channel: string;
302
+ name: string;
303
+ }>;
304
+ addBulk(queue: string, jobs: Array<{
305
+ name: string;
306
+ data: any;
307
+ }>): Promise<{
308
+ channel: string;
309
+ name: string;
310
+ }[]>;
311
+ createWorker(queue: string, processor: (job: {
312
+ name: string;
313
+ data: any;
314
+ id?: string;
315
+ }) => Promise<void>): void;
316
+ getQueue(name: string): {
317
+ name: string;
318
+ type: string;
319
+ };
320
+ shutdown(): Promise<void>;
321
+ }
322
+
323
+ export { type BulkJobEntry, BullMQProvider, Job, KafkaProvider, Process, QUEUE_MANAGER, QueueAdapter, type QueueAdapterOptions, type QueueProvider, QueueService, RabbitMQProvider, RedisPubSubProvider };
package/dist/index.js CHANGED
@@ -1,5 +1,11 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
+ }) : x)(function(x) {
6
+ if (typeof require !== "undefined") return require.apply(this, arguments);
7
+ throw Error('Dynamic require of "' + x + '" is not supported');
8
+ });
3
9
 
4
10
  // src/queue.adapter.ts
5
11
  import { Queue, Worker } from "bullmq";
@@ -134,6 +140,32 @@ var QueueAdapter = class {
134
140
  container.registerFactory(QUEUE_MANAGER, () => this.queueService, Scope.SINGLETON);
135
141
  log2.info(`QueueAdapter ready \u2014 ${this.queueService.getQueueNames().length} queue(s), ${this.workers.length} worker(s)`);
136
142
  }
143
+ /** Get all registered queue names (used by DevTools) */
144
+ getQueueNames() {
145
+ return this.queueService.getQueueNames();
146
+ }
147
+ /** Get stats for a specific queue (used by DevTools) */
148
+ async getQueueStats(name) {
149
+ const queue = this.queueService.getQueue(name);
150
+ if (!queue) return {
151
+ error: "Queue not found"
152
+ };
153
+ try {
154
+ const counts = await queue.getJobCounts();
155
+ return {
156
+ waiting: counts.waiting ?? 0,
157
+ active: counts.active ?? 0,
158
+ completed: counts.completed ?? 0,
159
+ failed: counts.failed ?? 0,
160
+ delayed: counts.delayed ?? 0,
161
+ paused: counts.paused ?? 0
162
+ };
163
+ } catch {
164
+ return {
165
+ error: "Stats unavailable"
166
+ };
167
+ }
168
+ }
137
169
  /** Gracefully close all workers and queues */
138
170
  async shutdown() {
139
171
  for (const worker of this.workers) {
@@ -168,11 +200,342 @@ function Process(jobName) {
168
200
  };
169
201
  }
170
202
  __name(Process, "Process");
203
+
204
+ // src/providers/bullmq.provider.ts
205
+ var BullMQProvider = class {
206
+ static {
207
+ __name(this, "BullMQProvider");
208
+ }
209
+ redis;
210
+ defaultConcurrency;
211
+ queues = /* @__PURE__ */ new Map();
212
+ workers = /* @__PURE__ */ new Map();
213
+ constructor(redis, defaultConcurrency = 1) {
214
+ this.redis = redis;
215
+ this.defaultConcurrency = defaultConcurrency;
216
+ }
217
+ async addJob(queue, name, data, opts) {
218
+ const q = this.getOrCreateQueue(queue);
219
+ return q.add(name, data, opts);
220
+ }
221
+ async addBulk(queue, jobs) {
222
+ const q = this.getOrCreateQueue(queue);
223
+ return q.addBulk(jobs);
224
+ }
225
+ createWorker(queue, processor, concurrency) {
226
+ const { Worker: Worker2 } = __require("bullmq");
227
+ const worker = new Worker2(queue, async (job) => processor({
228
+ name: job.name,
229
+ data: job.data,
230
+ id: job.id
231
+ }), {
232
+ connection: this.redis,
233
+ concurrency: concurrency ?? this.defaultConcurrency
234
+ });
235
+ this.workers.set(queue, worker);
236
+ return worker;
237
+ }
238
+ getQueue(name) {
239
+ return this.getOrCreateQueue(name);
240
+ }
241
+ async shutdown() {
242
+ await Promise.all([
243
+ ...this.workers.values()
244
+ ].map((w) => w.close()));
245
+ await Promise.all([
246
+ ...this.queues.values()
247
+ ].map((q) => q.close()));
248
+ }
249
+ getOrCreateQueue(name) {
250
+ if (!this.queues.has(name)) {
251
+ const { Queue: Queue2 } = __require("bullmq");
252
+ this.queues.set(name, new Queue2(name, {
253
+ connection: this.redis
254
+ }));
255
+ }
256
+ return this.queues.get(name);
257
+ }
258
+ };
259
+
260
+ // src/providers/rabbitmq.provider.ts
261
+ var RabbitMQProvider = class {
262
+ static {
263
+ __name(this, "RabbitMQProvider");
264
+ }
265
+ url;
266
+ connection = null;
267
+ channel = null;
268
+ consumers = [];
269
+ constructor(url) {
270
+ this.url = url;
271
+ }
272
+ async ensureChannel() {
273
+ if (!this.channel) {
274
+ const amqplib = __require("amqplib");
275
+ this.connection = await amqplib.connect(this.url);
276
+ this.channel = await this.connection.createChannel();
277
+ }
278
+ return this.channel;
279
+ }
280
+ async addJob(queue, name, data, opts) {
281
+ const ch = await this.ensureChannel();
282
+ await ch.assertQueue(queue, {
283
+ durable: true
284
+ });
285
+ const message = JSON.stringify({
286
+ name,
287
+ data,
288
+ timestamp: Date.now()
289
+ });
290
+ ch.sendToQueue(queue, Buffer.from(message), {
291
+ persistent: true,
292
+ ...opts
293
+ });
294
+ return {
295
+ queue,
296
+ name
297
+ };
298
+ }
299
+ async addBulk(queue, jobs) {
300
+ const results = [];
301
+ for (const job of jobs) {
302
+ results.push(await this.addJob(queue, job.name, job.data, job.opts));
303
+ }
304
+ return results;
305
+ }
306
+ createWorker(queue, processor, _concurrency) {
307
+ this.ensureChannel().then(async (ch) => {
308
+ await ch.assertQueue(queue, {
309
+ durable: true
310
+ });
311
+ if (_concurrency) ch.prefetch(_concurrency);
312
+ const { consumerTag } = await ch.consume(queue, async (msg) => {
313
+ if (!msg) return;
314
+ try {
315
+ const job = JSON.parse(msg.content.toString());
316
+ await processor({
317
+ name: job.name,
318
+ data: job.data,
319
+ id: msg.properties.messageId
320
+ });
321
+ ch.ack(msg);
322
+ } catch {
323
+ ch.nack(msg, false, true);
324
+ }
325
+ });
326
+ this.consumers.push(consumerTag);
327
+ });
328
+ }
329
+ getQueue(name) {
330
+ return {
331
+ name,
332
+ type: "rabbitmq"
333
+ };
334
+ }
335
+ async shutdown() {
336
+ if (this.channel) {
337
+ for (const tag of this.consumers) {
338
+ await this.channel.cancel(tag);
339
+ }
340
+ await this.channel.close();
341
+ }
342
+ if (this.connection) {
343
+ await this.connection.close();
344
+ }
345
+ }
346
+ };
347
+
348
+ // src/providers/kafka.provider.ts
349
+ var KafkaProvider = class {
350
+ static {
351
+ __name(this, "KafkaProvider");
352
+ }
353
+ config;
354
+ kafka = null;
355
+ producer = null;
356
+ consumers = [];
357
+ constructor(config) {
358
+ this.config = config;
359
+ }
360
+ async ensureProducer() {
361
+ if (!this.producer) {
362
+ const { Kafka } = __require("kafkajs");
363
+ this.kafka = new Kafka({
364
+ clientId: this.config.clientId ?? "kickjs-app",
365
+ brokers: this.config.brokers
366
+ });
367
+ this.producer = this.kafka.producer();
368
+ await this.producer.connect();
369
+ }
370
+ return this.producer;
371
+ }
372
+ async addJob(queue, name, data, _opts) {
373
+ const producer = await this.ensureProducer();
374
+ await producer.send({
375
+ topic: queue,
376
+ messages: [
377
+ {
378
+ key: name,
379
+ value: JSON.stringify({
380
+ name,
381
+ data,
382
+ timestamp: Date.now()
383
+ })
384
+ }
385
+ ]
386
+ });
387
+ return {
388
+ topic: queue,
389
+ name
390
+ };
391
+ }
392
+ async addBulk(queue, jobs) {
393
+ const producer = await this.ensureProducer();
394
+ await producer.send({
395
+ topic: queue,
396
+ messages: jobs.map((j) => ({
397
+ key: j.name,
398
+ value: JSON.stringify({
399
+ name: j.name,
400
+ data: j.data,
401
+ timestamp: Date.now()
402
+ })
403
+ }))
404
+ });
405
+ return jobs.map((j) => ({
406
+ topic: queue,
407
+ name: j.name
408
+ }));
409
+ }
410
+ createWorker(queue, processor, _concurrency) {
411
+ if (!this.kafka) {
412
+ const { Kafka } = __require("kafkajs");
413
+ this.kafka = new Kafka({
414
+ clientId: this.config.clientId ?? "kickjs-app",
415
+ brokers: this.config.brokers
416
+ });
417
+ }
418
+ const consumer = this.kafka.consumer({
419
+ groupId: this.config.groupId ?? `kickjs-${queue}`
420
+ });
421
+ this.consumers.push(consumer);
422
+ (async () => {
423
+ await consumer.connect();
424
+ await consumer.subscribe({
425
+ topic: queue,
426
+ fromBeginning: false
427
+ });
428
+ await consumer.run({
429
+ eachMessage: /* @__PURE__ */ __name(async ({ message }) => {
430
+ const parsed = JSON.parse(message.value.toString());
431
+ await processor({
432
+ name: parsed.name,
433
+ data: parsed.data,
434
+ id: message.offset
435
+ });
436
+ }, "eachMessage")
437
+ });
438
+ })();
439
+ }
440
+ getQueue(name) {
441
+ return {
442
+ name,
443
+ type: "kafka"
444
+ };
445
+ }
446
+ async shutdown() {
447
+ for (const consumer of this.consumers) {
448
+ await consumer.disconnect();
449
+ }
450
+ if (this.producer) {
451
+ await this.producer.disconnect();
452
+ }
453
+ }
454
+ };
455
+
456
+ // src/providers/redis-pubsub.provider.ts
457
+ var RedisPubSubProvider = class {
458
+ static {
459
+ __name(this, "RedisPubSubProvider");
460
+ }
461
+ redis;
462
+ publisher = null;
463
+ subscriber = null;
464
+ subscriptions = /* @__PURE__ */ new Map();
465
+ constructor(redis) {
466
+ this.redis = redis;
467
+ }
468
+ ensurePublisher() {
469
+ if (!this.publisher) {
470
+ const Redis = __require("ioredis");
471
+ this.publisher = new Redis(this.redis);
472
+ }
473
+ return this.publisher;
474
+ }
475
+ ensureSubscriber() {
476
+ if (!this.subscriber) {
477
+ const Redis = __require("ioredis");
478
+ this.subscriber = new Redis(this.redis);
479
+ this.subscriber.on("message", async (channel, message) => {
480
+ const processor = this.subscriptions.get(channel);
481
+ if (processor) {
482
+ const job = JSON.parse(message);
483
+ await processor(job);
484
+ }
485
+ });
486
+ }
487
+ return this.subscriber;
488
+ }
489
+ async addJob(queue, name, data) {
490
+ const pub = this.ensurePublisher();
491
+ const message = JSON.stringify({
492
+ name,
493
+ data,
494
+ timestamp: Date.now()
495
+ });
496
+ await pub.publish(queue, message);
497
+ return {
498
+ channel: queue,
499
+ name
500
+ };
501
+ }
502
+ async addBulk(queue, jobs) {
503
+ const results = [];
504
+ for (const job of jobs) {
505
+ results.push(await this.addJob(queue, job.name, job.data));
506
+ }
507
+ return results;
508
+ }
509
+ createWorker(queue, processor) {
510
+ const sub = this.ensureSubscriber();
511
+ this.subscriptions.set(queue, processor);
512
+ sub.subscribe(queue);
513
+ }
514
+ getQueue(name) {
515
+ return {
516
+ name,
517
+ type: "redis-pubsub"
518
+ };
519
+ }
520
+ async shutdown() {
521
+ if (this.subscriber) {
522
+ await this.subscriber.unsubscribe();
523
+ this.subscriber.disconnect();
524
+ }
525
+ if (this.publisher) {
526
+ this.publisher.disconnect();
527
+ }
528
+ }
529
+ };
171
530
  export {
531
+ BullMQProvider,
172
532
  Job,
533
+ KafkaProvider,
173
534
  Process,
174
535
  QUEUE_MANAGER,
175
536
  QueueAdapter,
176
- QueueService
537
+ QueueService,
538
+ RabbitMQProvider,
539
+ RedisPubSubProvider
177
540
  };
178
541
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/queue.adapter.ts","../src/types.ts","../src/queue.service.ts","../src/decorators.ts"],"sourcesContent":["import { Queue, Worker, type Job as BullMQJob } from 'bullmq'\nimport { Logger, type AppAdapter, type Container, Scope } from '@forinda/kickjs-core'\nimport {\n QUEUE_MANAGER,\n QUEUE_METADATA,\n jobRegistry,\n type QueueAdapterOptions,\n type ProcessDefinition,\n} from './types'\nimport { QueueService } from './queue.service'\n\nconst log = Logger.for('QueueAdapter')\n\n/**\n * BullMQ adapter for KickJS — creates queues and workers, wires @Job/@Process\n * decorated classes as job processors, and registers a QueueService in DI.\n *\n * @example\n * ```ts\n * import { QueueAdapter } from '@forinda/kickjs-queue'\n *\n * bootstrap({\n * modules: [EmailModule],\n * adapters: [\n * new QueueAdapter({\n * redis: { host: 'localhost', port: 6379 },\n * queues: ['email', 'notifications'],\n * concurrency: 5,\n * }),\n * ],\n * })\n * ```\n */\nexport class QueueAdapter implements AppAdapter {\n readonly name = 'QueueAdapter'\n\n private workers: Worker[] = []\n private queueService = new QueueService()\n\n constructor(private options: QueueAdapterOptions) {}\n\n beforeStart(_app: any, container: Container): void {\n const { redis, queues: preCreateQueues = [], concurrency = 1 } = this.options\n\n const connection = { host: redis.host, port: redis.port, password: redis.password }\n\n // Pre-create any explicitly listed queues\n for (const name of preCreateQueues) {\n if (!this.queueService.getQueue(name)) {\n const queue = new Queue(name, { connection })\n this.queueService.registerQueue(name, queue)\n }\n }\n\n // Discover all @Job-decorated classes and wire workers\n for (const jobClass of jobRegistry) {\n const queueName: string | undefined = Reflect.getMetadata(QUEUE_METADATA.JOB, jobClass)\n if (queueName === undefined) continue\n\n const handlers: ProcessDefinition[] =\n Reflect.getMetadata(QUEUE_METADATA.PROCESS, jobClass) || []\n\n if (handlers.length === 0) {\n log.warn(`@Job('${queueName}') class ${jobClass.name} has no @Process methods — skipping`)\n continue\n }\n\n // Ensure the queue exists\n if (!this.queueService.getQueue(queueName)) {\n const queue = new Queue(queueName, { connection })\n this.queueService.registerQueue(queueName, queue)\n }\n\n // Resolve the processor instance from DI\n const processor = container.resolve(jobClass)\n\n // Build the worker processor function\n const worker = new Worker(\n queueName,\n async (job: BullMQJob) => {\n // Find a handler matching the job name\n const specific = handlers.find((h) => h.jobName === job.name)\n // Fall back to the catch-all handler (no jobName)\n const handler = specific || handlers.find((h) => h.jobName === undefined)\n\n if (handler) {\n await processor[handler.handlerName](job)\n } else {\n log.warn(`No handler for job \"${job.name}\" in queue \"${queueName}\"`)\n }\n },\n { connection, concurrency },\n )\n\n worker.on('failed', (job, err) => {\n log.error({ err }, `Job failed: ${queueName}/${job?.name} (id: ${job?.id})`)\n })\n\n worker.on('completed', (job) => {\n log.debug(`Job completed: ${queueName}/${job.name} (id: ${job.id})`)\n })\n\n this.workers.push(worker)\n log.info(\n `Worker started: ${queueName} (${jobClass.name}, ${handlers.length} handler(s), concurrency: ${concurrency})`,\n )\n }\n\n // Register the QueueService in DI\n container.registerFactory(QUEUE_MANAGER, () => this.queueService, Scope.SINGLETON)\n\n log.info(\n `QueueAdapter ready — ${this.queueService.getQueueNames().length} queue(s), ${this.workers.length} worker(s)`,\n )\n }\n\n /** Gracefully close all workers and queues */\n async shutdown(): Promise<void> {\n // Close workers first so they stop picking up new jobs\n for (const worker of this.workers) {\n await worker.close()\n }\n log.info(`Closed ${this.workers.length} worker(s)`)\n this.workers = []\n\n // Then close queues\n await this.queueService.closeAll()\n log.info('All queues closed')\n }\n}\n","import 'reflect-metadata'\n\n/** Options for configuring the QueueAdapter */\nexport interface QueueAdapterOptions {\n /** Redis connection configuration */\n redis: {\n host: string\n port: number\n password?: string\n }\n /** Queue names to pre-create (optional — queues are also created on-demand) */\n queues?: string[]\n /** Default worker concurrency (default: 1) */\n concurrency?: number\n}\n\n/** DI token for resolving the QueueService from the container */\nexport const QUEUE_MANAGER = Symbol('QueueManager')\n\n/** Metadata keys for queue decorators */\nexport const QUEUE_METADATA = {\n JOB: Symbol('queue:job'),\n PROCESS: Symbol('queue:process'),\n} as const\n\n/** Metadata stored by @Process decorator */\nexport interface ProcessDefinition {\n /** Method name on the controller class */\n handlerName: string\n /** Job name to handle (undefined = handle all jobs in the queue) */\n jobName?: string\n}\n\n/** Global registry of @Job-decorated classes */\nexport const jobRegistry = new Set<any>()\n","import type { Queue, JobsOptions } from 'bullmq'\nimport { Logger } from '@forinda/kickjs-core'\n\nconst log = Logger.for('QueueService')\n\n/** Data shape for bulk job insertion */\nexport interface BulkJobEntry {\n name: string\n data: any\n opts?: JobsOptions\n}\n\n/**\n * Injectable service for adding jobs to BullMQ queues.\n *\n * Resolved from DI via `@Inject(QUEUE_MANAGER)`.\n *\n * @example\n * ```ts\n * @Service()\n * class EmailService {\n * @Inject(QUEUE_MANAGER) private queue: QueueService\n *\n * async sendWelcome(userId: string) {\n * await this.queue.add('email', 'welcome', { userId })\n * }\n * }\n * ```\n */\nexport class QueueService {\n private queues = new Map<string, Queue>()\n\n /** Register a queue instance (called by the adapter) */\n registerQueue(name: string, queue: Queue): void {\n this.queues.set(name, queue)\n log.debug(`Queue registered: ${name}`)\n }\n\n /** Get a raw BullMQ Queue instance by name */\n getQueue(name: string): Queue | undefined {\n return this.queues.get(name)\n }\n\n /** Add a single job to a queue */\n async add(queueName: string, jobName: string, data: any, opts?: JobsOptions) {\n const queue = this.queues.get(queueName)\n if (!queue) {\n throw new Error(`Queue \"${queueName}\" not found. Did you register it in QueueAdapter?`)\n }\n const job = await queue.add(jobName, data, opts)\n log.debug(`Job added: ${queueName}/${jobName} (id: ${job.id})`)\n return job\n }\n\n /** Add multiple jobs to a queue in bulk */\n async addBulk(queueName: string, jobs: BulkJobEntry[]) {\n const queue = this.queues.get(queueName)\n if (!queue) {\n throw new Error(`Queue \"${queueName}\" not found. Did you register it in QueueAdapter?`)\n }\n const result = await queue.addBulk(jobs)\n log.debug(`Bulk jobs added: ${queueName} (count: ${result.length})`)\n return result\n }\n\n /** Get all registered queue names */\n getQueueNames(): string[] {\n return Array.from(this.queues.keys())\n }\n\n /** Close all queues gracefully */\n async closeAll(): Promise<void> {\n for (const [name, queue] of this.queues) {\n await queue.close()\n log.debug(`Queue closed: ${name}`)\n }\n this.queues.clear()\n }\n}\n","import 'reflect-metadata'\nimport { Service } from '@forinda/kickjs-core'\nimport { QUEUE_METADATA, jobRegistry, type ProcessDefinition } from './types'\n\n/**\n * Mark a class as a job processor for a specific BullMQ queue.\n *\n * The class is automatically registered in the DI container and will\n * be discovered by the QueueAdapter during startup.\n *\n * @param queueName - The name of the BullMQ queue this class processes\n *\n * @example\n * ```ts\n * @Job('email')\n * export class EmailJobProcessor {\n * @Process('welcome')\n * async sendWelcome(job: BullMQJob) {\n * await sendEmail(job.data.to, 'Welcome!')\n * }\n *\n * @Process()\n * async handleAll(job: BullMQJob) {\n * console.log('Fallback handler for:', job.name)\n * }\n * }\n * ```\n */\nexport function Job(queueName: string): ClassDecorator {\n return (target: any) => {\n Service()(target)\n Reflect.defineMetadata(QUEUE_METADATA.JOB, queueName, target)\n jobRegistry.add(target)\n }\n}\n\n/**\n * Mark a method as a job processor within a @Job class.\n *\n * @param jobName - Specific job name to handle. Omit to handle all jobs in the queue.\n *\n * @example\n * ```ts\n * @Job('notifications')\n * export class NotificationProcessor {\n * @Process('push')\n * async handlePush(job: BullMQJob) { ... }\n *\n * @Process('sms')\n * async handleSms(job: BullMQJob) { ... }\n *\n * @Process()\n * async handleDefault(job: BullMQJob) { ... }\n * }\n * ```\n */\nexport function Process(jobName?: string): MethodDecorator {\n return (target, propertyKey) => {\n const handlers: ProcessDefinition[] =\n Reflect.getMetadata(QUEUE_METADATA.PROCESS, target.constructor) || []\n handlers.push({\n handlerName: propertyKey as string,\n jobName,\n })\n Reflect.defineMetadata(QUEUE_METADATA.PROCESS, handlers, target.constructor)\n }\n}\n"],"mappings":"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","names":["Queue","Worker","Logger","Scope","QUEUE_MANAGER","Symbol","QUEUE_METADATA","JOB","PROCESS","jobRegistry","Set","Logger","log","Logger","for","QueueService","queues","Map","registerQueue","name","queue","set","debug","getQueue","get","add","queueName","jobName","data","opts","Error","job","id","addBulk","jobs","result","length","getQueueNames","Array","from","keys","closeAll","close","clear","log","Logger","for","QueueAdapter","name","workers","queueService","QueueService","options","beforeStart","_app","container","redis","queues","preCreateQueues","concurrency","connection","host","port","password","getQueue","queue","Queue","registerQueue","jobClass","jobRegistry","queueName","Reflect","getMetadata","QUEUE_METADATA","JOB","undefined","handlers","PROCESS","length","warn","processor","resolve","worker","Worker","job","specific","find","h","jobName","handler","handlerName","on","err","error","id","debug","push","info","registerFactory","QUEUE_MANAGER","Scope","SINGLETON","getQueueNames","shutdown","close","closeAll","Service","Job","queueName","target","Service","Reflect","defineMetadata","QUEUE_METADATA","JOB","jobRegistry","add","Process","jobName","propertyKey","handlers","getMetadata","PROCESS","push","handlerName"]}
1
+ {"version":3,"sources":["../src/queue.adapter.ts","../src/types.ts","../src/queue.service.ts","../src/decorators.ts","../src/providers/bullmq.provider.ts","../src/providers/rabbitmq.provider.ts","../src/providers/kafka.provider.ts","../src/providers/redis-pubsub.provider.ts"],"sourcesContent":["import { Queue, Worker, type Job as BullMQJob } from 'bullmq'\nimport { Logger, type AppAdapter, type Container, Scope } from '@forinda/kickjs-core'\nimport {\n QUEUE_MANAGER,\n QUEUE_METADATA,\n jobRegistry,\n type QueueAdapterOptions,\n type ProcessDefinition,\n} from './types'\nimport { QueueService } from './queue.service'\n\nconst log = Logger.for('QueueAdapter')\n\n/**\n * BullMQ adapter for KickJS — creates queues and workers, wires @Job/@Process\n * decorated classes as job processors, and registers a QueueService in DI.\n *\n * @example\n * ```ts\n * import { QueueAdapter } from '@forinda/kickjs-queue'\n *\n * bootstrap({\n * modules: [EmailModule],\n * adapters: [\n * new QueueAdapter({\n * redis: { host: 'localhost', port: 6379 },\n * queues: ['email', 'notifications'],\n * concurrency: 5,\n * }),\n * ],\n * })\n * ```\n */\nexport class QueueAdapter implements AppAdapter {\n readonly name = 'QueueAdapter'\n\n private workers: Worker[] = []\n private queueService = new QueueService()\n\n constructor(private options: QueueAdapterOptions) {}\n\n beforeStart(_app: any, container: Container): void {\n const { redis, queues: preCreateQueues = [], concurrency = 1 } = this.options\n\n const connection = { host: redis.host, port: redis.port, password: redis.password }\n\n // Pre-create any explicitly listed queues\n for (const name of preCreateQueues) {\n if (!this.queueService.getQueue(name)) {\n const queue = new Queue(name, { connection })\n this.queueService.registerQueue(name, queue)\n }\n }\n\n // Discover all @Job-decorated classes and wire workers\n for (const jobClass of jobRegistry) {\n const queueName: string | undefined = Reflect.getMetadata(QUEUE_METADATA.JOB, jobClass)\n if (queueName === undefined) continue\n\n const handlers: ProcessDefinition[] =\n Reflect.getMetadata(QUEUE_METADATA.PROCESS, jobClass) || []\n\n if (handlers.length === 0) {\n log.warn(`@Job('${queueName}') class ${jobClass.name} has no @Process methods — skipping`)\n continue\n }\n\n // Ensure the queue exists\n if (!this.queueService.getQueue(queueName)) {\n const queue = new Queue(queueName, { connection })\n this.queueService.registerQueue(queueName, queue)\n }\n\n // Resolve the processor instance from DI\n const processor = container.resolve(jobClass)\n\n // Build the worker processor function\n const worker = new Worker(\n queueName,\n async (job: BullMQJob) => {\n // Find a handler matching the job name\n const specific = handlers.find((h) => h.jobName === job.name)\n // Fall back to the catch-all handler (no jobName)\n const handler = specific || handlers.find((h) => h.jobName === undefined)\n\n if (handler) {\n await processor[handler.handlerName](job)\n } else {\n log.warn(`No handler for job \"${job.name}\" in queue \"${queueName}\"`)\n }\n },\n { connection, concurrency },\n )\n\n worker.on('failed', (job, err) => {\n log.error({ err }, `Job failed: ${queueName}/${job?.name} (id: ${job?.id})`)\n })\n\n worker.on('completed', (job) => {\n log.debug(`Job completed: ${queueName}/${job.name} (id: ${job.id})`)\n })\n\n this.workers.push(worker)\n log.info(\n `Worker started: ${queueName} (${jobClass.name}, ${handlers.length} handler(s), concurrency: ${concurrency})`,\n )\n }\n\n // Register the QueueService in DI\n container.registerFactory(QUEUE_MANAGER, () => this.queueService, Scope.SINGLETON)\n\n log.info(\n `QueueAdapter ready — ${this.queueService.getQueueNames().length} queue(s), ${this.workers.length} worker(s)`,\n )\n }\n\n /** Get all registered queue names (used by DevTools) */\n getQueueNames(): string[] {\n return this.queueService.getQueueNames()\n }\n\n /** Get stats for a specific queue (used by DevTools) */\n async getQueueStats(name: string): Promise<Record<string, any>> {\n const queue = this.queueService.getQueue(name)\n if (!queue) return { error: 'Queue not found' }\n try {\n const counts = await queue.getJobCounts()\n return {\n waiting: counts.waiting ?? 0,\n active: counts.active ?? 0,\n completed: counts.completed ?? 0,\n failed: counts.failed ?? 0,\n delayed: counts.delayed ?? 0,\n paused: counts.paused ?? 0,\n }\n } catch {\n return { error: 'Stats unavailable' }\n }\n }\n\n /** Gracefully close all workers and queues */\n async shutdown(): Promise<void> {\n // Close workers first so they stop picking up new jobs\n for (const worker of this.workers) {\n await worker.close()\n }\n log.info(`Closed ${this.workers.length} worker(s)`)\n this.workers = []\n\n // Then close queues\n await this.queueService.closeAll()\n log.info('All queues closed')\n }\n}\n","import 'reflect-metadata'\n\n/** Options for configuring the QueueAdapter */\nexport interface QueueAdapterOptions {\n /** Redis connection configuration */\n redis: {\n host: string\n port: number\n password?: string\n }\n /** Queue names to pre-create (optional — queues are also created on-demand) */\n queues?: string[]\n /** Default worker concurrency (default: 1) */\n concurrency?: number\n}\n\n/** DI token for resolving the QueueService from the container */\nexport const QUEUE_MANAGER = Symbol('QueueManager')\n\n/**\n * Abstract interface for queue providers.\n * Implement this to use a different queue backend (RabbitMQ, SQS, Kafka, etc.)\n * while keeping the @Job/@Process decorators working.\n *\n * @example\n * ```ts\n * class RabbitMQProvider implements QueueProvider {\n * async addJob(queue, name, data) { ... }\n * async createWorker(queue, processor) { ... }\n * async shutdown() { ... }\n * }\n *\n * new QueueAdapter({ provider: new RabbitMQProvider(amqpUrl) })\n * ```\n */\nexport interface QueueProvider {\n /** Add a job to a queue */\n addJob(queue: string, name: string, data: any, opts?: any): Promise<any>\n /** Add multiple jobs to a queue */\n addBulk?(queue: string, jobs: Array<{ name: string; data: any; opts?: any }>): Promise<any[]>\n /** Create a worker that processes jobs from a queue */\n createWorker(\n queue: string,\n processor: (job: { name: string; data: any; id?: string }) => Promise<void>,\n concurrency?: number,\n ): any\n /** Get or create a queue by name */\n getQueue?(name: string): any\n /** Graceful shutdown — close all workers and queues */\n shutdown(): Promise<void>\n}\n\n/** Metadata keys for queue decorators */\nexport const QUEUE_METADATA = {\n JOB: Symbol('queue:job'),\n PROCESS: Symbol('queue:process'),\n} as const\n\n/** Metadata stored by @Process decorator */\nexport interface ProcessDefinition {\n /** Method name on the controller class */\n handlerName: string\n /** Job name to handle (undefined = handle all jobs in the queue) */\n jobName?: string\n}\n\n/** Global registry of @Job-decorated classes */\nexport const jobRegistry = new Set<any>()\n","import type { Queue, JobsOptions } from 'bullmq'\nimport { Logger } from '@forinda/kickjs-core'\n\nconst log = Logger.for('QueueService')\n\n/** Data shape for bulk job insertion */\nexport interface BulkJobEntry {\n name: string\n data: any\n opts?: JobsOptions\n}\n\n/**\n * Injectable service for adding jobs to BullMQ queues.\n *\n * Resolved from DI via `@Inject(QUEUE_MANAGER)`.\n *\n * @example\n * ```ts\n * @Service()\n * class EmailService {\n * @Inject(QUEUE_MANAGER) private queue: QueueService\n *\n * async sendWelcome(userId: string) {\n * await this.queue.add('email', 'welcome', { userId })\n * }\n * }\n * ```\n */\nexport class QueueService {\n private queues = new Map<string, Queue>()\n\n /** Register a queue instance (called by the adapter) */\n registerQueue(name: string, queue: Queue): void {\n this.queues.set(name, queue)\n log.debug(`Queue registered: ${name}`)\n }\n\n /** Get a raw BullMQ Queue instance by name */\n getQueue(name: string): Queue | undefined {\n return this.queues.get(name)\n }\n\n /** Add a single job to a queue */\n async add(queueName: string, jobName: string, data: any, opts?: JobsOptions) {\n const queue = this.queues.get(queueName)\n if (!queue) {\n throw new Error(`Queue \"${queueName}\" not found. Did you register it in QueueAdapter?`)\n }\n const job = await queue.add(jobName, data, opts)\n log.debug(`Job added: ${queueName}/${jobName} (id: ${job.id})`)\n return job\n }\n\n /** Add multiple jobs to a queue in bulk */\n async addBulk(queueName: string, jobs: BulkJobEntry[]) {\n const queue = this.queues.get(queueName)\n if (!queue) {\n throw new Error(`Queue \"${queueName}\" not found. Did you register it in QueueAdapter?`)\n }\n const result = await queue.addBulk(jobs)\n log.debug(`Bulk jobs added: ${queueName} (count: ${result.length})`)\n return result\n }\n\n /** Get all registered queue names */\n getQueueNames(): string[] {\n return Array.from(this.queues.keys())\n }\n\n /** Close all queues gracefully */\n async closeAll(): Promise<void> {\n for (const [name, queue] of this.queues) {\n await queue.close()\n log.debug(`Queue closed: ${name}`)\n }\n this.queues.clear()\n }\n}\n","import 'reflect-metadata'\nimport { Service } from '@forinda/kickjs-core'\nimport { QUEUE_METADATA, jobRegistry, type ProcessDefinition } from './types'\n\n/**\n * Mark a class as a job processor for a specific BullMQ queue.\n *\n * The class is automatically registered in the DI container and will\n * be discovered by the QueueAdapter during startup.\n *\n * @param queueName - The name of the BullMQ queue this class processes\n *\n * @example\n * ```ts\n * @Job('email')\n * export class EmailJobProcessor {\n * @Process('welcome')\n * async sendWelcome(job: BullMQJob) {\n * await sendEmail(job.data.to, 'Welcome!')\n * }\n *\n * @Process()\n * async handleAll(job: BullMQJob) {\n * console.log('Fallback handler for:', job.name)\n * }\n * }\n * ```\n */\nexport function Job(queueName: string): ClassDecorator {\n return (target: any) => {\n Service()(target)\n Reflect.defineMetadata(QUEUE_METADATA.JOB, queueName, target)\n jobRegistry.add(target)\n }\n}\n\n/**\n * Mark a method as a job processor within a @Job class.\n *\n * @param jobName - Specific job name to handle. Omit to handle all jobs in the queue.\n *\n * @example\n * ```ts\n * @Job('notifications')\n * export class NotificationProcessor {\n * @Process('push')\n * async handlePush(job: BullMQJob) { ... }\n *\n * @Process('sms')\n * async handleSms(job: BullMQJob) { ... }\n *\n * @Process()\n * async handleDefault(job: BullMQJob) { ... }\n * }\n * ```\n */\nexport function Process(jobName?: string): MethodDecorator {\n return (target, propertyKey) => {\n const handlers: ProcessDefinition[] =\n Reflect.getMetadata(QUEUE_METADATA.PROCESS, target.constructor) || []\n handlers.push({\n handlerName: propertyKey as string,\n jobName,\n })\n Reflect.defineMetadata(QUEUE_METADATA.PROCESS, handlers, target.constructor)\n }\n}\n","import type { QueueProvider } from '../types'\n\n/**\n * BullMQ queue provider — uses Redis via BullMQ.\n * Requires: `pnpm add bullmq ioredis`\n */\nexport class BullMQProvider implements QueueProvider {\n private queues = new Map<string, any>()\n private workers = new Map<string, any>()\n\n constructor(\n private redis: { host: string; port: number; password?: string },\n private defaultConcurrency = 1,\n ) {}\n\n async addJob(queue: string, name: string, data: any, opts?: any) {\n const q = this.getOrCreateQueue(queue)\n return q.add(name, data, opts)\n }\n\n async addBulk(queue: string, jobs: Array<{ name: string; data: any; opts?: any }>) {\n const q = this.getOrCreateQueue(queue)\n return q.addBulk(jobs)\n }\n\n createWorker(\n queue: string,\n processor: (job: { name: string; data: any; id?: string }) => Promise<void>,\n concurrency?: number,\n ) {\n const { Worker } = require('bullmq')\n const worker = new Worker(\n queue,\n async (job: any) => processor({ name: job.name, data: job.data, id: job.id }),\n { connection: this.redis, concurrency: concurrency ?? this.defaultConcurrency },\n )\n this.workers.set(queue, worker)\n return worker\n }\n\n getQueue(name: string) {\n return this.getOrCreateQueue(name)\n }\n\n async shutdown() {\n await Promise.all([...this.workers.values()].map((w) => w.close()))\n await Promise.all([...this.queues.values()].map((q) => q.close()))\n }\n\n private getOrCreateQueue(name: string) {\n if (!this.queues.has(name)) {\n const { Queue } = require('bullmq')\n this.queues.set(name, new Queue(name, { connection: this.redis }))\n }\n return this.queues.get(name)!\n }\n}\n","import type { QueueProvider } from '../types'\n\n/**\n * RabbitMQ queue provider — uses AMQP via amqplib.\n * Requires: `pnpm add amqplib @types/amqplib`\n */\nexport class RabbitMQProvider implements QueueProvider {\n private connection: any = null\n private channel: any = null\n private consumers: string[] = []\n\n constructor(private url: string) {}\n\n private async ensureChannel() {\n if (!this.channel) {\n const amqplib = require('amqplib')\n this.connection = await amqplib.connect(this.url)\n this.channel = await this.connection.createChannel()\n }\n return this.channel\n }\n\n async addJob(queue: string, name: string, data: any, opts?: any) {\n const ch = await this.ensureChannel()\n await ch.assertQueue(queue, { durable: true })\n const message = JSON.stringify({ name, data, timestamp: Date.now() })\n ch.sendToQueue(queue, Buffer.from(message), {\n persistent: true,\n ...opts,\n })\n return { queue, name }\n }\n\n async addBulk(queue: string, jobs: Array<{ name: string; data: any; opts?: any }>) {\n const results = []\n for (const job of jobs) {\n results.push(await this.addJob(queue, job.name, job.data, job.opts))\n }\n return results\n }\n\n createWorker(\n queue: string,\n processor: (job: { name: string; data: any; id?: string }) => Promise<void>,\n _concurrency?: number,\n ) {\n this.ensureChannel().then(async (ch: any) => {\n await ch.assertQueue(queue, { durable: true })\n if (_concurrency) ch.prefetch(_concurrency)\n\n const { consumerTag } = await ch.consume(queue, async (msg: any) => {\n if (!msg) return\n try {\n const job = JSON.parse(msg.content.toString())\n await processor({ name: job.name, data: job.data, id: msg.properties.messageId })\n ch.ack(msg)\n } catch {\n ch.nack(msg, false, true)\n }\n })\n\n this.consumers.push(consumerTag)\n })\n }\n\n getQueue(name: string) {\n return { name, type: 'rabbitmq' }\n }\n\n async shutdown() {\n if (this.channel) {\n for (const tag of this.consumers) {\n await this.channel.cancel(tag)\n }\n await this.channel.close()\n }\n if (this.connection) {\n await this.connection.close()\n }\n }\n}\n","import type { QueueProvider } from '../types'\n\n/**\n * Kafka queue provider — uses KafkaJS.\n * Requires: `pnpm add kafkajs`\n */\nexport class KafkaProvider implements QueueProvider {\n private kafka: any = null\n private producer: any = null\n private consumers: any[] = []\n\n constructor(\n private config: {\n brokers: string[]\n clientId?: string\n groupId?: string\n },\n ) {}\n\n private async ensureProducer() {\n if (!this.producer) {\n const { Kafka } = require('kafkajs')\n this.kafka = new Kafka({\n clientId: this.config.clientId ?? 'kickjs-app',\n brokers: this.config.brokers,\n })\n this.producer = this.kafka.producer()\n await this.producer.connect()\n }\n return this.producer\n }\n\n async addJob(queue: string, name: string, data: any, _opts?: any) {\n const producer = await this.ensureProducer()\n await producer.send({\n topic: queue,\n messages: [\n {\n key: name,\n value: JSON.stringify({ name, data, timestamp: Date.now() }),\n },\n ],\n })\n return { topic: queue, name }\n }\n\n async addBulk(queue: string, jobs: Array<{ name: string; data: any; opts?: any }>) {\n const producer = await this.ensureProducer()\n await producer.send({\n topic: queue,\n messages: jobs.map((j) => ({\n key: j.name,\n value: JSON.stringify({ name: j.name, data: j.data, timestamp: Date.now() }),\n })),\n })\n return jobs.map((j) => ({ topic: queue, name: j.name }))\n }\n\n createWorker(\n queue: string,\n processor: (job: { name: string; data: any; id?: string }) => Promise<void>,\n _concurrency?: number,\n ) {\n if (!this.kafka) {\n const { Kafka } = require('kafkajs')\n this.kafka = new Kafka({\n clientId: this.config.clientId ?? 'kickjs-app',\n brokers: this.config.brokers,\n })\n }\n\n const consumer = this.kafka.consumer({\n groupId: this.config.groupId ?? `kickjs-${queue}`,\n })\n\n this.consumers.push(consumer)\n ;(async () => {\n await consumer.connect()\n await consumer.subscribe({ topic: queue, fromBeginning: false })\n await consumer.run({\n eachMessage: async ({ message }: any) => {\n const parsed = JSON.parse(message.value.toString())\n await processor({\n name: parsed.name,\n data: parsed.data,\n id: message.offset,\n })\n },\n })\n })()\n }\n\n getQueue(name: string) {\n return { name, type: 'kafka' }\n }\n\n async shutdown() {\n for (const consumer of this.consumers) {\n await consumer.disconnect()\n }\n if (this.producer) {\n await this.producer.disconnect()\n }\n }\n}\n","import type { QueueProvider } from '../types'\n\n/**\n * Redis Pub/Sub provider — lightweight message passing without BullMQ.\n * No job persistence, retries, or delayed jobs — just fire-and-forget pub/sub.\n * Good for event broadcasting, cache invalidation, and real-time notifications.\n * Requires: `pnpm add ioredis`\n */\nexport class RedisPubSubProvider implements QueueProvider {\n private publisher: any = null\n private subscriber: any = null\n private subscriptions = new Map<string, (job: any) => Promise<void>>()\n\n constructor(private redis: { host: string; port: number; password?: string }) {}\n\n private ensurePublisher() {\n if (!this.publisher) {\n const Redis = require('ioredis')\n this.publisher = new Redis(this.redis)\n }\n return this.publisher\n }\n\n private ensureSubscriber() {\n if (!this.subscriber) {\n const Redis = require('ioredis')\n this.subscriber = new Redis(this.redis)\n\n this.subscriber.on('message', async (channel: string, message: string) => {\n const processor = this.subscriptions.get(channel)\n if (processor) {\n const job = JSON.parse(message)\n await processor(job)\n }\n })\n }\n return this.subscriber\n }\n\n async addJob(queue: string, name: string, data: any) {\n const pub = this.ensurePublisher()\n const message = JSON.stringify({ name, data, timestamp: Date.now() })\n await pub.publish(queue, message)\n return { channel: queue, name }\n }\n\n async addBulk(queue: string, jobs: Array<{ name: string; data: any }>) {\n const results = []\n for (const job of jobs) {\n results.push(await this.addJob(queue, job.name, job.data))\n }\n return results\n }\n\n createWorker(\n queue: string,\n processor: (job: { name: string; data: any; id?: string }) => Promise<void>,\n ) {\n const sub = this.ensureSubscriber()\n this.subscriptions.set(queue, processor)\n sub.subscribe(queue)\n }\n\n getQueue(name: string) {\n return { name, type: 'redis-pubsub' }\n }\n\n async shutdown() {\n if (this.subscriber) {\n await this.subscriber.unsubscribe()\n this.subscriber.disconnect()\n }\n if (this.publisher) {\n this.publisher.disconnect()\n }\n }\n}\n"],"mappings":"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","names":["Queue","Worker","Logger","Scope","QUEUE_MANAGER","Symbol","QUEUE_METADATA","JOB","PROCESS","jobRegistry","Set","Logger","log","Logger","for","QueueService","queues","Map","registerQueue","name","queue","set","debug","getQueue","get","add","queueName","jobName","data","opts","Error","job","id","addBulk","jobs","result","length","getQueueNames","Array","from","keys","closeAll","close","clear","log","Logger","for","QueueAdapter","name","workers","queueService","QueueService","options","beforeStart","_app","container","redis","queues","preCreateQueues","concurrency","connection","host","port","password","getQueue","queue","Queue","registerQueue","jobClass","jobRegistry","queueName","Reflect","getMetadata","QUEUE_METADATA","JOB","undefined","handlers","PROCESS","length","warn","processor","resolve","worker","Worker","job","specific","find","h","jobName","handler","handlerName","on","err","error","id","debug","push","info","registerFactory","QUEUE_MANAGER","Scope","SINGLETON","getQueueNames","getQueueStats","counts","getJobCounts","waiting","active","completed","failed","delayed","paused","shutdown","close","closeAll","Service","Job","queueName","target","Service","Reflect","defineMetadata","QUEUE_METADATA","JOB","jobRegistry","add","Process","jobName","propertyKey","handlers","getMetadata","PROCESS","push","handlerName","BullMQProvider","queues","Map","workers","redis","defaultConcurrency","addJob","queue","name","data","opts","q","getOrCreateQueue","add","addBulk","jobs","createWorker","processor","concurrency","Worker","require","worker","job","id","connection","set","getQueue","shutdown","Promise","all","values","map","w","close","has","Queue","get","RabbitMQProvider","connection","channel","consumers","url","ensureChannel","amqplib","require","connect","createChannel","addJob","queue","name","data","opts","ch","assertQueue","durable","message","JSON","stringify","timestamp","Date","now","sendToQueue","Buffer","from","persistent","addBulk","jobs","results","job","push","createWorker","processor","_concurrency","then","prefetch","consumerTag","consume","msg","parse","content","toString","id","properties","messageId","ack","nack","getQueue","type","shutdown","tag","cancel","close","KafkaProvider","kafka","producer","consumers","config","ensureProducer","Kafka","require","clientId","brokers","connect","addJob","queue","name","data","_opts","send","topic","messages","key","value","JSON","stringify","timestamp","Date","now","addBulk","jobs","map","j","createWorker","processor","_concurrency","consumer","groupId","push","subscribe","fromBeginning","run","eachMessage","message","parsed","parse","toString","id","offset","getQueue","type","shutdown","disconnect","RedisPubSubProvider","publisher","subscriber","subscriptions","Map","redis","ensurePublisher","Redis","require","ensureSubscriber","on","channel","message","processor","get","job","JSON","parse","addJob","queue","name","data","pub","stringify","timestamp","Date","now","publish","addBulk","jobs","results","push","createWorker","sub","set","subscribe","getQueue","type","shutdown","unsubscribe","disconnect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forinda/kickjs-queue",
3
- "version": "0.6.0",
3
+ "version": "1.1.1",
4
4
  "description": "BullMQ queue/worker adapter with decorator-driven job processing for KickJS",
5
5
  "keywords": [
6
6
  "kickjs",
@@ -28,7 +28,7 @@
28
28
  ],
29
29
  "dependencies": {
30
30
  "reflect-metadata": "^0.2.2",
31
- "@forinda/kickjs-core": "0.7.0"
31
+ "@forinda/kickjs-core": "1.1.1"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "bullmq": ">=5.0.0",