ocpp-ws-io 2.1.4 → 2.1.6

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.
@@ -4740,6 +4740,9 @@ interface ValidatorSchema {
4740
4740
  /**
4741
4741
  * Schema validator using AJV for OCPP message validation.
4742
4742
  * Each validator is bound to a specific subprotocol version.
4743
+ *
4744
+ * E2: Schemas are registered at construction time but compiled lazily
4745
+ * on first use, reducing startup time from ~400ms to ~5ms.
4743
4746
  */
4744
4747
  declare class Validator {
4745
4748
  readonly subprotocol: string;
@@ -4753,6 +4756,8 @@ declare class Validator {
4753
4756
  /**
4754
4757
  * Validate a payload against a schema identified by its $id.
4755
4758
  * Throws a typed RPCError if validation fails.
4759
+ *
4760
+ * E2: Schema is compiled on first call to this method (lazy).
4756
4761
  */
4757
4762
  validate(schemaId: string, params: unknown): void;
4758
4763
  /**
@@ -4761,7 +4766,9 @@ declare class Validator {
4761
4766
  hasSchema(schemaId: string): boolean;
4762
4767
  }
4763
4768
  /**
4764
- * Create a validator for a specific subprotocol version.
4769
+ * Create or retrieve a cached validator for a specific subprotocol version.
4770
+ * E5: Returns an existing instance if one was already created for this subprotocol,
4771
+ * preventing duplicate AJV instances and saving memory.
4765
4772
  */
4766
4773
  declare function createValidator(subprotocol: string, schemas: ValidatorSchema[]): Validator;
4767
4774
 
@@ -5179,6 +5186,13 @@ interface ServerOptions {
5179
5186
  * (default: false)
5180
5187
  */
5181
5188
  healthEndpoint?: boolean;
5189
+ /**
5190
+ * I1: Maximum WebSocket payload size in bytes. Messages exceeding this limit
5191
+ * are rejected at the transport layer before JSON parsing, preventing OOM
5192
+ * from oversized or malicious payloads.
5193
+ * (default: 65536 / 64KB — sufficient for any standard OCPP message)
5194
+ */
5195
+ maxPayloadBytes?: number;
5182
5196
  }
5183
5197
  interface OCPPServerStats {
5184
5198
  /** Number of currently connected WebSockets */
@@ -5252,6 +5266,22 @@ interface ClientEvents {
5252
5266
  }];
5253
5267
  }
5254
5268
 
5269
+ /**
5270
+ * I3: Structured security event for SIEM integration.
5271
+ * Emitted by the server for audit-relevant actions.
5272
+ */
5273
+ interface SecurityEvent {
5274
+ /** Event type identifier */
5275
+ type: "AUTH_FAILED" | "RATE_LIMIT_EXCEEDED" | "UPGRADE_ABORTED" | "CONNECTION_RATE_LIMIT" | "INVALID_PAYLOAD";
5276
+ /** Station identity (if known) */
5277
+ identity?: string;
5278
+ /** Remote IP address */
5279
+ ip?: string;
5280
+ /** ISO 8601 timestamp */
5281
+ timestamp: string;
5282
+ /** Event-specific details */
5283
+ details?: Record<string, unknown>;
5284
+ }
5255
5285
  interface ServerEvents {
5256
5286
  client: [OCPPServerClient];
5257
5287
  error: [Error];
@@ -5269,6 +5299,8 @@ interface ServerEvents {
5269
5299
  ];
5270
5300
  closing: [];
5271
5301
  close: [];
5302
+ /** I3: Structured security event for SIEM/audit pipelines */
5303
+ securityEvent: [SecurityEvent];
5272
5304
  connection: [
5273
5305
  socket: WebSocket.WebSocket,
5274
5306
  request: node_http.IncomingMessage
@@ -5431,4 +5463,4 @@ declare class RedisAdapter implements EventAdapterInterface {
5431
5463
  private _rehydratePresence;
5432
5464
  }
5433
5465
 
5434
- export { createValidator as $, type AuthCallback as A, type OCPP16Methods as B, type ConnectionMiddleware as C, type OCPP201Methods as D, type EventAdapterInterface as E, type OCPP21Methods as F, type OCPPCall as G, type HandlerContext as H, type OCPPCallError as I, type OCPPCallResult as J, OCPPClient as K, type LoggerLike as L, type MiddlewareFunction as M, NOREPLY as N, type OCPPProtocol as O, type OCPPMessage as P, type OCPPMethodMap as Q, type RouterConfig as R, type ServerEvents as S, type TypedEventEmitter as T, type OCPPProtocolKey as U, Validator as V, RedisAdapter as W, SecurityProfile as X, type SessionData as Y, type TLSOptions as Z, type WildcardHandler as _, type LoggingConfig as a, type RedisAdapterOptions as a0, type MiddlewareContext as b, type CORSOptions as c, type AllMethodNames as d, type RouterHandlerContext as e, type OCPPRequestType as f, type OCPPResponseType as g, type RouterWildcardHandler as h, type ServerOptions as i, type LoggerLikeNotOptional as j, OCPPServerClient as k, type OCPPServerStats as l, type ListenOptions as m, type CloseOptions as n, type CallOptions as o, type AnyOCPPProtocol as p, type AuthAccept as q, type CallHandler as r, type ClientEvents as s, type ClientOptions as t, type ConnectionContext as u, ConnectionState as v, type HandshakeInfo as w, MessageType as x, type MiddlewareNext as y, MiddlewareStack as z };
5466
+ export { createValidator as $, type AuthCallback as A, MiddlewareStack as B, type ConnectionMiddleware as C, type OCPP16Methods as D, type EventAdapterInterface as E, type OCPP201Methods as F, type OCPP21Methods as G, type HandlerContext as H, type OCPPCall as I, type OCPPCallError as J, type OCPPCallResult as K, type LoggerLike as L, type MiddlewareFunction as M, NOREPLY as N, type OCPPProtocol as O, OCPPClient as P, type OCPPMessage as Q, type RouterConfig as R, type ServerEvents as S, type TypedEventEmitter as T, type OCPPMethodMap as U, Validator as V, type OCPPProtocolKey as W, RedisAdapter as X, SecurityProfile as Y, type SessionData as Z, type WildcardHandler as _, type LoggingConfig as a, type RedisAdapterOptions as a0, type MiddlewareContext as b, type CORSOptions as c, type AllMethodNames as d, type RouterHandlerContext as e, type OCPPRequestType as f, type OCPPResponseType as g, type RouterWildcardHandler as h, type ServerOptions as i, type LoggerLikeNotOptional as j, OCPPServerClient as k, type OCPPServerStats as l, type ListenOptions as m, type TLSOptions as n, type CloseOptions as o, type CallOptions as p, type AnyOCPPProtocol as q, type AuthAccept as r, type CallHandler as s, type ClientEvents as t, type ClientOptions as u, type ConnectionContext as v, ConnectionState as w, type HandshakeInfo as x, MessageType as y, type MiddlewareNext as z };
@@ -4740,6 +4740,9 @@ interface ValidatorSchema {
4740
4740
  /**
4741
4741
  * Schema validator using AJV for OCPP message validation.
4742
4742
  * Each validator is bound to a specific subprotocol version.
4743
+ *
4744
+ * E2: Schemas are registered at construction time but compiled lazily
4745
+ * on first use, reducing startup time from ~400ms to ~5ms.
4743
4746
  */
4744
4747
  declare class Validator {
4745
4748
  readonly subprotocol: string;
@@ -4753,6 +4756,8 @@ declare class Validator {
4753
4756
  /**
4754
4757
  * Validate a payload against a schema identified by its $id.
4755
4758
  * Throws a typed RPCError if validation fails.
4759
+ *
4760
+ * E2: Schema is compiled on first call to this method (lazy).
4756
4761
  */
4757
4762
  validate(schemaId: string, params: unknown): void;
4758
4763
  /**
@@ -4761,7 +4766,9 @@ declare class Validator {
4761
4766
  hasSchema(schemaId: string): boolean;
4762
4767
  }
4763
4768
  /**
4764
- * Create a validator for a specific subprotocol version.
4769
+ * Create or retrieve a cached validator for a specific subprotocol version.
4770
+ * E5: Returns an existing instance if one was already created for this subprotocol,
4771
+ * preventing duplicate AJV instances and saving memory.
4765
4772
  */
4766
4773
  declare function createValidator(subprotocol: string, schemas: ValidatorSchema[]): Validator;
4767
4774
 
@@ -5179,6 +5186,13 @@ interface ServerOptions {
5179
5186
  * (default: false)
5180
5187
  */
5181
5188
  healthEndpoint?: boolean;
5189
+ /**
5190
+ * I1: Maximum WebSocket payload size in bytes. Messages exceeding this limit
5191
+ * are rejected at the transport layer before JSON parsing, preventing OOM
5192
+ * from oversized or malicious payloads.
5193
+ * (default: 65536 / 64KB — sufficient for any standard OCPP message)
5194
+ */
5195
+ maxPayloadBytes?: number;
5182
5196
  }
5183
5197
  interface OCPPServerStats {
5184
5198
  /** Number of currently connected WebSockets */
@@ -5252,6 +5266,22 @@ interface ClientEvents {
5252
5266
  }];
5253
5267
  }
5254
5268
 
5269
+ /**
5270
+ * I3: Structured security event for SIEM integration.
5271
+ * Emitted by the server for audit-relevant actions.
5272
+ */
5273
+ interface SecurityEvent {
5274
+ /** Event type identifier */
5275
+ type: "AUTH_FAILED" | "RATE_LIMIT_EXCEEDED" | "UPGRADE_ABORTED" | "CONNECTION_RATE_LIMIT" | "INVALID_PAYLOAD";
5276
+ /** Station identity (if known) */
5277
+ identity?: string;
5278
+ /** Remote IP address */
5279
+ ip?: string;
5280
+ /** ISO 8601 timestamp */
5281
+ timestamp: string;
5282
+ /** Event-specific details */
5283
+ details?: Record<string, unknown>;
5284
+ }
5255
5285
  interface ServerEvents {
5256
5286
  client: [OCPPServerClient];
5257
5287
  error: [Error];
@@ -5269,6 +5299,8 @@ interface ServerEvents {
5269
5299
  ];
5270
5300
  closing: [];
5271
5301
  close: [];
5302
+ /** I3: Structured security event for SIEM/audit pipelines */
5303
+ securityEvent: [SecurityEvent];
5272
5304
  connection: [
5273
5305
  socket: WebSocket.WebSocket,
5274
5306
  request: node_http.IncomingMessage
@@ -5431,4 +5463,4 @@ declare class RedisAdapter implements EventAdapterInterface {
5431
5463
  private _rehydratePresence;
5432
5464
  }
5433
5465
 
5434
- export { createValidator as $, type AuthCallback as A, type OCPP16Methods as B, type ConnectionMiddleware as C, type OCPP201Methods as D, type EventAdapterInterface as E, type OCPP21Methods as F, type OCPPCall as G, type HandlerContext as H, type OCPPCallError as I, type OCPPCallResult as J, OCPPClient as K, type LoggerLike as L, type MiddlewareFunction as M, NOREPLY as N, type OCPPProtocol as O, type OCPPMessage as P, type OCPPMethodMap as Q, type RouterConfig as R, type ServerEvents as S, type TypedEventEmitter as T, type OCPPProtocolKey as U, Validator as V, RedisAdapter as W, SecurityProfile as X, type SessionData as Y, type TLSOptions as Z, type WildcardHandler as _, type LoggingConfig as a, type RedisAdapterOptions as a0, type MiddlewareContext as b, type CORSOptions as c, type AllMethodNames as d, type RouterHandlerContext as e, type OCPPRequestType as f, type OCPPResponseType as g, type RouterWildcardHandler as h, type ServerOptions as i, type LoggerLikeNotOptional as j, OCPPServerClient as k, type OCPPServerStats as l, type ListenOptions as m, type CloseOptions as n, type CallOptions as o, type AnyOCPPProtocol as p, type AuthAccept as q, type CallHandler as r, type ClientEvents as s, type ClientOptions as t, type ConnectionContext as u, ConnectionState as v, type HandshakeInfo as w, MessageType as x, type MiddlewareNext as y, MiddlewareStack as z };
5466
+ export { createValidator as $, type AuthCallback as A, MiddlewareStack as B, type ConnectionMiddleware as C, type OCPP16Methods as D, type EventAdapterInterface as E, type OCPP201Methods as F, type OCPP21Methods as G, type HandlerContext as H, type OCPPCall as I, type OCPPCallError as J, type OCPPCallResult as K, type LoggerLike as L, type MiddlewareFunction as M, NOREPLY as N, type OCPPProtocol as O, OCPPClient as P, type OCPPMessage as Q, type RouterConfig as R, type ServerEvents as S, type TypedEventEmitter as T, type OCPPMethodMap as U, Validator as V, type OCPPProtocolKey as W, RedisAdapter as X, SecurityProfile as Y, type SessionData as Z, type WildcardHandler as _, type LoggingConfig as a, type RedisAdapterOptions as a0, type MiddlewareContext as b, type CORSOptions as c, type AllMethodNames as d, type RouterHandlerContext as e, type OCPPRequestType as f, type OCPPResponseType as g, type RouterWildcardHandler as h, type ServerOptions as i, type LoggerLikeNotOptional as j, OCPPServerClient as k, type OCPPServerStats as l, type ListenOptions as m, type TLSOptions as n, type CloseOptions as o, type CallOptions as p, type AnyOCPPProtocol as q, type AuthAccept as r, type CallHandler as s, type ClientEvents as t, type ClientOptions as u, type ConnectionContext as v, ConnectionState as w, type HandshakeInfo as x, MessageType as y, type MiddlewareNext as z };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { E as EventAdapterInterface, A as AuthCallback, L as LoggerLike, a as LoggingConfig, M as MiddlewareFunction, b as MiddlewareContext, C as ConnectionMiddleware, T as TypedEventEmitter, S as ServerEvents, c as CORSOptions, R as RouterConfig, O as OCPPProtocol, d as AllMethodNames, e as RouterHandlerContext, f as OCPPRequestType, g as OCPPResponseType, h as RouterWildcardHandler, i as ServerOptions, j as LoggerLikeNotOptional, k as OCPPServerClient, l as OCPPServerStats, m as ListenOptions, n as CloseOptions, o as CallOptions, V as Validator } from './index-CagcFzyZ.mjs';
2
- export { p as AnyOCPPProtocol, q as AuthAccept, r as CallHandler, s as ClientEvents, t as ClientOptions, u as ConnectionContext, v as ConnectionState, H as HandlerContext, w as HandshakeInfo, x as MessageType, y as MiddlewareNext, z as MiddlewareStack, N as NOREPLY, B as OCPP16Methods, D as OCPP201Methods, F as OCPP21Methods, G as OCPPCall, I as OCPPCallError, J as OCPPCallResult, K as OCPPClient, P as OCPPMessage, Q as OCPPMethodMap, U as OCPPProtocolKey, W as RedisAdapter, X as SecurityProfile, Y as SessionData, Z as TLSOptions, _ as WildcardHandler, $ as createValidator } from './index-CagcFzyZ.mjs';
1
+ import { E as EventAdapterInterface, A as AuthCallback, L as LoggerLike, a as LoggingConfig, M as MiddlewareFunction, b as MiddlewareContext, C as ConnectionMiddleware, T as TypedEventEmitter, S as ServerEvents, c as CORSOptions, R as RouterConfig, O as OCPPProtocol, d as AllMethodNames, e as RouterHandlerContext, f as OCPPRequestType, g as OCPPResponseType, h as RouterWildcardHandler, i as ServerOptions, j as LoggerLikeNotOptional, k as OCPPServerClient, l as OCPPServerStats, m as ListenOptions, n as TLSOptions, o as CloseOptions, p as CallOptions, V as Validator } from './index-1QBeqAuc.mjs';
2
+ export { q as AnyOCPPProtocol, r as AuthAccept, s as CallHandler, t as ClientEvents, u as ClientOptions, v as ConnectionContext, w as ConnectionState, H as HandlerContext, x as HandshakeInfo, y as MessageType, z as MiddlewareNext, B as MiddlewareStack, N as NOREPLY, D as OCPP16Methods, F as OCPP201Methods, G as OCPP21Methods, I as OCPPCall, J as OCPPCallError, K as OCPPCallResult, P as OCPPClient, Q as OCPPMessage, U as OCPPMethodMap, W as OCPPProtocolKey, X as RedisAdapter, Y as SecurityProfile, Z as SessionData, _ as WildcardHandler, $ as createValidator } from './index-1QBeqAuc.mjs';
3
3
  import { Server, IncomingMessage } from 'node:http';
4
4
  import { Duplex } from 'node:stream';
5
5
  import 'ws';
@@ -399,6 +399,28 @@ declare class OCPPServer extends OCPPServer_base {
399
399
  */
400
400
  private _registerRouter;
401
401
  listen(port?: number, host?: string, options?: ListenOptions): Promise<Server>;
402
+ /**
403
+ * Hot-reloads the TLS certificate on all active HTTPS servers without
404
+ * dropping any existing WebSocket connections.
405
+ *
406
+ * **When to use:** Call this whenever your TLS certificate is renewed —
407
+ * for example, after a Let's Encrypt auto-renewal (every ~90 days).
408
+ * Without this, you would need to restart the Node.js process to pick up
409
+ * the new certificate, disconnecting all connected charging stations.
410
+ *
411
+ * **How to use:**
412
+ * ```ts
413
+ * server.updateTLS({ cert: newCert, key: newKey });
414
+ * ```
415
+ *
416
+ * **Optional:** Only relevant if you are terminating TLS directly in Node.js
417
+ * (i.e. `SecurityProfile.TLS_BASIC_AUTH` or `TLS_CLIENT_CERT`). If you are
418
+ * running behind a reverse proxy (Nginx, AWS ALB, etc.) that handles TLS,
419
+ * you do not need this method — just rotate the cert on the proxy.
420
+ *
421
+ * @throws If the server is not using a TLS Security Profile.
422
+ */
423
+ updateTLS(tlsOpts: TLSOptions): void;
402
424
  get handleUpgrade(): (req: IncomingMessage, socket: Duplex, head: Buffer) => Promise<void>;
403
425
  /**
404
426
  * Core upgrade handler. Follows a strict pipeline:
@@ -456,11 +478,7 @@ declare class OCPPServer extends OCPPServer_base {
456
478
  broadcastBatch<V extends AllMethodNames<any>>(identities: string[], method: V, params: OCPPRequestType<any, V>, options?: CallOptions): Promise<void>;
457
479
  }
458
480
 
459
- /**
460
- * Pre-built validators for all supported OCPP protocol versions.
461
- * These are automatically registered when strict mode is enabled.
462
- */
463
- declare const standardValidators: Validator[];
481
+ declare function getStandardValidators(): Validator[];
464
482
 
465
483
  /**
466
484
  * Instantiate a typed RPCError from a string error code.
@@ -484,4 +502,4 @@ declare function getErrorPlainObject(err: Error): Record<string, unknown>;
484
502
  */
485
503
  declare function getPackageIdent(): string;
486
504
 
487
- export { AllMethodNames, AuthCallback, CORSOptions, CallOptions, CloseOptions, ConnectionMiddleware, EventAdapterInterface, InMemoryAdapter, LRUMap, ListenOptions, LoggerLike, LoggingConfig, MiddlewareFunction, OCPPProtocol, OCPPRequestType, OCPPResponseType, OCPPRouter, OCPPServer, OCPPServerClient, type RPCError, RPCFormatViolationError, RPCFormationViolationError, RPCFrameworkError, RPCGenericError, RPCInternalError, RPCMessageTypeNotSupportedError, RPCNotImplementedError, RPCNotSupportedError, RPCOccurrenceConstraintViolationError, RPCPropertyConstraintViolationError, RPCProtocolError, RPCSecurityError, RPCTypeConstraintViolationError, RouterConfig, ServerEvents, ServerOptions, TimeoutError, TypedEventEmitter, UnexpectedHttpResponse, Validator, WebsocketUpgradeError, combineAuth, createLoggingMiddleware, createRPCError, createRouter, defineAdapter, defineAuth, defineMiddleware, defineRpcMiddleware, getErrorPlainObject, getPackageIdent, standardValidators };
505
+ export { AllMethodNames, AuthCallback, CORSOptions, CallOptions, CloseOptions, ConnectionMiddleware, EventAdapterInterface, InMemoryAdapter, LRUMap, ListenOptions, LoggerLike, LoggingConfig, MiddlewareFunction, OCPPProtocol, OCPPRequestType, OCPPResponseType, OCPPRouter, OCPPServer, OCPPServerClient, type RPCError, RPCFormatViolationError, RPCFormationViolationError, RPCFrameworkError, RPCGenericError, RPCInternalError, RPCMessageTypeNotSupportedError, RPCNotImplementedError, RPCNotSupportedError, RPCOccurrenceConstraintViolationError, RPCPropertyConstraintViolationError, RPCProtocolError, RPCSecurityError, RPCTypeConstraintViolationError, RouterConfig, ServerEvents, ServerOptions, TLSOptions, TimeoutError, TypedEventEmitter, UnexpectedHttpResponse, Validator, WebsocketUpgradeError, combineAuth, createLoggingMiddleware, createRPCError, createRouter, defineAdapter, defineAuth, defineMiddleware, defineRpcMiddleware, getErrorPlainObject, getPackageIdent, getStandardValidators };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { E as EventAdapterInterface, A as AuthCallback, L as LoggerLike, a as LoggingConfig, M as MiddlewareFunction, b as MiddlewareContext, C as ConnectionMiddleware, T as TypedEventEmitter, S as ServerEvents, c as CORSOptions, R as RouterConfig, O as OCPPProtocol, d as AllMethodNames, e as RouterHandlerContext, f as OCPPRequestType, g as OCPPResponseType, h as RouterWildcardHandler, i as ServerOptions, j as LoggerLikeNotOptional, k as OCPPServerClient, l as OCPPServerStats, m as ListenOptions, n as CloseOptions, o as CallOptions, V as Validator } from './index-CagcFzyZ.js';
2
- export { p as AnyOCPPProtocol, q as AuthAccept, r as CallHandler, s as ClientEvents, t as ClientOptions, u as ConnectionContext, v as ConnectionState, H as HandlerContext, w as HandshakeInfo, x as MessageType, y as MiddlewareNext, z as MiddlewareStack, N as NOREPLY, B as OCPP16Methods, D as OCPP201Methods, F as OCPP21Methods, G as OCPPCall, I as OCPPCallError, J as OCPPCallResult, K as OCPPClient, P as OCPPMessage, Q as OCPPMethodMap, U as OCPPProtocolKey, W as RedisAdapter, X as SecurityProfile, Y as SessionData, Z as TLSOptions, _ as WildcardHandler, $ as createValidator } from './index-CagcFzyZ.js';
1
+ import { E as EventAdapterInterface, A as AuthCallback, L as LoggerLike, a as LoggingConfig, M as MiddlewareFunction, b as MiddlewareContext, C as ConnectionMiddleware, T as TypedEventEmitter, S as ServerEvents, c as CORSOptions, R as RouterConfig, O as OCPPProtocol, d as AllMethodNames, e as RouterHandlerContext, f as OCPPRequestType, g as OCPPResponseType, h as RouterWildcardHandler, i as ServerOptions, j as LoggerLikeNotOptional, k as OCPPServerClient, l as OCPPServerStats, m as ListenOptions, n as TLSOptions, o as CloseOptions, p as CallOptions, V as Validator } from './index-1QBeqAuc.js';
2
+ export { q as AnyOCPPProtocol, r as AuthAccept, s as CallHandler, t as ClientEvents, u as ClientOptions, v as ConnectionContext, w as ConnectionState, H as HandlerContext, x as HandshakeInfo, y as MessageType, z as MiddlewareNext, B as MiddlewareStack, N as NOREPLY, D as OCPP16Methods, F as OCPP201Methods, G as OCPP21Methods, I as OCPPCall, J as OCPPCallError, K as OCPPCallResult, P as OCPPClient, Q as OCPPMessage, U as OCPPMethodMap, W as OCPPProtocolKey, X as RedisAdapter, Y as SecurityProfile, Z as SessionData, _ as WildcardHandler, $ as createValidator } from './index-1QBeqAuc.js';
3
3
  import { Server, IncomingMessage } from 'node:http';
4
4
  import { Duplex } from 'node:stream';
5
5
  import 'ws';
@@ -399,6 +399,28 @@ declare class OCPPServer extends OCPPServer_base {
399
399
  */
400
400
  private _registerRouter;
401
401
  listen(port?: number, host?: string, options?: ListenOptions): Promise<Server>;
402
+ /**
403
+ * Hot-reloads the TLS certificate on all active HTTPS servers without
404
+ * dropping any existing WebSocket connections.
405
+ *
406
+ * **When to use:** Call this whenever your TLS certificate is renewed —
407
+ * for example, after a Let's Encrypt auto-renewal (every ~90 days).
408
+ * Without this, you would need to restart the Node.js process to pick up
409
+ * the new certificate, disconnecting all connected charging stations.
410
+ *
411
+ * **How to use:**
412
+ * ```ts
413
+ * server.updateTLS({ cert: newCert, key: newKey });
414
+ * ```
415
+ *
416
+ * **Optional:** Only relevant if you are terminating TLS directly in Node.js
417
+ * (i.e. `SecurityProfile.TLS_BASIC_AUTH` or `TLS_CLIENT_CERT`). If you are
418
+ * running behind a reverse proxy (Nginx, AWS ALB, etc.) that handles TLS,
419
+ * you do not need this method — just rotate the cert on the proxy.
420
+ *
421
+ * @throws If the server is not using a TLS Security Profile.
422
+ */
423
+ updateTLS(tlsOpts: TLSOptions): void;
402
424
  get handleUpgrade(): (req: IncomingMessage, socket: Duplex, head: Buffer) => Promise<void>;
403
425
  /**
404
426
  * Core upgrade handler. Follows a strict pipeline:
@@ -456,11 +478,7 @@ declare class OCPPServer extends OCPPServer_base {
456
478
  broadcastBatch<V extends AllMethodNames<any>>(identities: string[], method: V, params: OCPPRequestType<any, V>, options?: CallOptions): Promise<void>;
457
479
  }
458
480
 
459
- /**
460
- * Pre-built validators for all supported OCPP protocol versions.
461
- * These are automatically registered when strict mode is enabled.
462
- */
463
- declare const standardValidators: Validator[];
481
+ declare function getStandardValidators(): Validator[];
464
482
 
465
483
  /**
466
484
  * Instantiate a typed RPCError from a string error code.
@@ -484,4 +502,4 @@ declare function getErrorPlainObject(err: Error): Record<string, unknown>;
484
502
  */
485
503
  declare function getPackageIdent(): string;
486
504
 
487
- export { AllMethodNames, AuthCallback, CORSOptions, CallOptions, CloseOptions, ConnectionMiddleware, EventAdapterInterface, InMemoryAdapter, LRUMap, ListenOptions, LoggerLike, LoggingConfig, MiddlewareFunction, OCPPProtocol, OCPPRequestType, OCPPResponseType, OCPPRouter, OCPPServer, OCPPServerClient, type RPCError, RPCFormatViolationError, RPCFormationViolationError, RPCFrameworkError, RPCGenericError, RPCInternalError, RPCMessageTypeNotSupportedError, RPCNotImplementedError, RPCNotSupportedError, RPCOccurrenceConstraintViolationError, RPCPropertyConstraintViolationError, RPCProtocolError, RPCSecurityError, RPCTypeConstraintViolationError, RouterConfig, ServerEvents, ServerOptions, TimeoutError, TypedEventEmitter, UnexpectedHttpResponse, Validator, WebsocketUpgradeError, combineAuth, createLoggingMiddleware, createRPCError, createRouter, defineAdapter, defineAuth, defineMiddleware, defineRpcMiddleware, getErrorPlainObject, getPackageIdent, standardValidators };
505
+ export { AllMethodNames, AuthCallback, CORSOptions, CallOptions, CloseOptions, ConnectionMiddleware, EventAdapterInterface, InMemoryAdapter, LRUMap, ListenOptions, LoggerLike, LoggingConfig, MiddlewareFunction, OCPPProtocol, OCPPRequestType, OCPPResponseType, OCPPRouter, OCPPServer, OCPPServerClient, type RPCError, RPCFormatViolationError, RPCFormationViolationError, RPCFrameworkError, RPCGenericError, RPCInternalError, RPCMessageTypeNotSupportedError, RPCNotImplementedError, RPCNotSupportedError, RPCOccurrenceConstraintViolationError, RPCPropertyConstraintViolationError, RPCProtocolError, RPCSecurityError, RPCTypeConstraintViolationError, RouterConfig, ServerEvents, ServerOptions, TLSOptions, TimeoutError, TypedEventEmitter, UnexpectedHttpResponse, Validator, WebsocketUpgradeError, combineAuth, createLoggingMiddleware, createRPCError, createRouter, defineAdapter, defineAuth, defineMiddleware, defineRpcMiddleware, getErrorPlainObject, getPackageIdent, getStandardValidators };
package/dist/index.js CHANGED
@@ -70,7 +70,7 @@ __export(src_exports, {
70
70
  defineRpcMiddleware: () => defineRpcMiddleware,
71
71
  getErrorPlainObject: () => getErrorPlainObject,
72
72
  getPackageIdent: () => getPackageIdent,
73
- standardValidators: () => standardValidators
73
+ getStandardValidators: () => getStandardValidators
74
74
  });
75
75
  module.exports = __toCommonJS(src_exports);
76
76
 
@@ -386,9 +386,9 @@ var RedisAdapter = class {
386
386
  // Active streams to poll
387
387
  _polling = false;
388
388
  _closed = false;
389
- // C4: Per-stream sequence counter for message ordering
389
+ // Per-stream sequence counter for message ordering
390
390
  _sequenceCounters = /* @__PURE__ */ new Map();
391
- // C3: Rehydration callbacks
391
+ // Rehydration callbacks
392
392
  _unsubError;
393
393
  _unsubReconnect;
394
394
  // Stored presence entries for rehydration on reconnect
@@ -36991,6 +36991,8 @@ var Validator = class {
36991
36991
  /**
36992
36992
  * Validate a payload against a schema identified by its $id.
36993
36993
  * Throws a typed RPCError if validation fails.
36994
+ *
36995
+ * E2: Schema is compiled on first call to this method (lazy).
36994
36996
  */
36995
36997
  validate(schemaId, params) {
36996
36998
  const resolvedId = this._normalizeSchemaId(schemaId);
@@ -37013,16 +37015,26 @@ var Validator = class {
37013
37015
  return !!this._ajv.getSchema(this._normalizeSchemaId(schemaId));
37014
37016
  }
37015
37017
  };
37018
+ var _validatorRegistry = /* @__PURE__ */ new Map();
37016
37019
  function createValidator(subprotocol, schemas) {
37017
- return new Validator(subprotocol, schemas);
37020
+ const existing = _validatorRegistry.get(subprotocol);
37021
+ if (existing) return existing;
37022
+ const validator = new Validator(subprotocol, schemas);
37023
+ _validatorRegistry.set(subprotocol, validator);
37024
+ return validator;
37018
37025
  }
37019
37026
 
37020
37027
  // src/standard-validators.ts
37021
- var standardValidators = [
37022
- createValidator("ocpp1.6", ocpp1_6_default),
37023
- createValidator("ocpp2.0.1", ocpp2_0_1_default),
37024
- createValidator("ocpp2.1", ocpp2_1_default)
37025
- ];
37028
+ var _cached = null;
37029
+ function getStandardValidators() {
37030
+ if (_cached) return _cached;
37031
+ _cached = [
37032
+ createValidator("ocpp1.6", ocpp1_6_default),
37033
+ createValidator("ocpp2.0.1", ocpp2_0_1_default),
37034
+ createValidator("ocpp2.1", ocpp2_1_default)
37035
+ ];
37036
+ return _cached;
37037
+ }
37026
37038
 
37027
37039
  // src/types.ts
37028
37040
  var ConnectionState = {
@@ -37215,6 +37227,7 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
37215
37227
  _prettify = false;
37216
37228
  constructor(options) {
37217
37229
  super();
37230
+ this.setMaxListeners(0);
37218
37231
  if (!options.identity) {
37219
37232
  throw new Error("identity is required");
37220
37233
  }
@@ -37716,11 +37729,13 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
37716
37729
  this._recordActivity();
37717
37730
  let message;
37718
37731
  try {
37719
- const str = rawData.toString();
37720
- message = JSON.parse(str);
37732
+ message = JSON.parse(rawData);
37721
37733
  if (!Array.isArray(message)) throw new Error("Message is not an array");
37722
37734
  } catch (err) {
37723
- this._onBadMessage(rawData.toString(), err);
37735
+ this._onBadMessage(
37736
+ typeof rawData === "string" ? rawData : rawData.toString(),
37737
+ err
37738
+ );
37724
37739
  return;
37725
37740
  }
37726
37741
  const messageType = message[0];
@@ -38047,10 +38062,14 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
38047
38062
  }
38048
38063
  if (ws.bufferedAmount > _OCPPClient._BACKPRESSURE_THRESHOLD) {
38049
38064
  this._logger?.warn?.("Backpressure \u2014 pausing send", {
38065
+ identity: this._identity,
38050
38066
  bufferedAmount: ws.bufferedAmount,
38051
38067
  threshold: _OCPPClient._BACKPRESSURE_THRESHOLD
38052
38068
  });
38053
- this.emit("backpressure", { bufferedAmount: ws.bufferedAmount });
38069
+ this.emit("backpressure", {
38070
+ identity: this._identity,
38071
+ bufferedAmount: ws.bufferedAmount
38072
+ });
38054
38073
  let waited = 0;
38055
38074
  const drainCheck = setInterval(() => {
38056
38075
  waited += 50;
@@ -38114,7 +38133,7 @@ var OCPPClient = class _OCPPClient extends import_node_events.EventEmitter {
38114
38133
  if (this._options.strictModeValidators) {
38115
38134
  this._validators = this._options.strictModeValidators;
38116
38135
  } else {
38117
- this._validators = standardValidators;
38136
+ this._validators = getStandardValidators();
38118
38137
  }
38119
38138
  if (Array.isArray(this._options.strictMode)) {
38120
38139
  this._strictProtocols = this._options.strictMode;
@@ -38668,8 +38687,7 @@ var OCPPServerClient = class extends OCPPClient {
38668
38687
  let pData;
38669
38688
  if (limits.methods) {
38670
38689
  try {
38671
- const str = data.toString();
38672
- pData = JSON.parse(str);
38690
+ pData = JSON.parse(data);
38673
38691
  if (Array.isArray(pData) && pData[0] === 2) {
38674
38692
  method = pData[2];
38675
38693
  }
@@ -38796,6 +38814,7 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
38796
38814
  _sessionTimeoutMs;
38797
38815
  constructor(options = {}) {
38798
38816
  super();
38817
+ this.setMaxListeners(0);
38799
38818
  if (options.strictMode) {
38800
38819
  if (!options.strictModeValidators && !options.protocols?.length) {
38801
38820
  throw new Error(
@@ -38818,7 +38837,10 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
38818
38837
  this._sessionTimeoutMs = this._options.sessionTtlMs;
38819
38838
  const maxSessions = this._options.maxSessions ?? 5e4;
38820
38839
  this._sessions = new LRUMap(maxSessions);
38821
- this._wss = new import_ws2.WebSocketServer({ noServer: true });
38840
+ this._wss = new import_ws2.WebSocketServer({
38841
+ noServer: true,
38842
+ maxPayload: this._options.maxPayloadBytes ?? 65536
38843
+ });
38822
38844
  this._gcInterval = setInterval(() => {
38823
38845
  const now = Date.now();
38824
38846
  for (const [identity, session] of this._sessions.entries()) {
@@ -39124,6 +39146,51 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39124
39146
  }
39125
39147
  return httpServer;
39126
39148
  }
39149
+ /**
39150
+ * Hot-reloads the TLS certificate on all active HTTPS servers without
39151
+ * dropping any existing WebSocket connections.
39152
+ *
39153
+ * **When to use:** Call this whenever your TLS certificate is renewed —
39154
+ * for example, after a Let's Encrypt auto-renewal (every ~90 days).
39155
+ * Without this, you would need to restart the Node.js process to pick up
39156
+ * the new certificate, disconnecting all connected charging stations.
39157
+ *
39158
+ * **How to use:**
39159
+ * ```ts
39160
+ * server.updateTLS({ cert: newCert, key: newKey });
39161
+ * ```
39162
+ *
39163
+ * **Optional:** Only relevant if you are terminating TLS directly in Node.js
39164
+ * (i.e. `SecurityProfile.TLS_BASIC_AUTH` or `TLS_CLIENT_CERT`). If you are
39165
+ * running behind a reverse proxy (Nginx, AWS ALB, etc.) that handles TLS,
39166
+ * you do not need this method — just rotate the cert on the proxy.
39167
+ *
39168
+ * @throws If the server is not using a TLS Security Profile.
39169
+ */
39170
+ updateTLS(tlsOpts) {
39171
+ const profile = this._options.securityProfile ?? 0 /* NONE */;
39172
+ if (profile !== 2 /* TLS_BASIC_AUTH */ && profile !== 3 /* TLS_CLIENT_CERT */) {
39173
+ throw new Error(
39174
+ "updateTLS() requires a TLS Security Profile (TLS_BASIC_AUTH or TLS_CLIENT_CERT)"
39175
+ );
39176
+ }
39177
+ this._options.tls = { ...this._options.tls, ...tlsOpts };
39178
+ const httpsOptions = {};
39179
+ if (tlsOpts.cert) httpsOptions.cert = tlsOpts.cert;
39180
+ if (tlsOpts.key) httpsOptions.key = tlsOpts.key;
39181
+ if (tlsOpts.ca) httpsOptions.ca = tlsOpts.ca;
39182
+ if (tlsOpts.passphrase) httpsOptions.passphrase = tlsOpts.passphrase;
39183
+ let updated = 0;
39184
+ for (const srv of this._httpServers) {
39185
+ if ("setSecureContext" in srv && typeof srv.setSecureContext === "function") {
39186
+ srv.setSecureContext(httpsOptions);
39187
+ updated++;
39188
+ }
39189
+ }
39190
+ this._logger?.info?.(
39191
+ `TLS context hot-reloaded across ${updated} active server(s)`
39192
+ );
39193
+ }
39127
39194
  // ─── Handle Upgrade ──────────────────────────────────────────
39128
39195
  get handleUpgrade() {
39129
39196
  return (req, socket, head) => {
@@ -39177,6 +39244,12 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39177
39244
  }
39178
39245
  if (bucket.tokens < 1) {
39179
39246
  this._logger?.warn?.("Connection rate limit exceeded", { ip });
39247
+ this.emit("securityEvent", {
39248
+ type: "CONNECTION_RATE_LIMIT",
39249
+ ip,
39250
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
39251
+ details: { tokensRemaining: bucket.tokens }
39252
+ });
39180
39253
  abortHandshake(socket, 429, "Too Many Requests");
39181
39254
  return;
39182
39255
  }
@@ -39433,6 +39506,13 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39433
39506
  if (ac.signal.aborted) {
39434
39507
  const reason = err instanceof Error ? err.message : "Unknown abort";
39435
39508
  this._logger?.warn?.("Handshake aborted", { identity, reason });
39509
+ this.emit("securityEvent", {
39510
+ type: "UPGRADE_ABORTED",
39511
+ identity,
39512
+ ip: req.socket.remoteAddress,
39513
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
39514
+ details: { reason }
39515
+ });
39436
39516
  this.emit("upgradeAborted", {
39437
39517
  identity,
39438
39518
  reason,
@@ -39446,6 +39526,13 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39446
39526
  const code = typeof errObj?.code === "number" ? errObj.code : 401;
39447
39527
  const message = typeof errObj?.message === "string" ? errObj.message : "Unauthorized";
39448
39528
  this._logger?.warn?.("Auth rejected", { identity, code });
39529
+ this.emit("securityEvent", {
39530
+ type: "AUTH_FAILED",
39531
+ identity,
39532
+ ip: req.socket.remoteAddress,
39533
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
39534
+ details: { code, message }
39535
+ });
39449
39536
  abortHandshake(socket, code, message);
39450
39537
  return;
39451
39538
  } finally {
@@ -39469,7 +39556,10 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39469
39556
  return;
39470
39557
  }
39471
39558
  if (!this._wss) {
39472
- this._wss = new import_ws2.WebSocketServer({ noServer: true });
39559
+ this._wss = new import_ws2.WebSocketServer({
39560
+ noServer: true,
39561
+ maxPayload: this._options.maxPayloadBytes ?? 65536
39562
+ });
39473
39563
  }
39474
39564
  this._wss.handleUpgrade(req, socket, head, (ws) => {
39475
39565
  const clientOptions = {
@@ -39857,6 +39947,6 @@ var OCPPServer = class extends import_node_events3.EventEmitter {
39857
39947
  defineRpcMiddleware,
39858
39948
  getErrorPlainObject,
39859
39949
  getPackageIdent,
39860
- standardValidators
39950
+ getStandardValidators
39861
39951
  });
39862
39952
  //# sourceMappingURL=index.js.map