@naylence/runtime 0.3.5-test.934 → 0.3.5-test.937

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.
@@ -14,12 +14,12 @@ var fastify = require('fastify');
14
14
  var websocketPlugin = require('@fastify/websocket');
15
15
 
16
16
  // This file is auto-generated during build - do not edit manually
17
- // Generated from package.json version: 0.3.5-test.934
17
+ // Generated from package.json version: 0.3.5-test.937
18
18
  /**
19
19
  * The package version, injected at build time.
20
20
  * @internal
21
21
  */
22
- const VERSION = '0.3.5-test.934';
22
+ const VERSION = '0.3.5-test.937';
23
23
 
24
24
  /**
25
25
  * Fame protocol specific error classes with WebSocket close codes and proper inheritance.
@@ -9207,6 +9207,48 @@ class BaseAsyncConnector extends TaskSpawner {
9207
9207
  connector_id: this._connectorFlowId,
9208
9208
  });
9209
9209
  }
9210
+ /**
9211
+ * Pause the connector (suspends heartbeat and housekeeping, but keeps connection alive)
9212
+ */
9213
+ async pause() {
9214
+ logger$$.debug('pausing_connector', {
9215
+ current_state: this._state,
9216
+ connector_id: this._connectorFlowId,
9217
+ });
9218
+ if (this._state !== core.ConnectorState.STARTED) {
9219
+ logger$$.debug('connector_pause_invalid_state', {
9220
+ current_state: this._state,
9221
+ connector_id: this._connectorFlowId,
9222
+ });
9223
+ return;
9224
+ }
9225
+ this._setState(core.ConnectorState.PAUSED);
9226
+ logger$$.debug('connector_paused', {
9227
+ current_state: this._state,
9228
+ connector_id: this._connectorFlowId,
9229
+ });
9230
+ }
9231
+ /**
9232
+ * Resume the connector from paused state
9233
+ */
9234
+ async resume() {
9235
+ logger$$.debug('resuming_connector', {
9236
+ current_state: this._state,
9237
+ connector_id: this._connectorFlowId,
9238
+ });
9239
+ if (this._state !== core.ConnectorState.PAUSED) {
9240
+ logger$$.debug('connector_resume_invalid_state', {
9241
+ current_state: this._state,
9242
+ connector_id: this._connectorFlowId,
9243
+ });
9244
+ return;
9245
+ }
9246
+ this._setState(core.ConnectorState.STARTED);
9247
+ logger$$.debug('connector_resumed', {
9248
+ current_state: this._state,
9249
+ connector_id: this._connectorFlowId,
9250
+ });
9251
+ }
9210
9252
  /**
9211
9253
  * Close the connector with optional code and reason
9212
9254
  */
@@ -9542,7 +9584,7 @@ class BaseAsyncConnector extends TaskSpawner {
9542
9584
  });
9543
9585
  return;
9544
9586
  }
9545
- logger$$.info('connector_shutdown_starting', {
9587
+ logger$$.debug('connector_shutdown_starting', {
9546
9588
  connector_id: this._connectorFlowId,
9547
9589
  connector_type: this.constructor.name,
9548
9590
  code,
@@ -9572,19 +9614,19 @@ class BaseAsyncConnector extends TaskSpawner {
9572
9614
  this._sendPromiseResolve = undefined;
9573
9615
  }
9574
9616
  // Close transport
9575
- logger$$.info('connector_closing_transport', {
9617
+ logger$$.debug('connector_closing_transport', {
9576
9618
  connector_id: this._connectorFlowId,
9577
9619
  connector_type: this.constructor.name,
9578
9620
  timestamp: new Date().toISOString(),
9579
9621
  });
9580
9622
  await this._transportClose(code, reason);
9581
- logger$$.info('connector_transport_closed', {
9623
+ logger$$.debug('connector_transport_closed', {
9582
9624
  connector_id: this._connectorFlowId,
9583
9625
  connector_type: this.constructor.name,
9584
9626
  timestamp: new Date().toISOString(),
9585
9627
  });
9586
9628
  // Shutdown spawned tasks
9587
- logger$$.info('connector_shutting_down_tasks', {
9629
+ logger$$.debug('connector_shutting_down_tasks', {
9588
9630
  connector_id: this._connectorFlowId,
9589
9631
  connector_type: this.constructor.name,
9590
9632
  grace_period_ms: effectiveGracePeriod * 1000,
@@ -9596,7 +9638,7 @@ class BaseAsyncConnector extends TaskSpawner {
9596
9638
  gracePeriod: effectiveGracePeriod * 1000, // Convert to milliseconds
9597
9639
  joinTimeout: this._shutdownJoinTimeout,
9598
9640
  });
9599
- logger$$.info('connector_tasks_shutdown_complete', {
9641
+ logger$$.debug('connector_tasks_shutdown_complete', {
9600
9642
  connector_id: this._connectorFlowId,
9601
9643
  connector_type: this.constructor.name,
9602
9644
  timestamp: new Date().toISOString(),
@@ -9616,7 +9658,7 @@ class BaseAsyncConnector extends TaskSpawner {
9616
9658
  if (this._closeResolver) {
9617
9659
  this._closeResolver();
9618
9660
  }
9619
- logger$$.info('connector_shutdown_complete', {
9661
+ logger$$.debug('connector_shutdown_complete', {
9620
9662
  connector_id: this._connectorFlowId,
9621
9663
  connector_type: this.constructor.name,
9622
9664
  final_state: this._state,
@@ -9757,7 +9799,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9757
9799
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
9758
9800
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
9759
9801
  this.channel = new BroadcastChannel(this.channelName);
9760
- logger$_.info('broadcast_channel_connector_created', {
9802
+ logger$_.debug('broadcast_channel_connector_created', {
9761
9803
  channel: this.channelName,
9762
9804
  connector_id: this.connectorId,
9763
9805
  inbox_capacity: preferredCapacity,
@@ -9838,18 +9880,37 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9838
9880
  // Setup visibility change monitoring
9839
9881
  this.visibilityChangeHandler = () => {
9840
9882
  const isHidden = document.hidden;
9841
- logger$_.info('broadcast_channel_visibility_changed', {
9883
+ logger$_.debug('broadcast_channel_visibility_changed', {
9842
9884
  channel: this.channelName,
9843
9885
  connector_id: this.connectorId,
9844
9886
  visibility: isHidden ? 'hidden' : 'visible',
9845
9887
  timestamp: new Date().toISOString(),
9846
9888
  });
9889
+ // Pause/resume connector based on visibility
9890
+ if (isHidden && this.state === core.ConnectorState.STARTED) {
9891
+ this.pause().catch((err) => {
9892
+ logger$_.warning('broadcast_channel_pause_failed', {
9893
+ channel: this.channelName,
9894
+ connector_id: this.connectorId,
9895
+ error: err instanceof Error ? err.message : String(err),
9896
+ });
9897
+ });
9898
+ }
9899
+ else if (!isHidden && this.state === core.ConnectorState.PAUSED) {
9900
+ this.resume().catch((err) => {
9901
+ logger$_.warning('broadcast_channel_resume_failed', {
9902
+ channel: this.channelName,
9903
+ connector_id: this.connectorId,
9904
+ error: err instanceof Error ? err.message : String(err),
9905
+ });
9906
+ });
9907
+ }
9847
9908
  };
9848
9909
  if (typeof document !== 'undefined') {
9849
9910
  document.addEventListener('visibilitychange', this.visibilityChangeHandler);
9850
9911
  this.visibilityChangeListenerRegistered = true;
9851
9912
  // Log initial state
9852
- logger$_.info('broadcast_channel_initial_visibility', {
9913
+ logger$_.debug('broadcast_channel_initial_visibility', {
9853
9914
  channel: this.channelName,
9854
9915
  connector_id: this.connectorId,
9855
9916
  visibility: document.hidden ? 'hidden' : 'visible',
@@ -9899,7 +9960,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9899
9960
  return await this.inbox.dequeue();
9900
9961
  }
9901
9962
  async _transportClose(code, reason) {
9902
- logger$_.info('broadcast_channel_transport_closing', {
9963
+ logger$_.debug('broadcast_channel_transport_closing', {
9903
9964
  channel: this.channelName,
9904
9965
  connector_id: this.connectorId,
9905
9966
  code,
@@ -9908,14 +9969,14 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9908
9969
  timestamp: new Date().toISOString(),
9909
9970
  });
9910
9971
  if (this.listenerRegistered) {
9911
- logger$_.info('broadcast_channel_removing_listener', {
9972
+ logger$_.debug('broadcast_channel_removing_listener', {
9912
9973
  channel: this.channelName,
9913
9974
  connector_id: this.connectorId,
9914
9975
  timestamp: new Date().toISOString(),
9915
9976
  });
9916
9977
  this.channel.removeEventListener('message', this.onMsg);
9917
9978
  this.listenerRegistered = false;
9918
- logger$_.info('broadcast_channel_listener_removed', {
9979
+ logger$_.debug('broadcast_channel_listener_removed', {
9919
9980
  channel: this.channelName,
9920
9981
  connector_id: this.connectorId,
9921
9982
  timestamp: new Date().toISOString(),
@@ -9926,13 +9987,13 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9926
9987
  this.visibilityChangeListenerRegistered = false;
9927
9988
  this.visibilityChangeHandler = undefined;
9928
9989
  }
9929
- logger$_.info('broadcast_channel_closing', {
9990
+ logger$_.debug('broadcast_channel_closing', {
9930
9991
  channel: this.channelName,
9931
9992
  connector_id: this.connectorId,
9932
9993
  timestamp: new Date().toISOString(),
9933
9994
  });
9934
9995
  this.channel.close();
9935
- logger$_.info('broadcast_channel_closed', {
9996
+ logger$_.debug('broadcast_channel_closed', {
9936
9997
  channel: this.channelName,
9937
9998
  connector_id: this.connectorId,
9938
9999
  timestamp: new Date().toISOString(),
@@ -10023,6 +10084,28 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10023
10084
  }
10024
10085
  return undefined;
10025
10086
  }
10087
+ /**
10088
+ * Override start() to check initial visibility state
10089
+ */
10090
+ async start(inboundHandler) {
10091
+ await super.start(inboundHandler);
10092
+ // After transitioning to STARTED, check if tab is already hidden
10093
+ if (typeof document !== 'undefined' && document.hidden) {
10094
+ logger$_.debug('broadcast_channel_start_in_hidden_tab', {
10095
+ channel: this.channelName,
10096
+ connector_id: this.connectorId,
10097
+ timestamp: new Date().toISOString(),
10098
+ });
10099
+ // Immediately pause if tab is hidden at start time
10100
+ await this.pause().catch((err) => {
10101
+ logger$_.warning('broadcast_channel_initial_pause_failed', {
10102
+ channel: this.channelName,
10103
+ connector_id: this.connectorId,
10104
+ error: err instanceof Error ? err.message : String(err),
10105
+ });
10106
+ });
10107
+ }
10108
+ }
10026
10109
  _trimSeenAcks(now) {
10027
10110
  while (this.seenAckOrder.length > 0) {
10028
10111
  const candidate = this.seenAckOrder[0];
@@ -10733,6 +10816,15 @@ class UpstreamSessionManager extends TaskSpawner {
10733
10816
  if (stopEvt.isSet() || signal?.aborted) {
10734
10817
  break;
10735
10818
  }
10819
+ // Skip heartbeat if connector is paused (e.g., tab is hidden)
10820
+ // Keep ack time current so we don't timeout immediately after resuming
10821
+ if (connector.state === core.ConnectorState.PAUSED) {
10822
+ logger$Z.debug('skipping_heartbeat_connector_paused', {
10823
+ connector_state: connector.state,
10824
+ });
10825
+ this.lastHeartbeatAckTime = Date.now();
10826
+ continue;
10827
+ }
10736
10828
  const envelope = await this.makeHeartbeatEnvelope();
10737
10829
  logger$Z.debug('sending_heartbeat', {
10738
10830
  hb_corr_id: envelope.corrId,
@@ -10754,6 +10846,7 @@ class UpstreamSessionManager extends TaskSpawner {
10754
10846
  throw error;
10755
10847
  }
10756
10848
  await this.node.dispatchEvent('onHeartbeatSent', this.node, envelope);
10849
+ // Don't check heartbeat timeout when paused
10757
10850
  if (this.lastHeartbeatAckTime !== null &&
10758
10851
  Date.now() - this.lastHeartbeatAckTime > graceMs) {
10759
10852
  throw new FameConnectError('missed heartbeat acknowledgement');
@@ -13,12 +13,12 @@ import fastify from 'fastify';
13
13
  import websocketPlugin from '@fastify/websocket';
14
14
 
15
15
  // This file is auto-generated during build - do not edit manually
16
- // Generated from package.json version: 0.3.5-test.934
16
+ // Generated from package.json version: 0.3.5-test.937
17
17
  /**
18
18
  * The package version, injected at build time.
19
19
  * @internal
20
20
  */
21
- const VERSION = '0.3.5-test.934';
21
+ const VERSION = '0.3.5-test.937';
22
22
 
23
23
  /**
24
24
  * Fame protocol specific error classes with WebSocket close codes and proper inheritance.
@@ -9206,6 +9206,48 @@ class BaseAsyncConnector extends TaskSpawner {
9206
9206
  connector_id: this._connectorFlowId,
9207
9207
  });
9208
9208
  }
9209
+ /**
9210
+ * Pause the connector (suspends heartbeat and housekeeping, but keeps connection alive)
9211
+ */
9212
+ async pause() {
9213
+ logger$$.debug('pausing_connector', {
9214
+ current_state: this._state,
9215
+ connector_id: this._connectorFlowId,
9216
+ });
9217
+ if (this._state !== ConnectorState.STARTED) {
9218
+ logger$$.debug('connector_pause_invalid_state', {
9219
+ current_state: this._state,
9220
+ connector_id: this._connectorFlowId,
9221
+ });
9222
+ return;
9223
+ }
9224
+ this._setState(ConnectorState.PAUSED);
9225
+ logger$$.debug('connector_paused', {
9226
+ current_state: this._state,
9227
+ connector_id: this._connectorFlowId,
9228
+ });
9229
+ }
9230
+ /**
9231
+ * Resume the connector from paused state
9232
+ */
9233
+ async resume() {
9234
+ logger$$.debug('resuming_connector', {
9235
+ current_state: this._state,
9236
+ connector_id: this._connectorFlowId,
9237
+ });
9238
+ if (this._state !== ConnectorState.PAUSED) {
9239
+ logger$$.debug('connector_resume_invalid_state', {
9240
+ current_state: this._state,
9241
+ connector_id: this._connectorFlowId,
9242
+ });
9243
+ return;
9244
+ }
9245
+ this._setState(ConnectorState.STARTED);
9246
+ logger$$.debug('connector_resumed', {
9247
+ current_state: this._state,
9248
+ connector_id: this._connectorFlowId,
9249
+ });
9250
+ }
9209
9251
  /**
9210
9252
  * Close the connector with optional code and reason
9211
9253
  */
@@ -9541,7 +9583,7 @@ class BaseAsyncConnector extends TaskSpawner {
9541
9583
  });
9542
9584
  return;
9543
9585
  }
9544
- logger$$.info('connector_shutdown_starting', {
9586
+ logger$$.debug('connector_shutdown_starting', {
9545
9587
  connector_id: this._connectorFlowId,
9546
9588
  connector_type: this.constructor.name,
9547
9589
  code,
@@ -9571,19 +9613,19 @@ class BaseAsyncConnector extends TaskSpawner {
9571
9613
  this._sendPromiseResolve = undefined;
9572
9614
  }
9573
9615
  // Close transport
9574
- logger$$.info('connector_closing_transport', {
9616
+ logger$$.debug('connector_closing_transport', {
9575
9617
  connector_id: this._connectorFlowId,
9576
9618
  connector_type: this.constructor.name,
9577
9619
  timestamp: new Date().toISOString(),
9578
9620
  });
9579
9621
  await this._transportClose(code, reason);
9580
- logger$$.info('connector_transport_closed', {
9622
+ logger$$.debug('connector_transport_closed', {
9581
9623
  connector_id: this._connectorFlowId,
9582
9624
  connector_type: this.constructor.name,
9583
9625
  timestamp: new Date().toISOString(),
9584
9626
  });
9585
9627
  // Shutdown spawned tasks
9586
- logger$$.info('connector_shutting_down_tasks', {
9628
+ logger$$.debug('connector_shutting_down_tasks', {
9587
9629
  connector_id: this._connectorFlowId,
9588
9630
  connector_type: this.constructor.name,
9589
9631
  grace_period_ms: effectiveGracePeriod * 1000,
@@ -9595,7 +9637,7 @@ class BaseAsyncConnector extends TaskSpawner {
9595
9637
  gracePeriod: effectiveGracePeriod * 1000, // Convert to milliseconds
9596
9638
  joinTimeout: this._shutdownJoinTimeout,
9597
9639
  });
9598
- logger$$.info('connector_tasks_shutdown_complete', {
9640
+ logger$$.debug('connector_tasks_shutdown_complete', {
9599
9641
  connector_id: this._connectorFlowId,
9600
9642
  connector_type: this.constructor.name,
9601
9643
  timestamp: new Date().toISOString(),
@@ -9615,7 +9657,7 @@ class BaseAsyncConnector extends TaskSpawner {
9615
9657
  if (this._closeResolver) {
9616
9658
  this._closeResolver();
9617
9659
  }
9618
- logger$$.info('connector_shutdown_complete', {
9660
+ logger$$.debug('connector_shutdown_complete', {
9619
9661
  connector_id: this._connectorFlowId,
9620
9662
  connector_type: this.constructor.name,
9621
9663
  final_state: this._state,
@@ -9756,7 +9798,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9756
9798
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
9757
9799
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
9758
9800
  this.channel = new BroadcastChannel(this.channelName);
9759
- logger$_.info('broadcast_channel_connector_created', {
9801
+ logger$_.debug('broadcast_channel_connector_created', {
9760
9802
  channel: this.channelName,
9761
9803
  connector_id: this.connectorId,
9762
9804
  inbox_capacity: preferredCapacity,
@@ -9837,18 +9879,37 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9837
9879
  // Setup visibility change monitoring
9838
9880
  this.visibilityChangeHandler = () => {
9839
9881
  const isHidden = document.hidden;
9840
- logger$_.info('broadcast_channel_visibility_changed', {
9882
+ logger$_.debug('broadcast_channel_visibility_changed', {
9841
9883
  channel: this.channelName,
9842
9884
  connector_id: this.connectorId,
9843
9885
  visibility: isHidden ? 'hidden' : 'visible',
9844
9886
  timestamp: new Date().toISOString(),
9845
9887
  });
9888
+ // Pause/resume connector based on visibility
9889
+ if (isHidden && this.state === ConnectorState.STARTED) {
9890
+ this.pause().catch((err) => {
9891
+ logger$_.warning('broadcast_channel_pause_failed', {
9892
+ channel: this.channelName,
9893
+ connector_id: this.connectorId,
9894
+ error: err instanceof Error ? err.message : String(err),
9895
+ });
9896
+ });
9897
+ }
9898
+ else if (!isHidden && this.state === ConnectorState.PAUSED) {
9899
+ this.resume().catch((err) => {
9900
+ logger$_.warning('broadcast_channel_resume_failed', {
9901
+ channel: this.channelName,
9902
+ connector_id: this.connectorId,
9903
+ error: err instanceof Error ? err.message : String(err),
9904
+ });
9905
+ });
9906
+ }
9846
9907
  };
9847
9908
  if (typeof document !== 'undefined') {
9848
9909
  document.addEventListener('visibilitychange', this.visibilityChangeHandler);
9849
9910
  this.visibilityChangeListenerRegistered = true;
9850
9911
  // Log initial state
9851
- logger$_.info('broadcast_channel_initial_visibility', {
9912
+ logger$_.debug('broadcast_channel_initial_visibility', {
9852
9913
  channel: this.channelName,
9853
9914
  connector_id: this.connectorId,
9854
9915
  visibility: document.hidden ? 'hidden' : 'visible',
@@ -9898,7 +9959,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9898
9959
  return await this.inbox.dequeue();
9899
9960
  }
9900
9961
  async _transportClose(code, reason) {
9901
- logger$_.info('broadcast_channel_transport_closing', {
9962
+ logger$_.debug('broadcast_channel_transport_closing', {
9902
9963
  channel: this.channelName,
9903
9964
  connector_id: this.connectorId,
9904
9965
  code,
@@ -9907,14 +9968,14 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9907
9968
  timestamp: new Date().toISOString(),
9908
9969
  });
9909
9970
  if (this.listenerRegistered) {
9910
- logger$_.info('broadcast_channel_removing_listener', {
9971
+ logger$_.debug('broadcast_channel_removing_listener', {
9911
9972
  channel: this.channelName,
9912
9973
  connector_id: this.connectorId,
9913
9974
  timestamp: new Date().toISOString(),
9914
9975
  });
9915
9976
  this.channel.removeEventListener('message', this.onMsg);
9916
9977
  this.listenerRegistered = false;
9917
- logger$_.info('broadcast_channel_listener_removed', {
9978
+ logger$_.debug('broadcast_channel_listener_removed', {
9918
9979
  channel: this.channelName,
9919
9980
  connector_id: this.connectorId,
9920
9981
  timestamp: new Date().toISOString(),
@@ -9925,13 +9986,13 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9925
9986
  this.visibilityChangeListenerRegistered = false;
9926
9987
  this.visibilityChangeHandler = undefined;
9927
9988
  }
9928
- logger$_.info('broadcast_channel_closing', {
9989
+ logger$_.debug('broadcast_channel_closing', {
9929
9990
  channel: this.channelName,
9930
9991
  connector_id: this.connectorId,
9931
9992
  timestamp: new Date().toISOString(),
9932
9993
  });
9933
9994
  this.channel.close();
9934
- logger$_.info('broadcast_channel_closed', {
9995
+ logger$_.debug('broadcast_channel_closed', {
9935
9996
  channel: this.channelName,
9936
9997
  connector_id: this.connectorId,
9937
9998
  timestamp: new Date().toISOString(),
@@ -10022,6 +10083,28 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10022
10083
  }
10023
10084
  return undefined;
10024
10085
  }
10086
+ /**
10087
+ * Override start() to check initial visibility state
10088
+ */
10089
+ async start(inboundHandler) {
10090
+ await super.start(inboundHandler);
10091
+ // After transitioning to STARTED, check if tab is already hidden
10092
+ if (typeof document !== 'undefined' && document.hidden) {
10093
+ logger$_.debug('broadcast_channel_start_in_hidden_tab', {
10094
+ channel: this.channelName,
10095
+ connector_id: this.connectorId,
10096
+ timestamp: new Date().toISOString(),
10097
+ });
10098
+ // Immediately pause if tab is hidden at start time
10099
+ await this.pause().catch((err) => {
10100
+ logger$_.warning('broadcast_channel_initial_pause_failed', {
10101
+ channel: this.channelName,
10102
+ connector_id: this.connectorId,
10103
+ error: err instanceof Error ? err.message : String(err),
10104
+ });
10105
+ });
10106
+ }
10107
+ }
10025
10108
  _trimSeenAcks(now) {
10026
10109
  while (this.seenAckOrder.length > 0) {
10027
10110
  const candidate = this.seenAckOrder[0];
@@ -10732,6 +10815,15 @@ class UpstreamSessionManager extends TaskSpawner {
10732
10815
  if (stopEvt.isSet() || signal?.aborted) {
10733
10816
  break;
10734
10817
  }
10818
+ // Skip heartbeat if connector is paused (e.g., tab is hidden)
10819
+ // Keep ack time current so we don't timeout immediately after resuming
10820
+ if (connector.state === ConnectorState.PAUSED) {
10821
+ logger$Z.debug('skipping_heartbeat_connector_paused', {
10822
+ connector_state: connector.state,
10823
+ });
10824
+ this.lastHeartbeatAckTime = Date.now();
10825
+ continue;
10826
+ }
10735
10827
  const envelope = await this.makeHeartbeatEnvelope();
10736
10828
  logger$Z.debug('sending_heartbeat', {
10737
10829
  hb_corr_id: envelope.corrId,
@@ -10753,6 +10845,7 @@ class UpstreamSessionManager extends TaskSpawner {
10753
10845
  throw error;
10754
10846
  }
10755
10847
  await this.node.dispatchEvent('onHeartbeatSent', this.node, envelope);
10848
+ // Don't check heartbeat timeout when paused
10756
10849
  if (this.lastHeartbeatAckTime !== null &&
10757
10850
  Date.now() - this.lastHeartbeatAckTime > graceMs) {
10758
10851
  throw new FameConnectError('missed heartbeat acknowledgement');