ocpp-ws-io 2.1.15 → 2.2.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.
package/README.md CHANGED
@@ -1,25 +1,32 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/rohittiwari-dev/ocpp-ws-io/main/assets/banner.svg" alt="ocpp-ws-io" width="420" />
2
+ <img src="https://raw.githubusercontent.com/rohittiwari-dev/ocpp-ws-io/main/assets/banner.svg" alt="ocpp-ws-io - OCPP RPC WebSocket Client and Server" width="420" />
3
3
  </p>
4
4
 
5
- > built with TypeScript — supports OCPP 1.6, 2.0.1, and 2.1 with optional JSON schema validation, all security profiles, clustering support, and structured logging powered by [voltlog-io](https://ocpp-ws-io.rohittiwari.me/docs/voltlog-io).
5
+ # 🔌 OCPP RPC WebSocket Client & Server Library
6
+
7
+ The **production-ready OCPP RPC implementation** for building type-safe EV charging platforms, CSMS backends, charge points, and simulators. Supports OCPP 1.6J, 2.0.1, and 2.1 with full protocol compliance, all security profiles, Redis clustering, strict JSON schema validation, and structured logging.
8
+
9
+ > **Built with TypeScript** for EV charging, CSMS, CPO/EMSP platforms. Zero-dependency WebSocket RPC framework supporting OCPP versions 1.6, 2.0.1, and 2.1 with optional JSON schema validation, bidirectional messaging, all security profiles (0–3), Redis pub/sub clustering, and structured logging powered by [voltlog-io](https://ocpp-ws-io.rohittiwari.me/docs/voltlog-io).
6
10
 
7
11
  [![npm version](https://img.shields.io/npm/v/ocpp-ws-io.svg)](https://www.npmjs.com/package/ocpp-ws-io)
8
12
  [![License](https://img.shields.io/npm/l/ocpp-ws-io.svg)](https://github.com/rohittiwari-dev/ocpp-ws-io/blob/main/LICENSE)
9
13
 
10
- ## 📚 Documentation
14
+ ## 📚 Full Documentation & API Reference
11
15
 
12
16
  For full API reference, advanced usage, and guides, visit the **[Official Documentation](https://ocpp-ws-io.rohittiwari.me)**.
13
17
 
14
- ## ✨ Features
18
+ ## ✨ Key Features for OCPP RPC WebSocket Development
19
+
20
+ The library provides enterprise-grade features for building OCPP-compliant EV charging platforms:
15
21
 
16
- - ⚡ **Full OCPP-J RPC** — Compliant message framing
17
- - 🔒 **Security Profiles 0–3** — Plain WS, Basic Auth, TLS, mTLS
18
- - 🎯 **Type-Safe** — Auto-generated types for all OCPP messages
19
- - 📐 **Strict Mode** — Optional JSON schema validation
20
- - 📡 **Clustering** — Redis adapter support
21
- - 🌐 **Browser Client** — Zero-dependency client for simulators
22
- - ⚡ **CLI Ecosystem** — Built-in `ocpp-ws-cli` for generating types, load testing, fuzzing, and simulating virtual charge points
22
+ - ⚡ **Full OCPP-J RPC Support** — Complete JSON-RPC 2.0 OCPP message framing for 1.6J, 2.0.1, and 2.1
23
+ - 🔒 **All Security Profiles** — Supports Plain WS, Basic Authentication, TLS/SSL, and mutual TLS (mTLS)
24
+ - 🎯 **Type-Safe TypeScript** — Auto-generated, fully-typed interfaces for all OCPP versions and methods
25
+ - 📐 **Strict Schema Validation** — Optional JSON schema validation with AJV for data integrity
26
+ - 📡 **Redis Clustering & Pub/Sub** — Built-in Redis adapter for distributed CSMS deployments
27
+ - 🌐 **Browser-Ready Client** — Zero-dependency WebSocket client for EV charging simulators
28
+ - ⚡ **CLI Toolkit** — `ocpp-ws-cli` for type generation, load testing, fuzzing, and virtual charge point simulation
29
+ - 🛡️ **DDoS & Rate Limiting** — Token bucket rate limiting and adaptive throttling for charging station protection
23
30
 
24
31
  ## 📦 Installation
25
32
 
@@ -27,68 +34,72 @@ For full API reference, advanced usage, and guides, visit the **[Official Docume
27
34
  npm install ocpp-ws-io
28
35
  ```
29
36
 
30
- ## 🚀 Quick Start
37
+ ## 🚀 Quick Start: OCPP RPC Client & Server Examples
31
38
 
32
- ### Client (Charging Station Simulator)
39
+ Get up and running with OCPP WebSocket RPC in minutes. Choose your role: EV charging station (client) or CSMS backend (server).
40
+
41
+ ### OCPP RPC Client: Charge Point / Charging Station Simulator
33
42
 
34
43
  ```typescript
35
44
  import { OCPPClient } from "ocpp-ws-io";
36
45
 
37
46
  const client = new OCPPClient({
38
- endpoint: "ws://localhost:3000",
39
- identity: "CP001",
40
- protocols: ["ocpp1.6"],
47
+ endpoint: "ws://localhost:3000",
48
+ identity: "CP001",
49
+ protocols: ["ocpp1.6"],
41
50
  });
42
51
 
43
52
  await client.connect();
44
53
 
45
54
  // Fully typed call
46
55
  const response = await client.call("ocpp1.6", "BootNotification", {
47
- chargePointVendor: "VendorX",
48
- chargePointModel: "ModelY",
56
+ chargePointVendor: "VendorX",
57
+ chargePointModel: "ModelY",
49
58
  });
50
59
 
51
60
  console.log("Status:", response.status);
52
61
  ```
53
62
 
54
- ### Server (Central System)
63
+ ### OCPP RPC Server: Central System / CSMS Backend
55
64
 
56
65
  ```typescript
57
66
  import { OCPPServer } from "ocpp-ws-io";
58
67
 
59
68
  const server = new OCPPServer({
60
- protocols: ["ocpp1.6", "ocpp2.0.1"],
61
- logging: { prettify: true, exchangeLog: true, level: "info" },
69
+ protocols: ["ocpp1.6", "ocpp2.0.1"],
70
+ logging: { prettify: true, exchangeLog: true, level: "info" },
62
71
  });
63
72
 
64
73
  // Optional: Add authentication ringfence
65
74
  server.auth((ctx) => {
66
- console.log(
67
- `Connection from ${ctx.handshake.identity} at path ${ctx.handshake.pathname}`,
68
- );
69
- ctx.accept({ session: { authorized: true } });
75
+ console.log(
76
+ `Connection from ${ctx.handshake.identity} at path ${ctx.handshake.pathname}`,
77
+ );
78
+ ctx.accept({ session: { authorized: true } });
70
79
  });
71
80
 
72
81
  server.on("client", (client) => {
73
- console.log(`${client.identity} connected`);
74
-
75
- // Version-aware handler
76
- client.handle("ocpp1.6", "BootNotification", ({ params }) => {
77
- console.log("Boot from:", params.chargePointVendor);
78
- return {
79
- status: "Accepted",
80
- currentTime: new Date().toISOString(),
81
- interval: 300,
82
- };
83
- });
82
+ console.log(`${client.identity} connected`);
83
+
84
+ // Version-aware handler
85
+ client.handle("ocpp1.6", "BootNotification", ({ params }) => {
86
+ console.log("Boot from:", params.chargePointVendor);
87
+ return {
88
+ status: "Accepted",
89
+ currentTime: new Date().toISOString(),
90
+ interval: 300,
91
+ };
92
+ });
84
93
  });
85
94
 
86
95
  await server.listen(3000);
87
96
  ```
88
97
 
89
- ## ⚙️ Configuration
98
+ ## ⚙️ OCPP RPC Configuration & Options
99
+
100
+ Configure your OCPP WebSocket RPC client and server with extensive options for protocol negotiation, security, validation, and performance.
90
101
 
91
- ### `OCPPClient` Options
102
+ ### OCPP WebSocket RPC Client Configuration
92
103
 
93
104
  | Option | Type | Default | Description |
94
105
  | ------------------- | --------------------- | ---------- | --------------------------------------- |
@@ -110,7 +121,7 @@ When invoking `client.call()` you can safely decouple dynamically generated mess
110
121
  await client.call("ocpp1.6", "BootNotification", { ... }, { idempotencyKey: "unique-boot-123" });
111
122
  ```
112
123
 
113
- ### `OCPPServer` Options
124
+ ### OCPP WebSocket RPC Server Configuration
114
125
 
115
126
  | Option | Type | Default | Description |
116
127
  | -------------------- | ------------------ | --------- | ------------------------------------------ |
@@ -123,28 +134,86 @@ await client.call("ocpp1.6", "BootNotification", { ... }, { idempotencyKey: "uni
123
134
  | `rateLimit` | `RateLimitOptions` | — | Token bucket socket & method rate-limiter |
124
135
  | `healthEndpoint` | `boolean` | `false` | Expose HTTP `/health` and `/metrics` APIs |
125
136
 
126
- ## 🛠️ Advanced Server Configuration
137
+ ## Message Observability & Event Handling (v3.0.0+)
138
+
139
+ Unified message event API with direction tracking and rich contextual metadata for complete visibility into OCPP message flow.
140
+
141
+ ### Observe All Messages with Direction & Latency
142
+
143
+ ```typescript
144
+ const client = new OCPPClient({ ... });
145
+
146
+ // Single unified "message" event with direction, context, and metadata
147
+ client.on("message", ({ message, direction, ctx }) => {
148
+ console.log({
149
+ direction, // "IN" (from server) | "OUT" (to server)
150
+ method: ctx.method, // "BootNotification", "MeterValues", etc.
151
+ type: ctx.type, // "incoming_call", "outgoing_call", etc.
152
+ messageId: ctx.messageId, // Unique message ID
153
+ timestamp: ctx.timestamp, // ISO 8601 timestamp
154
+ latencyMs: ctx.latencyMs, // Response latency (if available)
155
+ protocol: ctx.protocol, // "ocpp1.6", "ocpp2.0.1", etc.
156
+ });
157
+ });
158
+
159
+ // Server-side observation (for each connected client)
160
+ const server = new OCPPServer({ ... });
161
+ server.on("client", (client) => {
162
+ client.on("message", ({ direction, ctx }) => {
163
+ console.log(`[${client.identity}] ${direction} ${ctx.method} (${ctx.latencyMs}ms)`);
164
+ });
165
+ });
166
+ ```
167
+
168
+ ### Message Event Payload Structure
169
+
170
+ ```typescript
171
+ interface MessageEventPayload {
172
+ message: OCPPMessage; // Original OCPP message tuple
173
+ direction: "IN" | "OUT"; // Message direction
174
+ ctx: MessageEventContext; // Enriched context with metadata
175
+ }
176
+
177
+ interface MessageEventContext {
178
+ type:
179
+ | "incoming_call"
180
+ | "outgoing_call"
181
+ | "incoming_result"
182
+ | "incoming_error";
183
+ messageId: string;
184
+ method?: string;
185
+ params?: unknown;
186
+ payload?: unknown;
187
+ timestamp: string; // ISO 8601
188
+ latencyMs?: number; // Response latency
189
+ protocol?: string;
190
+ }
191
+ ```
192
+
193
+ ## �🛠️ Advanced OCPP RPC Server Configuration & WebSocket Handshake
127
194
 
128
- ### Handshake & Upgrades
195
+ Build sophisticated OCPP server implementations with fine-tuned WebSocket upgrade handling, authentication, and message routing.
196
+
197
+ ### OCPP WebSocket Handshake, Upgrade & Authentication
129
198
 
130
199
  You can fine-tune how the server handles the WebSocket upgrade process, including timeouts for custom auth logic.
131
200
 
132
201
  ```typescript
133
202
  const server = new OCPPServer({
134
- // ...
135
- handshakeTimeoutMs: 5000, // Timeout if auth callback takes too long (default 30s)
203
+ // ...
204
+ handshakeTimeoutMs: 5000, // Timeout if auth callback takes too long (default 30s)
136
205
  });
137
206
 
138
207
  server.on("upgradeAborted", ({ identity, reason, socket }) => {
139
- console.warn(`Handshake aborted for ${identity}: ${reason}`);
208
+ console.warn(`Handshake aborted for ${identity}: ${reason}`);
140
209
  });
141
210
 
142
211
  server.on("upgradeError", ({ error, socket }) => {
143
- console.error("Upgrade failed:", error);
212
+ console.error("Upgrade failed:", error);
144
213
  });
145
214
  ```
146
215
 
147
- ### Server & Router Execution Flow
216
+ ### OCPP Server & Router Execution Flow (Connection & Message Phases)
148
217
 
149
218
  The `OCPPServer` and its internal `OCPPRouter` handle connections and messages in a strict, two-phase execution hierarchy:
150
219
 
@@ -160,7 +229,7 @@ Executes before the WebSocket connection is officially accepted.
160
229
 
161
230
  Executes after the connection is accepted and messages start flowing. 4. **Message Middleware (`client.use` / `server.use`)**: Intercepts every outgoing/incoming message for logging, schema validation, or metric tracking. 5. **Message Handlers (`client.handle` / `server.handle`)**: The **final piece of business logic** where the system reacts to a specific OCPP action (e.g., `BootNotification`).
162
231
 
163
- ### NOREPLY Suppression
232
+ ### NOREPLY Suppression in OCPP RPC Message Handlers
164
233
 
165
234
  Return `NOREPLY` directly from any message handler to safely suppress the automatic outbound `CALLRESULT` without violating strict internal tracking specifications.
166
235
 
@@ -168,53 +237,53 @@ Return `NOREPLY` directly from any message handler to safely suppress the automa
168
237
  import { NOREPLY } from "ocpp-ws-io";
169
238
 
170
239
  client.handle("StatusNotification", ({ params }) => {
171
- return NOREPLY;
240
+ return NOREPLY;
172
241
  });
173
242
  ```
174
243
 
175
- ## 📝 Logging
244
+ ## 📝 Structured Logging for OCPP RPC WebSocket Applications
176
245
 
177
- `ocpp-ws-io` includes **built-in structured JSON logging** powered by [voltlog-io](https://ocpp-ws-io.rohittiwari.me/docs/voltlog-io), designed for high-throughput WebSocket environments.
246
+ `ocpp-ws-io` provides **battle-tested structured JSON logging** optimized for high-throughput EV charging and OCPP WebSocket environments, powered by [voltlog-io](https://ocpp-ws-io.rohittiwari.me/docs/voltlog-io).
178
247
 
179
- ### Default Behavior
248
+ ### Default JSON Logging Behavior
180
249
 
181
250
  By default (`logging: true`), logs are output as structured JSON to `stdout`.
182
251
 
183
252
  ```json
184
253
  {
185
- "level": 30,
186
- "time": 1678900000000,
187
- "msg": "Client connected",
188
- "component": "OCPPServer",
189
- "identity": "CP001"
254
+ "level": 30,
255
+ "time": 1678900000000,
256
+ "msg": "Client connected",
257
+ "component": "OCPPServer",
258
+ "identity": "CP001"
190
259
  }
191
260
  ```
192
261
 
193
- ### Pretty Printing & Exchange Logs
262
+ ### Pretty Printing & OCPP Message Exchange Logs (Development Mode)
194
263
 
195
264
  Enable `prettify` for development to see colored output with icons.
196
265
  Enable `exchangeLog` to log all OCPP messages with direction (`IN`/`OUT`) and metadata.
197
266
 
198
267
  ```typescript
199
268
  const client = new OCPPClient({
200
- // ...
201
- logging: {
202
- enabled: true,
203
- prettify: true, // 🌈 Colors & icons
204
- exchangeLog: true, // ⚡ Log all OCPP messages
205
- level: "debug", // Default: 'info'
206
- },
269
+ // ...
270
+ logging: {
271
+ enabled: true,
272
+ prettify: true, // 🌈 Colors & icons
273
+ exchangeLog: true, // ⚡ Log all OCPP messages
274
+ level: "debug", // Default: 'info'
275
+ },
207
276
  });
208
277
  ```
209
278
 
210
279
  **Output:**
211
280
 
212
- ```
281
+ ```sh
213
282
  ⚡ CP-101 → BootNotification [OUT]
214
283
  ✅ CP-101 ← BootNotification [IN] { latencyMs: 45 }
215
284
  ```
216
285
 
217
- ### Custom Logger
286
+ ### Custom Logger Integration (Pino, Winston, etc.)
218
287
 
219
288
  You can bring your own logger (Pino, Winston, etc.) by implementing `LoggerLike`:
220
289
 
@@ -222,31 +291,33 @@ You can bring your own logger (Pino, Winston, etc.) by implementing `LoggerLike`
222
291
  import pino from "pino";
223
292
 
224
293
  const client = new OCPPClient({
225
- logging: {
226
- handler: pino(), // Use existing logger instance
227
- },
294
+ logging: {
295
+ handler: pino(), // Use existing logger instance
296
+ },
228
297
  });
229
298
  ```
230
299
 
231
- ## 🛡️ Safety & Reliability
300
+ ## 🛡️ OCPP RPC Safety, Reliability & Error Handling
301
+
302
+ Build fault-tolerant EV charging applications with safe call patterns, automatic error handling, and idempotency support.
232
303
 
233
- ### Safe Calls (`safeCall`)
304
+ ### Safe RPC Calls (`safeCall`) - Error Handling Without Try/Catch
234
305
 
235
306
  Perform RPC calls without `try/catch` blocks. Returns the response data on success, or `undefined` on failure while automatically logging the error. You can also pass per-call config options like timeouts.
236
307
 
237
308
  ```typescript
238
309
  const result = await client.safeCall(
239
- "ocpp1.6",
240
- "Heartbeat",
241
- {},
242
- {
243
- timeoutMs: 15000, // Finely control the timeout specifically for this request
244
- },
310
+ "ocpp1.6",
311
+ "Heartbeat",
312
+ {},
313
+ {
314
+ timeoutMs: 15000, // Finely control the timeout specifically for this request
315
+ },
245
316
  );
246
317
 
247
318
  if (result) {
248
- // Checked for undefined
249
- console.log("Heartbeat accepted:", result.currentTime);
319
+ // Checked for undefined
320
+ console.log("Heartbeat accepted:", result.currentTime);
250
321
  }
251
322
  ```
252
323
 
@@ -262,16 +333,16 @@ Throws an error if the client responds with a `CALLERROR` or if the timeout is r
262
333
 
263
334
  ```typescript
264
335
  try {
265
- const result = await server.sendToClient(
266
- "CP001",
267
- "ocpp1.6",
268
- "GetConfiguration",
269
- { key: ["ClockAlignedDataInterval"] },
270
- { timeoutMs: 10000 },
271
- );
272
- console.log("Configuration:", result);
336
+ const result = await server.sendToClient(
337
+ "CP001",
338
+ "ocpp1.6",
339
+ "GetConfiguration",
340
+ { key: ["ClockAlignedDataInterval"] },
341
+ { timeoutMs: 10000 },
342
+ );
343
+ console.log("Configuration:", result);
273
344
  } catch (error) {
274
- console.error("Failed to get configuration:", error);
345
+ console.error("Failed to get configuration:", error);
275
346
  }
276
347
  ```
277
348
 
@@ -281,15 +352,15 @@ Returns the response on success, or `undefined` on error, automatically logging
281
352
 
282
353
  ```typescript
283
354
  const result = await server.safeSendToClient(
284
- "CP001",
285
- "ocpp1.6",
286
- "GetConfiguration",
287
- { key: ["ClockAlignedDataInterval"] },
288
- { timeoutMs: 10000 },
355
+ "CP001",
356
+ "ocpp1.6",
357
+ "GetConfiguration",
358
+ { key: ["ClockAlignedDataInterval"] },
359
+ { timeoutMs: 10000 },
289
360
  );
290
361
 
291
362
  if (result) {
292
- console.log("Configuration:", result);
363
+ console.log("Configuration:", result);
293
364
  }
294
365
  ```
295
366
 
@@ -299,15 +370,15 @@ For intercepting HTTP WebSocket Upgrade requests before they become an OCPP Clie
299
370
 
300
371
  ```typescript
301
372
  const rateLimiter = defineMiddleware(async (ctx) => {
302
- const ip = ctx.handshake.remoteAddress;
303
- if (isRateLimited(ip)) {
304
- // Instantly aborts the WebSocket connection with an HTTP 429 status
305
- ctx.reject(429, "Too Many Requests");
306
- } else {
307
- // Or proceed down the execution chain. You can optionally pass an object
308
- // to next(), which will automatically be shallow-merged into `ctx.state`.
309
- await ctx.next({ rateLimitRemaining: 99 });
310
- }
373
+ const ip = ctx.handshake.remoteAddress;
374
+ if (isRateLimited(ip)) {
375
+ // Instantly aborts the WebSocket connection with an HTTP 429 status
376
+ ctx.reject(429, "Too Many Requests");
377
+ } else {
378
+ // Or proceed down the execution chain. You can optionally pass an object
379
+ // to next(), which will automatically be shallow-merged into `ctx.state`.
380
+ await ctx.next({ rateLimitRemaining: 99 });
381
+ }
311
382
  });
312
383
 
313
384
  server.use(rateLimiter);
@@ -321,101 +392,102 @@ The Rate Limiter safely throttles connections without dropping the connection im
321
392
 
322
393
  ```typescript
323
394
  const server = new OCPPServer({
324
- protocols: ["ocpp1.6"],
325
- rateLimit: {
326
- limit: 100, // Global limit
327
- windowMs: 60000, // per 60 seconds
328
- onLimitExceeded: "disconnect", // or "ignore", or a Custom Callback
329
- methods: {
330
- MeterValues: { limit: 10, windowMs: 60000 },
331
- Heartbeat: { limit: 2, windowMs: 60000 },
332
- },
395
+ protocols: ["ocpp1.6"],
396
+ rateLimit: {
397
+ limit: 100, // Global limit
398
+ windowMs: 60000, // per 60 seconds
399
+ onLimitExceeded: "disconnect", // or "ignore", or a Custom Callback
400
+ methods: {
401
+ MeterValues: { limit: 10, windowMs: 60000 },
402
+ Heartbeat: { limit: 2, windowMs: 60000 },
333
403
  },
404
+ },
334
405
  });
335
406
  ```
336
407
 
337
- ## 🧩 Middleware
408
+ ## 🧩 OCPP Message Middleware & Interceptor Pattern
338
409
 
339
- Intercept and modify OCPP messages using the middleware stack.
410
+ Intercept, validate, and modify OCPP RPC messages across the middleware stack for logging, schema validation, and metric tracking.
340
411
 
341
412
  ```typescript
342
413
  // Add logging middleware (enabled by default)
343
414
  client.use(async (ctx, next) => {
344
- console.log(`Processing ${ctx.method}`);
345
- await next();
415
+ console.log(`Processing ${ctx.method}`);
416
+ await next();
346
417
  });
347
418
  ```
348
419
 
349
- ## 📡 Clustering (Redis)
420
+ ## 📡 Redis Clustering & Distributed CSMS Architecture
350
421
 
351
- Scale your OCPP server across multiple nodes using Redis.
422
+ Scale OCPP RPC servers across multiple nodes with Redis pub/sub, Streams, and presence tracking for enterprise CSMS deployments.
352
423
 
353
- 1. **Install Adapter**:
424
+ ### Set Up Redis Clustering
354
425
 
355
- ```bash
356
- npm install ioredis
357
- ```
426
+ 1. **Install Redis Adapter**:
358
427
 
359
- 2. **Configure Server**:
428
+ ```bash
429
+ npm install ioredis
430
+ ```
360
431
 
361
- ```typescript
362
- import { OCPPServer } from "ocpp-ws-io";
363
- import { RedisAdapter } from "ocpp-ws-io/adapters/redis";
364
- import Redis from "ioredis";
432
+ 2. **Configure OCPP Server with Redis Adapter**:
365
433
 
366
- const redis = new Redis(process.env.REDIS_URL);
367
- const server = new OCPPServer({
434
+ ```typescript
435
+ import { OCPPServer } from "ocpp-ws-io";
436
+ import { RedisAdapter } from "ocpp-ws-io/adapters/redis";
437
+ import Redis from "ioredis";
438
+
439
+ const redis = new Redis(process.env.REDIS_URL);
440
+ const server = new OCPPServer({
368
441
  protocols: ["ocpp1.6"],
369
- });
442
+ });
370
443
 
371
- // Uses Redis Streams for clustering reliability
372
- await server.setAdapter(new RedisAdapter(redis));
373
- ```
444
+ // Uses Redis Streams for clustering reliability
445
+ await server.setAdapter(new RedisAdapter(redis));
446
+ ```
374
447
 
375
- **Features:**
448
+ **OCPP Redis Clustering Features:**
376
449
 
377
- - **Unicast Routing**: Send messages to any client on any node.
378
- - **Presence**: Track connected clients across the cluster.
379
- - **Reliability**: Uses Redis Streams for durable message delivery.
380
- - **Batch Processing**: Use `server.broadcastBatch` to combine multi-node calls effortlessly.
450
+ - **Unicast Routing**: Send OCPP RPC messages to any charge point on any cluster node.
451
+ - **Presence Tracking**: Real-time discovery of connected clients across the cluster.
452
+ - **Durability**: Redis Streams for guaranteed message delivery.
453
+ - **Batch Operations**: Use `server.broadcastBatch` for efficient multi-node requests.
381
454
 
382
- ### Custom Adapters (`EventAdapterInterface`)
455
+ ### Custom Clustering Adapters (RabbitMQ, Kafka, PubSub)
383
456
 
384
- You can build custom clustering adapters (e.g., RabbitMQ, Kafka, Postgres PUB/SUB) by implementing the exported `EventAdapterInterface`:
457
+ Build custom OCPP clustering solutions beyond Redis by implementing the `EventAdapterInterface`. This enables distributed CSMS architectures with your preferred message broker or database backend.
385
458
 
386
459
  ```typescript
387
460
  import type { EventAdapterInterface } from "ocpp-ws-io";
388
461
 
389
462
  export class CustomAdapter implements EventAdapterInterface {
390
- async publish(channel: string, data: unknown): Promise<void> {
391
- /* ... */
392
- }
393
- async subscribe(
394
- channel: string,
395
- handler: (data: unknown) => void,
396
- ): Promise<void> {
397
- /* ... */
398
- }
399
- async unsubscribe(channel: string): Promise<void> {
400
- /* ... */
401
- }
402
- async disconnect(): Promise<void> {
403
- /* ... */
404
- }
405
-
406
- // Optional primitives for advanced routing:
407
- async setPresence?(
408
- identity: string,
409
- nodeId: string,
410
- ttl: number,
411
- ): Promise<void>;
412
- async getPresence?(identity: string): Promise<string | null>;
413
- async removePresence?(identity: string): Promise<void>;
414
- async getPresenceBatch?(identities: string[]): Promise<(string | null)[]>;
415
- async publishBatch?(
416
- messages: { channel: string; data: unknown }[],
417
- ): Promise<void>;
418
- async metrics?(): Promise<Record<string, unknown>>;
463
+ async publish(channel: string, data: unknown): Promise<void> {
464
+ /* ... */
465
+ }
466
+ async subscribe(
467
+ channel: string,
468
+ handler: (data: unknown) => void,
469
+ ): Promise<void> {
470
+ /* ... */
471
+ }
472
+ async unsubscribe(channel: string): Promise<void> {
473
+ /* ... */
474
+ }
475
+ async disconnect(): Promise<void> {
476
+ /* ... */
477
+ }
478
+
479
+ // Optional primitives for advanced routing:
480
+ async setPresence?(
481
+ identity: string,
482
+ nodeId: string,
483
+ ttl: number,
484
+ ): Promise<void>;
485
+ async getPresence?(identity: string): Promise<string | null>;
486
+ async removePresence?(identity: string): Promise<void>;
487
+ async getPresenceBatch?(identities: string[]): Promise<(string | null)[]>;
488
+ async publishBatch?(
489
+ messages: { channel: string; data: unknown }[],
490
+ ): Promise<void>;
491
+ async metrics?(): Promise<Record<string, unknown>>;
419
492
  }
420
493
  ```
421
-
@@ -1,5 +1,5 @@
1
- import '../types-BZXEmDQ1.mjs';
2
- export { a as RedisAdapter, b as RedisAdapterOptions } from '../index-C0mn42-8.mjs';
1
+ import '../types-BunMs45p.mjs';
2
+ export { a as RedisAdapter, b as RedisAdapterOptions } from '../index-B98n5Et3.mjs';
3
3
  import 'ws';
4
4
  import 'node:https';
5
5
  import 'node:http';
@@ -1,5 +1,5 @@
1
- import '../types-BZXEmDQ1.js';
2
- export { a as RedisAdapter, b as RedisAdapterOptions } from '../index-s9f97CmV.js';
1
+ import '../types-BunMs45p.js';
2
+ export { a as RedisAdapter, b as RedisAdapterOptions } from '../index-xx7uU8pY.js';
3
3
  import 'ws';
4
4
  import 'node:https';
5
5
  import 'node:http';