vrpc 3.7.0 → 3.8.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.
- package/README.md +113 -37
- package/browser/vrpc.js +2 -2
- package/package.json +5 -5
- package/vrpc/VrpcAgent.js +17 -9
- package/vrpc/VrpcBrowserAgent.js +16 -8
- package/vrpc/VrpcClient.js +90 -23
- package/vrpc/VrpcPersistor.js +20 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vrpc",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.1",
|
|
4
4
|
"description": "Variadic Remote Procedure Calls",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"test": "npm run test:adapter && npm run test:agent && npm run test:client && npm run test:persistor && npm run test:production",
|
|
16
16
|
"test:adapter": "./node_modules/.bin/mocha tests/adapter/*.js --exit",
|
|
17
17
|
"test:native": "./node_modules/.bin/mocha tests/native/*.js --exit",
|
|
18
|
-
"test:agent": "tests/agent/test.sh"
|
|
18
|
+
"test:agent": "tests/agent/test.sh",
|
|
19
19
|
"test:client": "tests/client/test.sh",
|
|
20
20
|
"test:performance": "tests/performance/test.sh",
|
|
21
21
|
"test:persistor": "./node_modules/.bin/mocha tests/persistor/*.js --timeout 30000 --exit",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"homepage": "https://docs.heisenware.com/developers/vrpc",
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@heisenware/storage": "^
|
|
50
|
+
"@heisenware/storage": "^3.0.2",
|
|
51
51
|
"chai": "^4.3.10",
|
|
52
52
|
"crypto-browserify": "^3.12.1",
|
|
53
53
|
"dockerode": "^4.0.9",
|
|
@@ -78,11 +78,11 @@
|
|
|
78
78
|
"process": "^0.11.10"
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
|
-
"@heisenware/storage": "^1.2.2"
|
|
81
|
+
"@heisenware/storage": "^1.2.2 || ^2.0.0 || ^3.0.0"
|
|
82
82
|
},
|
|
83
83
|
"peerDependenciesMeta": {
|
|
84
84
|
"@heisenware/storage": {
|
|
85
85
|
"optional": true
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
-
}
|
|
88
|
+
}
|
package/vrpc/VrpcAgent.js
CHANGED
|
@@ -23,7 +23,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
23
23
|
* @param {String} defaults.agent This agent's name
|
|
24
24
|
* @param {String} defaults.username MQTT username (if no token is used)
|
|
25
25
|
* @param {String} defaults.password MQTT password (if no token is provided)
|
|
26
|
-
* @param {String} defaults.token Access token
|
|
26
|
+
* @param {String} defaults.token Access token
|
|
27
27
|
* @param {String} defaults.broker Broker url in form: `<scheme>://<host>:<port>`
|
|
28
28
|
* @param {String} defaults.version The (user-defined) version of this agent
|
|
29
29
|
* @returns {Agent} Agent instance
|
|
@@ -641,11 +641,13 @@ class VrpcAgent extends EventEmitter {
|
|
|
641
641
|
}
|
|
642
642
|
|
|
643
643
|
_handleClientInfoMessage (topic, json) {
|
|
644
|
-
//
|
|
645
|
-
|
|
644
|
+
// A client connection went offline. The topic carries the connection
|
|
645
|
+
// id (unique per VrpcClient instance) - the key of all bookkeeping -
|
|
646
|
+
// so ending one connection never touches others sharing its identity.
|
|
647
|
+
const connectionId = topic.slice(0, -15) // /__clientInfo__ = 15
|
|
646
648
|
if (json.status === 'offline') {
|
|
647
|
-
VrpcAdapter._unregisterClient(
|
|
648
|
-
const entry = this._isolatedInstances.get(
|
|
649
|
+
VrpcAdapter._unregisterClient(connectionId)
|
|
650
|
+
const entry = this._isolatedInstances.get(connectionId)
|
|
649
651
|
if (entry) {
|
|
650
652
|
entry.forEach(instanceId => {
|
|
651
653
|
const json = { f: '__delete__', a: [instanceId], r: null }
|
|
@@ -655,8 +657,10 @@ class VrpcAgent extends EventEmitter {
|
|
|
655
657
|
}
|
|
656
658
|
})
|
|
657
659
|
}
|
|
658
|
-
this._mqttUnsubscribe(`${
|
|
659
|
-
|
|
660
|
+
this._mqttUnsubscribe(`${connectionId}/__clientInfo__`)
|
|
661
|
+
// clientId: the identity-derived id shared by all connections of one
|
|
662
|
+
// identity (undefined for clients < 3.8.0)
|
|
663
|
+
this.emit('clientGone', connectionId, { clientId: json.clientId })
|
|
660
664
|
}
|
|
661
665
|
}
|
|
662
666
|
|
|
@@ -825,10 +829,14 @@ class VrpcAgent extends EventEmitter {
|
|
|
825
829
|
/**
|
|
826
830
|
* Event 'clientGone'
|
|
827
831
|
*
|
|
828
|
-
* Emitted when a tracked VRPC client
|
|
832
|
+
* Emitted when a tracked VRPC client connection ended. Listeners receive
|
|
833
|
+
* the connection id (unique per VrpcClient instance) and an info object
|
|
834
|
+
* whose `clientId` is the identity-derived id shared by all connections
|
|
835
|
+
* of one identity (undefined for clients < 3.8.0).
|
|
829
836
|
*
|
|
830
837
|
* @event VrpcAgent#clientGone
|
|
831
|
-
* @type {String}
|
|
838
|
+
* @type {String} connectionId
|
|
839
|
+
* @type {Object} info
|
|
832
840
|
*/
|
|
833
841
|
|
|
834
842
|
module.exports = VrpcAgent
|
package/vrpc/VrpcBrowserAgent.js
CHANGED
|
@@ -495,11 +495,13 @@ class VrpcAgent extends EventEmitter {
|
|
|
495
495
|
}
|
|
496
496
|
|
|
497
497
|
_handleClientInfoMessage (topic, json) {
|
|
498
|
-
//
|
|
499
|
-
|
|
498
|
+
// A client connection went offline. The topic carries the connection
|
|
499
|
+
// id (unique per VrpcClient instance) - the key of all bookkeeping -
|
|
500
|
+
// so ending one connection never touches others sharing its identity.
|
|
501
|
+
const connectionId = topic.slice(0, -15) // /__clientInfo__ = 15
|
|
500
502
|
if (json.status === 'offline') {
|
|
501
|
-
VrpcAdapter._unregisterClient(
|
|
502
|
-
const entry = this._isolatedInstances.get(
|
|
503
|
+
VrpcAdapter._unregisterClient(connectionId)
|
|
504
|
+
const entry = this._isolatedInstances.get(connectionId)
|
|
503
505
|
if (entry) {
|
|
504
506
|
entry.forEach(instanceId => {
|
|
505
507
|
const json = { f: '__delete__', a: [instanceId], r: null }
|
|
@@ -509,8 +511,10 @@ class VrpcAgent extends EventEmitter {
|
|
|
509
511
|
}
|
|
510
512
|
})
|
|
511
513
|
}
|
|
512
|
-
this._mqttUnsubscribe(`${
|
|
513
|
-
|
|
514
|
+
this._mqttUnsubscribe(`${connectionId}/__clientInfo__`)
|
|
515
|
+
// clientId: the identity-derived id shared by all connections of one
|
|
516
|
+
// identity (undefined for clients < 3.8.0)
|
|
517
|
+
this.emit('clientGone', connectionId, { clientId: json.clientId })
|
|
514
518
|
}
|
|
515
519
|
}
|
|
516
520
|
|
|
@@ -679,10 +683,14 @@ class VrpcAgent extends EventEmitter {
|
|
|
679
683
|
/**
|
|
680
684
|
* Event 'clientGone'
|
|
681
685
|
*
|
|
682
|
-
* Emitted when a tracked VRPC client
|
|
686
|
+
* Emitted when a tracked VRPC client connection ended. Listeners receive
|
|
687
|
+
* the connection id (unique per VrpcClient instance) and an info object
|
|
688
|
+
* whose `clientId` is the identity-derived id shared by all connections
|
|
689
|
+
* of one identity (undefined for clients < 3.8.0).
|
|
683
690
|
*
|
|
684
691
|
* @event VrpcAgent#clientGone
|
|
685
|
-
* @type {String}
|
|
692
|
+
* @type {String} connectionId
|
|
693
|
+
* @type {Object} info
|
|
686
694
|
*/
|
|
687
695
|
|
|
688
696
|
module.exports = VrpcAgent
|
package/vrpc/VrpcClient.js
CHANGED
|
@@ -74,7 +74,7 @@ class VrpcClient extends EventEmitter {
|
|
|
74
74
|
* @example
|
|
75
75
|
* const client = new VrpcClient({
|
|
76
76
|
* domain: 'vrpc',
|
|
77
|
-
* broker: '
|
|
77
|
+
* broker: 'mqtts://broker.hivemq.com:8883'
|
|
78
78
|
* })
|
|
79
79
|
*/
|
|
80
80
|
constructor ({
|
|
@@ -127,6 +127,7 @@ class VrpcClient extends EventEmitter {
|
|
|
127
127
|
this._instance = nanoid(8)
|
|
128
128
|
this._mqttClientId = mqttClientId || this._createMqttClientId()
|
|
129
129
|
this._vrpcClientId = this._createVrpcClientId()
|
|
130
|
+
this._vrpcConnectionId = this._createVrpcConnectionId()
|
|
130
131
|
this._agents = {}
|
|
131
132
|
this._eventEmitter = new EventEmitter()
|
|
132
133
|
this._invokeId = 0
|
|
@@ -147,7 +148,13 @@ class VrpcClient extends EventEmitter {
|
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
/**
|
|
150
|
-
* Provides
|
|
151
|
+
* Provides the identity-derived id of this client
|
|
152
|
+
*
|
|
153
|
+
* Every client constructed with the same domain, host and identity
|
|
154
|
+
* reports the same client id: it names the principal, not the
|
|
155
|
+
* connection. Agents receive it as `clientId` in the presence messages
|
|
156
|
+
* and can group the connections (e.g. browser tabs) of one identity by
|
|
157
|
+
* it. See getConnectionId() for the id that is unique per instance.
|
|
151
158
|
*
|
|
152
159
|
* @returns {String} clientId
|
|
153
160
|
*/
|
|
@@ -155,6 +162,21 @@ class VrpcClient extends EventEmitter {
|
|
|
155
162
|
return this._vrpcClientId
|
|
156
163
|
}
|
|
157
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Provides the unique id of this connection
|
|
167
|
+
*
|
|
168
|
+
* Unique per VrpcClient instance: two clients sharing one identity still
|
|
169
|
+
* have distinct connection ids. Agents key all their bookkeeping by it
|
|
170
|
+
* (response routing, event listeners, isolated instances, presence), so
|
|
171
|
+
* one connection ending never disturbs the others of the same identity.
|
|
172
|
+
* Without an identity the connection id equals the client id.
|
|
173
|
+
*
|
|
174
|
+
* @returns {String} connectionId
|
|
175
|
+
*/
|
|
176
|
+
getConnectionId () {
|
|
177
|
+
return this._vrpcConnectionId
|
|
178
|
+
}
|
|
179
|
+
|
|
158
180
|
/**
|
|
159
181
|
* Actually connects to the MQTT broker.
|
|
160
182
|
*
|
|
@@ -184,8 +206,11 @@ class VrpcClient extends EventEmitter {
|
|
|
184
206
|
rejectUnauthorized: false,
|
|
185
207
|
connectTimeout: this._timeout,
|
|
186
208
|
will: {
|
|
187
|
-
topic: `${this.
|
|
188
|
-
payload: JSON.stringify({
|
|
209
|
+
topic: `${this._vrpcConnectionId}/__clientInfo__`,
|
|
210
|
+
payload: JSON.stringify({
|
|
211
|
+
status: 'offline',
|
|
212
|
+
clientId: this._vrpcClientId
|
|
213
|
+
})
|
|
189
214
|
}
|
|
190
215
|
}
|
|
191
216
|
if (username === undefined) delete options.username
|
|
@@ -236,7 +261,7 @@ class VrpcClient extends EventEmitter {
|
|
|
236
261
|
)
|
|
237
262
|
}
|
|
238
263
|
// Single wildcard catches BOTH RPC responses (length 3) AND events (length 4)
|
|
239
|
-
await this._mqttSubscribe(`${this.
|
|
264
|
+
await this._mqttSubscribe(`${this._vrpcConnectionId}/#`)
|
|
240
265
|
|
|
241
266
|
// Re-subscribe to all cached event topics
|
|
242
267
|
this._log.debug('Restoring event subscriptions after reconnect...')
|
|
@@ -296,7 +321,7 @@ class VrpcClient extends EventEmitter {
|
|
|
296
321
|
if (removed.length !== 0) {
|
|
297
322
|
this.emit('instanceGone', removed, { domain, agent, className })
|
|
298
323
|
removed.forEach(lostInstance =>
|
|
299
|
-
this._clearCachedSubscriptions({ lostInstance })
|
|
324
|
+
this._clearCachedSubscriptions({ lostInstance, agent, className })
|
|
300
325
|
)
|
|
301
326
|
}
|
|
302
327
|
if (added.length !== 0) {
|
|
@@ -409,11 +434,18 @@ class VrpcClient extends EventEmitter {
|
|
|
409
434
|
f: isIsolated ? '__createIsolated__' : '__createShared__',
|
|
410
435
|
a: [instance, ...args],
|
|
411
436
|
i: `${this._instance}-${this._invokeId++ % Number.MAX_SAFE_INTEGER}`,
|
|
412
|
-
s: this.
|
|
437
|
+
s: this._vrpcConnectionId,
|
|
413
438
|
v: VRPC_PROTOCOL_VERSION
|
|
414
439
|
}
|
|
415
440
|
const proxy = await this._getProxy(agent, className, json)
|
|
416
|
-
if (instance && cacheProxy)
|
|
441
|
+
if (instance && cacheProxy) {
|
|
442
|
+
this._proxies[`${agent}/${className}/${instance}`] = {
|
|
443
|
+
agent,
|
|
444
|
+
className,
|
|
445
|
+
instance,
|
|
446
|
+
proxy
|
|
447
|
+
}
|
|
448
|
+
}
|
|
417
449
|
return proxy
|
|
418
450
|
}
|
|
419
451
|
|
|
@@ -435,7 +467,8 @@ class VrpcClient extends EventEmitter {
|
|
|
435
467
|
if (!this._isConnected()) {
|
|
436
468
|
throw new Error('Client is not connected')
|
|
437
469
|
}
|
|
438
|
-
|
|
470
|
+
const cached = this._cachedProxy(instance, options)
|
|
471
|
+
if (cached) return cached
|
|
439
472
|
const { agent, className } = await this._getInstanceData(instance, options)
|
|
440
473
|
return this._createProxy(agent, className, instance)
|
|
441
474
|
}
|
|
@@ -462,12 +495,12 @@ class VrpcClient extends EventEmitter {
|
|
|
462
495
|
f: '__delete__',
|
|
463
496
|
a: [instance],
|
|
464
497
|
i: `${this._instance}-${this._invokeId++ % Number.MAX_SAFE_INTEGER}`,
|
|
465
|
-
s: this.
|
|
498
|
+
s: this._vrpcConnectionId,
|
|
466
499
|
v: VRPC_PROTOCOL_VERSION
|
|
467
500
|
}
|
|
468
501
|
const topic = `${this._domain}/${agent}/${className}/__static__/__delete__`
|
|
469
502
|
this._mqttPublish(topic, JSON.stringify(json))
|
|
470
|
-
|
|
503
|
+
delete this._proxies[`${agent}/${className}/${instance}`]
|
|
471
504
|
return this._handleAgentAnswer(json, agent)
|
|
472
505
|
}
|
|
473
506
|
|
|
@@ -502,7 +535,7 @@ class VrpcClient extends EventEmitter {
|
|
|
502
535
|
f: functionName,
|
|
503
536
|
a: wrapped,
|
|
504
537
|
i: `${this._instance}-${this._invokeId++ % Number.MAX_SAFE_INTEGER}`,
|
|
505
|
-
s: this.
|
|
538
|
+
s: this._vrpcConnectionId,
|
|
506
539
|
v: VRPC_PROTOCOL_VERSION
|
|
507
540
|
}
|
|
508
541
|
const topic = `${this._domain}/${agent}/${className}/__static__/${functionName}`
|
|
@@ -545,7 +578,7 @@ class VrpcClient extends EventEmitter {
|
|
|
545
578
|
f: functionName,
|
|
546
579
|
a: [],
|
|
547
580
|
i: `${this._instance}-${this._invokeId++ % Number.MAX_SAFE_INTEGER}`,
|
|
548
|
-
s: this.
|
|
581
|
+
s: this._vrpcConnectionId,
|
|
549
582
|
v: VRPC_PROTOCOL_VERSION
|
|
550
583
|
}
|
|
551
584
|
if (agent === '*') {
|
|
@@ -708,7 +741,7 @@ class VrpcClient extends EventEmitter {
|
|
|
708
741
|
/**
|
|
709
742
|
* Reconnects to the broker by using a different token
|
|
710
743
|
*
|
|
711
|
-
* @param {String} token Access token
|
|
744
|
+
* @param {String} token Access token
|
|
712
745
|
* @param {Object} [options]
|
|
713
746
|
* @param {String} options.agent Agent name. If not provided class default is used
|
|
714
747
|
* @returns {Promise} Promise that resolves once re-connected
|
|
@@ -752,8 +785,12 @@ class VrpcClient extends EventEmitter {
|
|
|
752
785
|
async end () {
|
|
753
786
|
if (!this._client) return
|
|
754
787
|
this._mqttPublish(
|
|
755
|
-
`${this.
|
|
756
|
-
JSON.stringify({
|
|
788
|
+
`${this._vrpcConnectionId}/__clientInfo__`,
|
|
789
|
+
JSON.stringify({
|
|
790
|
+
status: 'offline',
|
|
791
|
+
clientId: this._vrpcClientId,
|
|
792
|
+
v: VRPC_PROTOCOL_VERSION
|
|
793
|
+
})
|
|
757
794
|
)
|
|
758
795
|
this._eventEmitter.removeAllListeners()
|
|
759
796
|
this.removeAllListeners()
|
|
@@ -819,6 +856,14 @@ class VrpcClient extends EventEmitter {
|
|
|
819
856
|
return `${this._domain}/${os.hostname()}/${identity}`
|
|
820
857
|
}
|
|
821
858
|
|
|
859
|
+
_createVrpcConnectionId () {
|
|
860
|
+
// Without an identity the client id already is unique per instance.
|
|
861
|
+
// With one, the instance token makes every connection of that identity
|
|
862
|
+
// distinct while keeping the three-segment layout agents parse.
|
|
863
|
+
if (!this._identity) return this._vrpcClientId
|
|
864
|
+
return `${this._vrpcClientId}:${this._instance}`
|
|
865
|
+
}
|
|
866
|
+
|
|
822
867
|
_mqttPublish (topic, message, options) {
|
|
823
868
|
this._client.publish(
|
|
824
869
|
topic,
|
|
@@ -981,6 +1026,7 @@ class VrpcClient extends EventEmitter {
|
|
|
981
1026
|
const proxyId = `${this._instance}-${this._proxyId++}`
|
|
982
1027
|
const proxy = {
|
|
983
1028
|
vrpcClientId: this._vrpcClientId,
|
|
1029
|
+
vrpcConnectionId: this._vrpcConnectionId,
|
|
984
1030
|
vrpcInstanceId: instance,
|
|
985
1031
|
vrpcProxyId: proxyId
|
|
986
1032
|
}
|
|
@@ -1027,7 +1073,7 @@ class VrpcClient extends EventEmitter {
|
|
|
1027
1073
|
i: `${this._instance}-${
|
|
1028
1074
|
this._invokeId++ % Number.MAX_SAFE_INTEGER
|
|
1029
1075
|
}`,
|
|
1030
|
-
s: this.
|
|
1076
|
+
s: this._vrpcConnectionId,
|
|
1031
1077
|
v: VRPC_PROTOCOL_VERSION
|
|
1032
1078
|
}
|
|
1033
1079
|
this._mqttPublish(
|
|
@@ -1061,7 +1107,7 @@ class VrpcClient extends EventEmitter {
|
|
|
1061
1107
|
i: `${this._instance}-${
|
|
1062
1108
|
this._invokeId++ % Number.MAX_SAFE_INTEGER
|
|
1063
1109
|
}`,
|
|
1064
|
-
s: this.
|
|
1110
|
+
s: this._vrpcConnectionId,
|
|
1065
1111
|
v: VRPC_PROTOCOL_VERSION
|
|
1066
1112
|
}
|
|
1067
1113
|
this._mqttPublish(
|
|
@@ -1096,7 +1142,7 @@ class VrpcClient extends EventEmitter {
|
|
|
1096
1142
|
i: `${this._instance}-${
|
|
1097
1143
|
this._invokeId++ % Number.MAX_SAFE_INTEGER
|
|
1098
1144
|
}`,
|
|
1099
|
-
s: this.
|
|
1145
|
+
s: this._vrpcConnectionId,
|
|
1100
1146
|
v: VRPC_PROTOCOL_VERSION
|
|
1101
1147
|
}
|
|
1102
1148
|
this._mqttPublish(
|
|
@@ -1280,7 +1326,7 @@ class VrpcClient extends EventEmitter {
|
|
|
1280
1326
|
const topicId = VrpcClient._createHash(hashInput, 12)
|
|
1281
1327
|
|
|
1282
1328
|
// Route directly to the client's single event inbox wildcard
|
|
1283
|
-
const topic = `${this.
|
|
1329
|
+
const topic = `${this._vrpcConnectionId}/${topicId}`
|
|
1284
1330
|
const id = `__e__${topic}`
|
|
1285
1331
|
|
|
1286
1332
|
const handler = ({ a }) => callback.apply(null, a)
|
|
@@ -1336,11 +1382,19 @@ class VrpcClient extends EventEmitter {
|
|
|
1336
1382
|
return foundId
|
|
1337
1383
|
}
|
|
1338
1384
|
|
|
1339
|
-
_clearCachedSubscriptions ({ lostAgent, lostInstance }) {
|
|
1385
|
+
_clearCachedSubscriptions ({ lostAgent, lostInstance, agent, className }) {
|
|
1340
1386
|
const obsoleteTopics = Object.keys(this._cachedSubscriptions).filter(
|
|
1341
1387
|
topic => {
|
|
1342
1388
|
const sub = this._cachedSubscriptions[topic]
|
|
1343
|
-
|
|
1389
|
+
if (lostAgent !== undefined) return sub.agent === lostAgent
|
|
1390
|
+
// Instance ids are unique per agent only: a twin of the same id on
|
|
1391
|
+
// another agent (a deployed app next to its builder) keeps its
|
|
1392
|
+
// subscriptions when this one goes
|
|
1393
|
+
return (
|
|
1394
|
+
sub.instance === lostInstance &&
|
|
1395
|
+
sub.agent === agent &&
|
|
1396
|
+
sub.className === className
|
|
1397
|
+
)
|
|
1344
1398
|
}
|
|
1345
1399
|
)
|
|
1346
1400
|
|
|
@@ -1394,12 +1448,25 @@ class VrpcClient extends EventEmitter {
|
|
|
1394
1448
|
return instanceData
|
|
1395
1449
|
}
|
|
1396
1450
|
|
|
1451
|
+
_cachedProxy (instance, { agent, className } = {}) {
|
|
1452
|
+
// Instance ids are unique per agent only: a cached proxy is served
|
|
1453
|
+
// solely when it is the one match for the given agent and class - twins
|
|
1454
|
+
// on several agents are resolved through the topology instead
|
|
1455
|
+
const hits = Object.values(this._proxies).filter(
|
|
1456
|
+
x =>
|
|
1457
|
+
x.instance === instance &&
|
|
1458
|
+
(!agent || x.agent === agent) &&
|
|
1459
|
+
(!className || x.className === className)
|
|
1460
|
+
)
|
|
1461
|
+
return hits.length === 1 ? hits[0].proxy : null
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1397
1464
|
_getInstanceFromCache (instance, options = {}) {
|
|
1398
1465
|
// when no agent is specified, but an explicit class default exists give
|
|
1399
1466
|
// such instances priority in being found
|
|
1400
1467
|
if (!options.agent && this._agent !== '*') {
|
|
1401
1468
|
const { classes, status } = this._agents[this._agent] || {}
|
|
1402
|
-
if (
|
|
1469
|
+
if (classes && status !== 'offline') {
|
|
1403
1470
|
for (const className in classes) {
|
|
1404
1471
|
if (options.className && className !== options.className) continue
|
|
1405
1472
|
const { instances } = classes[className]
|
package/vrpc/VrpcPersistor.js
CHANGED
|
@@ -47,6 +47,9 @@ const VrpcAdapter = require('./VrpcAdapter')
|
|
|
47
47
|
* for an 'update' event on instances to persist their state after creation.
|
|
48
48
|
*
|
|
49
49
|
* @requires @heisenware/storage - This peer dependency must be installed.
|
|
50
|
+
* Storage 1.x (synchronous constructor) and >= 2.x (async `Storage.open`)
|
|
51
|
+
* are both supported; the layer is opened lazily and every operation waits
|
|
52
|
+
* for it.
|
|
50
53
|
*/
|
|
51
54
|
class VrpcPersistor {
|
|
52
55
|
/**
|
|
@@ -76,8 +79,8 @@ class VrpcPersistor {
|
|
|
76
79
|
.toLocaleLowerCase()
|
|
77
80
|
.replace(/[^a-zA-Z0-9]/g, '-')}`
|
|
78
81
|
|
|
79
|
-
this._storage =
|
|
80
|
-
this._isInitialized = this._init()
|
|
82
|
+
this._storage = null
|
|
83
|
+
this._isInitialized = this._init(Storage)
|
|
81
84
|
|
|
82
85
|
this._log.info(
|
|
83
86
|
`[VrpcPersistor] Persistence layer enabled. Storage path: ${this._dir}`
|
|
@@ -166,8 +169,12 @@ class VrpcPersistor {
|
|
|
166
169
|
* Initializes the persistor by attaching listeners to VRPC adapter events.
|
|
167
170
|
* @private
|
|
168
171
|
*/
|
|
169
|
-
async _init () {
|
|
172
|
+
async _init (Storage) {
|
|
170
173
|
try {
|
|
174
|
+
// Listeners go first and synchronously: an instance created right
|
|
175
|
+
// after construction must be caught. The operations they trigger wait
|
|
176
|
+
// for the storage layer opened below.
|
|
177
|
+
|
|
171
178
|
// Persist new instance creation
|
|
172
179
|
VrpcAdapter.on('create', async ({ instance, className, args }) => {
|
|
173
180
|
this._log.info(
|
|
@@ -211,6 +218,14 @@ class VrpcPersistor {
|
|
|
211
218
|
)
|
|
212
219
|
)
|
|
213
220
|
})
|
|
221
|
+
|
|
222
|
+
// storage >= 2 constructs through the async factory only; 1.x has a
|
|
223
|
+
// synchronous constructor. No watcher: this persistor is the sole
|
|
224
|
+
// writer of its directory.
|
|
225
|
+
this._storage =
|
|
226
|
+
typeof Storage.open === 'function'
|
|
227
|
+
? await Storage.open({ dir: this._dir, log: this._log, watch: false })
|
|
228
|
+
: new Storage({ dir: this._dir, log: this._log })
|
|
214
229
|
} catch (err) {
|
|
215
230
|
this._log.error(
|
|
216
231
|
`[VrpcPersistor] Could not initialize persistence layer: ${err.message}`
|
|
@@ -225,6 +240,7 @@ class VrpcPersistor {
|
|
|
225
240
|
* @private
|
|
226
241
|
*/
|
|
227
242
|
async _persist (id, className, args) {
|
|
243
|
+
await this._isInitialized
|
|
228
244
|
await this._storage.setItem(id, { className, args }, { folder: className })
|
|
229
245
|
}
|
|
230
246
|
|
|
@@ -233,6 +249,7 @@ class VrpcPersistor {
|
|
|
233
249
|
* @private
|
|
234
250
|
*/
|
|
235
251
|
async _delete (id) {
|
|
252
|
+
await this._isInitialized
|
|
236
253
|
await this._storage.removeItem(id)
|
|
237
254
|
}
|
|
238
255
|
}
|