@laplace.live/event-bridge-sdk 1.0.2 → 1.0.3

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
@@ -78,7 +78,7 @@ interface ConnectionOptions {
78
78
  token?: string // Authentication token, default: ''
79
79
  reconnect?: boolean // Auto reconnect on disconnect, default: true
80
80
  reconnectInterval?: number // Milliseconds between reconnect attempts, default: 3000
81
- maxReconnectAttempts?: number // Maximum reconnect attempts, default: 10
81
+ maxReconnectAttempts?: number // Maximum reconnect attempts, default: 1000
82
82
  }
83
83
  ```
84
84
 
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- var E;((w)=>{w.DISCONNECTED="disconnected";w.CONNECTING="connecting";w.CONNECTED="connected";w.RECONNECTING="reconnecting"})(E||={});class F{ws=null;eventHandlers=new Map;anyEventHandlers=[];connectionStateHandlers=[];reconnectTimer=null;reconnectAttempts=0;clientId=null;serverVersion=null;connectionState="disconnected";lastPingTime=null;pingMonitorTimer=null;options={url:"ws://localhost:9696",token:"",reconnect:!0,reconnectInterval:3000,maxReconnectAttempts:10,pingTimeout:90000};constructor(f={}){this.options={...this.options,...f}}connect(){return new Promise((f,k)=>{try{if(this.ws)this.ws.close();this.setConnectionState("connecting");let q=this.options.url,A=[];if(this.options.token){A.push("laplace-event-bridge-role-client",this.options.token);let w=new URL(q);w.searchParams.set("token",this.options.token),q=w.toString()}this.ws=new WebSocket(q,A),this.ws.onopen=()=>{this.setConnectionState("connected"),this.reconnectAttempts=0,f()},this.ws.onmessage=(w)=>{try{let z=JSON.parse(w.data);if(z.type==="ping"){this.lastPingTime=Date.now(),this.ws?.send(JSON.stringify({type:"pong",timestamp:Date.now(),respondingTo:z.timestamp}));return}if(z.type==="established"){this.clientId=z.clientId,this.serverVersion=z.version;let D=(()=>{let B=new URL(q);if(B.searchParams.has("token"))B.searchParams.set("token","***");return B.toString()})();if(console.log(`Welcome to LAPLACE Event Bridge ${`v${z.version}`||"(unknown version)"}: ${D} with client ID ${this.clientId||"unknown"}`),this.shouldMonitorPing())this.startPingMonitoring()}this.processEvent(z)}catch(z){console.error("Failed to parse event data:",z)}},this.ws.onerror=(w)=>{console.error("WebSocket error:",w),k(w)},this.ws.onclose=()=>{if(console.log("Disconnected from LAPLACE Event Bridge"),this.options.reconnect&&this.reconnectAttempts<this.options.maxReconnectAttempts)this.reconnectAttempts++,this.setConnectionState("reconnecting"),console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.options.maxReconnectAttempts})...`),this.reconnectTimer=setTimeout(()=>{this.connect().catch((w)=>{console.error("Reconnection failed:",w)})},this.options.reconnectInterval);else this.setConnectionState("disconnected")}}catch(q){this.setConnectionState("disconnected"),k(q)}})}disconnect(){if(this.reconnectTimer)clearTimeout(this.reconnectTimer),this.reconnectTimer=null;if(this.stopPingMonitoring(),this.ws)this.ws.close(),this.ws=null;this.setConnectionState("disconnected"),this.clientId=null,this.serverVersion=null,this.lastPingTime=null}on(f,k){if(!this.eventHandlers.has(f))this.eventHandlers.set(f,[]);this.eventHandlers.get(f).push(k)}onAny(f){this.anyEventHandlers.push(f)}onConnectionStateChange(f){this.connectionStateHandlers.push(f),f(this.connectionState)}off(f,k){if(!this.eventHandlers.has(f))return;let q=this.eventHandlers.get(f),A=q.indexOf(k);if(A!==-1)q.splice(A,1);if(q.length===0)this.eventHandlers.delete(f)}offAny(f){let k=this.anyEventHandlers.indexOf(f);if(k!==-1)this.anyEventHandlers.splice(k,1)}offConnectionStateChange(f){let k=this.connectionStateHandlers.indexOf(f);if(k!==-1)this.connectionStateHandlers.splice(k,1)}isConnectedToBridge(){return this.connectionState==="connected"}getConnectionState(){return this.connectionState}getClientId(){return this.clientId}send(f){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw new Error("Not connected to LAPLACE Event Bridge");this.ws.send(JSON.stringify(f))}setConnectionState(f){if(this.connectionState!==f){this.connectionState=f;for(let k of this.connectionStateHandlers)try{k(f)}catch(q){console.error("Error in connection state change handler:",q)}}}processEvent(f){if(this.eventHandlers.has(f.type))for(let k of this.eventHandlers.get(f.type))try{k(f)}catch(q){console.error(`Error in event handler for type ${f.type}:`,q)}for(let k of this.anyEventHandlers)try{k(f)}catch(q){console.error("Error in any event handler:",q)}}shouldMonitorPing(){if(!this.serverVersion)return!1;let f=this.serverVersion.split(".").map((w)=>parseInt(w,10));if(f.length<3||f.some(isNaN))return console.warn(`Invalid server version format: ${this.serverVersion}`),!1;let k=f[0],q=f[1],A=f[2];if(k>4)return!0;if(k===4){if(q>0)return!0;if(q===0&&A>=3)return!0}return!1}startPingMonitoring(){this.stopPingMonitoring(),console.log(`Ping monitoring enabled (timeout: ${this.options.pingTimeout}ms)`),this.lastPingTime=Date.now(),this.pingMonitorTimer=setInterval(()=>{if(!this.lastPingTime)return;let f=Date.now()-this.lastPingTime;if(f>this.options.pingTimeout){if(console.warn(`Ping timeout detected (${f}ms since last ping). Reconnecting...`),this.stopPingMonitoring(),this.ws)this.ws.close()}},this.options.pingTimeout/3)}stopPingMonitoring(){if(this.pingMonitorTimer)clearInterval(this.pingMonitorTimer),this.pingMonitorTimer=null}}export{F as LaplaceEventBridgeClient,E as ConnectionState};
1
+ var K;((A)=>{A.DISCONNECTED="disconnected";A.CONNECTING="connecting";A.CONNECTED="connected";A.RECONNECTING="reconnecting"})(K||={});class N{ws=null;eventHandlers=new Map;anyEventHandlers=[];connectionStateHandlers=[];reconnectTimer=null;reconnectAttempts=0;clientId=null;serverVersion=null;connectionState="disconnected";lastPingTime=null;pingMonitorTimer=null;options={url:"ws://localhost:9696",token:"",reconnect:!0,reconnectInterval:3000,maxReconnectAttempts:1000,pingTimeout:90000};constructor(q={}){this.options={...this.options,...q}}connect(){return new Promise((q,w)=>{try{if(this.ws)this.ws.close();this.setConnectionState("connecting");let z=this.options.url,E=[];if(this.options.token){E.push("laplace-event-bridge-role-client",this.options.token);let A=new URL(z);A.searchParams.set("token",this.options.token),z=A.toString()}this.ws=new WebSocket(z,E),this.ws.onopen=()=>{this.setConnectionState("connected"),this.reconnectAttempts=0,q()},this.ws.onmessage=(A)=>{try{let B=JSON.parse(A.data);if(B.type==="ping"){this.lastPingTime=Date.now(),this.ws?.send(JSON.stringify({type:"pong",timestamp:Date.now(),respondingTo:B.timestamp}));return}if(B.type==="established"){this.clientId=B.clientId,this.serverVersion=B.version;let G=(()=>{let F=new URL(z);if(F.searchParams.has("token"))F.searchParams.set("token","***");return F.toString()})();if(console.log(`Welcome to LAPLACE Event Bridge ${`v${B.version}`||"(unknown version)"}: ${G} with client ID ${this.clientId||"unknown"}`),this.shouldMonitorPing())this.startPingMonitoring()}this.processEvent(B)}catch(B){console.error("Failed to parse event data:",B)}},this.ws.onerror=(A)=>{console.error("WebSocket error:",A),w(A)},this.ws.onclose=()=>{if(console.log("Disconnected from LAPLACE Event Bridge"),this.stopPingMonitoring(),this.lastPingTime=null,this.options.reconnect&&this.reconnectAttempts<this.options.maxReconnectAttempts){this.reconnectAttempts++,this.setConnectionState("reconnecting");let A=this.options.reconnectInterval,B=1.5,G=60000,F=Math.min(A*Math.pow(B,this.reconnectAttempts-1),G),H=Math.round(F);console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.options.maxReconnectAttempts}) in ${H}ms...`),this.reconnectTimer=setTimeout(()=>{this.connect().catch((J)=>{console.error("Reconnection failed:",J)})},H)}else this.setConnectionState("disconnected")}}catch(z){this.setConnectionState("disconnected"),w(z)}})}disconnect(){if(this.reconnectTimer)clearTimeout(this.reconnectTimer),this.reconnectTimer=null;if(this.stopPingMonitoring(),this.ws)this.ws.close(),this.ws=null;this.setConnectionState("disconnected"),this.clientId=null,this.serverVersion=null,this.lastPingTime=null}on(q,w){if(!this.eventHandlers.has(q))this.eventHandlers.set(q,[]);this.eventHandlers.get(q).push(w)}onAny(q){this.anyEventHandlers.push(q)}onConnectionStateChange(q){this.connectionStateHandlers.push(q),q(this.connectionState)}off(q,w){if(!this.eventHandlers.has(q))return;let z=this.eventHandlers.get(q),E=z.indexOf(w);if(E!==-1)z.splice(E,1);if(z.length===0)this.eventHandlers.delete(q)}offAny(q){let w=this.anyEventHandlers.indexOf(q);if(w!==-1)this.anyEventHandlers.splice(w,1)}offConnectionStateChange(q){let w=this.connectionStateHandlers.indexOf(q);if(w!==-1)this.connectionStateHandlers.splice(w,1)}isConnectedToBridge(){return this.connectionState==="connected"}getConnectionState(){return this.connectionState}getClientId(){return this.clientId}send(q){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw new Error("Not connected to LAPLACE Event Bridge");this.ws.send(JSON.stringify(q))}setConnectionState(q){if(this.connectionState!==q){this.connectionState=q;for(let w of this.connectionStateHandlers)try{w(q)}catch(z){console.error("Error in connection state change handler:",z)}}}processEvent(q){if(this.eventHandlers.has(q.type))for(let w of this.eventHandlers.get(q.type))try{w(q)}catch(z){console.error(`Error in event handler for type ${q.type}:`,z)}for(let w of this.anyEventHandlers)try{w(q)}catch(z){console.error("Error in any event handler:",z)}}shouldMonitorPing(){if(!this.serverVersion)return!1;let q=this.serverVersion.split(".").map((A)=>parseInt(A,10));if(q.length<3||q.some(isNaN))return console.warn(`Invalid server version format: ${this.serverVersion}`),!1;let w=q[0],z=q[1],E=q[2];if(w>4)return!0;if(w===4){if(z>0)return!0;if(z===0&&E>=3)return!0}return!1}startPingMonitoring(){this.stopPingMonitoring(),console.log(`Ping monitoring enabled (timeout: ${this.options.pingTimeout}ms)`),this.lastPingTime=Date.now(),this.pingMonitorTimer=setInterval(()=>{if(!this.lastPingTime)return;let q=Date.now()-this.lastPingTime;if(q>this.options.pingTimeout){if(console.warn(`Ping timeout detected (${q}ms since last ping). Reconnecting...`),this.stopPingMonitoring(),this.ws)this.ws.close()}},this.options.pingTimeout/3)}stopPingMonitoring(){if(this.pingMonitorTimer)clearInterval(this.pingMonitorTimer),this.pingMonitorTimer=null}}export{N as LaplaceEventBridgeClient,K as ConnectionState};
package/index.ts CHANGED
@@ -34,15 +34,24 @@ export interface ConnectionOptions {
34
34
  */
35
35
  reconnect?: boolean
36
36
  /**
37
- * The interval between reconnect attempts in milliseconds
37
+ * The base interval between reconnect attempts in milliseconds.
38
+ * With exponential backoff, each attempt multiplies this by 1.5^(attempt-1).
39
+ * The maximum interval is capped at 60 seconds.
38
40
  *
39
41
  * @default 3000
42
+ * @example
43
+ * // With base interval of 3000ms:
44
+ * // Attempt 1: 3000ms
45
+ * // Attempt 2: 4500ms
46
+ * // Attempt 3: 6750ms
47
+ * // ...
48
+ * // Capped at: 60000ms
40
49
  */
41
50
  reconnectInterval?: number
42
51
  /**
43
52
  * The maximum number of reconnect attempts
44
53
  *
45
- * @default 10
54
+ * @default 1000
46
55
  */
47
56
  maxReconnectAttempts?: number
48
57
  /**
@@ -73,7 +82,7 @@ export class LaplaceEventBridgeClient {
73
82
  token: '',
74
83
  reconnect: true,
75
84
  reconnectInterval: 3000,
76
- maxReconnectAttempts: 10,
85
+ maxReconnectAttempts: 1000,
77
86
  pingTimeout: 90000, // 90 seconds
78
87
  }
79
88
 
@@ -173,15 +182,36 @@ export class LaplaceEventBridgeClient {
173
182
  this.ws.onclose = () => {
174
183
  console.log('Disconnected from LAPLACE Event Bridge')
175
184
 
185
+ // Stop ping monitoring before attempting reconnection
186
+ this.stopPingMonitoring()
187
+
188
+ // Clear ping state
189
+ this.lastPingTime = null
190
+
176
191
  if (this.options.reconnect && this.reconnectAttempts < this.options.maxReconnectAttempts) {
177
192
  this.reconnectAttempts++
178
193
  this.setConnectionState(ConnectionState.RECONNECTING)
179
- console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.options.maxReconnectAttempts})...`)
194
+
195
+ // Calculate exponential backoff with cap at 60 seconds
196
+ const baseInterval = this.options.reconnectInterval
197
+ const backoffMultiplier = 1.5 // Increase by 50% each time
198
+ const maxInterval = 60000 // 60 seconds cap
199
+
200
+ // Calculate delay: base * (multiplier ^ (attempt - 1))
201
+ const calculatedDelay = Math.min(
202
+ baseInterval * Math.pow(backoffMultiplier, this.reconnectAttempts - 1),
203
+ maxInterval
204
+ )
205
+ const delay = Math.round(calculatedDelay)
206
+
207
+ console.log(
208
+ `Attempting to reconnect (${this.reconnectAttempts}/${this.options.maxReconnectAttempts}) in ${delay}ms...`
209
+ )
180
210
  this.reconnectTimer = setTimeout(() => {
181
211
  this.connect().catch(err => {
182
212
  console.error('Reconnection failed:', err)
183
213
  })
184
- }, this.options.reconnectInterval)
214
+ }, delay)
185
215
  } else {
186
216
  this.setConnectionState(ConnectionState.DISCONNECTED)
187
217
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@laplace.live/event-bridge-sdk",
3
3
  "description": "LAPLACE Event Bridge SDK",
4
- "version": "1.0.2",
4
+ "version": "1.0.3",
5
5
  "module": "index.ts",
6
6
  "types": "index.ts",
7
7
  "license": "MIT",