ocpp-ws-io 2.2.0 → 2.2.2-beta.1

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 CHANGED
@@ -27,6 +27,35 @@ The library provides enterprise-grade features for building OCPP-compliant EV ch
27
27
  - 🌐 **Browser-Ready Client** — Zero-dependency WebSocket client for EV charging simulators
28
28
  - ⚡ **CLI Toolkit** — `ocpp-ws-cli` for type generation, load testing, fuzzing, and virtual charge point simulation
29
29
  - 🛡️ **DDoS & Rate Limiting** — Token bucket rate limiting and adaptive throttling for charging station protection
30
+ - 🧩 **19 Built-in Plugins** — PII redaction, circuit breakers, deduplication, Kafka streaming, OpenTelemetry, and more
31
+
32
+ ## 🧩 Plugin Ecosystem
33
+
34
+ 19 built-in plugins organized into a **4-level power hierarchy**. Register from highest to lowest:
35
+
36
+ ```
37
+ Level 4 — Middleware pii-redactor · schema-versioning
38
+ Level 3 — Interceptor message-dedup · replay-buffer
39
+ Level 2 — Lifecycle Controller connection-guard · anomaly · circuit-breaker
40
+ Level 1 — Passive Hook kafka · webhook · metrics · otel · session-log · heartbeat · rate-limit-notifier
41
+ ```
42
+
43
+ ```typescript
44
+ import {
45
+ piiRedactorPlugin, messageDedupPlugin,
46
+ circuitBreakerPlugin, metricsPlugin, otelPlugin,
47
+ } from "ocpp-ws-io/plugins";
48
+
49
+ server.plugin(
50
+ piiRedactorPlugin(), // L4: transform payloads
51
+ messageDedupPlugin({ redis }), // L3: drop duplicates
52
+ circuitBreakerPlugin(), // L2: protect against flapping stations
53
+ metricsPlugin(), // L1: observe
54
+ otelPlugin(), // L1: trace
55
+ );
56
+ ```
57
+
58
+ > See the full [Plugin Documentation](https://ocpp-ws-io.rohittiwari.me/docs/ocpp-ws-io/plugins) for options, examples, and the `redisStyle` guide for Redis client compatibility.
30
59
 
31
60
  ## 📦 Installation
32
61
 
@@ -1,5 +1,5 @@
1
- import '../types-BunMs45p.mjs';
2
- export { a as RedisAdapter, b as RedisAdapterOptions } from '../index-B98n5Et3.mjs';
1
+ import '../types-xFfIgIuS.mjs';
2
+ export { a as RedisAdapter, b as RedisAdapterOptions } from '../index-B9rTwvbn.mjs';
3
3
  import 'ws';
4
4
  import 'node:https';
5
5
  import 'node:http';
@@ -1,5 +1,5 @@
1
- import '../types-BunMs45p.js';
2
- export { a as RedisAdapter, b as RedisAdapterOptions } from '../index-xx7uU8pY.js';
1
+ import '../types-xFfIgIuS.js';
2
+ export { a as RedisAdapter, b as RedisAdapterOptions } from '../index-D5pJ3wS4.js';
3
3
  import 'ws';
4
4
  import 'node:https';
5
5
  import 'node:http';
@@ -1 +1 @@
1
- 'use strict';var l=class{constructor(e,s,t){this.pub=e;this.sub=s;this.blocking=t;this.sub.on&&this.sub.on("message",(r,i)=>{let n=this._handlers.get(r);n&&n(i);});}_handlers=new Map;async publish(e,s){await this.pub.publish(e,s);}async subscribe(e,s){this._handlers.set(e,s),await this.sub.subscribe(e);}async unsubscribe(e){await this.sub.unsubscribe(e),this._handlers.delete(e);}async set(e,s,t){t?await this.pub.set(e,s,"EX",t):await this.pub.set(e,s);}async get(e){return await this.pub.get(e)||null}async mget(e){return e.length===0?[]:await this.pub.mget(...e)}async del(e){await this.pub.del(e);}async xadd(e,s,t){let r=[];t&&r.push("MAXLEN","~",t.toString()),r.push("*");for(let[i,n]of Object.entries(s))r.push(i,n);return await this.pub.xadd(e,...r)}async xaddBatch(e,s){if(e.length===0)return;let t=this.pub.pipeline();for(let r of e){let i=[];s&&i.push("MAXLEN","~",s.toString()),i.push("*");for(let[n,a]of Object.entries(r.args))i.push(n,a);t.xadd(r.stream,...i);}await t.exec();}async xread(e,s,t){let r=[];s&&r.push("COUNT",s),typeof t=="number"&&r.push("BLOCK",t),r.push("STREAMS"),e.forEach(a=>{r.push(a.key);}),e.forEach(a=>{r.push(a.id);});let n=await(t&&this.blocking?this.blocking:this.pub).xread(...r);return n?n.map(([a,o])=>({stream:a,messages:o.map(([u,h])=>{let m={};for(let d=0;d<h.length;d+=2)m[h[d]]=h[d+1];return {id:u,data:m}})})):null}async xlen(e){return await this.pub.xlen(e)}async disconnect(){this._handlers.clear();let e=async s=>{s.quit?await s.quit():s.disconnect&&await s.disconnect();};await Promise.all([e(this.pub),e(this.sub)]);}async setPresenceBatch(e){if(e.length===0)return;let s=this.pub.pipeline();for(let{key:t,value:r,ttlSeconds:i}of e)s.set(t,r,"EX",i);await s.exec();}async expire(e,s){await this.pub.expire(e,s);}onError(e){return this.pub.on("error",e),()=>this.pub.removeListener("error",e)}onReconnect(e){return this.pub.on("connect",e),()=>this.pub.removeListener("connect",e)}},g=class{constructor(e,s,t){this.pub=e;this.sub=s;this.blocking=t;}async publish(e,s){await this.pub.publish(e,s);}async subscribe(e,s){await this.sub.subscribe(e,s);}async unsubscribe(e){await this.sub.unsubscribe(e);}async set(e,s,t){t?await this.pub.set(e,s,{EX:t}):await this.pub.set(e,s);}async get(e){return await this.pub.get(e)||null}async mget(e){return e.length===0?[]:await this.pub.mGet(e)}async del(e){await this.pub.del(e);}async xadd(e,s,t){return await this.pub.xAdd(e,"*",s,{TRIM:t?{strategy:"MAXLEN",strategyModifier:"~",threshold:t}:void 0})}async xaddBatch(e,s){if(e.length===0)return;let t=this.pub.multi();for(let r of e)t.xAdd(r.stream,"*",r.args,{TRIM:s?{strategy:"MAXLEN",strategyModifier:"~",threshold:s}:void 0});await t.exec();}async xread(e,s,t){let r={};s&&(r.COUNT=s),typeof t=="number"&&(r.BLOCK=t);let i=e.map(o=>({key:o.key,id:o.id})),a=await(t&&this.blocking?this.blocking:this.pub).xRead(i,r);return !a||a.length===0?null:a.map(o=>({stream:o.name,messages:o.messages.map(u=>({id:u.id,data:u.message}))}))}async xlen(e){return await this.pub.xLen(e)}async disconnect(){await Promise.all([this.pub.disconnect(),this.sub.disconnect()]);}async setPresenceBatch(e){if(e.length===0)return;let s=this.pub.multi();for(let{key:t,value:r,ttlSeconds:i}of e)s.set(t,r,{EX:i});await s.exec();}async expire(e,s){await this.pub.expire(e,s);}onError(e){return this.pub.on("error",e),()=>this.pub.removeListener("error",e)}onReconnect(e){return this.pub.on("connect",e),()=>this.pub.removeListener("connect",e)}};function p(c,e,s){return e.isOpen!==void 0&&typeof e.subscribe=="function"?new g(c,e,s):new l(c,e,s)}var b=class{_driver;_prefix;_streamMaxLen;_streamTtlSeconds;_presenceTtlSeconds;_handlers=new Map;_streamOffsets=new Map;_streams=new Set;_polling=false;_closed=false;_sequenceCounters=new Map;_unsubError;_unsubReconnect;_presenceCache=new Map;_driverPool;_nextPoolIndex;constructor(e){this._prefix=e.prefix??"ocpp-ws-io:",this._streamMaxLen=e.streamMaxLen??1e3,this._streamTtlSeconds=e.streamTtlSeconds??300,this._presenceTtlSeconds=e.presenceTtlSeconds??300,this._driver=p(e.pubClient,e.subClient,e.blockingClient);let s=e.poolSize??1;if(this._driverPool=[this._driver],this._nextPoolIndex=0,s>1&&e.driverFactory)for(let t=1;t<s;t++)this._driverPool.push(e.driverFactory());this._driver.onError&&(this._unsubError=this._driver.onError(t=>{console.error("[RedisAdapter] Redis error:",t.message);})),this._driver.onReconnect&&(this._unsubReconnect=this._driver.onReconnect(()=>{this._rehydratePresence().catch(()=>{});}));}_getPoolDriver(){if(this._driverPool.length===1)return this._driver;let e=this._driverPool[this._nextPoolIndex];return this._nextPoolIndex=(this._nextPoolIndex+1)%this._driverPool.length,e}async publish(e,s){let t=this._prefix+e,r=s;if(r&&typeof r=="object"&&e.startsWith("ocpp:node:")){let n=(this._sequenceCounters.get(e)??0)+1;this._sequenceCounters.set(e,n),r.__seq=n;}let i=JSON.stringify(s);if(e.startsWith("ocpp:node:")){let n=this._getPoolDriver();await n.xadd(t,{message:i},this._streamMaxLen),await n.expire(t,this._streamTtlSeconds).catch(()=>{});}else await this._getPoolDriver().publish(t,i);}async publishBatch(e){let s=[],t=[];for(let i of e){let n=this._prefix+i.channel,a=JSON.stringify(i.data);i.channel.startsWith("ocpp:node:")?s.push({stream:n,args:{message:a}}):t.push({channel:n,message:a});}let r=[];s.length>0&&r.push(this._getPoolDriver().xaddBatch(s,this._streamMaxLen)),t.length>0&&r.push(Promise.all(t.map(i=>this._getPoolDriver().publish(i.channel,i.message))).then(()=>{})),await Promise.all(r);}async subscribe(e,s){if(!this._handlers.has(e)){this._handlers.set(e,new Set);let t=this._prefix+e;e.startsWith("ocpp:node:")?this._streams.has(t)||(this._streams.add(t),this._streamOffsets.set(t,"0"),this._ensurePolling()):await this._driver.subscribe(t,r=>{this._handleMessage(e,r);});}this._handlers.get(e)?.add(s);}async unsubscribe(e){let s=this._prefix+e;this._streams.has(s)?(this._streams.delete(s),this._streamOffsets.delete(s)):await this._driver.unsubscribe(s),this._handlers.delete(e);}async disconnect(){this._closed=true,this._handlers.clear(),this._streams.clear(),this._presenceCache.clear(),this._sequenceCounters.clear(),this._unsubError&&this._unsubError(),this._unsubReconnect&&this._unsubReconnect(),await Promise.allSettled(this._driverPool.map(e=>e.disconnect()));}_handleMessage(e,s){let t=this._handlers.get(e);if(!t)return;let r;try{r=JSON.parse(s);}catch{r=s;}for(let i of t)try{i(r);}catch{}}_ensurePolling(){this._polling||this._closed||(this._polling=true,this._pollLoop().catch(()=>{this._polling=false;}));}async _pollLoop(){for(;!this._closed;){if(this._streams.size===0){await new Promise(s=>setTimeout(s,1e3));continue}let e=Array.from(this._streams).map(s=>({key:s,id:this._streamOffsets.get(s)||"$"}));try{let s=await this._driver.xread(e,void 0,1e3);if(s)for(let t of s){let r=t.stream.replace(this._prefix,"");for(let i of t.messages){this._streamOffsets.set(t.stream,i.id);let n=i.data.message;n&&this._handleMessage(r,n);}}}catch{await new Promise(t=>setTimeout(t,1e3));}}this._polling=false;}async setPresence(e,s,t){let r=`${this._prefix}presence:${e}`;this._presenceCache.set(e,{nodeId:s,ttl:t}),await this._driver.set(r,s,t);}async getPresence(e){let s=`${this._prefix}presence:${e}`;return await this._driver.get(s)}async getPresenceBatch(e){if(e.length===0)return [];let s=e.map(t=>`${this._prefix}presence:${t}`);return this._driver.mget?await this._driver.mget(s):await Promise.all(s.map(t=>this._driver.get(t)))}async removePresence(e){let s=`${this._prefix}presence:${e}`;await this._driver.del(s);}async metrics(){let e=0,s={};for(let t of this._streams)try{let r=await this._driver.xlen(t);e+=r,s[t]=r;}catch{s[t]=-1;}return {pendingMessages:e,activeStreams:this._streams.size,streamDetails:s}}async setPresenceBatch(e){if(e.length===0)return;let s=e.map(({identity:t,nodeId:r,ttl:i})=>{let n=`${this._prefix}presence:${t}`,a=i??this._presenceTtlSeconds;return this._presenceCache.set(t,{nodeId:r,ttl:a}),{key:n,value:r,ttlSeconds:a}});await this._driver.setPresenceBatch(s);}async _rehydratePresence(){if(this._presenceCache.size===0)return;let e=Array.from(this._presenceCache.entries()).map(([s,{nodeId:t,ttl:r}])=>({key:`${this._prefix}presence:${s}`,value:t,ttlSeconds:r}));await this._driver.setPresenceBatch(e);}};exports.RedisAdapter=b;
1
+ 'use strict';var l=class{constructor(e,s,t){this.pub=e;this.sub=s;this.blocking=t;this.sub.on&&this.sub.on("message",(r,i)=>{let n=this._handlers.get(r);n&&n(i);});}pub;sub;blocking;_handlers=new Map;async publish(e,s){await this.pub.publish(e,s);}async subscribe(e,s){this._handlers.set(e,s),await this.sub.subscribe(e);}async unsubscribe(e){await this.sub.unsubscribe(e),this._handlers.delete(e);}async set(e,s,t){t?await this.pub.set(e,s,"EX",t):await this.pub.set(e,s);}async get(e){return await this.pub.get(e)||null}async mget(e){return e.length===0?[]:await this.pub.mget(...e)}async del(e){await this.pub.del(e);}async xadd(e,s,t){let r=[];t&&r.push("MAXLEN","~",t.toString()),r.push("*");for(let[i,n]of Object.entries(s))r.push(i,n);return await this.pub.xadd(e,...r)}async xaddBatch(e,s){if(e.length===0)return;let t=this.pub.pipeline();for(let r of e){let i=[];s&&i.push("MAXLEN","~",s.toString()),i.push("*");for(let[n,a]of Object.entries(r.args))i.push(n,a);t.xadd(r.stream,...i);}await t.exec();}async xread(e,s,t){let r=[];s&&r.push("COUNT",s),typeof t=="number"&&r.push("BLOCK",t),r.push("STREAMS"),e.forEach(a=>{r.push(a.key);}),e.forEach(a=>{r.push(a.id);});let n=await(t&&this.blocking?this.blocking:this.pub).xread(...r);return n?n.map(([a,o])=>({stream:a,messages:o.map(([u,h])=>{let m={};for(let d=0;d<h.length;d+=2)m[h[d]]=h[d+1];return {id:u,data:m}})})):null}async xlen(e){return await this.pub.xlen(e)}async disconnect(){this._handlers.clear();let e=async s=>{s.quit?await s.quit():s.disconnect&&await s.disconnect();};await Promise.all([e(this.pub),e(this.sub)]);}async setPresenceBatch(e){if(e.length===0)return;let s=this.pub.pipeline();for(let{key:t,value:r,ttlSeconds:i}of e)s.set(t,r,"EX",i);await s.exec();}async expire(e,s){await this.pub.expire(e,s);}onError(e){return this.pub.on("error",e),()=>this.pub.removeListener("error",e)}onReconnect(e){return this.pub.on("connect",e),()=>this.pub.removeListener("connect",e)}},g=class{constructor(e,s,t){this.pub=e;this.sub=s;this.blocking=t;}pub;sub;blocking;async publish(e,s){await this.pub.publish(e,s);}async subscribe(e,s){await this.sub.subscribe(e,s);}async unsubscribe(e){await this.sub.unsubscribe(e);}async set(e,s,t){t?await this.pub.set(e,s,{EX:t}):await this.pub.set(e,s);}async get(e){return await this.pub.get(e)||null}async mget(e){return e.length===0?[]:await this.pub.mGet(e)}async del(e){await this.pub.del(e);}async xadd(e,s,t){return await this.pub.xAdd(e,"*",s,{TRIM:t?{strategy:"MAXLEN",strategyModifier:"~",threshold:t}:void 0})}async xaddBatch(e,s){if(e.length===0)return;let t=this.pub.multi();for(let r of e)t.xAdd(r.stream,"*",r.args,{TRIM:s?{strategy:"MAXLEN",strategyModifier:"~",threshold:s}:void 0});await t.exec();}async xread(e,s,t){let r={};s&&(r.COUNT=s),typeof t=="number"&&(r.BLOCK=t);let i=e.map(o=>({key:o.key,id:o.id})),a=await(t&&this.blocking?this.blocking:this.pub).xRead(i,r);return !a||a.length===0?null:a.map(o=>({stream:o.name,messages:o.messages.map(u=>({id:u.id,data:u.message}))}))}async xlen(e){return await this.pub.xLen(e)}async disconnect(){await Promise.all([this.pub.disconnect(),this.sub.disconnect()]);}async setPresenceBatch(e){if(e.length===0)return;let s=this.pub.multi();for(let{key:t,value:r,ttlSeconds:i}of e)s.set(t,r,{EX:i});await s.exec();}async expire(e,s){await this.pub.expire(e,s);}onError(e){return this.pub.on("error",e),()=>this.pub.removeListener("error",e)}onReconnect(e){return this.pub.on("connect",e),()=>this.pub.removeListener("connect",e)}};function p(c,e,s){return e.isOpen!==void 0&&typeof e.subscribe=="function"?new g(c,e,s):new l(c,e,s)}var b=class{_driver;_prefix;_streamMaxLen;_streamTtlSeconds;_presenceTtlSeconds;_handlers=new Map;_streamOffsets=new Map;_streams=new Set;_polling=false;_closed=false;_sequenceCounters=new Map;_unsubError;_unsubReconnect;_presenceCache=new Map;_driverPool;_nextPoolIndex;constructor(e){this._prefix=e.prefix??"ocpp-ws-io:",this._streamMaxLen=e.streamMaxLen??1e3,this._streamTtlSeconds=e.streamTtlSeconds??300,this._presenceTtlSeconds=e.presenceTtlSeconds??300,this._driver=p(e.pubClient,e.subClient,e.blockingClient);let s=e.poolSize??1;if(this._driverPool=[this._driver],this._nextPoolIndex=0,s>1&&e.driverFactory)for(let t=1;t<s;t++)this._driverPool.push(e.driverFactory());this._driver.onError&&(this._unsubError=this._driver.onError(t=>{console.error("[RedisAdapter] Redis error:",t.message);})),this._driver.onReconnect&&(this._unsubReconnect=this._driver.onReconnect(()=>{this._rehydratePresence().catch(()=>{});}));}_getPoolDriver(){if(this._driverPool.length===1)return this._driver;let e=this._driverPool[this._nextPoolIndex];return this._nextPoolIndex=(this._nextPoolIndex+1)%this._driverPool.length,e}async publish(e,s){let t=this._prefix+e,r=s;if(r&&typeof r=="object"&&e.startsWith("ocpp:node:")){let n=(this._sequenceCounters.get(e)??0)+1;this._sequenceCounters.set(e,n),r.__seq=n;}let i=JSON.stringify(s);if(e.startsWith("ocpp:node:")){let n=this._getPoolDriver();await n.xadd(t,{message:i},this._streamMaxLen),await n.expire(t,this._streamTtlSeconds).catch(()=>{});}else await this._getPoolDriver().publish(t,i);}async publishBatch(e){let s=[],t=[];for(let i of e){let n=this._prefix+i.channel,a=JSON.stringify(i.data);i.channel.startsWith("ocpp:node:")?s.push({stream:n,args:{message:a}}):t.push({channel:n,message:a});}let r=[];s.length>0&&r.push(this._getPoolDriver().xaddBatch(s,this._streamMaxLen)),t.length>0&&r.push(Promise.all(t.map(i=>this._getPoolDriver().publish(i.channel,i.message))).then(()=>{})),await Promise.all(r);}async subscribe(e,s){if(!this._handlers.has(e)){this._handlers.set(e,new Set);let t=this._prefix+e;e.startsWith("ocpp:node:")?this._streams.has(t)||(this._streams.add(t),this._streamOffsets.set(t,"0"),this._ensurePolling()):await this._driver.subscribe(t,r=>{this._handleMessage(e,r);});}this._handlers.get(e)?.add(s);}async unsubscribe(e){let s=this._prefix+e;this._streams.has(s)?(this._streams.delete(s),this._streamOffsets.delete(s)):await this._driver.unsubscribe(s),this._handlers.delete(e);}async disconnect(){this._closed=true,this._handlers.clear(),this._streams.clear(),this._presenceCache.clear(),this._sequenceCounters.clear(),this._unsubError&&this._unsubError(),this._unsubReconnect&&this._unsubReconnect(),await Promise.allSettled(this._driverPool.map(e=>e.disconnect()));}_handleMessage(e,s){let t=this._handlers.get(e);if(!t)return;let r;try{r=JSON.parse(s);}catch{r=s;}for(let i of t)try{i(r);}catch{}}_ensurePolling(){this._polling||this._closed||(this._polling=true,this._pollLoop().catch(()=>{this._polling=false;}));}async _pollLoop(){for(;!this._closed;){if(this._streams.size===0){await new Promise(s=>setTimeout(s,1e3));continue}let e=Array.from(this._streams).map(s=>({key:s,id:this._streamOffsets.get(s)||"$"}));try{let s=await this._driver.xread(e,void 0,1e3);if(s)for(let t of s){let r=t.stream.replace(this._prefix,"");for(let i of t.messages){this._streamOffsets.set(t.stream,i.id);let n=i.data.message;n&&this._handleMessage(r,n);}}}catch{await new Promise(t=>setTimeout(t,1e3));}}this._polling=false;}async setPresence(e,s,t){let r=`${this._prefix}presence:${e}`;this._presenceCache.set(e,{nodeId:s,ttl:t}),await this._driver.set(r,s,t);}async getPresence(e){let s=`${this._prefix}presence:${e}`;return await this._driver.get(s)}async getPresenceBatch(e){if(e.length===0)return [];let s=e.map(t=>`${this._prefix}presence:${t}`);return this._driver.mget?await this._driver.mget(s):await Promise.all(s.map(t=>this._driver.get(t)))}async removePresence(e){let s=`${this._prefix}presence:${e}`;await this._driver.del(s);}async metrics(){let e=0,s={};for(let t of this._streams)try{let r=await this._driver.xlen(t);e+=r,s[t]=r;}catch{s[t]=-1;}return {pendingMessages:e,activeStreams:this._streams.size,streamDetails:s}}async setPresenceBatch(e){if(e.length===0)return;let s=e.map(({identity:t,nodeId:r,ttl:i})=>{let n=`${this._prefix}presence:${t}`,a=i??this._presenceTtlSeconds;return this._presenceCache.set(t,{nodeId:r,ttl:a}),{key:n,value:r,ttlSeconds:a}});await this._driver.setPresenceBatch(s);}async _rehydratePresence(){if(this._presenceCache.size===0)return;let e=Array.from(this._presenceCache.entries()).map(([s,{nodeId:t,ttl:r}])=>({key:`${this._prefix}presence:${s}`,value:t,ttlSeconds:r}));await this._driver.setPresenceBatch(e);}};exports.RedisAdapter=b;
@@ -1 +1 @@
1
- var l=class{constructor(e,s,t){this.pub=e;this.sub=s;this.blocking=t;this.sub.on&&this.sub.on("message",(r,i)=>{let n=this._handlers.get(r);n&&n(i);});}_handlers=new Map;async publish(e,s){await this.pub.publish(e,s);}async subscribe(e,s){this._handlers.set(e,s),await this.sub.subscribe(e);}async unsubscribe(e){await this.sub.unsubscribe(e),this._handlers.delete(e);}async set(e,s,t){t?await this.pub.set(e,s,"EX",t):await this.pub.set(e,s);}async get(e){return await this.pub.get(e)||null}async mget(e){return e.length===0?[]:await this.pub.mget(...e)}async del(e){await this.pub.del(e);}async xadd(e,s,t){let r=[];t&&r.push("MAXLEN","~",t.toString()),r.push("*");for(let[i,n]of Object.entries(s))r.push(i,n);return await this.pub.xadd(e,...r)}async xaddBatch(e,s){if(e.length===0)return;let t=this.pub.pipeline();for(let r of e){let i=[];s&&i.push("MAXLEN","~",s.toString()),i.push("*");for(let[n,a]of Object.entries(r.args))i.push(n,a);t.xadd(r.stream,...i);}await t.exec();}async xread(e,s,t){let r=[];s&&r.push("COUNT",s),typeof t=="number"&&r.push("BLOCK",t),r.push("STREAMS"),e.forEach(a=>{r.push(a.key);}),e.forEach(a=>{r.push(a.id);});let n=await(t&&this.blocking?this.blocking:this.pub).xread(...r);return n?n.map(([a,o])=>({stream:a,messages:o.map(([u,h])=>{let m={};for(let d=0;d<h.length;d+=2)m[h[d]]=h[d+1];return {id:u,data:m}})})):null}async xlen(e){return await this.pub.xlen(e)}async disconnect(){this._handlers.clear();let e=async s=>{s.quit?await s.quit():s.disconnect&&await s.disconnect();};await Promise.all([e(this.pub),e(this.sub)]);}async setPresenceBatch(e){if(e.length===0)return;let s=this.pub.pipeline();for(let{key:t,value:r,ttlSeconds:i}of e)s.set(t,r,"EX",i);await s.exec();}async expire(e,s){await this.pub.expire(e,s);}onError(e){return this.pub.on("error",e),()=>this.pub.removeListener("error",e)}onReconnect(e){return this.pub.on("connect",e),()=>this.pub.removeListener("connect",e)}},g=class{constructor(e,s,t){this.pub=e;this.sub=s;this.blocking=t;}async publish(e,s){await this.pub.publish(e,s);}async subscribe(e,s){await this.sub.subscribe(e,s);}async unsubscribe(e){await this.sub.unsubscribe(e);}async set(e,s,t){t?await this.pub.set(e,s,{EX:t}):await this.pub.set(e,s);}async get(e){return await this.pub.get(e)||null}async mget(e){return e.length===0?[]:await this.pub.mGet(e)}async del(e){await this.pub.del(e);}async xadd(e,s,t){return await this.pub.xAdd(e,"*",s,{TRIM:t?{strategy:"MAXLEN",strategyModifier:"~",threshold:t}:void 0})}async xaddBatch(e,s){if(e.length===0)return;let t=this.pub.multi();for(let r of e)t.xAdd(r.stream,"*",r.args,{TRIM:s?{strategy:"MAXLEN",strategyModifier:"~",threshold:s}:void 0});await t.exec();}async xread(e,s,t){let r={};s&&(r.COUNT=s),typeof t=="number"&&(r.BLOCK=t);let i=e.map(o=>({key:o.key,id:o.id})),a=await(t&&this.blocking?this.blocking:this.pub).xRead(i,r);return !a||a.length===0?null:a.map(o=>({stream:o.name,messages:o.messages.map(u=>({id:u.id,data:u.message}))}))}async xlen(e){return await this.pub.xLen(e)}async disconnect(){await Promise.all([this.pub.disconnect(),this.sub.disconnect()]);}async setPresenceBatch(e){if(e.length===0)return;let s=this.pub.multi();for(let{key:t,value:r,ttlSeconds:i}of e)s.set(t,r,{EX:i});await s.exec();}async expire(e,s){await this.pub.expire(e,s);}onError(e){return this.pub.on("error",e),()=>this.pub.removeListener("error",e)}onReconnect(e){return this.pub.on("connect",e),()=>this.pub.removeListener("connect",e)}};function p(c,e,s){return e.isOpen!==void 0&&typeof e.subscribe=="function"?new g(c,e,s):new l(c,e,s)}var b=class{_driver;_prefix;_streamMaxLen;_streamTtlSeconds;_presenceTtlSeconds;_handlers=new Map;_streamOffsets=new Map;_streams=new Set;_polling=false;_closed=false;_sequenceCounters=new Map;_unsubError;_unsubReconnect;_presenceCache=new Map;_driverPool;_nextPoolIndex;constructor(e){this._prefix=e.prefix??"ocpp-ws-io:",this._streamMaxLen=e.streamMaxLen??1e3,this._streamTtlSeconds=e.streamTtlSeconds??300,this._presenceTtlSeconds=e.presenceTtlSeconds??300,this._driver=p(e.pubClient,e.subClient,e.blockingClient);let s=e.poolSize??1;if(this._driverPool=[this._driver],this._nextPoolIndex=0,s>1&&e.driverFactory)for(let t=1;t<s;t++)this._driverPool.push(e.driverFactory());this._driver.onError&&(this._unsubError=this._driver.onError(t=>{console.error("[RedisAdapter] Redis error:",t.message);})),this._driver.onReconnect&&(this._unsubReconnect=this._driver.onReconnect(()=>{this._rehydratePresence().catch(()=>{});}));}_getPoolDriver(){if(this._driverPool.length===1)return this._driver;let e=this._driverPool[this._nextPoolIndex];return this._nextPoolIndex=(this._nextPoolIndex+1)%this._driverPool.length,e}async publish(e,s){let t=this._prefix+e,r=s;if(r&&typeof r=="object"&&e.startsWith("ocpp:node:")){let n=(this._sequenceCounters.get(e)??0)+1;this._sequenceCounters.set(e,n),r.__seq=n;}let i=JSON.stringify(s);if(e.startsWith("ocpp:node:")){let n=this._getPoolDriver();await n.xadd(t,{message:i},this._streamMaxLen),await n.expire(t,this._streamTtlSeconds).catch(()=>{});}else await this._getPoolDriver().publish(t,i);}async publishBatch(e){let s=[],t=[];for(let i of e){let n=this._prefix+i.channel,a=JSON.stringify(i.data);i.channel.startsWith("ocpp:node:")?s.push({stream:n,args:{message:a}}):t.push({channel:n,message:a});}let r=[];s.length>0&&r.push(this._getPoolDriver().xaddBatch(s,this._streamMaxLen)),t.length>0&&r.push(Promise.all(t.map(i=>this._getPoolDriver().publish(i.channel,i.message))).then(()=>{})),await Promise.all(r);}async subscribe(e,s){if(!this._handlers.has(e)){this._handlers.set(e,new Set);let t=this._prefix+e;e.startsWith("ocpp:node:")?this._streams.has(t)||(this._streams.add(t),this._streamOffsets.set(t,"0"),this._ensurePolling()):await this._driver.subscribe(t,r=>{this._handleMessage(e,r);});}this._handlers.get(e)?.add(s);}async unsubscribe(e){let s=this._prefix+e;this._streams.has(s)?(this._streams.delete(s),this._streamOffsets.delete(s)):await this._driver.unsubscribe(s),this._handlers.delete(e);}async disconnect(){this._closed=true,this._handlers.clear(),this._streams.clear(),this._presenceCache.clear(),this._sequenceCounters.clear(),this._unsubError&&this._unsubError(),this._unsubReconnect&&this._unsubReconnect(),await Promise.allSettled(this._driverPool.map(e=>e.disconnect()));}_handleMessage(e,s){let t=this._handlers.get(e);if(!t)return;let r;try{r=JSON.parse(s);}catch{r=s;}for(let i of t)try{i(r);}catch{}}_ensurePolling(){this._polling||this._closed||(this._polling=true,this._pollLoop().catch(()=>{this._polling=false;}));}async _pollLoop(){for(;!this._closed;){if(this._streams.size===0){await new Promise(s=>setTimeout(s,1e3));continue}let e=Array.from(this._streams).map(s=>({key:s,id:this._streamOffsets.get(s)||"$"}));try{let s=await this._driver.xread(e,void 0,1e3);if(s)for(let t of s){let r=t.stream.replace(this._prefix,"");for(let i of t.messages){this._streamOffsets.set(t.stream,i.id);let n=i.data.message;n&&this._handleMessage(r,n);}}}catch{await new Promise(t=>setTimeout(t,1e3));}}this._polling=false;}async setPresence(e,s,t){let r=`${this._prefix}presence:${e}`;this._presenceCache.set(e,{nodeId:s,ttl:t}),await this._driver.set(r,s,t);}async getPresence(e){let s=`${this._prefix}presence:${e}`;return await this._driver.get(s)}async getPresenceBatch(e){if(e.length===0)return [];let s=e.map(t=>`${this._prefix}presence:${t}`);return this._driver.mget?await this._driver.mget(s):await Promise.all(s.map(t=>this._driver.get(t)))}async removePresence(e){let s=`${this._prefix}presence:${e}`;await this._driver.del(s);}async metrics(){let e=0,s={};for(let t of this._streams)try{let r=await this._driver.xlen(t);e+=r,s[t]=r;}catch{s[t]=-1;}return {pendingMessages:e,activeStreams:this._streams.size,streamDetails:s}}async setPresenceBatch(e){if(e.length===0)return;let s=e.map(({identity:t,nodeId:r,ttl:i})=>{let n=`${this._prefix}presence:${t}`,a=i??this._presenceTtlSeconds;return this._presenceCache.set(t,{nodeId:r,ttl:a}),{key:n,value:r,ttlSeconds:a}});await this._driver.setPresenceBatch(s);}async _rehydratePresence(){if(this._presenceCache.size===0)return;let e=Array.from(this._presenceCache.entries()).map(([s,{nodeId:t,ttl:r}])=>({key:`${this._prefix}presence:${s}`,value:t,ttlSeconds:r}));await this._driver.setPresenceBatch(e);}};export{b as RedisAdapter};
1
+ var l=class{constructor(e,s,t){this.pub=e;this.sub=s;this.blocking=t;this.sub.on&&this.sub.on("message",(r,i)=>{let n=this._handlers.get(r);n&&n(i);});}pub;sub;blocking;_handlers=new Map;async publish(e,s){await this.pub.publish(e,s);}async subscribe(e,s){this._handlers.set(e,s),await this.sub.subscribe(e);}async unsubscribe(e){await this.sub.unsubscribe(e),this._handlers.delete(e);}async set(e,s,t){t?await this.pub.set(e,s,"EX",t):await this.pub.set(e,s);}async get(e){return await this.pub.get(e)||null}async mget(e){return e.length===0?[]:await this.pub.mget(...e)}async del(e){await this.pub.del(e);}async xadd(e,s,t){let r=[];t&&r.push("MAXLEN","~",t.toString()),r.push("*");for(let[i,n]of Object.entries(s))r.push(i,n);return await this.pub.xadd(e,...r)}async xaddBatch(e,s){if(e.length===0)return;let t=this.pub.pipeline();for(let r of e){let i=[];s&&i.push("MAXLEN","~",s.toString()),i.push("*");for(let[n,a]of Object.entries(r.args))i.push(n,a);t.xadd(r.stream,...i);}await t.exec();}async xread(e,s,t){let r=[];s&&r.push("COUNT",s),typeof t=="number"&&r.push("BLOCK",t),r.push("STREAMS"),e.forEach(a=>{r.push(a.key);}),e.forEach(a=>{r.push(a.id);});let n=await(t&&this.blocking?this.blocking:this.pub).xread(...r);return n?n.map(([a,o])=>({stream:a,messages:o.map(([u,h])=>{let m={};for(let d=0;d<h.length;d+=2)m[h[d]]=h[d+1];return {id:u,data:m}})})):null}async xlen(e){return await this.pub.xlen(e)}async disconnect(){this._handlers.clear();let e=async s=>{s.quit?await s.quit():s.disconnect&&await s.disconnect();};await Promise.all([e(this.pub),e(this.sub)]);}async setPresenceBatch(e){if(e.length===0)return;let s=this.pub.pipeline();for(let{key:t,value:r,ttlSeconds:i}of e)s.set(t,r,"EX",i);await s.exec();}async expire(e,s){await this.pub.expire(e,s);}onError(e){return this.pub.on("error",e),()=>this.pub.removeListener("error",e)}onReconnect(e){return this.pub.on("connect",e),()=>this.pub.removeListener("connect",e)}},g=class{constructor(e,s,t){this.pub=e;this.sub=s;this.blocking=t;}pub;sub;blocking;async publish(e,s){await this.pub.publish(e,s);}async subscribe(e,s){await this.sub.subscribe(e,s);}async unsubscribe(e){await this.sub.unsubscribe(e);}async set(e,s,t){t?await this.pub.set(e,s,{EX:t}):await this.pub.set(e,s);}async get(e){return await this.pub.get(e)||null}async mget(e){return e.length===0?[]:await this.pub.mGet(e)}async del(e){await this.pub.del(e);}async xadd(e,s,t){return await this.pub.xAdd(e,"*",s,{TRIM:t?{strategy:"MAXLEN",strategyModifier:"~",threshold:t}:void 0})}async xaddBatch(e,s){if(e.length===0)return;let t=this.pub.multi();for(let r of e)t.xAdd(r.stream,"*",r.args,{TRIM:s?{strategy:"MAXLEN",strategyModifier:"~",threshold:s}:void 0});await t.exec();}async xread(e,s,t){let r={};s&&(r.COUNT=s),typeof t=="number"&&(r.BLOCK=t);let i=e.map(o=>({key:o.key,id:o.id})),a=await(t&&this.blocking?this.blocking:this.pub).xRead(i,r);return !a||a.length===0?null:a.map(o=>({stream:o.name,messages:o.messages.map(u=>({id:u.id,data:u.message}))}))}async xlen(e){return await this.pub.xLen(e)}async disconnect(){await Promise.all([this.pub.disconnect(),this.sub.disconnect()]);}async setPresenceBatch(e){if(e.length===0)return;let s=this.pub.multi();for(let{key:t,value:r,ttlSeconds:i}of e)s.set(t,r,{EX:i});await s.exec();}async expire(e,s){await this.pub.expire(e,s);}onError(e){return this.pub.on("error",e),()=>this.pub.removeListener("error",e)}onReconnect(e){return this.pub.on("connect",e),()=>this.pub.removeListener("connect",e)}};function p(c,e,s){return e.isOpen!==void 0&&typeof e.subscribe=="function"?new g(c,e,s):new l(c,e,s)}var b=class{_driver;_prefix;_streamMaxLen;_streamTtlSeconds;_presenceTtlSeconds;_handlers=new Map;_streamOffsets=new Map;_streams=new Set;_polling=false;_closed=false;_sequenceCounters=new Map;_unsubError;_unsubReconnect;_presenceCache=new Map;_driverPool;_nextPoolIndex;constructor(e){this._prefix=e.prefix??"ocpp-ws-io:",this._streamMaxLen=e.streamMaxLen??1e3,this._streamTtlSeconds=e.streamTtlSeconds??300,this._presenceTtlSeconds=e.presenceTtlSeconds??300,this._driver=p(e.pubClient,e.subClient,e.blockingClient);let s=e.poolSize??1;if(this._driverPool=[this._driver],this._nextPoolIndex=0,s>1&&e.driverFactory)for(let t=1;t<s;t++)this._driverPool.push(e.driverFactory());this._driver.onError&&(this._unsubError=this._driver.onError(t=>{console.error("[RedisAdapter] Redis error:",t.message);})),this._driver.onReconnect&&(this._unsubReconnect=this._driver.onReconnect(()=>{this._rehydratePresence().catch(()=>{});}));}_getPoolDriver(){if(this._driverPool.length===1)return this._driver;let e=this._driverPool[this._nextPoolIndex];return this._nextPoolIndex=(this._nextPoolIndex+1)%this._driverPool.length,e}async publish(e,s){let t=this._prefix+e,r=s;if(r&&typeof r=="object"&&e.startsWith("ocpp:node:")){let n=(this._sequenceCounters.get(e)??0)+1;this._sequenceCounters.set(e,n),r.__seq=n;}let i=JSON.stringify(s);if(e.startsWith("ocpp:node:")){let n=this._getPoolDriver();await n.xadd(t,{message:i},this._streamMaxLen),await n.expire(t,this._streamTtlSeconds).catch(()=>{});}else await this._getPoolDriver().publish(t,i);}async publishBatch(e){let s=[],t=[];for(let i of e){let n=this._prefix+i.channel,a=JSON.stringify(i.data);i.channel.startsWith("ocpp:node:")?s.push({stream:n,args:{message:a}}):t.push({channel:n,message:a});}let r=[];s.length>0&&r.push(this._getPoolDriver().xaddBatch(s,this._streamMaxLen)),t.length>0&&r.push(Promise.all(t.map(i=>this._getPoolDriver().publish(i.channel,i.message))).then(()=>{})),await Promise.all(r);}async subscribe(e,s){if(!this._handlers.has(e)){this._handlers.set(e,new Set);let t=this._prefix+e;e.startsWith("ocpp:node:")?this._streams.has(t)||(this._streams.add(t),this._streamOffsets.set(t,"0"),this._ensurePolling()):await this._driver.subscribe(t,r=>{this._handleMessage(e,r);});}this._handlers.get(e)?.add(s);}async unsubscribe(e){let s=this._prefix+e;this._streams.has(s)?(this._streams.delete(s),this._streamOffsets.delete(s)):await this._driver.unsubscribe(s),this._handlers.delete(e);}async disconnect(){this._closed=true,this._handlers.clear(),this._streams.clear(),this._presenceCache.clear(),this._sequenceCounters.clear(),this._unsubError&&this._unsubError(),this._unsubReconnect&&this._unsubReconnect(),await Promise.allSettled(this._driverPool.map(e=>e.disconnect()));}_handleMessage(e,s){let t=this._handlers.get(e);if(!t)return;let r;try{r=JSON.parse(s);}catch{r=s;}for(let i of t)try{i(r);}catch{}}_ensurePolling(){this._polling||this._closed||(this._polling=true,this._pollLoop().catch(()=>{this._polling=false;}));}async _pollLoop(){for(;!this._closed;){if(this._streams.size===0){await new Promise(s=>setTimeout(s,1e3));continue}let e=Array.from(this._streams).map(s=>({key:s,id:this._streamOffsets.get(s)||"$"}));try{let s=await this._driver.xread(e,void 0,1e3);if(s)for(let t of s){let r=t.stream.replace(this._prefix,"");for(let i of t.messages){this._streamOffsets.set(t.stream,i.id);let n=i.data.message;n&&this._handleMessage(r,n);}}}catch{await new Promise(t=>setTimeout(t,1e3));}}this._polling=false;}async setPresence(e,s,t){let r=`${this._prefix}presence:${e}`;this._presenceCache.set(e,{nodeId:s,ttl:t}),await this._driver.set(r,s,t);}async getPresence(e){let s=`${this._prefix}presence:${e}`;return await this._driver.get(s)}async getPresenceBatch(e){if(e.length===0)return [];let s=e.map(t=>`${this._prefix}presence:${t}`);return this._driver.mget?await this._driver.mget(s):await Promise.all(s.map(t=>this._driver.get(t)))}async removePresence(e){let s=`${this._prefix}presence:${e}`;await this._driver.del(s);}async metrics(){let e=0,s={};for(let t of this._streams)try{let r=await this._driver.xlen(t);e+=r,s[t]=r;}catch{s[t]=-1;}return {pendingMessages:e,activeStreams:this._streams.size,streamDetails:s}}async setPresenceBatch(e){if(e.length===0)return;let s=e.map(({identity:t,nodeId:r,ttl:i})=>{let n=`${this._prefix}presence:${t}`,a=i??this._presenceTtlSeconds;return this._presenceCache.set(t,{nodeId:r,ttl:a}),{key:n,value:r,ttlSeconds:a}});await this._driver.setPresenceBatch(s);}async _rehydratePresence(){if(this._presenceCache.size===0)return;let e=Array.from(this._presenceCache.entries()).map(([s,{nodeId:t,ttl:r}])=>({key:`${this._prefix}presence:${s}`,value:t,ttlSeconds:r}));await this._driver.setPresenceBatch(e);}};export{b as RedisAdapter};
package/dist/browser.js CHANGED
@@ -1 +1 @@
1
- 'use strict';var client=require('voltlog-io/client');function F(s){return s}function G(s,e,t={}){let r=typeof t=="object"?t:{},{exchangeLog:n=false,prettify:i=false}=r;return async(o,a)=>{let l=Date.now(),c=o.method,p=n?"info":"debug";switch(o.type){case "incoming_call":n&&i?s[p]?.(`\u26A1 ${e} \u2190 ${c} [IN]`,{messageId:o.messageId,method:o.method,protocol:o.protocol,payload:o.params,direction:"IN"}):s[p]?.("CALL \u2190",{messageId:o.messageId,method:o.method,protocol:o.protocol,payload:o.params,direction:"IN"});break;case "outgoing_call":n&&i?s[p]?.(`\u26A1 ${e} \u2192 ${c} [OUT]`,{method:o.method,params:o.params,direction:"OUT"}):s[p]?.("CALL \u2192",{method:o.method,params:o.params,direction:"OUT"});break}try{let d=await a(),u=Date.now()-l;switch(o.type){case "incoming_call":d!=null&&(n&&i?s[p]?.(`\u2705 ${e} \u2192 ${c} [RES]`,{messageId:o.messageId,method:o.method,durationMs:u,params:d,direction:"OUT"}):s[p]?.("CALLRESULT \u2192",{messageId:o.messageId,method:o.method,durationMs:u,params:d,direction:"OUT"}));break;case "outgoing_call":n&&i?s[p]?.(`\u2705 ${e} \u2190 ${c} [RES]`,{messageId:o.messageId,method:o.method,durationMs:u,payload:d,direction:"IN"}):s[p]?.("CALLRESULT \u2190",{messageId:o.messageId,method:o.method,durationMs:u,payload:d,direction:"IN"});break}return d}catch(d){let u=d.message,m=Date.now()-l;throw o.type==="incoming_call"?n&&i?s.error?.(`\u{1F6A8} ${e} \u2192 ${c} [ERR]`,{messageId:o.messageId,method:o.method,durationMs:m,error:u,direction:"OUT"}):s.error?.("CALLERROR \u2192",{messageId:o.messageId,method:o.method,durationMs:m,error:u,direction:"OUT"}):o.type==="outgoing_call"&&(n&&i?s.warn?.(`\u{1F6A8} ${e} \u2190 ${c} [ERR]`,{messageId:o.messageId,method:o.method,durationMs:m,error:u,direction:"IN"}):s.warn?.("CALLERROR \u2190",{messageId:o.messageId,method:o.method,durationMs:m,error:u,direction:"IN"})),d}}}var A=class{_stack=[];use(e){this._stack.push(e);}async execute(e,t){let r=-1,n=async i=>{if(i<=r)throw new Error("next() called multiple times");r=i;let o=this._stack[i];if(i===this._stack.length)return t(e);if(o)return o(e,()=>n(i+1))};return n(0)}};var N=class{_listeners=new Map;on(e,t){let r=this._listeners.get(e);return r?r.push(t):this._listeners.set(e,[t]),this}once(e,t){let r=(...n)=>{this.off(e,r),t(...n);};return r.__wrapped=t,this.on(e,r)}off(e,t){let r=this._listeners.get(e);if(!r)return this;let n=r.findIndex(i=>i===t||i.__wrapped===t);return n!==-1&&r.splice(n,1),r.length===0&&this._listeners.delete(e),this}emit(e,...t){let r=this._listeners.get(e);if(!r||r.length===0)return false;for(let n of [...r])n(...t);return true}addListener(e,t){return this.on(e,t)}removeListener(e,t){return this.off(e,t)}removeAllListeners(e){return e?this._listeners.delete(e):this._listeners.clear(),this}listenerCount(e){return this._listeners.get(e)?.length??0}};var v=class extends Error{constructor(e="Operation timed out"){super(e),this.name="TimeoutError";}},g=class extends Error{rpcErrorCode="GenericError";rpcErrorMessage="";details;constructor(e,t={}){super(e),this.name="RPCGenericError",this.details=t;}},w=class extends g{rpcErrorCode="NotImplemented";rpcErrorMessage="Requested method is not known";constructor(e,t={}){super(e,t),this.name="RPCNotImplementedError";}},E=class extends g{rpcErrorCode="NotSupported";rpcErrorMessage="Requested method is recognised but not supported";constructor(e,t={}){super(e,t),this.name="RPCNotSupportedError";}},_=class extends g{rpcErrorCode="InternalError";rpcErrorMessage="An internal error occurred and the receiver was not able to process the requested action successfully";constructor(e,t={}){super(e,t),this.name="RPCInternalError";}},x=class extends g{rpcErrorCode="ProtocolError";rpcErrorMessage="Payload for action is incomplete";constructor(e,t={}){super(e,t),this.name="RPCProtocolError";}},R=class extends g{rpcErrorCode="SecurityError";rpcErrorMessage="During the processing of action a security issue occurred preventing receiver from completing the action successfully";constructor(e,t={}){super(e,t),this.name="RPCSecurityError";}},O=class extends g{rpcErrorCode="FormationViolation";rpcErrorMessage="Payload for action is syntactically incorrect or not conform the PDU structure for action";constructor(e,t={}){super(e,t),this.name="RPCFormationViolationError";}},M=class extends g{rpcErrorCode="FormatViolation";rpcErrorMessage="Payload is syntactically correct but at least one field contains an invalid value";constructor(e,t={}){super(e,t),this.name="RPCFormatViolationError";}},b=class extends g{rpcErrorCode="PropertyConstraintViolation";rpcErrorMessage="Payload is syntactically correct but at least one of the fields violates data type constraints";constructor(e,t={}){super(e,t),this.name="RPCPropertyConstraintViolationError";}},T=class extends g{rpcErrorCode="OccurrenceConstraintViolation";rpcErrorMessage="Payload for action is syntactically correct but at least one of the fields violates occurrence constraints";constructor(e,t={}){super(e,t),this.name="RPCOccurrenceConstraintViolationError";}},k=class extends g{rpcErrorCode="TypeConstraintViolation";rpcErrorMessage="Payload for action is syntactically correct but at least one of the fields violates type constraints";constructor(e,t={}){super(e,t),this.name="RPCTypeConstraintViolationError";}},y=class extends g{rpcErrorCode="MessageTypeNotSupported";rpcErrorMessage="A message with a Message Type Number received that is not supported by this implementation";constructor(e,t={}){super(e,t),this.name="RPCMessageTypeNotSupportedError";}},L=class extends g{rpcErrorCode="RpcFrameworkError";rpcErrorMessage="Content of the call is not a valid RPC request";constructor(e,t={}){super(e,t),this.name="RPCFrameworkError";}};function X(s){return s.showMetadata===false||s.showSourceMeta===false||s.prettifySource===true||s.prettifyMetadata===true}function Z(s){let e=s.showMetadata??true,t=s.showSourceMeta??true,r=s.prettifySource??false,n=s.prettifyMetadata??false,i="\x1B[2;37m",o="\x1B[0m",a="\x1B[36m";return (l,c)=>{if(!t)l.context=void 0;else if(r&&l.context){let d=[];l.context.component&&d.push(String(l.context.component)),l.context.identity&&d.push(String(l.context.identity)),d.length>0&&(l.message=`${i}[${d.join("/")}]${o} ${l.message}`,l.context=void 0);}let p=l.meta;if(!e)l.meta={};else if(n&&p&&Object.keys(p).length>0){let d=Object.entries(p).filter(([,u])=>u!=null).map(([u,m])=>{let P=typeof m=="object"?JSON.stringify(m):String(m);return typeof m=="string"?P=`${i}${P}${o}`:P=`${i}${P}${o}`,`${a}${u}${o}=${P}`}).join(" ");d&&(l.message=`${l.message} ${d}`),l.meta={};}c(l);}}function W(s,e){if(s===false||s?.enabled===false)return null;if(s?.logger)return e&&s.logger.child?s.logger.child(e):s.logger;let t=s?.level??"INFO",n=s?.prettify??false?[client.prettyTransport({level:t})]:[client.consoleTransport({level:t})];if(s?.handler){let a=s.handler;n.push({name:"customHandler",write:l=>a(l)});}let i=[];s&&X(s)&&i.push(Z(s));let o=client.createLogger({level:t,transports:n,middleware:i.length>0?i:void 0});return e&&Object.keys(e).length>0?o.child(e):o}var H=class{_concurrency;_running=0;_queue=[];constructor(e=1){this._concurrency=Math.max(1,e);}get concurrency(){return this._concurrency}get pending(){return this._queue.length}get running(){return this._running}get size(){return this._running+this._queue.length}setConcurrency(e){this._concurrency=Math.max(1,e),this._drain();}push(e){return new Promise((t,r)=>{this._queue.push({fn:e,resolve:t,reject:r}),this._drain();})}_drain(){for(;this._running<this._concurrency&&this._queue.length>0;){let e=this._queue.shift();this._running++,e?.fn().then(e.resolve).catch(e.reject).finally(()=>{this._running--,this._drain();});}}};var V={CONNECTING:0,OPEN:1,CLOSING:2,CLOSED:3};var h={CALL:2,CALLRESULT:3,CALLERROR:4},B=Symbol("NOREPLY");var $={debug:()=>{},info:()=>{},warn:()=>{},error:()=>{},child:()=>$};function f(s,e,t={}){switch(s){case "GenericError":return new g(e,t);case "RpcFrameworkError":return new L(e,t);case "MessageTypeNotSupported":return new y(e,t);case "NotImplemented":return new w(e,t);case "NotSupported":return new E(e,t);case "InternalError":return new _(e,t);case "ProtocolError":return new x(e,t);case "SecurityError":return new R(e,t);case "FormatViolation":return new M(e,t);case "FormationViolation":return new O(e,t);case "PropertyConstraintViolation":return new b(e,t);case "OccurrenceConstraintViolation":return new T(e,t);case "TypeConstraintViolation":return new k(e,t);default:return new g(e,t)}}var ee=["name","message","stack","code","rpcErrorCode","rpcErrorMessage","details"];function D(s){let e={};for(let t of ee){let r=s[t];if(r!==void 0){if(typeof r=="function"||typeof r=="symbol")continue;if(typeof r=="object"&&r!==null)try{JSON.stringify(r),e[t]=r;}catch{}else e[t]=r;}}return e.name||(e.name=s.name),e.message||(e.message=s.message),e}var {CONNECTING:S,OPEN:I,CLOSING:j,CLOSED:C}=V,q=class s extends N{static CONNECTING=S;static OPEN=I;static CLOSING=j;static CLOSED=C;_options;_state=C;_ws=null;_protocol;_identity;_handlers=new Map;_wildcardHandler=null;_pendingCalls=new Map;_pendingResponses=new Set;_callQueue;_closePromise=null;_reconnectAttempt=0;_reconnectTimer=null;_badMessageCount=0;_outboundBuffer=[];_logger;_middleware;constructor(e){if(super(),!e.identity)throw new Error("identity is required");this._identity=e.identity,this._options={reconnect:true,maxReconnects:1/0,backoffMin:1e3,backoffMax:3e4,callTimeoutMs:3e4,callConcurrency:1,maxBadMessages:1/0,respondWithDetailedErrors:false,...e},this._callQueue=new H(this._options.callConcurrency),this._middleware=new A;let t=W(this._options.logging,{component:"BrowserOCPPClient",identity:this._identity});this._logger=t||$;}get log(){return this._logger}get identity(){return this._identity}get protocol(){return this._protocol}get state(){return this._state}async connect(){if(this._state!==C)throw new Error(`Cannot connect: client is in state ${this._state}`);return this._state=S,this._reconnectAttempt=0,this._connectInternal()}async _connectInternal(){return new Promise((e,t)=>{let r=this._buildEndpoint();this._logger.debug?.("Connecting",{url:r}),this.emit("connecting",{url:r});let n;try{n=this._options.protocols?.length?new WebSocket(r,this._options.protocols):new WebSocket(r);}catch(c){this._state=C,t(c);return}this._ws=n;let i=c=>{if(l(),this._state=I,this._protocol=n.protocol||void 0,this._badMessageCount=0,n.protocol&&this._reconnectAttempt===0&&(this._options.protocols=[n.protocol]),this._attachWebsocket(n),this._outboundBuffer.length>0){let p=this._outboundBuffer;this._outboundBuffer=[];for(let d of p)this._ws?.send(d);}this._logger.info?.("Connected",{protocol:n.protocol||void 0}),this.emit("open",c),e();},o=c=>{l(),this._state=C,this._logger.error?.("Connection error"),this.emit("error",c),t(c);},a=()=>{l(),this._state===S&&(this._state=C,t(new Error("WebSocket closed during connection")));},l=()=>{n.removeEventListener("open",i),n.removeEventListener("error",o),n.removeEventListener("close",a);};n.addEventListener("open",i),n.addEventListener("error",o),n.addEventListener("close",a);})}async close(e={}){let{code:t=1e3,reason:r="",awaitPending:n=true,force:i=false}=e;return this._closePromise?this._closePromise:this._state===C?{code:1e3,reason:""}:(this._reconnectTimer&&(clearTimeout(this._reconnectTimer),this._reconnectTimer=null),this._closePromise=this._closeInternal(t,r,n,i),this._closePromise)}async _closeInternal(e,t,r,n){if(this._state=j,!n&&r){let i=Array.from(this._pendingCalls.values()).map(o=>new Promise(a=>{let l=o.resolve,c=o.reject;o.resolve=p=>{l(p),a();},o.reject=p=>{c(p),a();};}));i.length>0&&await Promise.allSettled(i);}return new Promise(i=>{if(!this._ws||this._ws.readyState===WebSocket.CLOSED){this._state=C,this._cleanup();let a={code:e,reason:t};this.emit("close",a),i(a);return}let o=a=>{this._ws?.removeEventListener("close",o),this._state=C,this._cleanup();let l={code:a.code,reason:a.reason};this.emit("close",l),i(l);};if(this._ws.addEventListener("close",o),n)this._ws.close();else {let a=e>=1e3&&e<=4999&&![1004,1005,1006].includes(e);this._ws.close(a?e:1e3,t);}})}handle(...e){if(e.length===1&&typeof e[0]=="function")this._wildcardHandler=e[0];else if(e.length===2&&typeof e[0]=="string"&&typeof e[1]=="function")this._handlers.set(e[0],e[1]);else if(e.length===3&&typeof e[0]=="string"&&typeof e[1]=="string"&&typeof e[2]=="function")this._handlers.set(`${e[0]}:${e[1]}`,e[2]);else throw new Error("Invalid arguments: provide (version, method, handler), (method, handler), or (wildcardHandler)")}removeHandler(e,t){e&&t?this._handlers.delete(`${e}:${t}`):e?this._handlers.delete(e):this._wildcardHandler=null;}removeAllHandlers(){this._handlers.clear(),this._wildcardHandler=null;}use(e){this._middleware.use(e);}async call(...e){let t,r,n;if(e.length>=3&&typeof e[0]=="string"&&typeof e[1]=="string"?(t=e[1],r=e[2]??{},n=e[3]??{}):(t=e[0],r=e[1]??{},n=e[2]??{}),this._state!==I)throw new Error(`Cannot call: client is in state ${this._state}`);return this._callQueue.push(()=>this._sendCall(t,r,n))}async _sendCall(e,t,r){let n=this._generateMessageId(),i=r.timeoutMs??this._options.callTimeoutMs,o={type:"outgoing_call",messageId:n,method:e,params:t,options:r},a;return await this._middleware.execute(o,async l=>{let c=l,p=[h.CALL,n,c.method,c.params],d=JSON.stringify(p);a=await new Promise((u,m)=>{let P=setTimeout(()=>{this._pendingCalls.delete(n),this._logger.warn?.("Call timed out",{messageId:n,method:c.method,timeoutMs:i}),m(new v(`Call to "${c.method}" timed out after ${i}ms`));},i),U={resolve:u,reject:m,timeoutHandle:P,method:c.method,sentAt:Date.now()};if(r.signal){if(r.signal.aborted){clearTimeout(P),m(r.signal.reason??new Error("Aborted"));return}let K=()=>{clearTimeout(P),this._pendingCalls.delete(n),m(r.signal?.reason??new Error("Aborted"));};r.signal.addEventListener("abort",K,{once:true}),U.abortHandler=K;}this._pendingCalls.set(n,U),this._ws?.send(d),this.emit("message",p),this.emit("call",p);});}),a}sendRaw(e){if(this._state===I&&this._ws)this._ws.send(e);else if(this._state===S)this._outboundBuffer.push(e);else throw new Error("Cannot send: client is not connected")}reconfigure(e){Object.assign(this._options,e),e.callConcurrency!==void 0&&this._callQueue.setConcurrency(e.callConcurrency);}_attachWebsocket(e){e.addEventListener("message",t=>this._onMessage(t.data)),e.addEventListener("close",t=>this._onClose(t.code,t.reason)),e.addEventListener("error",t=>this.emit("error",t));}_onMessage(e){let t=typeof e=="string"?e:String(e),r;try{if(r=JSON.parse(t),!Array.isArray(r))throw new Error("Message is not an array")}catch(i){this._onBadMessage(t,i);return}let n=r[0];switch(n){case h.CALL:this._handleIncomingCall(r);break;case h.CALLRESULT:this._handleCallResult(r);break;case h.CALLERROR:this._handleCallError(r);break;default:this._onBadMessage(JSON.stringify(r),new y(`Unknown message type: ${n}`));}}async _handleIncomingCall(e){let[,t,r,n]=e,i={type:"incoming_call",messageId:t,method:r,params:n,protocol:this._protocol};await this._middleware.execute(i,async o=>{let a=o,l=[h.CALL,a.messageId,a.method,a.params];if(this.emit("call",l),this._state===I)try{if(this._pendingResponses.has(a.messageId))throw f("RpcFrameworkError",`Already processing call with ID: ${a.messageId}`);let c=(this._protocol?this._handlers.get(`${this._protocol}:${a.method}`):void 0)??this._handlers.get(a.method);if(!c&&!this._wildcardHandler)throw f("NotImplemented",`No handler for method: ${a.method}`);this._pendingResponses.add(a.messageId);let p=new AbortController,d={messageId:a.messageId,method:a.method,protocol:this._protocol,params:a.params,signal:p.signal},u;if(c?u=await c(d):this._wildcardHandler&&(u=await this._wildcardHandler(a.method,d)),this._pendingResponses.delete(a.messageId),u===B)return;let m=[h.CALLRESULT,a.messageId,u];this._ws?.send(JSON.stringify(m)),this.emit("callResult",m);}catch(c){this._pendingResponses.delete(a.messageId),this._logger.error?.("Handler error",{messageId:a.messageId,method:a.method,error:c.message});let p=c instanceof g||c.rpcErrorCode?c:f("InternalError",c.message),d=this._options.respondWithDetailedErrors?D(c):{},u=[h.CALLERROR,a.messageId,p.rpcErrorCode,p.rpcErrorMessage||c.message||"",d];this._ws?.send(JSON.stringify(u)),this.emit("callError",u);}});}async _handleCallResult(e){let[,t,r]=e;if(!this._pendingCalls.has(t)){this._logger.warn?.("Received CallResult for unknown messageId",{messageId:t});return}let n=this._pendingCalls.get(t),i={type:"incoming_result",messageId:t,method:n.method,payload:r};await this._middleware.execute(i,async o=>{let a=o,l=[h.CALLRESULT,t,a.payload];this.emit("callResult",l),clearTimeout(n.timeoutHandle),this._pendingCalls.delete(t),n.resolve(a.payload);});}async _handleCallError(e){let[,t,r,n,i]=e;if(!this._pendingCalls.has(t)){this._logger.warn?.("Received CallError for unknown messageId",{messageId:t});return}let o=this._pendingCalls.get(t),a=f(r,n,i),l={type:"incoming_error",messageId:t,method:o.method,error:a};await this._middleware.execute(l,async c=>{let d=c.error,u=[h.CALLERROR,t,d.rpcErrorCode,d.message,d.rpcErrorDetails??{}];this.emit("callError",u),clearTimeout(o.timeoutHandle),this._pendingCalls.delete(t),o.reject(d);});}_onBadMessage(e,t){this._badMessageCount++,this._logger?.warn?.("Bad message",{error:t.message,count:this._badMessageCount}),this.emit("badMessage",{message:e,error:t});let r=e.match(/^\s*\[\s*2\s*,\s*"([^"]+)"/);if(r?.[1]&&this._ws){let n=[h.CALLERROR,r[1],"FormatViolation",t.message||"Invalid message format",{}];this._ws.send(JSON.stringify(n)),this.emit("callError",n);}this._badMessageCount>=this._options.maxBadMessages&&this.close({code:1002,reason:"Too many bad messages"}).catch(()=>{});}_rejectPendingCalls(e){for(let[,t]of this._pendingCalls)clearTimeout(t.timeoutHandle),t.reject(new Error(e));this._pendingCalls.clear(),this._pendingResponses.clear();}_onClose(e,t){this._rejectPendingCalls(`Connection closed (${e}: ${t})`),this._state!==j?(this._logger?.info?.("Disconnected",{code:e,reason:t}),this.emit("disconnect",{code:e,reason:t}),this._options.reconnect&&this._reconnectAttempt<this._options.maxReconnects?this._scheduleReconnect():(this._state=C,this.emit("close",{code:e,reason:t}))):this._state=C;}static _INTOLERABLE_ERRORS=new Set(["Maximum redirects exceeded","Server sent no subprotocol","Server sent an invalid subprotocol","Server sent a subprotocol but none was requested","Invalid Sec-WebSocket-Accept header"]);_scheduleReconnect(){this._reconnectAttempt++,this._state=S;let e=this._options.backoffMin,t=this._options.backoffMax,r=Math.min(t,e*2**(this._reconnectAttempt-1)*(.5+Math.random()*.5));this._logger?.warn?.("Reconnecting",{attempt:this._reconnectAttempt,delayMs:Math.round(r)}),this.emit("reconnect",{attempt:this._reconnectAttempt,delay:r}),this._reconnectTimer=setTimeout(async()=>{this._reconnectTimer=null;try{await this._connectInternal();}catch(n){let i=n instanceof Error?n.message:"";if(s._INTOLERABLE_ERRORS.has(i)){this._logger?.error?.("Intolerable error \u2014 stopping reconnection",{error:i}),this._state=C,this.emit("close",{code:1001,reason:i});return}this._reconnectAttempt<this._options.maxReconnects&&this._options.reconnect?this._scheduleReconnect():(this._state=C,this.emit("close",{code:1001,reason:"Max reconnection attempts exhausted"}));}},r);}_buildEndpoint(){let e=this._options.endpoint;if(e.endsWith("/")||(e+="/"),e+=encodeURIComponent(this._identity),this._options.query){let t=new URLSearchParams(this._options.query);e+=(e.includes("?")?"&":"?")+t.toString();}return e}_cleanup(){this._closePromise=null,this._ws=null;}_generateMessageId(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return (e==="x"?t:t&3|8).toString(16)})}};exports.BrowserOCPPClient=q;exports.ConnectionState=V;exports.MessageType=h;exports.MiddlewareStack=A;exports.NOREPLY=B;exports.RPCFormatViolationError=M;exports.RPCFormationViolationError=O;exports.RPCFrameworkError=L;exports.RPCGenericError=g;exports.RPCInternalError=_;exports.RPCMessageTypeNotSupportedError=y;exports.RPCNotImplementedError=w;exports.RPCNotSupportedError=E;exports.RPCOccurrenceConstraintViolationError=T;exports.RPCPropertyConstraintViolationError=b;exports.RPCProtocolError=x;exports.RPCSecurityError=R;exports.RPCTypeConstraintViolationError=k;exports.TimeoutError=v;exports.createLoggingMiddleware=G;exports.createRPCError=f;exports.defineRpcMiddleware=F;exports.getErrorPlainObject=D;
1
+ 'use strict';var client=require('voltlog-io/client');function G(s){return s}function Y(s,e,t={}){let r=typeof t=="object"?t:{},{exchangeLog:n=false,prettify:i=false}=r;return async(o,a)=>{let l=Date.now(),d=o.method,p=n?"info":"debug";switch(o.type){case "incoming_call":n&&i?s[p]?.(`\u26A1 ${e} \u2190 ${d} [IN]`,{messageId:o.messageId,method:o.method,protocol:o.protocol,payload:o.params,direction:"IN"}):s[p]?.("CALL \u2190",{messageId:o.messageId,method:o.method,protocol:o.protocol,payload:o.params,direction:"IN"});break;case "outgoing_call":n&&i?s[p]?.(`\u26A1 ${e} \u2192 ${d} [OUT]`,{method:o.method,params:o.params,direction:"OUT"}):s[p]?.("CALL \u2192",{method:o.method,params:o.params,direction:"OUT"});break}try{let c=await a(),u=Date.now()-l;switch(o.type){case "incoming_call":c!=null&&(n&&i?s[p]?.(`\u2705 ${e} \u2192 ${d} [RES]`,{messageId:o.messageId,method:o.method,durationMs:u,params:c,direction:"OUT"}):s[p]?.("CALLRESULT \u2192",{messageId:o.messageId,method:o.method,durationMs:u,params:c,direction:"OUT"}));break;case "outgoing_call":n&&i?s[p]?.(`\u2705 ${e} \u2190 ${d} [RES]`,{messageId:o.messageId,method:o.method,durationMs:u,payload:c,direction:"IN"}):s[p]?.("CALLRESULT \u2190",{messageId:o.messageId,method:o.method,durationMs:u,payload:c,direction:"IN"});break}return c}catch(c){let u=c.message,g=Date.now()-l;throw o.type==="incoming_call"?n&&i?s.error?.(`\u{1F6A8} ${e} \u2192 ${d} [ERR]`,{messageId:o.messageId,method:o.method,durationMs:g,error:u,direction:"OUT"}):s.error?.("CALLERROR \u2192",{messageId:o.messageId,method:o.method,durationMs:g,error:u,direction:"OUT"}):o.type==="outgoing_call"&&(n&&i?s.warn?.(`\u{1F6A8} ${e} \u2190 ${d} [ERR]`,{messageId:o.messageId,method:o.method,durationMs:g,error:u,direction:"IN"}):s.warn?.("CALLERROR \u2190",{messageId:o.messageId,method:o.method,durationMs:g,error:u,direction:"IN"})),c}}}var N=class{_stack=[];use(e){this._stack.push(e);}async execute(e,t){let r=-1,n=async i=>{if(i<=r)throw new Error("next() called multiple times");r=i;let o=this._stack[i];if(i===this._stack.length)return t(e);if(o)return o(e,()=>n(i+1))};return n(0)}};var H=class{_listeners=new Map;on(e,t){let r=this._listeners.get(e);return r?r.push(t):this._listeners.set(e,[t]),this}once(e,t){let r=(...n)=>{this.off(e,r),t(...n);};return r.__wrapped=t,this.on(e,r)}off(e,t){let r=this._listeners.get(e);if(!r)return this;let n=r.findIndex(i=>i===t||i.__wrapped===t);return n!==-1&&r.splice(n,1),r.length===0&&this._listeners.delete(e),this}emit(e,...t){let r=this._listeners.get(e);if(!r||r.length===0)return false;for(let n of [...r])n(...t);return true}addListener(e,t){return this.on(e,t)}removeListener(e,t){return this.off(e,t)}removeAllListeners(e){return e?this._listeners.delete(e):this._listeners.clear(),this}listenerCount(e){return this._listeners.get(e)?.length??0}};var w=class extends Error{constructor(e="Operation timed out"){super(e),this.name="TimeoutError";}},m=class extends Error{rpcErrorCode="GenericError";rpcErrorMessage="";details;constructor(e,t={}){super(e),this.name="RPCGenericError",this.details=t;}},E=class extends m{rpcErrorCode="NotImplemented";rpcErrorMessage="Requested method is not known";constructor(e,t={}){super(e,t),this.name="RPCNotImplementedError";}},_=class extends m{rpcErrorCode="NotSupported";rpcErrorMessage="Requested method is recognised but not supported";constructor(e,t={}){super(e,t),this.name="RPCNotSupportedError";}},x=class extends m{rpcErrorCode="InternalError";rpcErrorMessage="An internal error occurred and the receiver was not able to process the requested action successfully";constructor(e,t={}){super(e,t),this.name="RPCInternalError";}},O=class extends m{rpcErrorCode="ProtocolError";rpcErrorMessage="Payload for action is incomplete";constructor(e,t={}){super(e,t),this.name="RPCProtocolError";}},R=class extends m{rpcErrorCode="SecurityError";rpcErrorMessage="During the processing of action a security issue occurred preventing receiver from completing the action successfully";constructor(e,t={}){super(e,t),this.name="RPCSecurityError";}},M=class extends m{rpcErrorCode="FormationViolation";rpcErrorMessage="Payload for action is syntactically incorrect or not conform the PDU structure for action";constructor(e,t={}){super(e,t),this.name="RPCFormationViolationError";}},b=class extends m{rpcErrorCode="FormatViolation";rpcErrorMessage="Payload is syntactically correct but at least one field contains an invalid value";constructor(e,t={}){super(e,t),this.name="RPCFormatViolationError";}},T=class extends m{rpcErrorCode="PropertyConstraintViolation";rpcErrorMessage="Payload is syntactically correct but at least one of the fields violates data type constraints";constructor(e,t={}){super(e,t),this.name="RPCPropertyConstraintViolationError";}},k=class extends m{rpcErrorCode="OccurrenceConstraintViolation";rpcErrorMessage="Payload for action is syntactically correct but at least one of the fields violates occurrence constraints";constructor(e,t={}){super(e,t),this.name="RPCOccurrenceConstraintViolationError";}},L=class extends m{rpcErrorCode="TypeConstraintViolation";rpcErrorMessage="Payload for action is syntactically correct but at least one of the fields violates type constraints";constructor(e,t={}){super(e,t),this.name="RPCTypeConstraintViolationError";}},f=class extends m{rpcErrorCode="MessageTypeNotSupported";rpcErrorMessage="A message with a Message Type Number received that is not supported by this implementation";constructor(e,t={}){super(e,t),this.name="RPCMessageTypeNotSupportedError";}},S=class extends m{rpcErrorCode="RpcFrameworkError";rpcErrorMessage="Content of the call is not a valid RPC request";constructor(e,t={}){super(e,t),this.name="RPCFrameworkError";}};function X(s){return s.showMetadata===false||s.showSourceMeta===false||s.prettifySource===true||s.prettifyMetadata===true}function ee(s){let e=s.showMetadata??true,t=s.showSourceMeta??true,r=s.prettifySource??false,n=s.prettifyMetadata??false,i="\x1B[2;37m",o="\x1B[0m",a="\x1B[36m";return (l,d)=>{if(!t)l.context=void 0;else if(r&&l.context){let c=[];l.context.component&&c.push(String(l.context.component)),l.context.identity&&c.push(String(l.context.identity)),c.length>0&&(l.message=`${i}[${c.join("/")}]${o} ${l.message}`,l.context=void 0);}let p=l.meta;if(!e)l.meta={};else if(n&&p&&Object.keys(p).length>0){let c=Object.entries(p).filter(([,u])=>u!=null).map(([u,g])=>{let y=typeof g=="object"?JSON.stringify(g):String(g);return typeof g=="string"?y=`${i}${y}${o}`:y=`${i}${y}${o}`,`${a}${u}${o}=${y}`}).join(" ");c&&(l.message=`${l.message} ${c}`),l.meta={};}d(l);}}function W(s,e){if(s===false||s?.enabled===false)return null;if(s?.logger)return e&&s.logger.child?s.logger.child(e):s.logger;let t=s?.level??"INFO",n=s?.prettify??false?[client.prettyTransport({level:t})]:[client.consoleTransport({level:t})];if(s?.handler){let a=s.handler;n.push({name:"customHandler",write:l=>a(l)});}let i=[];s&&X(s)&&i.push(ee(s));let o=client.createLogger({level:t,transports:n,middleware:i.length>0?i:void 0});return e&&Object.keys(e).length>0?o.child(e):o}var j=class{_concurrency;_running=0;_queue=[];constructor(e=1){this._concurrency=Math.max(1,e);}get concurrency(){return this._concurrency}get pending(){return this._queue.length}get running(){return this._running}get size(){return this._running+this._queue.length}setConcurrency(e){this._concurrency=Math.max(1,e),this._drain();}push(e){return new Promise((t,r)=>{this._queue.push({fn:e,resolve:t,reject:r}),this._drain();})}_drain(){for(;this._running<this._concurrency&&this._queue.length>0;){let e=this._queue.shift();this._running++,e?.fn().then(e.resolve).catch(e.reject).finally(()=>{this._running--,this._drain();});}}};var B={CONNECTING:0,OPEN:1,CLOSING:2,CLOSED:3};var C={CALL:2,CALLRESULT:3,CALLERROR:4},V=Symbol("NOREPLY");var D={debug:()=>{},info:()=>{},warn:()=>{},error:()=>{},child:()=>D};function v(s,e,t={}){switch(s){case "GenericError":return new m(e,t);case "RpcFrameworkError":return new S(e,t);case "MessageTypeNotSupported":return new f(e,t);case "NotImplemented":return new E(e,t);case "NotSupported":return new _(e,t);case "InternalError":return new x(e,t);case "ProtocolError":return new O(e,t);case "SecurityError":return new R(e,t);case "FormatViolation":return new b(e,t);case "FormationViolation":return new M(e,t);case "PropertyConstraintViolation":return new T(e,t);case "OccurrenceConstraintViolation":return new k(e,t);case "TypeConstraintViolation":return new L(e,t);default:return new m(e,t)}}var te=["name","message","stack","code","rpcErrorCode","rpcErrorMessage","details"];function $(s){let e={};for(let t of te){let r=s[t];if(r!==void 0){if(typeof r=="function"||typeof r=="symbol")continue;if(typeof r=="object"&&r!==null)try{JSON.stringify(r),e[t]=r;}catch{}else e[t]=r;}}return e.name||(e.name=s.name),e.message||(e.message=s.message),e}var {CONNECTING:A,OPEN:I,CLOSING:U,CLOSED:h}=B,q=class s extends H{static CONNECTING=A;static OPEN=I;static CLOSING=U;static CLOSED=h;_options;_state=h;_ws=null;_protocol;_identity;_handlers=new Map;_wildcardHandler=null;_pendingCalls=new Map;_pendingResponses=new Set;_callQueue;_closePromise=null;_reconnectAttempt=0;_reconnectTimer=null;_badMessageCount=0;_outboundBuffer=[];_logger;_middleware;constructor(e){if(super(),!e.identity)throw new Error("identity is required");this._identity=e.identity,this._options={reconnect:true,maxReconnects:1/0,backoffMin:1e3,backoffMax:3e4,callTimeoutMs:3e4,callConcurrency:1,maxBadMessages:1/0,respondWithDetailedErrors:false,...e},this._callQueue=new j(this._options.callConcurrency),this._middleware=new N;let t=W(this._options.logging,{component:"BrowserOCPPClient",identity:this._identity});this._logger=t||D;}get log(){return this._logger}get identity(){return this._identity}get protocol(){return this._protocol}get state(){return this._state}async connect(){if(this._state!==h)throw new Error(`Cannot connect: client is in state ${this._state}`);return this._state=A,this._reconnectAttempt=0,this._connectInternal()}async _connectInternal(){return new Promise((e,t)=>{let r=this._buildEndpoint();this._logger.debug?.("Connecting",{url:r}),this.emit("connecting",{url:r});let n;try{n=this._options.protocols?.length?new WebSocket(r,this._options.protocols):new WebSocket(r);}catch(d){this._state=h,t(d);return}this._ws=n;let i=d=>{if(l(),this._state=I,this._protocol=n.protocol||void 0,this._badMessageCount=0,n.protocol&&this._reconnectAttempt===0&&(this._options.protocols=[n.protocol]),this._attachWebsocket(n),this._outboundBuffer.length>0){let p=this._outboundBuffer;this._outboundBuffer=[];for(let c of p)this._ws?.send(c);}this._logger.info?.("Connected",{protocol:n.protocol||void 0}),this.emit("open",d),e();},o=d=>{l(),this._state=h,this._logger.error?.("Connection error"),this.emit("error",d),t(d);},a=()=>{l(),this._state===A&&(this._state=h,t(new Error("WebSocket closed during connection")));},l=()=>{n.removeEventListener("open",i),n.removeEventListener("error",o),n.removeEventListener("close",a);};n.addEventListener("open",i),n.addEventListener("error",o),n.addEventListener("close",a);})}async close(e={}){let{code:t=1e3,reason:r="",awaitPending:n=true,force:i=false}=e;return this._closePromise?this._closePromise:this._state===h?{code:1e3,reason:""}:(this._reconnectTimer&&(clearTimeout(this._reconnectTimer),this._reconnectTimer=null),this._closePromise=this._closeInternal(t,r,n,i),this._closePromise)}async _closeInternal(e,t,r,n){if(this._state=U,!n&&r){let i=Array.from(this._pendingCalls.values()).map(o=>new Promise(a=>{let l=o.resolve,d=o.reject;o.resolve=p=>{l(p),a();},o.reject=p=>{d(p),a();};}));i.length>0&&await Promise.allSettled(i);}return new Promise(i=>{if(!this._ws||this._ws.readyState===WebSocket.CLOSED){this._state=h,this._cleanup();let a={code:e,reason:t};this.emit("close",a),i(a);return}let o=a=>{this._ws?.removeEventListener("close",o),this._state=h,this._cleanup();let l={code:a.code,reason:a.reason};this.emit("close",l),i(l);};if(this._ws.addEventListener("close",o),n)this._ws.close();else {let a=e>=1e3&&e<=4999&&![1004,1005,1006].includes(e);this._ws.close(a?e:1e3,t);}})}handle(...e){if(e.length===1&&typeof e[0]=="function")this._wildcardHandler=e[0];else if(e.length===2&&typeof e[0]=="string"&&typeof e[1]=="function")this._handlers.set(e[0],e[1]);else if(e.length===3&&typeof e[0]=="string"&&typeof e[1]=="string"&&typeof e[2]=="function")this._handlers.set(`${e[0]}:${e[1]}`,e[2]);else throw new Error("Invalid arguments: provide (version, method, handler), (method, handler), or (wildcardHandler)")}removeHandler(e,t){e&&t?this._handlers.delete(`${e}:${t}`):e?this._handlers.delete(e):this._wildcardHandler=null;}removeAllHandlers(){this._handlers.clear(),this._wildcardHandler=null;}use(e){this._middleware.use(e);}async call(...e){let t,r,n;if(e.length>=3&&typeof e[0]=="string"&&typeof e[1]=="string"?(t=e[1],r=e[2]??{},n=e[3]??{}):(t=e[0],r=e[1]??{},n=e[2]??{}),this._state!==I)throw new Error(`Cannot call: client is in state ${this._state}`);return this._callQueue.push(()=>this._sendCall(t,r,n))}async _sendCall(e,t,r){let n=this._generateMessageId(),i=r.timeoutMs??this._options.callTimeoutMs,o={type:"outgoing_call",messageId:n,method:e,params:t,options:r},a;return await this._middleware.execute(o,async l=>{let d=l,p=[C.CALL,n,d.method,d.params],c=JSON.stringify(p);a=await new Promise((u,g)=>{let y=setTimeout(()=>{this._pendingCalls.delete(n),this._logger.warn?.("Call timed out",{messageId:n,method:d.method,timeoutMs:i}),g(new w(`Call to "${d.method}" timed out after ${i}ms`));},i),F={resolve:u,reject:g,timeoutHandle:y,method:d.method,sentAt:Date.now()};if(r.signal){if(r.signal.aborted){clearTimeout(y),g(r.signal.reason??new Error("Aborted"));return}let K=()=>{clearTimeout(y),this._pendingCalls.delete(n),g(r.signal?.reason??new Error("Aborted"));};r.signal.addEventListener("abort",K,{once:true}),F.abortHandler=K;}this._pendingCalls.set(n,F),this._ws?.send(c),this.emit("message",p),this.emit("call",p);});}),a}sendRaw(e){if(this._state===I&&this._ws)this._ws.send(e);else if(this._state===A)this._outboundBuffer.push(e);else throw new Error("Cannot send: client is not connected")}reconfigure(e){Object.assign(this._options,e),e.callConcurrency!==void 0&&this._callQueue.setConcurrency(e.callConcurrency);}_attachWebsocket(e){e.addEventListener("message",t=>this._onMessage(t.data)),e.addEventListener("close",t=>this._onClose(t.code,t.reason)),e.addEventListener("error",t=>this.emit("error",t));}_onMessage(e){let t=typeof e=="string"?e:String(e),r;try{if(r=JSON.parse(t),!Array.isArray(r))throw new Error("Message is not an array")}catch(i){this._onBadMessage(t,i);return}let n=r[0];switch(n){case C.CALL:this._handleIncomingCall(r);break;case C.CALLRESULT:this._handleCallResult(r);break;case C.CALLERROR:this._handleCallError(r);break;default:this._onBadMessage(JSON.stringify(r),new f(`Unknown message type: ${n}`));}}async _handleIncomingCall(e){let[,t,r,n]=e,i={type:"incoming_call",messageId:t,method:r,params:n,protocol:this._protocol};await this._middleware.execute(i,async o=>{let a=o,l=[C.CALL,a.messageId,a.method,a.params];if(this.emit("call",l),this._state===I)try{if(this._pendingResponses.has(a.messageId))throw v("RpcFrameworkError",`Already processing call with ID: ${a.messageId}`);let d=(this._protocol?this._handlers.get(`${this._protocol}:${a.method}`):void 0)??this._handlers.get(a.method);if(!d&&!this._wildcardHandler)throw v("NotImplemented",`No handler for method: ${a.method}`);this._pendingResponses.add(a.messageId);let p=new AbortController,c={messageId:a.messageId,method:a.method,protocol:this._protocol,params:a.params,signal:p.signal},u;if(d?u=await d(c):this._wildcardHandler&&(u=await this._wildcardHandler(a.method,c)),this._pendingResponses.delete(a.messageId),u===V)return;let g=[C.CALLRESULT,a.messageId,u];this._ws?.send(JSON.stringify(g)),this.emit("callResult",g);}catch(d){this._pendingResponses.delete(a.messageId),this._logger.error?.("Handler error",{messageId:a.messageId,method:a.method,error:d.message});let p=d instanceof m||d.rpcErrorCode?d:v("InternalError",d.message),c=this._options.respondWithDetailedErrors?$(d):{},u=[C.CALLERROR,a.messageId,p.rpcErrorCode,p.rpcErrorMessage||d.message||"",c];this._ws?.send(JSON.stringify(u)),this.emit("callError",u);}});}async _handleCallResult(e){let[,t,r]=e;if(!this._pendingCalls.has(t)){this._logger.warn?.("Received CallResult for unknown messageId",{messageId:t});return}let n=this._pendingCalls.get(t),i={type:"incoming_result",messageId:t,method:n.method,payload:r};await this._middleware.execute(i,async o=>{let a=o,l=[C.CALLRESULT,t,a.payload];this.emit("callResult",l),clearTimeout(n.timeoutHandle),this._pendingCalls.delete(t),n.resolve(a.payload);});}async _handleCallError(e){let[,t,r,n,i]=e;if(!this._pendingCalls.has(t)){this._logger.warn?.("Received CallError for unknown messageId",{messageId:t});return}let o=this._pendingCalls.get(t),a=v(r,n,i),l={type:"incoming_error",messageId:t,method:o.method,error:a};await this._middleware.execute(l,async d=>{let c=d.error,u=[C.CALLERROR,t,c.rpcErrorCode,c.message,c.rpcErrorDetails??{}];this.emit("callError",u),clearTimeout(o.timeoutHandle),this._pendingCalls.delete(t),o.reject(c);});}_onBadMessage(e,t){this._badMessageCount++,this._logger?.warn?.("Bad message",{error:t.message,count:this._badMessageCount}),this.emit("badMessage",{message:e,error:t});let r=e.match(/^\s*\[\s*2\s*,\s*"([^"]+)"/);if(r?.[1]&&this._ws){let n=[C.CALLERROR,r[1],"FormatViolation",t.message||"Invalid message format",{}];this._ws.send(JSON.stringify(n)),this.emit("callError",n);}this._badMessageCount>=this._options.maxBadMessages&&this.close({code:1002,reason:"Too many bad messages"}).catch(()=>{});}_rejectPendingCalls(e){for(let[,t]of this._pendingCalls)clearTimeout(t.timeoutHandle),t.reject(new Error(e));this._pendingCalls.clear(),this._pendingResponses.clear();}_onClose(e,t){this._rejectPendingCalls(`Connection closed (${e}: ${t})`),this._state!==U?(this._logger?.info?.("Disconnected",{code:e,reason:t}),this.emit("disconnect",{code:e,reason:t}),this._options.reconnect&&this._reconnectAttempt<this._options.maxReconnects?this._scheduleReconnect():(this._state=h,this.emit("close",{code:e,reason:t}))):this._state=h;}static _INTOLERABLE_ERRORS=new Set(["Maximum redirects exceeded","Server sent no subprotocol","Server sent an invalid subprotocol","Server sent a subprotocol but none was requested","Invalid Sec-WebSocket-Accept header"]);_scheduleReconnect(){this._reconnectAttempt++,this._state=A;let e=this._options.backoffMin,t=this._options.backoffMax,r=Math.min(t,e*2**(this._reconnectAttempt-1)*(.5+Math.random()*.5));this._logger?.warn?.("Reconnecting",{attempt:this._reconnectAttempt,delayMs:Math.round(r)}),this.emit("reconnect",{attempt:this._reconnectAttempt,delay:r}),this._reconnectTimer=setTimeout(async()=>{this._reconnectTimer=null;try{await this._connectInternal();}catch(n){let i=n instanceof Error?n.message:"";if(s._INTOLERABLE_ERRORS.has(i)){this._logger?.error?.("Intolerable error \u2014 stopping reconnection",{error:i}),this._state=h,this.emit("close",{code:1001,reason:i});return}this._reconnectAttempt<this._options.maxReconnects&&this._options.reconnect?this._scheduleReconnect():(this._state=h,this.emit("close",{code:1001,reason:"Max reconnection attempts exhausted"}));}},r);}_buildEndpoint(){let e=this._options.endpoint;if(e.endsWith("/")||(e+="/"),e+=encodeURIComponent(this._identity),this._options.query){let t=new URLSearchParams(this._options.query);e+=(e.includes("?")?"&":"?")+t.toString();}return e}_cleanup(){this._closePromise=null,this._ws=null;}_generateMessageId(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return (e==="x"?t:t&3|8).toString(16)})}};exports.BrowserOCPPClient=q;exports.ConnectionState=B;exports.MessageType=C;exports.MiddlewareStack=N;exports.NOREPLY=V;exports.RPCFormatViolationError=b;exports.RPCFormationViolationError=M;exports.RPCFrameworkError=S;exports.RPCGenericError=m;exports.RPCInternalError=x;exports.RPCMessageTypeNotSupportedError=f;exports.RPCNotImplementedError=E;exports.RPCNotSupportedError=_;exports.RPCOccurrenceConstraintViolationError=k;exports.RPCPropertyConstraintViolationError=T;exports.RPCProtocolError=O;exports.RPCSecurityError=R;exports.RPCTypeConstraintViolationError=L;exports.TimeoutError=w;exports.createLoggingMiddleware=Y;exports.createRPCError=v;exports.defineRpcMiddleware=G;exports.getErrorPlainObject=$;
package/dist/browser.mjs CHANGED
@@ -1 +1 @@
1
- import {prettyTransport,consoleTransport,createLogger}from'voltlog-io/client';function F(s){return s}function G(s,e,t={}){let r=typeof t=="object"?t:{},{exchangeLog:n=false,prettify:i=false}=r;return async(o,a)=>{let l=Date.now(),c=o.method,p=n?"info":"debug";switch(o.type){case "incoming_call":n&&i?s[p]?.(`\u26A1 ${e} \u2190 ${c} [IN]`,{messageId:o.messageId,method:o.method,protocol:o.protocol,payload:o.params,direction:"IN"}):s[p]?.("CALL \u2190",{messageId:o.messageId,method:o.method,protocol:o.protocol,payload:o.params,direction:"IN"});break;case "outgoing_call":n&&i?s[p]?.(`\u26A1 ${e} \u2192 ${c} [OUT]`,{method:o.method,params:o.params,direction:"OUT"}):s[p]?.("CALL \u2192",{method:o.method,params:o.params,direction:"OUT"});break}try{let d=await a(),u=Date.now()-l;switch(o.type){case "incoming_call":d!=null&&(n&&i?s[p]?.(`\u2705 ${e} \u2192 ${c} [RES]`,{messageId:o.messageId,method:o.method,durationMs:u,params:d,direction:"OUT"}):s[p]?.("CALLRESULT \u2192",{messageId:o.messageId,method:o.method,durationMs:u,params:d,direction:"OUT"}));break;case "outgoing_call":n&&i?s[p]?.(`\u2705 ${e} \u2190 ${c} [RES]`,{messageId:o.messageId,method:o.method,durationMs:u,payload:d,direction:"IN"}):s[p]?.("CALLRESULT \u2190",{messageId:o.messageId,method:o.method,durationMs:u,payload:d,direction:"IN"});break}return d}catch(d){let u=d.message,m=Date.now()-l;throw o.type==="incoming_call"?n&&i?s.error?.(`\u{1F6A8} ${e} \u2192 ${c} [ERR]`,{messageId:o.messageId,method:o.method,durationMs:m,error:u,direction:"OUT"}):s.error?.("CALLERROR \u2192",{messageId:o.messageId,method:o.method,durationMs:m,error:u,direction:"OUT"}):o.type==="outgoing_call"&&(n&&i?s.warn?.(`\u{1F6A8} ${e} \u2190 ${c} [ERR]`,{messageId:o.messageId,method:o.method,durationMs:m,error:u,direction:"IN"}):s.warn?.("CALLERROR \u2190",{messageId:o.messageId,method:o.method,durationMs:m,error:u,direction:"IN"})),d}}}var A=class{_stack=[];use(e){this._stack.push(e);}async execute(e,t){let r=-1,n=async i=>{if(i<=r)throw new Error("next() called multiple times");r=i;let o=this._stack[i];if(i===this._stack.length)return t(e);if(o)return o(e,()=>n(i+1))};return n(0)}};var N=class{_listeners=new Map;on(e,t){let r=this._listeners.get(e);return r?r.push(t):this._listeners.set(e,[t]),this}once(e,t){let r=(...n)=>{this.off(e,r),t(...n);};return r.__wrapped=t,this.on(e,r)}off(e,t){let r=this._listeners.get(e);if(!r)return this;let n=r.findIndex(i=>i===t||i.__wrapped===t);return n!==-1&&r.splice(n,1),r.length===0&&this._listeners.delete(e),this}emit(e,...t){let r=this._listeners.get(e);if(!r||r.length===0)return false;for(let n of [...r])n(...t);return true}addListener(e,t){return this.on(e,t)}removeListener(e,t){return this.off(e,t)}removeAllListeners(e){return e?this._listeners.delete(e):this._listeners.clear(),this}listenerCount(e){return this._listeners.get(e)?.length??0}};var v=class extends Error{constructor(e="Operation timed out"){super(e),this.name="TimeoutError";}},g=class extends Error{rpcErrorCode="GenericError";rpcErrorMessage="";details;constructor(e,t={}){super(e),this.name="RPCGenericError",this.details=t;}},w=class extends g{rpcErrorCode="NotImplemented";rpcErrorMessage="Requested method is not known";constructor(e,t={}){super(e,t),this.name="RPCNotImplementedError";}},E=class extends g{rpcErrorCode="NotSupported";rpcErrorMessage="Requested method is recognised but not supported";constructor(e,t={}){super(e,t),this.name="RPCNotSupportedError";}},_=class extends g{rpcErrorCode="InternalError";rpcErrorMessage="An internal error occurred and the receiver was not able to process the requested action successfully";constructor(e,t={}){super(e,t),this.name="RPCInternalError";}},x=class extends g{rpcErrorCode="ProtocolError";rpcErrorMessage="Payload for action is incomplete";constructor(e,t={}){super(e,t),this.name="RPCProtocolError";}},R=class extends g{rpcErrorCode="SecurityError";rpcErrorMessage="During the processing of action a security issue occurred preventing receiver from completing the action successfully";constructor(e,t={}){super(e,t),this.name="RPCSecurityError";}},O=class extends g{rpcErrorCode="FormationViolation";rpcErrorMessage="Payload for action is syntactically incorrect or not conform the PDU structure for action";constructor(e,t={}){super(e,t),this.name="RPCFormationViolationError";}},M=class extends g{rpcErrorCode="FormatViolation";rpcErrorMessage="Payload is syntactically correct but at least one field contains an invalid value";constructor(e,t={}){super(e,t),this.name="RPCFormatViolationError";}},b=class extends g{rpcErrorCode="PropertyConstraintViolation";rpcErrorMessage="Payload is syntactically correct but at least one of the fields violates data type constraints";constructor(e,t={}){super(e,t),this.name="RPCPropertyConstraintViolationError";}},T=class extends g{rpcErrorCode="OccurrenceConstraintViolation";rpcErrorMessage="Payload for action is syntactically correct but at least one of the fields violates occurrence constraints";constructor(e,t={}){super(e,t),this.name="RPCOccurrenceConstraintViolationError";}},k=class extends g{rpcErrorCode="TypeConstraintViolation";rpcErrorMessage="Payload for action is syntactically correct but at least one of the fields violates type constraints";constructor(e,t={}){super(e,t),this.name="RPCTypeConstraintViolationError";}},y=class extends g{rpcErrorCode="MessageTypeNotSupported";rpcErrorMessage="A message with a Message Type Number received that is not supported by this implementation";constructor(e,t={}){super(e,t),this.name="RPCMessageTypeNotSupportedError";}},L=class extends g{rpcErrorCode="RpcFrameworkError";rpcErrorMessage="Content of the call is not a valid RPC request";constructor(e,t={}){super(e,t),this.name="RPCFrameworkError";}};function X(s){return s.showMetadata===false||s.showSourceMeta===false||s.prettifySource===true||s.prettifyMetadata===true}function Z(s){let e=s.showMetadata??true,t=s.showSourceMeta??true,r=s.prettifySource??false,n=s.prettifyMetadata??false,i="\x1B[2;37m",o="\x1B[0m",a="\x1B[36m";return (l,c)=>{if(!t)l.context=void 0;else if(r&&l.context){let d=[];l.context.component&&d.push(String(l.context.component)),l.context.identity&&d.push(String(l.context.identity)),d.length>0&&(l.message=`${i}[${d.join("/")}]${o} ${l.message}`,l.context=void 0);}let p=l.meta;if(!e)l.meta={};else if(n&&p&&Object.keys(p).length>0){let d=Object.entries(p).filter(([,u])=>u!=null).map(([u,m])=>{let P=typeof m=="object"?JSON.stringify(m):String(m);return typeof m=="string"?P=`${i}${P}${o}`:P=`${i}${P}${o}`,`${a}${u}${o}=${P}`}).join(" ");d&&(l.message=`${l.message} ${d}`),l.meta={};}c(l);}}function W(s,e){if(s===false||s?.enabled===false)return null;if(s?.logger)return e&&s.logger.child?s.logger.child(e):s.logger;let t=s?.level??"INFO",n=s?.prettify??false?[prettyTransport({level:t})]:[consoleTransport({level:t})];if(s?.handler){let a=s.handler;n.push({name:"customHandler",write:l=>a(l)});}let i=[];s&&X(s)&&i.push(Z(s));let o=createLogger({level:t,transports:n,middleware:i.length>0?i:void 0});return e&&Object.keys(e).length>0?o.child(e):o}var H=class{_concurrency;_running=0;_queue=[];constructor(e=1){this._concurrency=Math.max(1,e);}get concurrency(){return this._concurrency}get pending(){return this._queue.length}get running(){return this._running}get size(){return this._running+this._queue.length}setConcurrency(e){this._concurrency=Math.max(1,e),this._drain();}push(e){return new Promise((t,r)=>{this._queue.push({fn:e,resolve:t,reject:r}),this._drain();})}_drain(){for(;this._running<this._concurrency&&this._queue.length>0;){let e=this._queue.shift();this._running++,e?.fn().then(e.resolve).catch(e.reject).finally(()=>{this._running--,this._drain();});}}};var V={CONNECTING:0,OPEN:1,CLOSING:2,CLOSED:3};var h={CALL:2,CALLRESULT:3,CALLERROR:4},B=Symbol("NOREPLY");var $={debug:()=>{},info:()=>{},warn:()=>{},error:()=>{},child:()=>$};function f(s,e,t={}){switch(s){case "GenericError":return new g(e,t);case "RpcFrameworkError":return new L(e,t);case "MessageTypeNotSupported":return new y(e,t);case "NotImplemented":return new w(e,t);case "NotSupported":return new E(e,t);case "InternalError":return new _(e,t);case "ProtocolError":return new x(e,t);case "SecurityError":return new R(e,t);case "FormatViolation":return new M(e,t);case "FormationViolation":return new O(e,t);case "PropertyConstraintViolation":return new b(e,t);case "OccurrenceConstraintViolation":return new T(e,t);case "TypeConstraintViolation":return new k(e,t);default:return new g(e,t)}}var ee=["name","message","stack","code","rpcErrorCode","rpcErrorMessage","details"];function D(s){let e={};for(let t of ee){let r=s[t];if(r!==void 0){if(typeof r=="function"||typeof r=="symbol")continue;if(typeof r=="object"&&r!==null)try{JSON.stringify(r),e[t]=r;}catch{}else e[t]=r;}}return e.name||(e.name=s.name),e.message||(e.message=s.message),e}var {CONNECTING:S,OPEN:I,CLOSING:j,CLOSED:C}=V,q=class s extends N{static CONNECTING=S;static OPEN=I;static CLOSING=j;static CLOSED=C;_options;_state=C;_ws=null;_protocol;_identity;_handlers=new Map;_wildcardHandler=null;_pendingCalls=new Map;_pendingResponses=new Set;_callQueue;_closePromise=null;_reconnectAttempt=0;_reconnectTimer=null;_badMessageCount=0;_outboundBuffer=[];_logger;_middleware;constructor(e){if(super(),!e.identity)throw new Error("identity is required");this._identity=e.identity,this._options={reconnect:true,maxReconnects:1/0,backoffMin:1e3,backoffMax:3e4,callTimeoutMs:3e4,callConcurrency:1,maxBadMessages:1/0,respondWithDetailedErrors:false,...e},this._callQueue=new H(this._options.callConcurrency),this._middleware=new A;let t=W(this._options.logging,{component:"BrowserOCPPClient",identity:this._identity});this._logger=t||$;}get log(){return this._logger}get identity(){return this._identity}get protocol(){return this._protocol}get state(){return this._state}async connect(){if(this._state!==C)throw new Error(`Cannot connect: client is in state ${this._state}`);return this._state=S,this._reconnectAttempt=0,this._connectInternal()}async _connectInternal(){return new Promise((e,t)=>{let r=this._buildEndpoint();this._logger.debug?.("Connecting",{url:r}),this.emit("connecting",{url:r});let n;try{n=this._options.protocols?.length?new WebSocket(r,this._options.protocols):new WebSocket(r);}catch(c){this._state=C,t(c);return}this._ws=n;let i=c=>{if(l(),this._state=I,this._protocol=n.protocol||void 0,this._badMessageCount=0,n.protocol&&this._reconnectAttempt===0&&(this._options.protocols=[n.protocol]),this._attachWebsocket(n),this._outboundBuffer.length>0){let p=this._outboundBuffer;this._outboundBuffer=[];for(let d of p)this._ws?.send(d);}this._logger.info?.("Connected",{protocol:n.protocol||void 0}),this.emit("open",c),e();},o=c=>{l(),this._state=C,this._logger.error?.("Connection error"),this.emit("error",c),t(c);},a=()=>{l(),this._state===S&&(this._state=C,t(new Error("WebSocket closed during connection")));},l=()=>{n.removeEventListener("open",i),n.removeEventListener("error",o),n.removeEventListener("close",a);};n.addEventListener("open",i),n.addEventListener("error",o),n.addEventListener("close",a);})}async close(e={}){let{code:t=1e3,reason:r="",awaitPending:n=true,force:i=false}=e;return this._closePromise?this._closePromise:this._state===C?{code:1e3,reason:""}:(this._reconnectTimer&&(clearTimeout(this._reconnectTimer),this._reconnectTimer=null),this._closePromise=this._closeInternal(t,r,n,i),this._closePromise)}async _closeInternal(e,t,r,n){if(this._state=j,!n&&r){let i=Array.from(this._pendingCalls.values()).map(o=>new Promise(a=>{let l=o.resolve,c=o.reject;o.resolve=p=>{l(p),a();},o.reject=p=>{c(p),a();};}));i.length>0&&await Promise.allSettled(i);}return new Promise(i=>{if(!this._ws||this._ws.readyState===WebSocket.CLOSED){this._state=C,this._cleanup();let a={code:e,reason:t};this.emit("close",a),i(a);return}let o=a=>{this._ws?.removeEventListener("close",o),this._state=C,this._cleanup();let l={code:a.code,reason:a.reason};this.emit("close",l),i(l);};if(this._ws.addEventListener("close",o),n)this._ws.close();else {let a=e>=1e3&&e<=4999&&![1004,1005,1006].includes(e);this._ws.close(a?e:1e3,t);}})}handle(...e){if(e.length===1&&typeof e[0]=="function")this._wildcardHandler=e[0];else if(e.length===2&&typeof e[0]=="string"&&typeof e[1]=="function")this._handlers.set(e[0],e[1]);else if(e.length===3&&typeof e[0]=="string"&&typeof e[1]=="string"&&typeof e[2]=="function")this._handlers.set(`${e[0]}:${e[1]}`,e[2]);else throw new Error("Invalid arguments: provide (version, method, handler), (method, handler), or (wildcardHandler)")}removeHandler(e,t){e&&t?this._handlers.delete(`${e}:${t}`):e?this._handlers.delete(e):this._wildcardHandler=null;}removeAllHandlers(){this._handlers.clear(),this._wildcardHandler=null;}use(e){this._middleware.use(e);}async call(...e){let t,r,n;if(e.length>=3&&typeof e[0]=="string"&&typeof e[1]=="string"?(t=e[1],r=e[2]??{},n=e[3]??{}):(t=e[0],r=e[1]??{},n=e[2]??{}),this._state!==I)throw new Error(`Cannot call: client is in state ${this._state}`);return this._callQueue.push(()=>this._sendCall(t,r,n))}async _sendCall(e,t,r){let n=this._generateMessageId(),i=r.timeoutMs??this._options.callTimeoutMs,o={type:"outgoing_call",messageId:n,method:e,params:t,options:r},a;return await this._middleware.execute(o,async l=>{let c=l,p=[h.CALL,n,c.method,c.params],d=JSON.stringify(p);a=await new Promise((u,m)=>{let P=setTimeout(()=>{this._pendingCalls.delete(n),this._logger.warn?.("Call timed out",{messageId:n,method:c.method,timeoutMs:i}),m(new v(`Call to "${c.method}" timed out after ${i}ms`));},i),U={resolve:u,reject:m,timeoutHandle:P,method:c.method,sentAt:Date.now()};if(r.signal){if(r.signal.aborted){clearTimeout(P),m(r.signal.reason??new Error("Aborted"));return}let K=()=>{clearTimeout(P),this._pendingCalls.delete(n),m(r.signal?.reason??new Error("Aborted"));};r.signal.addEventListener("abort",K,{once:true}),U.abortHandler=K;}this._pendingCalls.set(n,U),this._ws?.send(d),this.emit("message",p),this.emit("call",p);});}),a}sendRaw(e){if(this._state===I&&this._ws)this._ws.send(e);else if(this._state===S)this._outboundBuffer.push(e);else throw new Error("Cannot send: client is not connected")}reconfigure(e){Object.assign(this._options,e),e.callConcurrency!==void 0&&this._callQueue.setConcurrency(e.callConcurrency);}_attachWebsocket(e){e.addEventListener("message",t=>this._onMessage(t.data)),e.addEventListener("close",t=>this._onClose(t.code,t.reason)),e.addEventListener("error",t=>this.emit("error",t));}_onMessage(e){let t=typeof e=="string"?e:String(e),r;try{if(r=JSON.parse(t),!Array.isArray(r))throw new Error("Message is not an array")}catch(i){this._onBadMessage(t,i);return}let n=r[0];switch(n){case h.CALL:this._handleIncomingCall(r);break;case h.CALLRESULT:this._handleCallResult(r);break;case h.CALLERROR:this._handleCallError(r);break;default:this._onBadMessage(JSON.stringify(r),new y(`Unknown message type: ${n}`));}}async _handleIncomingCall(e){let[,t,r,n]=e,i={type:"incoming_call",messageId:t,method:r,params:n,protocol:this._protocol};await this._middleware.execute(i,async o=>{let a=o,l=[h.CALL,a.messageId,a.method,a.params];if(this.emit("call",l),this._state===I)try{if(this._pendingResponses.has(a.messageId))throw f("RpcFrameworkError",`Already processing call with ID: ${a.messageId}`);let c=(this._protocol?this._handlers.get(`${this._protocol}:${a.method}`):void 0)??this._handlers.get(a.method);if(!c&&!this._wildcardHandler)throw f("NotImplemented",`No handler for method: ${a.method}`);this._pendingResponses.add(a.messageId);let p=new AbortController,d={messageId:a.messageId,method:a.method,protocol:this._protocol,params:a.params,signal:p.signal},u;if(c?u=await c(d):this._wildcardHandler&&(u=await this._wildcardHandler(a.method,d)),this._pendingResponses.delete(a.messageId),u===B)return;let m=[h.CALLRESULT,a.messageId,u];this._ws?.send(JSON.stringify(m)),this.emit("callResult",m);}catch(c){this._pendingResponses.delete(a.messageId),this._logger.error?.("Handler error",{messageId:a.messageId,method:a.method,error:c.message});let p=c instanceof g||c.rpcErrorCode?c:f("InternalError",c.message),d=this._options.respondWithDetailedErrors?D(c):{},u=[h.CALLERROR,a.messageId,p.rpcErrorCode,p.rpcErrorMessage||c.message||"",d];this._ws?.send(JSON.stringify(u)),this.emit("callError",u);}});}async _handleCallResult(e){let[,t,r]=e;if(!this._pendingCalls.has(t)){this._logger.warn?.("Received CallResult for unknown messageId",{messageId:t});return}let n=this._pendingCalls.get(t),i={type:"incoming_result",messageId:t,method:n.method,payload:r};await this._middleware.execute(i,async o=>{let a=o,l=[h.CALLRESULT,t,a.payload];this.emit("callResult",l),clearTimeout(n.timeoutHandle),this._pendingCalls.delete(t),n.resolve(a.payload);});}async _handleCallError(e){let[,t,r,n,i]=e;if(!this._pendingCalls.has(t)){this._logger.warn?.("Received CallError for unknown messageId",{messageId:t});return}let o=this._pendingCalls.get(t),a=f(r,n,i),l={type:"incoming_error",messageId:t,method:o.method,error:a};await this._middleware.execute(l,async c=>{let d=c.error,u=[h.CALLERROR,t,d.rpcErrorCode,d.message,d.rpcErrorDetails??{}];this.emit("callError",u),clearTimeout(o.timeoutHandle),this._pendingCalls.delete(t),o.reject(d);});}_onBadMessage(e,t){this._badMessageCount++,this._logger?.warn?.("Bad message",{error:t.message,count:this._badMessageCount}),this.emit("badMessage",{message:e,error:t});let r=e.match(/^\s*\[\s*2\s*,\s*"([^"]+)"/);if(r?.[1]&&this._ws){let n=[h.CALLERROR,r[1],"FormatViolation",t.message||"Invalid message format",{}];this._ws.send(JSON.stringify(n)),this.emit("callError",n);}this._badMessageCount>=this._options.maxBadMessages&&this.close({code:1002,reason:"Too many bad messages"}).catch(()=>{});}_rejectPendingCalls(e){for(let[,t]of this._pendingCalls)clearTimeout(t.timeoutHandle),t.reject(new Error(e));this._pendingCalls.clear(),this._pendingResponses.clear();}_onClose(e,t){this._rejectPendingCalls(`Connection closed (${e}: ${t})`),this._state!==j?(this._logger?.info?.("Disconnected",{code:e,reason:t}),this.emit("disconnect",{code:e,reason:t}),this._options.reconnect&&this._reconnectAttempt<this._options.maxReconnects?this._scheduleReconnect():(this._state=C,this.emit("close",{code:e,reason:t}))):this._state=C;}static _INTOLERABLE_ERRORS=new Set(["Maximum redirects exceeded","Server sent no subprotocol","Server sent an invalid subprotocol","Server sent a subprotocol but none was requested","Invalid Sec-WebSocket-Accept header"]);_scheduleReconnect(){this._reconnectAttempt++,this._state=S;let e=this._options.backoffMin,t=this._options.backoffMax,r=Math.min(t,e*2**(this._reconnectAttempt-1)*(.5+Math.random()*.5));this._logger?.warn?.("Reconnecting",{attempt:this._reconnectAttempt,delayMs:Math.round(r)}),this.emit("reconnect",{attempt:this._reconnectAttempt,delay:r}),this._reconnectTimer=setTimeout(async()=>{this._reconnectTimer=null;try{await this._connectInternal();}catch(n){let i=n instanceof Error?n.message:"";if(s._INTOLERABLE_ERRORS.has(i)){this._logger?.error?.("Intolerable error \u2014 stopping reconnection",{error:i}),this._state=C,this.emit("close",{code:1001,reason:i});return}this._reconnectAttempt<this._options.maxReconnects&&this._options.reconnect?this._scheduleReconnect():(this._state=C,this.emit("close",{code:1001,reason:"Max reconnection attempts exhausted"}));}},r);}_buildEndpoint(){let e=this._options.endpoint;if(e.endsWith("/")||(e+="/"),e+=encodeURIComponent(this._identity),this._options.query){let t=new URLSearchParams(this._options.query);e+=(e.includes("?")?"&":"?")+t.toString();}return e}_cleanup(){this._closePromise=null,this._ws=null;}_generateMessageId(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return (e==="x"?t:t&3|8).toString(16)})}};export{q as BrowserOCPPClient,V as ConnectionState,h as MessageType,A as MiddlewareStack,B as NOREPLY,M as RPCFormatViolationError,O as RPCFormationViolationError,L as RPCFrameworkError,g as RPCGenericError,_ as RPCInternalError,y as RPCMessageTypeNotSupportedError,w as RPCNotImplementedError,E as RPCNotSupportedError,T as RPCOccurrenceConstraintViolationError,b as RPCPropertyConstraintViolationError,x as RPCProtocolError,R as RPCSecurityError,k as RPCTypeConstraintViolationError,v as TimeoutError,G as createLoggingMiddleware,f as createRPCError,F as defineRpcMiddleware,D as getErrorPlainObject};
1
+ import {prettyTransport,consoleTransport,createLogger}from'voltlog-io/client';function W(s){return s}function G(s,e,t={}){let r=typeof t=="object"?t:{},{exchangeLog:n=false,prettify:i=false}=r;return async(o,a)=>{let l=Date.now(),d=o.method,p=n?"info":"debug";switch(o.type){case "incoming_call":n&&i?s[p]?.(`\u26A1 ${e} \u2190 ${d} [IN]`,{messageId:o.messageId,method:o.method,protocol:o.protocol,payload:o.params,direction:"IN"}):s[p]?.("CALL \u2190",{messageId:o.messageId,method:o.method,protocol:o.protocol,payload:o.params,direction:"IN"});break;case "outgoing_call":n&&i?s[p]?.(`\u26A1 ${e} \u2192 ${d} [OUT]`,{method:o.method,params:o.params,direction:"OUT"}):s[p]?.("CALL \u2192",{method:o.method,params:o.params,direction:"OUT"});break}try{let c=await a(),u=Date.now()-l;switch(o.type){case "incoming_call":c!=null&&(n&&i?s[p]?.(`\u2705 ${e} \u2192 ${d} [RES]`,{messageId:o.messageId,method:o.method,durationMs:u,params:c,direction:"OUT"}):s[p]?.("CALLRESULT \u2192",{messageId:o.messageId,method:o.method,durationMs:u,params:c,direction:"OUT"}));break;case "outgoing_call":n&&i?s[p]?.(`\u2705 ${e} \u2190 ${d} [RES]`,{messageId:o.messageId,method:o.method,durationMs:u,payload:c,direction:"IN"}):s[p]?.("CALLRESULT \u2190",{messageId:o.messageId,method:o.method,durationMs:u,payload:c,direction:"IN"});break}return c}catch(c){let u=c.message,g=Date.now()-l;throw o.type==="incoming_call"?n&&i?s.error?.(`\u{1F6A8} ${e} \u2192 ${d} [ERR]`,{messageId:o.messageId,method:o.method,durationMs:g,error:u,direction:"OUT"}):s.error?.("CALLERROR \u2192",{messageId:o.messageId,method:o.method,durationMs:g,error:u,direction:"OUT"}):o.type==="outgoing_call"&&(n&&i?s.warn?.(`\u{1F6A8} ${e} \u2190 ${d} [ERR]`,{messageId:o.messageId,method:o.method,durationMs:g,error:u,direction:"IN"}):s.warn?.("CALLERROR \u2190",{messageId:o.messageId,method:o.method,durationMs:g,error:u,direction:"IN"})),c}}}var I=class{_stack=[];use(e){this._stack.push(e);}async execute(e,t){let r=-1,n=async i=>{if(i<=r)throw new Error("next() called multiple times");r=i;let o=this._stack[i];if(i===this._stack.length)return t(e);if(o)return o(e,()=>n(i+1))};return n(0)}};var N=class{_listeners=new Map;on(e,t){let r=this._listeners.get(e);return r?r.push(t):this._listeners.set(e,[t]),this}once(e,t){let r=(...n)=>{this.off(e,r),t(...n);};return r.__wrapped=t,this.on(e,r)}off(e,t){let r=this._listeners.get(e);if(!r)return this;let n=r.findIndex(i=>i===t||i.__wrapped===t);return n!==-1&&r.splice(n,1),r.length===0&&this._listeners.delete(e),this}emit(e,...t){let r=this._listeners.get(e);if(!r||r.length===0)return false;for(let n of [...r])n(...t);return true}addListener(e,t){return this.on(e,t)}removeListener(e,t){return this.off(e,t)}removeAllListeners(e){return e?this._listeners.delete(e):this._listeners.clear(),this}listenerCount(e){return this._listeners.get(e)?.length??0}};var v=class extends Error{constructor(e="Operation timed out"){super(e),this.name="TimeoutError";}},m=class extends Error{rpcErrorCode="GenericError";rpcErrorMessage="";details;constructor(e,t={}){super(e),this.name="RPCGenericError",this.details=t;}},w=class extends m{rpcErrorCode="NotImplemented";rpcErrorMessage="Requested method is not known";constructor(e,t={}){super(e,t),this.name="RPCNotImplementedError";}},E=class extends m{rpcErrorCode="NotSupported";rpcErrorMessage="Requested method is recognised but not supported";constructor(e,t={}){super(e,t),this.name="RPCNotSupportedError";}},_=class extends m{rpcErrorCode="InternalError";rpcErrorMessage="An internal error occurred and the receiver was not able to process the requested action successfully";constructor(e,t={}){super(e,t),this.name="RPCInternalError";}},x=class extends m{rpcErrorCode="ProtocolError";rpcErrorMessage="Payload for action is incomplete";constructor(e,t={}){super(e,t),this.name="RPCProtocolError";}},O=class extends m{rpcErrorCode="SecurityError";rpcErrorMessage="During the processing of action a security issue occurred preventing receiver from completing the action successfully";constructor(e,t={}){super(e,t),this.name="RPCSecurityError";}},R=class extends m{rpcErrorCode="FormationViolation";rpcErrorMessage="Payload for action is syntactically incorrect or not conform the PDU structure for action";constructor(e,t={}){super(e,t),this.name="RPCFormationViolationError";}},M=class extends m{rpcErrorCode="FormatViolation";rpcErrorMessage="Payload is syntactically correct but at least one field contains an invalid value";constructor(e,t={}){super(e,t),this.name="RPCFormatViolationError";}},b=class extends m{rpcErrorCode="PropertyConstraintViolation";rpcErrorMessage="Payload is syntactically correct but at least one of the fields violates data type constraints";constructor(e,t={}){super(e,t),this.name="RPCPropertyConstraintViolationError";}},T=class extends m{rpcErrorCode="OccurrenceConstraintViolation";rpcErrorMessage="Payload for action is syntactically correct but at least one of the fields violates occurrence constraints";constructor(e,t={}){super(e,t),this.name="RPCOccurrenceConstraintViolationError";}},k=class extends m{rpcErrorCode="TypeConstraintViolation";rpcErrorMessage="Payload for action is syntactically correct but at least one of the fields violates type constraints";constructor(e,t={}){super(e,t),this.name="RPCTypeConstraintViolationError";}},y=class extends m{rpcErrorCode="MessageTypeNotSupported";rpcErrorMessage="A message with a Message Type Number received that is not supported by this implementation";constructor(e,t={}){super(e,t),this.name="RPCMessageTypeNotSupportedError";}},L=class extends m{rpcErrorCode="RpcFrameworkError";rpcErrorMessage="Content of the call is not a valid RPC request";constructor(e,t={}){super(e,t),this.name="RPCFrameworkError";}};function Z(s){return s.showMetadata===false||s.showSourceMeta===false||s.prettifySource===true||s.prettifyMetadata===true}function X(s){let e=s.showMetadata??true,t=s.showSourceMeta??true,r=s.prettifySource??false,n=s.prettifyMetadata??false,i="\x1B[2;37m",o="\x1B[0m",a="\x1B[36m";return (l,d)=>{if(!t)l.context=void 0;else if(r&&l.context){let c=[];l.context.component&&c.push(String(l.context.component)),l.context.identity&&c.push(String(l.context.identity)),c.length>0&&(l.message=`${i}[${c.join("/")}]${o} ${l.message}`,l.context=void 0);}let p=l.meta;if(!e)l.meta={};else if(n&&p&&Object.keys(p).length>0){let c=Object.entries(p).filter(([,u])=>u!=null).map(([u,g])=>{let P=typeof g=="object"?JSON.stringify(g):String(g);return typeof g=="string"?P=`${i}${P}${o}`:P=`${i}${P}${o}`,`${a}${u}${o}=${P}`}).join(" ");c&&(l.message=`${l.message} ${c}`),l.meta={};}d(l);}}function K(s,e){if(s===false||s?.enabled===false)return null;if(s?.logger)return e&&s.logger.child?s.logger.child(e):s.logger;let t=s?.level??"INFO",n=s?.prettify??false?[prettyTransport({level:t})]:[consoleTransport({level:t})];if(s?.handler){let a=s.handler;n.push({name:"customHandler",write:l=>a(l)});}let i=[];s&&Z(s)&&i.push(X(s));let o=createLogger({level:t,transports:n,middleware:i.length>0?i:void 0});return e&&Object.keys(e).length>0?o.child(e):o}var H=class{_concurrency;_running=0;_queue=[];constructor(e=1){this._concurrency=Math.max(1,e);}get concurrency(){return this._concurrency}get pending(){return this._queue.length}get running(){return this._running}get size(){return this._running+this._queue.length}setConcurrency(e){this._concurrency=Math.max(1,e),this._drain();}push(e){return new Promise((t,r)=>{this._queue.push({fn:e,resolve:t,reject:r}),this._drain();})}_drain(){for(;this._running<this._concurrency&&this._queue.length>0;){let e=this._queue.shift();this._running++,e?.fn().then(e.resolve).catch(e.reject).finally(()=>{this._running--,this._drain();});}}};var j={CONNECTING:0,OPEN:1,CLOSING:2,CLOSED:3};var C={CALL:2,CALLRESULT:3,CALLERROR:4},B=Symbol("NOREPLY");var V={debug:()=>{},info:()=>{},warn:()=>{},error:()=>{},child:()=>V};function f(s,e,t={}){switch(s){case "GenericError":return new m(e,t);case "RpcFrameworkError":return new L(e,t);case "MessageTypeNotSupported":return new y(e,t);case "NotImplemented":return new w(e,t);case "NotSupported":return new E(e,t);case "InternalError":return new _(e,t);case "ProtocolError":return new x(e,t);case "SecurityError":return new O(e,t);case "FormatViolation":return new M(e,t);case "FormationViolation":return new R(e,t);case "PropertyConstraintViolation":return new b(e,t);case "OccurrenceConstraintViolation":return new T(e,t);case "TypeConstraintViolation":return new k(e,t);default:return new m(e,t)}}var ee=["name","message","stack","code","rpcErrorCode","rpcErrorMessage","details"];function D(s){let e={};for(let t of ee){let r=s[t];if(r!==void 0){if(typeof r=="function"||typeof r=="symbol")continue;if(typeof r=="object"&&r!==null)try{JSON.stringify(r),e[t]=r;}catch{}else e[t]=r;}}return e.name||(e.name=s.name),e.message||(e.message=s.message),e}var {CONNECTING:S,OPEN:A,CLOSING:$,CLOSED:h}=j,U=class s extends N{static CONNECTING=S;static OPEN=A;static CLOSING=$;static CLOSED=h;_options;_state=h;_ws=null;_protocol;_identity;_handlers=new Map;_wildcardHandler=null;_pendingCalls=new Map;_pendingResponses=new Set;_callQueue;_closePromise=null;_reconnectAttempt=0;_reconnectTimer=null;_badMessageCount=0;_outboundBuffer=[];_logger;_middleware;constructor(e){if(super(),!e.identity)throw new Error("identity is required");this._identity=e.identity,this._options={reconnect:true,maxReconnects:1/0,backoffMin:1e3,backoffMax:3e4,callTimeoutMs:3e4,callConcurrency:1,maxBadMessages:1/0,respondWithDetailedErrors:false,...e},this._callQueue=new H(this._options.callConcurrency),this._middleware=new I;let t=K(this._options.logging,{component:"BrowserOCPPClient",identity:this._identity});this._logger=t||V;}get log(){return this._logger}get identity(){return this._identity}get protocol(){return this._protocol}get state(){return this._state}async connect(){if(this._state!==h)throw new Error(`Cannot connect: client is in state ${this._state}`);return this._state=S,this._reconnectAttempt=0,this._connectInternal()}async _connectInternal(){return new Promise((e,t)=>{let r=this._buildEndpoint();this._logger.debug?.("Connecting",{url:r}),this.emit("connecting",{url:r});let n;try{n=this._options.protocols?.length?new WebSocket(r,this._options.protocols):new WebSocket(r);}catch(d){this._state=h,t(d);return}this._ws=n;let i=d=>{if(l(),this._state=A,this._protocol=n.protocol||void 0,this._badMessageCount=0,n.protocol&&this._reconnectAttempt===0&&(this._options.protocols=[n.protocol]),this._attachWebsocket(n),this._outboundBuffer.length>0){let p=this._outboundBuffer;this._outboundBuffer=[];for(let c of p)this._ws?.send(c);}this._logger.info?.("Connected",{protocol:n.protocol||void 0}),this.emit("open",d),e();},o=d=>{l(),this._state=h,this._logger.error?.("Connection error"),this.emit("error",d),t(d);},a=()=>{l(),this._state===S&&(this._state=h,t(new Error("WebSocket closed during connection")));},l=()=>{n.removeEventListener("open",i),n.removeEventListener("error",o),n.removeEventListener("close",a);};n.addEventListener("open",i),n.addEventListener("error",o),n.addEventListener("close",a);})}async close(e={}){let{code:t=1e3,reason:r="",awaitPending:n=true,force:i=false}=e;return this._closePromise?this._closePromise:this._state===h?{code:1e3,reason:""}:(this._reconnectTimer&&(clearTimeout(this._reconnectTimer),this._reconnectTimer=null),this._closePromise=this._closeInternal(t,r,n,i),this._closePromise)}async _closeInternal(e,t,r,n){if(this._state=$,!n&&r){let i=Array.from(this._pendingCalls.values()).map(o=>new Promise(a=>{let l=o.resolve,d=o.reject;o.resolve=p=>{l(p),a();},o.reject=p=>{d(p),a();};}));i.length>0&&await Promise.allSettled(i);}return new Promise(i=>{if(!this._ws||this._ws.readyState===WebSocket.CLOSED){this._state=h,this._cleanup();let a={code:e,reason:t};this.emit("close",a),i(a);return}let o=a=>{this._ws?.removeEventListener("close",o),this._state=h,this._cleanup();let l={code:a.code,reason:a.reason};this.emit("close",l),i(l);};if(this._ws.addEventListener("close",o),n)this._ws.close();else {let a=e>=1e3&&e<=4999&&![1004,1005,1006].includes(e);this._ws.close(a?e:1e3,t);}})}handle(...e){if(e.length===1&&typeof e[0]=="function")this._wildcardHandler=e[0];else if(e.length===2&&typeof e[0]=="string"&&typeof e[1]=="function")this._handlers.set(e[0],e[1]);else if(e.length===3&&typeof e[0]=="string"&&typeof e[1]=="string"&&typeof e[2]=="function")this._handlers.set(`${e[0]}:${e[1]}`,e[2]);else throw new Error("Invalid arguments: provide (version, method, handler), (method, handler), or (wildcardHandler)")}removeHandler(e,t){e&&t?this._handlers.delete(`${e}:${t}`):e?this._handlers.delete(e):this._wildcardHandler=null;}removeAllHandlers(){this._handlers.clear(),this._wildcardHandler=null;}use(e){this._middleware.use(e);}async call(...e){let t,r,n;if(e.length>=3&&typeof e[0]=="string"&&typeof e[1]=="string"?(t=e[1],r=e[2]??{},n=e[3]??{}):(t=e[0],r=e[1]??{},n=e[2]??{}),this._state!==A)throw new Error(`Cannot call: client is in state ${this._state}`);return this._callQueue.push(()=>this._sendCall(t,r,n))}async _sendCall(e,t,r){let n=this._generateMessageId(),i=r.timeoutMs??this._options.callTimeoutMs,o={type:"outgoing_call",messageId:n,method:e,params:t,options:r},a;return await this._middleware.execute(o,async l=>{let d=l,p=[C.CALL,n,d.method,d.params],c=JSON.stringify(p);a=await new Promise((u,g)=>{let P=setTimeout(()=>{this._pendingCalls.delete(n),this._logger.warn?.("Call timed out",{messageId:n,method:d.method,timeoutMs:i}),g(new v(`Call to "${d.method}" timed out after ${i}ms`));},i),q={resolve:u,reject:g,timeoutHandle:P,method:d.method,sentAt:Date.now()};if(r.signal){if(r.signal.aborted){clearTimeout(P),g(r.signal.reason??new Error("Aborted"));return}let F=()=>{clearTimeout(P),this._pendingCalls.delete(n),g(r.signal?.reason??new Error("Aborted"));};r.signal.addEventListener("abort",F,{once:true}),q.abortHandler=F;}this._pendingCalls.set(n,q),this._ws?.send(c),this.emit("message",p),this.emit("call",p);});}),a}sendRaw(e){if(this._state===A&&this._ws)this._ws.send(e);else if(this._state===S)this._outboundBuffer.push(e);else throw new Error("Cannot send: client is not connected")}reconfigure(e){Object.assign(this._options,e),e.callConcurrency!==void 0&&this._callQueue.setConcurrency(e.callConcurrency);}_attachWebsocket(e){e.addEventListener("message",t=>this._onMessage(t.data)),e.addEventListener("close",t=>this._onClose(t.code,t.reason)),e.addEventListener("error",t=>this.emit("error",t));}_onMessage(e){let t=typeof e=="string"?e:String(e),r;try{if(r=JSON.parse(t),!Array.isArray(r))throw new Error("Message is not an array")}catch(i){this._onBadMessage(t,i);return}let n=r[0];switch(n){case C.CALL:this._handleIncomingCall(r);break;case C.CALLRESULT:this._handleCallResult(r);break;case C.CALLERROR:this._handleCallError(r);break;default:this._onBadMessage(JSON.stringify(r),new y(`Unknown message type: ${n}`));}}async _handleIncomingCall(e){let[,t,r,n]=e,i={type:"incoming_call",messageId:t,method:r,params:n,protocol:this._protocol};await this._middleware.execute(i,async o=>{let a=o,l=[C.CALL,a.messageId,a.method,a.params];if(this.emit("call",l),this._state===A)try{if(this._pendingResponses.has(a.messageId))throw f("RpcFrameworkError",`Already processing call with ID: ${a.messageId}`);let d=(this._protocol?this._handlers.get(`${this._protocol}:${a.method}`):void 0)??this._handlers.get(a.method);if(!d&&!this._wildcardHandler)throw f("NotImplemented",`No handler for method: ${a.method}`);this._pendingResponses.add(a.messageId);let p=new AbortController,c={messageId:a.messageId,method:a.method,protocol:this._protocol,params:a.params,signal:p.signal},u;if(d?u=await d(c):this._wildcardHandler&&(u=await this._wildcardHandler(a.method,c)),this._pendingResponses.delete(a.messageId),u===B)return;let g=[C.CALLRESULT,a.messageId,u];this._ws?.send(JSON.stringify(g)),this.emit("callResult",g);}catch(d){this._pendingResponses.delete(a.messageId),this._logger.error?.("Handler error",{messageId:a.messageId,method:a.method,error:d.message});let p=d instanceof m||d.rpcErrorCode?d:f("InternalError",d.message),c=this._options.respondWithDetailedErrors?D(d):{},u=[C.CALLERROR,a.messageId,p.rpcErrorCode,p.rpcErrorMessage||d.message||"",c];this._ws?.send(JSON.stringify(u)),this.emit("callError",u);}});}async _handleCallResult(e){let[,t,r]=e;if(!this._pendingCalls.has(t)){this._logger.warn?.("Received CallResult for unknown messageId",{messageId:t});return}let n=this._pendingCalls.get(t),i={type:"incoming_result",messageId:t,method:n.method,payload:r};await this._middleware.execute(i,async o=>{let a=o,l=[C.CALLRESULT,t,a.payload];this.emit("callResult",l),clearTimeout(n.timeoutHandle),this._pendingCalls.delete(t),n.resolve(a.payload);});}async _handleCallError(e){let[,t,r,n,i]=e;if(!this._pendingCalls.has(t)){this._logger.warn?.("Received CallError for unknown messageId",{messageId:t});return}let o=this._pendingCalls.get(t),a=f(r,n,i),l={type:"incoming_error",messageId:t,method:o.method,error:a};await this._middleware.execute(l,async d=>{let c=d.error,u=[C.CALLERROR,t,c.rpcErrorCode,c.message,c.rpcErrorDetails??{}];this.emit("callError",u),clearTimeout(o.timeoutHandle),this._pendingCalls.delete(t),o.reject(c);});}_onBadMessage(e,t){this._badMessageCount++,this._logger?.warn?.("Bad message",{error:t.message,count:this._badMessageCount}),this.emit("badMessage",{message:e,error:t});let r=e.match(/^\s*\[\s*2\s*,\s*"([^"]+)"/);if(r?.[1]&&this._ws){let n=[C.CALLERROR,r[1],"FormatViolation",t.message||"Invalid message format",{}];this._ws.send(JSON.stringify(n)),this.emit("callError",n);}this._badMessageCount>=this._options.maxBadMessages&&this.close({code:1002,reason:"Too many bad messages"}).catch(()=>{});}_rejectPendingCalls(e){for(let[,t]of this._pendingCalls)clearTimeout(t.timeoutHandle),t.reject(new Error(e));this._pendingCalls.clear(),this._pendingResponses.clear();}_onClose(e,t){this._rejectPendingCalls(`Connection closed (${e}: ${t})`),this._state!==$?(this._logger?.info?.("Disconnected",{code:e,reason:t}),this.emit("disconnect",{code:e,reason:t}),this._options.reconnect&&this._reconnectAttempt<this._options.maxReconnects?this._scheduleReconnect():(this._state=h,this.emit("close",{code:e,reason:t}))):this._state=h;}static _INTOLERABLE_ERRORS=new Set(["Maximum redirects exceeded","Server sent no subprotocol","Server sent an invalid subprotocol","Server sent a subprotocol but none was requested","Invalid Sec-WebSocket-Accept header"]);_scheduleReconnect(){this._reconnectAttempt++,this._state=S;let e=this._options.backoffMin,t=this._options.backoffMax,r=Math.min(t,e*2**(this._reconnectAttempt-1)*(.5+Math.random()*.5));this._logger?.warn?.("Reconnecting",{attempt:this._reconnectAttempt,delayMs:Math.round(r)}),this.emit("reconnect",{attempt:this._reconnectAttempt,delay:r}),this._reconnectTimer=setTimeout(async()=>{this._reconnectTimer=null;try{await this._connectInternal();}catch(n){let i=n instanceof Error?n.message:"";if(s._INTOLERABLE_ERRORS.has(i)){this._logger?.error?.("Intolerable error \u2014 stopping reconnection",{error:i}),this._state=h,this.emit("close",{code:1001,reason:i});return}this._reconnectAttempt<this._options.maxReconnects&&this._options.reconnect?this._scheduleReconnect():(this._state=h,this.emit("close",{code:1001,reason:"Max reconnection attempts exhausted"}));}},r);}_buildEndpoint(){let e=this._options.endpoint;if(e.endsWith("/")||(e+="/"),e+=encodeURIComponent(this._identity),this._options.query){let t=new URLSearchParams(this._options.query);e+=(e.includes("?")?"&":"?")+t.toString();}return e}_cleanup(){this._closePromise=null,this._ws=null;}_generateMessageId(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return (e==="x"?t:t&3|8).toString(16)})}};export{U as BrowserOCPPClient,j as ConnectionState,C as MessageType,I as MiddlewareStack,B as NOREPLY,M as RPCFormatViolationError,R as RPCFormationViolationError,L as RPCFrameworkError,m as RPCGenericError,_ as RPCInternalError,y as RPCMessageTypeNotSupportedError,w as RPCNotImplementedError,E as RPCNotSupportedError,T as RPCOccurrenceConstraintViolationError,b as RPCPropertyConstraintViolationError,x as RPCProtocolError,O as RPCSecurityError,k as RPCTypeConstraintViolationError,v as TimeoutError,G as createLoggingMiddleware,f as createRPCError,W as defineRpcMiddleware,D as getErrorPlainObject};
@@ -0,0 +1,21 @@
1
+ import { O as OCPPServer, a as OCPPServerClient, b as OCPPServerStats, c as OCPPProtocol, A as AllMethodNames, d as OCPPRequestType, C as CallOptions, e as OCPPResponseType, f as CloseOptions } from './types-xFfIgIuS.mjs';
2
+
3
+ declare abstract class BaseOcppContext {
4
+ readonly server: OCPPServer;
5
+ constructor(server: OCPPServer);
6
+ get clients(): ReadonlySet<OCPPServerClient>;
7
+ stats(): OCPPServerStats;
8
+ getClient(identity: string): OCPPServerClient | undefined;
9
+ getLocalClient(identity: string): OCPPServerClient | undefined;
10
+ hasLocalClient(identity: string): boolean;
11
+ hasClient(identity: string): Promise<boolean>;
12
+ sendToClient<V extends OCPPProtocol, M extends AllMethodNames<V>>(identity: string, version: V, method: M, params: OCPPRequestType<V, M>, options?: CallOptions): Promise<OCPPResponseType<V, M> | undefined>;
13
+ sendToClient<M extends AllMethodNames<any>>(identity: string, method: M, params: OCPPRequestType<any, M>, options?: CallOptions): Promise<OCPPResponseType<any, M> | undefined>;
14
+ sendToClient<TResult = any>(identity: string, method: string, params: Record<string, any>, options?: CallOptions): Promise<TResult | undefined>;
15
+ safeSendToClient<V extends OCPPProtocol, M extends AllMethodNames<V>>(identity: string, version: V, method: M, params: OCPPRequestType<V, M>, options?: CallOptions): Promise<OCPPResponseType<V, M> | undefined>;
16
+ safeSendToClient<M extends AllMethodNames<any>>(identity: string, method: M, params: OCPPRequestType<any, M>, options?: CallOptions): Promise<OCPPResponseType<any, M> | undefined>;
17
+ safeSendToClient<TResult = any>(identity: string, method: string, params: Record<string, any>, options?: CallOptions): Promise<TResult | undefined>;
18
+ close(options?: CloseOptions): Promise<void>;
19
+ }
20
+
21
+ export { BaseOcppContext as B };
@@ -0,0 +1,21 @@
1
+ import { O as OCPPServer, a as OCPPServerClient, b as OCPPServerStats, c as OCPPProtocol, A as AllMethodNames, d as OCPPRequestType, C as CallOptions, e as OCPPResponseType, f as CloseOptions } from './types-xFfIgIuS.js';
2
+
3
+ declare abstract class BaseOcppContext {
4
+ readonly server: OCPPServer;
5
+ constructor(server: OCPPServer);
6
+ get clients(): ReadonlySet<OCPPServerClient>;
7
+ stats(): OCPPServerStats;
8
+ getClient(identity: string): OCPPServerClient | undefined;
9
+ getLocalClient(identity: string): OCPPServerClient | undefined;
10
+ hasLocalClient(identity: string): boolean;
11
+ hasClient(identity: string): Promise<boolean>;
12
+ sendToClient<V extends OCPPProtocol, M extends AllMethodNames<V>>(identity: string, version: V, method: M, params: OCPPRequestType<V, M>, options?: CallOptions): Promise<OCPPResponseType<V, M> | undefined>;
13
+ sendToClient<M extends AllMethodNames<any>>(identity: string, method: M, params: OCPPRequestType<any, M>, options?: CallOptions): Promise<OCPPResponseType<any, M> | undefined>;
14
+ sendToClient<TResult = any>(identity: string, method: string, params: Record<string, any>, options?: CallOptions): Promise<TResult | undefined>;
15
+ safeSendToClient<V extends OCPPProtocol, M extends AllMethodNames<V>>(identity: string, version: V, method: M, params: OCPPRequestType<V, M>, options?: CallOptions): Promise<OCPPResponseType<V, M> | undefined>;
16
+ safeSendToClient<M extends AllMethodNames<any>>(identity: string, method: M, params: OCPPRequestType<any, M>, options?: CallOptions): Promise<OCPPResponseType<any, M> | undefined>;
17
+ safeSendToClient<TResult = any>(identity: string, method: string, params: Record<string, any>, options?: CallOptions): Promise<TResult | undefined>;
18
+ close(options?: CloseOptions): Promise<void>;
19
+ }
20
+
21
+ export { BaseOcppContext as B };
@@ -0,0 +1,70 @@
1
+ import { IncomingMessage, Server } from 'node:http';
2
+ import { O as OCPPServer, a as OCPPServerClient, b as OCPPServerStats, c as OCPPProtocol, A as AllMethodNames, d as OCPPRequestType, C as CallOptions, e as OCPPResponseType, f as CloseOptions } from './types-xFfIgIuS.mjs';
3
+ import 'ws';
4
+ import 'node:https';
5
+ import 'node:events';
6
+ import 'node:stream';
7
+ import 'node:tls';
8
+ import 'voltlog-io';
9
+ import 'ajv';
10
+
11
+ interface OcppExpressContext {
12
+ readonly server: OCPPServer;
13
+ readonly clients: ReadonlySet<OCPPServerClient>;
14
+ stats(): OCPPServerStats;
15
+ getClient(identity: string): OCPPServerClient | undefined;
16
+ getLocalClient(identity: string): OCPPServerClient | undefined;
17
+ hasLocalClient(identity: string): boolean;
18
+ hasClient(identity: string): Promise<boolean>;
19
+ sendToClient<V extends OCPPProtocol, M extends AllMethodNames<V>>(identity: string, version: V, method: M, params: OCPPRequestType<V, M>, options?: CallOptions): Promise<OCPPResponseType<V, M> | undefined>;
20
+ sendToClient<M extends AllMethodNames<any>>(identity: string, method: M, params: OCPPRequestType<any, M>, options?: CallOptions): Promise<OCPPResponseType<any, M> | undefined>;
21
+ sendToClient<TResult = any>(identity: string, method: string, params: Record<string, any>, options?: CallOptions): Promise<TResult | undefined>;
22
+ safeSendToClient<V extends OCPPProtocol, M extends AllMethodNames<V>>(identity: string, version: V, method: M, params: OCPPRequestType<V, M>, options?: CallOptions): Promise<OCPPResponseType<V, M> | undefined>;
23
+ safeSendToClient<M extends AllMethodNames<any>>(identity: string, method: M, params: OCPPRequestType<any, M>, options?: CallOptions): Promise<OCPPResponseType<any, M> | undefined>;
24
+ safeSendToClient<TResult = any>(identity: string, method: string, params: Record<string, any>, options?: CallOptions): Promise<TResult | undefined>;
25
+ close(options?: CloseOptions): Promise<void>;
26
+ }
27
+ interface OcppExpressRequest {
28
+ ocpp?: OcppExpressContext;
29
+ }
30
+ type OcppExpressNextFunction = (error?: unknown) => void;
31
+ type OcppExpressMiddleware = (req: OcppExpressRequest, res: unknown, next: OcppExpressNextFunction) => void;
32
+ interface AttachOcppExpressOptions {
33
+ /**
34
+ * Only pass matching upgrade requests to OCPP.
35
+ * Example: "/ocpp" handles "/ocpp/CP-001"; "/ocpp/*" does the same.
36
+ */
37
+ upgradePathPrefix?: string | string[];
38
+ /**
39
+ * Advanced upgrade filter. Return true to let OCPP handle the request.
40
+ */
41
+ upgradeFilter?: (pathname: string, req: IncomingMessage) => boolean;
42
+ /**
43
+ * Also close the owning HTTP server when binding.close() is called.
44
+ * Defaults to false because Express usually owns the HTTP server lifecycle.
45
+ */
46
+ closeHttpServer?: boolean;
47
+ }
48
+ interface OcppExpressBinding {
49
+ readonly server: OCPPServer;
50
+ readonly httpServer: Server;
51
+ readonly context: OcppExpressContext;
52
+ dispose(): void;
53
+ close(options?: CloseOptions): Promise<void>;
54
+ }
55
+ declare global {
56
+ namespace Express {
57
+ interface Request {
58
+ ocpp: OcppExpressContext;
59
+ }
60
+ }
61
+ }
62
+
63
+ declare function attachOcppExpress(httpServer: Server, server: OCPPServer, options?: AttachOcppExpressOptions): OcppExpressBinding;
64
+
65
+ declare function createOcppExpressContext(server: OCPPServer): OcppExpressContext;
66
+
67
+ declare function ocppMiddleware(context: OcppExpressContext): OcppExpressMiddleware;
68
+ declare function ocppMiddleware(server: OCPPServer): OcppExpressMiddleware;
69
+
70
+ export { type AttachOcppExpressOptions, type OcppExpressBinding, type OcppExpressContext, type OcppExpressMiddleware, type OcppExpressNextFunction, type OcppExpressRequest, attachOcppExpress, createOcppExpressContext, ocppMiddleware };
@@ -0,0 +1,70 @@
1
+ import { IncomingMessage, Server } from 'node:http';
2
+ import { O as OCPPServer, a as OCPPServerClient, b as OCPPServerStats, c as OCPPProtocol, A as AllMethodNames, d as OCPPRequestType, C as CallOptions, e as OCPPResponseType, f as CloseOptions } from './types-xFfIgIuS.js';
3
+ import 'ws';
4
+ import 'node:https';
5
+ import 'node:events';
6
+ import 'node:stream';
7
+ import 'node:tls';
8
+ import 'voltlog-io';
9
+ import 'ajv';
10
+
11
+ interface OcppExpressContext {
12
+ readonly server: OCPPServer;
13
+ readonly clients: ReadonlySet<OCPPServerClient>;
14
+ stats(): OCPPServerStats;
15
+ getClient(identity: string): OCPPServerClient | undefined;
16
+ getLocalClient(identity: string): OCPPServerClient | undefined;
17
+ hasLocalClient(identity: string): boolean;
18
+ hasClient(identity: string): Promise<boolean>;
19
+ sendToClient<V extends OCPPProtocol, M extends AllMethodNames<V>>(identity: string, version: V, method: M, params: OCPPRequestType<V, M>, options?: CallOptions): Promise<OCPPResponseType<V, M> | undefined>;
20
+ sendToClient<M extends AllMethodNames<any>>(identity: string, method: M, params: OCPPRequestType<any, M>, options?: CallOptions): Promise<OCPPResponseType<any, M> | undefined>;
21
+ sendToClient<TResult = any>(identity: string, method: string, params: Record<string, any>, options?: CallOptions): Promise<TResult | undefined>;
22
+ safeSendToClient<V extends OCPPProtocol, M extends AllMethodNames<V>>(identity: string, version: V, method: M, params: OCPPRequestType<V, M>, options?: CallOptions): Promise<OCPPResponseType<V, M> | undefined>;
23
+ safeSendToClient<M extends AllMethodNames<any>>(identity: string, method: M, params: OCPPRequestType<any, M>, options?: CallOptions): Promise<OCPPResponseType<any, M> | undefined>;
24
+ safeSendToClient<TResult = any>(identity: string, method: string, params: Record<string, any>, options?: CallOptions): Promise<TResult | undefined>;
25
+ close(options?: CloseOptions): Promise<void>;
26
+ }
27
+ interface OcppExpressRequest {
28
+ ocpp?: OcppExpressContext;
29
+ }
30
+ type OcppExpressNextFunction = (error?: unknown) => void;
31
+ type OcppExpressMiddleware = (req: OcppExpressRequest, res: unknown, next: OcppExpressNextFunction) => void;
32
+ interface AttachOcppExpressOptions {
33
+ /**
34
+ * Only pass matching upgrade requests to OCPP.
35
+ * Example: "/ocpp" handles "/ocpp/CP-001"; "/ocpp/*" does the same.
36
+ */
37
+ upgradePathPrefix?: string | string[];
38
+ /**
39
+ * Advanced upgrade filter. Return true to let OCPP handle the request.
40
+ */
41
+ upgradeFilter?: (pathname: string, req: IncomingMessage) => boolean;
42
+ /**
43
+ * Also close the owning HTTP server when binding.close() is called.
44
+ * Defaults to false because Express usually owns the HTTP server lifecycle.
45
+ */
46
+ closeHttpServer?: boolean;
47
+ }
48
+ interface OcppExpressBinding {
49
+ readonly server: OCPPServer;
50
+ readonly httpServer: Server;
51
+ readonly context: OcppExpressContext;
52
+ dispose(): void;
53
+ close(options?: CloseOptions): Promise<void>;
54
+ }
55
+ declare global {
56
+ namespace Express {
57
+ interface Request {
58
+ ocpp: OcppExpressContext;
59
+ }
60
+ }
61
+ }
62
+
63
+ declare function attachOcppExpress(httpServer: Server, server: OCPPServer, options?: AttachOcppExpressOptions): OcppExpressBinding;
64
+
65
+ declare function createOcppExpressContext(server: OCPPServer): OcppExpressContext;
66
+
67
+ declare function ocppMiddleware(context: OcppExpressContext): OcppExpressMiddleware;
68
+ declare function ocppMiddleware(server: OCPPServer): OcppExpressMiddleware;
69
+
70
+ export { type AttachOcppExpressOptions, type OcppExpressBinding, type OcppExpressContext, type OcppExpressMiddleware, type OcppExpressNextFunction, type OcppExpressRequest, attachOcppExpress, createOcppExpressContext, ocppMiddleware };
@@ -0,0 +1,2 @@
1
+ 'use strict';function m(e,t){if(e.headers.upgrade?.toLowerCase()!=="websocket")return false;let s=O(e);if(!s)return false;if(t.upgradeFilter)return t.upgradeFilter(s,e);let o=u(t.upgradePathPrefix);return o.length===0?true:o.some(n=>y(n,s))}function O(e){try{return new URL(e.url??"/",`http://${e.headers.host??"localhost"}`).pathname}catch{return}}function u(e){return e?(Array.isArray(e)?e:[e]).filter(Boolean):[]}function y(e,t){let s=e.endsWith("/*")?e.slice(0,-2):e;return s===""||s==="/"?true:t===s||t.startsWith(`${s}/`)}var p=class{constructor(t){this.server=t;}server;get clients(){return this.server.clients}stats(){return this.server.stats()}getClient(t){return this.server.getLocalClient(t)}getLocalClient(t){return this.server.getLocalClient(t)}hasLocalClient(t){return this.server.hasLocalClient(t)}hasClient(t){return this.server.isClientConnected(t)}async sendToClient(...t){return this.server.sendToClient.apply(this.server,t)}async safeSendToClient(...t){return this.server.safeSendToClient.apply(this.server,t)}close(t){return this.server.close(t)}};var d=class extends p{};function a(e){return new d(e)}function E(e,t,s={}){let o=a(t),n=false,P=(i,l,C)=>{m(i,s)&&t.handleUpgrade(i,l,C);};e.on("upgrade",P);let c=()=>{n||(e.removeListener("upgrade",P),n=true);};return {server:t,httpServer:e,context:o,dispose:c,async close(i){c(),await t.close(i),s.closeHttpServer&&e.listening&&await new Promise(l=>e.close(()=>l()));}}}function q(e){let t="server"in e?e:a(e);return (s,o,n)=>{s.ocpp=t,n();}}
2
+ exports.attachOcppExpress=E;exports.createOcppExpressContext=a;exports.ocppMiddleware=q;
@@ -0,0 +1,2 @@
1
+ function C(e,t){if(e.headers.upgrade?.toLowerCase()!=="websocket")return false;let s=u(e);if(!s)return false;if(t.upgradeFilter)return t.upgradeFilter(s,e);let i=y(t.upgradePathPrefix);return i.length===0?true:i.some(n=>x(n,s))}function u(e){try{return new URL(e.url??"/",`http://${e.headers.host??"localhost"}`).pathname}catch{return}}function y(e){return e?(Array.isArray(e)?e:[e]).filter(Boolean):[]}function x(e,t){let s=e.endsWith("/*")?e.slice(0,-2):e;return s===""||s==="/"?true:t===s||t.startsWith(`${s}/`)}var a=class{constructor(t){this.server=t;}server;get clients(){return this.server.clients}stats(){return this.server.stats()}getClient(t){return this.server.getLocalClient(t)}getLocalClient(t){return this.server.getLocalClient(t)}hasLocalClient(t){return this.server.hasLocalClient(t)}hasClient(t){return this.server.isClientConnected(t)}async sendToClient(...t){return this.server.sendToClient.apply(this.server,t)}async safeSendToClient(...t){return this.server.safeSendToClient.apply(this.server,t)}close(t){return this.server.close(t)}};var P=class extends a{};function l(e){return new P(e)}function V(e,t,s={}){let i=l(t),n=false,c=(p,d,O)=>{C(p,s)&&t.handleUpgrade(p,d,O);};e.on("upgrade",c);let m=()=>{n||(e.removeListener("upgrade",c),n=true);};return {server:t,httpServer:e,context:i,dispose:m,async close(p){m(),await t.close(p),s.closeHttpServer&&e.listening&&await new Promise(d=>e.close(()=>d()));}}}function N(e){let t="server"in e?e:l(e);return (s,i,n)=>{s.ocpp=t,n();}}
2
+ export{V as attachOcppExpress,l as createOcppExpressContext,N as ocppMiddleware};
@@ -0,0 +1,37 @@
1
+ import { FastifyPluginAsync } from 'fastify';
2
+ import { O as OCPPServer } from './types-xFfIgIuS.mjs';
3
+ import { IncomingMessage } from 'node:http';
4
+ import { B as BaseOcppContext } from './context-Cy7YIKyU.mjs';
5
+ import 'ws';
6
+ import 'node:https';
7
+ import 'node:events';
8
+ import 'node:stream';
9
+ import 'node:tls';
10
+ import 'voltlog-io';
11
+ import 'ajv';
12
+
13
+ interface OcppFastifyContext extends BaseOcppContext {
14
+ }
15
+ interface OcppFastifyPluginOptions {
16
+ ocppServer: OCPPServer;
17
+ /**
18
+ * Only requests with a URL starting with this prefix will be upgraded.
19
+ * E.g., "/ocpp" or ["/v1/ocpp", "/v2/ocpp"]
20
+ */
21
+ upgradePathPrefix?: string | string[];
22
+ /**
23
+ * Advanced filter for upgrade requests. If provided, this overrides `upgradePathPrefix`.
24
+ * Return true to allow the upgrade.
25
+ */
26
+ upgradeFilter?: (pathname: string, req: IncomingMessage) => boolean;
27
+ }
28
+ declare module "fastify" {
29
+ interface FastifyRequest {
30
+ ocpp: OcppFastifyContext;
31
+ }
32
+ }
33
+
34
+ declare function createOcppFastifyContext(server: OCPPServer): OcppFastifyContext;
35
+ declare const ocppFastifyPlugin: FastifyPluginAsync<OcppFastifyPluginOptions>;
36
+
37
+ export { type OcppFastifyContext, type OcppFastifyPluginOptions, createOcppFastifyContext, ocppFastifyPlugin };