ocpp-ws-io 1.0.0-alpha
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.
Potentially problematic release.
This version of ocpp-ws-io might be problematic. Click here for more details.
- package/.github/workflows/publish.yml +52 -0
- package/LICENSE +21 -0
- package/README.md +773 -0
- package/dist/adapters/redis.d.mts +73 -0
- package/dist/adapters/redis.d.ts +73 -0
- package/dist/adapters/redis.js +96 -0
- package/dist/adapters/redis.js.map +1 -0
- package/dist/adapters/redis.mjs +71 -0
- package/dist/adapters/redis.mjs.map +1 -0
- package/dist/index.d.mts +268 -0
- package/dist/index.d.ts +268 -0
- package/dist/index.js +38919 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +38855 -0
- package/dist/index.mjs.map +1 -0
- package/dist/types-6LVUoXof.d.mts +284 -0
- package/dist/types-6LVUoXof.d.ts +284 -0
- package/package.json +59 -0
- package/src/adapters/adapter.ts +40 -0
- package/src/adapters/redis.ts +144 -0
- package/src/client.ts +882 -0
- package/src/errors.ts +183 -0
- package/src/event-buffer.ts +73 -0
- package/src/index.ts +68 -0
- package/src/queue.ts +65 -0
- package/src/schemas/ocpp1_6.json +2376 -0
- package/src/schemas/ocpp2_0_1.json +11878 -0
- package/src/schemas/ocpp2_1.json +23176 -0
- package/src/server-client.ts +65 -0
- package/src/server.ts +374 -0
- package/src/standard-validators.ts +18 -0
- package/src/types.ts +316 -0
- package/src/util.ts +119 -0
- package/src/validator.ts +148 -0
- package/src/ws-util.ts +186 -0
- package/test/adapter.test.ts +88 -0
- package/test/client.test.ts +297 -0
- package/test/errors.test.ts +132 -0
- package/test/queue.test.ts +133 -0
- package/test/server.test.ts +274 -0
- package/test/util.test.ts +103 -0
- package/test/ws-util.test.ts +93 -0
- package/tsconfig.json +25 -0
- package/tsup.config.ts +16 -0
- package/vitest.config.ts +10 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { EventAdapterInterface } from "../types.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generic Redis-compatible client interface.
|
|
5
|
+
* This works with both `ioredis` and the official `redis` package,
|
|
6
|
+
* or any other client that implements these methods.
|
|
7
|
+
*/
|
|
8
|
+
export interface RedisLikeClient {
|
|
9
|
+
publish(channel: string, message: string): Promise<number | unknown>;
|
|
10
|
+
subscribe(channel: string, ...args: unknown[]): Promise<unknown>;
|
|
11
|
+
unsubscribe(channel: string, ...args: unknown[]): Promise<unknown>;
|
|
12
|
+
on(
|
|
13
|
+
event: "message",
|
|
14
|
+
callback: (channel: string, message: string) => void,
|
|
15
|
+
): unknown;
|
|
16
|
+
disconnect?(): Promise<void>;
|
|
17
|
+
quit?(): Promise<unknown>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface RedisAdapterOptions {
|
|
21
|
+
/** Redis client for publishing */
|
|
22
|
+
pubClient: RedisLikeClient;
|
|
23
|
+
/** Redis client for subscribing (must be a separate connection) */
|
|
24
|
+
subClient: RedisLikeClient;
|
|
25
|
+
/** Optional key prefix for channels (default: 'ocpp-ws-io:') */
|
|
26
|
+
prefix?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Redis adapter for cross-process event distribution.
|
|
31
|
+
*
|
|
32
|
+
* This adapter is **generic** — it works with any Redis-compatible client
|
|
33
|
+
* that implements the `RedisLikeClient` interface:
|
|
34
|
+
* - `ioredis`
|
|
35
|
+
* - `redis` (node-redis)
|
|
36
|
+
* - Any custom implementation
|
|
37
|
+
*
|
|
38
|
+
* The user provides their own pub/sub client instances.
|
|
39
|
+
* No Redis dependency is forced on the user.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* // With ioredis
|
|
44
|
+
* import Redis from 'ioredis';
|
|
45
|
+
* const adapter = new RedisAdapter({
|
|
46
|
+
* pubClient: new Redis(),
|
|
47
|
+
* subClient: new Redis(),
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* // With node-redis
|
|
51
|
+
* import { createClient } from 'redis';
|
|
52
|
+
* const pub = createClient();
|
|
53
|
+
* const sub = pub.duplicate();
|
|
54
|
+
* await pub.connect();
|
|
55
|
+
* await sub.connect();
|
|
56
|
+
* const adapter = new RedisAdapter({ pubClient: pub, subClient: sub });
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export class RedisAdapter implements EventAdapterInterface {
|
|
60
|
+
private _pub: RedisLikeClient;
|
|
61
|
+
private _sub: RedisLikeClient;
|
|
62
|
+
private _prefix: string;
|
|
63
|
+
private _handlers = new Map<string, Set<(data: unknown) => void>>();
|
|
64
|
+
private _listening = false;
|
|
65
|
+
|
|
66
|
+
constructor(options: RedisAdapterOptions) {
|
|
67
|
+
this._pub = options.pubClient;
|
|
68
|
+
this._sub = options.subClient;
|
|
69
|
+
this._prefix = options.prefix ?? "ocpp-ws-io:";
|
|
70
|
+
|
|
71
|
+
this._setupSubscriber();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private _setupSubscriber(): void {
|
|
75
|
+
if (this._listening) return;
|
|
76
|
+
this._listening = true;
|
|
77
|
+
|
|
78
|
+
this._sub.on("message", (channel: string, message: string) => {
|
|
79
|
+
// Strip prefix
|
|
80
|
+
const actualChannel = channel.startsWith(this._prefix)
|
|
81
|
+
? channel.slice(this._prefix.length)
|
|
82
|
+
: channel;
|
|
83
|
+
|
|
84
|
+
const handlers = this._handlers.get(actualChannel);
|
|
85
|
+
if (handlers) {
|
|
86
|
+
let data: unknown;
|
|
87
|
+
try {
|
|
88
|
+
data = JSON.parse(message);
|
|
89
|
+
} catch {
|
|
90
|
+
data = message;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
for (const handler of handlers) {
|
|
94
|
+
try {
|
|
95
|
+
handler(data);
|
|
96
|
+
} catch {
|
|
97
|
+
// Swallow handler errors
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async publish(channel: string, data: unknown): Promise<void> {
|
|
105
|
+
const prefixedChannel = this._prefix + channel;
|
|
106
|
+
const message = JSON.stringify(data);
|
|
107
|
+
await this._pub.publish(prefixedChannel, message);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async subscribe(
|
|
111
|
+
channel: string,
|
|
112
|
+
handler: (data: unknown) => void,
|
|
113
|
+
): Promise<void> {
|
|
114
|
+
if (!this._handlers.has(channel)) {
|
|
115
|
+
this._handlers.set(channel, new Set());
|
|
116
|
+
const prefixedChannel = this._prefix + channel;
|
|
117
|
+
await this._sub.subscribe(prefixedChannel);
|
|
118
|
+
}
|
|
119
|
+
this._handlers.get(channel)!.add(handler);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async unsubscribe(channel: string): Promise<void> {
|
|
123
|
+
const prefixedChannel = this._prefix + channel;
|
|
124
|
+
await this._sub.unsubscribe(prefixedChannel);
|
|
125
|
+
this._handlers.delete(channel);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async disconnect(): Promise<void> {
|
|
129
|
+
this._handlers.clear();
|
|
130
|
+
|
|
131
|
+
// Gracefully disconnect both clients
|
|
132
|
+
if (this._pub.quit) {
|
|
133
|
+
await this._pub.quit();
|
|
134
|
+
} else if (this._pub.disconnect) {
|
|
135
|
+
await this._pub.disconnect();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (this._sub.quit) {
|
|
139
|
+
await this._sub.quit();
|
|
140
|
+
} else if (this._sub.disconnect) {
|
|
141
|
+
await this._sub.disconnect();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|