vrpc 3.0.2 → 3.1.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/CHANGELOG.md +67 -26
- package/browser/vrpc.js +1 -1
- package/package.json +1 -1
- package/vrpc/VrpcAdapter.js +151 -95
- package/vrpc/VrpcAgent.js +72 -51
- package/vrpc/VrpcClient.js +73 -13
- package/vrpc/VrpcNative.js +36 -4
package/vrpc/VrpcClient.js
CHANGED
|
@@ -59,15 +59,17 @@ class VrpcClient extends EventEmitter {
|
|
|
59
59
|
*
|
|
60
60
|
* @constructor
|
|
61
61
|
* @param {Object} options
|
|
62
|
-
* @param {String} options.
|
|
63
|
-
* @param {String} options.
|
|
64
|
-
* @param {String} options.
|
|
62
|
+
* @param {String} [options.username] MQTT username
|
|
63
|
+
* @param {String} [options.password] MQTT password (if no token is provided)
|
|
64
|
+
* @param {String} [options.token] Access token
|
|
65
65
|
* @param {String} options.domain Sets the domain
|
|
66
66
|
* @param {String} [options.agent="*"] Sets default agent
|
|
67
67
|
* @param {String} [options.broker="mqtts://vrpc.io:8883"] Broker url in form: `<scheme>://<host>:<port>`
|
|
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',
|
|
@@ -76,14 +78,16 @@ class VrpcClient extends EventEmitter {
|
|
|
76
78
|
*/
|
|
77
79
|
constructor ({
|
|
78
80
|
token,
|
|
79
|
-
username,
|
|
80
81
|
password,
|
|
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,6 +163,7 @@ class VrpcClient extends EventEmitter {
|
|
|
152
163
|
let username = this._username
|
|
153
164
|
let password = this._password
|
|
154
165
|
if (this._token) {
|
|
166
|
+
username = username || this._generateUserName()
|
|
155
167
|
password = this._token
|
|
156
168
|
}
|
|
157
169
|
const options = {
|
|
@@ -323,7 +335,7 @@ class VrpcClient extends EventEmitter {
|
|
|
323
335
|
f: isIsolated ? '__createIsolated__' : '__createShared__',
|
|
324
336
|
a: [instance, ...args],
|
|
325
337
|
i: `${this._instance}-${this._invokeId++ % Number.MAX_SAFE_INTEGER}`,
|
|
326
|
-
s:
|
|
338
|
+
s: this._vrpcClientId,
|
|
327
339
|
v: VRPC_PROTOCOL_VERSION
|
|
328
340
|
}
|
|
329
341
|
const proxy = await this._getProxy(agent, className, json)
|
|
@@ -370,7 +382,7 @@ class VrpcClient extends EventEmitter {
|
|
|
370
382
|
f: '__delete__',
|
|
371
383
|
a: [instance],
|
|
372
384
|
i: `${this._instance}-${this._invokeId++ % Number.MAX_SAFE_INTEGER}`,
|
|
373
|
-
s:
|
|
385
|
+
s: this._vrpcClientId,
|
|
374
386
|
v: VRPC_PROTOCOL_VERSION
|
|
375
387
|
}
|
|
376
388
|
const topic = `${this._domain}/${agent}/${className}/__static__/__delete__`
|
|
@@ -659,10 +671,13 @@ class VrpcClient extends EventEmitter {
|
|
|
659
671
|
return new Promise(resolve => this._client.end(false, {}, resolve))
|
|
660
672
|
}
|
|
661
673
|
|
|
674
|
+
_generateUserName () {
|
|
675
|
+
return `${this._domain}:client@${os.hostname()}-${os.platform()}-js`
|
|
676
|
+
}
|
|
677
|
+
|
|
662
678
|
_createMqttClientId () {
|
|
663
679
|
const clientInfo =
|
|
664
680
|
os.arch() +
|
|
665
|
-
JSON.stringify(os.cpus()) +
|
|
666
681
|
os.homedir() +
|
|
667
682
|
os.hostname() +
|
|
668
683
|
JSON.stringify(os.networkInterfaces()) +
|
|
@@ -680,7 +695,8 @@ class VrpcClient extends EventEmitter {
|
|
|
680
695
|
}
|
|
681
696
|
|
|
682
697
|
_createVrpcClientId () {
|
|
683
|
-
|
|
698
|
+
const identity = this._identity || this._instance
|
|
699
|
+
return `${this._domain}/${os.hostname()}/${identity}`
|
|
684
700
|
}
|
|
685
701
|
|
|
686
702
|
_mqttPublish (topic, message, options) {
|
|
@@ -693,6 +709,12 @@ class VrpcClient extends EventEmitter {
|
|
|
693
709
|
this._log.warn(
|
|
694
710
|
`Could not publish MQTT message because: ${err.message}`
|
|
695
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
|
+
}
|
|
696
718
|
}
|
|
697
719
|
}
|
|
698
720
|
)
|
|
@@ -705,9 +727,44 @@ class VrpcClient extends EventEmitter {
|
|
|
705
727
|
(err, granted) => {
|
|
706
728
|
if (err) {
|
|
707
729
|
this._log.warn(
|
|
708
|
-
`Could not subscribe to topic: ${topic} because: ${err.message}`
|
|
730
|
+
`Could not subscribe to topic(s): '${topic}', because: ${err.message}`
|
|
709
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
|
+
}
|
|
710
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
|
+
}
|
|
711
768
|
if (granted.length === 0) {
|
|
712
769
|
this._log.debug(`Already subscribed to: ${topic}`)
|
|
713
770
|
}
|
|
@@ -932,7 +989,8 @@ class VrpcClient extends EventEmitter {
|
|
|
932
989
|
if (typeof ret === 'string' && ret.substring(0, 5) === '__p__') {
|
|
933
990
|
this._eventEmitter.once(ret, promiseData => {
|
|
934
991
|
// TODO improve error message here, like above
|
|
935
|
-
if (promiseData.e)
|
|
992
|
+
if (promiseData.e)
|
|
993
|
+
reject(new Error(`[vrpc ${agent}-${c}-${f}]: ${promiseData.e}`))
|
|
936
994
|
else resolve(promiseData.r)
|
|
937
995
|
})
|
|
938
996
|
} else {
|
|
@@ -1067,7 +1125,9 @@ class VrpcClient extends EventEmitter {
|
|
|
1067
1125
|
: `${this._domain}/${agent}/${className}/${event}`
|
|
1068
1126
|
const id = `__e__${topic}`
|
|
1069
1127
|
if (this._cachedSubscriptions[topic]) {
|
|
1070
|
-
const index = this._cachedSubscriptions[topic].findIndex(
|
|
1128
|
+
const index = this._cachedSubscriptions[topic].findIndex(
|
|
1129
|
+
x => x.callback === callback
|
|
1130
|
+
)
|
|
1071
1131
|
if (index === -1) {
|
|
1072
1132
|
this._log.warn(`Failed removing event listener ${id}`)
|
|
1073
1133
|
return null
|
package/vrpc/VrpcNative.js
CHANGED
|
@@ -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 =>
|
|
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 =>
|
|
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 =>
|
|
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 =>
|
|
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
|