jsgar 4.13.0 → 4.13.2

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.
Files changed (3) hide show
  1. package/dist/gar.umd.js +32 -0
  2. package/gar.js +32 -0
  3. package/package.json +3 -1
package/dist/gar.umd.js CHANGED
@@ -852,6 +852,31 @@
852
852
  */
853
853
  sendMessage(message) {
854
854
  this.messageQueue.push(message);
855
+ // Drain synchronously when possible: background tabs throttle timers — the polling
856
+ // drain loop (and any setInterval sender) can stretch from 100ms to minutes — but
857
+ // synchronous sends from an event or enqueue are never throttled.
858
+ this._flushQueue();
859
+ }
860
+
861
+ /**
862
+ * Synchronously send everything sendable. Safe alongside the polling loop (same JS
863
+ * thread); leaves the null shutdown sentinel for the loop's exit handling.
864
+ */
865
+ _flushQueue() {
866
+ while (this.connected && this.messageQueue.length > 0 && this._isSocketOpen()) {
867
+ if (this.messageQueue[0] === null) return;
868
+ const message = this.messageQueue.shift();
869
+ try {
870
+ this.websocket.send(JSON.stringify(message));
871
+ if (this.logEveryMessage) {
872
+ this.log('INFO', `Sent: ${JSON.stringify(message)}`);
873
+ }
874
+ } catch (e) {
875
+ this.log('WARNING', `Error sending message: ${e.message}`);
876
+ this.halt();
877
+ return;
878
+ }
879
+ }
855
880
  }
856
881
 
857
882
  /**
@@ -979,6 +1004,13 @@
979
1004
  this.invalidatedKeyIds.add(message.value.client_introduced_key_id);
980
1005
  } else if (msgType === 'Heartbeat') {
981
1006
  this.lastHeartbeatTime = Date.now() / 1000;
1007
+ // Echo our own heartbeat on the server's cadence. A hidden tab's throttled timers
1008
+ // can silence the periodic sender far past the server's timeout while this
1009
+ // (unthrottled) receive path keeps delivering — the server then rightly declares
1010
+ // us dead. Event-driven liveness: receive -> reply, flushed synchronously above.
1011
+ if (this.running) {
1012
+ this.sendMessage({ message_type: 'Heartbeat', value: { u_milliseconds: Date.now() } });
1013
+ }
982
1014
  if (this._initialGracePeriod) {
983
1015
  // Resolve first-heartbeat waiters and end the initial grace window
984
1016
  if (this._firstHeartbeatResolve) {
package/gar.js CHANGED
@@ -845,6 +845,31 @@ class GARClient {
845
845
  */
846
846
  sendMessage(message) {
847
847
  this.messageQueue.push(message);
848
+ // Drain synchronously when possible: background tabs throttle timers — the polling
849
+ // drain loop (and any setInterval sender) can stretch from 100ms to minutes — but
850
+ // synchronous sends from an event or enqueue are never throttled.
851
+ this._flushQueue();
852
+ }
853
+
854
+ /**
855
+ * Synchronously send everything sendable. Safe alongside the polling loop (same JS
856
+ * thread); leaves the null shutdown sentinel for the loop's exit handling.
857
+ */
858
+ _flushQueue() {
859
+ while (this.connected && this.messageQueue.length > 0 && this._isSocketOpen()) {
860
+ if (this.messageQueue[0] === null) return;
861
+ const message = this.messageQueue.shift();
862
+ try {
863
+ this.websocket.send(JSON.stringify(message));
864
+ if (this.logEveryMessage) {
865
+ this.log('INFO', `Sent: ${JSON.stringify(message)}`);
866
+ }
867
+ } catch (e) {
868
+ this.log('WARNING', `Error sending message: ${e.message}`);
869
+ this.halt();
870
+ return;
871
+ }
872
+ }
848
873
  }
849
874
 
850
875
  /**
@@ -972,6 +997,13 @@ class GARClient {
972
997
  this.invalidatedKeyIds.add(message.value.client_introduced_key_id);
973
998
  } else if (msgType === 'Heartbeat') {
974
999
  this.lastHeartbeatTime = Date.now() / 1000;
1000
+ // Echo our own heartbeat on the server's cadence. A hidden tab's throttled timers
1001
+ // can silence the periodic sender far past the server's timeout while this
1002
+ // (unthrottled) receive path keeps delivering — the server then rightly declares
1003
+ // us dead. Event-driven liveness: receive -> reply, flushed synchronously above.
1004
+ if (this.running) {
1005
+ this.sendMessage({ message_type: 'Heartbeat', value: { u_milliseconds: Date.now() } });
1006
+ }
975
1007
  if (this._initialGracePeriod) {
976
1008
  // Resolve first-heartbeat waiters and end the initial grace window
977
1009
  if (this._firstHeartbeatResolve) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsgar",
3
- "version": "4.13.0",
3
+ "version": "4.13.2",
4
4
  "description": "A Javascript client for the GAR protocol",
5
5
  "type": "module",
6
6
  "main": "dist/gar.umd.js",
@@ -36,6 +36,8 @@
36
36
  "@eslint/json": "^0.13.2",
37
37
  "@rollup/plugin-commonjs": "^24.0.0",
38
38
  "@rollup/plugin-node-resolve": "^15.0.0",
39
+ "@xterm/addon-fit": "^0.10.0",
40
+ "@xterm/xterm": "^5.5.0",
39
41
  "ag-grid-community": "^34.1.1",
40
42
  "ag-grid-enterprise": "^34.1.1",
41
43
  "eslint": "^9.39.4",