queen-mq 0.1.6 → 0.2.11

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 (85) hide show
  1. package/README.md +166 -2078
  2. package/{src → client-js}/benchmark/consumer.js +5 -3
  3. package/{src → client-js}/benchmark/producer.js +9 -4
  4. package/client-js/client/client.js +1511 -0
  5. package/{src → client-js}/client/index.js +1 -4
  6. package/{src → client-js}/client/utils/http.js +3 -2
  7. package/{src → client-js}/client/utils/retry.js +7 -1
  8. package/client-js/test/advanced-client-tests.js +761 -0
  9. package/{src → client-js}/test/advanced-pattern-tests.js +2 -2
  10. package/{src → client-js}/test/bus-mode-tests.js +6 -6
  11. package/{src → client-js}/test/core-tests.js +8 -3
  12. package/{src → client-js}/test/edge-case-tests.js +4 -3
  13. package/client-js/test/human.js +162 -0
  14. package/client-js/test/qos0-tests.js +334 -0
  15. package/{src → client-js}/test/test-new.js +78 -1
  16. package/package.json +8 -29
  17. package/init-db.js +0 -20
  18. package/src/client/client.js +0 -586
  19. package/src/cluster-server.js +0 -242
  20. package/src/config.js +0 -229
  21. package/src/database/connection.js +0 -129
  22. package/src/database/poolManager.js +0 -199
  23. package/src/database/schema-v2.sql +0 -278
  24. package/src/managers/eventManager.js +0 -59
  25. package/src/managers/queueManagerOptimized.js +0 -1431
  26. package/src/managers/resourceCache.js +0 -96
  27. package/src/managers/systemEventManager.js +0 -132
  28. package/src/routes/ack.js +0 -26
  29. package/src/routes/configure.js +0 -46
  30. package/src/routes/messages.js +0 -368
  31. package/src/routes/pop.js +0 -69
  32. package/src/routes/push.js +0 -28
  33. package/src/routes/resources.js +0 -330
  34. package/src/routes/status.js +0 -1037
  35. package/src/server.js +0 -1528
  36. package/src/test/MIGRATION_ISSUES.md +0 -174
  37. package/src/test/test.js +0 -4524
  38. package/src/utils/streaming.js +0 -231
  39. package/src/webapp-dist/assets/Analytics-DJrE4Rz2.css +0 -1
  40. package/src/webapp-dist/assets/Analytics-HEMXKmfJ.js +0 -1
  41. package/src/webapp-dist/assets/ConfirmDialog-YEoCdE7x.js +0 -1
  42. package/src/webapp-dist/assets/ConsumerGroups-CC5VJywp.css +0 -1
  43. package/src/webapp-dist/assets/ConsumerGroups-D8Z20HhZ.js +0 -1
  44. package/src/webapp-dist/assets/Dashboard-BRFOz6it.css +0 -1
  45. package/src/webapp-dist/assets/Dashboard-Bpx9I32h.js +0 -1
  46. package/src/webapp-dist/assets/LoadingSpinner-BPttXcbs.js +0 -1
  47. package/src/webapp-dist/assets/Messages-BOfVHKma.css +0 -1
  48. package/src/webapp-dist/assets/Messages-EKURCbLl.js +0 -1
  49. package/src/webapp-dist/assets/QueueDetail-BXXA7xoZ.css +0 -1
  50. package/src/webapp-dist/assets/QueueDetail-Bmxg0KiA.js +0 -1
  51. package/src/webapp-dist/assets/Queues-CvrJkrZT.css +0 -1
  52. package/src/webapp-dist/assets/Queues-yUx9_3-9.js +0 -1
  53. package/src/webapp-dist/assets/StatusBadge-C1bBtXHh.css +0 -1
  54. package/src/webapp-dist/assets/StatusBadge-C6N84XmO.js +0 -1
  55. package/src/webapp-dist/assets/analytics-BapTekLM.js +0 -1
  56. package/src/webapp-dist/assets/colors-5z6Q_kUr.js +0 -18
  57. package/src/webapp-dist/assets/index-B7w2bKT6.css +0 -1
  58. package/src/webapp-dist/assets/index-CUSnwYTH.js +0 -31
  59. package/src/webapp-dist/assets/messages-CRV6E-xU.js +0 -1
  60. package/src/webapp-dist/assets/queen-logo-blue.svg +0 -210
  61. package/src/webapp-dist/assets/queen-logo-cyan.svg +0 -210
  62. package/src/webapp-dist/assets/queen-logo-indigo.svg +0 -210
  63. package/src/webapp-dist/assets/queen-logo-orange.svg +0 -210
  64. package/src/webapp-dist/assets/queen-logo-pink.svg +0 -210
  65. package/src/webapp-dist/assets/queen-logo-purple.svg +0 -210
  66. package/src/webapp-dist/assets/queen-logo-rose.svg +0 -239
  67. package/src/webapp-dist/assets/queen-logo.svg +0 -263
  68. package/src/webapp-dist/assets/queues-BdUbxfBC.js +0 -1
  69. package/src/webapp-dist/assets/resources-Bs9U-ceF.js +0 -1
  70. package/src/webapp-dist/index.html +0 -15
  71. package/src/websocket/wsServer.js +0 -228
  72. /package/{src → client-js}/benchmark/consumer_multi.js +0 -0
  73. /package/{src → client-js}/benchmark/producer_multi.js +0 -0
  74. /package/{src → client-js}/client/utils/loadBalancer.js +0 -0
  75. /package/{src → client-js}/services/encryptionService.js +0 -0
  76. /package/{src → client-js}/services/evictionService.js +0 -0
  77. /package/{src → client-js}/services/retentionService.js +0 -0
  78. /package/{src → client-js}/services/startupSync.js +0 -0
  79. /package/{src → client-js}/test/README.md +0 -0
  80. /package/{src → client-js}/test/enterprise-tests.js +0 -0
  81. /package/{src → client-js}/test/partition-locking-tests.js +0 -0
  82. /package/{src → client-js}/test/utils.js +0 -0
  83. /package/{src → client-js}/test/window-buffer-test.js +0 -0
  84. /package/{src → client-js}/utils/logger.js +0 -0
  85. /package/{src → client-js}/utils/uuid.js +0 -0
@@ -1,586 +0,0 @@
1
- import { createHttpClient, createLoadBalancedHttpClient } from './utils/http.js';
2
- import { withRetry } from './utils/retry.js';
3
- import { createLoadBalancer, LoadBalancingStrategy } from './utils/loadBalancer.js';
4
-
5
- /**
6
- * Minimalist Queen Message Queue Client
7
- *
8
- * Simple, powerful API with just 4 methods:
9
- * - queue: Configure a queue
10
- * - push: Send messages
11
- * - take: Receive messages (async iterator)
12
- * - ack: Acknowledge messages
13
- */
14
- export class Queen {
15
- #config;
16
- #http;
17
- #loadBalancer;
18
- #connected = false;
19
-
20
- constructor(config = {}) {
21
- this.#config = {
22
- baseUrls: null,
23
- loadBalancingStrategy: LoadBalancingStrategy.ROUND_ROBIN,
24
- enableFailover: true,
25
- timeout: 30000,
26
- retryAttempts: 3,
27
- retryDelay: 1000,
28
- ...config
29
- };
30
- }
31
-
32
- /**
33
- * Ensure HTTP client is connected
34
- */
35
- async #ensureConnected() {
36
- if (this.#connected) return;
37
-
38
- const { baseUrl, baseUrls, loadBalancingStrategy, enableFailover, timeout } = this.#config;
39
-
40
- if (baseUrls && Array.isArray(baseUrls) && baseUrls.length > 0) {
41
- // Multiple servers with load balancing
42
- this.#loadBalancer = createLoadBalancer(baseUrls, loadBalancingStrategy);
43
- this.#http = createLoadBalancedHttpClient({
44
- baseUrls,
45
- loadBalancer: this.#loadBalancer,
46
- timeout,
47
- enableFailover
48
- });
49
- } else {
50
- // Single server
51
- const singleUrl = baseUrls && baseUrls.length === 1 ? baseUrls[0] : baseUrl;
52
- this.#http = createHttpClient({ baseUrl: singleUrl, timeout });
53
- }
54
-
55
- this.#connected = true;
56
- }
57
-
58
- /**
59
- * Validate if a string is a valid UUID v4
60
- */
61
- #isValidUUID(str) {
62
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
63
- return uuidRegex.test(str);
64
- }
65
-
66
- /**
67
- * Parse address string into components
68
- * Examples:
69
- * - "myqueue" -> { queue: "myqueue" }
70
- * - "myqueue/urgent" -> { queue: "myqueue", partition: "urgent" }
71
- * - "myqueue@workers" -> { queue: "myqueue", consumerGroup: "workers" }
72
- * - "myqueue/urgent@workers" -> { queue: "myqueue", partition: "urgent", consumerGroup: "workers" }
73
- * - "namespace:billing" -> { namespace: "billing" }
74
- * - "task:process" -> { task: "process" }
75
- * - "namespace:billing/task:process" -> { namespace: "billing", task: "process" }
76
- */
77
- #parseAddress(address) {
78
- // Check for namespace/task pattern
79
- if (address.includes('namespace:') || address.includes('task:')) {
80
- const parts = {};
81
-
82
- // Extract consumer group if present (after @)
83
- let workingAddress = address;
84
- const atIndex = address.lastIndexOf('@');
85
- if (atIndex > 0) {
86
- parts.consumerGroup = address.substring(atIndex + 1);
87
- workingAddress = address.substring(0, atIndex);
88
- }
89
-
90
- const segments = workingAddress.split('/');
91
-
92
- for (const segment of segments) {
93
- if (segment.startsWith('namespace:')) {
94
- parts.namespace = segment.substring(10);
95
- } else if (segment.startsWith('task:')) {
96
- parts.task = segment.substring(5);
97
- }
98
- }
99
-
100
- return parts;
101
- }
102
-
103
- // Standard queue[/partition][@group] pattern
104
- const match = address.match(/^([^/@]+)(?:\/([^@]+))?(?:@(.+))?$/);
105
- if (!match) {
106
- throw new Error(`Invalid address format: ${address}`);
107
- }
108
-
109
- return {
110
- queue: match[1],
111
- partition: match[2] || 'Default',
112
- consumerGroup: match[3] || null
113
- };
114
- }
115
-
116
- /**
117
- * Configure a queue with options
118
- * @param {string} name - Queue name
119
- * @param {Object} options - Queue configuration options
120
- * @param {Object} metadata - Optional metadata { namespace, task }
121
- */
122
- async queue(name, options = {}, metadata = {}) {
123
- await this.#ensureConnected();
124
-
125
- const { namespace, task } = metadata;
126
-
127
- const result = await withRetry(
128
- () => this.#http.post('/api/v1/configure', {
129
- queue: name,
130
- namespace: namespace || null,
131
- task: task || null,
132
- options
133
- }),
134
- this.#config.retryAttempts,
135
- this.#config.retryDelay
136
- );
137
-
138
- if (result && result.error) {
139
- throw new Error(result.error);
140
- }
141
-
142
- return result;
143
- }
144
-
145
- /**
146
- * Push messages to a queue
147
- * @param {string} address - Queue address (e.g., "myqueue" or "myqueue/partition")
148
- * @param {Object|Array} payload - Single message or array of messages
149
- * @param {Object} options - Optional message properties { transactionId, traceId }
150
- */
151
- async push(address, payload, options = {}) {
152
- await this.#ensureConnected();
153
-
154
- const { queue, partition } = this.#parseAddress(address);
155
-
156
- // Handle both single and array inputs
157
- const items = Array.isArray(payload) ? payload : [payload];
158
-
159
- // Format items for the API
160
- const formattedItems = items.map(item => {
161
- // If item is an object with special properties, extract them
162
- const isMessageObject = typeof item === 'object' && item !== null &&
163
- (item._payload || item._transactionId || item._traceId);
164
-
165
- if (isMessageObject) {
166
- const result = {
167
- queue,
168
- partition,
169
- payload: item._payload || item,
170
- transactionId: item._transactionId || item.transactionId
171
- };
172
-
173
- // Include traceId if provided and valid UUID
174
- const traceId = item._traceId || item.traceId;
175
- if (traceId && this.#isValidUUID(traceId)) {
176
- result.traceId = traceId;
177
- }
178
-
179
- return result;
180
- }
181
-
182
- // Otherwise use the item as payload and apply options
183
- const result = {
184
- queue,
185
- partition,
186
- payload: item,
187
- transactionId: options.transactionId || (item && typeof item === 'object' ? item.transactionId : undefined)
188
- };
189
-
190
- // Include traceId if provided and valid UUID
191
- const traceId = options.traceId || (item && typeof item === 'object' ? item.traceId : undefined);
192
- if (traceId && this.#isValidUUID(traceId)) {
193
- result.traceId = traceId;
194
- }
195
-
196
- return result;
197
- });
198
-
199
- const result = await withRetry(
200
- () => this.#http.post('/api/v1/push', {
201
- items: formattedItems
202
- }),
203
- this.#config.retryAttempts,
204
- this.#config.retryDelay
205
- );
206
-
207
- if (result && result.error) {
208
- throw new Error(result.error);
209
- }
210
-
211
- return result;
212
- }
213
-
214
- /**
215
- * Internal method that yields batches of messages (arrays)
216
- * @private
217
- * @param {string} address - Queue address
218
- * @param {Object} options - Options for taking messages
219
- * @yields {Array} Arrays of message objects
220
- */
221
- async *#takeInternal(address, options = {}) {
222
- await this.#ensureConnected();
223
-
224
- const { queue, partition, consumerGroup, namespace, task } = this.#parseAddress(address);
225
- const {
226
- limit = null,
227
- batch = 1,
228
- wait = false,
229
- timeout = 30000,
230
- subscriptionMode = null,
231
- subscriptionFrom = null,
232
- idleTimeout = null
233
- } = options;
234
-
235
- let totalCount = 0;
236
- let consecutiveEmptyResponses = 0;
237
- let consecutiveNetworkErrors = 0;
238
- const maxConsecutiveEmpty = 3;
239
- let lastMessageTime = idleTimeout ? Date.now() : null;
240
-
241
- while (true) {
242
- // Check if we've reached the limit
243
- if (limit && totalCount >= limit) break;
244
-
245
- // Check idle timeout
246
- if (idleTimeout && lastMessageTime) {
247
- const idleTime = Date.now() - lastMessageTime;
248
- if (idleTime >= idleTimeout) {
249
- break; // Exit if idle time exceeded
250
- }
251
- }
252
-
253
- // Calculate batch size respecting the limit
254
- const effectiveBatch = limit ? Math.min(batch, limit - totalCount) : batch;
255
-
256
- // Build the request path and parameters
257
- let path;
258
- const params = new URLSearchParams({
259
- wait: wait.toString(),
260
- timeout: timeout.toString(),
261
- batch: effectiveBatch.toString()
262
- });
263
-
264
- // Add consumer group parameters if provided
265
- if (consumerGroup) params.append('consumerGroup', consumerGroup);
266
- if (subscriptionMode) params.append('subscriptionMode', subscriptionMode);
267
- if (subscriptionFrom) params.append('subscriptionFrom', subscriptionFrom);
268
-
269
- // Determine the endpoint based on parameters
270
- if (queue) {
271
- if (partition && partition !== 'Default') {
272
- path = `/api/v1/pop/queue/${queue}/partition/${partition}`;
273
- } else {
274
- path = `/api/v1/pop/queue/${queue}`;
275
- }
276
- } else if (namespace || task) {
277
- // Pop by namespace/task
278
- path = '/api/v1/pop';
279
- if (namespace) params.append('namespace', namespace);
280
- if (task) params.append('task', task);
281
- } else {
282
- throw new Error('Must specify either queue, namespace, or task');
283
- }
284
-
285
- try {
286
- // For long polling, use a slightly longer client timeout
287
- const clientTimeout = wait ? timeout + 5000 : timeout;
288
-
289
- const result = await this.#http.get(`${path}?${params}`, clientTimeout);
290
-
291
- // Handle empty response
292
- if (!result || !result.messages || result.messages.length === 0) {
293
- if (wait) {
294
- // For long polling, immediately retry
295
- continue;
296
- } else {
297
- // For non-waiting mode, stop after several empty responses
298
- consecutiveEmptyResponses++;
299
- if (consecutiveEmptyResponses >= maxConsecutiveEmpty) {
300
- break;
301
- }
302
- // Small delay before retry
303
- await new Promise(resolve => setTimeout(resolve, 100));
304
- continue;
305
- }
306
- }
307
-
308
- // Reset counters on successful fetch
309
- consecutiveEmptyResponses = 0;
310
- consecutiveNetworkErrors = 0;
311
-
312
- // Update last message time if tracking idle timeout
313
- if (idleTimeout) {
314
- lastMessageTime = Date.now();
315
- }
316
-
317
- // Filter out null/undefined messages
318
- const messages = result.messages.filter(msg => msg != null);
319
-
320
- if (messages.length > 0) {
321
- totalCount += messages.length;
322
- yield messages;
323
-
324
- // If we've hit the limit, stop
325
- if (limit && totalCount >= limit) {
326
- break;
327
- }
328
- }
329
-
330
- } catch (error) {
331
- // Check if this is a timeout error (expected for long polling)
332
- const isTimeoutError = error.name === 'AbortError' ||
333
- error.message?.includes('abort') ||
334
- error.message?.includes('timeout');
335
-
336
- if (isTimeoutError && wait) {
337
- // For long polling timeout, immediately retry
338
- continue;
339
- }
340
-
341
- // Check if this is a network error (connection refused, socket closed, etc.)
342
- const isNetworkError = error.message?.includes('fetch failed') ||
343
- error.message?.includes('ECONNREFUSED') ||
344
- error.message?.includes('ECONNRESET') ||
345
- error.message?.includes('closed') ||
346
- error.cause?.code === 'UND_ERR_SOCKET' ||
347
- error.code === 'ECONNREFUSED' ||
348
- error.code === 'ECONNRESET';
349
-
350
- if (isNetworkError) {
351
- // Log the network error
352
- console.warn(`Network error while polling queue: ${error.message}`);
353
-
354
- // Retry with exponential backoff (capped at 30 seconds)
355
- const retryDelay = Math.min(this.#config.retryDelay * Math.pow(2, Math.min(consecutiveNetworkErrors, 10)), 30000);
356
- console.warn(`Retrying in ${retryDelay}ms... (attempt ${consecutiveNetworkErrors + 1})`);
357
- await new Promise(resolve => setTimeout(resolve, retryDelay));
358
-
359
- consecutiveNetworkErrors++;
360
-
361
- // Continue retrying indefinitely
362
- continue;
363
- }
364
-
365
- // For other errors, throw
366
- throw error;
367
- }
368
- }
369
- }
370
-
371
- /**
372
- * Take messages from a queue one at a time (async iterator)
373
- * @param {string} address - Queue address (e.g., "myqueue", "myqueue/partition", "myqueue@group")
374
- * @param {Object} options - Options for taking messages
375
- * @yields {Object} Individual message objects
376
- */
377
- async *take(address, options = {}) {
378
- let count = 0;
379
- const { limit = null } = options;
380
-
381
- for await (const messages of this.#takeInternal(address, options)) {
382
- for (const message of messages) {
383
- yield message;
384
- count++;
385
-
386
- // Double-check limit (internal method also checks, but this ensures exact limit)
387
- if (limit && count >= limit) {
388
- return;
389
- }
390
- }
391
- }
392
- }
393
-
394
- /**
395
- * Take messages from a queue in batches (async iterator)
396
- * @param {string} address - Queue address (e.g., "myqueue", "myqueue/partition", "myqueue@group")
397
- * @param {Object} options - Options for taking messages
398
- * @yields {Array} Arrays of message objects
399
- */
400
- async *takeBatch(address, options = {}) {
401
- for await (const messages of this.#takeInternal(address, options)) {
402
- // Only yield non-empty batches
403
- if (messages && messages.length > 0) {
404
- yield messages;
405
- }
406
- }
407
- }
408
-
409
- /**
410
- * Acknowledge a message or batch of messages
411
- * @param {Object|string|Array} message - Message object, transaction ID, or array of messages
412
- * @param {boolean|string} status - true for success, false for failure, or 'retry'
413
- * @param {Object} context - Optional context (e.g., { group: 'workers', error: 'reason' })
414
- */
415
- async ack(message, status = true, context = {}) {
416
- await this.#ensureConnected();
417
-
418
- // Handle batch acknowledgment
419
- if (Array.isArray(message)) {
420
- if (message.length === 0) {
421
- return { processed: 0, results: [] };
422
- }
423
-
424
- // Check if messages have individual status (Option B pattern)
425
- const hasIndividualStatus = message.some(msg =>
426
- typeof msg === 'object' && msg !== null && ('_status' in msg || '_error' in msg)
427
- );
428
-
429
- let acknowledgments;
430
-
431
- if (hasIndividualStatus) {
432
- // Option B: Each message has its own status
433
- acknowledgments = message.map(msg => {
434
- // Extract transaction ID
435
- let transactionId;
436
- if (typeof msg === 'string') {
437
- transactionId = msg;
438
- } else if (typeof msg === 'object' && msg !== null) {
439
- transactionId = msg.transactionId || msg.id;
440
- if (!transactionId) {
441
- throw new Error('Message object must have transactionId or id property');
442
- }
443
- } else {
444
- throw new Error('Invalid message in batch');
445
- }
446
-
447
- // Get individual status or fall back to parameter
448
- let msgStatus = msg._status !== undefined ? msg._status : status;
449
- let statusStr;
450
- if (typeof msgStatus === 'boolean') {
451
- statusStr = msgStatus ? 'completed' : 'failed';
452
- } else {
453
- statusStr = msgStatus;
454
- }
455
-
456
- return {
457
- transactionId,
458
- status: statusStr,
459
- error: msg._error || context.error || null
460
- };
461
- });
462
- } else {
463
- // Option A: Same status for all messages
464
- const statusStr = typeof status === 'boolean'
465
- ? (status ? 'completed' : 'failed')
466
- : status;
467
-
468
- acknowledgments = message.map(msg => {
469
- // Extract transaction ID
470
- let transactionId;
471
- if (typeof msg === 'string') {
472
- transactionId = msg;
473
- } else if (typeof msg === 'object' && msg !== null) {
474
- transactionId = msg.transactionId || msg.id;
475
- if (!transactionId) {
476
- throw new Error('Message object must have transactionId or id property');
477
- }
478
- } else {
479
- throw new Error('Invalid message in batch');
480
- }
481
-
482
- return {
483
- transactionId,
484
- status: statusStr,
485
- error: context.error || null
486
- };
487
- });
488
- }
489
-
490
- // Call batch ack endpoint
491
- const result = await withRetry(
492
- () => this.#http.post('/api/v1/ack/batch', {
493
- acknowledgments,
494
- consumerGroup: context.group || null
495
- }),
496
- this.#config.retryAttempts,
497
- this.#config.retryDelay
498
- );
499
-
500
- if (result && result.error) {
501
- throw new Error(result.error);
502
- }
503
-
504
- return result;
505
- }
506
-
507
- // Handle single message acknowledgment
508
- // Extract transaction ID
509
- let transactionId;
510
- if (typeof message === 'string') {
511
- transactionId = message;
512
- } else if (typeof message === 'object' && message !== null) {
513
- transactionId = message.transactionId || message.id;
514
- if (!transactionId) {
515
- throw new Error('Message object must have transactionId or id property');
516
- }
517
- } else {
518
- throw new Error('Message must be a string (transaction ID) or object with transactionId');
519
- }
520
-
521
- // Determine status string
522
- let statusStr;
523
- if (typeof status === 'boolean') {
524
- statusStr = status ? 'completed' : 'failed';
525
- } else {
526
- statusStr = status; // Allow custom status like 'retry'
527
- }
528
-
529
- // Build request body
530
- const body = {
531
- transactionId,
532
- status: statusStr,
533
- error: context.error || null,
534
- consumerGroup: context.group || null
535
- };
536
-
537
- const result = await withRetry(
538
- () => this.#http.post('/api/v1/ack', body),
539
- this.#config.retryAttempts,
540
- this.#config.retryDelay
541
- );
542
-
543
- if (result && result.error) {
544
- throw new Error(result.error);
545
- }
546
-
547
- return result;
548
- }
549
-
550
- /**
551
- * Delete a queue (removes queue and all its messages/partitions)
552
- * @param {string} name - Queue name to delete
553
- * @returns {Promise<Object>} Deletion result
554
- */
555
- async queueDelete(name) {
556
- await this.#ensureConnected();
557
-
558
- if (typeof name !== 'string' || !name) {
559
- throw new Error('Queue name must be a non-empty string');
560
- }
561
-
562
- const result = await withRetry(
563
- () => this.#http.delete(`/api/v1/resources/queues/${encodeURIComponent(name)}`),
564
- this.#config.retryAttempts,
565
- this.#config.retryDelay
566
- );
567
-
568
- if (result && result.error) {
569
- throw new Error(result.error);
570
- }
571
-
572
- return result;
573
- }
574
-
575
- /**
576
- * Close the client connection
577
- */
578
- async close() {
579
- this.#connected = false;
580
- this.#http = null;
581
- this.#loadBalancer = null;
582
- }
583
- }
584
-
585
- // Export the class as default as well for convenience
586
- export default Queen;