@dxos/network-manager 0.1.20 → 0.1.21

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 (35) hide show
  1. package/dist/lib/browser/index.mjs +279 -116
  2. package/dist/lib/browser/index.mjs.map +3 -3
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/testing/index.mjs +288 -116
  5. package/dist/lib/browser/testing/index.mjs.map +3 -3
  6. package/dist/lib/node/index.cjs +274 -107
  7. package/dist/lib/node/index.cjs.map +3 -3
  8. package/dist/lib/node/meta.json +1 -1
  9. package/dist/lib/node/testing/index.cjs +283 -107
  10. package/dist/lib/node/testing/index.cjs.map +3 -3
  11. package/dist/types/src/network-manager.d.ts +6 -0
  12. package/dist/types/src/network-manager.d.ts.map +1 -1
  13. package/dist/types/src/swarm/peer.d.ts.map +1 -1
  14. package/dist/types/src/swarm/swarm.d.ts +4 -2
  15. package/dist/types/src/swarm/swarm.d.ts.map +1 -1
  16. package/dist/types/src/testing/test-builder.d.ts +2 -0
  17. package/dist/types/src/testing/test-builder.d.ts.map +1 -1
  18. package/dist/types/src/tests/basic-test-suite.d.ts.map +1 -1
  19. package/dist/types/src/transport/webrtc-transport-proxy.d.ts +2 -1
  20. package/dist/types/src/transport/webrtc-transport-proxy.d.ts.map +1 -1
  21. package/dist/types/src/transport/webrtc-transport.d.ts +1 -0
  22. package/dist/types/src/transport/webrtc-transport.d.ts.map +1 -1
  23. package/package.json +16 -16
  24. package/src/network-manager.ts +44 -3
  25. package/src/signal/integration.test.ts +2 -0
  26. package/src/signal/message-router.test.ts +1 -1
  27. package/src/swarm/peer.ts +2 -1
  28. package/src/swarm/swarm.test.ts +1 -1
  29. package/src/swarm/swarm.ts +86 -62
  30. package/src/testing/test-builder.ts +9 -0
  31. package/src/tests/basic-test-suite.ts +31 -4
  32. package/src/tests/webrtc-transport.test.ts +3 -0
  33. package/src/transport/webrtc-transport-proxy.test.ts +2 -1
  34. package/src/transport/webrtc-transport-proxy.ts +9 -3
  35. package/src/transport/webrtc-transport.ts +6 -1
@@ -29,12 +29,38 @@ var __decorate = function(decorators, target, key, desc) {
29
29
  return c > 3 && r && Object.defineProperty(target, key, r), r;
30
30
  };
31
31
  var ConnectionState;
32
- (function(ConnectionState4) {
33
- ConnectionState4["INITIAL"] = "INITIAL";
34
- ConnectionState4["CONNECTING"] = "CONNECTING";
35
- ConnectionState4["CONNECTED"] = "CONNECTED";
36
- ConnectionState4["CLOSING"] = "CLOSING";
37
- ConnectionState4["CLOSED"] = "CLOSED";
32
+ (function(ConnectionState5) {
33
+ ConnectionState5[
34
+ /**
35
+ * Initial state. Connection is registered but no attempt to connect to the remote peer has been performed.
36
+ * Might mean that we are waiting for the answer signal from the remote peer.
37
+ */
38
+ "INITIAL"
39
+ ] = "INITIAL";
40
+ ConnectionState5[
41
+ /**
42
+ * Trying to establish connection.
43
+ */
44
+ "CONNECTING"
45
+ ] = "CONNECTING";
46
+ ConnectionState5[
47
+ /**
48
+ * Connection is established.
49
+ */
50
+ "CONNECTED"
51
+ ] = "CONNECTED";
52
+ ConnectionState5[
53
+ /**
54
+ * Connection is being closed.
55
+ */
56
+ "CLOSING"
57
+ ] = "CLOSING";
58
+ ConnectionState5[
59
+ /**
60
+ * Connection closed.
61
+ */
62
+ "CLOSED"
63
+ ] = "CLOSED";
38
64
  })(ConnectionState || (ConnectionState = {}));
39
65
  var Connection = class {
40
66
  constructor(topic, ownId, remoteId, sessionId, initiator, _signalMessaging, _protocol, _transportFactory) {
@@ -60,6 +86,10 @@ var Connection = class {
60
86
  get protocol() {
61
87
  return this._protocol;
62
88
  }
89
+ /**
90
+ * Create an underlying transport and prepares it for the connection.
91
+ */
92
+ // TODO(burdon): Make async?
63
93
  openConnection() {
64
94
  assert(this._state === ConnectionState.INITIAL, "Invalid state.");
65
95
  this._changeState(ConnectionState.CONNECTING);
@@ -197,7 +227,7 @@ __decorate([
197
227
 
198
228
  // packages/core/mesh/network-manager/src/swarm/swarm.ts
199
229
  import assert4 from "@dxos/node-std/assert";
200
- import { Event as Event2, scheduleTask, sleep } from "@dxos/async";
230
+ import { Event as Event2, scheduleTask, sleep, synchronized as synchronized3 } from "@dxos/async";
201
231
  import { Context } from "@dxos/context";
202
232
  import { ErrorStream as ErrorStream2 } from "@dxos/debug";
203
233
  import { PublicKey as PublicKey3 } from "@dxos/keys";
@@ -283,6 +313,7 @@ var MessageRouter = class {
283
313
  var _a;
284
314
  const networkMessage = {
285
315
  ...message,
316
+ // Setting unique message_id if it not specified yet.
286
317
  messageId: (_a = message.messageId) != null ? _a : PublicKey.random()
287
318
  };
288
319
  log2("sending", {
@@ -395,17 +426,20 @@ var Peer = class {
395
426
  this.advertizing = false;
396
427
  this.initiating = false;
397
428
  }
429
+ /**
430
+ * Respond to remote offer.
431
+ */
398
432
  async onOffer(message) {
399
433
  const remoteId = message.author;
400
434
  if (this.connection || this.initiating) {
401
435
  if (remoteId.toHex() < this.localPeerId.toHex()) {
402
- log3.info("closing local connection and accepting remote peer's offer", {
436
+ log3("closing local connection and accepting remote peer's offer", {
403
437
  id: this.id,
404
438
  topic: this.topic,
405
439
  peerId: this.localPeerId
406
440
  }, {
407
441
  file: "peer.ts",
408
- line: 83,
442
+ line: 84,
409
443
  scope: this,
410
444
  callSite: (f, a) => f(...a)
411
445
  });
@@ -432,7 +466,7 @@ var Peer = class {
432
466
  err
433
467
  }, {
434
468
  file: "peer.ts",
435
- line: 107,
469
+ line: 108,
436
470
  scope: this,
437
471
  callSite: (f, a) => f(...a)
438
472
  });
@@ -447,6 +481,9 @@ var Peer = class {
447
481
  accept: false
448
482
  };
449
483
  }
484
+ /**
485
+ * Initiate a connection to the remote peer.
486
+ */
450
487
  async initiateConnection() {
451
488
  assert3(!this.initiating, "Initiation in progress.");
452
489
  assert3(!this.connection, "Already connected.");
@@ -458,7 +495,7 @@ var Peer = class {
458
495
  sessionId
459
496
  }, {
460
497
  file: "peer.ts",
461
- line: 125,
498
+ line: 126,
462
499
  scope: this,
463
500
  callSite: (f, a) => f(...a)
464
501
  });
@@ -481,14 +518,14 @@ var Peer = class {
481
518
  remoteId: this.id
482
519
  }, {
483
520
  file: "peer.ts",
484
- line: 137,
521
+ line: 138,
485
522
  scope: this,
486
523
  callSite: (f, a) => f(...a)
487
524
  });
488
525
  if (connection.state !== ConnectionState.INITIAL) {
489
526
  log3("ignoring response", {}, {
490
527
  file: "peer.ts",
491
- line: 139,
528
+ line: 140,
492
529
  scope: this,
493
530
  callSite: (f, a) => f(...a)
494
531
  });
@@ -508,7 +545,7 @@ var Peer = class {
508
545
  err
509
546
  }, {
510
547
  file: "peer.ts",
511
- line: 150,
548
+ line: 151,
512
549
  scope: this,
513
550
  callSite: (f, a) => f(...a)
514
551
  });
@@ -518,6 +555,10 @@ var Peer = class {
518
555
  this.initiating = false;
519
556
  }
520
557
  }
558
+ /**
559
+ * Create new connection.
560
+ * Either we're initiating a connection or creating one in response to an offer from the other peer.
561
+ */
521
562
  _createConnection(initiator, sessionId) {
522
563
  log3("creating connection", {
523
564
  topic: this.topic,
@@ -527,7 +568,7 @@ var Peer = class {
527
568
  sessionId
528
569
  }, {
529
570
  file: "peer.ts",
530
- line: 164,
571
+ line: 165,
531
572
  scope: this,
532
573
  callSite: (f, a) => f(...a)
533
574
  });
@@ -539,6 +580,7 @@ var Peer = class {
539
580
  sessionId,
540
581
  initiator,
541
582
  this._signalMessaging,
583
+ // TODO(dmaretskyi): Init only when connection is established.
542
584
  this._protocolProvider({
543
585
  initiator,
544
586
  localPeerId: this.localPeerId,
@@ -562,7 +604,7 @@ var Peer = class {
562
604
  initiator
563
605
  }, {
564
606
  file: "peer.ts",
565
- line: 193,
607
+ line: 194,
566
608
  scope: this,
567
609
  callSite: (f, a) => f(...a)
568
610
  });
@@ -582,7 +624,7 @@ var Peer = class {
582
624
  err
583
625
  }, {
584
626
  file: "peer.ts",
585
- line: 203,
627
+ line: 204,
586
628
  scope: this,
587
629
  callSite: (f, a) => f(...a)
588
630
  });
@@ -601,7 +643,7 @@ var Peer = class {
601
643
  sessionId: connection.sessionId
602
644
  }, {
603
645
  file: "peer.ts",
604
- line: 219,
646
+ line: 220,
605
647
  scope: this,
606
648
  callSite: (f, a) => f(...a)
607
649
  });
@@ -611,7 +653,7 @@ var Peer = class {
611
653
  sessionId: connection.sessionId
612
654
  }, {
613
655
  file: "peer.ts",
614
- line: 225,
656
+ line: 226,
615
657
  scope: this,
616
658
  callSite: (f, a) => f(...a)
617
659
  });
@@ -622,7 +664,7 @@ var Peer = class {
622
664
  message
623
665
  }, {
624
666
  file: "peer.ts",
625
- line: 230,
667
+ line: 231,
626
668
  scope: this,
627
669
  callSite: (f, a) => f(...a)
628
670
  });
@@ -637,7 +679,7 @@ var Peer = class {
637
679
  topic: this.topic
638
680
  }, {
639
681
  file: "peer.ts",
640
- line: 238,
682
+ line: 239,
641
683
  scope: this,
642
684
  callSite: (f, a) => f(...a)
643
685
  });
@@ -662,6 +704,8 @@ var __decorate3 = function(decorators, target, key, desc) {
662
704
  var INITIATION_DELAY = 100;
663
705
  var getClassName = (obj) => Object.getPrototypeOf(obj).constructor.name;
664
706
  var Swarm = class {
707
+ // TODO(burdon): Swarm => Peer.create/destroy =< Connection.open/close
708
+ // TODO(burdon): Split up properties.
665
709
  constructor(_topic, _ownPeerId, _topology, _protocolProvider, _messenger, _transportFactory, _label) {
666
710
  this._topic = _topic;
667
711
  this._ownPeerId = _ownPeerId;
@@ -709,12 +753,16 @@ var Swarm = class {
709
753
  get ownPeerId() {
710
754
  return this._ownPeerId;
711
755
  }
756
+ /**
757
+ * Custom label assigned to this swarm. Used in devtools to display human-readable names for swarms.
758
+ */
712
759
  get label() {
713
760
  return this._label;
714
761
  }
715
762
  get topic() {
716
763
  return this._topic;
717
764
  }
765
+ // TODO(burdon): async open?
718
766
  async destroy() {
719
767
  log4("destroying...", {}, {
720
768
  file: "swarm.ts",
@@ -733,6 +781,7 @@ var Swarm = class {
733
781
  });
734
782
  }
735
783
  async setTopology(topology) {
784
+ assert4(!this._ctx.disposed, "Swarm is offline");
736
785
  if (topology === this._topology) {
737
786
  return;
738
787
  }
@@ -741,7 +790,7 @@ var Swarm = class {
741
790
  topology: getClassName(topology)
742
791
  }, {
743
792
  file: "swarm.ts",
744
- line: 135,
793
+ line: 136,
745
794
  scope: this,
746
795
  callSite: (f, a) => f(...a)
747
796
  });
@@ -750,69 +799,31 @@ var Swarm = class {
750
799
  this._topology.init(this._getSwarmController());
751
800
  this._topology.update();
752
801
  }
753
- _getOrCreatePeer(peerId) {
754
- let peer = this._peers.get(peerId);
755
- if (!peer) {
756
- peer = new Peer(peerId, this._topic, this._ownPeerId, this._swarmMessenger, this._protocolProvider, this._transportFactory, {
757
- onInitiated: (connection) => {
758
- this.connectionAdded.emit(connection);
759
- },
760
- onConnected: () => {
761
- this.connected.emit(peerId);
762
- },
763
- onDisconnected: async () => {
764
- if (!peer.advertizing) {
765
- await this._destroyPeer(peer.id);
766
- }
767
- this.disconnected.emit(peerId);
768
- this._topology.update();
769
- },
770
- onRejected: () => {
771
- if (this._peers.has(peerId)) {
772
- void this._destroyPeer(peerId);
773
- }
774
- },
775
- onAccepted: () => {
776
- this._topology.update();
777
- },
778
- onOffer: (remoteId) => {
779
- return this._topology.onOffer(remoteId);
780
- }
781
- });
782
- this._peers.set(peerId, peer);
783
- }
784
- return peer;
785
- }
786
- async _destroyPeer(peerId) {
787
- assert4(this._peers.has(peerId));
788
- await this._peers.get(peerId).destroy();
789
- this._peers.delete(peerId);
790
- }
791
802
  onSwarmEvent(swarmEvent) {
792
803
  log4("swarm event", {
793
804
  swarmEvent
794
805
  }, {
795
806
  file: "swarm.ts",
796
- line: 199,
807
+ line: 149,
797
808
  scope: this,
798
809
  callSite: (f, a) => f(...a)
799
810
  });
800
- if (this._ctx.disposed) {
801
- log4.warn("ignored for destroyed swarm", {}, {
802
- file: "swarm.ts",
803
- line: 201,
804
- scope: this,
805
- callSite: (f, a) => f(...a)
806
- });
807
- return;
808
- }
809
811
  if (swarmEvent.peerAvailable) {
812
+ if (this._ctx.disposed) {
813
+ log4.warn("ignored for offline swarm", {}, {
814
+ file: "swarm.ts",
815
+ line: 153,
816
+ scope: this,
817
+ callSite: (f, a) => f(...a)
818
+ });
819
+ return;
820
+ }
810
821
  const peerId = PublicKey3.from(swarmEvent.peerAvailable.peer);
811
822
  log4("new peer", {
812
823
  peerId
813
824
  }, {
814
825
  file: "swarm.ts",
815
- line: 207,
826
+ line: 157,
816
827
  scope: this,
817
828
  callSite: (f, a) => f(...a)
818
829
  });
@@ -824,8 +835,13 @@ var Swarm = class {
824
835
  const peer1 = this._peers.get(PublicKey3.from(swarmEvent.peerLeft.peer));
825
836
  if (peer1) {
826
837
  peer1.advertizing = false;
827
- if (!peer1.connection) {
828
- void this._destroyPeer(peer1.id);
838
+ if (!peer1.connection || peer1.connection.state !== ConnectionState.CONNECTED) {
839
+ void this._destroyPeer(peer1.id).catch((err) => log4.catch(err, {}, {
840
+ file: "swarm.ts",
841
+ line: 168,
842
+ scope: this,
843
+ callSite: (f, a) => f(...a)
844
+ }));
829
845
  }
830
846
  }
831
847
  }
@@ -837,14 +853,14 @@ var Swarm = class {
837
853
  message
838
854
  }, {
839
855
  file: "swarm.ts",
840
- line: 226,
856
+ line: 178,
841
857
  scope: this,
842
858
  callSite: (f, a) => f(...a)
843
859
  });
844
860
  if (this._ctx.disposed) {
845
- log4.info("ignored for destroyed swarm", {}, {
861
+ log4.info("ignored for offline swarm", {}, {
846
862
  file: "swarm.ts",
847
- line: 228,
863
+ line: 180,
848
864
  scope: this,
849
865
  callSite: (f, a) => f(...a)
850
866
  });
@@ -858,7 +874,7 @@ var Swarm = class {
858
874
  message
859
875
  }, {
860
876
  file: "swarm.ts",
861
- line: 235,
877
+ line: 187,
862
878
  scope: this,
863
879
  callSite: (f, a) => f(...a)
864
880
  });
@@ -871,7 +887,7 @@ var Swarm = class {
871
887
  message
872
888
  }, {
873
889
  file: "swarm.ts",
874
- line: 239,
890
+ line: 191,
875
891
  scope: this,
876
892
  callSite: (f, a) => f(...a)
877
893
  });
@@ -890,14 +906,14 @@ var Swarm = class {
890
906
  message
891
907
  }, {
892
908
  file: "swarm.ts",
893
- line: 250,
909
+ line: 202,
894
910
  scope: this,
895
911
  callSite: (f, a) => f(...a)
896
912
  });
897
913
  if (this._ctx.disposed) {
898
- log4.info("ignored for destroyed swarm", {}, {
914
+ log4.info("ignored for offline swarm", {}, {
899
915
  file: "swarm.ts",
900
- line: 252,
916
+ line: 204,
901
917
  scope: this,
902
918
  callSite: (f, a) => f(...a)
903
919
  });
@@ -909,6 +925,66 @@ var Swarm = class {
909
925
  const peer = this._getOrCreatePeer(message.author);
910
926
  await peer.onSignal(message);
911
927
  }
928
+ // For debug purposes
929
+ async goOffline() {
930
+ await this._ctx.dispose();
931
+ [
932
+ ...this._peers.values()
933
+ ].forEach((peer) => {
934
+ peer.advertizing = false;
935
+ });
936
+ await Promise.all([
937
+ ...this._peers.keys()
938
+ ].map((peerId) => this._closeConnection(peerId)));
939
+ }
940
+ // For debug purposes
941
+ async goOnline() {
942
+ this._ctx = new Context();
943
+ [
944
+ ...this._peers.values()
945
+ ].forEach((peer) => {
946
+ peer.advertizing = true;
947
+ });
948
+ this._topology.update();
949
+ }
950
+ _getOrCreatePeer(peerId) {
951
+ let peer = this._peers.get(peerId);
952
+ if (!peer) {
953
+ peer = new Peer(peerId, this._topic, this._ownPeerId, this._swarmMessenger, this._protocolProvider, this._transportFactory, {
954
+ onInitiated: (connection) => {
955
+ this.connectionAdded.emit(connection);
956
+ },
957
+ onConnected: () => {
958
+ this.connected.emit(peerId);
959
+ },
960
+ onDisconnected: async () => {
961
+ if (!peer.advertizing) {
962
+ await this._destroyPeer(peer.id);
963
+ }
964
+ this.disconnected.emit(peerId);
965
+ this._topology.update();
966
+ },
967
+ onRejected: () => {
968
+ if (this._peers.has(peerId)) {
969
+ void this._destroyPeer(peerId);
970
+ }
971
+ },
972
+ onAccepted: () => {
973
+ this._topology.update();
974
+ },
975
+ onOffer: (remoteId) => {
976
+ return this._topology.onOffer(remoteId);
977
+ }
978
+ });
979
+ this._peers.set(peerId, peer);
980
+ }
981
+ return peer;
982
+ }
983
+ async _destroyPeer(peerId) {
984
+ assert4(this._peers.has(peerId));
985
+ await this._peers.get(peerId).destroy();
986
+ this._peers.delete(peerId);
987
+ }
912
988
  _getSwarmController() {
913
989
  return {
914
990
  getState: () => ({
@@ -926,7 +1002,7 @@ var Swarm = class {
926
1002
  } catch (err) {
927
1003
  log4.warn("initiation error", err, {
928
1004
  file: "swarm.ts",
929
- line: 287,
1005
+ line: 311,
930
1006
  scope: this,
931
1007
  callSite: (f, a) => f(...a)
932
1008
  });
@@ -944,13 +1020,16 @@ var Swarm = class {
944
1020
  }
945
1021
  };
946
1022
  }
1023
+ /**
1024
+ * Creates a connection then sends message over signal network.
1025
+ */
947
1026
  async _initiateConnection(remoteId) {
948
1027
  if (remoteId.toHex() < this._ownPeerId.toHex()) {
949
1028
  log4("initiation delay", {
950
1029
  remoteId
951
1030
  }, {
952
1031
  file: "swarm.ts",
953
- line: 312,
1032
+ line: 336,
954
1033
  scope: this,
955
1034
  callSite: (f, a) => f(...a)
956
1035
  });
@@ -967,7 +1046,7 @@ var Swarm = class {
967
1046
  remoteId
968
1047
  }, {
969
1048
  file: "swarm.ts",
970
- line: 326,
1049
+ line: 350,
971
1050
  scope: this,
972
1051
  callSite: (f, a) => f(...a)
973
1052
  });
@@ -977,7 +1056,7 @@ var Swarm = class {
977
1056
  remoteId
978
1057
  }, {
979
1058
  file: "swarm.ts",
980
- line: 329,
1059
+ line: 353,
981
1060
  scope: this,
982
1061
  callSite: (f, a) => f(...a)
983
1062
  });
@@ -999,6 +1078,15 @@ __decorate3([
999
1078
  __decorate3([
1000
1079
  logInfo
1001
1080
  ], Swarm.prototype, "topic", null);
1081
+ __decorate3([
1082
+ synchronized3
1083
+ ], Swarm.prototype, "onSwarmEvent", null);
1084
+ __decorate3([
1085
+ synchronized3
1086
+ ], Swarm.prototype, "onOffer", null);
1087
+ __decorate3([
1088
+ synchronized3
1089
+ ], Swarm.prototype, "goOnline", null);
1002
1090
 
1003
1091
  // packages/core/mesh/network-manager/src/swarm/swarm-mapper.ts
1004
1092
  import { Event as Event3, EventSubscriptions } from "@dxos/async";
@@ -1009,6 +1097,7 @@ var SwarmMapper = class {
1009
1097
  get peers() {
1010
1098
  return Array.from(this._peers.values());
1011
1099
  }
1100
+ // prettier-ignore
1012
1101
  constructor(_swarm) {
1013
1102
  this._swarm = _swarm;
1014
1103
  this._subscriptions = new EventSubscriptions();
@@ -1062,6 +1151,7 @@ var SwarmMapper = class {
1062
1151
  });
1063
1152
  this.mapUpdated.emit(Array.from(this._peers.values()));
1064
1153
  }
1154
+ // TODO(burdon): Async open/close.
1065
1155
  destroy() {
1066
1156
  Array.from(this._connectionSubscriptions.values()).forEach((cb) => cb());
1067
1157
  this._subscriptions.clear();
@@ -1079,6 +1169,9 @@ var EventType;
1079
1169
  })(EventType || (EventType = {}));
1080
1170
  var ConnectionLog = class {
1081
1171
  constructor() {
1172
+ /**
1173
+ * SwarmId => info
1174
+ */
1082
1175
  this._swarms = new ComplexMap4(PublicKey5.hash);
1083
1176
  this.update = new Event4();
1084
1177
  }
@@ -1132,11 +1225,14 @@ import { Event as Event5 } from "@dxos/async";
1132
1225
  import { PublicKey as PublicKey6 } from "@dxos/keys";
1133
1226
  import { log as log6 } from "@dxos/log";
1134
1227
  import { Messenger } from "@dxos/messaging";
1228
+ import { ConnectionState as ConnectionState2 } from "@dxos/protocols/proto/dxos/client/services";
1135
1229
  import { ComplexMap as ComplexMap5 } from "@dxos/util";
1136
1230
  var NetworkManager = class {
1137
1231
  constructor({ transportFactory, signalManager, log: log13 }) {
1138
1232
  this._swarms = new ComplexMap5(PublicKey6.hash);
1139
1233
  this._mappers = new ComplexMap5(PublicKey6.hash);
1234
+ this._connectionState = ConnectionState2.ONLINE;
1235
+ this.connectionStateChanged = new Event5();
1140
1236
  this.topicsUpdated = new Event5();
1141
1237
  this._transportFactory = transportFactory;
1142
1238
  this._signalManager = signalManager;
@@ -1155,12 +1251,18 @@ var NetworkManager = class {
1155
1251
  this._connectionLog = new ConnectionLog();
1156
1252
  }
1157
1253
  }
1254
+ // TODO(burdon): Remove access (Devtools only).
1158
1255
  get connectionLog() {
1159
1256
  return this._connectionLog;
1160
1257
  }
1258
+ // TODO(burdon): Remove access (Devtools only).
1161
1259
  get signalManager() {
1162
1260
  return this._signalManager;
1163
1261
  }
1262
+ get connectionState() {
1263
+ return this._connectionState;
1264
+ }
1265
+ // TODO(burdon): Reconcile with "discovery_key".
1164
1266
  get topics() {
1165
1267
  return Array.from(this._swarms.keys());
1166
1268
  }
@@ -1170,19 +1272,27 @@ var NetworkManager = class {
1170
1272
  getSwarm(topic) {
1171
1273
  return this._swarms.get(topic);
1172
1274
  }
1275
+ async open() {
1276
+ await this._messenger.open();
1277
+ await this._signalManager.open();
1278
+ }
1173
1279
  async close() {
1174
1280
  for (const topic of this._swarms.keys()) {
1175
1281
  await this.leaveSwarm(topic).catch((err) => {
1176
1282
  log6(err, {}, {
1177
1283
  file: "network-manager.ts",
1178
- line: 122,
1284
+ line: 136,
1179
1285
  scope: this,
1180
1286
  callSite: (f, a) => f(...a)
1181
1287
  });
1182
1288
  });
1183
1289
  }
1184
- await this._signalManager.destroy();
1290
+ await this._messenger.close();
1291
+ await this._signalManager.close();
1185
1292
  }
1293
+ /**
1294
+ * Join the swarm.
1295
+ */
1186
1296
  async joinSwarm({ topic, peerId, topology, protocolProvider: protocol, label }) {
1187
1297
  var _a;
1188
1298
  assert5(PublicKey6.isPublicKey(topic));
@@ -1198,7 +1308,7 @@ var NetworkManager = class {
1198
1308
  topology: topology.toString()
1199
1309
  }, {
1200
1310
  file: "network-manager.ts",
1201
- line: 148,
1311
+ line: 162,
1202
1312
  scope: this,
1203
1313
  callSite: (f, a) => f(...a)
1204
1314
  });
@@ -1208,7 +1318,7 @@ var NetworkManager = class {
1208
1318
  error
1209
1319
  }, {
1210
1320
  file: "network-manager.ts",
1211
- line: 151,
1321
+ line: 165,
1212
1322
  scope: this,
1213
1323
  callSite: (f, a) => f(...a)
1214
1324
  });
@@ -1219,7 +1329,7 @@ var NetworkManager = class {
1219
1329
  peerId
1220
1330
  }).catch((error) => log6.catch(error, {}, {
1221
1331
  file: "network-manager.ts",
1222
- line: 155,
1332
+ line: 169,
1223
1333
  scope: this,
1224
1334
  callSite: (f, a) => f(...a)
1225
1335
  }));
@@ -1231,7 +1341,7 @@ var NetworkManager = class {
1231
1341
  count: this._swarms.size
1232
1342
  }, {
1233
1343
  file: "network-manager.ts",
1234
- line: 160,
1344
+ line: 174,
1235
1345
  scope: this,
1236
1346
  callSite: (f, a) => f(...a)
1237
1347
  });
@@ -1239,14 +1349,17 @@ var NetworkManager = class {
1239
1349
  close: () => this.leaveSwarm(topic)
1240
1350
  };
1241
1351
  }
1352
+ /**
1353
+ * Close the connection.
1354
+ */
1242
1355
  async leaveSwarm(topic) {
1243
1356
  var _a;
1244
1357
  if (!this._swarms.has(topic)) {
1245
1358
  log6.warn("swarm not open", {
1246
- topic: PublicKey6.from(topic)
1359
+ topic: PublicKey6.from(topic).truncate()
1247
1360
  }, {
1248
1361
  file: "network-manager.ts",
1249
- line: 172,
1362
+ line: 186,
1250
1363
  scope: this,
1251
1364
  callSite: (f, a) => f(...a)
1252
1365
  });
@@ -1256,7 +1369,7 @@ var NetworkManager = class {
1256
1369
  topic: PublicKey6.from(topic)
1257
1370
  }, {
1258
1371
  file: "network-manager.ts",
1259
- line: 176,
1372
+ line: 190,
1260
1373
  scope: this,
1261
1374
  callSite: (f, a) => f(...a)
1262
1375
  });
@@ -1277,11 +1390,37 @@ var NetworkManager = class {
1277
1390
  count: this._swarms.size
1278
1391
  }, {
1279
1392
  file: "network-manager.ts",
1280
- line: 190,
1393
+ line: 204,
1281
1394
  scope: this,
1282
1395
  callSite: (f, a) => f(...a)
1283
1396
  });
1284
1397
  }
1398
+ async setConnectionState(state) {
1399
+ if (state === this._connectionState) {
1400
+ return;
1401
+ }
1402
+ switch (state) {
1403
+ case ConnectionState2.OFFLINE: {
1404
+ this._connectionState = state;
1405
+ await this._messenger.close();
1406
+ await this._signalManager.close();
1407
+ await Promise.all([
1408
+ ...this._swarms.values()
1409
+ ].map((swarm) => swarm.goOffline()));
1410
+ break;
1411
+ }
1412
+ case ConnectionState2.ONLINE: {
1413
+ this._connectionState = state;
1414
+ this._messenger.open();
1415
+ await this._signalManager.open();
1416
+ await Promise.all([
1417
+ ...this._swarms.values()
1418
+ ].map((swarm) => swarm.goOnline()));
1419
+ break;
1420
+ }
1421
+ }
1422
+ this.connectionStateChanged.emit(this._connectionState);
1423
+ }
1285
1424
  };
1286
1425
 
1287
1426
  // packages/core/mesh/network-manager/src/topology/fully-connected-topology.ts
@@ -1389,6 +1528,7 @@ var sortByXorDistance = (keys, reference) => keys.sort((a, b) => distance.gt(dis
1389
1528
  import assert8 from "@dxos/node-std/assert";
1390
1529
  import { log as log8 } from "@dxos/log";
1391
1530
  var StarTopology = class {
1531
+ // prettier-ignore
1392
1532
  constructor(_centralPeer) {
1393
1533
  this._centralPeer = _centralPeer;
1394
1534
  }
@@ -1612,6 +1752,7 @@ var _MemoryTransport = class {
1612
1752
  }
1613
1753
  };
1614
1754
  var MemoryTransport = _MemoryTransport;
1755
+ // TODO(burdon): Remove static properties (inject context into constructor).
1615
1756
  MemoryTransport._connections = new ComplexMap6(PublicKey7.hash);
1616
1757
  __decorate4([
1617
1758
  logInfo2
@@ -1639,12 +1780,13 @@ var WebRTCTransport = class {
1639
1780
  constructor(params) {
1640
1781
  var _a;
1641
1782
  this.params = params;
1783
+ this._closed = false;
1642
1784
  this.closed = new Event7();
1643
1785
  this.connected = new Event7();
1644
1786
  this.errors = new ErrorStream4();
1645
1787
  log10("created connection", params, {
1646
1788
  file: "webrtc-transport.ts",
1647
- line: 34,
1789
+ line: 35,
1648
1790
  scope: this,
1649
1791
  callSite: (f, a) => f(...a)
1650
1792
  });
@@ -1656,7 +1798,7 @@ var WebRTCTransport = class {
1656
1798
  this._peer.on("signal", async (data) => {
1657
1799
  log10("signal", data, {
1658
1800
  file: "webrtc-transport.ts",
1659
- line: 42,
1801
+ line: 43,
1660
1802
  scope: this,
1661
1803
  callSite: (f, a) => f(...a)
1662
1804
  });
@@ -1669,7 +1811,7 @@ var WebRTCTransport = class {
1669
1811
  this._peer.on("connect", () => {
1670
1812
  log10("connected", {}, {
1671
1813
  file: "webrtc-transport.ts",
1672
- line: 47,
1814
+ line: 48,
1673
1815
  scope: this,
1674
1816
  callSite: (f, a) => f(...a)
1675
1817
  });
@@ -1679,7 +1821,7 @@ var WebRTCTransport = class {
1679
1821
  this._peer.on("close", async () => {
1680
1822
  log10("closed", {}, {
1681
1823
  file: "webrtc-transport.ts",
1682
- line: 53,
1824
+ line: 54,
1683
1825
  scope: this,
1684
1826
  callSite: (f, a) => f(...a)
1685
1827
  });
@@ -1694,22 +1836,25 @@ var WebRTCTransport = class {
1694
1836
  async destroy() {
1695
1837
  log10("closing...", {}, {
1696
1838
  file: "webrtc-transport.ts",
1697
- line: 65,
1839
+ line: 66,
1698
1840
  scope: this,
1699
1841
  callSite: (f, a) => f(...a)
1700
1842
  });
1843
+ this._closed = true;
1701
1844
  await this._disconnectStreams();
1702
1845
  this._peer.destroy();
1703
1846
  this.closed.emit();
1704
1847
  log10("closed", {}, {
1705
1848
  file: "webrtc-transport.ts",
1706
- line: 69,
1849
+ line: 71,
1707
1850
  scope: this,
1708
1851
  callSite: (f, a) => f(...a)
1709
1852
  });
1710
1853
  }
1711
1854
  signal(signal) {
1712
- assert10(this._peer, "Connection not ready to accept signals.");
1855
+ if (this._closed) {
1856
+ return;
1857
+ }
1713
1858
  assert10(signal.payload.data, "Signal message must contain signal data.");
1714
1859
  this._peer.signal(signal.payload.data);
1715
1860
  }
@@ -1731,9 +1876,10 @@ import { Duplex } from "@dxos/node-std/stream";
1731
1876
  import { Stream } from "@dxos/codec-protobuf";
1732
1877
  import { PublicKey as PublicKey8 } from "@dxos/keys";
1733
1878
  import { log as log11 } from "@dxos/log";
1734
- import { ConnectionState as ConnectionState2 } from "@dxos/protocols/proto/dxos/mesh/bridge";
1879
+ import { ConnectionState as ConnectionState3 } from "@dxos/protocols/proto/dxos/mesh/bridge";
1735
1880
  import { ComplexMap as ComplexMap7 } from "@dxos/util";
1736
1881
  var WebRTCTransportService = class {
1882
+ // prettier-ignore
1737
1883
  constructor(_webrtcConfig) {
1738
1884
  this._webrtcConfig = _webrtcConfig;
1739
1885
  this.transports = new ComplexMap7(PublicKey8.hash);
@@ -1765,20 +1911,20 @@ var WebRTCTransportService = class {
1765
1911
  });
1766
1912
  next({
1767
1913
  connection: {
1768
- state: ConnectionState2.CONNECTING
1914
+ state: ConnectionState3.CONNECTING
1769
1915
  }
1770
1916
  });
1771
1917
  transport.connected.on(() => {
1772
1918
  next({
1773
1919
  connection: {
1774
- state: ConnectionState2.CONNECTED
1920
+ state: ConnectionState3.CONNECTED
1775
1921
  }
1776
1922
  });
1777
1923
  });
1778
1924
  transport.errors.handle((err) => {
1779
1925
  next({
1780
1926
  connection: {
1781
- state: ConnectionState2.CLOSED,
1927
+ state: ConnectionState3.CLOSED,
1782
1928
  error: err.toString()
1783
1929
  }
1784
1930
  });
@@ -1787,7 +1933,7 @@ var WebRTCTransportService = class {
1787
1933
  transport.closed.on(() => {
1788
1934
  next({
1789
1935
  connection: {
1790
- state: ConnectionState2.CLOSED
1936
+ state: ConnectionState3.CLOSED
1791
1937
  }
1792
1938
  });
1793
1939
  close();
@@ -1825,18 +1971,21 @@ var WebRTCTransportService = class {
1825
1971
  // packages/core/mesh/network-manager/src/transport/webrtc-transport-proxy.ts
1826
1972
  import assert12 from "@dxos/node-std/assert";
1827
1973
  import { Event as Event8 } from "@dxos/async";
1974
+ import { Context as Context2 } from "@dxos/context";
1828
1975
  import { ErrorStream as ErrorStream5 } from "@dxos/debug";
1829
1976
  import { PublicKey as PublicKey9 } from "@dxos/keys";
1830
1977
  import { log as log12 } from "@dxos/log";
1831
- import { ConnectionState as ConnectionState3 } from "@dxos/protocols/proto/dxos/mesh/bridge";
1978
+ import { ConnectionState as ConnectionState4 } from "@dxos/protocols/proto/dxos/mesh/bridge";
1832
1979
  var WebRTCTransportProxy = class {
1980
+ // prettier-ignore
1833
1981
  constructor(_params) {
1834
1982
  this._params = _params;
1983
+ this._proxyId = PublicKey9.random();
1984
+ this._ctx = new Context2();
1835
1985
  this.closed = new Event8();
1836
1986
  this._closed = false;
1837
1987
  this.connected = new Event8();
1838
1988
  this.errors = new ErrorStream5();
1839
- this._proxyId = PublicKey9.random();
1840
1989
  this._serviceStream = this._params.bridgeService.open({
1841
1990
  proxyId: this._proxyId,
1842
1991
  initiator: this._params.initiator
@@ -1845,7 +1994,7 @@ var WebRTCTransportProxy = class {
1845
1994
  this._serviceStream.subscribe(async (event) => {
1846
1995
  log12("WebRTCTransportProxy: event", event, {
1847
1996
  file: "webrtc-transport-proxy.ts",
1848
- line: 46,
1997
+ line: 49,
1849
1998
  scope: this,
1850
1999
  callSite: (f, a) => f(...a)
1851
2000
  });
@@ -1857,7 +2006,7 @@ var WebRTCTransportProxy = class {
1857
2006
  await this._handleSignal(event.signal);
1858
2007
  }
1859
2008
  });
1860
- this._params.stream.on("data", async (data) => {
2009
+ const dataListener = async (data) => {
1861
2010
  try {
1862
2011
  await this._params.bridgeService.sendData({
1863
2012
  proxyId: this._proxyId,
@@ -1866,15 +2015,19 @@ var WebRTCTransportProxy = class {
1866
2015
  } catch (err) {
1867
2016
  log12.catch(err, {}, {
1868
2017
  file: "webrtc-transport-proxy.ts",
1869
- line: 63,
2018
+ line: 66,
1870
2019
  scope: this,
1871
2020
  callSite: (f, a) => f(...a)
1872
2021
  });
1873
2022
  }
2023
+ };
2024
+ this._params.stream.on("data", dataListener);
2025
+ this._ctx.onDispose(() => {
2026
+ this._params.stream.off("data", dataListener);
1874
2027
  });
1875
2028
  }, (error) => log12.catch(error, {}, {
1876
2029
  file: "webrtc-transport-proxy.ts",
1877
- line: 67,
2030
+ line: 72,
1878
2031
  scope: this,
1879
2032
  callSite: (f, a) => f(...a)
1880
2033
  }));
@@ -1884,11 +2037,11 @@ var WebRTCTransportProxy = class {
1884
2037
  this.errors.raise(new Error(connectionEvent.error));
1885
2038
  }
1886
2039
  switch (connectionEvent.state) {
1887
- case ConnectionState3.CONNECTED: {
2040
+ case ConnectionState4.CONNECTED: {
1888
2041
  this.connected.emit();
1889
2042
  break;
1890
2043
  }
1891
- case ConnectionState3.CLOSED: {
2044
+ case ConnectionState4.CLOSED: {
1892
2045
  await this.destroy();
1893
2046
  break;
1894
2047
  }
@@ -1906,7 +2059,9 @@ var WebRTCTransportProxy = class {
1906
2059
  signal
1907
2060
  }).catch((err) => this.errors.raise(err));
1908
2061
  }
2062
+ // TODO(burdon): Move open from constructor.
1909
2063
  async destroy() {
2064
+ await this._ctx.dispose();
1910
2065
  if (this._closed) {
1911
2066
  return;
1912
2067
  }
@@ -1918,7 +2073,7 @@ var WebRTCTransportProxy = class {
1918
2073
  } catch (err) {
1919
2074
  log12.catch(err, {}, {
1920
2075
  file: "webrtc-transport-proxy.ts",
1921
- line: 117,
2076
+ line: 123,
1922
2077
  scope: this,
1923
2078
  callSite: (f, a) => f(...a)
1924
2079
  });
@@ -1926,6 +2081,10 @@ var WebRTCTransportProxy = class {
1926
2081
  this.closed.emit();
1927
2082
  this._closed = true;
1928
2083
  }
2084
+ /**
2085
+ * Called when underlying proxy service becomes unavailable.
2086
+ */
2087
+ // TODO(burdon): Option on close method.
1929
2088
  forceClose() {
1930
2089
  this._serviceStream.close();
1931
2090
  this.closed.emit();
@@ -1936,6 +2095,10 @@ var WebRTCTransportProxyFactory = class {
1936
2095
  constructor() {
1937
2096
  this._connections = /* @__PURE__ */ new Set();
1938
2097
  }
2098
+ /**
2099
+ * Sets the current BridgeService to be used to open connections.
2100
+ * Calling this method will close any existing connections.
2101
+ */
1939
2102
  setBridgeService(bridgeService) {
1940
2103
  this._bridgeService = bridgeService;
1941
2104
  for (const connection of this._connections) {