@dxos/echo-pipeline 0.5.2 → 0.5.3-main.088a2c8

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 (48) hide show
  1. package/dist/lib/browser/{chunk-VQQD32DM.mjs → chunk-VUJXFVSK.mjs} +49 -30
  2. package/dist/lib/browser/{chunk-VQQD32DM.mjs.map → chunk-VUJXFVSK.mjs.map} +3 -3
  3. package/dist/lib/browser/index.mjs +435 -288
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +3 -1
  7. package/dist/lib/browser/testing/index.mjs.map +3 -3
  8. package/dist/lib/node/{chunk-P7L7ICAH.cjs → chunk-6733E3WM.cjs} +50 -32
  9. package/dist/lib/node/{chunk-P7L7ICAH.cjs.map → chunk-6733E3WM.cjs.map} +3 -3
  10. package/dist/lib/node/index.cjs +446 -299
  11. package/dist/lib/node/index.cjs.map +4 -4
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/testing/index.cjs +13 -11
  14. package/dist/lib/node/testing/index.cjs.map +3 -3
  15. package/dist/types/src/automerge/automerge-host.d.ts +4 -11
  16. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
  17. package/dist/types/src/automerge/echo-network-adapter.d.ts +6 -0
  18. package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
  19. package/dist/types/src/automerge/echo-replicator.d.ts +2 -0
  20. package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
  21. package/dist/types/src/automerge/index.d.ts +1 -1
  22. package/dist/types/src/automerge/leveldb-storage-adapter.d.ts +2 -2
  23. package/dist/types/src/automerge/mesh-echo-replicator.d.ts +23 -0
  24. package/dist/types/src/automerge/mesh-echo-replicator.d.ts.map +1 -0
  25. package/dist/types/src/automerge/migrations.d.ts +2 -2
  26. package/dist/types/src/space/space-manager.d.ts +3 -2
  27. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  28. package/dist/types/src/space/space-protocol.d.ts +2 -0
  29. package/dist/types/src/space/space-protocol.d.ts.map +1 -1
  30. package/dist/types/src/space/space.d.ts +4 -3
  31. package/dist/types/src/space/space.d.ts.map +1 -1
  32. package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
  33. package/package.json +33 -33
  34. package/src/automerge/automerge-host.test.ts +22 -9
  35. package/src/automerge/automerge-host.ts +62 -88
  36. package/src/automerge/echo-network-adapter.ts +19 -0
  37. package/src/automerge/echo-replicator.ts +3 -0
  38. package/src/automerge/index.ts +1 -1
  39. package/src/automerge/leveldb-storage-adapter.ts +2 -2
  40. package/src/automerge/mesh-echo-replicator.ts +232 -0
  41. package/src/automerge/migrations.ts +2 -2
  42. package/src/space/space-manager.ts +4 -1
  43. package/src/space/space-protocol.ts +11 -8
  44. package/src/space/space.ts +8 -3
  45. package/src/testing/test-agent-builder.ts +1 -0
  46. package/dist/types/src/automerge/mesh-network-adapter.d.ts +0 -18
  47. package/dist/types/src/automerge/mesh-network-adapter.d.ts.map +0 -1
  48. package/src/automerge/mesh-network-adapter.ts +0 -107
@@ -22,19 +22,19 @@ import {
22
22
  mapTimeframeToFeedIndexes,
23
23
  startAfter,
24
24
  valueEncoding
25
- } from "./chunk-VQQD32DM.mjs";
25
+ } from "./chunk-VUJXFVSK.mjs";
26
26
 
27
27
  // packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts
28
28
  import { Event } from "@dxos/async";
29
29
  import { next as automerge, getBackend, getHeads } from "@dxos/automerge/automerge";
30
30
  import { Repo } from "@dxos/automerge/automerge-repo";
31
31
  import { Context } from "@dxos/context";
32
- import { invariant as invariant4 } from "@dxos/invariant";
32
+ import { invariant as invariant3 } from "@dxos/invariant";
33
33
  import { PublicKey } from "@dxos/keys";
34
- import { log as log4 } from "@dxos/log";
34
+ import { log as log3 } from "@dxos/log";
35
35
  import { idCodec } from "@dxos/protocols";
36
36
  import { trace } from "@dxos/tracing";
37
- import { ComplexMap, ComplexSet, defaultMap, mapValues } from "@dxos/util";
37
+ import { mapValues } from "@dxos/util";
38
38
 
39
39
  // packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts
40
40
  import { Trigger, synchronized } from "@dxos/async";
@@ -54,12 +54,10 @@ function _ts_decorate(decorators, target, key, desc) {
54
54
  }
55
55
  var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts";
56
56
  var EchoNetworkAdapter = class extends NetworkAdapter {
57
- constructor() {
58
- super(...arguments);
57
+ constructor(_params) {
58
+ super();
59
+ this._params = _params;
59
60
  this._replicators = /* @__PURE__ */ new Set();
60
- /**
61
- * Remote peer id -> connection.
62
- */
63
61
  this._connections = /* @__PURE__ */ new Map();
64
62
  this._lifecycleState = LifecycleState.CLOSED;
65
63
  this._connected = new Trigger();
@@ -78,7 +76,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
78
76
  if (connectionEntry.isOpen) {
79
77
  log.catch(err, void 0, {
80
78
  F: __dxlog_file,
81
- L: 40,
79
+ L: 49,
82
80
  S: this,
83
81
  C: (f, a) => f(...a)
84
82
  });
@@ -90,7 +88,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
90
88
  async open() {
91
89
  invariant(this._lifecycleState === LifecycleState.CLOSED, void 0, {
92
90
  F: __dxlog_file,
93
- L: 51,
91
+ L: 60,
94
92
  S: this,
95
93
  A: [
96
94
  "this._lifecycleState === LifecycleState.CLOSED",
@@ -98,6 +96,12 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
98
96
  ]
99
97
  });
100
98
  this._lifecycleState = LifecycleState.OPEN;
99
+ log("emit ready", void 0, {
100
+ F: __dxlog_file,
101
+ L: 63,
102
+ S: this,
103
+ C: (f, a) => f(...a)
104
+ });
101
105
  this.emit("ready", {
102
106
  network: this
103
107
  });
@@ -105,7 +109,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
105
109
  async close() {
106
110
  invariant(this._lifecycleState === LifecycleState.OPEN, void 0, {
107
111
  F: __dxlog_file,
108
- L: 61,
112
+ L: 71,
109
113
  S: this,
110
114
  A: [
111
115
  "this._lifecycleState === LifecycleState.OPEN",
@@ -124,9 +128,18 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
124
128
  });
125
129
  }
126
130
  async addReplicator(replicator) {
131
+ invariant(this._lifecycleState === LifecycleState.OPEN, void 0, {
132
+ F: __dxlog_file,
133
+ L: 87,
134
+ S: this,
135
+ A: [
136
+ "this._lifecycleState === LifecycleState.OPEN",
137
+ ""
138
+ ]
139
+ });
127
140
  invariant(this.peerId, void 0, {
128
141
  F: __dxlog_file,
129
- L: 77,
142
+ L: 88,
130
143
  S: this,
131
144
  A: [
132
145
  "this.peerId",
@@ -135,30 +148,43 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
135
148
  });
136
149
  invariant(!this._replicators.has(replicator), void 0, {
137
150
  F: __dxlog_file,
138
- L: 78,
151
+ L: 89,
139
152
  S: this,
140
153
  A: [
141
154
  "!this._replicators.has(replicator)",
142
155
  ""
143
156
  ]
144
157
  });
158
+ this._replicators.add(replicator);
145
159
  await replicator.connect({
146
160
  peerId: this.peerId,
147
161
  onConnectionOpen: this._onConnectionOpen.bind(this),
148
- onConnectionClosed: this._onConnectionClosed.bind(this)
162
+ onConnectionClosed: this._onConnectionClosed.bind(this),
163
+ getContainingSpaceForDocument: this._params.getContainingSpaceForDocument
149
164
  });
150
165
  }
151
166
  async removeReplicator(replicator) {
167
+ invariant(this._lifecycleState === LifecycleState.OPEN, void 0, {
168
+ F: __dxlog_file,
169
+ L: 102,
170
+ S: this,
171
+ A: [
172
+ "this._lifecycleState === LifecycleState.OPEN",
173
+ ""
174
+ ]
175
+ });
152
176
  invariant(this._replicators.has(replicator), void 0, {
153
177
  F: __dxlog_file,
154
- L: 89,
178
+ L: 103,
155
179
  S: this,
156
180
  A: [
157
181
  "this._replicators.has(replicator)",
158
182
  ""
159
183
  ]
160
184
  });
185
+ "";
161
186
  await replicator.disconnect();
187
+ this._replicators.delete(replicator);
162
188
  }
163
189
  async shouldAdvertize(peerId, params) {
164
190
  const connection = this._connections.get(peerId);
@@ -168,9 +194,17 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
168
194
  return connection.connection.shouldAdvertize(params);
169
195
  }
170
196
  _onConnectionOpen(connection) {
197
+ log("Connection opened", {
198
+ peerId: connection.peerId
199
+ }, {
200
+ F: __dxlog_file,
201
+ L: 119,
202
+ S: this,
203
+ C: (f, a) => f(...a)
204
+ });
171
205
  invariant(!this._connections.has(connection.peerId), void 0, {
172
206
  F: __dxlog_file,
173
- L: 103,
207
+ L: 120,
174
208
  S: this,
175
209
  A: [
176
210
  "!this._connections.has(connection.peerId as PeerId)",
@@ -199,13 +233,21 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
199
233
  if (connectionEntry.isOpen) {
200
234
  log.catch(err, void 0, {
201
235
  F: __dxlog_file,
202
- L: 122,
236
+ L: 139,
203
237
  S: this,
204
238
  C: (f, a) => f(...a)
205
239
  });
206
240
  }
207
241
  }
208
242
  });
243
+ log("emit peer-candidate", {
244
+ peerId: connection.peerId
245
+ }, {
246
+ F: __dxlog_file,
247
+ L: 144,
248
+ S: this,
249
+ C: (f, a) => f(...a)
250
+ });
209
251
  this.emit("peer-candidate", {
210
252
  peerId: connection.peerId,
211
253
  peerMetadata: {
@@ -215,10 +257,18 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
215
257
  });
216
258
  }
217
259
  _onConnectionClosed(connection) {
260
+ log("Connection closed", {
261
+ peerId: connection.peerId
262
+ }, {
263
+ F: __dxlog_file,
264
+ L: 155,
265
+ S: this,
266
+ C: (f, a) => f(...a)
267
+ });
218
268
  const entry = this._connections.get(connection.peerId);
219
269
  invariant(entry, void 0, {
220
270
  F: __dxlog_file,
221
- L: 138,
271
+ L: 157,
222
272
  S: this,
223
273
  A: [
224
274
  "entry",
@@ -231,13 +281,13 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
231
281
  });
232
282
  void entry.reader.cancel().catch((err) => log.catch(err, void 0, {
233
283
  F: __dxlog_file,
234
- L: 143,
284
+ L: 162,
235
285
  S: this,
236
286
  C: (f, a) => f(...a)
237
287
  }));
238
288
  void entry.writer.abort().catch((err) => log.catch(err, void 0, {
239
289
  F: __dxlog_file,
240
- L: 144,
290
+ L: 163,
241
291
  S: this,
242
292
  C: (f, a) => f(...a)
243
293
  }));
@@ -491,126 +541,9 @@ var LocalHostNetworkAdapter = class extends NetworkAdapter2 {
491
541
  }
492
542
  };
493
543
 
494
- // packages/core/echo/echo-pipeline/src/automerge/mesh-network-adapter.ts
495
- import { Trigger as Trigger3 } from "@dxos/async";
496
- import { NetworkAdapter as NetworkAdapter3, cbor as cbor2 } from "@dxos/automerge/automerge-repo";
497
- import { invariant as invariant3 } from "@dxos/invariant";
498
- import { log as log2 } from "@dxos/log";
499
- import { AutomergeReplicator } from "@dxos/teleport-extension-automerge-replicator";
500
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-network-adapter.ts";
501
- var MeshNetworkAdapter = class extends NetworkAdapter3 {
502
- constructor() {
503
- super(...arguments);
504
- this._extensions = /* @__PURE__ */ new Map();
505
- this._connected = new Trigger3();
506
- }
507
- /**
508
- * Emits `ready` event. That signals to `Repo` that it can start using the adapter.
509
- */
510
- ready() {
511
- this.emit("ready", {
512
- network: this
513
- });
514
- }
515
- connect(peerId) {
516
- this.peerId = peerId;
517
- this._connected.wake();
518
- }
519
- send(message) {
520
- const receiverId = message.targetId;
521
- const extension = this._extensions.get(receiverId);
522
- invariant3(extension, "Extension not found.", {
523
- F: __dxlog_file3,
524
- L: 38,
525
- S: this,
526
- A: [
527
- "extension",
528
- "'Extension not found.'"
529
- ]
530
- });
531
- extension.sendSyncMessage({
532
- payload: cbor2.encode(message)
533
- }).catch((err) => log2.catch(err, void 0, {
534
- F: __dxlog_file3,
535
- L: 39,
536
- S: this,
537
- C: (f, a) => f(...a)
538
- }));
539
- }
540
- disconnect() {
541
- }
542
- createExtension() {
543
- invariant3(this.peerId, "Peer id not set.", {
544
- F: __dxlog_file3,
545
- L: 47,
546
- S: this,
547
- A: [
548
- "this.peerId",
549
- "'Peer id not set.'"
550
- ]
551
- });
552
- let peerInfo;
553
- const extension = new AutomergeReplicator({
554
- peerId: this.peerId
555
- }, {
556
- onStartReplication: async (info, remotePeerId) => {
557
- await this._connected.wait();
558
- log2("onStartReplication", {
559
- id: info.id,
560
- thisPeerId: this.peerId,
561
- remotePeerId: remotePeerId.toHex()
562
- }, {
563
- F: __dxlog_file3,
564
- L: 70,
565
- S: this,
566
- C: (f, a) => f(...a)
567
- });
568
- if (!this._extensions.has(info.id)) {
569
- peerInfo = info;
570
- this._extensions.set(info.id, extension);
571
- log2("peer-candidate", {
572
- id: info.id,
573
- thisPeerId: this.peerId,
574
- remotePeerId: remotePeerId.toHex()
575
- }, {
576
- F: __dxlog_file3,
577
- L: 76,
578
- S: this,
579
- C: (f, a) => f(...a)
580
- });
581
- this.emit("peer-candidate", {
582
- // TODO(mykola): Hack, stop abusing `peerMetadata` field.
583
- peerMetadata: {
584
- dxos_deviceKey: remotePeerId.toHex()
585
- },
586
- peerId: info.id
587
- });
588
- }
589
- },
590
- onSyncMessage: async ({ payload }) => {
591
- if (!peerInfo) {
592
- return;
593
- }
594
- const message = cbor2.decode(payload);
595
- this.emit("message", message);
596
- },
597
- onClose: async () => {
598
- if (!peerInfo) {
599
- return;
600
- }
601
- this.emit("peer-disconnected", {
602
- peerId: peerInfo.id
603
- });
604
- this._extensions.delete(peerInfo.id);
605
- }
606
- });
607
- return extension;
608
- }
609
- };
610
-
611
544
  // packages/core/echo/echo-pipeline/src/automerge/migrations.ts
612
545
  import { IndexedDBStorageAdapter } from "@dxos/automerge/automerge-repo-storage-indexeddb";
613
- import { log as log3 } from "@dxos/log";
546
+ import { log as log2 } from "@dxos/log";
614
547
  import { StorageType } from "@dxos/random-access-storage";
615
548
 
616
549
  // packages/core/echo/echo-pipeline/src/automerge/automerge-storage-adapter.ts
@@ -690,7 +623,7 @@ var AutomergeStorageAdapter = class {
690
623
  };
691
624
 
692
625
  // packages/core/echo/echo-pipeline/src/automerge/migrations.ts
693
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/migrations.ts";
626
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/migrations.ts";
694
627
  var levelMigration = async ({ db, directory }) => {
695
628
  const isNewLevel = !await db.iterator({
696
629
  ...encodingOptions
@@ -704,10 +637,10 @@ var levelMigration = async ({ db, directory }) => {
704
637
  return;
705
638
  }
706
639
  const batch = db.batch();
707
- log3.info("found chunks on old storage adapter", {
640
+ log2.info("found chunks on old storage adapter", {
708
641
  chunks: chunks.length
709
642
  }, {
710
- F: __dxlog_file4,
643
+ F: __dxlog_file3,
711
644
  L: 36,
712
645
  S: void 0,
713
646
  C: (f, a) => f(...a)
@@ -731,15 +664,13 @@ function _ts_decorate2(decorators, target, key, desc) {
731
664
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
732
665
  return c > 3 && r && Object.defineProperty(target, key, r), r;
733
666
  }
734
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts";
667
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts";
735
668
  var AutomergeHost = class {
736
669
  constructor({ directory, db, indexMetadataStore }) {
737
670
  this._ctx = new Context();
738
- this._echoNetworkAdapter = new EchoNetworkAdapter();
739
- /**
740
- * spaceKey -> deviceKey[]
741
- */
742
- this._authorizedDevices = new ComplexMap(PublicKey.hash);
671
+ this._echoNetworkAdapter = new EchoNetworkAdapter({
672
+ getContainingSpaceForDocument: this._getContainingSpaceForDocument.bind(this)
673
+ });
743
674
  this._requestedDocs = /* @__PURE__ */ new Set();
744
675
  this._directory = directory;
745
676
  this._db = db;
@@ -759,103 +690,17 @@ var AutomergeHost = class {
759
690
  });
760
691
  await this._storage.open?.();
761
692
  this._peerId = `host-${PublicKey.random().toHex()}`;
762
- this._meshNetwork = new MeshNetworkAdapter();
763
693
  this._clientNetwork = new LocalHostNetworkAdapter();
764
694
  this._repo = new Repo({
765
695
  peerId: this._peerId,
766
696
  network: [
767
697
  this._clientNetwork,
768
- this._meshNetwork,
769
698
  this._echoNetworkAdapter
770
699
  ],
771
700
  storage: this._storage,
772
- // TODO(dmaretskyi): Share based on HALO permissions and space affinity.
773
- // Hosts, running in the worker, don't share documents unless requested by other peers.
774
- sharePolicy: async (peerId, documentId) => {
775
- if (peerId.startsWith("client-")) {
776
- return false;
777
- }
778
- if (!documentId) {
779
- return false;
780
- }
781
- const peerMetadata = this.repo.peerMetadataByPeerId[peerId];
782
- if (peerMetadata?.dxos_peerSource === "EchoNetworkAdapter") {
783
- return this._echoNetworkAdapter.shouldAdvertize(peerId, {
784
- documentId
785
- });
786
- }
787
- const doc = this._repo.handles[documentId]?.docSync();
788
- if (!doc) {
789
- const isRequested = this._requestedDocs.has(`automerge:${documentId}`);
790
- log4("doc share policy check", {
791
- peerId,
792
- documentId,
793
- isRequested
794
- }, {
795
- F: __dxlog_file5,
796
- L: 124,
797
- S: this,
798
- C: (f, a) => f(...a)
799
- });
800
- return isRequested;
801
- }
802
- try {
803
- const spaceKey = getSpaceKeyFromDoc(doc);
804
- if (!spaceKey) {
805
- log4("space key not found for share policy check", {
806
- peerId,
807
- documentId
808
- }, {
809
- F: __dxlog_file5,
810
- L: 131,
811
- S: this,
812
- C: (f, a) => f(...a)
813
- });
814
- return false;
815
- }
816
- const authorizedDevices = this._authorizedDevices.get(PublicKey.from(spaceKey));
817
- const deviceKeyHex = peerMetadata?.dxos_deviceKey;
818
- if (!deviceKeyHex) {
819
- log4("device key not found for share policy check", {
820
- peerId,
821
- documentId
822
- }, {
823
- F: __dxlog_file5,
824
- L: 140,
825
- S: this,
826
- C: (f, a) => f(...a)
827
- });
828
- return false;
829
- }
830
- const deviceKey = PublicKey.from(deviceKeyHex);
831
- const isAuthorized = authorizedDevices?.has(deviceKey) ?? false;
832
- log4("share policy check", {
833
- localPeer: this._peerId,
834
- remotePeer: peerId,
835
- documentId,
836
- deviceKey,
837
- spaceKey,
838
- isAuthorized
839
- }, {
840
- F: __dxlog_file5,
841
- L: 146,
842
- S: this,
843
- C: (f, a) => f(...a)
844
- });
845
- return isAuthorized;
846
- } catch (err) {
847
- log4.catch(err, void 0, {
848
- F: __dxlog_file5,
849
- L: 156,
850
- S: this,
851
- C: (f, a) => f(...a)
852
- });
853
- return false;
854
- }
855
- }
701
+ sharePolicy: this._sharePolicy.bind(this)
856
702
  });
857
703
  this._clientNetwork.ready();
858
- this._meshNetwork.ready();
859
704
  await this._echoNetworkAdapter.open();
860
705
  await this._clientNetwork.whenConnected();
861
706
  await this._echoNetworkAdapter.whenConnected();
@@ -875,6 +720,40 @@ var AutomergeHost = class {
875
720
  async removeReplicator(replicator) {
876
721
  await this._echoNetworkAdapter.removeReplicator(replicator);
877
722
  }
723
+ // TODO(dmaretskyi): Share based on HALO permissions and space affinity.
724
+ // Hosts, running in the worker, don't share documents unless requested by other peers.
725
+ // NOTE: If both peers return sharePolicy=false the replication will not happen
726
+ // https://github.com/automerge/automerge-repo/pull/292
727
+ async _sharePolicy(peerId, documentId) {
728
+ if (peerId.startsWith("client-")) {
729
+ return false;
730
+ }
731
+ if (!documentId) {
732
+ return false;
733
+ }
734
+ const doc = this._repo.handles[documentId]?.docSync();
735
+ if (!doc) {
736
+ const isRequested = this._requestedDocs.has(`automerge:${documentId}`);
737
+ log3("doc share policy check", {
738
+ peerId,
739
+ documentId,
740
+ isRequested
741
+ }, {
742
+ F: __dxlog_file4,
743
+ L: 149,
744
+ S: this,
745
+ C: (f, a) => f(...a)
746
+ });
747
+ return isRequested;
748
+ }
749
+ const peerMetadata = this.repo.peerMetadataByPeerId[peerId];
750
+ if (peerMetadata?.dxos_peerSource === "EchoNetworkAdapter") {
751
+ return this._echoNetworkAdapter.shouldAdvertize(peerId, {
752
+ documentId
753
+ });
754
+ }
755
+ return false;
756
+ }
878
757
  async _beforeSave({ path, batch }) {
879
758
  const handle = this._repo.handles[path[0]];
880
759
  if (!handle) {
@@ -927,14 +806,25 @@ var AutomergeHost = class {
927
806
  _automergePeers() {
928
807
  return this._repo.peers;
929
808
  }
809
+ async _getContainingSpaceForDocument(documentId) {
810
+ const doc = this._repo.handles[documentId]?.docSync();
811
+ if (!doc) {
812
+ return null;
813
+ }
814
+ const spaceKeyHex = getSpaceKeyFromDoc(doc);
815
+ if (!spaceKeyHex) {
816
+ return null;
817
+ }
818
+ return PublicKey.from(spaceKeyHex);
819
+ }
930
820
  //
931
821
  // Methods for client-services.
932
822
  //
933
823
  async flush({ states }) {
934
824
  await Promise.all(states?.map(async ({ heads, documentId }) => {
935
- invariant4(heads, "heads are required for flush", {
936
- F: __dxlog_file5,
937
- L: 252,
825
+ invariant3(heads, "heads are required for flush", {
826
+ F: __dxlog_file4,
827
+ L: 239,
938
828
  S: this,
939
829
  A: [
940
830
  "heads",
@@ -955,24 +845,6 @@ var AutomergeHost = class {
955
845
  async getHostInfo() {
956
846
  return this._clientNetwork.getHostInfo();
957
847
  }
958
- //
959
- // Mesh replication.
960
- //
961
- createExtension() {
962
- return this._meshNetwork.createExtension();
963
- }
964
- authorizeDevice(spaceKey, deviceKey) {
965
- log4("authorizeDevice", {
966
- spaceKey,
967
- deviceKey
968
- }, {
969
- F: __dxlog_file5,
970
- L: 282,
971
- S: this,
972
- C: (f, a) => f(...a)
973
- });
974
- defaultMap(this._authorizedDevices, spaceKey, () => new ComplexSet(PublicKey.hash)).add(deviceKey);
975
- }
976
848
  };
977
849
  _ts_decorate2([
978
850
  trace.info()
@@ -1025,8 +897,8 @@ var changeIsPresentInDoc = (doc, changeHash) => {
1025
897
  import { Event as Event2 } from "@dxos/async";
1026
898
  import { cancelWithContext } from "@dxos/context";
1027
899
  import { warnAfterTimeout } from "@dxos/debug";
1028
- import { invariant as invariant5 } from "@dxos/invariant";
1029
- import { log as log5 } from "@dxos/log";
900
+ import { invariant as invariant4 } from "@dxos/invariant";
901
+ import { log as log4 } from "@dxos/log";
1030
902
  import { trace as trace2 } from "@dxos/tracing";
1031
903
  function _ts_decorate3(decorators, target, key, desc) {
1032
904
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -1038,7 +910,7 @@ function _ts_decorate3(decorators, target, key, desc) {
1038
910
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1039
911
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1040
912
  }
1041
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-doc-loader.ts";
913
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-doc-loader.ts";
1042
914
  var AutomergeDocumentLoaderImpl = class {
1043
915
  constructor(_spaceKey, _repo) {
1044
916
  this._spaceKey = _spaceKey;
@@ -1059,10 +931,10 @@ var AutomergeDocumentLoaderImpl = class {
1059
931
  return;
1060
932
  }
1061
933
  if (!spaceState.rootUrl) {
1062
- log5.error("Database opened with no rootUrl", {
934
+ log4.error("Database opened with no rootUrl", {
1063
935
  spaceKey: this._spaceKey
1064
936
  }, {
1065
- F: __dxlog_file6,
937
+ F: __dxlog_file5,
1066
938
  L: 70,
1067
939
  S: this,
1068
940
  C: (f, a) => f(...a)
@@ -1071,8 +943,8 @@ var AutomergeDocumentLoaderImpl = class {
1071
943
  } else {
1072
944
  const existingDocHandle = await this._initDocHandle(ctx, spaceState.rootUrl);
1073
945
  const doc = existingDocHandle.docSync();
1074
- invariant5(doc, void 0, {
1075
- F: __dxlog_file6,
946
+ invariant4(doc, void 0, {
947
+ F: __dxlog_file5,
1076
948
  L: 75,
1077
949
  S: this,
1078
950
  A: [
@@ -1093,8 +965,8 @@ var AutomergeDocumentLoaderImpl = class {
1093
965
  let hasUrlsToLoad = false;
1094
966
  const urlsToLoad = {};
1095
967
  for (const objectId of objectIds) {
1096
- invariant5(this._spaceRootDocHandle, void 0, {
1097
- F: __dxlog_file6,
968
+ invariant4(this._spaceRootDocHandle, void 0, {
969
+ F: __dxlog_file5,
1098
970
  L: 88,
1099
971
  S: this,
1100
972
  A: [
@@ -1106,8 +978,8 @@ var AutomergeDocumentLoaderImpl = class {
1106
978
  continue;
1107
979
  }
1108
980
  const spaceRootDoc = this._spaceRootDocHandle.docSync();
1109
- invariant5(spaceRootDoc, void 0, {
1110
- F: __dxlog_file6,
981
+ invariant4(spaceRootDoc, void 0, {
982
+ F: __dxlog_file5,
1111
983
  L: 93,
1112
984
  S: this,
1113
985
  A: [
@@ -1118,10 +990,10 @@ var AutomergeDocumentLoaderImpl = class {
1118
990
  const documentUrl = (spaceRootDoc.links ?? {})[objectId];
1119
991
  if (documentUrl == null) {
1120
992
  this._objectsPendingDocumentLoad.add(objectId);
1121
- log5.info("loading delayed until object links are initialized", {
993
+ log4.info("loading delayed until object links are initialized", {
1122
994
  objectId
1123
995
  }, {
1124
- F: __dxlog_file6,
996
+ F: __dxlog_file5,
1125
997
  L: 97,
1126
998
  S: this,
1127
999
  C: (f, a) => f(...a)
@@ -1144,8 +1016,8 @@ var AutomergeDocumentLoaderImpl = class {
1144
1016
  linksAwaitingLoad.forEach(([objectId]) => this._objectsPendingDocumentLoad.delete(objectId));
1145
1017
  }
1146
1018
  getSpaceRootDocHandle() {
1147
- invariant5(this._spaceRootDocHandle, void 0, {
1148
- F: __dxlog_file6,
1019
+ invariant4(this._spaceRootDocHandle, void 0, {
1020
+ F: __dxlog_file5,
1149
1021
  L: 120,
1150
1022
  S: this,
1151
1023
  A: [
@@ -1156,8 +1028,8 @@ var AutomergeDocumentLoaderImpl = class {
1156
1028
  return this._spaceRootDocHandle;
1157
1029
  }
1158
1030
  createDocumentForObject(objectId) {
1159
- invariant5(this._spaceRootDocHandle, void 0, {
1160
- F: __dxlog_file6,
1031
+ invariant4(this._spaceRootDocHandle, void 0, {
1032
+ F: __dxlog_file5,
1161
1033
  L: 125,
1162
1034
  S: this,
1163
1035
  A: [
@@ -1196,11 +1068,11 @@ var AutomergeDocumentLoaderImpl = class {
1196
1068
  };
1197
1069
  const objectDocumentHandle = this._objectDocumentHandles.get(objectId);
1198
1070
  if (objectDocumentHandle != null && objectDocumentHandle.url !== automergeUrl) {
1199
- log5.warn("object already inlined in a different document, ignoring the link", {
1071
+ log4.warn("object already inlined in a different document, ignoring the link", {
1200
1072
  ...logMeta,
1201
1073
  actualDocumentUrl: objectDocumentHandle.url
1202
1074
  }, {
1203
- F: __dxlog_file6,
1075
+ F: __dxlog_file5,
1204
1076
  L: 155,
1205
1077
  S: this,
1206
1078
  C: (f, a) => f(...a)
@@ -1208,8 +1080,8 @@ var AutomergeDocumentLoaderImpl = class {
1208
1080
  continue;
1209
1081
  }
1210
1082
  if (objectDocumentHandle?.url === automergeUrl) {
1211
- log5.warn("object document was already loaded", logMeta, {
1212
- F: __dxlog_file6,
1083
+ log4.warn("object document was already loaded", logMeta, {
1084
+ F: __dxlog_file5,
1213
1085
  L: 162,
1214
1086
  S: this,
1215
1087
  C: (f, a) => f(...a)
@@ -1217,8 +1089,8 @@ var AutomergeDocumentLoaderImpl = class {
1217
1089
  continue;
1218
1090
  }
1219
1091
  const handle = this._repo.find(automergeUrl);
1220
- log5.debug("document loading triggered", logMeta, {
1221
- F: __dxlog_file6,
1092
+ log4.debug("document loading triggered", logMeta, {
1093
+ F: __dxlog_file5,
1222
1094
  L: 166,
1223
1095
  S: this,
1224
1096
  C: (f, a) => f(...a)
@@ -1237,11 +1109,11 @@ var AutomergeDocumentLoaderImpl = class {
1237
1109
  break;
1238
1110
  } catch (err) {
1239
1111
  if (`${err}`.includes("Timeout")) {
1240
- log5.info("wraparound", {
1112
+ log4.info("wraparound", {
1241
1113
  id: docHandle.documentId,
1242
1114
  state: docHandle.state
1243
1115
  }, {
1244
- F: __dxlog_file6,
1116
+ F: __dxlog_file5,
1245
1117
  L: 182,
1246
1118
  S: this,
1247
1119
  C: (f, a) => f(...a)
@@ -1282,8 +1154,8 @@ var AutomergeDocumentLoaderImpl = class {
1282
1154
  docUrl: handle.url
1283
1155
  };
1284
1156
  if (this.onObjectDocumentLoaded.listenerCount() === 0) {
1285
- log5.info("document loaded after all listeners were removed", logMeta, {
1286
- F: __dxlog_file6,
1157
+ log4.info("document loaded after all listeners were removed", logMeta, {
1158
+ F: __dxlog_file5,
1287
1159
  L: 218,
1288
1160
  S: this,
1289
1161
  C: (f, a) => f(...a)
@@ -1292,8 +1164,8 @@ var AutomergeDocumentLoaderImpl = class {
1292
1164
  }
1293
1165
  const objectDocHandle = this._objectDocumentHandles.get(objectId);
1294
1166
  if (objectDocHandle?.url !== handle.url) {
1295
- log5.warn("object was rebound while a document was loading, discarding handle", logMeta, {
1296
- F: __dxlog_file6,
1167
+ log4.warn("object was rebound while a document was loading, discarding handle", logMeta, {
1168
+ F: __dxlog_file5,
1297
1169
  L: 223,
1298
1170
  S: this,
1299
1171
  C: (f, a) => f(...a)
@@ -1306,13 +1178,13 @@ var AutomergeDocumentLoaderImpl = class {
1306
1178
  });
1307
1179
  } catch (err) {
1308
1180
  const shouldRetryLoading = this.onObjectDocumentLoaded.listenerCount() > 0;
1309
- log5.warn("failed to load a document", {
1181
+ log4.warn("failed to load a document", {
1310
1182
  objectId,
1311
1183
  automergeUrl: handle.url,
1312
1184
  retryLoading: shouldRetryLoading,
1313
1185
  err
1314
1186
  }, {
1315
- F: __dxlog_file6,
1187
+ F: __dxlog_file5,
1316
1188
  L: 229,
1317
1189
  S: this,
1318
1190
  C: (f, a) => f(...a)
@@ -1331,6 +1203,281 @@ _ts_decorate3([
1331
1203
  AutomergeDocumentLoaderImpl = _ts_decorate3([
1332
1204
  trace2.resource()
1333
1205
  ], AutomergeDocumentLoaderImpl);
1206
+
1207
+ // packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts
1208
+ import { cbor as cbor2 } from "@dxos/automerge/automerge-repo";
1209
+ import { Resource as Resource2 } from "@dxos/context";
1210
+ import { invariant as invariant5 } from "@dxos/invariant";
1211
+ import { PublicKey as PublicKey2 } from "@dxos/keys";
1212
+ import { log as log5 } from "@dxos/log";
1213
+ import { AutomergeReplicator } from "@dxos/teleport-extension-automerge-replicator";
1214
+ import { ComplexMap, ComplexSet, defaultMap } from "@dxos/util";
1215
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts";
1216
+ var MeshEchoReplicator = class {
1217
+ constructor() {
1218
+ this._connections = /* @__PURE__ */ new Set();
1219
+ /**
1220
+ * Using automerge peerId as a key.
1221
+ */
1222
+ this._connectionsPerPeer = /* @__PURE__ */ new Map();
1223
+ /**
1224
+ * spaceKey -> deviceKey[]
1225
+ */
1226
+ this._authorizedDevices = new ComplexMap(PublicKey2.hash);
1227
+ this._context = null;
1228
+ }
1229
+ async connect(context) {
1230
+ this._context = context;
1231
+ }
1232
+ async disconnect() {
1233
+ for (const connection of this._connections) {
1234
+ await connection.close();
1235
+ }
1236
+ this._connections.clear();
1237
+ this._connectionsPerPeer.clear();
1238
+ this._context = null;
1239
+ }
1240
+ createExtension() {
1241
+ invariant5(this._context, void 0, {
1242
+ F: __dxlog_file6,
1243
+ L: 54,
1244
+ S: this,
1245
+ A: [
1246
+ "this._context",
1247
+ ""
1248
+ ]
1249
+ });
1250
+ const connection = new MeshReplicatorConnection({
1251
+ ownPeerId: this._context.peerId,
1252
+ onRemoteConnected: async () => {
1253
+ log5("onRemoteConnected", {
1254
+ peerId: connection.peerId
1255
+ }, {
1256
+ F: __dxlog_file6,
1257
+ L: 59,
1258
+ S: this,
1259
+ C: (f, a) => f(...a)
1260
+ });
1261
+ invariant5(this._context, void 0, {
1262
+ F: __dxlog_file6,
1263
+ L: 60,
1264
+ S: this,
1265
+ A: [
1266
+ "this._context",
1267
+ ""
1268
+ ]
1269
+ });
1270
+ if (!this._connectionsPerPeer.has(connection.peerId)) {
1271
+ this._connectionsPerPeer.set(connection.peerId, connection);
1272
+ await connection.enable();
1273
+ this._context.onConnectionOpen(connection);
1274
+ }
1275
+ },
1276
+ onRemoteDisconnected: async () => {
1277
+ log5("onRemoteDisconnected", {
1278
+ peerId: connection.peerId
1279
+ }, {
1280
+ F: __dxlog_file6,
1281
+ L: 69,
1282
+ S: this,
1283
+ C: (f, a) => f(...a)
1284
+ });
1285
+ invariant5(this._context, void 0, {
1286
+ F: __dxlog_file6,
1287
+ L: 70,
1288
+ S: this,
1289
+ A: [
1290
+ "this._context",
1291
+ ""
1292
+ ]
1293
+ });
1294
+ this._context.onConnectionClosed(connection);
1295
+ await connection.disable();
1296
+ this._connectionsPerPeer.delete(connection.peerId);
1297
+ this._connections.delete(connection);
1298
+ },
1299
+ shouldAdvertize: async (params) => {
1300
+ log5("shouldAdvertize", {
1301
+ peerId: connection.peerId,
1302
+ documentId: params.documentId
1303
+ }, {
1304
+ F: __dxlog_file6,
1305
+ L: 77,
1306
+ S: this,
1307
+ C: (f, a) => f(...a)
1308
+ });
1309
+ invariant5(this._context, void 0, {
1310
+ F: __dxlog_file6,
1311
+ L: 78,
1312
+ S: this,
1313
+ A: [
1314
+ "this._context",
1315
+ ""
1316
+ ]
1317
+ });
1318
+ try {
1319
+ const spaceKey = await this._context.getContainingSpaceForDocument(params.documentId);
1320
+ if (!spaceKey) {
1321
+ log5("space key not found for share policy check", {
1322
+ peerId: connection.peerId,
1323
+ documentId: params.documentId
1324
+ }, {
1325
+ F: __dxlog_file6,
1326
+ L: 82,
1327
+ S: this,
1328
+ C: (f, a) => f(...a)
1329
+ });
1330
+ return false;
1331
+ }
1332
+ const authorizedDevices = this._authorizedDevices.get(spaceKey);
1333
+ if (!connection.remoteDeviceKey) {
1334
+ log5("device key not found for share policy check", {
1335
+ peerId: connection.peerId,
1336
+ documentId: params.documentId
1337
+ }, {
1338
+ F: __dxlog_file6,
1339
+ L: 92,
1340
+ S: this,
1341
+ C: (f, a) => f(...a)
1342
+ });
1343
+ return false;
1344
+ }
1345
+ const isAuthorized = authorizedDevices?.has(connection.remoteDeviceKey) ?? false;
1346
+ log5("share policy check", {
1347
+ localPeer: this._context.peerId,
1348
+ remotePeer: connection.peerId,
1349
+ documentId: params.documentId,
1350
+ deviceKey: connection.remoteDeviceKey,
1351
+ spaceKey,
1352
+ isAuthorized
1353
+ }, {
1354
+ F: __dxlog_file6,
1355
+ L: 100,
1356
+ S: this,
1357
+ C: (f, a) => f(...a)
1358
+ });
1359
+ return isAuthorized;
1360
+ } catch (err) {
1361
+ log5.catch(err, void 0, {
1362
+ F: __dxlog_file6,
1363
+ L: 110,
1364
+ S: this,
1365
+ C: (f, a) => f(...a)
1366
+ });
1367
+ return false;
1368
+ }
1369
+ }
1370
+ });
1371
+ this._connections.add(connection);
1372
+ return connection.replicatorExtension;
1373
+ }
1374
+ authorizeDevice(spaceKey, deviceKey) {
1375
+ log5("authorizeDevice", {
1376
+ spaceKey,
1377
+ deviceKey
1378
+ }, {
1379
+ F: __dxlog_file6,
1380
+ L: 121,
1381
+ S: this,
1382
+ C: (f, a) => f(...a)
1383
+ });
1384
+ defaultMap(this._authorizedDevices, spaceKey, () => new ComplexSet(PublicKey2.hash)).add(deviceKey);
1385
+ }
1386
+ };
1387
+ var MeshReplicatorConnection = class extends Resource2 {
1388
+ constructor(_params) {
1389
+ super();
1390
+ this._params = _params;
1391
+ this.remoteDeviceKey = null;
1392
+ this._remotePeerId = null;
1393
+ this._isEnabled = false;
1394
+ let readableStreamController;
1395
+ this.readable = new ReadableStream({
1396
+ start: (controller) => {
1397
+ readableStreamController = controller;
1398
+ this._ctx.onDispose(() => controller.close());
1399
+ }
1400
+ });
1401
+ this.writable = new WritableStream({
1402
+ write: async (message, controller) => {
1403
+ this.replicatorExtension.sendSyncMessage({
1404
+ payload: cbor2.encode(message)
1405
+ }).catch((err) => {
1406
+ controller.error(err);
1407
+ });
1408
+ }
1409
+ });
1410
+ this.replicatorExtension = new AutomergeReplicator({
1411
+ peerId: this._params.ownPeerId
1412
+ }, {
1413
+ onStartReplication: async (info, remotePeerId) => {
1414
+ this.remoteDeviceKey = remotePeerId;
1415
+ this._remotePeerId = info.id;
1416
+ log5("onStartReplication", {
1417
+ id: info.id,
1418
+ thisPeerId: this.peerId,
1419
+ remotePeerId: remotePeerId.toHex()
1420
+ }, {
1421
+ F: __dxlog_file6,
1422
+ L: 186,
1423
+ S: this,
1424
+ C: (f, a) => f(...a)
1425
+ });
1426
+ await this._params.onRemoteConnected();
1427
+ },
1428
+ onSyncMessage: async ({ payload }) => {
1429
+ if (!this._isEnabled) {
1430
+ return;
1431
+ }
1432
+ const message = cbor2.decode(payload);
1433
+ readableStreamController.enqueue(message);
1434
+ },
1435
+ onClose: async () => {
1436
+ if (!this._isEnabled) {
1437
+ return;
1438
+ }
1439
+ await this._params.onRemoteDisconnected();
1440
+ }
1441
+ });
1442
+ }
1443
+ get peerId() {
1444
+ invariant5(this._remotePeerId != null, "Remote peer has not connected yet.", {
1445
+ F: __dxlog_file6,
1446
+ L: 209,
1447
+ S: this,
1448
+ A: [
1449
+ "this._remotePeerId != null",
1450
+ "'Remote peer has not connected yet.'"
1451
+ ]
1452
+ });
1453
+ return this._remotePeerId;
1454
+ }
1455
+ async shouldAdvertize(params) {
1456
+ return this._params.shouldAdvertize(params);
1457
+ }
1458
+ /**
1459
+ * Start exchanging messages with the remote peer.
1460
+ * Call after the remote peer has connected.
1461
+ */
1462
+ async enable() {
1463
+ invariant5(this._remotePeerId != null, "Remote peer has not connected yet.", {
1464
+ F: __dxlog_file6,
1465
+ L: 222,
1466
+ S: this,
1467
+ A: [
1468
+ "this._remotePeerId != null",
1469
+ "'Remote peer has not connected yet.'"
1470
+ ]
1471
+ });
1472
+ this._isEnabled = true;
1473
+ }
1474
+ /**
1475
+ * Stop exchanging messages with the remote peer.
1476
+ */
1477
+ async disable() {
1478
+ this._isEnabled = false;
1479
+ }
1480
+ };
1334
1481
  export {
1335
1482
  AuthExtension,
1336
1483
  AuthStatus,
@@ -1342,7 +1489,7 @@ export {
1342
1489
  LocalHostNetworkAdapter,
1343
1490
  MOCK_AUTH_PROVIDER,
1344
1491
  MOCK_AUTH_VERIFIER,
1345
- MeshNetworkAdapter,
1492
+ MeshEchoReplicator,
1346
1493
  MetadataStore,
1347
1494
  Pipeline,
1348
1495
  SnapshotManager,