queen-mq 0.1.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 (90) hide show
  1. package/API.md +1116 -0
  2. package/CACHE.md +519 -0
  3. package/DASHBOARD-V3.md +478 -0
  4. package/DASHBOARD.md +382 -0
  5. package/MOD_QUEUE.md +453 -0
  6. package/PARTITION_LOCKING_DESIGN.md +989 -0
  7. package/PLAN.md +707 -0
  8. package/QUERY_ANALSYS.md +72 -0
  9. package/QUEUE_BUS.md +334 -0
  10. package/README.md +1495 -0
  11. package/V2-PLAN.md +236 -0
  12. package/assets/dashboard.png +0 -0
  13. package/dashboard/.vscode/extensions.json +3 -0
  14. package/dashboard/README.md +5 -0
  15. package/dashboard/index.html +14 -0
  16. package/dashboard/package-lock.json +1458 -0
  17. package/dashboard/package.json +25 -0
  18. package/dashboard/public/vite.svg +1 -0
  19. package/dashboard/src/App.vue +29 -0
  20. package/dashboard/src/assets/styles/main.css +908 -0
  21. package/dashboard/src/assets/vue.svg +1 -0
  22. package/dashboard/src/components/cards/MetricCard.vue +298 -0
  23. package/dashboard/src/components/charts/QueueDepthChart.vue +276 -0
  24. package/dashboard/src/components/charts/QueueLagChart.vue +436 -0
  25. package/dashboard/src/components/charts/ThroughputChart.vue +302 -0
  26. package/dashboard/src/components/common/ActivityFeed.vue +251 -0
  27. package/dashboard/src/components/layout/AppHeader.vue +208 -0
  28. package/dashboard/src/components/layout/AppLayout.vue +88 -0
  29. package/dashboard/src/components/layout/AppSidebar.vue +261 -0
  30. package/dashboard/src/main.js +44 -0
  31. package/dashboard/src/router.js +54 -0
  32. package/dashboard/src/services/api.js +187 -0
  33. package/dashboard/src/services/websocket.js +167 -0
  34. package/dashboard/src/utils/constants.js +56 -0
  35. package/dashboard/src/utils/helpers.js +118 -0
  36. package/dashboard/src/views/Analytics.vue +912 -0
  37. package/dashboard/src/views/Dashboard.vue +906 -0
  38. package/dashboard/src/views/Messages.vue +437 -0
  39. package/dashboard/src/views/QueueDetail.vue +501 -0
  40. package/dashboard/src/views/Queues.vue +333 -0
  41. package/dashboard/vite.config.js +30 -0
  42. package/debug-namespace.js +110 -0
  43. package/docs/long-polling.md +159 -0
  44. package/docs/multi-server-cache-solutions.md +185 -0
  45. package/docs/performance-tuning.md +222 -0
  46. package/examples/bus-mode.js +239 -0
  47. package/examples/continuous-consumer-optimized.js +215 -0
  48. package/examples/continuous-consumer.js +159 -0
  49. package/examples/continuous-producer.js +343 -0
  50. package/examples/mixed-mode.js +277 -0
  51. package/examples/multi-server-test.js +305 -0
  52. package/examples/single.js +64 -0
  53. package/examples/smartchat-dealyed.js +42 -0
  54. package/examples/smartchat.js +52 -0
  55. package/examples/test-cache-invalidation.js +119 -0
  56. package/examples/test-cache-multi-server.js +245 -0
  57. package/examples/test-minimal-client.js +112 -0
  58. package/examples/test-queue-creation-policy.js +137 -0
  59. package/init-db.js +20 -0
  60. package/package.json +36 -0
  61. package/src/client/client.js +291 -0
  62. package/src/client/index.js +6 -0
  63. package/src/client/queenClient.js +513 -0
  64. package/src/client/utils/http.js +172 -0
  65. package/src/client/utils/loadBalancer.js +152 -0
  66. package/src/client/utils/retry.js +35 -0
  67. package/src/config.js +215 -0
  68. package/src/database/connection.js +103 -0
  69. package/src/database/poolManager.js +192 -0
  70. package/src/database/schema-v2.sql +214 -0
  71. package/src/managers/eventManager.js +59 -0
  72. package/src/managers/queueManagerOptimized.js +1512 -0
  73. package/src/managers/resourceCache.js +96 -0
  74. package/src/managers/systemEventManager.js +127 -0
  75. package/src/routes/ack.js +26 -0
  76. package/src/routes/analytics.js +812 -0
  77. package/src/routes/configure.js +46 -0
  78. package/src/routes/messages.js +298 -0
  79. package/src/routes/pop.js +85 -0
  80. package/src/routes/push.js +28 -0
  81. package/src/routes/resources.js +296 -0
  82. package/src/server.js +1286 -0
  83. package/src/services/encryptionService.js +82 -0
  84. package/src/services/evictionService.js +131 -0
  85. package/src/services/retentionService.js +129 -0
  86. package/src/services/startupSync.js +35 -0
  87. package/src/test/test.js +4521 -0
  88. package/src/utils/logger.js +44 -0
  89. package/src/utils/uuid.js +5 -0
  90. package/src/websocket/wsServer.js +221 -0
@@ -0,0 +1,208 @@
1
+ <template>
2
+ <header class="app-header">
3
+ <div class="header-content">
4
+ <div class="header-left">
5
+ <Button
6
+ icon="pi pi-bars"
7
+ class="p-button-text p-button-plain menu-toggle"
8
+ @click="toggleSidebar"
9
+ />
10
+ <div class="logo">
11
+ <i class="pi pi-crown logo-icon"></i>
12
+ <span class="logo-text">Queen Dashboard</span>
13
+ </div>
14
+ </div>
15
+
16
+ <div class="header-center">
17
+ <h1 class="page-title">{{ pageTitle }}</h1>
18
+ </div>
19
+
20
+ <div class="header-right">
21
+ <div class="connection-status" :class="connectionClass">
22
+ <span class="indicator"></span>
23
+ <span>{{ connectionText }}</span>
24
+ </div>
25
+
26
+ <Button
27
+ icon="pi pi-refresh"
28
+ class="p-button-text p-button-plain header-btn"
29
+ v-tooltip="'Refresh'"
30
+ @click="handleRefresh"
31
+ />
32
+
33
+ <Button
34
+ icon="pi pi-cog"
35
+ class="p-button-text p-button-plain header-btn"
36
+ v-tooltip="'Settings'"
37
+ />
38
+ </div>
39
+ </div>
40
+ </header>
41
+ </template>
42
+
43
+ <script setup>
44
+ import { ref, computed, watch, inject } from 'vue'
45
+ import { useRoute } from 'vue-router'
46
+ import Button from 'primevue/button'
47
+ import websocket from '../../services/websocket.js'
48
+
49
+ const route = useRoute()
50
+ const toggleSidebar = inject('toggleSidebar')
51
+
52
+ // Connection status
53
+ const isConnected = ref(websocket.getConnectionStatus())
54
+
55
+ // Watch for connection changes
56
+ websocket.on('connected', () => {
57
+ isConnected.value = true
58
+ })
59
+
60
+ websocket.on('disconnected', () => {
61
+ isConnected.value = false
62
+ })
63
+
64
+ const connectionClass = computed(() => ({
65
+ connected: isConnected.value,
66
+ disconnected: !isConnected.value
67
+ }))
68
+
69
+ const connectionText = computed(() =>
70
+ isConnected.value ? 'Connected' : 'Disconnected'
71
+ )
72
+
73
+ // Page title from route
74
+ const pageTitle = computed(() => route.meta.title || 'Dashboard')
75
+
76
+ // Refresh handler
77
+ const handleRefresh = () => {
78
+ window.location.reload()
79
+ }
80
+ </script>
81
+
82
+ <style scoped>
83
+ .app-header {
84
+ height: 64px;
85
+ background: rgba(28, 25, 23, 0.95); /* stone-900 with opacity */
86
+ backdrop-filter: blur(12px);
87
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
88
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
89
+ position: sticky;
90
+ top: 0;
91
+ z-index: 100;
92
+ }
93
+
94
+ .header-content {
95
+ height: 100%;
96
+ display: flex;
97
+ align-items: center;
98
+ justify-content: space-between;
99
+ padding: 0 1.5rem;
100
+ }
101
+
102
+ .header-left,
103
+ .header-right {
104
+ display: flex;
105
+ align-items: center;
106
+ gap: 1rem;
107
+ }
108
+
109
+ .menu-toggle {
110
+ display: flex !important;
111
+ margin-right: 1rem;
112
+ color: var(--surface-600) !important;
113
+ }
114
+
115
+ .menu-toggle:hover {
116
+ color: var(--primary-500) !important;
117
+ background: rgba(236, 72, 153, 0.1) !important;
118
+ }
119
+
120
+ .logo {
121
+ display: flex;
122
+ align-items: center;
123
+ gap: 0.5rem;
124
+ font-size: 1rem;
125
+ font-weight: 600;
126
+ background: var(--gradient-primary);
127
+ -webkit-background-clip: text;
128
+ -webkit-text-fill-color: transparent;
129
+ background-clip: text;
130
+ }
131
+
132
+ .logo-icon {
133
+ font-size: 1.25rem;
134
+ color: var(--primary-500);
135
+ }
136
+
137
+ .page-title {
138
+ font-size: 1.125rem;
139
+ font-weight: 600;
140
+ color: var(--surface-700);
141
+ margin: 0;
142
+ }
143
+
144
+ .connection-status {
145
+ display: inline-flex;
146
+ align-items: center;
147
+ gap: 0.25rem;
148
+ padding: 0.25rem 0.75rem;
149
+ border-radius: 9999px;
150
+ font-size: 0.875rem;
151
+ font-weight: 500;
152
+ transition: all 0.3s ease;
153
+ }
154
+
155
+ .connection-status.connected {
156
+ background: rgba(16, 185, 129, 0.15);
157
+ color: var(--success-color);
158
+ }
159
+
160
+ .connection-status.disconnected {
161
+ background: rgba(239, 68, 68, 0.15);
162
+ color: var(--danger-color);
163
+ }
164
+
165
+ .connection-status .indicator {
166
+ width: 8px;
167
+ height: 8px;
168
+ border-radius: 50%;
169
+ background: currentColor;
170
+ }
171
+
172
+ .connection-status.connected .indicator {
173
+ animation: pulse 2s infinite;
174
+ }
175
+
176
+ @keyframes pulse {
177
+ 0%, 100% {
178
+ opacity: 1;
179
+ }
180
+ 50% {
181
+ opacity: 0.5;
182
+ }
183
+ }
184
+
185
+ .header-btn {
186
+ color: var(--surface-500) !important;
187
+ }
188
+
189
+ .header-btn:hover {
190
+ color: var(--primary-500) !important;
191
+ background: rgba(236, 72, 153, 0.1) !important;
192
+ }
193
+
194
+ /* Responsive */
195
+ @media (max-width: 768px) {
196
+ .menu-toggle {
197
+ display: flex;
198
+ }
199
+
200
+ .header-center {
201
+ display: none;
202
+ }
203
+
204
+ .logo-text {
205
+ display: none;
206
+ }
207
+ }
208
+ </style>
@@ -0,0 +1,88 @@
1
+ <template>
2
+ <div class="app-layout">
3
+ <div class="layout-main">
4
+ <AppSidebar v-model:visible="sidebarVisible" />
5
+ <div class="layout-content">
6
+ <router-view />
7
+ </div>
8
+ </div>
9
+ </div>
10
+ </template>
11
+
12
+ <script setup>
13
+ import { ref, provide } from 'vue'
14
+ import AppSidebar from './AppSidebar.vue'
15
+
16
+ const sidebarVisible = ref(false)
17
+
18
+ // Provide sidebar visibility to header
19
+ provide('toggleSidebar', () => {
20
+ sidebarVisible.value = !sidebarVisible.value
21
+ })
22
+ </script>
23
+
24
+ <style scoped>
25
+ .app-layout {
26
+ min-height: 100vh;
27
+ display: flex;
28
+ flex-direction: column;
29
+ background: var(--surface-0);
30
+ }
31
+
32
+ .layout-main {
33
+ flex: 1;
34
+ display: flex;
35
+ position: relative;
36
+ }
37
+
38
+ .layout-content {
39
+ flex: 1;
40
+ padding: 2rem;
41
+ margin-left: 80px;
42
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
43
+ background: #0c0a09;
44
+ min-height: 100vh;
45
+ position: relative;
46
+ }
47
+
48
+ .layout-main:has(.sidebar-wrapper.collapsed) .layout-content {
49
+ margin-left: 80px;
50
+ }
51
+
52
+ .layout-main:has(.sidebar-wrapper.expanded) .layout-content {
53
+ margin-left: 250px;
54
+ }
55
+
56
+ /* Fade transition */
57
+ .fade-enter-active,
58
+ .fade-leave-active {
59
+ transition: opacity 0.2s ease;
60
+ }
61
+
62
+ .fade-enter-from,
63
+ .fade-leave-to {
64
+ opacity: 0;
65
+ }
66
+
67
+ /* Loading container */
68
+ .loading-container {
69
+ display: flex;
70
+ justify-content: center;
71
+ align-items: center;
72
+ min-height: 400px;
73
+ color: #ec4899;
74
+ }
75
+
76
+ /* Responsive */
77
+ @media (max-width: 768px) {
78
+ .layout-content {
79
+ margin-left: 0 !important;
80
+ padding: 0.75rem;
81
+ background: var(--surface-0);
82
+ }
83
+
84
+ .layout-main:has(.sidebar-wrapper:not(.collapsed)) .layout-content {
85
+ margin-left: 0 !important;
86
+ }
87
+ }
88
+ </style>
@@ -0,0 +1,261 @@
1
+ <template>
2
+ <div class="sidebar-wrapper" :class="{ collapsed: !visible, expanded: visible }">
3
+ <div class="sidebar-content">
4
+ <!-- Logo -->
5
+ <div class="sidebar-logo">
6
+ <div class="logo-icon">
7
+ <i class="pi pi-bolt"></i>
8
+ </div>
9
+ </div>
10
+
11
+ <!-- Navigation -->
12
+ <nav class="sidebar-nav">
13
+ <router-link
14
+ v-for="item in menuItems"
15
+ :key="item.path"
16
+ :to="item.path"
17
+ class="nav-item"
18
+ :class="{ active: isActive(item.path) }"
19
+ v-tooltip.right="!visible ? item.label : ''"
20
+ >
21
+ <div class="nav-icon">
22
+ <i :class="item.icon"></i>
23
+ </div>
24
+ </router-link>
25
+ </nav>
26
+
27
+ <!-- Footer -->
28
+ <div class="sidebar-footer">
29
+ <div class="nav-item settings-item">
30
+ <div class="nav-icon">
31
+ <i class="pi pi-cog"></i>
32
+ </div>
33
+ </div>
34
+ <div class="user-item">
35
+ <div class="user-avatar">
36
+ <i class="pi pi-user"></i>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ </div>
42
+ </template>
43
+
44
+ <script setup>
45
+ import { ref, computed, onMounted, onUnmounted } from 'vue'
46
+ import { useRoute } from 'vue-router'
47
+
48
+ const props = defineProps({
49
+ visible: {
50
+ type: Boolean,
51
+ default: false
52
+ }
53
+ })
54
+
55
+ const emit = defineEmits(['update:visible'])
56
+ const route = useRoute()
57
+
58
+ const visible = computed({
59
+ get: () => props.visible,
60
+ set: (val) => emit('update:visible', val)
61
+ })
62
+
63
+ // Menu items - icon only sidebar
64
+ const menuItems = ref([
65
+ {
66
+ path: '/',
67
+ label: 'Dashboard',
68
+ icon: 'pi pi-home'
69
+ },
70
+ {
71
+ path: '/queues',
72
+ label: 'Queues',
73
+ icon: 'pi pi-inbox'
74
+ },
75
+ {
76
+ path: '/analytics',
77
+ label: 'Analytics',
78
+ icon: 'pi pi-chart-line'
79
+ },
80
+ {
81
+ path: '/messages',
82
+ label: 'Messages',
83
+ icon: 'pi pi-envelope'
84
+ },
85
+ ])
86
+
87
+ // Check if route is active
88
+ const isActive = (path) => {
89
+ if (path === '/') {
90
+ return route.path === '/'
91
+ }
92
+ return route.path.startsWith(path)
93
+ }
94
+ </script>
95
+
96
+ <style scoped>
97
+ .sidebar-wrapper {
98
+ position: fixed;
99
+ left: 0;
100
+ top: 0;
101
+ width: 80px;
102
+ height: 100vh;
103
+ background: #0c0a09;
104
+ border-right: 1px solid rgba(255, 255, 255, 0.05);
105
+ transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
106
+ z-index: 100;
107
+ display: flex;
108
+ flex-direction: column;
109
+ }
110
+
111
+ .sidebar-wrapper.expanded {
112
+ width: 250px;
113
+ }
114
+
115
+ .sidebar-content {
116
+ height: 100%;
117
+ display: flex;
118
+ flex-direction: column;
119
+ padding: 1rem 0;
120
+ }
121
+
122
+ /* Logo */
123
+ .sidebar-logo {
124
+ display: flex;
125
+ align-items: center;
126
+ justify-content: center;
127
+ padding: 1rem 0 2rem 0;
128
+ margin-bottom: 1rem;
129
+ }
130
+
131
+ .logo-icon {
132
+ width: 48px;
133
+ height: 48px;
134
+ border-radius: 12px;
135
+ background: linear-gradient(135deg, #ec4899 0%, #db2777 100%);
136
+ display: flex;
137
+ align-items: center;
138
+ justify-content: center;
139
+ color: white;
140
+ font-size: 1.5rem;
141
+ box-shadow: 0 4px 12px rgba(236, 72, 153, 0.3);
142
+ }
143
+
144
+ /* Navigation */
145
+ .sidebar-nav {
146
+ flex: 1;
147
+ display: flex;
148
+ flex-direction: column;
149
+ gap: 0.5rem;
150
+ padding: 0 1rem;
151
+ }
152
+
153
+ .nav-item {
154
+ display: flex;
155
+ align-items: center;
156
+ justify-content: center;
157
+ width: 48px;
158
+ height: 48px;
159
+ margin: 0 auto;
160
+ color: #78716c;
161
+ text-decoration: none;
162
+ transition: all 0.2s ease;
163
+ position: relative;
164
+ border-radius: 12px;
165
+ cursor: pointer;
166
+ }
167
+
168
+ .nav-item:hover {
169
+ color: #d6d3d1;
170
+ background: rgba(236, 72, 153, 0.1);
171
+ }
172
+
173
+ .nav-item.active {
174
+ color: #ec4899;
175
+ background: rgba(236, 72, 153, 0.15);
176
+ }
177
+
178
+ .nav-item.active::before {
179
+ content: '';
180
+ position: absolute;
181
+ left: -1rem;
182
+ top: 50%;
183
+ transform: translateY(-50%);
184
+ width: 3px;
185
+ height: 24px;
186
+ background: #ec4899;
187
+ border-radius: 0 2px 2px 0;
188
+ }
189
+
190
+ .nav-icon {
191
+ font-size: 1.25rem;
192
+ display: flex;
193
+ align-items: center;
194
+ justify-content: center;
195
+ }
196
+
197
+ /* Footer */
198
+ .sidebar-footer {
199
+ display: flex;
200
+ flex-direction: column;
201
+ align-items: center;
202
+ gap: 1rem;
203
+ padding: 1rem 0;
204
+ margin-top: auto;
205
+ }
206
+
207
+ .settings-item {
208
+ margin: 0;
209
+ }
210
+
211
+ .user-item {
212
+ display: flex;
213
+ align-items: center;
214
+ justify-content: center;
215
+ }
216
+
217
+ .user-avatar {
218
+ width: 36px;
219
+ height: 36px;
220
+ border-radius: 10px;
221
+ background: #1c1917;
222
+ border: 2px solid #292524;
223
+ display: flex;
224
+ align-items: center;
225
+ justify-content: center;
226
+ color: #78716c;
227
+ font-size: 1rem;
228
+ cursor: pointer;
229
+ transition: all 0.2s ease;
230
+ }
231
+
232
+ .user-avatar:hover {
233
+ border-color: #ec4899;
234
+ color: #ec4899;
235
+ }
236
+
237
+ /* Expanded state styles */
238
+ .sidebar-wrapper.expanded .nav-item {
239
+ justify-content: flex-start;
240
+ width: auto;
241
+ padding: 0 1rem;
242
+ gap: 1rem;
243
+ }
244
+
245
+ .sidebar-wrapper.expanded .nav-item::after {
246
+ content: attr(data-label);
247
+ font-size: 0.875rem;
248
+ font-weight: 500;
249
+ }
250
+
251
+ /* Mobile responsive */
252
+ @media (max-width: 768px) {
253
+ .sidebar-wrapper {
254
+ transform: translateX(-100%);
255
+ }
256
+
257
+ .sidebar-wrapper.expanded {
258
+ transform: translateX(0);
259
+ }
260
+ }
261
+ </style>
@@ -0,0 +1,44 @@
1
+ import { createApp } from 'vue'
2
+ import { createRouter, createWebHistory } from 'vue-router'
3
+ import PrimeVue from 'primevue/config'
4
+ import Aura from '@primevue/themes/aura'
5
+ import ToastService from 'primevue/toastservice'
6
+ import ConfirmationService from 'primevue/confirmationservice'
7
+ import Tooltip from 'primevue/tooltip'
8
+
9
+ // Import global styles
10
+ import 'primeicons/primeicons.css'
11
+ import './assets/styles/main.css'
12
+
13
+ import App from './App.vue'
14
+ import router from './router.js'
15
+
16
+ const app = createApp(App)
17
+
18
+ // Configure PrimeVue with Aura Dark Theme
19
+ app.use(PrimeVue, {
20
+ theme: {
21
+ preset: Aura,
22
+ options: {
23
+ primary: 'rose',
24
+ darkModeSelector: '.dark',
25
+ cssLayer: {
26
+ name: 'primevue',
27
+ order: 'primevue'
28
+ }
29
+ }
30
+ },
31
+ ripple: true
32
+ })
33
+
34
+ // Set dark mode by default
35
+ document.documentElement.classList.add('dark')
36
+
37
+ app.use(ToastService)
38
+ app.use(ConfirmationService)
39
+ app.use(router)
40
+
41
+ // Register directives
42
+ app.directive('tooltip', Tooltip)
43
+
44
+ app.mount('#app')
@@ -0,0 +1,54 @@
1
+ import { createRouter, createWebHistory } from 'vue-router'
2
+
3
+ // Lazy load views for better performance
4
+ const Dashboard = () => import('./views/Dashboard.vue')
5
+ const Queues = () => import('./views/Queues.vue')
6
+ const QueueDetail = () => import('./views/QueueDetail.vue')
7
+ const Analytics = () => import('./views/Analytics.vue')
8
+ const Messages = () => import('./views/Messages.vue')
9
+
10
+ const routes = [
11
+ {
12
+ path: '/',
13
+ name: 'dashboard',
14
+ component: Dashboard,
15
+ meta: { title: 'Dashboard' }
16
+ },
17
+ {
18
+ path: '/queues',
19
+ name: 'queues',
20
+ component: Queues,
21
+ meta: { title: 'Queues' }
22
+ },
23
+ {
24
+ path: '/queues/:name',
25
+ name: 'queue-detail',
26
+ component: QueueDetail,
27
+ meta: { title: 'Queue Detail' }
28
+ },
29
+ {
30
+ path: '/analytics',
31
+ name: 'analytics',
32
+ component: Analytics,
33
+ meta: { title: 'Analytics' }
34
+ },
35
+ {
36
+ path: '/messages',
37
+ name: 'messages',
38
+ component: Messages,
39
+ meta: { title: 'Messages' }
40
+ },
41
+ ]
42
+
43
+ const router = createRouter({
44
+ history: createWebHistory('/dashboard/'),
45
+ routes
46
+ })
47
+
48
+ // Update page title on route change
49
+ router.beforeEach((to, from, next) => {
50
+ document.title = `${to.meta.title || 'Queen'} - Queen Dashboard`
51
+ next()
52
+ })
53
+
54
+ export default router