vrpc 2.4.0 → 3.0.0-alpha.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.
@@ -10,9 +10,9 @@ __\/\\\_______\/\\\__/\\\///////\\\___\/\\\/////////\\\____/\\\////////__
10
10
  ________\///________\///________\///__\///___________________\/////////__
11
11
 
12
12
 
13
- Non-intrusively binds code and provides access in form of asynchronous remote
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/bheisen/vrpc)
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>.
@@ -42,8 +42,8 @@ const path = require('path')
42
42
  const fs = require('fs')
43
43
  const Ajv = require('ajv')
44
44
  const caller = require('caller')
45
- const shortid = require('shortid')
46
45
  const commentParser = require('./comment-parser')
46
+ const EventEmitter = require('events')
47
47
 
48
48
  /**
49
49
  * Generates an adapter layer for existing code and enables further VRPC-based
@@ -94,7 +94,7 @@ class VrpcAdapter {
94
94
  * using the `new` operator
95
95
  * @param {Object} [options.schema=null] If provided is used to validate ctor
96
96
  * parameters (only works if registered code reflects a single class)
97
- * @param {Boolean} options.jsdocPath if provided, parses documentation and
97
+ * @param {String} options.jsdocPath if provided, parses documentation and
98
98
  * provides it as meta information
99
99
  *
100
100
  * NOTE: This function currently only supports registration of classes (either
@@ -110,7 +110,7 @@ class VrpcAdapter {
110
110
  } else {
111
111
  const { jsdocPath } = options
112
112
  if (jsdocPath) {
113
- const absJsdocPath = path.resolve(caller(), '../', jsdocPath, '.js')
113
+ const absJsdocPath = path.resolve(caller(), '../', `${jsdocPath}.js`)
114
114
  this._registerClass(code, absJsdocPath, { ...options, jsdoc: true })
115
115
  } else {
116
116
  this._registerClass(code, null, options)
@@ -118,6 +118,49 @@ class VrpcAdapter {
118
118
  }
119
119
  }
120
120
 
121
+ /**
122
+ * Registers an existing instance and make it (remotely) callable
123
+ *
124
+ * @param {Object} obj The instance to be registered
125
+ * @param {Object} options
126
+ * @param {String} options.className Class name of the instance
127
+ * @param {String} options.instance Name of the instance
128
+ * @param {Boolean} [options.onlyPublic=true] If true, only registers
129
+ * functions that do not begin with an underscore
130
+ * @param {String} [options.jsdocPath] if provided, parses documentation and
131
+ * provides it as meta information
132
+ */
133
+ static registerInstance (
134
+ obj,
135
+ { className, instance, jsdocPath, onlyPublic = true } = {}
136
+ ) {
137
+ let memberFunctions = VrpcAdapter._extractMemberFunctions(obj)
138
+ if (onlyPublic) {
139
+ memberFunctions = memberFunctions.filter(f => {
140
+ return !f.startsWith('_')
141
+ })
142
+ }
143
+ let meta = null
144
+ if (jsdocPath) {
145
+ const absJsdocPath = path.resolve(caller(), '../', `${jsdocPath}.js`)
146
+ const content = fs.readFileSync(absJsdocPath).toString('utf8')
147
+ meta = this._parseComments(content)
148
+ }
149
+ VrpcAdapter._functionRegistry.set(className, {
150
+ memberFunctions,
151
+ meta,
152
+ Klass: {},
153
+ withNew: false,
154
+ staticFunctions: [],
155
+ schema: null
156
+ })
157
+ VrpcAdapter._instances.set(instance, {
158
+ className,
159
+ instance: obj,
160
+ isIsolated: false
161
+ })
162
+ }
163
+
121
164
  static getClasses () {
122
165
  return JSON.stringify(VrpcAdapter._getClassesArray())
123
166
  }
@@ -260,14 +303,14 @@ class VrpcAdapter {
260
303
  * @typedef {Object.<String, Func>} MetaData Associates meta data to any function
261
304
  */
262
305
 
263
- /**
264
- * @typedef Func
265
- * @param {String} description Function description
266
- * @param {Array.<Param>} params Array of parameter details in order of signature
267
- * @param {Ret} ret Object associating further information to return value
268
- */
306
+ /**
307
+ * @typedef Func
308
+ * @param {String} description Function description
309
+ * @param {Array.<Param>} params Array of parameter details in order of signature
310
+ * @param {Ret} ret Object associating further information to return value
311
+ */
269
312
 
270
- /**
313
+ /**
271
314
  * @typedef {Object} Param
272
315
  * @param {String} name Parameter name
273
316
  * @param {Boolean} optional Whether parameter is optional
@@ -276,23 +319,34 @@ class VrpcAdapter {
276
319
  * @param {Any} [default] The default to be injected when not provided
277
320
  */
278
321
 
279
- /**
280
- * @typedef {Object} Ret
281
- * @param {String} description Return value description
282
- * @param {String} [type] Return value type
283
- */
322
+ /**
323
+ * @typedef {Object} Ret
324
+ * @param {String} description Return value description
325
+ * @param {String} [type] Return value type
326
+ */
327
+
328
+ static on (eventName, listener) {
329
+ VrpcAdapter._emitter.on(eventName, listener)
330
+ }
331
+
332
+ static once (eventName, listener) {
333
+ VrpcAdapter._emitter.once(eventName, listener)
334
+ }
335
+
336
+ static off (eventName, listener) {
337
+ VrpcAdapter._emitter.off(eventName, listener)
338
+ }
339
+
340
+ static removeAllListeners (eventName) {
341
+ VrpcAdapter._emitter.removeAllListeners(eventName)
342
+ }
284
343
 
285
344
  // private:
286
345
 
287
346
  static _registerClass (
288
347
  Klass,
289
348
  absJsdocPath = null,
290
- {
291
- onlyPublic = true,
292
- withNew = true,
293
- schema = null,
294
- jsdoc = true
295
- } = {}
349
+ { onlyPublic = true, withNew = true, schema = null, jsdoc = true } = {}
296
350
  ) {
297
351
  // Get all static static functions
298
352
  let staticFunctions = VrpcAdapter._extractStaticFunctions(Klass)
@@ -300,24 +354,29 @@ class VrpcAdapter {
300
354
  staticFunctions = staticFunctions.filter(f => !f.startsWith('_'))
301
355
  }
302
356
  // Inject constructor and destructor
303
- staticFunctions.push('__create__')
304
- staticFunctions.push('__delete__')
305
- staticFunctions.push('__createNamed__')
306
- staticFunctions.push('__getNamed__')
357
+ staticFunctions.push('__createIsolated__')
358
+ staticFunctions.push('__createShared__')
307
359
  staticFunctions.push('__callAll__')
360
+ staticFunctions.push('__delete__')
308
361
  let memberFunctions = VrpcAdapter._extractMemberFunctions(Klass)
309
362
  if (onlyPublic) {
310
- memberFunctions = memberFunctions.filter(f => !f.startsWith('_'))
363
+ memberFunctions = memberFunctions.filter(f => {
364
+ return !f.startsWith('_')
365
+ })
311
366
  }
312
367
  let meta = null
313
368
  if (jsdoc && absJsdocPath) {
314
369
  const content = fs.readFileSync(absJsdocPath).toString('utf8')
315
370
  meta = this._parseComments(content)
316
371
  }
317
- VrpcAdapter._functionRegistry.set(
318
- Klass.name,
319
- { Klass, withNew, staticFunctions, memberFunctions, schema, meta }
320
- )
372
+ VrpcAdapter._functionRegistry.set(Klass.name, {
373
+ Klass,
374
+ withNew,
375
+ staticFunctions,
376
+ memberFunctions,
377
+ schema,
378
+ meta
379
+ })
321
380
  }
322
381
 
323
382
  static _parseComments (content) {
@@ -326,7 +385,9 @@ class VrpcAdapter {
326
385
  comments.forEach(({ tags, description, functionName }) => {
327
386
  if (functionName) {
328
387
  if (tags) {
329
- let params = tags.filter(({ tag }) => tag === 'param' || tag === 'arg' || tag === 'argument')
388
+ let params = tags.filter(
389
+ ({ tag }) => tag === 'param' || tag === 'arg' || tag === 'argument'
390
+ )
330
391
  if (params.length > 0) {
331
392
  params = params.map(({ name, optional, description, type }) => {
332
393
  return { name, optional, description, type }
@@ -334,10 +395,15 @@ class VrpcAdapter {
334
395
  } else {
335
396
  params = []
336
397
  }
337
- let ret = tags.filter(({ tag }) => tag === 'returns' || tag === 'return')
398
+ let ret = tags.filter(
399
+ ({ tag }) => tag === 'returns' || tag === 'return'
400
+ )
338
401
  if (ret.length === 1) {
339
402
  const { source, type } = ret[0]
340
- const description = source.split(' ').splice(2).join(' ')
403
+ const description = source
404
+ .split(' ')
405
+ .splice(2)
406
+ .join(' ')
341
407
  ret = { description, type }
342
408
  } else {
343
409
  ret = null
@@ -352,7 +418,7 @@ class VrpcAdapter {
352
418
  },
353
419
  ...params
354
420
  ]
355
- meta.__createNamed__ = { description, params: modified, ret }
421
+ meta.__createShared__ = { description, params: modified, ret }
356
422
  } else {
357
423
  meta[functionName] = { description, params, ret }
358
424
  }
@@ -363,120 +429,146 @@ class VrpcAdapter {
363
429
  }
364
430
 
365
431
  static _call (json) {
366
- const { context, method, data } = json
367
- const wrappedArgs = VrpcAdapter._wrapCallbacks(json)
368
- // VrpcAdapter._log.debug(`Calling function: ${method} with payload: ${data}`)
369
- switch (method) {
370
- // Special case: ctor
371
- case '__create__':
372
- try {
373
- const instance = VrpcAdapter._create(context, ...wrappedArgs)
374
- const instanceId = shortid.generate()
375
- VrpcAdapter._instances.set(instanceId, { context, instance })
376
- data.r = instanceId
377
- } catch (err) {
378
- data.e = err.message
379
- }
432
+ switch (json.f) {
433
+ case '__createIsolated__':
434
+ VrpcAdapter._handleCreateIsolated(json)
435
+ break
436
+ case '__createShared__':
437
+ VrpcAdapter._handleCreateShared(json)
438
+ break
439
+ case '__callAll__':
440
+ VrpcAdapter._handleCallAll(json)
380
441
  break
381
- // Special case: dtor
382
442
  case '__delete__':
383
- try {
384
- const instanceId = wrappedArgs[0]
385
- data.r = VrpcAdapter._delete(instanceId)
386
- } catch (err) {
387
- data.e = err.message
388
- }
443
+ VrpcAdapter._handleDelete(json)
389
444
  break
390
- // Special case: named construction
391
- case '__createNamed__':
445
+ default:
446
+ VrpcAdapter._handleCall(json)
447
+ }
448
+ }
449
+
450
+ static _handleCreateIsolated (json) {
451
+ try {
452
+ const className = json.c
453
+ const [instanceId, ...args] = json.a
454
+ const instance = VrpcAdapter._create(className, instanceId, ...args)
455
+ VrpcAdapter._instances.set(instanceId, {
456
+ instance,
457
+ className,
458
+ isIsolated: true
459
+ })
460
+ json.r = instanceId
461
+ } catch (err) {
462
+ json.e = err.message
463
+ }
464
+ }
465
+
466
+ static _handleCreateShared (json) {
467
+ try {
468
+ const className = json.c
469
+ const [instanceId, ...args] = json.a
470
+ const instance = VrpcAdapter._create(className, instanceId, ...args)
471
+ VrpcAdapter._instances.set(instanceId, {
472
+ instance,
473
+ className,
474
+ isIsolated: false
475
+ })
476
+ json.r = instanceId
477
+ } catch (err) {
478
+ json.e = err.message
479
+ }
480
+ }
481
+
482
+ static _handleCallAll (json) {
483
+ try {
484
+ const calls = []
485
+ for (const [id, { className, instance }] of VrpcAdapter._instances) {
486
+ if (className !== json.c) continue
487
+ let v
488
+ let e = null
392
489
  try {
393
- VrpcAdapter._createNamed(context, ...wrappedArgs)
394
- data.r = wrappedArgs[0] // First argument is instanceId
490
+ const wrappedArgs = VrpcAdapter._wrapArguments(json, id)
491
+ const funcName = wrappedArgs[0]
492
+ v = instance[funcName].apply(instance, wrappedArgs.slice(1))
395
493
  } catch (err) {
396
- data.e = err.message
494
+ e = err.message
397
495
  }
398
- break
399
- case '__getNamed__': // Special case: retrieval of known instance
496
+ if (VrpcAdapter._isPromise(v)) {
497
+ calls.push(
498
+ v
499
+ .then(val => ({ id, val, err: null }))
500
+ .catch(err => ({ id, err, val: null }))
501
+ )
502
+ } else {
503
+ calls.push({ id, val: v, err: e })
504
+ }
505
+ }
506
+ this._handlePromise(json, Promise.all(calls))
507
+ } catch (err) {
508
+ json.e = err.message
509
+ }
510
+ }
511
+
512
+ static _handleDelete (json) {
513
+ try {
514
+ const wrappedArgs = VrpcAdapter._wrapArguments(json)
515
+ const instanceId = wrappedArgs[0]
516
+ json.r = VrpcAdapter._delete(instanceId)
517
+ VrpcAdapter._emitter.emit('delete', {
518
+ className: json.c,
519
+ instance: wrappedArgs[0]
520
+ })
521
+ } catch (err) {
522
+ json.e = err.message
523
+ }
524
+ }
525
+
526
+ static _handleCall (json) {
527
+ const wrappedArgs = VrpcAdapter._wrapArguments(json)
528
+ // Check whether context is a registered class
529
+ const entry = VrpcAdapter._functionRegistry.get(json.c)
530
+ if (entry !== undefined) {
531
+ // entry is class -> function is static
532
+ const { Klass } = entry
533
+ // TODO Think about whether to do live checking (like here) or
534
+ // rather sticking to those functions registered before...
535
+ // TODO This is even more important now as the agent does a wildcard
536
+ // subscription against all functions
537
+ if (VrpcAdapter._isFunction(Klass[json.f])) {
400
538
  try {
401
- const instanceId = wrappedArgs[0]
402
- const entry = VrpcAdapter._instances.get(instanceId)
403
- if (entry) data.r = instanceId
404
- else data.e = `Instance with id: ${instanceId} does not exist`
539
+ const ret = Klass[json.f].apply(null, wrappedArgs)
540
+ // check if function returns promise
541
+ if (VrpcAdapter._isPromise(ret)) {
542
+ this._handlePromise(json, ret)
543
+ } else json.r = ret
405
544
  } catch (err) {
406
- data.e = err.message
545
+ json.e = err.message
407
546
  }
408
- break
409
- case '__callAll__': // Special case: call on all known instances
547
+ } else throw new Error(`Could not find function: ${json.f}`)
548
+ } else {
549
+ // is not static
550
+ const entry = VrpcAdapter._instances.get(json.c)
551
+ if (entry === undefined) {
552
+ throw new Error(`Could not find context: ${json.c}`)
553
+ }
554
+ const { instance } = entry
555
+ if (VrpcAdapter._isFunction(instance[json.f])) {
410
556
  try {
411
- const funcName = wrappedArgs[0]
412
- const calls = []
413
- for (const [id, { className, instance }] of VrpcAdapter._instances) {
414
- if (className !== context) continue
415
- let v
416
- let e = null
417
- try {
418
- v = instance[funcName].apply(instance, wrappedArgs.slice(1))
419
- } catch (err) {
420
- e = err
421
- }
422
- // check wether function returned a promise
423
- if (VrpcAdapter._isPromise(v)) {
424
- calls.push(v
425
- .then(val => ({ id, val, err: null }))
426
- .catch(err => ({ id, err, val: null })))
427
- } else {
428
- calls.push({ id, val: v, err: e })
429
- }
430
- }
431
- this._handlePromise(json, method, Promise.all(calls))
557
+ const ret = instance[json.f].apply(instance, wrappedArgs)
558
+ // check if function returns promise
559
+ if (VrpcAdapter._isPromise(ret)) {
560
+ this._handlePromise(json, ret)
561
+ } else json.r = ret
432
562
  } catch (err) {
433
- data.e = err.message
434
- }
435
- break
436
- // Regular function call
437
- default: {
438
- // Check whether context is a registered class
439
- const entry = VrpcAdapter._functionRegistry.get(context)
440
- if (entry !== undefined) { // entry is class -> function is static
441
- const { Klass } = entry
442
- // TODO Think about whether to do live checking (like here) or
443
- // rather sticking to those functions registered before...
444
- // TODO This is even more important now as the agent does a wildcard
445
- // subscription against all functions
446
- if (VrpcAdapter._isFunction(Klass[method])) {
447
- try {
448
- const ret = Klass[method].apply(null, wrappedArgs)
449
- // check if function returns promise
450
- if (ret && this._isFunction(ret.then)) {
451
- this._handlePromise(json, method, ret)
452
- } else data.r = ret
453
- } catch (err) {
454
- data.e = err.message
455
- }
456
- } else throw new Error(`Could not find function: ${method}`)
457
- } else { // is not static
458
- const entry = VrpcAdapter._instances.get(context)
459
- if (entry === undefined) {
460
- throw new Error(`Could not find context: ${context}`)
461
- }
462
- const { instance } = entry
463
- if (VrpcAdapter._isFunction(instance[method])) {
464
- try {
465
- const ret = instance[method].apply(instance, wrappedArgs)
466
- // check if function returns promise
467
- if (VrpcAdapter._isPromise(ret)) {
468
- this._handlePromise(json, method, ret)
469
- } else data.r = ret
470
- } catch (err) {
471
- data.e = err.message
472
- }
473
- } else throw new Error(`Could not find function: ${method}`)
563
+ json.e = err.message
474
564
  }
475
- }
565
+ } else throw new Error(`Could not find function: ${json.f}`)
476
566
  }
477
567
  }
478
568
 
479
- static _create (className, ...args) {
569
+ static _create (className, instanceId, ...args) {
570
+ const entry = VrpcAdapter._instances.get(instanceId)
571
+ if (entry) return entry.instance
480
572
  const { Klass, withNew, schema } = VrpcAdapter._getClassEntry(className)
481
573
  if (schema !== null) {
482
574
  VrpcAdapter._validate(schema, ...args)
@@ -485,16 +577,6 @@ class VrpcAdapter {
485
577
  return Klass(...args)
486
578
  }
487
579
 
488
- static _createNamed (className, instanceId, ...args) {
489
- const entry = VrpcAdapter._instances.get(instanceId)
490
- if (!entry) {
491
- const instance = VrpcAdapter._create(className, ...args)
492
- VrpcAdapter._instances.set(instanceId, { className, instance })
493
- return instance
494
- }
495
- return entry.instance
496
- }
497
-
498
580
  static _delete (instanceId) {
499
581
  const entry = VrpcAdapter._instances.get(instanceId)
500
582
  if (entry) {
@@ -511,6 +593,7 @@ class VrpcAdapter {
511
593
  VrpcAdapter.getInstance(i).removeListener(e, f)
512
594
  })
513
595
  }
596
+ delete VrpcAdapter._listeners[clientId]
514
597
  }
515
598
 
516
599
  static _validate (schema, params) {
@@ -521,92 +604,71 @@ class VrpcAdapter {
521
604
  if (!valid) throw new Error(VrpcAdapter._ajv.errorsText())
522
605
  }
523
606
 
524
- static _wrapCallbacks (json) {
525
- const args = VrpcAdapter._extractDataToArray(json.data)
526
- const wrappedArgs = []
607
+ static _wrapArguments (json, instanceId) {
608
+ const { f: func, s: sender, a: args, c: context } = json
609
+ const wrapped = []
527
610
  args.forEach(arg => {
528
611
  // Find those args that actually need to be function callbacks
529
612
  if (typeof arg === 'string' && arg.substr(0, 5) === '__f__') {
530
- const f = (...innerArgs) => {
531
- const data = {}
532
- innerArgs.forEach((value, index) => {
533
- data[`_${index + 1}`] = value
534
- })
535
- const callbackJson = Object.assign({}, json, { data, id: arg })
536
- const callbackJsonString = VrpcAdapter._stringifySafely(callbackJson)
537
- VrpcAdapter._callback(callbackJsonString, callbackJson)
538
- }
613
+ const f = this._generateWrapper(arg, json, instanceId)
614
+ wrapped.push(f)
615
+ if (!sender) return
616
+ // Register this client to later know when he is dead and skip callbacks
617
+ if (!VrpcAdapter._listeners[sender]) VrpcAdapter._listeners[sender] = []
539
618
  // Check whether injected callback is an EventEmitter registration
540
- if (json.method === 'on' && typeof args[0] === 'string') {
619
+ if (func === 'on' && typeof args[0] === 'string') {
541
620
  const e = args[0]
542
- const i = json.context
621
+ const i = context
543
622
  const r = arg
544
- VrpcAdapter._listeners[json.sender]
545
- ? VrpcAdapter._listeners[json.sender].push({ i, e, f, r })
546
- : VrpcAdapter._listeners[json.sender] = [{ i, e, f, r }]
623
+ VrpcAdapter._listeners[sender].push({ i, e, f, r })
547
624
  }
548
- if ((json.method === 'off' || json.method === 'removeListener') &&
549
- typeof args[0] === 'string') {
550
- const r = arg
551
- const sender = VrpcAdapter._listeners[json.sender]
552
- const entry = sender && sender.find(x => x.r === r)
625
+ if (
626
+ (func === 'off' || func === 'removeListener') &&
627
+ typeof args[0] === 'string'
628
+ ) {
629
+ const listener = VrpcAdapter._listeners[sender]
630
+ const entry = listener && listener.find(x => x.r === arg)
553
631
  if (entry) {
554
632
  const { i, e, f } = entry
555
633
  VrpcAdapter.getInstance(i).removeListener(e, f)
556
634
  }
557
635
  }
558
- wrappedArgs.push(f)
559
- // Leave the others untouched
560
636
  } else {
561
- wrappedArgs.push(arg)
637
+ // Leave the others untouched
638
+ wrapped.push(arg)
562
639
  }
563
640
  })
564
- return wrappedArgs
641
+ return wrapped
565
642
  }
566
643
 
567
- static _handlePromise (json, method, promise) {
568
- try {
569
- const cid = VrpcAdapter._correlationId++ % Number.MAX_SAFE_INTEGER
570
- const id = `__p__${method}-${cid}`
571
- json.data.r = id
572
- promise
573
- .then(value => {
574
- const data = { r: value }
575
- const promiseJson = Object.assign({}, json, { data, id })
576
- const promiseJsonString = VrpcAdapter._stringifySafely(promiseJson)
577
- VrpcAdapter._callback(promiseJsonString, promiseJson)
578
- })
579
- .catch(err => {
580
- const data = { e: err.message }
581
- const promiseJson = Object.assign({}, json, { data, id })
582
- const promiseJsonString = VrpcAdapter._stringifySafely(promiseJson)
583
- VrpcAdapter._callback(promiseJsonString, promiseJson)
584
- })
585
- } catch (err) {
586
- json.data.e = err.message
644
+ static _generateWrapper (id, json, instanceId) {
645
+ const wrapper = (...innerArgs) => {
646
+ // do not even trigger a callback for already offline clients
647
+ if (json.s && !VrpcAdapter._listeners[json.s]) return
648
+ const a = instanceId ? [instanceId, ...innerArgs] : [...innerArgs]
649
+ VrpcAdapter._callback({ ...json, a, i: id })
587
650
  }
651
+ return wrapper
588
652
  }
589
653
 
590
- static _stringifySafely (json) {
591
- let jsonString
654
+ static _handlePromise (json, promise) {
592
655
  try {
593
- jsonString = JSON.stringify(json)
656
+ const cid = VrpcAdapter._correlationId++ % Number.MAX_SAFE_INTEGER
657
+ const id = `__p__${json.f}-${cid}`
658
+ json.r = id
659
+ promise
660
+ .then(r => VrpcAdapter._callback({ ...json, r, i: id }))
661
+ .catch(err => VrpcAdapter._callback({ ...json, e: err.message, i: id }))
594
662
  } catch (err) {
595
- this._log.debug(`Failed serialization of return value for: ${json.context}::${json.method}, because: ${err.message}`)
596
- json.data.r = '__vrpc::not-serializable__'
597
- jsonString = JSON.stringify(json)
663
+ json.data.e = err.message
598
664
  }
599
- return jsonString
600
- }
601
-
602
- static _extractDataToArray (data) {
603
- return Object.keys(data).sort()
604
- .filter(value => value[0] === '_')
605
- .map(key => data[key])
606
665
  }
607
666
 
608
667
  static _generateId (object) {
609
- return crypto.createHash('md5').update(JSON.stringify(object)).digest('hex')
668
+ return crypto
669
+ .createHash('md5')
670
+ .update(JSON.stringify(object))
671
+ .digest('hex')
610
672
  }
611
673
 
612
674
  static _getClassEntry (className) {
@@ -626,11 +688,17 @@ class VrpcAdapter {
626
688
  }
627
689
 
628
690
  static _isClass (obj) {
629
- return !!obj && (typeof obj === 'function' && /^\s*class\s+/.test(obj.toString()))
691
+ return (
692
+ !!obj && typeof obj === 'function' && /^\s*class\s+/.test(obj.toString())
693
+ )
630
694
  }
631
695
 
632
696
  static _isPromise (obj) {
633
- return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'
697
+ return (
698
+ !!obj &&
699
+ (typeof obj === 'object' || typeof obj === 'function') &&
700
+ typeof obj.then === 'function'
701
+ )
634
702
  }
635
703
 
636
704
  static _extractMemberFunctions (klass) {
@@ -638,12 +706,20 @@ class VrpcAdapter {
638
706
  let fs = []
639
707
  do {
640
708
  if (klass_.prototype) {
641
- fs = [...fs, ...Object.getOwnPropertyNames(klass_.prototype)]
709
+ const funcs = Object.getOwnPropertyNames(klass_.prototype).filter(
710
+ x =>
711
+ !VrpcAdapter._blackList.has(x) &&
712
+ this._isFunction(klass_.prototype[x])
713
+ )
714
+ fs = [...fs, ...funcs]
642
715
  } else {
643
- fs = [...fs, ...Object.getOwnPropertyNames(klass_)]
716
+ const funcs = Object.getOwnPropertyNames(klass_).filter(
717
+ x => !VrpcAdapter._blackList.has(x) && this._isFunction(klass_[x])
718
+ )
719
+ fs = [...fs, ...funcs]
644
720
  }
645
721
  klass_ = Object.getPrototypeOf(klass_)
646
- } while (klass_ && klass_.name)
722
+ } while (klass_)
647
723
  // Filter out all duplicates
648
724
  return Array.from(new Set(fs))
649
725
  }
@@ -652,10 +728,13 @@ class VrpcAdapter {
652
728
  let klass_ = klass
653
729
  let fs = []
654
730
  do {
655
- fs = [...fs, ...Object.getOwnPropertyNames(klass_).filter(prop => {
656
- const desc = Object.getOwnPropertyDescriptor(klass_, prop)
657
- return !!desc && typeof desc.value === 'function'
658
- })]
731
+ fs = [
732
+ ...fs,
733
+ ...Object.getOwnPropertyNames(klass_).filter(prop => {
734
+ const desc = Object.getOwnPropertyDescriptor(klass_, prop)
735
+ return !!desc && typeof desc.value === 'function'
736
+ })
737
+ ]
659
738
  klass_ = Object.getPrototypeOf(klass_)
660
739
  } while (klass_ && klass_.name)
661
740
  // Filter out all duplicates
@@ -668,8 +747,8 @@ class VrpcAdapter {
668
747
 
669
748
  static _getInstancesArray (className) {
670
749
  const instances = []
671
- VrpcAdapter._instances.forEach((value, key) => {
672
- if (value.className === className) instances.push(key)
750
+ VrpcAdapter._instances.forEach((v, k) => {
751
+ if (v.className === className && !v.isIsolated) instances.push(k)
673
752
  })
674
753
  return instances
675
754
  }
@@ -693,10 +772,52 @@ class VrpcAdapter {
693
772
  }
694
773
  }
695
774
 
775
+ /**
776
+ * Event 'create'
777
+ *
778
+ * Emitted on creation of named instance
779
+ *
780
+ * @event VrpcAdapter#create
781
+ * @type {Object}
782
+ * @property {String} className The class name of the create instance
783
+ * @property {String} instance The instance name
784
+ * @property {Array.<Any>} args The constructor arguments
785
+ */
786
+
787
+ /**
788
+ * Event 'delete'
789
+ *
790
+ * Emitted on deletion of named instance
791
+ *
792
+ * @event VrpcAdapter#delete
793
+ * @type {Object}
794
+ * @property {String} className The class name of the deleted instance
795
+ * @property {String} instance The instance name
796
+ */
797
+
696
798
  // Initialize static members
697
799
  VrpcAdapter._functionRegistry = new Map()
698
800
  VrpcAdapter._instances = new Map()
801
+ VrpcAdapter._emitter = new EventEmitter()
699
802
  VrpcAdapter._correlationId = 0
700
803
  VrpcAdapter._listeners = {}
804
+ VrpcAdapter._blackList = new Set([
805
+ 'caller',
806
+ 'callee',
807
+ 'arguments',
808
+ 'apply',
809
+ 'bind',
810
+ 'call',
811
+ 'toString',
812
+ 'hasOwnProperty',
813
+ 'isPrototypeOf',
814
+ 'propertyIsEnumerable',
815
+ 'valueOf',
816
+ 'toLocaleString',
817
+ '__defineGetter__',
818
+ '__defineSetter__',
819
+ '__lookupGetter__',
820
+ '__lookupSetter__'
821
+ ])
701
822
 
702
823
  module.exports = VrpcAdapter