jsgar 4.9.0 → 4.9.2

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.
Files changed (3) hide show
  1. package/dist/gar.umd.js +35 -17
  2. package/gar.js +35 -17
  3. package/package.json +1 -1
package/dist/gar.umd.js CHANGED
@@ -1040,10 +1040,14 @@
1040
1040
  const keyId = keyUpdate.key_id;
1041
1041
  const keyName = keyUpdate.name;
1042
1042
 
1043
- // Handle key introduction if name is provided and key is new
1044
- if (keyName && !this.serverKeyIdToName.has(keyId)) {
1045
- this.serverKeyIdToName.set(keyId, keyName);
1046
- this.serverKeyNameToId.set(keyName, keyId);
1043
+ // Handle key introduction if name is provided. De-dup only the
1044
+ // serverKey*ToName/Id maps; always invoke the registered handler
1045
+ // so callers see every BatchUpdate-carried key event.
1046
+ if (keyName) {
1047
+ if (!this.serverKeyIdToName.has(keyId)) {
1048
+ this.serverKeyIdToName.set(keyId, keyName);
1049
+ this.serverKeyNameToId.set(keyName, keyId);
1050
+ }
1047
1051
 
1048
1052
  // If no batch handler but key handler exists, call KeyIntroduction handler
1049
1053
  if (!hasBatchHandler && keyHandler) {
@@ -1101,9 +1105,18 @@
1101
1105
  this.log('INFO', 'Received Logoff from server');
1102
1106
  this.stop();
1103
1107
  } else if (msgType === 'Error') {
1104
- this.log('ERROR', `GAR ${message.value.message}`);
1105
- this.exitCode = 1;
1106
- this.stop();
1108
+ // proto.gar's `error` struct carries `recoverable: bool`. Recoverable errors mean
1109
+ // the server processed the failing message (e.g. a publish that violated some
1110
+ // server-side check), logged it, and kept the connection alive. We dispatch the
1111
+ // message to the user's error handler but don't tear down the client. Non-recoverable
1112
+ // errors are followed by a server disconnect; we set exit_code + stop() so callers
1113
+ // (CLI tools, the html_view UI) see the failure.
1114
+ const recoverable = !!message.value.recoverable;
1115
+ this.log(recoverable ? 'WARNING' : 'ERROR', `GAR ${message.value.message}`);
1116
+ if (!recoverable) {
1117
+ this.exitCode = 1;
1118
+ this.stop();
1119
+ }
1107
1120
  }
1108
1121
 
1109
1122
  this.checkHeartbeat();
@@ -1387,28 +1400,25 @@
1387
1400
  }
1388
1401
 
1389
1402
  /**
1390
- * Introduce a new key if not already known and return local key ID.
1403
+ * Introduce a new key if not already known and return local key ID. className is required;
1404
+ * a bare introduction with no class_list can't be routed by upstream proxies.
1391
1405
  * @param {string} name - Key name
1392
- * @param {string|Array<string>|null} [className=null] - Class name(s)
1406
+ * @param {string|Array<string>} className - Class name(s)
1393
1407
  * @returns {number} Local key ID
1394
1408
  */
1395
- getAndPossiblyIntroduceKeyId(name, className = null) {
1409
+ getAndPossiblyIntroduceKeyId(name, className) {
1396
1410
  const existingId = this.localKeyMap.get(name);
1397
1411
  if (existingId !== undefined && this.invalidatedKeyIds.has(existingId)) {
1398
1412
  this.invalidatedKeyIds.delete(existingId);
1399
1413
  this.localKeyMap.delete(name);
1400
1414
  }
1401
1415
  if (!this.localKeyMap.has(name)) {
1416
+ if (!className) throw new Error(`getAndPossiblyIntroduceKeyId: className required to introduce key "${name}"`);
1402
1417
  const keyId = this.localKeyCounter++;
1403
1418
  this.localKeyMap.set(name, keyId);
1404
1419
  const value = { key_id: keyId, name };
1405
- if (className) {
1406
- if (typeof className === 'string') {
1407
- value.class_list = className.split(/\s+/).filter(Boolean);
1408
- } else if (Array.isArray(className)) {
1409
- value.class_list = className;
1410
- }
1411
- }
1420
+ if (typeof className === 'string') value.class_list = className.split(/\s+/).filter(Boolean);
1421
+ else if (Array.isArray(className)) value.class_list = className;
1412
1422
  this.sendMessage({ message_type: 'KeyIntroduction', value });
1413
1423
  }
1414
1424
  return this.localKeyMap.get(name);
@@ -1511,6 +1521,10 @@
1511
1521
  * @param {any} value - JSON-serializable value
1512
1522
  */
1513
1523
  publishRecordWithIds(keyId, topicId, value) {
1524
+ if (value === undefined) {
1525
+ this.log('ERROR', `publishRecordWithIds(${keyId}, ${topicId}): refusing to publish undefined — JSON.stringify would drop the "value" field and the server would reject with "Missing record 'value' field"`);
1526
+ return;
1527
+ }
1514
1528
  const updateMsg = {
1515
1529
  message_type: 'JSONRecordUpdate',
1516
1530
  value: {
@@ -1529,6 +1543,10 @@
1529
1543
  * @param {string|null} [className=null] - Class name
1530
1544
  */
1531
1545
  publishRecord(keyName, topicName, value, className = null) {
1546
+ if (value === undefined) {
1547
+ this.log('ERROR', `publishRecord(${keyName}, ${topicName}): refusing to publish undefined — JSON.stringify would drop the "value" field and the server would reject with "Missing record 'value' field"`);
1548
+ return;
1549
+ }
1532
1550
  const keyId = this.getAndPossiblyIntroduceKeyId(keyName, className);
1533
1551
  const topicId = this.getAndPossiblyIntroduceTopicId(topicName);
1534
1552
  this.publishRecordWithIds(keyId, topicId, value);
package/gar.js CHANGED
@@ -1034,10 +1034,14 @@ class GARClient {
1034
1034
  const keyId = keyUpdate.key_id;
1035
1035
  const keyName = keyUpdate.name;
1036
1036
 
1037
- // Handle key introduction if name is provided and key is new
1038
- if (keyName && !this.serverKeyIdToName.has(keyId)) {
1039
- this.serverKeyIdToName.set(keyId, keyName);
1040
- this.serverKeyNameToId.set(keyName, keyId);
1037
+ // Handle key introduction if name is provided. De-dup only the
1038
+ // serverKey*ToName/Id maps; always invoke the registered handler
1039
+ // so callers see every BatchUpdate-carried key event.
1040
+ if (keyName) {
1041
+ if (!this.serverKeyIdToName.has(keyId)) {
1042
+ this.serverKeyIdToName.set(keyId, keyName);
1043
+ this.serverKeyNameToId.set(keyName, keyId);
1044
+ }
1041
1045
 
1042
1046
  // If no batch handler but key handler exists, call KeyIntroduction handler
1043
1047
  if (!hasBatchHandler && keyHandler) {
@@ -1095,9 +1099,18 @@ class GARClient {
1095
1099
  this.log('INFO', 'Received Logoff from server');
1096
1100
  this.stop();
1097
1101
  } else if (msgType === 'Error') {
1098
- this.log('ERROR', `GAR ${message.value.message}`);
1099
- this.exitCode = 1;
1100
- this.stop();
1102
+ // proto.gar's `error` struct carries `recoverable: bool`. Recoverable errors mean
1103
+ // the server processed the failing message (e.g. a publish that violated some
1104
+ // server-side check), logged it, and kept the connection alive. We dispatch the
1105
+ // message to the user's error handler but don't tear down the client. Non-recoverable
1106
+ // errors are followed by a server disconnect; we set exit_code + stop() so callers
1107
+ // (CLI tools, the html_view UI) see the failure.
1108
+ const recoverable = !!message.value.recoverable;
1109
+ this.log(recoverable ? 'WARNING' : 'ERROR', `GAR ${message.value.message}`);
1110
+ if (!recoverable) {
1111
+ this.exitCode = 1;
1112
+ this.stop();
1113
+ }
1101
1114
  }
1102
1115
 
1103
1116
  this.checkHeartbeat();
@@ -1381,28 +1394,25 @@ class GARClient {
1381
1394
  }
1382
1395
 
1383
1396
  /**
1384
- * Introduce a new key if not already known and return local key ID.
1397
+ * Introduce a new key if not already known and return local key ID. className is required;
1398
+ * a bare introduction with no class_list can't be routed by upstream proxies.
1385
1399
  * @param {string} name - Key name
1386
- * @param {string|Array<string>|null} [className=null] - Class name(s)
1400
+ * @param {string|Array<string>} className - Class name(s)
1387
1401
  * @returns {number} Local key ID
1388
1402
  */
1389
- getAndPossiblyIntroduceKeyId(name, className = null) {
1403
+ getAndPossiblyIntroduceKeyId(name, className) {
1390
1404
  const existingId = this.localKeyMap.get(name);
1391
1405
  if (existingId !== undefined && this.invalidatedKeyIds.has(existingId)) {
1392
1406
  this.invalidatedKeyIds.delete(existingId);
1393
1407
  this.localKeyMap.delete(name);
1394
1408
  }
1395
1409
  if (!this.localKeyMap.has(name)) {
1410
+ if (!className) throw new Error(`getAndPossiblyIntroduceKeyId: className required to introduce key "${name}"`);
1396
1411
  const keyId = this.localKeyCounter++;
1397
1412
  this.localKeyMap.set(name, keyId);
1398
1413
  const value = { key_id: keyId, name };
1399
- if (className) {
1400
- if (typeof className === 'string') {
1401
- value.class_list = className.split(/\s+/).filter(Boolean);
1402
- } else if (Array.isArray(className)) {
1403
- value.class_list = className;
1404
- }
1405
- }
1414
+ if (typeof className === 'string') value.class_list = className.split(/\s+/).filter(Boolean);
1415
+ else if (Array.isArray(className)) value.class_list = className;
1406
1416
  this.sendMessage({ message_type: 'KeyIntroduction', value });
1407
1417
  }
1408
1418
  return this.localKeyMap.get(name);
@@ -1505,6 +1515,10 @@ class GARClient {
1505
1515
  * @param {any} value - JSON-serializable value
1506
1516
  */
1507
1517
  publishRecordWithIds(keyId, topicId, value) {
1518
+ if (value === undefined) {
1519
+ this.log('ERROR', `publishRecordWithIds(${keyId}, ${topicId}): refusing to publish undefined — JSON.stringify would drop the "value" field and the server would reject with "Missing record 'value' field"`);
1520
+ return;
1521
+ }
1508
1522
  const updateMsg = {
1509
1523
  message_type: 'JSONRecordUpdate',
1510
1524
  value: {
@@ -1523,6 +1537,10 @@ class GARClient {
1523
1537
  * @param {string|null} [className=null] - Class name
1524
1538
  */
1525
1539
  publishRecord(keyName, topicName, value, className = null) {
1540
+ if (value === undefined) {
1541
+ this.log('ERROR', `publishRecord(${keyName}, ${topicName}): refusing to publish undefined — JSON.stringify would drop the "value" field and the server would reject with "Missing record 'value' field"`);
1542
+ return;
1543
+ }
1526
1544
  const keyId = this.getAndPossiblyIntroduceKeyId(keyName, className);
1527
1545
  const topicId = this.getAndPossiblyIntroduceTopicId(topicName);
1528
1546
  this.publishRecordWithIds(keyId, topicId, value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsgar",
3
- "version": "4.9.0",
3
+ "version": "4.9.2",
4
4
  "description": "A Javascript client for the GAR protocol",
5
5
  "type": "module",
6
6
  "main": "dist/gar.umd.js",