queen-mq 0.1.0 → 0.1.2

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 (160) hide show
  1. package/API.md +862 -752
  2. package/AUTH.md +2044 -0
  3. package/LICENSE.md +202 -0
  4. package/README.md +1705 -1051
  5. package/WEBAPP.md +1889 -0
  6. package/assets/dashboard-01.png +0 -0
  7. package/assets/queen-logo-blue.svg +210 -0
  8. package/assets/queen-logo-cyan.svg +210 -0
  9. package/assets/queen-logo-indigo.svg +210 -0
  10. package/assets/queen-logo-orange.svg +210 -0
  11. package/assets/queen-logo-pink.svg +210 -0
  12. package/assets/queen-logo-purple.svg +210 -0
  13. package/assets/queen-logo-rose.svg +239 -0
  14. package/assets/queen-logo.svg +263 -0
  15. package/examples/batch-processing.js +58 -0
  16. package/examples/test-complete-client.js +260 -0
  17. package/examples/test-dashboard-api.js +200 -0
  18. package/examples/test-traceid.js +147 -0
  19. package/package.json +17 -4
  20. package/server.log +1 -0
  21. package/src/benchmark/consumer.js +207 -0
  22. package/src/benchmark/consumer_multi.js +216 -0
  23. package/src/benchmark/producer.js +75 -0
  24. package/src/benchmark/producer_multi.js +115 -0
  25. package/src/client/client.js +300 -31
  26. package/src/client/queenClient.js +5 -0
  27. package/src/cluster-server.js +242 -0
  28. package/src/config.js +19 -5
  29. package/src/database/connection.js +42 -16
  30. package/src/database/poolManager.js +7 -0
  31. package/src/database/schema-v2.sql +194 -130
  32. package/src/managers/queueManagerOptimized.js +823 -933
  33. package/src/managers/systemEventManager.js +8 -3
  34. package/src/routes/messages.js +127 -57
  35. package/src/routes/pop.js +27 -43
  36. package/src/routes/resources.js +61 -27
  37. package/src/routes/status.js +1037 -0
  38. package/src/server.js +308 -272
  39. package/src/services/evictionService.js +57 -28
  40. package/src/services/retentionService.js +44 -11
  41. package/src/test/MIGRATION_ISSUES.md +174 -0
  42. package/src/test/README.md +203 -0
  43. package/src/test/advanced-pattern-tests.js +1137 -0
  44. package/src/test/bus-mode-tests.js +361 -0
  45. package/src/test/core-tests.js +342 -0
  46. package/src/test/edge-case-tests.js +561 -0
  47. package/src/test/enterprise-tests.js +637 -0
  48. package/src/test/partition-locking-tests.js +545 -0
  49. package/src/test/test-new.js +278 -0
  50. package/src/test/test.js +6 -3
  51. package/src/test/utils.js +169 -0
  52. package/src/utils/streaming.js +231 -0
  53. package/src/utils/uuid.js +2 -2
  54. package/src/websocket/wsServer.js +10 -3
  55. package/test-keepalive-v2.sh +22 -0
  56. package/webapp/COLOR_GUIDE.md +118 -0
  57. package/webapp/README.md +143 -0
  58. package/webapp/index.html +14 -0
  59. package/webapp/package-lock.json +3184 -0
  60. package/webapp/package.json +25 -0
  61. package/webapp/postcss.config.js +7 -0
  62. package/webapp/public/assets/queen-logo-blue.svg +210 -0
  63. package/webapp/public/assets/queen-logo-cyan.svg +210 -0
  64. package/webapp/public/assets/queen-logo-indigo.svg +210 -0
  65. package/webapp/public/assets/queen-logo-orange.svg +210 -0
  66. package/webapp/public/assets/queen-logo-pink.svg +210 -0
  67. package/webapp/public/assets/queen-logo-purple.svg +210 -0
  68. package/webapp/public/assets/queen-logo-rose.svg +239 -0
  69. package/webapp/public/assets/queen-logo.svg +263 -0
  70. package/webapp/src/App.vue +19 -0
  71. package/webapp/src/api/analytics.js +10 -0
  72. package/webapp/src/api/client.js +29 -0
  73. package/webapp/src/api/consumers.js +52 -0
  74. package/webapp/src/api/health.js +7 -0
  75. package/webapp/src/api/messages.js +26 -0
  76. package/webapp/src/api/queues.js +14 -0
  77. package/webapp/src/api/resources.js +8 -0
  78. package/webapp/src/assets/styles/main.css +357 -0
  79. package/webapp/src/components/analytics/AnalyticsFilters.vue +87 -0
  80. package/webapp/src/components/analytics/AnalyticsMetrics.vue +57 -0
  81. package/webapp/src/components/analytics/MessageDistributionChart.vue +111 -0
  82. package/webapp/src/components/analytics/MessageFlowChart.vue +173 -0
  83. package/webapp/src/components/analytics/TimeRangeSelector.vue +27 -0
  84. package/webapp/src/components/analytics/TopQueuesChart.vue +132 -0
  85. package/webapp/src/components/common/ConfirmDialog.vue +56 -0
  86. package/webapp/src/components/common/LoadingSpinner.vue +6 -0
  87. package/webapp/src/components/common/MetricCard.vue +43 -0
  88. package/webapp/src/components/common/StatusBadge.vue +45 -0
  89. package/webapp/src/components/dashboard/MessageStatusCard.vue +50 -0
  90. package/webapp/src/components/dashboard/PerformanceCard.vue +38 -0
  91. package/webapp/src/components/dashboard/ThroughputChart.vue +182 -0
  92. package/webapp/src/components/dashboard/TopQueuesTable.vue +53 -0
  93. package/webapp/src/components/layout/AppLayout.vue +110 -0
  94. package/webapp/src/components/layout/AppSidebar.vue +304 -0
  95. package/webapp/src/components/messages/MessageDetailPanel.vue +242 -0
  96. package/webapp/src/components/messages/MessageFilters.vue +114 -0
  97. package/webapp/src/components/queue-detail/PartitionList.vue +79 -0
  98. package/webapp/src/components/queue-detail/PushMessageModal.vue +175 -0
  99. package/webapp/src/components/queue-detail/QueueConfig.vue +63 -0
  100. package/webapp/src/components/queue-detail/QueueDetailHeader.vue +53 -0
  101. package/webapp/src/components/queue-detail/RecentMessages.vue +76 -0
  102. package/webapp/src/components/queues/CreateQueueModal.vue +193 -0
  103. package/webapp/src/components/queues/QueueFilters.vue +90 -0
  104. package/webapp/src/composables/useApi.js +34 -0
  105. package/webapp/src/composables/useTheme.js +36 -0
  106. package/webapp/src/main.js +11 -0
  107. package/webapp/src/router/index.js +42 -0
  108. package/webapp/src/utils/colors.js +96 -0
  109. package/webapp/src/utils/formatters.js +49 -0
  110. package/webapp/src/views/Analytics.vue +377 -0
  111. package/webapp/src/views/ConsumerGroups.vue +433 -0
  112. package/webapp/src/views/Dashboard.vue +418 -0
  113. package/webapp/src/views/Messages.vue +361 -0
  114. package/webapp/src/views/QueueDetail.vue +582 -0
  115. package/webapp/src/views/Queues.vue +496 -0
  116. package/webapp/tailwind.config.js +25 -0
  117. package/webapp/vite.config.js +10 -0
  118. package/CACHE.md +0 -519
  119. package/DASHBOARD-V3.md +0 -478
  120. package/DASHBOARD.md +0 -382
  121. package/MOD_QUEUE.md +0 -453
  122. package/PARTITION_LOCKING_DESIGN.md +0 -989
  123. package/PLAN.md +0 -707
  124. package/QUERY_ANALSYS.md +0 -72
  125. package/QUEUE_BUS.md +0 -334
  126. package/V2-PLAN.md +0 -236
  127. package/dashboard/.vscode/extensions.json +0 -3
  128. package/dashboard/README.md +0 -5
  129. package/dashboard/index.html +0 -14
  130. package/dashboard/package-lock.json +0 -1458
  131. package/dashboard/package.json +0 -25
  132. package/dashboard/public/vite.svg +0 -1
  133. package/dashboard/src/App.vue +0 -29
  134. package/dashboard/src/assets/styles/main.css +0 -908
  135. package/dashboard/src/assets/vue.svg +0 -1
  136. package/dashboard/src/components/cards/MetricCard.vue +0 -298
  137. package/dashboard/src/components/charts/QueueDepthChart.vue +0 -276
  138. package/dashboard/src/components/charts/QueueLagChart.vue +0 -436
  139. package/dashboard/src/components/charts/ThroughputChart.vue +0 -302
  140. package/dashboard/src/components/common/ActivityFeed.vue +0 -251
  141. package/dashboard/src/components/layout/AppHeader.vue +0 -208
  142. package/dashboard/src/components/layout/AppLayout.vue +0 -88
  143. package/dashboard/src/components/layout/AppSidebar.vue +0 -261
  144. package/dashboard/src/main.js +0 -44
  145. package/dashboard/src/router.js +0 -54
  146. package/dashboard/src/services/api.js +0 -187
  147. package/dashboard/src/services/websocket.js +0 -167
  148. package/dashboard/src/utils/constants.js +0 -56
  149. package/dashboard/src/utils/helpers.js +0 -118
  150. package/dashboard/src/views/Analytics.vue +0 -912
  151. package/dashboard/src/views/Dashboard.vue +0 -906
  152. package/dashboard/src/views/Messages.vue +0 -437
  153. package/dashboard/src/views/QueueDetail.vue +0 -501
  154. package/dashboard/src/views/Queues.vue +0 -333
  155. package/dashboard/vite.config.js +0 -30
  156. package/debug-namespace.js +0 -110
  157. package/docs/long-polling.md +0 -159
  158. package/docs/multi-server-cache-solutions.md +0 -185
  159. package/docs/performance-tuning.md +0 -222
  160. package/src/routes/analytics.js +0 -812
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Complete test of the minimalist Queen client
5
+ * Shows all features including namespace/task and message properties
6
+ */
7
+
8
+ import { Queen } from '../src/client/client.js';
9
+
10
+ async function main() {
11
+ console.log('🧪 Testing Complete Minimalist Queen Client Features\n');
12
+
13
+ const client = new Queen({
14
+ baseUrls: ['http://localhost:6632'],
15
+ timeout: 30000
16
+ });
17
+
18
+ try {
19
+ // ============================================
20
+ // 1. Queue Configuration with Namespace/Task
21
+ // ============================================
22
+ console.log('1️⃣ Configuring queues with namespace and task...');
23
+
24
+ // Configure queue with namespace and task
25
+ await client.queue('orders-processing', {
26
+ leaseTime: 300,
27
+ retryLimit: 3,
28
+ priority: 10
29
+ }, {
30
+ namespace: 'ecommerce',
31
+ task: 'process'
32
+ });
33
+
34
+ await client.queue('inventory-updates', {
35
+ leaseTime: 300,
36
+ priority: 5
37
+ }, {
38
+ namespace: 'ecommerce',
39
+ task: 'update'
40
+ });
41
+
42
+ await client.queue('notifications', {
43
+ priority: 8
44
+ }, {
45
+ namespace: 'ecommerce',
46
+ task: 'notify'
47
+ });
48
+
49
+ console.log('✅ Queues configured with namespace/task metadata\n');
50
+
51
+ // ============================================
52
+ // 2. Push with Message Properties
53
+ // ============================================
54
+ console.log('2️⃣ Pushing messages with properties...');
55
+
56
+ // Method 1: Properties in options
57
+ await client.push('orders-processing', {
58
+ orderId: 123,
59
+ amount: 99.99
60
+ }, {
61
+ transactionId: 'txn-123'
62
+ });
63
+
64
+ // Method 2: Properties in the message object
65
+ await client.push('orders-processing', {
66
+ orderId: 124,
67
+ amount: 199.99,
68
+ transactionId: 'txn-124' // Will be extracted
69
+ });
70
+
71
+ // Method 3: Batch with mixed properties
72
+ await client.push('inventory-updates', [
73
+ {
74
+ sku: 'PROD-001',
75
+ quantity: 5,
76
+ transactionId: 'inv-001'
77
+ },
78
+ {
79
+ sku: 'PROD-002',
80
+ quantity: 10,
81
+ transactionId: 'inv-002'
82
+ }
83
+ ]);
84
+
85
+ // Method 4: Using special properties format
86
+ await client.push('notifications', {
87
+ _payload: { userId: 789, message: 'Order shipped' },
88
+ _transactionId: 'notif-001'
89
+ });
90
+
91
+ console.log('✅ Messages pushed with various property patterns\n');
92
+
93
+ // ============================================
94
+ // 3. Take by Queue/Partition
95
+ // ============================================
96
+ console.log('3️⃣ Taking messages by queue/partition...');
97
+
98
+ // Take from specific queue
99
+ let count = 0;
100
+ for await (const msg of client.take('orders-processing', { limit: 2 })) {
101
+ count++;
102
+ console.log(` 📩 Order ${count}:`, msg.data);
103
+ console.log(` Transaction ID: ${msg.transactionId}`);
104
+ await client.ack(msg);
105
+ }
106
+ console.log('✅ Processed orders\n');
107
+
108
+ // ============================================
109
+ // 4. Take by Namespace/Task
110
+ // ============================================
111
+ console.log('4️⃣ Taking messages by namespace/task...');
112
+
113
+ // Take all messages from ecommerce namespace
114
+ count = 0;
115
+ for await (const msg of client.take('namespace:ecommerce', { limit: 2 })) {
116
+ count++;
117
+ console.log(` 📦 Namespace message ${count} from ${msg.queue}:`, msg.data);
118
+ await client.ack(msg);
119
+ }
120
+
121
+ // Take only 'update' tasks
122
+ for await (const msg of client.take('task:update', { limit: 2 })) {
123
+ console.log(` 🔄 Update task from ${msg.queue}:`, msg.data);
124
+ await client.ack(msg);
125
+ }
126
+
127
+ // Combine namespace and task
128
+ for await (const msg of client.take('namespace:ecommerce/task:notify', { limit: 1 })) {
129
+ console.log(` 🔔 Notification:`, msg.data);
130
+ await client.ack(msg);
131
+ }
132
+
133
+ console.log('✅ Processed messages by namespace/task\n');
134
+
135
+ // ============================================
136
+ // 5. Consumer Groups with Subscription Modes
137
+ // ============================================
138
+ console.log('5️⃣ Testing consumer groups with subscription modes...');
139
+
140
+ // Configure events queue first
141
+ await client.queue('events', {
142
+ leaseTime: 300,
143
+ retryLimit: 3
144
+ });
145
+
146
+ // Push test events
147
+ await client.push('events', [
148
+ { event: 'user.signup', userId: 1 },
149
+ { event: 'user.login', userId: 2 },
150
+ { event: 'user.purchase', userId: 3 }
151
+ ]);
152
+
153
+ // Consumer group starting from all messages
154
+ for await (const event of client.take('events@analytics', {
155
+ limit: 1,
156
+ subscriptionMode: 'all'
157
+ })) {
158
+ console.log(' 📊 Analytics (all):', event.data);
159
+ await client.ack(event, true, { group: 'analytics' });
160
+ }
161
+
162
+ // Consumer group starting from new messages only
163
+ for await (const event of client.take('events@realtime', {
164
+ limit: 1,
165
+ subscriptionMode: 'new'
166
+ })) {
167
+ console.log(' ⚡ Realtime (new only):', event.data);
168
+ await client.ack(event, true, { group: 'realtime' });
169
+ }
170
+
171
+ console.log('✅ Consumer groups tested\n');
172
+
173
+ // ============================================
174
+ // 6. Advanced Patterns
175
+ // ============================================
176
+ console.log('6️⃣ Testing advanced patterns...');
177
+
178
+ // Configure tasks queue
179
+ await client.queue('tasks', {
180
+ leaseTime: 300,
181
+ priority: 8
182
+ });
183
+
184
+ // Pattern 1: Partitioned messages with properties
185
+ await client.push('tasks/urgent', {
186
+ taskId: 'TASK-001',
187
+ priority: 'critical'
188
+ }, {
189
+ transactionId: 'task-txn-001'
190
+ });
191
+
192
+ // Pattern 2: Consumer group on specific partition
193
+ for await (const task of client.take('tasks/urgent@workers', { limit: 1 })) {
194
+ console.log(' 🚨 Urgent task for worker group:', task.data);
195
+ await client.ack(task, true, { group: 'workers' });
196
+ }
197
+
198
+ // Configure logs queue
199
+ await client.queue('logs', {
200
+ leaseTime: 300,
201
+ retentionSeconds: 86400 // Keep for 1 day
202
+ });
203
+
204
+ // Pattern 3: Batch push with partition
205
+ await client.push('logs/audit', [
206
+ { action: 'login', user: 'alice', timestamp: Date.now() },
207
+ { action: 'update', user: 'bob', timestamp: Date.now() },
208
+ { action: 'delete', user: 'charlie', timestamp: Date.now() }
209
+ ]);
210
+
211
+ console.log('✅ Advanced patterns tested\n');
212
+
213
+ // ============================================
214
+ // 7. Error Handling
215
+ // ============================================
216
+ console.log('7️⃣ Testing error handling...');
217
+
218
+ // Configure error-test queue
219
+ await client.queue('error-test', {
220
+ leaseTime: 60,
221
+ retryLimit: 2
222
+ });
223
+
224
+ // Push a message that will fail processing
225
+ await client.push('error-test', {
226
+ willFail: true,
227
+ reason: 'Testing failure path'
228
+ });
229
+
230
+ for await (const msg of client.take('error-test', { limit: 1 })) {
231
+ try {
232
+ // Simulate processing error
233
+ throw new Error('Simulated processing error');
234
+ } catch (error) {
235
+ console.log(' ❌ Failed to process:', msg.data);
236
+ // Acknowledge with failure and error context
237
+ await client.ack(msg, false, {
238
+ error: error.message,
239
+ retryable: true
240
+ });
241
+ }
242
+ }
243
+
244
+ console.log('✅ Error handling tested\n');
245
+
246
+ console.log('🎉 All features tested successfully!');
247
+
248
+ } catch (error) {
249
+ console.error('❌ Test failed:', error.message);
250
+ process.exit(1);
251
+ } finally {
252
+ await client.close();
253
+ }
254
+ }
255
+
256
+ // Run tests
257
+ main().catch(error => {
258
+ console.error('Fatal error:', error);
259
+ process.exit(1);
260
+ });
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Test script for the new Dashboard Status API endpoints
3
+ *
4
+ * This script demonstrates all 5 new endpoints:
5
+ * 1. GET /api/v1/status - Dashboard overview
6
+ * 2. GET /api/v1/status/queues - List queues with stats
7
+ * 3. GET /api/v1/status/queues/:queue - Queue detail
8
+ * 4. GET /api/v1/status/queues/:queue/messages - Queue messages
9
+ * 5. GET /api/v1/status/analytics - Advanced analytics
10
+ */
11
+
12
+ import { createQueenClient } from '../src/client/queenClient.js';
13
+
14
+ const BASE_URL = process.env.QUEEN_URL || 'http://localhost:3000';
15
+
16
+ // Create client
17
+ const client = createQueenClient({ baseUrl: BASE_URL });
18
+
19
+ // Helper to make GET requests
20
+ async function get(path) {
21
+ const response = await fetch(`${BASE_URL}${path}`, {
22
+ method: 'GET',
23
+ headers: {
24
+ 'Content-Type': 'application/json'
25
+ }
26
+ });
27
+
28
+ if (!response.ok) {
29
+ const error = await response.text();
30
+ throw new Error(`HTTP ${response.status}: ${error}`);
31
+ }
32
+
33
+ return await response.json();
34
+ }
35
+
36
+ // Helper to format JSON output
37
+ function printJson(label, data) {
38
+ console.log(`\n${'='.repeat(80)}`);
39
+ console.log(`📊 ${label}`);
40
+ console.log('='.repeat(80));
41
+ console.log(JSON.stringify(data, null, 2));
42
+ }
43
+
44
+ async function runTests() {
45
+ console.log('🚀 Testing Queen Dashboard Status API\n');
46
+
47
+ try {
48
+ // First, let's create some test data
49
+ console.log('📝 Setting up test data...');
50
+
51
+ // Configure a test queue
52
+ await client.configure({
53
+ queue: 'dashboard-test-queue',
54
+ namespace: 'test-namespace',
55
+ task: 'test-task',
56
+ partitions: 3
57
+ });
58
+
59
+ // Push some test messages
60
+ const messages = [];
61
+ for (let i = 0; i < 20; i++) {
62
+ messages.push({
63
+ queue: 'dashboard-test-queue',
64
+ partition: `partition-${i % 3}`,
65
+ payload: {
66
+ messageNumber: i,
67
+ timestamp: new Date().toISOString(),
68
+ data: `Test message ${i}`
69
+ }
70
+ });
71
+ }
72
+
73
+ await client.pushBatch(messages);
74
+ console.log('✅ Created test queue with 20 messages\n');
75
+
76
+ // Wait a bit for messages to be processed
77
+ await new Promise(resolve => setTimeout(resolve, 1000));
78
+
79
+ // Test 1: Dashboard Status
80
+ console.log('🧪 Test 1: Dashboard Status (GET /api/v1/status)');
81
+ const status = await get('/api/v1/status');
82
+ printJson('Dashboard Status', {
83
+ timeRange: status.timeRange,
84
+ queuesCount: status.queues.length,
85
+ messages: status.messages,
86
+ leases: status.leases,
87
+ throughputSamples: status.throughput.length,
88
+ dlqTotal: status.deadLetterQueue.totalMessages
89
+ });
90
+
91
+ // Test 2: Queues List
92
+ console.log('\n🧪 Test 2: List Queues (GET /api/v1/status/queues)');
93
+ const queues = await get('/api/v1/status/queues?limit=10');
94
+ printJson('Queues List', {
95
+ total: queues.pagination.total,
96
+ queues: queues.queues.map(q => ({
97
+ name: q.name,
98
+ namespace: q.namespace,
99
+ partitions: q.partitions,
100
+ pending: q.messages.pending,
101
+ completed: q.messages.completed
102
+ }))
103
+ });
104
+
105
+ // Test 3: Queue Detail
106
+ console.log('\n🧪 Test 3: Queue Detail (GET /api/v1/status/queues/:queue)');
107
+ const queueDetail = await get('/api/v1/status/queues/dashboard-test-queue');
108
+ printJson('Queue Detail', {
109
+ queue: queueDetail.queue,
110
+ totals: queueDetail.totals,
111
+ partitionCount: queueDetail.partitions.length,
112
+ partitions: queueDetail.partitions.map(p => ({
113
+ name: p.name,
114
+ messages: p.messages,
115
+ cursor: p.cursor
116
+ }))
117
+ });
118
+
119
+ // Test 4: Queue Messages
120
+ console.log('\n🧪 Test 4: Queue Messages (GET /api/v1/status/queues/:queue/messages)');
121
+ const messages_result = await get('/api/v1/status/queues/dashboard-test-queue/messages?limit=5');
122
+ printJson('Queue Messages', {
123
+ total: messages_result.pagination.total,
124
+ showing: messages_result.messages.length,
125
+ messages: messages_result.messages.map(m => ({
126
+ transactionId: m.transactionId,
127
+ partition: m.partition,
128
+ status: m.status,
129
+ createdAt: m.createdAt
130
+ }))
131
+ });
132
+
133
+ // Test 5: Filter by status
134
+ console.log('\n🧪 Test 5: Filter Messages by Status (GET /api/v1/status/queues/:queue/messages?status=pending)');
135
+ const pendingMessages = await get('/api/v1/status/queues/dashboard-test-queue/messages?status=pending&limit=5');
136
+ printJson('Pending Messages', {
137
+ total: pendingMessages.pagination.total,
138
+ messages: pendingMessages.messages.map(m => ({
139
+ transactionId: m.transactionId,
140
+ status: m.status,
141
+ age: m.age
142
+ }))
143
+ });
144
+
145
+ // Test 6: Analytics (last hour with minute interval)
146
+ console.log('\n🧪 Test 6: Analytics (GET /api/v1/status/analytics?interval=minute)');
147
+ const analytics = await get('/api/v1/status/analytics?interval=minute');
148
+ printJson('Analytics Overview', {
149
+ timeRange: analytics.timeRange,
150
+ throughput: {
151
+ totals: analytics.throughput.totals,
152
+ recentSamples: analytics.throughput.timeSeries.slice(0, 3)
153
+ },
154
+ latency: analytics.latency.overall,
155
+ errorRates: analytics.errorRates.overall,
156
+ currentDepth: analytics.queueDepths.current,
157
+ topQueues: analytics.topQueues.slice(0, 3),
158
+ dlqTotal: analytics.deadLetterQueue.total
159
+ });
160
+
161
+ // Test 7: Filter by namespace
162
+ console.log('\n🧪 Test 7: Dashboard Status filtered by namespace (GET /api/v1/status?namespace=test-namespace)');
163
+ const statusFiltered = await get('/api/v1/status?namespace=test-namespace');
164
+ printJson('Filtered Status', {
165
+ queuesCount: statusFiltered.queues.length,
166
+ queues: statusFiltered.queues.map(q => q.name),
167
+ messages: statusFiltered.messages
168
+ });
169
+
170
+ // Test 8: Analytics with time range
171
+ console.log('\n🧪 Test 8: Analytics with custom time range (last 30 minutes)');
172
+ const thirtyMinutesAgo = new Date(Date.now() - 30 * 60 * 1000).toISOString();
173
+ const now = new Date().toISOString();
174
+ const analyticsTimeRange = await get(`/api/v1/status/analytics?from=${thirtyMinutesAgo}&to=${now}&interval=minute`);
175
+ printJson('Analytics (Last 30 min)', {
176
+ timeRange: analyticsTimeRange.timeRange,
177
+ throughputSamples: analyticsTimeRange.throughput.timeSeries.length,
178
+ totals: analyticsTimeRange.throughput.totals
179
+ });
180
+
181
+ console.log('\n' + '='.repeat(80));
182
+ console.log('✅ All tests completed successfully!');
183
+ console.log('='.repeat(80));
184
+
185
+ } catch (error) {
186
+ console.error('\n❌ Test failed:', error.message);
187
+ console.error(error.stack);
188
+ process.exit(1);
189
+ }
190
+ }
191
+
192
+ // Run tests
193
+ runTests().then(() => {
194
+ console.log('\n✨ Dashboard API test complete!');
195
+ process.exit(0);
196
+ }).catch(error => {
197
+ console.error('Fatal error:', error);
198
+ process.exit(1);
199
+ });
200
+
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Test client-provided traceId functionality
5
+ */
6
+
7
+ import { Queen } from '../src/client/client.js';
8
+ import crypto from 'crypto';
9
+
10
+ // Helper to generate a valid UUID v4
11
+ function generateUUID() {
12
+ return crypto.randomUUID();
13
+ }
14
+
15
+ async function main() {
16
+ console.log('🧪 Testing Client-Provided TraceId\n');
17
+
18
+ const client = new Queen({
19
+ baseUrls: ['http://localhost:6632'],
20
+ timeout: 30000
21
+ });
22
+
23
+ try {
24
+ // Configure test queue
25
+ await client.queue('trace-test', {
26
+ leaseTime: 300,
27
+ retryLimit: 3
28
+ });
29
+
30
+ // Test 1: Push with valid UUID traceId
31
+ console.log('1️⃣ Testing valid UUID traceId...');
32
+ const validTraceId = generateUUID();
33
+ console.log(` Generated UUID: ${validTraceId}`);
34
+
35
+ await client.push('trace-test', {
36
+ message: 'Test with valid traceId'
37
+ }, {
38
+ transactionId: 'txn-001',
39
+ traceId: validTraceId
40
+ });
41
+ console.log('✅ Message pushed with valid traceId\n');
42
+
43
+ // Test 2: Push with invalid traceId (should be ignored)
44
+ console.log('2️⃣ Testing invalid traceId (should be ignored)...');
45
+ const invalidTraceId = 'not-a-valid-uuid';
46
+ console.log(` Invalid traceId: ${invalidTraceId}`);
47
+
48
+ await client.push('trace-test', {
49
+ message: 'Test with invalid traceId'
50
+ }, {
51
+ transactionId: 'txn-002',
52
+ traceId: invalidTraceId // Will be ignored, server will generate one
53
+ });
54
+ console.log('✅ Message pushed (invalid traceId ignored)\n');
55
+
56
+ // Test 3: Push without traceId (server generates)
57
+ console.log('3️⃣ Testing without traceId (server generates)...');
58
+ await client.push('trace-test', {
59
+ message: 'Test without traceId'
60
+ }, {
61
+ transactionId: 'txn-003'
62
+ });
63
+ console.log('✅ Message pushed (server generated traceId)\n');
64
+
65
+ // Test 4: Using special properties format
66
+ console.log('4️⃣ Testing special properties format...');
67
+ const anotherValidTraceId = generateUUID();
68
+
69
+ await client.push('trace-test', {
70
+ _payload: { message: 'Special format test' },
71
+ _transactionId: 'txn-004',
72
+ _traceId: anotherValidTraceId
73
+ });
74
+ console.log('✅ Message pushed with special format\n');
75
+
76
+ // Test 5: Batch with mixed traceIds
77
+ console.log('5️⃣ Testing batch with mixed traceIds...');
78
+ const batchTraceId1 = generateUUID();
79
+ const batchTraceId2 = generateUUID();
80
+
81
+ await client.push('trace-test', [
82
+ {
83
+ message: 'Batch message 1',
84
+ transactionId: 'batch-001',
85
+ traceId: batchTraceId1 // Valid UUID
86
+ },
87
+ {
88
+ message: 'Batch message 2',
89
+ transactionId: 'batch-002',
90
+ traceId: 'invalid-trace' // Invalid, will be ignored
91
+ },
92
+ {
93
+ message: 'Batch message 3',
94
+ transactionId: 'batch-003'
95
+ // No traceId, server will generate
96
+ }
97
+ ]);
98
+ console.log('✅ Batch pushed with mixed traceId scenarios\n');
99
+
100
+ // Retrieve and display messages
101
+ console.log('📥 Retrieving messages to verify traceIds...\n');
102
+ let count = 0;
103
+ for await (const msg of client.take('trace-test', { limit: 7 })) {
104
+ count++;
105
+ console.log(`Message ${count}:`);
106
+ console.log(` Transaction ID: ${msg.transactionId}`);
107
+ console.log(` Trace ID: ${msg.traceId}`);
108
+ console.log(` Data: ${JSON.stringify(msg.data)}`);
109
+
110
+ // Check if our valid traceIds were preserved
111
+ if (msg.transactionId === 'txn-001') {
112
+ if (msg.traceId === validTraceId) {
113
+ console.log(' ✅ Client-provided traceId was preserved!');
114
+ } else {
115
+ console.log(' ❌ Client-provided traceId was not preserved');
116
+ }
117
+ }
118
+
119
+ if (msg.transactionId === 'txn-002') {
120
+ if (msg.traceId !== invalidTraceId) {
121
+ console.log(' ✅ Invalid traceId was correctly replaced');
122
+ }
123
+ }
124
+
125
+ await client.ack(msg);
126
+ console.log('');
127
+ }
128
+
129
+ console.log('🎉 TraceId test completed successfully!');
130
+ console.log('\nSummary:');
131
+ console.log('- Valid UUID traceIds are accepted and preserved');
132
+ console.log('- Invalid traceIds are ignored (server generates new ones)');
133
+ console.log('- Missing traceIds are auto-generated by server');
134
+
135
+ } catch (error) {
136
+ console.error('❌ Test failed:', error.message);
137
+ process.exit(1);
138
+ } finally {
139
+ await client.close();
140
+ }
141
+ }
142
+
143
+ // Run test
144
+ main().catch(error => {
145
+ console.error('Fatal error:', error);
146
+ process.exit(1);
147
+ });
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "queen-mq",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "High-performance message queue backed by PostgreSQL",
6
6
  "main": "src/client/index.js",
7
-
8
7
  "scripts": {
9
8
  "start": "node src/server.js",
9
+ "start:cluster": "node src/cluster-server.js",
10
10
  "test:produce": "node test.js produce",
11
11
  "test:consume": "node test.js consume",
12
12
  "test": "node src/test/test.js",
@@ -23,6 +23,8 @@
23
23
  "init": "node init-db.js"
24
24
  },
25
25
  "dependencies": {
26
+ "axios": "^1.12.2",
27
+ "chart.js": "^4.5.1",
26
28
  "pg": "^8.13.3",
27
29
  "uuid": "^13.0.0",
28
30
  "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.52.0",
@@ -31,6 +33,17 @@
31
33
  "engines": {
32
34
  "node": ">=22.0.0"
33
35
  },
34
- "author": "",
35
- "license": "ISC"
36
+ "author": "Smartness",
37
+ "license": "Apache-2.0",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/smartpricing/queen"
41
+ },
42
+ "keywords": [
43
+ "message-queue",
44
+ "queue",
45
+ "broker",
46
+ "message-queue-system",
47
+ "fifo"
48
+ ]
36
49
  }
package/server.log ADDED
@@ -0,0 +1 @@
1
+ zsh: command not found: node