ocpp-ws-io 2.1.7 → 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.
@@ -1,4 +1,10 @@
1
- import { IncomingMessage } from 'node:http';
1
+ import * as node_http from 'node:http';
2
+ import { Server, IncomingMessage } from 'node:http';
3
+ import { Duplex } from 'node:stream';
4
+ import * as WebSocket from 'ws';
5
+ import WebSocket__default, { WebSocket as WebSocket$1 } from 'ws';
6
+ import * as node_https from 'node:https';
7
+ import { EventEmitter as EventEmitter$1 } from 'node:events';
2
8
  import { TLSSocket } from 'node:tls';
3
9
  import { LogEntry } from 'voltlog-io';
4
10
  import Ajv from 'ajv';
@@ -4425,6 +4431,95 @@ type OCPPResponseType$1<P extends keyof OCPPMethodMap, M extends string> = P ext
4425
4431
  response: infer R;
4426
4432
  } ? R : never : never : never;
4427
4433
 
4434
+ /**
4435
+ * Compiled regex pattern for RegExp-based route fallback.
4436
+ * Only used when a user registers a RegExp pattern (not string patterns).
4437
+ * @internal
4438
+ */
4439
+ interface CompiledRegexPattern {
4440
+ regex: RegExp;
4441
+ paramNames: string[];
4442
+ }
4443
+ declare const OCPPRouter_base: new () => TypedEventEmitter<ServerEvents>;
4444
+ /**
4445
+ * OCPPRouter — An Express-like Connection dispatcher.
4446
+ * Isolated handler for a specific set of matching URL route patterns.
4447
+ *
4448
+ * String patterns are matched via radix trie (O(k) lookup, managed by OCPPServer).
4449
+ * RegExp patterns fall back to linear matching.
4450
+ */
4451
+ declare class OCPPRouter extends OCPPRouter_base {
4452
+ /** Raw registered patterns (strings and/or RegExp) for reference. */
4453
+ patterns: Array<string | RegExp>;
4454
+ /** Connection middlewares attached to this router. */
4455
+ middlewares: ConnectionMiddleware[];
4456
+ /** Auth callback for this route endpoint. */
4457
+ authCallback: AuthCallback<unknown> | null;
4458
+ /** Route-level CORS options. */
4459
+ _routeCORS?: CORSOptions;
4460
+ /** Route-level config overrides. */
4461
+ _routeConfig?: RouterConfig;
4462
+ /**
4463
+ * Compiled RegExp patterns for fallback linear matching.
4464
+ * Only populated when RegExp patterns are registered.
4465
+ * @internal
4466
+ */
4467
+ _regexPatterns: CompiledRegexPattern[];
4468
+ constructor(patterns?: Array<string | RegExp>, middlewares?: ConnectionMiddleware[]);
4469
+ /**
4470
+ * Appends URL paths or regular expressions to this router's match condition.
4471
+ * String patterns are stored for trie insertion by OCPPServer.
4472
+ * RegExp patterns are compiled for linear fallback matching.
4473
+ */
4474
+ route(...patterns: Array<string | RegExp>): this;
4475
+ /**
4476
+ * Appends connection middlewares to this router's execution chain.
4477
+ */
4478
+ use(...middlewares: ConnectionMiddleware[]): this;
4479
+ /**
4480
+ * Applies specific CORS rules to connections matching this router's paths.
4481
+ */
4482
+ cors(options: CORSOptions): this;
4483
+ /**
4484
+ * Overrides global connection settings (e.g. timeouts, protocols) for this router.
4485
+ */
4486
+ config(options: RouterConfig): this;
4487
+ /**
4488
+ * Registers an authentication and protocol-negotiation callback for this route endpoint.
4489
+ */
4490
+ auth<TSession = Record<string, unknown>>(callback: AuthCallback<TSession>): this;
4491
+ /**
4492
+ * Binds a version-specific OCPP message handler directly to all clients that match this route.
4493
+ *
4494
+ * @throws {Error} AT RUNTIME when a client connects, if a handler for this version and method is already registered for that client.
4495
+ */
4496
+ handle<V extends OCPPProtocol$1, M extends AllMethodNames$1<V>>(version: V, method: M, handler: (context: RouterHandlerContext<OCPPRequestType$1<V, M>>) => OCPPResponseType$1<V, M> | Promise<OCPPResponseType$1<V, M>>): this;
4497
+ /**
4498
+ * Binds a custom/extension message handler directly to all clients that match this route.
4499
+ *
4500
+ * @throws {Error} AT RUNTIME when a client connects, if a handler for this protocol and method is already registered for that client.
4501
+ */
4502
+ handle<S extends string>(version: S extends OCPPProtocol$1 ? never : S, method: string, handler: (context: RouterHandlerContext<Record<string, any>>) => any): this;
4503
+ /**
4504
+ * Binds a message handler directly to all clients that match this route using the default protocol.
4505
+ *
4506
+ * @throws {Error} AT RUNTIME when a client connects, if a handler for this method is already registered for that client.
4507
+ */
4508
+ handle<M extends AllMethodNames$1<OCPPProtocol$1>>(method: M, handler: (context: RouterHandlerContext<OCPPRequestType$1<OCPPProtocol$1, M>>) => OCPPResponseType$1<OCPPProtocol$1, M> | Promise<OCPPResponseType$1<OCPPProtocol$1, M>>): this;
4509
+ /**
4510
+ * Binds a custom/extension method not in the typed map.
4511
+ *
4512
+ * @throws {Error} AT RUNTIME when a client connects, if a handler for this method is already registered for that client.
4513
+ */
4514
+ handle(method: string, handler: (context: RouterHandlerContext<Record<string, any>>) => any): this;
4515
+ /**
4516
+ * Binds a wildcard handler to all clients that match this route.
4517
+ *
4518
+ * @throws {Error} AT RUNTIME when a client connects, if a wildcard handler is already registered for that client.
4519
+ */
4520
+ handle(handler: RouterWildcardHandler): this;
4521
+ }
4522
+
4428
4523
  /**
4429
4524
  * Middleware handling for intercepting and modifying OCPP operations.
4430
4525
  *
@@ -4448,6 +4543,561 @@ declare class MiddlewareStack<TContext> {
4448
4543
  execute<TReturn = unknown>(context: TContext, runner: (context: TContext) => Promise<TReturn> | TReturn): Promise<TReturn>;
4449
4544
  }
4450
4545
 
4546
+ declare const OCPPClient_base: new () => TypedEventEmitter<ClientEvents>;
4547
+ /**
4548
+ * OCPPClient — A typed WebSocket RPC client for OCPP communication.
4549
+ *
4550
+ * Supports all 3 OCPP Security Profiles:
4551
+ * - Profile 1: Basic Auth over unsecured WS
4552
+ * - Profile 2: TLS + Basic Auth
4553
+ * - Profile 3: Mutual TLS (client certificates)
4554
+ */
4555
+ declare class OCPPClient<P extends OCPPProtocol$1 = OCPPProtocol$1> extends OCPPClient_base {
4556
+ static readonly CONNECTING: 0;
4557
+ static readonly OPEN: 1;
4558
+ static readonly CLOSING: 2;
4559
+ static readonly CLOSED: 3;
4560
+ protected _options: Required<Pick<ClientOptions, "identity" | "endpoint" | "callTimeoutMs" | "pingIntervalMs" | "deferPingsOnActivity" | "callConcurrency" | "maxBadMessages" | "respondWithDetailedErrors" | "reconnect" | "maxReconnects" | "backoffMin" | "backoffMax">> & ClientOptions;
4561
+ protected _state: ConnectionState;
4562
+ protected _ws: WebSocket__default | null;
4563
+ protected _protocol: string | undefined;
4564
+ protected _identity: string;
4565
+ private _handlers;
4566
+ private _wildcardHandler;
4567
+ private _pendingCalls;
4568
+ private _pendingResponses;
4569
+ private _callQueue;
4570
+ private _pingTimer;
4571
+ private _pongTimer;
4572
+ private _closePromise;
4573
+ private _reconnectAttempt;
4574
+ private _reconnectTimer;
4575
+ private _badMessageCount;
4576
+ private _lastActivity;
4577
+ private _outboundBuffer;
4578
+ private _offlineQueue;
4579
+ private _middleware;
4580
+ private _validators;
4581
+ private _strictProtocols;
4582
+ protected _handshake: unknown;
4583
+ protected _logger: LoggerLike$1;
4584
+ protected _exchangeLog: boolean;
4585
+ protected _prettify: boolean;
4586
+ constructor(options: ClientOptions);
4587
+ /**
4588
+ * Log an OCPP message exchange.
4589
+ * - Default: "CALL → { method }" at debug level
4590
+ * - exchangeLog: adds `direction` to meta
4591
+ * - prettify + exchangeLog: renders styled line like "⚡ CP-101 → BootNotification [OUT]"
4592
+ */
4593
+ protected _logExchange(direction: "IN" | "OUT", type: "CALL" | "CALLRESULT" | "CALLERROR", method: string | undefined, meta: Record<string, unknown>): void;
4594
+ /**
4595
+ * Returns the underlying logger instance wrapper.
4596
+ */
4597
+ get log(): LoggerLikeNotOptional$1;
4598
+ /**
4599
+ * The unique client identity (Charge Point ID or Central System ID).
4600
+ */
4601
+ get identity(): string;
4602
+ /**
4603
+ * The connection endpoint URL.
4604
+ */
4605
+ get endpoint(): string;
4606
+ /**
4607
+ * The current configuration options for this client.
4608
+ */
4609
+ get options(): Readonly<ClientOptions>;
4610
+ /**
4611
+ * The negotiated OCPP protocol version, available after connection.
4612
+ */
4613
+ get protocol(): string | undefined;
4614
+ /**
4615
+ * The current WebSocket connection state.
4616
+ */
4617
+ get state(): ConnectionState;
4618
+ /**
4619
+ * The configured security profile.
4620
+ */
4621
+ get securityProfile(): SecurityProfile;
4622
+ /**
4623
+ * Connect to the OCPP endpoint via WebSocket.
4624
+ * Throws an error if the connection attempt fails, or if already connected/connecting.
4625
+ *
4626
+ * @returns A promise that resolves to an object containing the HTTP response from the upgrade request.
4627
+ */
4628
+ connect(): Promise<{
4629
+ response: node_http.IncomingMessage;
4630
+ }>;
4631
+ private _connectInternal;
4632
+ /**
4633
+ * Close the WebSocket connection.
4634
+ * By default, it awaits any pending calls to finish before closing.
4635
+ *
4636
+ * @param options Configuration for closing the connection (code, reason, awaiting pending calls, force close).
4637
+ * @returns A promise that resolves when the connection is fully closed.
4638
+ */
4639
+ close(options?: CloseOptions$1): Promise<{
4640
+ code: number;
4641
+ reason: string;
4642
+ }>;
4643
+ private _closeInternal;
4644
+ /**
4645
+ * Register a version-specific handler — `handle("ocpp1.6", "BootNotification", handler)`.
4646
+ * This handler is only invoked when the active protocol matches the given version.
4647
+ *
4648
+ * @throws {Error} If a handler for this version and method is already registered on this client instance.
4649
+ */
4650
+ handle<V extends OCPPProtocol$1, M extends AllMethodNames$1<V>>(version: V, method: M, handler: (context: HandlerContext$1<OCPPRequestType$1<V, M>>) => OCPPResponseType$1<V, M> | Promise<OCPPResponseType$1<V, M>> | typeof NOREPLY): void;
4651
+ /**
4652
+ * Register a handler for a custom/extension protocol/method not in the typed OCPP method maps.
4653
+ * `handle("my-protocol", "my-method", handler)`
4654
+ *
4655
+ * Note: This overload matches only if the protocol is NOT a known strict protocol of standard OCPP versions.
4656
+ *
4657
+ * @throws {Error} If a handler for this protocol and method is already registered on this client instance.
4658
+ */
4659
+ handle<S extends string>(version: S extends OCPPProtocol$1 ? never : S, method: string, handler: (context: HandlerContext$1<Record<string, any>>) => any): void;
4660
+ /**
4661
+ * Register a handler for the client's default protocol — `handle("BootNotification", handler)`.
4662
+ * Uses the default protocol type parameter `P`.
4663
+ *
4664
+ * @throws {Error} If a handler for this method is already registered on this client instance.
4665
+ */
4666
+ handle<M extends AllMethodNames$1<P>>(method: M, handler: (context: HandlerContext$1<OCPPRequestType$1<P, M>>) => OCPPResponseType$1<P, M> | Promise<OCPPResponseType$1<P, M>> | typeof NOREPLY): void;
4667
+ /**
4668
+ * Register a handler for a custom/extension method not in the typed OCPP method maps.
4669
+ *
4670
+ * @throws {Error} If a handler for this method is already registered on this client instance.
4671
+ */
4672
+ handle(method: string, handler: (context: HandlerContext$1<Record<string, any>>) => any): void;
4673
+ /**
4674
+ * Register a wildcard handler for all unhandled methods.
4675
+ *
4676
+ * @throws {Error} If a wildcard handler is already registered on this client instance.
4677
+ */
4678
+ handle(handler: WildcardHandler$1): void;
4679
+ /**
4680
+ * Remove a registered handler for a specific method on the default protocol.
4681
+ * @param method The method to remove the handler for.
4682
+ */
4683
+ removeHandler(method?: string): void;
4684
+ /**
4685
+ * Remove a registered handler for a specific version and method.
4686
+ */
4687
+ removeHandler(version: OCPPProtocol$1, method: string): void;
4688
+ /**
4689
+ * Remove all registered handlers for this client, including the wildcard handler.
4690
+ */
4691
+ removeAllHandlers(): void;
4692
+ /**
4693
+ * Register a middleware function to intercept calls and results.
4694
+ * Middleware executes in the order registered.
4695
+ */
4696
+ use(middleware: MiddlewareFunction<MiddlewareContext>): void;
4697
+ /**
4698
+ * Call a version-specific typed method — `call("ocpp1.6", "BootNotification", {...})`.
4699
+ * Provides full type inference for params and response based on the OCPP version.
4700
+ */
4701
+ call<V extends OCPPProtocol$1, M extends AllMethodNames$1<V>>(version: V, method: M, params: OCPPRequestType$1<V, M>, options?: CallOptions$1): Promise<OCPPResponseType$1<V, M>>;
4702
+ /**
4703
+ * Call a custom/extension protocol/method not in the typed OCPP method maps.
4704
+ * `call("my-protocol", "my-method", params)`
4705
+ *
4706
+ * Note: This overload matches only if the protocol is NOT a known strict protocol of standard OCPP versions.
4707
+ */
4708
+ call<S extends string, TResult = any>(version: S extends OCPPProtocol$1 ? never : S, method: string, params: Record<string, any>, options?: CallOptions$1): Promise<TResult>;
4709
+ /** Call a known typed method using the client's default protocol. */
4710
+ call<M extends AllMethodNames$1<P>>(method: M, params: OCPPRequestType$1<P, M>, options?: CallOptions$1): Promise<OCPPResponseType$1<P, M>>;
4711
+ /** Call a known typed method with explicit response type. */
4712
+ call<TResult = unknown>(method: string, params?: Record<string, unknown>, options?: CallOptions$1): Promise<TResult>;
4713
+ /**
4714
+ * Version-specific safe call. Returns `undefined` on error instead of throwing.
4715
+ */
4716
+ safeCall<V extends OCPPProtocol$1, M extends AllMethodNames$1<V>>(version: V, method: M, params: OCPPRequestType$1<V, M>, options?: CallOptions$1): Promise<OCPPResponseType$1<V, M> | undefined>;
4717
+ /**
4718
+ * Custom/Extension safe call.
4719
+ */
4720
+ safeCall<S extends string, TResult = any>(version: S extends OCPPProtocol$1 ? never : S, method: string, params: Record<string, any>, options?: CallOptions$1): Promise<TResult | undefined>;
4721
+ /** Default protocol safe call. */
4722
+ safeCall<M extends AllMethodNames$1<P>>(method: M, params: OCPPRequestType$1<P, M>, options?: CallOptions$1): Promise<OCPPResponseType$1<P, M> | undefined>;
4723
+ /** Explicit result safe call. */
4724
+ safeCall<TResult = unknown>(method: string, params?: Record<string, unknown>, options?: CallOptions$1): Promise<TResult | undefined>;
4725
+ private _sendCall;
4726
+ /**
4727
+ * Send a raw string message over the WebSocket (use with caution).
4728
+ * Messages sent while CONNECTING are buffered and flushed on open.
4729
+ */
4730
+ sendRaw(message: string): void;
4731
+ reconfigure(options: Partial<ClientOptions>): void;
4732
+ protected _attachWebsocket(ws: WebSocket__default): void;
4733
+ protected _onMessage(rawData: WebSocket__default.RawData, preParsed?: unknown): void;
4734
+ private _handleIncomingCall;
4735
+ private _handleCallResult;
4736
+ private _handleCallError;
4737
+ private _onBadMessage;
4738
+ /**
4739
+ * Reject all in-flight calls and clear pending state.
4740
+ */
4741
+ private _rejectPendingCalls;
4742
+ private _onClose;
4743
+ /** Errors that should stop reconnection immediately */
4744
+ private static readonly _INTOLERABLE_ERRORS;
4745
+ private _scheduleReconnect;
4746
+ /**
4747
+ * Atomically drains the offline queue and sends each message via _sendCall.
4748
+ * Uses splice(0) to prevent re-entry bugs (double billing) if the connection
4749
+ * drops again mid-flush — the queue is empty before any sends begin.
4750
+ */
4751
+ private _flushOfflineQueue;
4752
+ /**
4753
+ * Retry wrapper using Full Jitter exponential backoff.
4754
+ * delay = random(0, min(retryMaxDelayMs, retryDelayMs * 2^attempt))
4755
+ * Only retries on TimeoutError — all other errors propagate immediately.
4756
+ */
4757
+ private _callWithRetry;
4758
+ /** Maximum bytes allowed in the ws send buffer before applying backpressure (512KB) */
4759
+ private static readonly _BACKPRESSURE_THRESHOLD;
4760
+ /**
4761
+ * Wraps ws.send() with backpressure protection.
4762
+ * If bufferedAmount exceeds the threshold, waits for the buffer to drain
4763
+ * before sending. Prevents OOM on slow 2G/3G charger connections.
4764
+ */
4765
+ private _safeSend;
4766
+ private _startPing;
4767
+ private _stopPing;
4768
+ private _recordActivity;
4769
+ private _setupValidators;
4770
+ private _validateOutbound;
4771
+ private _validateInbound;
4772
+ private _findValidator;
4773
+ private _buildEndpoint;
4774
+ private _buildWsOptions;
4775
+ private _cleanup;
4776
+ }
4777
+
4778
+ interface WorkerPoolOptions {
4779
+ /** Number of worker threads (default: Math.max(2, cpus - 2)) */
4780
+ poolSize?: number;
4781
+ /** Max pending parse jobs before rejecting (default: 10000) */
4782
+ maxQueueSize?: number;
4783
+ }
4784
+ interface ParseResult {
4785
+ message: unknown;
4786
+ validationError?: {
4787
+ schemaId: string;
4788
+ errors: string;
4789
+ };
4790
+ }
4791
+ declare class WorkerPool {
4792
+ private _workers;
4793
+ private _nextWorker;
4794
+ private _taskId;
4795
+ private _pending;
4796
+ private _maxQueueSize;
4797
+ private _terminated;
4798
+ constructor(options?: WorkerPoolOptions);
4799
+ /** Number of worker threads in the pool */
4800
+ get size(): number;
4801
+ /** Number of pending (unresolved) parse tasks */
4802
+ get pendingTasks(): number;
4803
+ /**
4804
+ * Send raw data to a worker for JSON parsing + optional validation.
4805
+ * Uses round-robin worker selection.
4806
+ */
4807
+ parse(data: Buffer | string, schemaInfo?: {
4808
+ protocol: string;
4809
+ schemas: Record<string, unknown>;
4810
+ }): Promise<ParseResult>;
4811
+ /** Gracefully terminate all workers */
4812
+ shutdown(): Promise<void>;
4813
+ }
4814
+
4815
+ /**
4816
+ * OCPPServerClient — A server-side client representation.
4817
+ *
4818
+ * Created by OCPPServer when a charging station connects.
4819
+ * Extends OCPPClient but is pre-connected (cannot call connect()).
4820
+ */
4821
+ declare class OCPPServerClient extends OCPPClient {
4822
+ private _serverSession;
4823
+ private _serverHandshake;
4824
+ constructor(options: ClientOptions, context: {
4825
+ ws: WebSocket$1;
4826
+ handshake: HandshakeInfo;
4827
+ session: Record<string, any>;
4828
+ protocol?: string;
4829
+ /** Optional adaptive rate multiplier getter (from OCPPServer.AdaptiveLimiter) */
4830
+ adaptiveMultiplier?: () => number;
4831
+ /** Optional worker pool for off-thread JSON parsing */
4832
+ workerPool?: WorkerPool;
4833
+ });
4834
+ private _rateLimits;
4835
+ private _adaptiveMultiplier;
4836
+ private _workerPool;
4837
+ private _checkRateLimit;
4838
+ private _attachServerWebsocket;
4839
+ private _handleRateLimitExceeded;
4840
+ /**
4841
+ * Session data associated with this client connection.
4842
+ */
4843
+ get session(): Record<string, any>;
4844
+ /**
4845
+ * Handshake information from the initial connection.
4846
+ */
4847
+ get handshake(): HandshakeInfo;
4848
+ /**
4849
+ * Server clients cannot initiate connections.
4850
+ * @throws Always throws — use OCPPClient for outbound connections.
4851
+ */
4852
+ connect(): Promise<never>;
4853
+ /**
4854
+ * Forcibly disconnects this charging station from the server.
4855
+ * Useful for authentication revocation, administrative kicks, or clearing hung connections.
4856
+ * By default, waits for pending calls to finish before closing (awaitPending: true).
4857
+ *
4858
+ * @example
4859
+ * await client.close({ code: 1000, reason: "Admin revocation" });
4860
+ */
4861
+ close(options?: CloseOptions$1): Promise<{
4862
+ code: number;
4863
+ reason: string;
4864
+ }>;
4865
+ }
4866
+
4867
+ declare const OCPPServer_base: new () => TypedEventEmitter<ServerEvents>;
4868
+ /**
4869
+ * OCPPServer — A typed WebSocket RPC server for OCPP communication.
4870
+ *
4871
+ * Supports all 3 OCPP Security Profiles:
4872
+ * - Profile 1: Basic Auth over unsecured WS
4873
+ * - Profile 2: TLS + Basic Auth (HTTPS server)
4874
+ * - Profile 3: Mutual TLS (HTTPS server with requestCert)
4875
+ */
4876
+ declare class OCPPServer extends OCPPServer_base {
4877
+ private _options;
4878
+ /** Radix trie for O(k) route matching (string patterns). */
4879
+ private _trie;
4880
+ /** Global middleware routers (server.use() with no patterns — catch-all). */
4881
+ private _globalMiddlewareRouters;
4882
+ /** Routers with RegExp patterns (fallback linear scan). */
4883
+ private _regexRouters;
4884
+ private _clients;
4885
+ private _clientsByIdentity;
4886
+ private _httpServers;
4887
+ private _wss;
4888
+ private _state;
4889
+ private _adapter;
4890
+ private _httpServerAbortControllers;
4891
+ private _logger;
4892
+ private _globalCORS?;
4893
+ private _connectionBuckets;
4894
+ private _adaptiveLimiter;
4895
+ private _plugins;
4896
+ private _workerPool;
4897
+ private readonly _nodeId;
4898
+ private _sessions;
4899
+ private _gcInterval;
4900
+ private readonly _sessionTimeoutMs;
4901
+ constructor(options?: ServerOptions);
4902
+ get log(): LoggerLikeNotOptional$1;
4903
+ /**
4904
+ * Returns a readonly set of all currently connected OCPPServerClient instances.
4905
+ */
4906
+ get clients(): ReadonlySet<OCPPServerClient>;
4907
+ /**
4908
+ * Returns the current server state (OPEN, CLOSING, CLOSED).
4909
+ */
4910
+ get state(): "OPEN" | "CLOSING" | "CLOSED";
4911
+ /**
4912
+ * Returns current node observability statistics
4913
+ * (e.g. connected socket count, tracked memory sessions, and process CPU/Memory usage).
4914
+ * Fully compatible with Loki/Prometheus node metric ingestion.
4915
+ */
4916
+ stats(): OCPPServerStats;
4917
+ /**
4918
+ * Returns observability statistics from the active Event Adapter (e.g. Redis).
4919
+ * Useful for tracking consumer backlog and enabling Horizontal Pod Autoscaling.
4920
+ */
4921
+ adapterMetrics(): Promise<Record<string, unknown> | null>;
4922
+ /**
4923
+ * Synchronously returns the OCPPServerClient instance if the specific identity
4924
+ * is connected to THIS local server node.
4925
+ * Note: In a clustered environment, clients connected to other nodes will NOT be returned here.
4926
+ *
4927
+ * @param identity The client identity (username/station ID)
4928
+ */
4929
+ getLocalClient(identity: string): OCPPServerClient | undefined;
4930
+ /**
4931
+ * Synchronously checks if the specific identity is connected to THIS local server node.
4932
+ * Note: In a clustered environment, this will return false if the client is connected to another node.
4933
+ *
4934
+ * @param identity The client identity (username/station ID)
4935
+ */
4936
+ hasLocalClient(identity: string): boolean;
4937
+ /**
4938
+ * Asynchronously checks if the specific identity is connected to the server.
4939
+ * In a single-node setup, this checks the local connections.
4940
+ * In a clustered setup (with a pub/sub adapter), this will also check the global presence registry
4941
+ * to see if the client is connected to ANY node in the cluster.
4942
+ *
4943
+ * @param identity The client identity (username/station ID)
4944
+ */
4945
+ isClientConnected(identity: string): Promise<boolean>;
4946
+ /**
4947
+ * Applies global CORS rules to all incoming connections before routing.
4948
+ */
4949
+ cors(options: CORSOptions): this;
4950
+ /**
4951
+ * Registers a new routing dispatcher for multiplexing connections.
4952
+ * `server.route("/api/:tenant").use(middleware).auth(cb).on("client", ...)`
4953
+ */
4954
+ route(...patterns: Array<string | RegExp>): OCPPRouter;
4955
+ /**
4956
+ * Attaches one or more standalone modular routers created via `createRouter()`.
4957
+ * This is useful for separating route definitions across different files.
4958
+ */
4959
+ attachRouters(...routers: OCPPRouter[]): this;
4960
+ /**
4961
+ * Registers one or more plugins for server lifecycle hooks.
4962
+ * Plugins are called in registration order for all lifecycle events.
4963
+ *
4964
+ * @example Single plugin
4965
+ * ```ts
4966
+ * server.plugin(metricsPlugin);
4967
+ * ```
4968
+ *
4969
+ * @example Multiple plugins
4970
+ * ```ts
4971
+ * server.plugin(metricsPlugin, loggingPlugin, otelPlugin);
4972
+ * ```
4973
+ */
4974
+ plugin(...plugins: OCPPPlugin[]): this;
4975
+ /**
4976
+ * Registers middleware chain(s) as a wildcard/catch-all router.
4977
+ *
4978
+ * @example
4979
+ * ```ts
4980
+ * server.use(myMiddleware).route("/api").on("client", ...);
4981
+ * ```
4982
+ */
4983
+ use(...middlewares: ConnectionMiddleware[]): OCPPRouter;
4984
+ /**
4985
+ * Registers a top-level auth handler, returning a router to attach `.on()` or `.use()`.
4986
+ */
4987
+ auth<TSession = Record<string, unknown>>(callback: AuthCallback<TSession>): OCPPRouter;
4988
+ /**
4989
+ * Routes a router into the appropriate internal structure:
4990
+ * - String patterns → radix trie (O(k) lookup)
4991
+ * - RegExp patterns → linear fallback array
4992
+ * - No patterns → global middleware (catch-all)
4993
+ * @internal
4994
+ */
4995
+ private _registerRouter;
4996
+ listen(port?: number, host?: string, options?: ListenOptions): Promise<Server>;
4997
+ /**
4998
+ * Hot-reloads the TLS certificate on all active HTTPS servers without
4999
+ * dropping any existing WebSocket connections.
5000
+ *
5001
+ * **When to use:** Call this whenever your TLS certificate is renewed —
5002
+ * for example, after a Let's Encrypt auto-renewal (every ~90 days).
5003
+ * Without this, you would need to restart the Node.js process to pick up
5004
+ * the new certificate, disconnecting all connected charging stations.
5005
+ *
5006
+ * **How to use:**
5007
+ * ```ts
5008
+ * server.updateTLS({ cert: newCert, key: newKey });
5009
+ * ```
5010
+ *
5011
+ * **Optional:** Only relevant if you are terminating TLS directly in Node.js
5012
+ * (i.e. `SecurityProfile.TLS_BASIC_AUTH` or `TLS_CLIENT_CERT`). If you are
5013
+ * running behind a reverse proxy (Nginx, AWS ALB, etc.) that handles TLS,
5014
+ * you do not need this method — just rotate the cert on the proxy.
5015
+ *
5016
+ * @throws If the server is not using a TLS Security Profile.
5017
+ */
5018
+ updateTLS(tlsOpts: TLSOptions): void;
5019
+ get handleUpgrade(): (req: IncomingMessage, socket: Duplex, head: Buffer) => Promise<void>;
5020
+ /**
5021
+ * Core upgrade handler. Follows a strict pipeline:
5022
+ *
5023
+ * 1. Validate socket readyState & upgrade header
5024
+ * 2. Parse URL → identity + endpoint
5025
+ * 3. Enable TCP Keep-Alive
5026
+ * 4. Parse & negotiate subprotocols
5027
+ * 5. Parse Basic Auth (via modular parseBasicAuth)
5028
+ * 6. Extract TLS client certificate (Profile 3)
5029
+ * 7. Build HandshakeInfo
5030
+ * 8. Run auth callback with AbortController + handshake timeout
5031
+ * 9. Complete WebSocket upgrade
5032
+ * 10. Create OCPPServerClient
5033
+ */
5034
+ private _handleUpgrade;
5035
+ private _updateSessionActivity;
5036
+ close(options?: CloseOptions$1): Promise<void>;
5037
+ reconfigure(options: Partial<ServerOptions>): void;
5038
+ /**
5039
+ * Send a request to a specific client (local or remote).
5040
+ *
5041
+ * 1. Checks local clients.
5042
+ * 2. Checks Presence Registry -> Unicast.
5043
+ * 3. Fallback: Broadcast.
5044
+ */
5045
+ /**
5046
+ * Send a request to a specific client (local or remote).
5047
+ *
5048
+ * 1. Checks local clients.
5049
+ * 2. Checks Presence Registry -> Unicast.
5050
+ * 3. Fallback: Error (Client not found).
5051
+ */
5052
+ sendToClient<V extends OCPPProtocol$1, M extends AllMethodNames$1<V>>(identity: string, version: V, method: M, params: OCPPRequestType$1<V, M>, options?: CallOptions$1): Promise<OCPPResponseType$1<V, M> | undefined>;
5053
+ sendToClient<M extends AllMethodNames$1<any>>(identity: string, method: M, params: OCPPRequestType$1<any, M>, options?: CallOptions$1): Promise<OCPPResponseType$1<any, M> | undefined>;
5054
+ sendToClient<_T = any>(identity: string, method: string, params: Record<string, any>, options?: CallOptions$1): Promise<any | undefined>;
5055
+ safeSendToClient<V extends OCPPProtocol$1, M extends AllMethodNames$1<V>>(identity: string, version: V, method: M, params: OCPPRequestType$1<V, M>, options?: CallOptions$1): Promise<OCPPResponseType$1<V, M> | undefined>;
5056
+ safeSendToClient<M extends AllMethodNames$1<any>>(identity: string, method: M, params: OCPPRequestType$1<any, M>, options?: CallOptions$1): Promise<OCPPResponseType$1<any, M> | undefined>;
5057
+ safeSendToClient<_T = any>(identity: string, method: string, params: Record<string, any>, options?: CallOptions$1): Promise<any | undefined>;
5058
+ /**
5059
+ * Pipeline multiple calls to a single client into a concurrent batch.
5060
+ * Useful for reconnection warm-up (e.g. GetConfiguration, ChangeAvailability, etc.)
5061
+ * where sequential calls would add unnecessary round-trip latency.
5062
+ *
5063
+ * @param identity The client identity to send calls to
5064
+ * @param calls Array of { method, params, options? } to execute concurrently
5065
+ * @returns Array of results in the same order as the calls array.
5066
+ * Each element is the call result, or `undefined` if that individual call failed.
5067
+ *
5068
+ * @example
5069
+ * ```ts
5070
+ * const results = await server.sendBatch('CP-101', [
5071
+ * { method: 'GetConfiguration', params: { key: ['MeterInterval'] } },
5072
+ * { method: 'ChangeAvailability', params: { type: 'Operative' } },
5073
+ * { method: 'TriggerMessage', params: { requestedMessage: 'StatusNotification' } },
5074
+ * ]);
5075
+ * ```
5076
+ */
5077
+ sendBatch(identity: string, calls: Array<{
5078
+ method: string;
5079
+ params: Record<string, unknown>;
5080
+ options?: CallOptions$1;
5081
+ }>): Promise<Array<unknown | undefined>>;
5082
+ setAdapter(adapter: EventAdapterInterface): Promise<void>;
5083
+ private _onBroadcast;
5084
+ private _onUnicast;
5085
+ publish(channel: string, data: unknown): Promise<void>;
5086
+ broadcast<V extends AllMethodNames$1<any>>(method: V, params: OCPPRequestType$1<any, V>): Promise<void>;
5087
+ /**
5088
+ * Send a specific method & params to a list of specific clients efficiently.
5089
+ * This leverages adapter pipelining (e.g. Redis .pipeline()) to minimize network overhead
5090
+ * when communicating with thousands of nodes simultaneously.
5091
+ *
5092
+ * @param identities Array of target client identities
5093
+ * @param method The OCPP method to send
5094
+ * @param params The request parameters
5095
+ * @param options Call options
5096
+ */
5097
+ broadcastBatch<V extends AllMethodNames$1<any>>(identities: string[], method: V, params: OCPPRequestType$1<any, V>, options?: CallOptions$1): Promise<void>;
5098
+ private _buildCompressionConfig;
5099
+ }
5100
+
4451
5101
  interface ValidatorSchema {
4452
5102
  $schema?: string;
4453
5103
  $id?: string;
@@ -4482,6 +5132,20 @@ declare class Validator {
4482
5132
  hasSchema(schemaId: string): boolean;
4483
5133
  }
4484
5134
 
5135
+ /**
5136
+ * Utility type that overlays typed `.on()`, `.off()`, `.emit()` etc.
5137
+ * on top of Node.js EventEmitter. This is the foundation for type-safe
5138
+ * event handling throughout the library.
5139
+ */
5140
+ type TypedEventEmitter<TEvents extends Record<keyof TEvents, unknown[]>> = Omit<EventEmitter$1, "on" | "once" | "off" | "emit" | "removeListener" | "addListener" | "removeAllListeners"> & {
5141
+ on<K extends keyof TEvents | (string & {})>(event: K, listener: K extends keyof TEvents ? (...args: TEvents[K]) => void : (...args: unknown[]) => void): TypedEventEmitter<TEvents>;
5142
+ once<K extends keyof TEvents | (string & {})>(event: K, listener: K extends keyof TEvents ? (...args: TEvents[K]) => void : (...args: unknown[]) => void): TypedEventEmitter<TEvents>;
5143
+ off<K extends keyof TEvents | (string & {})>(event: K, listener: K extends keyof TEvents ? (...args: TEvents[K]) => void : (...args: unknown[]) => void): TypedEventEmitter<TEvents>;
5144
+ emit<K extends keyof TEvents | (string & {})>(event: K, ...args: K extends keyof TEvents ? TEvents[K] : unknown[]): boolean;
5145
+ addListener<K extends keyof TEvents | (string & {})>(event: K, listener: K extends keyof TEvents ? (...args: TEvents[K]) => void : (...args: unknown[]) => void): TypedEventEmitter<TEvents>;
5146
+ removeListener<K extends keyof TEvents | (string & {})>(event: K, listener: K extends keyof TEvents ? (...args: TEvents[K]) => void : (...args: unknown[]) => void): TypedEventEmitter<TEvents>;
5147
+ removeAllListeners<K extends keyof TEvents | (string & {})>(event?: K): TypedEventEmitter<TEvents>;
5148
+ };
4485
5149
  type OCPPProtocol$1 = OCPPProtocolKey;
4486
5150
  type AnyOCPPProtocol$1 = OCPPProtocol$1 | (string & {});
4487
5151
  declare const ConnectionState: {
@@ -4517,6 +5181,18 @@ type OCPPCallError$1 = [
4517
5181
  Record<string, unknown>
4518
5182
  ];
4519
5183
  type OCPPMessage$1<T = unknown> = OCPPCall$1<T> | OCPPCallResult$1<T> | OCPPCallError$1;
5184
+ interface TLSOptions {
5185
+ /** Server/client certificate (PEM) */
5186
+ cert?: string | Buffer;
5187
+ /** Private key (PEM) */
5188
+ key?: string | Buffer;
5189
+ /** CA certificate(s) for verification */
5190
+ ca?: string | Buffer | Array<string | Buffer>;
5191
+ /** Reject unauthorized certs (default: true) */
5192
+ rejectUnauthorized?: boolean;
5193
+ /** Passphrase for encrypted private key */
5194
+ passphrase?: string;
5195
+ }
4520
5196
  interface HandlerContext$1<T = unknown> {
4521
5197
  /** Unique message ID */
4522
5198
  messageId: string;
@@ -4531,6 +5207,11 @@ interface HandlerContext$1<T = unknown> {
4531
5207
  }
4532
5208
  type CallHandler$1<TParams = unknown, TResult = unknown> = (context: HandlerContext$1<TParams>) => TResult | Promise<TResult>;
4533
5209
  type WildcardHandler$1 = (method: string, context: HandlerContext$1) => unknown | Promise<unknown>;
5210
+ interface RouterHandlerContext<T = unknown> extends HandlerContext$1<T> {
5211
+ /** The specific server client that issued the message. */
5212
+ client: OCPPServerClient;
5213
+ }
5214
+ type RouterWildcardHandler = (method: string, context: RouterHandlerContext) => unknown | Promise<unknown>;
4534
5215
  interface CallOptions$1 {
4535
5216
  /** Timeout in milliseconds for this specific call */
4536
5217
  timeoutMs?: number;
@@ -4678,6 +5359,297 @@ interface LoggingConfig$1 {
4678
5359
  */
4679
5360
  prettifyMetadata?: boolean;
4680
5361
  }
5362
+ interface ClientOptions {
5363
+ /** Unique identity for this client (charging station ID) */
5364
+ identity: string;
5365
+ /** WebSocket endpoint URL (ws:// or wss://) */
5366
+ endpoint: string;
5367
+ /** OCPP Security Profile (default: NONE) */
5368
+ securityProfile?: SecurityProfile;
5369
+ /** Password for Basic Auth (Profile 1 & 2) */
5370
+ password?: string | Buffer;
5371
+ /** TLS options (Profile 2 & 3) */
5372
+ tls?: TLSOptions;
5373
+ /** OCPP subprotocols to negotiate */
5374
+ protocols?: AnyOCPPProtocol$1[];
5375
+ /** Additional WebSocket headers */
5376
+ headers?: Record<string, string>;
5377
+ /** Additional query parameters */
5378
+ query?: Record<string, string>;
5379
+ /** Enable automatic reconnection (default: true) */
5380
+ reconnect?: boolean;
5381
+ /** Maximum reconnection attempts (default: Infinity) */
5382
+ maxReconnects?: number;
5383
+ /** Back-off base delay in ms (default: 1000) */
5384
+ backoffMin?: number;
5385
+ /** Back-off max delay in ms (default: 30000) */
5386
+ backoffMax?: number;
5387
+ /** Call timeout in ms (default: 30000) */
5388
+ callTimeoutMs?: number;
5389
+ /** Ping interval in ms (default: 30000, 0 to disable) */
5390
+ pingIntervalMs?: number;
5391
+ /** Defer pings if activity detected (default: false) */
5392
+ deferPingsOnActivity?: boolean;
5393
+ /**
5394
+ * Pong response timeout in ms. If no pong is received within this
5395
+ * window after a ping, the connection is considered dead and terminated.
5396
+ * (default: pingIntervalMs + 5000, 0 to disable)
5397
+ */
5398
+ pongTimeoutMs?: number;
5399
+ /** Maximum concurrent outbound calls (default: 1) */
5400
+ callConcurrency?: number;
5401
+ /** Enable strict mode validation (default: false) */
5402
+ strictMode?: boolean | OCPPProtocol$1[];
5403
+ /** If defined, restricts strict mode validation ONLY to these methods */
5404
+ strictModeMethods?: Array<AllMethodNames$1<OCPPProtocol$1>>;
5405
+ /** Custom validators for strict mode */
5406
+ strictModeValidators?: Validator[];
5407
+ /** Max number of bad messages before closing (default: Infinity) */
5408
+ maxBadMessages?: number;
5409
+ /** Include error details in responses (default: false) */
5410
+ respondWithDetailedErrors?: boolean;
5411
+ /**
5412
+ * Logging configuration.
5413
+ * - `undefined` / not set → default voltlog-io with console (logging enabled)
5414
+ * - `false` → logging disabled entirely
5415
+ * - `LoggingConfig` → custom configuration
5416
+ */
5417
+ logging?: LoggingConfig$1 | false;
5418
+ /** Rate Limiting configuration (Token Bucket) */
5419
+ rateLimit?: RateLimitOptions;
5420
+ /**
5421
+ * If true, calls made while disconnected are queued in-memory
5422
+ * and flushed automatically on reconnect. (default: false)
5423
+ */
5424
+ offlineQueue?: boolean;
5425
+ /**
5426
+ * Maximum number of messages to queue while offline.
5427
+ * Oldest messages are dropped when exceeded. (default: 100)
5428
+ */
5429
+ offlineQueueMaxSize?: number;
5430
+ /**
5431
+ * Enable WebSocket `permessage-deflate` compression.
5432
+ * Reduces bandwidth by ~80% for JSON payloads at the cost of ~0.2ms CPU per message.
5433
+ * - `true` → sensible defaults (threshold: 1024, level: 6)
5434
+ * - `object` → fine-tuned configuration
5435
+ * (default: false)
5436
+ */
5437
+ compression?: boolean | CompressionOptions;
5438
+ }
5439
+ interface CompressionOptions {
5440
+ /** Minimum payload size in bytes to compress (default: 1024) */
5441
+ threshold?: number;
5442
+ /** zlib compression level 1 (fastest) to 9 (smallest) (default: 6) */
5443
+ level?: number;
5444
+ /** zlib memory level 1–9 (default: 8) */
5445
+ memLevel?: number;
5446
+ /** Server does not retain deflate context between messages (default: true — saves ~120KB/conn) */
5447
+ serverNoContextTakeover?: boolean;
5448
+ /** Client does not retain deflate context between messages (default: true) */
5449
+ clientNoContextTakeover?: boolean;
5450
+ }
5451
+ interface RateLimitOptions {
5452
+ /** Maximum number of messages allowed within the window */
5453
+ limit: number;
5454
+ /** Window size in milliseconds */
5455
+ windowMs: number;
5456
+ /**
5457
+ * Action to take when rate limit is exceeded.
5458
+ * - 'disconnect': Terminate the socket immediately (hard enforce).
5459
+ * - 'ignore': Drop the message entirely, letting the client back-off and retry.
5460
+ * - Custom callback: Perform custom logging or logic when exceeded.
5461
+ * (default: 'ignore')
5462
+ */
5463
+ onLimitExceeded?: "disconnect" | "ignore" | ((client: OCPPServerClient, rawData: unknown) => void | Promise<void>);
5464
+ /**
5465
+ * Specific limits applied purely to individual methods (e.g. Heartbeat, BootNotification).
5466
+ * Note: The method must be parsed from the raw JSON payload to apply this.
5467
+ */
5468
+ methods?: Record<string, {
5469
+ limit: number;
5470
+ windowMs: number;
5471
+ }>;
5472
+ /**
5473
+ * Enable adaptive rate limiting based on CPU/memory pressure.
5474
+ * When enabled, the token refill rate is automatically reduced under
5475
+ * high load and restored after a cooldown period. (default: false)
5476
+ */
5477
+ adaptive?: boolean;
5478
+ /**
5479
+ * CPU usage percent threshold to begin throttling.
5480
+ * Applies only when `adaptive` is true. (default: 80)
5481
+ */
5482
+ cpuThresholdPercent?: number;
5483
+ /**
5484
+ * Heap usage percent threshold to begin throttling.
5485
+ * Applies only when `adaptive` is true. (default: 85)
5486
+ */
5487
+ memThresholdPercent?: number;
5488
+ /**
5489
+ * Time (ms) both CPU and memory must stay below their thresholds
5490
+ * before restoring the original rate. (default: 5000)
5491
+ */
5492
+ cooldownMs?: number;
5493
+ }
5494
+ interface RouterConfig {
5495
+ /** Accepted OCPP subprotocols (e.g. ["ocpp1.6"]) */
5496
+ protocols?: AnyOCPPProtocol$1[];
5497
+ /** Call timeout in ms — overrides server default */
5498
+ callTimeoutMs?: number;
5499
+ /** Ping interval in ms — overrides server default */
5500
+ pingIntervalMs?: number;
5501
+ /** Defer pings if activity detected — overrides server default */
5502
+ deferPingsOnActivity?: boolean;
5503
+ /** Max concurrent outbound calls — overrides server default */
5504
+ callConcurrency?: number;
5505
+ /** Enable strict mode validation — overrides server default */
5506
+ strictMode?: boolean | OCPPProtocol$1[];
5507
+ /** If defined, restricts strict mode validation ONLY to these methods */
5508
+ strictModeMethods?: Array<AllMethodNames$1<OCPPProtocol$1>>;
5509
+ /** Rate Limiting configuration — overrides server default */
5510
+ rateLimit?: RateLimitOptions;
5511
+ }
5512
+ interface CORSOptions {
5513
+ /** Allowed IPv4, IPv6, or CIDR ranges (e.g. "10.0.0.0/8") */
5514
+ allowedIPs?: string[];
5515
+ /** Allowed Origin header values (e.g. "https://dashboard.example.com") */
5516
+ allowedOrigins?: string[];
5517
+ /** Allowed WebSocket protocol schemes */
5518
+ allowedSchemes?: ("ws" | "wss")[];
5519
+ }
5520
+ interface ServerOptionsBase {
5521
+ /** OCPP Security Profile (default: NONE) */
5522
+ securityProfile?: SecurityProfile;
5523
+ /** TLS options for HTTPS server (Profile 2 & 3) */
5524
+ tls?: TLSOptions;
5525
+ /** Call timeout in ms — inherited by server clients (default: 30000) */
5526
+ callTimeoutMs?: number;
5527
+ /** Ping interval in ms — inherited by server clients (default: 30000) */
5528
+ pingIntervalMs?: number;
5529
+ /** Defer pings if activity detected — inherited (default: false) */
5530
+ deferPingsOnActivity?: boolean;
5531
+ /** Max concurrent outbound calls — inherited (default: 1) */
5532
+ callConcurrency?: number;
5533
+ /** If defined, restricts strict mode validation ONLY to these methods */
5534
+ strictModeMethods?: Array<AllMethodNames$1<OCPPProtocol$1>>;
5535
+ /** Custom validators — inherited */
5536
+ strictModeValidators?: Validator[];
5537
+ /** Rate Limiting configuration — inherited */
5538
+ rateLimit?: RateLimitOptions;
5539
+ /** Max bad messages — inherited (default: Infinity) */
5540
+ maxBadMessages?: number;
5541
+ /** Include error details in responses — inherited (default: false) */
5542
+ respondWithDetailedErrors?: boolean;
5543
+ /**
5544
+ * Session inactivity timeout in milliseconds before garbage collection.
5545
+ * (default: 7200000 / 2 hours)
5546
+ */
5547
+ sessionTtlMs?: number;
5548
+ /**
5549
+ * Maximum time (ms) to wait for the auth callback to resolve during
5550
+ * a WebSocket upgrade handshake. If the callback does not settle within
5551
+ * this window, the socket is destroyed and an `upgradeAborted` event
5552
+ * is emitted. Set to `0` to disable. (default: 30000)
5553
+ */
5554
+ handshakeTimeoutMs?: number;
5555
+ /**
5556
+ * Logging configuration — inherited by server clients.
5557
+ * - `undefined` / not set → default voltlog-io with console
5558
+ * - `false` → logging disabled
5559
+ * - `LoggingConfig` → custom configuration
5560
+ */
5561
+ logging?: LoggingConfig$1 | false;
5562
+ /**
5563
+ * Connection-level rate limiting (per-IP) applied at the HTTP upgrade boundary,
5564
+ * before any auth, TLS or JSON parsing occurs — blocks DDoS connection floods in ~1µs.
5565
+ * - `limit`: Max upgrade requests per IP within `windowMs` (default: 20)
5566
+ * - `windowMs`: Sliding window in ms (default: 10000)
5567
+ */
5568
+ connectionRateLimit?: {
5569
+ limit: number;
5570
+ windowMs: number;
5571
+ };
5572
+ /**
5573
+ * Maximum number of inactive sessions to retain in the bounded LRU cache.
5574
+ * Prevents OOM under DDoS or reconnection storms with transient identities.
5575
+ * (default: 50000)
5576
+ */
5577
+ maxSessions?: number;
5578
+ /**
5579
+ * Enable the built-in HTTP health/metrics endpoint.
5580
+ * When enabled, non-upgrade HTTP requests to `/health` return a JSON health check,
5581
+ * and requests to `/metrics` return Prometheus-compatible text metrics.
5582
+ * (default: false)
5583
+ */
5584
+ healthEndpoint?: boolean;
5585
+ /**
5586
+ * Maximum WebSocket payload size in bytes. Messages exceeding this limit
5587
+ * are rejected at the transport layer before JSON parsing, preventing OOM
5588
+ * from oversized or malicious payloads.
5589
+ * (default: 65536 / 64KB — sufficient for any standard OCPP message)
5590
+ */
5591
+ maxPayloadBytes?: number;
5592
+ /**
5593
+ * Enable worker thread pool for JSON parsing (+ optional AJV validation).
5594
+ * Offloads CPU-heavy work to worker threads, keeping the main event loop free.
5595
+ * Recommended for 10k+ concurrent connections. (default: false)
5596
+ *
5597
+ * - `true` → uses default pool size: `Math.max(2, os.cpus() - 2)`
5598
+ * - `{ poolSize, maxQueueSize }` → fine-tuned pool configuration
5599
+ */
5600
+ workerThreads?: boolean | {
5601
+ poolSize?: number;
5602
+ maxQueueSize?: number;
5603
+ };
5604
+ /**
5605
+ * Enable WebSocket `permessage-deflate` compression.
5606
+ * Reduces bandwidth by ~80% for JSON payloads at the cost of ~0.2ms CPU per message.
5607
+ * - `true` → sensible defaults (threshold: 1024, level: 6)
5608
+ * - `object` → fine-tuned configuration
5609
+ * (default: false)
5610
+ */
5611
+ compression?: boolean | CompressionOptions;
5612
+ }
5613
+ /** When strictMode is enabled, protocols MUST be specified */
5614
+ interface StrictServerOptions extends ServerOptionsBase {
5615
+ strictMode: true | OCPPProtocol$1[];
5616
+ protocols: AnyOCPPProtocol$1[];
5617
+ }
5618
+ /** When strictMode is disabled or omitted, protocols are optional */
5619
+ interface RelaxedServerOptions extends ServerOptionsBase {
5620
+ strictMode?: false;
5621
+ protocols?: AnyOCPPProtocol$1[];
5622
+ }
5623
+ type ServerOptions = StrictServerOptions | RelaxedServerOptions;
5624
+ interface OCPPServerStats {
5625
+ /** Number of currently connected WebSockets */
5626
+ connectedClients: number;
5627
+ /** Number of active memory sessions */
5628
+ activeSessions: number;
5629
+ /** Process uptime in seconds */
5630
+ uptimeSeconds: number;
5631
+ /** Process Memory Usage (bytes) */
5632
+ memoryUsage: NodeJS.MemoryUsage;
5633
+ /** Process CPU Time (microseconds) */
5634
+ cpuUsage: NodeJS.CpuUsage;
5635
+ /** Process ID */
5636
+ pid: number;
5637
+ /** Low-level WebSocket Server metrics */
5638
+ webSockets?: {
5639
+ /** Total active clients managed by the underlying ws server */
5640
+ total: number;
5641
+ /** Current messages waiting to be flushed to network (bytes) */
5642
+ bufferedAmount: number;
5643
+ };
5644
+ }
5645
+ interface ListenOptions {
5646
+ /** Existing HTTP/HTTPS server to attach to */
5647
+ server?: node_http.Server | node_https.Server;
5648
+ /** Hostname to bind to */
5649
+ host?: string;
5650
+ /** Signal to abort the listen */
5651
+ signal?: AbortSignal;
5652
+ }
4681
5653
  interface AuthAccept<TSession = Record<string, unknown>> {
4682
5654
  /** Subprotocol to use for this client */
4683
5655
  protocol?: string;
@@ -4685,6 +5657,141 @@ interface AuthAccept<TSession = Record<string, unknown>> {
4685
5657
  session?: TSession;
4686
5658
  }
4687
5659
  type AuthCallback<TSession = Record<string, unknown>> = (ctx: AuthContext<TSession>) => void | Promise<void>;
5660
+ interface ClientEvents {
5661
+ open: [{
5662
+ response: IncomingMessage;
5663
+ }];
5664
+ close: [{
5665
+ code: number;
5666
+ reason: string;
5667
+ }];
5668
+ disconnect: [{
5669
+ code: number;
5670
+ reason: string;
5671
+ }];
5672
+ error: [Error];
5673
+ connecting: [{
5674
+ url: string;
5675
+ }];
5676
+ reconnect: [{
5677
+ attempt: number;
5678
+ delay: number;
5679
+ }];
5680
+ message: [OCPPMessage$1];
5681
+ call: [OCPPCall$1];
5682
+ callResult: [OCPPCallResult$1];
5683
+ callError: [OCPPCallError$1];
5684
+ badMessage: [{
5685
+ message: string;
5686
+ error: Error;
5687
+ }];
5688
+ ping: [];
5689
+ pong: [];
5690
+ strictValidationFailure: [{
5691
+ message: unknown;
5692
+ error: Error;
5693
+ }];
5694
+ }
5695
+
5696
+ /**
5697
+ * I3: Structured security event for SIEM integration.
5698
+ * Emitted by the server for audit-relevant actions.
5699
+ */
5700
+ interface SecurityEvent {
5701
+ /** Event type identifier */
5702
+ type: "AUTH_FAILED" | "RATE_LIMIT_EXCEEDED" | "UPGRADE_ABORTED" | "CONNECTION_RATE_LIMIT" | "INVALID_PAYLOAD";
5703
+ /** Station identity (if known) */
5704
+ identity?: string;
5705
+ /** Remote IP address */
5706
+ ip?: string;
5707
+ /** ISO 8601 timestamp */
5708
+ timestamp: string;
5709
+ /** Event-specific details */
5710
+ details?: Record<string, unknown>;
5711
+ }
5712
+ interface ServerEvents {
5713
+ client: [OCPPServerClient];
5714
+ error: [Error];
5715
+ upgradeError: [{
5716
+ error: Error;
5717
+ socket: Duplex;
5718
+ }];
5719
+ upgradeAborted: [
5720
+ {
5721
+ identity: string;
5722
+ reason: string;
5723
+ socket: Duplex;
5724
+ request: IncomingMessage;
5725
+ }
5726
+ ];
5727
+ closing: [];
5728
+ close: [];
5729
+ /** I3: Structured security event for SIEM/audit pipelines */
5730
+ securityEvent: [SecurityEvent];
5731
+ connection: [
5732
+ socket: WebSocket.WebSocket,
5733
+ request: node_http.IncomingMessage
5734
+ ];
5735
+ listening: [];
5736
+ headers: [headers: string[], request: node_http.IncomingMessage];
5737
+ }
5738
+ interface EventAdapterInterface {
5739
+ publish(channel: string, data: unknown): Promise<void>;
5740
+ publishBatch?(messages: {
5741
+ channel: string;
5742
+ data: unknown;
5743
+ }[]): Promise<void>;
5744
+ subscribe(channel: string, handler: (data: unknown) => void): Promise<void>;
5745
+ unsubscribe(channel: string): Promise<void>;
5746
+ disconnect(): Promise<void>;
5747
+ setPresence?(identity: string, nodeId: string, ttl: number): Promise<void>;
5748
+ getPresence?(identity: string): Promise<string | null>;
5749
+ getPresenceBatch?(identities: string[]): Promise<(string | null)[]>;
5750
+ removePresence?(identity: string): Promise<void>;
5751
+ /**
5752
+ * Batch set multiple presence entries in a single pipeline.
5753
+ * Reduces N network round-trips to 1 for bulk presence updates.
5754
+ */
5755
+ setPresenceBatch?(entries: {
5756
+ identity: string;
5757
+ nodeId: string;
5758
+ ttl?: number;
5759
+ }[]): Promise<void>;
5760
+ metrics?(): Promise<Record<string, unknown>>;
5761
+ }
5762
+ /**
5763
+ * Plugin interface for extending OCPPServer functionality.
5764
+ *
5765
+ * Plugins provide a unified way to hook into server lifecycle events
5766
+ * without modifying core internals. Useful for:
5767
+ * - Observability (OpenTelemetry, Prometheus)
5768
+ * - Custom adapters and integrations
5769
+ * - Auditing and compliance
5770
+ *
5771
+ * @example
5772
+ * ```ts
5773
+ * const myPlugin: OCPPPlugin = {
5774
+ * name: 'my-plugin',
5775
+ * onInit(server) { console.log('Plugin initialized'); },
5776
+ * onConnection(client) { console.log(`${client.identity} connected`); },
5777
+ * onDisconnect(client) { console.log(`${client.identity} disconnected`); },
5778
+ * onClose() { console.log('Server shutting down'); },
5779
+ * };
5780
+ * server.plugin(myPlugin);
5781
+ * ```
5782
+ */
5783
+ interface OCPPPlugin {
5784
+ /** Unique plugin name (used for logging and deduplication) */
5785
+ name: string;
5786
+ /** Called when the plugin is registered via server.plugin(plugin) */
5787
+ onInit?(server: OCPPServer): void | Promise<void>;
5788
+ /** Called for each new client connection after auth succeeds */
5789
+ onConnection?(client: OCPPServerClient): void | Promise<void>;
5790
+ /** Called when a client disconnects */
5791
+ onDisconnect?(client: OCPPServerClient, code: number, reason: string): void;
5792
+ /** Called during server.close() for plugin cleanup */
5793
+ onClose?(): void | Promise<void>;
5794
+ }
4688
5795
  declare const NOREPLY: unique symbol;
4689
5796
  type MiddlewareContext = {
4690
5797
  type: "incoming_call";
@@ -4735,6 +5842,26 @@ type ConnectionMiddleware = (ctx: ConnectionContext) => Promise<void> | void;
4735
5842
  * This provides immediate IDE autocomplete for the `ConnectionContext`.
4736
5843
  */
4737
5844
  declare function defineMiddleware(mw: ConnectionMiddleware): ConnectionMiddleware;
5845
+ /**
5846
+ * Utility to define and strongly-type an `OCPPPlugin` object.
5847
+ * Provides full IDE autocomplete for all lifecycle hooks.
5848
+ *
5849
+ * @example
5850
+ * ```ts
5851
+ * import { createPlugin } from 'ocpp-ws-io';
5852
+ *
5853
+ * const metricsPlugin = createPlugin({
5854
+ * name: 'metrics',
5855
+ * onInit(server) { console.log('Metrics plugin ready'); },
5856
+ * onConnection(client) { metrics.gauge('connections').inc(); },
5857
+ * onDisconnect(client) { metrics.gauge('connections').dec(); },
5858
+ * onClose() { metrics.flush(); },
5859
+ * });
5860
+ *
5861
+ * server.plugin(metricsPlugin);
5862
+ * ```
5863
+ */
5864
+ declare function createPlugin(plugin: OCPPPlugin): OCPPPlugin;
4738
5865
  /**
4739
5866
  * Utility to define and strongly-type an RPC Middleware function.
4740
5867
  * This provides immediate IDE autocomplete for the `MiddlewareContext`
@@ -5054,4 +6181,4 @@ declare function createRPCError(code: string, message?: string, details?: Record
5054
6181
  */
5055
6182
  declare function getErrorPlainObject(err: Error): Record<string, unknown>;
5056
6183
 
5057
- export { type AllMethodNames, type AnyOCPPProtocol, type BrowserClientEvents, type BrowserClientOptions, BrowserOCPPClient, type CallHandler, type CallOptions, type CloseOptions, ConnectionState, type HandlerContext, type LoggerLike, type LoggingConfig, MessageType, type MiddlewareFunction, type MiddlewareNext, MiddlewareStack, NOREPLY, type OCPPCall, type OCPPCallError, type OCPPCallResult, type OCPPMessage, type OCPPProtocol, type OCPPRequestType, type OCPPResponseType, type RPCError, RPCFormatViolationError, RPCFormationViolationError, RPCFrameworkError, RPCGenericError, RPCInternalError, RPCMessageTypeNotSupportedError, RPCNotImplementedError, RPCNotSupportedError, RPCOccurrenceConstraintViolationError, RPCPropertyConstraintViolationError, RPCProtocolError, RPCSecurityError, RPCTypeConstraintViolationError, TimeoutError, Validator, type ValidatorSchema, type WildcardHandler, combineAuth, createLoggingMiddleware, createRPCError, defineAuth, defineMiddleware, defineRpcMiddleware, getErrorPlainObject };
6184
+ export { type AllMethodNames, type AnyOCPPProtocol, type BrowserClientEvents, type BrowserClientOptions, BrowserOCPPClient, type CallHandler, type CallOptions, type CloseOptions, ConnectionState, type HandlerContext, type LoggerLike, type LoggingConfig, MessageType, type MiddlewareFunction, type MiddlewareNext, MiddlewareStack, NOREPLY, type OCPPCall, type OCPPCallError, type OCPPCallResult, type OCPPMessage, type OCPPProtocol, type OCPPRequestType, type OCPPResponseType, type RPCError, RPCFormatViolationError, RPCFormationViolationError, RPCFrameworkError, RPCGenericError, RPCInternalError, RPCMessageTypeNotSupportedError, RPCNotImplementedError, RPCNotSupportedError, RPCOccurrenceConstraintViolationError, RPCPropertyConstraintViolationError, RPCProtocolError, RPCSecurityError, RPCTypeConstraintViolationError, TimeoutError, Validator, type ValidatorSchema, type WildcardHandler, combineAuth, createLoggingMiddleware, createPlugin, createRPCError, defineAuth, defineMiddleware, defineRpcMiddleware, getErrorPlainObject };