jsgar 3.7.1 → 3.7.8
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/gar.umd.js +74 -29
- package/package.json +1 -1
package/dist/gar.umd.js
CHANGED
|
@@ -47,7 +47,11 @@
|
|
|
47
47
|
this.reconnectDelay = 5000; // Milliseconds
|
|
48
48
|
this.user = user;
|
|
49
49
|
this.working_namespace = working_namespace;
|
|
50
|
-
this.
|
|
50
|
+
this.timeoutScale = (typeof process !== 'undefined' && process.env && process.env.TRS_TIMEOUT_SCALE) ? parseFloat(process.env.TRS_TIMEOUT_SCALE) : 1.0;
|
|
51
|
+
this.scaledHeartbeatTimeoutInterval = heartbeatTimeoutInterval;
|
|
52
|
+
if (this.timeoutScale !== 1.0) {
|
|
53
|
+
this.scaledHeartbeatTimeoutInterval *= this.timeoutScale;
|
|
54
|
+
}
|
|
51
55
|
this.version = 650707;
|
|
52
56
|
|
|
53
57
|
if (typeof window !== 'undefined' && window.location) {
|
|
@@ -70,7 +74,6 @@
|
|
|
70
74
|
this.heartbeatIntervalId = null;
|
|
71
75
|
this.messageHandlers = new Map();
|
|
72
76
|
this.lastHeartbeatTime = Date.now() / 1000;
|
|
73
|
-
this.heartbeatTimeout = 3; // Seconds
|
|
74
77
|
|
|
75
78
|
this.heartbeatTimeoutCallback = null;
|
|
76
79
|
this.stoppedCallback = null;
|
|
@@ -145,42 +148,72 @@
|
|
|
145
148
|
|
|
146
149
|
/**
|
|
147
150
|
* Establish WebSocket connection with reconnection logic.
|
|
151
|
+
* @param {number} [openTimeout=20] - Connection timeout in seconds
|
|
148
152
|
*/
|
|
149
|
-
|
|
150
|
-
* Establish WebSocket connection with reconnection logic.
|
|
151
|
-
*/
|
|
152
|
-
async connect() {
|
|
153
|
+
async connect(openTimeout = 20) {
|
|
153
154
|
// Before attempting a new connection, clear any previous state
|
|
154
155
|
this.clearConnectionState();
|
|
155
156
|
|
|
157
|
+
let scaledOpenTimeout = openTimeout * this.timeoutScale;
|
|
158
|
+
|
|
156
159
|
while (this.running && !this.connected) {
|
|
157
160
|
try {
|
|
158
161
|
const WS = await this._ensureWebSocketCtor();
|
|
159
162
|
const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
163
|
+
|
|
164
|
+
this.log('INFO', `Connecting to WebSocket server at ${this.wsEndpoint}`);
|
|
165
|
+
|
|
166
|
+
const connectionPromise = new Promise((resolve, reject) => {
|
|
167
|
+
let websocket;
|
|
168
|
+
if (this.allowSelfSignedCertificate && isNode) {
|
|
169
|
+
// Node.js 'ws' supports options for TLS; browsers do not.
|
|
170
|
+
websocket = new WS(this.wsEndpoint, ['gar-protocol'], { rejectUnauthorized: false });
|
|
171
|
+
} else {
|
|
172
|
+
websocket = new WS(this.wsEndpoint, ['gar-protocol']);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const timeoutId = setTimeout(() => {
|
|
176
|
+
websocket.onopen = null;
|
|
177
|
+
websocket.onclose = null;
|
|
178
|
+
websocket.onerror = null;
|
|
179
|
+
if (websocket.terminate) websocket.terminate();
|
|
180
|
+
else if (websocket.close) websocket.close();
|
|
181
|
+
reject(new Error(`Connection timed out after ${scaledOpenTimeout}s`));
|
|
182
|
+
}, scaledOpenTimeout * 1000);
|
|
183
|
+
|
|
184
|
+
websocket.onopen = () => {
|
|
185
|
+
clearTimeout(timeoutId);
|
|
186
|
+
resolve(websocket);
|
|
187
|
+
};
|
|
188
|
+
websocket.onerror = (error) => {
|
|
189
|
+
clearTimeout(timeoutId);
|
|
190
|
+
reject(new Error(error.message || 'Unknown WebSocket error'));
|
|
191
|
+
};
|
|
192
|
+
websocket.onclose = () => {
|
|
193
|
+
clearTimeout(timeoutId);
|
|
194
|
+
reject(new Error('WebSocket closed during connection attempt'));
|
|
195
|
+
};
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
this.websocket = await connectionPromise;
|
|
199
|
+
this.connected = true;
|
|
200
|
+
this.log('INFO', `Connected to WebSocket server at ${this.wsEndpoint} using gar-protocol`);
|
|
201
|
+
|
|
172
202
|
this.websocket.onclose = () => {
|
|
173
203
|
this.log('WARNING', 'WebSocket connection closed.');
|
|
174
204
|
this.connected = false;
|
|
175
205
|
this.websocket = null;
|
|
206
|
+
this._reconnect();
|
|
176
207
|
};
|
|
177
208
|
this.websocket.onerror = (error) => {
|
|
178
209
|
this.log('ERROR', `WebSocket error: ${error.message || 'Unknown error'}`);
|
|
179
210
|
this.connected = false;
|
|
180
211
|
this.websocket = null;
|
|
181
212
|
};
|
|
182
|
-
|
|
183
|
-
|
|
213
|
+
|
|
214
|
+
this._sendMessages();
|
|
215
|
+
this._receiveMessages();
|
|
216
|
+
|
|
184
217
|
} catch (e) {
|
|
185
218
|
this.log('ERROR', `WebSocket connection failed: ${e.message}. Reconnecting in ${this.reconnectDelay / 1000} seconds...`);
|
|
186
219
|
this.connected = false;
|
|
@@ -457,13 +490,13 @@
|
|
|
457
490
|
message_type: 'Introduction',
|
|
458
491
|
value: {
|
|
459
492
|
version: this.version,
|
|
460
|
-
heartbeat_timeout_interval: this.
|
|
493
|
+
heartbeat_timeout_interval: Math.floor(this.scaledHeartbeatTimeoutInterval),
|
|
461
494
|
user: this.user,
|
|
462
495
|
working_namespace: this.working_namespace
|
|
463
496
|
}
|
|
464
497
|
};
|
|
465
498
|
this.sendMessage(introMsg);
|
|
466
|
-
this.heartbeatIntervalId = setInterval(() => this._heartbeatLoop(), this.
|
|
499
|
+
this.heartbeatIntervalId = setInterval(() => this._heartbeatLoop(), this.scaledHeartbeatTimeoutInterval / 2);
|
|
467
500
|
this.connect();
|
|
468
501
|
}
|
|
469
502
|
|
|
@@ -590,7 +623,7 @@
|
|
|
590
623
|
*/
|
|
591
624
|
checkHeartbeat() {
|
|
592
625
|
const now = Date.now() / 1000;
|
|
593
|
-
const cutoff = this._initialGracePeriod ? this._initialGraceDeadline : this.lastHeartbeatTime + this.
|
|
626
|
+
const cutoff = this._initialGracePeriod ? this._initialGraceDeadline : this.lastHeartbeatTime + (this.scaledHeartbeatTimeoutInterval / 1000.0);
|
|
594
627
|
if (now > cutoff) {
|
|
595
628
|
this.log('WARNING', `Heartbeat failed; previous heartbeat: ${this.lastHeartbeatTime.toFixed(3)}s`);
|
|
596
629
|
this.exitCode = 1;
|
|
@@ -678,10 +711,14 @@
|
|
|
678
711
|
this.serverKeyIdToName.clear();
|
|
679
712
|
this.serverKeyNameToId.clear();
|
|
680
713
|
this.recordMap.clear();
|
|
681
|
-
|
|
714
|
+
let newInterval = value.heartbeat_timeout_interval;
|
|
715
|
+
if (this.timeoutScale !== 1.0) {
|
|
716
|
+
newInterval *= this.timeoutScale;
|
|
717
|
+
}
|
|
718
|
+
this.scaledHeartbeatTimeoutInterval = Math.max(this.scaledHeartbeatTimeoutInterval, newInterval);
|
|
682
719
|
this.lastHeartbeatTime = Date.now() / 1000;
|
|
683
720
|
this._initialGracePeriod = true;
|
|
684
|
-
this._initialGraceDeadline = this.lastHeartbeatTime + this.
|
|
721
|
+
this._initialGraceDeadline = this.lastHeartbeatTime + (this.scaledHeartbeatTimeoutInterval / 1000.0) * 10;
|
|
685
722
|
// New Introduction: do not reset the first-heartbeat promise to avoid racing
|
|
686
723
|
// with consumers already awaiting it. The existing promise will resolve on
|
|
687
724
|
// the first Heartbeat observed during the grace period above.
|
|
@@ -1075,6 +1112,17 @@
|
|
|
1075
1112
|
this.sendMessage(msg);
|
|
1076
1113
|
}
|
|
1077
1114
|
|
|
1115
|
+
/**
|
|
1116
|
+
* Delete a key by name if it exists in the localKeyMap. Safe to call multiple times.
|
|
1117
|
+
* @param {string} keyName - Key name
|
|
1118
|
+
*/
|
|
1119
|
+
deleteKey(keyName) {
|
|
1120
|
+
const keyId = this.localKeyMap.get(keyName);
|
|
1121
|
+
if (keyId) {
|
|
1122
|
+
this.publishDeleteKey(keyId);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1078
1126
|
/**
|
|
1079
1127
|
* Publish a DeleteRecord message using local key and topic IDs.
|
|
1080
1128
|
* @param {number} keyId - Key ID
|
|
@@ -1199,10 +1247,7 @@
|
|
|
1199
1247
|
const resultContainer = { };
|
|
1200
1248
|
|
|
1201
1249
|
const cleanup = () => {
|
|
1202
|
-
|
|
1203
|
-
if (keyId) {
|
|
1204
|
-
this.publishDeleteKey(keyId);
|
|
1205
|
-
}
|
|
1250
|
+
this.deleteKey(queryKey);
|
|
1206
1251
|
};
|
|
1207
1252
|
|
|
1208
1253
|
return new Promise((resolve) => {
|