vrpc 3.0.3 → 3.1.1

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.
@@ -59,7 +59,7 @@ class VrpcClient extends EventEmitter {
59
59
  *
60
60
  * @constructor
61
61
  * @param {Object} options
62
- * @param {String} [options.username='<user>@<hostname>-<platform>-js'] MQTT username
62
+ * @param {String} [options.username] MQTT username
63
63
  * @param {String} [options.password] MQTT password (if no token is provided)
64
64
  * @param {String} [options.token] Access token
65
65
  * @param {String} options.domain Sets the domain
@@ -68,6 +68,8 @@ class VrpcClient extends EventEmitter {
68
68
  * @param {Number} [options.timeout=6000] Maximum time in ms to wait for a RPC answer
69
69
  * @param {Object} [options.log=console] Log object (must support debug, info, warn, and error level)
70
70
  * @param {Boolean} [options.bestEffort=false] If true, message will be sent with best effort, i.e. no caching if offline
71
+ * @param {String} [options.mqttClientId='<generated()>'] Explicitly sets the mqtt client id
72
+ * @param {String} [options.identity] Explicitly sets a vrpc client identity
71
73
  * @example
72
74
  * const client = new VrpcClient({
73
75
  * domain: 'vrpc',
@@ -77,13 +79,15 @@ class VrpcClient extends EventEmitter {
77
79
  constructor ({
78
80
  token,
79
81
  password,
80
- username = VrpcClient._generateUserName(),
82
+ username,
81
83
  domain,
82
84
  agent = '*',
83
85
  broker = 'mqtts://vrpc.io:8883',
84
86
  timeout = 6 * 1000,
85
87
  log = 'console',
86
- bestEffort = false
88
+ bestEffort = false,
89
+ mqttClientId = null,
90
+ identity = null
87
91
  } = {}) {
88
92
  super()
89
93
  // domain sanity check
@@ -99,6 +103,12 @@ class VrpcClient extends EventEmitter {
99
103
  'The agent must NOT contain any of those characters: "+", "/", "#"'
100
104
  )
101
105
  }
106
+ // identity sanity check
107
+ if (identity && identity.match(/[+/#$]/)) {
108
+ throw new Error(
109
+ 'The identity must NOT contain any of those characters: "+", "/", "#", "$"'
110
+ )
111
+ }
102
112
  this._token = token
103
113
  this._username = username
104
114
  this._password = password
@@ -106,10 +116,11 @@ class VrpcClient extends EventEmitter {
106
116
  this._domain = domain
107
117
  this._broker = broker
108
118
  this._timeout = timeout
119
+ this._identity = identity
109
120
  this._qos = this._qos = bestEffort ? 0 : 1
110
121
 
111
122
  this._instance = nanoid(8)
112
- this._mqttClientId = this._createMqttClientId()
123
+ this._mqttClientId = mqttClientId || this._createMqttClientId()
113
124
  this._vrpcClientId = this._createVrpcClientId()
114
125
  this._agents = {}
115
126
  this._eventEmitter = new EventEmitter()
@@ -152,7 +163,7 @@ class VrpcClient extends EventEmitter {
152
163
  let username = this._username
153
164
  let password = this._password
154
165
  if (this._token) {
155
- username = username || VrpcClient._generateUserName()
166
+ username = username || this._generateUserName()
156
167
  password = this._token
157
168
  }
158
169
  const options = {
@@ -324,7 +335,7 @@ class VrpcClient extends EventEmitter {
324
335
  f: isIsolated ? '__createIsolated__' : '__createShared__',
325
336
  a: [instance, ...args],
326
337
  i: `${this._instance}-${this._invokeId++ % Number.MAX_SAFE_INTEGER}`,
327
- s: `${this._domain}/${os.hostname()}/${this._instance}`,
338
+ s: this._vrpcClientId,
328
339
  v: VRPC_PROTOCOL_VERSION
329
340
  }
330
341
  const proxy = await this._getProxy(agent, className, json)
@@ -371,7 +382,7 @@ class VrpcClient extends EventEmitter {
371
382
  f: '__delete__',
372
383
  a: [instance],
373
384
  i: `${this._instance}-${this._invokeId++ % Number.MAX_SAFE_INTEGER}`,
374
- s: `${this._domain}/${os.hostname()}/${this._instance}`,
385
+ s: this._vrpcClientId,
375
386
  v: VRPC_PROTOCOL_VERSION
376
387
  }
377
388
  const topic = `${this._domain}/${agent}/${className}/__static__/__delete__`
@@ -660,14 +671,13 @@ class VrpcClient extends EventEmitter {
660
671
  return new Promise(resolve => this._client.end(false, {}, resolve))
661
672
  }
662
673
 
663
- static _generateUserName () {
664
- return `${os.userInfo().username}@${os.hostname()}-${os.platform()}-js`
674
+ _generateUserName () {
675
+ return `${this._domain}:client@${os.hostname()}-${os.platform()}-js`
665
676
  }
666
677
 
667
678
  _createMqttClientId () {
668
679
  const clientInfo =
669
680
  os.arch() +
670
- JSON.stringify(os.cpus()) +
671
681
  os.homedir() +
672
682
  os.hostname() +
673
683
  JSON.stringify(os.networkInterfaces()) +
@@ -685,7 +695,8 @@ class VrpcClient extends EventEmitter {
685
695
  }
686
696
 
687
697
  _createVrpcClientId () {
688
- return `${this._domain}/${os.hostname()}/${this._instance}`
698
+ const identity = this._identity || this._instance
699
+ return `${this._domain}/${os.hostname()}/${identity}`
689
700
  }
690
701
 
691
702
  _mqttPublish (topic, message, options) {
@@ -698,6 +709,12 @@ class VrpcClient extends EventEmitter {
698
709
  this._log.warn(
699
710
  `Could not publish MQTT message because: ${err.message}`
700
711
  )
712
+ if (err.message === 'client disconnecting') {
713
+ // Workaround for an already reported bug in MQTT.js (#1284)
714
+ this._log.info('Forcing reconnect now...')
715
+ this._client.disconnecting = false
716
+ this._client.end(true, {}, () => this._client.reconnect())
717
+ }
701
718
  }
702
719
  }
703
720
  )
@@ -710,9 +727,44 @@ class VrpcClient extends EventEmitter {
710
727
  (err, granted) => {
711
728
  if (err) {
712
729
  this._log.warn(
713
- `Could not subscribe to topic: ${topic} because: ${err.message}`
730
+ `Could not subscribe to topic(s): '${topic}', because: ${err.message}`
714
731
  )
732
+ if (err.message === 'client disconnecting') {
733
+ // Workaround for an already reported bug in MQTT.js (#1284)
734
+ this._log.info('Forcing reconnect now...')
735
+ this._client.disconnecting = false
736
+ this._client.end(true, {}, () => this._client.reconnect())
737
+ }
715
738
  } else {
739
+ const topicArray = Array.isArray(topic) ? topic : [topic]
740
+ const erroneousGranted = granted
741
+ .filter(x => x.qos === 128)
742
+ .map(x => x.topic)
743
+ if (erroneousGranted.length > 0) {
744
+ err = new Error(
745
+ `Could not subscribe all ${topicArray.length} topic(s) but got error qos=128 on following ${erroneousGranted.length} topic(s): ${erroneousGranted}`
746
+ )
747
+ err.code = 'SUBSCRIBE_FAILED'
748
+ err.subscribeOptions = options
749
+ this._log.error(err)
750
+ this.emit('error', err)
751
+ }
752
+ const reducedQos = granted.filter(x => x.qos < this._qos)
753
+ if (reducedQos.length > 0) {
754
+ err = new Error(
755
+ `Could not subscribe all ${
756
+ topicArray.length
757
+ } topic(s) at desired qos=${
758
+ this._qos
759
+ } but got reduced qos on following ${
760
+ reducedQos.length
761
+ } topic(s): ${JSON.stringify(reducedQos)}`
762
+ )
763
+ err.code = 'SUBSCRIBE_REDUCED_QOS'
764
+ err.subscribeOptions = options
765
+ this._log.warn(err)
766
+ this.emit('error', err)
767
+ }
716
768
  if (granted.length === 0) {
717
769
  this._log.debug(`Already subscribed to: ${topic}`)
718
770
  }
@@ -106,12 +106,28 @@ class VrpcNative {
106
106
  if (functionName.startsWith('vrpcOn')) {
107
107
  const id = `__f__${context}-${functionName}`
108
108
  wrapped.push(id)
109
- eventEmitter.on(id, a => x.apply(null, a))
109
+ eventEmitter.on(id, a => {
110
+ try {
111
+ x.apply(null, a)
112
+ } catch (err) {
113
+ console.error(
114
+ `[VrpcNative ${context}-${functionName}]: Failed to execute callback, because: ${err.message}`
115
+ )
116
+ }
117
+ })
110
118
  } else {
111
119
  const id = `__f__${context}-${functionName}-${i}-${invokeId++ %
112
120
  Number.MAX_SAFE_INTEGER}`
113
121
  wrapped.push(id)
114
- eventEmitter.once(id, a => x.apply(null, a))
122
+ eventEmitter.once(id, a => {
123
+ try {
124
+ x.apply(null, a)
125
+ } catch (err) {
126
+ console.error(
127
+ `[VrpcNative ${context}-${functionName}]: Failed to execute callback, because: ${err.message}`
128
+ )
129
+ }
130
+ })
115
131
  }
116
132
  } else if (VrpcNative._isEmitter(x)) {
117
133
  const { emitter, event } = x
@@ -273,12 +289,28 @@ class VrpcNative {
273
289
  if (functionName.startsWith('vrpcOn')) {
274
290
  const id = `__f__${className}-${functionName}:${args[0]}`
275
291
  wrapped.push(id)
276
- this._eventEmitter.on(id, a => x.apply(null, a))
292
+ this._eventEmitter.on(id, a => {
293
+ try {
294
+ x.apply(null, a)
295
+ } catch (err) {
296
+ console.error(
297
+ `[VrpcNative ${className}-${functionName}]: Failed to execute callback, because: ${err.message}`
298
+ )
299
+ }
300
+ })
277
301
  } else {
278
302
  const id = `__f__${className}-${functionName}-${i}-${invokeId++ %
279
303
  Number.MAX_SAFE_INTEGER}`
280
304
  wrapped.push(id)
281
- this._eventEmitter.once(id, a => x.apply(null, a))
305
+ this._eventEmitter.once(id, a => {
306
+ try {
307
+ x.apply(null, a)
308
+ } catch (err) {
309
+ console.error(
310
+ `[VrpcNative ${className}-${functionName}]: Failed to execute callback, because: ${err.message}`
311
+ )
312
+ }
313
+ })
282
314
  }
283
315
  } else if (VrpcNative._isEmitter(x)) {
284
316
  const { emitter, event } = x