@forinda/kickjs-queue 0.6.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Felix Orinda
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,138 @@
1
+ import { AppAdapter, Container } from '@forinda/kickjs-core';
2
+ import * as bullmq from 'bullmq';
3
+ import { JobsOptions, Queue } from 'bullmq';
4
+
5
+ /** Options for configuring the QueueAdapter */
6
+ interface QueueAdapterOptions {
7
+ /** Redis connection configuration */
8
+ redis: {
9
+ host: string;
10
+ port: number;
11
+ password?: string;
12
+ };
13
+ /** Queue names to pre-create (optional — queues are also created on-demand) */
14
+ queues?: string[];
15
+ /** Default worker concurrency (default: 1) */
16
+ concurrency?: number;
17
+ }
18
+ /** DI token for resolving the QueueService from the container */
19
+ declare const QUEUE_MANAGER: unique symbol;
20
+
21
+ /**
22
+ * BullMQ adapter for KickJS — creates queues and workers, wires @Job/@Process
23
+ * decorated classes as job processors, and registers a QueueService in DI.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { QueueAdapter } from '@forinda/kickjs-queue'
28
+ *
29
+ * bootstrap({
30
+ * modules: [EmailModule],
31
+ * adapters: [
32
+ * new QueueAdapter({
33
+ * redis: { host: 'localhost', port: 6379 },
34
+ * queues: ['email', 'notifications'],
35
+ * concurrency: 5,
36
+ * }),
37
+ * ],
38
+ * })
39
+ * ```
40
+ */
41
+ declare class QueueAdapter implements AppAdapter {
42
+ private options;
43
+ readonly name = "QueueAdapter";
44
+ private workers;
45
+ private queueService;
46
+ constructor(options: QueueAdapterOptions);
47
+ beforeStart(_app: any, container: Container): void;
48
+ /** Gracefully close all workers and queues */
49
+ shutdown(): Promise<void>;
50
+ }
51
+
52
+ /** Data shape for bulk job insertion */
53
+ interface BulkJobEntry {
54
+ name: string;
55
+ data: any;
56
+ opts?: JobsOptions;
57
+ }
58
+ /**
59
+ * Injectable service for adding jobs to BullMQ queues.
60
+ *
61
+ * Resolved from DI via `@Inject(QUEUE_MANAGER)`.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * @Service()
66
+ * class EmailService {
67
+ * @Inject(QUEUE_MANAGER) private queue: QueueService
68
+ *
69
+ * async sendWelcome(userId: string) {
70
+ * await this.queue.add('email', 'welcome', { userId })
71
+ * }
72
+ * }
73
+ * ```
74
+ */
75
+ declare class QueueService {
76
+ private queues;
77
+ /** Register a queue instance (called by the adapter) */
78
+ registerQueue(name: string, queue: Queue): void;
79
+ /** Get a raw BullMQ Queue instance by name */
80
+ getQueue(name: string): Queue | undefined;
81
+ /** Add a single job to a queue */
82
+ add(queueName: string, jobName: string, data: any, opts?: JobsOptions): Promise<bullmq.Job<any, any, string>>;
83
+ /** Add multiple jobs to a queue in bulk */
84
+ addBulk(queueName: string, jobs: BulkJobEntry[]): Promise<bullmq.Job<any, any, string>[]>;
85
+ /** Get all registered queue names */
86
+ getQueueNames(): string[];
87
+ /** Close all queues gracefully */
88
+ closeAll(): Promise<void>;
89
+ }
90
+
91
+ /**
92
+ * Mark a class as a job processor for a specific BullMQ queue.
93
+ *
94
+ * The class is automatically registered in the DI container and will
95
+ * be discovered by the QueueAdapter during startup.
96
+ *
97
+ * @param queueName - The name of the BullMQ queue this class processes
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * @Job('email')
102
+ * export class EmailJobProcessor {
103
+ * @Process('welcome')
104
+ * async sendWelcome(job: BullMQJob) {
105
+ * await sendEmail(job.data.to, 'Welcome!')
106
+ * }
107
+ *
108
+ * @Process()
109
+ * async handleAll(job: BullMQJob) {
110
+ * console.log('Fallback handler for:', job.name)
111
+ * }
112
+ * }
113
+ * ```
114
+ */
115
+ declare function Job(queueName: string): ClassDecorator;
116
+ /**
117
+ * Mark a method as a job processor within a @Job class.
118
+ *
119
+ * @param jobName - Specific job name to handle. Omit to handle all jobs in the queue.
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * @Job('notifications')
124
+ * export class NotificationProcessor {
125
+ * @Process('push')
126
+ * async handlePush(job: BullMQJob) { ... }
127
+ *
128
+ * @Process('sms')
129
+ * async handleSms(job: BullMQJob) { ... }
130
+ *
131
+ * @Process()
132
+ * async handleDefault(job: BullMQJob) { ... }
133
+ * }
134
+ * ```
135
+ */
136
+ declare function Process(jobName?: string): MethodDecorator;
137
+
138
+ export { type BulkJobEntry, Job, Process, QUEUE_MANAGER, QueueAdapter, type QueueAdapterOptions, QueueService };
package/dist/index.js ADDED
@@ -0,0 +1,178 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/queue.adapter.ts
5
+ import { Queue, Worker } from "bullmq";
6
+ import { Logger as Logger2, Scope } from "@forinda/kickjs-core";
7
+
8
+ // src/types.ts
9
+ import "reflect-metadata";
10
+ var QUEUE_MANAGER = /* @__PURE__ */ Symbol("QueueManager");
11
+ var QUEUE_METADATA = {
12
+ JOB: /* @__PURE__ */ Symbol("queue:job"),
13
+ PROCESS: /* @__PURE__ */ Symbol("queue:process")
14
+ };
15
+ var jobRegistry = /* @__PURE__ */ new Set();
16
+
17
+ // src/queue.service.ts
18
+ import { Logger } from "@forinda/kickjs-core";
19
+ var log = Logger.for("QueueService");
20
+ var QueueService = class {
21
+ static {
22
+ __name(this, "QueueService");
23
+ }
24
+ queues = /* @__PURE__ */ new Map();
25
+ /** Register a queue instance (called by the adapter) */
26
+ registerQueue(name, queue) {
27
+ this.queues.set(name, queue);
28
+ log.debug(`Queue registered: ${name}`);
29
+ }
30
+ /** Get a raw BullMQ Queue instance by name */
31
+ getQueue(name) {
32
+ return this.queues.get(name);
33
+ }
34
+ /** Add a single job to a queue */
35
+ async add(queueName, jobName, data, opts) {
36
+ const queue = this.queues.get(queueName);
37
+ if (!queue) {
38
+ throw new Error(`Queue "${queueName}" not found. Did you register it in QueueAdapter?`);
39
+ }
40
+ const job = await queue.add(jobName, data, opts);
41
+ log.debug(`Job added: ${queueName}/${jobName} (id: ${job.id})`);
42
+ return job;
43
+ }
44
+ /** Add multiple jobs to a queue in bulk */
45
+ async addBulk(queueName, jobs) {
46
+ const queue = this.queues.get(queueName);
47
+ if (!queue) {
48
+ throw new Error(`Queue "${queueName}" not found. Did you register it in QueueAdapter?`);
49
+ }
50
+ const result = await queue.addBulk(jobs);
51
+ log.debug(`Bulk jobs added: ${queueName} (count: ${result.length})`);
52
+ return result;
53
+ }
54
+ /** Get all registered queue names */
55
+ getQueueNames() {
56
+ return Array.from(this.queues.keys());
57
+ }
58
+ /** Close all queues gracefully */
59
+ async closeAll() {
60
+ for (const [name, queue] of this.queues) {
61
+ await queue.close();
62
+ log.debug(`Queue closed: ${name}`);
63
+ }
64
+ this.queues.clear();
65
+ }
66
+ };
67
+
68
+ // src/queue.adapter.ts
69
+ var log2 = Logger2.for("QueueAdapter");
70
+ var QueueAdapter = class {
71
+ static {
72
+ __name(this, "QueueAdapter");
73
+ }
74
+ options;
75
+ name = "QueueAdapter";
76
+ workers = [];
77
+ queueService = new QueueService();
78
+ constructor(options) {
79
+ this.options = options;
80
+ }
81
+ beforeStart(_app, container) {
82
+ const { redis, queues: preCreateQueues = [], concurrency = 1 } = this.options;
83
+ const connection = {
84
+ host: redis.host,
85
+ port: redis.port,
86
+ password: redis.password
87
+ };
88
+ for (const name of preCreateQueues) {
89
+ if (!this.queueService.getQueue(name)) {
90
+ const queue = new Queue(name, {
91
+ connection
92
+ });
93
+ this.queueService.registerQueue(name, queue);
94
+ }
95
+ }
96
+ for (const jobClass of jobRegistry) {
97
+ const queueName = Reflect.getMetadata(QUEUE_METADATA.JOB, jobClass);
98
+ if (queueName === void 0) continue;
99
+ const handlers = Reflect.getMetadata(QUEUE_METADATA.PROCESS, jobClass) || [];
100
+ if (handlers.length === 0) {
101
+ log2.warn(`@Job('${queueName}') class ${jobClass.name} has no @Process methods \u2014 skipping`);
102
+ continue;
103
+ }
104
+ if (!this.queueService.getQueue(queueName)) {
105
+ const queue = new Queue(queueName, {
106
+ connection
107
+ });
108
+ this.queueService.registerQueue(queueName, queue);
109
+ }
110
+ const processor = container.resolve(jobClass);
111
+ const worker = new Worker(queueName, async (job) => {
112
+ const specific = handlers.find((h) => h.jobName === job.name);
113
+ const handler = specific || handlers.find((h) => h.jobName === void 0);
114
+ if (handler) {
115
+ await processor[handler.handlerName](job);
116
+ } else {
117
+ log2.warn(`No handler for job "${job.name}" in queue "${queueName}"`);
118
+ }
119
+ }, {
120
+ connection,
121
+ concurrency
122
+ });
123
+ worker.on("failed", (job, err) => {
124
+ log2.error({
125
+ err
126
+ }, `Job failed: ${queueName}/${job?.name} (id: ${job?.id})`);
127
+ });
128
+ worker.on("completed", (job) => {
129
+ log2.debug(`Job completed: ${queueName}/${job.name} (id: ${job.id})`);
130
+ });
131
+ this.workers.push(worker);
132
+ log2.info(`Worker started: ${queueName} (${jobClass.name}, ${handlers.length} handler(s), concurrency: ${concurrency})`);
133
+ }
134
+ container.registerFactory(QUEUE_MANAGER, () => this.queueService, Scope.SINGLETON);
135
+ log2.info(`QueueAdapter ready \u2014 ${this.queueService.getQueueNames().length} queue(s), ${this.workers.length} worker(s)`);
136
+ }
137
+ /** Gracefully close all workers and queues */
138
+ async shutdown() {
139
+ for (const worker of this.workers) {
140
+ await worker.close();
141
+ }
142
+ log2.info(`Closed ${this.workers.length} worker(s)`);
143
+ this.workers = [];
144
+ await this.queueService.closeAll();
145
+ log2.info("All queues closed");
146
+ }
147
+ };
148
+
149
+ // src/decorators.ts
150
+ import "reflect-metadata";
151
+ import { Service } from "@forinda/kickjs-core";
152
+ function Job(queueName) {
153
+ return (target) => {
154
+ Service()(target);
155
+ Reflect.defineMetadata(QUEUE_METADATA.JOB, queueName, target);
156
+ jobRegistry.add(target);
157
+ };
158
+ }
159
+ __name(Job, "Job");
160
+ function Process(jobName) {
161
+ return (target, propertyKey) => {
162
+ const handlers = Reflect.getMetadata(QUEUE_METADATA.PROCESS, target.constructor) || [];
163
+ handlers.push({
164
+ handlerName: propertyKey,
165
+ jobName
166
+ });
167
+ Reflect.defineMetadata(QUEUE_METADATA.PROCESS, handlers, target.constructor);
168
+ };
169
+ }
170
+ __name(Process, "Process");
171
+ export {
172
+ Job,
173
+ Process,
174
+ QUEUE_MANAGER,
175
+ QueueAdapter,
176
+ QueueService
177
+ };
178
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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":";;;;AAAA,SAASA,OAAOC,cAAqC;AACrD,SAASC,UAAAA,SAAyCC,aAAa;;;ACD/D,OAAO;AAiBA,IAAMC,gBAAgBC,uBAAO,cAAA;AAG7B,IAAMC,iBAAiB;EAC5BC,KAAKF,uBAAO,WAAA;EACZG,SAASH,uBAAO,eAAA;AAClB;AAWO,IAAMI,cAAc,oBAAIC,IAAAA;;;ACjC/B,SAASC,cAAc;AAEvB,IAAMC,MAAMC,OAAOC,IAAI,cAAA;AA0BhB,IAAMC,eAAN,MAAMA;EA5Bb,OA4BaA;;;EACHC,SAAS,oBAAIC,IAAAA;;EAGrBC,cAAcC,MAAcC,OAAoB;AAC9C,SAAKJ,OAAOK,IAAIF,MAAMC,KAAAA;AACtBR,QAAIU,MAAM,qBAAqBH,IAAAA,EAAM;EACvC;;EAGAI,SAASJ,MAAiC;AACxC,WAAO,KAAKH,OAAOQ,IAAIL,IAAAA;EACzB;;EAGA,MAAMM,IAAIC,WAAmBC,SAAiBC,MAAWC,MAAoB;AAC3E,UAAMT,QAAQ,KAAKJ,OAAOQ,IAAIE,SAAAA;AAC9B,QAAI,CAACN,OAAO;AACV,YAAM,IAAIU,MAAM,UAAUJ,SAAAA,mDAA4D;IACxF;AACA,UAAMK,MAAM,MAAMX,MAAMK,IAAIE,SAASC,MAAMC,IAAAA;AAC3CjB,QAAIU,MAAM,cAAcI,SAAAA,IAAaC,OAAAA,SAAgBI,IAAIC,EAAE,GAAG;AAC9D,WAAOD;EACT;;EAGA,MAAME,QAAQP,WAAmBQ,MAAsB;AACrD,UAAMd,QAAQ,KAAKJ,OAAOQ,IAAIE,SAAAA;AAC9B,QAAI,CAACN,OAAO;AACV,YAAM,IAAIU,MAAM,UAAUJ,SAAAA,mDAA4D;IACxF;AACA,UAAMS,SAAS,MAAMf,MAAMa,QAAQC,IAAAA;AACnCtB,QAAIU,MAAM,oBAAoBI,SAAAA,YAAqBS,OAAOC,MAAM,GAAG;AACnE,WAAOD;EACT;;EAGAE,gBAA0B;AACxB,WAAOC,MAAMC,KAAK,KAAKvB,OAAOwB,KAAI,CAAA;EACpC;;EAGA,MAAMC,WAA0B;AAC9B,eAAW,CAACtB,MAAMC,KAAAA,KAAU,KAAKJ,QAAQ;AACvC,YAAMI,MAAMsB,MAAK;AACjB9B,UAAIU,MAAM,iBAAiBH,IAAAA,EAAM;IACnC;AACA,SAAKH,OAAO2B,MAAK;EACnB;AACF;;;AFnEA,IAAMC,OAAMC,QAAOC,IAAI,cAAA;AAsBhB,IAAMC,eAAN,MAAMA;EAjCb,OAiCaA;;;;EACFC,OAAO;EAERC,UAAoB,CAAA;EACpBC,eAAe,IAAIC,aAAAA;EAE3B,YAAoBC,SAA8B;SAA9BA,UAAAA;EAA+B;EAEnDC,YAAYC,MAAWC,WAA4B;AACjD,UAAM,EAAEC,OAAOC,QAAQC,kBAAkB,CAAA,GAAIC,cAAc,EAAC,IAAK,KAAKP;AAEtE,UAAMQ,aAAa;MAAEC,MAAML,MAAMK;MAAMC,MAAMN,MAAMM;MAAMC,UAAUP,MAAMO;IAAS;AAGlF,eAAWf,QAAQU,iBAAiB;AAClC,UAAI,CAAC,KAAKR,aAAac,SAAShB,IAAAA,GAAO;AACrC,cAAMiB,QAAQ,IAAIC,MAAMlB,MAAM;UAAEY;QAAW,CAAA;AAC3C,aAAKV,aAAaiB,cAAcnB,MAAMiB,KAAAA;MACxC;IACF;AAGA,eAAWG,YAAYC,aAAa;AAClC,YAAMC,YAAgCC,QAAQC,YAAYC,eAAeC,KAAKN,QAAAA;AAC9E,UAAIE,cAAcK,OAAW;AAE7B,YAAMC,WACJL,QAAQC,YAAYC,eAAeI,SAAST,QAAAA,KAAa,CAAA;AAE3D,UAAIQ,SAASE,WAAW,GAAG;AACzBlC,QAAAA,KAAImC,KAAK,SAAST,SAAAA,YAAqBF,SAASpB,IAAI,0CAAqC;AACzF;MACF;AAGA,UAAI,CAAC,KAAKE,aAAac,SAASM,SAAAA,GAAY;AAC1C,cAAML,QAAQ,IAAIC,MAAMI,WAAW;UAAEV;QAAW,CAAA;AAChD,aAAKV,aAAaiB,cAAcG,WAAWL,KAAAA;MAC7C;AAGA,YAAMe,YAAYzB,UAAU0B,QAAQb,QAAAA;AAGpC,YAAMc,SAAS,IAAIC,OACjBb,WACA,OAAOc,QAAAA;AAEL,cAAMC,WAAWT,SAASU,KAAK,CAACC,MAAMA,EAAEC,YAAYJ,IAAIpC,IAAI;AAE5D,cAAMyC,UAAUJ,YAAYT,SAASU,KAAK,CAACC,MAAMA,EAAEC,YAAYb,MAAAA;AAE/D,YAAIc,SAAS;AACX,gBAAMT,UAAUS,QAAQC,WAAW,EAAEN,GAAAA;QACvC,OAAO;AACLxC,UAAAA,KAAImC,KAAK,uBAAuBK,IAAIpC,IAAI,eAAesB,SAAAA,GAAY;QACrE;MACF,GACA;QAAEV;QAAYD;MAAY,CAAA;AAG5BuB,aAAOS,GAAG,UAAU,CAACP,KAAKQ,QAAAA;AACxBhD,QAAAA,KAAIiD,MAAM;UAAED;QAAI,GAAG,eAAetB,SAAAA,IAAac,KAAKpC,IAAAA,SAAaoC,KAAKU,EAAAA,GAAK;MAC7E,CAAA;AAEAZ,aAAOS,GAAG,aAAa,CAACP,QAAAA;AACtBxC,QAAAA,KAAImD,MAAM,kBAAkBzB,SAAAA,IAAac,IAAIpC,IAAI,SAASoC,IAAIU,EAAE,GAAG;MACrE,CAAA;AAEA,WAAK7C,QAAQ+C,KAAKd,MAAAA;AAClBtC,MAAAA,KAAIqD,KACF,mBAAmB3B,SAAAA,KAAcF,SAASpB,IAAI,KAAK4B,SAASE,MAAM,6BAA6BnB,WAAAA,GAAc;IAEjH;AAGAJ,cAAU2C,gBAAgBC,eAAe,MAAM,KAAKjD,cAAckD,MAAMC,SAAS;AAEjFzD,IAAAA,KAAIqD,KACF,6BAAwB,KAAK/C,aAAaoD,cAAa,EAAGxB,MAAM,cAAc,KAAK7B,QAAQ6B,MAAM,YAAY;EAEjH;;EAGA,MAAMyB,WAA0B;AAE9B,eAAWrB,UAAU,KAAKjC,SAAS;AACjC,YAAMiC,OAAOsB,MAAK;IACpB;AACA5D,IAAAA,KAAIqD,KAAK,UAAU,KAAKhD,QAAQ6B,MAAM,YAAY;AAClD,SAAK7B,UAAU,CAAA;AAGf,UAAM,KAAKC,aAAauD,SAAQ;AAChC7D,IAAAA,KAAIqD,KAAK,mBAAA;EACX;AACF;;;AGjIA,OAAO;AACP,SAASS,eAAe;AA2BjB,SAASC,IAAIC,WAAiB;AACnC,SAAO,CAACC,WAAAA;AACNC,YAAAA,EAAUD,MAAAA;AACVE,YAAQC,eAAeC,eAAeC,KAAKN,WAAWC,MAAAA;AACtDM,gBAAYC,IAAIP,MAAAA;EAClB;AACF;AANgBF;AA4BT,SAASU,QAAQC,SAAgB;AACtC,SAAO,CAACT,QAAQU,gBAAAA;AACd,UAAMC,WACJT,QAAQU,YAAYR,eAAeS,SAASb,OAAO,WAAW,KAAK,CAAA;AACrEW,aAASG,KAAK;MACZC,aAAaL;MACbD;IACF,CAAA;AACAP,YAAQC,eAAeC,eAAeS,SAASF,UAAUX,OAAO,WAAW;EAC7E;AACF;AAVgBQ;","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"]}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@forinda/kickjs-queue",
3
+ "version": "0.6.0",
4
+ "description": "BullMQ queue/worker adapter with decorator-driven job processing for KickJS",
5
+ "keywords": [
6
+ "kickjs",
7
+ "nodejs",
8
+ "typescript",
9
+ "queue",
10
+ "worker",
11
+ "bullmq",
12
+ "jobs",
13
+ "background-jobs",
14
+ "adapter",
15
+ "@forinda/kickjs-core"
16
+ ],
17
+ "type": "module",
18
+ "main": "dist/index.js",
19
+ "types": "dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "import": "./dist/index.js",
23
+ "types": "./dist/index.d.ts"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "dependencies": {
30
+ "reflect-metadata": "^0.2.2",
31
+ "@forinda/kickjs-core": "0.7.0"
32
+ },
33
+ "peerDependencies": {
34
+ "bullmq": ">=5.0.0",
35
+ "ioredis": ">=5.0.0"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^24.5.2",
39
+ "tsup": "^8.5.0",
40
+ "typescript": "^5.9.2"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "license": "MIT",
46
+ "author": "Felix Orinda",
47
+ "engines": {
48
+ "node": ">=20.0"
49
+ },
50
+ "homepage": "https://forinda.github.io/kick-js/",
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "https://github.com/forinda/kick-js.git",
54
+ "directory": "packages/queue"
55
+ },
56
+ "bugs": {
57
+ "url": "https://github.com/forinda/kick-js/issues"
58
+ },
59
+ "scripts": {
60
+ "build": "tsup",
61
+ "dev": "tsup --watch",
62
+ "typecheck": "tsc --noEmit",
63
+ "clean": "rm -rf dist .turbo",
64
+ "lint": "tsc --noEmit"
65
+ }
66
+ }