@automerge/automerge-repo 1.0.17 → 1.0.19

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 (37) hide show
  1. package/dist/DocHandle.d.ts +16 -3
  2. package/dist/DocHandle.d.ts.map +1 -1
  3. package/dist/DocHandle.js +20 -13
  4. package/dist/Repo.d.ts +2 -0
  5. package/dist/Repo.d.ts.map +1 -1
  6. package/dist/Repo.js +17 -6
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/network/messages.d.ts +7 -0
  10. package/dist/network/messages.d.ts.map +1 -1
  11. package/dist/storage/StorageSubsystem.d.ts +3 -1
  12. package/dist/storage/StorageSubsystem.d.ts.map +1 -1
  13. package/dist/storage/StorageSubsystem.js +10 -0
  14. package/dist/storage/chunkTypeFromKey.d.ts +1 -2
  15. package/dist/storage/chunkTypeFromKey.d.ts.map +1 -1
  16. package/dist/synchronizer/CollectionSynchronizer.d.ts +2 -0
  17. package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
  18. package/dist/synchronizer/CollectionSynchronizer.js +14 -1
  19. package/dist/synchronizer/DocSynchronizer.d.ts +6 -2
  20. package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
  21. package/dist/synchronizer/DocSynchronizer.js +119 -76
  22. package/dist/synchronizer/Synchronizer.d.ts +2 -1
  23. package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
  24. package/package.json +3 -5
  25. package/src/DocHandle.ts +32 -13
  26. package/src/Repo.ts +23 -6
  27. package/src/index.ts +1 -0
  28. package/src/network/messages.ts +8 -0
  29. package/src/storage/StorageSubsystem.ts +20 -2
  30. package/src/storage/chunkTypeFromKey.ts +1 -2
  31. package/src/synchronizer/CollectionSynchronizer.ts +19 -1
  32. package/src/synchronizer/DocSynchronizer.ts +168 -94
  33. package/src/synchronizer/Synchronizer.ts +6 -1
  34. package/test/DocHandle.test.ts +19 -2
  35. package/test/DocSynchronizer.test.ts +47 -16
  36. package/test/Repo.test.ts +159 -4
  37. package/test/StorageSubsystem.test.ts +30 -2
package/test/Repo.test.ts CHANGED
@@ -1,9 +1,10 @@
1
+ import { next as A } from "@automerge/automerge"
1
2
  import { MessageChannelNetworkAdapter } from "@automerge/automerge-repo-network-messagechannel"
2
3
  import assert from "assert"
3
4
  import * as Uuid from "uuid"
4
5
  import { describe, it } from "vitest"
6
+ import { DocHandleRemoteHeadsPayload, READY } from "../src/DocHandle.js"
5
7
  import { parseAutomergeUrl } from "../src/AutomergeUrl.js"
6
- import { READY } from "../src/DocHandle.js"
7
8
  import {
8
9
  generateAutomergeUrl,
9
10
  stringifyAutomergeUrl,
@@ -425,24 +426,31 @@ describe("Repo", () => {
425
426
 
426
427
  const aliceNetworkAdapter = new MessageChannelNetworkAdapter(ab)
427
428
 
429
+ const alice = "alice" as PeerId
428
430
  const aliceRepo = new Repo({
429
431
  network: connectAlice ? [aliceNetworkAdapter] : [],
430
- peerId: "alice" as PeerId,
432
+ peerId: alice,
431
433
  sharePolicy,
432
434
  })
433
435
 
436
+ const bob = "bob" as PeerId
437
+ const bobStorage = new DummyStorageAdapter()
434
438
  const bobRepo = new Repo({
439
+ storage: bobStorage,
435
440
  network: [
436
441
  new MessageChannelNetworkAdapter(ba),
437
442
  new MessageChannelNetworkAdapter(bc),
438
443
  ],
439
- peerId: "bob" as PeerId,
444
+ peerId: bob,
440
445
  sharePolicy,
441
446
  })
442
447
 
448
+ const charlie = "charlie" as PeerId
449
+ const charlieStorage = new DummyStorageAdapter()
443
450
  const charlieRepo = new Repo({
451
+ storage: charlieStorage,
444
452
  network: [new MessageChannelNetworkAdapter(cb)],
445
- peerId: "charlie" as PeerId,
453
+ peerId: charlie,
446
454
  })
447
455
 
448
456
  const teardown = () => {
@@ -488,8 +496,13 @@ describe("Repo", () => {
488
496
  ])
489
497
 
490
498
  return {
499
+ alice,
491
500
  aliceRepo,
501
+ bob,
502
+ bobStorage,
492
503
  bobRepo,
504
+ charlie,
505
+ charlieStorage,
493
506
  charlieRepo,
494
507
  aliceHandle,
495
508
  notForCharlie,
@@ -541,6 +554,9 @@ describe("Repo", () => {
541
554
  const handle = charlieRepo.find<TestDoc>(
542
555
  stringifyAutomergeUrl({ documentId: notForCharlie })
543
556
  )
557
+
558
+ await pause(50)
559
+
544
560
  const doc = await handle.doc()
545
561
 
546
562
  assert.deepStrictEqual(doc, { foo: "baz" })
@@ -554,6 +570,9 @@ describe("Repo", () => {
554
570
  const handle = charlieRepo.find<TestDoc>(
555
571
  stringifyAutomergeUrl({ documentId: notForBob })
556
572
  )
573
+
574
+ await pause(50)
575
+
557
576
  const doc = await handle.doc()
558
577
  assert.deepStrictEqual(doc, { foo: "bap" })
559
578
 
@@ -768,6 +787,142 @@ describe("Repo", () => {
768
787
  await charliePromise
769
788
  teardown()
770
789
  })
790
+
791
+ it("should save sync state of other peers", async () => {
792
+ const { bobRepo, teardown, charlie } = await setup({
793
+ connectAlice: false,
794
+ })
795
+
796
+ const bobHandle = bobRepo.create<TestDoc>()
797
+ bobHandle.change(d => {
798
+ d.foo = "bar"
799
+ })
800
+
801
+ await pause(200)
802
+
803
+ // bob should store the sync state of charlie
804
+ const storedSyncState = await bobRepo.storageSubsystem.loadSyncState(
805
+ bobHandle.documentId,
806
+ charlie
807
+ )
808
+ const docHeads = A.getHeads(bobHandle.docSync())
809
+ assert.deepStrictEqual(storedSyncState.sharedHeads, docHeads)
810
+
811
+ teardown()
812
+ })
813
+
814
+ it("should load sync state from storage", async () => {
815
+ const { bobRepo, teardown, charlie, charlieRepo, bobStorage, bob } =
816
+ await setup({
817
+ connectAlice: false,
818
+ })
819
+
820
+ // create a new doc and count sync messages
821
+ const bobHandle = bobRepo.create<TestDoc>()
822
+ bobHandle.change(d => {
823
+ d.foo = "bar"
824
+ })
825
+ let bobSyncMessages = 0
826
+ bobRepo.networkSubsystem.on("message", () => {
827
+ bobSyncMessages++
828
+ })
829
+ await pause(500)
830
+
831
+ // repo has no stored sync state for charlie so we should see two sync messages
832
+ assert.strictEqual(bobSyncMessages, 2)
833
+
834
+ // setup new repo which uses bob's storage
835
+ const bob2Repo = new Repo({
836
+ storage: bobStorage,
837
+ network: [],
838
+ peerId: "bob-2" as PeerId,
839
+ })
840
+
841
+ // connnect it with charlie
842
+ const channel = new MessageChannel()
843
+ bob2Repo.networkSubsystem.addNetworkAdapter(
844
+ new MessageChannelNetworkAdapter(channel.port2)
845
+ )
846
+ charlieRepo.networkSubsystem.addNetworkAdapter(
847
+ new MessageChannelNetworkAdapter(channel.port1)
848
+ )
849
+
850
+ // lookup doc we've previously created and count the messages
851
+ bob2Repo.find(bobHandle.documentId)
852
+ let bob2SyncMessages = 0
853
+ bob2Repo.networkSubsystem.on("message", m => {
854
+ bob2SyncMessages++
855
+ })
856
+ await pause(100)
857
+
858
+ // repo has stored sync state for charlie so we should see one sync messages
859
+ assert.strictEqual(bob2SyncMessages, 1)
860
+
861
+ channel.port1.close()
862
+ teardown()
863
+ })
864
+
865
+ it("should report the remote heads when they change", async () => {
866
+ const { bobRepo, charlieRepo, teardown } = await setup({
867
+ connectAlice: false,
868
+ })
869
+
870
+ const handle = bobRepo.create<TestDoc>()
871
+ handle.change(d => {
872
+ d.foo = "bar"
873
+ })
874
+
875
+ // pause to let the sync happen
876
+ await pause(50)
877
+
878
+ const nextRemoteHeadsPromise = new Promise<{
879
+ peerId: PeerId
880
+ heads: A.Heads
881
+ }>(resolve => {
882
+ handle.on("remote-heads", ({ peerId, heads }) => {
883
+ resolve({ peerId, heads })
884
+ })
885
+ })
886
+
887
+ const charlieHandle = charlieRepo.find<TestDoc>(handle.url)
888
+ await charlieHandle.whenReady()
889
+
890
+ // make a change on charlie
891
+ charlieHandle.change(d => {
892
+ d.foo = "baz"
893
+ })
894
+
895
+ // pause to let the sync happen
896
+ await pause(100)
897
+
898
+ const charlieHeads = A.getHeads(charlieHandle.docSync())
899
+ const bobHeads = A.getHeads(handle.docSync())
900
+
901
+ assert.deepStrictEqual(charlieHeads, bobHeads)
902
+
903
+ const nextRemoteHeads = await nextRemoteHeadsPromise
904
+ assert.deepStrictEqual(nextRemoteHeads.peerId, "charlie")
905
+ assert.deepStrictEqual(nextRemoteHeads.heads, charlieHeads)
906
+
907
+ assert.deepStrictEqual(
908
+ handle.getRemoteHeads("charlie" as PeerId),
909
+ A.getHeads(charlieHandle.docSync())
910
+ )
911
+
912
+ teardown()
913
+ })
914
+
915
+ it("can report the connected peers", async () => {
916
+ const { bobRepo, charlieRepo, teardown } = await setup()
917
+
918
+ // pause to let the connections happen
919
+ await pause(1)
920
+
921
+ assert.deepStrictEqual(bobRepo.peers, ["alice", "charlie"])
922
+ assert.deepStrictEqual(charlieRepo.peers, ["bob"])
923
+
924
+ teardown()
925
+ })
771
926
  })
772
927
 
773
928
  describe("with peers (mesh network)", () => {
@@ -6,10 +6,9 @@ import os from "os"
6
6
  import path from "path"
7
7
  import { describe, it } from "vitest"
8
8
  import { generateAutomergeUrl, parseAutomergeUrl } from "../src/AutomergeUrl.js"
9
+ import { PeerId, cbor } from "../src/index.js"
9
10
  import { StorageSubsystem } from "../src/storage/StorageSubsystem.js"
10
11
  import { DummyStorageAdapter } from "./helpers/DummyStorageAdapter.js"
11
- import { cbor } from "../src/index.js"
12
- import { pause } from "../src/helpers/pause.js"
13
12
 
14
13
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "automerge-repo-tests"))
15
14
 
@@ -176,6 +175,35 @@ describe("StorageSubsystem", () => {
176
175
  assert.equal(reloadedValue2, undefined)
177
176
  })
178
177
  })
178
+
179
+ describe("sync state", () => {
180
+ it("stores and retrieve sync state", async () => {
181
+ const storage = new StorageSubsystem(adapter)
182
+
183
+ const { documentId } = parseAutomergeUrl(generateAutomergeUrl())
184
+ const syncState = A.initSyncState()
185
+ const bob = "bob" as PeerId
186
+
187
+ const rawSyncState = A.decodeSyncState(A.encodeSyncState(syncState))
188
+
189
+ await storage.saveSyncState(documentId, bob, syncState)
190
+ const loadedSyncState = await storage.loadSyncState(documentId, bob)
191
+ assert.deepStrictEqual(loadedSyncState, rawSyncState)
192
+ })
193
+
194
+ it("delete sync state if document is deleted", async () => {
195
+ const storage = new StorageSubsystem(adapter)
196
+
197
+ const { documentId } = parseAutomergeUrl(generateAutomergeUrl())
198
+ const syncState = A.initSyncState()
199
+ const bob = "bob" as PeerId
200
+
201
+ await storage.saveSyncState(documentId, bob, syncState)
202
+ await storage.removeDoc(documentId)
203
+ const loadedSyncState = await storage.loadSyncState(documentId, bob)
204
+ assert.strictEqual(loadedSyncState, undefined)
205
+ })
206
+ })
179
207
  })
180
208
  })
181
209
  })