@dxos/echo-pipeline 0.3.9-next.4584ca9 → 0.3.9-next.8ad58eb

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.
@@ -14,8 +14,11 @@ import {
14
14
  } from '@dxos/automerge/automerge-repo';
15
15
  import { Stream } from '@dxos/codec-protobuf';
16
16
  import { invariant } from '@dxos/invariant';
17
+ import { log } from '@dxos/log';
17
18
  import { type HostInfo, type SyncRepoRequest, type SyncRepoResponse } from '@dxos/protocols/proto/dxos/echo/service';
19
+ import { type PeerInfo } from '@dxos/protocols/proto/dxos/mesh/teleport/automerge';
18
20
  import { type Directory } from '@dxos/random-access-storage';
21
+ import { AutomergeReplicator } from '@dxos/teleport-extension-automerge-replicator';
19
22
  import { arrayToBuffer, bufferToArray } from '@dxos/util';
20
23
 
21
24
  export class AutomergeHost {
@@ -29,17 +32,14 @@ export class AutomergeHost {
29
32
  this._clientNetwork = new LocalHostNetworkAdapter();
30
33
  this._storage = new AutomergeStorageAdapter(storageDirectory);
31
34
  this._repo = new Repo({
32
- network: [
33
- // this._meshNetwork,
34
- this._clientNetwork,
35
- ],
36
-
35
+ network: [this._clientNetwork, this._meshNetwork],
37
36
  storage: this._storage,
38
37
 
39
38
  // TODO(dmaretskyi): Share based on HALO permissions and space affinity.
40
39
  sharePolicy: async (peerId, documentId) => true, // Share everything.
41
40
  });
42
41
  this._clientNetwork.ready();
42
+ this._meshNetwork.ready();
43
43
  }
44
44
 
45
45
  get repo(): Repo {
@@ -65,6 +65,14 @@ export class AutomergeHost {
65
65
  getHostInfo(): HostInfo {
66
66
  return this._clientNetwork.getHostInfo();
67
67
  }
68
+
69
+ //
70
+ // Mesh replication.
71
+ //
72
+
73
+ createExtension(): AutomergeReplicator {
74
+ return this._meshNetwork.createExtension();
75
+ }
68
76
  }
69
77
 
70
78
  type ClientSyncState = {
@@ -159,16 +167,64 @@ class LocalHostNetworkAdapter extends NetworkAdapter {
159
167
  * Used to replicate with other peers over the network.
160
168
  */
161
169
  class MeshNetworkAdapter extends NetworkAdapter {
170
+ private readonly _extensions: Map<string, AutomergeReplicator> = new Map();
171
+
172
+ /**
173
+ * Emits `ready` event. That signals to `Repo` that it can start using the adapter.
174
+ */
175
+ ready() {
176
+ // NOTE: Emitting `ready` event in NetworkAdapter`s constructor causes a race condition
177
+ // because `Repo` waits for `ready` event (which it never receives) before it starts using the adapter.
178
+ this.emit('ready', {
179
+ network: this,
180
+ });
181
+ }
182
+
162
183
  override connect(peerId: PeerId): void {
163
- throw new Error('Method not implemented.');
184
+ this.peerId = peerId;
164
185
  }
165
186
 
166
187
  override send(message: Message): void {
167
- throw new Error('Method not implemented.');
188
+ const receiverId = message.targetId;
189
+ const extension = this._extensions.get(receiverId);
190
+ invariant(extension, 'Extension not found.');
191
+ extension.sendSyncMessage({ payload: cbor.encode(message) }).catch((err) => log.catch(err));
168
192
  }
169
193
 
170
194
  override disconnect(): void {
171
- throw new Error('Method not implemented.');
195
+ // No-op
196
+ }
197
+
198
+ createExtension(): AutomergeReplicator {
199
+ invariant(this.peerId, 'Peer id not set.');
200
+
201
+ let peerInfo: PeerInfo;
202
+ const extension = new AutomergeReplicator(
203
+ {
204
+ peerId: this.peerId,
205
+ },
206
+ {
207
+ onStartReplication: async (info) => {
208
+ peerInfo = info;
209
+ // TODO(mykola): Fix race condition?
210
+ this._extensions.set(info.id, extension);
211
+ this.emit('peer-candidate', {
212
+ peerId: info.id as PeerId,
213
+ });
214
+ },
215
+ onSyncMessage: async ({ payload }) => {
216
+ const message = cbor.decode(payload) as Message;
217
+ this.emit('message', message);
218
+ },
219
+ onClose: async () => {
220
+ peerInfo &&
221
+ this.emit('peer-disconnected', {
222
+ peerId: peerInfo.id as PeerId,
223
+ });
224
+ },
225
+ },
226
+ );
227
+ return extension;
172
228
  }
173
229
  }
174
230
 
@@ -39,6 +39,9 @@ export type ConstructSpaceParams = {
39
39
  metadata: SpaceMetadata;
40
40
  swarmIdentity: SwarmIdentity;
41
41
  memberKey: PublicKey;
42
+ /**
43
+ * Called when connection auth passed successful.
44
+ */
42
45
  onNetworkConnection: (session: Teleport) => void;
43
46
  onAuthFailure?: (session: Teleport) => void;
44
47
  };