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.mjs CHANGED
@@ -1,3 +1,18 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // ../../node_modules/tsup/assets/esm_shims.js
9
+ import path from "path";
10
+ import { fileURLToPath } from "url";
11
+ var getFilename = () => fileURLToPath(import.meta.url);
12
+ var getDirname = () => path.dirname(getFilename());
13
+ var __dirname = /* @__PURE__ */ getDirname();
14
+ var __filename = /* @__PURE__ */ getFilename();
15
+
1
16
  // src/adapters/adapter.ts
2
17
  var InMemoryAdapter = class {
3
18
  _channels = /* @__PURE__ */ new Map();
@@ -54,6 +69,145 @@ function defineAdapter(adapter) {
54
69
  return adapter;
55
70
  }
56
71
 
72
+ // src/adapters/redis/cluster-driver.ts
73
+ var ClusterDriver = class {
74
+ _cluster;
75
+ _subscriber;
76
+ _handlers = /* @__PURE__ */ new Map();
77
+ constructor(_options) {
78
+ try {
79
+ const { createRequire } = __require("module");
80
+ const dynamicRequire = createRequire(__filename);
81
+ const Redis = dynamicRequire("ioredis");
82
+ const redisOpts = _options.redisOptions ?? {};
83
+ if (_options.natMap) {
84
+ redisOpts.natMap = _options.natMap;
85
+ }
86
+ this._cluster = new Redis.Cluster(
87
+ _options.nodes.map((n) => ({ host: n.host, port: n.port })),
88
+ { redisOptions: redisOpts }
89
+ );
90
+ this._subscriber = new Redis.Cluster(
91
+ _options.nodes.map((n) => ({ host: n.host, port: n.port })),
92
+ { redisOptions: redisOpts }
93
+ );
94
+ this._subscriber.on("message", (channel, message) => {
95
+ const handler = this._handlers.get(channel);
96
+ if (handler) handler(message);
97
+ });
98
+ } catch {
99
+ throw new Error(
100
+ "ClusterDriver requires 'ioredis' as a peer dependency. Install it with: npm i ioredis"
101
+ );
102
+ }
103
+ }
104
+ async publish(channel, message) {
105
+ await this._cluster.publish(channel, message);
106
+ }
107
+ async subscribe(channel, handler) {
108
+ this._handlers.set(channel, handler);
109
+ await this._subscriber.subscribe(channel);
110
+ }
111
+ async unsubscribe(channel) {
112
+ await this._subscriber.unsubscribe(channel);
113
+ this._handlers.delete(channel);
114
+ }
115
+ async set(key, value, ttlSeconds) {
116
+ if (ttlSeconds) {
117
+ await this._cluster.set(key, value, "EX", ttlSeconds);
118
+ } else {
119
+ await this._cluster.set(key, value);
120
+ }
121
+ }
122
+ async get(key) {
123
+ return await this._cluster.get(key) || null;
124
+ }
125
+ async mget(keys) {
126
+ if (keys.length === 0) return [];
127
+ try {
128
+ return await this._cluster.mget(...keys);
129
+ } catch {
130
+ return await Promise.all(keys.map((k) => this.get(k)));
131
+ }
132
+ }
133
+ async del(key) {
134
+ await this._cluster.del(key);
135
+ }
136
+ async xadd(stream, args, maxLen) {
137
+ const flatArgs = [];
138
+ if (maxLen) {
139
+ flatArgs.push("MAXLEN", "~", maxLen.toString());
140
+ }
141
+ flatArgs.push("*");
142
+ for (const [k, v] of Object.entries(args)) {
143
+ flatArgs.push(k, v);
144
+ }
145
+ return await this._cluster.xadd(stream, ...flatArgs);
146
+ }
147
+ async xaddBatch(messages, maxLen) {
148
+ if (messages.length === 0) return;
149
+ const pipeline = this._cluster.pipeline();
150
+ for (const msg of messages) {
151
+ const flatArgs = [];
152
+ if (maxLen) {
153
+ flatArgs.push("MAXLEN", "~", maxLen.toString());
154
+ }
155
+ flatArgs.push("*");
156
+ for (const [k, v] of Object.entries(msg.args)) {
157
+ flatArgs.push(k, v);
158
+ }
159
+ pipeline.xadd(msg.stream, ...flatArgs);
160
+ }
161
+ await pipeline.exec();
162
+ }
163
+ async xread(streams, count, block) {
164
+ const args = [];
165
+ if (count) args.push("COUNT", count);
166
+ if (typeof block === "number") args.push("BLOCK", block);
167
+ args.push("STREAMS");
168
+ for (const s of streams) args.push(s.key);
169
+ for (const s of streams) args.push(s.id);
170
+ const result = await this._cluster.xread(...args);
171
+ if (!result) return null;
172
+ return result.map(([stream, messages]) => ({
173
+ stream,
174
+ messages: messages.map(([id, fields]) => {
175
+ const data = {};
176
+ for (let i = 0; i < fields.length; i += 2) {
177
+ data[fields[i]] = fields[i + 1];
178
+ }
179
+ return { id, data };
180
+ })
181
+ }));
182
+ }
183
+ async xlen(stream) {
184
+ return await this._cluster.xlen(stream);
185
+ }
186
+ async disconnect() {
187
+ this._handlers.clear();
188
+ await Promise.allSettled([this._cluster.quit(), this._subscriber.quit()]);
189
+ }
190
+ async setPresenceBatch(entries) {
191
+ if (entries.length === 0) return;
192
+ const pipeline = this._cluster.pipeline();
193
+ for (const { key, value, ttlSeconds } of entries) {
194
+ pipeline.set(key, value, "EX", ttlSeconds);
195
+ }
196
+ await pipeline.exec();
197
+ }
198
+ async expire(key, ttlSeconds) {
199
+ await this._cluster.expire(key, ttlSeconds);
200
+ }
201
+ onError(handler) {
202
+ this._cluster.on("error", handler);
203
+ return () => this._cluster.removeListener("error", handler);
204
+ }
205
+ onReconnect(handler) {
206
+ this._cluster.on("reconnecting", handler);
207
+ return () => this._cluster.removeListener("reconnecting", handler);
208
+ }
209
+ };
210
+
57
211
  // src/adapters/redis/helpers.ts
58
212
  var IoRedisDriver = class {
59
213
  constructor(pub, sub, blocking) {
@@ -317,6 +471,9 @@ var RedisAdapter = class {
317
471
  _unsubReconnect;
318
472
  // Stored presence entries for rehydration on reconnect
319
473
  _presenceCache = /* @__PURE__ */ new Map();
474
+ // Connection pool
475
+ _driverPool;
476
+ _nextPoolIndex;
320
477
  constructor(options) {
321
478
  this._prefix = options.prefix ?? "ocpp-ws-io:";
322
479
  this._streamMaxLen = options.streamMaxLen ?? 1e3;
@@ -327,6 +484,14 @@ var RedisAdapter = class {
327
484
  options.subClient,
328
485
  options.blockingClient
329
486
  );
487
+ const poolSize = options.poolSize ?? 1;
488
+ this._driverPool = [this._driver];
489
+ this._nextPoolIndex = 0;
490
+ if (poolSize > 1 && options.driverFactory) {
491
+ for (let i = 1; i < poolSize; i++) {
492
+ this._driverPool.push(options.driverFactory());
493
+ }
494
+ }
330
495
  if (this._driver.onError) {
331
496
  this._unsubError = this._driver.onError((err) => {
332
497
  console.error("[RedisAdapter] Redis error:", err.message);
@@ -339,6 +504,13 @@ var RedisAdapter = class {
339
504
  });
340
505
  }
341
506
  }
507
+ /** Get the next driver from the pool (round-robin) */
508
+ _getPoolDriver() {
509
+ if (this._driverPool.length === 1) return this._driver;
510
+ const driver = this._driverPool[this._nextPoolIndex];
511
+ this._nextPoolIndex = (this._nextPoolIndex + 1) % this._driverPool.length;
512
+ return driver;
513
+ }
342
514
  async publish(channel, data) {
343
515
  const prefixedChannel = this._prefix + channel;
344
516
  const payload = data;
@@ -349,11 +521,12 @@ var RedisAdapter = class {
349
521
  }
350
522
  const message = JSON.stringify(data);
351
523
  if (channel.startsWith("ocpp:node:")) {
352
- await this._driver.xadd(prefixedChannel, { message }, this._streamMaxLen);
353
- await this._driver.expire(prefixedChannel, this._streamTtlSeconds).catch(() => {
524
+ const poolDriver = this._getPoolDriver();
525
+ await poolDriver.xadd(prefixedChannel, { message }, this._streamMaxLen);
526
+ await poolDriver.expire(prefixedChannel, this._streamTtlSeconds).catch(() => {
354
527
  });
355
528
  } else {
356
- await this._driver.publish(prefixedChannel, message);
529
+ await this._getPoolDriver().publish(prefixedChannel, message);
357
530
  }
358
531
  }
359
532
  async publishBatch(messages) {
@@ -370,13 +543,15 @@ var RedisAdapter = class {
370
543
  }
371
544
  const promises = [];
372
545
  if (streamMessages.length > 0) {
373
- promises.push(this._driver.xaddBatch(streamMessages, this._streamMaxLen));
546
+ promises.push(
547
+ this._getPoolDriver().xaddBatch(streamMessages, this._streamMaxLen)
548
+ );
374
549
  }
375
550
  if (broadcastMessages.length > 0) {
376
551
  promises.push(
377
552
  Promise.all(
378
553
  broadcastMessages.map(
379
- (bm) => this._driver.publish(bm.channel, bm.message)
554
+ (bm) => this._getPoolDriver().publish(bm.channel, bm.message)
380
555
  )
381
556
  ).then(() => {
382
557
  })
@@ -421,7 +596,7 @@ var RedisAdapter = class {
421
596
  this._sequenceCounters.clear();
422
597
  if (this._unsubError) this._unsubError();
423
598
  if (this._unsubReconnect) this._unsubReconnect();
424
- await this._driver.disconnect();
599
+ await Promise.allSettled(this._driverPool.map((d) => d.disconnect()));
425
600
  }
426
601
  _handleMessage(channel, message) {
427
602
  const handlers = this._handlers.get(channel);
@@ -450,7 +625,7 @@ var RedisAdapter = class {
450
625
  async _pollLoop() {
451
626
  while (!this._closed) {
452
627
  if (this._streams.size === 0) {
453
- await new Promise((resolve) => setTimeout(resolve, 1e3));
628
+ await new Promise((resolve2) => setTimeout(resolve2, 1e3));
454
629
  continue;
455
630
  }
456
631
  const streamsArg = Array.from(this._streams).map((key) => ({
@@ -472,7 +647,7 @@ var RedisAdapter = class {
472
647
  }
473
648
  }
474
649
  } catch (_err) {
475
- await new Promise((resolve) => setTimeout(resolve, 1e3));
650
+ await new Promise((resolve2) => setTimeout(resolve2, 1e3));
476
651
  }
477
652
  }
478
653
  this._polling = false;
@@ -551,8 +726,84 @@ var RedisAdapter = class {
551
726
  }
552
727
  };
553
728
 
554
- // src/client.ts
729
+ // src/adaptive-limiter.ts
555
730
  import { EventEmitter } from "events";
731
+ import { cpus, freemem, totalmem } from "os";
732
+ var AdaptiveLimiter = class extends EventEmitter {
733
+ _cpuThreshold;
734
+ _memThreshold;
735
+ _cooldownMs;
736
+ _sampleInterval;
737
+ _timer = null;
738
+ _lastOverloadTime = 0;
739
+ _multiplier = 1;
740
+ _prevCpuUsage = null;
741
+ _prevTimestamp = 0;
742
+ constructor(options = {}) {
743
+ super();
744
+ this._cpuThreshold = options.cpuThresholdPercent ?? 70;
745
+ this._memThreshold = options.memThresholdPercent ?? 85;
746
+ this._cooldownMs = options.cooldownMs ?? 1e4;
747
+ this._sampleInterval = options.sampleIntervalMs ?? 2e3;
748
+ }
749
+ /** Current rate multiplier: 1.0 = normal, 0.25 = heavily throttled */
750
+ get multiplier() {
751
+ return this._multiplier;
752
+ }
753
+ /** Start periodic sampling */
754
+ start() {
755
+ if (this._timer) return;
756
+ this._prevCpuUsage = process.cpuUsage();
757
+ this._prevTimestamp = Date.now();
758
+ this._timer = setInterval(() => this._sample(), this._sampleInterval);
759
+ if (this._timer.unref) this._timer.unref();
760
+ }
761
+ /** Stop sampling and reset multiplier */
762
+ stop() {
763
+ if (this._timer) {
764
+ clearInterval(this._timer);
765
+ this._timer = null;
766
+ }
767
+ this._multiplier = 1;
768
+ }
769
+ _sample() {
770
+ const now = Date.now();
771
+ const cpuUsage = process.cpuUsage(this._prevCpuUsage ?? void 0);
772
+ const elapsedMs = now - this._prevTimestamp;
773
+ const cpuPercent = (cpuUsage.user + cpuUsage.system) / 1e3 / elapsedMs / cpus().length * 100;
774
+ this._prevCpuUsage = process.cpuUsage();
775
+ this._prevTimestamp = now;
776
+ const total = totalmem();
777
+ const free = freemem();
778
+ const memPercent = (total - free) / total * 100;
779
+ const cpuOverload = cpuPercent > this._cpuThreshold;
780
+ const memOverload = memPercent > this._memThreshold;
781
+ const prevMultiplier = this._multiplier;
782
+ if (cpuOverload || memOverload) {
783
+ this._lastOverloadTime = now;
784
+ this._multiplier = Math.max(0.25, this._multiplier * 0.5);
785
+ } else if (now - this._lastOverloadTime > this._cooldownMs) {
786
+ this._multiplier = Math.min(1, this._multiplier + 0.1);
787
+ }
788
+ if (this._multiplier !== prevMultiplier) {
789
+ this.emit("adapted", {
790
+ multiplier: this._multiplier,
791
+ cpuPercent: Math.round(cpuPercent * 100) / 100,
792
+ memPercent: Math.round(memPercent * 100) / 100
793
+ });
794
+ }
795
+ }
796
+ // Typed event emitter overrides
797
+ on(event, listener) {
798
+ return super.on(event, listener);
799
+ }
800
+ emit(event, data) {
801
+ return super.emit(event, data);
802
+ }
803
+ };
804
+
805
+ // src/client.ts
806
+ import { EventEmitter as EventEmitter2 } from "events";
556
807
  import { createId } from "@paralleldrive/cuid2";
557
808
  import WebSocket from "ws";
558
809
 
@@ -690,6 +941,9 @@ var RPCFrameworkError = class extends RPCGenericError {
690
941
  function defineMiddleware(mw) {
691
942
  return mw;
692
943
  }
944
+ function createPlugin(plugin) {
945
+ return plugin;
946
+ }
693
947
  function defineRpcMiddleware(mw) {
694
948
  return mw;
695
949
  }
@@ -1018,10 +1272,10 @@ var Queue = class {
1018
1272
  this._drain();
1019
1273
  }
1020
1274
  push(fn) {
1021
- return new Promise((resolve, reject) => {
1275
+ return new Promise((resolve2, reject) => {
1022
1276
  this._queue.push({
1023
1277
  fn,
1024
- resolve,
1278
+ resolve: resolve2,
1025
1279
  reject
1026
1280
  });
1027
1281
  this._drain();
@@ -37121,7 +37375,7 @@ ${body}`
37121
37375
 
37122
37376
  // src/client.ts
37123
37377
  var { CONNECTING, OPEN, CLOSING, CLOSED } = ConnectionState;
37124
- var OCPPClient = class _OCPPClient extends EventEmitter {
37378
+ var OCPPClient = class _OCPPClient extends EventEmitter2 {
37125
37379
  // Static connection states
37126
37380
  static CONNECTING = CONNECTING;
37127
37381
  static OPEN = OPEN;
@@ -37280,7 +37534,7 @@ var OCPPClient = class _OCPPClient extends EventEmitter {
37280
37534
  return this._connectInternal();
37281
37535
  }
37282
37536
  async _connectInternal() {
37283
- return new Promise((resolve, reject) => {
37537
+ return new Promise((resolve2, reject) => {
37284
37538
  const endpoint = this._buildEndpoint();
37285
37539
  const wsOptions = this._buildWsOptions();
37286
37540
  this._logger?.debug?.("Connecting", { url: endpoint });
@@ -37313,7 +37567,7 @@ var OCPPClient = class _OCPPClient extends EventEmitter {
37313
37567
  response
37314
37568
  };
37315
37569
  this.emit("open", result);
37316
- resolve(result);
37570
+ resolve2(result);
37317
37571
  };
37318
37572
  const onError = (err) => {
37319
37573
  cleanup();
@@ -37377,16 +37631,16 @@ var OCPPClient = class _OCPPClient extends EventEmitter {
37377
37631
  this._stopPing();
37378
37632
  if (!force && awaitPending) {
37379
37633
  const pendingPromises = Array.from(this._pendingCalls.values()).map(
37380
- (p) => new Promise((resolve) => {
37634
+ (p) => new Promise((resolve2) => {
37381
37635
  const origResolve = p.resolve;
37382
37636
  const origReject = p.reject;
37383
37637
  p.resolve = (v) => {
37384
37638
  origResolve(v);
37385
- resolve();
37639
+ resolve2();
37386
37640
  };
37387
37641
  p.reject = (r) => {
37388
37642
  origReject(r);
37389
- resolve();
37643
+ resolve2();
37390
37644
  };
37391
37645
  })
37392
37646
  );
@@ -37394,13 +37648,13 @@ var OCPPClient = class _OCPPClient extends EventEmitter {
37394
37648
  await Promise.allSettled(pendingPromises);
37395
37649
  }
37396
37650
  }
37397
- return new Promise((resolve) => {
37651
+ return new Promise((resolve2) => {
37398
37652
  if (!this._ws || this._ws.readyState === WebSocket.CLOSED) {
37399
37653
  this._state = CLOSED;
37400
37654
  this._cleanup();
37401
37655
  const result = { code, reason };
37402
37656
  this.emit("close", result);
37403
- resolve(result);
37657
+ resolve2(result);
37404
37658
  return;
37405
37659
  }
37406
37660
  const onClose = (closeCode, closeReason) => {
@@ -37409,7 +37663,7 @@ var OCPPClient = class _OCPPClient extends EventEmitter {
37409
37663
  this._cleanup();
37410
37664
  const result = { code: closeCode, reason: closeReason.toString() };
37411
37665
  this.emit("close", result);
37412
- resolve(result);
37666
+ resolve2(result);
37413
37667
  };
37414
37668
  this._ws.on("close", onClose);
37415
37669
  if (force) {
@@ -37484,7 +37738,7 @@ var OCPPClient = class _OCPPClient extends EventEmitter {
37484
37738
  }
37485
37739
  if (this._state !== OPEN) {
37486
37740
  if (this._options.offlineQueue && (this._state === CLOSED || this._state === CONNECTING)) {
37487
- return new Promise((resolve, reject) => {
37741
+ return new Promise((resolve2, reject) => {
37488
37742
  const maxSize = this._options.offlineQueueMaxSize ?? 100;
37489
37743
  if (this._offlineQueue.length >= maxSize) {
37490
37744
  this._offlineQueue.shift();
@@ -37496,7 +37750,7 @@ var OCPPClient = class _OCPPClient extends EventEmitter {
37496
37750
  }
37497
37751
  );
37498
37752
  }
37499
- this._offlineQueue.push({ method, params, options, resolve, reject });
37753
+ this._offlineQueue.push({ method, params, options, resolve: resolve2, reject });
37500
37754
  this._logger?.debug?.("Call queued offline", {
37501
37755
  method,
37502
37756
  queueSize: this._offlineQueue.length
@@ -37555,7 +37809,7 @@ var OCPPClient = class _OCPPClient extends EventEmitter {
37555
37809
  ctxvals.params
37556
37810
  ];
37557
37811
  const messageStr = JSON.stringify(message);
37558
- callResult = await new Promise((resolve, reject) => {
37812
+ callResult = await new Promise((resolve2, reject) => {
37559
37813
  const timeoutHandle = setTimeout(() => {
37560
37814
  this._pendingCalls.delete(msgId);
37561
37815
  reject(
@@ -37573,7 +37827,7 @@ var OCPPClient = class _OCPPClient extends EventEmitter {
37573
37827
  options.signal.addEventListener("abort", abortHandler);
37574
37828
  }
37575
37829
  this._pendingCalls.set(msgId, {
37576
- resolve,
37830
+ resolve: resolve2,
37577
37831
  reject,
37578
37832
  timeoutHandle,
37579
37833
  abortHandler: options.signal ? () => options.signal?.removeEventListener("abort", abortHandler) : void 0,
@@ -37653,11 +37907,15 @@ var OCPPClient = class _OCPPClient extends EventEmitter {
37653
37907
  });
37654
37908
  }
37655
37909
  // ─── Internal: Message handling ──────────────────────────────
37656
- _onMessage(rawData) {
37910
+ _onMessage(rawData, preParsed) {
37657
37911
  this._recordActivity();
37658
37912
  let message;
37659
37913
  try {
37660
- message = JSON.parse(rawData);
37914
+ if (preParsed !== void 0) {
37915
+ message = preParsed;
37916
+ } else {
37917
+ message = JSON.parse(rawData);
37918
+ }
37661
37919
  if (!Array.isArray(message)) throw new Error("Message is not an array");
37662
37920
  } catch (err) {
37663
37921
  this._onBadMessage(
@@ -37667,6 +37925,36 @@ var OCPPClient = class _OCPPClient extends EventEmitter {
37667
37925
  return;
37668
37926
  }
37669
37927
  const messageType = message[0];
37928
+ const messageId = message[1];
37929
+ if (typeof messageId !== "string") {
37930
+ this._onBadMessage(
37931
+ typeof rawData === "string" ? rawData : rawData.toString(),
37932
+ new RPCMessageTypeNotSupportedError(
37933
+ `Invalid MessageId type: ${typeof messageId} (expected string)`
37934
+ )
37935
+ );
37936
+ return;
37937
+ }
37938
+ if (messageType === MessageType.CALL && message.length < 4 || messageType === MessageType.CALLRESULT && message.length < 3 || messageType === MessageType.CALLERROR && message.length < 5) {
37939
+ this._onBadMessage(
37940
+ JSON.stringify(message),
37941
+ new RPCMessageTypeNotSupportedError(
37942
+ `Missing payload elements for message type ${messageType}`
37943
+ )
37944
+ );
37945
+ return;
37946
+ }
37947
+ const payloadIndex = messageType === MessageType.CALLERROR ? 4 : messageType === MessageType.CALL ? 3 : 2;
37948
+ const payload = message[payloadIndex];
37949
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
37950
+ this._onBadMessage(
37951
+ JSON.stringify(message),
37952
+ new RPCMessageTypeNotSupportedError(
37953
+ `Payload must be a JSON object, got ${payload === null ? "null" : Array.isArray(payload) ? "array" : typeof payload}`
37954
+ )
37955
+ );
37956
+ return;
37957
+ }
37670
37958
  switch (messageType) {
37671
37959
  case MessageType.CALL:
37672
37960
  this._handleIncomingCall(message);
@@ -38146,6 +38434,23 @@ var OCPPClient = class _OCPPClient extends EventEmitter {
38146
38434
  if (tls.passphrase) opts.passphrase = tls.passphrase;
38147
38435
  }
38148
38436
  }
38437
+ const compression = this._options.compression;
38438
+ if (compression) {
38439
+ opts.perMessageDeflate = compression === true ? {
38440
+ zlibDeflateOptions: { level: 6, memLevel: 8 },
38441
+ zlibInflateOptions: {},
38442
+ clientNoContextTakeover: true,
38443
+ serverNoContextTakeover: true
38444
+ } : {
38445
+ zlibDeflateOptions: {
38446
+ level: compression.level ?? 6,
38447
+ memLevel: compression.memLevel ?? 8
38448
+ },
38449
+ zlibInflateOptions: {},
38450
+ clientNoContextTakeover: compression.clientNoContextTakeover ?? true,
38451
+ serverNoContextTakeover: compression.serverNoContextTakeover ?? true
38452
+ };
38453
+ }
38149
38454
  return opts;
38150
38455
  }
38151
38456
  // ─── Internal: Cleanup ───────────────────────────────────────
@@ -38211,7 +38516,7 @@ var LRUMap = class extends Map {
38211
38516
  };
38212
38517
 
38213
38518
  // src/router.ts
38214
- import { EventEmitter as EventEmitter2 } from "events";
38519
+ import { EventEmitter as EventEmitter3 } from "events";
38215
38520
  async function executeMiddlewareChain(middlewares, ctx) {
38216
38521
  let index = -1;
38217
38522
  const dispatch = async (i, payload) => {
@@ -38235,7 +38540,7 @@ async function executeMiddlewareChain(middlewares, ctx) {
38235
38540
  };
38236
38541
  await dispatch(0);
38237
38542
  }
38238
- var OCPPRouter = class extends EventEmitter2 {
38543
+ var OCPPRouter = class extends EventEmitter3 {
38239
38544
  /** Raw registered patterns (strings and/or RegExp) for reference. */
38240
38545
  patterns;
38241
38546
  /** Connection middlewares attached to this router. */
@@ -38331,7 +38636,7 @@ function createRouter(...patterns) {
38331
38636
  }
38332
38637
 
38333
38638
  // src/server.ts
38334
- import { EventEmitter as EventEmitter3 } from "events";
38639
+ import { EventEmitter as EventEmitter4 } from "events";
38335
38640
  import {
38336
38641
  createServer as createHttpServer
38337
38642
  } from "http";
@@ -38415,8 +38720,8 @@ var TrieNode = class {
38415
38720
  /** Routers registered at this exact node (leaf). */
38416
38721
  routers = [];
38417
38722
  };
38418
- function normalizePath(path) {
38419
- return path.replace(/\/+/g, "/").replace(/^\/|\/$/g, "").split("/").filter(Boolean);
38723
+ function normalizePath(path2) {
38724
+ return path2.replace(/\/+/g, "/").replace(/^\/|\/$/g, "").split("/").filter(Boolean);
38420
38725
  }
38421
38726
  var RadixTrie = class {
38422
38727
  root = new TrieNode();
@@ -38563,6 +38868,8 @@ var OCPPServerClient = class extends OCPPClient {
38563
38868
  super(options);
38564
38869
  this._serverSession = context.session;
38565
38870
  this._serverHandshake = context.handshake;
38871
+ this._adaptiveMultiplier = context.adaptiveMultiplier ?? null;
38872
+ this._workerPool = context.workerPool ?? null;
38566
38873
  this._state = ConnectionState.OPEN;
38567
38874
  this._identity = this._options.identity;
38568
38875
  this._ws = context.ws;
@@ -38572,6 +38879,8 @@ var OCPPServerClient = class extends OCPPClient {
38572
38879
  }
38573
38880
  // ─── Rate Limiting State ──────────────────────────────────────────
38574
38881
  _rateLimits = {};
38882
+ _adaptiveMultiplier = null;
38883
+ _workerPool = null;
38575
38884
  _checkRateLimit(method) {
38576
38885
  const limits = this._options.rateLimit;
38577
38886
  if (!limits) return true;
@@ -38583,7 +38892,8 @@ var OCPPServerClient = class extends OCPPClient {
38583
38892
  this._rateLimits[key] = bucket;
38584
38893
  } else {
38585
38894
  const timePassed = now - bucket.lastRefill;
38586
- const refillRate = limit / windowMs;
38895
+ const adaptiveScale = this._adaptiveMultiplier?.() ?? 1;
38896
+ const refillRate = limit / windowMs * adaptiveScale;
38587
38897
  const tokensToAdd = timePassed * refillRate;
38588
38898
  if (tokensToAdd > 0) {
38589
38899
  bucket.tokens = Math.min(limit, bucket.tokens + tokensToAdd);
@@ -38628,6 +38938,19 @@ var OCPPServerClient = class extends OCPPClient {
38628
38938
  this._handleRateLimitExceeded(pData || data.toString());
38629
38939
  return;
38630
38940
  }
38941
+ if (pData !== void 0) {
38942
+ this._onMessage(data, pData);
38943
+ return;
38944
+ }
38945
+ }
38946
+ if (this._workerPool) {
38947
+ const raw = typeof data === "string" ? data : data;
38948
+ this._workerPool.parse(raw).then((result) => {
38949
+ this._onMessage(data, result.message);
38950
+ }).catch(() => {
38951
+ this._onMessage(data);
38952
+ });
38953
+ return;
38631
38954
  }
38632
38955
  this._onMessage(data);
38633
38956
  });
@@ -38638,7 +38961,18 @@ var OCPPServerClient = class extends OCPPClient {
38638
38961
  this._onClose(code, reason)
38639
38962
  )
38640
38963
  );
38641
- ws.on("error", (err) => this.emit("error", err));
38964
+ ws.on("error", (err) => {
38965
+ if (this.listenerCount("error") > 0) {
38966
+ this.emit("error", err);
38967
+ } else {
38968
+ this._logger?.debug?.(
38969
+ "WebSocket error (unhandled by client listener)",
38970
+ {
38971
+ error: err.message
38972
+ }
38973
+ );
38974
+ }
38975
+ });
38642
38976
  ws.on("ping", () => {
38643
38977
  this._recordActivity();
38644
38978
  this.emit("ping");
@@ -38717,8 +39051,110 @@ var OCPPServerClient = class extends OCPPClient {
38717
39051
  }
38718
39052
  };
38719
39053
 
39054
+ // src/worker-pool.ts
39055
+ import { cpus as cpus2 } from "os";
39056
+ import { resolve } from "path";
39057
+ import { Worker } from "worker_threads";
39058
+ var WorkerPool = class {
39059
+ _workers = [];
39060
+ _nextWorker = 0;
39061
+ _taskId = 0;
39062
+ _pending = /* @__PURE__ */ new Map();
39063
+ _maxQueueSize;
39064
+ _terminated = false;
39065
+ constructor(options = {}) {
39066
+ const poolSize = options.poolSize ?? Math.max(2, cpus2().length - 2);
39067
+ this._maxQueueSize = options.maxQueueSize ?? 1e4;
39068
+ const workerPath = resolve(__dirname, "parse-worker.js");
39069
+ for (let i = 0; i < poolSize; i++) {
39070
+ const worker = new Worker(workerPath);
39071
+ worker.on(
39072
+ "message",
39073
+ (response) => {
39074
+ const task = this._pending.get(response.id);
39075
+ if (!task) return;
39076
+ this._pending.delete(response.id);
39077
+ if (response.error) {
39078
+ task.reject(new Error(response.error));
39079
+ } else {
39080
+ task.resolve({
39081
+ message: response.message,
39082
+ validationError: response.validationError
39083
+ });
39084
+ }
39085
+ }
39086
+ );
39087
+ worker.on("error", (err) => {
39088
+ console.error(`[WorkerPool] Worker ${i} error:`, err.message);
39089
+ });
39090
+ this._workers.push(worker);
39091
+ }
39092
+ }
39093
+ /** Number of worker threads in the pool */
39094
+ get size() {
39095
+ return this._workers.length;
39096
+ }
39097
+ /** Number of pending (unresolved) parse tasks */
39098
+ get pendingTasks() {
39099
+ return this._pending.size;
39100
+ }
39101
+ /**
39102
+ * Send raw data to a worker for JSON parsing + optional validation.
39103
+ * Uses round-robin worker selection.
39104
+ */
39105
+ parse(data, schemaInfo) {
39106
+ if (this._terminated) {
39107
+ return Promise.reject(new Error("WorkerPool has been shut down"));
39108
+ }
39109
+ if (this._pending.size >= this._maxQueueSize) {
39110
+ return Promise.reject(
39111
+ new Error(
39112
+ `WorkerPool queue full (${this._maxQueueSize} pending tasks)`
39113
+ )
39114
+ );
39115
+ }
39116
+ return new Promise((resolve2, reject) => {
39117
+ const id = this._taskId++;
39118
+ this._pending.set(id, {
39119
+ resolve: resolve2,
39120
+ reject
39121
+ });
39122
+ const worker = this._workers[this._nextWorker % this._workers.length];
39123
+ this._nextWorker = (this._nextWorker + 1) % this._workers.length;
39124
+ worker.postMessage({ id, buffer: data, schemaInfo });
39125
+ });
39126
+ }
39127
+ /** Gracefully terminate all workers */
39128
+ async shutdown() {
39129
+ if (this._terminated) return;
39130
+ this._terminated = true;
39131
+ for (const [id, task] of this._pending) {
39132
+ task.reject(new Error("WorkerPool shutting down"));
39133
+ this._pending.delete(id);
39134
+ }
39135
+ const terminatePromises = this._workers.map(async (worker) => {
39136
+ try {
39137
+ await Promise.race([
39138
+ worker.terminate(),
39139
+ new Promise((resolve2) => setTimeout(resolve2, 5e3))
39140
+ ]);
39141
+ } catch {
39142
+ }
39143
+ });
39144
+ await Promise.allSettled(terminatePromises);
39145
+ this._workers = [];
39146
+ }
39147
+ };
39148
+ function createWorkerPool(options = {}) {
39149
+ try {
39150
+ return new WorkerPool(options);
39151
+ } catch {
39152
+ return null;
39153
+ }
39154
+ }
39155
+
38720
39156
  // src/server.ts
38721
- var OCPPServer = class extends EventEmitter3 {
39157
+ var OCPPServer = class extends EventEmitter4 {
38722
39158
  _options;
38723
39159
  /** Radix trie for O(k) route matching (string patterns). */
38724
39160
  _trie = new RadixTrie();
@@ -38737,6 +39173,9 @@ var OCPPServer = class extends EventEmitter3 {
38737
39173
  _globalCORS;
38738
39174
  // Connection-level rate limiting (per-IP token bucket)
38739
39175
  _connectionBuckets = /* @__PURE__ */ new Map();
39176
+ _adaptiveLimiter = null;
39177
+ _plugins = [];
39178
+ _workerPool = null;
38740
39179
  // Robustness & Clustering
38741
39180
  _nodeId = createId2();
38742
39181
  _sessions;
@@ -38746,9 +39185,9 @@ var OCPPServer = class extends EventEmitter3 {
38746
39185
  super();
38747
39186
  this.setMaxListeners(0);
38748
39187
  if (options.strictMode) {
38749
- if (!options.strictModeValidators && !options.protocols?.length) {
39188
+ if (!options.protocols?.length) {
38750
39189
  throw new Error(
38751
- "strictMode requires either strictModeValidators or protocols to be specified"
39190
+ "strictMode requires protocols to be specified (e.g. protocols: ['ocpp1.6'])"
38752
39191
  );
38753
39192
  }
38754
39193
  }
@@ -38769,7 +39208,8 @@ var OCPPServer = class extends EventEmitter3 {
38769
39208
  this._sessions = new LRUMap(maxSessions);
38770
39209
  this._wss = new WebSocketServer({
38771
39210
  noServer: true,
38772
- maxPayload: this._options.maxPayloadBytes ?? 65536
39211
+ maxPayload: this._options.maxPayloadBytes ?? 65536,
39212
+ perMessageDeflate: this._buildCompressionConfig()
38773
39213
  });
38774
39214
  this._gcInterval = setInterval(() => {
38775
39215
  const now = Date.now();
@@ -38782,6 +39222,32 @@ var OCPPServer = class extends EventEmitter3 {
38782
39222
  this._logger = initLogger(this._options.logging, {
38783
39223
  component: "OCPPServer"
38784
39224
  });
39225
+ const rl = this._options.rateLimit;
39226
+ if (rl?.adaptive) {
39227
+ this._adaptiveLimiter = new AdaptiveLimiter({
39228
+ cpuThresholdPercent: rl.cpuThresholdPercent,
39229
+ memThresholdPercent: rl.memThresholdPercent,
39230
+ cooldownMs: rl.cooldownMs
39231
+ });
39232
+ this._adaptiveLimiter.on(
39233
+ "adapted",
39234
+ (event) => {
39235
+ this._logger?.info?.("Adaptive rate limit adjusted", event);
39236
+ this.emit("rateLimit:adapted", event);
39237
+ }
39238
+ );
39239
+ this._adaptiveLimiter.start();
39240
+ }
39241
+ const wt = this._options.workerThreads;
39242
+ if (wt) {
39243
+ const poolOpts = typeof wt === "object" ? wt : {};
39244
+ this._workerPool = createWorkerPool(poolOpts);
39245
+ if (this._workerPool) {
39246
+ this._logger?.info?.("Worker thread pool initialized", {
39247
+ poolSize: this._workerPool.size
39248
+ });
39249
+ }
39250
+ }
38785
39251
  }
38786
39252
  // ─── Getters ─────────────────────────────────────────────────
38787
39253
  get log() {
@@ -38904,8 +39370,44 @@ var OCPPServer = class extends EventEmitter3 {
38904
39370
  return this;
38905
39371
  }
38906
39372
  /**
38907
- * Registers a new middleware chain, acting as a wildcard/catch-all router if no patterns are added.
38908
- * `server.use(middleware).route("/api").on("client", ...)`
39373
+ * Registers one or more plugins for server lifecycle hooks.
39374
+ * Plugins are called in registration order for all lifecycle events.
39375
+ *
39376
+ * @example Single plugin
39377
+ * ```ts
39378
+ * server.plugin(metricsPlugin);
39379
+ * ```
39380
+ *
39381
+ * @example Multiple plugins
39382
+ * ```ts
39383
+ * server.plugin(metricsPlugin, loggingPlugin, otelPlugin);
39384
+ * ```
39385
+ */
39386
+ plugin(...plugins) {
39387
+ for (const plugin of plugins) {
39388
+ this._plugins.push(plugin);
39389
+ this._logger?.info?.("Plugin registered", { name: plugin.name });
39390
+ if (plugin.onInit) {
39391
+ const result = plugin.onInit(this);
39392
+ if (result instanceof Promise) {
39393
+ result.catch((err) => {
39394
+ this._logger?.error?.("Plugin onInit error", {
39395
+ name: plugin.name,
39396
+ error: err.message
39397
+ });
39398
+ });
39399
+ }
39400
+ }
39401
+ }
39402
+ return this;
39403
+ }
39404
+ /**
39405
+ * Registers middleware chain(s) as a wildcard/catch-all router.
39406
+ *
39407
+ * @example
39408
+ * ```ts
39409
+ * server.use(myMiddleware).route("/api").on("client", ...);
39410
+ * ```
38909
39411
  */
38910
39412
  use(...middlewares) {
38911
39413
  const router = new OCPPRouter();
@@ -39061,7 +39563,7 @@ var OCPPServer = class extends EventEmitter3 {
39061
39563
  );
39062
39564
  }
39063
39565
  if (!options?.server) {
39064
- await new Promise((resolve, reject) => {
39566
+ await new Promise((resolve2, reject) => {
39065
39567
  httpServer.on("error", reject);
39066
39568
  httpServer.listen(port, host, () => {
39067
39569
  httpServer.removeListener("error", reject);
@@ -39070,7 +39572,7 @@ var OCPPServer = class extends EventEmitter3 {
39070
39572
  port: typeof addr === "object" ? addr?.port : port,
39071
39573
  host: host ?? "0.0.0.0"
39072
39574
  });
39073
- resolve();
39575
+ resolve2();
39074
39576
  });
39075
39577
  });
39076
39578
  }
@@ -39379,13 +39881,13 @@ var OCPPServer = class extends EventEmitter3 {
39379
39881
  selectedProtocol = handshake.protocols.values().next().value ?? void 0;
39380
39882
  } else {
39381
39883
  acceptOptions = await new Promise(
39382
- (resolve, reject) => {
39884
+ (resolve2, reject) => {
39383
39885
  let settled = false;
39384
39886
  const accept = (opts) => {
39385
39887
  if (settled) return;
39386
39888
  settled = true;
39387
39889
  if (opts?.protocol) selectedProtocol = opts.protocol;
39388
- resolve(opts);
39890
+ resolve2(opts);
39389
39891
  };
39390
39892
  const rejectAuth = (code = 401, message = "Unauthorized") => {
39391
39893
  if (!settled) {
@@ -39517,7 +40019,9 @@ var OCPPServer = class extends EventEmitter3 {
39517
40019
  ws,
39518
40020
  handshake,
39519
40021
  session: finalSession,
39520
- protocol: selectedProtocol
40022
+ protocol: selectedProtocol,
40023
+ adaptiveMultiplier: this._adaptiveLimiter ? () => this._adaptiveLimiter.multiplier : void 0,
40024
+ workerPool: this._workerPool ?? void 0
39521
40025
  });
39522
40026
  this._updateSessionActivity(identity, client.session);
39523
40027
  const existingClient = this._clientsByIdentity.get(identity);
@@ -39549,21 +40053,52 @@ var OCPPServer = class extends EventEmitter3 {
39549
40053
  remoteAddress: req.socket.remoteAddress,
39550
40054
  protocol: selectedProtocol
39551
40055
  });
39552
- client.on("close", () => {
39553
- this._clients.delete(client);
39554
- if (this._clientsByIdentity.get(identity) === client) {
39555
- this._clientsByIdentity.delete(identity);
40056
+ client.on(
40057
+ "close",
40058
+ ({ code, reason }) => {
40059
+ this._clients.delete(client);
40060
+ if (this._clientsByIdentity.get(identity) === client) {
40061
+ this._clientsByIdentity.delete(identity);
40062
+ }
40063
+ if (this?._adapter?.removePresence) {
40064
+ this._adapter.removePresence(identity).catch((err) => {
40065
+ this._logger?.error?.("Error removing presence", {
40066
+ identity,
40067
+ error: err
40068
+ });
40069
+ });
40070
+ }
40071
+ for (const plugin of this._plugins) {
40072
+ try {
40073
+ plugin.onDisconnect?.(client, code, reason);
40074
+ } catch (err) {
40075
+ this._logger?.error?.("Plugin onDisconnect error", {
40076
+ name: plugin.name,
40077
+ error: err.message
40078
+ });
40079
+ }
40080
+ }
40081
+ this._logger?.info?.("Client disconnected", { identity });
39556
40082
  }
39557
- if (this?._adapter?.removePresence) {
39558
- this._adapter.removePresence(identity).catch((err) => {
39559
- this._logger?.error?.("Error removing presence", {
39560
- identity,
39561
- error: err
40083
+ );
40084
+ for (const plugin of this._plugins) {
40085
+ try {
40086
+ const result = plugin.onConnection?.(client);
40087
+ if (result instanceof Promise) {
40088
+ result.catch((err) => {
40089
+ this._logger?.error?.("Plugin onConnection error", {
40090
+ name: plugin.name,
40091
+ error: err.message
40092
+ });
39562
40093
  });
40094
+ }
40095
+ } catch (err) {
40096
+ this._logger?.error?.("Plugin onConnection error", {
40097
+ name: plugin.name,
40098
+ error: err.message
39563
40099
  });
39564
40100
  }
39565
- this._logger?.info?.("Client disconnected", { identity });
39566
- });
40101
+ }
39567
40102
  this.emit("client", client);
39568
40103
  for (const router of matchedRouters) {
39569
40104
  router.emit("client", client);
@@ -39598,13 +40133,13 @@ var OCPPServer = class extends EventEmitter3 {
39598
40133
  identity: client.identity,
39599
40134
  bufferedAmount: ws.bufferedAmount
39600
40135
  });
39601
- await new Promise((resolve) => {
40136
+ await new Promise((resolve2) => {
39602
40137
  let elapsed = 0;
39603
40138
  const check = setInterval(() => {
39604
40139
  elapsed += 50;
39605
40140
  if (!ws || ws.bufferedAmount === 0 || elapsed >= drainTimeout) {
39606
40141
  clearInterval(check);
39607
- resolve();
40142
+ resolve2();
39608
40143
  }
39609
40144
  }, 50);
39610
40145
  });
@@ -39626,12 +40161,28 @@ var OCPPServer = class extends EventEmitter3 {
39626
40161
  this._wss = new WebSocketServer({ noServer: true });
39627
40162
  }
39628
40163
  const serverClosePromises = Array.from(this._httpServers).map(
39629
- (server) => new Promise((resolve) => {
39630
- server.close(() => resolve());
40164
+ (server) => new Promise((resolve2) => {
40165
+ server.close(() => resolve2());
39631
40166
  })
39632
40167
  );
39633
40168
  await Promise.allSettled(serverClosePromises);
39634
40169
  this._httpServers.clear();
40170
+ if (this._adaptiveLimiter) {
40171
+ this._adaptiveLimiter.stop();
40172
+ }
40173
+ for (const plugin of this._plugins) {
40174
+ try {
40175
+ const result = plugin.onClose?.();
40176
+ if (result instanceof Promise) {
40177
+ await result;
40178
+ }
40179
+ } catch (err) {
40180
+ this._logger?.error?.("Plugin onClose error", {
40181
+ name: plugin.name,
40182
+ error: err.message
40183
+ });
40184
+ }
40185
+ }
39635
40186
  if (this._adapter) {
39636
40187
  await this._adapter.disconnect();
39637
40188
  }
@@ -39693,6 +40244,55 @@ var OCPPServer = class extends EventEmitter3 {
39693
40244
  return void 0;
39694
40245
  }
39695
40246
  }
40247
+ // ─── Batch Calls ──────────────────────────────────────────────
40248
+ /**
40249
+ * Pipeline multiple calls to a single client into a concurrent batch.
40250
+ * Useful for reconnection warm-up (e.g. GetConfiguration, ChangeAvailability, etc.)
40251
+ * where sequential calls would add unnecessary round-trip latency.
40252
+ *
40253
+ * @param identity The client identity to send calls to
40254
+ * @param calls Array of { method, params, options? } to execute concurrently
40255
+ * @returns Array of results in the same order as the calls array.
40256
+ * Each element is the call result, or `undefined` if that individual call failed.
40257
+ *
40258
+ * @example
40259
+ * ```ts
40260
+ * const results = await server.sendBatch('CP-101', [
40261
+ * { method: 'GetConfiguration', params: { key: ['MeterInterval'] } },
40262
+ * { method: 'ChangeAvailability', params: { type: 'Operative' } },
40263
+ * { method: 'TriggerMessage', params: { requestedMessage: 'StatusNotification' } },
40264
+ * ]);
40265
+ * ```
40266
+ */
40267
+ async sendBatch(identity, calls) {
40268
+ if (calls.length === 0) return [];
40269
+ const client = this._clientsByIdentity.get(identity);
40270
+ if (!client) {
40271
+ this._logger?.warn?.("sendBatch: client not found locally", { identity });
40272
+ return calls.map(() => void 0);
40273
+ }
40274
+ const originalConcurrency = client.options.callConcurrency ?? 1;
40275
+ if (calls.length > originalConcurrency) {
40276
+ client.reconfigure({ callConcurrency: calls.length });
40277
+ }
40278
+ try {
40279
+ const results = await Promise.allSettled(
40280
+ calls.map((c) => client.call(c.method, c.params, c.options ?? {}))
40281
+ );
40282
+ return results.map((r) => {
40283
+ if (r.status === "fulfilled") return r.value;
40284
+ this._logger?.warn?.("sendBatch: individual call failed", {
40285
+ identity,
40286
+ error: r.reason?.message
40287
+ });
40288
+ return void 0;
40289
+ });
40290
+ } finally {
40291
+ if (calls.length > originalConcurrency) {
40292
+ client.reconfigure({ callConcurrency: originalConcurrency });
40293
+ }
40294
+ }
40295
+ }
39696
40296
  // ─── Pub/Sub Adapter ─────────────────────────────────────────
39697
40297
  async setAdapter(adapter) {
39698
40298
  this._adapter = adapter;
@@ -39834,8 +40434,34 @@ var OCPPServer = class extends EventEmitter3 {
39834
40434
  }
39835
40435
  await Promise.all(localPromises);
39836
40436
  }
40437
+ // ─── Internal: Compression Config ───────────────────────────────
40438
+ _buildCompressionConfig() {
40439
+ const compression = this._options.compression;
40440
+ if (!compression) return false;
40441
+ if (compression === true) {
40442
+ return {
40443
+ threshold: 1024,
40444
+ zlibDeflateOptions: { level: 6, memLevel: 8 },
40445
+ zlibInflateOptions: {},
40446
+ serverNoContextTakeover: true,
40447
+ clientNoContextTakeover: true
40448
+ };
40449
+ }
40450
+ return {
40451
+ threshold: compression.threshold ?? 1024,
40452
+ zlibDeflateOptions: {
40453
+ level: compression.level ?? 6,
40454
+ memLevel: compression.memLevel ?? 8
40455
+ },
40456
+ zlibInflateOptions: {},
40457
+ serverNoContextTakeover: compression.serverNoContextTakeover ?? true,
40458
+ clientNoContextTakeover: compression.clientNoContextTakeover ?? true
40459
+ };
40460
+ }
39837
40461
  };
39838
40462
  export {
40463
+ AdaptiveLimiter,
40464
+ ClusterDriver,
39839
40465
  ConnectionState,
39840
40466
  InMemoryAdapter,
39841
40467
  LRUMap,
@@ -39867,6 +40493,7 @@ export {
39867
40493
  WebsocketUpgradeError,
39868
40494
  combineAuth,
39869
40495
  createLoggingMiddleware,
40496
+ createPlugin,
39870
40497
  createRPCError,
39871
40498
  createRouter,
39872
40499
  createValidator,