@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.
@@ -98,12 +98,12 @@ installProcessEnvShim();
98
98
  // --- END ENV SHIM ---
99
99
 
100
100
  // This file is auto-generated during build - do not edit manually
101
- // Generated from package.json version: 0.3.5-test.934
101
+ // Generated from package.json version: 0.3.5-test.937
102
102
  /**
103
103
  * The package version, injected at build time.
104
104
  * @internal
105
105
  */
106
- const VERSION = '0.3.5-test.934';
106
+ const VERSION = '0.3.5-test.937';
107
107
 
108
108
  /**
109
109
  * Fame protocol specific error classes with WebSocket close codes and proper inheritance.
@@ -9291,6 +9291,48 @@ class BaseAsyncConnector extends TaskSpawner {
9291
9291
  connector_id: this._connectorFlowId,
9292
9292
  });
9293
9293
  }
9294
+ /**
9295
+ * Pause the connector (suspends heartbeat and housekeeping, but keeps connection alive)
9296
+ */
9297
+ async pause() {
9298
+ logger$$.debug('pausing_connector', {
9299
+ current_state: this._state,
9300
+ connector_id: this._connectorFlowId,
9301
+ });
9302
+ if (this._state !== core.ConnectorState.STARTED) {
9303
+ logger$$.debug('connector_pause_invalid_state', {
9304
+ current_state: this._state,
9305
+ connector_id: this._connectorFlowId,
9306
+ });
9307
+ return;
9308
+ }
9309
+ this._setState(core.ConnectorState.PAUSED);
9310
+ logger$$.debug('connector_paused', {
9311
+ current_state: this._state,
9312
+ connector_id: this._connectorFlowId,
9313
+ });
9314
+ }
9315
+ /**
9316
+ * Resume the connector from paused state
9317
+ */
9318
+ async resume() {
9319
+ logger$$.debug('resuming_connector', {
9320
+ current_state: this._state,
9321
+ connector_id: this._connectorFlowId,
9322
+ });
9323
+ if (this._state !== core.ConnectorState.PAUSED) {
9324
+ logger$$.debug('connector_resume_invalid_state', {
9325
+ current_state: this._state,
9326
+ connector_id: this._connectorFlowId,
9327
+ });
9328
+ return;
9329
+ }
9330
+ this._setState(core.ConnectorState.STARTED);
9331
+ logger$$.debug('connector_resumed', {
9332
+ current_state: this._state,
9333
+ connector_id: this._connectorFlowId,
9334
+ });
9335
+ }
9294
9336
  /**
9295
9337
  * Close the connector with optional code and reason
9296
9338
  */
@@ -9626,7 +9668,7 @@ class BaseAsyncConnector extends TaskSpawner {
9626
9668
  });
9627
9669
  return;
9628
9670
  }
9629
- logger$$.info('connector_shutdown_starting', {
9671
+ logger$$.debug('connector_shutdown_starting', {
9630
9672
  connector_id: this._connectorFlowId,
9631
9673
  connector_type: this.constructor.name,
9632
9674
  code,
@@ -9656,19 +9698,19 @@ class BaseAsyncConnector extends TaskSpawner {
9656
9698
  this._sendPromiseResolve = undefined;
9657
9699
  }
9658
9700
  // Close transport
9659
- logger$$.info('connector_closing_transport', {
9701
+ logger$$.debug('connector_closing_transport', {
9660
9702
  connector_id: this._connectorFlowId,
9661
9703
  connector_type: this.constructor.name,
9662
9704
  timestamp: new Date().toISOString(),
9663
9705
  });
9664
9706
  await this._transportClose(code, reason);
9665
- logger$$.info('connector_transport_closed', {
9707
+ logger$$.debug('connector_transport_closed', {
9666
9708
  connector_id: this._connectorFlowId,
9667
9709
  connector_type: this.constructor.name,
9668
9710
  timestamp: new Date().toISOString(),
9669
9711
  });
9670
9712
  // Shutdown spawned tasks
9671
- logger$$.info('connector_shutting_down_tasks', {
9713
+ logger$$.debug('connector_shutting_down_tasks', {
9672
9714
  connector_id: this._connectorFlowId,
9673
9715
  connector_type: this.constructor.name,
9674
9716
  grace_period_ms: effectiveGracePeriod * 1000,
@@ -9680,7 +9722,7 @@ class BaseAsyncConnector extends TaskSpawner {
9680
9722
  gracePeriod: effectiveGracePeriod * 1000, // Convert to milliseconds
9681
9723
  joinTimeout: this._shutdownJoinTimeout,
9682
9724
  });
9683
- logger$$.info('connector_tasks_shutdown_complete', {
9725
+ logger$$.debug('connector_tasks_shutdown_complete', {
9684
9726
  connector_id: this._connectorFlowId,
9685
9727
  connector_type: this.constructor.name,
9686
9728
  timestamp: new Date().toISOString(),
@@ -9700,7 +9742,7 @@ class BaseAsyncConnector extends TaskSpawner {
9700
9742
  if (this._closeResolver) {
9701
9743
  this._closeResolver();
9702
9744
  }
9703
- logger$$.info('connector_shutdown_complete', {
9745
+ logger$$.debug('connector_shutdown_complete', {
9704
9746
  connector_id: this._connectorFlowId,
9705
9747
  connector_type: this.constructor.name,
9706
9748
  final_state: this._state,
@@ -9841,7 +9883,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9841
9883
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
9842
9884
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
9843
9885
  this.channel = new BroadcastChannel(this.channelName);
9844
- logger$_.info('broadcast_channel_connector_created', {
9886
+ logger$_.debug('broadcast_channel_connector_created', {
9845
9887
  channel: this.channelName,
9846
9888
  connector_id: this.connectorId,
9847
9889
  inbox_capacity: preferredCapacity,
@@ -9922,18 +9964,37 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9922
9964
  // Setup visibility change monitoring
9923
9965
  this.visibilityChangeHandler = () => {
9924
9966
  const isHidden = document.hidden;
9925
- logger$_.info('broadcast_channel_visibility_changed', {
9967
+ logger$_.debug('broadcast_channel_visibility_changed', {
9926
9968
  channel: this.channelName,
9927
9969
  connector_id: this.connectorId,
9928
9970
  visibility: isHidden ? 'hidden' : 'visible',
9929
9971
  timestamp: new Date().toISOString(),
9930
9972
  });
9973
+ // Pause/resume connector based on visibility
9974
+ if (isHidden && this.state === core.ConnectorState.STARTED) {
9975
+ this.pause().catch((err) => {
9976
+ logger$_.warning('broadcast_channel_pause_failed', {
9977
+ channel: this.channelName,
9978
+ connector_id: this.connectorId,
9979
+ error: err instanceof Error ? err.message : String(err),
9980
+ });
9981
+ });
9982
+ }
9983
+ else if (!isHidden && this.state === core.ConnectorState.PAUSED) {
9984
+ this.resume().catch((err) => {
9985
+ logger$_.warning('broadcast_channel_resume_failed', {
9986
+ channel: this.channelName,
9987
+ connector_id: this.connectorId,
9988
+ error: err instanceof Error ? err.message : String(err),
9989
+ });
9990
+ });
9991
+ }
9931
9992
  };
9932
9993
  if (typeof document !== 'undefined') {
9933
9994
  document.addEventListener('visibilitychange', this.visibilityChangeHandler);
9934
9995
  this.visibilityChangeListenerRegistered = true;
9935
9996
  // Log initial state
9936
- logger$_.info('broadcast_channel_initial_visibility', {
9997
+ logger$_.debug('broadcast_channel_initial_visibility', {
9937
9998
  channel: this.channelName,
9938
9999
  connector_id: this.connectorId,
9939
10000
  visibility: document.hidden ? 'hidden' : 'visible',
@@ -9983,7 +10044,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9983
10044
  return await this.inbox.dequeue();
9984
10045
  }
9985
10046
  async _transportClose(code, reason) {
9986
- logger$_.info('broadcast_channel_transport_closing', {
10047
+ logger$_.debug('broadcast_channel_transport_closing', {
9987
10048
  channel: this.channelName,
9988
10049
  connector_id: this.connectorId,
9989
10050
  code,
@@ -9992,14 +10053,14 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9992
10053
  timestamp: new Date().toISOString(),
9993
10054
  });
9994
10055
  if (this.listenerRegistered) {
9995
- logger$_.info('broadcast_channel_removing_listener', {
10056
+ logger$_.debug('broadcast_channel_removing_listener', {
9996
10057
  channel: this.channelName,
9997
10058
  connector_id: this.connectorId,
9998
10059
  timestamp: new Date().toISOString(),
9999
10060
  });
10000
10061
  this.channel.removeEventListener('message', this.onMsg);
10001
10062
  this.listenerRegistered = false;
10002
- logger$_.info('broadcast_channel_listener_removed', {
10063
+ logger$_.debug('broadcast_channel_listener_removed', {
10003
10064
  channel: this.channelName,
10004
10065
  connector_id: this.connectorId,
10005
10066
  timestamp: new Date().toISOString(),
@@ -10010,13 +10071,13 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10010
10071
  this.visibilityChangeListenerRegistered = false;
10011
10072
  this.visibilityChangeHandler = undefined;
10012
10073
  }
10013
- logger$_.info('broadcast_channel_closing', {
10074
+ logger$_.debug('broadcast_channel_closing', {
10014
10075
  channel: this.channelName,
10015
10076
  connector_id: this.connectorId,
10016
10077
  timestamp: new Date().toISOString(),
10017
10078
  });
10018
10079
  this.channel.close();
10019
- logger$_.info('broadcast_channel_closed', {
10080
+ logger$_.debug('broadcast_channel_closed', {
10020
10081
  channel: this.channelName,
10021
10082
  connector_id: this.connectorId,
10022
10083
  timestamp: new Date().toISOString(),
@@ -10107,6 +10168,28 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10107
10168
  }
10108
10169
  return undefined;
10109
10170
  }
10171
+ /**
10172
+ * Override start() to check initial visibility state
10173
+ */
10174
+ async start(inboundHandler) {
10175
+ await super.start(inboundHandler);
10176
+ // After transitioning to STARTED, check if tab is already hidden
10177
+ if (typeof document !== 'undefined' && document.hidden) {
10178
+ logger$_.debug('broadcast_channel_start_in_hidden_tab', {
10179
+ channel: this.channelName,
10180
+ connector_id: this.connectorId,
10181
+ timestamp: new Date().toISOString(),
10182
+ });
10183
+ // Immediately pause if tab is hidden at start time
10184
+ await this.pause().catch((err) => {
10185
+ logger$_.warning('broadcast_channel_initial_pause_failed', {
10186
+ channel: this.channelName,
10187
+ connector_id: this.connectorId,
10188
+ error: err instanceof Error ? err.message : String(err),
10189
+ });
10190
+ });
10191
+ }
10192
+ }
10110
10193
  _trimSeenAcks(now) {
10111
10194
  while (this.seenAckOrder.length > 0) {
10112
10195
  const candidate = this.seenAckOrder[0];
@@ -10817,6 +10900,15 @@ class UpstreamSessionManager extends TaskSpawner {
10817
10900
  if (stopEvt.isSet() || signal?.aborted) {
10818
10901
  break;
10819
10902
  }
10903
+ // Skip heartbeat if connector is paused (e.g., tab is hidden)
10904
+ // Keep ack time current so we don't timeout immediately after resuming
10905
+ if (connector.state === core.ConnectorState.PAUSED) {
10906
+ logger$Z.debug('skipping_heartbeat_connector_paused', {
10907
+ connector_state: connector.state,
10908
+ });
10909
+ this.lastHeartbeatAckTime = Date.now();
10910
+ continue;
10911
+ }
10820
10912
  const envelope = await this.makeHeartbeatEnvelope();
10821
10913
  logger$Z.debug('sending_heartbeat', {
10822
10914
  hb_corr_id: envelope.corrId,
@@ -10838,6 +10930,7 @@ class UpstreamSessionManager extends TaskSpawner {
10838
10930
  throw error;
10839
10931
  }
10840
10932
  await this.node.dispatchEvent('onHeartbeatSent', this.node, envelope);
10933
+ // Don't check heartbeat timeout when paused
10841
10934
  if (this.lastHeartbeatAckTime !== null &&
10842
10935
  Date.now() - this.lastHeartbeatAckTime > graceMs) {
10843
10936
  throw new FameConnectError('missed heartbeat acknowledgement');
@@ -96,12 +96,12 @@ installProcessEnvShim();
96
96
  // --- END ENV SHIM ---
97
97
 
98
98
  // This file is auto-generated during build - do not edit manually
99
- // Generated from package.json version: 0.3.5-test.934
99
+ // Generated from package.json version: 0.3.5-test.937
100
100
  /**
101
101
  * The package version, injected at build time.
102
102
  * @internal
103
103
  */
104
- const VERSION = '0.3.5-test.934';
104
+ const VERSION = '0.3.5-test.937';
105
105
 
106
106
  /**
107
107
  * Fame protocol specific error classes with WebSocket close codes and proper inheritance.
@@ -9289,6 +9289,48 @@ class BaseAsyncConnector extends TaskSpawner {
9289
9289
  connector_id: this._connectorFlowId,
9290
9290
  });
9291
9291
  }
9292
+ /**
9293
+ * Pause the connector (suspends heartbeat and housekeeping, but keeps connection alive)
9294
+ */
9295
+ async pause() {
9296
+ logger$$.debug('pausing_connector', {
9297
+ current_state: this._state,
9298
+ connector_id: this._connectorFlowId,
9299
+ });
9300
+ if (this._state !== ConnectorState.STARTED) {
9301
+ logger$$.debug('connector_pause_invalid_state', {
9302
+ current_state: this._state,
9303
+ connector_id: this._connectorFlowId,
9304
+ });
9305
+ return;
9306
+ }
9307
+ this._setState(ConnectorState.PAUSED);
9308
+ logger$$.debug('connector_paused', {
9309
+ current_state: this._state,
9310
+ connector_id: this._connectorFlowId,
9311
+ });
9312
+ }
9313
+ /**
9314
+ * Resume the connector from paused state
9315
+ */
9316
+ async resume() {
9317
+ logger$$.debug('resuming_connector', {
9318
+ current_state: this._state,
9319
+ connector_id: this._connectorFlowId,
9320
+ });
9321
+ if (this._state !== ConnectorState.PAUSED) {
9322
+ logger$$.debug('connector_resume_invalid_state', {
9323
+ current_state: this._state,
9324
+ connector_id: this._connectorFlowId,
9325
+ });
9326
+ return;
9327
+ }
9328
+ this._setState(ConnectorState.STARTED);
9329
+ logger$$.debug('connector_resumed', {
9330
+ current_state: this._state,
9331
+ connector_id: this._connectorFlowId,
9332
+ });
9333
+ }
9292
9334
  /**
9293
9335
  * Close the connector with optional code and reason
9294
9336
  */
@@ -9624,7 +9666,7 @@ class BaseAsyncConnector extends TaskSpawner {
9624
9666
  });
9625
9667
  return;
9626
9668
  }
9627
- logger$$.info('connector_shutdown_starting', {
9669
+ logger$$.debug('connector_shutdown_starting', {
9628
9670
  connector_id: this._connectorFlowId,
9629
9671
  connector_type: this.constructor.name,
9630
9672
  code,
@@ -9654,19 +9696,19 @@ class BaseAsyncConnector extends TaskSpawner {
9654
9696
  this._sendPromiseResolve = undefined;
9655
9697
  }
9656
9698
  // Close transport
9657
- logger$$.info('connector_closing_transport', {
9699
+ logger$$.debug('connector_closing_transport', {
9658
9700
  connector_id: this._connectorFlowId,
9659
9701
  connector_type: this.constructor.name,
9660
9702
  timestamp: new Date().toISOString(),
9661
9703
  });
9662
9704
  await this._transportClose(code, reason);
9663
- logger$$.info('connector_transport_closed', {
9705
+ logger$$.debug('connector_transport_closed', {
9664
9706
  connector_id: this._connectorFlowId,
9665
9707
  connector_type: this.constructor.name,
9666
9708
  timestamp: new Date().toISOString(),
9667
9709
  });
9668
9710
  // Shutdown spawned tasks
9669
- logger$$.info('connector_shutting_down_tasks', {
9711
+ logger$$.debug('connector_shutting_down_tasks', {
9670
9712
  connector_id: this._connectorFlowId,
9671
9713
  connector_type: this.constructor.name,
9672
9714
  grace_period_ms: effectiveGracePeriod * 1000,
@@ -9678,7 +9720,7 @@ class BaseAsyncConnector extends TaskSpawner {
9678
9720
  gracePeriod: effectiveGracePeriod * 1000, // Convert to milliseconds
9679
9721
  joinTimeout: this._shutdownJoinTimeout,
9680
9722
  });
9681
- logger$$.info('connector_tasks_shutdown_complete', {
9723
+ logger$$.debug('connector_tasks_shutdown_complete', {
9682
9724
  connector_id: this._connectorFlowId,
9683
9725
  connector_type: this.constructor.name,
9684
9726
  timestamp: new Date().toISOString(),
@@ -9698,7 +9740,7 @@ class BaseAsyncConnector extends TaskSpawner {
9698
9740
  if (this._closeResolver) {
9699
9741
  this._closeResolver();
9700
9742
  }
9701
- logger$$.info('connector_shutdown_complete', {
9743
+ logger$$.debug('connector_shutdown_complete', {
9702
9744
  connector_id: this._connectorFlowId,
9703
9745
  connector_type: this.constructor.name,
9704
9746
  final_state: this._state,
@@ -9839,7 +9881,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9839
9881
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
9840
9882
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
9841
9883
  this.channel = new BroadcastChannel(this.channelName);
9842
- logger$_.info('broadcast_channel_connector_created', {
9884
+ logger$_.debug('broadcast_channel_connector_created', {
9843
9885
  channel: this.channelName,
9844
9886
  connector_id: this.connectorId,
9845
9887
  inbox_capacity: preferredCapacity,
@@ -9920,18 +9962,37 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9920
9962
  // Setup visibility change monitoring
9921
9963
  this.visibilityChangeHandler = () => {
9922
9964
  const isHidden = document.hidden;
9923
- logger$_.info('broadcast_channel_visibility_changed', {
9965
+ logger$_.debug('broadcast_channel_visibility_changed', {
9924
9966
  channel: this.channelName,
9925
9967
  connector_id: this.connectorId,
9926
9968
  visibility: isHidden ? 'hidden' : 'visible',
9927
9969
  timestamp: new Date().toISOString(),
9928
9970
  });
9971
+ // Pause/resume connector based on visibility
9972
+ if (isHidden && this.state === ConnectorState.STARTED) {
9973
+ this.pause().catch((err) => {
9974
+ logger$_.warning('broadcast_channel_pause_failed', {
9975
+ channel: this.channelName,
9976
+ connector_id: this.connectorId,
9977
+ error: err instanceof Error ? err.message : String(err),
9978
+ });
9979
+ });
9980
+ }
9981
+ else if (!isHidden && this.state === ConnectorState.PAUSED) {
9982
+ this.resume().catch((err) => {
9983
+ logger$_.warning('broadcast_channel_resume_failed', {
9984
+ channel: this.channelName,
9985
+ connector_id: this.connectorId,
9986
+ error: err instanceof Error ? err.message : String(err),
9987
+ });
9988
+ });
9989
+ }
9929
9990
  };
9930
9991
  if (typeof document !== 'undefined') {
9931
9992
  document.addEventListener('visibilitychange', this.visibilityChangeHandler);
9932
9993
  this.visibilityChangeListenerRegistered = true;
9933
9994
  // Log initial state
9934
- logger$_.info('broadcast_channel_initial_visibility', {
9995
+ logger$_.debug('broadcast_channel_initial_visibility', {
9935
9996
  channel: this.channelName,
9936
9997
  connector_id: this.connectorId,
9937
9998
  visibility: document.hidden ? 'hidden' : 'visible',
@@ -9981,7 +10042,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9981
10042
  return await this.inbox.dequeue();
9982
10043
  }
9983
10044
  async _transportClose(code, reason) {
9984
- logger$_.info('broadcast_channel_transport_closing', {
10045
+ logger$_.debug('broadcast_channel_transport_closing', {
9985
10046
  channel: this.channelName,
9986
10047
  connector_id: this.connectorId,
9987
10048
  code,
@@ -9990,14 +10051,14 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9990
10051
  timestamp: new Date().toISOString(),
9991
10052
  });
9992
10053
  if (this.listenerRegistered) {
9993
- logger$_.info('broadcast_channel_removing_listener', {
10054
+ logger$_.debug('broadcast_channel_removing_listener', {
9994
10055
  channel: this.channelName,
9995
10056
  connector_id: this.connectorId,
9996
10057
  timestamp: new Date().toISOString(),
9997
10058
  });
9998
10059
  this.channel.removeEventListener('message', this.onMsg);
9999
10060
  this.listenerRegistered = false;
10000
- logger$_.info('broadcast_channel_listener_removed', {
10061
+ logger$_.debug('broadcast_channel_listener_removed', {
10001
10062
  channel: this.channelName,
10002
10063
  connector_id: this.connectorId,
10003
10064
  timestamp: new Date().toISOString(),
@@ -10008,13 +10069,13 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10008
10069
  this.visibilityChangeListenerRegistered = false;
10009
10070
  this.visibilityChangeHandler = undefined;
10010
10071
  }
10011
- logger$_.info('broadcast_channel_closing', {
10072
+ logger$_.debug('broadcast_channel_closing', {
10012
10073
  channel: this.channelName,
10013
10074
  connector_id: this.connectorId,
10014
10075
  timestamp: new Date().toISOString(),
10015
10076
  });
10016
10077
  this.channel.close();
10017
- logger$_.info('broadcast_channel_closed', {
10078
+ logger$_.debug('broadcast_channel_closed', {
10018
10079
  channel: this.channelName,
10019
10080
  connector_id: this.connectorId,
10020
10081
  timestamp: new Date().toISOString(),
@@ -10105,6 +10166,28 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10105
10166
  }
10106
10167
  return undefined;
10107
10168
  }
10169
+ /**
10170
+ * Override start() to check initial visibility state
10171
+ */
10172
+ async start(inboundHandler) {
10173
+ await super.start(inboundHandler);
10174
+ // After transitioning to STARTED, check if tab is already hidden
10175
+ if (typeof document !== 'undefined' && document.hidden) {
10176
+ logger$_.debug('broadcast_channel_start_in_hidden_tab', {
10177
+ channel: this.channelName,
10178
+ connector_id: this.connectorId,
10179
+ timestamp: new Date().toISOString(),
10180
+ });
10181
+ // Immediately pause if tab is hidden at start time
10182
+ await this.pause().catch((err) => {
10183
+ logger$_.warning('broadcast_channel_initial_pause_failed', {
10184
+ channel: this.channelName,
10185
+ connector_id: this.connectorId,
10186
+ error: err instanceof Error ? err.message : String(err),
10187
+ });
10188
+ });
10189
+ }
10190
+ }
10108
10191
  _trimSeenAcks(now) {
10109
10192
  while (this.seenAckOrder.length > 0) {
10110
10193
  const candidate = this.seenAckOrder[0];
@@ -10815,6 +10898,15 @@ class UpstreamSessionManager extends TaskSpawner {
10815
10898
  if (stopEvt.isSet() || signal?.aborted) {
10816
10899
  break;
10817
10900
  }
10901
+ // Skip heartbeat if connector is paused (e.g., tab is hidden)
10902
+ // Keep ack time current so we don't timeout immediately after resuming
10903
+ if (connector.state === ConnectorState.PAUSED) {
10904
+ logger$Z.debug('skipping_heartbeat_connector_paused', {
10905
+ connector_state: connector.state,
10906
+ });
10907
+ this.lastHeartbeatAckTime = Date.now();
10908
+ continue;
10909
+ }
10818
10910
  const envelope = await this.makeHeartbeatEnvelope();
10819
10911
  logger$Z.debug('sending_heartbeat', {
10820
10912
  hb_corr_id: envelope.corrId,
@@ -10836,6 +10928,7 @@ class UpstreamSessionManager extends TaskSpawner {
10836
10928
  throw error;
10837
10929
  }
10838
10930
  await this.node.dispatchEvent('onHeartbeatSent', this.node, envelope);
10931
+ // Don't check heartbeat timeout when paused
10839
10932
  if (this.lastHeartbeatAckTime !== null &&
10840
10933
  Date.now() - this.lastHeartbeatAckTime > graceMs) {
10841
10934
  throw new FameConnectError('missed heartbeat acknowledgement');
@@ -169,6 +169,48 @@ class BaseAsyncConnector extends task_spawner_js_1.TaskSpawner {
169
169
  connector_id: this._connectorFlowId,
170
170
  });
171
171
  }
172
+ /**
173
+ * Pause the connector (suspends heartbeat and housekeeping, but keeps connection alive)
174
+ */
175
+ async pause() {
176
+ logger.debug('pausing_connector', {
177
+ current_state: this._state,
178
+ connector_id: this._connectorFlowId,
179
+ });
180
+ if (this._state !== core_1.ConnectorState.STARTED) {
181
+ logger.debug('connector_pause_invalid_state', {
182
+ current_state: this._state,
183
+ connector_id: this._connectorFlowId,
184
+ });
185
+ return;
186
+ }
187
+ this._setState(core_1.ConnectorState.PAUSED);
188
+ logger.debug('connector_paused', {
189
+ current_state: this._state,
190
+ connector_id: this._connectorFlowId,
191
+ });
192
+ }
193
+ /**
194
+ * Resume the connector from paused state
195
+ */
196
+ async resume() {
197
+ logger.debug('resuming_connector', {
198
+ current_state: this._state,
199
+ connector_id: this._connectorFlowId,
200
+ });
201
+ if (this._state !== core_1.ConnectorState.PAUSED) {
202
+ logger.debug('connector_resume_invalid_state', {
203
+ current_state: this._state,
204
+ connector_id: this._connectorFlowId,
205
+ });
206
+ return;
207
+ }
208
+ this._setState(core_1.ConnectorState.STARTED);
209
+ logger.debug('connector_resumed', {
210
+ current_state: this._state,
211
+ connector_id: this._connectorFlowId,
212
+ });
213
+ }
172
214
  /**
173
215
  * Close the connector with optional code and reason
174
216
  */
@@ -504,7 +546,7 @@ class BaseAsyncConnector extends task_spawner_js_1.TaskSpawner {
504
546
  });
505
547
  return;
506
548
  }
507
- logger.info('connector_shutdown_starting', {
549
+ logger.debug('connector_shutdown_starting', {
508
550
  connector_id: this._connectorFlowId,
509
551
  connector_type: this.constructor.name,
510
552
  code,
@@ -534,19 +576,19 @@ class BaseAsyncConnector extends task_spawner_js_1.TaskSpawner {
534
576
  this._sendPromiseResolve = undefined;
535
577
  }
536
578
  // Close transport
537
- logger.info('connector_closing_transport', {
579
+ logger.debug('connector_closing_transport', {
538
580
  connector_id: this._connectorFlowId,
539
581
  connector_type: this.constructor.name,
540
582
  timestamp: new Date().toISOString(),
541
583
  });
542
584
  await this._transportClose(code, reason);
543
- logger.info('connector_transport_closed', {
585
+ logger.debug('connector_transport_closed', {
544
586
  connector_id: this._connectorFlowId,
545
587
  connector_type: this.constructor.name,
546
588
  timestamp: new Date().toISOString(),
547
589
  });
548
590
  // Shutdown spawned tasks
549
- logger.info('connector_shutting_down_tasks', {
591
+ logger.debug('connector_shutting_down_tasks', {
550
592
  connector_id: this._connectorFlowId,
551
593
  connector_type: this.constructor.name,
552
594
  grace_period_ms: effectiveGracePeriod * 1000,
@@ -558,7 +600,7 @@ class BaseAsyncConnector extends task_spawner_js_1.TaskSpawner {
558
600
  gracePeriod: effectiveGracePeriod * 1000, // Convert to milliseconds
559
601
  joinTimeout: this._shutdownJoinTimeout,
560
602
  });
561
- logger.info('connector_tasks_shutdown_complete', {
603
+ logger.debug('connector_tasks_shutdown_complete', {
562
604
  connector_id: this._connectorFlowId,
563
605
  connector_type: this.constructor.name,
564
606
  timestamp: new Date().toISOString(),
@@ -578,7 +620,7 @@ class BaseAsyncConnector extends task_spawner_js_1.TaskSpawner {
578
620
  if (this._closeResolver) {
579
621
  this._closeResolver();
580
622
  }
581
- logger.info('connector_shutdown_complete', {
623
+ logger.debug('connector_shutdown_complete', {
582
624
  connector_id: this._connectorFlowId,
583
625
  connector_type: this.constructor.name,
584
626
  final_state: this._state,