@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 +1 -1
- package/dist/index.js +1 -1
- package/index.ts +35 -5
- package/package.json +1 -1
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:
|
|
81
|
+
maxReconnectAttempts?: number // Maximum reconnect attempts, default: 1000
|
|
82
82
|
}
|
|
83
83
|
```
|
|
84
84
|
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
},
|
|
214
|
+
}, delay)
|
|
185
215
|
} else {
|
|
186
216
|
this.setConnectionState(ConnectionState.DISCONNECTED)
|
|
187
217
|
}
|