@venizia/ignis-docs 0.0.5 → 0.0.6-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/package.json +1 -1
  2. package/wiki/best-practices/architecture-decisions.md +0 -8
  3. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  4. package/wiki/best-practices/performance-optimization.md +3 -3
  5. package/wiki/best-practices/security-guidelines.md +2 -2
  6. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  7. package/wiki/guides/core-concepts/components-guide.md +1 -1
  8. package/wiki/guides/core-concepts/components.md +2 -2
  9. package/wiki/guides/core-concepts/dependency-injection.md +1 -1
  10. package/wiki/guides/core-concepts/services.md +1 -1
  11. package/wiki/guides/tutorials/building-a-crud-api.md +1 -1
  12. package/wiki/guides/tutorials/ecommerce-api.md +2 -2
  13. package/wiki/guides/tutorials/realtime-chat.md +6 -6
  14. package/wiki/guides/tutorials/testing.md +1 -1
  15. package/wiki/references/base/bootstrapping.md +0 -2
  16. package/wiki/references/base/components.md +2 -2
  17. package/wiki/references/base/controllers.md +0 -1
  18. package/wiki/references/base/datasources.md +1 -1
  19. package/wiki/references/base/dependency-injection.md +1 -1
  20. package/wiki/references/base/filter-system/quick-reference.md +0 -14
  21. package/wiki/references/base/middlewares.md +0 -8
  22. package/wiki/references/base/providers.md +0 -9
  23. package/wiki/references/base/services.md +0 -1
  24. package/wiki/references/components/authentication/api.md +444 -0
  25. package/wiki/references/components/authentication/errors.md +177 -0
  26. package/wiki/references/components/authentication/index.md +571 -0
  27. package/wiki/references/components/authentication/usage.md +781 -0
  28. package/wiki/references/components/health-check.md +292 -103
  29. package/wiki/references/components/index.md +14 -12
  30. package/wiki/references/components/mail/api.md +505 -0
  31. package/wiki/references/components/mail/errors.md +176 -0
  32. package/wiki/references/components/mail/index.md +535 -0
  33. package/wiki/references/components/mail/usage.md +404 -0
  34. package/wiki/references/components/request-tracker.md +229 -25
  35. package/wiki/references/components/socket-io/api.md +1051 -0
  36. package/wiki/references/components/socket-io/errors.md +119 -0
  37. package/wiki/references/components/socket-io/index.md +410 -0
  38. package/wiki/references/components/socket-io/usage.md +322 -0
  39. package/wiki/references/components/static-asset/api.md +261 -0
  40. package/wiki/references/components/static-asset/errors.md +89 -0
  41. package/wiki/references/components/static-asset/index.md +617 -0
  42. package/wiki/references/components/static-asset/usage.md +364 -0
  43. package/wiki/references/components/swagger.md +390 -110
  44. package/wiki/references/components/template/api-page.md +125 -0
  45. package/wiki/references/components/template/errors-page.md +100 -0
  46. package/wiki/references/components/template/index.md +104 -0
  47. package/wiki/references/components/template/setup-page.md +134 -0
  48. package/wiki/references/components/template/single-page.md +132 -0
  49. package/wiki/references/components/template/usage-page.md +127 -0
  50. package/wiki/references/components/websocket/api.md +508 -0
  51. package/wiki/references/components/websocket/errors.md +123 -0
  52. package/wiki/references/components/websocket/index.md +453 -0
  53. package/wiki/references/components/websocket/usage.md +475 -0
  54. package/wiki/references/helpers/cron/index.md +224 -0
  55. package/wiki/references/helpers/crypto/index.md +537 -0
  56. package/wiki/references/helpers/env/index.md +214 -0
  57. package/wiki/references/helpers/error/index.md +232 -0
  58. package/wiki/references/helpers/index.md +16 -15
  59. package/wiki/references/helpers/inversion/index.md +608 -0
  60. package/wiki/references/helpers/logger/index.md +600 -0
  61. package/wiki/references/helpers/network/api.md +986 -0
  62. package/wiki/references/helpers/network/index.md +620 -0
  63. package/wiki/references/helpers/queue/index.md +589 -0
  64. package/wiki/references/helpers/redis/index.md +495 -0
  65. package/wiki/references/helpers/socket-io/api.md +497 -0
  66. package/wiki/references/helpers/socket-io/index.md +513 -0
  67. package/wiki/references/helpers/storage/api.md +705 -0
  68. package/wiki/references/helpers/storage/index.md +583 -0
  69. package/wiki/references/helpers/template/index.md +66 -0
  70. package/wiki/references/helpers/template/single-page.md +126 -0
  71. package/wiki/references/helpers/testing/index.md +510 -0
  72. package/wiki/references/helpers/types/index.md +512 -0
  73. package/wiki/references/helpers/uid/index.md +272 -0
  74. package/wiki/references/helpers/websocket/api.md +736 -0
  75. package/wiki/references/helpers/websocket/index.md +574 -0
  76. package/wiki/references/helpers/worker-thread/index.md +470 -0
  77. package/wiki/references/quick-reference.md +3 -18
  78. package/wiki/references/utilities/jsx.md +1 -8
  79. package/wiki/references/utilities/statuses.md +0 -7
  80. package/wiki/references/components/authentication.md +0 -476
  81. package/wiki/references/components/mail.md +0 -687
  82. package/wiki/references/components/socket-io.md +0 -562
  83. package/wiki/references/components/static-asset.md +0 -1277
  84. package/wiki/references/helpers/cron.md +0 -108
  85. package/wiki/references/helpers/crypto.md +0 -132
  86. package/wiki/references/helpers/env.md +0 -83
  87. package/wiki/references/helpers/error.md +0 -97
  88. package/wiki/references/helpers/inversion.md +0 -176
  89. package/wiki/references/helpers/logger.md +0 -296
  90. package/wiki/references/helpers/network.md +0 -396
  91. package/wiki/references/helpers/queue.md +0 -150
  92. package/wiki/references/helpers/redis.md +0 -142
  93. package/wiki/references/helpers/socket-io.md +0 -932
  94. package/wiki/references/helpers/storage.md +0 -665
  95. package/wiki/references/helpers/testing.md +0 -133
  96. package/wiki/references/helpers/types.md +0 -167
  97. package/wiki/references/helpers/uid.md +0 -167
  98. package/wiki/references/helpers/worker-thread.md +0 -178
@@ -0,0 +1,589 @@
1
+ # Queue
2
+
3
+ Message queuing and asynchronous task management with BullMQ, MQTT, and in-memory solutions.
4
+
5
+ ## Quick Reference
6
+
7
+ | Class | Extends | Peer Dependency | Use Case |
8
+ |-------|---------|-----------------|----------|
9
+ | **BullMQHelper** | `BaseHelper` | `bullmq` (^5.63.1) | Redis-backed job queue -- background processing, task scheduling |
10
+ | **MQTTClientHelper** | `BaseHelper` | `mqtt` (^5.14.1) | MQTT broker messaging -- real-time events, IoT |
11
+ | **QueueHelper** | `BaseHelper` | None | In-memory generator queue -- sequential tasks, single process |
12
+
13
+ #### Common Operations
14
+
15
+ | Helper | Subscribe / Consume | Publish / Produce |
16
+ |--------|---------------------|-------------------|
17
+ | **BullMQ** | Create with `role: 'worker'` | `queue.add(name, data)` via the exposed BullMQ `Queue` instance |
18
+ | **MQTT** | `subscribe({ topics })` | `publish({ topic, message })` |
19
+ | **In-Memory** | `new QueueHelper({ onMessage })` | `enqueue(payload)` |
20
+
21
+ #### Import Paths
22
+
23
+ ```typescript
24
+ // In-memory queue (from base package)
25
+ import { QueueHelper, QueueStatuses } from '@venizia/ignis-helpers';
26
+ import type { TQueueStatus, TQueueElement } from '@venizia/ignis-helpers';
27
+
28
+ // BullMQ (separate export path)
29
+ import { BullMQHelper } from '@venizia/ignis-helpers/bullmq';
30
+ import type { TBullQueueRole } from '@venizia/ignis-helpers/bullmq';
31
+
32
+ // MQTT (separate export path)
33
+ import { MQTTClientHelper } from '@venizia/ignis-helpers/mqtt';
34
+ import type { IMQTTClientOptions } from '@venizia/ignis-helpers/mqtt';
35
+ ```
36
+
37
+ ## Creating an Instance
38
+
39
+ All three queue helpers extend `BaseHelper`, providing scoped logging via `this.logger`.
40
+
41
+ ### BullMQHelper
42
+
43
+ The `BullMQHelper` wraps the BullMQ library for Redis-backed job queuing. It operates in one of two roles: `'queue'` (producer) or `'worker'` (consumer). The role is set at construction time and determines which BullMQ primitives are initialized.
44
+
45
+ ```typescript
46
+ import { DefaultRedisHelper } from '@venizia/ignis-helpers';
47
+ import { BullMQHelper } from '@venizia/ignis-helpers/bullmq';
48
+
49
+ const worker = new BullMQHelper({
50
+ queueName: 'email-queue',
51
+ identifier: 'email-worker',
52
+ role: 'worker',
53
+ redisConnection: redisHelper,
54
+ numberOfWorker: 3,
55
+ lockDuration: 90 * 60 * 1000,
56
+ onWorkerData: async (job) => {
57
+ console.log(`Processing job ${job.id}:`, job.data);
58
+ return { status: 'sent' };
59
+ },
60
+ onWorkerDataCompleted: async (job, result) => {
61
+ console.log(`Job ${job.id} completed:`, result);
62
+ },
63
+ onWorkerDataFail: async (job, error) => {
64
+ console.error(`Job ${job?.id} failed:`, error.message);
65
+ },
66
+ });
67
+ ```
68
+
69
+ #### IBullMQOptions
70
+
71
+ `IBullMQOptions<TQueueElement = any, TQueueResult = any>`
72
+
73
+ | Option | Type | Default | Description |
74
+ |--------|------|---------|-------------|
75
+ | `queueName` | `string` | -- | Name of the BullMQ queue. Must be non-empty. |
76
+ | `identifier` | `string` | -- | Unique identifier used for scoped logging. |
77
+ | `role` | `TBullQueueRole` | -- | `'queue'` (producer) or `'worker'` (consumer). |
78
+ | `redisConnection` | `DefaultRedisHelper` | -- | Redis helper instance. The helper calls `getClient().duplicate()` internally. |
79
+ | `numberOfWorker` | `number` | `1` | Worker concurrency (number of jobs processed in parallel). |
80
+ | `lockDuration` | `number` | `5400000` | Job lock duration in milliseconds (default: 90 minutes). |
81
+ | `onWorkerData` | `(job: Job<TQueueElement, TQueueResult>) => Promise<any>` | `undefined` | Job processing callback. If omitted, the worker logs job details. |
82
+ | `onWorkerDataCompleted` | `(job: Job<TQueueElement, TQueueResult>, result: any) => Promise<void>` | `undefined` | Callback fired when a job completes successfully. |
83
+ | `onWorkerDataFail` | `(job: Job<TQueueElement, TQueueResult> \| undefined, error: Error) => Promise<void>` | `undefined` | Callback fired when a job fails. |
84
+
85
+ > [!IMPORTANT]
86
+ > Pass the `DefaultRedisHelper` instance to `redisConnection`, **not** the raw ioredis client. The helper internally calls `redisConnection.getClient().duplicate()` to create dedicated connections for the queue and worker.
87
+
88
+ ### MQTTClientHelper
89
+
90
+ The `MQTTClientHelper` provides a pub/sub interface to an MQTT broker. The client connects automatically during construction.
91
+
92
+ ```typescript
93
+ import { MQTTClientHelper } from '@venizia/ignis-helpers/mqtt';
94
+
95
+ const mqttClient = new MQTTClientHelper({
96
+ identifier: 'sensor-client',
97
+ url: 'mqtt://localhost:1883',
98
+ options: {
99
+ username: 'user',
100
+ password: 'password',
101
+ },
102
+ onMessage: ({ topic, message }) => {
103
+ console.log(`Received on ${topic}:`, message.toString());
104
+ },
105
+ onConnect: () => {
106
+ console.log('Connected to MQTT broker');
107
+ },
108
+ onDisconnect: () => {
109
+ console.log('Disconnected from MQTT broker');
110
+ },
111
+ onError: (error) => {
112
+ console.error('MQTT error:', error);
113
+ },
114
+ onClose: (error) => {
115
+ if (error) console.error('Connection closed with error:', error);
116
+ },
117
+ });
118
+ ```
119
+
120
+ #### IMQTTClientOptions
121
+
122
+ | Option | Type | Default | Description |
123
+ |--------|------|---------|-------------|
124
+ | `identifier` | `string` | -- | Unique identifier for scoped logging. |
125
+ | `url` | `string` | -- | MQTT broker URL (e.g., `mqtt://localhost:1883`). Must be non-empty. |
126
+ | `options` | `mqtt.IClientOptions` | -- | MQTT.js client options (username, password, keepalive, etc.). |
127
+ | `onMessage` | `(opts: { topic: string; message: Buffer }) => void` | -- | Message handler. Required. |
128
+ | `onConnect` | `() => void` | `undefined` | Callback fired when the client connects to the broker. |
129
+ | `onDisconnect` | `() => void` | `undefined` | Callback fired on disconnection. |
130
+ | `onError` | `(error: Error) => void` | `undefined` | Callback fired on client errors. |
131
+ | `onClose` | `(error?: Error) => void` | `undefined` | Callback fired when the connection is closed. |
132
+
133
+ ### QueueHelper
134
+
135
+ The `QueueHelper` is a generator-based, in-memory queue with a built-in state machine. It processes enqueued items one at a time, making it suitable for sequential task processing within a single process.
136
+
137
+ ```typescript
138
+ import { QueueHelper } from '@venizia/ignis-helpers';
139
+
140
+ const queue = new QueueHelper<string>({
141
+ identifier: 'task-queue',
142
+ autoDispatch: true,
143
+ onMessage: async ({ identifier, queueElement }) => {
144
+ console.log(`[${identifier}] Processing:`, queueElement.payload);
145
+ },
146
+ onDataEnqueue: async ({ identifier, queueElement }) => {
147
+ console.log(`[${identifier}] Enqueued:`, queueElement.payload);
148
+ },
149
+ onDataDequeue: async ({ identifier, queueElement }) => {
150
+ console.log(`[${identifier}] Dequeued:`, queueElement.payload);
151
+ },
152
+ onStateChange: async ({ identifier, from, to }) => {
153
+ console.log(`[${identifier}] State: ${from} -> ${to}`);
154
+ },
155
+ });
156
+ ```
157
+
158
+ #### IQueueCallback
159
+
160
+ `IQueueCallback<TElementPayload>`
161
+
162
+ | Option | Type | Default | Description |
163
+ |--------|------|---------|-------------|
164
+ | `identifier` | `string` | -- | Unique identifier for scoped logging. |
165
+ | `autoDispatch` | `boolean` | `true` | If `true`, automatically triggers processing when an element is enqueued. |
166
+ | `onMessage` | `(opts: { identifier: string; queueElement: TQueueElement<T> }) => ValueOrPromise<void>` | `undefined` | Message processing callback. If omitted, the generator exits immediately. |
167
+ | `onDataEnqueue` | `(opts: { identifier: string; queueElement: TQueueElement<T> }) => ValueOrPromise<void>` | `undefined` | Callback fired after an element is added to the queue. |
168
+ | `onDataDequeue` | `(opts: { identifier: string; queueElement: TQueueElement<T> }) => ValueOrPromise<void>` | `undefined` | Callback fired after an element is removed from the queue. |
169
+ | `onStateChange` | `(opts: { identifier: string; from: TQueueStatus; to: TQueueStatus }) => ValueOrPromise<void>` | `undefined` | Callback fired on every state transition. |
170
+
171
+ #### TQueueElement
172
+
173
+ Each element in the queue is wrapped in a `TQueueElement`:
174
+
175
+ ```typescript
176
+ type TQueueElement<T> = { isLocked: boolean; payload: T };
177
+ ```
178
+
179
+ ## Usage
180
+
181
+ ### BullMQ -- Adding Jobs
182
+
183
+ When created with `role: 'queue'`, the helper exposes a `queue` property (a BullMQ `Queue` instance) for adding jobs.
184
+
185
+ ```typescript
186
+ const producer = new BullMQHelper({
187
+ queueName: 'email-queue',
188
+ identifier: 'email-producer',
189
+ role: 'queue',
190
+ redisConnection: redisHelper,
191
+ });
192
+
193
+ // Add a job via the BullMQ Queue API
194
+ await producer.queue.add('send-welcome', { email: 'user@example.com', template: 'welcome' });
195
+ await producer.queue.add('send-reset', { email: 'user@example.com', token: 'abc123' });
196
+ ```
197
+
198
+ > [!TIP]
199
+ > You can also use the static factory method: `BullMQHelper.newInstance({ ... })` which is equivalent to `new BullMQHelper({ ... })`.
200
+
201
+ #### Default Job Options
202
+
203
+ Jobs are created with these defaults:
204
+
205
+ ```typescript
206
+ defaultJobOptions: {
207
+ removeOnComplete: true,
208
+ removeOnFail: true,
209
+ }
210
+ ```
211
+
212
+ ### BullMQ -- Processing Jobs
213
+
214
+ When created with `role: 'worker'`, the helper initializes a BullMQ `Worker` that listens for jobs on the specified queue.
215
+
216
+ ```typescript
217
+ const consumer = new BullMQHelper<{ email: string }, { status: string }>({
218
+ queueName: 'email-queue',
219
+ identifier: 'email-consumer',
220
+ role: 'worker',
221
+ redisConnection: redisHelper,
222
+ numberOfWorker: 3,
223
+ lockDuration: 10 * 60 * 1000, // 10 minutes
224
+ onWorkerData: async (job) => {
225
+ await sendEmail(job.data.email);
226
+ return { status: 'sent' };
227
+ },
228
+ onWorkerDataCompleted: async (job, result) => {
229
+ console.log(`Job ${job.id} done:`, result);
230
+ },
231
+ onWorkerDataFail: async (job, error) => {
232
+ console.error(`Job ${job?.id} failed:`, error.message);
233
+ },
234
+ });
235
+ ```
236
+
237
+ If `onWorkerData` is not provided, the worker logs the job's `id`, `name`, and `data` at the info level.
238
+
239
+ ### BullMQ -- Redis Cluster
240
+
241
+ When using Redis Cluster with BullMQ, you must set `maxRetriesPerRequest: null` on the cluster config -- this is **required** by BullMQ.
242
+
243
+ ```typescript
244
+ import { Cluster } from 'ioredis';
245
+ import { DefaultRedisHelper } from '@venizia/ignis-helpers';
246
+ import { BullMQHelper } from '@venizia/ignis-helpers/bullmq';
247
+
248
+ const cluster = new Cluster(
249
+ [
250
+ { host: 'node1.redis.example.com', port: 6379 },
251
+ { host: 'node2.redis.example.com', port: 6379 },
252
+ { host: 'node3.redis.example.com', port: 6379 },
253
+ ],
254
+ {
255
+ maxRetriesPerRequest: null, // Required by BullMQ
256
+ enableReadyCheck: true,
257
+ scaleReads: 'slave',
258
+ redisOptions: {
259
+ password: 'your-password',
260
+ tls: {},
261
+ },
262
+ }
263
+ );
264
+
265
+ const redisHelper = new DefaultRedisHelper({
266
+ scope: 'BullMQ',
267
+ identifier: 'cluster-redis',
268
+ client: cluster,
269
+ });
270
+
271
+ const worker = BullMQHelper.newInstance({
272
+ queueName: 'my-queue',
273
+ identifier: 'cluster-worker',
274
+ role: 'worker',
275
+ redisConnection: redisHelper,
276
+ onWorkerData: async (job) => {
277
+ // process job
278
+ },
279
+ });
280
+ ```
281
+
282
+ ### BullMQ -- Graceful Shutdown
283
+
284
+ Call `close()` to gracefully shut down both the worker and queue connections.
285
+
286
+ ```typescript
287
+ await producer.close();
288
+ await consumer.close();
289
+ ```
290
+
291
+ `close()` calls `worker.close()` and `queue.close()` in sequence. If closing fails, it logs the error and re-throws.
292
+
293
+ ### MQTT -- Subscribe and Publish
294
+
295
+ After the client connects to the broker, use `subscribe()` and `publish()` for topic-based messaging.
296
+
297
+ ```typescript
298
+ // Subscribe to multiple topics
299
+ await mqttClient.subscribe({ topics: ['sensors/temperature', 'sensors/humidity'] });
300
+
301
+ // Publish a string message
302
+ await mqttClient.publish({ topic: 'sensors/temperature', message: '23.5' });
303
+
304
+ // Publish a Buffer message
305
+ await mqttClient.publish({ topic: 'sensors/raw', message: Buffer.from([0x01, 0x02]) });
306
+ ```
307
+
308
+ > [!NOTE]
309
+ > Both `subscribe()` and `publish()` reject with an `ApplicationError` (status 400) if the MQTT client is not connected. Ensure the connection is established before calling these methods.
310
+
311
+ ### MQTT -- Event Handling
312
+
313
+ The `MQTTClientHelper` calls `configure()` automatically during construction. Once connected, the `onMessage` callback receives messages for all subscribed topics.
314
+
315
+ ```typescript
316
+ const client = new MQTTClientHelper({
317
+ identifier: 'iot-gateway',
318
+ url: 'mqtt://broker.example.com:1883',
319
+ options: { keepalive: 60 },
320
+ onConnect: () => {
321
+ // Subscribe once connected
322
+ client.subscribe({ topics: ['devices/+/status'] });
323
+ },
324
+ onMessage: ({ topic, message }) => {
325
+ const deviceId = topic.split('/')[1];
326
+ console.log(`Device ${deviceId}:`, message.toString());
327
+ },
328
+ onError: (error) => {
329
+ console.error('Connection error:', error.message);
330
+ },
331
+ onClose: () => {
332
+ console.log('Connection closed');
333
+ },
334
+ });
335
+ ```
336
+
337
+ ### In-Memory Queue -- Enqueueing and Processing
338
+
339
+ With `autoDispatch: true` (default), elements are processed automatically as they are enqueued.
340
+
341
+ ```typescript
342
+ import { QueueHelper } from '@venizia/ignis-helpers';
343
+
344
+ const queue = new QueueHelper<{ task: string; priority: number }>({
345
+ identifier: 'task-processor',
346
+ onMessage: async ({ queueElement }) => {
347
+ console.log('Processing:', queueElement.payload.task);
348
+ await performTask(queueElement.payload);
349
+ },
350
+ });
351
+
352
+ // Elements are processed one at a time, in order
353
+ await queue.enqueue({ task: 'resize-image', priority: 1 });
354
+ await queue.enqueue({ task: 'send-notification', priority: 2 });
355
+ ```
356
+
357
+ #### Manual Dispatch
358
+
359
+ Set `autoDispatch: false` to control when processing begins. Call `nextMessage()` to trigger processing of the next element.
360
+
361
+ ```typescript
362
+ const queue = new QueueHelper<string>({
363
+ identifier: 'manual-queue',
364
+ autoDispatch: false,
365
+ onMessage: async ({ queueElement }) => {
366
+ console.log('Processing:', queueElement.payload);
367
+ },
368
+ });
369
+
370
+ await queue.enqueue('item-1');
371
+ await queue.enqueue('item-2');
372
+
373
+ // Nothing processed yet -- trigger manually
374
+ queue.nextMessage(); // processes 'item-1'
375
+ ```
376
+
377
+ > [!NOTE]
378
+ > `nextMessage()` only triggers processing when the queue state is `WAITING`. It logs a warning and returns if the queue is in any other state.
379
+
380
+ ### In-Memory Queue -- State Machine
381
+
382
+ The `QueueHelper` uses a state machine to manage its lifecycle:
383
+
384
+ ```
385
+ WAITING ──enqueue──> PROCESSING ──done──> WAITING
386
+ | |
387
+ └──lock()──> LOCKED <─┘
388
+ |
389
+ unlock()──> WAITING
390
+ |
391
+ settle()──> SETTLED (terminal)
392
+ ```
393
+
394
+ | State | Value | Description |
395
+ |-------|-------|-------------|
396
+ | `QueueStatuses.WAITING` | `'000_WAITING'` | Idle, ready to process the next element. |
397
+ | `QueueStatuses.PROCESSING` | `'100_PROCESSING'` | Currently handling a message via `onMessage`. |
398
+ | `QueueStatuses.LOCKED` | `'200_LOCKED'` | Paused. No new processing until `unlock()` is called. |
399
+ | `QueueStatuses.SETTLED` | `'300_SETTLED'` | Terminal state. No more elements accepted. |
400
+
401
+ You can validate a state string with `QueueStatuses.isValid(state)`.
402
+
403
+ ### In-Memory Queue -- Lock and Unlock
404
+
405
+ Use `lock()` / `unlock()` to pause and resume processing without losing queued elements.
406
+
407
+ ```typescript
408
+ // Pause the queue (e.g., during maintenance)
409
+ queue.lock();
410
+
411
+ // Elements can still be enqueued while locked,
412
+ // but they won't be processed until unlocked
413
+ await queue.enqueue('queued-while-locked');
414
+
415
+ // Resume processing
416
+ queue.unlock({ shouldProcessNextElement: true });
417
+
418
+ // Resume without processing the next element
419
+ queue.unlock({ shouldProcessNextElement: false });
420
+ ```
421
+
422
+ `lock()` logs an error and returns if the queue is already `LOCKED` or `SETTLED`.
423
+
424
+ `unlock()` logs an error and returns if the queue is `SETTLED` (past `LOCKED` state).
425
+
426
+ ### In-Memory Queue -- Settling and Closing
427
+
428
+ Once settled, the queue rejects new elements and transitions to `SETTLED` after all in-flight work completes.
429
+
430
+ ```typescript
431
+ // Signal that no more elements will be added
432
+ queue.settle();
433
+
434
+ // Check if the queue is settled and empty
435
+ if (queue.isSettled()) {
436
+ console.log('All work done, total events:', queue.getTotalEvent());
437
+ }
438
+
439
+ // Or close entirely (settle + terminate generator)
440
+ queue.close();
441
+ ```
442
+
443
+ `settle()` sets `isSettleRequested` to `true`. If the queue is not currently processing, it immediately transitions to `SETTLED`. If processing, it transitions to `SETTLED` after the current message completes and the storage is empty.
444
+
445
+ `close()` calls `settle()` and then terminates the internal generator via `generator.return()`.
446
+
447
+ ## API Summary
448
+
449
+ ### BullMQHelper
450
+
451
+ | Method | Returns | Description |
452
+ |--------|---------|-------------|
453
+ | `static newInstance(opts)` | `BullMQHelper` | Factory method, equivalent to `new BullMQHelper(opts)`. |
454
+ | `configureQueue()` | `void` | Sets up the BullMQ `Queue` instance. Called automatically for `role: 'queue'`. |
455
+ | `configureWorker()` | `void` | Sets up the BullMQ `Worker` instance. Called automatically for `role: 'worker'`. |
456
+ | `configure()` | `void` | Delegates to `configureQueue()` or `configureWorker()` based on the `role`. |
457
+ | `close()` | `Promise<void>` | Gracefully closes the worker and queue connections. |
458
+
459
+ #### Properties
460
+
461
+ | Property | Type | Description |
462
+ |----------|------|-------------|
463
+ | `queue` | `Queue<TQueueElement, TQueueResult>` | BullMQ `Queue` instance (available when `role: 'queue'`). |
464
+ | `worker` | `Worker<TQueueElement, TQueueResult>` | BullMQ `Worker` instance (available when `role: 'worker'`). |
465
+
466
+ ### MQTTClientHelper
467
+
468
+ | Method | Returns | Description |
469
+ |--------|---------|-------------|
470
+ | `configure()` | `void` | Connects to the MQTT broker. Called automatically by the constructor. |
471
+ | `subscribe(opts)` | `Promise<string[]>` | Subscribe to one or more topics. `opts: { topics: string[] }` |
472
+ | `publish(opts)` | `Promise<{ topic, message }>` | Publish a message to a topic. `opts: { topic: string; message: string \| Buffer }` |
473
+
474
+ ### QueueHelper
475
+
476
+ | Method | Returns | Description |
477
+ |--------|---------|-------------|
478
+ | `enqueue(payload)` | `Promise<void>` | Add an element to the queue. Rejected if settled. |
479
+ | `dequeue()` | `TQueueElement<T> \| undefined` | Remove and return the first element. |
480
+ | `nextMessage()` | `void` | Manually trigger processing of the next element. Only works in `WAITING` state. |
481
+ | `lock()` | `void` | Pause processing. State becomes `LOCKED`. |
482
+ | `unlock(opts)` | `void` | Resume processing. `opts: { shouldProcessNextElement?: boolean }` (default: `true`). |
483
+ | `settle()` | `void` | Mark queue as settled. No new elements accepted after this. |
484
+ | `isSettled()` | `boolean` | Returns `true` if state is `SETTLED` and storage is empty. |
485
+ | `close()` | `void` | Settle the queue and terminate the internal generator. |
486
+ | `getElementAt(position)` | `TQueueElement<T>` | Peek at an element by index. |
487
+ | `getState()` | `TQueueStatus` | Returns the current queue state. |
488
+ | `getTotalEvent()` | `number` | Returns the total number of elements ever enqueued. |
489
+ | `getProcessingEvents()` | `Set<TQueueElement<T>>` | Returns the set of currently processing elements. |
490
+
491
+ ## Troubleshooting
492
+
493
+ ### "Invalid queue name"
494
+
495
+ **Cause:** The `queueName` option is empty or falsy when creating a BullMQ queue or worker.
496
+
497
+ **Fix:** Provide a non-empty `queueName`:
498
+
499
+ ```typescript
500
+ // Wrong
501
+ new BullMQHelper({ queueName: '', role: 'queue', ... });
502
+
503
+ // Correct
504
+ new BullMQHelper({ queueName: 'my-email-queue', role: 'queue', ... });
505
+ ```
506
+
507
+ ### "Invalid client role to configure"
508
+
509
+ **Cause:** The `role` option is missing or not one of `'queue'` / `'worker'`.
510
+
511
+ **Fix:** Set `role` to either `'queue'` or `'worker'`:
512
+
513
+ ```typescript
514
+ // Wrong
515
+ new BullMQHelper({ role: undefined as any, ... });
516
+
517
+ // Correct
518
+ new BullMQHelper({ role: 'worker', ... });
519
+ ```
520
+
521
+ ### "Invalid url to configure mqtt client!"
522
+
523
+ **Cause:** The `url` option is empty when constructing an `MQTTClientHelper`. Throws an `ApplicationError` with status 500.
524
+
525
+ **Fix:** Pass a valid MQTT broker URL:
526
+
527
+ ```typescript
528
+ // Wrong
529
+ new MQTTClientHelper({ url: '', ... });
530
+
531
+ // Correct
532
+ new MQTTClientHelper({ url: 'mqtt://localhost:1883', ... });
533
+ ```
534
+
535
+ ### "MQTT Client is not available to subscribe topic!" / "MQTT Client is not available to publish message!"
536
+
537
+ **Cause:** `subscribe()` or `publish()` was called before the MQTT client finished connecting, or after the client disconnected. Throws an `ApplicationError` with status 400.
538
+
539
+ **Fix:** Wait for the `onConnect` callback before subscribing or publishing, or verify the client is connected:
540
+
541
+ ```typescript
542
+ const client = new MQTTClientHelper({
543
+ identifier: 'my-client',
544
+ url: 'mqtt://localhost:1883',
545
+ options: {},
546
+ onConnect: () => {
547
+ // Safe to subscribe/publish here
548
+ client.subscribe({ topics: ['my/topic'] });
549
+ },
550
+ onMessage: ({ topic, message }) => { /* ... */ },
551
+ });
552
+ ```
553
+
554
+ ### Elements not processing in In-Memory Queue
555
+
556
+ **Cause:** Multiple possible reasons why `onMessage` is never called.
557
+
558
+ **Checklist:**
559
+ - Verify `onMessage` callback is provided -- the generator logs a warning and exits if missing
560
+ - Check if the queue is locked -- call `unlock({ shouldProcessNextElement: true })` to resume
561
+ - Check if `autoDispatch` is `false` -- call `nextMessage()` manually after each `enqueue()`
562
+ - Check if the queue is settled -- a settled queue rejects new elements; create a new `QueueHelper` instance
563
+
564
+ ### "Queue was SETTLED | No more element acceptable"
565
+
566
+ **Cause:** `enqueue()` was called after `settle()` or `close()`.
567
+
568
+ **Fix:** Create a new `QueueHelper` instance if you need to continue processing:
569
+
570
+ ```typescript
571
+ queue.close();
572
+
573
+ // Start a new queue for further work
574
+ const newQueue = new QueueHelper<string>({
575
+ identifier: 'task-queue-v2',
576
+ onMessage: async ({ queueElement }) => { /* ... */ },
577
+ });
578
+ ```
579
+
580
+ ## See Also
581
+
582
+ - **Other Helpers:**
583
+ - [Helpers Index](../index) -- All available helpers
584
+ - [Cron Helper](../cron/) -- Scheduled tasks with cron expressions
585
+ - [Redis Helper](../redis/) -- Redis connection management (required for BullMQ)
586
+
587
+ - **External Resources:**
588
+ - [BullMQ Documentation](https://docs.bullmq.io/) -- BullMQ queue library
589
+ - [MQTT.js](https://github.com/mqttjs/MQTT.js) -- MQTT client library