@motiadev/plugin-bullmq 0.14.0-beta.165-516298 → 0.14.0-beta.165-454838

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.
Files changed (42) hide show
  1. package/dist/api.d.ts +4 -0
  2. package/dist/api.d.ts.map +1 -0
  3. package/dist/components/dlq-panel.d.ts +2 -0
  4. package/dist/components/dlq-panel.d.ts.map +1 -0
  5. package/dist/components/job-detail.d.ts +2 -0
  6. package/dist/components/job-detail.d.ts.map +1 -0
  7. package/dist/components/jobs-table.d.ts +2 -0
  8. package/dist/components/jobs-table.d.ts.map +1 -0
  9. package/dist/components/queue-detail.d.ts +2 -0
  10. package/dist/components/queue-detail.d.ts.map +1 -0
  11. package/dist/components/queue-list.d.ts +2 -0
  12. package/dist/components/queue-list.d.ts.map +1 -0
  13. package/dist/components/queues-page.d.ts +2 -0
  14. package/dist/components/queues-page.d.ts.map +1 -0
  15. package/dist/hooks/use-jobs-mutations.d.ts +23 -0
  16. package/dist/hooks/use-jobs-mutations.d.ts.map +1 -0
  17. package/dist/hooks/use-jobs-query.d.ts +5 -0
  18. package/dist/hooks/use-jobs-query.d.ts.map +1 -0
  19. package/dist/hooks/use-queues.d.ts +11 -0
  20. package/dist/hooks/use-queues.d.ts.map +1 -0
  21. package/dist/index.cjs +98 -0
  22. package/dist/index.d.ts +2 -62
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +4509 -2896
  25. package/dist/plugin-bullmq.css +1 -0
  26. package/dist/plugin.cjs +1 -0
  27. package/dist/plugin.d.ts +2 -6
  28. package/dist/plugin.d.ts.map +1 -1
  29. package/dist/plugin.js +384 -630
  30. package/dist/providers/query-provider.d.ts +7 -0
  31. package/dist/providers/query-provider.d.ts.map +1 -0
  32. package/dist/stores/use-bullmq-store.d.ts +22 -0
  33. package/dist/stores/use-bullmq-store.d.ts.map +1 -0
  34. package/dist/streams/queues-stream.d.ts +33 -0
  35. package/dist/streams/queues-stream.d.ts.map +1 -0
  36. package/dist/types/queue.d.ts +54 -0
  37. package/dist/types/queue.d.ts.map +1 -0
  38. package/package.json +24 -17
  39. package/dist/index.css +0 -1161
  40. package/dist/index.css.map +0 -1
  41. package/dist/index.js.map +0 -1
  42. package/dist/plugin.js.map +0 -1
package/dist/plugin.js CHANGED
@@ -1,631 +1,385 @@
1
- import IORedis from "ioredis";
2
- import { StreamAdapter } from "@motiadev/core";
3
- import { Queue, QueueEvents } from "bullmq";
4
-
5
- //#region src/streams/queues-stream.ts
6
- const queues = /* @__PURE__ */ new Map();
7
- const queueEvents = /* @__PURE__ */ new Map();
8
- const getOrCreateQueue = (name, connection, prefix) => {
9
- const existing = queues.get(name);
10
- if (existing) return existing;
11
- const queue = new Queue(name, {
12
- connection,
13
- prefix
14
- });
15
- queues.set(name, queue);
16
- return queue;
17
- };
18
- const discoverQueueNames = async (connection, prefix) => {
19
- const pattern = `${prefix}:*:id`;
20
- const keys = await connection.keys(pattern);
21
- const queueNames = /* @__PURE__ */ new Set();
22
- for (const key of keys) {
23
- const withoutId = key.slice(prefix.length + 1).slice(0, -3);
24
- queueNames.add(withoutId);
25
- }
26
- return queueNames;
27
- };
28
- const getQueueInfo = async (name, connection, prefix, dlqSuffix) => {
29
- const queue = getOrCreateQueue(name, connection, prefix);
30
- const [isPaused, counts] = await Promise.all([queue.isPaused(), queue.getJobCounts()]);
31
- return {
32
- name,
33
- displayName: name,
34
- isPaused,
35
- isDLQ: name.endsWith(dlqSuffix),
36
- stats: {
37
- waiting: counts.waiting || 0,
38
- active: counts.active || 0,
39
- completed: counts.completed || 0,
40
- failed: counts.failed || 0,
41
- delayed: counts.delayed || 0,
42
- paused: counts.paused || 0,
43
- prioritized: counts.prioritized || 0
44
- }
45
- };
46
- };
47
- const DEBOUNCE_MS = 500;
48
- var QueuesStream = class extends StreamAdapter {
49
- constructor(connection, prefix, dlqSuffix) {
50
- super("__motia.bullmq-queues");
51
- this.knownQueues = /* @__PURE__ */ new Set();
52
- this.lastStatsCache = /* @__PURE__ */ new Map();
53
- this.debounceTimers = /* @__PURE__ */ new Map();
54
- this.connection = connection;
55
- this.prefix = prefix;
56
- this.dlqSuffix = dlqSuffix;
57
- }
58
- setUpdateCallback(callback) {
59
- this.onQueueUpdate = callback;
60
- }
61
- async get(_groupId, id) {
62
- try {
63
- const info = await getQueueInfo(id, this.connection, this.prefix, this.dlqSuffix);
64
- return {
65
- ...info,
66
- id: info.name
67
- };
68
- } catch {
69
- return null;
70
- }
71
- }
72
- async set(_groupId, _id, data) {
73
- return data;
74
- }
75
- async delete(_groupId, id) {
76
- this.knownQueues.delete(id);
77
- this.lastStatsCache.delete(id);
78
- return { id };
79
- }
80
- async getGroup(_groupId) {
81
- const queueNames = await discoverQueueNames(this.connection, this.prefix);
82
- const queueInfos = [];
83
- for (const name of queueNames) {
84
- this.knownQueues.add(name);
85
- const info = await getQueueInfo(name, this.connection, this.prefix, this.dlqSuffix);
86
- const streamInfo = {
87
- ...info,
88
- id: info.name
89
- };
90
- queueInfos.push(streamInfo);
91
- this.lastStatsCache.set(name, JSON.stringify({
92
- stats: info.stats,
93
- isPaused: info.isPaused
94
- }));
95
- }
96
- return queueInfos;
97
- }
98
- async refreshQueue(name) {
99
- const info = await getQueueInfo(name, this.connection, this.prefix, this.dlqSuffix);
100
- const streamInfo = {
101
- ...info,
102
- id: info.name
103
- };
104
- this.knownQueues.add(name);
105
- const newStatsKey = JSON.stringify({
106
- stats: info.stats,
107
- isPaused: info.isPaused
108
- });
109
- if (this.lastStatsCache.get(name) !== newStatsKey) {
110
- this.lastStatsCache.set(name, newStatsKey);
111
- this.onQueueUpdate?.(streamInfo);
112
- }
113
- return streamInfo;
114
- }
115
- debouncedRefresh(queueName) {
116
- const existing = this.debounceTimers.get(queueName);
117
- if (existing) clearTimeout(existing);
118
- const timer = setTimeout(() => {
119
- this.debounceTimers.delete(queueName);
120
- this.refreshQueue(queueName);
121
- }, DEBOUNCE_MS);
122
- this.debounceTimers.set(queueName, timer);
123
- }
124
- setupQueueEvents(queueName) {
125
- if (queueEvents.has(queueName)) return;
126
- const events = new QueueEvents(queueName, {
127
- connection: this.connection,
128
- prefix: this.prefix
129
- });
130
- const refreshOnEvent = () => {
131
- this.debouncedRefresh(queueName);
132
- };
133
- events.on("waiting", refreshOnEvent);
134
- events.on("active", refreshOnEvent);
135
- events.on("completed", refreshOnEvent);
136
- events.on("failed", refreshOnEvent);
137
- events.on("delayed", refreshOnEvent);
138
- events.on("removed", refreshOnEvent);
139
- }
140
- async setupAllQueueEvents() {
141
- const queueNames = await discoverQueueNames(this.connection, this.prefix);
142
- for (const name of queueNames) this.setupQueueEvents(name);
143
- }
144
- async closeAllQueueEvents() {
145
- for (const timer of this.debounceTimers.values()) clearTimeout(timer);
146
- this.debounceTimers.clear();
147
- for (const [, events] of queueEvents) await events.close();
148
- queueEvents.clear();
149
- }
150
- getKnownQueues() {
151
- return this.knownQueues;
152
- }
153
- };
154
-
155
- //#endregion
156
- //#region src/api.ts
157
- const discoverQueues = async (connection, prefix, dlqSuffix) => {
158
- const queueNames = await discoverQueueNames(connection, prefix);
159
- const queueInfos = [];
160
- for (const name of queueNames) {
161
- const info = await getQueueInfo(name, connection, prefix, dlqSuffix);
162
- queueInfos.push(info);
163
- }
164
- return queueInfos;
165
- };
166
- const api = ({ registerApi }, prefix, dlqSuffix, connection) => {
167
- registerApi({
168
- method: "GET",
169
- path: "/__motia/bullmq/queues"
170
- }, async () => {
171
- try {
172
- return {
173
- status: 200,
174
- body: { queues: await discoverQueues(connection, prefix, dlqSuffix) }
175
- };
176
- } catch (error) {
177
- return {
178
- status: 500,
179
- body: { error: error instanceof Error ? error.message : "Unknown error" }
180
- };
181
- }
182
- });
183
- registerApi({
184
- method: "GET",
185
- path: "/__motia/bullmq/queues/:name"
186
- }, async (req) => {
187
- try {
188
- const name = req.pathParams.name;
189
- const queue = getOrCreateQueue(name, connection, prefix);
190
- const [isPaused, counts] = await Promise.all([queue.isPaused(), queue.getJobCounts()]);
191
- return {
192
- status: 200,
193
- body: {
194
- name,
195
- displayName: name,
196
- isPaused,
197
- isDLQ: name.endsWith(dlqSuffix),
198
- stats: {
199
- waiting: counts.waiting || 0,
200
- active: counts.active || 0,
201
- completed: counts.completed || 0,
202
- failed: counts.failed || 0,
203
- delayed: counts.delayed || 0,
204
- paused: counts.paused || 0,
205
- prioritized: counts.prioritized || 0
206
- }
207
- }
208
- };
209
- } catch (error) {
210
- return {
211
- status: 500,
212
- body: { error: error instanceof Error ? error.message : "Unknown error" }
213
- };
214
- }
215
- });
216
- registerApi({
217
- method: "POST",
218
- path: "/__motia/bullmq/queues/:name/pause"
219
- }, async (req) => {
220
- try {
221
- const name = req.pathParams.name;
222
- await getOrCreateQueue(name, connection, prefix).pause();
223
- return {
224
- status: 200,
225
- body: { message: "Queue paused" }
226
- };
227
- } catch (error) {
228
- return {
229
- status: 500,
230
- body: { error: error instanceof Error ? error.message : "Unknown error" }
231
- };
232
- }
233
- });
234
- registerApi({
235
- method: "POST",
236
- path: "/__motia/bullmq/queues/:name/resume"
237
- }, async (req) => {
238
- try {
239
- const name = req.pathParams.name;
240
- await getOrCreateQueue(name, connection, prefix).resume();
241
- return {
242
- status: 200,
243
- body: { message: "Queue resumed" }
244
- };
245
- } catch (error) {
246
- return {
247
- status: 500,
248
- body: { error: error instanceof Error ? error.message : "Unknown error" }
249
- };
250
- }
251
- });
252
- registerApi({
253
- method: "POST",
254
- path: "/__motia/bullmq/queues/:name/clean"
255
- }, async (req) => {
256
- try {
257
- const name = req.pathParams.name;
258
- const body = req.body;
259
- const options = {
260
- grace: body.grace ?? 0,
261
- limit: body.limit ?? 1e3,
262
- status: body.status ?? "completed"
263
- };
264
- const deletedIds = await getOrCreateQueue(name, connection, prefix).clean(options.grace, options.limit, options.status);
265
- return {
266
- status: 200,
267
- body: {
268
- deleted: deletedIds.length,
269
- ids: deletedIds
270
- }
271
- };
272
- } catch (error) {
273
- return {
274
- status: 500,
275
- body: { error: error instanceof Error ? error.message : "Unknown error" }
276
- };
277
- }
278
- });
279
- registerApi({
280
- method: "POST",
281
- path: "/__motia/bullmq/queues/:name/drain"
282
- }, async (req) => {
283
- try {
284
- const name = req.pathParams.name;
285
- await getOrCreateQueue(name, connection, prefix).drain();
286
- return {
287
- status: 200,
288
- body: { message: "Queue drained" }
289
- };
290
- } catch (error) {
291
- return {
292
- status: 500,
293
- body: { error: error instanceof Error ? error.message : "Unknown error" }
294
- };
295
- }
296
- });
297
- registerApi({
298
- method: "GET",
299
- path: "/__motia/bullmq/queues/:name/jobs"
300
- }, async (req) => {
301
- try {
302
- const name = req.pathParams.name;
303
- const status = req.queryParams.status || "waiting";
304
- const start = parseInt(req.queryParams.start, 10) || 0;
305
- const end = parseInt(req.queryParams.end, 10) || 100;
306
- return {
307
- status: 200,
308
- body: { jobs: (await getOrCreateQueue(name, connection, prefix).getJobs([status], start, end)).map((job) => ({
309
- id: job.id || "",
310
- name: job.name,
311
- data: job.data,
312
- opts: job.opts,
313
- progress: typeof job.progress === "object" ? JSON.stringify(job.progress) : job.progress,
314
- timestamp: job.timestamp,
315
- attemptsMade: job.attemptsMade,
316
- processedOn: job.processedOn,
317
- finishedOn: job.finishedOn,
318
- returnvalue: job.returnvalue,
319
- failedReason: job.failedReason,
320
- stacktrace: job.stacktrace
321
- })) }
322
- };
323
- } catch (error) {
324
- return {
325
- status: 500,
326
- body: { error: error instanceof Error ? error.message : "Unknown error" }
327
- };
328
- }
329
- });
330
- registerApi({
331
- method: "GET",
332
- path: "/__motia/bullmq/queues/:queueName/jobs/:jobId"
333
- }, async (req) => {
334
- try {
335
- const queueName = req.pathParams.queueName;
336
- const jobId = req.pathParams.jobId;
337
- const job = await getOrCreateQueue(queueName, connection, prefix).getJob(jobId);
338
- if (!job) return {
339
- status: 404,
340
- body: { error: "Job not found" }
341
- };
342
- return {
343
- status: 200,
344
- body: {
345
- id: job.id || "",
346
- name: job.name,
347
- data: job.data,
348
- opts: job.opts,
349
- progress: typeof job.progress === "object" ? JSON.stringify(job.progress) : job.progress,
350
- timestamp: job.timestamp,
351
- attemptsMade: job.attemptsMade,
352
- processedOn: job.processedOn,
353
- finishedOn: job.finishedOn,
354
- returnvalue: job.returnvalue,
355
- failedReason: job.failedReason,
356
- stacktrace: job.stacktrace
357
- }
358
- };
359
- } catch (error) {
360
- return {
361
- status: 500,
362
- body: { error: error instanceof Error ? error.message : "Unknown error" }
363
- };
364
- }
365
- });
366
- registerApi({
367
- method: "POST",
368
- path: "/__motia/bullmq/queues/:queueName/jobs/:jobId/retry"
369
- }, async (req) => {
370
- try {
371
- const queueName = req.pathParams.queueName;
372
- const jobId = req.pathParams.jobId;
373
- const job = await getOrCreateQueue(queueName, connection, prefix).getJob(jobId);
374
- if (!job) return {
375
- status: 404,
376
- body: { error: "Job not found" }
377
- };
378
- await job.retry();
379
- return {
380
- status: 200,
381
- body: { message: "Job retried" }
382
- };
383
- } catch (error) {
384
- return {
385
- status: 500,
386
- body: { error: error instanceof Error ? error.message : "Unknown error" }
387
- };
388
- }
389
- });
390
- registerApi({
391
- method: "POST",
392
- path: "/__motia/bullmq/queues/:queueName/jobs/:jobId/remove"
393
- }, async (req) => {
394
- try {
395
- const queueName = req.pathParams.queueName;
396
- const jobId = req.pathParams.jobId;
397
- const job = await getOrCreateQueue(queueName, connection, prefix).getJob(jobId);
398
- if (!job) return {
399
- status: 404,
400
- body: { error: "Job not found" }
401
- };
402
- await job.remove();
403
- return {
404
- status: 200,
405
- body: { message: "Job removed" }
406
- };
407
- } catch (error) {
408
- return {
409
- status: 500,
410
- body: { error: error instanceof Error ? error.message : "Unknown error" }
411
- };
412
- }
413
- });
414
- registerApi({
415
- method: "POST",
416
- path: "/__motia/bullmq/queues/:queueName/jobs/:jobId/promote"
417
- }, async (req) => {
418
- try {
419
- const queueName = req.pathParams.queueName;
420
- const jobId = req.pathParams.jobId;
421
- const job = await getOrCreateQueue(queueName, connection, prefix).getJob(jobId);
422
- if (!job) return {
423
- status: 404,
424
- body: { error: "Job not found" }
425
- };
426
- await job.promote();
427
- return {
428
- status: 200,
429
- body: { message: "Job promoted" }
430
- };
431
- } catch (error) {
432
- return {
433
- status: 500,
434
- body: { error: error instanceof Error ? error.message : "Unknown error" }
435
- };
436
- }
437
- });
438
- registerApi({
439
- method: "GET",
440
- path: "/__motia/bullmq/dlq/:name/jobs"
441
- }, async (req) => {
442
- try {
443
- const name = req.pathParams.name;
444
- const start = parseInt(req.queryParams.start, 10) || 0;
445
- const end = parseInt(req.queryParams.end, 10) || 100;
446
- return {
447
- status: 200,
448
- body: { jobs: (await getOrCreateQueue(name.endsWith(dlqSuffix) ? name : `${name}${dlqSuffix}`, connection, prefix).getJobs(["waiting", "completed"], start, end)).map((job) => ({
449
- id: job.id || "",
450
- name: job.name,
451
- data: job.data,
452
- timestamp: job.timestamp,
453
- originalEvent: job.data?.originalEvent,
454
- failureReason: job.data?.failureReason || job.failedReason,
455
- failureTimestamp: job.data?.failureTimestamp || job.finishedOn,
456
- attemptsMade: job.data?.attemptsMade || job.attemptsMade
457
- })) }
458
- };
459
- } catch (error) {
460
- return {
461
- status: 500,
462
- body: { error: error instanceof Error ? error.message : "Unknown error" }
463
- };
464
- }
465
- });
466
- registerApi({
467
- method: "POST",
468
- path: "/__motia/bullmq/dlq/:name/retry/:jobId"
469
- }, async (req) => {
470
- try {
471
- const name = req.pathParams.name;
472
- const jobId = req.pathParams.jobId;
473
- const dlqName = name.endsWith(dlqSuffix) ? name : `${name}${dlqSuffix}`;
474
- const job = await getOrCreateQueue(dlqName, connection, prefix).getJob(jobId);
475
- if (!job) return {
476
- status: 404,
477
- body: { error: "Job not found in DLQ" }
478
- };
479
- const originalEvent = job.data?.originalEvent;
480
- if (originalEvent) {
481
- const originalQueue = getOrCreateQueue(dlqName.replace(dlqSuffix, ""), connection, prefix);
482
- const jobData = {
483
- topic: originalEvent.topic,
484
- data: originalEvent.data,
485
- traceId: originalEvent.traceId
486
- };
487
- if (originalEvent.flows) jobData.flows = originalEvent.flows;
488
- if (originalEvent.messageGroupId) jobData.messageGroupId = originalEvent.messageGroupId;
489
- await originalQueue.add(originalEvent.topic || job.name, jobData);
490
- }
491
- await job.remove();
492
- return {
493
- status: 200,
494
- body: { message: "Job retried from DLQ" }
495
- };
496
- } catch (error) {
497
- return {
498
- status: 500,
499
- body: { error: error instanceof Error ? error.message : "Unknown error" }
500
- };
501
- }
502
- });
503
- registerApi({
504
- method: "POST",
505
- path: "/__motia/bullmq/dlq/:name/retry-all"
506
- }, async (req) => {
507
- try {
508
- const name = req.pathParams.name;
509
- const dlqName = name.endsWith(dlqSuffix) ? name : `${name}${dlqSuffix}`;
510
- const jobs = await getOrCreateQueue(dlqName, connection, prefix).getJobs(["waiting", "completed"]);
511
- const originalQueue = getOrCreateQueue(dlqName.replace(dlqSuffix, ""), connection, prefix);
512
- let count = 0;
513
- for (const job of jobs) {
514
- const originalEvent = job.data?.originalEvent;
515
- if (originalEvent) {
516
- const jobData = {
517
- topic: originalEvent.topic,
518
- data: originalEvent.data,
519
- traceId: originalEvent.traceId
520
- };
521
- if (originalEvent.flows) jobData.flows = originalEvent.flows;
522
- if (originalEvent.messageGroupId) jobData.messageGroupId = originalEvent.messageGroupId;
523
- await originalQueue.add(originalEvent.topic || job.name, jobData);
524
- }
525
- await job.remove();
526
- count++;
527
- }
528
- return {
529
- status: 200,
530
- body: {
531
- message: `Retried ${count} jobs from DLQ`,
532
- count
533
- }
534
- };
535
- } catch (error) {
536
- return {
537
- status: 500,
538
- body: { error: error instanceof Error ? error.message : "Unknown error" }
539
- };
540
- }
541
- });
542
- registerApi({
543
- method: "POST",
544
- path: "/__motia/bullmq/dlq/:name/clear"
545
- }, async (req) => {
546
- try {
547
- const name = req.pathParams.name;
548
- await getOrCreateQueue(name.endsWith(dlqSuffix) ? name : `${name}${dlqSuffix}`, connection, prefix).obliterate({ force: true });
549
- return {
550
- status: 200,
551
- body: { message: "DLQ cleared" }
552
- };
553
- } catch (error) {
554
- return {
555
- status: 500,
556
- body: { error: error instanceof Error ? error.message : "Unknown error" }
557
- };
558
- }
559
- });
560
- };
561
-
562
- //#endregion
563
- //#region src/plugin.ts
564
- const STREAM_NAME = "__motia.bullmq-queues";
565
- const isBullMQAdapter = (adapter) => {
566
- return adapter !== null && typeof adapter === "object" && "connection" in adapter && "prefix" in adapter && "dlqSuffix" in adapter;
567
- };
568
- function plugin(motia) {
569
- let connection;
570
- let prefix;
571
- let dlqSuffix;
572
- let ownsConnection = false;
573
- if (isBullMQAdapter(motia.eventAdapter)) {
574
- connection = motia.eventAdapter.connection;
575
- prefix = motia.eventAdapter.prefix;
576
- dlqSuffix = motia.eventAdapter.dlqSuffix;
577
- } else {
578
- const host = process.env.BULLMQ_REDIS_HOST || process.env.REDIS_HOST || "localhost";
579
- const port = parseInt(process.env.BULLMQ_REDIS_PORT || process.env.REDIS_PORT || "6379", 10);
580
- const password = process.env.BULLMQ_REDIS_PASSWORD || process.env.REDIS_PASSWORD || void 0;
581
- prefix = process.env.BULLMQ_PREFIX || "motia:events";
582
- dlqSuffix = process.env.BULLMQ_DLQ_SUFFIX || ".dlq";
583
- connection = new IORedis({
584
- host,
585
- port,
586
- password,
587
- maxRetriesPerRequest: null
588
- });
589
- ownsConnection = true;
590
- }
591
- const queuesStream = new QueuesStream(connection, prefix, dlqSuffix);
592
- const stream = motia.lockedData.createStream({
593
- filePath: `${STREAM_NAME}.ts`,
594
- hidden: true,
595
- config: {
596
- name: STREAM_NAME,
597
- baseConfig: {
598
- storageType: "custom",
599
- factory: () => queuesStream
600
- },
601
- schema: null
602
- }
603
- })();
604
- queuesStream.setUpdateCallback((queueInfo) => {
605
- stream.set("default", queueInfo.id, queueInfo);
606
- });
607
- queuesStream.setupAllQueueEvents().then(() => {
608
- queuesStream.getGroup("default").then((queues$1) => {
609
- for (const queue of queues$1) stream.set("default", queue.id, queue);
610
- });
611
- });
612
- api(motia, prefix, dlqSuffix, connection);
613
- return {
614
- workbench: [{
615
- packageName: "@motiadev/plugin-bullmq",
616
- cssImports: ["@motiadev/plugin-bullmq/dist/index.css"],
617
- label: "Queues",
618
- position: "top",
619
- componentName: "QueuesPage",
620
- labelIcon: "layers"
621
- }],
622
- onShutdown: async () => {
623
- await queuesStream.closeAllQueueEvents();
624
- if (ownsConnection) await connection.quit();
625
- }
626
- };
1
+ import P from "ioredis";
2
+ import { StreamAdapter as I } from "@motiadev/core";
3
+ import { QueueEvents as _, Queue as v } from "bullmq";
4
+ const g = /* @__PURE__ */ new Map(), f = /* @__PURE__ */ new Map(), d = (u, s, a) => {
5
+ const o = g.get(u);
6
+ if (o)
7
+ return o;
8
+ const t = new v(u, { connection: s, prefix: a });
9
+ return g.set(u, t), t;
10
+ }, w = async (u, s) => {
11
+ const a = `${s}:*:id`, o = await u.keys(a), t = /* @__PURE__ */ new Set();
12
+ for (const e of o) {
13
+ const i = e.slice(s.length + 1).slice(0, -3);
14
+ t.add(i);
15
+ }
16
+ return t;
17
+ }, b = async (u, s, a, o) => {
18
+ const t = d(u, s, a), [e, n] = await Promise.all([t.isPaused(), t.getJobCounts()]);
19
+ return {
20
+ name: u,
21
+ displayName: u,
22
+ isPaused: e,
23
+ isDLQ: u.endsWith(o),
24
+ stats: {
25
+ waiting: n.waiting || 0,
26
+ active: n.active || 0,
27
+ completed: n.completed || 0,
28
+ failed: n.failed || 0,
29
+ delayed: n.delayed || 0,
30
+ paused: n.paused || 0,
31
+ prioritized: n.prioritized || 0
32
+ }
33
+ };
34
+ }, Q = 500;
35
+ class E extends I {
36
+ constructor(s, a, o) {
37
+ super("__motia.bullmq-queues"), this.knownQueues = /* @__PURE__ */ new Set(), this.lastStatsCache = /* @__PURE__ */ new Map(), this.debounceTimers = /* @__PURE__ */ new Map(), this.connection = s, this.prefix = a, this.dlqSuffix = o;
38
+ }
39
+ setUpdateCallback(s) {
40
+ this.onQueueUpdate = s;
41
+ }
42
+ async get(s, a) {
43
+ try {
44
+ const o = await b(a, this.connection, this.prefix, this.dlqSuffix);
45
+ return { ...o, id: o.name };
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
50
+ async set(s, a, o) {
51
+ return o;
52
+ }
53
+ async delete(s, a) {
54
+ return this.knownQueues.delete(a), this.lastStatsCache.delete(a), { id: a };
55
+ }
56
+ async getGroup(s) {
57
+ const a = await w(this.connection, this.prefix), o = [];
58
+ for (const t of a) {
59
+ this.knownQueues.add(t);
60
+ const e = await b(t, this.connection, this.prefix, this.dlqSuffix), n = { ...e, id: e.name };
61
+ o.push(n), this.lastStatsCache.set(t, JSON.stringify({ stats: e.stats, isPaused: e.isPaused }));
62
+ }
63
+ return o;
64
+ }
65
+ async refreshQueue(s) {
66
+ const a = await b(s, this.connection, this.prefix, this.dlqSuffix), o = { ...a, id: a.name };
67
+ this.knownQueues.add(s);
68
+ const t = JSON.stringify({ stats: a.stats, isPaused: a.isPaused });
69
+ return this.lastStatsCache.get(s) !== t && (this.lastStatsCache.set(s, t), this.onQueueUpdate?.(o)), o;
70
+ }
71
+ debouncedRefresh(s) {
72
+ const a = this.debounceTimers.get(s);
73
+ a && clearTimeout(a);
74
+ const o = setTimeout(() => {
75
+ this.debounceTimers.delete(s), this.refreshQueue(s);
76
+ }, Q);
77
+ this.debounceTimers.set(s, o);
78
+ }
79
+ setupQueueEvents(s) {
80
+ if (f.has(s))
81
+ return;
82
+ const a = new _(s, {
83
+ connection: this.connection,
84
+ prefix: this.prefix
85
+ }), o = () => {
86
+ this.debouncedRefresh(s);
87
+ };
88
+ a.on("waiting", o), a.on("active", o), a.on("completed", o), a.on("failed", o), a.on("delayed", o), a.on("removed", o);
89
+ }
90
+ async setupAllQueueEvents() {
91
+ const s = await w(this.connection, this.prefix);
92
+ for (const a of s)
93
+ this.setupQueueEvents(a);
94
+ }
95
+ async closeAllQueueEvents() {
96
+ for (const s of this.debounceTimers.values())
97
+ clearTimeout(s);
98
+ this.debounceTimers.clear();
99
+ for (const [, s] of f)
100
+ await s.close();
101
+ f.clear();
102
+ }
103
+ getKnownQueues() {
104
+ return this.knownQueues;
105
+ }
627
106
  }
628
-
629
- //#endregion
630
- export { plugin as default };
631
- //# sourceMappingURL=plugin.js.map
107
+ const S = async (u, s, a) => {
108
+ const o = await w(u, s), t = [];
109
+ for (const e of o) {
110
+ const n = await b(e, u, s, a);
111
+ t.push(n);
112
+ }
113
+ return t;
114
+ }, O = ({ registerApi: u }, s, a, o) => {
115
+ u({ method: "GET", path: "/__motia/bullmq/queues" }, async () => {
116
+ try {
117
+ return { status: 200, body: { queues: await S(o, s, a) } };
118
+ } catch (t) {
119
+ return { status: 500, body: { error: t instanceof Error ? t.message : "Unknown error" } };
120
+ }
121
+ }), u(
122
+ { method: "GET", path: "/__motia/bullmq/queues/:name" },
123
+ async (t) => {
124
+ try {
125
+ const e = t.pathParams.name, n = d(e, o, s), [i, r] = await Promise.all([n.isPaused(), n.getJobCounts()]);
126
+ return {
127
+ status: 200,
128
+ body: {
129
+ name: e,
130
+ displayName: e,
131
+ isPaused: i,
132
+ isDLQ: e.endsWith(a),
133
+ stats: {
134
+ waiting: r.waiting || 0,
135
+ active: r.active || 0,
136
+ completed: r.completed || 0,
137
+ failed: r.failed || 0,
138
+ delayed: r.delayed || 0,
139
+ paused: r.paused || 0,
140
+ prioritized: r.prioritized || 0
141
+ }
142
+ }
143
+ };
144
+ } catch (e) {
145
+ return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
146
+ }
147
+ }
148
+ ), u(
149
+ { method: "POST", path: "/__motia/bullmq/queues/:name/pause" },
150
+ async (t) => {
151
+ try {
152
+ const e = t.pathParams.name;
153
+ return await d(e, o, s).pause(), { status: 200, body: { message: "Queue paused" } };
154
+ } catch (e) {
155
+ return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
156
+ }
157
+ }
158
+ ), u(
159
+ { method: "POST", path: "/__motia/bullmq/queues/:name/resume" },
160
+ async (t) => {
161
+ try {
162
+ const e = t.pathParams.name;
163
+ return await d(e, o, s).resume(), { status: 200, body: { message: "Queue resumed" } };
164
+ } catch (e) {
165
+ return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
166
+ }
167
+ }
168
+ ), u(
169
+ { method: "POST", path: "/__motia/bullmq/queues/:name/clean" },
170
+ async (t) => {
171
+ try {
172
+ const e = t.pathParams.name, n = t.body, i = {
173
+ grace: n.grace ?? 0,
174
+ limit: n.limit ?? 1e3,
175
+ status: n.status ?? "completed"
176
+ }, m = await d(e, o, s).clean(i.grace, i.limit, i.status);
177
+ return { status: 200, body: { deleted: m.length, ids: m } };
178
+ } catch (e) {
179
+ return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
180
+ }
181
+ }
182
+ ), u(
183
+ { method: "POST", path: "/__motia/bullmq/queues/:name/drain" },
184
+ async (t) => {
185
+ try {
186
+ const e = t.pathParams.name;
187
+ return await d(e, o, s).drain(), { status: 200, body: { message: "Queue drained" } };
188
+ } catch (e) {
189
+ return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
190
+ }
191
+ }
192
+ ), u(
193
+ { method: "GET", path: "/__motia/bullmq/queues/:name/jobs" },
194
+ async (t) => {
195
+ try {
196
+ const e = t.pathParams.name, n = t.queryParams.status || "waiting", i = parseInt(t.queryParams.start, 10) || 0, r = parseInt(t.queryParams.end, 10) || 100;
197
+ return { status: 200, body: { jobs: (await d(e, o, s).getJobs([n], i, r)).map((c) => ({
198
+ id: c.id || "",
199
+ name: c.name,
200
+ data: c.data,
201
+ opts: c.opts,
202
+ progress: typeof c.progress == "object" ? JSON.stringify(c.progress) : c.progress,
203
+ timestamp: c.timestamp,
204
+ attemptsMade: c.attemptsMade,
205
+ processedOn: c.processedOn,
206
+ finishedOn: c.finishedOn,
207
+ returnvalue: c.returnvalue,
208
+ failedReason: c.failedReason,
209
+ stacktrace: c.stacktrace
210
+ })) } };
211
+ } catch (e) {
212
+ return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
213
+ }
214
+ }
215
+ ), u(
216
+ { method: "GET", path: "/__motia/bullmq/queues/:queueName/jobs/:jobId" },
217
+ async (t) => {
218
+ try {
219
+ const e = t.pathParams.queueName, n = t.pathParams.jobId, r = await d(e, o, s).getJob(n);
220
+ return r ? { status: 200, body: {
221
+ id: r.id || "",
222
+ name: r.name,
223
+ data: r.data,
224
+ opts: r.opts,
225
+ progress: typeof r.progress == "object" ? JSON.stringify(r.progress) : r.progress,
226
+ timestamp: r.timestamp,
227
+ attemptsMade: r.attemptsMade,
228
+ processedOn: r.processedOn,
229
+ finishedOn: r.finishedOn,
230
+ returnvalue: r.returnvalue,
231
+ failedReason: r.failedReason,
232
+ stacktrace: r.stacktrace
233
+ } } : { status: 404, body: { error: "Job not found" } };
234
+ } catch (e) {
235
+ return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
236
+ }
237
+ }
238
+ ), u(
239
+ { method: "POST", path: "/__motia/bullmq/queues/:queueName/jobs/:jobId/retry" },
240
+ async (t) => {
241
+ try {
242
+ const e = t.pathParams.queueName, n = t.pathParams.jobId, r = await d(e, o, s).getJob(n);
243
+ return r ? (await r.retry(), { status: 200, body: { message: "Job retried" } }) : { status: 404, body: { error: "Job not found" } };
244
+ } catch (e) {
245
+ return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
246
+ }
247
+ }
248
+ ), u(
249
+ { method: "POST", path: "/__motia/bullmq/queues/:queueName/jobs/:jobId/remove" },
250
+ async (t) => {
251
+ try {
252
+ const e = t.pathParams.queueName, n = t.pathParams.jobId, r = await d(e, o, s).getJob(n);
253
+ return r ? (await r.remove(), { status: 200, body: { message: "Job removed" } }) : { status: 404, body: { error: "Job not found" } };
254
+ } catch (e) {
255
+ return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
256
+ }
257
+ }
258
+ ), u(
259
+ { method: "POST", path: "/__motia/bullmq/queues/:queueName/jobs/:jobId/promote" },
260
+ async (t) => {
261
+ try {
262
+ const e = t.pathParams.queueName, n = t.pathParams.jobId, r = await d(e, o, s).getJob(n);
263
+ return r ? (await r.promote(), { status: 200, body: { message: "Job promoted" } }) : { status: 404, body: { error: "Job not found" } };
264
+ } catch (e) {
265
+ return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
266
+ }
267
+ }
268
+ ), u(
269
+ { method: "GET", path: "/__motia/bullmq/dlq/:name/jobs" },
270
+ async (t) => {
271
+ try {
272
+ const e = t.pathParams.name, n = parseInt(t.queryParams.start, 10) || 0, i = parseInt(t.queryParams.end, 10) || 100, r = e.endsWith(a) ? e : `${e}${a}`;
273
+ return { status: 200, body: { jobs: (await d(r, o, s).getJobs(["waiting", "completed"], n, i)).map((c) => ({
274
+ id: c.id || "",
275
+ name: c.name,
276
+ data: c.data,
277
+ timestamp: c.timestamp,
278
+ originalEvent: c.data?.originalEvent,
279
+ failureReason: c.data?.failureReason || c.failedReason,
280
+ failureTimestamp: c.data?.failureTimestamp || c.finishedOn,
281
+ attemptsMade: c.data?.attemptsMade || c.attemptsMade
282
+ })) } };
283
+ } catch (e) {
284
+ return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
285
+ }
286
+ }
287
+ ), u(
288
+ { method: "POST", path: "/__motia/bullmq/dlq/:name/retry/:jobId" },
289
+ async (t) => {
290
+ try {
291
+ const e = t.pathParams.name, n = t.pathParams.jobId, i = e.endsWith(a) ? e : `${e}${a}`, m = await d(i, o, s).getJob(n);
292
+ if (!m)
293
+ return { status: 404, body: { error: "Job not found in DLQ" } };
294
+ const l = m.data?.originalEvent;
295
+ if (l) {
296
+ const p = i.replace(a, ""), c = d(p, o, s), h = {
297
+ topic: l.topic,
298
+ data: l.data,
299
+ traceId: l.traceId
300
+ };
301
+ l.flows && (h.flows = l.flows), l.messageGroupId && (h.messageGroupId = l.messageGroupId), await c.add(l.topic || m.name, h);
302
+ }
303
+ return await m.remove(), { status: 200, body: { message: "Job retried from DLQ" } };
304
+ } catch (e) {
305
+ return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
306
+ }
307
+ }
308
+ ), u(
309
+ { method: "POST", path: "/__motia/bullmq/dlq/:name/retry-all" },
310
+ async (t) => {
311
+ try {
312
+ const e = t.pathParams.name, n = e.endsWith(a) ? e : `${e}${a}`, r = await d(n, o, s).getJobs(["waiting", "completed"]), m = n.replace(a, ""), l = d(m, o, s);
313
+ let p = 0;
314
+ for (const c of r) {
315
+ const h = c.data?.originalEvent;
316
+ if (h) {
317
+ const y = {
318
+ topic: h.topic,
319
+ data: h.data,
320
+ traceId: h.traceId
321
+ };
322
+ h.flows && (y.flows = h.flows), h.messageGroupId && (y.messageGroupId = h.messageGroupId), await l.add(h.topic || c.name, y);
323
+ }
324
+ await c.remove(), p++;
325
+ }
326
+ return { status: 200, body: { message: `Retried ${p} jobs from DLQ`, count: p } };
327
+ } catch (e) {
328
+ return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
329
+ }
330
+ }
331
+ ), u(
332
+ { method: "POST", path: "/__motia/bullmq/dlq/:name/clear" },
333
+ async (t) => {
334
+ try {
335
+ const e = t.pathParams.name, n = e.endsWith(a) ? e : `${e}${a}`;
336
+ return await d(n, o, s).obliterate({ force: !0 }), { status: 200, body: { message: "DLQ cleared" } };
337
+ } catch (e) {
338
+ return { status: 500, body: { error: e instanceof Error ? e.message : "Unknown error" } };
339
+ }
340
+ }
341
+ );
342
+ }, q = "__motia.bullmq-queues", k = (u) => u !== null && typeof u == "object" && "connection" in u && "prefix" in u && "dlqSuffix" in u;
343
+ function U(u) {
344
+ let s, a, o, t = !1;
345
+ if (k(u.eventAdapter))
346
+ s = u.eventAdapter.connection, a = u.eventAdapter.prefix, o = u.eventAdapter.dlqSuffix;
347
+ else {
348
+ const i = process.env.BULLMQ_REDIS_HOST || process.env.REDIS_HOST || "localhost", r = parseInt(process.env.BULLMQ_REDIS_PORT || process.env.REDIS_PORT || "6379", 10), m = process.env.BULLMQ_REDIS_PASSWORD || process.env.REDIS_PASSWORD || void 0;
349
+ a = process.env.BULLMQ_PREFIX || "motia:events", o = process.env.BULLMQ_DLQ_SUFFIX || ".dlq", s = new P({ host: i, port: r, password: m, maxRetriesPerRequest: null }), t = !0;
350
+ }
351
+ const e = new E(s, a, o), n = u.lockedData.createStream({
352
+ filePath: `${q}.ts`,
353
+ hidden: !0,
354
+ config: {
355
+ name: q,
356
+ baseConfig: { storageType: "custom", factory: () => e },
357
+ schema: null
358
+ }
359
+ })();
360
+ return e.setUpdateCallback((i) => {
361
+ n.set("default", i.id, i);
362
+ }), e.setupAllQueueEvents().then(() => {
363
+ e.getGroup("default").then((i) => {
364
+ for (const r of i)
365
+ n.set("default", r.id, r);
366
+ });
367
+ }), O(u, a, o, s), {
368
+ workbench: [
369
+ {
370
+ packageName: "@motiadev/plugin-bullmq",
371
+ cssImports: ["@motiadev/plugin-bullmq/dist/plugin-bullmq.css"],
372
+ label: "Queues",
373
+ position: "top",
374
+ componentName: "QueuesPage",
375
+ labelIcon: "layers"
376
+ }
377
+ ],
378
+ onShutdown: async () => {
379
+ await e.closeAllQueueEvents(), t && await s.quit();
380
+ }
381
+ };
382
+ }
383
+ export {
384
+ U as default
385
+ };