jsgar 4.6.3 → 4.9.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 +69 -11
- package/gar.js +69 -11
- package/package.json +4 -1
package/dist/gar.umd.js
CHANGED
|
@@ -200,6 +200,7 @@
|
|
|
200
200
|
this.websocket = null;
|
|
201
201
|
this.messageQueue = [];
|
|
202
202
|
this.connected = false;
|
|
203
|
+
this.connectedCallback = null;
|
|
203
204
|
this.reconnectDelay = 5000; // Milliseconds
|
|
204
205
|
this.user = user;
|
|
205
206
|
this.working_namespace = working_namespace;
|
|
@@ -383,18 +384,18 @@
|
|
|
383
384
|
});
|
|
384
385
|
|
|
385
386
|
this.websocket = await connectionPromise;
|
|
386
|
-
this.
|
|
387
|
+
this._setConnected(true);
|
|
387
388
|
this.log('INFO', `Connected to WebSocket server at ${this.wsEndpoint} using gar-protocol`);
|
|
388
389
|
|
|
389
390
|
this.websocket.onclose = () => {
|
|
390
391
|
this.log('WARNING', 'WebSocket connection closed.');
|
|
391
|
-
this.
|
|
392
|
+
this._setConnected(false);
|
|
392
393
|
this.websocket = null;
|
|
393
394
|
this._reconnect();
|
|
394
395
|
};
|
|
395
396
|
this.websocket.onerror = (error) => {
|
|
396
397
|
this.log('ERROR', `WebSocket error: ${error.message || 'Unknown error'}`);
|
|
397
|
-
this.
|
|
398
|
+
this._setConnected(false);
|
|
398
399
|
this.websocket = null;
|
|
399
400
|
};
|
|
400
401
|
|
|
@@ -402,7 +403,7 @@
|
|
|
402
403
|
this._receiveMessages();
|
|
403
404
|
|
|
404
405
|
} catch (e) {
|
|
405
|
-
this.
|
|
406
|
+
this._setConnected(false);
|
|
406
407
|
this.websocket = null;
|
|
407
408
|
// 401 is a permanent auth rejection — retrying won't help
|
|
408
409
|
if (e.message && e.message.includes('401')) {
|
|
@@ -454,7 +455,7 @@
|
|
|
454
455
|
}
|
|
455
456
|
this.log('INFO', 'Done sending messages.');
|
|
456
457
|
this.stop();
|
|
457
|
-
this.
|
|
458
|
+
this._setConnected(false);
|
|
458
459
|
}
|
|
459
460
|
|
|
460
461
|
/**
|
|
@@ -467,6 +468,34 @@
|
|
|
467
468
|
};
|
|
468
469
|
}
|
|
469
470
|
|
|
471
|
+
/**
|
|
472
|
+
* Register a callback fired when the WS `connected` flag transitions.
|
|
473
|
+
* @param {Function} handler - Callback receiving the new boolean value
|
|
474
|
+
*/
|
|
475
|
+
registerConnectedHandler(handler) {
|
|
476
|
+
this.connectedCallback = handler;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Clear the connected handler.
|
|
481
|
+
*/
|
|
482
|
+
clearConnectedHandler() {
|
|
483
|
+
this.connectedCallback = null;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Internal setter that fires connectedCallback on state transitions.
|
|
488
|
+
* @param {boolean} value
|
|
489
|
+
*/
|
|
490
|
+
_setConnected(value) {
|
|
491
|
+
if (this.connected === value) return;
|
|
492
|
+
this.connected = value;
|
|
493
|
+
if (this.connectedCallback) {
|
|
494
|
+
try { this.connectedCallback(value); }
|
|
495
|
+
catch (e) { this.log('ERROR', `connectedCallback threw: ${e.message}`); }
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
470
499
|
/**
|
|
471
500
|
* Register a callback handler for a specific message type.
|
|
472
501
|
* @param {string} messageType - Type of message
|
|
@@ -802,7 +831,7 @@
|
|
|
802
831
|
* Stop the client without sending pending messages.
|
|
803
832
|
*/
|
|
804
833
|
halt() {
|
|
805
|
-
this.
|
|
834
|
+
this._setConnected(false);
|
|
806
835
|
this.stop();
|
|
807
836
|
}
|
|
808
837
|
|
|
@@ -1331,13 +1360,29 @@
|
|
|
1331
1360
|
* @param {boolean} [options.skipOwnershipChecks=false] - Bypass ownership validation.
|
|
1332
1361
|
* @param {boolean} [options.requireExisting=false] - Only write to existing keys; do not create new keys.
|
|
1333
1362
|
* @param {number} [options.clientKeyId=0] - The g::Client key ID for ownership. 0 uses the connection key.
|
|
1363
|
+
* @param {string} [options.clientKeyName=''] - If non-empty, atomically introduce this g::Client key with
|
|
1364
|
+
* the message. The server creates the key and maps the remote id in one round-trip; no separate
|
|
1365
|
+
* KeyIntroduction is sent. Ignored if the name is already mapped locally (existing id is reused).
|
|
1334
1366
|
*/
|
|
1335
|
-
updateActiveOwnership({ msgRef = 0, ownershipAction = 'None', skipOwnershipChecks = false, requireExisting = false, clientKeyId = 0 } = {}) {
|
|
1367
|
+
updateActiveOwnership({ msgRef = 0, ownershipAction = 'None', skipOwnershipChecks = false, requireExisting = false, clientKeyId = 0, clientKeyName = '' } = {}) {
|
|
1368
|
+
if (clientKeyName) {
|
|
1369
|
+
const existingId = this.localKeyMap.get(clientKeyName);
|
|
1370
|
+
if (existingId !== undefined) {
|
|
1371
|
+
clientKeyId = existingId;
|
|
1372
|
+
clientKeyName = '';
|
|
1373
|
+
} else {
|
|
1374
|
+
clientKeyId = this.localKeyCounter++;
|
|
1375
|
+
this.localKeyMap.set(clientKeyName, clientKeyId);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1336
1378
|
const newOwnership = { msg_ref: msgRef, ownership_action: ownershipAction, skip_ownership_checks: skipOwnershipChecks, require_existing: requireExisting, client_key_id: clientKeyId };
|
|
1337
1379
|
const cur = this._activeOwnership;
|
|
1338
|
-
|
|
1380
|
+
const changed = newOwnership.msg_ref !== cur.msg_ref || newOwnership.ownership_action !== cur.ownership_action || newOwnership.skip_ownership_checks !== cur.skip_ownership_checks || newOwnership.require_existing !== cur.require_existing || newOwnership.client_key_id !== cur.client_key_id;
|
|
1381
|
+
if (changed || clientKeyName) {
|
|
1339
1382
|
this._activeOwnership = newOwnership;
|
|
1340
|
-
|
|
1383
|
+
const value = { ...newOwnership };
|
|
1384
|
+
if (clientKeyName) value.client_key_name = clientKeyName;
|
|
1385
|
+
this.sendMessage({ message_type: 'ActiveOwnership', value });
|
|
1341
1386
|
}
|
|
1342
1387
|
}
|
|
1343
1388
|
|
|
@@ -1405,11 +1450,24 @@
|
|
|
1405
1450
|
/**
|
|
1406
1451
|
* Delete a key by name if it exists in the localKeyMap. Safe to call multiple times.
|
|
1407
1452
|
* @param {string} keyName - Key name
|
|
1453
|
+
* @param {string|null} [className=null] - If set, removes the key from this class only via
|
|
1454
|
+
* a KeyIntroduction with deleted_class set; the key may still exist on the server in
|
|
1455
|
+
* other classes and the localKeyMap entry is preserved. If unset, sends DeleteKey to
|
|
1456
|
+
* remove the key entirely and forgets the local id so a future
|
|
1457
|
+
* getAndPossiblyIntroduceKeyId(keyName) re-introduces with a fresh id.
|
|
1408
1458
|
*/
|
|
1409
|
-
deleteKey(keyName) {
|
|
1459
|
+
deleteKey(keyName, className = null) {
|
|
1410
1460
|
const keyId = this.localKeyMap.get(keyName);
|
|
1411
|
-
if (keyId)
|
|
1461
|
+
if (keyId === undefined) return;
|
|
1462
|
+
if (className) {
|
|
1463
|
+
this.sendMessage({
|
|
1464
|
+
message_type: 'KeyIntroduction',
|
|
1465
|
+
value: { key_id: keyId, name: keyName, deleted_class: className },
|
|
1466
|
+
});
|
|
1467
|
+
} else {
|
|
1412
1468
|
this.publishDeleteKey(keyId);
|
|
1469
|
+
// Forget the local id so future re-introduction allocates a new one.
|
|
1470
|
+
this.localKeyMap.delete(keyName);
|
|
1413
1471
|
}
|
|
1414
1472
|
}
|
|
1415
1473
|
|
package/gar.js
CHANGED
|
@@ -194,6 +194,7 @@ class GARClient {
|
|
|
194
194
|
this.websocket = null;
|
|
195
195
|
this.messageQueue = [];
|
|
196
196
|
this.connected = false;
|
|
197
|
+
this.connectedCallback = null;
|
|
197
198
|
this.reconnectDelay = 5000; // Milliseconds
|
|
198
199
|
this.user = user;
|
|
199
200
|
this.working_namespace = working_namespace;
|
|
@@ -377,18 +378,18 @@ class GARClient {
|
|
|
377
378
|
});
|
|
378
379
|
|
|
379
380
|
this.websocket = await connectionPromise;
|
|
380
|
-
this.
|
|
381
|
+
this._setConnected(true);
|
|
381
382
|
this.log('INFO', `Connected to WebSocket server at ${this.wsEndpoint} using gar-protocol`);
|
|
382
383
|
|
|
383
384
|
this.websocket.onclose = () => {
|
|
384
385
|
this.log('WARNING', 'WebSocket connection closed.');
|
|
385
|
-
this.
|
|
386
|
+
this._setConnected(false);
|
|
386
387
|
this.websocket = null;
|
|
387
388
|
this._reconnect();
|
|
388
389
|
};
|
|
389
390
|
this.websocket.onerror = (error) => {
|
|
390
391
|
this.log('ERROR', `WebSocket error: ${error.message || 'Unknown error'}`);
|
|
391
|
-
this.
|
|
392
|
+
this._setConnected(false);
|
|
392
393
|
this.websocket = null;
|
|
393
394
|
};
|
|
394
395
|
|
|
@@ -396,7 +397,7 @@ class GARClient {
|
|
|
396
397
|
this._receiveMessages();
|
|
397
398
|
|
|
398
399
|
} catch (e) {
|
|
399
|
-
this.
|
|
400
|
+
this._setConnected(false);
|
|
400
401
|
this.websocket = null;
|
|
401
402
|
// 401 is a permanent auth rejection — retrying won't help
|
|
402
403
|
if (e.message && e.message.includes('401')) {
|
|
@@ -448,7 +449,7 @@ class GARClient {
|
|
|
448
449
|
}
|
|
449
450
|
this.log('INFO', 'Done sending messages.');
|
|
450
451
|
this.stop()
|
|
451
|
-
this.
|
|
452
|
+
this._setConnected(false);
|
|
452
453
|
}
|
|
453
454
|
|
|
454
455
|
/**
|
|
@@ -461,6 +462,34 @@ class GARClient {
|
|
|
461
462
|
};
|
|
462
463
|
}
|
|
463
464
|
|
|
465
|
+
/**
|
|
466
|
+
* Register a callback fired when the WS `connected` flag transitions.
|
|
467
|
+
* @param {Function} handler - Callback receiving the new boolean value
|
|
468
|
+
*/
|
|
469
|
+
registerConnectedHandler(handler) {
|
|
470
|
+
this.connectedCallback = handler;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Clear the connected handler.
|
|
475
|
+
*/
|
|
476
|
+
clearConnectedHandler() {
|
|
477
|
+
this.connectedCallback = null;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Internal setter that fires connectedCallback on state transitions.
|
|
482
|
+
* @param {boolean} value
|
|
483
|
+
*/
|
|
484
|
+
_setConnected(value) {
|
|
485
|
+
if (this.connected === value) return;
|
|
486
|
+
this.connected = value;
|
|
487
|
+
if (this.connectedCallback) {
|
|
488
|
+
try { this.connectedCallback(value); }
|
|
489
|
+
catch (e) { this.log('ERROR', `connectedCallback threw: ${e.message}`); }
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
464
493
|
/**
|
|
465
494
|
* Register a callback handler for a specific message type.
|
|
466
495
|
* @param {string} messageType - Type of message
|
|
@@ -796,7 +825,7 @@ class GARClient {
|
|
|
796
825
|
* Stop the client without sending pending messages.
|
|
797
826
|
*/
|
|
798
827
|
halt() {
|
|
799
|
-
this.
|
|
828
|
+
this._setConnected(false);
|
|
800
829
|
this.stop();
|
|
801
830
|
}
|
|
802
831
|
|
|
@@ -1325,13 +1354,29 @@ class GARClient {
|
|
|
1325
1354
|
* @param {boolean} [options.skipOwnershipChecks=false] - Bypass ownership validation.
|
|
1326
1355
|
* @param {boolean} [options.requireExisting=false] - Only write to existing keys; do not create new keys.
|
|
1327
1356
|
* @param {number} [options.clientKeyId=0] - The g::Client key ID for ownership. 0 uses the connection key.
|
|
1357
|
+
* @param {string} [options.clientKeyName=''] - If non-empty, atomically introduce this g::Client key with
|
|
1358
|
+
* the message. The server creates the key and maps the remote id in one round-trip; no separate
|
|
1359
|
+
* KeyIntroduction is sent. Ignored if the name is already mapped locally (existing id is reused).
|
|
1328
1360
|
*/
|
|
1329
|
-
updateActiveOwnership({ msgRef = 0, ownershipAction = 'None', skipOwnershipChecks = false, requireExisting = false, clientKeyId = 0 } = {}) {
|
|
1361
|
+
updateActiveOwnership({ msgRef = 0, ownershipAction = 'None', skipOwnershipChecks = false, requireExisting = false, clientKeyId = 0, clientKeyName = '' } = {}) {
|
|
1362
|
+
if (clientKeyName) {
|
|
1363
|
+
const existingId = this.localKeyMap.get(clientKeyName);
|
|
1364
|
+
if (existingId !== undefined) {
|
|
1365
|
+
clientKeyId = existingId;
|
|
1366
|
+
clientKeyName = '';
|
|
1367
|
+
} else {
|
|
1368
|
+
clientKeyId = this.localKeyCounter++;
|
|
1369
|
+
this.localKeyMap.set(clientKeyName, clientKeyId);
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1330
1372
|
const newOwnership = { msg_ref: msgRef, ownership_action: ownershipAction, skip_ownership_checks: skipOwnershipChecks, require_existing: requireExisting, client_key_id: clientKeyId };
|
|
1331
1373
|
const cur = this._activeOwnership;
|
|
1332
|
-
|
|
1374
|
+
const changed = newOwnership.msg_ref !== cur.msg_ref || newOwnership.ownership_action !== cur.ownership_action || newOwnership.skip_ownership_checks !== cur.skip_ownership_checks || newOwnership.require_existing !== cur.require_existing || newOwnership.client_key_id !== cur.client_key_id;
|
|
1375
|
+
if (changed || clientKeyName) {
|
|
1333
1376
|
this._activeOwnership = newOwnership;
|
|
1334
|
-
|
|
1377
|
+
const value = { ...newOwnership };
|
|
1378
|
+
if (clientKeyName) value.client_key_name = clientKeyName;
|
|
1379
|
+
this.sendMessage({ message_type: 'ActiveOwnership', value });
|
|
1335
1380
|
}
|
|
1336
1381
|
}
|
|
1337
1382
|
|
|
@@ -1399,11 +1444,24 @@ class GARClient {
|
|
|
1399
1444
|
/**
|
|
1400
1445
|
* Delete a key by name if it exists in the localKeyMap. Safe to call multiple times.
|
|
1401
1446
|
* @param {string} keyName - Key name
|
|
1447
|
+
* @param {string|null} [className=null] - If set, removes the key from this class only via
|
|
1448
|
+
* a KeyIntroduction with deleted_class set; the key may still exist on the server in
|
|
1449
|
+
* other classes and the localKeyMap entry is preserved. If unset, sends DeleteKey to
|
|
1450
|
+
* remove the key entirely and forgets the local id so a future
|
|
1451
|
+
* getAndPossiblyIntroduceKeyId(keyName) re-introduces with a fresh id.
|
|
1402
1452
|
*/
|
|
1403
|
-
deleteKey(keyName) {
|
|
1453
|
+
deleteKey(keyName, className = null) {
|
|
1404
1454
|
const keyId = this.localKeyMap.get(keyName);
|
|
1405
|
-
if (keyId)
|
|
1455
|
+
if (keyId === undefined) return;
|
|
1456
|
+
if (className) {
|
|
1457
|
+
this.sendMessage({
|
|
1458
|
+
message_type: 'KeyIntroduction',
|
|
1459
|
+
value: { key_id: keyId, name: keyName, deleted_class: className },
|
|
1460
|
+
});
|
|
1461
|
+
} else {
|
|
1406
1462
|
this.publishDeleteKey(keyId);
|
|
1463
|
+
// Forget the local id so future re-introduction allocates a new one.
|
|
1464
|
+
this.localKeyMap.delete(keyName);
|
|
1407
1465
|
}
|
|
1408
1466
|
}
|
|
1409
1467
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsgar",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.0",
|
|
4
4
|
"description": "A Javascript client for the GAR protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/gar.umd.js",
|
|
@@ -36,8 +36,11 @@
|
|
|
36
36
|
"@eslint/json": "^0.13.2",
|
|
37
37
|
"@rollup/plugin-commonjs": "^24.0.0",
|
|
38
38
|
"@rollup/plugin-node-resolve": "^15.0.0",
|
|
39
|
+
"ag-grid-community": "^34.1.1",
|
|
40
|
+
"ag-grid-enterprise": "^34.1.1",
|
|
39
41
|
"eslint": "^9.39.4",
|
|
40
42
|
"globals": "^16.0.0",
|
|
43
|
+
"playwright": "^1.50.0",
|
|
41
44
|
"rollup": "^3.0.0"
|
|
42
45
|
}
|
|
43
46
|
}
|