@dxos/messaging 0.6.8-main.3be982f → 0.6.8-staging.63bcb81

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.
Files changed (60) hide show
  1. package/dist/lib/browser/index.mjs +466 -199
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +456 -197
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/types/src/messenger.blueprint-test.d.ts +4 -0
  8. package/dist/types/src/messenger.blueprint-test.d.ts.map +1 -0
  9. package/dist/types/src/messenger.d.ts +4 -10
  10. package/dist/types/src/messenger.d.ts.map +1 -1
  11. package/dist/types/src/signal-client/signal-client.d.ts +6 -10
  12. package/dist/types/src/signal-client/signal-client.d.ts.map +1 -1
  13. package/dist/types/src/signal-client/signal-local-state.d.ts +2 -10
  14. package/dist/types/src/signal-client/signal-local-state.d.ts.map +1 -1
  15. package/dist/types/src/signal-manager/edge-signal-manager.d.ts +18 -19
  16. package/dist/types/src/signal-manager/edge-signal-manager.d.ts.map +1 -1
  17. package/dist/types/src/signal-manager/index.d.ts +1 -0
  18. package/dist/types/src/signal-manager/index.d.ts.map +1 -1
  19. package/dist/types/src/signal-manager/memory-signal-manager.d.ts +14 -25
  20. package/dist/types/src/signal-manager/memory-signal-manager.d.ts.map +1 -1
  21. package/dist/types/src/signal-manager/signal-manager.d.ts +5 -6
  22. package/dist/types/src/signal-manager/signal-manager.d.ts.map +1 -1
  23. package/dist/types/src/signal-manager/websocket-signal-manager.d.ts +14 -29
  24. package/dist/types/src/signal-manager/websocket-signal-manager.d.ts.map +1 -1
  25. package/dist/types/src/signal-methods.d.ts +29 -14
  26. package/dist/types/src/signal-methods.d.ts.map +1 -1
  27. package/dist/types/src/testing/index.d.ts +5 -0
  28. package/dist/types/src/testing/index.d.ts.map +1 -0
  29. package/dist/types/src/testing/test-builder.d.ts +19 -0
  30. package/dist/types/src/testing/test-builder.d.ts.map +1 -0
  31. package/dist/types/src/testing/test-messages.d.ts +6 -0
  32. package/dist/types/src/testing/test-messages.d.ts.map +1 -0
  33. package/dist/types/src/testing/test-peer.d.ts +23 -0
  34. package/dist/types/src/testing/test-peer.d.ts.map +1 -0
  35. package/dist/types/src/testing/utils.d.ts +10 -0
  36. package/dist/types/src/testing/utils.d.ts.map +1 -0
  37. package/package.json +15 -14
  38. package/src/messenger.blueprint-test.ts +346 -0
  39. package/src/messenger.test.ts +26 -362
  40. package/src/messenger.ts +20 -18
  41. package/src/signal-client/signal-client-monitor.ts +3 -3
  42. package/src/signal-client/signal-client.test.ts +56 -52
  43. package/src/signal-client/signal-client.ts +30 -16
  44. package/src/signal-client/signal-local-state.ts +28 -10
  45. package/src/signal-manager/edge-signal-manager.test.ts +31 -35
  46. package/src/signal-manager/edge-signal-manager.ts +88 -36
  47. package/src/signal-manager/index.ts +1 -0
  48. package/src/signal-manager/memory-signal-manager.ts +36 -50
  49. package/src/signal-manager/signal-manager.ts +5 -7
  50. package/src/signal-manager/websocket-signal-manager.test.ts +27 -31
  51. package/src/signal-manager/websocket-signal-manager.ts +34 -65
  52. package/src/signal-methods.ts +33 -11
  53. package/src/testing/index.ts +8 -0
  54. package/src/testing/test-builder.ts +54 -0
  55. package/src/testing/test-messages.ts +24 -0
  56. package/src/testing/test-peer.ts +66 -0
  57. package/src/testing/utils.ts +50 -0
  58. package/dist/types/src/testing.d.ts +0 -40
  59. package/dist/types/src/testing.d.ts.map +0 -1
  60. package/src/testing.ts +0 -120
@@ -3,20 +3,25 @@
3
3
  //
4
4
 
5
5
  import { Event, sleep, synchronized } from '@dxos/async';
6
- import { type Any } from '@dxos/codec-protobuf';
7
- import { Context } from '@dxos/context';
6
+ import { LifecycleState, Resource } from '@dxos/context';
8
7
  import { invariant } from '@dxos/invariant';
9
8
  import { PublicKey } from '@dxos/keys';
10
9
  import { log } from '@dxos/log';
11
10
  import { RateLimitExceededError, TimeoutError, trace } from '@dxos/protocols';
12
11
  import { type Runtime } from '@dxos/protocols/proto/dxos/config';
13
- import { type SwarmEvent } from '@dxos/protocols/proto/dxos/mesh/signal';
14
12
  import { BitField, safeAwaitAll } from '@dxos/util';
15
13
 
16
14
  import { type SignalManager } from './signal-manager';
17
15
  import { WebsocketSignalManagerMonitor } from './websocket-signal-manager-monitor';
18
16
  import { SignalClient } from '../signal-client';
19
- import { type SignalClientMethods, type SignalMethods, type SignalStatus } from '../signal-methods';
17
+ import {
18
+ type PeerInfo,
19
+ type Message,
20
+ type SignalClientMethods,
21
+ type SignalMethods,
22
+ type SignalStatus,
23
+ type SwarmEvent,
24
+ } from '../signal-methods';
20
25
 
21
26
  const MAX_SERVER_FAILURES = 5;
22
27
  const WSS_SIGNAL_SERVER_REBOOT_DELAY = 3_000;
@@ -24,7 +29,7 @@ const WSS_SIGNAL_SERVER_REBOOT_DELAY = 3_000;
24
29
  /**
25
30
  * Manages connection to multiple Signal Servers over WebSocket
26
31
  */
27
- export class WebsocketSignalManager implements SignalManager {
32
+ export class WebsocketSignalManager extends Resource implements SignalManager {
28
33
  private readonly _servers = new Map<string, SignalClientMethods>();
29
34
  private readonly _monitor = new WebsocketSignalManagerMonitor();
30
35
 
@@ -33,21 +38,11 @@ export class WebsocketSignalManager implements SignalManager {
33
38
  */
34
39
  private readonly _failedServersBitfield: Uint8Array;
35
40
 
36
- private _ctx!: Context;
37
- private _opened = false;
38
-
39
41
  readonly failureCount = new Map<string, number>();
40
42
  readonly statusChanged = new Event<SignalStatus[]>();
41
- readonly swarmEvent = new Event<{
42
- topic: PublicKey;
43
- swarmEvent: SwarmEvent;
44
- }>();
43
+ readonly swarmEvent = new Event<SwarmEvent>();
45
44
 
46
- readonly onMessage = new Event<{
47
- author: PublicKey;
48
- recipient: PublicKey;
49
- payload: Any;
50
- }>();
45
+ readonly onMessage = new Event<Message>();
51
46
 
52
47
  private readonly _instanceId = PublicKey.random().toHex();
53
48
 
@@ -55,6 +50,7 @@ export class WebsocketSignalManager implements SignalManager {
55
50
  private readonly _hosts: Runtime.Services.Signal[],
56
51
  private readonly _getMetadata?: () => any,
57
52
  ) {
53
+ super();
58
54
  log('Created WebsocketSignalManager', { hosts: this._hosts });
59
55
  for (const host of this._hosts) {
60
56
  if (this._servers.has(host.server)) {
@@ -74,35 +70,22 @@ export class WebsocketSignalManager implements SignalManager {
74
70
  this._failedServersBitfield = BitField.zeros(this._hosts.length);
75
71
  }
76
72
 
77
- @synchronized
78
- async open() {
79
- if (this._opened) {
80
- return;
81
- }
73
+ protected override async _open() {
82
74
  log('open signal manager', { hosts: this._hosts });
83
75
  log.trace('dxos.mesh.websocket-signal-manager.open', trace.begin({ id: this._instanceId }));
84
76
 
85
- this._initContext();
86
-
87
77
  await safeAwaitAll(this._servers.values(), (server) => server.open());
88
78
 
89
- this._opened = true;
90
79
  log.trace('dxos.mesh.websocket-signal-manager.open', trace.end({ id: this._instanceId }));
91
80
  }
92
81
 
93
- @synchronized
94
- async close() {
95
- if (!this._opened) {
96
- return;
97
- }
98
- this._opened = false;
99
- await this._ctx.dispose();
82
+ protected override async _close() {
100
83
  await safeAwaitAll(this._servers.values(), (server) => server.close());
101
84
  }
102
85
 
103
86
  async restartServer(serverName: string) {
104
87
  log('restarting server', { serverName });
105
- invariant(this._opened, 'server already closed');
88
+ invariant(this._lifecycleState === LifecycleState.OPEN);
106
89
 
107
90
  const server = this._servers.get(serverName);
108
91
  invariant(server, 'server not found');
@@ -117,30 +100,22 @@ export class WebsocketSignalManager implements SignalManager {
117
100
  }
118
101
 
119
102
  @synchronized
120
- async join({ topic, peerId }: { topic: PublicKey; peerId: PublicKey }) {
121
- log('join', { topic, peerId });
122
- invariant(this._opened, 'Closed');
123
- await this._forEachServer((server) => server.join({ topic, peerId }));
103
+ async join({ topic, peer }: { topic: PublicKey; peer: PeerInfo }) {
104
+ log('join', { topic, peer });
105
+ invariant(this._lifecycleState === LifecycleState.OPEN);
106
+ await this._forEachServer((server) => server.join({ topic, peer }));
124
107
  }
125
108
 
126
109
  @synchronized
127
- async leave({ topic, peerId }: { topic: PublicKey; peerId: PublicKey }) {
128
- log('leaving', { topic, peerId });
129
- invariant(this._opened, 'Closed');
130
- await this._forEachServer((server) => server.leave({ topic, peerId }));
110
+ async leave({ topic, peer }: { topic: PublicKey; peer: PeerInfo }) {
111
+ log('leaving', { topic, peer });
112
+ invariant(this._lifecycleState === LifecycleState.OPEN);
113
+ await this._forEachServer((server) => server.leave({ topic, peer }));
131
114
  }
132
115
 
133
- async sendMessage({
134
- author,
135
- recipient,
136
- payload,
137
- }: {
138
- author: PublicKey;
139
- recipient: PublicKey;
140
- payload: Any;
141
- }): Promise<void> {
116
+ async sendMessage({ author, recipient, payload }: Message): Promise<void> {
142
117
  log('signal', { recipient });
143
- invariant(this._opened, 'Closed');
118
+ invariant(this._lifecycleState === LifecycleState.OPEN);
144
119
 
145
120
  void this._forEachServer(async (server, serverName, index) => {
146
121
  void server
@@ -187,24 +162,18 @@ export class WebsocketSignalManager implements SignalManager {
187
162
  }
188
163
  }
189
164
 
190
- async subscribeMessages(peerId: PublicKey) {
191
- log('subscribed for message stream', { peerId });
192
- invariant(this._opened, 'Closed');
165
+ async subscribeMessages(peer: PeerInfo) {
166
+ log('subscribed for message stream', { peer });
167
+ invariant(this._lifecycleState === LifecycleState.OPEN);
193
168
 
194
- await this._forEachServer(async (server) => server.subscribeMessages(peerId));
169
+ await this._forEachServer(async (server) => server.subscribeMessages(peer));
195
170
  }
196
171
 
197
- async unsubscribeMessages(peerId: PublicKey) {
198
- log('subscribed for message stream', { peerId });
199
- invariant(this._opened, 'Closed');
172
+ async unsubscribeMessages(peer: PeerInfo) {
173
+ log('subscribed for message stream', { peer });
174
+ invariant(this._lifecycleState === LifecycleState.OPEN);
200
175
 
201
- await this._forEachServer(async (server) => server.unsubscribeMessages(peerId));
202
- }
203
-
204
- private _initContext() {
205
- this._ctx = new Context({
206
- onError: (err) => log.catch(err),
207
- });
176
+ await this._forEachServer(async (server) => server.unsubscribeMessages(peer));
208
177
  }
209
178
 
210
179
  private async _forEachServer<ReturnType>(
@@ -3,12 +3,17 @@
3
3
  //
4
4
 
5
5
  import { type Event } from '@dxos/async';
6
+ import { type Lifecycle } from '@dxos/context';
6
7
  import { type PublicKey } from '@dxos/keys';
7
- import { type SwarmEvent, type SignalState } from '@dxos/protocols/proto/dxos/mesh/signal';
8
+ import { type Peer } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
9
+ import { type SignalState } from '@dxos/protocols/proto/dxos/mesh/signal';
10
+
11
+ export type PeerInfo = Partial<Peer> & Required<Pick<Peer, 'peerKey'>>;
12
+ export const PeerInfoHash = ({ peerKey }: PeerInfo) => peerKey;
8
13
 
9
14
  export interface Message {
10
- author: PublicKey;
11
- recipient: PublicKey;
15
+ author: PeerInfo;
16
+ recipient: PeerInfo;
12
17
  payload: {
13
18
  type_url: string;
14
19
  value: Uint8Array;
@@ -24,6 +29,25 @@ export type SignalStatus = {
24
29
  lastStateChange: Date;
25
30
  };
26
31
 
32
+ export type SwarmEvent = {
33
+ topic: PublicKey;
34
+
35
+ /**
36
+ * The peer was announced as available on the swarm.
37
+ */
38
+ peerAvailable?: {
39
+ peer: PeerInfo;
40
+ since: Date;
41
+ };
42
+
43
+ /**
44
+ * The peer left, or their announcement timed out.
45
+ */
46
+ peerLeft?: {
47
+ peer: PeerInfo;
48
+ };
49
+ };
50
+
27
51
  /**
28
52
  * Message routing interface.
29
53
  */
@@ -31,7 +55,7 @@ export interface SignalMethods {
31
55
  /**
32
56
  * Emits when other peers join or leave the swarm.
33
57
  */
34
- swarmEvent: Event<{ topic: PublicKey; swarmEvent: SwarmEvent }>;
58
+ swarmEvent: Event<SwarmEvent>;
35
59
 
36
60
  /**
37
61
  * Emits when a message is received.
@@ -41,12 +65,12 @@ export interface SignalMethods {
41
65
  /**
42
66
  * Join topic on signal network, to be discoverable by other peers.
43
67
  */
44
- join: (params: { topic: PublicKey; peerId: PublicKey }) => Promise<void>;
68
+ join: (params: { topic: PublicKey; peer: PeerInfo }) => Promise<void>;
45
69
 
46
70
  /**
47
71
  * Leave topic on signal network, to stop being discoverable by other peers.
48
72
  */
49
- leave: (params: { topic: PublicKey; peerId: PublicKey }) => Promise<void>;
73
+ leave: (params: { topic: PublicKey; peer: PeerInfo }) => Promise<void>;
50
74
 
51
75
  /**
52
76
  * Send message to peer.
@@ -57,19 +81,17 @@ export interface SignalMethods {
57
81
  * Start receiving messages from peer.
58
82
  */
59
83
  // TODO(burdon): Return unsubscribe function. Encapsulate callback/routing here.
60
- subscribeMessages: (peerId: PublicKey) => Promise<void>;
84
+ subscribeMessages: (peer: PeerInfo) => Promise<void>;
61
85
 
62
86
  /**
63
87
  * Stop receiving messages from peer.
64
88
  */
65
- unsubscribeMessages: (peerId: PublicKey) => Promise<void>;
89
+ unsubscribeMessages: (peer: PeerInfo) => Promise<void>;
66
90
  }
67
91
 
68
92
  /**
69
93
  * Signaling client.
70
94
  */
71
- export interface SignalClientMethods extends SignalMethods {
72
- open(): Promise<this>;
73
- close(): Promise<this>;
95
+ export interface SignalClientMethods extends SignalMethods, Required<Lifecycle> {
74
96
  getStatus(): SignalStatus;
75
97
  }
@@ -0,0 +1,8 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './test-builder';
6
+ export * from './test-peer';
7
+ export * from './test-messages';
8
+ export * from './utils';
@@ -0,0 +1,54 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ import { type PublicKey } from '@dxos/keys';
6
+
7
+ import { TestPeer } from './test-peer';
8
+ import { type SignalManager, MemorySignalManager, MemorySignalManagerContext } from '../signal-manager';
9
+ import { type Message } from '../signal-methods';
10
+
11
+ export type TestBuilderOptions = {
12
+ signalManagerFactory?: (identityKey: PublicKey, deviceKey: PublicKey) => Promise<SignalManager>;
13
+ messageDisruption?: (msg: Message) => Message[];
14
+ };
15
+
16
+ export class TestBuilder {
17
+ private _signalContext = new MemorySignalManagerContext();
18
+ private readonly _peers: TestPeer[] = [];
19
+
20
+ constructor(public options: TestBuilderOptions) {}
21
+
22
+ async createSignalManager(identityKey: PublicKey, deviceKey: PublicKey) {
23
+ const signalManager =
24
+ (await this.options.signalManagerFactory?.(identityKey, deviceKey)) ??
25
+ new MemorySignalManager(this._signalContext);
26
+
27
+ if (this.options.messageDisruption) {
28
+ // Imitates signal network disruptions (e. g. message doubling, ).
29
+ const trueSend = signalManager.sendMessage.bind(signalManager);
30
+ signalManager.sendMessage = async (message: Message) => {
31
+ for (const msg of this.options.messageDisruption!(message)) {
32
+ await trueSend(msg);
33
+ }
34
+ };
35
+ }
36
+
37
+ return signalManager;
38
+ }
39
+
40
+ async createPeer(): Promise<TestPeer> {
41
+ const peer = new TestPeer(this);
42
+ await peer.open();
43
+ this._peers.push(peer);
44
+ return peer;
45
+ }
46
+
47
+ async createPeers(count: number): Promise<TestPeer[]> {
48
+ return Promise.all(Array.from({ length: count }, () => this.createPeer()));
49
+ }
50
+
51
+ async close() {
52
+ await Promise.all(this._peers.map((peer) => peer.close()));
53
+ }
54
+ }
@@ -0,0 +1,24 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ import { type TaggedType } from '@dxos/codec-protobuf';
6
+ import { type TYPES } from '@dxos/protocols';
7
+
8
+ export const PAYLOAD_1: TaggedType<TYPES, 'google.protobuf.Any'> = {
9
+ '@type': 'google.protobuf.Any',
10
+ type_url: 'dxos.Example1',
11
+ value: Buffer.from('1'),
12
+ };
13
+
14
+ export const PAYLOAD_2: TaggedType<TYPES, 'google.protobuf.Any'> = {
15
+ '@type': 'google.protobuf.Any',
16
+ type_url: 'dxos.Example2',
17
+ value: Buffer.from('2'),
18
+ };
19
+
20
+ export const PAYLOAD_3: TaggedType<TYPES, 'google.protobuf.Any'> = {
21
+ '@type': 'google.protobuf.Any',
22
+ type_url: 'dxos.Example3',
23
+ value: Buffer.from('3'),
24
+ };
@@ -0,0 +1,66 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ import { Event } from '@dxos/async';
6
+ import { Resource } from '@dxos/context';
7
+ import { PublicKey } from '@dxos/keys';
8
+ import { log } from '@dxos/log';
9
+
10
+ import { type TestBuilder } from './test-builder';
11
+ import { expectPeerAvailable, expectPeerLeft, expectReceivedMessage } from './utils';
12
+ import { Messenger } from '../messenger';
13
+ import { type SignalManager } from '../signal-manager';
14
+ import { type Message, type PeerInfo } from '../signal-methods';
15
+
16
+ export class TestPeer extends Resource {
17
+ public peerId = PublicKey.random();
18
+ public identityKey = PublicKey.random();
19
+ public signalManager!: SignalManager;
20
+ public messenger!: Messenger;
21
+ public defaultReceived = new Event<Message>();
22
+
23
+ constructor(private readonly testBuilder: TestBuilder) {
24
+ super();
25
+ }
26
+
27
+ get peerInfo(): PeerInfo {
28
+ return {
29
+ peerKey: this.peerId.toHex(),
30
+ identityKey: this.identityKey.toHex(),
31
+ };
32
+ }
33
+
34
+ async waitTillReceive(message: Message) {
35
+ return expectReceivedMessage(this.defaultReceived, message);
36
+ }
37
+
38
+ async waitForPeerAvailable(topic: PublicKey, peer: PeerInfo) {
39
+ return expectPeerAvailable(this.signalManager, topic, peer);
40
+ }
41
+
42
+ async waitForPeerLeft(topic: PublicKey, peer: PeerInfo) {
43
+ return expectPeerLeft(this.signalManager, topic, peer);
44
+ }
45
+
46
+ protected override async _open() {
47
+ this.signalManager = await this.testBuilder.createSignalManager(this.identityKey, this.peerId);
48
+ this.messenger = new Messenger({ signalManager: this.signalManager });
49
+
50
+ await this.signalManager.open();
51
+ this.messenger.open();
52
+ await this.messenger
53
+ .listen({
54
+ peer: this.peerInfo,
55
+ onMessage: async (msg) => {
56
+ this.defaultReceived.emit(msg);
57
+ },
58
+ })
59
+ .catch((err) => log.catch(err));
60
+ }
61
+
62
+ protected override async _close() {
63
+ await this.messenger.close();
64
+ await this.signalManager.close();
65
+ }
66
+ }
@@ -0,0 +1,50 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ import { asyncTimeout, type Event } from '@dxos/async';
6
+ import { type Any } from '@dxos/codec-protobuf';
7
+ import { PublicKey } from '@dxos/keys';
8
+
9
+ import { PAYLOAD_1 } from './test-messages';
10
+ import { type SignalMethods, type Message, type PeerInfo } from '../signal-methods';
11
+
12
+ export const expectPeerAvailable = (client: SignalMethods, expectedTopic: PublicKey, peer: PeerInfo) =>
13
+ asyncTimeout(
14
+ client.swarmEvent.waitFor(
15
+ ({ peerAvailable, topic }) =>
16
+ !!peerAvailable && peer.peerKey === peerAvailable.peer.peerKey && expectedTopic.equals(topic),
17
+ ),
18
+ 1000,
19
+ );
20
+
21
+ export const expectPeerLeft = (client: SignalMethods, expectedTopic: PublicKey, peer: PeerInfo) =>
22
+ asyncTimeout(
23
+ client.swarmEvent.waitFor(
24
+ ({ peerLeft, topic }) => !!peerLeft && peer.peerKey === peerLeft.peer.peerKey && expectedTopic.equals(topic),
25
+ ),
26
+ 1000,
27
+ );
28
+
29
+ export const expectReceivedMessage = (event: Event<Message>, expectedMessage: Message) => {
30
+ return asyncTimeout(
31
+ event.waitFor(
32
+ (msg) =>
33
+ msg.author.peerKey === expectedMessage.author.peerKey &&
34
+ msg.recipient.peerKey === expectedMessage.recipient.peerKey &&
35
+ PublicKey.from(msg.payload.value).equals(expectedMessage.payload.value),
36
+ ),
37
+ 5000,
38
+ );
39
+ };
40
+
41
+ export const createMessage = (author: PeerInfo, recipient: PeerInfo, payload: Any = PAYLOAD_1): Message => ({
42
+ author,
43
+ recipient,
44
+ payload,
45
+ });
46
+
47
+ export const messageEqual = (msg1: Message, msg2: Message) =>
48
+ msg1.author.peerKey === msg2.author.peerKey &&
49
+ msg1.recipient.peerKey === msg2.recipient.peerKey &&
50
+ PublicKey.from(msg1.payload.value).equals(msg2.payload.value);
@@ -1,40 +0,0 @@
1
- import { Event } from '@dxos/async';
2
- import { PublicKey } from '@dxos/keys';
3
- import { type Runtime } from '@dxos/protocols/proto/dxos/config';
4
- import { Messenger } from './messenger';
5
- import { type SignalManager } from './signal-manager';
6
- import { type SignalMethods, type Message } from './signal-methods';
7
- export type TestBuilderOptions = {
8
- signalHosts?: Runtime.Services.Signal[];
9
- messageDisruption?: (msg: Message) => Message[];
10
- };
11
- export declare class TestBuilder {
12
- options: TestBuilderOptions;
13
- private _signalContext;
14
- private readonly _peers;
15
- constructor(options: TestBuilderOptions);
16
- createSignalManager(): SignalManager;
17
- createPeer(): TestPeer;
18
- close(): Promise<void>;
19
- }
20
- export declare class TestPeer {
21
- private readonly testBuilder;
22
- peerId: PublicKey;
23
- signalManager: SignalManager;
24
- messenger: Messenger;
25
- defaultReceived: Event<Message>;
26
- constructor(testBuilder: TestBuilder);
27
- waitTillReceive(message: Message): Promise<Message>;
28
- open(): Promise<void>;
29
- close(): Promise<void>;
30
- }
31
- export declare const expectPeerAvailable: (client: SignalMethods, expectedTopic: PublicKey, peer: PublicKey) => Promise<{
32
- topic: PublicKey;
33
- swarmEvent: import("packages/core/protocols/dist/esm/src/proto/gen/dxos/mesh/signal").SwarmEvent;
34
- }>;
35
- export declare const expectPeerLeft: (client: SignalMethods, expectedTopic: PublicKey, peer: PublicKey) => Promise<{
36
- topic: PublicKey;
37
- swarmEvent: import("packages/core/protocols/dist/esm/src/proto/gen/dxos/mesh/signal").SwarmEvent;
38
- }>;
39
- export declare const expectReceivedMessage: (client: SignalMethods, expectedMessage: Message) => Promise<Message>;
40
- //# sourceMappingURL=testing.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../../src/testing.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,mCAAmC,CAAC;AAEjE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEpE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;IACxC,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,EAAE,CAAC;CACjD,CAAC;AAEF,qBAAa,WAAW;IAIH,OAAO,EAAE,kBAAkB;IAH9C,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;gBAEtB,OAAO,EAAE,kBAAkB;IAE9C,mBAAmB;IAqBnB,UAAU,IAAI,QAAQ;IAMhB,KAAK;CAGZ;AAED,qBAAa,QAAQ;IAMP,OAAO,CAAC,QAAQ,CAAC,WAAW;IALjC,MAAM,YAAsB;IAC5B,aAAa,EAAE,aAAa,CAAC;IAC7B,SAAS,EAAE,SAAS,CAAC;IACrB,eAAe,iBAAwB;gBAEjB,WAAW,EAAE,WAAW;IAKrD,eAAe,CAAC,OAAO,EAAE,OAAO;IAS1B,IAAI;IAaJ,KAAK;CAIZ;AAED,eAAO,MAAM,mBAAmB,WAAY,aAAa,iBAAiB,SAAS,QAAQ,SAAS;;;EAIjG,CAAC;AAEJ,eAAO,MAAM,cAAc,WAAY,aAAa,iBAAiB,SAAS,QAAQ,SAAS;;;EAI5F,CAAC;AAEJ,eAAO,MAAM,qBAAqB,WAAY,aAAa,mBAAmB,OAAO,qBAOpF,CAAC"}
package/src/testing.ts DELETED
@@ -1,120 +0,0 @@
1
- //
2
- // Copyright 2022 DXOS.org
3
- //
4
-
5
- import { Event } from '@dxos/async';
6
- import { PublicKey } from '@dxos/keys';
7
- import { log } from '@dxos/log';
8
- import { type Runtime } from '@dxos/protocols/proto/dxos/config';
9
-
10
- import { Messenger } from './messenger';
11
- import {
12
- type SignalManager,
13
- MemorySignalManager,
14
- MemorySignalManagerContext,
15
- WebsocketSignalManager,
16
- } from './signal-manager';
17
- import { type SignalMethods, type Message } from './signal-methods';
18
-
19
- export type TestBuilderOptions = {
20
- signalHosts?: Runtime.Services.Signal[];
21
- messageDisruption?: (msg: Message) => Message[];
22
- };
23
-
24
- export class TestBuilder {
25
- private _signalContext = new MemorySignalManagerContext();
26
- private readonly _peers: TestPeer[] = [];
27
-
28
- constructor(public options: TestBuilderOptions) {}
29
-
30
- createSignalManager() {
31
- let signalManager: SignalManager;
32
- if (this.options.signalHosts) {
33
- signalManager = new WebsocketSignalManager(this.options.signalHosts);
34
- } else {
35
- signalManager = new MemorySignalManager(this._signalContext);
36
- }
37
-
38
- if (this.options.messageDisruption) {
39
- // Imitates signal network disruptions (e. g. message doubling, ).
40
- const trueSend = signalManager.sendMessage.bind(signalManager);
41
- signalManager.sendMessage = async (message) => {
42
- for (const msg of this.options.messageDisruption!(message)) {
43
- await trueSend(msg);
44
- }
45
- };
46
- }
47
-
48
- return signalManager;
49
- }
50
-
51
- createPeer(): TestPeer {
52
- const peer = new TestPeer(this);
53
- this._peers.push(peer);
54
- return peer;
55
- }
56
-
57
- async close() {
58
- await Promise.all(this._peers.map((peer) => peer.close()));
59
- }
60
- }
61
-
62
- export class TestPeer {
63
- public peerId = PublicKey.random();
64
- public signalManager: SignalManager;
65
- public messenger: Messenger;
66
- public defaultReceived = new Event<Message>();
67
-
68
- constructor(private readonly testBuilder: TestBuilder) {
69
- this.signalManager = testBuilder.createSignalManager();
70
- this.messenger = new Messenger({ signalManager: this.signalManager });
71
- }
72
-
73
- waitTillReceive(message: Message) {
74
- return this.defaultReceived.waitFor(
75
- (msg) =>
76
- msg.author.equals(message.author) &&
77
- msg.recipient.equals(message.recipient) &&
78
- msg.payload.value.every((value, index) => value === message.payload.value[index]),
79
- );
80
- }
81
-
82
- async open() {
83
- await this.signalManager.open();
84
- this.messenger.open();
85
- await this.messenger
86
- .listen({
87
- peerId: this.peerId,
88
- onMessage: async (msg) => {
89
- this.defaultReceived.emit(msg);
90
- },
91
- })
92
- .catch((err) => log.catch(err));
93
- }
94
-
95
- async close() {
96
- await this.messenger.close();
97
- await this.signalManager.close();
98
- }
99
- }
100
-
101
- export const expectPeerAvailable = (client: SignalMethods, expectedTopic: PublicKey, peer: PublicKey) =>
102
- client.swarmEvent.waitFor(
103
- ({ swarmEvent, topic }) =>
104
- !!swarmEvent.peerAvailable && peer.equals(swarmEvent.peerAvailable.peer) && expectedTopic.equals(topic),
105
- );
106
-
107
- export const expectPeerLeft = (client: SignalMethods, expectedTopic: PublicKey, peer: PublicKey) =>
108
- client.swarmEvent.waitFor(
109
- ({ swarmEvent, topic }) =>
110
- !!swarmEvent.peerLeft && peer.equals(swarmEvent.peerLeft.peer) && expectedTopic.equals(topic),
111
- );
112
-
113
- export const expectReceivedMessage = (client: SignalMethods, expectedMessage: Message) => {
114
- return client.onMessage.waitFor(
115
- (msg) =>
116
- msg.author.equals(expectedMessage.author) &&
117
- msg.recipient.equals(expectedMessage.recipient) &&
118
- PublicKey.from(msg.payload.value).equals(expectedMessage.payload.value),
119
- );
120
- };