@emanuelepifani/nexo-client 0.2.0 → 0.3.9
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 +10 -172
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Nexo Client SDK
|
|
2
2
|
|
|
3
|
-
High-performance TypeScript client for [Nexo](https://
|
|
3
|
+
High-performance TypeScript client for [Nexo](https://nexo-docs-hub.vercel.app/).
|
|
4
|
+
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
## Quick Start
|
|
6
8
|
|
|
@@ -49,94 +51,6 @@ await mailQ.subscribe((msg) => console.log(msg));
|
|
|
49
51
|
await mailQ.delete();
|
|
50
52
|
```
|
|
51
53
|
|
|
52
|
-
<details>
|
|
53
|
-
<summary><strong>Advanced Features (Persistence, Retry, Delay, Priority, DLQ)</strong></summary>
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
// -------------------------------------------------------------
|
|
57
|
-
// 1. CREATION (Default behavior for all messages in this queue)
|
|
58
|
-
// -------------------------------------------------------------
|
|
59
|
-
interface CriticalTask { type: string; payload: any; }
|
|
60
|
-
|
|
61
|
-
const criticalQueue = await client.queue<CriticalTask>('critical-tasks').create({
|
|
62
|
-
// RELIABILITY:
|
|
63
|
-
visibilityTimeoutMs: 10000, // Retry delivery if not ACKed within 10s (default=30s)
|
|
64
|
-
maxRetries: 5, // Move to DLQ after 5 failures (default=5)
|
|
65
|
-
ttlMs: 60000, // Message expires if not consumed in 60s (default=7days)
|
|
66
|
-
|
|
67
|
-
// PERSISTENCE:
|
|
68
|
-
// - 'file_sync': Save every message (Safest, Slowest)
|
|
69
|
-
// - 'file_async': Flush periodically (Fast & Durable) -
|
|
70
|
-
// DEFAULT: 'file_async': Flush every 50ms or 5000 msgs
|
|
71
|
-
persistence: 'file_sync',
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// ---------------------------------------------------------
|
|
76
|
-
// 2. PRODUCING (Override specific behaviors per message)
|
|
77
|
-
// ---------------------------------------------------------
|
|
78
|
-
|
|
79
|
-
// PRIORITY: Higher value (255) delivered before lower values (0)
|
|
80
|
-
// This message jumps ahead of all priority < 255 messages sent previously and still not consumed.
|
|
81
|
-
await criticalQueue.push({ type: 'urgent' }, { priority: 255 });
|
|
82
|
-
|
|
83
|
-
// SCHEDULING: Delay visibility
|
|
84
|
-
// This message is hidden for 1 hour (default delayMs: 0, instant)
|
|
85
|
-
await criticalQueue.push({ type: 'scheduled' }, { delayMs: 3600000 });
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// ---------------------------------------------------------
|
|
90
|
-
// 3. CONSUMING (Worker Tuning to optimize throughput and latency)
|
|
91
|
-
// ---------------------------------------------------------
|
|
92
|
-
await criticalQueue.subscribe(
|
|
93
|
-
async (task) => { await processTask(task); },
|
|
94
|
-
{
|
|
95
|
-
batchSize: 100, // Network: Fetch 100 messages in one request
|
|
96
|
-
concurrency: 10, // Local: Process 10 messages concurrently (useful for I/O tasks)
|
|
97
|
-
waitMs: 5000 // Polling: If empty, wait 5s for new messages before retrying
|
|
98
|
-
}
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
// ---------------------------------------------------------
|
|
102
|
-
// 4. DEAD LETTER QUEUE (DLQ) - Failed Message Management
|
|
103
|
-
// ---------------------------------------------------------
|
|
104
|
-
|
|
105
|
-
// DLQ is automatically available for every main queue.
|
|
106
|
-
// When messages exceed maxRetries, they're moved to DLQ automatically
|
|
107
|
-
|
|
108
|
-
// Inspect failed messages
|
|
109
|
-
const failedMessages = await criticalQueue.dlq.peek(10);
|
|
110
|
-
console.log(`Found ${failedMessages.total} failed messages`);
|
|
111
|
-
|
|
112
|
-
for (const msg of failedMessages.items) {
|
|
113
|
-
console.log(`Message ${msg.id}: attempts=${msg.attempts}, reason=${msg.failureReason}`);
|
|
114
|
-
console.log(`Payload:`, msg.data);
|
|
115
|
-
|
|
116
|
-
// Decision logic based on failure reason
|
|
117
|
-
if (shouldRetry(msg.data)) {
|
|
118
|
-
// Replay: Move back to main queue (resets attempts to 0)
|
|
119
|
-
// The message will be immediately available for consumption
|
|
120
|
-
const moved = await criticalQueue.dlq.moveToQueue(msg.id);
|
|
121
|
-
console.log(`Replayed message ${msg.id}: ${moved}`);
|
|
122
|
-
} else {
|
|
123
|
-
// Discard: Permanently delete from DLQ
|
|
124
|
-
const deleted = await criticalQueue.dlq.delete(msg.id);
|
|
125
|
-
console.log(`Deleted message ${msg.id}: ${deleted}`);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Bulk operations
|
|
130
|
-
const purgedCount = await criticalQueue.dlq.purge(); // Clear all DLQ messages
|
|
131
|
-
console.log(`Purged ${purgedCount} messages from DLQ`);
|
|
132
|
-
|
|
133
|
-
// DLQ API:
|
|
134
|
-
// - peek(limit, offset): Inspect messages without removing them. Returns { total, items: [] }
|
|
135
|
-
// - moveToQueue(messageId): Replay a specific message (returns false if not found)
|
|
136
|
-
// - delete(messageId): Permanently remove a specific message (returns false if not found)
|
|
137
|
-
// - purge(): Remove all messages (returns count)
|
|
138
|
-
```
|
|
139
|
-
</details>
|
|
140
54
|
|
|
141
55
|
### 3. PUB/SUB
|
|
142
56
|
|
|
@@ -149,35 +63,6 @@ await alerts.subscribe((msg) => console.log(msg));
|
|
|
149
63
|
await alerts.publish({ level: "high" });
|
|
150
64
|
```
|
|
151
65
|
|
|
152
|
-
<details>
|
|
153
|
-
<summary><strong>Wildcards & Retained Messages</strong></summary>
|
|
154
|
-
|
|
155
|
-
```typescript
|
|
156
|
-
// WILDCARD SUBSCRIPTIONS
|
|
157
|
-
// ----------------------
|
|
158
|
-
|
|
159
|
-
// 1. Single-Level Wildcard (+)
|
|
160
|
-
// Matches: 'home/kitchen/light', 'home/garage/light'
|
|
161
|
-
const roomLights = client.pubsub<LightStatus>('home/+/light');
|
|
162
|
-
await roomLights.subscribe((status) => console.log('Light is:', status.state));
|
|
163
|
-
|
|
164
|
-
// 2. Multi-Level Wildcard (#)
|
|
165
|
-
// Matches all topics under 'sensors/'
|
|
166
|
-
const allSensors = client.pubsub<SensorData>('sensors/#');
|
|
167
|
-
await allSensors.subscribe((data) => console.log('Sensor value:', data.value));
|
|
168
|
-
|
|
169
|
-
// PUBLISHING (No wildcards allowed!)
|
|
170
|
-
// ---------------------------------
|
|
171
|
-
// You must publish to concrete topics with matching types
|
|
172
|
-
await client.pubsub<LightStatus>('home/kitchen/light').publish({ state: 'ON' });
|
|
173
|
-
await client.pubsub<SensorData>('sensors/kitchen/temp').publish({ value: 22.5, unit: 'C' });
|
|
174
|
-
|
|
175
|
-
// RETAINED MESSAGES
|
|
176
|
-
// -----------------
|
|
177
|
-
// Last value is stored and immediately sent to new subscribers
|
|
178
|
-
await client.pubsub<string>('config/theme').publish('dark', { retain: true });
|
|
179
|
-
```
|
|
180
|
-
</details>
|
|
181
66
|
|
|
182
67
|
### 4. STREAM
|
|
183
68
|
|
|
@@ -192,56 +77,11 @@ await stream.subscribe('analytics', (msg) => {console.log(`User ${msg.userId} pe
|
|
|
192
77
|
await stream.delete();
|
|
193
78
|
```
|
|
194
79
|
|
|
195
|
-
<details>
|
|
196
|
-
<summary><strong>Advanced Features (Consumer Groups, Persistence, Retention)</strong></summary>
|
|
197
|
-
|
|
198
|
-
```typescript
|
|
199
|
-
// ---------------------------------------------------------
|
|
200
|
-
// 1. STREAM CREATION & POLICY
|
|
201
|
-
// ---------------------------------------------------------
|
|
202
|
-
const orders = await client.stream<Order>('orders').create({
|
|
203
|
-
// SCALING
|
|
204
|
-
partitions: 4, // Max concurrent consumers per group on same topic (default=8)
|
|
205
|
-
|
|
206
|
-
// PERSISTENCE:
|
|
207
|
-
// - 'file_sync': Save every message (Safest, Slowest)
|
|
208
|
-
// - 'file_async': Flush periodically (Fast & Durable) -
|
|
209
|
-
// DEFAULT: 'file_async': Flush every 50ms or 5000 msgs
|
|
210
|
-
persistence: 'file_sync',
|
|
211
|
-
|
|
212
|
-
// RETENTION (Cleanup Policy)
|
|
213
|
-
// --------------------------
|
|
214
|
-
// Delete old data when EITHER limit is reached:
|
|
215
|
-
retention: {
|
|
216
|
-
maxAgeMs: 86400000, // 1 Day (Default: 7 Days)
|
|
217
|
-
maxBytes: 536870912 // 512 MB (Default: 1 GB)
|
|
218
|
-
},
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
// ---------------------------------------------------------
|
|
222
|
-
// 2. CONSUMING (Scaling & Broadcast patterns)
|
|
223
|
-
// ---------------------------------------------------------
|
|
224
|
-
|
|
225
|
-
// SCALING (Microservices Replicas / K8s Pods)
|
|
226
|
-
// Same Group ('workers') -> Automatic Load Balancing & Rebalancing
|
|
227
|
-
// Partitions are distributed among workers.
|
|
228
|
-
await orders.subscribe('workers', (order) => process(order));
|
|
229
|
-
await orders.subscribe('workers', (order) => process(order));
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
// BROADCAST (Independent Domains)
|
|
233
|
-
// Different Groups -> Each group gets a full copy of the stream.
|
|
234
|
-
// Useful for independent services reacting to the same event.
|
|
235
|
-
await orders.subscribe('analytics', (order) => trackMetrics(order));
|
|
236
|
-
await orders.subscribe('audit-log', (order) => saveAudit(order));
|
|
237
|
-
```
|
|
238
|
-
</details>
|
|
239
|
-
|
|
240
80
|
|
|
241
81
|
|
|
242
82
|
---
|
|
243
83
|
|
|
244
|
-
### Binary Payloads
|
|
84
|
+
### Binary Payloads
|
|
245
85
|
|
|
246
86
|
All Nexo brokers (**Store, Queue, Stream, PubSub**) natively support raw binary data (`Buffer`).
|
|
247
87
|
Bypassing JSON serialization drastically reduces Latency, increases Throughput, and saves Bandwidth.
|
|
@@ -252,13 +92,13 @@ Bypassing JSON serialization drastically reduces Latency, increases Throughput,
|
|
|
252
92
|
// Send 1MB raw buffer (30% smaller than JSON/Base64)
|
|
253
93
|
const heavyPayload = Buffer.alloc(1024 * 1024);
|
|
254
94
|
|
|
255
|
-
// 1. STREAM
|
|
95
|
+
// 1. STREAM
|
|
256
96
|
await client.stream('cctv-archive').publish(heavyPayload);
|
|
257
|
-
// 2. PUBSUB
|
|
97
|
+
// 2. PUBSUB
|
|
258
98
|
await client.pubsub('live-audio-call').publish(heavyPayload);
|
|
259
|
-
// 3. STORE
|
|
99
|
+
// 3. STORE
|
|
260
100
|
await client.store.map.set('user:avatar:1', heavyPayload);
|
|
261
|
-
// 4. QUEUE
|
|
101
|
+
// 4. QUEUE
|
|
262
102
|
await client.queue('pdf-processing').push(heavyPayload);
|
|
263
103
|
```
|
|
264
104
|
|
|
@@ -271,10 +111,8 @@ MIT
|
|
|
271
111
|
|
|
272
112
|
## Links
|
|
273
113
|
|
|
274
|
-
-
|
|
275
|
-
-
|
|
276
|
-
- **SDK Source:** [sdk/ts](https://github.com/emanuel-epifani/nexo/tree/main/sdk/ts)
|
|
277
|
-
- **Docker Image:** [emanuelepifani/nexo](https://hub.docker.com/r/emanuelepifani/nexo)
|
|
114
|
+
- **📚 Full Documentation:** [Nexo Docs](https://nexo-docs-hub.vercel.app/)
|
|
115
|
+
- **🐳 Docker Image:** [emanuelepifani/nexo](https://hub.docker.com/r/emanuelepifani/nexo)
|
|
278
116
|
|
|
279
117
|
## Author
|
|
280
118
|
|