@usermetrics/queuebit 1.0.1 → 1.0.6
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.
- package/README.md +92 -118
- package/docs/API.md +116 -376
- package/docs/EXAMPLES.md +94 -225
- package/docs/QUICKSTART.md +64 -65
- package/package.json +4 -5
- package/src/client.js +0 -3
- package/src/index.js +5 -5
package/docs/EXAMPLES.md
CHANGED
|
@@ -4,164 +4,120 @@ Practical examples for common use cases.
|
|
|
4
4
|
|
|
5
5
|
## Table of Contents
|
|
6
6
|
|
|
7
|
+
- [Runnable Examples](#runnable-examples)
|
|
7
8
|
- [Chat Application](#chat-application)
|
|
8
|
-
- [Task Queue
|
|
9
|
+
- [Task Queue with Load Balancer](#task-queue-with-load-balancer)
|
|
9
10
|
- [Real-time Analytics](#real-time-analytics)
|
|
10
11
|
- [Microservices Communication](#microservices-communication)
|
|
11
12
|
- [Event Sourcing](#event-sourcing)
|
|
12
13
|
|
|
13
14
|
---
|
|
14
15
|
|
|
15
|
-
##
|
|
16
|
+
## Runnable Examples
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
| File | Description |
|
|
19
|
+
|------|-------------|
|
|
20
|
+
| [`examples/server2server.js`](../examples/server2server.js) | HTTP server using an external QueueBit server |
|
|
21
|
+
| [`examples/inprocessserver.js`](../examples/inprocessserver.js) | HTTP server with QueueBit running in-process |
|
|
22
|
+
| [`examples/queuegroup.js`](../examples/queuegroup.js) | Load balancer demo with 3 workers |
|
|
23
|
+
| [`examples/qpanel.html`](../examples/qpanel.html) | Browser dashboard with publish, subscribe, load balancer, and perf testing |
|
|
24
|
+
| [`test/test-harness.js`](../test/test-harness.js) | Full test suite |
|
|
18
25
|
|
|
19
|
-
|
|
20
|
-
const { QueueBitServer, QueueBitClient } = require('queuebit');
|
|
26
|
+
---
|
|
21
27
|
|
|
22
|
-
|
|
23
|
-
const server = new QueueBitServer({ port: 3000 });
|
|
28
|
+
## Chat Application
|
|
24
29
|
|
|
25
|
-
|
|
30
|
+
```javascript
|
|
31
|
+
const { QueueBitServer } = require('./src/server');
|
|
32
|
+
const { QueueBitClient } = require('./src/client-node');
|
|
33
|
+
|
|
34
|
+
const server = new QueueBitServer({ port: 3333 });
|
|
26
35
|
const username = process.argv[2] || 'Anonymous';
|
|
27
|
-
const client = new QueueBitClient('http://localhost:
|
|
36
|
+
const client = new QueueBitClient('http://localhost:3333');
|
|
28
37
|
|
|
29
|
-
// Receive messages
|
|
30
38
|
await client.subscribe((message) => {
|
|
31
39
|
const { user, text, timestamp } = message.data;
|
|
32
|
-
|
|
33
|
-
console.log(`[${time}] ${user}: ${text}`);
|
|
40
|
+
console.log(`[${new Date(timestamp).toLocaleTimeString()}] ${user}: ${text}`);
|
|
34
41
|
}, { subject: 'chat' });
|
|
35
42
|
|
|
36
|
-
// Send messages
|
|
37
43
|
const readline = require('readline');
|
|
38
|
-
const rl = readline.createInterface({
|
|
39
|
-
input: process.stdin,
|
|
40
|
-
output: process.stdout
|
|
41
|
-
});
|
|
44
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
42
45
|
|
|
43
46
|
rl.on('line', async (text) => {
|
|
44
|
-
await client.publish(
|
|
45
|
-
{ user: username, text, timestamp: new Date() },
|
|
46
|
-
{ subject: 'chat' }
|
|
47
|
-
);
|
|
47
|
+
await client.publish({ user: username, text, timestamp: new Date() }, { subject: 'chat' });
|
|
48
48
|
});
|
|
49
|
-
|
|
50
|
-
console.log(`Joined chat as ${username}`);
|
|
51
49
|
```
|
|
52
50
|
|
|
53
51
|
---
|
|
54
52
|
|
|
55
|
-
## Task Queue
|
|
53
|
+
## Task Queue with Load Balancer
|
|
56
54
|
|
|
57
|
-
|
|
55
|
+
Messages are distributed round-robin — each message goes to exactly one worker.
|
|
58
56
|
|
|
59
57
|
```javascript
|
|
60
|
-
|
|
61
|
-
const { QueueBitClient } = require('
|
|
62
|
-
const client = new QueueBitClient('http://localhost:3000');
|
|
63
|
-
|
|
64
|
-
async function produceTasks() {
|
|
65
|
-
for (let i = 1; i <= 100; i++) {
|
|
66
|
-
await client.publish(
|
|
67
|
-
{
|
|
68
|
-
taskId: i,
|
|
69
|
-
type: 'process-image',
|
|
70
|
-
imageUrl: `https://example.com/img${i}.jpg`,
|
|
71
|
-
priority: i % 10 === 0 ? 'high' : 'normal'
|
|
72
|
-
},
|
|
73
|
-
{ subject: 'tasks' }
|
|
74
|
-
);
|
|
75
|
-
console.log(`Task ${i} queued`);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
58
|
+
const { QueueBitServer } = require('./src/server');
|
|
59
|
+
const { QueueBitClient } = require('./src/client-node');
|
|
78
60
|
|
|
79
|
-
|
|
61
|
+
const server = new QueueBitServer({ port: 3333 });
|
|
80
62
|
|
|
81
|
-
|
|
82
|
-
const { QueueBitClient } = require('queuebit');
|
|
83
|
-
const workerId = process.argv[2] || '1';
|
|
84
|
-
const client = new QueueBitClient('http://localhost:3000');
|
|
63
|
+
async function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
85
64
|
|
|
86
|
-
await
|
|
87
|
-
const { taskId, type, imageUrl, priority } = message.data;
|
|
88
|
-
|
|
89
|
-
console.log(`Worker ${workerId} processing task ${taskId} (${priority})`);
|
|
90
|
-
|
|
91
|
-
// Simulate work
|
|
92
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
93
|
-
|
|
94
|
-
// Publish result
|
|
95
|
-
await client.publish(
|
|
96
|
-
{ taskId, workerId, status: 'completed', timestamp: new Date() },
|
|
97
|
-
{ subject: 'results' }
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
console.log(`Worker ${workerId} completed task ${taskId}`);
|
|
101
|
-
}, { subject: 'tasks', queue: 'workers' });
|
|
65
|
+
await sleep(500);
|
|
102
66
|
|
|
103
|
-
|
|
67
|
+
const worker1 = new QueueBitClient('http://localhost:3333');
|
|
68
|
+
const worker2 = new QueueBitClient('http://localhost:3333');
|
|
69
|
+
const worker3 = new QueueBitClient('http://localhost:3333');
|
|
70
|
+
|
|
71
|
+
await sleep(500);
|
|
72
|
+
|
|
73
|
+
// Each worker has a unique queue name → unique load balancer ID
|
|
74
|
+
await worker1.subscribe((msg) => {
|
|
75
|
+
console.log(`LB#${msg.loadBalancerId} Worker1:`, msg.data);
|
|
76
|
+
}, { subject: 'tasks', queue: 'worker-1' });
|
|
77
|
+
|
|
78
|
+
await worker2.subscribe((msg) => {
|
|
79
|
+
console.log(`LB#${msg.loadBalancerId} Worker2:`, msg.data);
|
|
80
|
+
}, { subject: 'tasks', queue: 'worker-2' });
|
|
81
|
+
|
|
82
|
+
await worker3.subscribe((msg) => {
|
|
83
|
+
console.log(`LB#${msg.loadBalancerId} Worker3:`, msg.data);
|
|
84
|
+
}, { subject: 'tasks', queue: 'worker-3' });
|
|
85
|
+
|
|
86
|
+
const publisher = new QueueBitClient('http://localhost:3333');
|
|
87
|
+
await sleep(500);
|
|
88
|
+
|
|
89
|
+
// Messages cycle: LB#1 → LB#2 → LB#3 → LB#1 → ...
|
|
90
|
+
for (let i = 1; i <= 9; i++) {
|
|
91
|
+
await publisher.publish({ taskId: i }, { subject: 'tasks' });
|
|
92
|
+
await sleep(100);
|
|
93
|
+
}
|
|
104
94
|
```
|
|
105
95
|
|
|
96
|
+
See [`examples/queuegroup.js`](../examples/queuegroup.js) for the runnable version.
|
|
97
|
+
|
|
106
98
|
---
|
|
107
99
|
|
|
108
100
|
## Real-time Analytics
|
|
109
101
|
|
|
110
|
-
Collect and aggregate analytics events.
|
|
111
|
-
|
|
112
102
|
```javascript
|
|
113
|
-
|
|
114
|
-
const { QueueBitServer, QueueBitClient } = require('queuebit');
|
|
115
|
-
|
|
116
|
-
const server = new QueueBitServer({ port: 3000 });
|
|
117
|
-
const collector = new QueueBitClient('http://localhost:3000');
|
|
118
|
-
|
|
119
|
-
const stats = {
|
|
120
|
-
pageViews: 0,
|
|
121
|
-
clicks: 0,
|
|
122
|
-
purchases: 0
|
|
123
|
-
};
|
|
103
|
+
const stats = { pageViews: 0, clicks: 0, purchases: 0 };
|
|
124
104
|
|
|
125
105
|
await collector.subscribe((message) => {
|
|
126
|
-
const { eventType
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
stats.pageViews++;
|
|
131
|
-
break;
|
|
132
|
-
case 'click':
|
|
133
|
-
stats.clicks++;
|
|
134
|
-
break;
|
|
135
|
-
case 'purchase':
|
|
136
|
-
stats.purchases++;
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
console.log('Stats:', stats);
|
|
106
|
+
const { eventType } = message.data;
|
|
107
|
+
if (eventType === 'pageview') stats.pageViews++;
|
|
108
|
+
if (eventType === 'click') stats.clicks++;
|
|
109
|
+
if (eventType === 'purchase') stats.purchases++;
|
|
141
110
|
}, { subject: 'analytics' });
|
|
142
111
|
|
|
143
|
-
// Display stats every 5 seconds
|
|
144
112
|
setInterval(() => {
|
|
145
|
-
console.log('
|
|
146
|
-
console.log(`Page Views: ${stats.pageViews}`);
|
|
147
|
-
console.log(`Clicks: ${stats.clicks}`);
|
|
148
|
-
console.log(`Purchases: ${stats.purchases}`);
|
|
149
|
-
console.log('===========================\n');
|
|
113
|
+
console.log('Stats:', stats);
|
|
150
114
|
}, 5000);
|
|
151
115
|
|
|
152
|
-
//
|
|
153
|
-
const client = new QueueBitClient('http://localhost:3000');
|
|
154
|
-
|
|
116
|
+
// Simulate events
|
|
155
117
|
setInterval(async () => {
|
|
156
118
|
const events = ['pageview', 'click', 'purchase'];
|
|
157
|
-
const eventType = events[Math.floor(Math.random() * events.length)];
|
|
158
|
-
|
|
159
119
|
await client.publish(
|
|
160
|
-
{
|
|
161
|
-
eventType,
|
|
162
|
-
data: { userId: Math.floor(Math.random() * 1000) },
|
|
163
|
-
timestamp: new Date()
|
|
164
|
-
},
|
|
120
|
+
{ eventType: events[Math.floor(Math.random() * 3)] },
|
|
165
121
|
{ subject: 'analytics' }
|
|
166
122
|
);
|
|
167
123
|
}, 100);
|
|
@@ -171,65 +127,33 @@ setInterval(async () => {
|
|
|
171
127
|
|
|
172
128
|
## Microservices Communication
|
|
173
129
|
|
|
174
|
-
Services communicate through QueueBit.
|
|
175
|
-
|
|
176
130
|
```javascript
|
|
177
131
|
// user-service.js
|
|
178
|
-
const { QueueBitClient } = require('queuebit');
|
|
179
|
-
const client = new QueueBitClient('http://localhost:3000');
|
|
180
|
-
|
|
181
|
-
// Listen for user creation requests
|
|
182
132
|
await client.subscribe(async (message) => {
|
|
183
133
|
const { requestId, username, email } = message.data;
|
|
184
|
-
|
|
185
|
-
// Create user in database
|
|
186
134
|
const userId = await createUser(username, email);
|
|
187
|
-
|
|
188
|
-
// Publish user created event
|
|
189
|
-
await client.publish(
|
|
190
|
-
{ userId, username, email },
|
|
191
|
-
{ subject: 'user.created' }
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
// Send response
|
|
195
|
-
await client.publish(
|
|
196
|
-
{ requestId, success: true, userId },
|
|
197
|
-
{ subject: 'responses' }
|
|
198
|
-
);
|
|
199
|
-
}, { subject: 'user.create' });
|
|
200
135
|
|
|
201
|
-
|
|
202
|
-
|
|
136
|
+
await client.publish({ requestId, success: true, userId }, { subject: 'responses' });
|
|
137
|
+
await client.publish({ userId, username, email }, { subject: 'user.created' });
|
|
138
|
+
}, { subject: 'user.create' });
|
|
203
139
|
|
|
204
|
-
//
|
|
140
|
+
// order-service.js - reacts to user creation events
|
|
205
141
|
await client.subscribe(async (message) => {
|
|
206
|
-
const { userId
|
|
207
|
-
|
|
208
|
-
console.log(`New user ${username} (${userId}) - initializing order history`);
|
|
142
|
+
const { userId } = message.data;
|
|
209
143
|
await initializeOrderHistory(userId);
|
|
210
144
|
}, { subject: 'user.created' });
|
|
211
145
|
|
|
212
146
|
// api-gateway.js
|
|
213
|
-
const client = new QueueBitClient('http://localhost:3000');
|
|
214
|
-
|
|
215
147
|
async function createUser(username, email) {
|
|
216
|
-
const requestId =
|
|
217
|
-
|
|
218
|
-
// Listen for response
|
|
148
|
+
const requestId = crypto.randomUUID();
|
|
149
|
+
|
|
219
150
|
const responsePromise = new Promise((resolve) => {
|
|
220
|
-
client.subscribe((
|
|
221
|
-
if (
|
|
222
|
-
resolve(message.data);
|
|
223
|
-
}
|
|
151
|
+
client.subscribe((msg) => {
|
|
152
|
+
if (msg.data.requestId === requestId) resolve(msg.data);
|
|
224
153
|
}, { subject: 'responses' });
|
|
225
154
|
});
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
await client.publish(
|
|
229
|
-
{ requestId, username, email },
|
|
230
|
-
{ subject: 'user.create' }
|
|
231
|
-
);
|
|
232
|
-
|
|
155
|
+
|
|
156
|
+
await client.publish({ requestId, username, email }, { subject: 'user.create' });
|
|
233
157
|
return responsePromise;
|
|
234
158
|
}
|
|
235
159
|
```
|
|
@@ -238,93 +162,38 @@ async function createUser(username, email) {
|
|
|
238
162
|
|
|
239
163
|
## Event Sourcing
|
|
240
164
|
|
|
241
|
-
Store all events and rebuild state.
|
|
242
|
-
|
|
243
165
|
```javascript
|
|
244
|
-
// event-store.js
|
|
245
|
-
const { QueueBitClient } = require('queuebit');
|
|
246
|
-
const client = new QueueBitClient('http://localhost:3000');
|
|
247
|
-
|
|
248
166
|
const events = [];
|
|
249
167
|
|
|
250
168
|
// Store all events
|
|
251
169
|
await client.subscribe((message) => {
|
|
252
170
|
events.push(message.data);
|
|
253
|
-
|
|
254
|
-
}, { subject: 'events' });
|
|
171
|
+
}, { subject: 'account.events' });
|
|
255
172
|
|
|
256
173
|
// Rebuild state from events
|
|
257
|
-
function
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
state.balance += event.amount;
|
|
264
|
-
state.transactions.push(event);
|
|
265
|
-
break;
|
|
266
|
-
case 'withdraw':
|
|
267
|
-
state.balance -= event.amount;
|
|
268
|
-
state.transactions.push(event);
|
|
269
|
-
break;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return state;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// bank-account.js
|
|
277
|
-
const client = new QueueBitClient('http://localhost:3000');
|
|
278
|
-
|
|
279
|
-
async function deposit(amount) {
|
|
280
|
-
await client.publish(
|
|
281
|
-
{
|
|
282
|
-
type: 'deposit',
|
|
283
|
-
amount,
|
|
284
|
-
timestamp: new Date(),
|
|
285
|
-
accountId: 'ACC123'
|
|
286
|
-
},
|
|
287
|
-
{ subject: 'events' }
|
|
288
|
-
);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
async function withdraw(amount) {
|
|
292
|
-
await client.publish(
|
|
293
|
-
{
|
|
294
|
-
type: 'withdraw',
|
|
295
|
-
amount,
|
|
296
|
-
timestamp: new Date(),
|
|
297
|
-
accountId: 'ACC123'
|
|
298
|
-
},
|
|
299
|
-
{ subject: 'events' }
|
|
300
|
-
);
|
|
174
|
+
function getBalance() {
|
|
175
|
+
return events.reduce((bal, e) => {
|
|
176
|
+
if (e.type === 'deposit') return bal + e.amount;
|
|
177
|
+
if (e.type === 'withdraw') return bal - e.amount;
|
|
178
|
+
return bal;
|
|
179
|
+
}, 0);
|
|
301
180
|
}
|
|
302
181
|
|
|
303
|
-
//
|
|
304
|
-
await deposit
|
|
305
|
-
await withdraw
|
|
306
|
-
await deposit
|
|
182
|
+
// Publish events
|
|
183
|
+
await client.publish({ type: 'deposit', amount: 100 }, { subject: 'account.events' });
|
|
184
|
+
await client.publish({ type: 'withdraw', amount: 30 }, { subject: 'account.events' });
|
|
185
|
+
await client.publish({ type: 'deposit', amount: 50 }, { subject: 'account.events' });
|
|
307
186
|
|
|
308
|
-
|
|
309
|
-
const state = rebuildState();
|
|
310
|
-
console.log('Current balance:', state.balance); // 125
|
|
187
|
+
console.log('Balance:', getBalance()); // 120
|
|
311
188
|
```
|
|
312
189
|
|
|
313
190
|
---
|
|
314
191
|
|
|
315
|
-
## More Examples
|
|
316
|
-
|
|
317
|
-
See the [examples folder](../examples/) for:
|
|
318
|
-
- Browser-based example with UI
|
|
319
|
-
- Performance testing
|
|
320
|
-
- Advanced patterns
|
|
321
|
-
|
|
322
|
-
---
|
|
323
|
-
|
|
324
192
|
## Tips
|
|
325
193
|
|
|
326
|
-
1.
|
|
327
|
-
2. **
|
|
328
|
-
3.
|
|
329
|
-
4.
|
|
330
|
-
5.
|
|
194
|
+
1. Use **subjects** to organize message types (e.g. `orders.created`, `users.login`)
|
|
195
|
+
2. Use **load balancers** with unique queue names per worker for scalable processing
|
|
196
|
+
3. Use **`removeAfterRead: true`** for one-time notifications
|
|
197
|
+
4. Use **`expiry`** to auto-clean temporary messages
|
|
198
|
+
5. The **`loadBalancerId`** in received messages identifies which load balancer delivered it
|
|
199
|
+
6. Load balancer messages are **consumed** — not replayed to late subscribers
|
package/docs/QUICKSTART.md
CHANGED
|
@@ -2,121 +2,120 @@
|
|
|
2
2
|
|
|
3
3
|
Get started with QueueBit in 5 minutes!
|
|
4
4
|
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install queuebit
|
|
9
|
-
```
|
|
10
|
-
|
|
11
5
|
## Start the Server
|
|
12
6
|
|
|
13
7
|
```javascript
|
|
14
8
|
// server.js
|
|
15
|
-
const { QueueBitServer } = require('
|
|
9
|
+
const { QueueBitServer } = require('./src/server');
|
|
16
10
|
|
|
17
|
-
const server = new QueueBitServer({ port:
|
|
18
|
-
|
|
11
|
+
const server = new QueueBitServer({ port: 3333 });
|
|
12
|
+
// Starts listening immediately
|
|
19
13
|
```
|
|
20
14
|
|
|
21
|
-
Run it:
|
|
22
|
-
|
|
23
15
|
```bash
|
|
24
16
|
node server.js
|
|
25
17
|
```
|
|
26
18
|
|
|
27
|
-
## Publisher
|
|
19
|
+
## Publisher
|
|
28
20
|
|
|
29
21
|
```javascript
|
|
30
|
-
|
|
31
|
-
const { QueueBitClient } = require('queuebit');
|
|
22
|
+
const { QueueBitClient } = require('./src/client-node');
|
|
32
23
|
|
|
33
|
-
const client = new QueueBitClient('http://localhost:
|
|
24
|
+
const client = new QueueBitClient('http://localhost:3333');
|
|
34
25
|
|
|
35
|
-
// Wait for connection
|
|
36
26
|
setTimeout(async () => {
|
|
37
|
-
|
|
38
|
-
await client.publish({
|
|
39
|
-
message: 'Hello, QueueBit!',
|
|
40
|
-
timestamp: new Date()
|
|
41
|
-
});
|
|
42
|
-
|
|
27
|
+
await client.publish({ message: 'Hello, QueueBit!', timestamp: new Date() });
|
|
43
28
|
console.log('Message published!');
|
|
44
29
|
}, 1000);
|
|
45
30
|
```
|
|
46
31
|
|
|
47
|
-
## Subscriber
|
|
32
|
+
## Subscriber
|
|
48
33
|
|
|
49
34
|
```javascript
|
|
50
|
-
|
|
51
|
-
const { QueueBitClient } = require('queuebit');
|
|
35
|
+
const { QueueBitClient } = require('./src/client-node');
|
|
52
36
|
|
|
53
|
-
const client = new QueueBitClient('http://localhost:
|
|
37
|
+
const client = new QueueBitClient('http://localhost:3333');
|
|
54
38
|
|
|
55
|
-
// Subscribe to messages
|
|
56
39
|
client.subscribe((message) => {
|
|
57
40
|
console.log('Received:', message.data);
|
|
58
41
|
});
|
|
59
|
-
|
|
60
|
-
console.log('Waiting for messages...');
|
|
61
42
|
```
|
|
62
43
|
|
|
63
|
-
##
|
|
64
|
-
|
|
65
|
-
Open three terminals:
|
|
44
|
+
## In-Process (Server + Client Together)
|
|
66
45
|
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
node
|
|
46
|
+
```javascript
|
|
47
|
+
const { QueueBitServer } = require('./src/server');
|
|
48
|
+
const { QueueBitClient } = require('./src/client-node');
|
|
70
49
|
|
|
71
|
-
|
|
72
|
-
|
|
50
|
+
const server = new QueueBitServer({ port: 3333 });
|
|
51
|
+
const client = new QueueBitClient('http://localhost:3333');
|
|
73
52
|
|
|
74
|
-
|
|
75
|
-
|
|
53
|
+
setTimeout(async () => {
|
|
54
|
+
await client.subscribe((msg) => console.log('Got:', msg.data));
|
|
55
|
+
await client.publish({ hello: 'world' });
|
|
56
|
+
}, 500);
|
|
76
57
|
```
|
|
77
58
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
- Read the [API Documentation](./API.md)
|
|
81
|
-
- Check out [Examples](./EXAMPLES.md)
|
|
82
|
-
- See the [browser example](../examples/browser-example.html)
|
|
59
|
+
See [`examples/inprocessserver.js`](../examples/inprocessserver.js) for a full example.
|
|
83
60
|
|
|
84
61
|
## Common Patterns
|
|
85
62
|
|
|
86
|
-
###
|
|
63
|
+
### Regular Subscription (all subscribers receive every message)
|
|
87
64
|
|
|
88
65
|
```javascript
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}, { subject: 'tasks', queue: 'workers' });
|
|
66
|
+
await client.subscribe((message) => {
|
|
67
|
+
console.log('Event:', message.data);
|
|
68
|
+
}, { subject: 'events' });
|
|
93
69
|
```
|
|
94
70
|
|
|
95
|
-
###
|
|
71
|
+
### Load Balancer (round-robin, one subscriber per message)
|
|
96
72
|
|
|
97
73
|
```javascript
|
|
98
|
-
//
|
|
99
|
-
await
|
|
100
|
-
|
|
101
|
-
}, { subject: '
|
|
74
|
+
// Each worker gets a unique queue name → unique LB ID
|
|
75
|
+
await worker1.subscribe((msg) => {
|
|
76
|
+
console.log(`LB#${msg.loadBalancerId}:`, msg.data);
|
|
77
|
+
}, { subject: 'tasks', queue: 'worker-1' });
|
|
78
|
+
|
|
79
|
+
await worker2.subscribe((msg) => {
|
|
80
|
+
console.log(`LB#${msg.loadBalancerId}:`, msg.data);
|
|
81
|
+
}, { subject: 'tasks', queue: 'worker-2' });
|
|
102
82
|
```
|
|
103
83
|
|
|
104
|
-
###
|
|
84
|
+
### Ephemeral Messages (removed after first read)
|
|
105
85
|
|
|
106
86
|
```javascript
|
|
107
|
-
|
|
108
|
-
|
|
87
|
+
await client.publish(
|
|
88
|
+
{ notification: 'One-time alert' },
|
|
89
|
+
{ removeAfterRead: true, subject: 'alerts' }
|
|
90
|
+
);
|
|
91
|
+
```
|
|
109
92
|
|
|
110
|
-
|
|
111
|
-
if (msg.data.requestId === requestId) {
|
|
112
|
-
console.log('Response:', msg.data);
|
|
113
|
-
}
|
|
114
|
-
}, { subject: 'responses' });
|
|
93
|
+
### Message Expiry
|
|
115
94
|
|
|
95
|
+
```javascript
|
|
116
96
|
await client.publish(
|
|
117
|
-
{
|
|
118
|
-
{
|
|
97
|
+
{ code: 'ABC123' },
|
|
98
|
+
{ expiry: new Date(Date.now() + 300000) } // expires in 5 minutes
|
|
119
99
|
);
|
|
120
100
|
```
|
|
121
101
|
|
|
122
|
-
|
|
102
|
+
## Browser
|
|
103
|
+
|
|
104
|
+
```html
|
|
105
|
+
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
|
|
106
|
+
<script src="src/client-browser.js"></script>
|
|
107
|
+
<script>
|
|
108
|
+
const client = new QueueBitClient('http://localhost:3333');
|
|
109
|
+
client.subscribe((msg) => console.log(msg.data));
|
|
110
|
+
client.publish({ text: 'Hello!' });
|
|
111
|
+
</script>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Open [`examples/qpanel.html`](../examples/qpanel.html) for the browser dashboard.
|
|
115
|
+
|
|
116
|
+
## Next Steps
|
|
117
|
+
|
|
118
|
+
- [API Documentation](./API.md) - Full API reference
|
|
119
|
+
- [Examples](./EXAMPLES.md) - More use case examples
|
|
120
|
+
- [`examples/`](../examples/) folder - Runnable examples
|
|
121
|
+
- [`test/test-harness.js`](../test/test-harness.js) - Run all tests
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usermetrics/queuebit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"description": "A
|
|
7
|
+
"description": "A high performance queue with guaranteed delivery and built-in load balancer. Runs in-process or as a standalone server. Supports browser clients.",
|
|
8
8
|
"main": "src/index.js",
|
|
9
9
|
"browser": "src/client-browser.js",
|
|
10
10
|
"scripts": {
|
|
@@ -27,11 +27,10 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"socket.io": "^4.7.2",
|
|
30
|
+
"socket.io-client": "^4.7.2",
|
|
30
31
|
"uuid": "^9.0.1"
|
|
31
32
|
},
|
|
32
|
-
"devDependencies": {
|
|
33
|
-
"socket.io-client": "^4.7.2"
|
|
34
|
-
},
|
|
33
|
+
"devDependencies": {},
|
|
35
34
|
"files": [
|
|
36
35
|
"src/",
|
|
37
36
|
"docs/",
|
package/src/client.js
CHANGED
package/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { QueueBitServer } = require('./server');
|
|
2
|
-
const { QueueBitClient } = require('./client');
|
|
2
|
+
const { QueueBitClient } = require('./client-node');
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
// Allow both: require('@usermetrics/queuebit') and const { QueueBitClient } = require('@usermetrics/queuebit')
|
|
5
|
+
module.exports = QueueBitClient;
|
|
6
|
+
module.exports.QueueBitClient = QueueBitClient;
|
|
7
|
+
module.exports.QueueBitServer = QueueBitServer;
|