vrpc 3.14.0 → 3.16.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/LICENSE +1 -1
- package/README.md +100 -77
- package/browser/vrpc.js +1 -7
- package/index.js +7 -7
- package/package.json +23 -15
- package/vrpc/Registrations.js +850 -0
- package/vrpc/SubscribeRetrier.js +33 -0
- package/vrpc/VrpcAdapter.js +556 -294
- package/vrpc/VrpcAgent.js +230 -153
- package/vrpc/VrpcBrowserAdapter.js +435 -286
- package/vrpc/VrpcBrowserAgent.js +239 -128
- package/vrpc/VrpcClient.js +1190 -224
- package/vrpc/VrpcNative.js +10 -5
- package/vrpc/VrpcPersistor.js +1 -1
- package/vrpc/adapter.hpp +1 -1
- package/vrpc/addon.cpp +1 -1
- package/vrpc/comment-parser/parser.js +23 -0
- package/vrpc/namePattern.js +86 -0
- package/vrpc/vrpc-agent-js +3 -11
|
@@ -14,7 +14,7 @@ procedure calls (RPC).
|
|
|
14
14
|
Author: Dr. Burkhard C. Heisen (https://github.com/heisenware/vrpc)
|
|
15
15
|
|
|
16
16
|
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
|
17
|
-
Copyright (c) 2018
|
|
17
|
+
Copyright (c) 2018 Dr. Burkhard C. Heisen <burkhard.heisen@heisenware.com>.
|
|
18
18
|
|
|
19
19
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
20
20
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -38,6 +38,8 @@ SOFTWARE.
|
|
|
38
38
|
const Ajv = require('ajv')
|
|
39
39
|
const EventEmitter = require('events')
|
|
40
40
|
const { nanoid } = require('nanoid')
|
|
41
|
+
const Registrations = require('./Registrations')
|
|
42
|
+
const NamePattern = require('./namePattern')
|
|
41
43
|
|
|
42
44
|
// A runtime without dispose symbols (older browsers, Node before 18.18 /
|
|
43
45
|
// 20.4) gets Node's own stand-ins from the symbol registry, so a class file
|
|
@@ -67,7 +69,9 @@ class VrpcAdapter {
|
|
|
67
69
|
* @param {Boolean} [options.withNew=true] If true, class will be constructed
|
|
68
70
|
* using the `new` operator
|
|
69
71
|
* @param {Object} [options.schema=null] If provided is used to validate ctor
|
|
70
|
-
* parameters (only works if registered code reflects a single class)
|
|
72
|
+
* parameters (only works if registered code reflects a single class). The
|
|
73
|
+
* browser bundle (`browser/vrpc.js`) does not validate: it warns once and
|
|
74
|
+
* constructs with the arguments as given
|
|
71
75
|
*/
|
|
72
76
|
static registerClass (
|
|
73
77
|
Klass,
|
|
@@ -82,6 +86,7 @@ class VrpcAdapter {
|
|
|
82
86
|
staticFunctions.push('__createIsolated__')
|
|
83
87
|
staticFunctions.push('__createShared__')
|
|
84
88
|
staticFunctions.push('__callAll__')
|
|
89
|
+
staticFunctions.push('__callMatching__')
|
|
85
90
|
staticFunctions.push('__delete__')
|
|
86
91
|
let memberFunctions = VrpcAdapter._extractMemberFunctions(Klass)
|
|
87
92
|
if (onlyPublic) {
|
|
@@ -303,8 +308,35 @@ class VrpcAdapter {
|
|
|
303
308
|
|
|
304
309
|
// private:
|
|
305
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Handles one request in place: the answer lands on `json` as `r` or `e`.
|
|
313
|
+
* Every request is answered - a malformed one, an unknown context or
|
|
314
|
+
* function with an error - so no caller waits for its timeout.
|
|
315
|
+
*
|
|
316
|
+
* @returns {Boolean} Whether the call took subscriptions of its sender,
|
|
317
|
+
* whose presence the agent then watches
|
|
318
|
+
*/
|
|
306
319
|
static _call (json) {
|
|
307
320
|
VrpcAdapter._mustTrackClient = false // reset client tracking flag
|
|
321
|
+
// the answer is what the call yields, never what the request brought
|
|
322
|
+
delete json.r
|
|
323
|
+
delete json.e
|
|
324
|
+
try {
|
|
325
|
+
// a request without arguments has none
|
|
326
|
+
if (json.a === undefined) json.a = []
|
|
327
|
+
if (!Array.isArray(json.a)) {
|
|
328
|
+
throw new Error('The arguments of a request must be an array')
|
|
329
|
+
}
|
|
330
|
+
VrpcAdapter._dispatch(json)
|
|
331
|
+
} catch (err) {
|
|
332
|
+
json.e = VrpcAdapter._error(err)
|
|
333
|
+
}
|
|
334
|
+
// if we took a listener on board we must tell our caller such that
|
|
335
|
+
// things can be cleaned up later.
|
|
336
|
+
return VrpcAdapter._mustTrackClient
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
static _dispatch (json) {
|
|
308
340
|
switch (json.f) {
|
|
309
341
|
case '__createIsolated__':
|
|
310
342
|
VrpcAdapter._handleCreateIsolated(json)
|
|
@@ -315,16 +347,49 @@ class VrpcAdapter {
|
|
|
315
347
|
case '__callAll__':
|
|
316
348
|
VrpcAdapter._handleCallAll(json)
|
|
317
349
|
break
|
|
350
|
+
case '__callMatching__':
|
|
351
|
+
VrpcAdapter._handleCallMatching(json)
|
|
352
|
+
break
|
|
318
353
|
case '__delete__':
|
|
319
354
|
VrpcAdapter._handleDelete(json)
|
|
320
355
|
break
|
|
356
|
+
case '__unsubscribe__':
|
|
357
|
+
VrpcAdapter._handleUnsubscribe(json)
|
|
358
|
+
break
|
|
359
|
+
case '__registrations__':
|
|
360
|
+
VrpcAdapter._handleRegistrations(json)
|
|
361
|
+
break
|
|
362
|
+
case '__end__':
|
|
363
|
+
VrpcAdapter._handleEnd(json)
|
|
364
|
+
break
|
|
321
365
|
default:
|
|
322
366
|
// this call may have the side-effect of taking a listener on board
|
|
323
367
|
VrpcAdapter._handleCall(json)
|
|
324
368
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// an error as it travels: `{ message, cause? }`, whatever was thrown
|
|
372
|
+
static _error (err) {
|
|
373
|
+
if (err && typeof err === 'object' && 'message' in err) {
|
|
374
|
+
const e = { message: String(err.message) }
|
|
375
|
+
if (err.cause !== undefined) e.cause = err.cause
|
|
376
|
+
return e
|
|
377
|
+
}
|
|
378
|
+
return { message: String(err) }
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Whether a request may call a function: only what the class info lists
|
|
383
|
+
* is reachable - a member function of the instance's class, a static
|
|
384
|
+
* function of the class, or a registered global function. Anything else
|
|
385
|
+
* an object happens to have (an EventEmitter's `emit`, a private helper)
|
|
386
|
+
* stays inside the agent.
|
|
387
|
+
*/
|
|
388
|
+
static _isPublished (className, func, isStatic) {
|
|
389
|
+
const entry = VrpcAdapter._functionRegistry.get(className)
|
|
390
|
+
if (!entry) return false
|
|
391
|
+
const published = isStatic ? entry.staticFunctions : entry.memberFunctions
|
|
392
|
+
return published.includes(func)
|
|
328
393
|
}
|
|
329
394
|
|
|
330
395
|
/**
|
|
@@ -343,11 +408,28 @@ class VrpcAdapter {
|
|
|
343
408
|
)
|
|
344
409
|
}
|
|
345
410
|
|
|
411
|
+
/**
|
|
412
|
+
* An instance name is one topic level of its own: a non-empty string
|
|
413
|
+
* without `/`, `+`, `#` or `*` (which would read as a pattern), and never
|
|
414
|
+
* `__static__`, which stands for the class in a request topic.
|
|
415
|
+
*/
|
|
416
|
+
static _assertInstanceName (name) {
|
|
417
|
+
if (
|
|
418
|
+
typeof name !== 'string' ||
|
|
419
|
+
name.length === 0 ||
|
|
420
|
+
/[/+#*]/.test(name) ||
|
|
421
|
+
name === '__static__'
|
|
422
|
+
) {
|
|
423
|
+
throw new Error(`Invalid instance name: ${JSON.stringify(name)}`)
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
346
427
|
static _handleCreateIsolated (json) {
|
|
347
428
|
let instance
|
|
348
429
|
try {
|
|
349
430
|
const className = json.c
|
|
350
431
|
const [instanceId, ...args] = json.a
|
|
432
|
+
VrpcAdapter._assertInstanceName(instanceId)
|
|
351
433
|
const existing = VrpcAdapter._instances.get(instanceId)
|
|
352
434
|
if (existing) {
|
|
353
435
|
// an id is never re-homed: a shared instance stays shared, an
|
|
@@ -375,7 +457,7 @@ class VrpcAdapter {
|
|
|
375
457
|
})
|
|
376
458
|
json.r = instanceId
|
|
377
459
|
} catch (err) {
|
|
378
|
-
json.e = err
|
|
460
|
+
json.e = VrpcAdapter._error(err)
|
|
379
461
|
}
|
|
380
462
|
return instance
|
|
381
463
|
}
|
|
@@ -385,6 +467,7 @@ class VrpcAdapter {
|
|
|
385
467
|
try {
|
|
386
468
|
const className = json.c
|
|
387
469
|
const [instanceId, ...args] = json.a
|
|
470
|
+
VrpcAdapter._assertInstanceName(instanceId)
|
|
388
471
|
const existing = VrpcAdapter._instances.get(instanceId)
|
|
389
472
|
if (existing && existing.isIsolated) {
|
|
390
473
|
throw new Error(`Instance '${instanceId}' exists as an isolated instance`)
|
|
@@ -403,66 +486,203 @@ class VrpcAdapter {
|
|
|
403
486
|
instance: instanceId
|
|
404
487
|
})
|
|
405
488
|
} catch (err) {
|
|
406
|
-
json.e = err
|
|
489
|
+
json.e = VrpcAdapter._error(err)
|
|
407
490
|
}
|
|
408
491
|
return instance
|
|
409
492
|
}
|
|
410
493
|
|
|
494
|
+
// `a` = [function, ...arguments]: every shared instance of the class (ALL-1)
|
|
411
495
|
static _handleCallAll (json) {
|
|
496
|
+
const [func, ...args] = json.a
|
|
497
|
+
VrpcAdapter._callAll(json, func, args, () => true)
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// `a` = [selection, function, ...arguments]: the shared instances whose
|
|
501
|
+
// name the selection matches, a pattern or a list of them (ALL-4, ALL-5)
|
|
502
|
+
static _handleCallMatching (json) {
|
|
503
|
+
const [selection, func, ...args] = json.a
|
|
504
|
+
if (!NamePattern.isSelection(selection)) {
|
|
505
|
+
json.e = {
|
|
506
|
+
message: `Invalid selection: ${JSON.stringify(selection)} (a pattern or a list of them)`
|
|
507
|
+
}
|
|
508
|
+
return
|
|
509
|
+
}
|
|
510
|
+
VrpcAdapter._callAll(json, func, args, id =>
|
|
511
|
+
NamePattern.selects(selection, id)
|
|
512
|
+
)
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// calls a function on the shared instances of a class `chosen` takes, and
|
|
516
|
+
// answers one entry per instance (ALL-2)
|
|
517
|
+
static _callAll (json, func, args, chosen) {
|
|
412
518
|
try {
|
|
413
519
|
const calls = []
|
|
520
|
+
const subscribes = Registrations.carriesEventIds(args)
|
|
521
|
+
// a remover carrying ids never reaches the class (see Registrations)
|
|
522
|
+
const withdraws = subscribes && Registrations.isRemover(func, json.v)
|
|
523
|
+
if (!withdraws && !VrpcAdapter._isPublished(json.c, func, false)) {
|
|
524
|
+
throw new Error(`Could not find function: ${func}`)
|
|
525
|
+
}
|
|
526
|
+
// what the instance's callbacks unwrap from: the function and its
|
|
527
|
+
// arguments, as a __callAll__ request carries them
|
|
528
|
+
const asCallAll = { ...json, a: [func, ...args] }
|
|
414
529
|
for (const [
|
|
415
530
|
id,
|
|
416
531
|
{ className, instance, isIsolated }
|
|
417
532
|
] of VrpcAdapter._instances) {
|
|
418
|
-
//
|
|
419
|
-
if (className !== json.c || isIsolated) continue
|
|
420
|
-
|
|
421
|
-
|
|
533
|
+
// isolated instances never take part
|
|
534
|
+
if (className !== json.c || isIsolated || !chosen(id)) continue
|
|
535
|
+
// one answer per instance: `{ id, val }`, or `{ id, err }` with the
|
|
536
|
+
// error as it travels
|
|
422
537
|
try {
|
|
423
|
-
|
|
424
|
-
if (
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
538
|
+
let val
|
|
539
|
+
if (subscribes) {
|
|
540
|
+
val = VrpcAdapter._subscribe(json, {
|
|
541
|
+
contextKey: id,
|
|
542
|
+
contextName: id,
|
|
543
|
+
target: instance,
|
|
544
|
+
func,
|
|
545
|
+
args,
|
|
546
|
+
isCallAll: true
|
|
547
|
+
}).value
|
|
548
|
+
} else {
|
|
549
|
+
const unwrapped = VrpcAdapter._unwrapArguments(asCallAll, id)
|
|
550
|
+
val = instance[func].apply(instance, unwrapped.slice(1))
|
|
551
|
+
val = VrpcAdapter.sanitizeEventListenerReturnValues(val)
|
|
428
552
|
}
|
|
429
|
-
} catch (err) {
|
|
430
|
-
e = err.message
|
|
431
|
-
}
|
|
432
|
-
if (VrpcAdapter._isPromise(v)) {
|
|
433
553
|
calls.push(
|
|
434
|
-
|
|
435
|
-
.then(
|
|
436
|
-
|
|
554
|
+
VrpcAdapter._isPromise(val)
|
|
555
|
+
? val.then(
|
|
556
|
+
x => ({ id, val: x }),
|
|
557
|
+
err => ({ id, err: VrpcAdapter._error(err) })
|
|
558
|
+
)
|
|
559
|
+
: { id, val }
|
|
437
560
|
)
|
|
438
|
-
}
|
|
439
|
-
calls.push({ id,
|
|
561
|
+
} catch (err) {
|
|
562
|
+
calls.push({ id, err: VrpcAdapter._error(err) })
|
|
440
563
|
}
|
|
441
564
|
}
|
|
442
|
-
|
|
565
|
+
VrpcAdapter._handlePromise(
|
|
443
566
|
json,
|
|
444
567
|
Promise.allSettled(calls).then(result =>
|
|
445
568
|
result.filter(x => x.status === 'fulfilled').map(x => x.value)
|
|
446
569
|
)
|
|
447
570
|
)
|
|
448
571
|
} catch (err) {
|
|
449
|
-
json.e = err
|
|
572
|
+
json.e = VrpcAdapter._error(err)
|
|
450
573
|
}
|
|
451
574
|
}
|
|
452
575
|
|
|
453
576
|
static sanitizeEventListenerReturnValues (ret) {
|
|
454
|
-
return ret
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
577
|
+
return Registrations.sanitize(ret)
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* The book key of a request's context: an instance id, or the class for
|
|
582
|
+
* static functions (kept apart from an instance of the same name).
|
|
583
|
+
*/
|
|
584
|
+
static _contextKey (context, isStatic) {
|
|
585
|
+
return isStatic ? `\u0000${context}` : context
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// a class name addresses the class, as in _handleCall
|
|
589
|
+
static _isStaticContext (context) {
|
|
590
|
+
return VrpcAdapter._functionRegistry.has(context)
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* A call carrying subscription ids: one registration per context,
|
|
595
|
+
* function and arguments, shared by every subscription (see
|
|
596
|
+
* Registrations).
|
|
597
|
+
*/
|
|
598
|
+
static _subscribe (
|
|
599
|
+
json,
|
|
600
|
+
{ contextKey, contextName, target, func, args, isCallAll = false }
|
|
601
|
+
) {
|
|
602
|
+
const result = VrpcAdapter._registrations.call({
|
|
603
|
+
json,
|
|
604
|
+
contextKey,
|
|
605
|
+
contextName,
|
|
606
|
+
target,
|
|
607
|
+
func,
|
|
608
|
+
args,
|
|
609
|
+
isCallAll,
|
|
610
|
+
speaks: VrpcAdapter._speaksFor(contextName),
|
|
611
|
+
makeCallback: eventId =>
|
|
612
|
+
VrpcAdapter._generateCallback({
|
|
613
|
+
clientId: json.s,
|
|
614
|
+
isCallAll,
|
|
615
|
+
instanceId: contextName,
|
|
616
|
+
eventId,
|
|
617
|
+
receiverVersion: json.v
|
|
618
|
+
})
|
|
619
|
+
})
|
|
620
|
+
// a subscriber is tracked: its going withdraws what it holds
|
|
621
|
+
if (result.ids.length > 0) VrpcAdapter._mustTrackClient = true
|
|
622
|
+
return result
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* `__unsubscribe__ [id, ...]`: withdraws the sender's subscriptions
|
|
627
|
+
* wherever they are held; an entry `{ id, context }` withdraws that id
|
|
628
|
+
* on that instance only (one instance of a callAll subscription).
|
|
629
|
+
* Answers true.
|
|
630
|
+
*/
|
|
631
|
+
static _handleUnsubscribe (json) {
|
|
632
|
+
VrpcAdapter._registrations.unsubscribe(json.a || [], json.s)
|
|
633
|
+
json.r = true
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* `__registrations__`: lists the registrations of an instance or (on the
|
|
638
|
+
* static topic) a class.
|
|
639
|
+
*/
|
|
640
|
+
static _handleRegistrations (json) {
|
|
641
|
+
try {
|
|
642
|
+
const isStatic = VrpcAdapter._isStaticContext(json.c)
|
|
643
|
+
if (!isStatic) {
|
|
644
|
+
VrpcAdapter._assertOwner(VrpcAdapter._instances.get(json.c), json, json.c)
|
|
645
|
+
}
|
|
646
|
+
json.r = VrpcAdapter._registrations.list(
|
|
647
|
+
VrpcAdapter._contextKey(json.c, isStatic)
|
|
648
|
+
)
|
|
649
|
+
} catch (err) {
|
|
650
|
+
json.e = VrpcAdapter._error(err)
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* `__end__ [function, args, by]`: ends one registration for all its
|
|
656
|
+
* subscriptions, which are told who ended it. Answers whether it existed.
|
|
657
|
+
*/
|
|
658
|
+
static _handleEnd (json) {
|
|
659
|
+
try {
|
|
660
|
+
const isStatic = VrpcAdapter._isStaticContext(json.c)
|
|
661
|
+
if (!isStatic) {
|
|
662
|
+
VrpcAdapter._assertOwner(VrpcAdapter._instances.get(json.c), json, json.c)
|
|
663
|
+
}
|
|
664
|
+
const [func, args = [], by = null] = json.a || []
|
|
665
|
+
json.r = VrpcAdapter._registrations.end(
|
|
666
|
+
VrpcAdapter._contextKey(json.c, isStatic),
|
|
667
|
+
func,
|
|
668
|
+
args,
|
|
669
|
+
by
|
|
670
|
+
)
|
|
671
|
+
} catch (err) {
|
|
672
|
+
json.e = VrpcAdapter._error(err)
|
|
673
|
+
}
|
|
460
674
|
}
|
|
461
675
|
|
|
462
676
|
static _handleDelete (json) {
|
|
463
677
|
try {
|
|
464
678
|
const instance = json.a[0] // first arg is instance to be deleted
|
|
465
|
-
VrpcAdapter.
|
|
679
|
+
const entry = VrpcAdapter._instances.get(instance)
|
|
680
|
+
// a delete names the instance's own class: the class info of that
|
|
681
|
+
// class is what announces it gone
|
|
682
|
+
if (entry && entry.className !== json.c && json.s !== VrpcAdapter.LOCAL_SENDER) {
|
|
683
|
+
throw new Error(`Instance '${instance}' is not of class '${json.c}'`)
|
|
684
|
+
}
|
|
685
|
+
VrpcAdapter._assertOwner(entry, json, instance)
|
|
466
686
|
VrpcAdapter._emitter.emit('beforeDelete', {
|
|
467
687
|
instance,
|
|
468
688
|
className: json.c
|
|
@@ -479,7 +699,7 @@ class VrpcAdapter {
|
|
|
479
699
|
VrpcAdapter._handlePromise(json, disposed.then(() => ended))
|
|
480
700
|
} else json.r = ended
|
|
481
701
|
} catch (err) {
|
|
482
|
-
json.e = err
|
|
702
|
+
json.e = VrpcAdapter._error(err)
|
|
483
703
|
}
|
|
484
704
|
}
|
|
485
705
|
|
|
@@ -488,13 +708,19 @@ class VrpcAdapter {
|
|
|
488
708
|
try {
|
|
489
709
|
VrpcAdapter._assertOwner(VrpcAdapter._instances.get(json.c), json, json.c)
|
|
490
710
|
} catch (err) {
|
|
491
|
-
|
|
492
|
-
json.e = { message, cause }
|
|
711
|
+
json.e = VrpcAdapter._error(err)
|
|
493
712
|
return
|
|
494
713
|
}
|
|
495
|
-
// special case: removeAllListeners
|
|
714
|
+
// special case: removeAllListeners withdraws the caller's subscriptions
|
|
715
|
+
// of that event - the emitter itself is never touched, it holds other
|
|
716
|
+
// clients' and its own listeners
|
|
496
717
|
if (json.f === 'removeAllListeners') {
|
|
497
|
-
VrpcAdapter.
|
|
718
|
+
const isStatic = VrpcAdapter._isStaticContext(json.c)
|
|
719
|
+
VrpcAdapter._registrations.withdrawEvent(
|
|
720
|
+
VrpcAdapter._contextKey(json.c, isStatic),
|
|
721
|
+
json.a[0],
|
|
722
|
+
json.s
|
|
723
|
+
)
|
|
498
724
|
json.r = true
|
|
499
725
|
return
|
|
500
726
|
}
|
|
@@ -507,66 +733,93 @@ class VrpcAdapter {
|
|
|
507
733
|
) {
|
|
508
734
|
throw new Error(`Could not find context: ${json.c}`)
|
|
509
735
|
}
|
|
510
|
-
const unwrapped = VrpcAdapter._unwrapArguments(json)
|
|
511
|
-
if (!unwrapped) {
|
|
512
|
-
// this call must be skipped (event emitter stuff)
|
|
513
|
-
json.r = true
|
|
514
|
-
return
|
|
515
|
-
}
|
|
516
|
-
|
|
517
736
|
// Check whether context is global
|
|
518
737
|
if (json.c === VRPC_GLOBAL) {
|
|
738
|
+
if (!VrpcAdapter._isPublished(VRPC_GLOBAL, json.f, true)) {
|
|
739
|
+
throw new Error(`Could not find function: ${json.f}`)
|
|
740
|
+
}
|
|
519
741
|
try {
|
|
742
|
+
const unwrapped = VrpcAdapter._unwrapArguments(json)
|
|
520
743
|
const ret = window[VRPC_WINDOW_NS][json.f].apply(null, unwrapped)
|
|
521
744
|
// check if function returns promise
|
|
522
745
|
if (VrpcAdapter._isPromise(ret)) {
|
|
523
746
|
VrpcAdapter._handlePromise(json, ret)
|
|
524
747
|
} else json.r = ret
|
|
525
748
|
} catch (err) {
|
|
526
|
-
json.e = err
|
|
749
|
+
json.e = VrpcAdapter._error(err)
|
|
527
750
|
}
|
|
528
751
|
return
|
|
529
752
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
// rather sticking to those functions registered before...
|
|
538
|
-
// TODO This is even more important now as the agent does a wildcard
|
|
539
|
-
// subscription against all functions
|
|
540
|
-
if (VrpcAdapter._isFunction(Klass[json.f])) {
|
|
541
|
-
try {
|
|
542
|
-
const ret = Klass[json.f].apply(null, unwrapped)
|
|
543
|
-
// check if function returns promise
|
|
544
|
-
if (VrpcAdapter._isPromise(ret)) {
|
|
545
|
-
VrpcAdapter._handlePromise(json, ret)
|
|
546
|
-
} else json.r = ret
|
|
547
|
-
} catch (err) {
|
|
548
|
-
json.e = err.message
|
|
549
|
-
}
|
|
550
|
-
} else throw new Error(`Could not find static function: ${json.f}`)
|
|
753
|
+
// Check whether context is a registered class (function is static)
|
|
754
|
+
const classEntry = VrpcAdapter._functionRegistry.get(json.c)
|
|
755
|
+
const isStatic = classEntry !== undefined
|
|
756
|
+
let target
|
|
757
|
+
let className = json.c
|
|
758
|
+
if (isStatic) {
|
|
759
|
+
target = classEntry.Klass
|
|
551
760
|
} else {
|
|
552
|
-
// is not static
|
|
553
761
|
const entry = VrpcAdapter._instances.get(json.c)
|
|
554
762
|
if (entry === undefined) {
|
|
555
763
|
throw new Error(`Could not find context: ${json.c}`)
|
|
556
764
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
765
|
+
target = entry.instance
|
|
766
|
+
className = entry.className
|
|
767
|
+
}
|
|
768
|
+
const carriesIds = Registrations.carriesEventIds(json.a)
|
|
769
|
+
// only what the class info lists is called; a remover carrying ids never
|
|
770
|
+
// reaches the class (see Registrations)
|
|
771
|
+
if (
|
|
772
|
+
!(carriesIds && Registrations.isRemover(json.f, json.v)) &&
|
|
773
|
+
!VrpcAdapter._isPublished(className, json.f, isStatic)
|
|
774
|
+
) {
|
|
775
|
+
throw new Error(`Could not find function: ${json.f}`)
|
|
776
|
+
}
|
|
777
|
+
// a call carrying subscription ids subscribes (or withdraws)
|
|
778
|
+
if (carriesIds) {
|
|
779
|
+
try {
|
|
780
|
+
const { value } = VrpcAdapter._subscribe(json, {
|
|
781
|
+
contextKey: VrpcAdapter._contextKey(json.c, isStatic),
|
|
782
|
+
contextName: json.c,
|
|
783
|
+
target,
|
|
784
|
+
func: json.f,
|
|
785
|
+
args: json.a
|
|
786
|
+
})
|
|
787
|
+
if (VrpcAdapter._isPromise(value)) {
|
|
788
|
+
VrpcAdapter._handlePromise(json, value)
|
|
789
|
+
} else json.r = value
|
|
790
|
+
} catch (err) {
|
|
791
|
+
json.e = VrpcAdapter._error(err)
|
|
792
|
+
}
|
|
793
|
+
return
|
|
794
|
+
}
|
|
795
|
+
const unwrapped = VrpcAdapter._unwrapArguments(json)
|
|
796
|
+
// special case: removing a listener the adapter does not hold (the
|
|
797
|
+
// client's subscription outlived a restart of this agent, or it sent
|
|
798
|
+
// no listener at all) - nothing to remove, and the instance's
|
|
799
|
+
// EventEmitter would throw on a listener that is not a function
|
|
800
|
+
if (
|
|
801
|
+
(json.f === 'off' || json.f === 'removeListener') &&
|
|
802
|
+
typeof unwrapped[1] !== 'function'
|
|
803
|
+
) {
|
|
804
|
+
json.r = true
|
|
805
|
+
return
|
|
806
|
+
}
|
|
807
|
+
// TODO Think about whether to do live checking (like here) or
|
|
808
|
+
// rather sticking to those functions registered before...
|
|
809
|
+
if (!VrpcAdapter._isFunction(target[json.f])) {
|
|
810
|
+
throw new Error(`Could not find function: ${json.f}`)
|
|
811
|
+
}
|
|
812
|
+
try {
|
|
813
|
+
let ret = isStatic
|
|
814
|
+
? target[json.f].apply(null, unwrapped)
|
|
815
|
+
: target[json.f].apply(target, unwrapped)
|
|
816
|
+
if (!isStatic) ret = VrpcAdapter.sanitizeEventListenerReturnValues(ret)
|
|
817
|
+
// check if function returns promise
|
|
818
|
+
if (VrpcAdapter._isPromise(ret)) {
|
|
819
|
+
VrpcAdapter._handlePromise(json, ret)
|
|
820
|
+
} else json.r = ret
|
|
821
|
+
} catch (err) {
|
|
822
|
+
json.e = VrpcAdapter._error(err)
|
|
570
823
|
}
|
|
571
824
|
}
|
|
572
825
|
|
|
@@ -588,9 +841,10 @@ class VrpcAdapter {
|
|
|
588
841
|
/**
|
|
589
842
|
* Ends an instance - the one teardown behind every door (a remote delete,
|
|
590
843
|
* the auto-delete of an isolated instance whose client went, an
|
|
591
|
-
* in-process delete): its name is free at once, the adapter
|
|
592
|
-
*
|
|
593
|
-
*
|
|
844
|
+
* in-process delete): its name is free at once, the adapter forgets its
|
|
845
|
+
* registrations (silent from now on, see Registrations; released by the
|
|
846
|
+
* adapter only for an object without a dispose method, and Node's own
|
|
847
|
+
* `on` always), and the object releases what it runs through the
|
|
594
848
|
* ECMAScript dispose protocol, `[Symbol.asyncDispose]` or else
|
|
595
849
|
* `[Symbol.dispose]`.
|
|
596
850
|
*
|
|
@@ -601,33 +855,16 @@ class VrpcAdapter {
|
|
|
601
855
|
const entry = VrpcAdapter._instances.get(instanceId)
|
|
602
856
|
if (!entry) return { ended: false, disposed: null }
|
|
603
857
|
VrpcAdapter._instances.delete(instanceId)
|
|
604
|
-
|
|
858
|
+
const { instance } = entry
|
|
859
|
+
VrpcAdapter._registrations.forgetContext(instanceId, {
|
|
860
|
+
release: !(
|
|
861
|
+
VrpcAdapter._isFunction(instance[Symbol.asyncDispose]) ||
|
|
862
|
+
VrpcAdapter._isFunction(instance[Symbol.dispose])
|
|
863
|
+
)
|
|
864
|
+
})
|
|
605
865
|
return { ended: true, disposed: VrpcAdapter._dispose(instanceId, entry) }
|
|
606
866
|
}
|
|
607
867
|
|
|
608
|
-
static _detachListeners (instanceId, instance) {
|
|
609
|
-
const registrations = VrpcAdapter._listeners[instanceId]
|
|
610
|
-
delete VrpcAdapter._listeners[instanceId]
|
|
611
|
-
if (!registrations || !VrpcAdapter._isFunction(instance.removeListener)) {
|
|
612
|
-
return
|
|
613
|
-
}
|
|
614
|
-
for (const { event, listener, clients } of Object.values(registrations)) {
|
|
615
|
-
try {
|
|
616
|
-
VrpcAdapter._removeTimes(instance, event, listener, clients.length)
|
|
617
|
-
} catch (err) {
|
|
618
|
-
// a class whose removeListener fails keeps a silent listener
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
// a subscription repeated n times may hold n attachments of one listener
|
|
624
|
-
// (EventEmitter semantics): each removeListener takes one of them
|
|
625
|
-
static _removeTimes (instance, event, listener, times) {
|
|
626
|
-
for (let n = 0; n < Math.max(1, times); n++) {
|
|
627
|
-
instance.removeListener(event, listener)
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
|
|
631
868
|
/**
|
|
632
869
|
* Runs the object's dispose method. A dispose that fails or outlasts
|
|
633
870
|
* VrpcAdapter.DISPOSE_TIMEOUT is reported (event 'disposeFailed') and
|
|
@@ -673,27 +910,21 @@ class VrpcAdapter {
|
|
|
673
910
|
}
|
|
674
911
|
|
|
675
912
|
static _unregisterClient (clientId) {
|
|
676
|
-
|
|
677
|
-
Object.entries(iv).forEach(([ek, ev]) => {
|
|
678
|
-
const { listener, event } = ev
|
|
679
|
-
// a client is listed once per subscription: its going ends them all
|
|
680
|
-
const remaining = ev.clients.filter(x => x !== clientId)
|
|
681
|
-
const gone = ev.clients.length - remaining.length
|
|
682
|
-
if (gone === 0) return
|
|
683
|
-
ev.clients = remaining
|
|
684
|
-
if (remaining.length === 0) {
|
|
685
|
-
const entry = VrpcAdapter._instances.get(ik)
|
|
686
|
-
const instance = entry && entry.instance
|
|
687
|
-
if (instance && instance.removeListener) {
|
|
688
|
-
VrpcAdapter._removeTimes(instance, event, listener, gone)
|
|
689
|
-
}
|
|
690
|
-
delete VrpcAdapter._listeners[ik][ek]
|
|
691
|
-
}
|
|
692
|
-
})
|
|
693
|
-
})
|
|
913
|
+
VrpcAdapter._registrations.withdrawClient(clientId)
|
|
694
914
|
}
|
|
695
915
|
|
|
696
916
|
static _validate (schema, params) {
|
|
917
|
+
// the browser bundle leaves ajv out - a fifth of its size, for agents in
|
|
918
|
+
// a page, which are rare: there a schema is not checked
|
|
919
|
+
if (typeof Ajv !== 'function') {
|
|
920
|
+
if (!VrpcAdapter._warnedNoSchemas) {
|
|
921
|
+
VrpcAdapter._warnedNoSchemas = true
|
|
922
|
+
console.warn(
|
|
923
|
+
'vrpc: schema validation is not available in the browser build, constructor arguments are not checked'
|
|
924
|
+
)
|
|
925
|
+
}
|
|
926
|
+
return
|
|
927
|
+
}
|
|
697
928
|
if (!VrpcAdapter._ajv) {
|
|
698
929
|
VrpcAdapter._ajv = new Ajv({ useDefaults: true })
|
|
699
930
|
}
|
|
@@ -701,6 +932,7 @@ class VrpcAdapter {
|
|
|
701
932
|
if (!valid) throw new Error(VrpcAdapter._ajv.errorsText())
|
|
702
933
|
}
|
|
703
934
|
|
|
935
|
+
// the arguments of a plain call: `__f__` ids become one-shot callbacks
|
|
704
936
|
static _unwrapArguments (json, instanceId) {
|
|
705
937
|
const clientId = json.s
|
|
706
938
|
// if we came from a "callAll" we will have an instanceId defined
|
|
@@ -711,59 +943,17 @@ class VrpcAdapter {
|
|
|
711
943
|
const func = isCallAll ? json.a[0] : json.f
|
|
712
944
|
const unwrapped = isCallAll ? [func] : []
|
|
713
945
|
for (const arg of args) {
|
|
714
|
-
if (typeof arg === 'string') {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
)
|
|
725
|
-
} else if (arg.startsWith('__e__')) {
|
|
726
|
-
// event registration
|
|
727
|
-
if (func === 'on' || func === 'addListener') {
|
|
728
|
-
VrpcAdapter._mustTrackClient = true
|
|
729
|
-
const held = VrpcAdapter._holdsListener(context, arg)
|
|
730
|
-
const listener = VrpcAdapter._registerListener({
|
|
731
|
-
clientId,
|
|
732
|
-
isCallAll,
|
|
733
|
-
instanceId: context,
|
|
734
|
-
eventId: arg,
|
|
735
|
-
event: args[0]
|
|
736
|
-
})
|
|
737
|
-
// attached already: skip the call, one listener hears once
|
|
738
|
-
if (held) return null
|
|
739
|
-
unwrapped.push(listener)
|
|
740
|
-
// event un-registration
|
|
741
|
-
} else if (func === 'off' || func === 'removeListener') {
|
|
742
|
-
const listener = VrpcAdapter._unregisterListener({
|
|
743
|
-
clientId,
|
|
744
|
-
instanceId: context,
|
|
745
|
-
eventId: arg
|
|
746
|
-
})
|
|
747
|
-
if (!listener) {
|
|
748
|
-
return null // skip call as others are still interested
|
|
749
|
-
}
|
|
750
|
-
unwrapped.push(listener)
|
|
751
|
-
} else {
|
|
752
|
-
VrpcAdapter._mustTrackClient = true
|
|
753
|
-
const listener = VrpcAdapter._registerListener({
|
|
754
|
-
clientId,
|
|
755
|
-
isCallAll,
|
|
756
|
-
instanceId: context,
|
|
757
|
-
eventId: arg,
|
|
758
|
-
event: null
|
|
759
|
-
})
|
|
760
|
-
unwrapped.push(listener)
|
|
761
|
-
}
|
|
762
|
-
} else {
|
|
763
|
-
unwrapped.push(arg)
|
|
764
|
-
}
|
|
946
|
+
if (typeof arg === 'string' && arg.startsWith('__f__')) {
|
|
947
|
+
unwrapped.push(
|
|
948
|
+
VrpcAdapter._generateCallback({
|
|
949
|
+
clientId,
|
|
950
|
+
isCallAll,
|
|
951
|
+
instanceId: context,
|
|
952
|
+
eventId: arg,
|
|
953
|
+
receiverVersion: json.v
|
|
954
|
+
})
|
|
955
|
+
)
|
|
765
956
|
} else {
|
|
766
|
-
// regular argument
|
|
767
957
|
unwrapped.push(arg)
|
|
768
958
|
}
|
|
769
959
|
}
|
|
@@ -787,113 +977,40 @@ class VrpcAdapter {
|
|
|
787
977
|
}
|
|
788
978
|
}
|
|
789
979
|
|
|
790
|
-
static
|
|
791
|
-
const registrations = VrpcAdapter._listeners[instanceId]
|
|
792
|
-
return Boolean(registrations && registrations[eventId])
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
static _generateCallback ({ eventId, clientId, instanceId, isCallAll }) {
|
|
796
|
-
const speaks = VrpcAdapter._speaksFor(instanceId)
|
|
797
|
-
return (...innerArgs) => {
|
|
798
|
-
if (!speaks()) return
|
|
799
|
-
// "callAll" event notifications expect the instanceId as first argument
|
|
800
|
-
const a = isCallAll ? [instanceId, ...innerArgs] : [...innerArgs]
|
|
801
|
-
VrpcAdapter._callback({ a, s: clientId, i: eventId })
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
static _registerListener ({
|
|
980
|
+
static _generateCallback ({
|
|
806
981
|
eventId,
|
|
807
982
|
clientId,
|
|
808
983
|
instanceId,
|
|
809
|
-
|
|
810
|
-
|
|
984
|
+
isCallAll,
|
|
985
|
+
receiverVersion
|
|
811
986
|
}) {
|
|
812
|
-
if (!VrpcAdapter._listeners[instanceId]) {
|
|
813
|
-
VrpcAdapter._listeners[instanceId] = {
|
|
814
|
-
[eventId]: {
|
|
815
|
-
event,
|
|
816
|
-
clients: [clientId],
|
|
817
|
-
listener: VrpcAdapter._generateListener({
|
|
818
|
-
eventId,
|
|
819
|
-
instanceId,
|
|
820
|
-
isCallAll
|
|
821
|
-
})
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
return VrpcAdapter._listeners[instanceId][eventId].listener
|
|
825
|
-
}
|
|
826
|
-
if (!VrpcAdapter._listeners[instanceId][eventId]) {
|
|
827
|
-
VrpcAdapter._listeners[instanceId][eventId] = {
|
|
828
|
-
event,
|
|
829
|
-
clients: [clientId],
|
|
830
|
-
listener: VrpcAdapter._generateListener({
|
|
831
|
-
eventId,
|
|
832
|
-
instanceId,
|
|
833
|
-
isCallAll
|
|
834
|
-
})
|
|
835
|
-
}
|
|
836
|
-
return VrpcAdapter._listeners[instanceId][eventId].listener
|
|
837
|
-
}
|
|
838
|
-
VrpcAdapter._listeners[instanceId][eventId].clients.push(clientId)
|
|
839
|
-
return VrpcAdapter._listeners[instanceId][eventId].listener
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
static _generateListener ({ eventId, instanceId, isCallAll }) {
|
|
843
987
|
const speaks = VrpcAdapter._speaksFor(instanceId)
|
|
844
988
|
return (...innerArgs) => {
|
|
845
989
|
if (!speaks()) return
|
|
990
|
+
// "callAll" event notifications expect the instanceId as first argument
|
|
846
991
|
const a = isCallAll ? [instanceId, ...innerArgs] : [...innerArgs]
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
static _unregisterListener ({ eventId, clientId, instanceId }) {
|
|
852
|
-
if (
|
|
853
|
-
VrpcAdapter._listeners[instanceId] &&
|
|
854
|
-
VrpcAdapter._listeners[instanceId][eventId]
|
|
855
|
-
) {
|
|
856
|
-
const { listener, clients } = VrpcAdapter._listeners[instanceId][eventId]
|
|
857
|
-
const index = clients.indexOf(clientId)
|
|
858
|
-
if (index !== -1) clients.splice(index, 1)
|
|
859
|
-
if (clients.length === 0) {
|
|
860
|
-
delete VrpcAdapter._listeners[instanceId][eventId]
|
|
861
|
-
if (Object.keys(VrpcAdapter._listeners[instanceId]).length === 0) {
|
|
862
|
-
delete VrpcAdapter._listeners[instanceId]
|
|
863
|
-
}
|
|
864
|
-
return listener
|
|
865
|
-
}
|
|
992
|
+
// the receiver's protocol version: whether it can be told of a
|
|
993
|
+
// callback that cannot travel (CALL-10)
|
|
994
|
+
VrpcAdapter._callback({ a, s: clientId, i: eventId, receiverVersion })
|
|
866
995
|
}
|
|
867
996
|
}
|
|
868
997
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
VrpcAdapter.getInstance(instanceId).removeListener(
|
|
877
|
-
ev.event,
|
|
878
|
-
ev.listener
|
|
879
|
-
)
|
|
880
|
-
delete VrpcAdapter._listeners[instanceId][ek]
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
})
|
|
884
|
-
}
|
|
885
|
-
|
|
998
|
+
/**
|
|
999
|
+
* Answers a promise twice: at once with `r: '__p__<f>-<nonce>-<n>'`, and
|
|
1000
|
+
* once it settled with a second answer under that id, `{ s, i, r }` or
|
|
1001
|
+
* `{ s, i, e }`. A client waits for the second answer under the bare id,
|
|
1002
|
+
* whichever agent sends it: the nonce keeps the ids of two agents apart,
|
|
1003
|
+
* whose counters may well stand at the same value.
|
|
1004
|
+
*/
|
|
886
1005
|
static _handlePromise (json, promise) {
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
json.data.e = err.message
|
|
896
|
-
}
|
|
1006
|
+
const cid = VrpcAdapter._correlationId++ % Number.MAX_SAFE_INTEGER
|
|
1007
|
+
const id = `__p__${json.f}-${VrpcAdapter._promiseNonce}-${cid}`
|
|
1008
|
+
const { s } = json
|
|
1009
|
+
json.r = id
|
|
1010
|
+
promise.then(
|
|
1011
|
+
r => VrpcAdapter._callback({ s, i: id, r }),
|
|
1012
|
+
err => VrpcAdapter._callback({ s, i: id, e: VrpcAdapter._error(err) })
|
|
1013
|
+
)
|
|
897
1014
|
}
|
|
898
1015
|
|
|
899
1016
|
static _getClassEntry (className) {
|
|
@@ -929,6 +1046,18 @@ class VrpcAdapter {
|
|
|
929
1046
|
static _extractMemberFunctions (klass) {
|
|
930
1047
|
let klass_ = klass
|
|
931
1048
|
let fs = []
|
|
1049
|
+
// Node's EventEmitter offers subscribing, withdrawing and once to
|
|
1050
|
+
// clients; emit (anyone could fake an event to every subscriber, or emit
|
|
1051
|
+
// 'error' and end the agent) and its internals stay inside the class
|
|
1052
|
+
const isEmitter =
|
|
1053
|
+
klass instanceof EventEmitter ||
|
|
1054
|
+
(!!klass.prototype && klass.prototype instanceof EventEmitter)
|
|
1055
|
+
const published = (names, level) => {
|
|
1056
|
+
if (level === EventEmitter.prototype) {
|
|
1057
|
+
return names.filter(x => VrpcAdapter._emitterPublic.has(x))
|
|
1058
|
+
}
|
|
1059
|
+
return isEmitter ? names.filter(x => x !== 'emit') : names
|
|
1060
|
+
}
|
|
932
1061
|
do {
|
|
933
1062
|
if (klass_.prototype) {
|
|
934
1063
|
const funcs = Object.getOwnPropertyNames(klass_.prototype).filter(
|
|
@@ -936,13 +1065,12 @@ class VrpcAdapter {
|
|
|
936
1065
|
!VrpcAdapter._blackList.has(x) &&
|
|
937
1066
|
VrpcAdapter._isFunction(klass_.prototype[x])
|
|
938
1067
|
)
|
|
939
|
-
fs = [...fs, ...funcs]
|
|
1068
|
+
fs = [...fs, ...published(funcs, klass_.prototype)]
|
|
940
1069
|
} else {
|
|
941
1070
|
const funcs = Object.getOwnPropertyNames(klass_).filter(
|
|
942
|
-
x =>
|
|
943
|
-
!VrpcAdapter._blackList.has(x) && VrpcAdapter._isFunction(klass_[x])
|
|
1071
|
+
x => !VrpcAdapter._blackList.has(x) && VrpcAdapter._isFunction(klass_[x])
|
|
944
1072
|
)
|
|
945
|
-
fs = [...fs, ...funcs]
|
|
1073
|
+
fs = [...fs, ...published(funcs, klass_)]
|
|
946
1074
|
}
|
|
947
1075
|
klass_ = Object.getPrototypeOf(klass_)
|
|
948
1076
|
} while (klass_)
|
|
@@ -954,13 +1082,16 @@ class VrpcAdapter {
|
|
|
954
1082
|
let klass_ = klass
|
|
955
1083
|
let fs = []
|
|
956
1084
|
do {
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1085
|
+
// EventEmitter's own statics are Node helpers, not the class's API
|
|
1086
|
+
if (klass_ !== EventEmitter) {
|
|
1087
|
+
fs = [
|
|
1088
|
+
...fs,
|
|
1089
|
+
...Object.getOwnPropertyNames(klass_).filter(prop => {
|
|
1090
|
+
const desc = Object.getOwnPropertyDescriptor(klass_, prop)
|
|
1091
|
+
return !!desc && typeof desc.value === 'function'
|
|
1092
|
+
})
|
|
1093
|
+
]
|
|
1094
|
+
}
|
|
964
1095
|
klass_ = Object.getPrototypeOf(klass_)
|
|
965
1096
|
} while (klass_ && klass_.name)
|
|
966
1097
|
// Filter out all duplicates
|
|
@@ -1041,10 +1172,28 @@ VrpcAdapter._functionRegistry = new Map()
|
|
|
1041
1172
|
VrpcAdapter._instances = new Map()
|
|
1042
1173
|
VrpcAdapter._emitter = new EventEmitter()
|
|
1043
1174
|
VrpcAdapter._correlationId = 0
|
|
1044
|
-
|
|
1175
|
+
// tells this process's promise ids from those of every other agent
|
|
1176
|
+
VrpcAdapter._promiseNonce = nanoid(8)
|
|
1177
|
+
// the event registrations of every context and their subscriptions
|
|
1178
|
+
VrpcAdapter._registrations = new Registrations({
|
|
1179
|
+
publish: message => VrpcAdapter._callback(message),
|
|
1180
|
+
emit: (event, payload) => VrpcAdapter._emitter.emit(event, payload)
|
|
1181
|
+
})
|
|
1045
1182
|
// the longest a delete waits for the dispose method of its instance
|
|
1046
1183
|
VrpcAdapter.DISPOSE_TIMEOUT = 10000
|
|
1184
|
+
// the EventEmitter functions published to clients
|
|
1185
|
+
VrpcAdapter._emitterPublic = new Set([
|
|
1186
|
+
'on',
|
|
1187
|
+
'addListener',
|
|
1188
|
+
'off',
|
|
1189
|
+
'removeListener',
|
|
1190
|
+
'once',
|
|
1191
|
+
'removeAllListeners'
|
|
1192
|
+
])
|
|
1047
1193
|
VrpcAdapter._blackList = new Set([
|
|
1194
|
+
// a class's constructor is reached through __createShared__ and
|
|
1195
|
+
// __createIsolated__, never as a member function
|
|
1196
|
+
'constructor',
|
|
1048
1197
|
'caller',
|
|
1049
1198
|
'callee',
|
|
1050
1199
|
'arguments',
|