@nexusts/queue 0.7.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 ADDED
@@ -0,0 +1,45 @@
1
+ # @nexusts/queue
2
+
3
+ > **NexusTS** — Bun-native fullstack framework
4
+
5
+ ## Description
6
+
7
+ Background jobs (BullMQ / Cloudflare / memory).
8
+
9
+ Three backends: BullMQ (Redis-backed, multi-pod), Cloudflare Queues (edge), in-memory (single-process dev). `@OnQueueReady('worker')` decorator wires handler methods to queue names.
10
+
11
+ ## Install
12
+
13
+ This module is part of the NexusTS monorepo. Each module is published as its own npm package under the `@nexusts/` scope.
14
+
15
+ Most apps start with just the core:
16
+
17
+ ```bash
18
+ bun add @nexusts/core reflect-metadata zod hono
19
+ ```
20
+
21
+ Then add this module only if you need it:
22
+
23
+ ```bash
24
+ bun add @nexusts/queue
25
+ ```
26
+
27
+ ## Peer dependencies
28
+
29
+ ```bash
30
+ bun add bullmq + ioredis
31
+ ```
32
+
33
+ Required by this module. Without them the module loads but its public methods throw a clear error pointing to this install command on first call.
34
+
35
+ ## Usage
36
+
37
+ ```typescript
38
+ import { /* public API */ } from "@nexusts/queue";
39
+ ```
40
+
41
+ See the [user guide](../../docs/user-guide/queue.md) and the [example app](../../examples/) for a working demo.
42
+
43
+ ## License
44
+
45
+ MIT — see the root [LICENSE](../../LICENSE).
package/dist/index.js ADDED
@@ -0,0 +1,723 @@
1
+ // @bun
2
+ var __legacyDecorateClassTS = function(decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
5
+ r = Reflect.decorate(decorators, target, key, desc);
6
+ else
7
+ for (var i = decorators.length - 1;i >= 0; i--)
8
+ if (d = decorators[i])
9
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
10
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
11
+ };
12
+ var __legacyDecorateParamTS = (index, decorator) => (target, key) => decorator(target, key, index);
13
+ var __legacyMetadataTS = (k, v) => {
14
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
15
+ return Reflect.metadata(k, v);
16
+ };
17
+ // packages/queue/src/backends/memory.ts
18
+ class MemoryWorkerHandle {
19
+ #running = true;
20
+ #handler;
21
+ #context;
22
+ constructor(name, handler) {
23
+ this.#handler = handler;
24
+ this.#context = { jobId: "", name };
25
+ }
26
+ get name() {
27
+ return this.#context.name;
28
+ }
29
+ async close() {
30
+ this.#running = false;
31
+ }
32
+ async pause() {
33
+ this.#running = false;
34
+ }
35
+ async resume() {
36
+ this.#running = true;
37
+ }
38
+ isRunning() {
39
+ return this.#running;
40
+ }
41
+ }
42
+
43
+ class MemoryQueueBackend {
44
+ name = "memory";
45
+ #queue = [];
46
+ #handlers = new Map;
47
+ #workerOptions = new Map;
48
+ #listeners = new Set;
49
+ #tickHandle = null;
50
+ #inFlight = 0;
51
+ constructor() {
52
+ this.#tickHandle = setInterval(() => this.#tick(), 100);
53
+ if (typeof this.#tickHandle.unref === "function") {
54
+ this.#tickHandle.unref();
55
+ }
56
+ }
57
+ async add(name, data, options = {}) {
58
+ const jobId = `mem-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
59
+ const delayMs = (options.delaySeconds ?? 0) * 1000;
60
+ this.#queue.push({
61
+ jobId,
62
+ name,
63
+ data,
64
+ options,
65
+ resolveAt: Date.now() + delayMs
66
+ });
67
+ this.#emit({ kind: "job:added", jobId, name });
68
+ return { jobId, name };
69
+ }
70
+ async addBatch(jobs) {
71
+ return Promise.all(jobs.map((j) => this.add(j.name, j.data, j.options)));
72
+ }
73
+ async process(name, handler, options = {}) {
74
+ this.#handlers.set(name, handler);
75
+ this.#workerOptions.set(name, options);
76
+ const handle = new MemoryWorkerHandle(name, handler);
77
+ this.#emit({
78
+ kind: "worker:started",
79
+ name,
80
+ concurrency: options.concurrency ?? 1
81
+ });
82
+ return handle;
83
+ }
84
+ async drain() {
85
+ while (this.#inFlight > 0 || this.#queue.length > 0) {
86
+ await new Promise((r) => setTimeout(r, 50));
87
+ }
88
+ }
89
+ async stop() {
90
+ for (const name of this.#handlers.keys()) {
91
+ this.#emit({ kind: "worker:stopped", name });
92
+ }
93
+ if (this.#tickHandle)
94
+ clearInterval(this.#tickHandle);
95
+ this.#tickHandle = null;
96
+ }
97
+ on(listener) {
98
+ this.#listeners.add(listener);
99
+ return () => this.#listeners.delete(listener);
100
+ }
101
+ async#tick() {
102
+ if (this.#queue.length === 0)
103
+ return;
104
+ const now = Date.now();
105
+ const due = this.#queue.filter((j) => j.resolveAt <= now);
106
+ for (const job of due) {
107
+ this.#queue = this.#queue.filter((j) => j !== job);
108
+ const handler = this.#handlers.get(job.name);
109
+ if (!handler)
110
+ continue;
111
+ const options = this.#workerOptions.get(job.name) ?? {};
112
+ const concurrency = options.concurrency ?? 1;
113
+ if (this.#inFlight >= concurrency) {
114
+ this.#queue.unshift(job);
115
+ continue;
116
+ }
117
+ this.#runJob(job, handler, options);
118
+ }
119
+ }
120
+ async#runJob(job, handler, options) {
121
+ this.#inFlight++;
122
+ const ctx = {
123
+ jobId: job.jobId,
124
+ attempts: 1,
125
+ job: { name: job.name, data: job.data },
126
+ prefix: `[queue:${job.name}]`
127
+ };
128
+ this.#emit({
129
+ kind: "job:active",
130
+ jobId: job.jobId,
131
+ name: job.name,
132
+ attempts: 1
133
+ });
134
+ try {
135
+ const result = await handler(job.data, ctx);
136
+ if (result && typeof result === "object" && "status" in result) {
137
+ const r = result;
138
+ if (r.status === "failed") {
139
+ this.#emit({
140
+ kind: "job:failed",
141
+ jobId: job.jobId,
142
+ name: job.name,
143
+ error: r.error ?? new Error("unknown"),
144
+ willRetry: false
145
+ });
146
+ } else {
147
+ this.#emit({
148
+ kind: "job:completed",
149
+ jobId: job.jobId,
150
+ name: job.name,
151
+ returnvalue: r.returnvalue
152
+ });
153
+ }
154
+ } else {
155
+ this.#emit({
156
+ kind: "job:completed",
157
+ jobId: job.jobId,
158
+ name: job.name,
159
+ returnvalue: result
160
+ });
161
+ }
162
+ } catch (err) {
163
+ const error = err instanceof Error ? err : new Error(String(err));
164
+ const willRetry = (job.options.attempts ?? 1) > 1;
165
+ this.#emit({
166
+ kind: "job:failed",
167
+ jobId: job.jobId,
168
+ name: job.name,
169
+ error,
170
+ willRetry
171
+ });
172
+ if (willRetry) {
173
+ const delayMs = (job.options.backoff?.delayMs ?? 1000) * (job.options.backoff?.type === "exponential" ? 2 ** ctx.attempts : 1);
174
+ this.#queue.push({
175
+ ...job,
176
+ resolveAt: Date.now() + delayMs
177
+ });
178
+ }
179
+ } finally {
180
+ this.#inFlight--;
181
+ }
182
+ }
183
+ #emit(event) {
184
+ for (const l of this.#listeners) {
185
+ Promise.resolve(l(event));
186
+ }
187
+ }
188
+ }
189
+ // packages/queue/src/backends/bullmq.ts
190
+ import {
191
+ Queue,
192
+ Worker
193
+ } from "bullmq";
194
+ import IORedis from "ioredis";
195
+
196
+ class BullMQWorkerHandle {
197
+ #worker;
198
+ #name;
199
+ constructor(name, worker) {
200
+ this.#name = name;
201
+ this.#worker = worker;
202
+ }
203
+ get name() {
204
+ return this.#name;
205
+ }
206
+ async close() {
207
+ await this.#worker.close();
208
+ }
209
+ async pause() {
210
+ await this.#worker.pause();
211
+ }
212
+ async resume() {
213
+ await this.#worker.resume();
214
+ }
215
+ isRunning() {
216
+ return !this.#worker.closing;
217
+ }
218
+ }
219
+
220
+ class BullMQBackend {
221
+ name = "bullmq";
222
+ #queue;
223
+ #connection;
224
+ #prefix;
225
+ #defaultJobOptions;
226
+ #workers = new Map;
227
+ #listeners = new Set;
228
+ #closed = false;
229
+ constructor(options) {
230
+ this.#connection = typeof options.connection === "string" ? new IORedis(options.connection, {
231
+ maxRetriesPerRequest: null
232
+ }) : options.connection;
233
+ this.#prefix = options.prefix ?? "nexusjs";
234
+ this.#defaultJobOptions = options.defaultJobOptions ?? {};
235
+ this.#queue = new Queue("nexus-queue", {
236
+ connection: this.#connection,
237
+ prefix: this.#prefix,
238
+ defaultJobOptions: this.#toBullJobOptions(this.#defaultJobOptions)
239
+ });
240
+ }
241
+ async add(name, data, options = {}) {
242
+ const merged = { ...this.#defaultJobOptions, ...options };
243
+ const job = await this.#queue.add(name, data, this.#toBullJobOptions(merged));
244
+ this.#emit({ kind: "job:added", jobId: String(job.id ?? ""), name });
245
+ return { jobId: String(job.id ?? ""), name, handle: job };
246
+ }
247
+ async addBatch(jobs) {
248
+ const bullJobs = jobs.map((j) => ({
249
+ name: j.name,
250
+ data: j.data,
251
+ opts: this.#toBullJobOptions({
252
+ ...this.#defaultJobOptions,
253
+ ...j.options
254
+ })
255
+ }));
256
+ const added = await this.#queue.addBulk(bullJobs);
257
+ for (const job of added) {
258
+ this.#emit({
259
+ kind: "job:added",
260
+ jobId: String(job.id ?? ""),
261
+ name: job.name
262
+ });
263
+ }
264
+ return added.map((job) => ({
265
+ jobId: String(job.id ?? ""),
266
+ name: job.name,
267
+ handle: job
268
+ }));
269
+ }
270
+ async process(name, handler, options = {}) {
271
+ const worker = new Worker("nexus-queue", async (job) => {
272
+ const ctx = {
273
+ jobId: String(job.id ?? ""),
274
+ attempts: job.attemptsMade + 1,
275
+ job: { name: job.name, data: job.data },
276
+ prefix: `[queue:${job.name}]`
277
+ };
278
+ this.#emit({
279
+ kind: "job:active",
280
+ jobId: ctx.jobId,
281
+ name: job.name,
282
+ attempts: ctx.attempts
283
+ });
284
+ try {
285
+ const result = await handler(job.data, ctx);
286
+ if (result && typeof result === "object" && "status" in result) {
287
+ const r = result;
288
+ if (r.status === "completed") {
289
+ this.#emit({
290
+ kind: "job:completed",
291
+ jobId: ctx.jobId,
292
+ name: job.name,
293
+ returnvalue: r.returnvalue
294
+ });
295
+ return r.returnvalue;
296
+ }
297
+ if (r.status === "retry") {
298
+ const r2 = result;
299
+ throw new RetryError(r2.reason ?? "retry requested", r2.delaySeconds);
300
+ }
301
+ }
302
+ this.#emit({
303
+ kind: "job:completed",
304
+ jobId: ctx.jobId,
305
+ name: job.name,
306
+ returnvalue: result
307
+ });
308
+ return result;
309
+ } catch (err) {
310
+ if (err instanceof RetryError) {
311
+ await job.moveToDelayed(Date.now() + (err.delaySeconds ?? 0) * 1000, job.token);
312
+ return;
313
+ }
314
+ const error = err instanceof Error ? err : new Error(String(err));
315
+ const willRetry = (job.opts.attempts ?? 1) > job.attemptsMade;
316
+ this.#emit({
317
+ kind: "job:failed",
318
+ jobId: ctx.jobId,
319
+ name: job.name,
320
+ error,
321
+ willRetry
322
+ });
323
+ throw err;
324
+ }
325
+ }, {
326
+ connection: this.#connection,
327
+ prefix: this.#prefix,
328
+ concurrency: options.concurrency ?? 1,
329
+ lockDuration: options.lockDurationMs ?? 30000,
330
+ limiter: options.limiter ? { max: options.limiter.max, duration: options.limiter.durationMs } : undefined
331
+ });
332
+ this.#workers.set(name, worker);
333
+ this.#emit({
334
+ kind: "worker:started",
335
+ name,
336
+ concurrency: options.concurrency ?? 1
337
+ });
338
+ return new BullMQWorkerHandle(name, worker);
339
+ }
340
+ async drain() {
341
+ const workers = [...this.#workers.values()];
342
+ await Promise.all(workers.map((w) => w.waitUntilReady()));
343
+ while (workers.some((w) => w._job !== undefined)) {
344
+ await new Promise((r) => setTimeout(r, 50));
345
+ }
346
+ }
347
+ async stop() {
348
+ if (this.#closed)
349
+ return;
350
+ this.#closed = true;
351
+ for (const [name, worker] of this.#workers) {
352
+ await worker.close();
353
+ this.#emit({ kind: "worker:stopped", name });
354
+ }
355
+ await this.#queue.close();
356
+ try {
357
+ const conn = this.#connection;
358
+ if (typeof conn?.quit === "function")
359
+ await conn.quit();
360
+ } catch {}
361
+ }
362
+ on(listener) {
363
+ this.#listeners.add(listener);
364
+ return () => this.#listeners.delete(listener);
365
+ }
366
+ #toBullJobOptions(opts) {
367
+ const out = {};
368
+ if (opts.delaySeconds !== undefined)
369
+ out.delay = opts.delaySeconds * 1000;
370
+ if (opts.attempts !== undefined)
371
+ out.attempts = opts.attempts;
372
+ if (opts.backoff)
373
+ out.backoff = { type: opts.backoff.type, delay: opts.backoff.delayMs };
374
+ if (opts.priority !== undefined)
375
+ out.priority = opts.priority;
376
+ if (opts.jobId !== undefined)
377
+ out.jobId = opts.jobId;
378
+ if (opts.removeOnComplete !== undefined)
379
+ out.removeOnComplete = opts.removeOnComplete;
380
+ if (opts.removeOnFail !== undefined)
381
+ out.removeOnFail = opts.removeOnFail;
382
+ return out;
383
+ }
384
+ #emit(event) {
385
+ for (const l of this.#listeners) {
386
+ Promise.resolve(l(event));
387
+ }
388
+ }
389
+ }
390
+
391
+ class RetryError extends Error {
392
+ delaySeconds;
393
+ __retry = true;
394
+ constructor(message, delaySeconds) {
395
+ super(message);
396
+ this.delaySeconds = delaySeconds;
397
+ this.name = "RetryError";
398
+ }
399
+ }
400
+ // packages/queue/src/backends/cloudflare.ts
401
+ class CloudflareWorkerHandle {
402
+ #name;
403
+ #running = true;
404
+ constructor(name) {
405
+ this.#name = name;
406
+ }
407
+ get name() {
408
+ return this.#name;
409
+ }
410
+ async close() {
411
+ this.#running = false;
412
+ }
413
+ async pause() {
414
+ this.#running = false;
415
+ }
416
+ async resume() {
417
+ this.#running = true;
418
+ }
419
+ isRunning() {
420
+ return this.#running;
421
+ }
422
+ }
423
+
424
+ class CloudflareQueueBackend {
425
+ name = "cloudflare";
426
+ #queue = null;
427
+ #resolveBinding;
428
+ #handlers = new Map;
429
+ #workerOptions = new Map;
430
+ #listeners = new Set;
431
+ #queueName;
432
+ constructor(options) {
433
+ this.#resolveBinding = options.resolveBinding;
434
+ this.#queueName = options.name ?? "queue";
435
+ }
436
+ bind(env) {
437
+ this.#queue = this.#resolveBinding(env);
438
+ }
439
+ async add(name, data, options = {}) {
440
+ if (!this.#queue)
441
+ throw new Error("[queue/cloudflare] bind() must be called before add()");
442
+ const id = `cf-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
443
+ await this.#queue.send({ name, data, jobId: id, options }, { delaySeconds: options.delaySeconds });
444
+ this.#emit({ kind: "job:added", jobId: id, name });
445
+ return { jobId: id, name };
446
+ }
447
+ async addBatch(jobs) {
448
+ if (!this.#queue)
449
+ throw new Error("[queue/cloudflare] bind() must be called before addBatch()");
450
+ const ids = jobs.map(() => `cf-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`);
451
+ await this.#queue.sendBatch(jobs.map((j, i) => ({
452
+ body: { name: j.name, data: j.data, jobId: ids[i], options: j.options },
453
+ delaySeconds: j.options?.delaySeconds
454
+ })));
455
+ for (let i = 0;i < jobs.length; i++) {
456
+ const j = jobs[i];
457
+ this.#emit({ kind: "job:added", jobId: ids[i], name: j.name });
458
+ }
459
+ return jobs.map((j, i) => ({ jobId: ids[i], name: j.name }));
460
+ }
461
+ async process(name, handler, options = {}) {
462
+ this.#handlers.set(name, handler);
463
+ this.#workerOptions.set(name, options);
464
+ this.#emit({
465
+ kind: "worker:started",
466
+ name,
467
+ concurrency: options.concurrency ?? 1
468
+ });
469
+ return new CloudflareWorkerHandle(name);
470
+ }
471
+ consumerHandler() {
472
+ return async (batch) => {
473
+ for (const message of batch.messages) {
474
+ const body = message.body ?? {};
475
+ const jobName = body.name ?? "";
476
+ const handler = this.#handlers.get(jobName);
477
+ if (!handler) {
478
+ message.retry();
479
+ continue;
480
+ }
481
+ const ctx = {
482
+ jobId: body.jobId ?? message.id,
483
+ attempts: message.attempts,
484
+ job: { name: jobName, data: body.data },
485
+ prefix: `[queue:${jobName}]`
486
+ };
487
+ this.#emit({
488
+ kind: "job:active",
489
+ jobId: ctx.jobId,
490
+ name: jobName,
491
+ attempts: ctx.attempts
492
+ });
493
+ try {
494
+ const result = await handler(body.data, ctx);
495
+ if (result && typeof result === "object" && "status" in result) {
496
+ const r = result;
497
+ if (r.status === "failed") {
498
+ this.#emit({
499
+ kind: "job:failed",
500
+ jobId: ctx.jobId,
501
+ name: jobName,
502
+ error: r.error ?? new Error("failed"),
503
+ willRetry: true
504
+ });
505
+ message.retry();
506
+ continue;
507
+ }
508
+ if (r.status === "retry") {
509
+ const r2 = result;
510
+ message.retry({ delaySeconds: r2.delaySeconds });
511
+ continue;
512
+ }
513
+ this.#emit({
514
+ kind: "job:completed",
515
+ jobId: ctx.jobId,
516
+ name: jobName,
517
+ returnvalue: r.returnvalue
518
+ });
519
+ } else {
520
+ this.#emit({
521
+ kind: "job:completed",
522
+ jobId: ctx.jobId,
523
+ name: jobName,
524
+ returnvalue: result
525
+ });
526
+ }
527
+ } catch (err) {
528
+ const error = err instanceof Error ? err : new Error(String(err));
529
+ const willRetry = (body.options?.attempts ?? 1) > message.attempts;
530
+ this.#emit({
531
+ kind: "job:failed",
532
+ jobId: ctx.jobId,
533
+ name: jobName,
534
+ error,
535
+ willRetry
536
+ });
537
+ if (willRetry)
538
+ message.retry();
539
+ else
540
+ message.ack();
541
+ }
542
+ }
543
+ };
544
+ }
545
+ async drain() {}
546
+ async stop() {
547
+ for (const name of this.#handlers.keys()) {
548
+ this.#emit({ kind: "worker:stopped", name });
549
+ }
550
+ this.#handlers.clear();
551
+ this.#workerOptions.clear();
552
+ }
553
+ on(listener) {
554
+ this.#listeners.add(listener);
555
+ return () => this.#listeners.delete(listener);
556
+ }
557
+ #emit(event) {
558
+ for (const l of this.#listeners) {
559
+ Promise.resolve(l(event));
560
+ }
561
+ }
562
+ get queueName() {
563
+ return this.#queueName;
564
+ }
565
+ }
566
+ // packages/queue/src/queue.service.ts
567
+ import { Inject, Injectable } from "@nexusts/core/src/decorators/index.js";
568
+ class QueueService {
569
+ config;
570
+ static TOKEN = Symbol.for("nexus:QueueService");
571
+ backend;
572
+ #workers = new Map;
573
+ #listeners = new Set;
574
+ #started = false;
575
+ constructor(config) {
576
+ this.config = config;
577
+ this.backend = this.#createBackend(config);
578
+ }
579
+ async add(name, data, options = {}) {
580
+ const merged = { ...this.config.defaults, ...options };
581
+ return this.backend.add(name, data, merged);
582
+ }
583
+ async addBatch(jobs) {
584
+ return this.backend.addBatch(jobs);
585
+ }
586
+ async process(name, handler) {
587
+ const handle = await this.backend.process(name, handler);
588
+ this.#workers.set(name, handle);
589
+ return handle;
590
+ }
591
+ on(listener) {
592
+ this.#listeners.add(listener);
593
+ return () => this.#listeners.delete(listener);
594
+ }
595
+ async start() {
596
+ if (this.#started)
597
+ return;
598
+ this.#started = true;
599
+ this.backend.on((event) => this.#broadcast(event));
600
+ }
601
+ async stop() {
602
+ if (!this.#started)
603
+ return;
604
+ this.#started = false;
605
+ await this.backend.drain();
606
+ for (const handle of this.#workers.values()) {
607
+ await handle.close();
608
+ }
609
+ await this.backend.stop();
610
+ this.#workers.clear();
611
+ }
612
+ getCloudflareBackend() {
613
+ return this.backend instanceof CloudflareQueueBackend ? this.backend : null;
614
+ }
615
+ #createBackend(config) {
616
+ switch (config.backend) {
617
+ case "memory":
618
+ return new MemoryQueueBackend;
619
+ case "bullmq": {
620
+ if (!config.bullmq) {
621
+ throw new Error("[queue] backend=bullmq requires `bullmq.connection` in config.");
622
+ }
623
+ return new BullMQBackend({
624
+ connection: config.bullmq.connection,
625
+ prefix: config.bullmq.prefix,
626
+ defaultJobOptions: config.bullmq.defaultJobOptions
627
+ });
628
+ }
629
+ case "cloudflare": {
630
+ if (!config.cloudflare) {
631
+ throw new Error("[queue] backend=cloudflare requires `cloudflare.resolveBinding` in config.");
632
+ }
633
+ return new CloudflareQueueBackend({
634
+ resolveBinding: config.cloudflare.resolveBinding,
635
+ name: config.cloudflare.name
636
+ });
637
+ }
638
+ }
639
+ }
640
+ #broadcast(event) {
641
+ for (const l of this.#listeners) {
642
+ Promise.resolve(l(event));
643
+ }
644
+ }
645
+ }
646
+ QueueService = __legacyDecorateClassTS([
647
+ Injectable(),
648
+ __legacyDecorateParamTS(0, Inject("QUEUE_CONFIG")),
649
+ __legacyMetadataTS("design:paramtypes", [
650
+ typeof QueueConfig === "undefined" ? Object : QueueConfig
651
+ ])
652
+ ], QueueService);
653
+ // packages/queue/src/queue.module.ts
654
+ import"reflect-metadata";
655
+ import { Module } from "@nexusts/core/decorators/module.js";
656
+ class QueueModule {
657
+ static forRoot(config) {
658
+ class ConfiguredQueueModule {
659
+ }
660
+ ConfiguredQueueModule = __legacyDecorateClassTS([
661
+ Module({
662
+ providers: [
663
+ QueueService,
664
+ { provide: QueueService.TOKEN, useExisting: QueueService },
665
+ { provide: "QUEUE_CONFIG", useValue: config }
666
+ ],
667
+ exports: [QueueService, QueueService.TOKEN]
668
+ })
669
+ ], ConfiguredQueueModule);
670
+ Object.defineProperty(ConfiguredQueueModule, "name", {
671
+ value: "ConfiguredQueueModule"
672
+ });
673
+ return ConfiguredQueueModule;
674
+ }
675
+ }
676
+ QueueModule = __legacyDecorateClassTS([
677
+ Module({
678
+ providers: [
679
+ QueueService,
680
+ { provide: QueueService.TOKEN, useExisting: QueueService }
681
+ ],
682
+ exports: [QueueService, QueueService.TOKEN]
683
+ })
684
+ ], QueueModule);
685
+ // packages/queue/src/decorators/on-queue-ready.ts
686
+ import"reflect-metadata";
687
+ function OnQueueReady() {
688
+ return (target, propertyKey, descriptor) => {
689
+ if (!descriptor || typeof descriptor.value !== "function") {
690
+ throw new Error("@OnQueueReady can only decorate methods.");
691
+ }
692
+ const ctor = target.constructor;
693
+ const hooks = Reflect.getMetadata("nexus:queue:ready-hooks", ctor) ?? [];
694
+ hooks.push(propertyKey);
695
+ Reflect.defineMetadata("nexus:queue:ready-hooks", hooks, ctor);
696
+ };
697
+ }
698
+ function getQueueReadyHooks(target) {
699
+ const ctor = target.constructor ?? target;
700
+ return Reflect.getMetadata("nexus:queue:ready-hooks", ctor) ?? [];
701
+ }
702
+ async function invokeQueueReadyHooks(instance) {
703
+ const hooks = getQueueReadyHooks(instance);
704
+ for (const key of hooks) {
705
+ const fn = instance[key];
706
+ if (typeof fn === "function") {
707
+ await fn.call(instance);
708
+ }
709
+ }
710
+ }
711
+ export {
712
+ invokeQueueReadyHooks,
713
+ getQueueReadyHooks,
714
+ QueueService,
715
+ QueueModule,
716
+ OnQueueReady,
717
+ MemoryQueueBackend,
718
+ CloudflareQueueBackend,
719
+ BullMQBackend
720
+ };
721
+
722
+ //# debugId=77CE421E465283D764756E2164756E21
723
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,15 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/backends/memory.ts", "../src/backends/bullmq.ts", "../src/backends/cloudflare.ts", "../src/queue.service.ts", "../src/queue.module.ts", "../src/decorators/on-queue-ready.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * In-memory queue backend — for tests, single-instance dev, and the\n * `bunx nx dev` workflow when Redis isn't running yet.\n *\n * NOT for production. It does not persist across restarts, does not\n * distribute across workers, and silently drops jobs on crash.\n */\n\nimport type {\n\tQueueBackend,\n\tJobHandler,\n\tWorkerHandle,\n\tWorkerOptions,\n\tAddedJob,\n\tAddOptions,\n\tQueueEvent,\n\tQueueEventListener,\n\tJobContext,\n} from \"../types.js\";\n\ninterface PendingJob {\n\tjobId: string;\n\tname: string;\n\tdata: unknown;\n\toptions: AddOptions;\n\tresolveAt: number; // ms epoch\n}\n\n/** Per-job worker handle. */\nclass MemoryWorkerHandle implements WorkerHandle {\n\t#running = true;\n\t#handler: JobHandler;\n\t#context: { jobId: string; name: string };\n\tconstructor(name: string, handler: JobHandler) {\n\t\tthis.#handler = handler;\n\t\tthis.#context = { jobId: \"\", name };\n\t}\n\tget name() {\n\t\treturn this.#context.name;\n\t}\n\tasync close() {\n\t\tthis.#running = false;\n\t}\n\tasync pause() {\n\t\tthis.#running = false;\n\t}\n\tasync resume() {\n\t\tthis.#running = true;\n\t}\n\tisRunning() {\n\t\treturn this.#running;\n\t}\n}\n\nexport class MemoryQueueBackend implements QueueBackend {\n\treadonly name = \"memory\" as const;\n\t#queue: PendingJob[] = [];\n\t#handlers = new Map<string, JobHandler>();\n\t#workerOptions = new Map<string, WorkerOptions>();\n\t#listeners = new Set<QueueEventListener>();\n\t#tickHandle: ReturnType<typeof setInterval> | null = null;\n\t#inFlight = 0;\n\n\tconstructor() {\n\t\t// Tick every 100 ms to dispatch due jobs.\n\t\tthis.#tickHandle = setInterval(() => this.#tick(), 100);\n\t\t// Allow Node to exit if only this timer is running.\n\t\tif (\n\t\t\ttypeof (this.#tickHandle as { unref?: () => void }).unref === \"function\"\n\t\t) {\n\t\t\t(this.#tickHandle as { unref: () => void }).unref();\n\t\t}\n\t}\n\n\t// ===========================================================================\n\t// Producer\n\t// ===========================================================================\n\n\tasync add(\n\t\tname: string,\n\t\tdata: unknown,\n\t\toptions: AddOptions = {},\n\t): Promise<AddedJob> {\n\t\tconst jobId = `mem-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n\t\tconst delayMs = (options.delaySeconds ?? 0) * 1000;\n\t\tthis.#queue.push({\n\t\t\tjobId,\n\t\t\tname,\n\t\t\tdata,\n\t\t\toptions,\n\t\t\tresolveAt: Date.now() + delayMs,\n\t\t});\n\t\tthis.#emit({ kind: \"job:added\", jobId, name });\n\t\treturn { jobId, name };\n\t}\n\n\tasync addBatch(\n\t\tjobs: Array<{ name: string; data: unknown; options?: AddOptions }>,\n\t): Promise<AddedJob[]> {\n\t\treturn Promise.all(jobs.map((j) => this.add(j.name, j.data, j.options)));\n\t}\n\n\t// ===========================================================================\n\t// Worker\n\t// ===========================================================================\n\n\tasync process<T>(\n\t\tname: string,\n\t\thandler: JobHandler<T>,\n\t\toptions: WorkerOptions = {},\n\t): Promise<WorkerHandle> {\n\t\tthis.#handlers.set(name, handler as JobHandler);\n\t\tthis.#workerOptions.set(name, options);\n\t\tconst handle = new MemoryWorkerHandle(name, handler as JobHandler);\n\t\tthis.#emit({\n\t\t\tkind: \"worker:started\",\n\t\t\tname,\n\t\t\tconcurrency: options.concurrency ?? 1,\n\t\t});\n\t\treturn handle;\n\t}\n\n\t// ===========================================================================\n\t// Lifecycle\n\t// ===========================================================================\n\n\tasync drain(): Promise<void> {\n\t\twhile (this.#inFlight > 0 || this.#queue.length > 0) {\n\t\t\tawait new Promise((r) => setTimeout(r, 50));\n\t\t}\n\t}\n\n\tasync stop(): Promise<void> {\n\t\tfor (const name of this.#handlers.keys()) {\n\t\t\tthis.#emit({ kind: \"worker:stopped\", name });\n\t\t}\n\t\tif (this.#tickHandle) clearInterval(this.#tickHandle);\n\t\tthis.#tickHandle = null;\n\t}\n\n\t// ===========================================================================\n\t// Events\n\t// ===========================================================================\n\n\ton(listener: QueueEventListener): () => void {\n\t\tthis.#listeners.add(listener);\n\t\treturn () => this.#listeners.delete(listener);\n\t}\n\n\t// ===========================================================================\n\t// Internal\n\t// ===========================================================================\n\n\tasync #tick() {\n\t\tif (this.#queue.length === 0) return;\n\t\tconst now = Date.now();\n\t\tconst due = this.#queue.filter((j) => j.resolveAt <= now);\n\t\tfor (const job of due) {\n\t\t\tthis.#queue = this.#queue.filter((j) => j !== job);\n\t\t\tconst handler = this.#handlers.get(job.name);\n\t\t\tif (!handler) continue;\n\t\t\tconst options = this.#workerOptions.get(job.name) ?? {};\n\t\t\tconst concurrency = options.concurrency ?? 1;\n\t\t\tif (this.#inFlight >= concurrency) {\n\t\t\t\t// Re-queue at the front.\n\t\t\t\tthis.#queue.unshift(job);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvoid this.#runJob(job, handler, options);\n\t\t}\n\t}\n\n\tasync #runJob(job: PendingJob, handler: JobHandler, options: WorkerOptions) {\n\t\tthis.#inFlight++;\n\t\tconst ctx: JobContext = {\n\t\t\tjobId: job.jobId,\n\t\t\tattempts: 1,\n\t\t\tjob: { name: job.name, data: job.data },\n\t\t\tprefix: `[queue:${job.name}]`,\n\t\t};\n\t\tthis.#emit({\n\t\t\tkind: \"job:active\",\n\t\t\tjobId: job.jobId,\n\t\t\tname: job.name,\n\t\t\tattempts: 1,\n\t\t});\n\t\ttry {\n\t\t\tconst result = await handler(job.data, ctx);\n\t\t\tif (result && typeof result === \"object\" && \"status\" in result) {\n\t\t\t\tconst r = result as {\n\t\t\t\t\tstatus: string;\n\t\t\t\t\treturnvalue?: unknown;\n\t\t\t\t\terror?: Error;\n\t\t\t\t};\n\t\t\t\tif (r.status === \"failed\") {\n\t\t\t\t\tthis.#emit({\n\t\t\t\t\t\tkind: \"job:failed\",\n\t\t\t\t\t\tjobId: job.jobId,\n\t\t\t\t\t\tname: job.name,\n\t\t\t\t\t\terror: r.error ?? new Error(\"unknown\"),\n\t\t\t\t\t\twillRetry: false,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tthis.#emit({\n\t\t\t\t\t\tkind: \"job:completed\",\n\t\t\t\t\t\tjobId: job.jobId,\n\t\t\t\t\t\tname: job.name,\n\t\t\t\t\t\treturnvalue: r.returnvalue,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.#emit({\n\t\t\t\t\tkind: \"job:completed\",\n\t\t\t\t\tjobId: job.jobId,\n\t\t\t\t\tname: job.name,\n\t\t\t\t\treturnvalue: result,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconst error = err instanceof Error ? err : new Error(String(err));\n\t\t\tconst willRetry = (job.options.attempts ?? 1) > 1;\n\t\t\tthis.#emit({\n\t\t\t\tkind: \"job:failed\",\n\t\t\t\tjobId: job.jobId,\n\t\t\t\tname: job.name,\n\t\t\t\terror,\n\t\t\t\twillRetry,\n\t\t\t});\n\t\t\tif (willRetry) {\n\t\t\t\tconst delayMs =\n\t\t\t\t\t(job.options.backoff?.delayMs ?? 1000) *\n\t\t\t\t\t(job.options.backoff?.type === \"exponential\" ? 2 ** ctx.attempts : 1);\n\t\t\t\tthis.#queue.push({\n\t\t\t\t\t...job,\n\t\t\t\t\tresolveAt: Date.now() + delayMs,\n\t\t\t\t});\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.#inFlight--;\n\t\t}\n\t\t// Touch options to silence \"unused\" warnings in strict configs.\n\t\tvoid options;\n\t}\n\n\t#emit(event: QueueEvent) {\n\t\tfor (const l of this.#listeners) {\n\t\t\tvoid Promise.resolve(l(event));\n\t\t}\n\t}\n}\n",
6
+ "/**\n * BullMQ backend — Redis-backed queue for Bun / Node.\n *\n * Wraps `bullmq.Queue` (producer) and `bullmq.Worker` (consumer) with\n * the common `QueueBackend` interface. We use the `Job` wrapper class\n * (vs the lower-level `QueueBase`) so we can read job IDs back as\n * strings without leaking BullMQ types to user code.\n *\n * Usage:\n * const backend = new BullMQBackend({\n * connection: 'redis://localhost:6379',\n * prefix: 'nexusjs',\n * });\n * await backend.process('send-email', async (data) => {\n * // ...\n * });\n * await backend.add('send-email', { to: 'a@b.c' });\n */\n\nimport {\n\tQueue,\n\tWorker,\n\ttype ConnectionOptions,\n\ttype JobsOptions,\n} from \"bullmq\";\nimport IORedis from \"ioredis\";\nimport type {\n\tQueueBackend,\n\tJobHandler,\n\tWorkerHandle,\n\tWorkerOptions,\n\tAddedJob,\n\tAddOptions,\n\tQueueEvent,\n\tQueueEventListener,\n\tJobContext,\n} from \"../types.js\";\n\nexport interface BullMQBackendOptions {\n\tconnection: string | ConnectionOptions;\n\tprefix?: string;\n\tdefaultJobOptions?: AddOptions;\n}\n\nclass BullMQWorkerHandle implements WorkerHandle {\n\t#worker: Worker;\n\t#name: string;\n\tconstructor(name: string, worker: Worker) {\n\t\tthis.#name = name;\n\t\tthis.#worker = worker;\n\t}\n\tget name() {\n\t\treturn this.#name;\n\t}\n\tasync close() {\n\t\tawait this.#worker.close();\n\t}\n\tasync pause() {\n\t\tawait this.#worker.pause();\n\t}\n\tasync resume() {\n\t\tawait this.#worker.resume();\n\t}\n\tisRunning() {\n\t\treturn !this.#worker.closing;\n\t}\n}\n\nexport class BullMQBackend implements QueueBackend {\n\treadonly name = \"bullmq\" as const;\n\t#queue: Queue;\n\t#connection: ConnectionOptions;\n\t#prefix: string;\n\t#defaultJobOptions: AddOptions;\n\t#workers = new Map<string, Worker>();\n\t#listeners = new Set<QueueEventListener>();\n\t#closed = false;\n\n\tconstructor(options: BullMQBackendOptions) {\n\t\tthis.#connection =\n\t\t\ttypeof options.connection === \"string\"\n\t\t\t\t? (new IORedis(options.connection, {\n\t\t\t\t\t\tmaxRetriesPerRequest: null,\n\t\t\t\t\t}) as unknown as ConnectionOptions)\n\t\t\t\t: options.connection;\n\t\tthis.#prefix = options.prefix ?? \"nexusjs\";\n\t\tthis.#defaultJobOptions = options.defaultJobOptions ?? {};\n\n\t\tthis.#queue = new Queue(\"nexus-queue\", {\n\t\t\tconnection: this.#connection,\n\t\t\tprefix: this.#prefix,\n\t\t\tdefaultJobOptions: this.#toBullJobOptions(this.#defaultJobOptions),\n\t\t});\n\t}\n\n\t// ===========================================================================\n\t// Producer\n\t// ===========================================================================\n\n\tasync add(\n\t\tname: string,\n\t\tdata: unknown,\n\t\toptions: AddOptions = {},\n\t): Promise<AddedJob> {\n\t\tconst merged = { ...this.#defaultJobOptions, ...options };\n\t\tconst job = await this.#queue.add(\n\t\t\tname,\n\t\t\tdata,\n\t\t\tthis.#toBullJobOptions(merged),\n\t\t);\n\t\tthis.#emit({ kind: \"job:added\", jobId: String(job.id ?? \"\"), name });\n\t\treturn { jobId: String(job.id ?? \"\"), name, handle: job };\n\t}\n\n\tasync addBatch(\n\t\tjobs: Array<{ name: string; data: unknown; options?: AddOptions }>,\n\t): Promise<AddedJob[]> {\n\t\tconst bullJobs = jobs.map((j) => ({\n\t\t\tname: j.name,\n\t\t\tdata: j.data,\n\t\t\topts: this.#toBullJobOptions({\n\t\t\t\t...this.#defaultJobOptions,\n\t\t\t\t...j.options,\n\t\t\t}),\n\t\t}));\n\t\tconst added = await this.#queue.addBulk(bullJobs);\n\t\tfor (const job of added) {\n\t\t\tthis.#emit({\n\t\t\t\tkind: \"job:added\",\n\t\t\t\tjobId: String(job.id ?? \"\"),\n\t\t\t\tname: job.name,\n\t\t\t});\n\t\t}\n\t\treturn added.map((job) => ({\n\t\t\tjobId: String(job.id ?? \"\"),\n\t\t\tname: job.name,\n\t\t\thandle: job,\n\t\t}));\n\t}\n\n\t// ===========================================================================\n\t// Worker\n\t// ===========================================================================\n\n\tasync process<T = unknown>(\n\t\tname: string,\n\t\thandler: JobHandler<T>,\n\t\toptions: WorkerOptions = {},\n\t): Promise<WorkerHandle> {\n\t\tconst worker = new Worker(\n\t\t\t\"nexus-queue\",\n\t\t\tasync (job) => {\n\t\t\t\tconst ctx: JobContext = {\n\t\t\t\t\tjobId: String(job.id ?? \"\"),\n\t\t\t\t\tattempts: job.attemptsMade + 1,\n\t\t\t\t\tjob: { name: job.name, data: job.data },\n\t\t\t\t\tprefix: `[queue:${job.name}]`,\n\t\t\t\t};\n\t\t\t\tthis.#emit({\n\t\t\t\t\tkind: \"job:active\",\n\t\t\t\t\tjobId: ctx.jobId,\n\t\t\t\t\tname: job.name,\n\t\t\t\t\tattempts: ctx.attempts,\n\t\t\t\t});\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await handler(job.data as T, ctx);\n\t\t\t\t\tif (result && typeof result === \"object\" && \"status\" in result) {\n\t\t\t\t\t\tconst r = result as { status: string; returnvalue?: unknown };\n\t\t\t\t\t\tif (r.status === \"completed\") {\n\t\t\t\t\t\t\tthis.#emit({\n\t\t\t\t\t\t\t\tkind: \"job:completed\",\n\t\t\t\t\t\t\t\tjobId: ctx.jobId,\n\t\t\t\t\t\t\t\tname: job.name,\n\t\t\t\t\t\t\t\treturnvalue: r.returnvalue,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\treturn r.returnvalue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (r.status === \"retry\") {\n\t\t\t\t\t\t\tconst r2 = result as { delaySeconds?: number; reason?: string };\n\t\t\t\t\t\t\tthrow new RetryError(\n\t\t\t\t\t\t\t\tr2.reason ?? \"retry requested\",\n\t\t\t\t\t\t\t\tr2.delaySeconds,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthis.#emit({\n\t\t\t\t\t\tkind: \"job:completed\",\n\t\t\t\t\t\tjobId: ctx.jobId,\n\t\t\t\t\t\tname: job.name,\n\t\t\t\t\t\treturnvalue: result,\n\t\t\t\t\t});\n\t\t\t\t\treturn result;\n\t\t\t\t} catch (err) {\n\t\t\t\t\tif (err instanceof RetryError) {\n\t\t\t\t\t\t// Force a retry with optional delay.\n\t\t\t\t\t\tawait job.moveToDelayed(\n\t\t\t\t\t\t\tDate.now() + (err.delaySeconds ?? 0) * 1000,\n\t\t\t\t\t\t\tjob.token!,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst error = err instanceof Error ? err : new Error(String(err));\n\t\t\t\t\tconst willRetry = (job.opts.attempts ?? 1) > job.attemptsMade;\n\t\t\t\t\tthis.#emit({\n\t\t\t\t\t\tkind: \"job:failed\",\n\t\t\t\t\t\tjobId: ctx.jobId,\n\t\t\t\t\t\tname: job.name,\n\t\t\t\t\t\terror,\n\t\t\t\t\t\twillRetry,\n\t\t\t\t\t});\n\t\t\t\t\tthrow err; // let BullMQ handle the retry\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tconnection: this.#connection,\n\t\t\t\tprefix: this.#prefix,\n\t\t\t\tconcurrency: options.concurrency ?? 1,\n\t\t\t\tlockDuration: options.lockDurationMs ?? 30000,\n\t\t\t\tlimiter: options.limiter\n\t\t\t\t\t? { max: options.limiter.max, duration: options.limiter.durationMs }\n\t\t\t\t\t: undefined,\n\t\t\t},\n\t\t);\n\n\t\tthis.#workers.set(name, worker);\n\t\tthis.#emit({\n\t\t\tkind: \"worker:started\",\n\t\t\tname,\n\t\t\tconcurrency: options.concurrency ?? 1,\n\t\t});\n\t\treturn new BullMQWorkerHandle(name, worker);\n\t}\n\n\t// ===========================================================================\n\t// Lifecycle\n\t// ===========================================================================\n\n\tasync drain(): Promise<void> {\n\t\t// Wait for active jobs on every worker to reach zero.\n\t\tconst workers = [...this.#workers.values()];\n\t\tawait Promise.all(workers.map((w) => w.waitUntilReady()));\n\t\twhile (\n\t\t\tworkers.some(\n\t\t\t\t(w) => (w as unknown as { _job?: unknown })._job !== undefined,\n\t\t\t)\n\t\t) {\n\t\t\tawait new Promise((r) => setTimeout(r, 50));\n\t\t}\n\t}\n\n\tasync stop(): Promise<void> {\n\t\tif (this.#closed) return;\n\t\tthis.#closed = true;\n\t\tfor (const [name, worker] of this.#workers) {\n\t\t\tawait worker.close();\n\t\t\tthis.#emit({ kind: \"worker:stopped\", name });\n\t\t}\n\t\tawait this.#queue.close();\n\t\t// If we own the connection, close it.\n\t\ttry {\n\t\t\tconst conn = this.#connection as { quit?: () => Promise<unknown> };\n\t\t\tif (typeof conn?.quit === \"function\") await conn.quit();\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\t// ===========================================================================\n\t// Events\n\t// ===========================================================================\n\n\ton(listener: QueueEventListener): () => void {\n\t\tthis.#listeners.add(listener);\n\t\treturn () => this.#listeners.delete(listener);\n\t}\n\n\t// ===========================================================================\n\t// Internal\n\t// ===========================================================================\n\n\t#toBullJobOptions(opts: AddOptions): JobsOptions {\n\t\tconst out: JobsOptions = {};\n\t\tif (opts.delaySeconds !== undefined) out.delay = opts.delaySeconds * 1000;\n\t\tif (opts.attempts !== undefined) out.attempts = opts.attempts;\n\t\tif (opts.backoff)\n\t\t\tout.backoff = { type: opts.backoff.type, delay: opts.backoff.delayMs };\n\t\tif (opts.priority !== undefined) out.priority = opts.priority;\n\t\tif (opts.jobId !== undefined) out.jobId = opts.jobId;\n\t\tif (opts.removeOnComplete !== undefined)\n\t\t\tout.removeOnComplete = opts.removeOnComplete;\n\t\tif (opts.removeOnFail !== undefined) out.removeOnFail = opts.removeOnFail;\n\t\treturn out;\n\t}\n\n\t#emit(event: QueueEvent) {\n\t\tfor (const l of this.#listeners) {\n\t\t\tvoid Promise.resolve(l(event));\n\t\t}\n\t}\n}\n\n/** Internal marker error to ask BullMQ to retry with optional delay. */\nclass RetryError extends Error {\n\treadonly __retry = true;\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly delaySeconds?: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"RetryError\";\n\t}\n}\n",
7
+ "/**\n * Cloudflare Queues backend — Workers-native, edge-friendly.\n *\n * Cloudflare Queues has a different shape from BullMQ:\n * - The producer calls `queue.send(body)` / `queue.sendBatch(...)`.\n * - The consumer is a Worker's `queue()` handler that receives a\n * `MessageBatch`.\n *\n * We adapt the two halves to our common `QueueBackend` interface:\n * - `add(name, data)` calls `queue.send({ name, data })` so the\n * consumer knows which handler to route to.\n * - `process(name, handler)` registers the handler in a local\n * `Map`. When the consumer's `queue()` callback fires, we\n * dispatch each message to the matching handler.\n *\n * Because Workers can't start a long-running process, `process()` is\n * a no-op on the producer side. The actual `queue()` handler must be\n * registered separately by the user (or by `QueueModule`'s\n * `workerHandler(env, ctx, batch)` export).\n */\n\nimport type {\n\tQueueBackend,\n\tJobHandler,\n\tWorkerHandle,\n\tWorkerOptions,\n\tAddedJob,\n\tAddOptions,\n\tQueueEvent,\n\tQueueEventListener,\n\tJobContext,\n} from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Cloudflare type stubs (mirrored from @cloudflare/workers-types).\n// We don't import the package to keep the queue module light.\n// ---------------------------------------------------------------------------\n\ninterface CFQueue<Body = unknown> {\n\tsend(\n\t\tbody: Body,\n\t\toptions?: { contentType?: string; delaySeconds?: number },\n\t): Promise<unknown>;\n\tsendBatch(\n\t\tmessages: Array<{\n\t\t\tbody: unknown;\n\t\t\tcontentType?: string;\n\t\t\tdelaySeconds?: number;\n\t\t}>,\n\t\toptions?: { delaySeconds?: number },\n\t): Promise<unknown>;\n}\n\ninterface CFMessage<Body = unknown> {\n\treadonly id: string;\n\treadonly timestamp: Date;\n\treadonly body: Body;\n\treadonly attempts: number;\n\tack(): void;\n\tretry(options?: { delaySeconds?: number }): void;\n}\n\ninterface CFMessageBatch<Body = unknown> {\n\treadonly queue: string;\n\treadonly messages: readonly CFMessage<Body>[];\n\tackAll(): void;\n\tretryAll(options?: { delaySeconds?: number }): void;\n}\n\n// ---------------------------------------------------------------------------\n// Worker handle\n// ---------------------------------------------------------------------------\n\nclass CloudflareWorkerHandle implements WorkerHandle {\n\t#name: string;\n\t#running = true;\n\tconstructor(name: string) {\n\t\tthis.#name = name;\n\t}\n\tget name() {\n\t\treturn this.#name;\n\t}\n\tasync close() {\n\t\tthis.#running = false;\n\t}\n\tasync pause() {\n\t\tthis.#running = false;\n\t}\n\tasync resume() {\n\t\tthis.#running = true;\n\t}\n\tisRunning() {\n\t\treturn this.#running;\n\t}\n}\n\nexport interface CloudflareBackendOptions {\n\t/** Resolver that pulls the Queue binding from the Worker's env. */\n\tresolveBinding: (env: Record<string, unknown>) => CFQueue;\n\t/** Queue name (for diagnostics + MessageBatch.queue matching). */\n\tname?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Backend\n// ---------------------------------------------------------------------------\n\nexport class CloudflareQueueBackend implements QueueBackend {\n\treadonly name = \"cloudflare\" as const;\n\t#queue: CFQueue | null = null;\n\t#resolveBinding: (env: Record<string, unknown>) => CFQueue;\n\t#handlers = new Map<string, JobHandler>();\n\t#workerOptions = new Map<string, WorkerOptions>();\n\t#listeners = new Set<QueueEventListener>();\n\t#queueName: string;\n\n\tconstructor(options: CloudflareBackendOptions) {\n\t\tthis.#resolveBinding = options.resolveBinding;\n\t\tthis.#queueName = options.name ?? \"queue\";\n\t}\n\n\t/** Bind to the Worker's `env` once it's available. */\n\tbind(env: Record<string, unknown>): void {\n\t\tthis.#queue = this.#resolveBinding(env);\n\t}\n\n\t// ===========================================================================\n\t// Producer\n\t// ===========================================================================\n\n\tasync add(\n\t\tname: string,\n\t\tdata: unknown,\n\t\toptions: AddOptions = {},\n\t): Promise<AddedJob> {\n\t\tif (!this.#queue)\n\t\t\tthrow new Error(\"[queue/cloudflare] bind() must be called before add()\");\n\t\tconst id = `cf-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n\t\tawait this.#queue.send(\n\t\t\t{ name, data, jobId: id, options },\n\t\t\t{ delaySeconds: options.delaySeconds },\n\t\t);\n\t\tthis.#emit({ kind: \"job:added\", jobId: id, name });\n\t\treturn { jobId: id, name };\n\t}\n\n\tasync addBatch(\n\t\tjobs: Array<{ name: string; data: unknown; options?: AddOptions }>,\n\t): Promise<AddedJob[]> {\n\t\tif (!this.#queue)\n\t\t\tthrow new Error(\n\t\t\t\t\"[queue/cloudflare] bind() must be called before addBatch()\",\n\t\t\t);\n\t\tconst ids = jobs.map(\n\t\t\t() => `cf-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,\n\t\t);\n\t\tawait this.#queue.sendBatch(\n\t\t\tjobs.map((j, i) => ({\n\t\t\t\tbody: { name: j.name, data: j.data, jobId: ids[i], options: j.options },\n\t\t\t\tdelaySeconds: j.options?.delaySeconds,\n\t\t\t})),\n\t\t);\n\t\tfor (let i = 0; i < jobs.length; i++) {\n\t\t\tconst j = jobs[i]!;\n\t\t\tthis.#emit({ kind: \"job:added\", jobId: ids[i]!, name: j.name });\n\t\t}\n\t\treturn jobs.map((j, i) => ({ jobId: ids[i]!, name: j.name }));\n\t}\n\n\t// ===========================================================================\n\t// Worker registration\n\t// ===========================================================================\n\n\tasync process<T = unknown>(\n\t\tname: string,\n\t\thandler: JobHandler<T>,\n\t\toptions: WorkerOptions = {},\n\t): Promise<WorkerHandle> {\n\t\tthis.#handlers.set(name, handler as JobHandler);\n\t\tthis.#workerOptions.set(name, options);\n\t\tthis.#emit({\n\t\t\tkind: \"worker:started\",\n\t\t\tname,\n\t\t\tconcurrency: options.concurrency ?? 1,\n\t\t});\n\t\treturn new CloudflareWorkerHandle(name);\n\t}\n\n\t// ===========================================================================\n\t// Cloudflare consumer entry point\n\t// ===========================================================================\n\n\t/**\n\t * Build the Worker's `queue()` handler. Mount it as\n\t * `export default { queue: backend.consumerHandler() }` in the\n\t * Worker entry file.\n\t */\n\tconsumerHandler(): (batch: CFMessageBatch<unknown>) => Promise<void> {\n\t\treturn async (batch: CFMessageBatch<unknown>) => {\n\t\t\tfor (const message of batch.messages) {\n\t\t\t\tconst body = (message.body ?? {}) as {\n\t\t\t\t\tname?: string;\n\t\t\t\t\tdata?: unknown;\n\t\t\t\t\tjobId?: string;\n\t\t\t\t\toptions?: AddOptions;\n\t\t\t\t};\n\t\t\t\tconst jobName = body.name ?? \"\";\n\t\t\t\tconst handler = this.#handlers.get(jobName);\n\t\t\t\tif (!handler) {\n\t\t\t\t\t// No handler registered — fail so the message is retried.\n\t\t\t\t\tmessage.retry();\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst ctx: JobContext = {\n\t\t\t\t\tjobId: body.jobId ?? message.id,\n\t\t\t\t\tattempts: message.attempts,\n\t\t\t\t\tjob: { name: jobName, data: body.data },\n\t\t\t\t\tprefix: `[queue:${jobName}]`,\n\t\t\t\t};\n\t\t\t\tthis.#emit({\n\t\t\t\t\tkind: \"job:active\",\n\t\t\t\t\tjobId: ctx.jobId,\n\t\t\t\t\tname: jobName,\n\t\t\t\t\tattempts: ctx.attempts,\n\t\t\t\t});\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await handler(body.data, ctx);\n\t\t\t\t\tif (result && typeof result === \"object\" && \"status\" in result) {\n\t\t\t\t\t\tconst r = result as {\n\t\t\t\t\t\t\tstatus: string;\n\t\t\t\t\t\t\treturnvalue?: unknown;\n\t\t\t\t\t\t\terror?: Error;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tif (r.status === \"failed\") {\n\t\t\t\t\t\t\tthis.#emit({\n\t\t\t\t\t\t\t\tkind: \"job:failed\",\n\t\t\t\t\t\t\t\tjobId: ctx.jobId,\n\t\t\t\t\t\t\t\tname: jobName,\n\t\t\t\t\t\t\t\terror: r.error ?? new Error(\"failed\"),\n\t\t\t\t\t\t\t\twillRetry: true,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tmessage.retry();\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (r.status === \"retry\") {\n\t\t\t\t\t\t\tconst r2 = result as { delaySeconds?: number };\n\t\t\t\t\t\t\tmessage.retry({ delaySeconds: r2.delaySeconds });\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.#emit({\n\t\t\t\t\t\t\tkind: \"job:completed\",\n\t\t\t\t\t\t\tjobId: ctx.jobId,\n\t\t\t\t\t\t\tname: jobName,\n\t\t\t\t\t\t\treturnvalue: r.returnvalue,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.#emit({\n\t\t\t\t\t\t\tkind: \"job:completed\",\n\t\t\t\t\t\t\tjobId: ctx.jobId,\n\t\t\t\t\t\t\tname: jobName,\n\t\t\t\t\t\t\treturnvalue: result,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst error = err instanceof Error ? err : new Error(String(err));\n\t\t\t\t\tconst willRetry = (body.options?.attempts ?? 1) > message.attempts;\n\t\t\t\t\tthis.#emit({\n\t\t\t\t\t\tkind: \"job:failed\",\n\t\t\t\t\t\tjobId: ctx.jobId,\n\t\t\t\t\t\tname: jobName,\n\t\t\t\t\t\terror,\n\t\t\t\t\t\twillRetry,\n\t\t\t\t\t});\n\t\t\t\t\tif (willRetry) message.retry();\n\t\t\t\t\telse message.ack();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n\n\t// ===========================================================================\n\t// Lifecycle\n\t// ===========================================================================\n\n\tasync drain(): Promise<void> {\n\t\t// Cloudflare handles draining at the platform level (between\n\t\t// requests, the isolate is torn down). Nothing to do here.\n\t}\n\n\tasync stop(): Promise<void> {\n\t\tfor (const name of this.#handlers.keys()) {\n\t\t\tthis.#emit({ kind: \"worker:stopped\", name });\n\t\t}\n\t\tthis.#handlers.clear();\n\t\tthis.#workerOptions.clear();\n\t}\n\n\t// ===========================================================================\n\t// Events\n\t// ===========================================================================\n\n\ton(listener: QueueEventListener): () => void {\n\t\tthis.#listeners.add(listener);\n\t\treturn () => this.#listeners.delete(listener);\n\t}\n\n\t// ===========================================================================\n\t// Internal\n\t// ===========================================================================\n\n\t#emit(event: QueueEvent) {\n\t\tfor (const l of this.#listeners) {\n\t\t\tvoid Promise.resolve(l(event));\n\t\t}\n\t}\n\n\tget queueName() {\n\t\treturn this.#queueName;\n\t}\n}\n",
8
+ "/**\n * `QueueService` — DI-friendly facade over a `QueueBackend`.\n *\n * Controllers and other services inject this via `@Inject(QueueService.TOKEN)`\n * (or just `@Inject(QueueService)`) and call high-level methods without\n * caring whether the underlying backend is BullMQ, Cloudflare Queues,\n * or the in-memory test backend.\n *\n * Two layers:\n * - `QueueService.add(name, data, options?)` → schedule a job\n * - `QueueService.process(name, handler)` → register a worker\n *\n * The lifecycle is owned by `QueueService.start()` (called by\n * `QueueModule.forRoot` when the application boots) and\n * `QueueService.stop()` (called on shutdown).\n */\n\nimport { Inject, Injectable } from '@nexusts/core/src/decorators/index.js';\nimport type {\n\tQueueBackend,\n\tQueueConfig,\n\tJobHandler,\n\tWorkerHandle,\n\tAddedJob,\n\tAddOptions,\n\tQueueEvent,\n\tQueueEventListener,\n} from './types.js';\nimport {\n\tMemoryQueueBackend,\n\tBullMQBackend,\n\tCloudflareQueueBackend,\n} from './backends/index.js';\n\n@Injectable()\nexport class QueueService {\n\t/** DI token — use with `@Inject(QueueService.TOKEN)`. */\n\tstatic readonly TOKEN = Symbol.for('nexus:QueueService');\n\n\t/** The underlying backend. */\n\treadonly backend: QueueBackend;\n\t#workers = new Map<string, WorkerHandle>();\n\t#listeners = new Set<QueueEventListener>();\n\t#started = false;\n\n\tconstructor(@Inject('QUEUE_CONFIG') private readonly config: QueueConfig) {\n\t\tthis.backend = this.#createBackend(config);\n\t}\n\n\t// ===========================================================================\n\t// Producer API\n\t// ===========================================================================\n\n\t/**\n\t * Enqueue a job. Returns immediately (the backend may still be\n\t * persisting). On Cloudflare, returns once the message is on disk.\n\t */\n\tasync add<T = unknown>(\n\t\tname: string,\n\t\tdata: T,\n\t\toptions: AddOptions = {},\n\t): Promise<AddedJob> {\n\t\tconst merged = { ...this.config.defaults, ...options };\n\t\treturn this.backend.add(name, data, merged);\n\t}\n\n\t/** Enqueue many jobs at once (atomic on Cloudflare; batched on BullMQ). */\n\tasync addBatch<T = unknown>(\n\t\tjobs: Array<{ name: string; data: T; options?: AddOptions }>,\n\t): Promise<AddedJob[]> {\n\t\treturn this.backend.addBatch(jobs);\n\t}\n\n\t// ===========================================================================\n\t// Worker API\n\t// ===========================================================================\n\n\t/**\n\t * Register a worker for the given job name. Call this from\n\t * `Application.bootstrap` (or a feature module's `onInit`) — the\n\t * module wires it via DI so user code doesn't have to call this\n\t * manually.\n\t */\n\tasync process<T = unknown>(\n\t\tname: string,\n\t\thandler: JobHandler<T>,\n\t): Promise<WorkerHandle> {\n\t\tconst handle = await this.backend.process(name, handler);\n\t\tthis.#workers.set(name, handle);\n\t\treturn handle;\n\t}\n\n\t// ===========================================================================\n\t// Events\n\t// ===========================================================================\n\n\ton(listener: QueueEventListener): () => void {\n\t\tthis.#listeners.add(listener);\n\t\treturn () => this.#listeners.delete(listener);\n\t}\n\n\t// ===========================================================================\n\t// Lifecycle\n\t// ===========================================================================\n\n\t/** Called by `QueueModule.forRoot` when the app boots. */\n\tasync start(): Promise<void> {\n\t\tif (this.#started) return;\n\t\tthis.#started = true;\n\t\t// Bridge backend events through our listener set.\n\t\tthis.backend.on((event) => this.#broadcast(event));\n\t}\n\n\t/** Drain in-flight jobs and close all workers. */\n\tasync stop(): Promise<void> {\n\t\tif (!this.#started) return;\n\t\tthis.#started = false;\n\t\tawait this.backend.drain();\n\t\tfor (const handle of this.#workers.values()) {\n\t\t\tawait handle.close();\n\t\t}\n\t\tawait this.backend.stop();\n\t\tthis.#workers.clear();\n\t}\n\n\t// ===========================================================================\n\t// Cloudflare binding helper\n\t// ===========================================================================\n\n\t/**\n\t * For Cloudflare backends, call this once with the Worker's env so\n\t * the producer can find the Queue binding.\n\t *\n\t * const service = app.container.resolve(QueueService.TOKEN);\n\t * if (service.backend.name === 'cloudflare') {\n\t * (service.backend as CloudflareQueueBackend).bind(env);\n\t * }\n\t */\n\tgetCloudflareBackend(): CloudflareQueueBackend | null {\n\t\treturn this.backend instanceof CloudflareQueueBackend ? this.backend : null;\n\t}\n\n\t// ===========================================================================\n\t// Internal\n\t// ===========================================================================\n\n\t#createBackend(config: QueueConfig): QueueBackend {\n\t\tswitch (config.backend) {\n\t\t\tcase 'memory':\n\t\t\t\treturn new MemoryQueueBackend();\n\t\t\tcase 'bullmq': {\n\t\t\t\tif (!config.bullmq) {\n\t\t\t\t\tthrow new Error('[queue] backend=bullmq requires `bullmq.connection` in config.');\n\t\t\t\t}\n\t\t\t\treturn new BullMQBackend({\n\t\t\t\t\tconnection: config.bullmq.connection,\n\t\t\t\t\tprefix: config.bullmq.prefix,\n\t\t\t\t\tdefaultJobOptions: config.bullmq.defaultJobOptions,\n\t\t\t\t});\n\t\t\t}\n\t\t\tcase 'cloudflare': {\n\t\t\t\tif (!config.cloudflare) {\n\t\t\t\t\tthrow new Error('[queue] backend=cloudflare requires `cloudflare.resolveBinding` in config.');\n\t\t\t\t}\n\t\t\t\treturn new CloudflareQueueBackend({\n\t\t\t\t\tresolveBinding: config.cloudflare.resolveBinding as never,\n\t\t\t\t\tname: config.cloudflare.name,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t#broadcast(event: QueueEvent) {\n\t\tfor (const l of this.#listeners) {\n\t\t\tvoid Promise.resolve(l(event));\n\t\t}\n\t}\n}",
9
+ "/**\n * `QueueModule` — drop-in module for adding background jobs to a\n * NexusTS app.\n *\n * Usage:\n * // src/app/app.module.ts\n * @Module({\n * imports: [\n * QueueModule.forRoot({\n * backend: 'bullmq',\n * bullmq: { connection: 'redis://localhost:6379' },\n * }),\n * ],\n * })\n * export class AppModule {}\n *\n * // any controller or service\n * constructor(@Inject(QueueService.TOKEN) private queue: QueueService) {}\n * await this.queue.add('send-email', { to: 'a@b.c' });\n *\n * // any service — register a worker\n * class EmailWorker {\n * constructor(@Inject(QueueService.TOKEN) private queue: QueueService) {}\n * async onInit() {\n * await this.queue.process('send-email', async (data) => {\n * // ... send the email\n * return { status: 'completed' };\n * });\n * }\n * }\n */\n\nimport \"reflect-metadata\";\nimport { Module } from \"@nexusts/core/decorators/module.js\";\nimport { QueueService } from \"./queue.service.js\";\nimport type { QueueConfig } from \"./types.js\";\n\n@Module({\n\tproviders: [\n\t\tQueueService,\n\t\t{ provide: QueueService.TOKEN, useExisting: QueueService },\n\t],\n\texports: [QueueService, QueueService.TOKEN],\n})\nexport class QueueModule {\n\t/**\n\t * Build a configured `QueueModule` class with the given queue config.\n\t *\n\t * The returned class can be `imports`-ed by any other module and\n\t * will provide the `QueueService` (and a `QUEUE_CONFIG` value\n\t * provider) to its container.\n\t */\n\tstatic forRoot(config: QueueConfig) {\n\t\t@Module({\n\t\t\tproviders: [\n\t\t\t\tQueueService,\n\t\t\t\t{ provide: QueueService.TOKEN, useExisting: QueueService },\n\t\t\t\t{ provide: \"QUEUE_CONFIG\", useValue: config },\n\t\t\t],\n\t\t\texports: [QueueService, QueueService.TOKEN],\n\t\t})\n\t\tclass ConfiguredQueueModule {}\n\n\t\tObject.defineProperty(ConfiguredQueueModule, \"name\", {\n\t\t\tvalue: \"ConfiguredQueueModule\",\n\t\t});\n\n\t\treturn ConfiguredQueueModule;\n\t}\n}\n",
10
+ "/**\n * `@OnQueueReady()` — lifecycle hook that runs once when the\n * application has booted and the queue service is ready.\n *\n * Use this to register workers without coupling to the `Application`\n * lifecycle directly.\n *\n * Usage:\n * class EmailWorker {\n * constructor(@Inject(QueueService.TOKEN) private queue: QueueService) {}\n *\n * @OnQueueReady()\n * async register() {\n * await this.queue.process('send-email', this.handle);\n * }\n *\n * handle = async (data: { to: string }) => { ... };\n * }\n *\n * @Module({\n * providers: [EmailWorker],\n * })\n * class WorkerModule {}\n */\n\nimport \"reflect-metadata\";\nimport type { QueueService } from \"../queue.service.js\";\n\n/**\n * Method decorator. The decorated method is invoked once with no\n * arguments after the application has booted. Pair it with\n * `QueueService.start()` — typically called by `Application.bootstrap`.\n */\nexport function OnQueueReady(): MethodDecorator {\n\treturn (target, propertyKey, descriptor) => {\n\t\tif (!descriptor || typeof descriptor.value !== \"function\") {\n\t\t\tthrow new Error(\"@OnQueueReady can only decorate methods.\");\n\t\t}\n\t\t// Register the hook in a per-class metadata slot. The bootstrap\n\t\t// code reads METADATA_KEY.QUEUE_READY_HOOKS and calls each.\n\t\tconst ctor = target.constructor as object;\n\t\tconst hooks: Array<string | symbol> =\n\t\t\t(Reflect.getMetadata(\"nexus:queue:ready-hooks\", ctor) as\n\t\t\t\t| Array<string | symbol>\n\t\t\t\t| undefined) ?? [];\n\t\thooks.push(propertyKey!);\n\t\tReflect.defineMetadata(\"nexus:queue:ready-hooks\", hooks, ctor);\n\t};\n}\n\n/**\n * Get the queue-ready hooks declared on a class.\n */\nexport function getQueueReadyHooks(target: unknown): Array<string | symbol> {\n\tconst ctor =\n\t\t(target as { constructor?: object }).constructor ?? (target as object);\n\treturn (\n\t\t(Reflect.getMetadata(\"nexus:queue:ready-hooks\", ctor) as\n\t\t\t| Array<string | symbol>\n\t\t\t| undefined) ?? []\n\t);\n}\n\n/**\n * Helper — invoke all `@OnQueueReady` hooks on an instance.\n * Pair this with `QueueService.start()` for a complete bootstrap.\n */\nexport async function invokeQueueReadyHooks(instance: object): Promise<void> {\n\tconst hooks = getQueueReadyHooks(instance);\n\tfor (const key of hooks) {\n\t\tconst fn = (instance as Record<string | symbol, unknown>)[key] as\n\t\t\t| ((...args: unknown[]) => Promise<void> | void)\n\t\t\t| undefined;\n\t\tif (typeof fn === \"function\") {\n\t\t\tawait fn.call(instance);\n\t\t}\n\t}\n}\n\n// Re-export for convenience.\nexport type { QueueService };\n"
11
+ ],
12
+ "mappings": "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",
13
+ "debugId": "77CE421E465283D764756E2164756E21",
14
+ "names": []
15
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@nexusts/queue",
3
+ "version": "0.7.0",
4
+ "description": "Background jobs (BullMQ / Cloudflare / memory)",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "bun run ../../build.ts"
21
+ },
22
+ "keywords": [
23
+ "nexusts",
24
+ "framework",
25
+ "bun"
26
+ ],
27
+ "license": "MIT",
28
+ "peerDependencies": {
29
+ "bullmq": "^5.79.0",
30
+ "ioredis": "^5.11.1"
31
+ },
32
+ "peerDependenciesMeta": {
33
+ "bullmq": {
34
+ "optional": true
35
+ },
36
+ "ioredis": {
37
+ "optional": true
38
+ }
39
+ },
40
+ "dependencies": {
41
+ "@nexusts/core": "^0.7.0"
42
+ }
43
+ }