@dxos/network-manager 0.1.19 → 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
@@ -18,6 +18,10 @@ var __copyProps = (to, from, except, desc) => {
18
18
  return to;
19
19
  };
20
20
  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
25
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
26
  mod
23
27
  ));
@@ -70,12 +74,38 @@ var __decorate = function(decorators, target, key, desc) {
70
74
  return c > 3 && r && Object.defineProperty(target, key, r), r;
71
75
  };
72
76
  var ConnectionState;
73
- (function(ConnectionState4) {
74
- ConnectionState4["INITIAL"] = "INITIAL";
75
- ConnectionState4["CONNECTING"] = "CONNECTING";
76
- ConnectionState4["CONNECTED"] = "CONNECTED";
77
- ConnectionState4["CLOSING"] = "CLOSING";
78
- ConnectionState4["CLOSED"] = "CLOSED";
77
+ (function(ConnectionState5) {
78
+ ConnectionState5[
79
+ /**
80
+ * Initial state. Connection is registered but no attempt to connect to the remote peer has been performed.
81
+ * Might mean that we are waiting for the answer signal from the remote peer.
82
+ */
83
+ "INITIAL"
84
+ ] = "INITIAL";
85
+ ConnectionState5[
86
+ /**
87
+ * Trying to establish connection.
88
+ */
89
+ "CONNECTING"
90
+ ] = "CONNECTING";
91
+ ConnectionState5[
92
+ /**
93
+ * Connection is established.
94
+ */
95
+ "CONNECTED"
96
+ ] = "CONNECTED";
97
+ ConnectionState5[
98
+ /**
99
+ * Connection is being closed.
100
+ */
101
+ "CLOSING"
102
+ ] = "CLOSING";
103
+ ConnectionState5[
104
+ /**
105
+ * Connection closed.
106
+ */
107
+ "CLOSED"
108
+ ] = "CLOSED";
79
109
  })(ConnectionState || (ConnectionState = {}));
80
110
  var Connection = class {
81
111
  constructor(topic, ownId, remoteId, sessionId, initiator, _signalMessaging, _protocol, _transportFactory) {
@@ -101,6 +131,10 @@ var Connection = class {
101
131
  get protocol() {
102
132
  return this._protocol;
103
133
  }
134
+ /**
135
+ * Create an underlying transport and prepares it for the connection.
136
+ */
137
+ // TODO(burdon): Make async?
104
138
  openConnection() {
105
139
  (0, import_node_assert.default)(this._state === ConnectionState.INITIAL, "Invalid state.");
106
140
  this._changeState(ConnectionState.CONNECTING);
@@ -324,6 +358,7 @@ var MessageRouter = class {
324
358
  var _a;
325
359
  const networkMessage = {
326
360
  ...message,
361
+ // Setting unique message_id if it not specified yet.
327
362
  messageId: (_a = message.messageId) != null ? _a : import_keys.PublicKey.random()
328
363
  };
329
364
  (0, import_log2.log)("sending", {
@@ -436,17 +471,20 @@ var Peer = class {
436
471
  this.advertizing = false;
437
472
  this.initiating = false;
438
473
  }
474
+ /**
475
+ * Respond to remote offer.
476
+ */
439
477
  async onOffer(message) {
440
478
  const remoteId = message.author;
441
479
  if (this.connection || this.initiating) {
442
480
  if (remoteId.toHex() < this.localPeerId.toHex()) {
443
- import_log3.log.info("closing local connection and accepting remote peer's offer", {
481
+ (0, import_log3.log)("closing local connection and accepting remote peer's offer", {
444
482
  id: this.id,
445
483
  topic: this.topic,
446
484
  peerId: this.localPeerId
447
485
  }, {
448
486
  file: "peer.ts",
449
- line: 83,
487
+ line: 84,
450
488
  scope: this,
451
489
  callSite: (f, a) => f(...a)
452
490
  });
@@ -473,7 +511,7 @@ var Peer = class {
473
511
  err
474
512
  }, {
475
513
  file: "peer.ts",
476
- line: 107,
514
+ line: 108,
477
515
  scope: this,
478
516
  callSite: (f, a) => f(...a)
479
517
  });
@@ -488,6 +526,9 @@ var Peer = class {
488
526
  accept: false
489
527
  };
490
528
  }
529
+ /**
530
+ * Initiate a connection to the remote peer.
531
+ */
491
532
  async initiateConnection() {
492
533
  (0, import_node_assert3.default)(!this.initiating, "Initiation in progress.");
493
534
  (0, import_node_assert3.default)(!this.connection, "Already connected.");
@@ -499,7 +540,7 @@ var Peer = class {
499
540
  sessionId
500
541
  }, {
501
542
  file: "peer.ts",
502
- line: 125,
543
+ line: 126,
503
544
  scope: this,
504
545
  callSite: (f, a) => f(...a)
505
546
  });
@@ -522,14 +563,14 @@ var Peer = class {
522
563
  remoteId: this.id
523
564
  }, {
524
565
  file: "peer.ts",
525
- line: 137,
566
+ line: 138,
526
567
  scope: this,
527
568
  callSite: (f, a) => f(...a)
528
569
  });
529
570
  if (connection.state !== ConnectionState.INITIAL) {
530
571
  (0, import_log3.log)("ignoring response", {}, {
531
572
  file: "peer.ts",
532
- line: 139,
573
+ line: 140,
533
574
  scope: this,
534
575
  callSite: (f, a) => f(...a)
535
576
  });
@@ -549,7 +590,7 @@ var Peer = class {
549
590
  err
550
591
  }, {
551
592
  file: "peer.ts",
552
- line: 150,
593
+ line: 151,
553
594
  scope: this,
554
595
  callSite: (f, a) => f(...a)
555
596
  });
@@ -559,6 +600,10 @@ var Peer = class {
559
600
  this.initiating = false;
560
601
  }
561
602
  }
603
+ /**
604
+ * Create new connection.
605
+ * Either we're initiating a connection or creating one in response to an offer from the other peer.
606
+ */
562
607
  _createConnection(initiator, sessionId) {
563
608
  (0, import_log3.log)("creating connection", {
564
609
  topic: this.topic,
@@ -568,7 +613,7 @@ var Peer = class {
568
613
  sessionId
569
614
  }, {
570
615
  file: "peer.ts",
571
- line: 164,
616
+ line: 165,
572
617
  scope: this,
573
618
  callSite: (f, a) => f(...a)
574
619
  });
@@ -580,6 +625,7 @@ var Peer = class {
580
625
  sessionId,
581
626
  initiator,
582
627
  this._signalMessaging,
628
+ // TODO(dmaretskyi): Init only when connection is established.
583
629
  this._protocolProvider({
584
630
  initiator,
585
631
  localPeerId: this.localPeerId,
@@ -603,7 +649,7 @@ var Peer = class {
603
649
  initiator
604
650
  }, {
605
651
  file: "peer.ts",
606
- line: 193,
652
+ line: 194,
607
653
  scope: this,
608
654
  callSite: (f, a) => f(...a)
609
655
  });
@@ -623,7 +669,7 @@ var Peer = class {
623
669
  err
624
670
  }, {
625
671
  file: "peer.ts",
626
- line: 203,
672
+ line: 204,
627
673
  scope: this,
628
674
  callSite: (f, a) => f(...a)
629
675
  });
@@ -642,7 +688,7 @@ var Peer = class {
642
688
  sessionId: connection.sessionId
643
689
  }, {
644
690
  file: "peer.ts",
645
- line: 219,
691
+ line: 220,
646
692
  scope: this,
647
693
  callSite: (f, a) => f(...a)
648
694
  });
@@ -652,7 +698,7 @@ var Peer = class {
652
698
  sessionId: connection.sessionId
653
699
  }, {
654
700
  file: "peer.ts",
655
- line: 225,
701
+ line: 226,
656
702
  scope: this,
657
703
  callSite: (f, a) => f(...a)
658
704
  });
@@ -663,7 +709,7 @@ var Peer = class {
663
709
  message
664
710
  }, {
665
711
  file: "peer.ts",
666
- line: 230,
712
+ line: 231,
667
713
  scope: this,
668
714
  callSite: (f, a) => f(...a)
669
715
  });
@@ -678,7 +724,7 @@ var Peer = class {
678
724
  topic: this.topic
679
725
  }, {
680
726
  file: "peer.ts",
681
- line: 238,
727
+ line: 239,
682
728
  scope: this,
683
729
  callSite: (f, a) => f(...a)
684
730
  });
@@ -703,6 +749,8 @@ var __decorate3 = function(decorators, target, key, desc) {
703
749
  var INITIATION_DELAY = 100;
704
750
  var getClassName = (obj) => Object.getPrototypeOf(obj).constructor.name;
705
751
  var Swarm = class {
752
+ // TODO(burdon): Swarm => Peer.create/destroy =< Connection.open/close
753
+ // TODO(burdon): Split up properties.
706
754
  constructor(_topic, _ownPeerId, _topology, _protocolProvider, _messenger, _transportFactory, _label) {
707
755
  this._topic = _topic;
708
756
  this._ownPeerId = _ownPeerId;
@@ -750,12 +798,16 @@ var Swarm = class {
750
798
  get ownPeerId() {
751
799
  return this._ownPeerId;
752
800
  }
801
+ /**
802
+ * Custom label assigned to this swarm. Used in devtools to display human-readable names for swarms.
803
+ */
753
804
  get label() {
754
805
  return this._label;
755
806
  }
756
807
  get topic() {
757
808
  return this._topic;
758
809
  }
810
+ // TODO(burdon): async open?
759
811
  async destroy() {
760
812
  (0, import_log4.log)("destroying...", {}, {
761
813
  file: "swarm.ts",
@@ -774,6 +826,7 @@ var Swarm = class {
774
826
  });
775
827
  }
776
828
  async setTopology(topology) {
829
+ (0, import_node_assert4.default)(!this._ctx.disposed, "Swarm is offline");
777
830
  if (topology === this._topology) {
778
831
  return;
779
832
  }
@@ -782,7 +835,7 @@ var Swarm = class {
782
835
  topology: getClassName(topology)
783
836
  }, {
784
837
  file: "swarm.ts",
785
- line: 135,
838
+ line: 136,
786
839
  scope: this,
787
840
  callSite: (f, a) => f(...a)
788
841
  });
@@ -791,69 +844,31 @@ var Swarm = class {
791
844
  this._topology.init(this._getSwarmController());
792
845
  this._topology.update();
793
846
  }
794
- _getOrCreatePeer(peerId) {
795
- let peer = this._peers.get(peerId);
796
- if (!peer) {
797
- peer = new Peer(peerId, this._topic, this._ownPeerId, this._swarmMessenger, this._protocolProvider, this._transportFactory, {
798
- onInitiated: (connection) => {
799
- this.connectionAdded.emit(connection);
800
- },
801
- onConnected: () => {
802
- this.connected.emit(peerId);
803
- },
804
- onDisconnected: async () => {
805
- if (!peer.advertizing) {
806
- await this._destroyPeer(peer.id);
807
- }
808
- this.disconnected.emit(peerId);
809
- this._topology.update();
810
- },
811
- onRejected: () => {
812
- if (this._peers.has(peerId)) {
813
- void this._destroyPeer(peerId);
814
- }
815
- },
816
- onAccepted: () => {
817
- this._topology.update();
818
- },
819
- onOffer: (remoteId) => {
820
- return this._topology.onOffer(remoteId);
821
- }
822
- });
823
- this._peers.set(peerId, peer);
824
- }
825
- return peer;
826
- }
827
- async _destroyPeer(peerId) {
828
- (0, import_node_assert4.default)(this._peers.has(peerId));
829
- await this._peers.get(peerId).destroy();
830
- this._peers.delete(peerId);
831
- }
832
847
  onSwarmEvent(swarmEvent) {
833
848
  (0, import_log4.log)("swarm event", {
834
849
  swarmEvent
835
850
  }, {
836
851
  file: "swarm.ts",
837
- line: 199,
852
+ line: 149,
838
853
  scope: this,
839
854
  callSite: (f, a) => f(...a)
840
855
  });
841
- if (this._ctx.disposed) {
842
- import_log4.log.warn("ignored for destroyed swarm", {}, {
843
- file: "swarm.ts",
844
- line: 201,
845
- scope: this,
846
- callSite: (f, a) => f(...a)
847
- });
848
- return;
849
- }
850
856
  if (swarmEvent.peerAvailable) {
857
+ if (this._ctx.disposed) {
858
+ import_log4.log.warn("ignored for offline swarm", {}, {
859
+ file: "swarm.ts",
860
+ line: 153,
861
+ scope: this,
862
+ callSite: (f, a) => f(...a)
863
+ });
864
+ return;
865
+ }
851
866
  const peerId = import_keys3.PublicKey.from(swarmEvent.peerAvailable.peer);
852
867
  (0, import_log4.log)("new peer", {
853
868
  peerId
854
869
  }, {
855
870
  file: "swarm.ts",
856
- line: 207,
871
+ line: 157,
857
872
  scope: this,
858
873
  callSite: (f, a) => f(...a)
859
874
  });
@@ -865,8 +880,13 @@ var Swarm = class {
865
880
  const peer1 = this._peers.get(import_keys3.PublicKey.from(swarmEvent.peerLeft.peer));
866
881
  if (peer1) {
867
882
  peer1.advertizing = false;
868
- if (!peer1.connection) {
869
- void this._destroyPeer(peer1.id);
883
+ if (!peer1.connection || peer1.connection.state !== ConnectionState.CONNECTED) {
884
+ void this._destroyPeer(peer1.id).catch((err) => import_log4.log.catch(err, {}, {
885
+ file: "swarm.ts",
886
+ line: 168,
887
+ scope: this,
888
+ callSite: (f, a) => f(...a)
889
+ }));
870
890
  }
871
891
  }
872
892
  }
@@ -878,14 +898,14 @@ var Swarm = class {
878
898
  message
879
899
  }, {
880
900
  file: "swarm.ts",
881
- line: 226,
901
+ line: 178,
882
902
  scope: this,
883
903
  callSite: (f, a) => f(...a)
884
904
  });
885
905
  if (this._ctx.disposed) {
886
- import_log4.log.info("ignored for destroyed swarm", {}, {
906
+ import_log4.log.info("ignored for offline swarm", {}, {
887
907
  file: "swarm.ts",
888
- line: 228,
908
+ line: 180,
889
909
  scope: this,
890
910
  callSite: (f, a) => f(...a)
891
911
  });
@@ -899,7 +919,7 @@ var Swarm = class {
899
919
  message
900
920
  }, {
901
921
  file: "swarm.ts",
902
- line: 235,
922
+ line: 187,
903
923
  scope: this,
904
924
  callSite: (f, a) => f(...a)
905
925
  });
@@ -912,7 +932,7 @@ var Swarm = class {
912
932
  message
913
933
  }, {
914
934
  file: "swarm.ts",
915
- line: 239,
935
+ line: 191,
916
936
  scope: this,
917
937
  callSite: (f, a) => f(...a)
918
938
  });
@@ -931,14 +951,14 @@ var Swarm = class {
931
951
  message
932
952
  }, {
933
953
  file: "swarm.ts",
934
- line: 250,
954
+ line: 202,
935
955
  scope: this,
936
956
  callSite: (f, a) => f(...a)
937
957
  });
938
958
  if (this._ctx.disposed) {
939
- import_log4.log.info("ignored for destroyed swarm", {}, {
959
+ import_log4.log.info("ignored for offline swarm", {}, {
940
960
  file: "swarm.ts",
941
- line: 252,
961
+ line: 204,
942
962
  scope: this,
943
963
  callSite: (f, a) => f(...a)
944
964
  });
@@ -950,6 +970,66 @@ var Swarm = class {
950
970
  const peer = this._getOrCreatePeer(message.author);
951
971
  await peer.onSignal(message);
952
972
  }
973
+ // For debug purposes
974
+ async goOffline() {
975
+ await this._ctx.dispose();
976
+ [
977
+ ...this._peers.values()
978
+ ].forEach((peer) => {
979
+ peer.advertizing = false;
980
+ });
981
+ await Promise.all([
982
+ ...this._peers.keys()
983
+ ].map((peerId) => this._closeConnection(peerId)));
984
+ }
985
+ // For debug purposes
986
+ async goOnline() {
987
+ this._ctx = new import_context.Context();
988
+ [
989
+ ...this._peers.values()
990
+ ].forEach((peer) => {
991
+ peer.advertizing = true;
992
+ });
993
+ this._topology.update();
994
+ }
995
+ _getOrCreatePeer(peerId) {
996
+ let peer = this._peers.get(peerId);
997
+ if (!peer) {
998
+ peer = new Peer(peerId, this._topic, this._ownPeerId, this._swarmMessenger, this._protocolProvider, this._transportFactory, {
999
+ onInitiated: (connection) => {
1000
+ this.connectionAdded.emit(connection);
1001
+ },
1002
+ onConnected: () => {
1003
+ this.connected.emit(peerId);
1004
+ },
1005
+ onDisconnected: async () => {
1006
+ if (!peer.advertizing) {
1007
+ await this._destroyPeer(peer.id);
1008
+ }
1009
+ this.disconnected.emit(peerId);
1010
+ this._topology.update();
1011
+ },
1012
+ onRejected: () => {
1013
+ if (this._peers.has(peerId)) {
1014
+ void this._destroyPeer(peerId);
1015
+ }
1016
+ },
1017
+ onAccepted: () => {
1018
+ this._topology.update();
1019
+ },
1020
+ onOffer: (remoteId) => {
1021
+ return this._topology.onOffer(remoteId);
1022
+ }
1023
+ });
1024
+ this._peers.set(peerId, peer);
1025
+ }
1026
+ return peer;
1027
+ }
1028
+ async _destroyPeer(peerId) {
1029
+ (0, import_node_assert4.default)(this._peers.has(peerId));
1030
+ await this._peers.get(peerId).destroy();
1031
+ this._peers.delete(peerId);
1032
+ }
953
1033
  _getSwarmController() {
954
1034
  return {
955
1035
  getState: () => ({
@@ -967,7 +1047,7 @@ var Swarm = class {
967
1047
  } catch (err) {
968
1048
  import_log4.log.warn("initiation error", err, {
969
1049
  file: "swarm.ts",
970
- line: 287,
1050
+ line: 311,
971
1051
  scope: this,
972
1052
  callSite: (f, a) => f(...a)
973
1053
  });
@@ -985,13 +1065,16 @@ var Swarm = class {
985
1065
  }
986
1066
  };
987
1067
  }
1068
+ /**
1069
+ * Creates a connection then sends message over signal network.
1070
+ */
988
1071
  async _initiateConnection(remoteId) {
989
1072
  if (remoteId.toHex() < this._ownPeerId.toHex()) {
990
1073
  (0, import_log4.log)("initiation delay", {
991
1074
  remoteId
992
1075
  }, {
993
1076
  file: "swarm.ts",
994
- line: 312,
1077
+ line: 336,
995
1078
  scope: this,
996
1079
  callSite: (f, a) => f(...a)
997
1080
  });
@@ -1008,7 +1091,7 @@ var Swarm = class {
1008
1091
  remoteId
1009
1092
  }, {
1010
1093
  file: "swarm.ts",
1011
- line: 326,
1094
+ line: 350,
1012
1095
  scope: this,
1013
1096
  callSite: (f, a) => f(...a)
1014
1097
  });
@@ -1018,7 +1101,7 @@ var Swarm = class {
1018
1101
  remoteId
1019
1102
  }, {
1020
1103
  file: "swarm.ts",
1021
- line: 329,
1104
+ line: 353,
1022
1105
  scope: this,
1023
1106
  callSite: (f, a) => f(...a)
1024
1107
  });
@@ -1040,6 +1123,15 @@ __decorate3([
1040
1123
  __decorate3([
1041
1124
  import_log4.logInfo
1042
1125
  ], Swarm.prototype, "topic", null);
1126
+ __decorate3([
1127
+ import_async3.synchronized
1128
+ ], Swarm.prototype, "onSwarmEvent", null);
1129
+ __decorate3([
1130
+ import_async3.synchronized
1131
+ ], Swarm.prototype, "onOffer", null);
1132
+ __decorate3([
1133
+ import_async3.synchronized
1134
+ ], Swarm.prototype, "goOnline", null);
1043
1135
 
1044
1136
  // packages/core/mesh/network-manager/src/swarm/swarm-mapper.ts
1045
1137
  var import_async4 = require("@dxos/async");
@@ -1050,6 +1142,7 @@ var SwarmMapper = class {
1050
1142
  get peers() {
1051
1143
  return Array.from(this._peers.values());
1052
1144
  }
1145
+ // prettier-ignore
1053
1146
  constructor(_swarm) {
1054
1147
  this._swarm = _swarm;
1055
1148
  this._subscriptions = new import_async4.EventSubscriptions();
@@ -1103,6 +1196,7 @@ var SwarmMapper = class {
1103
1196
  });
1104
1197
  this.mapUpdated.emit(Array.from(this._peers.values()));
1105
1198
  }
1199
+ // TODO(burdon): Async open/close.
1106
1200
  destroy() {
1107
1201
  Array.from(this._connectionSubscriptions.values()).forEach((cb) => cb());
1108
1202
  this._subscriptions.clear();
@@ -1120,6 +1214,9 @@ var EventType;
1120
1214
  })(EventType || (EventType = {}));
1121
1215
  var ConnectionLog = class {
1122
1216
  constructor() {
1217
+ /**
1218
+ * SwarmId => info
1219
+ */
1123
1220
  this._swarms = new import_util4.ComplexMap(import_keys5.PublicKey.hash);
1124
1221
  this.update = new import_async5.Event();
1125
1222
  }
@@ -1173,11 +1270,14 @@ var import_async6 = require("@dxos/async");
1173
1270
  var import_keys6 = require("@dxos/keys");
1174
1271
  var import_log6 = require("@dxos/log");
1175
1272
  var import_messaging = require("@dxos/messaging");
1273
+ var import_services = require("@dxos/protocols/proto/dxos/client/services");
1176
1274
  var import_util5 = require("@dxos/util");
1177
1275
  var NetworkManager = class {
1178
1276
  constructor({ transportFactory, signalManager, log: log13 }) {
1179
1277
  this._swarms = new import_util5.ComplexMap(import_keys6.PublicKey.hash);
1180
1278
  this._mappers = new import_util5.ComplexMap(import_keys6.PublicKey.hash);
1279
+ this._connectionState = import_services.ConnectionState.ONLINE;
1280
+ this.connectionStateChanged = new import_async6.Event();
1181
1281
  this.topicsUpdated = new import_async6.Event();
1182
1282
  this._transportFactory = transportFactory;
1183
1283
  this._signalManager = signalManager;
@@ -1196,12 +1296,18 @@ var NetworkManager = class {
1196
1296
  this._connectionLog = new ConnectionLog();
1197
1297
  }
1198
1298
  }
1299
+ // TODO(burdon): Remove access (Devtools only).
1199
1300
  get connectionLog() {
1200
1301
  return this._connectionLog;
1201
1302
  }
1303
+ // TODO(burdon): Remove access (Devtools only).
1202
1304
  get signalManager() {
1203
1305
  return this._signalManager;
1204
1306
  }
1307
+ get connectionState() {
1308
+ return this._connectionState;
1309
+ }
1310
+ // TODO(burdon): Reconcile with "discovery_key".
1205
1311
  get topics() {
1206
1312
  return Array.from(this._swarms.keys());
1207
1313
  }
@@ -1211,19 +1317,27 @@ var NetworkManager = class {
1211
1317
  getSwarm(topic) {
1212
1318
  return this._swarms.get(topic);
1213
1319
  }
1320
+ async open() {
1321
+ await this._messenger.open();
1322
+ await this._signalManager.open();
1323
+ }
1214
1324
  async close() {
1215
1325
  for (const topic of this._swarms.keys()) {
1216
1326
  await this.leaveSwarm(topic).catch((err) => {
1217
1327
  (0, import_log6.log)(err, {}, {
1218
1328
  file: "network-manager.ts",
1219
- line: 122,
1329
+ line: 136,
1220
1330
  scope: this,
1221
1331
  callSite: (f, a) => f(...a)
1222
1332
  });
1223
1333
  });
1224
1334
  }
1225
- await this._signalManager.destroy();
1335
+ await this._messenger.close();
1336
+ await this._signalManager.close();
1226
1337
  }
1338
+ /**
1339
+ * Join the swarm.
1340
+ */
1227
1341
  async joinSwarm({ topic, peerId, topology, protocolProvider: protocol, label }) {
1228
1342
  var _a;
1229
1343
  (0, import_node_assert5.default)(import_keys6.PublicKey.isPublicKey(topic));
@@ -1239,7 +1353,7 @@ var NetworkManager = class {
1239
1353
  topology: topology.toString()
1240
1354
  }, {
1241
1355
  file: "network-manager.ts",
1242
- line: 148,
1356
+ line: 162,
1243
1357
  scope: this,
1244
1358
  callSite: (f, a) => f(...a)
1245
1359
  });
@@ -1249,7 +1363,7 @@ var NetworkManager = class {
1249
1363
  error
1250
1364
  }, {
1251
1365
  file: "network-manager.ts",
1252
- line: 151,
1366
+ line: 165,
1253
1367
  scope: this,
1254
1368
  callSite: (f, a) => f(...a)
1255
1369
  });
@@ -1260,7 +1374,7 @@ var NetworkManager = class {
1260
1374
  peerId
1261
1375
  }).catch((error) => import_log6.log.catch(error, {}, {
1262
1376
  file: "network-manager.ts",
1263
- line: 155,
1377
+ line: 169,
1264
1378
  scope: this,
1265
1379
  callSite: (f, a) => f(...a)
1266
1380
  }));
@@ -1272,7 +1386,7 @@ var NetworkManager = class {
1272
1386
  count: this._swarms.size
1273
1387
  }, {
1274
1388
  file: "network-manager.ts",
1275
- line: 160,
1389
+ line: 174,
1276
1390
  scope: this,
1277
1391
  callSite: (f, a) => f(...a)
1278
1392
  });
@@ -1280,14 +1394,17 @@ var NetworkManager = class {
1280
1394
  close: () => this.leaveSwarm(topic)
1281
1395
  };
1282
1396
  }
1397
+ /**
1398
+ * Close the connection.
1399
+ */
1283
1400
  async leaveSwarm(topic) {
1284
1401
  var _a;
1285
1402
  if (!this._swarms.has(topic)) {
1286
1403
  import_log6.log.warn("swarm not open", {
1287
- topic: import_keys6.PublicKey.from(topic)
1404
+ topic: import_keys6.PublicKey.from(topic).truncate()
1288
1405
  }, {
1289
1406
  file: "network-manager.ts",
1290
- line: 172,
1407
+ line: 186,
1291
1408
  scope: this,
1292
1409
  callSite: (f, a) => f(...a)
1293
1410
  });
@@ -1297,7 +1414,7 @@ var NetworkManager = class {
1297
1414
  topic: import_keys6.PublicKey.from(topic)
1298
1415
  }, {
1299
1416
  file: "network-manager.ts",
1300
- line: 176,
1417
+ line: 190,
1301
1418
  scope: this,
1302
1419
  callSite: (f, a) => f(...a)
1303
1420
  });
@@ -1318,11 +1435,37 @@ var NetworkManager = class {
1318
1435
  count: this._swarms.size
1319
1436
  }, {
1320
1437
  file: "network-manager.ts",
1321
- line: 190,
1438
+ line: 204,
1322
1439
  scope: this,
1323
1440
  callSite: (f, a) => f(...a)
1324
1441
  });
1325
1442
  }
1443
+ async setConnectionState(state) {
1444
+ if (state === this._connectionState) {
1445
+ return;
1446
+ }
1447
+ switch (state) {
1448
+ case import_services.ConnectionState.OFFLINE: {
1449
+ this._connectionState = state;
1450
+ await this._messenger.close();
1451
+ await this._signalManager.close();
1452
+ await Promise.all([
1453
+ ...this._swarms.values()
1454
+ ].map((swarm) => swarm.goOffline()));
1455
+ break;
1456
+ }
1457
+ case import_services.ConnectionState.ONLINE: {
1458
+ this._connectionState = state;
1459
+ this._messenger.open();
1460
+ await this._signalManager.open();
1461
+ await Promise.all([
1462
+ ...this._swarms.values()
1463
+ ].map((swarm) => swarm.goOnline()));
1464
+ break;
1465
+ }
1466
+ }
1467
+ this.connectionStateChanged.emit(this._connectionState);
1468
+ }
1326
1469
  };
1327
1470
 
1328
1471
  // packages/core/mesh/network-manager/src/topology/fully-connected-topology.ts
@@ -1430,6 +1573,7 @@ var sortByXorDistance = (keys, reference) => keys.sort((a, b) => import_xor_dist
1430
1573
  var import_node_assert8 = __toESM(require("node:assert"));
1431
1574
  var import_log8 = require("@dxos/log");
1432
1575
  var StarTopology = class {
1576
+ // prettier-ignore
1433
1577
  constructor(_centralPeer) {
1434
1578
  this._centralPeer = _centralPeer;
1435
1579
  }
@@ -1653,6 +1797,7 @@ var _MemoryTransport = class {
1653
1797
  }
1654
1798
  };
1655
1799
  var MemoryTransport = _MemoryTransport;
1800
+ // TODO(burdon): Remove static properties (inject context into constructor).
1656
1801
  MemoryTransport._connections = new import_util6.ComplexMap(import_keys7.PublicKey.hash);
1657
1802
  __decorate4([
1658
1803
  import_log9.logInfo
@@ -1680,12 +1825,13 @@ var WebRTCTransport = class {
1680
1825
  constructor(params) {
1681
1826
  var _a;
1682
1827
  this.params = params;
1828
+ this._closed = false;
1683
1829
  this.closed = new import_async8.Event();
1684
1830
  this.connected = new import_async8.Event();
1685
1831
  this.errors = new import_debug5.ErrorStream();
1686
1832
  (0, import_log10.log)("created connection", params, {
1687
1833
  file: "webrtc-transport.ts",
1688
- line: 34,
1834
+ line: 35,
1689
1835
  scope: this,
1690
1836
  callSite: (f, a) => f(...a)
1691
1837
  });
@@ -1697,7 +1843,7 @@ var WebRTCTransport = class {
1697
1843
  this._peer.on("signal", async (data) => {
1698
1844
  (0, import_log10.log)("signal", data, {
1699
1845
  file: "webrtc-transport.ts",
1700
- line: 42,
1846
+ line: 43,
1701
1847
  scope: this,
1702
1848
  callSite: (f, a) => f(...a)
1703
1849
  });
@@ -1710,7 +1856,7 @@ var WebRTCTransport = class {
1710
1856
  this._peer.on("connect", () => {
1711
1857
  (0, import_log10.log)("connected", {}, {
1712
1858
  file: "webrtc-transport.ts",
1713
- line: 47,
1859
+ line: 48,
1714
1860
  scope: this,
1715
1861
  callSite: (f, a) => f(...a)
1716
1862
  });
@@ -1720,7 +1866,7 @@ var WebRTCTransport = class {
1720
1866
  this._peer.on("close", async () => {
1721
1867
  (0, import_log10.log)("closed", {}, {
1722
1868
  file: "webrtc-transport.ts",
1723
- line: 53,
1869
+ line: 54,
1724
1870
  scope: this,
1725
1871
  callSite: (f, a) => f(...a)
1726
1872
  });
@@ -1735,22 +1881,25 @@ var WebRTCTransport = class {
1735
1881
  async destroy() {
1736
1882
  (0, import_log10.log)("closing...", {}, {
1737
1883
  file: "webrtc-transport.ts",
1738
- line: 65,
1884
+ line: 66,
1739
1885
  scope: this,
1740
1886
  callSite: (f, a) => f(...a)
1741
1887
  });
1888
+ this._closed = true;
1742
1889
  await this._disconnectStreams();
1743
1890
  this._peer.destroy();
1744
1891
  this.closed.emit();
1745
1892
  (0, import_log10.log)("closed", {}, {
1746
1893
  file: "webrtc-transport.ts",
1747
- line: 69,
1894
+ line: 71,
1748
1895
  scope: this,
1749
1896
  callSite: (f, a) => f(...a)
1750
1897
  });
1751
1898
  }
1752
1899
  signal(signal) {
1753
- (0, import_node_assert10.default)(this._peer, "Connection not ready to accept signals.");
1900
+ if (this._closed) {
1901
+ return;
1902
+ }
1754
1903
  (0, import_node_assert10.default)(signal.payload.data, "Signal message must contain signal data.");
1755
1904
  this._peer.signal(signal.payload.data);
1756
1905
  }
@@ -1775,6 +1924,7 @@ var import_log11 = require("@dxos/log");
1775
1924
  var import_bridge = require("@dxos/protocols/proto/dxos/mesh/bridge");
1776
1925
  var import_util7 = require("@dxos/util");
1777
1926
  var WebRTCTransportService = class {
1927
+ // prettier-ignore
1778
1928
  constructor(_webrtcConfig) {
1779
1929
  this._webrtcConfig = _webrtcConfig;
1780
1930
  this.transports = new import_util7.ComplexMap(import_keys8.PublicKey.hash);
@@ -1866,18 +2016,21 @@ var WebRTCTransportService = class {
1866
2016
  // packages/core/mesh/network-manager/src/transport/webrtc-transport-proxy.ts
1867
2017
  var import_node_assert12 = __toESM(require("node:assert"));
1868
2018
  var import_async9 = require("@dxos/async");
2019
+ var import_context2 = require("@dxos/context");
1869
2020
  var import_debug6 = require("@dxos/debug");
1870
2021
  var import_keys9 = require("@dxos/keys");
1871
2022
  var import_log12 = require("@dxos/log");
1872
2023
  var import_bridge2 = require("@dxos/protocols/proto/dxos/mesh/bridge");
1873
2024
  var WebRTCTransportProxy = class {
2025
+ // prettier-ignore
1874
2026
  constructor(_params) {
1875
2027
  this._params = _params;
2028
+ this._proxyId = import_keys9.PublicKey.random();
2029
+ this._ctx = new import_context2.Context();
1876
2030
  this.closed = new import_async9.Event();
1877
2031
  this._closed = false;
1878
2032
  this.connected = new import_async9.Event();
1879
2033
  this.errors = new import_debug6.ErrorStream();
1880
- this._proxyId = import_keys9.PublicKey.random();
1881
2034
  this._serviceStream = this._params.bridgeService.open({
1882
2035
  proxyId: this._proxyId,
1883
2036
  initiator: this._params.initiator
@@ -1886,7 +2039,7 @@ var WebRTCTransportProxy = class {
1886
2039
  this._serviceStream.subscribe(async (event) => {
1887
2040
  (0, import_log12.log)("WebRTCTransportProxy: event", event, {
1888
2041
  file: "webrtc-transport-proxy.ts",
1889
- line: 46,
2042
+ line: 49,
1890
2043
  scope: this,
1891
2044
  callSite: (f, a) => f(...a)
1892
2045
  });
@@ -1898,7 +2051,7 @@ var WebRTCTransportProxy = class {
1898
2051
  await this._handleSignal(event.signal);
1899
2052
  }
1900
2053
  });
1901
- this._params.stream.on("data", async (data) => {
2054
+ const dataListener = async (data) => {
1902
2055
  try {
1903
2056
  await this._params.bridgeService.sendData({
1904
2057
  proxyId: this._proxyId,
@@ -1907,15 +2060,19 @@ var WebRTCTransportProxy = class {
1907
2060
  } catch (err) {
1908
2061
  import_log12.log.catch(err, {}, {
1909
2062
  file: "webrtc-transport-proxy.ts",
1910
- line: 63,
2063
+ line: 66,
1911
2064
  scope: this,
1912
2065
  callSite: (f, a) => f(...a)
1913
2066
  });
1914
2067
  }
2068
+ };
2069
+ this._params.stream.on("data", dataListener);
2070
+ this._ctx.onDispose(() => {
2071
+ this._params.stream.off("data", dataListener);
1915
2072
  });
1916
2073
  }, (error) => import_log12.log.catch(error, {}, {
1917
2074
  file: "webrtc-transport-proxy.ts",
1918
- line: 67,
2075
+ line: 72,
1919
2076
  scope: this,
1920
2077
  callSite: (f, a) => f(...a)
1921
2078
  }));
@@ -1947,7 +2104,9 @@ var WebRTCTransportProxy = class {
1947
2104
  signal
1948
2105
  }).catch((err) => this.errors.raise(err));
1949
2106
  }
2107
+ // TODO(burdon): Move open from constructor.
1950
2108
  async destroy() {
2109
+ await this._ctx.dispose();
1951
2110
  if (this._closed) {
1952
2111
  return;
1953
2112
  }
@@ -1959,7 +2118,7 @@ var WebRTCTransportProxy = class {
1959
2118
  } catch (err) {
1960
2119
  import_log12.log.catch(err, {}, {
1961
2120
  file: "webrtc-transport-proxy.ts",
1962
- line: 117,
2121
+ line: 123,
1963
2122
  scope: this,
1964
2123
  callSite: (f, a) => f(...a)
1965
2124
  });
@@ -1967,6 +2126,10 @@ var WebRTCTransportProxy = class {
1967
2126
  this.closed.emit();
1968
2127
  this._closed = true;
1969
2128
  }
2129
+ /**
2130
+ * Called when underlying proxy service becomes unavailable.
2131
+ */
2132
+ // TODO(burdon): Option on close method.
1970
2133
  forceClose() {
1971
2134
  this._serviceStream.close();
1972
2135
  this.closed.emit();
@@ -1977,6 +2140,10 @@ var WebRTCTransportProxyFactory = class {
1977
2140
  constructor() {
1978
2141
  this._connections = /* @__PURE__ */ new Set();
1979
2142
  }
2143
+ /**
2144
+ * Sets the current BridgeService to be used to open connections.
2145
+ * Calling this method will close any existing connections.
2146
+ */
1980
2147
  setBridgeService(bridgeService) {
1981
2148
  this._bridgeService = bridgeService;
1982
2149
  for (const connection of this._connections) {