@naylence/runtime 0.3.5-test.933 → 0.3.5-test.936

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.
@@ -2116,6 +2116,10 @@ class BaseAsyncConnector extends TaskSpawner {
2116
2116
  * Stop the connector gracefully
2117
2117
  */
2118
2118
  async stop() {
2119
+ logger$1f.debug('stopping_connector', {
2120
+ current_state: this._state,
2121
+ connector_id: this._connectorFlowId,
2122
+ });
2119
2123
  if (!core.ConnectorStateUtils.canStop(this._state)) {
2120
2124
  logger$1f.debug('connector_stop_already_stopped', {
2121
2125
  current_state: this._state,
@@ -2128,6 +2132,52 @@ class BaseAsyncConnector extends TaskSpawner {
2128
2132
  if (this._lastError) {
2129
2133
  throw this._lastError;
2130
2134
  }
2135
+ logger$1f.debug('connector_stopped', {
2136
+ current_state: this._state,
2137
+ connector_id: this._connectorFlowId,
2138
+ });
2139
+ }
2140
+ /**
2141
+ * Pause the connector (suspends heartbeat and housekeeping, but keeps connection alive)
2142
+ */
2143
+ async pause() {
2144
+ logger$1f.debug('pausing_connector', {
2145
+ current_state: this._state,
2146
+ connector_id: this._connectorFlowId,
2147
+ });
2148
+ if (this._state !== core.ConnectorState.STARTED) {
2149
+ logger$1f.debug('connector_pause_invalid_state', {
2150
+ current_state: this._state,
2151
+ connector_id: this._connectorFlowId,
2152
+ });
2153
+ return;
2154
+ }
2155
+ this._setState(core.ConnectorState.PAUSED);
2156
+ logger$1f.debug('connector_paused', {
2157
+ current_state: this._state,
2158
+ connector_id: this._connectorFlowId,
2159
+ });
2160
+ }
2161
+ /**
2162
+ * Resume the connector from paused state
2163
+ */
2164
+ async resume() {
2165
+ logger$1f.debug('resuming_connector', {
2166
+ current_state: this._state,
2167
+ connector_id: this._connectorFlowId,
2168
+ });
2169
+ if (this._state !== core.ConnectorState.PAUSED) {
2170
+ logger$1f.debug('connector_resume_invalid_state', {
2171
+ current_state: this._state,
2172
+ connector_id: this._connectorFlowId,
2173
+ });
2174
+ return;
2175
+ }
2176
+ this._setState(core.ConnectorState.STARTED);
2177
+ logger$1f.debug('connector_resumed', {
2178
+ current_state: this._state,
2179
+ connector_id: this._connectorFlowId,
2180
+ });
2131
2181
  }
2132
2182
  /**
2133
2183
  * Close the connector with optional code and reason
@@ -2458,8 +2508,21 @@ class BaseAsyncConnector extends TaskSpawner {
2458
2508
  */
2459
2509
  async _shutdown(code, reason, gracePeriod, exc) {
2460
2510
  if (this._closed) {
2511
+ logger$1f.debug('shutdown_already_closed', {
2512
+ connector_id: this._connectorFlowId,
2513
+ current_state: this._state,
2514
+ });
2461
2515
  return;
2462
2516
  }
2517
+ logger$1f.info('connector_shutdown_starting', {
2518
+ connector_id: this._connectorFlowId,
2519
+ connector_type: this.constructor.name,
2520
+ code,
2521
+ reason,
2522
+ current_state: this._state,
2523
+ has_error: !!exc,
2524
+ timestamp: new Date().toISOString(),
2525
+ });
2463
2526
  this._closed = true;
2464
2527
  this._closeCode = code;
2465
2528
  this._closeReason = reason;
@@ -2481,16 +2544,39 @@ class BaseAsyncConnector extends TaskSpawner {
2481
2544
  this._sendPromiseResolve = undefined;
2482
2545
  }
2483
2546
  // Close transport
2547
+ logger$1f.info('connector_closing_transport', {
2548
+ connector_id: this._connectorFlowId,
2549
+ connector_type: this.constructor.name,
2550
+ timestamp: new Date().toISOString(),
2551
+ });
2484
2552
  await this._transportClose(code, reason);
2553
+ logger$1f.info('connector_transport_closed', {
2554
+ connector_id: this._connectorFlowId,
2555
+ connector_type: this.constructor.name,
2556
+ timestamp: new Date().toISOString(),
2557
+ });
2485
2558
  // Shutdown spawned tasks
2559
+ logger$1f.info('connector_shutting_down_tasks', {
2560
+ connector_id: this._connectorFlowId,
2561
+ connector_type: this.constructor.name,
2562
+ grace_period_ms: effectiveGracePeriod * 1000,
2563
+ join_timeout_ms: this._shutdownJoinTimeout,
2564
+ timestamp: new Date().toISOString(),
2565
+ });
2486
2566
  try {
2487
2567
  await this.shutdownTasks({
2488
2568
  gracePeriod: effectiveGracePeriod * 1000, // Convert to milliseconds
2489
2569
  joinTimeout: this._shutdownJoinTimeout,
2490
2570
  });
2571
+ logger$1f.info('connector_tasks_shutdown_complete', {
2572
+ connector_id: this._connectorFlowId,
2573
+ connector_type: this.constructor.name,
2574
+ timestamp: new Date().toISOString(),
2575
+ });
2491
2576
  }
2492
2577
  catch (error) {
2493
2578
  logger$1f.warning('task_shutdown_error', {
2579
+ connector_id: this._connectorFlowId,
2494
2580
  error: error instanceof Error ? error.message : String(error),
2495
2581
  });
2496
2582
  }
@@ -2502,6 +2588,12 @@ class BaseAsyncConnector extends TaskSpawner {
2502
2588
  if (this._closeResolver) {
2503
2589
  this._closeResolver();
2504
2590
  }
2591
+ logger$1f.info('connector_shutdown_complete', {
2592
+ connector_id: this._connectorFlowId,
2593
+ connector_type: this.constructor.name,
2594
+ final_state: this._state,
2595
+ timestamp: new Date().toISOString(),
2596
+ });
2505
2597
  }
2506
2598
  /**
2507
2599
  * Close the underlying transport
@@ -5386,12 +5478,12 @@ for (const [name, config] of Object.entries(SQLITE_PROFILES)) {
5386
5478
  }
5387
5479
 
5388
5480
  // This file is auto-generated during build - do not edit manually
5389
- // Generated from package.json version: 0.3.5-test.933
5481
+ // Generated from package.json version: 0.3.5-test.936
5390
5482
  /**
5391
5483
  * The package version, injected at build time.
5392
5484
  * @internal
5393
5485
  */
5394
- const VERSION = '0.3.5-test.933';
5486
+ const VERSION = '0.3.5-test.936';
5395
5487
 
5396
5488
  /**
5397
5489
  * Fame errors module - Fame protocol specific error classes
@@ -11444,12 +11536,22 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
11444
11536
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
11445
11537
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
11446
11538
  this.channel = new BroadcastChannel(this.channelName);
11447
- logger$10.debug('broadcast_channel_connector_initialized', {
11539
+ logger$10.info('broadcast_channel_connector_created', {
11448
11540
  channel: this.channelName,
11449
11541
  connector_id: this.connectorId,
11450
11542
  inbox_capacity: preferredCapacity,
11543
+ timestamp: new Date().toISOString(),
11451
11544
  });
11452
11545
  this.onMsg = (event) => {
11546
+ // Guard: Don't process if listener was unregistered
11547
+ if (!this.listenerRegistered) {
11548
+ logger$10.warning('broadcast_channel_message_after_unregister', {
11549
+ channel: this.channelName,
11550
+ connector_id: this.connectorId,
11551
+ timestamp: new Date().toISOString(),
11552
+ });
11553
+ return;
11554
+ }
11453
11555
  const message = event.data;
11454
11556
  logger$10.debug('broadcast_channel_raw_event', {
11455
11557
  channel: this.channelName,
@@ -11521,6 +11623,25 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
11521
11623
  visibility: isHidden ? 'hidden' : 'visible',
11522
11624
  timestamp: new Date().toISOString(),
11523
11625
  });
11626
+ // Pause/resume connector based on visibility
11627
+ if (isHidden && this.state === core.ConnectorState.STARTED) {
11628
+ this.pause().catch((err) => {
11629
+ logger$10.warning('broadcast_channel_pause_failed', {
11630
+ channel: this.channelName,
11631
+ connector_id: this.connectorId,
11632
+ error: err instanceof Error ? err.message : String(err),
11633
+ });
11634
+ });
11635
+ }
11636
+ else if (!isHidden && this.state === core.ConnectorState.PAUSED) {
11637
+ this.resume().catch((err) => {
11638
+ logger$10.warning('broadcast_channel_resume_failed', {
11639
+ channel: this.channelName,
11640
+ connector_id: this.connectorId,
11641
+ error: err instanceof Error ? err.message : String(err),
11642
+ });
11643
+ });
11644
+ }
11524
11645
  };
11525
11646
  if (typeof document !== 'undefined') {
11526
11647
  document.addEventListener('visibilitychange', this.visibilityChangeHandler);
@@ -11576,16 +11697,44 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
11576
11697
  return await this.inbox.dequeue();
11577
11698
  }
11578
11699
  async _transportClose(code, reason) {
11700
+ logger$10.info('broadcast_channel_transport_closing', {
11701
+ channel: this.channelName,
11702
+ connector_id: this.connectorId,
11703
+ code,
11704
+ reason,
11705
+ listener_registered: this.listenerRegistered,
11706
+ timestamp: new Date().toISOString(),
11707
+ });
11579
11708
  if (this.listenerRegistered) {
11709
+ logger$10.info('broadcast_channel_removing_listener', {
11710
+ channel: this.channelName,
11711
+ connector_id: this.connectorId,
11712
+ timestamp: new Date().toISOString(),
11713
+ });
11580
11714
  this.channel.removeEventListener('message', this.onMsg);
11581
11715
  this.listenerRegistered = false;
11716
+ logger$10.info('broadcast_channel_listener_removed', {
11717
+ channel: this.channelName,
11718
+ connector_id: this.connectorId,
11719
+ timestamp: new Date().toISOString(),
11720
+ });
11582
11721
  }
11583
11722
  if (this.visibilityChangeListenerRegistered && this.visibilityChangeHandler && typeof document !== 'undefined') {
11584
11723
  document.removeEventListener('visibilitychange', this.visibilityChangeHandler);
11585
11724
  this.visibilityChangeListenerRegistered = false;
11586
11725
  this.visibilityChangeHandler = undefined;
11587
11726
  }
11727
+ logger$10.info('broadcast_channel_closing', {
11728
+ channel: this.channelName,
11729
+ connector_id: this.connectorId,
11730
+ timestamp: new Date().toISOString(),
11731
+ });
11588
11732
  this.channel.close();
11733
+ logger$10.info('broadcast_channel_closed', {
11734
+ channel: this.channelName,
11735
+ connector_id: this.connectorId,
11736
+ timestamp: new Date().toISOString(),
11737
+ });
11589
11738
  const closeCode = typeof code === 'number' ? code : 1000;
11590
11739
  const closeReason = typeof reason === 'string' && reason.length > 0 ? reason : 'closed';
11591
11740
  const shutdownError = new FameTransportClose(closeReason, closeCode);
@@ -12188,7 +12337,22 @@ class UpstreamSessionManager extends TaskSpawner {
12188
12337
  this.currentStopSubtasks = null;
12189
12338
  await Promise.allSettled(tasks.map((task) => task.promise));
12190
12339
  if (this.connector) {
12191
- await this.connector.stop().catch(() => undefined);
12340
+ logger$$.info('upstream_stopping_old_connector', {
12341
+ connect_epoch: this.connectEpoch,
12342
+ target_system_id: this.targetSystemId,
12343
+ timestamp: new Date().toISOString(),
12344
+ });
12345
+ await this.connector.stop().catch((err) => {
12346
+ logger$$.warning('upstream_connector_stop_error', {
12347
+ connect_epoch: this.connectEpoch,
12348
+ error: err instanceof Error ? err.message : String(err),
12349
+ });
12350
+ });
12351
+ logger$$.info('upstream_old_connector_stopped', {
12352
+ connect_epoch: this.connectEpoch,
12353
+ target_system_id: this.targetSystemId,
12354
+ timestamp: new Date().toISOString(),
12355
+ });
12192
12356
  this.connector = null;
12193
12357
  }
12194
12358
  }
@@ -12322,6 +12486,15 @@ class UpstreamSessionManager extends TaskSpawner {
12322
12486
  if (stopEvt.isSet() || signal?.aborted) {
12323
12487
  break;
12324
12488
  }
12489
+ // Skip heartbeat if connector is paused (e.g., tab is hidden)
12490
+ // Keep ack time current so we don't timeout immediately after resuming
12491
+ if (connector.state === core.ConnectorState.PAUSED) {
12492
+ logger$$.debug('skipping_heartbeat_connector_paused', {
12493
+ connector_state: connector.state,
12494
+ });
12495
+ this.lastHeartbeatAckTime = Date.now();
12496
+ continue;
12497
+ }
12325
12498
  const envelope = await this.makeHeartbeatEnvelope();
12326
12499
  logger$$.debug('sending_heartbeat', {
12327
12500
  hb_corr_id: envelope.corrId,
@@ -12343,6 +12516,7 @@ class UpstreamSessionManager extends TaskSpawner {
12343
12516
  throw error;
12344
12517
  }
12345
12518
  await this.node.dispatchEvent('onHeartbeatSent', this.node, envelope);
12519
+ // Don't check heartbeat timeout when paused
12346
12520
  if (this.lastHeartbeatAckTime !== null &&
12347
12521
  Date.now() - this.lastHeartbeatAckTime > graceMs) {
12348
12522
  throw new FameConnectError('missed heartbeat acknowledgement');
@@ -2115,6 +2115,10 @@ class BaseAsyncConnector extends TaskSpawner {
2115
2115
  * Stop the connector gracefully
2116
2116
  */
2117
2117
  async stop() {
2118
+ logger$1f.debug('stopping_connector', {
2119
+ current_state: this._state,
2120
+ connector_id: this._connectorFlowId,
2121
+ });
2118
2122
  if (!ConnectorStateUtils.canStop(this._state)) {
2119
2123
  logger$1f.debug('connector_stop_already_stopped', {
2120
2124
  current_state: this._state,
@@ -2127,6 +2131,52 @@ class BaseAsyncConnector extends TaskSpawner {
2127
2131
  if (this._lastError) {
2128
2132
  throw this._lastError;
2129
2133
  }
2134
+ logger$1f.debug('connector_stopped', {
2135
+ current_state: this._state,
2136
+ connector_id: this._connectorFlowId,
2137
+ });
2138
+ }
2139
+ /**
2140
+ * Pause the connector (suspends heartbeat and housekeeping, but keeps connection alive)
2141
+ */
2142
+ async pause() {
2143
+ logger$1f.debug('pausing_connector', {
2144
+ current_state: this._state,
2145
+ connector_id: this._connectorFlowId,
2146
+ });
2147
+ if (this._state !== ConnectorState.STARTED) {
2148
+ logger$1f.debug('connector_pause_invalid_state', {
2149
+ current_state: this._state,
2150
+ connector_id: this._connectorFlowId,
2151
+ });
2152
+ return;
2153
+ }
2154
+ this._setState(ConnectorState.PAUSED);
2155
+ logger$1f.debug('connector_paused', {
2156
+ current_state: this._state,
2157
+ connector_id: this._connectorFlowId,
2158
+ });
2159
+ }
2160
+ /**
2161
+ * Resume the connector from paused state
2162
+ */
2163
+ async resume() {
2164
+ logger$1f.debug('resuming_connector', {
2165
+ current_state: this._state,
2166
+ connector_id: this._connectorFlowId,
2167
+ });
2168
+ if (this._state !== ConnectorState.PAUSED) {
2169
+ logger$1f.debug('connector_resume_invalid_state', {
2170
+ current_state: this._state,
2171
+ connector_id: this._connectorFlowId,
2172
+ });
2173
+ return;
2174
+ }
2175
+ this._setState(ConnectorState.STARTED);
2176
+ logger$1f.debug('connector_resumed', {
2177
+ current_state: this._state,
2178
+ connector_id: this._connectorFlowId,
2179
+ });
2130
2180
  }
2131
2181
  /**
2132
2182
  * Close the connector with optional code and reason
@@ -2457,8 +2507,21 @@ class BaseAsyncConnector extends TaskSpawner {
2457
2507
  */
2458
2508
  async _shutdown(code, reason, gracePeriod, exc) {
2459
2509
  if (this._closed) {
2510
+ logger$1f.debug('shutdown_already_closed', {
2511
+ connector_id: this._connectorFlowId,
2512
+ current_state: this._state,
2513
+ });
2460
2514
  return;
2461
2515
  }
2516
+ logger$1f.info('connector_shutdown_starting', {
2517
+ connector_id: this._connectorFlowId,
2518
+ connector_type: this.constructor.name,
2519
+ code,
2520
+ reason,
2521
+ current_state: this._state,
2522
+ has_error: !!exc,
2523
+ timestamp: new Date().toISOString(),
2524
+ });
2462
2525
  this._closed = true;
2463
2526
  this._closeCode = code;
2464
2527
  this._closeReason = reason;
@@ -2480,16 +2543,39 @@ class BaseAsyncConnector extends TaskSpawner {
2480
2543
  this._sendPromiseResolve = undefined;
2481
2544
  }
2482
2545
  // Close transport
2546
+ logger$1f.info('connector_closing_transport', {
2547
+ connector_id: this._connectorFlowId,
2548
+ connector_type: this.constructor.name,
2549
+ timestamp: new Date().toISOString(),
2550
+ });
2483
2551
  await this._transportClose(code, reason);
2552
+ logger$1f.info('connector_transport_closed', {
2553
+ connector_id: this._connectorFlowId,
2554
+ connector_type: this.constructor.name,
2555
+ timestamp: new Date().toISOString(),
2556
+ });
2484
2557
  // Shutdown spawned tasks
2558
+ logger$1f.info('connector_shutting_down_tasks', {
2559
+ connector_id: this._connectorFlowId,
2560
+ connector_type: this.constructor.name,
2561
+ grace_period_ms: effectiveGracePeriod * 1000,
2562
+ join_timeout_ms: this._shutdownJoinTimeout,
2563
+ timestamp: new Date().toISOString(),
2564
+ });
2485
2565
  try {
2486
2566
  await this.shutdownTasks({
2487
2567
  gracePeriod: effectiveGracePeriod * 1000, // Convert to milliseconds
2488
2568
  joinTimeout: this._shutdownJoinTimeout,
2489
2569
  });
2570
+ logger$1f.info('connector_tasks_shutdown_complete', {
2571
+ connector_id: this._connectorFlowId,
2572
+ connector_type: this.constructor.name,
2573
+ timestamp: new Date().toISOString(),
2574
+ });
2490
2575
  }
2491
2576
  catch (error) {
2492
2577
  logger$1f.warning('task_shutdown_error', {
2578
+ connector_id: this._connectorFlowId,
2493
2579
  error: error instanceof Error ? error.message : String(error),
2494
2580
  });
2495
2581
  }
@@ -2501,6 +2587,12 @@ class BaseAsyncConnector extends TaskSpawner {
2501
2587
  if (this._closeResolver) {
2502
2588
  this._closeResolver();
2503
2589
  }
2590
+ logger$1f.info('connector_shutdown_complete', {
2591
+ connector_id: this._connectorFlowId,
2592
+ connector_type: this.constructor.name,
2593
+ final_state: this._state,
2594
+ timestamp: new Date().toISOString(),
2595
+ });
2504
2596
  }
2505
2597
  /**
2506
2598
  * Close the underlying transport
@@ -5385,12 +5477,12 @@ for (const [name, config] of Object.entries(SQLITE_PROFILES)) {
5385
5477
  }
5386
5478
 
5387
5479
  // This file is auto-generated during build - do not edit manually
5388
- // Generated from package.json version: 0.3.5-test.933
5480
+ // Generated from package.json version: 0.3.5-test.936
5389
5481
  /**
5390
5482
  * The package version, injected at build time.
5391
5483
  * @internal
5392
5484
  */
5393
- const VERSION = '0.3.5-test.933';
5485
+ const VERSION = '0.3.5-test.936';
5394
5486
 
5395
5487
  /**
5396
5488
  * Fame errors module - Fame protocol specific error classes
@@ -11443,12 +11535,22 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
11443
11535
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
11444
11536
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
11445
11537
  this.channel = new BroadcastChannel(this.channelName);
11446
- logger$10.debug('broadcast_channel_connector_initialized', {
11538
+ logger$10.info('broadcast_channel_connector_created', {
11447
11539
  channel: this.channelName,
11448
11540
  connector_id: this.connectorId,
11449
11541
  inbox_capacity: preferredCapacity,
11542
+ timestamp: new Date().toISOString(),
11450
11543
  });
11451
11544
  this.onMsg = (event) => {
11545
+ // Guard: Don't process if listener was unregistered
11546
+ if (!this.listenerRegistered) {
11547
+ logger$10.warning('broadcast_channel_message_after_unregister', {
11548
+ channel: this.channelName,
11549
+ connector_id: this.connectorId,
11550
+ timestamp: new Date().toISOString(),
11551
+ });
11552
+ return;
11553
+ }
11452
11554
  const message = event.data;
11453
11555
  logger$10.debug('broadcast_channel_raw_event', {
11454
11556
  channel: this.channelName,
@@ -11520,6 +11622,25 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
11520
11622
  visibility: isHidden ? 'hidden' : 'visible',
11521
11623
  timestamp: new Date().toISOString(),
11522
11624
  });
11625
+ // Pause/resume connector based on visibility
11626
+ if (isHidden && this.state === ConnectorState.STARTED) {
11627
+ this.pause().catch((err) => {
11628
+ logger$10.warning('broadcast_channel_pause_failed', {
11629
+ channel: this.channelName,
11630
+ connector_id: this.connectorId,
11631
+ error: err instanceof Error ? err.message : String(err),
11632
+ });
11633
+ });
11634
+ }
11635
+ else if (!isHidden && this.state === ConnectorState.PAUSED) {
11636
+ this.resume().catch((err) => {
11637
+ logger$10.warning('broadcast_channel_resume_failed', {
11638
+ channel: this.channelName,
11639
+ connector_id: this.connectorId,
11640
+ error: err instanceof Error ? err.message : String(err),
11641
+ });
11642
+ });
11643
+ }
11523
11644
  };
11524
11645
  if (typeof document !== 'undefined') {
11525
11646
  document.addEventListener('visibilitychange', this.visibilityChangeHandler);
@@ -11575,16 +11696,44 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
11575
11696
  return await this.inbox.dequeue();
11576
11697
  }
11577
11698
  async _transportClose(code, reason) {
11699
+ logger$10.info('broadcast_channel_transport_closing', {
11700
+ channel: this.channelName,
11701
+ connector_id: this.connectorId,
11702
+ code,
11703
+ reason,
11704
+ listener_registered: this.listenerRegistered,
11705
+ timestamp: new Date().toISOString(),
11706
+ });
11578
11707
  if (this.listenerRegistered) {
11708
+ logger$10.info('broadcast_channel_removing_listener', {
11709
+ channel: this.channelName,
11710
+ connector_id: this.connectorId,
11711
+ timestamp: new Date().toISOString(),
11712
+ });
11579
11713
  this.channel.removeEventListener('message', this.onMsg);
11580
11714
  this.listenerRegistered = false;
11715
+ logger$10.info('broadcast_channel_listener_removed', {
11716
+ channel: this.channelName,
11717
+ connector_id: this.connectorId,
11718
+ timestamp: new Date().toISOString(),
11719
+ });
11581
11720
  }
11582
11721
  if (this.visibilityChangeListenerRegistered && this.visibilityChangeHandler && typeof document !== 'undefined') {
11583
11722
  document.removeEventListener('visibilitychange', this.visibilityChangeHandler);
11584
11723
  this.visibilityChangeListenerRegistered = false;
11585
11724
  this.visibilityChangeHandler = undefined;
11586
11725
  }
11726
+ logger$10.info('broadcast_channel_closing', {
11727
+ channel: this.channelName,
11728
+ connector_id: this.connectorId,
11729
+ timestamp: new Date().toISOString(),
11730
+ });
11587
11731
  this.channel.close();
11732
+ logger$10.info('broadcast_channel_closed', {
11733
+ channel: this.channelName,
11734
+ connector_id: this.connectorId,
11735
+ timestamp: new Date().toISOString(),
11736
+ });
11588
11737
  const closeCode = typeof code === 'number' ? code : 1000;
11589
11738
  const closeReason = typeof reason === 'string' && reason.length > 0 ? reason : 'closed';
11590
11739
  const shutdownError = new FameTransportClose(closeReason, closeCode);
@@ -12187,7 +12336,22 @@ class UpstreamSessionManager extends TaskSpawner {
12187
12336
  this.currentStopSubtasks = null;
12188
12337
  await Promise.allSettled(tasks.map((task) => task.promise));
12189
12338
  if (this.connector) {
12190
- await this.connector.stop().catch(() => undefined);
12339
+ logger$$.info('upstream_stopping_old_connector', {
12340
+ connect_epoch: this.connectEpoch,
12341
+ target_system_id: this.targetSystemId,
12342
+ timestamp: new Date().toISOString(),
12343
+ });
12344
+ await this.connector.stop().catch((err) => {
12345
+ logger$$.warning('upstream_connector_stop_error', {
12346
+ connect_epoch: this.connectEpoch,
12347
+ error: err instanceof Error ? err.message : String(err),
12348
+ });
12349
+ });
12350
+ logger$$.info('upstream_old_connector_stopped', {
12351
+ connect_epoch: this.connectEpoch,
12352
+ target_system_id: this.targetSystemId,
12353
+ timestamp: new Date().toISOString(),
12354
+ });
12191
12355
  this.connector = null;
12192
12356
  }
12193
12357
  }
@@ -12321,6 +12485,15 @@ class UpstreamSessionManager extends TaskSpawner {
12321
12485
  if (stopEvt.isSet() || signal?.aborted) {
12322
12486
  break;
12323
12487
  }
12488
+ // Skip heartbeat if connector is paused (e.g., tab is hidden)
12489
+ // Keep ack time current so we don't timeout immediately after resuming
12490
+ if (connector.state === ConnectorState.PAUSED) {
12491
+ logger$$.debug('skipping_heartbeat_connector_paused', {
12492
+ connector_state: connector.state,
12493
+ });
12494
+ this.lastHeartbeatAckTime = Date.now();
12495
+ continue;
12496
+ }
12324
12497
  const envelope = await this.makeHeartbeatEnvelope();
12325
12498
  logger$$.debug('sending_heartbeat', {
12326
12499
  hb_corr_id: envelope.corrId,
@@ -12342,6 +12515,7 @@ class UpstreamSessionManager extends TaskSpawner {
12342
12515
  throw error;
12343
12516
  }
12344
12517
  await this.node.dispatchEvent('onHeartbeatSent', this.node, envelope);
12518
+ // Don't check heartbeat timeout when paused
12345
12519
  if (this.lastHeartbeatAckTime !== null &&
12346
12520
  Date.now() - this.lastHeartbeatAckTime > graceMs) {
12347
12521
  throw new FameConnectError('missed heartbeat acknowledgement');
@@ -103,6 +103,14 @@ export declare abstract class BaseAsyncConnector extends TaskSpawner implements
103
103
  * Stop the connector gracefully
104
104
  */
105
105
  stop(): Promise<void>;
106
+ /**
107
+ * Pause the connector (suspends heartbeat and housekeeping, but keeps connection alive)
108
+ */
109
+ pause(): Promise<void>;
110
+ /**
111
+ * Resume the connector from paused state
112
+ */
113
+ resume(): Promise<void>;
106
114
  /**
107
115
  * Close the connector with optional code and reason
108
116
  */
@@ -2,4 +2,4 @@
2
2
  * The package version, injected at build time.
3
3
  * @internal
4
4
  */
5
- export declare const VERSION = "0.3.5-test.933";
5
+ export declare const VERSION = "0.3.5-test.936";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naylence/runtime",
3
- "version": "0.3.5-test.933",
3
+ "version": "0.3.5-test.936",
4
4
  "type": "module",
5
5
  "description": "Naylence Runtime - Complete TypeScript runtime",
6
6
  "author": "Naylence Dev <naylencedev@gmail.com>",
@@ -176,7 +176,7 @@
176
176
  "dependencies": {
177
177
  "@fastify/formbody": "^8.0.2",
178
178
  "@fastify/websocket": "^11.2.0",
179
- "@naylence/core": "^0.3.4",
179
+ "@naylence/core": "^0.3.5",
180
180
  "@noble/ciphers": "^2.0.1",
181
181
  "@noble/curves": "^2.0.1",
182
182
  "@noble/ed25519": "^3.0.0",