ocpp-ws-io 2.1.6 → 2.1.8

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/dist/index.js CHANGED
@@ -30,6 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
+ AdaptiveLimiter: () => AdaptiveLimiter,
34
+ ClusterDriver: () => ClusterDriver,
33
35
  ConnectionState: () => ConnectionState,
34
36
  InMemoryAdapter: () => InMemoryAdapter,
35
37
  LRUMap: () => LRUMap,
@@ -61,6 +63,7 @@ __export(src_exports, {
61
63
  WebsocketUpgradeError: () => WebsocketUpgradeError,
62
64
  combineAuth: () => combineAuth,
63
65
  createLoggingMiddleware: () => createLoggingMiddleware,
66
+ createPlugin: () => createPlugin,
64
67
  createRPCError: () => createRPCError,
65
68
  createRouter: () => createRouter,
66
69
  createValidator: () => createValidator,
@@ -130,6 +133,145 @@ function defineAdapter(adapter) {
130
133
  return adapter;
131
134
  }
132
135
 
136
+ // src/adapters/redis/cluster-driver.ts
137
+ var ClusterDriver = class {
138
+ _cluster;
139
+ _subscriber;
140
+ _handlers = /* @__PURE__ */ new Map();
141
+ constructor(_options) {
142
+ try {
143
+ const { createRequire } = require("module");
144
+ const dynamicRequire = createRequire(__filename);
145
+ const Redis = dynamicRequire("ioredis");
146
+ const redisOpts = _options.redisOptions ?? {};
147
+ if (_options.natMap) {
148
+ redisOpts.natMap = _options.natMap;
149
+ }
150
+ this._cluster = new Redis.Cluster(
151
+ _options.nodes.map((n) => ({ host: n.host, port: n.port })),
152
+ { redisOptions: redisOpts }
153
+ );
154
+ this._subscriber = new Redis.Cluster(
155
+ _options.nodes.map((n) => ({ host: n.host, port: n.port })),
156
+ { redisOptions: redisOpts }
157
+ );
158
+ this._subscriber.on("message", (channel, message) => {
159
+ const handler = this._handlers.get(channel);
160
+ if (handler) handler(message);
161
+ });
162
+ } catch {
163
+ throw new Error(
164
+ "ClusterDriver requires 'ioredis' as a peer dependency. Install it with: npm i ioredis"
165
+ );
166
+ }
167
+ }
168
+ async publish(channel, message) {
169
+ await this._cluster.publish(channel, message);
170
+ }
171
+ async subscribe(channel, handler) {
172
+ this._handlers.set(channel, handler);
173
+ await this._subscriber.subscribe(channel);
174
+ }
175
+ async unsubscribe(channel) {
176
+ await this._subscriber.unsubscribe(channel);
177
+ this._handlers.delete(channel);
178
+ }
179
+ async set(key, value, ttlSeconds) {
180
+ if (ttlSeconds) {
181
+ await this._cluster.set(key, value, "EX", ttlSeconds);
182
+ } else {
183
+ await this._cluster.set(key, value);
184
+ }
185
+ }
186
+ async get(key) {
187
+ return await this._cluster.get(key) || null;
188
+ }
189
+ async mget(keys) {
190
+ if (keys.length === 0) return [];
191
+ try {
192
+ return await this._cluster.mget(...keys);
193
+ } catch {
194
+ return await Promise.all(keys.map((k) => this.get(k)));
195
+ }
196
+ }
197
+ async del(key) {
198
+ await this._cluster.del(key);
199
+ }
200
+ async xadd(stream, args, maxLen) {
201
+ const flatArgs = [];
202
+ if (maxLen) {
203
+ flatArgs.push("MAXLEN", "~", maxLen.toString());
204
+ }
205
+ flatArgs.push("*");
206
+ for (const [k, v] of Object.entries(args)) {
207
+ flatArgs.push(k, v);
208
+ }
209
+ return await this._cluster.xadd(stream, ...flatArgs);
210
+ }
211
+ async xaddBatch(messages, maxLen) {
212
+ if (messages.length === 0) return;
213
+ const pipeline = this._cluster.pipeline();
214
+ for (const msg of messages) {
215
+ const flatArgs = [];
216
+ if (maxLen) {
217
+ flatArgs.push("MAXLEN", "~", maxLen.toString());
218
+ }
219
+ flatArgs.push("*");
220
+ for (const [k, v] of Object.entries(msg.args)) {
221
+ flatArgs.push(k, v);
222
+ }
223
+ pipeline.xadd(msg.stream, ...flatArgs);
224
+ }
225
+ await pipeline.exec();
226
+ }
227
+ async xread(streams, count, block) {
228
+ const args = [];
229
+ if (count) args.push("COUNT", count);
230
+ if (typeof block === "number") args.push("BLOCK", block);
231
+ args.push("STREAMS");
232
+ for (const s of streams) args.push(s.key);
233
+ for (const s of streams) args.push(s.id);
234
+ const result = await this._cluster.xread(...args);
235
+ if (!result) return null;
236
+ return result.map(([stream, messages]) => ({
237
+ stream,
238
+ messages: messages.map(([id, fields]) => {
239
+ const data = {};
240
+ for (let i = 0; i < fields.length; i += 2) {
241
+ data[fields[i]] = fields[i + 1];
242
+ }
243
+ return { id, data };
244
+ })
245
+ }));
246
+ }
247
+ async xlen(stream) {
248
+ return await this._cluster.xlen(stream);
249
+ }
250
+ async disconnect() {
251
+ this._handlers.clear();
252
+ await Promise.allSettled([this._cluster.quit(), this._subscriber.quit()]);
253
+ }
254
+ async setPresenceBatch(entries) {
255
+ if (entries.length === 0) return;
256
+ const pipeline = this._cluster.pipeline();
257
+ for (const { key, value, ttlSeconds } of entries) {
258
+ pipeline.set(key, value, "EX", ttlSeconds);
259
+ }
260
+ await pipeline.exec();
261
+ }
262
+ async expire(key, ttlSeconds) {
263
+ await this._cluster.expire(key, ttlSeconds);
264
+ }
265
+ onError(handler) {
266
+ this._cluster.on("error", handler);
267
+ return () => this._cluster.removeListener("error", handler);
268
+ }
269
+ onReconnect(handler) {
270
+ this._cluster.on("reconnecting", handler);
271
+ return () => this._cluster.removeListener("reconnecting", handler);
272
+ }
273
+ };
274
+
133
275
  // src/adapters/redis/helpers.ts
134
276
  var IoRedisDriver = class {
135
277
  constructor(pub, sub, blocking) {
@@ -393,6 +535,9 @@ var RedisAdapter = class {
393
535
  _unsubReconnect;
394
536
  // Stored presence entries for rehydration on reconnect
395
537
  _presenceCache = /* @__PURE__ */ new Map();
538
+ // Connection pool
539
+ _driverPool;
540
+ _nextPoolIndex;
396
541
  constructor(options) {
397
542
  this._prefix = options.prefix ?? "ocpp-ws-io:";
398
543
  this._streamMaxLen = options.streamMaxLen ?? 1e3;
@@ -403,6 +548,14 @@ var RedisAdapter = class {
403
548
  options.subClient,
404
549
  options.blockingClient
405
550
  );
551
+ const poolSize = options.poolSize ?? 1;
552
+ this._driverPool = [this._driver];
553
+ this._nextPoolIndex = 0;
554
+ if (poolSize > 1 && options.driverFactory) {
555
+ for (let i = 1; i < poolSize; i++) {
556
+ this._driverPool.push(options.driverFactory());
557
+ }
558
+ }
406
559
  if (this._driver.onError) {
407
560
  this._unsubError = this._driver.onError((err) => {
408
561
  console.error("[RedisAdapter] Redis error:", err.message);
@@ -415,6 +568,13 @@ var RedisAdapter = class {
415
568
  });
416
569
  }
417
570
  }
571
+ /** Get the next driver from the pool (round-robin) */
572
+ _getPoolDriver() {
573
+ if (this._driverPool.length === 1) return this._driver;
574
+ const driver = this._driverPool[this._nextPoolIndex];
575
+ this._nextPoolIndex = (this._nextPoolIndex + 1) % this._driverPool.length;
576
+ return driver;
577
+ }
418
578
  async publish(channel, data) {
419
579
  const prefixedChannel = this._prefix + channel;
420
580
  const payload = data;
@@ -425,11 +585,12 @@ var RedisAdapter = class {
425
585
  }
426
586
  const message = JSON.stringify(data);
427
587
  if (channel.startsWith("ocpp:node:")) {
428
- await this._driver.xadd(prefixedChannel, { message }, this._streamMaxLen);
429
- await this._driver.expire(prefixedChannel, this._streamTtlSeconds).catch(() => {
588
+ const poolDriver = this._getPoolDriver();
589
+ await poolDriver.xadd(prefixedChannel, { message }, this._streamMaxLen);
590
+ await poolDriver.expire(prefixedChannel, this._streamTtlSeconds).catch(() => {
430
591
  });
431
592
  } else {
432
- await this._driver.publish(prefixedChannel, message);
593
+ await this._getPoolDriver().publish(prefixedChannel, message);
433
594
  }
434
595
  }
435
596
  async publishBatch(messages) {
@@ -446,13 +607,15 @@ var RedisAdapter = class {
446
607
  }
447
608
  const promises = [];
448
609
  if (streamMessages.length > 0) {
449
- promises.push(this._driver.xaddBatch(streamMessages, this._streamMaxLen));
610
+ promises.push(
611
+ this._getPoolDriver().xaddBatch(streamMessages, this._streamMaxLen)
612
+ );
450
613
  }
451
614
  if (broadcastMessages.length > 0) {
452
615
  promises.push(
453
616
  Promise.all(
454
617
  broadcastMessages.map(
455
- (bm) => this._driver.publish(bm.channel, bm.message)
618
+ (bm) => this._getPoolDriver().publish(bm.channel, bm.message)
456
619
  )
457
620
  ).then(() => {
458
621
  })
@@ -497,7 +660,7 @@ var RedisAdapter = class {
497
660
  this._sequenceCounters.clear();
498
661
  if (this._unsubError) this._unsubError();
499
662
  if (this._unsubReconnect) this._unsubReconnect();
500
- await this._driver.disconnect();
663
+ await Promise.allSettled(this._driverPool.map((d) => d.disconnect()));
501
664
  }
502
665
  _handleMessage(channel, message) {
503
666
  const handlers = this._handlers.get(channel);
@@ -526,7 +689,7 @@ var RedisAdapter = class {
526
689
  async _pollLoop() {
527
690
  while (!this._closed) {
528
691
  if (this._streams.size === 0) {
529
- await new Promise((resolve) => setTimeout(resolve, 1e3));
692
+ await new Promise((resolve2) => setTimeout(resolve2, 1e3));
530
693
  continue;
531
694
  }
532
695
  const streamsArg = Array.from(this._streams).map((key) => ({
@@ -548,7 +711,7 @@ var RedisAdapter = class {
548
711
  }
549
712
  }
550
713
  } catch (_err) {
551
- await new Promise((resolve) => setTimeout(resolve, 1e3));
714
+ await new Promise((resolve2) => setTimeout(resolve2, 1e3));
552
715
  }
553
716
  }
554
717
  this._polling = false;
@@ -627,8 +790,84 @@ var RedisAdapter = class {
627
790
  }
628
791
  };
629
792
 
630
- // src/client.ts
793
+ // src/adaptive-limiter.ts
631
794
  var import_node_events = require("events");
795
+ var import_node_os = require("os");
796
+ var AdaptiveLimiter = class extends import_node_events.EventEmitter {
797
+ _cpuThreshold;
798
+ _memThreshold;
799
+ _cooldownMs;
800
+ _sampleInterval;
801
+ _timer = null;
802
+ _lastOverloadTime = 0;
803
+ _multiplier = 1;
804
+ _prevCpuUsage = null;
805
+ _prevTimestamp = 0;
806
+ constructor(options = {}) {
807
+ super();
808
+ this._cpuThreshold = options.cpuThresholdPercent ?? 70;
809
+ this._memThreshold = options.memThresholdPercent ?? 85;
810
+ this._cooldownMs = options.cooldownMs ?? 1e4;
811
+ this._sampleInterval = options.sampleIntervalMs ?? 2e3;
812
+ }
813
+ /** Current rate multiplier: 1.0 = normal, 0.25 = heavily throttled */
814
+ get multiplier() {
815
+ return this._multiplier;
816
+ }
817
+ /** Start periodic sampling */
818
+ start() {
819
+ if (this._timer) return;
820
+ this._prevCpuUsage = process.cpuUsage();
821
+ this._prevTimestamp = Date.now();
822
+ this._timer = setInterval(() => this._sample(), this._sampleInterval);
823
+ if (this._timer.unref) this._timer.unref();
824
+ }
825
+ /** Stop sampling and reset multiplier */
826
+ stop() {
827
+ if (this._timer) {
828
+ clearInterval(this._timer);
829
+ this._timer = null;
830
+ }
831
+ this._multiplier = 1;
832
+ }
833
+ _sample() {
834
+ const now = Date.now();
835
+ const cpuUsage = process.cpuUsage(this._prevCpuUsage ?? void 0);
836
+ const elapsedMs = now - this._prevTimestamp;
837
+ const cpuPercent = (cpuUsage.user + cpuUsage.system) / 1e3 / elapsedMs / (0, import_node_os.cpus)().length * 100;
838
+ this._prevCpuUsage = process.cpuUsage();
839
+ this._prevTimestamp = now;
840
+ const total = (0, import_node_os.totalmem)();
841
+ const free = (0, import_node_os.freemem)();
842
+ const memPercent = (total - free) / total * 100;
843
+ const cpuOverload = cpuPercent > this._cpuThreshold;
844
+ const memOverload = memPercent > this._memThreshold;
845
+ const prevMultiplier = this._multiplier;
846
+ if (cpuOverload || memOverload) {
847
+ this._lastOverloadTime = now;
848
+ this._multiplier = Math.max(0.25, this._multiplier * 0.5);
849
+ } else if (now - this._lastOverloadTime > this._cooldownMs) {
850
+ this._multiplier = Math.min(1, this._multiplier + 0.1);
851
+ }
852
+ if (this._multiplier !== prevMultiplier) {
853
+ this.emit("adapted", {
854
+ multiplier: this._multiplier,
855
+ cpuPercent: Math.round(cpuPercent * 100) / 100,
856
+ memPercent: Math.round(memPercent * 100) / 100
857
+ });
858
+ }
859
+ }
860
+ // Typed event emitter overrides
861
+ on(event, listener) {
862
+ return super.on(event, listener);
863
+ }
864
+ emit(event, data) {
865
+ return super.emit(event, data);
866
+ }
867
+ };
868
+
869
+ // src/client.ts
870
+ var import_node_events2 = require("events");
632
871
  var import_cuid2 = require("@paralleldrive/cuid2");
633
872
  var import_ws = __toESM(require("ws"));
634
873
 
@@ -766,6 +1005,9 @@ var RPCFrameworkError = class extends RPCGenericError {
766
1005
  function defineMiddleware(mw) {
767
1006
  return mw;
768
1007
  }
1008
+ function createPlugin(plugin) {
1009
+ return plugin;
1010
+ }
769
1011
  function defineRpcMiddleware(mw) {
770
1012
  return mw;
771
1013
  }
@@ -1090,10 +1332,10 @@ var Queue = class {
1090
1332
  this._drain();
1091
1333
  }
1092
1334
  push(fn) {
1093
- return new Promise((resolve, reject) => {
1335
+ return new Promise((resolve2, reject) => {
1094
1336
  this._queue.push({
1095
1337
  fn,
1096
- resolve,
1338
+ resolve: resolve2,
1097
1339
  reject
1098
1340
  });
1099
1341
  this._drain();
@@ -37193,7 +37435,7 @@ ${body}`
37193
37435
 
37194
37436
  // src/client.ts
37195
37437
  var { CONNECTING, OPEN, CLOSING, CLOSED } = ConnectionState;
37196
- var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
37438
+ var OCPPClient = class _OCPPClient extends import_node_events2.EventEmitter {
37197
37439
  // Static connection states
37198
37440
  static CONNECTING = CONNECTING;
37199
37441
  static OPEN = OPEN;
@@ -37352,7 +37594,7 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
37352
37594
  return this._connectInternal();
37353
37595
  }
37354
37596
  async _connectInternal() {
37355
- return new Promise((resolve, reject) => {
37597
+ return new Promise((resolve2, reject) => {
37356
37598
  const endpoint = this._buildEndpoint();
37357
37599
  const wsOptions = this._buildWsOptions();
37358
37600
  this._logger?.debug?.("Connecting", { url: endpoint });
@@ -37385,7 +37627,7 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
37385
37627
  response
37386
37628
  };
37387
37629
  this.emit("open", result);
37388
- resolve(result);
37630
+ resolve2(result);
37389
37631
  };
37390
37632
  const onError = (err) => {
37391
37633
  cleanup();
@@ -37449,16 +37691,16 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
37449
37691
  this._stopPing();
37450
37692
  if (!force && awaitPending) {
37451
37693
  const pendingPromises = Array.from(this._pendingCalls.values()).map(
37452
- (p) => new Promise((resolve) => {
37694
+ (p) => new Promise((resolve2) => {
37453
37695
  const origResolve = p.resolve;
37454
37696
  const origReject = p.reject;
37455
37697
  p.resolve = (v) => {
37456
37698
  origResolve(v);
37457
- resolve();
37699
+ resolve2();
37458
37700
  };
37459
37701
  p.reject = (r) => {
37460
37702
  origReject(r);
37461
- resolve();
37703
+ resolve2();
37462
37704
  };
37463
37705
  })
37464
37706
  );
@@ -37466,13 +37708,13 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
37466
37708
  await Promise.allSettled(pendingPromises);
37467
37709
  }
37468
37710
  }
37469
- return new Promise((resolve) => {
37711
+ return new Promise((resolve2) => {
37470
37712
  if (!this._ws || this._ws.readyState === import_ws.default.CLOSED) {
37471
37713
  this._state = CLOSED;
37472
37714
  this._cleanup();
37473
37715
  const result = { code, reason };
37474
37716
  this.emit("close", result);
37475
- resolve(result);
37717
+ resolve2(result);
37476
37718
  return;
37477
37719
  }
37478
37720
  const onClose = (closeCode, closeReason) => {
@@ -37481,7 +37723,7 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
37481
37723
  this._cleanup();
37482
37724
  const result = { code: closeCode, reason: closeReason.toString() };
37483
37725
  this.emit("close", result);
37484
- resolve(result);
37726
+ resolve2(result);
37485
37727
  };
37486
37728
  this._ws.on("close", onClose);
37487
37729
  if (force) {
@@ -37556,7 +37798,7 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
37556
37798
  }
37557
37799
  if (this._state !== OPEN) {
37558
37800
  if (this._options.offlineQueue && (this._state === CLOSED || this._state === CONNECTING)) {
37559
- return new Promise((resolve, reject) => {
37801
+ return new Promise((resolve2, reject) => {
37560
37802
  const maxSize = this._options.offlineQueueMaxSize ?? 100;
37561
37803
  if (this._offlineQueue.length >= maxSize) {
37562
37804
  this._offlineQueue.shift();
@@ -37568,7 +37810,7 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
37568
37810
  }
37569
37811
  );
37570
37812
  }
37571
- this._offlineQueue.push({ method, params, options, resolve, reject });
37813
+ this._offlineQueue.push({ method, params, options, resolve: resolve2, reject });
37572
37814
  this._logger?.debug?.("Call queued offline", {
37573
37815
  method,
37574
37816
  queueSize: this._offlineQueue.length
@@ -37627,7 +37869,7 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
37627
37869
  ctxvals.params
37628
37870
  ];
37629
37871
  const messageStr = JSON.stringify(message);
37630
- callResult = await new Promise((resolve, reject) => {
37872
+ callResult = await new Promise((resolve2, reject) => {
37631
37873
  const timeoutHandle = setTimeout(() => {
37632
37874
  this._pendingCalls.delete(msgId);
37633
37875
  reject(
@@ -37645,7 +37887,7 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
37645
37887
  options.signal.addEventListener("abort", abortHandler);
37646
37888
  }
37647
37889
  this._pendingCalls.set(msgId, {
37648
- resolve,
37890
+ resolve: resolve2,
37649
37891
  reject,
37650
37892
  timeoutHandle,
37651
37893
  abortHandler: options.signal ? () => options.signal?.removeEventListener("abort", abortHandler) : void 0,
@@ -37725,11 +37967,15 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
37725
37967
  });
37726
37968
  }
37727
37969
  // ─── Internal: Message handling ──────────────────────────────
37728
- _onMessage(rawData) {
37970
+ _onMessage(rawData, preParsed) {
37729
37971
  this._recordActivity();
37730
37972
  let message;
37731
37973
  try {
37732
- message = JSON.parse(rawData);
37974
+ if (preParsed !== void 0) {
37975
+ message = preParsed;
37976
+ } else {
37977
+ message = JSON.parse(rawData);
37978
+ }
37733
37979
  if (!Array.isArray(message)) throw new Error("Message is not an array");
37734
37980
  } catch (err) {
37735
37981
  this._onBadMessage(
@@ -37739,6 +37985,36 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
37739
37985
  return;
37740
37986
  }
37741
37987
  const messageType = message[0];
37988
+ const messageId = message[1];
37989
+ if (typeof messageId !== "string") {
37990
+ this._onBadMessage(
37991
+ typeof rawData === "string" ? rawData : rawData.toString(),
37992
+ new RPCMessageTypeNotSupportedError(
37993
+ `Invalid MessageId type: ${typeof messageId} (expected string)`
37994
+ )
37995
+ );
37996
+ return;
37997
+ }
37998
+ if (messageType === MessageType.CALL && message.length < 4 || messageType === MessageType.CALLRESULT && message.length < 3 || messageType === MessageType.CALLERROR && message.length < 5) {
37999
+ this._onBadMessage(
38000
+ JSON.stringify(message),
38001
+ new RPCMessageTypeNotSupportedError(
38002
+ `Missing payload elements for message type ${messageType}`
38003
+ )
38004
+ );
38005
+ return;
38006
+ }
38007
+ const payloadIndex = messageType === MessageType.CALLERROR ? 4 : messageType === MessageType.CALL ? 3 : 2;
38008
+ const payload = message[payloadIndex];
38009
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
38010
+ this._onBadMessage(
38011
+ JSON.stringify(message),
38012
+ new RPCMessageTypeNotSupportedError(
38013
+ `Payload must be a JSON object, got ${payload === null ? "null" : Array.isArray(payload) ? "array" : typeof payload}`
38014
+ )
38015
+ );
38016
+ return;
38017
+ }
37742
38018
  switch (messageType) {
37743
38019
  case MessageType.CALL:
37744
38020
  this._handleIncomingCall(message);
@@ -38218,6 +38494,23 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
38218
38494
  if (tls.passphrase) opts.passphrase = tls.passphrase;
38219
38495
  }
38220
38496
  }
38497
+ const compression = this._options.compression;
38498
+ if (compression) {
38499
+ opts.perMessageDeflate = compression === true ? {
38500
+ zlibDeflateOptions: { level: 6, memLevel: 8 },
38501
+ zlibInflateOptions: {},
38502
+ clientNoContextTakeover: true,
38503
+ serverNoContextTakeover: true
38504
+ } : {
38505
+ zlibDeflateOptions: {
38506
+ level: compression.level ?? 6,
38507
+ memLevel: compression.memLevel ?? 8
38508
+ },
38509
+ zlibInflateOptions: {},
38510
+ clientNoContextTakeover: compression.clientNoContextTakeover ?? true,
38511
+ serverNoContextTakeover: compression.serverNoContextTakeover ?? true
38512
+ };
38513
+ }
38221
38514
  return opts;
38222
38515
  }
38223
38516
  // ─── Internal: Cleanup ───────────────────────────────────────
@@ -38283,7 +38576,7 @@ var LRUMap = class extends Map {
38283
38576
  };
38284
38577
 
38285
38578
  // src/router.ts
38286
- var import_node_events2 = require("events");
38579
+ var import_node_events3 = require("events");
38287
38580
  async function executeMiddlewareChain(middlewares, ctx) {
38288
38581
  let index = -1;
38289
38582
  const dispatch = async (i, payload) => {
@@ -38307,7 +38600,7 @@ async function executeMiddlewareChain(middlewares, ctx) {
38307
38600
  };
38308
38601
  await dispatch(0);
38309
38602
  }
38310
- var OCPPRouter = class extends import_node_events2.EventEmitter {
38603
+ var OCPPRouter = class extends import_node_events3.EventEmitter {
38311
38604
  /** Raw registered patterns (strings and/or RegExp) for reference. */
38312
38605
  patterns;
38313
38606
  /** Connection middlewares attached to this router. */
@@ -38403,7 +38696,7 @@ function createRouter(...patterns) {
38403
38696
  }
38404
38697
 
38405
38698
  // src/server.ts
38406
- var import_node_events3 = require("events");
38699
+ var import_node_events4 = require("events");
38407
38700
  var import_node_http2 = require("http");
38408
38701
  var import_node_https = require("https");
38409
38702
  var import_cuid22 = require("@paralleldrive/cuid2");
@@ -38633,6 +38926,8 @@ var OCPPServerClient = class extends OCPPClient {
38633
38926
  super(options);
38634
38927
  this._serverSession = context.session;
38635
38928
  this._serverHandshake = context.handshake;
38929
+ this._adaptiveMultiplier = context.adaptiveMultiplier ?? null;
38930
+ this._workerPool = context.workerPool ?? null;
38636
38931
  this._state = ConnectionState.OPEN;
38637
38932
  this._identity = this._options.identity;
38638
38933
  this._ws = context.ws;
@@ -38642,6 +38937,8 @@ var OCPPServerClient = class extends OCPPClient {
38642
38937
  }
38643
38938
  // ─── Rate Limiting State ──────────────────────────────────────────
38644
38939
  _rateLimits = {};
38940
+ _adaptiveMultiplier = null;
38941
+ _workerPool = null;
38645
38942
  _checkRateLimit(method) {
38646
38943
  const limits = this._options.rateLimit;
38647
38944
  if (!limits) return true;
@@ -38653,7 +38950,8 @@ var OCPPServerClient = class extends OCPPClient {
38653
38950
  this._rateLimits[key] = bucket;
38654
38951
  } else {
38655
38952
  const timePassed = now - bucket.lastRefill;
38656
- const refillRate = limit / windowMs;
38953
+ const adaptiveScale = this._adaptiveMultiplier?.() ?? 1;
38954
+ const refillRate = limit / windowMs * adaptiveScale;
38657
38955
  const tokensToAdd = timePassed * refillRate;
38658
38956
  if (tokensToAdd > 0) {
38659
38957
  bucket.tokens = Math.min(limit, bucket.tokens + tokensToAdd);
@@ -38698,6 +38996,19 @@ var OCPPServerClient = class extends OCPPClient {
38698
38996
  this._handleRateLimitExceeded(pData || data.toString());
38699
38997
  return;
38700
38998
  }
38999
+ if (pData !== void 0) {
39000
+ this._onMessage(data, pData);
39001
+ return;
39002
+ }
39003
+ }
39004
+ if (this._workerPool) {
39005
+ const raw = typeof data === "string" ? data : data;
39006
+ this._workerPool.parse(raw).then((result) => {
39007
+ this._onMessage(data, result.message);
39008
+ }).catch(() => {
39009
+ this._onMessage(data);
39010
+ });
39011
+ return;
38701
39012
  }
38702
39013
  this._onMessage(data);
38703
39014
  });
@@ -38708,7 +39019,18 @@ var OCPPServerClient = class extends OCPPClient {
38708
39019
  this._onClose(code, reason)
38709
39020
  )
38710
39021
  );
38711
- ws.on("error", (err) => this.emit("error", err));
39022
+ ws.on("error", (err) => {
39023
+ if (this.listenerCount("error") > 0) {
39024
+ this.emit("error", err);
39025
+ } else {
39026
+ this._logger?.debug?.(
39027
+ "WebSocket error (unhandled by client listener)",
39028
+ {
39029
+ error: err.message
39030
+ }
39031
+ );
39032
+ }
39033
+ });
38712
39034
  ws.on("ping", () => {
38713
39035
  this._recordActivity();
38714
39036
  this.emit("ping");
@@ -38787,8 +39109,110 @@ var OCPPServerClient = class extends OCPPClient {
38787
39109
  }
38788
39110
  };
38789
39111
 
39112
+ // src/worker-pool.ts
39113
+ var import_node_os2 = require("os");
39114
+ var import_node_path = require("path");
39115
+ var import_node_worker_threads = require("worker_threads");
39116
+ var WorkerPool = class {
39117
+ _workers = [];
39118
+ _nextWorker = 0;
39119
+ _taskId = 0;
39120
+ _pending = /* @__PURE__ */ new Map();
39121
+ _maxQueueSize;
39122
+ _terminated = false;
39123
+ constructor(options = {}) {
39124
+ const poolSize = options.poolSize ?? Math.max(2, (0, import_node_os2.cpus)().length - 2);
39125
+ this._maxQueueSize = options.maxQueueSize ?? 1e4;
39126
+ const workerPath = (0, import_node_path.resolve)(__dirname, "parse-worker.js");
39127
+ for (let i = 0; i < poolSize; i++) {
39128
+ const worker = new import_node_worker_threads.Worker(workerPath);
39129
+ worker.on(
39130
+ "message",
39131
+ (response) => {
39132
+ const task = this._pending.get(response.id);
39133
+ if (!task) return;
39134
+ this._pending.delete(response.id);
39135
+ if (response.error) {
39136
+ task.reject(new Error(response.error));
39137
+ } else {
39138
+ task.resolve({
39139
+ message: response.message,
39140
+ validationError: response.validationError
39141
+ });
39142
+ }
39143
+ }
39144
+ );
39145
+ worker.on("error", (err) => {
39146
+ console.error(`[WorkerPool] Worker ${i} error:`, err.message);
39147
+ });
39148
+ this._workers.push(worker);
39149
+ }
39150
+ }
39151
+ /** Number of worker threads in the pool */
39152
+ get size() {
39153
+ return this._workers.length;
39154
+ }
39155
+ /** Number of pending (unresolved) parse tasks */
39156
+ get pendingTasks() {
39157
+ return this._pending.size;
39158
+ }
39159
+ /**
39160
+ * Send raw data to a worker for JSON parsing + optional validation.
39161
+ * Uses round-robin worker selection.
39162
+ */
39163
+ parse(data, schemaInfo) {
39164
+ if (this._terminated) {
39165
+ return Promise.reject(new Error("WorkerPool has been shut down"));
39166
+ }
39167
+ if (this._pending.size >= this._maxQueueSize) {
39168
+ return Promise.reject(
39169
+ new Error(
39170
+ `WorkerPool queue full (${this._maxQueueSize} pending tasks)`
39171
+ )
39172
+ );
39173
+ }
39174
+ return new Promise((resolve2, reject) => {
39175
+ const id = this._taskId++;
39176
+ this._pending.set(id, {
39177
+ resolve: resolve2,
39178
+ reject
39179
+ });
39180
+ const worker = this._workers[this._nextWorker % this._workers.length];
39181
+ this._nextWorker = (this._nextWorker + 1) % this._workers.length;
39182
+ worker.postMessage({ id, buffer: data, schemaInfo });
39183
+ });
39184
+ }
39185
+ /** Gracefully terminate all workers */
39186
+ async shutdown() {
39187
+ if (this._terminated) return;
39188
+ this._terminated = true;
39189
+ for (const [id, task] of this._pending) {
39190
+ task.reject(new Error("WorkerPool shutting down"));
39191
+ this._pending.delete(id);
39192
+ }
39193
+ const terminatePromises = this._workers.map(async (worker) => {
39194
+ try {
39195
+ await Promise.race([
39196
+ worker.terminate(),
39197
+ new Promise((resolve2) => setTimeout(resolve2, 5e3))
39198
+ ]);
39199
+ } catch {
39200
+ }
39201
+ });
39202
+ await Promise.allSettled(terminatePromises);
39203
+ this._workers = [];
39204
+ }
39205
+ };
39206
+ function createWorkerPool(options = {}) {
39207
+ try {
39208
+ return new WorkerPool(options);
39209
+ } catch {
39210
+ return null;
39211
+ }
39212
+ }
39213
+
38790
39214
  // src/server.ts
38791
- var OCPPServer = class extends import_node_events3.EventEmitter {
39215
+ var OCPPServer = class extends import_node_events4.EventEmitter {
38792
39216
  _options;
38793
39217
  /** Radix trie for O(k) route matching (string patterns). */
38794
39218
  _trie = new RadixTrie();
@@ -38807,6 +39231,9 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
38807
39231
  _globalCORS;
38808
39232
  // Connection-level rate limiting (per-IP token bucket)
38809
39233
  _connectionBuckets = /* @__PURE__ */ new Map();
39234
+ _adaptiveLimiter = null;
39235
+ _plugins = [];
39236
+ _workerPool = null;
38810
39237
  // Robustness & Clustering
38811
39238
  _nodeId = (0, import_cuid22.createId)();
38812
39239
  _sessions;
@@ -38816,9 +39243,9 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
38816
39243
  super();
38817
39244
  this.setMaxListeners(0);
38818
39245
  if (options.strictMode) {
38819
- if (!options.strictModeValidators && !options.protocols?.length) {
39246
+ if (!options.protocols?.length) {
38820
39247
  throw new Error(
38821
- "strictMode requires either strictModeValidators or protocols to be specified"
39248
+ "strictMode requires protocols to be specified (e.g. protocols: ['ocpp1.6'])"
38822
39249
  );
38823
39250
  }
38824
39251
  }
@@ -38839,7 +39266,8 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
38839
39266
  this._sessions = new LRUMap(maxSessions);
38840
39267
  this._wss = new import_ws2.WebSocketServer({
38841
39268
  noServer: true,
38842
- maxPayload: this._options.maxPayloadBytes ?? 65536
39269
+ maxPayload: this._options.maxPayloadBytes ?? 65536,
39270
+ perMessageDeflate: this._buildCompressionConfig()
38843
39271
  });
38844
39272
  this._gcInterval = setInterval(() => {
38845
39273
  const now = Date.now();
@@ -38852,6 +39280,32 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
38852
39280
  this._logger = initLogger(this._options.logging, {
38853
39281
  component: "OCPPServer"
38854
39282
  });
39283
+ const rl = this._options.rateLimit;
39284
+ if (rl?.adaptive) {
39285
+ this._adaptiveLimiter = new AdaptiveLimiter({
39286
+ cpuThresholdPercent: rl.cpuThresholdPercent,
39287
+ memThresholdPercent: rl.memThresholdPercent,
39288
+ cooldownMs: rl.cooldownMs
39289
+ });
39290
+ this._adaptiveLimiter.on(
39291
+ "adapted",
39292
+ (event) => {
39293
+ this._logger?.info?.("Adaptive rate limit adjusted", event);
39294
+ this.emit("rateLimit:adapted", event);
39295
+ }
39296
+ );
39297
+ this._adaptiveLimiter.start();
39298
+ }
39299
+ const wt = this._options.workerThreads;
39300
+ if (wt) {
39301
+ const poolOpts = typeof wt === "object" ? wt : {};
39302
+ this._workerPool = createWorkerPool(poolOpts);
39303
+ if (this._workerPool) {
39304
+ this._logger?.info?.("Worker thread pool initialized", {
39305
+ poolSize: this._workerPool.size
39306
+ });
39307
+ }
39308
+ }
38855
39309
  }
38856
39310
  // ─── Getters ─────────────────────────────────────────────────
38857
39311
  get log() {
@@ -38974,8 +39428,44 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
38974
39428
  return this;
38975
39429
  }
38976
39430
  /**
38977
- * Registers a new middleware chain, acting as a wildcard/catch-all router if no patterns are added.
38978
- * `server.use(middleware).route("/api").on("client", ...)`
39431
+ * Registers one or more plugins for server lifecycle hooks.
39432
+ * Plugins are called in registration order for all lifecycle events.
39433
+ *
39434
+ * @example Single plugin
39435
+ * ```ts
39436
+ * server.plugin(metricsPlugin);
39437
+ * ```
39438
+ *
39439
+ * @example Multiple plugins
39440
+ * ```ts
39441
+ * server.plugin(metricsPlugin, loggingPlugin, otelPlugin);
39442
+ * ```
39443
+ */
39444
+ plugin(...plugins) {
39445
+ for (const plugin of plugins) {
39446
+ this._plugins.push(plugin);
39447
+ this._logger?.info?.("Plugin registered", { name: plugin.name });
39448
+ if (plugin.onInit) {
39449
+ const result = plugin.onInit(this);
39450
+ if (result instanceof Promise) {
39451
+ result.catch((err) => {
39452
+ this._logger?.error?.("Plugin onInit error", {
39453
+ name: plugin.name,
39454
+ error: err.message
39455
+ });
39456
+ });
39457
+ }
39458
+ }
39459
+ }
39460
+ return this;
39461
+ }
39462
+ /**
39463
+ * Registers middleware chain(s) as a wildcard/catch-all router.
39464
+ *
39465
+ * @example
39466
+ * ```ts
39467
+ * server.use(myMiddleware).route("/api").on("client", ...);
39468
+ * ```
38979
39469
  */
38980
39470
  use(...middlewares) {
38981
39471
  const router = new OCPPRouter();
@@ -39131,7 +39621,7 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39131
39621
  );
39132
39622
  }
39133
39623
  if (!options?.server) {
39134
- await new Promise((resolve, reject) => {
39624
+ await new Promise((resolve2, reject) => {
39135
39625
  httpServer.on("error", reject);
39136
39626
  httpServer.listen(port, host, () => {
39137
39627
  httpServer.removeListener("error", reject);
@@ -39140,7 +39630,7 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39140
39630
  port: typeof addr === "object" ? addr?.port : port,
39141
39631
  host: host ?? "0.0.0.0"
39142
39632
  });
39143
- resolve();
39633
+ resolve2();
39144
39634
  });
39145
39635
  });
39146
39636
  }
@@ -39449,13 +39939,13 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39449
39939
  selectedProtocol = handshake.protocols.values().next().value ?? void 0;
39450
39940
  } else {
39451
39941
  acceptOptions = await new Promise(
39452
- (resolve, reject) => {
39942
+ (resolve2, reject) => {
39453
39943
  let settled = false;
39454
39944
  const accept = (opts) => {
39455
39945
  if (settled) return;
39456
39946
  settled = true;
39457
39947
  if (opts?.protocol) selectedProtocol = opts.protocol;
39458
- resolve(opts);
39948
+ resolve2(opts);
39459
39949
  };
39460
39950
  const rejectAuth = (code = 401, message = "Unauthorized") => {
39461
39951
  if (!settled) {
@@ -39587,7 +40077,9 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39587
40077
  ws,
39588
40078
  handshake,
39589
40079
  session: finalSession,
39590
- protocol: selectedProtocol
40080
+ protocol: selectedProtocol,
40081
+ adaptiveMultiplier: this._adaptiveLimiter ? () => this._adaptiveLimiter.multiplier : void 0,
40082
+ workerPool: this._workerPool ?? void 0
39591
40083
  });
39592
40084
  this._updateSessionActivity(identity, client.session);
39593
40085
  const existingClient = this._clientsByIdentity.get(identity);
@@ -39619,21 +40111,52 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39619
40111
  remoteAddress: req.socket.remoteAddress,
39620
40112
  protocol: selectedProtocol
39621
40113
  });
39622
- client.on("close", () => {
39623
- this._clients.delete(client);
39624
- if (this._clientsByIdentity.get(identity) === client) {
39625
- this._clientsByIdentity.delete(identity);
40114
+ client.on(
40115
+ "close",
40116
+ ({ code, reason }) => {
40117
+ this._clients.delete(client);
40118
+ if (this._clientsByIdentity.get(identity) === client) {
40119
+ this._clientsByIdentity.delete(identity);
40120
+ }
40121
+ if (this?._adapter?.removePresence) {
40122
+ this._adapter.removePresence(identity).catch((err) => {
40123
+ this._logger?.error?.("Error removing presence", {
40124
+ identity,
40125
+ error: err
40126
+ });
40127
+ });
40128
+ }
40129
+ for (const plugin of this._plugins) {
40130
+ try {
40131
+ plugin.onDisconnect?.(client, code, reason);
40132
+ } catch (err) {
40133
+ this._logger?.error?.("Plugin onDisconnect error", {
40134
+ name: plugin.name,
40135
+ error: err.message
40136
+ });
40137
+ }
40138
+ }
40139
+ this._logger?.info?.("Client disconnected", { identity });
39626
40140
  }
39627
- if (this?._adapter?.removePresence) {
39628
- this._adapter.removePresence(identity).catch((err) => {
39629
- this._logger?.error?.("Error removing presence", {
39630
- identity,
39631
- error: err
40141
+ );
40142
+ for (const plugin of this._plugins) {
40143
+ try {
40144
+ const result = plugin.onConnection?.(client);
40145
+ if (result instanceof Promise) {
40146
+ result.catch((err) => {
40147
+ this._logger?.error?.("Plugin onConnection error", {
40148
+ name: plugin.name,
40149
+ error: err.message
40150
+ });
39632
40151
  });
40152
+ }
40153
+ } catch (err) {
40154
+ this._logger?.error?.("Plugin onConnection error", {
40155
+ name: plugin.name,
40156
+ error: err.message
39633
40157
  });
39634
40158
  }
39635
- this._logger?.info?.("Client disconnected", { identity });
39636
- });
40159
+ }
39637
40160
  this.emit("client", client);
39638
40161
  for (const router of matchedRouters) {
39639
40162
  router.emit("client", client);
@@ -39668,13 +40191,13 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39668
40191
  identity: client.identity,
39669
40192
  bufferedAmount: ws.bufferedAmount
39670
40193
  });
39671
- await new Promise((resolve) => {
40194
+ await new Promise((resolve2) => {
39672
40195
  let elapsed = 0;
39673
40196
  const check = setInterval(() => {
39674
40197
  elapsed += 50;
39675
40198
  if (!ws || ws.bufferedAmount === 0 || elapsed >= drainTimeout) {
39676
40199
  clearInterval(check);
39677
- resolve();
40200
+ resolve2();
39678
40201
  }
39679
40202
  }, 50);
39680
40203
  });
@@ -39696,12 +40219,28 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39696
40219
  this._wss = new import_ws2.WebSocketServer({ noServer: true });
39697
40220
  }
39698
40221
  const serverClosePromises = Array.from(this._httpServers).map(
39699
- (server) => new Promise((resolve) => {
39700
- server.close(() => resolve());
40222
+ (server) => new Promise((resolve2) => {
40223
+ server.close(() => resolve2());
39701
40224
  })
39702
40225
  );
39703
40226
  await Promise.allSettled(serverClosePromises);
39704
40227
  this._httpServers.clear();
40228
+ if (this._adaptiveLimiter) {
40229
+ this._adaptiveLimiter.stop();
40230
+ }
40231
+ for (const plugin of this._plugins) {
40232
+ try {
40233
+ const result = plugin.onClose?.();
40234
+ if (result instanceof Promise) {
40235
+ await result;
40236
+ }
40237
+ } catch (err) {
40238
+ this._logger?.error?.("Plugin onClose error", {
40239
+ name: plugin.name,
40240
+ error: err.message
40241
+ });
40242
+ }
40243
+ }
39705
40244
  if (this._adapter) {
39706
40245
  await this._adapter.disconnect();
39707
40246
  }
@@ -39763,6 +40302,55 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39763
40302
  return void 0;
39764
40303
  }
39765
40304
  }
40305
+ // ─── Batch Calls ──────────────────────────────────────────────
40306
+ /**
40307
+ * Pipeline multiple calls to a single client into a concurrent batch.
40308
+ * Useful for reconnection warm-up (e.g. GetConfiguration, ChangeAvailability, etc.)
40309
+ * where sequential calls would add unnecessary round-trip latency.
40310
+ *
40311
+ * @param identity The client identity to send calls to
40312
+ * @param calls Array of { method, params, options? } to execute concurrently
40313
+ * @returns Array of results in the same order as the calls array.
40314
+ * Each element is the call result, or `undefined` if that individual call failed.
40315
+ *
40316
+ * @example
40317
+ * ```ts
40318
+ * const results = await server.sendBatch('CP-101', [
40319
+ * { method: 'GetConfiguration', params: { key: ['MeterInterval'] } },
40320
+ * { method: 'ChangeAvailability', params: { type: 'Operative' } },
40321
+ * { method: 'TriggerMessage', params: { requestedMessage: 'StatusNotification' } },
40322
+ * ]);
40323
+ * ```
40324
+ */
40325
+ async sendBatch(identity, calls) {
40326
+ if (calls.length === 0) return [];
40327
+ const client = this._clientsByIdentity.get(identity);
40328
+ if (!client) {
40329
+ this._logger?.warn?.("sendBatch: client not found locally", { identity });
40330
+ return calls.map(() => void 0);
40331
+ }
40332
+ const originalConcurrency = client.options.callConcurrency ?? 1;
40333
+ if (calls.length > originalConcurrency) {
40334
+ client.reconfigure({ callConcurrency: calls.length });
40335
+ }
40336
+ try {
40337
+ const results = await Promise.allSettled(
40338
+ calls.map((c) => client.call(c.method, c.params, c.options ?? {}))
40339
+ );
40340
+ return results.map((r) => {
40341
+ if (r.status === "fulfilled") return r.value;
40342
+ this._logger?.warn?.("sendBatch: individual call failed", {
40343
+ identity,
40344
+ error: r.reason?.message
40345
+ });
40346
+ return void 0;
40347
+ });
40348
+ } finally {
40349
+ if (calls.length > originalConcurrency) {
40350
+ client.reconfigure({ callConcurrency: originalConcurrency });
40351
+ }
40352
+ }
40353
+ }
39766
40354
  // ─── Pub/Sub Adapter ─────────────────────────────────────────
39767
40355
  async setAdapter(adapter) {
39768
40356
  this._adapter = adapter;
@@ -39904,9 +40492,35 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39904
40492
  }
39905
40493
  await Promise.all(localPromises);
39906
40494
  }
40495
+ // ─── Internal: Compression Config ───────────────────────────────
40496
+ _buildCompressionConfig() {
40497
+ const compression = this._options.compression;
40498
+ if (!compression) return false;
40499
+ if (compression === true) {
40500
+ return {
40501
+ threshold: 1024,
40502
+ zlibDeflateOptions: { level: 6, memLevel: 8 },
40503
+ zlibInflateOptions: {},
40504
+ serverNoContextTakeover: true,
40505
+ clientNoContextTakeover: true
40506
+ };
40507
+ }
40508
+ return {
40509
+ threshold: compression.threshold ?? 1024,
40510
+ zlibDeflateOptions: {
40511
+ level: compression.level ?? 6,
40512
+ memLevel: compression.memLevel ?? 8
40513
+ },
40514
+ zlibInflateOptions: {},
40515
+ serverNoContextTakeover: compression.serverNoContextTakeover ?? true,
40516
+ clientNoContextTakeover: compression.clientNoContextTakeover ?? true
40517
+ };
40518
+ }
39907
40519
  };
39908
40520
  // Annotate the CommonJS export names for ESM import in node:
39909
40521
  0 && (module.exports = {
40522
+ AdaptiveLimiter,
40523
+ ClusterDriver,
39910
40524
  ConnectionState,
39911
40525
  InMemoryAdapter,
39912
40526
  LRUMap,
@@ -39938,6 +40552,7 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39938
40552
  WebsocketUpgradeError,
39939
40553
  combineAuth,
39940
40554
  createLoggingMiddleware,
40555
+ createPlugin,
39941
40556
  createRPCError,
39942
40557
  createRouter,
39943
40558
  createValidator,