@dxos/echo-pipeline 0.5.2 → 0.5.3-main.056e7da

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 (62) hide show
  1. package/dist/lib/browser/{chunk-VQQD32DM.mjs → chunk-GANAND63.mjs} +49 -30
  2. package/dist/lib/browser/chunk-GANAND63.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +431 -291
  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-M475BGBI.cjs} +50 -32
  9. package/dist/lib/node/chunk-M475BGBI.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +441 -301
  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/automerge/migrations.d.ts.map +1 -1
  27. package/dist/types/src/common/feeds.d.ts.map +1 -1
  28. package/dist/types/src/db-host/snapshot-manager.d.ts +2 -2
  29. package/dist/types/src/db-host/snapshot-manager.d.ts.map +1 -1
  30. package/dist/types/src/db-host/snapshot-store.d.ts +2 -2
  31. package/dist/types/src/db-host/snapshot-store.d.ts.map +1 -1
  32. package/dist/types/src/metadata/metadata-store.d.ts +1 -1
  33. package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
  34. package/dist/types/src/space/space-manager.d.ts +3 -2
  35. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  36. package/dist/types/src/space/space-protocol.d.ts +2 -0
  37. package/dist/types/src/space/space-protocol.d.ts.map +1 -1
  38. package/dist/types/src/space/space.d.ts +4 -3
  39. package/dist/types/src/space/space.d.ts.map +1 -1
  40. package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
  41. package/package.json +33 -33
  42. package/src/automerge/automerge-host.test.ts +22 -9
  43. package/src/automerge/automerge-host.ts +68 -90
  44. package/src/automerge/automerge-repo.test.ts +10 -1
  45. package/src/automerge/echo-network-adapter.ts +19 -0
  46. package/src/automerge/echo-replicator.ts +3 -0
  47. package/src/automerge/index.ts +1 -1
  48. package/src/automerge/leveldb-storage-adapter.ts +2 -2
  49. package/src/automerge/mesh-echo-replicator.ts +231 -0
  50. package/src/automerge/migrations.ts +2 -2
  51. package/src/db-host/snapshot-manager.ts +1 -1
  52. package/src/db-host/snapshot-store.ts +1 -1
  53. package/src/metadata/metadata-store.ts +2 -2
  54. package/src/space/space-manager.ts +4 -1
  55. package/src/space/space-protocol.ts +11 -8
  56. package/src/space/space.ts +8 -3
  57. package/src/testing/test-agent-builder.ts +1 -0
  58. package/dist/lib/browser/chunk-VQQD32DM.mjs.map +0 -7
  59. package/dist/lib/node/chunk-P7L7ICAH.cjs.map +0 -7
  60. package/dist/types/src/automerge/mesh-network-adapter.d.ts +0 -18
  61. package/dist/types/src/automerge/mesh-network-adapter.d.ts.map +0 -1
  62. 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-GANAND63.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";
35
- import { idCodec } from "@dxos/protocols";
34
+ import { log as log3 } from "@dxos/log";
35
+ import { objectPointerCodec } 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) {
@@ -884,11 +763,13 @@ var AutomergeHost = class {
884
763
  if (!doc) {
885
764
  return;
886
765
  }
766
+ const spaceKey = getSpaceKeyFromDoc(doc) ?? void 0;
887
767
  const lastAvailableHash = getHeads(doc);
888
768
  const objectIds = Object.keys(doc.objects ?? {});
889
- const encodedIds = objectIds.map((objectId) => idCodec.encode({
769
+ const encodedIds = objectIds.map((objectId) => objectPointerCodec.encode({
890
770
  documentId: handle.documentId,
891
- objectId
771
+ objectId,
772
+ spaceKey
892
773
  }));
893
774
  const idToLastHash = new Map(encodedIds.map((id) => [
894
775
  id,
@@ -927,14 +808,25 @@ var AutomergeHost = class {
927
808
  _automergePeers() {
928
809
  return this._repo.peers;
929
810
  }
811
+ async _getContainingSpaceForDocument(documentId) {
812
+ const doc = this._repo.handles[documentId]?.docSync();
813
+ if (!doc) {
814
+ return null;
815
+ }
816
+ const spaceKeyHex = getSpaceKeyFromDoc(doc);
817
+ if (!spaceKeyHex) {
818
+ return null;
819
+ }
820
+ return PublicKey.from(spaceKeyHex);
821
+ }
930
822
  //
931
823
  // Methods for client-services.
932
824
  //
933
825
  async flush({ states }) {
934
826
  await Promise.all(states?.map(async ({ heads, documentId }) => {
935
- invariant4(heads, "heads are required for flush", {
936
- F: __dxlog_file5,
937
- L: 252,
827
+ invariant3(heads, "heads are required for flush", {
828
+ F: __dxlog_file4,
829
+ L: 243,
938
830
  S: this,
939
831
  A: [
940
832
  "heads",
@@ -955,24 +847,6 @@ var AutomergeHost = class {
955
847
  async getHostInfo() {
956
848
  return this._clientNetwork.getHostInfo();
957
849
  }
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
850
  };
977
851
  _ts_decorate2([
978
852
  trace.info()
@@ -1025,8 +899,8 @@ var changeIsPresentInDoc = (doc, changeHash) => {
1025
899
  import { Event as Event2 } from "@dxos/async";
1026
900
  import { cancelWithContext } from "@dxos/context";
1027
901
  import { warnAfterTimeout } from "@dxos/debug";
1028
- import { invariant as invariant5 } from "@dxos/invariant";
1029
- import { log as log5 } from "@dxos/log";
902
+ import { invariant as invariant4 } from "@dxos/invariant";
903
+ import { log as log4 } from "@dxos/log";
1030
904
  import { trace as trace2 } from "@dxos/tracing";
1031
905
  function _ts_decorate3(decorators, target, key, desc) {
1032
906
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -1038,7 +912,7 @@ function _ts_decorate3(decorators, target, key, desc) {
1038
912
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1039
913
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1040
914
  }
1041
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-doc-loader.ts";
915
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-doc-loader.ts";
1042
916
  var AutomergeDocumentLoaderImpl = class {
1043
917
  constructor(_spaceKey, _repo) {
1044
918
  this._spaceKey = _spaceKey;
@@ -1059,10 +933,10 @@ var AutomergeDocumentLoaderImpl = class {
1059
933
  return;
1060
934
  }
1061
935
  if (!spaceState.rootUrl) {
1062
- log5.error("Database opened with no rootUrl", {
936
+ log4.error("Database opened with no rootUrl", {
1063
937
  spaceKey: this._spaceKey
1064
938
  }, {
1065
- F: __dxlog_file6,
939
+ F: __dxlog_file5,
1066
940
  L: 70,
1067
941
  S: this,
1068
942
  C: (f, a) => f(...a)
@@ -1071,8 +945,8 @@ var AutomergeDocumentLoaderImpl = class {
1071
945
  } else {
1072
946
  const existingDocHandle = await this._initDocHandle(ctx, spaceState.rootUrl);
1073
947
  const doc = existingDocHandle.docSync();
1074
- invariant5(doc, void 0, {
1075
- F: __dxlog_file6,
948
+ invariant4(doc, void 0, {
949
+ F: __dxlog_file5,
1076
950
  L: 75,
1077
951
  S: this,
1078
952
  A: [
@@ -1093,8 +967,8 @@ var AutomergeDocumentLoaderImpl = class {
1093
967
  let hasUrlsToLoad = false;
1094
968
  const urlsToLoad = {};
1095
969
  for (const objectId of objectIds) {
1096
- invariant5(this._spaceRootDocHandle, void 0, {
1097
- F: __dxlog_file6,
970
+ invariant4(this._spaceRootDocHandle, void 0, {
971
+ F: __dxlog_file5,
1098
972
  L: 88,
1099
973
  S: this,
1100
974
  A: [
@@ -1106,8 +980,8 @@ var AutomergeDocumentLoaderImpl = class {
1106
980
  continue;
1107
981
  }
1108
982
  const spaceRootDoc = this._spaceRootDocHandle.docSync();
1109
- invariant5(spaceRootDoc, void 0, {
1110
- F: __dxlog_file6,
983
+ invariant4(spaceRootDoc, void 0, {
984
+ F: __dxlog_file5,
1111
985
  L: 93,
1112
986
  S: this,
1113
987
  A: [
@@ -1118,10 +992,10 @@ var AutomergeDocumentLoaderImpl = class {
1118
992
  const documentUrl = (spaceRootDoc.links ?? {})[objectId];
1119
993
  if (documentUrl == null) {
1120
994
  this._objectsPendingDocumentLoad.add(objectId);
1121
- log5.info("loading delayed until object links are initialized", {
995
+ log4.info("loading delayed until object links are initialized", {
1122
996
  objectId
1123
997
  }, {
1124
- F: __dxlog_file6,
998
+ F: __dxlog_file5,
1125
999
  L: 97,
1126
1000
  S: this,
1127
1001
  C: (f, a) => f(...a)
@@ -1144,8 +1018,8 @@ var AutomergeDocumentLoaderImpl = class {
1144
1018
  linksAwaitingLoad.forEach(([objectId]) => this._objectsPendingDocumentLoad.delete(objectId));
1145
1019
  }
1146
1020
  getSpaceRootDocHandle() {
1147
- invariant5(this._spaceRootDocHandle, void 0, {
1148
- F: __dxlog_file6,
1021
+ invariant4(this._spaceRootDocHandle, void 0, {
1022
+ F: __dxlog_file5,
1149
1023
  L: 120,
1150
1024
  S: this,
1151
1025
  A: [
@@ -1156,8 +1030,8 @@ var AutomergeDocumentLoaderImpl = class {
1156
1030
  return this._spaceRootDocHandle;
1157
1031
  }
1158
1032
  createDocumentForObject(objectId) {
1159
- invariant5(this._spaceRootDocHandle, void 0, {
1160
- F: __dxlog_file6,
1033
+ invariant4(this._spaceRootDocHandle, void 0, {
1034
+ F: __dxlog_file5,
1161
1035
  L: 125,
1162
1036
  S: this,
1163
1037
  A: [
@@ -1196,11 +1070,11 @@ var AutomergeDocumentLoaderImpl = class {
1196
1070
  };
1197
1071
  const objectDocumentHandle = this._objectDocumentHandles.get(objectId);
1198
1072
  if (objectDocumentHandle != null && objectDocumentHandle.url !== automergeUrl) {
1199
- log5.warn("object already inlined in a different document, ignoring the link", {
1073
+ log4.warn("object already inlined in a different document, ignoring the link", {
1200
1074
  ...logMeta,
1201
1075
  actualDocumentUrl: objectDocumentHandle.url
1202
1076
  }, {
1203
- F: __dxlog_file6,
1077
+ F: __dxlog_file5,
1204
1078
  L: 155,
1205
1079
  S: this,
1206
1080
  C: (f, a) => f(...a)
@@ -1208,8 +1082,8 @@ var AutomergeDocumentLoaderImpl = class {
1208
1082
  continue;
1209
1083
  }
1210
1084
  if (objectDocumentHandle?.url === automergeUrl) {
1211
- log5.warn("object document was already loaded", logMeta, {
1212
- F: __dxlog_file6,
1085
+ log4.warn("object document was already loaded", logMeta, {
1086
+ F: __dxlog_file5,
1213
1087
  L: 162,
1214
1088
  S: this,
1215
1089
  C: (f, a) => f(...a)
@@ -1217,8 +1091,8 @@ var AutomergeDocumentLoaderImpl = class {
1217
1091
  continue;
1218
1092
  }
1219
1093
  const handle = this._repo.find(automergeUrl);
1220
- log5.debug("document loading triggered", logMeta, {
1221
- F: __dxlog_file6,
1094
+ log4.debug("document loading triggered", logMeta, {
1095
+ F: __dxlog_file5,
1222
1096
  L: 166,
1223
1097
  S: this,
1224
1098
  C: (f, a) => f(...a)
@@ -1237,11 +1111,11 @@ var AutomergeDocumentLoaderImpl = class {
1237
1111
  break;
1238
1112
  } catch (err) {
1239
1113
  if (`${err}`.includes("Timeout")) {
1240
- log5.info("wraparound", {
1114
+ log4.info("wraparound", {
1241
1115
  id: docHandle.documentId,
1242
1116
  state: docHandle.state
1243
1117
  }, {
1244
- F: __dxlog_file6,
1118
+ F: __dxlog_file5,
1245
1119
  L: 182,
1246
1120
  S: this,
1247
1121
  C: (f, a) => f(...a)
@@ -1282,8 +1156,8 @@ var AutomergeDocumentLoaderImpl = class {
1282
1156
  docUrl: handle.url
1283
1157
  };
1284
1158
  if (this.onObjectDocumentLoaded.listenerCount() === 0) {
1285
- log5.info("document loaded after all listeners were removed", logMeta, {
1286
- F: __dxlog_file6,
1159
+ log4.info("document loaded after all listeners were removed", logMeta, {
1160
+ F: __dxlog_file5,
1287
1161
  L: 218,
1288
1162
  S: this,
1289
1163
  C: (f, a) => f(...a)
@@ -1292,8 +1166,8 @@ var AutomergeDocumentLoaderImpl = class {
1292
1166
  }
1293
1167
  const objectDocHandle = this._objectDocumentHandles.get(objectId);
1294
1168
  if (objectDocHandle?.url !== handle.url) {
1295
- log5.warn("object was rebound while a document was loading, discarding handle", logMeta, {
1296
- F: __dxlog_file6,
1169
+ log4.warn("object was rebound while a document was loading, discarding handle", logMeta, {
1170
+ F: __dxlog_file5,
1297
1171
  L: 223,
1298
1172
  S: this,
1299
1173
  C: (f, a) => f(...a)
@@ -1306,13 +1180,13 @@ var AutomergeDocumentLoaderImpl = class {
1306
1180
  });
1307
1181
  } catch (err) {
1308
1182
  const shouldRetryLoading = this.onObjectDocumentLoaded.listenerCount() > 0;
1309
- log5.warn("failed to load a document", {
1183
+ log4.warn("failed to load a document", {
1310
1184
  objectId,
1311
1185
  automergeUrl: handle.url,
1312
1186
  retryLoading: shouldRetryLoading,
1313
1187
  err
1314
1188
  }, {
1315
- F: __dxlog_file6,
1189
+ F: __dxlog_file5,
1316
1190
  L: 229,
1317
1191
  S: this,
1318
1192
  C: (f, a) => f(...a)
@@ -1331,6 +1205,272 @@ _ts_decorate3([
1331
1205
  AutomergeDocumentLoaderImpl = _ts_decorate3([
1332
1206
  trace2.resource()
1333
1207
  ], AutomergeDocumentLoaderImpl);
1208
+
1209
+ // packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts
1210
+ import { cbor as cbor2 } from "@dxos/automerge/automerge-repo";
1211
+ import { Resource as Resource2 } from "@dxos/context";
1212
+ import { invariant as invariant5 } from "@dxos/invariant";
1213
+ import { PublicKey as PublicKey2 } from "@dxos/keys";
1214
+ import { log as log5 } from "@dxos/log";
1215
+ import { AutomergeReplicator } from "@dxos/teleport-extension-automerge-replicator";
1216
+ import { ComplexMap, ComplexSet, defaultMap } from "@dxos/util";
1217
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts";
1218
+ var MeshEchoReplicator = class {
1219
+ constructor() {
1220
+ this._connections = /* @__PURE__ */ new Set();
1221
+ /**
1222
+ * Using automerge peerId as a key.
1223
+ */
1224
+ this._connectionsPerPeer = /* @__PURE__ */ new Map();
1225
+ /**
1226
+ * spaceKey -> deviceKey[]
1227
+ */
1228
+ this._authorizedDevices = new ComplexMap(PublicKey2.hash);
1229
+ this._context = null;
1230
+ }
1231
+ async connect(context) {
1232
+ this._context = context;
1233
+ }
1234
+ async disconnect() {
1235
+ for (const connection of this._connections) {
1236
+ await connection.close();
1237
+ }
1238
+ this._connections.clear();
1239
+ this._connectionsPerPeer.clear();
1240
+ this._context = null;
1241
+ }
1242
+ createExtension() {
1243
+ invariant5(this._context, void 0, {
1244
+ F: __dxlog_file6,
1245
+ L: 54,
1246
+ S: this,
1247
+ A: [
1248
+ "this._context",
1249
+ ""
1250
+ ]
1251
+ });
1252
+ const connection = new MeshReplicatorConnection({
1253
+ ownPeerId: this._context.peerId,
1254
+ onRemoteConnected: async () => {
1255
+ log5("onRemoteConnected", {
1256
+ peerId: connection.peerId
1257
+ }, {
1258
+ F: __dxlog_file6,
1259
+ L: 59,
1260
+ S: this,
1261
+ C: (f, a) => f(...a)
1262
+ });
1263
+ invariant5(this._context, void 0, {
1264
+ F: __dxlog_file6,
1265
+ L: 60,
1266
+ S: this,
1267
+ A: [
1268
+ "this._context",
1269
+ ""
1270
+ ]
1271
+ });
1272
+ if (!this._connectionsPerPeer.has(connection.peerId)) {
1273
+ this._connectionsPerPeer.set(connection.peerId, connection);
1274
+ await connection.enable();
1275
+ this._context.onConnectionOpen(connection);
1276
+ }
1277
+ },
1278
+ onRemoteDisconnected: async () => {
1279
+ log5("onRemoteDisconnected", {
1280
+ peerId: connection.peerId
1281
+ }, {
1282
+ F: __dxlog_file6,
1283
+ L: 69,
1284
+ S: this,
1285
+ C: (f, a) => f(...a)
1286
+ });
1287
+ this._context?.onConnectionClosed(connection);
1288
+ await connection.disable();
1289
+ this._connectionsPerPeer.delete(connection.peerId);
1290
+ this._connections.delete(connection);
1291
+ },
1292
+ shouldAdvertize: async (params) => {
1293
+ log5("shouldAdvertize", {
1294
+ peerId: connection.peerId,
1295
+ documentId: params.documentId
1296
+ }, {
1297
+ F: __dxlog_file6,
1298
+ L: 76,
1299
+ S: this,
1300
+ C: (f, a) => f(...a)
1301
+ });
1302
+ invariant5(this._context, void 0, {
1303
+ F: __dxlog_file6,
1304
+ L: 77,
1305
+ S: this,
1306
+ A: [
1307
+ "this._context",
1308
+ ""
1309
+ ]
1310
+ });
1311
+ try {
1312
+ const spaceKey = await this._context.getContainingSpaceForDocument(params.documentId);
1313
+ if (!spaceKey) {
1314
+ log5("space key not found for share policy check", {
1315
+ peerId: connection.peerId,
1316
+ documentId: params.documentId
1317
+ }, {
1318
+ F: __dxlog_file6,
1319
+ L: 81,
1320
+ S: this,
1321
+ C: (f, a) => f(...a)
1322
+ });
1323
+ return false;
1324
+ }
1325
+ const authorizedDevices = this._authorizedDevices.get(spaceKey);
1326
+ if (!connection.remoteDeviceKey) {
1327
+ log5("device key not found for share policy check", {
1328
+ peerId: connection.peerId,
1329
+ documentId: params.documentId
1330
+ }, {
1331
+ F: __dxlog_file6,
1332
+ L: 91,
1333
+ S: this,
1334
+ C: (f, a) => f(...a)
1335
+ });
1336
+ return false;
1337
+ }
1338
+ const isAuthorized = authorizedDevices?.has(connection.remoteDeviceKey) ?? false;
1339
+ log5("share policy check", {
1340
+ localPeer: this._context.peerId,
1341
+ remotePeer: connection.peerId,
1342
+ documentId: params.documentId,
1343
+ deviceKey: connection.remoteDeviceKey,
1344
+ spaceKey,
1345
+ isAuthorized
1346
+ }, {
1347
+ F: __dxlog_file6,
1348
+ L: 99,
1349
+ S: this,
1350
+ C: (f, a) => f(...a)
1351
+ });
1352
+ return isAuthorized;
1353
+ } catch (err) {
1354
+ log5.catch(err, void 0, {
1355
+ F: __dxlog_file6,
1356
+ L: 109,
1357
+ S: this,
1358
+ C: (f, a) => f(...a)
1359
+ });
1360
+ return false;
1361
+ }
1362
+ }
1363
+ });
1364
+ this._connections.add(connection);
1365
+ return connection.replicatorExtension;
1366
+ }
1367
+ authorizeDevice(spaceKey, deviceKey) {
1368
+ log5("authorizeDevice", {
1369
+ spaceKey,
1370
+ deviceKey
1371
+ }, {
1372
+ F: __dxlog_file6,
1373
+ L: 120,
1374
+ S: this,
1375
+ C: (f, a) => f(...a)
1376
+ });
1377
+ defaultMap(this._authorizedDevices, spaceKey, () => new ComplexSet(PublicKey2.hash)).add(deviceKey);
1378
+ }
1379
+ };
1380
+ var MeshReplicatorConnection = class extends Resource2 {
1381
+ constructor(_params) {
1382
+ super();
1383
+ this._params = _params;
1384
+ this.remoteDeviceKey = null;
1385
+ this._remotePeerId = null;
1386
+ this._isEnabled = false;
1387
+ let readableStreamController;
1388
+ this.readable = new ReadableStream({
1389
+ start: (controller) => {
1390
+ readableStreamController = controller;
1391
+ this._ctx.onDispose(() => controller.close());
1392
+ }
1393
+ });
1394
+ this.writable = new WritableStream({
1395
+ write: async (message, controller) => {
1396
+ this.replicatorExtension.sendSyncMessage({
1397
+ payload: cbor2.encode(message)
1398
+ }).catch((err) => {
1399
+ controller.error(err);
1400
+ });
1401
+ }
1402
+ });
1403
+ this.replicatorExtension = new AutomergeReplicator({
1404
+ peerId: this._params.ownPeerId
1405
+ }, {
1406
+ onStartReplication: async (info, remotePeerId) => {
1407
+ this.remoteDeviceKey = remotePeerId;
1408
+ this._remotePeerId = info.id;
1409
+ log5("onStartReplication", {
1410
+ id: info.id,
1411
+ thisPeerId: this.peerId,
1412
+ remotePeerId: remotePeerId.toHex()
1413
+ }, {
1414
+ F: __dxlog_file6,
1415
+ L: 185,
1416
+ S: this,
1417
+ C: (f, a) => f(...a)
1418
+ });
1419
+ await this._params.onRemoteConnected();
1420
+ },
1421
+ onSyncMessage: async ({ payload }) => {
1422
+ if (!this._isEnabled) {
1423
+ return;
1424
+ }
1425
+ const message = cbor2.decode(payload);
1426
+ readableStreamController.enqueue(message);
1427
+ },
1428
+ onClose: async () => {
1429
+ if (!this._isEnabled) {
1430
+ return;
1431
+ }
1432
+ await this._params.onRemoteDisconnected();
1433
+ }
1434
+ });
1435
+ }
1436
+ get peerId() {
1437
+ invariant5(this._remotePeerId != null, "Remote peer has not connected yet.", {
1438
+ F: __dxlog_file6,
1439
+ L: 208,
1440
+ S: this,
1441
+ A: [
1442
+ "this._remotePeerId != null",
1443
+ "'Remote peer has not connected yet.'"
1444
+ ]
1445
+ });
1446
+ return this._remotePeerId;
1447
+ }
1448
+ async shouldAdvertize(params) {
1449
+ return this._params.shouldAdvertize(params);
1450
+ }
1451
+ /**
1452
+ * Start exchanging messages with the remote peer.
1453
+ * Call after the remote peer has connected.
1454
+ */
1455
+ async enable() {
1456
+ invariant5(this._remotePeerId != null, "Remote peer has not connected yet.", {
1457
+ F: __dxlog_file6,
1458
+ L: 221,
1459
+ S: this,
1460
+ A: [
1461
+ "this._remotePeerId != null",
1462
+ "'Remote peer has not connected yet.'"
1463
+ ]
1464
+ });
1465
+ this._isEnabled = true;
1466
+ }
1467
+ /**
1468
+ * Stop exchanging messages with the remote peer.
1469
+ */
1470
+ async disable() {
1471
+ this._isEnabled = false;
1472
+ }
1473
+ };
1334
1474
  export {
1335
1475
  AuthExtension,
1336
1476
  AuthStatus,
@@ -1342,7 +1482,7 @@ export {
1342
1482
  LocalHostNetworkAdapter,
1343
1483
  MOCK_AUTH_PROVIDER,
1344
1484
  MOCK_AUTH_VERIFIER,
1345
- MeshNetworkAdapter,
1485
+ MeshEchoReplicator,
1346
1486
  MetadataStore,
1347
1487
  Pipeline,
1348
1488
  SnapshotManager,