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.
- package/README.md +6 -6
- package/dist/adapters/redis.d.mts +1 -1
- package/dist/adapters/redis.d.ts +1 -1
- package/dist/adapters/redis.js +2 -2
- package/dist/adapters/redis.js.map +1 -1
- package/dist/adapters/redis.mjs +2 -2
- package/dist/adapters/redis.mjs.map +1 -1
- package/dist/browser.d.mts +5 -0
- package/dist/browser.d.ts +5 -0
- package/dist/browser.js.map +1 -1
- package/dist/browser.mjs.map +1 -1
- package/dist/{index-CagcFzyZ.d.mts → index-1QBeqAuc.d.mts} +34 -2
- package/dist/{index-CagcFzyZ.d.ts → index-1QBeqAuc.d.ts} +34 -2
- package/dist/index.d.mts +26 -8
- package/dist/index.d.ts +26 -8
- package/dist/index.js +109 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +108 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -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,
|
|
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,
|
|
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
|
|
2
|
-
export {
|
|
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,
|
|
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
|
|
2
|
-
export {
|
|
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,
|
|
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
|
-
|
|
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
|
-
//
|
|
389
|
+
// Per-stream sequence counter for message ordering
|
|
390
390
|
_sequenceCounters = /* @__PURE__ */ new Map();
|
|
391
|
-
//
|
|
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
|
-
|
|
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
|
|
37022
|
-
|
|
37023
|
-
|
|
37024
|
-
|
|
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
|
-
|
|
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(
|
|
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", {
|
|
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 =
|
|
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
|
-
|
|
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({
|
|
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({
|
|
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
|
-
|
|
39950
|
+
getStandardValidators
|
|
39861
39951
|
});
|
|
39862
39952
|
//# sourceMappingURL=index.js.map
|