@laplace.live/event-bridge-sdk 1.0.2 → 1.0.4
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 +45 -17
- package/package.json +4 -2
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,z)=>{try{if(this.ws)this.ws.close();this.setConnectionState("connecting");let w=this.options.url,B=[];if(this.options.token){B.push("laplace-event-bridge-role-client",this.options.token);let A=new URL(w);A.searchParams.set("token",this.options.token),w=A.toString()}this.ws=new WebSocket(w,B),this.ws.onopen=()=>{this.setConnectionState("connected"),this.reconnectAttempts=0,q()},this.ws.onmessage=(A)=>{try{let E=JSON.parse(A.data);if(E.type==="ping"){this.lastPingTime=Date.now(),this.ws?.send(JSON.stringify({type:"pong",timestamp:Date.now(),respondingTo:E.timestamp}));return}if(E.type==="established"){this.clientId=E.clientId,this.serverVersion=E.version;let G=(()=>{let F=new URL(w);if(F.searchParams.has("token"))F.searchParams.set("token","***");return F.toString()})();if(console.log(`Welcome to LAPLACE Event Bridge ${`v${E.version}`||"(unknown version)"}: ${G} with client ID ${this.clientId||"unknown"}`),this.shouldMonitorPing())this.startPingMonitoring()}this.processEvent(E)}catch(E){console.error("Failed to parse event data:",E)}},this.ws.onerror=(A)=>{console.error("WebSocket error:",A),z(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,E=1.5,G=60000,F=Math.min(A*E**(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(w){this.setConnectionState("disconnected"),z(w)}})}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,z){let w=this.eventHandlers.get(q)||[];w.push(z),this.eventHandlers.set(q,w)}onAny(q){this.anyEventHandlers.push(q)}onConnectionStateChange(q){this.connectionStateHandlers.push(q),q(this.connectionState)}off(q,z){let w=this.eventHandlers.get(q);if(!w)return;let B=w.indexOf(z);if(B!==-1)w.splice(B,1);if(w.length===0)this.eventHandlers.delete(q)}offAny(q){let z=this.anyEventHandlers.indexOf(q);if(z!==-1)this.anyEventHandlers.splice(z,1)}offConnectionStateChange(q){let z=this.connectionStateHandlers.indexOf(q);if(z!==-1)this.connectionStateHandlers.splice(z,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 z of this.connectionStateHandlers)try{z(q)}catch(w){console.error("Error in connection state change handler:",w)}}}processEvent(q){let z=this.eventHandlers.get(q.type);if(z)for(let w of z)try{w(q)}catch(B){console.error(`Error in event handler for type ${q.type}:`,B)}for(let w of this.anyEventHandlers)try{w(q)}catch(B){console.error("Error in any event handler:",B)}}shouldMonitorPing(){if(!this.serverVersion)return!1;let q=this.serverVersion.split(".").map((A)=>parseInt(A,10));if(q.length<3||q.some(Number.isNaN))return console.warn(`Invalid server version format: ${this.serverVersion}`),!1;let[z=0,w=0,B=0]=q;if(z>4)return!0;if(z===4){if(w>0)return!0;if(w===0&&B>=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 * 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
|
}
|
|
@@ -221,10 +251,9 @@ export class LaplaceEventBridgeClient {
|
|
|
221
251
|
* @param handler The handler function to call when the event is received
|
|
222
252
|
*/
|
|
223
253
|
public on<T extends LaplaceEventTypes>(eventType: T, handler: (event: EventTypeMap[T]) => void): void {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
this.eventHandlers.get(eventType)!.push(handler)
|
|
254
|
+
const handlers = this.eventHandlers.get(eventType) || []
|
|
255
|
+
handlers.push(handler)
|
|
256
|
+
this.eventHandlers.set(eventType, handlers)
|
|
228
257
|
}
|
|
229
258
|
|
|
230
259
|
/**
|
|
@@ -251,11 +280,11 @@ export class LaplaceEventBridgeClient {
|
|
|
251
280
|
* @param handler The handler function to remove
|
|
252
281
|
*/
|
|
253
282
|
public off<T extends LaplaceEventTypes>(eventType: T, handler: (event: EventTypeMap[T]) => void): void {
|
|
254
|
-
|
|
283
|
+
const handlers = this.eventHandlers.get(eventType)
|
|
284
|
+
if (!handlers) {
|
|
255
285
|
return
|
|
256
286
|
}
|
|
257
287
|
|
|
258
|
-
const handlers = this.eventHandlers.get(eventType)!
|
|
259
288
|
const index = handlers.indexOf(handler)
|
|
260
289
|
|
|
261
290
|
if (index !== -1) {
|
|
@@ -339,8 +368,9 @@ export class LaplaceEventBridgeClient {
|
|
|
339
368
|
|
|
340
369
|
private processEvent(event: LaplaceEvent): void {
|
|
341
370
|
// Call specific event handlers
|
|
342
|
-
|
|
343
|
-
|
|
371
|
+
const handlers = this.eventHandlers.get(event.type)
|
|
372
|
+
if (handlers) {
|
|
373
|
+
for (const handler of handlers) {
|
|
344
374
|
try {
|
|
345
375
|
handler(event)
|
|
346
376
|
} catch (err) {
|
|
@@ -371,14 +401,12 @@ export class LaplaceEventBridgeClient {
|
|
|
371
401
|
const versionParts = this.serverVersion.split('.').map(part => parseInt(part, 10))
|
|
372
402
|
|
|
373
403
|
// Ensure we have at least 3 version parts
|
|
374
|
-
if (versionParts.length < 3 || versionParts.some(isNaN)) {
|
|
404
|
+
if (versionParts.length < 3 || versionParts.some(Number.isNaN)) {
|
|
375
405
|
console.warn(`Invalid server version format: ${this.serverVersion}`)
|
|
376
406
|
return false
|
|
377
407
|
}
|
|
378
408
|
|
|
379
|
-
const major =
|
|
380
|
-
const minor = versionParts[1]!
|
|
381
|
-
const patch = versionParts[2]!
|
|
409
|
+
const [major = 0, minor = 0, patch = 0] = versionParts
|
|
382
410
|
|
|
383
411
|
// Check if version is >= 4.0.3
|
|
384
412
|
if (major > 4) return true
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@laplace.live/event-bridge-sdk",
|
|
3
3
|
"description": "LAPLACE Event Bridge SDK",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.4",
|
|
5
5
|
"module": "index.ts",
|
|
6
6
|
"types": "index.ts",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"scripts": {
|
|
10
|
-
"build": "bun build index.ts --outdir dist --target browser --minify"
|
|
10
|
+
"build": "bun build index.ts --outdir dist --target browser --minify",
|
|
11
|
+
"lint": "biome check",
|
|
12
|
+
"format": "biome format --write"
|
|
11
13
|
},
|
|
12
14
|
"exports": {
|
|
13
15
|
".": {
|