@webqit/port-plus 0.1.0 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/index.html +28 -5
- package/dist/main.js +1 -1
- package/dist/main.js.map +4 -4
- package/package.json +1 -1
- package/src/MessageChannelPlus.js +2 -7
- package/src/MessageEventPlus.js +4 -3
- package/src/MessagePortPlus.js +99 -38
- package/src/RelayPort.js +22 -12
- package/src/index.browser.js +3 -1
- package/src/index.js +2 -1
- package/test/basic.test.js +22 -5
package/package.json
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
import { MessagePortPlus
|
|
1
|
+
import { MessagePortPlus } from './MessagePortPlus.js';
|
|
2
2
|
|
|
3
3
|
export class MessageChannelPlus extends MessageChannel {
|
|
4
4
|
|
|
5
5
|
constructor({ autoStart = true, postAwaitsOpen = false } = {}) {
|
|
6
6
|
super();
|
|
7
7
|
[this.port1, this.port2].forEach((port, i) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
portPlusMeta.set('options', { autoStart, postAwaitsOpen });
|
|
11
|
-
// Must come before upgradeInPlace()
|
|
12
|
-
|
|
13
|
-
MessagePortPlus.upgradeInPlace(port);
|
|
8
|
+
MessagePortPlus.upgradeInPlace(port, { autoStart, postAwaitsOpen });
|
|
14
9
|
});
|
|
15
10
|
}
|
|
16
11
|
}
|
package/src/MessageEventPlus.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { _isTypeObject } from '@webqit/util/js/index.js';
|
|
2
|
-
import { MessagePortPlus, applyMutations } from './MessagePortPlus.js';
|
|
2
|
+
import { MessagePortPlus, applyMutations, _options } from './MessagePortPlus.js';
|
|
3
3
|
|
|
4
|
-
export class MessageEventPlus extends
|
|
4
|
+
export class MessageEventPlus extends MessageEvent {
|
|
5
5
|
|
|
6
6
|
#originalTarget;
|
|
7
7
|
get originalTarget() { return this.#originalTarget; }
|
|
@@ -50,7 +50,8 @@ export class MessageEventPlus extends Event {
|
|
|
50
50
|
this.#honourDoneMutationFlags = honourDoneMutationFlags;
|
|
51
51
|
|
|
52
52
|
this.#ports = ports;
|
|
53
|
-
this.#
|
|
53
|
+
const options = this.#originalTarget && _options(this.#originalTarget) || {};
|
|
54
|
+
this.#ports.forEach((port) => MessagePortPlus.upgradeInPlace(port, { autoStart: options.autoStart, postAwaitsOpen: options.postAwaitsOpen }));
|
|
54
55
|
|
|
55
56
|
if (_isTypeObject(this.#data) && this.#live) {
|
|
56
57
|
if (typeof eventID !== 'string') {
|
package/src/MessagePortPlus.js
CHANGED
|
@@ -2,6 +2,7 @@ import { _wq as $wq } from '@webqit/util/js/index.js';
|
|
|
2
2
|
import { _isObject, _isTypeObject } from '@webqit/util/js/index.js';
|
|
3
3
|
import { MessageEventPlus } from './MessageEventPlus.js';
|
|
4
4
|
import Observer from '@webqit/observer';
|
|
5
|
+
import { WebSocketPort } from './WebSocketPort.js';
|
|
5
6
|
|
|
6
7
|
export const _wq = (target, ...args) => $wq(target, 'port+', ...args);
|
|
7
8
|
export const _options = (target) => $wq(target, 'port+', 'meta').get('options') || {};
|
|
@@ -13,7 +14,7 @@ const portPlusMethods = [
|
|
|
13
14
|
'postMessage',
|
|
14
15
|
'postRequest',
|
|
15
16
|
'dispatchEvent',
|
|
16
|
-
'
|
|
17
|
+
'relay',
|
|
17
18
|
'start',
|
|
18
19
|
'readyStateChange',
|
|
19
20
|
'removeEventListener',
|
|
@@ -46,7 +47,7 @@ export class MessagePortPlus extends MessagePortPlusMixin(EventTarget) {
|
|
|
46
47
|
export function MessagePortPlusMixin(superClass) {
|
|
47
48
|
return class extends superClass {
|
|
48
49
|
|
|
49
|
-
static upgradeInPlace(port) {
|
|
50
|
+
static upgradeInPlace(port, options = {}) {
|
|
50
51
|
if (port instanceof MessagePortPlus) {
|
|
51
52
|
return port;
|
|
52
53
|
}
|
|
@@ -69,6 +70,9 @@ export function MessagePortPlusMixin(superClass) {
|
|
|
69
70
|
Object.defineProperty(port, prop, { ...plus, configurable: true });
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
const portPlusMeta = _wq(port, 'meta');
|
|
74
|
+
portPlusMeta.set('options', options);
|
|
75
|
+
|
|
72
76
|
this.upgradeEvents(port);
|
|
73
77
|
}
|
|
74
78
|
|
|
@@ -354,12 +358,15 @@ export function MessagePortPlusMixin(superClass) {
|
|
|
354
358
|
|
|
355
359
|
// Format payload if not yet in the ['.wq'] format
|
|
356
360
|
let _relayedFrom;
|
|
357
|
-
|
|
358
|
-
|
|
361
|
+
const { portOptions, wqOptions: { relayedFrom, ...wqOptions } } = preProcessPostMessage.call(this, message, transferOrOptions);
|
|
362
|
+
if (wqOptions.type !== 'message'
|
|
363
|
+
|| wqOptions.live
|
|
364
|
+
|| wqOptions.bubbles
|
|
365
|
+
|| portOptions.transfer?.length && (this instanceof BroadcastChannel || this instanceof WebSocketPort)) {
|
|
359
366
|
message = { message, ['.wq']: wqOptions };
|
|
360
|
-
transferOrOptions = portOptions;
|
|
361
|
-
_relayedFrom = relayedFrom;
|
|
362
367
|
}
|
|
368
|
+
transferOrOptions = portOptions;
|
|
369
|
+
_relayedFrom = relayedFrom;
|
|
363
370
|
|
|
364
371
|
// Exec
|
|
365
372
|
const post = () => {
|
|
@@ -413,25 +420,45 @@ export function MessagePortPlusMixin(superClass) {
|
|
|
413
420
|
|
|
414
421
|
// Forwarding
|
|
415
422
|
|
|
416
|
-
|
|
417
|
-
if (
|
|
418
|
-
throw new Error('
|
|
423
|
+
relay({ to: targetPort, from: sourcePort, types = '*', channel = null, resolveMessage = null, bidirectional = false } = {}) {
|
|
424
|
+
if (targetPort && sourcePort) {
|
|
425
|
+
throw new Error('Only one of "to" and "from" may be specified');
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (sourcePort) {
|
|
429
|
+
targetPort = this;
|
|
430
|
+
} else sourcePort = this;
|
|
431
|
+
|
|
432
|
+
if (targetPort === sourcePort) {
|
|
433
|
+
throw new Error('Source and target ports cannot be the same');
|
|
419
434
|
}
|
|
420
|
-
|
|
435
|
+
|
|
436
|
+
if (!(sourcePort instanceof MessagePortPlus) || !(targetPort instanceof MessagePortPlus)) {
|
|
437
|
+
throw new Error('Both source and target ports must be instance of MessagePortPlus.');
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (typeof types !== 'function' && !(types = [].concat(types)).length) {
|
|
421
441
|
throw new Error('Event types must be specified.');
|
|
422
442
|
}
|
|
423
443
|
|
|
424
|
-
|
|
425
|
-
|
|
444
|
+
if (typeof channel === 'string') {
|
|
445
|
+
channel = { from: channel };
|
|
446
|
+
} else if (_isObject(channel)) {
|
|
447
|
+
if (Object.keys(channel).filter((k) => !['from', 'to'].includes(k)).length) {
|
|
448
|
+
throw new Error('Channel must be a string or an object of "from"/"to" members');
|
|
449
|
+
}
|
|
450
|
+
} else if (channel) {
|
|
451
|
+
throw new Error('Invalid channel parameter');
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const downstreamRegistry = getDownstreamRegistry.call(sourcePort);
|
|
455
|
+
const registration = { targetPort, types, channel, resolveMessage };
|
|
426
456
|
downstreamRegistry.add(registration);
|
|
427
457
|
|
|
428
458
|
let cleanup2;
|
|
429
459
|
if (bidirectional) {
|
|
430
|
-
cleanup2 =
|
|
431
|
-
|
|
432
|
-
typeof eventTypes === 'function' ? eventTypes : eventTypes.filter((s) => s !== 'close'),
|
|
433
|
-
this,
|
|
434
|
-
{ resolveData, bidirectional: false, namespace1: namespace2, namespace2: namespace1 }
|
|
460
|
+
cleanup2 = targetPort.relay(
|
|
461
|
+
{ to: sourcePort, types, channel: channel && { to: channel.from, from: channel.to }, resolveMessage, bidirectional: false }
|
|
435
462
|
);
|
|
436
463
|
}
|
|
437
464
|
|
|
@@ -441,6 +468,40 @@ export function MessagePortPlusMixin(superClass) {
|
|
|
441
468
|
};
|
|
442
469
|
}
|
|
443
470
|
|
|
471
|
+
channel(channelSpec, resolveMessage = null) {
|
|
472
|
+
const channel = new MessageChannel;
|
|
473
|
+
|
|
474
|
+
MessagePortPlus.upgradeInPlace(channel.port1, { autoStart: this.options.autoStart, postAwaitsOpen: this.options.postAwaitsOpen });
|
|
475
|
+
MessagePortPlus.upgradeInPlace(channel.port2, { autoStart: this.options.autoStart, postAwaitsOpen: this.options.postAwaitsOpen });
|
|
476
|
+
|
|
477
|
+
const garbageCollection = getGarbageCollection.call(this);
|
|
478
|
+
garbageCollection.add(this.relay({ channel: channelSpec, to: channel.port1, bidirectional: true, resolveMessage }));
|
|
479
|
+
|
|
480
|
+
channel.port1.start();
|
|
481
|
+
this.readyStateChange('close').then(() => {
|
|
482
|
+
channel.port1.close();
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
return channel.port2;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
projectMutations({ from, to, ...options }) {
|
|
489
|
+
if (!from || !to
|
|
490
|
+
|| typeof from === 'string' && typeof to === 'string'
|
|
491
|
+
|| _isTypeObject(from) && _isTypeObject(to)) {
|
|
492
|
+
throw new Error('Invalid "from"/"to" parameters');
|
|
493
|
+
}
|
|
494
|
+
if (typeof from === 'string') {
|
|
495
|
+
if (!_isTypeObject(to)) throw new Error('Invalid "to" parameter. Object/array expected');
|
|
496
|
+
return applyMutations.call(this, to, from, options);
|
|
497
|
+
}
|
|
498
|
+
if (typeof to === 'string') {
|
|
499
|
+
if (!_isTypeObject(from)) throw new Error('Invalid "from" parameter. Object/array expected');
|
|
500
|
+
return publishMutations.call(this, from, to, options);
|
|
501
|
+
}
|
|
502
|
+
throw new Error('Invalid "from"/"to" parameters');
|
|
503
|
+
}
|
|
504
|
+
|
|
444
505
|
// Lifecycle
|
|
445
506
|
|
|
446
507
|
get readyState() {
|
|
@@ -571,17 +632,8 @@ export function MessagePortPlusMockPortsMixin(superClass) {
|
|
|
571
632
|
Object.defineProperty(event, 'ports', { value: [], configurable: true });
|
|
572
633
|
|
|
573
634
|
for (let i = 0; i < numPorts; i++) {
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
MessagePortPlus.upgradeInPlace(channel.port1);
|
|
578
|
-
garbageCollection.add(portPlus.forwardPort('*', channel.port1, { bidirectional: true, namespace1: `${event.data['.wq'].eventID}:${i}` }));
|
|
579
|
-
event.ports.push(channel.port2);
|
|
580
|
-
|
|
581
|
-
portPlus.readyStateChange('close').then(() => {
|
|
582
|
-
channel.port1.close();
|
|
583
|
-
channel.port2.close();
|
|
584
|
-
});
|
|
635
|
+
const port = portPlus.channel(`${event.data['.wq'].eventID}:${i}`);
|
|
636
|
+
event.ports.push(port);
|
|
585
637
|
}
|
|
586
638
|
|
|
587
639
|
return event;
|
|
@@ -598,7 +650,16 @@ export function MessagePortPlusMockPortsMixin(superClass) {
|
|
|
598
650
|
|
|
599
651
|
for (let i = 0; i < numPorts; i++) {
|
|
600
652
|
MessagePortPlus.upgradeInPlace(messagePorts[i]);
|
|
601
|
-
|
|
653
|
+
|
|
654
|
+
garbageCollection.add(this.relay({ channel: `${payload['.wq'].eventID}:${i}`, to: messagePorts[i], bidirectional: true }));
|
|
655
|
+
|
|
656
|
+
messagePorts[i].start();
|
|
657
|
+
messagePorts[i].readyStateChange('close').then(() => {
|
|
658
|
+
// Send a disconnect ping to port2
|
|
659
|
+
const { wqOptions } = preProcessPostMessage.call(this);
|
|
660
|
+
const pingData = { ['.wq']: wqOptions, ping: 'disconnect' };
|
|
661
|
+
this.postMessage(pingData, { type: `${payload['.wq'].eventID}:${i}:message` });
|
|
662
|
+
});
|
|
602
663
|
}
|
|
603
664
|
|
|
604
665
|
payload['.wq'].numPorts = numPorts; // IMPORTANT: numPorts must be set before ports are added
|
|
@@ -629,27 +690,27 @@ export function propagateEvent(event) {
|
|
|
629
690
|
const { type, eventID, data, live, bubbles, ports } = event;
|
|
630
691
|
|
|
631
692
|
const called = new WeakSet;
|
|
632
|
-
for (const { targetPort,
|
|
693
|
+
for (const { targetPort, types, channel, resolveMessage } of downstreamRegistry) {
|
|
633
694
|
if (called.has(targetPort)) continue;
|
|
634
695
|
|
|
635
696
|
let $type = type;
|
|
636
|
-
if (
|
|
637
|
-
[, $type] = (new RegExp(`^${
|
|
697
|
+
if (channel.from) {
|
|
698
|
+
[, $type] = (new RegExp(`^${channel.from.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}:([^:]+)$`)).exec(type) || [];
|
|
638
699
|
if (!$type) continue;
|
|
639
700
|
}
|
|
640
701
|
|
|
641
|
-
const matches = typeof
|
|
642
|
-
?
|
|
643
|
-
: [].concat(
|
|
702
|
+
const matches = typeof types === 'function'
|
|
703
|
+
? types($type, this, targetPort, channel)
|
|
704
|
+
: [].concat(types).find((t) => {
|
|
644
705
|
return t === $type || t === '*';
|
|
645
706
|
});
|
|
646
707
|
|
|
647
708
|
if (!matches) continue;
|
|
648
709
|
called.add(targetPort);
|
|
649
710
|
|
|
650
|
-
targetPort.postMessage(
|
|
711
|
+
targetPort.postMessage(resolveMessage ? resolveMessage(data, this, targetPort, channel) : data, {
|
|
651
712
|
transfer: ports,
|
|
652
|
-
type:
|
|
713
|
+
type: channel.to ? `${channel.to}:${$type}` : $type,
|
|
653
714
|
eventID,
|
|
654
715
|
bubbles,
|
|
655
716
|
live,
|
|
@@ -682,7 +743,7 @@ export function getReadyStateInternals() {
|
|
|
682
743
|
const portPlusMeta = _meta(this);
|
|
683
744
|
if (!portPlusMeta.has('readystate_registry')) {
|
|
684
745
|
const $ref = (o) => {
|
|
685
|
-
o.promise = new Promise((
|
|
746
|
+
o.promise = new Promise((res, rej) => (o.resolve = res, o.reject = rej));
|
|
686
747
|
return o;
|
|
687
748
|
};
|
|
688
749
|
portPlusMeta.set('readystate_registry', {
|
package/src/RelayPort.js
CHANGED
|
@@ -1,39 +1,49 @@
|
|
|
1
|
+
import { _isObject } from '@webqit/util/js/index.js';
|
|
1
2
|
import { StarPort } from './StarPort.js';
|
|
2
3
|
|
|
3
4
|
export class RelayPort extends StarPort {
|
|
4
5
|
|
|
5
|
-
#
|
|
6
|
-
get
|
|
6
|
+
#channelSpec;
|
|
7
|
+
get channelSpec() { return this.#channelSpec; }
|
|
7
8
|
|
|
8
|
-
constructor(
|
|
9
|
+
constructor(channel = null) {
|
|
9
10
|
super();
|
|
10
|
-
|
|
11
|
+
if (typeof channel === 'string') {
|
|
12
|
+
channel = { from: channel, to: channel };
|
|
13
|
+
} else if (_isObject(channel)) {
|
|
14
|
+
if (Object.keys(channel).filter((k) => !['from', 'to'].includes(k)).length) {
|
|
15
|
+
throw new Error('Channel must be a string or an object of "from"/"to" members');
|
|
16
|
+
}
|
|
17
|
+
} else if (channel) {
|
|
18
|
+
throw new Error('Invalid channel parameter');
|
|
19
|
+
}
|
|
20
|
+
this.#channelSpec = channel;
|
|
11
21
|
}
|
|
12
22
|
|
|
13
|
-
addPort(portPlus, {
|
|
14
|
-
const $
|
|
15
|
-
if (
|
|
23
|
+
addPort(portPlus, { resolveMessage = null } = {}) {
|
|
24
|
+
const $resolveMessage = (data, ...args) => {
|
|
25
|
+
if (resolveMessage) return resolveMessage(data, ...args);
|
|
16
26
|
return data;
|
|
17
27
|
};
|
|
18
28
|
|
|
19
29
|
// Add port and forward its messages back to this relay instance
|
|
20
30
|
const unadd = super.addPort(portPlus, { enableBubbling: false });
|
|
21
|
-
const unforward = portPlus.
|
|
31
|
+
const unforward = portPlus.relay({ channel: this.#channelSpec, to: this, bidirectional: false/* IMPORTANT */, resolveMessage: $resolveMessage });
|
|
22
32
|
|
|
23
|
-
const messageType_ping = this.
|
|
24
|
-
&& `${this.
|
|
33
|
+
const messageType_ping = this.#channelSpec.from
|
|
34
|
+
&& `${this.#channelSpec.from}:message`
|
|
25
35
|
|| 'message';
|
|
26
36
|
|
|
27
37
|
// PING: joins
|
|
28
38
|
this.postMessage(
|
|
29
|
-
$
|
|
39
|
+
$resolveMessage({ event: 'joins' }, portPlus, this),
|
|
30
40
|
{ type: messageType_ping, relayedFrom: portPlus }
|
|
31
41
|
);
|
|
32
42
|
|
|
33
43
|
const leaves = () => {
|
|
34
44
|
// PING: leaves
|
|
35
45
|
this.postMessage(
|
|
36
|
-
$
|
|
46
|
+
$resolveMessage({ event: 'leaves' }, portPlus, this),
|
|
37
47
|
{ type: messageType_ping, relayedFrom: portPlus }
|
|
38
48
|
);
|
|
39
49
|
};
|
package/src/index.browser.js
CHANGED
package/src/index.js
CHANGED
|
@@ -4,4 +4,5 @@ export { MessageEventPlus } from './MessageEventPlus.js';
|
|
|
4
4
|
export { MessagePortPlus } from './MessagePortPlus.js';
|
|
5
5
|
export { RelayPort } from './RelayPort.js';
|
|
6
6
|
export { StarPort } from './StarPort.js';
|
|
7
|
-
export { WebSocketPort } from './WebSocketPort.js';
|
|
7
|
+
export { WebSocketPort } from './WebSocketPort.js';
|
|
8
|
+
export { default as Observer } from '@webqit/observer';
|
package/test/basic.test.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { MessagePortPlus, MessageChannelPlus,
|
|
1
|
+
import { MessagePortPlus, MessageChannelPlus, BroadcastChannelPlus, Observer } from '../src/index.js';
|
|
2
2
|
|
|
3
3
|
const t = new MessagePortPlus;
|
|
4
4
|
|
|
5
|
-
let m, q =
|
|
5
|
+
let m, q = 4;
|
|
6
6
|
|
|
7
7
|
if (q === 1) {
|
|
8
8
|
m = new MessageChannel;
|
|
@@ -30,9 +30,26 @@ m.port1.addEventListener('close', () => console.log('close1'));
|
|
|
30
30
|
m.port2.addEventListener('open', () => console.log('open2'));
|
|
31
31
|
m.port2.addEventListener('close', () => console.log('close2'));
|
|
32
32
|
|
|
33
|
-
m.port1.addEventListener('message', (e) =>
|
|
34
|
-
|
|
33
|
+
m.port1.addEventListener('message', (e) => {
|
|
34
|
+
console.log('message', e.data);
|
|
35
|
+
console.log('numPorts', e.ports.length);
|
|
36
|
+
e.ports[0]?.addEventListener('message', (e) => {
|
|
37
|
+
console.log('Port message', e.data, 'isLive', e.live, );
|
|
38
|
+
Observer.observe(e.data, (mm) => {
|
|
39
|
+
console.log('-------mutations', e.data);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
e.ports[0]?.addEventListener('close', (e) => console.log('Port close'));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const p = new MessageChannelPlus;
|
|
46
|
+
m.port2.postMessage('hello', [p.port1]);
|
|
47
|
+
const pMessage = { greeting: 'Hi' };
|
|
48
|
+
p.port2.postMessage(pMessage, { live: true });
|
|
49
|
+
Observer.set(pMessage, '_K', '_V');
|
|
50
|
+
|
|
51
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
52
|
+
p.port2.close();
|
|
35
53
|
|
|
36
54
|
await new Promise((r) => setTimeout(r, 3000));
|
|
37
|
-
//m.port1.close();
|
|
38
55
|
m.port2.close();
|