@robono/calling 0.6.0 → 0.6.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/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.6.1 — unpublished candidate
2
+
3
+ Recover event listening after extended disconnects; use bounded idle fallback only while event transport is unavailable. Keep current session authorization on retries.
4
+
1
5
  ## 0.5.0 — release candidate
2
6
 
3
7
  ## 0.6.0
package/README.md CHANGED
@@ -170,10 +170,10 @@ Use `@robono/calling@0.1.3` with `@robono/calling-react-native@0.2.2` when using
170
170
  - Active calls: events trigger state reconciliation immediately; a state health check runs every 3 seconds. The optional native runtime also provides a three-second background recovery check when device timers are suspended; simultaneous refreshes share one request.
171
171
  - Idle in background: call `setForeground(false)`. The event socket and state checks stop. Your backend delivers Robono's signed incoming-call events through your app's APNs/FCM notifications. A WebSocket does not replace push delivery.
172
172
  - Return to foreground: call `setForeground(true)`. The socket reconnects and current state is checked. Do not create a new call to recover a listening session. The optional controller forwards this lifecycle method to the engine.
173
- - Event reconnection: delays start at 1 second, double to 30 seconds (plus up to 20% jitter), honor Retry-After, and stop after eight retries. Foreground re-entry can restart recovery. State recovery stops after six consecutive failed idle checks.
173
+ - Event reconnection: delays start at 1 second, double to 30 seconds (plus up to 20% jitter), honor Retry-After, and continue at a bounded 60-second interval after eight failures. Successful reconnect immediately reconciles current calls, including invitations missed during the outage. Authentication failures stop recovery and require a fresh authorized session.
174
174
  - Expired idle sessions: `onSessionExpired` requests new admission without presenting a failed-call screen. Dispose the expired client and create a new one with a new session from your backend. Use bounded backoff for admission failures. Authorization revocation is not a retryable session expiration. An active call cannot be transferred to a new session.
175
175
 
176
- `createHttpTransport` supplies the event subscription automatically. If wrapping it, preserve `request.subscribe = transport.subscribe`. A custom request without `subscribe`, or `{events:false}`, retains the compatibility polling interval (`pollMs`, default 1000 ms).
176
+ `createHttpTransport` supplies the event subscription automatically. If wrapping it, preserve `request.subscribe = transport.subscribe`. A custom request without `subscribe`, or `{events:false}`, uses a five-second idle fallback; active calls without event support use `pollMs` (default 1000 ms). Event-enabled active calls use a three-second health check. While event transport is unavailable, the five-second idle fallback runs until events reconnect.
177
177
 
178
178
  The 0.1.2 engine had polling and retry backoff but did not include this event transport. No native permissions or rebuild are added by this JavaScript patch to an app already using the native calling package.
179
179
 
package/SBOM.spdx.json CHANGED
@@ -2,10 +2,10 @@
2
2
  "spdxVersion": "SPDX-2.3",
3
3
  "dataLicense": "CC0-1.0",
4
4
  "SPDXID": "SPDXRef-DOCUMENT",
5
- "name": "@robono/calling@0.6.0",
6
- "documentNamespace": "https://robono.com/sbom/calling/0.6.0/c5987c1f423c9f088b93fbd9727b38beca106163",
5
+ "name": "@robono/calling@0.6.1",
6
+ "documentNamespace": "https://robono.com/sbom/calling/0.6.1/f5c50ecfaf7a2db3a710b484bf070d2f193eb3ba",
7
7
  "creationInfo": {
8
- "created": "2026-11-05T15:40:42.440Z",
8
+ "created": "2026-11-05T21:12:01.483Z",
9
9
  "creators": [
10
10
  "Organization: Robono (support@robono.com)"
11
11
  ]
@@ -14,11 +14,11 @@
14
14
  {
15
15
  "name": "@robono/calling",
16
16
  "SPDXID": "SPDXRef-Package",
17
- "versionInfo": "0.6.0",
17
+ "versionInfo": "0.6.1",
18
18
  "downloadLocation": "NOASSERTION",
19
19
  "filesAnalyzed": true,
20
20
  "packageVerificationCode": {
21
- "packageVerificationCodeValue": "c5987c1f423c9f088b93fbd9727b38beca106163"
21
+ "packageVerificationCodeValue": "f5c50ecfaf7a2db3a710b484bf070d2f193eb3ba"
22
22
  },
23
23
  "licenseConcluded": "LicenseRef-RobonoSDK",
24
24
  "licenseDeclared": "LicenseRef-RobonoSDK",
@@ -36,11 +36,11 @@
36
36
  "checksums": [
37
37
  {
38
38
  "algorithm": "SHA256",
39
- "checksumValue": "604c25bc9a6af29a7616e7d0a9a6146187ea34637029393a464c9c305b58acdb"
39
+ "checksumValue": "68c6e8b0a4f9e84628ef323e489f960209b41e8ede7632a8c88352352fa7bb5e"
40
40
  },
41
41
  {
42
42
  "algorithm": "SHA1",
43
- "checksumValue": "d31e5c9fdd55f8af1d09c74220b95e9c40fd336d"
43
+ "checksumValue": "6d56922bf302f5d37cf789c3c8cc0658f03b08a4"
44
44
  }
45
45
  ],
46
46
  "licenseConcluded": "LicenseRef-RobonoSDK",
@@ -74,11 +74,11 @@
74
74
  "checksums": [
75
75
  {
76
76
  "algorithm": "SHA256",
77
- "checksumValue": "9ce33cd3476398ba270658642687ef92c9acaf163819314435786efbca0777c7"
77
+ "checksumValue": "bd5766e55afacca485f559d7c61670196d0c4d17c863af5bafdff0b5855697be"
78
78
  },
79
79
  {
80
80
  "algorithm": "SHA1",
81
- "checksumValue": "1d0174393e6647e3f61e682ce4ff2eeb6cafa59d"
81
+ "checksumValue": "75c19775b2c5b157c8c82fc4fd6b94b6dbe0bda9"
82
82
  }
83
83
  ],
84
84
  "licenseConcluded": "LicenseRef-RobonoSDK",
@@ -112,11 +112,11 @@
112
112
  "checksums": [
113
113
  {
114
114
  "algorithm": "SHA256",
115
- "checksumValue": "e3ef0cf938886194ecf6bea82e87f932775d2674800fa47880883d5a716d1053"
115
+ "checksumValue": "fd43de7c6cff097037cf176ff989d588100df0888e0850c697aa8835ad249c93"
116
116
  },
117
117
  {
118
118
  "algorithm": "SHA1",
119
- "checksumValue": "838931c25b514c9941dac1602c9e93b21fe02d9b"
119
+ "checksumValue": "12065077a00ce6b7f4139087928b411ef219f4f6"
120
120
  }
121
121
  ],
122
122
  "licenseConcluded": "LicenseRef-RobonoSDK",
@@ -150,11 +150,11 @@
150
150
  "checksums": [
151
151
  {
152
152
  "algorithm": "SHA256",
153
- "checksumValue": "cc1bb25f4b12048b6d0254d108323cd0f759ba0bb9124d0f67699e384ff0f4a8"
153
+ "checksumValue": "4ebf2e55d0cdb81fca34d4fea04e259c2f483b1483937e040097438ae121723b"
154
154
  },
155
155
  {
156
156
  "algorithm": "SHA1",
157
- "checksumValue": "2abc7d893f4ed5304b019e4367370abff879ebd0"
157
+ "checksumValue": "380eb91dab062b68104110979a269831bfc3a6c8"
158
158
  }
159
159
  ],
160
160
  "licenseConcluded": "LicenseRef-RobonoSDK",
@@ -226,11 +226,11 @@
226
226
  "checksums": [
227
227
  {
228
228
  "algorithm": "SHA256",
229
- "checksumValue": "43fc3a380533b224941f2c3087905380ed5ad4078493dde16d504d11abc499cb"
229
+ "checksumValue": "7b36a9665086d19d853c418d23729a6c42643c7356197104fd37e1e8ce3af35e"
230
230
  },
231
231
  {
232
232
  "algorithm": "SHA1",
233
- "checksumValue": "51a42dd09073ff8d8522b29c67648f5bc94e56bb"
233
+ "checksumValue": "0085e483137bac9082d105f421a800e881fef819"
234
234
  }
235
235
  ],
236
236
  "licenseConcluded": "LicenseRef-RobonoSDK",
@@ -378,11 +378,11 @@
378
378
  "checksums": [
379
379
  {
380
380
  "algorithm": "SHA256",
381
- "checksumValue": "7fa0e72cfac51ab3a164ad821bf6dd9b157353b58856687f6fc124c0a3a11e08"
381
+ "checksumValue": "a37456ec20501c3272e7129d3496d8c569e196a2f951bf3d968910673651f984"
382
382
  },
383
383
  {
384
384
  "algorithm": "SHA1",
385
- "checksumValue": "d47d93563ec9e50154c9e4f372e13be27867e2c3"
385
+ "checksumValue": "626ccb97b88ca313a2819e7407873dccbe9871b2"
386
386
  }
387
387
  ],
388
388
  "licenseConcluded": "LicenseRef-RobonoSDK",
package/dist/events.js CHANGED
@@ -6,7 +6,7 @@ export function subscribeCallingEvents({baseUrl,token,WebSocketImpl}, {onEvent,o
6
6
  function disconnect(){const previous=socket;socket=null;if(previous){previous.onclose=null;previous.onmessage=null;previous.onerror=()=>{};previous.close();}}
7
7
  function retry(){
8
8
  clear();if(closed||paused||terminal)return;
9
- if(failures>=8){onStatus('disconnected');return;}
9
+ if(failures>=8){onStatus('disconnected');timer=setTimeout(connect,Math.max(60000,retryAfter));retryAfter=0;return;}
10
10
  const delay=Math.max(retryAfter,Math.min(30000,1000*2**failures++)*(1+Math.random()*.2));retryAfter=0;
11
11
  onStatus('reconnecting');timer=setTimeout(connect,delay);
12
12
  }
package/dist/groups.js CHANGED
@@ -42,7 +42,7 @@ export class RobonoGroupCallingClient {
42
42
  setCamera(value){this.state.cameraEnabled=value;this.syncTracks();this.emit();}
43
43
  setVideoForeground(value){this.foreground=value;this.syncTracks();}
44
44
  async availability(connectionId,kind='voice'){if(this.closed)throw Error('calling_client_closed');return this.request('groups/availability',{connectionId,kind});}
45
- async listen(){if(this.started||this.closed)return;this.started=true;this.watch=setInterval(()=>{if(!this.state.preparing&&this.state.call&&this.stream&&(!(this.state.localHeld&&this.holdDeadline>Date.now())&&Date.now()-this.lastSuccess>10000)){void this.leave().catch(()=>{});}},500);if(this.request.subscribe)this.events=this.request.subscribe({onEvent:()=>this.wake(),onStatus:status=>{this.state.listeningStatus=status;this.emit();},onError:e=>this.sessionError(e)});await this.poll();}
45
+ async listen(){if(this.started||this.closed)return;this.started=true;this.watch=setInterval(()=>{if(!this.state.preparing&&this.state.call&&this.stream&&(!(this.state.localHeld&&this.holdDeadline>Date.now())&&Date.now()-this.lastSuccess>10000)){void this.leave().catch(()=>{});}},500);if(this.request.subscribe)this.events=this.request.subscribe({onEvent:()=>this.wake(),onStatus:status=>{this.state.listeningStatus=status;this.emit();if(['reconnecting','disconnected'].includes(status)&&!this.timer&&!this.polling)this.wake();},onError:e=>this.sessionError(e)});await this.poll();}
46
46
  sessionError(e){const active=this.state.call&&this.state.call.state!=='ended';this.state.error=active?e.message:null;this.state.listeningStatus=e.status===403?'access_revoked':'session_expired';this.reset();this.closed=true;clearTimeout(this.timer);clearInterval(this.watch);this.events?.close();this.emit();if(e.status===401)this.onSessionExpired(e);}
47
47
  wake(){if(this.closed)return;clearTimeout(this.timer);if(this.polling||this.state.preparing){this.eventPending=true;return;}void this.poll();}
48
48
  setForeground(value){this.foreground=value;this.setVideoForeground(value);this.events?.setEnabled(value||!!this.state.call&&this.state.call.state!=='ended');if(value)this.wake();else if(!this.state.call||this.state.call.state==='ended')clearTimeout(this.timer);}
@@ -52,7 +52,8 @@ export class RobonoGroupCallingClient {
52
52
  if(this.closed)return;const active=this.state.call&&this.state.call.state!=='ended';
53
53
  if(!this.foreground&&!active){this.events?.setEnabled(false);return;}
54
54
  const pending=this.eventPending;this.eventPending=false;
55
- if(active||!this.events||this.failures>0&&this.failures<6||pending)this.timer=setTimeout(()=>this.poll(),Math.min(2147483647,Math.max(pending?0:this.events?3000:this.pollMs,this.notBefore-Date.now())));
55
+ const degraded=!!this.events&&this.state.listeningStatus!=='listening';
56
+ if(active||!this.events||degraded||this.failures>0&&this.failures<6||pending)this.timer=setTimeout(()=>this.poll(),Math.min(2147483647,Math.max(pending?0:active?(this.events?3000:this.pollMs):5000,this.notBefore-Date.now())));
56
57
  }
57
58
  async refresh(){
58
59
  if(this.closed||this.state.preparing||Date.now()<this.notBefore)return false;
package/dist/index.js CHANGED
@@ -125,7 +125,7 @@ export class RobonoCallingClient {
125
125
  }
126
126
  }, 500);
127
127
  if(this.request.subscribe) this.events=this.request.subscribe({
128
- onEvent:()=>this.wake(), onStatus:status=>{this.state.listeningStatus=status;this.emit();},
128
+ onEvent:()=>this.wake(), onStatus:status=>{this.state.listeningStatus=status;this.emit();if(['reconnecting','disconnected'].includes(status)&&!this.timer&&!this.polling)this.wake();},
129
129
  onError:e=>this.sessionError(e),
130
130
  });
131
131
  await this.poll();
@@ -188,8 +188,9 @@ export class RobonoCallingClient {
188
188
  if(!this.foreground&&!active){this.events?.setEnabled(false);return;}
189
189
  const eventPending=this.eventPending;this.eventPending=false;
190
190
  const retry=this.pollFailures>0&&this.pollFailures<6;
191
- const delay=Math.max(eventPending?0:this.events?3000:this.pollMs,this.pollNotBefore-Date.now());
192
- if(active||!this.events||retry||eventPending)this.timer=setTimeout(()=>this.poll(),Math.min(2147483647,delay));
191
+ const degraded=!!this.events&&this.state.listeningStatus!=='listening';
192
+ const delay=Math.max(eventPending?0:active?(this.events?3000:this.pollMs):5000,this.pollNotBefore-Date.now());
193
+ if(active||!this.events||degraded||retry||eventPending)this.timer=setTimeout(()=>this.poll(),Math.min(2147483647,delay));
193
194
  }
194
195
  async apply(call) {
195
196
  // A pending/late server poll cannot reopen a call the person already ended.
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "provenance": true
18
18
  },
19
19
  "name": "@robono/calling",
20
- "version": "0.6.0",
20
+ "version": "0.6.1",
21
21
  "description": "WebRTC voice, video, group calling and screen sharing within apps and across Robono Bridge networks.",
22
22
  "homepage": "https://robono.com/docs#calling",
23
23
  "repository": {
@@ -61,7 +61,7 @@
61
61
  },
62
62
  "scripts": {
63
63
  "build": "node build.mjs",
64
- "test": "npm run build && node --test test/client.test.mjs test/package.test.mjs test/group-client.test.mjs test/sharing.test.mjs test/telemetry.test.mjs test/events.test.mjs test/messenger.test.mjs test/diagnostics.test.mjs",
64
+ "test": "npm run build && node --test test/client.test.mjs test/package.test.mjs test/group-client.test.mjs test/sharing.test.mjs test/telemetry.test.mjs test/events.test.mjs test/event-recovery.test.mjs test/messenger.test.mjs test/diagnostics.test.mjs",
65
65
  "prepack": "npm run build && node ../../scripts/generate-package-sbom.mjs",
66
66
  "prepublishOnly": "npm test"
67
67
  }