jsgar 3.7.2 → 3.8.0
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 +76 -31
- 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;
|
|
@@ -329,11 +362,11 @@
|
|
|
329
362
|
|
|
330
363
|
/**
|
|
331
364
|
* Register handler for DeleteKey message.
|
|
332
|
-
* @param {Function} handler - Callback with (keyId)
|
|
365
|
+
* @param {Function} handler - Callback with (keyId, keyName)
|
|
333
366
|
* @param subscriptionGroup - The subscription group for callback (default 0)
|
|
334
367
|
*/
|
|
335
368
|
registerDeleteKeyHandler(handler, subscriptionGroup = 0) {
|
|
336
|
-
this.registerHandler('DeleteKey', (msg) => handler(msg.value.key_id), subscriptionGroup);
|
|
369
|
+
this.registerHandler('DeleteKey', (msg) => handler(msg.value.key_id, (msg.value && ('name' in msg.value)) ? msg.value.name : null), subscriptionGroup);
|
|
337
370
|
}
|
|
338
371
|
|
|
339
372
|
/**
|
|
@@ -426,8 +459,8 @@
|
|
|
426
459
|
this.log('DEBUG', `New server topic: ${name} (Server ID: ${topicId})`));
|
|
427
460
|
this.registerKeyIntroductionHandler((keyId, name, classList, deletedClass) =>
|
|
428
461
|
this.log('DEBUG', `Key: ${name} : ${keyId} (Classes: ${JSON.stringify(classList)}/-${deletedClass})`));
|
|
429
|
-
this.registerDeleteKeyHandler((keyId) =>
|
|
430
|
-
this.log('DEBUG', `Delete key: ${
|
|
462
|
+
this.registerDeleteKeyHandler((keyId, keyName) =>
|
|
463
|
+
this.log('DEBUG', `Delete key: ${keyName || 'unknown'} (Server ID: ${keyId})`));
|
|
431
464
|
this.registerSubscriptionStatusHandler(this._defaultSubscriptionStatusHandler.bind(this));
|
|
432
465
|
this.registerDeleteRecordHandler((keyId, topicId) =>
|
|
433
466
|
this.log('DEBUG', `Delete record: ${this.serverKeyIdToName.get(keyId) || 'unknown'} - ${this.serverTopicIdToName.get(topicId) || 'unknown'}`));
|
|
@@ -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;
|
|
@@ -660,8 +693,16 @@
|
|
|
660
693
|
this.serverKeyNameToId.set(message.value.name, message.value.key_id);
|
|
661
694
|
} else if (msgType === 'DeleteKey') {
|
|
662
695
|
subscriptionGroup = this.activeSubscriptionGroup;
|
|
663
|
-
|
|
664
|
-
this.serverKeyIdToName.
|
|
696
|
+
const keyId = message.value.key_id;
|
|
697
|
+
const keyName = this.serverKeyIdToName.get(keyId) || null;
|
|
698
|
+
message.value.name = keyName;
|
|
699
|
+
if (keyName) {
|
|
700
|
+
this.serverKeyNameToId.delete(keyName);
|
|
701
|
+
} else {
|
|
702
|
+
// Ensure 'name' field exists even if unknown
|
|
703
|
+
if (!('name' in message.value)) message.value.name = null;
|
|
704
|
+
}
|
|
705
|
+
this.serverKeyIdToName.delete(keyId);
|
|
665
706
|
} else if (msgType === 'Heartbeat') {
|
|
666
707
|
this.lastHeartbeatTime = Date.now() / 1000;
|
|
667
708
|
if (this._initialGracePeriod) {
|
|
@@ -678,10 +719,14 @@
|
|
|
678
719
|
this.serverKeyIdToName.clear();
|
|
679
720
|
this.serverKeyNameToId.clear();
|
|
680
721
|
this.recordMap.clear();
|
|
681
|
-
|
|
722
|
+
let newInterval = value.heartbeat_timeout_interval;
|
|
723
|
+
if (this.timeoutScale !== 1.0) {
|
|
724
|
+
newInterval *= this.timeoutScale;
|
|
725
|
+
}
|
|
726
|
+
this.scaledHeartbeatTimeoutInterval = Math.max(this.scaledHeartbeatTimeoutInterval, newInterval);
|
|
682
727
|
this.lastHeartbeatTime = Date.now() / 1000;
|
|
683
728
|
this._initialGracePeriod = true;
|
|
684
|
-
this._initialGraceDeadline = this.lastHeartbeatTime + this.
|
|
729
|
+
this._initialGraceDeadline = this.lastHeartbeatTime + (this.scaledHeartbeatTimeoutInterval / 1000.0) * 10;
|
|
685
730
|
// New Introduction: do not reset the first-heartbeat promise to avoid racing
|
|
686
731
|
// with consumers already awaiting it. The existing promise will resolve on
|
|
687
732
|
// the first Heartbeat observed during the grace period above.
|