@dxos/echo-pipeline 0.6.1 → 0.6.2-main.8a232a5

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 (45) hide show
  1. package/dist/lib/browser/{chunk-DMUP426Q.mjs → chunk-UJQ5VS5V.mjs} +383 -196
  2. package/dist/lib/browser/chunk-UJQ5VS5V.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +59 -562
  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 +1 -1
  7. package/dist/lib/node/{chunk-NH5WJKOW.cjs → chunk-RH6TDRML.cjs} +438 -256
  8. package/dist/lib/node/chunk-RH6TDRML.cjs.map +7 -0
  9. package/dist/lib/node/index.cjs +80 -581
  10. package/dist/lib/node/index.cjs.map +4 -4
  11. package/dist/lib/node/meta.json +1 -1
  12. package/dist/lib/node/testing/index.cjs +11 -11
  13. package/dist/types/src/automerge/automerge-host.d.ts +2 -17
  14. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
  15. package/dist/types/src/automerge/echo-network-adapter.d.ts +1 -1
  16. package/dist/types/src/automerge/index.d.ts +0 -2
  17. package/dist/types/src/automerge/index.d.ts.map +1 -1
  18. package/dist/types/src/db-host/data-service.d.ts +10 -7
  19. package/dist/types/src/db-host/data-service.d.ts.map +1 -1
  20. package/dist/types/src/db-host/documents-synchronizer.d.ts +43 -0
  21. package/dist/types/src/db-host/documents-synchronizer.d.ts.map +1 -0
  22. package/dist/types/src/db-host/documents-synchronizer.test.d.ts +2 -0
  23. package/dist/types/src/db-host/documents-synchronizer.test.d.ts.map +1 -0
  24. package/dist/types/src/db-host/index.d.ts +1 -0
  25. package/dist/types/src/db-host/index.d.ts.map +1 -1
  26. package/package.json +33 -33
  27. package/src/automerge/automerge-host.ts +6 -56
  28. package/src/automerge/automerge-repo.test.ts +124 -1
  29. package/src/automerge/echo-network-adapter.ts +1 -1
  30. package/src/automerge/index.ts +0 -2
  31. package/src/db-host/data-service.ts +49 -25
  32. package/src/db-host/documents-synchronizer.test.ts +40 -0
  33. package/src/db-host/documents-synchronizer.ts +156 -0
  34. package/src/db-host/index.ts +1 -0
  35. package/dist/lib/browser/chunk-DMUP426Q.mjs.map +0 -7
  36. package/dist/lib/node/chunk-NH5WJKOW.cjs.map +0 -7
  37. package/dist/types/src/automerge/automerge-doc-loader.d.ts +0 -71
  38. package/dist/types/src/automerge/automerge-doc-loader.d.ts.map +0 -1
  39. package/dist/types/src/automerge/automerge-doc-loader.test.d.ts +0 -2
  40. package/dist/types/src/automerge/automerge-doc-loader.test.d.ts.map +0 -1
  41. package/dist/types/src/automerge/local-host-network-adapter.d.ts +0 -30
  42. package/dist/types/src/automerge/local-host-network-adapter.d.ts.map +0 -1
  43. package/src/automerge/automerge-doc-loader.test.ts +0 -103
  44. package/src/automerge/automerge-doc-loader.ts +0 -267
  45. package/src/automerge/local-host-network-adapter.ts +0 -115
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/echo-pipeline",
3
- "version": "0.6.1",
3
+ "version": "0.6.2-main.8a232a5",
4
4
  "description": "ECHO database.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -39,38 +39,38 @@
39
39
  "crc-32": "^1.2.2",
40
40
  "level": "^8.0.1",
41
41
  "level-transcoder": "^1.0.1",
42
- "@dxos/automerge": "0.6.1",
43
- "@dxos/context": "0.6.1",
44
- "@dxos/codec-protobuf": "0.6.1",
45
- "@dxos/credentials": "0.6.1",
46
- "@dxos/crypto": "0.6.1",
47
- "@dxos/debug": "0.6.1",
48
- "@dxos/echo-schema": "0.6.1",
49
- "@dxos/feed-store": "0.6.1",
50
- "@dxos/echo-protocol": "0.6.1",
51
- "@dxos/hypercore": "0.6.1",
52
- "@dxos/invariant": "0.6.1",
53
- "@dxos/indexing": "0.6.1",
54
- "@dxos/async": "0.6.1",
55
- "@dxos/keyring": "0.6.1",
56
- "@dxos/keys": "0.6.1",
57
- "@dxos/kv-store": "0.6.1",
58
- "@dxos/log": "0.6.1",
59
- "@dxos/messaging": "0.6.1",
60
- "@dxos/network-manager": "0.6.1",
61
- "@dxos/node-std": "0.6.1",
62
- "@dxos/protocols": "0.6.1",
63
- "@dxos/random-access-storage": "0.6.1",
64
- "@dxos/rpc": "0.6.1",
65
- "@dxos/teleport": "0.6.1",
66
- "@dxos/teleport-extension-automerge-replicator": "0.6.1",
67
- "@dxos/teleport-extension-object-sync": "0.6.1",
68
- "@dxos/teleport-extension-gossip": "0.6.1",
69
- "@dxos/teleport-extension-replicator": "0.6.1",
70
- "@dxos/timeframe": "0.6.1",
71
- "@dxos/tracing": "0.6.1",
72
- "@dxos/typings": "0.6.1",
73
- "@dxos/util": "0.6.1"
42
+ "@dxos/async": "0.6.2-main.8a232a5",
43
+ "@dxos/codec-protobuf": "0.6.2-main.8a232a5",
44
+ "@dxos/automerge": "0.6.2-main.8a232a5",
45
+ "@dxos/context": "0.6.2-main.8a232a5",
46
+ "@dxos/credentials": "0.6.2-main.8a232a5",
47
+ "@dxos/crypto": "0.6.2-main.8a232a5",
48
+ "@dxos/echo-protocol": "0.6.2-main.8a232a5",
49
+ "@dxos/debug": "0.6.2-main.8a232a5",
50
+ "@dxos/echo-schema": "0.6.2-main.8a232a5",
51
+ "@dxos/feed-store": "0.6.2-main.8a232a5",
52
+ "@dxos/hypercore": "0.6.2-main.8a232a5",
53
+ "@dxos/indexing": "0.6.2-main.8a232a5",
54
+ "@dxos/invariant": "0.6.2-main.8a232a5",
55
+ "@dxos/keyring": "0.6.2-main.8a232a5",
56
+ "@dxos/keys": "0.6.2-main.8a232a5",
57
+ "@dxos/kv-store": "0.6.2-main.8a232a5",
58
+ "@dxos/messaging": "0.6.2-main.8a232a5",
59
+ "@dxos/log": "0.6.2-main.8a232a5",
60
+ "@dxos/network-manager": "0.6.2-main.8a232a5",
61
+ "@dxos/protocols": "0.6.2-main.8a232a5",
62
+ "@dxos/node-std": "0.6.2-main.8a232a5",
63
+ "@dxos/rpc": "0.6.2-main.8a232a5",
64
+ "@dxos/random-access-storage": "0.6.2-main.8a232a5",
65
+ "@dxos/teleport": "0.6.2-main.8a232a5",
66
+ "@dxos/teleport-extension-gossip": "0.6.2-main.8a232a5",
67
+ "@dxos/teleport-extension-automerge-replicator": "0.6.2-main.8a232a5",
68
+ "@dxos/teleport-extension-replicator": "0.6.2-main.8a232a5",
69
+ "@dxos/teleport-extension-object-sync": "0.6.2-main.8a232a5",
70
+ "@dxos/timeframe": "0.6.2-main.8a232a5",
71
+ "@dxos/tracing": "0.6.2-main.8a232a5",
72
+ "@dxos/typings": "0.6.2-main.8a232a5",
73
+ "@dxos/util": "0.6.2-main.8a232a5"
74
74
  },
75
75
  "devDependencies": {
76
76
  "fast-check": "^3.19.0",
@@ -8,21 +8,20 @@ import {
8
8
  getBackend,
9
9
  getHeads,
10
10
  isAutomerge,
11
- save,
12
11
  equals as headsEquals,
12
+ save,
13
13
  type Doc,
14
14
  type Heads,
15
15
  } from '@dxos/automerge/automerge';
16
16
  import {
17
+ type DocHandleChangePayload,
17
18
  Repo,
18
19
  type AnyDocumentId,
19
20
  type DocHandle,
20
- type DocHandleChangePayload,
21
21
  type DocumentId,
22
22
  type PeerId,
23
23
  type StorageAdapterInterface,
24
24
  } from '@dxos/automerge/automerge-repo';
25
- import { type Stream } from '@dxos/codec-protobuf';
26
25
  import { Context, Resource, cancelWithContext, type Lifecycle } from '@dxos/context';
27
26
  import { type SpaceDoc } from '@dxos/echo-protocol';
28
27
  import { type IndexMetadataStore } from '@dxos/indexing';
@@ -31,13 +30,7 @@ import { PublicKey } from '@dxos/keys';
31
30
  import { type LevelDB } from '@dxos/kv-store';
32
31
  import { log } from '@dxos/log';
33
32
  import { objectPointerCodec } from '@dxos/protocols';
34
- import {
35
- type DocHeadsList,
36
- type FlushRequest,
37
- type HostInfo,
38
- type SyncRepoRequest,
39
- type SyncRepoResponse,
40
- } from '@dxos/protocols/proto/dxos/echo/service';
33
+ import { type DocHeadsList, type FlushRequest } from '@dxos/protocols/proto/dxos/echo/service';
41
34
  import { trace } from '@dxos/tracing';
42
35
  import { mapValues } from '@dxos/util';
43
36
 
@@ -45,10 +38,6 @@ import { EchoNetworkAdapter, isEchoPeerMetadata } from './echo-network-adapter';
45
38
  import { type EchoReplicator } from './echo-replicator';
46
39
  import { HeadsStore } from './heads-store';
47
40
  import { LevelDBStorageAdapter, type BeforeSaveParams } from './leveldb-storage-adapter';
48
- import { LocalHostNetworkAdapter } from './local-host-network-adapter';
49
-
50
- // TODO: Remove
51
- export type { DocumentId };
52
41
 
53
42
  export type AutomergeHostParams = {
54
43
  db: LevelDB;
@@ -79,7 +68,6 @@ export class AutomergeHost extends Resource {
79
68
  });
80
69
 
81
70
  private _repo!: Repo;
82
- private _clientNetwork!: LocalHostNetworkAdapter;
83
71
  private _storage!: StorageAdapterInterface & Lifecycle;
84
72
  private readonly _headsStore: HeadsStore;
85
73
 
@@ -105,7 +93,6 @@ export class AutomergeHost extends Resource {
105
93
  this._peerId = `host-${PublicKey.random().toHex()}` as PeerId;
106
94
 
107
95
  await this._storage.open?.();
108
- this._clientNetwork = new LocalHostNetworkAdapter();
109
96
 
110
97
  // Construct the automerge repo.
111
98
  this._repo = new Repo({
@@ -113,22 +100,17 @@ export class AutomergeHost extends Resource {
113
100
  sharePolicy: this._sharePolicy.bind(this),
114
101
  storage: this._storage,
115
102
  network: [
116
- // Downstream client.
117
- this._clientNetwork,
118
103
  // Upstream swarm.
119
104
  this._echoNetworkAdapter,
120
105
  ],
121
106
  });
122
107
 
123
- this._clientNetwork.ready();
124
108
  await this._echoNetworkAdapter.open();
125
- await this._clientNetwork.whenConnected();
126
109
  await this._echoNetworkAdapter.whenConnected();
127
110
  }
128
111
 
129
112
  protected override async _close() {
130
113
  await this._storage.close?.();
131
- await this._clientNetwork.close();
132
114
  await this._echoNetworkAdapter.close();
133
115
  await this._ctx.dispose();
134
116
  }
@@ -335,21 +317,10 @@ export class AutomergeHost extends Resource {
335
317
  * Flush documents to disk.
336
318
  */
337
319
  @trace.span({ showInBrowserTimeline: true })
338
- async flush({ states }: FlushRequest = {}): Promise<void> {
339
- // Note: Wait for all requested documents to be loaded/synced from thin-client.
340
- if (states) {
341
- await Promise.all(
342
- states.map(async ({ heads, documentId }) => {
343
- if (!heads) {
344
- return;
345
- }
346
- const handle = this._repo.handles[documentId as DocumentId] ?? this._repo.find(documentId as DocumentId);
347
- await waitForHeads(handle, heads);
348
- }) ?? [],
349
- );
350
- }
320
+ async flush({ documentIds }: FlushRequest = {}): Promise<void> {
321
+ // Note: Sync protocol for client and services ensures that all handles should have all changes.
351
322
 
352
- await this._repo.flush(states?.map(({ documentId }) => documentId as DocumentId));
323
+ await this._repo.flush(documentIds as DocumentId[] | undefined);
353
324
  }
354
325
 
355
326
  async getHeads(documentId: DocumentId): Promise<Heads | undefined> {
@@ -364,27 +335,6 @@ export class AutomergeHost extends Resource {
364
335
  return this._headsStore.getHeads(documentId);
365
336
  }
366
337
  }
367
-
368
- /**
369
- * Host <-> Client sync.
370
- */
371
- syncRepo(request: SyncRepoRequest): Stream<SyncRepoResponse> {
372
- return this._clientNetwork.syncRepo(request);
373
- }
374
-
375
- /**
376
- * Host <-> Client sync.
377
- */
378
- sendSyncMessage(request: SyncRepoRequest): Promise<void> {
379
- return this._clientNetwork.sendSyncMessage(request);
380
- }
381
-
382
- /**
383
- * Host <-> Client sync.
384
- */
385
- async getHostInfo(): Promise<HostInfo> {
386
- return this._clientNetwork.getHostInfo();
387
- }
388
338
  }
389
339
 
390
340
  export const getSpaceKeyFromDoc = (doc: Doc<SpaceDoc>): string | null => {
@@ -6,7 +6,18 @@ import { expect } from 'chai';
6
6
  import waitForExpect from 'wait-for-expect';
7
7
 
8
8
  import { asyncTimeout, sleep } from '@dxos/async';
9
- import { type Heads, change, clone, equals, from, getBackend, getHeads } from '@dxos/automerge/automerge';
9
+ import {
10
+ next as A,
11
+ type Heads,
12
+ change,
13
+ clone,
14
+ equals,
15
+ from,
16
+ getBackend,
17
+ getHeads,
18
+ save,
19
+ saveSince,
20
+ } from '@dxos/automerge/automerge';
10
21
  import {
11
22
  type Message,
12
23
  Repo,
@@ -14,6 +25,8 @@ import {
14
25
  type DocumentId,
15
26
  type HandleState,
16
27
  type AutomergeUrl,
28
+ parseAutomergeUrl,
29
+ generateAutomergeUrl,
17
30
  } from '@dxos/automerge/automerge-repo';
18
31
  import { randomBytes } from '@dxos/crypto';
19
32
  import { PublicKey } from '@dxos/keys';
@@ -630,5 +643,115 @@ describe('AutomergeRepo', () => {
630
643
  expect(peer2.handles[hostHandle.documentId]).to.not.be.undefined;
631
644
  });
632
645
  });
646
+
647
+ test('client cold-starts and syncs doc from a Repo', async () => {
648
+ const repo = new Repo({ network: [] });
649
+ const serverHandle = repo.create<{ field?: string }>();
650
+
651
+ let clientDoc = A.from<{ field?: string }>({});
652
+ const receiveByClient = (blob: Uint8Array) => {
653
+ clientDoc = A.loadIncremental(clientDoc, blob);
654
+ };
655
+
656
+ // Sync handshake.
657
+ let syncedHeads = getHeads(serverHandle.docSync());
658
+ receiveByClient(save(serverHandle.docSync()));
659
+
660
+ serverHandle.on('change', ({ doc }) => {
661
+ // Note: This is mock of a sync protocol between client and server.
662
+ const blob = saveSince(doc, syncedHeads);
663
+ syncedHeads = getHeads(doc);
664
+ receiveByClient(blob);
665
+ });
666
+
667
+ {
668
+ const value = 'text to test if sync works';
669
+ serverHandle.change((doc: any) => {
670
+ doc.field = value;
671
+ });
672
+ expect(clientDoc.field).to.deep.equal(value);
673
+ }
674
+
675
+ {
676
+ const value = 'test if updates propagate';
677
+ serverHandle.change((doc: any) => {
678
+ doc.field = value;
679
+ });
680
+ expect(clientDoc.field).to.deep.equal(value);
681
+ }
682
+ });
683
+
684
+ test('client creates doc and syncs with a Repo', async () => {
685
+ const repo = new Repo({ network: [] });
686
+ const receiveByServer = async (blob: Uint8Array, docId: DocumentId) => {
687
+ const serverHandle = repo.find(docId);
688
+ serverHandle.update((doc) => {
689
+ return A.loadIncremental(doc, blob);
690
+ });
691
+ };
692
+
693
+ let clientDoc = A.from<{ field?: string }>({});
694
+ const { documentId } = parseAutomergeUrl(generateAutomergeUrl());
695
+ // Sync handshake.
696
+ let sentHeads = getHeads(clientDoc);
697
+
698
+ // Sync protocol.
699
+ const sendDoc = async (doc: A.Doc<any>) => {
700
+ await receiveByServer(saveSince(doc, sentHeads), documentId);
701
+ sentHeads = getHeads(doc);
702
+ };
703
+
704
+ {
705
+ // Change doc and send changes to server.
706
+ const value = 'text to test if sync works';
707
+ clientDoc = A.change(clientDoc, (doc: any) => {
708
+ doc.field = value;
709
+ });
710
+ await sendDoc(clientDoc);
711
+
712
+ await repo.find(documentId).whenReady();
713
+ expect(repo.find(documentId).docSync().field).to.deep.equal(value);
714
+ }
715
+ });
716
+
717
+ test('two repo sync docs on `update` call', async () => {
718
+ const [adapter1, adapter2] = TestAdapter.createPair();
719
+ const repoA = new Repo({
720
+ peerId: 'A' as any,
721
+ network: [adapter1],
722
+ sharePolicy: async () => true,
723
+ });
724
+ const repoB = new Repo({
725
+ peerId: 'B' as any,
726
+ network: [adapter2],
727
+ sharePolicy: async () => true,
728
+ });
729
+
730
+ {
731
+ // Connect repos.
732
+ adapter1.ready();
733
+ adapter2.ready();
734
+ await adapter1.onConnect.wait();
735
+ await adapter2.onConnect.wait();
736
+ adapter1.peerCandidate(adapter2.peerId!);
737
+ adapter2.peerCandidate(adapter1.peerId!);
738
+ }
739
+
740
+ const handleA = repoA.create();
741
+ const handleB = repoB.find(handleA.url);
742
+
743
+ const text = 'Hello world';
744
+ handleA.update((doc: any) => {
745
+ const newDoc = A.change(doc, (doc: any) => {
746
+ doc.text = text;
747
+ });
748
+ return newDoc;
749
+ });
750
+
751
+ expect(handleA.docSync().text).to.equal(text);
752
+
753
+ await asyncTimeout(handleB.whenReady(), 1000);
754
+ expect(handleB.docSync().text).to.equal(text);
755
+ });
633
756
  });
634
757
  });
@@ -71,7 +71,7 @@ export class EchoNetworkAdapter extends NetworkAdapter {
71
71
  @synchronized
72
72
  async close() {
73
73
  if (this._lifecycleState === LifecycleState.CLOSED) {
74
- return;
74
+ return this;
75
75
  }
76
76
 
77
77
  for (const replicator of this._replicators) {
@@ -3,8 +3,6 @@
3
3
  //
4
4
 
5
5
  export * from './automerge-host';
6
- export * from './automerge-doc-loader';
7
6
  export * from './leveldb-storage-adapter';
8
- export * from './local-host-network-adapter';
9
7
  export * from './mesh-echo-replicator';
10
8
  export * from './echo-replicator';
@@ -2,25 +2,26 @@
2
2
  // Copyright 2021 DXOS.org
3
3
  //
4
4
 
5
- import { type RequestOptions, type Stream } from '@dxos/codec-protobuf';
5
+ import { type DocumentId } from '@dxos/automerge/automerge-repo';
6
+ import { type RequestOptions, Stream } from '@dxos/codec-protobuf';
7
+ import { invariant } from '@dxos/invariant';
8
+ import { log } from '@dxos/log';
6
9
  import {
7
10
  type DataService,
8
11
  type DocHeadsList,
9
- type EchoEvent,
10
12
  type FlushRequest,
13
+ type SubscribeRequest,
14
+ type BatchedDocumentUpdates,
15
+ type UpdateSubscriptionRequest,
11
16
  type GetDocumentHeadsRequest,
12
17
  type GetDocumentHeadsResponse,
13
- type HostInfo,
14
- type MutationReceipt,
15
18
  type ReIndexHeadsRequest,
16
- type SubscribeRequest,
17
- type SyncRepoRequest,
18
- type SyncRepoResponse,
19
19
  type WaitUntilHeadsReplicatedRequest,
20
- type WriteRequest,
20
+ type UpdateRequest,
21
21
  } from '@dxos/protocols/proto/dxos/echo/service';
22
22
 
23
- import { type AutomergeHost, type DocumentId } from '../automerge';
23
+ import { DocumentsSynchronizer } from './documents-synchronizer';
24
+ import { type AutomergeHost } from '../automerge';
24
25
 
25
26
  export type DataServiceParams = {
26
27
  automergeHost: AutomergeHost;
@@ -32,6 +33,12 @@ export type DataServiceParams = {
32
33
  */
33
34
  // TODO(burdon): Move to client-services.
34
35
  export class DataServiceImpl implements DataService {
36
+ /**
37
+ * Map of subscriptions.
38
+ * subscriptionId -> DocumentsSynchronizer
39
+ */
40
+ private readonly _subscriptions = new Map<string, DocumentsSynchronizer>();
41
+
35
42
  private readonly _automergeHost: AutomergeHost;
36
43
  private readonly _updateIndexes: () => Promise<void>;
37
44
 
@@ -40,30 +47,47 @@ export class DataServiceImpl implements DataService {
40
47
  this._updateIndexes = params.updateIndexes;
41
48
  }
42
49
 
43
- subscribe(request: SubscribeRequest): Stream<EchoEvent> {
44
- throw new Error('Deprecated.');
50
+ subscribe(request: SubscribeRequest): Stream<BatchedDocumentUpdates> {
51
+ return new Stream<BatchedDocumentUpdates>(({ next, ready }) => {
52
+ const synchronizer = new DocumentsSynchronizer({
53
+ repo: this._automergeHost.repo,
54
+ sendUpdates: (updates) => next(updates),
55
+ });
56
+ synchronizer
57
+ .open()
58
+ .then(() => {
59
+ this._subscriptions.set(request.subscriptionId, synchronizer);
60
+ ready();
61
+ })
62
+ .catch((err) => log.catch(err));
63
+ return () => synchronizer.close();
64
+ });
45
65
  }
46
66
 
47
- write(request: WriteRequest): Promise<MutationReceipt> {
48
- throw new Error('Deprecated.');
49
- }
67
+ async updateSubscription(request: UpdateSubscriptionRequest) {
68
+ const synchronizer = this._subscriptions.get(request.subscriptionId);
69
+ invariant(synchronizer, 'Subscription not found');
50
70
 
51
- async flush(request: FlushRequest): Promise<void> {
52
- await this._automergeHost.flush(request);
71
+ if (request.addIds?.length) {
72
+ await synchronizer.addDocuments(request.addIds as DocumentId[]);
73
+ }
74
+ if (request.removeIds?.length) {
75
+ await synchronizer.removeDocuments(request.removeIds as DocumentId[]);
76
+ }
53
77
  }
54
78
 
55
- // Automerge specific.
56
-
57
- async getHostInfo(request: void): Promise<HostInfo> {
58
- return this._automergeHost.getHostInfo();
59
- }
79
+ async update(request: UpdateRequest): Promise<void> {
80
+ if (!request.updates) {
81
+ return;
82
+ }
83
+ const synchronizer = this._subscriptions.get(request.subscriptionId);
84
+ invariant(synchronizer, 'Subscription not found');
60
85
 
61
- syncRepo(request: SyncRepoRequest): Stream<SyncRepoResponse> {
62
- return this._automergeHost.syncRepo(request);
86
+ synchronizer.update(request.updates);
63
87
  }
64
88
 
65
- sendSyncMessage(request: SyncRepoRequest): Promise<void> {
66
- return this._automergeHost.sendSyncMessage(request);
89
+ async flush(request: FlushRequest): Promise<void> {
90
+ await this._automergeHost.flush(request);
67
91
  }
68
92
 
69
93
  async getDocumentHeads(request: GetDocumentHeadsRequest): Promise<GetDocumentHeadsResponse> {
@@ -0,0 +1,40 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { expect } from 'chai';
6
+
7
+ import { sleep } from '@dxos/async';
8
+ import { next as A } from '@dxos/automerge/automerge';
9
+ import { generateAutomergeUrl, parseAutomergeUrl, Repo } from '@dxos/automerge/automerge-repo';
10
+ import { describe, openAndClose, test } from '@dxos/test';
11
+
12
+ import { DocumentsSynchronizer } from './documents-synchronizer';
13
+
14
+ describe('DocumentsSynchronizer', () => {
15
+ test('do not get init changes for client created docs', async () => {
16
+ let counter = 0;
17
+ const serverRepo = new Repo({ network: [] });
18
+ const synchronizer = new DocumentsSynchronizer({
19
+ repo: serverRepo,
20
+ sendUpdates: () => {
21
+ counter++;
22
+ },
23
+ });
24
+ await openAndClose(synchronizer);
25
+
26
+ synchronizer.update([
27
+ {
28
+ documentId: parseAutomergeUrl(generateAutomergeUrl()).documentId,
29
+ mutation: A.save(A.from({ text: 'hello' })),
30
+ isNew: true,
31
+ },
32
+ ]);
33
+
34
+ // Wait for the changes to be processed.
35
+ await sleep(100);
36
+
37
+ // No updates should be sent.
38
+ expect(counter).to.eq(0);
39
+ });
40
+ });