@dxos/echo-pipeline 0.5.8 → 0.5.9-main.0a0e87d

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 (54) hide show
  1. package/dist/lib/browser/{chunk-GANAND63.mjs → chunk-I2J5TTHJ.mjs} +48 -23
  2. package/dist/lib/browser/{chunk-GANAND63.mjs.map → chunk-I2J5TTHJ.mjs.map} +3 -3
  3. package/dist/lib/browser/index.mjs +89 -51
  4. package/dist/lib/browser/index.mjs.map +3 -3
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +99 -4
  7. package/dist/lib/browser/testing/index.mjs.map +4 -4
  8. package/dist/lib/node/{chunk-M475BGBI.cjs → chunk-QPCNQ4ZK.cjs} +51 -25
  9. package/dist/lib/node/chunk-QPCNQ4ZK.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +110 -72
  11. package/dist/lib/node/index.cjs.map +3 -3
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/testing/index.cjs +107 -13
  14. package/dist/lib/node/testing/index.cjs.map +4 -4
  15. package/dist/types/src/automerge/automerge-doc-loader.d.ts +7 -3
  16. package/dist/types/src/automerge/automerge-doc-loader.d.ts.map +1 -1
  17. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
  18. package/dist/types/src/automerge/echo-network-adapter.d.ts +6 -0
  19. package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
  20. package/dist/types/src/automerge/echo-replicator.d.ts +1 -0
  21. package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
  22. package/dist/types/src/automerge/mesh-echo-replicator.d.ts.map +1 -1
  23. package/dist/types/src/automerge/migrations.d.ts.map +1 -1
  24. package/dist/types/src/space/space-manager.d.ts +2 -2
  25. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  26. package/dist/types/src/space/space-protocol.d.ts +2 -2
  27. package/dist/types/src/space/space-protocol.d.ts.map +1 -1
  28. package/dist/types/src/space/space.d.ts +9 -1
  29. package/dist/types/src/space/space.d.ts.map +1 -1
  30. package/dist/types/src/testing/index.d.ts +1 -0
  31. package/dist/types/src/testing/index.d.ts.map +1 -1
  32. package/dist/types/src/testing/test-agent-builder.d.ts +2 -2
  33. package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
  34. package/dist/types/src/testing/test-network-adapter.d.ts +18 -0
  35. package/dist/types/src/testing/test-network-adapter.d.ts.map +1 -0
  36. package/package.json +33 -33
  37. package/src/automerge/automerge-doc-loader.test.ts +5 -2
  38. package/src/automerge/automerge-doc-loader.ts +6 -4
  39. package/src/automerge/automerge-host.test.ts +1 -553
  40. package/src/automerge/automerge-host.ts +12 -5
  41. package/src/automerge/automerge-repo.test.ts +450 -2
  42. package/src/automerge/echo-network-adapter.ts +24 -8
  43. package/src/automerge/echo-replicator.ts +2 -0
  44. package/src/automerge/mesh-echo-replicator.ts +3 -1
  45. package/src/automerge/migrations.ts +2 -1
  46. package/src/automerge/storage-adapter.test.ts +81 -15
  47. package/src/space/space-manager.ts +6 -4
  48. package/src/space/space-protocol.test.ts +5 -3
  49. package/src/space/space-protocol.ts +3 -3
  50. package/src/space/space.ts +32 -2
  51. package/src/testing/index.ts +1 -0
  52. package/src/testing/test-agent-builder.ts +4 -4
  53. package/src/testing/test-network-adapter.ts +62 -0
  54. package/dist/lib/node/chunk-M475BGBI.cjs.map +0 -7
@@ -5,18 +5,24 @@
5
5
  import { expect } from 'chai';
6
6
 
7
7
  import { type StorageAdapterInterface } from '@dxos/automerge/automerge-repo';
8
+ import { randomBytes } from '@dxos/crypto';
8
9
  import { PublicKey } from '@dxos/keys';
9
10
  import { createTestLevel } from '@dxos/kv-store/testing';
10
11
  import { StorageType, createStorage } from '@dxos/random-access-storage';
11
- import { afterTest, describe, openAndClose, test } from '@dxos/test';
12
- import { type MaybePromise } from '@dxos/util';
12
+ import { afterTest, describe, test } from '@dxos/test';
13
+ import { arrayToBuffer, bufferToArray, type MaybePromise } from '@dxos/util';
13
14
 
14
15
  import { AutomergeStorageAdapter } from './automerge-storage-adapter';
15
16
  import { LevelDBStorageAdapter } from './leveldb-storage-adapter';
16
17
 
17
18
  const runTests = (
18
19
  testNamespace: string,
19
- /** Run per test. Expects automatic clean-up with `afterTest`. */ createAdapter: () => MaybePromise<StorageAdapterInterface>,
20
+ /** Run per test. Expects automatic clean-up with `afterTest`. */
21
+ createAdapter: (root?: string) => MaybePromise<{
22
+ adapter: StorageAdapterInterface;
23
+ /** Would be called automatically with `afterTest`. Exposed for mid-test clean-up. */
24
+ close: () => MaybePromise<void>;
25
+ }>,
20
26
  ) => {
21
27
  describe(testNamespace, () => {
22
28
  const chunks = [
@@ -27,14 +33,14 @@ const runTests = (
27
33
  ];
28
34
 
29
35
  test('should store and retrieve data', async () => {
30
- const adapter = await createAdapter();
36
+ const { adapter } = await createAdapter();
31
37
 
32
38
  await adapter.save(chunks[0].key, chunks[0].data);
33
39
  expect(await adapter.load(chunks[0].key)).to.deep.equal(chunks[0].data);
34
40
  });
35
41
 
36
42
  test('loadRange return inputs with correct prefixes', async () => {
37
- const adapter = await createAdapter();
43
+ const { adapter } = await createAdapter();
38
44
 
39
45
  for (const chunk of chunks) {
40
46
  await adapter.save(chunk.key, chunk.data);
@@ -46,7 +52,7 @@ const runTests = (
46
52
  });
47
53
 
48
54
  test('deletion works', async () => {
49
- const adapter = await createAdapter();
55
+ const { adapter } = await createAdapter();
50
56
 
51
57
  for (const chunk of chunks) {
52
58
  await adapter.save(chunk.key, chunk.data);
@@ -64,27 +70,87 @@ const runTests = (
64
70
  expect(await adapter.load(['a', 'b', 'c', '2'])).to.deep.equal(chunks[1].data);
65
71
  expect(await adapter.load(['a', 'b', 'd', '3'])).to.be.undefined;
66
72
  });
73
+
74
+ test('loadRange', async () => {
75
+ const root = `/tmp/${randomBytes(16).toString('hex')}`;
76
+ {
77
+ const { adapter, close } = await createAdapter(root);
78
+ await adapter.save(['test', '1'], bufferToArray(Buffer.from('one')));
79
+ await adapter.save(['test', '2'], bufferToArray(Buffer.from('two')));
80
+ await adapter.save(['bar', '1'], bufferToArray(Buffer.from('bar')));
81
+ await close();
82
+ }
83
+
84
+ {
85
+ const { adapter } = await createAdapter(root);
86
+ const range = await adapter.loadRange(['test']);
87
+ expect(range.map((chunk) => arrayToBuffer(chunk.data!).toString())).to.deep.eq(['one', 'two']);
88
+ expect(range.map((chunk) => chunk.key)).to.deep.eq([
89
+ ['test', '1'],
90
+ ['test', '2'],
91
+ ]);
92
+ }
93
+ });
94
+
95
+ test('removeRange', async () => {
96
+ const root = `/tmp/${randomBytes(16).toString('hex')}`;
97
+ {
98
+ const { adapter, close } = await createAdapter(root);
99
+ await adapter.save(['test', '1'], bufferToArray(Buffer.from('one')));
100
+ await adapter.save(['test', '2'], bufferToArray(Buffer.from('two')));
101
+ await adapter.save(['bar', '1'], bufferToArray(Buffer.from('bar')));
102
+ await close();
103
+ }
104
+
105
+ {
106
+ const { adapter } = await createAdapter(root);
107
+ await adapter.removeRange(['test']);
108
+ const range = await adapter.loadRange(['test']);
109
+ expect(range.map((chunk) => arrayToBuffer(chunk.data!).toString())).to.deep.eq([]);
110
+ const range2 = await adapter.loadRange(['bar']);
111
+ expect(range2.map((chunk) => arrayToBuffer(chunk.data!).toString())).to.deep.eq(['bar']);
112
+ expect(range2.map((chunk) => chunk.key)).to.deep.eq([['bar', '1']]);
113
+ }
114
+ });
67
115
  });
68
116
  };
69
117
 
70
118
  /**
71
119
  * Run tests for AutomergeStorageAdapter.
72
120
  */
73
- runTests('AutomergeStorageAdapter', () => {
74
- const storage = createStorage({ type: StorageType.RAM });
75
- afterTest(() => storage.close());
121
+ runTests('AutomergeStorageAdapter', (root?: string) => {
122
+ const storage = createStorage({ type: root ? StorageType.NODE : StorageType.RAM, root });
76
123
  const dir = storage.createDirectory('automerge');
77
124
  const adapter = new AutomergeStorageAdapter(dir);
78
- afterTest(() => adapter.close());
79
- return adapter;
125
+
126
+ const close = async () => {
127
+ await adapter.close();
128
+ await storage.close();
129
+ };
130
+ afterTest(close);
131
+
132
+ return {
133
+ adapter,
134
+ close,
135
+ };
80
136
  });
81
137
 
82
138
  /**
83
139
  * Run tests for LevelDBStorageAdapter.
84
140
  */
85
- runTests('LevelDBStorageAdapter', async () => {
86
- const level = createTestLevel();
141
+ runTests('LevelDBStorageAdapter', async (root?: string) => {
142
+ const level = createTestLevel(root);
143
+ await level.open();
87
144
  const adapter = new LevelDBStorageAdapter({ db: level.sublevel('automerge') });
88
- await openAndClose(level, adapter as any);
89
- return adapter;
145
+ await adapter.open?.();
146
+
147
+ const close = async () => {
148
+ await adapter.close();
149
+ await level.close();
150
+ };
151
+ afterTest(close);
152
+ return {
153
+ adapter,
154
+ close,
155
+ };
90
156
  });
@@ -8,7 +8,7 @@ import { failUndefined } from '@dxos/debug';
8
8
  import { type FeedStore } from '@dxos/feed-store';
9
9
  import { PublicKey } from '@dxos/keys';
10
10
  import { log } from '@dxos/log';
11
- import { type NetworkManager } from '@dxos/network-manager';
11
+ import { type SwarmNetworkManager } from '@dxos/network-manager';
12
12
  import { trace } from '@dxos/protocols';
13
13
  import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
14
14
  import { type SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
@@ -16,14 +16,14 @@ import { type Teleport } from '@dxos/teleport';
16
16
  import { type BlobStore } from '@dxos/teleport-extension-object-sync';
17
17
  import { ComplexMap } from '@dxos/util';
18
18
 
19
- import { Space } from './space';
19
+ import { Space, createIdFromSpaceKey } from './space';
20
20
  import { SpaceProtocol, type SwarmIdentity } from './space-protocol';
21
21
  import { SnapshotManager, type SnapshotStore } from '../db-host';
22
22
  import { type MetadataStore } from '../metadata';
23
23
 
24
24
  export type SpaceManagerParams = {
25
25
  feedStore: FeedStore<FeedMessage>;
26
- networkManager: NetworkManager;
26
+ networkManager: SwarmNetworkManager;
27
27
  metadataStore: MetadataStore;
28
28
 
29
29
  /**
@@ -54,7 +54,7 @@ export type ConstructSpaceParams = {
54
54
  export class SpaceManager {
55
55
  private readonly _spaces = new ComplexMap<PublicKey, Space>(PublicKey.hash);
56
56
  private readonly _feedStore: FeedStore<FeedMessage>;
57
- private readonly _networkManager: NetworkManager;
57
+ private readonly _networkManager: SwarmNetworkManager;
58
58
  private readonly _metadataStore: MetadataStore;
59
59
  private readonly _snapshotStore: SnapshotStore;
60
60
  private readonly _blobStore: BlobStore;
@@ -98,6 +98,7 @@ export class SpaceManager {
98
98
  const genesisFeed = await this._feedStore.openFeed(metadata.genesisFeedKey ?? failUndefined());
99
99
 
100
100
  const spaceKey = metadata.key;
101
+ const spaceId = await createIdFromSpaceKey(spaceKey);
101
102
  const protocol = new SpaceProtocol({
102
103
  topic: spaceKey,
103
104
  swarmIdentity,
@@ -109,6 +110,7 @@ export class SpaceManager {
109
110
  const snapshotManager = new SnapshotManager(this._snapshotStore, this._blobStore, protocol.blobSync);
110
111
 
111
112
  const space = new Space({
113
+ id: spaceId,
112
114
  spaceKey,
113
115
  protocol,
114
116
  genesisFeed,
@@ -7,7 +7,7 @@ import waitForExpect from 'wait-for-expect';
7
7
 
8
8
  import { PublicKey } from '@dxos/keys';
9
9
  import { MemorySignalManager, MemorySignalManagerContext } from '@dxos/messaging';
10
- import { MemoryTransportFactory, NetworkManager } from '@dxos/network-manager';
10
+ import { MemoryTransportFactory, SwarmNetworkManager } from '@dxos/network-manager';
11
11
  import { StorageType, createStorage } from '@dxos/random-access-storage';
12
12
  import { BlobStore } from '@dxos/teleport-extension-object-sync';
13
13
  import { describe, test, afterTest } from '@dxos/test';
@@ -25,11 +25,13 @@ describe('space/space-protocol', () => {
25
25
  const peer1 = await builder.createPeer();
26
26
  const gossip1 = peer1.createGossip();
27
27
  const presence1 = peer1.createPresence(gossip1);
28
+ await presence1.open();
28
29
  const protocol1 = peer1.createSpaceProtocol(topic, gossip1);
29
30
 
30
31
  const peer2 = await builder.createPeer();
31
32
  const gossip2 = peer2.createGossip();
32
33
  const presence2 = peer2.createPresence(gossip2);
34
+ await presence2.open();
33
35
  const protocol2 = peer2.createSpaceProtocol(topic, gossip2);
34
36
 
35
37
  await protocol1.start();
@@ -56,7 +58,7 @@ describe('space/space-protocol', () => {
56
58
  credentialAuthenticator: async () => false, // Reject everyone.
57
59
  },
58
60
  blobStore: new BlobStore(createStorage({ type: StorageType.RAM }).createDirectory()),
59
- networkManager: new NetworkManager({
61
+ networkManager: new SwarmNetworkManager({
60
62
  signalManager: new MemorySignalManager(signalContext),
61
63
  transportFactory: MemoryTransportFactory,
62
64
  }),
@@ -70,7 +72,7 @@ describe('space/space-protocol', () => {
70
72
  credentialAuthenticator: MOCK_AUTH_VERIFIER,
71
73
  },
72
74
  blobStore: new BlobStore(createStorage({ type: StorageType.RAM }).createDirectory()),
73
- networkManager: new NetworkManager({
75
+ networkManager: new SwarmNetworkManager({
74
76
  signalManager: new MemorySignalManager(signalContext),
75
77
  transportFactory: MemoryTransportFactory,
76
78
  }),
@@ -9,7 +9,7 @@ import { PublicKey } from '@dxos/keys';
9
9
  import { log, logInfo } from '@dxos/log';
10
10
  import {
11
11
  MMSTTopology,
12
- type NetworkManager,
12
+ type SwarmNetworkManager,
13
13
  type SwarmConnection,
14
14
  type WireProtocol,
15
15
  type WireProtocolParams,
@@ -37,7 +37,7 @@ export interface SwarmIdentity {
37
37
  export type SpaceProtocolOptions = {
38
38
  topic: PublicKey; // TODO(burdon): Rename?
39
39
  swarmIdentity: SwarmIdentity;
40
- networkManager: NetworkManager;
40
+ networkManager: SwarmNetworkManager;
41
41
 
42
42
  blobStore: BlobStore;
43
43
 
@@ -54,7 +54,7 @@ export type SpaceProtocolOptions = {
54
54
  */
55
55
  @trace.resource()
56
56
  export class SpaceProtocol {
57
- private readonly _networkManager: NetworkManager;
57
+ private readonly _networkManager: SwarmNetworkManager;
58
58
  private readonly _swarmIdentity: SwarmIdentity;
59
59
  private readonly _onSessionAuth?: (session: Teleport) => void;
60
60
  private readonly _onAuthFailure?: (session: Teleport) => void;
@@ -5,15 +5,16 @@
5
5
  import { Event, Mutex, synchronized, trackLeaks } from '@dxos/async';
6
6
  import { type Context, LifecycleState, Resource } from '@dxos/context';
7
7
  import { type DelegateInvitationCredential, type FeedInfo, type MemberInfo } from '@dxos/credentials';
8
+ import { subtleCrypto } from '@dxos/crypto';
8
9
  import { type FeedOptions, type FeedWrapper } from '@dxos/feed-store';
9
10
  import { invariant } from '@dxos/invariant';
10
- import { type PublicKey } from '@dxos/keys';
11
+ import { PublicKey, SpaceId } from '@dxos/keys';
11
12
  import { log, logInfo } from '@dxos/log';
12
13
  import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
13
14
  import { AdmittedFeed, type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
14
15
  import { type Timeframe } from '@dxos/timeframe';
15
16
  import { trace } from '@dxos/tracing';
16
- import { type AsyncCallback, Callback } from '@dxos/util';
17
+ import { type AsyncCallback, Callback, ComplexMap } from '@dxos/util';
17
18
 
18
19
  import { ControlPipeline } from './control-pipeline';
19
20
  import { type SpaceProtocol } from './space-protocol';
@@ -25,6 +26,7 @@ import { type PipelineAccessor } from '../pipeline';
25
26
  type FeedProvider = (feedKey: PublicKey, opts?: FeedOptions) => Promise<FeedWrapper<FeedMessage>>;
26
27
 
27
28
  export type SpaceParams = {
29
+ id: SpaceId;
28
30
  spaceKey: PublicKey;
29
31
  protocol: SpaceProtocol;
30
32
  genesisFeed: FeedWrapper<FeedMessage>;
@@ -59,6 +61,7 @@ export class Space extends Resource {
59
61
  @trace.info()
60
62
  public readonly protocol: SpaceProtocol;
61
63
 
64
+ private readonly _id: SpaceId;
62
65
  private readonly _key: PublicKey;
63
66
  private readonly _genesisFeedKey: PublicKey;
64
67
  private readonly _feedProvider: FeedProvider;
@@ -73,6 +76,7 @@ export class Space extends Resource {
73
76
  constructor(params: SpaceParams) {
74
77
  super();
75
78
  invariant(params.spaceKey && params.feedProvider);
79
+ this._id = params.id;
76
80
  this._key = params.spaceKey;
77
81
  this._genesisFeedKey = params.genesisFeed.key;
78
82
  this._feedProvider = params.feedProvider;
@@ -120,6 +124,12 @@ export class Space extends Resource {
120
124
  this.protocol.addFeed(params.genesisFeed);
121
125
  }
122
126
 
127
+ @logInfo
128
+ @trace.info()
129
+ get id() {
130
+ return this._id;
131
+ }
132
+
123
133
  @logInfo
124
134
  @trace.info()
125
135
  get key() {
@@ -199,3 +209,23 @@ export class Space extends Resource {
199
209
  log('closed');
200
210
  }
201
211
  }
212
+
213
+ const SPACE_IDS_CACHE = new ComplexMap<PublicKey, SpaceId>(PublicKey.hash);
214
+
215
+ /**
216
+ * Space keys are generated by creating a keypair, and then taking the first 20 bytes of the SHA-256 hash of the public key and encoding them to multibase RFC4648 base-32 format (prefixed with B, see Multibase Table).
217
+ * Inspired by how ethereum addresses are derived.
218
+ */
219
+ export const createIdFromSpaceKey = async (spaceKey: PublicKey): Promise<SpaceId> => {
220
+ const cachedValue = SPACE_IDS_CACHE.get(spaceKey);
221
+ if (cachedValue !== undefined) {
222
+ return cachedValue;
223
+ }
224
+
225
+ const digest = await subtleCrypto.digest('SHA-256', spaceKey.asUint8Array());
226
+
227
+ const bytes = new Uint8Array(digest).slice(0, SpaceId.byteLength);
228
+ const spaceId = SpaceId.encode(bytes);
229
+ SPACE_IDS_CACHE.set(spaceKey, spaceId);
230
+ return spaceId;
231
+ };
@@ -5,3 +5,4 @@
5
5
  export * from './change-metadata';
6
6
  export * from './test-agent-builder';
7
7
  export * from './test-feed-builder';
8
+ export * from './test-network-adapter';
@@ -8,7 +8,7 @@ import { type FeedStore } from '@dxos/feed-store';
8
8
  import { type Keyring } from '@dxos/keyring';
9
9
  import { PublicKey } from '@dxos/keys';
10
10
  import { MemorySignalManager, MemorySignalManagerContext, WebsocketSignalManager } from '@dxos/messaging';
11
- import { MemoryTransportFactory, NetworkManager, createSimplePeerTransportFactory } from '@dxos/network-manager';
11
+ import { MemoryTransportFactory, SwarmNetworkManager, createSimplePeerTransportFactory } from '@dxos/network-manager';
12
12
  import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
13
13
  import { type SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
14
14
  import { AdmittedFeed } from '@dxos/protocols/proto/dxos/halo/credentials';
@@ -22,12 +22,12 @@ import { SnapshotStore } from '../db-host';
22
22
  import { MetadataStore } from '../metadata';
23
23
  import { MOCK_AUTH_PROVIDER, MOCK_AUTH_VERIFIER, SpaceManager, SpaceProtocol, type Space } from '../space';
24
24
 
25
- export type NetworkManagerProvider = () => NetworkManager;
25
+ export type NetworkManagerProvider = () => SwarmNetworkManager;
26
26
 
27
27
  export const MemoryNetworkManagerProvider =
28
28
  (signalContext: MemorySignalManagerContext): NetworkManagerProvider =>
29
29
  () =>
30
- new NetworkManager({
30
+ new SwarmNetworkManager({
31
31
  signalManager: new MemorySignalManager(signalContext),
32
32
  transportFactory: MemoryTransportFactory,
33
33
  });
@@ -35,7 +35,7 @@ export const MemoryNetworkManagerProvider =
35
35
  export const WebsocketNetworkManagerProvider =
36
36
  (signalUrl: string): NetworkManagerProvider =>
37
37
  () =>
38
- new NetworkManager({
38
+ new SwarmNetworkManager({
39
39
  signalManager: new WebsocketSignalManager([{ server: signalUrl }]),
40
40
  transportFactory: createSimplePeerTransportFactory(),
41
41
  });
@@ -0,0 +1,62 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { Trigger, sleep } from '@dxos/async';
6
+ import { type Message, NetworkAdapter, type PeerId } from '@dxos/automerge/automerge-repo';
7
+ import { invariant } from '@dxos/invariant';
8
+ import { log } from '@dxos/log';
9
+
10
+ export class TestAdapter extends NetworkAdapter {
11
+ static createPair() {
12
+ const adapter1: TestAdapter = new TestAdapter({
13
+ send: (message: Message) => sleep(10).then(() => adapter2.receive(message)),
14
+ });
15
+ const adapter2: TestAdapter = new TestAdapter({
16
+ send: (message: Message) => sleep(10).then(() => adapter1.receive(message)),
17
+ });
18
+
19
+ return [adapter1, adapter2];
20
+ }
21
+
22
+ public onConnect = new Trigger();
23
+
24
+ constructor(private readonly _params: { send: (message: Message) => void }) {
25
+ super();
26
+ }
27
+
28
+ // NOTE: Emitting `ready` event in NetworkAdapter`s constructor causes a race condition
29
+ // because `Repo` waits for `ready` event (which it never receives) before it starts using the adapter.
30
+ ready() {
31
+ this.emit('ready', { network: this });
32
+ }
33
+
34
+ override connect(peerId: PeerId) {
35
+ this.peerId = peerId;
36
+ this.onConnect.wake();
37
+ }
38
+
39
+ peerCandidate(peerId: PeerId) {
40
+ invariant(peerId, 'PeerId is required');
41
+ this.emit('peer-candidate', { peerId, peerMetadata: {} });
42
+ }
43
+
44
+ peerDisconnected(peerId: PeerId) {
45
+ invariant(peerId, 'PeerId is required');
46
+ this.emit('peer-disconnected', { peerId });
47
+ }
48
+
49
+ override send(message: Message) {
50
+ log('send', { from: message.senderId, to: message.targetId, type: message.type });
51
+ this._params.send(message);
52
+ }
53
+
54
+ override disconnect() {
55
+ this.peerId = undefined;
56
+ }
57
+
58
+ receive(message: Message) {
59
+ invariant(this.peerId, 'Peer id is not set');
60
+ this.emit('message', message);
61
+ }
62
+ }