@naylence/runtime 0.3.5-test.934 → 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.
- package/dist/browser/index.cjs +73 -2
- package/dist/browser/index.mjs +73 -2
- package/dist/cjs/naylence/fame/connector/base-async-connector.js +42 -0
- package/dist/cjs/naylence/fame/connector/broadcast-channel-connector.browser.js +20 -0
- package/dist/cjs/naylence/fame/node/upstream-session-manager.js +10 -0
- package/dist/cjs/version.js +2 -2
- package/dist/esm/naylence/fame/connector/base-async-connector.js +42 -0
- package/dist/esm/naylence/fame/connector/broadcast-channel-connector.browser.js +20 -0
- package/dist/esm/naylence/fame/node/upstream-session-manager.js +11 -1
- package/dist/esm/version.js +2 -2
- package/dist/node/index.cjs +73 -2
- package/dist/node/index.mjs +73 -2
- package/dist/node/node.cjs +73 -2
- package/dist/node/node.mjs +73 -2
- package/dist/types/naylence/fame/connector/base-async-connector.d.ts +8 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +2 -2
package/dist/browser/index.cjs
CHANGED
|
@@ -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.
|
|
101
|
+
// Generated from package.json version: 0.3.5-test.936
|
|
102
102
|
/**
|
|
103
103
|
* The package version, injected at build time.
|
|
104
104
|
* @internal
|
|
105
105
|
*/
|
|
106
|
-
const VERSION = '0.3.5-test.
|
|
106
|
+
const VERSION = '0.3.5-test.936';
|
|
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
|
*/
|
|
@@ -9928,6 +9970,25 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
|
|
|
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);
|
|
@@ -10817,6 +10878,15 @@ class UpstreamSessionManager extends TaskSpawner {
|
|
|
10817
10878
|
if (stopEvt.isSet() || signal?.aborted) {
|
|
10818
10879
|
break;
|
|
10819
10880
|
}
|
|
10881
|
+
// Skip heartbeat if connector is paused (e.g., tab is hidden)
|
|
10882
|
+
// Keep ack time current so we don't timeout immediately after resuming
|
|
10883
|
+
if (connector.state === core.ConnectorState.PAUSED) {
|
|
10884
|
+
logger$Z.debug('skipping_heartbeat_connector_paused', {
|
|
10885
|
+
connector_state: connector.state,
|
|
10886
|
+
});
|
|
10887
|
+
this.lastHeartbeatAckTime = Date.now();
|
|
10888
|
+
continue;
|
|
10889
|
+
}
|
|
10820
10890
|
const envelope = await this.makeHeartbeatEnvelope();
|
|
10821
10891
|
logger$Z.debug('sending_heartbeat', {
|
|
10822
10892
|
hb_corr_id: envelope.corrId,
|
|
@@ -10838,6 +10908,7 @@ class UpstreamSessionManager extends TaskSpawner {
|
|
|
10838
10908
|
throw error;
|
|
10839
10909
|
}
|
|
10840
10910
|
await this.node.dispatchEvent('onHeartbeatSent', this.node, envelope);
|
|
10911
|
+
// Don't check heartbeat timeout when paused
|
|
10841
10912
|
if (this.lastHeartbeatAckTime !== null &&
|
|
10842
10913
|
Date.now() - this.lastHeartbeatAckTime > graceMs) {
|
|
10843
10914
|
throw new FameConnectError('missed heartbeat acknowledgement');
|
package/dist/browser/index.mjs
CHANGED
|
@@ -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.
|
|
99
|
+
// Generated from package.json version: 0.3.5-test.936
|
|
100
100
|
/**
|
|
101
101
|
* The package version, injected at build time.
|
|
102
102
|
* @internal
|
|
103
103
|
*/
|
|
104
|
-
const VERSION = '0.3.5-test.
|
|
104
|
+
const VERSION = '0.3.5-test.936';
|
|
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
|
*/
|
|
@@ -9926,6 +9968,25 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
|
|
|
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);
|
|
@@ -10815,6 +10876,15 @@ class UpstreamSessionManager extends TaskSpawner {
|
|
|
10815
10876
|
if (stopEvt.isSet() || signal?.aborted) {
|
|
10816
10877
|
break;
|
|
10817
10878
|
}
|
|
10879
|
+
// Skip heartbeat if connector is paused (e.g., tab is hidden)
|
|
10880
|
+
// Keep ack time current so we don't timeout immediately after resuming
|
|
10881
|
+
if (connector.state === ConnectorState.PAUSED) {
|
|
10882
|
+
logger$Z.debug('skipping_heartbeat_connector_paused', {
|
|
10883
|
+
connector_state: connector.state,
|
|
10884
|
+
});
|
|
10885
|
+
this.lastHeartbeatAckTime = Date.now();
|
|
10886
|
+
continue;
|
|
10887
|
+
}
|
|
10818
10888
|
const envelope = await this.makeHeartbeatEnvelope();
|
|
10819
10889
|
logger$Z.debug('sending_heartbeat', {
|
|
10820
10890
|
hb_corr_id: envelope.corrId,
|
|
@@ -10836,6 +10906,7 @@ class UpstreamSessionManager extends TaskSpawner {
|
|
|
10836
10906
|
throw error;
|
|
10837
10907
|
}
|
|
10838
10908
|
await this.node.dispatchEvent('onHeartbeatSent', this.node, envelope);
|
|
10909
|
+
// Don't check heartbeat timeout when paused
|
|
10839
10910
|
if (this.lastHeartbeatAckTime !== null &&
|
|
10840
10911
|
Date.now() - this.lastHeartbeatAckTime > graceMs) {
|
|
10841
10912
|
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
|
*/
|
|
@@ -5,6 +5,7 @@ const base_async_connector_js_1 = require("./base-async-connector.js");
|
|
|
5
5
|
const errors_js_1 = require("../errors/errors.js");
|
|
6
6
|
const logging_js_1 = require("../util/logging.js");
|
|
7
7
|
const bounded_async_queue_js_1 = require("../util/bounded-async-queue.js");
|
|
8
|
+
const core_1 = require("@naylence/core");
|
|
8
9
|
const logger = (0, logging_js_1.getLogger)('naylence.fame.connector.broadcast_channel_connector');
|
|
9
10
|
exports.BROADCAST_CHANNEL_CONNECTOR_TYPE = 'broadcast-channel-connector';
|
|
10
11
|
const DEFAULT_CHANNEL = 'naylence-fabric';
|
|
@@ -157,6 +158,25 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
|
|
|
157
158
|
visibility: isHidden ? 'hidden' : 'visible',
|
|
158
159
|
timestamp: new Date().toISOString(),
|
|
159
160
|
});
|
|
161
|
+
// Pause/resume connector based on visibility
|
|
162
|
+
if (isHidden && this.state === core_1.ConnectorState.STARTED) {
|
|
163
|
+
this.pause().catch((err) => {
|
|
164
|
+
logger.warning('broadcast_channel_pause_failed', {
|
|
165
|
+
channel: this.channelName,
|
|
166
|
+
connector_id: this.connectorId,
|
|
167
|
+
error: err instanceof Error ? err.message : String(err),
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
else if (!isHidden && this.state === core_1.ConnectorState.PAUSED) {
|
|
172
|
+
this.resume().catch((err) => {
|
|
173
|
+
logger.warning('broadcast_channel_resume_failed', {
|
|
174
|
+
channel: this.channelName,
|
|
175
|
+
connector_id: this.connectorId,
|
|
176
|
+
error: err instanceof Error ? err.message : String(err),
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
}
|
|
160
180
|
};
|
|
161
181
|
if (typeof document !== 'undefined') {
|
|
162
182
|
document.addEventListener('visibilitychange', this.visibilityChangeHandler);
|
|
@@ -536,6 +536,15 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
536
536
|
if (stopEvt.isSet() || signal?.aborted) {
|
|
537
537
|
break;
|
|
538
538
|
}
|
|
539
|
+
// Skip heartbeat if connector is paused (e.g., tab is hidden)
|
|
540
|
+
// Keep ack time current so we don't timeout immediately after resuming
|
|
541
|
+
if (connector.state === core_1.ConnectorState.PAUSED) {
|
|
542
|
+
logger.debug('skipping_heartbeat_connector_paused', {
|
|
543
|
+
connector_state: connector.state,
|
|
544
|
+
});
|
|
545
|
+
this.lastHeartbeatAckTime = Date.now();
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
539
548
|
const envelope = await this.makeHeartbeatEnvelope();
|
|
540
549
|
logger.debug('sending_heartbeat', {
|
|
541
550
|
hb_corr_id: envelope.corrId,
|
|
@@ -557,6 +566,7 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
557
566
|
throw error;
|
|
558
567
|
}
|
|
559
568
|
await this.node.dispatchEvent('onHeartbeatSent', this.node, envelope);
|
|
569
|
+
// Don't check heartbeat timeout when paused
|
|
560
570
|
if (this.lastHeartbeatAckTime !== null &&
|
|
561
571
|
Date.now() - this.lastHeartbeatAckTime > graceMs) {
|
|
562
572
|
throw new errors_js_1.FameConnectError('missed heartbeat acknowledgement');
|
package/dist/cjs/version.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// This file is auto-generated during build - do not edit manually
|
|
3
|
-
// Generated from package.json version: 0.3.5-test.
|
|
3
|
+
// Generated from package.json version: 0.3.5-test.936
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
5
|
exports.VERSION = void 0;
|
|
6
6
|
/**
|
|
7
7
|
* The package version, injected at build time.
|
|
8
8
|
* @internal
|
|
9
9
|
*/
|
|
10
|
-
exports.VERSION = '0.3.5-test.
|
|
10
|
+
exports.VERSION = '0.3.5-test.936';
|
|
@@ -165,6 +165,48 @@ export class BaseAsyncConnector extends TaskSpawner {
|
|
|
165
165
|
connector_id: this._connectorFlowId,
|
|
166
166
|
});
|
|
167
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Pause the connector (suspends heartbeat and housekeeping, but keeps connection alive)
|
|
170
|
+
*/
|
|
171
|
+
async pause() {
|
|
172
|
+
logger.debug('pausing_connector', {
|
|
173
|
+
current_state: this._state,
|
|
174
|
+
connector_id: this._connectorFlowId,
|
|
175
|
+
});
|
|
176
|
+
if (this._state !== ConnectorState.STARTED) {
|
|
177
|
+
logger.debug('connector_pause_invalid_state', {
|
|
178
|
+
current_state: this._state,
|
|
179
|
+
connector_id: this._connectorFlowId,
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
this._setState(ConnectorState.PAUSED);
|
|
184
|
+
logger.debug('connector_paused', {
|
|
185
|
+
current_state: this._state,
|
|
186
|
+
connector_id: this._connectorFlowId,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Resume the connector from paused state
|
|
191
|
+
*/
|
|
192
|
+
async resume() {
|
|
193
|
+
logger.debug('resuming_connector', {
|
|
194
|
+
current_state: this._state,
|
|
195
|
+
connector_id: this._connectorFlowId,
|
|
196
|
+
});
|
|
197
|
+
if (this._state !== ConnectorState.PAUSED) {
|
|
198
|
+
logger.debug('connector_resume_invalid_state', {
|
|
199
|
+
current_state: this._state,
|
|
200
|
+
connector_id: this._connectorFlowId,
|
|
201
|
+
});
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
this._setState(ConnectorState.STARTED);
|
|
205
|
+
logger.debug('connector_resumed', {
|
|
206
|
+
current_state: this._state,
|
|
207
|
+
connector_id: this._connectorFlowId,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
168
210
|
/**
|
|
169
211
|
* Close the connector with optional code and reason
|
|
170
212
|
*/
|
|
@@ -2,6 +2,7 @@ import { BaseAsyncConnector, } from './base-async-connector.js';
|
|
|
2
2
|
import { FameTransportClose } from '../errors/errors.js';
|
|
3
3
|
import { getLogger } from '../util/logging.js';
|
|
4
4
|
import { BoundedAsyncQueue, QueueFullError, } from '../util/bounded-async-queue.js';
|
|
5
|
+
import { ConnectorState } from '@naylence/core';
|
|
5
6
|
const logger = getLogger('naylence.fame.connector.broadcast_channel_connector');
|
|
6
7
|
export const BROADCAST_CHANNEL_CONNECTOR_TYPE = 'broadcast-channel-connector';
|
|
7
8
|
const DEFAULT_CHANNEL = 'naylence-fabric';
|
|
@@ -154,6 +155,25 @@ export class BroadcastChannelConnector extends BaseAsyncConnector {
|
|
|
154
155
|
visibility: isHidden ? 'hidden' : 'visible',
|
|
155
156
|
timestamp: new Date().toISOString(),
|
|
156
157
|
});
|
|
158
|
+
// Pause/resume connector based on visibility
|
|
159
|
+
if (isHidden && this.state === ConnectorState.STARTED) {
|
|
160
|
+
this.pause().catch((err) => {
|
|
161
|
+
logger.warning('broadcast_channel_pause_failed', {
|
|
162
|
+
channel: this.channelName,
|
|
163
|
+
connector_id: this.connectorId,
|
|
164
|
+
error: err instanceof Error ? err.message : String(err),
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
else if (!isHidden && this.state === ConnectorState.PAUSED) {
|
|
169
|
+
this.resume().catch((err) => {
|
|
170
|
+
logger.warning('broadcast_channel_resume_failed', {
|
|
171
|
+
channel: this.channelName,
|
|
172
|
+
connector_id: this.connectorId,
|
|
173
|
+
error: err instanceof Error ? err.message : String(err),
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
}
|
|
157
177
|
};
|
|
158
178
|
if (typeof document !== 'undefined') {
|
|
159
179
|
document.addEventListener('visibilitychange', this.visibilityChangeHandler);
|
|
@@ -2,7 +2,7 @@ import { ConnectorFactory } from '../connector/connector-factory.js';
|
|
|
2
2
|
import { TaskSpawner } from '../util/task-spawner.js';
|
|
3
3
|
import { AsyncEvent } from '../util/async-event.js';
|
|
4
4
|
import { getLogger } from '../util/logging.js';
|
|
5
|
-
import { DeliveryOriginType, FameFabric, generateId, } from '@naylence/core';
|
|
5
|
+
import { ConnectorState, DeliveryOriginType, FameFabric, generateId, } from '@naylence/core';
|
|
6
6
|
import { FameConnectError, FameMessageTooLarge, FameTransportClose, } from '../errors/errors.js';
|
|
7
7
|
import { TaskCancelledError } from '../util/task-types.js';
|
|
8
8
|
import { FameResponseType } from '@naylence/core';
|
|
@@ -533,6 +533,15 @@ export class UpstreamSessionManager extends TaskSpawner {
|
|
|
533
533
|
if (stopEvt.isSet() || signal?.aborted) {
|
|
534
534
|
break;
|
|
535
535
|
}
|
|
536
|
+
// Skip heartbeat if connector is paused (e.g., tab is hidden)
|
|
537
|
+
// Keep ack time current so we don't timeout immediately after resuming
|
|
538
|
+
if (connector.state === ConnectorState.PAUSED) {
|
|
539
|
+
logger.debug('skipping_heartbeat_connector_paused', {
|
|
540
|
+
connector_state: connector.state,
|
|
541
|
+
});
|
|
542
|
+
this.lastHeartbeatAckTime = Date.now();
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
536
545
|
const envelope = await this.makeHeartbeatEnvelope();
|
|
537
546
|
logger.debug('sending_heartbeat', {
|
|
538
547
|
hb_corr_id: envelope.corrId,
|
|
@@ -554,6 +563,7 @@ export class UpstreamSessionManager extends TaskSpawner {
|
|
|
554
563
|
throw error;
|
|
555
564
|
}
|
|
556
565
|
await this.node.dispatchEvent('onHeartbeatSent', this.node, envelope);
|
|
566
|
+
// Don't check heartbeat timeout when paused
|
|
557
567
|
if (this.lastHeartbeatAckTime !== null &&
|
|
558
568
|
Date.now() - this.lastHeartbeatAckTime > graceMs) {
|
|
559
569
|
throw new FameConnectError('missed heartbeat acknowledgement');
|
package/dist/esm/version.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// This file is auto-generated during build - do not edit manually
|
|
2
|
-
// Generated from package.json version: 0.3.5-test.
|
|
2
|
+
// Generated from package.json version: 0.3.5-test.936
|
|
3
3
|
/**
|
|
4
4
|
* The package version, injected at build time.
|
|
5
5
|
* @internal
|
|
6
6
|
*/
|
|
7
|
-
export const VERSION = '0.3.5-test.
|
|
7
|
+
export const VERSION = '0.3.5-test.936';
|
package/dist/node/index.cjs
CHANGED
|
@@ -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.
|
|
17
|
+
// Generated from package.json version: 0.3.5-test.936
|
|
18
18
|
/**
|
|
19
19
|
* The package version, injected at build time.
|
|
20
20
|
* @internal
|
|
21
21
|
*/
|
|
22
|
-
const VERSION = '0.3.5-test.
|
|
22
|
+
const VERSION = '0.3.5-test.936';
|
|
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
|
*/
|
|
@@ -9844,6 +9886,25 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
|
|
|
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);
|
|
@@ -10733,6 +10794,15 @@ class UpstreamSessionManager extends TaskSpawner {
|
|
|
10733
10794
|
if (stopEvt.isSet() || signal?.aborted) {
|
|
10734
10795
|
break;
|
|
10735
10796
|
}
|
|
10797
|
+
// Skip heartbeat if connector is paused (e.g., tab is hidden)
|
|
10798
|
+
// Keep ack time current so we don't timeout immediately after resuming
|
|
10799
|
+
if (connector.state === core.ConnectorState.PAUSED) {
|
|
10800
|
+
logger$Z.debug('skipping_heartbeat_connector_paused', {
|
|
10801
|
+
connector_state: connector.state,
|
|
10802
|
+
});
|
|
10803
|
+
this.lastHeartbeatAckTime = Date.now();
|
|
10804
|
+
continue;
|
|
10805
|
+
}
|
|
10736
10806
|
const envelope = await this.makeHeartbeatEnvelope();
|
|
10737
10807
|
logger$Z.debug('sending_heartbeat', {
|
|
10738
10808
|
hb_corr_id: envelope.corrId,
|
|
@@ -10754,6 +10824,7 @@ class UpstreamSessionManager extends TaskSpawner {
|
|
|
10754
10824
|
throw error;
|
|
10755
10825
|
}
|
|
10756
10826
|
await this.node.dispatchEvent('onHeartbeatSent', this.node, envelope);
|
|
10827
|
+
// Don't check heartbeat timeout when paused
|
|
10757
10828
|
if (this.lastHeartbeatAckTime !== null &&
|
|
10758
10829
|
Date.now() - this.lastHeartbeatAckTime > graceMs) {
|
|
10759
10830
|
throw new FameConnectError('missed heartbeat acknowledgement');
|
package/dist/node/index.mjs
CHANGED
|
@@ -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.
|
|
16
|
+
// Generated from package.json version: 0.3.5-test.936
|
|
17
17
|
/**
|
|
18
18
|
* The package version, injected at build time.
|
|
19
19
|
* @internal
|
|
20
20
|
*/
|
|
21
|
-
const VERSION = '0.3.5-test.
|
|
21
|
+
const VERSION = '0.3.5-test.936';
|
|
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
|
*/
|
|
@@ -9843,6 +9885,25 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
|
|
|
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);
|
|
@@ -10732,6 +10793,15 @@ class UpstreamSessionManager extends TaskSpawner {
|
|
|
10732
10793
|
if (stopEvt.isSet() || signal?.aborted) {
|
|
10733
10794
|
break;
|
|
10734
10795
|
}
|
|
10796
|
+
// Skip heartbeat if connector is paused (e.g., tab is hidden)
|
|
10797
|
+
// Keep ack time current so we don't timeout immediately after resuming
|
|
10798
|
+
if (connector.state === ConnectorState.PAUSED) {
|
|
10799
|
+
logger$Z.debug('skipping_heartbeat_connector_paused', {
|
|
10800
|
+
connector_state: connector.state,
|
|
10801
|
+
});
|
|
10802
|
+
this.lastHeartbeatAckTime = Date.now();
|
|
10803
|
+
continue;
|
|
10804
|
+
}
|
|
10735
10805
|
const envelope = await this.makeHeartbeatEnvelope();
|
|
10736
10806
|
logger$Z.debug('sending_heartbeat', {
|
|
10737
10807
|
hb_corr_id: envelope.corrId,
|
|
@@ -10753,6 +10823,7 @@ class UpstreamSessionManager extends TaskSpawner {
|
|
|
10753
10823
|
throw error;
|
|
10754
10824
|
}
|
|
10755
10825
|
await this.node.dispatchEvent('onHeartbeatSent', this.node, envelope);
|
|
10826
|
+
// Don't check heartbeat timeout when paused
|
|
10756
10827
|
if (this.lastHeartbeatAckTime !== null &&
|
|
10757
10828
|
Date.now() - this.lastHeartbeatAckTime > graceMs) {
|
|
10758
10829
|
throw new FameConnectError('missed heartbeat acknowledgement');
|
package/dist/node/node.cjs
CHANGED
|
@@ -2137,6 +2137,48 @@ class BaseAsyncConnector extends TaskSpawner {
|
|
|
2137
2137
|
connector_id: this._connectorFlowId,
|
|
2138
2138
|
});
|
|
2139
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
|
+
});
|
|
2181
|
+
}
|
|
2140
2182
|
/**
|
|
2141
2183
|
* Close the connector with optional code and reason
|
|
2142
2184
|
*/
|
|
@@ -5436,12 +5478,12 @@ for (const [name, config] of Object.entries(SQLITE_PROFILES)) {
|
|
|
5436
5478
|
}
|
|
5437
5479
|
|
|
5438
5480
|
// This file is auto-generated during build - do not edit manually
|
|
5439
|
-
// Generated from package.json version: 0.3.5-test.
|
|
5481
|
+
// Generated from package.json version: 0.3.5-test.936
|
|
5440
5482
|
/**
|
|
5441
5483
|
* The package version, injected at build time.
|
|
5442
5484
|
* @internal
|
|
5443
5485
|
*/
|
|
5444
|
-
const VERSION = '0.3.5-test.
|
|
5486
|
+
const VERSION = '0.3.5-test.936';
|
|
5445
5487
|
|
|
5446
5488
|
/**
|
|
5447
5489
|
* Fame errors module - Fame protocol specific error classes
|
|
@@ -11581,6 +11623,25 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
|
|
|
11581
11623
|
visibility: isHidden ? 'hidden' : 'visible',
|
|
11582
11624
|
timestamp: new Date().toISOString(),
|
|
11583
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
|
+
}
|
|
11584
11645
|
};
|
|
11585
11646
|
if (typeof document !== 'undefined') {
|
|
11586
11647
|
document.addEventListener('visibilitychange', this.visibilityChangeHandler);
|
|
@@ -12425,6 +12486,15 @@ class UpstreamSessionManager extends TaskSpawner {
|
|
|
12425
12486
|
if (stopEvt.isSet() || signal?.aborted) {
|
|
12426
12487
|
break;
|
|
12427
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
|
+
}
|
|
12428
12498
|
const envelope = await this.makeHeartbeatEnvelope();
|
|
12429
12499
|
logger$$.debug('sending_heartbeat', {
|
|
12430
12500
|
hb_corr_id: envelope.corrId,
|
|
@@ -12446,6 +12516,7 @@ class UpstreamSessionManager extends TaskSpawner {
|
|
|
12446
12516
|
throw error;
|
|
12447
12517
|
}
|
|
12448
12518
|
await this.node.dispatchEvent('onHeartbeatSent', this.node, envelope);
|
|
12519
|
+
// Don't check heartbeat timeout when paused
|
|
12449
12520
|
if (this.lastHeartbeatAckTime !== null &&
|
|
12450
12521
|
Date.now() - this.lastHeartbeatAckTime > graceMs) {
|
|
12451
12522
|
throw new FameConnectError('missed heartbeat acknowledgement');
|
package/dist/node/node.mjs
CHANGED
|
@@ -2136,6 +2136,48 @@ class BaseAsyncConnector extends TaskSpawner {
|
|
|
2136
2136
|
connector_id: this._connectorFlowId,
|
|
2137
2137
|
});
|
|
2138
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
|
+
});
|
|
2180
|
+
}
|
|
2139
2181
|
/**
|
|
2140
2182
|
* Close the connector with optional code and reason
|
|
2141
2183
|
*/
|
|
@@ -5435,12 +5477,12 @@ for (const [name, config] of Object.entries(SQLITE_PROFILES)) {
|
|
|
5435
5477
|
}
|
|
5436
5478
|
|
|
5437
5479
|
// This file is auto-generated during build - do not edit manually
|
|
5438
|
-
// Generated from package.json version: 0.3.5-test.
|
|
5480
|
+
// Generated from package.json version: 0.3.5-test.936
|
|
5439
5481
|
/**
|
|
5440
5482
|
* The package version, injected at build time.
|
|
5441
5483
|
* @internal
|
|
5442
5484
|
*/
|
|
5443
|
-
const VERSION = '0.3.5-test.
|
|
5485
|
+
const VERSION = '0.3.5-test.936';
|
|
5444
5486
|
|
|
5445
5487
|
/**
|
|
5446
5488
|
* Fame errors module - Fame protocol specific error classes
|
|
@@ -11580,6 +11622,25 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
|
|
|
11580
11622
|
visibility: isHidden ? 'hidden' : 'visible',
|
|
11581
11623
|
timestamp: new Date().toISOString(),
|
|
11582
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
|
+
}
|
|
11583
11644
|
};
|
|
11584
11645
|
if (typeof document !== 'undefined') {
|
|
11585
11646
|
document.addEventListener('visibilitychange', this.visibilityChangeHandler);
|
|
@@ -12424,6 +12485,15 @@ class UpstreamSessionManager extends TaskSpawner {
|
|
|
12424
12485
|
if (stopEvt.isSet() || signal?.aborted) {
|
|
12425
12486
|
break;
|
|
12426
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
|
+
}
|
|
12427
12497
|
const envelope = await this.makeHeartbeatEnvelope();
|
|
12428
12498
|
logger$$.debug('sending_heartbeat', {
|
|
12429
12499
|
hb_corr_id: envelope.corrId,
|
|
@@ -12445,6 +12515,7 @@ class UpstreamSessionManager extends TaskSpawner {
|
|
|
12445
12515
|
throw error;
|
|
12446
12516
|
}
|
|
12447
12517
|
await this.node.dispatchEvent('onHeartbeatSent', this.node, envelope);
|
|
12518
|
+
// Don't check heartbeat timeout when paused
|
|
12448
12519
|
if (this.lastHeartbeatAckTime !== null &&
|
|
12449
12520
|
Date.now() - this.lastHeartbeatAckTime > graceMs) {
|
|
12450
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
|
*/
|
package/dist/types/version.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naylence/runtime",
|
|
3
|
-
"version": "0.3.5-test.
|
|
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.
|
|
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",
|