vrpc 3.1.1 → 3.2.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,3 +10,5 @@
10
10
  /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
11
11
 
12
12
  /*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
13
+
14
+ /** @license URI.js v4.4.1 (c) 2011 Gary Court. License: http://github.com/garycourt/uri-js */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vrpc",
3
- "version": "3.1.1",
3
+ "version": "3.2.1",
4
4
  "description": "Variadic Remote Procedure Calls",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -10,8 +10,8 @@
10
10
  },
11
11
  "scripts": {
12
12
  "build:addon": "ln -sf binding.gyp.no-auto binding.gyp && node-gyp rebuild && rm binding.gyp",
13
- "build:browser": "./node_modules/.bin/webpack",
14
- "build:doc": "./node_modules/.bin/jsdoc2md -f vrpc/*.js --param-list-format=list --separators > docs/api.md",
13
+ "build:browser": "./node_modules/.bin/webpack --stats-error-details",
14
+ "build:doc": "./node_modules/.bin/jsdoc2md -f vrpc/VrpcAdapter.js vrpc/VrpcAgent.js vrpc/VrpcClient.js vrpc/VrpcNative.js --param-list-format=list --separators > docs/api.md",
15
15
  "test": "npm run test:adapter && npm run test:native && npm run test:agent && npm run test:client && 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",
@@ -69,6 +69,7 @@
69
69
  "argparse": "^2.0.1",
70
70
  "buffer": "^6.0.3",
71
71
  "caller": "^1.0.1",
72
+ "json-stringify-safe": "^5.0.1",
72
73
  "mqtt": "^4.2.6",
73
74
  "nanoid": "^3.1.23",
74
75
  "process": "^0.11.10"
@@ -111,10 +111,8 @@ class VrpcAdapter {
111
111
  this._registerClass(Klass, absJsdocPath, options)
112
112
  } else {
113
113
  const { jsdocPath } = options
114
- const sJsdocPath =
115
- jsdocPath && jsdocPath.endsWith('.js') ? jsdocPath : `${jsdocPath}.js`
116
114
  if (jsdocPath) {
117
- const absJsdocPath = path.resolve(caller(), '../', sJsdocPath)
115
+ const absJsdocPath = path.resolve(caller(), '../', jsdocPath)
118
116
  this._registerClass(code, absJsdocPath, { ...options, jsdoc: true })
119
117
  } else {
120
118
  this._registerClass(code, null, options)
@@ -146,7 +144,7 @@ class VrpcAdapter {
146
144
  }
147
145
  let meta = null
148
146
  if (jsdocPath) {
149
- const absJsdocPath = path.resolve(caller(), '../', `${jsdocPath}.js`)
147
+ const absJsdocPath = path.resolve(caller(), '../', jsdocPath)
150
148
  const content = fs.readFileSync(absJsdocPath).toString('utf8')
151
149
  meta = this._parseComments(content)
152
150
  }
@@ -194,7 +192,10 @@ class VrpcAdapter {
194
192
  const obj = isIsolated
195
193
  ? VrpcAdapter._handleCreateIsolated(json)
196
194
  : VrpcAdapter._handleCreateShared(json)
197
- if (json.e) throw new Error(json.e)
195
+ if (json.e) {
196
+ const { message, cause } = json.e
197
+ throw new Error(message, { cause })
198
+ }
198
199
  return obj
199
200
  }
200
201
 
@@ -369,10 +370,7 @@ class VrpcAdapter {
369
370
  )
370
371
  if (ret.length === 1) {
371
372
  const { source, type } = ret[0]
372
- const description = source
373
- .split(' ')
374
- .splice(2)
375
- .join(' ')
373
+ const description = source.split(' ').splice(2).join(' ')
376
374
  ret = { description, type }
377
375
  } else {
378
376
  ret = null
@@ -441,7 +439,8 @@ class VrpcAdapter {
441
439
  })
442
440
  json.r = instanceId
443
441
  } catch (err) {
444
- json.e = err.message
442
+ const { message, cause } = err
443
+ json.e = { message, cause }
445
444
  }
446
445
  return instance
447
446
  }
@@ -465,7 +464,8 @@ class VrpcAdapter {
465
464
  instance: instanceId
466
465
  })
467
466
  } catch (err) {
468
- json.e = err.message
467
+ const { message, cause } = err
468
+ json.e = { message, cause }
469
469
  }
470
470
  return instance
471
471
  }
@@ -501,9 +501,15 @@ class VrpcAdapter {
501
501
  calls.push({ id, val: v, err: e })
502
502
  }
503
503
  }
504
- this._handlePromise(json, Promise.all(calls))
504
+ this._handlePromise(
505
+ json,
506
+ Promise.allSettled(calls).then(result =>
507
+ result.filter(x => x.status === 'fulfilled').map(x => x.value)
508
+ )
509
+ )
505
510
  } catch (err) {
506
- json.e = err.message
511
+ const { message, cause } = err
512
+ json.e = { message, cause }
507
513
  }
508
514
  }
509
515
 
@@ -519,13 +525,18 @@ class VrpcAdapter {
519
525
  static _handleDelete (json) {
520
526
  try {
521
527
  const instance = json.a[0] // first arg is instance to be deleted
528
+ VrpcAdapter._emitter.emit('beforeDelete', {
529
+ instance,
530
+ className: json.c
531
+ })
522
532
  json.r = VrpcAdapter._delete(instance)
523
533
  VrpcAdapter._emitter.emit('delete', {
524
534
  instance,
525
535
  className: json.c
526
536
  })
527
537
  } catch (err) {
528
- json.e = err.message
538
+ const { message, cause } = err
539
+ json.e = { message, cause }
529
540
  }
530
541
  }
531
542
 
@@ -559,7 +570,8 @@ class VrpcAdapter {
559
570
  this._handlePromise(json, ret)
560
571
  } else json.r = ret
561
572
  } catch (err) {
562
- json.e = err.message
573
+ const { message, cause } = err
574
+ json.e = { message, cause }
563
575
  }
564
576
  } else throw new Error(`Could not find function: ${json.f}`)
565
577
  } else {
@@ -578,7 +590,8 @@ class VrpcAdapter {
578
590
  this._handlePromise(json, ret)
579
591
  } else json.r = ret
580
592
  } catch (err) {
581
- json.e = err.message
593
+ const { message, cause } = err
594
+ json.e = { message, cause }
582
595
  }
583
596
  } else throw new Error(`Could not find function: ${json.f}`)
584
597
  }
@@ -613,9 +626,10 @@ class VrpcAdapter {
613
626
  Object.entries(VrpcAdapter._listeners).forEach(([ik, iv]) => {
614
627
  Object.entries(iv).forEach(([ek, ev]) => {
615
628
  const { clients, listener, event } = ev
616
- if (clients.has(clientId)) {
617
- clients.delete(clientId)
618
- if (clients.size === 0) {
629
+ const index = clients.indexOf(clientId)
630
+ if (index !== -1) {
631
+ clients.splice(index, 1)
632
+ if (clients.length === 0) {
619
633
  const instance = VrpcAdapter.getInstance(ik)
620
634
  if (instance && instance.removeListener) {
621
635
  instance.removeListener(event, listener)
@@ -667,11 +681,7 @@ class VrpcAdapter {
667
681
  eventId: arg,
668
682
  event: args[0]
669
683
  })
670
- if (
671
- VrpcAdapter.getInstance(context)
672
- .listeners(arg)
673
- .includes(listener)
674
- ) {
684
+ if (!listener) {
675
685
  return null // skip call as listener is already registered
676
686
  }
677
687
  unwrapped.push(listener)
@@ -727,7 +737,7 @@ class VrpcAdapter {
727
737
  VrpcAdapter._listeners[instanceId] = {
728
738
  [eventId]: {
729
739
  event,
730
- clients: new Set([clientId]),
740
+ clients: [clientId],
731
741
  listener: VrpcAdapter._generateListener({
732
742
  eventId,
733
743
  instanceId,
@@ -735,20 +745,22 @@ class VrpcAdapter {
735
745
  })
736
746
  }
737
747
  }
738
- } else if (!VrpcAdapter._listeners[instanceId][eventId]) {
748
+ return VrpcAdapter._listeners[instanceId][eventId].listener
749
+ }
750
+ if (!VrpcAdapter._listeners[instanceId][eventId]) {
739
751
  VrpcAdapter._listeners[instanceId][eventId] = {
740
752
  event,
741
- clients: new Set([clientId]),
753
+ clients: [clientId],
742
754
  listener: VrpcAdapter._generateListener({
743
755
  eventId,
744
756
  instanceId,
745
757
  isCallAll
746
758
  })
747
759
  }
748
- } else {
749
- VrpcAdapter._listeners[instanceId][eventId].clients.add(clientId)
760
+ return VrpcAdapter._listeners[instanceId][eventId].listener
750
761
  }
751
- return VrpcAdapter._listeners[instanceId][eventId].listener
762
+ VrpcAdapter._listeners[instanceId][eventId].clients.push(clientId)
763
+ return null
752
764
  }
753
765
 
754
766
  static _generateListener ({ eventId, instanceId, isCallAll }) {
@@ -764,8 +776,9 @@ class VrpcAdapter {
764
776
  VrpcAdapter._listeners[instanceId][eventId]
765
777
  ) {
766
778
  const { listener, clients } = VrpcAdapter._listeners[instanceId][eventId]
767
- clients.delete(clientId)
768
- if (clients.size === 0) {
779
+ const index = clients.indexOf(clientId)
780
+ if (index !== -1) clients.splice(index, 1)
781
+ if (clients.length === 0) {
769
782
  delete VrpcAdapter._listeners[instanceId][eventId]
770
783
  if (Object.keys(VrpcAdapter._listeners[instanceId]).length === 0) {
771
784
  delete VrpcAdapter._listeners[instanceId]
@@ -779,9 +792,9 @@ class VrpcAdapter {
779
792
  const eventIds = VrpcAdapter._listeners[instanceId]
780
793
  if (!eventIds) return
781
794
  Object.entries(eventIds).forEach(([ek, ev]) => {
782
- if (ev.event === event && ev.clients.has(clientId)) {
783
- ev.clients.delete(clientId)
784
- if (ev.clients.size === 0) {
795
+ if (ev.event === event && ev.clients.includes(clientId)) {
796
+ ev.clients = ev.clients.filter(x => x !== clientId)
797
+ if (ev.clients.length === 0) {
785
798
  VrpcAdapter.getInstance(instanceId).removeListener(
786
799
  ev.event,
787
800
  ev.listener
@@ -799,17 +812,21 @@ class VrpcAdapter {
799
812
  json.r = id
800
813
  promise
801
814
  .then(r => VrpcAdapter._callback({ ...json, r, i: id }))
802
- .catch(err => VrpcAdapter._callback({ ...json, e: err.message, i: id }))
815
+ .catch(err =>
816
+ VrpcAdapter._callback({
817
+ ...json,
818
+ e: { message: err.message, cause: err.cause },
819
+ i: id
820
+ })
821
+ )
803
822
  } catch (err) {
804
- json.data.e = err.message
823
+ const { message, cause } = err
824
+ json.data.e = { message, cause }
805
825
  }
806
826
  }
807
827
 
808
828
  static _generateId (object) {
809
- return crypto
810
- .createHash('md5')
811
- .update(JSON.stringify(object))
812
- .digest('hex')
829
+ return crypto.createHash('md5').update(JSON.stringify(object)).digest('hex')
813
830
  }
814
831
 
815
832
  static _getClassEntry (className) {
@@ -930,6 +947,7 @@ class VrpcAdapter {
930
947
  *
931
948
  * Emitted on deletion of shared instance
932
949
  *
950
+ * @event VrpcAdapter#beforeDelete
933
951
  * @event VrpcAdapter#delete
934
952
  * @type {Object}
935
953
  * @property {String} className The class name of the deleted instance
package/vrpc/VrpcAgent.js CHANGED
@@ -3,8 +3,9 @@ const path = require('path')
3
3
  const mqtt = require('mqtt')
4
4
  const crypto = require('crypto')
5
5
  const { ArgumentParser } = require('argparse')
6
- const VrpcAdapter = require('./VrpcAdapter')
7
6
  const EventEmitter = require('events')
7
+ const jsonStringifySafe = require('json-stringify-safe')
8
+ const VrpcAdapter = require('./VrpcAdapter')
8
9
 
9
10
  const VRPC_PROTOCOL_VERSION = 3
10
11
 
@@ -67,13 +68,11 @@ class VrpcAgent extends EventEmitter {
67
68
  default: password
68
69
  })
69
70
  parser.add_argument('--bestEffort', {
70
- help:
71
- 'Calls function on best-effort. Improves performance but may fail under unstable network connections.',
71
+ help: 'Calls function on best-effort. Improves performance but may fail under unstable network connections.',
72
72
  action: 'store_true'
73
73
  })
74
74
  parser.add_argument('-V', '-v', '--userVersion', {
75
- help:
76
- 'User defined agent version. May be checked on the remote side for compatibility checks.',
75
+ help: 'User defined agent version. May be checked on the remote side for compatibility checks.',
77
76
  required: false,
78
77
  default: version,
79
78
  dest: 'version'
@@ -99,7 +98,7 @@ class VrpcAgent extends EventEmitter {
99
98
  * @param {String} [obj.agent='<user>-<pathId>@<hostname>-<platform>-js'] This agent's name
100
99
  * @param {String} [obj.broker='mqtts://vrpc.io:8883'] Broker url in form: `<scheme>://<host>:<port>`
101
100
  * @param {Object} [obj.log=console] Log object (must support debug, info, warn, and error level)
102
- * @param {String} [obj.bestEffort=false] If true, message will be sent with best effort, i.e. no caching if offline
101
+ * @param {String} [obj.bestEffort=true] If true, message will be sent with best effort, i.e. no caching if offline
103
102
  * @param {String} [obj.version=''] The (user-defined) version of this agent
104
103
  * @param {String} [obj.mqttClientId='<generated()>'] Explicitly set the mqtt client id.
105
104
  *
@@ -117,7 +116,7 @@ class VrpcAgent extends EventEmitter {
117
116
  agent = VrpcAgent._generateAgentName(),
118
117
  broker = 'mqtts://vrpc.io:8883',
119
118
  log = 'console',
120
- bestEffort = false,
119
+ bestEffort = true,
121
120
  version = '',
122
121
  mqttClientId = null
123
122
  } = {}) {
@@ -244,6 +243,31 @@ class VrpcAgent extends EventEmitter {
244
243
  }
245
244
  }
246
245
 
246
+ /**
247
+ * Creates a new instance locally
248
+ *
249
+ * NOTE: The instance must previously be registered by the local VrpcAdapter
250
+ *
251
+ * @param {Object} options
252
+ * @param {String} options.className Name of the class which should be
253
+ * instantiated
254
+ * @param {String} [options.instance] Name of the created instance. If not
255
+ * provided an id will be generated
256
+ * @param {Array} [options.args] Positional arguments for the constructor call
257
+ * @param {bool} [options.isIsolated=false] If true the created instance will
258
+ * be visible only to the client who created it
259
+ * @returns {Object} The real instance (not a proxy!)
260
+ */
261
+ create ({ className, instance = nanoid(8), args = [], isIsolated = false }) {
262
+ const obj = VrpcAdapter.create({ className, instance, args, isIsolated })
263
+ if (!this._hasSharedInstance(instance)) {
264
+ this._subscribeToMethodsOfNewInstance(className, instance)
265
+ this._publishClassInfoMessage(className)
266
+ this._publishClassInfoConciseMessage(className)
267
+ }
268
+ return obj
269
+ }
270
+
247
271
  static _generateAgentName () {
248
272
  const { username } = os.userInfo()
249
273
  const pathId = crypto
@@ -269,10 +293,7 @@ class VrpcAgent extends EventEmitter {
269
293
  os.type() +
270
294
  JSON.stringify(os.networkInterfaces()) +
271
295
  JSON.stringify(os.cpus().map(({ model }) => model))
272
- return crypto
273
- .createHash('md5')
274
- .update(uid)
275
- .digest('hex')
296
+ return crypto.createHash('md5').update(uid).digest('hex')
276
297
  }
277
298
 
278
299
  _validateDomain (domain) {
@@ -427,9 +448,9 @@ class VrpcAgent extends EventEmitter {
427
448
  let str
428
449
  const { c, f } = json
429
450
  try {
430
- str = JSON.stringify(json)
451
+ str = jsonStringifySafe(json)
431
452
  } catch (err) {
432
- this._log.debug(
453
+ console.error(
433
454
  `Failed serialization of return value for: ${c}::${f}, because: ${err.message}`
434
455
  )
435
456
  json.r = '__vrpc::not-serializable__'
@@ -456,6 +477,7 @@ class VrpcAgent extends EventEmitter {
456
477
  const classes = this._getClasses()
457
478
  classes.forEach(className => {
458
479
  this._publishClassInfoMessage(className)
480
+ this._publishClassInfoConciseMessage(className)
459
481
  })
460
482
  // Register all pre-existing instances
461
483
  for (const [
@@ -477,7 +499,7 @@ class VrpcAgent extends EventEmitter {
477
499
 
478
500
  _publishClassInfoMessage (className) {
479
501
  const json = {
480
- className: className,
502
+ className,
481
503
  instances: this._getInstances(className),
482
504
  memberFunctions: this._getMemberFunctions(className),
483
505
  staticFunctions: this._getStaticFunctions(className),
@@ -490,6 +512,25 @@ class VrpcAgent extends EventEmitter {
490
512
  JSON.stringify(json),
491
513
  { retain: true }
492
514
  )
515
+ } catch (err) {
516
+ this._log.error(err, `Problem during publishing schema: ${err.message}`)
517
+ }
518
+ }
519
+
520
+ _publishClassInfoConciseMessage (className) {
521
+ const json = {
522
+ className,
523
+ instances: this._getInstances(className),
524
+ memberFunctions: this._getMemberFunctions(className),
525
+ staticFunctions: this._getStaticFunctions(className),
526
+ v: VRPC_PROTOCOL_VERSION
527
+ }
528
+ try {
529
+ this._mqttPublish(
530
+ `${this._baseTopic}/${className}/__classInfoConcise__`,
531
+ JSON.stringify(json),
532
+ { retain: true }
533
+ )
493
534
  } catch (err) {
494
535
  this._log.error(
495
536
  err,
@@ -563,6 +604,7 @@ class VrpcAgent extends EventEmitter {
563
604
  if (!this._hasSharedInstance(instanceId)) {
564
605
  this._subscribeToMethodsOfNewInstance(className, instanceId)
565
606
  this._publishClassInfoMessage(className)
607
+ this._publishClassInfoConciseMessage(className)
566
608
  }
567
609
  this._registerSharedInstance(instanceId, json.s)
568
610
  break
@@ -570,7 +612,10 @@ class VrpcAgent extends EventEmitter {
570
612
  case '__delete__': {
571
613
  this._unsubscribeMethodsOfDeletedInstance(className, instance)
572
614
  const wasShared = this._unregisterInstance(json.a[0], json.s)
573
- if (wasShared) this._publishClassInfoMessage(className)
615
+ if (wasShared) {
616
+ this._publishClassInfoMessage(className)
617
+ this._publishClassInfoConciseMessage(className)
618
+ }
574
619
  break
575
620
  }
576
621
  }
@@ -666,10 +711,10 @@ class VrpcAgent extends EventEmitter {
666
711
  }
667
712
  })
668
713
  if (!found) {
669
- this._log.warn(
670
- `Failed un-registering not registered instance: ${instanceId}`
714
+ this._log.info(
715
+ `Unregistering non-tracked (possibly by agent created) instance: ${instanceId}`
671
716
  )
672
- return false
717
+ return true
673
718
  }
674
719
  return true
675
720
  }