unetjs 3.0.0 → 3.1.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.
package/dist/cjs/unet.cjs CHANGED
@@ -1,8 +1,8 @@
1
- /* unet.js v3.0.0 2023-12-11T11:03:52.183Z */
1
+ /* unet.js v3.1.2 2024-02-21T08:15:43.663Z */
2
2
 
3
3
  'use strict';
4
4
 
5
- /* fjage.js v1.11.2 */
5
+ /* fjage.js v1.12.1 */
6
6
 
7
7
  const isBrowser =
8
8
  typeof window !== "undefined" && typeof window.document !== "undefined";
@@ -46,21 +46,20 @@ class TCPconnector {
46
46
  /**
47
47
  * Create an TCPConnector to connect to a fjage master over TCP
48
48
  * @param {Object} opts
49
- * @param {string} [opts.hostname='localhost'] - hostname/ip address of the master container to connect to
50
- * @param {number} opts.port - port number of the master container to connect to
51
- * @param {string} opts.pathname - path of the master container to connect to
49
+ * @param {string} opts.hostname - hostname/ip address of the master container to connect to
50
+ * @param {string} opts.port - port number of the master container to connect to
52
51
  * @param {boolean} opts.keepAlive - try to reconnect if the connection is lost
53
52
  * @param {number} [opts.reconnectTime=5000] - time before reconnection is attempted after an error
54
53
  */
55
54
  constructor(opts = {}) {
55
+ let host = opts.hostname;
56
+ let port = opts.port;
57
+ this._keepAlive = opts.keepAlive;
58
+ this._reconnectTime = opts.reconnectTime || DEFAULT_RECONNECT_TIME$1;
56
59
  this.url = new URL('tcp://localhost');
57
- let host = opts.hostname || 'localhost';
58
- let port = opts.port || -1;
59
60
  this.url.hostname = host;
60
61
  this.url.port = port;
61
62
  this._buf = '';
62
- this._reconnectTime = opts.reconnectTime || DEFAULT_RECONNECT_TIME$1;
63
- this._keepAlive = opts.keepAlive || true;
64
63
  this._firstConn = true; // if the Gateway has managed to connect to a server before
65
64
  this._firstReConn = true; // if the Gateway has attempted to reconnect to a server before
66
65
  this.pendingOnOpen = []; // list of callbacks make as soon as gateway is open
@@ -232,7 +231,7 @@ class WSConnector {
232
231
  * Create an WSConnector to connect to a fjage master over WebSockets
233
232
  * @param {Object} opts
234
233
  * @param {string} opts.hostname - hostname/ip address of the master container to connect to
235
- * @param {number} opts.port - port number of the master container to connect to
234
+ * @param {string} opts.port - port number of the master container to connect to
236
235
  * @param {string} opts.pathname - path of the master container to connect to
237
236
  * @param {boolean} opts.keepAlive - try to reconnect if the connection is lost
238
237
  * @param {number} [opts.reconnectTime=5000] - time before reconnection is attempted after an error
@@ -242,8 +241,8 @@ class WSConnector {
242
241
  this.url.hostname = opts.hostname;
243
242
  this.url.port = opts.port;
244
243
  this.url.pathname = opts.pathname;
244
+ this._keepAlive = opts.keepAlive;
245
245
  this._reconnectTime = opts.reconnectTime || DEFAULT_RECONNECT_TIME;
246
- this._keepAlive = opts.keepAlive || true;
247
246
  this.debug = opts.debug || false; // debug info to be logged to console?
248
247
  this._firstConn = true; // if the Gateway has managed to connect to a server before
249
248
  this._firstReConn = true; // if the Gateway has attempted to reconnect to a server before
@@ -502,14 +501,17 @@ class AgentID {
502
501
  msg.index = Number.isInteger(index) ? index : -1;
503
502
  const rsp = await this.owner.request(msg, timeout);
504
503
  var ret = Array.isArray(params) ? new Array(params.length).fill(null) : null;
505
- if (!rsp || rsp.perf != Performative.INFORM || !rsp.param) return ret;
504
+ if (!rsp || rsp.perf != Performative.INFORM || !rsp.param) {
505
+ if (this.owner._returnNullOnFailedResponse) return ret;
506
+ else throw new Error(`Unable to set ${this.name}.${params} to ${values}`);
507
+ }
506
508
  if (Array.isArray(params)) {
507
509
  if (!rsp.values) rsp.values = {};
508
510
  if (rsp.param) rsp.values[rsp.param] = rsp.value;
509
511
  const rvals = Object.keys(rsp.values);
510
512
  return params.map( p => {
511
513
  let f = rvals.find(rv => rv.endsWith(p));
512
- return f ? rsp.values[f] : null;
514
+ return f ? rsp.values[f] : undefined;
513
515
  });
514
516
  } else {
515
517
  return rsp.value;
@@ -540,7 +542,10 @@ class AgentID {
540
542
  msg.index = Number.isInteger(index) ? index : -1;
541
543
  const rsp = await this.owner.request(msg, timeout);
542
544
  var ret = Array.isArray(params) ? new Array(params.length).fill(null) : null;
543
- if (!rsp || rsp.perf != Performative.INFORM || (params && (!rsp.param))) return ret;
545
+ if (!rsp || rsp.perf != Performative.INFORM || !rsp.param) {
546
+ if (this.owner._returnNullOnFailedResponse) return ret;
547
+ else throw new Error(`Unable to get ${this.name}.${params}`);
548
+ }
544
549
  // Request for listing of all parameters.
545
550
  if (!params) {
546
551
  if (!rsp.values) rsp.values = {};
@@ -552,7 +557,7 @@ class AgentID {
552
557
  const rvals = Object.keys(rsp.values);
553
558
  return params.map(p => {
554
559
  let f = rvals.find(rv => rv.endsWith(p));
555
- return f ? rsp.values[f] : null;
560
+ return f ? rsp.values[f] : undefined;
556
561
  });
557
562
  } else {
558
563
  return rsp.value;
@@ -636,12 +641,17 @@ class Message {
636
641
  return null;
637
642
  }
638
643
  }
639
- let qclazz = obj.clazz;
640
- let clazz = qclazz.replace(/^.*\./, '');
641
- let rv = MessageClass[clazz] ? new MessageClass[clazz] : new Message();
642
- rv.__clazz__ = qclazz;
643
- rv._inflate(obj.data);
644
- return rv;
644
+ try {
645
+ let qclazz = obj.clazz;
646
+ let clazz = qclazz.replace(/^.*\./, '');
647
+ let rv = MessageClass[clazz] ? new MessageClass[clazz] : new Message();
648
+ rv.__clazz__ = qclazz;
649
+ rv._inflate(obj.data);
650
+ return rv;
651
+ } catch (err) {
652
+ console.warn('Error trying to deserialize JSON object : ', obj, err);
653
+ return null;
654
+ }
645
655
  }
646
656
  }
647
657
 
@@ -652,16 +662,17 @@ class Message {
652
662
  *
653
663
  * @class
654
664
  * @param {Object} opts
655
- * @param {string} [opts.hostname="localhost"] - hostname/ip address of the master container to connect to
656
- * @param {number} [opts.port=1100] - port number of the master container to connect to
657
- * @param {string} [opts.pathname=""] - path of the master container to connect to (for WebSockets)
658
- * @param {string} [opts.keepAlive=true] - try to reconnect if the connection is lost
659
- * @param {number} [opts.queueSize=128] - size of the queue of received messages that haven't been consumed yet
660
- * @param {number} [opts.timeout=1000] - timeout for fjage level messages in ms
661
- * @param {string} [hostname="localhost"] - <strike>Deprecated : hostname/ip address of the master container to connect to</strike>
662
- * @param {number} [port=] - <strike>Deprecated : port number of the master container to connect to</strike>
663
- * @param {string} [pathname=="/ws/"] - <strike>Deprecated : path of the master container to connect to (for WebSockets)</strike>
664
- * @param {number} [timeout=1000] - <strike>Deprecated : timeout for fjage level messages in ms</strike>
665
+ * @param {string} [opts.hostname="localhost"] - hostname/ip address of the master container to connect to
666
+ * @param {string} [opts.port='1100'] - port number of the master container to connect to
667
+ * @param {string} [opts.pathname=""] - path of the master container to connect to (for WebSockets)
668
+ * @param {boolean} [opts.keepAlive=true] - try to reconnect if the connection is lost
669
+ * @param {number} [opts.queueSize=128] - size of the queue of received messages that haven't been consumed yet
670
+ * @param {number} [opts.timeout=1000] - timeout for fjage level messages in ms
671
+ * @param {boolean} [opts.returnNullOnFailedResponse=true] - return null instead of throwing an error when a parameter is not found
672
+ * @param {string} [hostname="localhost"] - <strike>Deprecated : hostname/ip address of the master container to connect to</strike>
673
+ * @param {number} [port=] - <strike>Deprecated : port number of the master container to connect to</strike>
674
+ * @param {string} [pathname=="/ws/"] - <strike>Deprecated : path of the master container to connect to (for WebSockets)</strike>
675
+ * @param {number} [timeout=1000] - <strike>Deprecated : timeout for fjage level messages in ms</strike>
665
676
  */
666
677
  class Gateway {
667
678
 
@@ -670,13 +681,17 @@ class Gateway {
670
681
  if (typeof opts === 'string' || opts instanceof String){
671
682
  opts = {
672
683
  'hostname': opts,
673
- 'port' : port || gObj.location.port,
684
+ 'port' : port,
674
685
  'pathname' : pathname,
675
686
  'timeout' : timeout
676
687
  };
677
688
  console.warn('Deprecated use of Gateway constructor');
678
689
  }
679
- opts = Object.assign({}, GATEWAY_DEFAULTS, opts);
690
+
691
+ // Set defaults
692
+ for (var key in GATEWAY_DEFAULTS){
693
+ if (opts[key] == undefined || opts[key] === '') opts[key] = GATEWAY_DEFAULTS[key];
694
+ }
680
695
  var url = DEFAULT_URL;
681
696
  url.hostname = opts.hostname;
682
697
  url.port = opts.port;
@@ -685,7 +700,8 @@ class Gateway {
685
700
  if (existing) return existing;
686
701
  this._timeout = opts.timeout; // timeout for fjage level messages (agentForService etc)
687
702
  this._keepAlive = opts.keepAlive; // reconnect if connection gets closed/errored
688
- this._queueSize = opts.queueSize; // size of queue
703
+ this._queueSize = opts.queueSize; // size of queue
704
+ this._returnNullOnFailedResponse = opts.returnNullOnFailedResponse; // null or error
689
705
  this.pending = {}; // msgid to callback mapping for pending requests to server
690
706
  this.subscriptions = {}; // hashset for all topics that are subscribed
691
707
  this.listener = {}; // set of callbacks that want to listen to incoming messages
@@ -1036,6 +1052,30 @@ class Gateway {
1036
1052
  this._update_watch();
1037
1053
  }
1038
1054
 
1055
+ /**
1056
+ * Gets a list of all agents in the container.
1057
+ * @returns {Promise<AgentID[]>} - a promise which returns an array of all agent ids when resolved
1058
+ */
1059
+ async agents() {
1060
+ let rq = { action: 'agents' };
1061
+ let rsp = await this._msgTxRx(rq);
1062
+ if (!rsp || !Array.isArray(rsp.agentIDs)) throw new Error('Unable to get agents');
1063
+ return rsp.agentIDs.map(aid => new AgentID(aid, false, this));
1064
+ }
1065
+
1066
+ /**
1067
+ * Check if an agent with a given name exists in the container.
1068
+ *
1069
+ * @param {AgentID|String} agentID - the agent id to check
1070
+ * @returns {Promise<boolean>} - a promise which returns true if the agent exists when resolved
1071
+ */
1072
+ async containsAgent(agentID) {
1073
+ let rq = { action: 'containsAgent', agentID: agentID instanceof AgentID ? agentID.getName() : agentID };
1074
+ let rsp = await this._msgTxRx(rq);
1075
+ if (!rsp) throw new Error('Unable to check if agent exists');
1076
+ return !!rsp.answer;
1077
+ }
1078
+
1039
1079
  /**
1040
1080
  * Finds an agent that provides a named service. If multiple agents are registered
1041
1081
  * to provide a given service, any of the agents' id may be returned.
@@ -1046,7 +1086,11 @@ class Gateway {
1046
1086
  async agentForService(service) {
1047
1087
  let rq = { action: 'agentForService', service: service };
1048
1088
  let rsp = await this._msgTxRx(rq);
1049
- if (!rsp || !rsp.agentID) return;
1089
+ if (!rsp) {
1090
+ if (this._returnNullOnFailedResponse) return null;
1091
+ else throw new Error('Unable to get agent for service');
1092
+ }
1093
+ if (!rsp.agentID) return null;
1050
1094
  return new AgentID(rsp.agentID, false, this);
1051
1095
  }
1052
1096
 
@@ -1060,7 +1104,11 @@ class Gateway {
1060
1104
  let rq = { action: 'agentsForService', service: service };
1061
1105
  let rsp = await this._msgTxRx(rq);
1062
1106
  let aids = [];
1063
- if (!rsp || !Array.isArray(rsp.agentIDs)) return aids;
1107
+ if (!rsp) {
1108
+ if (this._returnNullOnFailedResponse) return aids;
1109
+ else throw new Error('Unable to get agents for service');
1110
+ }
1111
+ if (!Array.isArray(rsp.agentIDs)) return aids;
1064
1112
  for (var i = 0; i < rsp.agentIDs.length; i++)
1065
1113
  aids.push(new AgentID(rsp.agentIDs[i], false, this));
1066
1114
  return aids;
@@ -1279,7 +1327,8 @@ if (isBrowser || isWebWorker){
1279
1327
  'pathname' : '/ws/',
1280
1328
  'timeout': 1000,
1281
1329
  'keepAlive' : true,
1282
- 'queueSize': DEFAULT_QUEUE_SIZE
1330
+ 'queueSize': DEFAULT_QUEUE_SIZE,
1331
+ 'returnNullOnFailedResponse': true
1283
1332
  });
1284
1333
  DEFAULT_URL = new URL('ws://localhost');
1285
1334
  // Enable caching of Gateways
@@ -1293,7 +1342,8 @@ if (isBrowser || isWebWorker){
1293
1342
  'pathname': '',
1294
1343
  'timeout': 1000,
1295
1344
  'keepAlive' : true,
1296
- 'queueSize': DEFAULT_QUEUE_SIZE
1345
+ 'queueSize': DEFAULT_QUEUE_SIZE,
1346
+ 'returnNullOnFailedResponse': true
1297
1347
  });
1298
1348
  DEFAULT_URL = new URL('tcp://localhost');
1299
1349
  gObj.atob = a => Buffer.from(a, 'base64').toString('binary');
@@ -1728,7 +1778,7 @@ const RxFrameNtf = UnetMessages.RxFrameNtf;
1728
1778
  *
1729
1779
  * @class UnetSocket
1730
1780
  * @param {string} [hostname] - hostname/ip address of the master container to connect to
1731
- * @param {number} [port] - port number of the master container to connect to
1781
+ * @param {string} [port] - port number of the master container to connect to
1732
1782
  * @param {string} [path=''] - path of the master container to connect to (for WebSockets)
1733
1783
  * @returns {Promise<UnetSocket>} - Promise which resolves to the UnetSocket object being constructed
1734
1784
  *
package/dist/esm/unet.js CHANGED
@@ -1,6 +1,6 @@
1
- /* unet.js v3.0.0 2023-12-11T11:03:52.183Z */
1
+ /* unet.js v3.1.2 2024-02-21T08:15:43.662Z */
2
2
 
3
- /* fjage.js v1.11.2 */
3
+ /* fjage.js v1.12.1 */
4
4
 
5
5
  const isBrowser =
6
6
  typeof window !== "undefined" && typeof window.document !== "undefined";
@@ -44,21 +44,20 @@ class TCPconnector {
44
44
  /**
45
45
  * Create an TCPConnector to connect to a fjage master over TCP
46
46
  * @param {Object} opts
47
- * @param {string} [opts.hostname='localhost'] - hostname/ip address of the master container to connect to
48
- * @param {number} opts.port - port number of the master container to connect to
49
- * @param {string} opts.pathname - path of the master container to connect to
47
+ * @param {string} opts.hostname - hostname/ip address of the master container to connect to
48
+ * @param {string} opts.port - port number of the master container to connect to
50
49
  * @param {boolean} opts.keepAlive - try to reconnect if the connection is lost
51
50
  * @param {number} [opts.reconnectTime=5000] - time before reconnection is attempted after an error
52
51
  */
53
52
  constructor(opts = {}) {
53
+ let host = opts.hostname;
54
+ let port = opts.port;
55
+ this._keepAlive = opts.keepAlive;
56
+ this._reconnectTime = opts.reconnectTime || DEFAULT_RECONNECT_TIME$1;
54
57
  this.url = new URL('tcp://localhost');
55
- let host = opts.hostname || 'localhost';
56
- let port = opts.port || -1;
57
58
  this.url.hostname = host;
58
59
  this.url.port = port;
59
60
  this._buf = '';
60
- this._reconnectTime = opts.reconnectTime || DEFAULT_RECONNECT_TIME$1;
61
- this._keepAlive = opts.keepAlive || true;
62
61
  this._firstConn = true; // if the Gateway has managed to connect to a server before
63
62
  this._firstReConn = true; // if the Gateway has attempted to reconnect to a server before
64
63
  this.pendingOnOpen = []; // list of callbacks make as soon as gateway is open
@@ -230,7 +229,7 @@ class WSConnector {
230
229
  * Create an WSConnector to connect to a fjage master over WebSockets
231
230
  * @param {Object} opts
232
231
  * @param {string} opts.hostname - hostname/ip address of the master container to connect to
233
- * @param {number} opts.port - port number of the master container to connect to
232
+ * @param {string} opts.port - port number of the master container to connect to
234
233
  * @param {string} opts.pathname - path of the master container to connect to
235
234
  * @param {boolean} opts.keepAlive - try to reconnect if the connection is lost
236
235
  * @param {number} [opts.reconnectTime=5000] - time before reconnection is attempted after an error
@@ -240,8 +239,8 @@ class WSConnector {
240
239
  this.url.hostname = opts.hostname;
241
240
  this.url.port = opts.port;
242
241
  this.url.pathname = opts.pathname;
242
+ this._keepAlive = opts.keepAlive;
243
243
  this._reconnectTime = opts.reconnectTime || DEFAULT_RECONNECT_TIME;
244
- this._keepAlive = opts.keepAlive || true;
245
244
  this.debug = opts.debug || false; // debug info to be logged to console?
246
245
  this._firstConn = true; // if the Gateway has managed to connect to a server before
247
246
  this._firstReConn = true; // if the Gateway has attempted to reconnect to a server before
@@ -500,14 +499,17 @@ class AgentID {
500
499
  msg.index = Number.isInteger(index) ? index : -1;
501
500
  const rsp = await this.owner.request(msg, timeout);
502
501
  var ret = Array.isArray(params) ? new Array(params.length).fill(null) : null;
503
- if (!rsp || rsp.perf != Performative.INFORM || !rsp.param) return ret;
502
+ if (!rsp || rsp.perf != Performative.INFORM || !rsp.param) {
503
+ if (this.owner._returnNullOnFailedResponse) return ret;
504
+ else throw new Error(`Unable to set ${this.name}.${params} to ${values}`);
505
+ }
504
506
  if (Array.isArray(params)) {
505
507
  if (!rsp.values) rsp.values = {};
506
508
  if (rsp.param) rsp.values[rsp.param] = rsp.value;
507
509
  const rvals = Object.keys(rsp.values);
508
510
  return params.map( p => {
509
511
  let f = rvals.find(rv => rv.endsWith(p));
510
- return f ? rsp.values[f] : null;
512
+ return f ? rsp.values[f] : undefined;
511
513
  });
512
514
  } else {
513
515
  return rsp.value;
@@ -538,7 +540,10 @@ class AgentID {
538
540
  msg.index = Number.isInteger(index) ? index : -1;
539
541
  const rsp = await this.owner.request(msg, timeout);
540
542
  var ret = Array.isArray(params) ? new Array(params.length).fill(null) : null;
541
- if (!rsp || rsp.perf != Performative.INFORM || (params && (!rsp.param))) return ret;
543
+ if (!rsp || rsp.perf != Performative.INFORM || !rsp.param) {
544
+ if (this.owner._returnNullOnFailedResponse) return ret;
545
+ else throw new Error(`Unable to get ${this.name}.${params}`);
546
+ }
542
547
  // Request for listing of all parameters.
543
548
  if (!params) {
544
549
  if (!rsp.values) rsp.values = {};
@@ -550,7 +555,7 @@ class AgentID {
550
555
  const rvals = Object.keys(rsp.values);
551
556
  return params.map(p => {
552
557
  let f = rvals.find(rv => rv.endsWith(p));
553
- return f ? rsp.values[f] : null;
558
+ return f ? rsp.values[f] : undefined;
554
559
  });
555
560
  } else {
556
561
  return rsp.value;
@@ -634,12 +639,17 @@ class Message {
634
639
  return null;
635
640
  }
636
641
  }
637
- let qclazz = obj.clazz;
638
- let clazz = qclazz.replace(/^.*\./, '');
639
- let rv = MessageClass[clazz] ? new MessageClass[clazz] : new Message();
640
- rv.__clazz__ = qclazz;
641
- rv._inflate(obj.data);
642
- return rv;
642
+ try {
643
+ let qclazz = obj.clazz;
644
+ let clazz = qclazz.replace(/^.*\./, '');
645
+ let rv = MessageClass[clazz] ? new MessageClass[clazz] : new Message();
646
+ rv.__clazz__ = qclazz;
647
+ rv._inflate(obj.data);
648
+ return rv;
649
+ } catch (err) {
650
+ console.warn('Error trying to deserialize JSON object : ', obj, err);
651
+ return null;
652
+ }
643
653
  }
644
654
  }
645
655
 
@@ -650,16 +660,17 @@ class Message {
650
660
  *
651
661
  * @class
652
662
  * @param {Object} opts
653
- * @param {string} [opts.hostname="localhost"] - hostname/ip address of the master container to connect to
654
- * @param {number} [opts.port=1100] - port number of the master container to connect to
655
- * @param {string} [opts.pathname=""] - path of the master container to connect to (for WebSockets)
656
- * @param {string} [opts.keepAlive=true] - try to reconnect if the connection is lost
657
- * @param {number} [opts.queueSize=128] - size of the queue of received messages that haven't been consumed yet
658
- * @param {number} [opts.timeout=1000] - timeout for fjage level messages in ms
659
- * @param {string} [hostname="localhost"] - <strike>Deprecated : hostname/ip address of the master container to connect to</strike>
660
- * @param {number} [port=] - <strike>Deprecated : port number of the master container to connect to</strike>
661
- * @param {string} [pathname=="/ws/"] - <strike>Deprecated : path of the master container to connect to (for WebSockets)</strike>
662
- * @param {number} [timeout=1000] - <strike>Deprecated : timeout for fjage level messages in ms</strike>
663
+ * @param {string} [opts.hostname="localhost"] - hostname/ip address of the master container to connect to
664
+ * @param {string} [opts.port='1100'] - port number of the master container to connect to
665
+ * @param {string} [opts.pathname=""] - path of the master container to connect to (for WebSockets)
666
+ * @param {boolean} [opts.keepAlive=true] - try to reconnect if the connection is lost
667
+ * @param {number} [opts.queueSize=128] - size of the queue of received messages that haven't been consumed yet
668
+ * @param {number} [opts.timeout=1000] - timeout for fjage level messages in ms
669
+ * @param {boolean} [opts.returnNullOnFailedResponse=true] - return null instead of throwing an error when a parameter is not found
670
+ * @param {string} [hostname="localhost"] - <strike>Deprecated : hostname/ip address of the master container to connect to</strike>
671
+ * @param {number} [port=] - <strike>Deprecated : port number of the master container to connect to</strike>
672
+ * @param {string} [pathname=="/ws/"] - <strike>Deprecated : path of the master container to connect to (for WebSockets)</strike>
673
+ * @param {number} [timeout=1000] - <strike>Deprecated : timeout for fjage level messages in ms</strike>
663
674
  */
664
675
  class Gateway {
665
676
 
@@ -668,13 +679,17 @@ class Gateway {
668
679
  if (typeof opts === 'string' || opts instanceof String){
669
680
  opts = {
670
681
  'hostname': opts,
671
- 'port' : port || gObj.location.port,
682
+ 'port' : port,
672
683
  'pathname' : pathname,
673
684
  'timeout' : timeout
674
685
  };
675
686
  console.warn('Deprecated use of Gateway constructor');
676
687
  }
677
- opts = Object.assign({}, GATEWAY_DEFAULTS, opts);
688
+
689
+ // Set defaults
690
+ for (var key in GATEWAY_DEFAULTS){
691
+ if (opts[key] == undefined || opts[key] === '') opts[key] = GATEWAY_DEFAULTS[key];
692
+ }
678
693
  var url = DEFAULT_URL;
679
694
  url.hostname = opts.hostname;
680
695
  url.port = opts.port;
@@ -683,7 +698,8 @@ class Gateway {
683
698
  if (existing) return existing;
684
699
  this._timeout = opts.timeout; // timeout for fjage level messages (agentForService etc)
685
700
  this._keepAlive = opts.keepAlive; // reconnect if connection gets closed/errored
686
- this._queueSize = opts.queueSize; // size of queue
701
+ this._queueSize = opts.queueSize; // size of queue
702
+ this._returnNullOnFailedResponse = opts.returnNullOnFailedResponse; // null or error
687
703
  this.pending = {}; // msgid to callback mapping for pending requests to server
688
704
  this.subscriptions = {}; // hashset for all topics that are subscribed
689
705
  this.listener = {}; // set of callbacks that want to listen to incoming messages
@@ -1034,6 +1050,30 @@ class Gateway {
1034
1050
  this._update_watch();
1035
1051
  }
1036
1052
 
1053
+ /**
1054
+ * Gets a list of all agents in the container.
1055
+ * @returns {Promise<AgentID[]>} - a promise which returns an array of all agent ids when resolved
1056
+ */
1057
+ async agents() {
1058
+ let rq = { action: 'agents' };
1059
+ let rsp = await this._msgTxRx(rq);
1060
+ if (!rsp || !Array.isArray(rsp.agentIDs)) throw new Error('Unable to get agents');
1061
+ return rsp.agentIDs.map(aid => new AgentID(aid, false, this));
1062
+ }
1063
+
1064
+ /**
1065
+ * Check if an agent with a given name exists in the container.
1066
+ *
1067
+ * @param {AgentID|String} agentID - the agent id to check
1068
+ * @returns {Promise<boolean>} - a promise which returns true if the agent exists when resolved
1069
+ */
1070
+ async containsAgent(agentID) {
1071
+ let rq = { action: 'containsAgent', agentID: agentID instanceof AgentID ? agentID.getName() : agentID };
1072
+ let rsp = await this._msgTxRx(rq);
1073
+ if (!rsp) throw new Error('Unable to check if agent exists');
1074
+ return !!rsp.answer;
1075
+ }
1076
+
1037
1077
  /**
1038
1078
  * Finds an agent that provides a named service. If multiple agents are registered
1039
1079
  * to provide a given service, any of the agents' id may be returned.
@@ -1044,7 +1084,11 @@ class Gateway {
1044
1084
  async agentForService(service) {
1045
1085
  let rq = { action: 'agentForService', service: service };
1046
1086
  let rsp = await this._msgTxRx(rq);
1047
- if (!rsp || !rsp.agentID) return;
1087
+ if (!rsp) {
1088
+ if (this._returnNullOnFailedResponse) return null;
1089
+ else throw new Error('Unable to get agent for service');
1090
+ }
1091
+ if (!rsp.agentID) return null;
1048
1092
  return new AgentID(rsp.agentID, false, this);
1049
1093
  }
1050
1094
 
@@ -1058,7 +1102,11 @@ class Gateway {
1058
1102
  let rq = { action: 'agentsForService', service: service };
1059
1103
  let rsp = await this._msgTxRx(rq);
1060
1104
  let aids = [];
1061
- if (!rsp || !Array.isArray(rsp.agentIDs)) return aids;
1105
+ if (!rsp) {
1106
+ if (this._returnNullOnFailedResponse) return aids;
1107
+ else throw new Error('Unable to get agents for service');
1108
+ }
1109
+ if (!Array.isArray(rsp.agentIDs)) return aids;
1062
1110
  for (var i = 0; i < rsp.agentIDs.length; i++)
1063
1111
  aids.push(new AgentID(rsp.agentIDs[i], false, this));
1064
1112
  return aids;
@@ -1277,7 +1325,8 @@ if (isBrowser || isWebWorker){
1277
1325
  'pathname' : '/ws/',
1278
1326
  'timeout': 1000,
1279
1327
  'keepAlive' : true,
1280
- 'queueSize': DEFAULT_QUEUE_SIZE
1328
+ 'queueSize': DEFAULT_QUEUE_SIZE,
1329
+ 'returnNullOnFailedResponse': true
1281
1330
  });
1282
1331
  DEFAULT_URL = new URL('ws://localhost');
1283
1332
  // Enable caching of Gateways
@@ -1291,7 +1340,8 @@ if (isBrowser || isWebWorker){
1291
1340
  'pathname': '',
1292
1341
  'timeout': 1000,
1293
1342
  'keepAlive' : true,
1294
- 'queueSize': DEFAULT_QUEUE_SIZE
1343
+ 'queueSize': DEFAULT_QUEUE_SIZE,
1344
+ 'returnNullOnFailedResponse': true
1295
1345
  });
1296
1346
  DEFAULT_URL = new URL('tcp://localhost');
1297
1347
  gObj.atob = a => Buffer.from(a, 'base64').toString('binary');
@@ -1726,7 +1776,7 @@ const RxFrameNtf = UnetMessages.RxFrameNtf;
1726
1776
  *
1727
1777
  * @class UnetSocket
1728
1778
  * @param {string} [hostname] - hostname/ip address of the master container to connect to
1729
- * @param {number} [port] - port number of the master container to connect to
1779
+ * @param {string} [port] - port number of the master container to connect to
1730
1780
  * @param {string} [path=''] - path of the master container to connect to (for WebSockets)
1731
1781
  * @returns {Promise<UnetSocket>} - Promise which resolves to the UnetSocket object being constructed
1732
1782
  *