vrpc 2.6.0 → 3.0.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 +83 -0
- package/LICENSE +1 -1
- package/README.md +34 -60
- package/binding.gyp.no-auto +4 -26
- package/browser/vrpc.js +1 -1
- package/index.js +2 -2
- package/package.json +16 -17
- package/vrpc/VrpcAdapter.js +293 -311
- package/vrpc/VrpcAgent.js +94 -87
- package/vrpc/{VrpcRemote.js → VrpcClient.js} +375 -413
- package/vrpc/VrpcNative.js +330 -0
- package/vrpc/{vrpc.hpp → adapter.hpp} +1003 -1115
- package/vrpc/addon.cpp +6 -6
- package/vrpc/json.hpp +1769 -870
- package/vrpc/vrpc-agent-js +4 -4
- package/README.rst +0 -18
- package/third_party/include/catch.hpp +0 -12749
- package/third_party/install.sh +0 -22
- package/vrpc/VrpcLocal.js +0 -269
- package/vrpc/VrpcLocal.py +0 -171
- package/vrpc/__init__.py +0 -1
- package/vrpc/module.cpp +0 -183
package/vrpc/VrpcAdapter.js
CHANGED
|
@@ -10,13 +10,13 @@ __\/\\\_______\/\\\__/\\\///////\\\___\/\\\/////////\\\____/\\\////////__
|
|
|
10
10
|
________\///________\///________\///__\///___________________\/////////__
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
Non-intrusively
|
|
13
|
+
Non-intrusively adapts code and provides access in form of asynchronous remote
|
|
14
14
|
procedure calls (RPC).
|
|
15
|
-
Author: Dr. Burkhard C. Heisen (https://github.com/
|
|
15
|
+
Author: Dr. Burkhard C. Heisen (https://github.com/heisenware/vrpc)
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
|
19
|
-
Copyright (c) 2018 -
|
|
19
|
+
Copyright (c) 2018 - 2022 Dr. Burkhard C. Heisen <burkhard.heisen@heisenware.com>.
|
|
20
20
|
|
|
21
21
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
22
22
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -42,9 +42,9 @@ const path = require('path')
|
|
|
42
42
|
const fs = require('fs')
|
|
43
43
|
const Ajv = require('ajv')
|
|
44
44
|
const caller = require('caller')
|
|
45
|
-
const { nanoid } = require('nanoid')
|
|
46
45
|
const commentParser = require('./comment-parser')
|
|
47
46
|
const EventEmitter = require('events')
|
|
47
|
+
const { nanoid } = require('nanoid')
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
50
|
* Generates an adapter layer for existing code and enables further VRPC-based
|
|
@@ -101,17 +101,20 @@ class VrpcAdapter {
|
|
|
101
101
|
* NOTE: This function currently only supports registration of classes (either
|
|
102
102
|
* when provided as object or when exported on the provided module path)
|
|
103
103
|
*/
|
|
104
|
-
|
|
104
|
+
static register (code, options = {}) {
|
|
105
105
|
if (typeof code === 'string') {
|
|
106
106
|
const absPath = path.resolve(caller(), '../', code)
|
|
107
107
|
const relPath = path.relative(__dirname, absPath)
|
|
108
|
-
const absJsdocPath =
|
|
108
|
+
const absJsdocPath =
|
|
109
|
+
absPath && absPath.endsWith('.js') ? absPath : `${absPath}.js`
|
|
109
110
|
const Klass = require(relPath)
|
|
110
111
|
this._registerClass(Klass, absJsdocPath, options)
|
|
111
112
|
} else {
|
|
112
113
|
const { jsdocPath } = options
|
|
114
|
+
const sJsdocPath =
|
|
115
|
+
jsdocPath && jsdocPath.endsWith('.js') ? jsdocPath : `${jsdocPath}.js`
|
|
113
116
|
if (jsdocPath) {
|
|
114
|
-
const absJsdocPath = path.resolve(caller(), '../',
|
|
117
|
+
const absJsdocPath = path.resolve(caller(), '../', sJsdocPath)
|
|
115
118
|
this._registerClass(code, absJsdocPath, { ...options, jsdoc: true })
|
|
116
119
|
} else {
|
|
117
120
|
this._registerClass(code, null, options)
|
|
@@ -147,92 +150,73 @@ class VrpcAdapter {
|
|
|
147
150
|
const content = fs.readFileSync(absJsdocPath).toString('utf8')
|
|
148
151
|
meta = this._parseComments(content)
|
|
149
152
|
}
|
|
150
|
-
VrpcAdapter._functionRegistry.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
153
|
+
if (!VrpcAdapter._functionRegistry.has(className)) {
|
|
154
|
+
VrpcAdapter._functionRegistry.set(className, {
|
|
155
|
+
memberFunctions,
|
|
156
|
+
meta,
|
|
157
|
+
Klass: {},
|
|
158
|
+
withNew: false,
|
|
159
|
+
staticFunctions: [],
|
|
160
|
+
schema: null
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
VrpcAdapter._instances.set(instance, {
|
|
164
|
+
className,
|
|
165
|
+
instance: obj,
|
|
166
|
+
isIsolated: false
|
|
157
167
|
})
|
|
158
|
-
VrpcAdapter._instances.set(instance, { className, instance: obj })
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
static getClasses () {
|
|
162
|
-
return JSON.stringify(VrpcAdapter._getClassesArray())
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
static getMemberFunctions (className) {
|
|
166
|
-
return JSON.stringify(VrpcAdapter._getMemberFunctionsArray(className))
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
static getStaticFunctions (className) {
|
|
170
|
-
return JSON.stringify(VrpcAdapter._getStaticFunctionsArray(className))
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
static getMetaData (className) {
|
|
174
|
-
return JSON.stringify(VrpcAdapter._getMetaData(className))
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
static getInstances (className) {
|
|
178
|
-
return JSON.stringify(VrpcAdapter._getInstancesArray(className))
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
static call (jsonString) {
|
|
182
|
-
const json = JSON.parse(jsonString)
|
|
183
|
-
VrpcAdapter._call(json)
|
|
184
|
-
return JSON.stringify(json)
|
|
185
168
|
}
|
|
186
169
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
// convenience interface if used in parallel - as local factory and from remote
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Creates an un-managed, anonymous instance
|
|
195
|
-
*
|
|
196
|
-
* @param {String} className Name of the class to create an instance of
|
|
197
|
-
* @param {...any} args Arguments to provide to the constructor
|
|
198
|
-
* @returns {Object} The real instance (not a proxy!)
|
|
199
|
-
*/
|
|
200
|
-
static create (className, ...args) {
|
|
201
|
-
if (typeof className === 'string') {
|
|
202
|
-
return VrpcAdapter._create(className, ...args)
|
|
203
|
-
}
|
|
204
|
-
if (typeof className === 'object') {
|
|
205
|
-
if (className.instance) {
|
|
206
|
-
return VrpcAdapter._createNamed(
|
|
207
|
-
className.className,
|
|
208
|
-
className.instance,
|
|
209
|
-
...className.args
|
|
210
|
-
)
|
|
211
|
-
}
|
|
212
|
-
return VrpcAdapter._create(className.className, ...className.args)
|
|
213
|
-
}
|
|
214
|
-
}
|
|
170
|
+
/*****************************************************
|
|
171
|
+
* convenience interface if used as plug-able factory *
|
|
172
|
+
******************************************************/
|
|
215
173
|
|
|
216
174
|
/**
|
|
217
|
-
* Creates a
|
|
175
|
+
* Creates a new instance
|
|
218
176
|
*
|
|
219
|
-
* @param {
|
|
220
|
-
* @param {String}
|
|
221
|
-
*
|
|
177
|
+
* @param {Object} options
|
|
178
|
+
* @param {String} options.className Name of the class which should be
|
|
179
|
+
* instantiated
|
|
180
|
+
* @param {String} [options.instance] Name of the created instance. If not
|
|
181
|
+
* provided an id will be generated
|
|
182
|
+
* @param {Array} [options.args] Positional arguments for the constructor call
|
|
183
|
+
* @param {bool} [options.isIsolated=false] If true the created instance will
|
|
184
|
+
* be visible only to the client who created it
|
|
222
185
|
* @returns {Object} The real instance (not a proxy!)
|
|
223
186
|
*/
|
|
224
|
-
static
|
|
225
|
-
|
|
187
|
+
static create ({
|
|
188
|
+
className,
|
|
189
|
+
instance = nanoid(8),
|
|
190
|
+
args = [],
|
|
191
|
+
isIsolated = false
|
|
192
|
+
} = {}) {
|
|
193
|
+
const json = { c: className, a: [instance, ...args] }
|
|
194
|
+
const obj = isIsolated
|
|
195
|
+
? VrpcAdapter._handleCreateIsolated(json)
|
|
196
|
+
: VrpcAdapter._handleCreateShared(json)
|
|
197
|
+
if (json.e) throw new Error(json.e)
|
|
198
|
+
return obj
|
|
226
199
|
}
|
|
227
200
|
|
|
228
201
|
/**
|
|
229
|
-
* Deletes
|
|
202
|
+
* Deletes an instance
|
|
230
203
|
*
|
|
231
|
-
* @param {String} instance
|
|
204
|
+
* @param {String|Object} instance Instance (name or object itself) to be deleted
|
|
232
205
|
* @returns {Boolean} True in case of success, false otherwise
|
|
233
206
|
*/
|
|
234
207
|
static delete (instance) {
|
|
235
|
-
|
|
208
|
+
if (typeof instance === 'string') {
|
|
209
|
+
return VrpcAdapter._delete(instance)
|
|
210
|
+
}
|
|
211
|
+
if (typeof instance === 'object') {
|
|
212
|
+
for (const [k, v] of VrpcAdapter._instances) {
|
|
213
|
+
if (v.instance === instance) {
|
|
214
|
+
VrpcAdapter._instances.delete(k)
|
|
215
|
+
return true
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return false
|
|
219
|
+
}
|
|
236
220
|
}
|
|
237
221
|
|
|
238
222
|
/**
|
|
@@ -266,34 +250,14 @@ class VrpcAdapter {
|
|
|
266
250
|
return VrpcAdapter._getInstancesArray(className)
|
|
267
251
|
}
|
|
268
252
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
* @return {Array.<String>} Array of member function names
|
|
274
|
-
*/
|
|
275
|
-
static getAvailableMemberFunctions (className) {
|
|
276
|
-
return VrpcAdapter._getMemberFunctionsArray(className)
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Provides all available static functions of a registered class.
|
|
281
|
-
*
|
|
282
|
-
* @param {String} className Name of class to provide static functions for
|
|
283
|
-
* @returns {Array.<String>} Array of static function names
|
|
284
|
-
*/
|
|
285
|
-
static getAvailableStaticFunctions (className) {
|
|
286
|
-
return VrpcAdapter._getStaticFunctionsArray(className)
|
|
253
|
+
static call (jsonString) {
|
|
254
|
+
const json = JSON.parse(jsonString)
|
|
255
|
+
VrpcAdapter._call(json)
|
|
256
|
+
return JSON.stringify(json)
|
|
287
257
|
}
|
|
288
258
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
*
|
|
292
|
-
* @param {String} className Name of class to provide meta data for
|
|
293
|
-
* @returns {MetaData} Meta Data
|
|
294
|
-
*/
|
|
295
|
-
static getAvailableMetaData (className) {
|
|
296
|
-
return VrpcAdapter._getMetaData(className)
|
|
259
|
+
static onCallback (callback) {
|
|
260
|
+
VrpcAdapter._callback = callback
|
|
297
261
|
}
|
|
298
262
|
|
|
299
263
|
/**
|
|
@@ -351,11 +315,10 @@ class VrpcAdapter {
|
|
|
351
315
|
staticFunctions = staticFunctions.filter(f => !f.startsWith('_'))
|
|
352
316
|
}
|
|
353
317
|
// Inject constructor and destructor
|
|
354
|
-
staticFunctions.push('
|
|
355
|
-
staticFunctions.push('
|
|
356
|
-
staticFunctions.push('__createNamed__')
|
|
357
|
-
staticFunctions.push('__getNamed__')
|
|
318
|
+
staticFunctions.push('__createIsolated__')
|
|
319
|
+
staticFunctions.push('__createShared__')
|
|
358
320
|
staticFunctions.push('__callAll__')
|
|
321
|
+
staticFunctions.push('__delete__')
|
|
359
322
|
let memberFunctions = VrpcAdapter._extractMemberFunctions(Klass)
|
|
360
323
|
if (onlyPublic) {
|
|
361
324
|
memberFunctions = memberFunctions.filter(f => {
|
|
@@ -416,7 +379,7 @@ class VrpcAdapter {
|
|
|
416
379
|
},
|
|
417
380
|
...params
|
|
418
381
|
]
|
|
419
|
-
meta.
|
|
382
|
+
meta.__createShared__ = { description, params: modified, ret }
|
|
420
383
|
} else {
|
|
421
384
|
meta[functionName] = { description, params, ret }
|
|
422
385
|
}
|
|
@@ -427,99 +390,98 @@ class VrpcAdapter {
|
|
|
427
390
|
}
|
|
428
391
|
|
|
429
392
|
static _call (json) {
|
|
430
|
-
const
|
|
431
|
-
switch (
|
|
432
|
-
case '
|
|
433
|
-
VrpcAdapter.
|
|
393
|
+
const before = Object.keys(VrpcAdapter._listeners).length
|
|
394
|
+
switch (json.f) {
|
|
395
|
+
case '__createIsolated__':
|
|
396
|
+
VrpcAdapter._handleCreateIsolated(json)
|
|
434
397
|
break
|
|
435
|
-
case '
|
|
436
|
-
VrpcAdapter.
|
|
437
|
-
break
|
|
438
|
-
case '__createNamed__':
|
|
439
|
-
VrpcAdapter._handleCreateNamed(json)
|
|
440
|
-
break
|
|
441
|
-
case '__getNamed__':
|
|
442
|
-
VrpcAdapter._handleGetNamed(json)
|
|
398
|
+
case '__createShared__':
|
|
399
|
+
VrpcAdapter._handleCreateShared(json)
|
|
443
400
|
break
|
|
444
401
|
case '__callAll__':
|
|
445
402
|
VrpcAdapter._handleCallAll(json)
|
|
446
403
|
break
|
|
404
|
+
case '__delete__':
|
|
405
|
+
VrpcAdapter._handleDelete(json)
|
|
406
|
+
break
|
|
447
407
|
default:
|
|
408
|
+
// this call may have the side-effect of taking a listener on board
|
|
448
409
|
VrpcAdapter._handleCall(json)
|
|
449
410
|
}
|
|
411
|
+
const after = Object.keys(VrpcAdapter._listeners).length
|
|
412
|
+
// if we took a listener on board we must tell our caller such that
|
|
413
|
+
// things can be cleaned up later.
|
|
414
|
+
return after > before
|
|
450
415
|
}
|
|
451
416
|
|
|
452
|
-
static
|
|
453
|
-
|
|
417
|
+
static _handleCreateIsolated (json) {
|
|
418
|
+
let instance
|
|
454
419
|
try {
|
|
455
|
-
const
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
VrpcAdapter._instances.set(instanceId, {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
static _handleDelete (json) {
|
|
466
|
-
const { context, data } = json
|
|
467
|
-
try {
|
|
468
|
-
const wrappedArgs = VrpcAdapter._wrapArguments(json)
|
|
469
|
-
const instanceId = wrappedArgs[0]
|
|
470
|
-
data.r = VrpcAdapter._delete(instanceId)
|
|
471
|
-
VrpcAdapter._emitter.emit('delete', {
|
|
472
|
-
className: context,
|
|
473
|
-
instance: wrappedArgs[0]
|
|
420
|
+
const className = json.c
|
|
421
|
+
const [instanceId, ...args] = json.a
|
|
422
|
+
instance = VrpcAdapter._create(className, instanceId, ...args)
|
|
423
|
+
VrpcAdapter._instances.set(instanceId, {
|
|
424
|
+
instance,
|
|
425
|
+
className,
|
|
426
|
+
isIsolated: true
|
|
474
427
|
})
|
|
475
|
-
} catch (err) {
|
|
476
|
-
data.e = err.message
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
static _handleCreateNamed (json) {
|
|
481
|
-
const { context, data } = json
|
|
482
|
-
try {
|
|
483
|
-
const wrappedArgs = VrpcAdapter._wrapArguments(json)
|
|
484
|
-
VrpcAdapter._createNamed(context, ...wrappedArgs)
|
|
485
|
-
data.r = wrappedArgs[0] // First argument is instanceId
|
|
486
428
|
VrpcAdapter._emitter.emit('create', {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
429
|
+
args,
|
|
430
|
+
className,
|
|
431
|
+
isIsolated: true,
|
|
432
|
+
instance: instanceId
|
|
490
433
|
})
|
|
434
|
+
json.r = instanceId
|
|
491
435
|
} catch (err) {
|
|
492
|
-
|
|
436
|
+
json.e = err.message
|
|
493
437
|
}
|
|
438
|
+
return instance
|
|
494
439
|
}
|
|
495
440
|
|
|
496
|
-
static
|
|
497
|
-
|
|
441
|
+
static _handleCreateShared (json) {
|
|
442
|
+
let instance
|
|
498
443
|
try {
|
|
499
|
-
const
|
|
500
|
-
const instanceId =
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
444
|
+
const className = json.c
|
|
445
|
+
const [instanceId, ...args] = json.a
|
|
446
|
+
instance = VrpcAdapter._create(className, instanceId, ...args)
|
|
447
|
+
VrpcAdapter._instances.set(instanceId, {
|
|
448
|
+
instance,
|
|
449
|
+
className,
|
|
450
|
+
isIsolated: false
|
|
451
|
+
})
|
|
452
|
+
json.r = instanceId
|
|
453
|
+
VrpcAdapter._emitter.emit('create', {
|
|
454
|
+
args,
|
|
455
|
+
className,
|
|
456
|
+
isIsolated: false,
|
|
457
|
+
instance: instanceId
|
|
458
|
+
})
|
|
504
459
|
} catch (err) {
|
|
505
|
-
|
|
460
|
+
json.e = err.message
|
|
506
461
|
}
|
|
462
|
+
return instance
|
|
507
463
|
}
|
|
508
464
|
|
|
509
465
|
static _handleCallAll (json) {
|
|
510
|
-
const { context, method, data } = json
|
|
511
466
|
try {
|
|
512
467
|
const calls = []
|
|
513
|
-
for (const [
|
|
514
|
-
|
|
515
|
-
|
|
468
|
+
for (const [
|
|
469
|
+
id,
|
|
470
|
+
{ className, instance, isIsolated }
|
|
471
|
+
] of VrpcAdapter._instances) {
|
|
472
|
+
// TODO Think about a configurable behavior w.r.t. isolated instances...
|
|
473
|
+
if (className !== json.c || isIsolated) continue
|
|
474
|
+
let v = true
|
|
516
475
|
let e = null
|
|
517
476
|
try {
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
477
|
+
const unwrapped = VrpcAdapter._unwrapArguments(json, id)
|
|
478
|
+
if (unwrapped) {
|
|
479
|
+
const funcName = unwrapped[0]
|
|
480
|
+
v = instance[funcName].apply(instance, unwrapped.slice(1))
|
|
481
|
+
v = VrpcAdapter.sanitizeEventListenerReturnValues(v)
|
|
482
|
+
}
|
|
521
483
|
} catch (err) {
|
|
522
|
-
e = err
|
|
484
|
+
e = err.message
|
|
523
485
|
}
|
|
524
486
|
if (VrpcAdapter._isPromise(v)) {
|
|
525
487
|
calls.push(
|
|
@@ -531,17 +493,48 @@ class VrpcAdapter {
|
|
|
531
493
|
calls.push({ id, val: v, err: e })
|
|
532
494
|
}
|
|
533
495
|
}
|
|
534
|
-
this._handlePromise(json,
|
|
496
|
+
this._handlePromise(json, Promise.all(calls))
|
|
497
|
+
} catch (err) {
|
|
498
|
+
json.e = err.message
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
static sanitizeEventListenerReturnValues (ret) {
|
|
503
|
+
return typeof ret === 'object' &&
|
|
504
|
+
ret._events !== undefined &&
|
|
505
|
+
ret._eventsCount !== undefined
|
|
506
|
+
? true
|
|
507
|
+
: ret
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
static _handleDelete (json) {
|
|
511
|
+
try {
|
|
512
|
+
const instance = json.a[0] // first arg is instance to be deleted
|
|
513
|
+
json.r = VrpcAdapter._delete(instance)
|
|
514
|
+
VrpcAdapter._emitter.emit('delete', {
|
|
515
|
+
instance,
|
|
516
|
+
className: json.c
|
|
517
|
+
})
|
|
535
518
|
} catch (err) {
|
|
536
|
-
|
|
519
|
+
json.e = err.message
|
|
537
520
|
}
|
|
538
521
|
}
|
|
539
522
|
|
|
540
523
|
static _handleCall (json) {
|
|
541
|
-
|
|
542
|
-
|
|
524
|
+
// special case: removeAllListeners
|
|
525
|
+
if (json.f === 'removeAllListeners') {
|
|
526
|
+
VrpcAdapter._removeAllListeners(json.a[0], json.s, json.c)
|
|
527
|
+
json.r = true
|
|
528
|
+
return
|
|
529
|
+
}
|
|
530
|
+
const unwrapped = VrpcAdapter._unwrapArguments(json)
|
|
531
|
+
if (!unwrapped) {
|
|
532
|
+
// this call must be skipped (event emitter stuff)
|
|
533
|
+
json.r = true
|
|
534
|
+
return
|
|
535
|
+
}
|
|
543
536
|
// Check whether context is a registered class
|
|
544
|
-
const entry = VrpcAdapter._functionRegistry.get(
|
|
537
|
+
const entry = VrpcAdapter._functionRegistry.get(json.c)
|
|
545
538
|
if (entry !== undefined) {
|
|
546
539
|
// entry is class -> function is static
|
|
547
540
|
const { Klass } = entry
|
|
@@ -549,39 +542,42 @@ class VrpcAdapter {
|
|
|
549
542
|
// rather sticking to those functions registered before...
|
|
550
543
|
// TODO This is even more important now as the agent does a wildcard
|
|
551
544
|
// subscription against all functions
|
|
552
|
-
if (VrpcAdapter._isFunction(Klass[
|
|
545
|
+
if (VrpcAdapter._isFunction(Klass[json.f])) {
|
|
553
546
|
try {
|
|
554
|
-
const ret = Klass[
|
|
547
|
+
const ret = Klass[json.f].apply(null, unwrapped)
|
|
555
548
|
// check if function returns promise
|
|
556
549
|
if (VrpcAdapter._isPromise(ret)) {
|
|
557
|
-
this._handlePromise(json,
|
|
558
|
-
} else
|
|
550
|
+
this._handlePromise(json, ret)
|
|
551
|
+
} else json.r = ret
|
|
559
552
|
} catch (err) {
|
|
560
|
-
|
|
553
|
+
json.e = err.message
|
|
561
554
|
}
|
|
562
|
-
} else throw new Error(`Could not find function: ${
|
|
555
|
+
} else throw new Error(`Could not find function: ${json.f}`)
|
|
563
556
|
} else {
|
|
564
557
|
// is not static
|
|
565
|
-
const entry = VrpcAdapter._instances.get(
|
|
558
|
+
const entry = VrpcAdapter._instances.get(json.c)
|
|
566
559
|
if (entry === undefined) {
|
|
567
|
-
throw new Error(`Could not find context: ${
|
|
560
|
+
throw new Error(`Could not find context: ${json.c}`)
|
|
568
561
|
}
|
|
569
562
|
const { instance } = entry
|
|
570
|
-
if (VrpcAdapter._isFunction(instance[
|
|
563
|
+
if (VrpcAdapter._isFunction(instance[json.f])) {
|
|
571
564
|
try {
|
|
572
|
-
|
|
565
|
+
let ret = instance[json.f].apply(instance, unwrapped)
|
|
566
|
+
ret = VrpcAdapter.sanitizeEventListenerReturnValues(ret)
|
|
573
567
|
// check if function returns promise
|
|
574
568
|
if (VrpcAdapter._isPromise(ret)) {
|
|
575
|
-
this._handlePromise(json,
|
|
576
|
-
} else
|
|
569
|
+
this._handlePromise(json, ret)
|
|
570
|
+
} else json.r = ret
|
|
577
571
|
} catch (err) {
|
|
578
|
-
|
|
572
|
+
json.e = err.message
|
|
579
573
|
}
|
|
580
|
-
} else throw new Error(`Could not find function: ${
|
|
574
|
+
} else throw new Error(`Could not find function: ${json.f}`)
|
|
581
575
|
}
|
|
582
576
|
}
|
|
583
577
|
|
|
584
|
-
static _create (className, ...args) {
|
|
578
|
+
static _create (className, instanceId, ...args) {
|
|
579
|
+
const entry = VrpcAdapter._instances.get(instanceId)
|
|
580
|
+
if (entry) return entry.instance
|
|
585
581
|
const { Klass, withNew, schema } = VrpcAdapter._getClassEntry(className)
|
|
586
582
|
if (schema !== null) {
|
|
587
583
|
VrpcAdapter._validate(schema, ...args)
|
|
@@ -590,33 +586,39 @@ class VrpcAdapter {
|
|
|
590
586
|
return Klass(...args)
|
|
591
587
|
}
|
|
592
588
|
|
|
593
|
-
static _createNamed (className, instanceId, ...args) {
|
|
594
|
-
const entry = VrpcAdapter._instances.get(instanceId)
|
|
595
|
-
if (!entry) {
|
|
596
|
-
const instance = VrpcAdapter._create(className, ...args)
|
|
597
|
-
VrpcAdapter._instances.set(instanceId, { className, instance })
|
|
598
|
-
return instance
|
|
599
|
-
}
|
|
600
|
-
return entry.instance
|
|
601
|
-
}
|
|
602
|
-
|
|
603
589
|
static _delete (instanceId) {
|
|
604
590
|
const entry = VrpcAdapter._instances.get(instanceId)
|
|
605
591
|
if (entry) {
|
|
592
|
+
// in case the instance was an EventEmitter, free all registered listeners
|
|
593
|
+
if (VrpcAdapter._isFunction(entry.removeAllListeners)) {
|
|
594
|
+
entry.removeAllListeners()
|
|
595
|
+
}
|
|
596
|
+
for (const [key, { clients, instance }] of Object.entries(
|
|
597
|
+
VrpcAdapter._listeners
|
|
598
|
+
)) {
|
|
599
|
+
if (instance === instanceId) {
|
|
600
|
+
VrpcAdapter._listeners[key] = { clients }
|
|
601
|
+
}
|
|
602
|
+
}
|
|
606
603
|
VrpcAdapter._instances.delete(instanceId)
|
|
607
604
|
return true
|
|
608
605
|
}
|
|
609
606
|
return false
|
|
610
607
|
}
|
|
611
608
|
|
|
612
|
-
static
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
609
|
+
static _unregisterClient (clientId) {
|
|
610
|
+
for (const [key, value] of Object.entries(VrpcAdapter._listeners)) {
|
|
611
|
+
const { instance, event, listener, clients } = value
|
|
612
|
+
const filteredClients = clients.filter(x => x !== clientId)
|
|
613
|
+
if (filteredClients.length === 0) {
|
|
614
|
+
if (event) {
|
|
615
|
+
VrpcAdapter.getInstance(instance).removeListener(event, listener)
|
|
616
|
+
}
|
|
617
|
+
delete VrpcAdapter._listeners[key]
|
|
618
|
+
} else {
|
|
619
|
+
VrpcAdapter._listeners[key].clients = filteredClients
|
|
620
|
+
}
|
|
618
621
|
}
|
|
619
|
-
delete VrpcAdapter._listeners[clientId]
|
|
620
622
|
}
|
|
621
623
|
|
|
622
624
|
static _validate (schema, params) {
|
|
@@ -627,119 +629,99 @@ class VrpcAdapter {
|
|
|
627
629
|
if (!valid) throw new Error(VrpcAdapter._ajv.errorsText())
|
|
628
630
|
}
|
|
629
631
|
|
|
630
|
-
static
|
|
631
|
-
const {
|
|
632
|
-
const
|
|
633
|
-
const
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
632
|
+
static _unwrapArguments (json, instanceId) {
|
|
633
|
+
const { f: func, s: sender, a: args, c: context } = json
|
|
634
|
+
const unwrapped = []
|
|
635
|
+
for (const arg of args) {
|
|
636
|
+
// find those args that actually need to be functions
|
|
637
|
+
if (typeof arg === 'string' && arg.slice(0, 5) === '__f__') {
|
|
638
|
+
const callback = this._generateCallback(arg, json, instanceId)
|
|
639
|
+
unwrapped.push(callback)
|
|
640
|
+
} else if (typeof arg === 'string' && arg.slice(0, 5) === '__e__') {
|
|
641
|
+
const listener = this._generateListener(arg, json, instanceId)
|
|
642
|
+
unwrapped.push(listener)
|
|
643
|
+
// start tracking clients that are registering event listeners
|
|
644
|
+
if (!VrpcAdapter._listeners[arg]) {
|
|
645
|
+
VrpcAdapter._listeners[arg] = {
|
|
646
|
+
listener,
|
|
647
|
+
clients: [sender],
|
|
648
|
+
instance: context,
|
|
649
|
+
event: null
|
|
650
|
+
}
|
|
651
|
+
} else {
|
|
652
|
+
VrpcAdapter._listeners[arg].clients.push(sender)
|
|
648
653
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
const s = VrpcAdapter._listeners[sender]
|
|
655
|
-
const entry = s && s.find(x => x.r === r)
|
|
656
|
-
if (entry) {
|
|
657
|
-
const { i, e, f } = entry
|
|
658
|
-
VrpcAdapter.getInstance(i).removeListener(e, f)
|
|
654
|
+
// see whether the requested listener is an EventEmitter registration
|
|
655
|
+
if (func === 'on' || func === 'addListener') {
|
|
656
|
+
// if this event is already registered avoid a second registration
|
|
657
|
+
if (VrpcAdapter._listeners[arg].event) {
|
|
658
|
+
return null // means that this call won't be forwarded
|
|
659
659
|
}
|
|
660
|
+
VrpcAdapter._listeners[arg].event = args[0]
|
|
661
|
+
}
|
|
662
|
+
if (func === 'off' || func === 'removeListener') {
|
|
663
|
+
const { clients, listener, event } = VrpcAdapter._listeners[arg]
|
|
664
|
+
const filteredClients = clients.filter(x => x !== sender)
|
|
665
|
+
// don't unregister this event, as other clients are still listening
|
|
666
|
+
if (filteredClients.length > 0) {
|
|
667
|
+
VrpcAdapter._listeners[arg].clients = filteredClients
|
|
668
|
+
} else {
|
|
669
|
+
delete VrpcAdapter._listeners[arg]
|
|
670
|
+
VrpcAdapter.getInstance(context).off(event, listener)
|
|
671
|
+
}
|
|
672
|
+
return null // means that this call won't be forwarded
|
|
660
673
|
}
|
|
661
674
|
} else {
|
|
662
|
-
//
|
|
663
|
-
|
|
675
|
+
// default behavior is to not touch
|
|
676
|
+
unwrapped.push(arg)
|
|
664
677
|
}
|
|
665
|
-
}
|
|
666
|
-
return
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
static
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
}
|
|
679
|
-
// TODO Heal that crazy API !!
|
|
680
|
-
const callbackJson = { ...json, data, id }
|
|
681
|
-
const callbackJsonString = VrpcAdapter._stringifySafely(callbackJson)
|
|
682
|
-
VrpcAdapter._callback(callbackJsonString, callbackJson)
|
|
678
|
+
}
|
|
679
|
+
return unwrapped
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
static _removeAllListeners (event, sender, context) {
|
|
683
|
+
for (const [key, value] of Object.entries(VrpcAdapter._listeners)) {
|
|
684
|
+
if (value.event === event) {
|
|
685
|
+
const filteredClients = value.clients.filter(x => x !== sender)
|
|
686
|
+
if (filteredClients.length > 0) {
|
|
687
|
+
VrpcAdapter._listeners[key].clients = filteredClients
|
|
688
|
+
} else {
|
|
689
|
+
delete VrpcAdapter._listeners[key]
|
|
690
|
+
VrpcAdapter.getInstance(context).off(event, value.listener)
|
|
691
|
+
}
|
|
683
692
|
}
|
|
684
|
-
return wrapper
|
|
685
693
|
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
})
|
|
693
|
-
// TODO Heal that crazy API !!
|
|
694
|
-
const callbackJson = { ...json, data, id }
|
|
695
|
-
const callbackJsonString = VrpcAdapter._stringifySafely(callbackJson)
|
|
696
|
-
VrpcAdapter._callback(callbackJsonString, callbackJson)
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
static _generateCallback (id, json, instanceId) {
|
|
697
|
+
return (...innerArgs) => {
|
|
698
|
+
// callAll expects the instanceId as first argument
|
|
699
|
+
const a = instanceId ? [instanceId, ...innerArgs] : [...innerArgs]
|
|
700
|
+
VrpcAdapter._callback({ a, s: json.s, i: id })
|
|
697
701
|
}
|
|
698
|
-
return wrapper
|
|
699
702
|
}
|
|
700
703
|
|
|
701
|
-
static
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
const data = { r: value }
|
|
709
|
-
const promiseJson = Object.assign({}, json, { data, id })
|
|
710
|
-
const promiseJsonString = VrpcAdapter._stringifySafely(promiseJson)
|
|
711
|
-
VrpcAdapter._callback(promiseJsonString, promiseJson)
|
|
712
|
-
})
|
|
713
|
-
.catch(err => {
|
|
714
|
-
const data = { e: err.message }
|
|
715
|
-
const promiseJson = Object.assign({}, json, { data, id })
|
|
716
|
-
const promiseJsonString = VrpcAdapter._stringifySafely(promiseJson)
|
|
717
|
-
VrpcAdapter._callback(promiseJsonString, promiseJson)
|
|
718
|
-
})
|
|
719
|
-
} catch (err) {
|
|
720
|
-
json.data.e = err.message
|
|
704
|
+
static _generateListener (id, json, instanceId) {
|
|
705
|
+
return (...innerArgs) => {
|
|
706
|
+
// do not even emit for already offline clients
|
|
707
|
+
if (!VrpcAdapter._listeners[id]) return
|
|
708
|
+
// callAll expects the instanceId as first argument
|
|
709
|
+
const a = instanceId ? [instanceId, ...innerArgs] : [...innerArgs]
|
|
710
|
+
VrpcAdapter._callback({ a, s: json.s, i: id })
|
|
721
711
|
}
|
|
722
712
|
}
|
|
723
713
|
|
|
724
|
-
static
|
|
725
|
-
let jsonString
|
|
714
|
+
static _handlePromise (json, promise) {
|
|
726
715
|
try {
|
|
727
|
-
|
|
716
|
+
const cid = VrpcAdapter._correlationId++ % Number.MAX_SAFE_INTEGER
|
|
717
|
+
const id = `__p__${json.f}-${cid}`
|
|
718
|
+
json.r = id
|
|
719
|
+
promise
|
|
720
|
+
.then(r => VrpcAdapter._callback({ ...json, r, i: id }))
|
|
721
|
+
.catch(err => VrpcAdapter._callback({ ...json, e: err.message, i: id }))
|
|
728
722
|
} catch (err) {
|
|
729
|
-
|
|
730
|
-
`Failed serialization of return value for: ${json.context}::${json.method}, because: ${err.message}`
|
|
731
|
-
)
|
|
732
|
-
json.data.r = '__vrpc::not-serializable__'
|
|
733
|
-
jsonString = JSON.stringify(json)
|
|
723
|
+
json.data.e = err.message
|
|
734
724
|
}
|
|
735
|
-
return jsonString
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
static _extractDataToArray (data) {
|
|
739
|
-
return Object.keys(data)
|
|
740
|
-
.sort()
|
|
741
|
-
.filter(value => value[0] === '_')
|
|
742
|
-
.map(key => data[key])
|
|
743
725
|
}
|
|
744
726
|
|
|
745
727
|
static _generateId (object) {
|
|
@@ -825,8 +807,8 @@ class VrpcAdapter {
|
|
|
825
807
|
|
|
826
808
|
static _getInstancesArray (className) {
|
|
827
809
|
const instances = []
|
|
828
|
-
VrpcAdapter._instances.forEach((
|
|
829
|
-
if (
|
|
810
|
+
VrpcAdapter._instances.forEach((v, k) => {
|
|
811
|
+
if (v.className === className && !v.isIsolated) instances.push(k)
|
|
830
812
|
})
|
|
831
813
|
return instances
|
|
832
814
|
}
|
|
@@ -853,7 +835,7 @@ class VrpcAdapter {
|
|
|
853
835
|
/**
|
|
854
836
|
* Event 'create'
|
|
855
837
|
*
|
|
856
|
-
* Emitted on creation of
|
|
838
|
+
* Emitted on creation of shared instance
|
|
857
839
|
*
|
|
858
840
|
* @event VrpcAdapter#create
|
|
859
841
|
* @type {Object}
|
|
@@ -865,7 +847,7 @@ class VrpcAdapter {
|
|
|
865
847
|
/**
|
|
866
848
|
* Event 'delete'
|
|
867
849
|
*
|
|
868
|
-
* Emitted on deletion of
|
|
850
|
+
* Emitted on deletion of shared instance
|
|
869
851
|
*
|
|
870
852
|
* @event VrpcAdapter#delete
|
|
871
853
|
* @type {Object}
|