@automerge/automerge-repo 1.0.6 → 1.0.7

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 (63) hide show
  1. package/.eslintrc +1 -1
  2. package/dist/DocHandle.d.ts +7 -7
  3. package/dist/DocHandle.d.ts.map +1 -1
  4. package/dist/DocHandle.js +3 -7
  5. package/dist/EphemeralData.d.ts +2 -2
  6. package/dist/EphemeralData.d.ts.map +1 -1
  7. package/dist/Repo.d.ts.map +1 -1
  8. package/dist/Repo.js +7 -11
  9. package/dist/helpers/cbor.d.ts +2 -2
  10. package/dist/helpers/cbor.d.ts.map +1 -1
  11. package/dist/helpers/cbor.js +1 -1
  12. package/dist/helpers/pause.d.ts.map +1 -1
  13. package/dist/helpers/pause.js +3 -1
  14. package/dist/helpers/tests/network-adapter-tests.d.ts.map +1 -1
  15. package/dist/helpers/tests/network-adapter-tests.js +2 -2
  16. package/dist/index.d.ts +11 -9
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +4 -4
  19. package/dist/network/NetworkAdapter.d.ts +3 -3
  20. package/dist/network/NetworkAdapter.d.ts.map +1 -1
  21. package/dist/network/NetworkSubsystem.d.ts +2 -2
  22. package/dist/network/NetworkSubsystem.d.ts.map +1 -1
  23. package/dist/network/NetworkSubsystem.js +30 -18
  24. package/dist/network/messages.d.ts +38 -68
  25. package/dist/network/messages.d.ts.map +1 -1
  26. package/dist/network/messages.js +13 -21
  27. package/dist/storage/StorageSubsystem.js +7 -7
  28. package/dist/synchronizer/CollectionSynchronizer.d.ts +3 -3
  29. package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
  30. package/dist/synchronizer/CollectionSynchronizer.js +2 -2
  31. package/dist/synchronizer/DocSynchronizer.d.ts +3 -3
  32. package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
  33. package/dist/synchronizer/DocSynchronizer.js +22 -29
  34. package/dist/synchronizer/Synchronizer.d.ts +2 -2
  35. package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
  36. package/dist/types.d.ts +5 -1
  37. package/dist/types.d.ts.map +1 -1
  38. package/package.json +5 -13
  39. package/src/DocHandle.ts +9 -11
  40. package/src/EphemeralData.ts +2 -2
  41. package/src/Repo.ts +10 -14
  42. package/src/helpers/cbor.ts +4 -4
  43. package/src/helpers/pause.ts +7 -2
  44. package/src/helpers/tests/network-adapter-tests.ts +3 -3
  45. package/src/helpers/withTimeout.ts +2 -2
  46. package/src/index.ts +36 -29
  47. package/src/network/NetworkAdapter.ts +7 -3
  48. package/src/network/NetworkSubsystem.ts +31 -23
  49. package/src/network/messages.ts +88 -151
  50. package/src/storage/StorageSubsystem.ts +8 -8
  51. package/src/synchronizer/CollectionSynchronizer.ts +6 -15
  52. package/src/synchronizer/DocSynchronizer.ts +34 -48
  53. package/src/synchronizer/Synchronizer.ts +2 -2
  54. package/src/types.ts +8 -3
  55. package/test/CollectionSynchronizer.test.ts +58 -53
  56. package/test/DocHandle.test.ts +35 -36
  57. package/test/DocSynchronizer.test.ts +9 -8
  58. package/test/Network.test.ts +1 -0
  59. package/test/Repo.test.ts +177 -97
  60. package/test/StorageSubsystem.test.ts +6 -9
  61. package/test/tsconfig.json +8 -0
  62. package/typedoc.json +3 -3
  63. package/.mocharc.json +0 -5
package/test/Repo.test.ts CHANGED
@@ -1,7 +1,13 @@
1
- import assert from "assert"
2
1
  import { MessageChannelNetworkAdapter } from "@automerge/automerge-repo-network-messagechannel"
3
- import { BroadcastChannelNetworkAdapter } from "@automerge/automerge-repo-network-broadcastchannel"
4
-
2
+ import assert from "assert"
3
+ import * as Uuid from "uuid"
4
+ import { describe, it } from "vitest"
5
+ import { parseAutomergeUrl } from "../dist/DocUrl.js"
6
+ import { READY } from "../src/DocHandle.js"
7
+ import { generateAutomergeUrl, stringifyAutomergeUrl } from "../src/DocUrl.js"
8
+ import { Repo } from "../src/Repo.js"
9
+ import { eventPromise } from "../src/helpers/eventPromise.js"
10
+ import { pause } from "../src/helpers/pause.js"
5
11
  import {
6
12
  AutomergeUrl,
7
13
  DocHandle,
@@ -9,22 +15,14 @@ import {
9
15
  PeerId,
10
16
  SharePolicy,
11
17
  } from "../src/index.js"
12
- import { eventPromise } from "../src/helpers/eventPromise.js"
13
- import { pause, rejectOnTimeout } from "../src/helpers/pause.js"
14
- import { Repo } from "../src/Repo.js"
15
18
  import { DummyNetworkAdapter } from "./helpers/DummyNetworkAdapter.js"
16
19
  import { DummyStorageAdapter } from "./helpers/DummyStorageAdapter.js"
17
- import { getRandomItem } from "./helpers/getRandomItem.js"
18
- import { TestDoc } from "./types.js"
19
- import { generateAutomergeUrl, stringifyAutomergeUrl } from "../src/DocUrl.js"
20
- import { READY, AWAITING_NETWORK } from "../src/DocHandle.js"
21
20
  import {
22
- generateLargeObject,
23
21
  LargeObject,
22
+ generateLargeObject,
24
23
  } from "./helpers/generate-large-object.js"
25
- import { parseAutomergeUrl } from "../dist/DocUrl.js"
26
-
27
- import * as Uuid from "uuid"
24
+ import { getRandomItem } from "./helpers/getRandomItem.js"
25
+ import { TestDoc } from "./types.js"
28
26
 
29
27
  describe("Repo", () => {
30
28
  describe("single repo", () => {
@@ -66,6 +64,8 @@ describe("Repo", () => {
66
64
  })
67
65
 
68
66
  it("can find a document using a legacy UUID (for now)", () => {
67
+ disableConsoleWarn()
68
+
69
69
  const { repo } = setup()
70
70
  const handle = repo.create<TestDoc>()
71
71
  handle.change((d: TestDoc) => {
@@ -79,6 +79,8 @@ describe("Repo", () => {
79
79
  const handle2 = repo.find(legacyDocumentId)
80
80
  assert.equal(handle, handle2)
81
81
  assert.deepEqual(handle2.docSync(), { foo: "bar" })
82
+
83
+ reenableConsoleWarn()
82
84
  })
83
85
 
84
86
  it("can change a document", async () => {
@@ -257,19 +259,14 @@ describe("Repo", () => {
257
259
  })
258
260
  // we now have a snapshot and an incremental change in storage
259
261
  assert.equal(handle.isReady(), true)
260
- await handle.doc()
262
+ const foo = await handle.doc()
263
+ assert.equal(foo?.foo, "bar")
264
+
265
+ await pause()
261
266
  repo.delete(handle.documentId)
262
267
 
263
268
  assert(handle.isDeleted())
264
269
  assert.equal(repo.handles[handle.documentId], undefined)
265
-
266
- const bobHandle = repo.find<TestDoc>(handle.url)
267
- await assert.rejects(
268
- rejectOnTimeout(bobHandle.doc(), 10),
269
- "document should have been deleted"
270
- )
271
-
272
- assert(!bobHandle.isReady())
273
270
  })
274
271
 
275
272
  it("can delete an existing document by url", async () => {
@@ -280,36 +277,31 @@ describe("Repo", () => {
280
277
  })
281
278
  assert.equal(handle.isReady(), true)
282
279
  await handle.doc()
280
+
281
+ await pause()
283
282
  repo.delete(handle.url)
284
283
 
285
284
  assert(handle.isDeleted())
286
285
  assert.equal(repo.handles[handle.documentId], undefined)
287
-
288
- const bobHandle = repo.find<TestDoc>(handle.url)
289
- await assert.rejects(
290
- rejectOnTimeout(bobHandle.doc(), 10),
291
- "document should have been deleted"
292
- )
293
-
294
- assert(!bobHandle.isReady())
295
286
  })
296
287
 
297
- it("deleting a document emits an event", async done => {
298
- const { repo } = setup()
299
- const handle = repo.create<TestDoc>()
300
- handle.change(d => {
301
- d.foo = "bar"
302
- })
303
- assert.equal(handle.isReady(), true)
288
+ it("deleting a document emits an event", async () =>
289
+ new Promise<void>(done => {
290
+ const { repo } = setup()
291
+ const handle = repo.create<TestDoc>()
292
+ handle.change(d => {
293
+ d.foo = "bar"
294
+ })
295
+ assert.equal(handle.isReady(), true)
304
296
 
305
- repo.on("delete-document", ({ documentId }) => {
306
- assert.equal(documentId, handle.documentId)
297
+ repo.on("delete-document", ({ documentId }) => {
298
+ assert.equal(documentId, handle.documentId)
307
299
 
308
- done()
309
- })
300
+ done()
301
+ })
310
302
 
311
- repo.delete(handle.documentId)
312
- })
303
+ repo.delete(handle.documentId)
304
+ }))
313
305
 
314
306
  it("storage state doesn't change across reloads when the document hasn't changed", async () => {
315
307
  const storage = new DummyStorageAdapter()
@@ -403,16 +395,16 @@ describe("Repo", () => {
403
395
  return true
404
396
  }
405
397
 
406
- const setupRepos = (connectAlice = true) => {
398
+ const setupLinearNetwork = (connectAlice = true) => {
407
399
  // Set up three repos; connect Alice to Bob, and Bob to Charlie
408
400
 
409
- const aliceBobChannel = new MessageChannel()
410
- const bobCharlieChannel = new MessageChannel()
401
+ const abChannel = new MessageChannel()
402
+ const bcChannel = new MessageChannel()
411
403
 
412
- const { port1: aliceToBob, port2: bobToAlice } = aliceBobChannel
413
- const { port1: bobToCharlie, port2: charlieToBob } = bobCharlieChannel
404
+ const { port1: ab, port2: ba } = abChannel
405
+ const { port1: bc, port2: cb } = bcChannel
414
406
 
415
- const aliceNetworkAdapter = new MessageChannelNetworkAdapter(aliceToBob)
407
+ const aliceNetworkAdapter = new MessageChannelNetworkAdapter(ab)
416
408
 
417
409
  const aliceRepo = new Repo({
418
410
  network: connectAlice ? [aliceNetworkAdapter] : [],
@@ -422,26 +414,26 @@ describe("Repo", () => {
422
414
 
423
415
  const bobRepo = new Repo({
424
416
  network: [
425
- new MessageChannelNetworkAdapter(bobToAlice),
426
- new MessageChannelNetworkAdapter(bobToCharlie),
417
+ new MessageChannelNetworkAdapter(ba),
418
+ new MessageChannelNetworkAdapter(bc),
427
419
  ],
428
420
  peerId: "bob" as PeerId,
429
421
  sharePolicy,
430
422
  })
431
423
 
432
424
  const charlieRepo = new Repo({
433
- network: [new MessageChannelNetworkAdapter(charlieToBob)],
425
+ network: [new MessageChannelNetworkAdapter(cb)],
434
426
  peerId: "charlie" as PeerId,
435
427
  })
436
428
 
437
429
  const teardown = () => {
438
- aliceBobChannel.port1.close()
439
- bobCharlieChannel.port1.close()
430
+ abChannel.port1.close()
431
+ bcChannel.port1.close()
440
432
  }
441
433
 
442
434
  function doConnectAlice() {
443
435
  aliceRepo.networkSubsystem.addNetworkAdapter(
444
- new MessageChannelNetworkAdapter(aliceToBob)
436
+ new MessageChannelNetworkAdapter(ab)
445
437
  )
446
438
  //bobRepo.networkSubsystem.addNetworkAdapter(new MessageChannelNetworkAdapter(bobToAlice))
447
439
  }
@@ -459,9 +451,60 @@ describe("Repo", () => {
459
451
  }
460
452
  }
461
453
 
454
+ const setupMeshNetwork = () => {
455
+ // Set up three repos; connect Alice to Bob, Bob to Charlie, and Alice to Charlie
456
+
457
+ const abChannel = new MessageChannel()
458
+ const bcChannel = new MessageChannel()
459
+ const acChannel = new MessageChannel()
460
+
461
+ const { port1: ab, port2: ba } = abChannel
462
+ const { port1: bc, port2: cb } = bcChannel
463
+ const { port1: ac, port2: ca } = acChannel
464
+
465
+ const aliceRepo = new Repo({
466
+ network: [
467
+ new MessageChannelNetworkAdapter(ab),
468
+ new MessageChannelNetworkAdapter(ac),
469
+ ],
470
+ peerId: "alice" as PeerId,
471
+ sharePolicy,
472
+ })
473
+
474
+ const bobRepo = new Repo({
475
+ network: [
476
+ new MessageChannelNetworkAdapter(ba),
477
+ new MessageChannelNetworkAdapter(bc),
478
+ ],
479
+ peerId: "bob" as PeerId,
480
+ sharePolicy,
481
+ })
482
+
483
+ const charlieRepo = new Repo({
484
+ network: [
485
+ new MessageChannelNetworkAdapter(ca),
486
+ new MessageChannelNetworkAdapter(cb),
487
+ ],
488
+ peerId: "charlie" as PeerId,
489
+ })
490
+
491
+ const teardown = () => {
492
+ abChannel.port1.close()
493
+ bcChannel.port1.close()
494
+ acChannel.port1.close()
495
+ }
496
+
497
+ return {
498
+ teardown,
499
+ aliceRepo,
500
+ bobRepo,
501
+ charlieRepo,
502
+ }
503
+ }
504
+
462
505
  const setup = async (connectAlice = true) => {
463
506
  const { teardown, aliceRepo, bobRepo, charlieRepo, connectAliceToBob } =
464
- setupRepos(connectAlice)
507
+ setupLinearNetwork(connectAlice)
465
508
 
466
509
  const aliceHandle = aliceRepo.create<TestDoc>()
467
510
  aliceHandle.change(d => {
@@ -620,6 +663,56 @@ describe("Repo", () => {
620
663
  teardown()
621
664
  })
622
665
 
666
+ it("a previously unavailable document becomes available if the network adapter initially has no peers", async () => {
667
+ // It is possible for a network adapter to be ready without any peer
668
+ // being announced (e.g. the BroadcastChannelNetworkAdapter). In this
669
+ // case attempting to `Repo.find` a document which is not in the storage
670
+ // will result in an unavailable document. If a peer is later announced
671
+ // on the NetworkAdapter we should attempt to sync with the new peer and
672
+ // if the new peer has the document, the DocHandle should eventually
673
+ // transition to "ready"
674
+
675
+ // first create a repo with no network adapter and add a document so that
676
+ // we have a storage containing the document to pass to a new repo later
677
+ const storage = new DummyStorageAdapter()
678
+ const isolatedRepo = new Repo({
679
+ network: [],
680
+ storage,
681
+ })
682
+ const unsyncedHandle = isolatedRepo.create<TestDoc>()
683
+ const url = unsyncedHandle.url
684
+
685
+ // Now create a message channel to connect two repos
686
+ const abChannel = new MessageChannel()
687
+ const { port1: ab, port2: ba } = abChannel
688
+
689
+ // Create an empty repo to request the document from
690
+ const a = new Repo({
691
+ network: [new MessageChannelNetworkAdapter(ab)],
692
+ peerId: "a" as PeerId,
693
+ sharePolicy: async () => true
694
+ })
695
+
696
+ const handle = a.find(url)
697
+
698
+ // We expect this to be unavailable as there is no connected peer and
699
+ // the repo has no storage.
700
+ await eventPromise(handle, "unavailable")
701
+
702
+ // Now create a repo pointing at the storage containing the document and
703
+ // connect it to the other end of the MessageChannel
704
+ const b = new Repo({
705
+ storage,
706
+ peerId: "b" as PeerId,
707
+ network: [new MessageChannelNetworkAdapter(ba)],
708
+ })
709
+
710
+ // The empty repo should be notified of the new peer, send it a request
711
+ // and eventually resolve the handle to "READY"
712
+ await handle.whenReady()
713
+
714
+ })
715
+
623
716
  it("a deleted document from charlieRepo can be refetched", async () => {
624
717
  const { charlieRepo, aliceHandle, teardown } = await setup()
625
718
 
@@ -642,34 +735,8 @@ describe("Repo", () => {
642
735
  teardown()
643
736
  })
644
737
 
645
- const setupMeshNetwork = async () => {
646
- const aliceRepo = new Repo({
647
- network: [new BroadcastChannelNetworkAdapter()],
648
- peerId: "alice" as PeerId,
649
- })
650
-
651
- const bobRepo = new Repo({
652
- network: [new BroadcastChannelNetworkAdapter()],
653
- peerId: "bob" as PeerId,
654
- })
655
-
656
- const charlieRepo = new Repo({
657
- network: [new BroadcastChannelNetworkAdapter()],
658
- peerId: "charlie" as PeerId,
659
- })
660
-
661
- // pause to let the network set up
662
- await pause(50)
663
-
664
- return {
665
- aliceRepo,
666
- bobRepo,
667
- charlieRepo,
668
- }
669
- }
670
-
671
738
  it("can emit an 'unavailable' event when it's not found on the network", async () => {
672
- const { charlieRepo } = await setupMeshNetwork()
739
+ const { charlieRepo } = setupMeshNetwork()
673
740
 
674
741
  const url = generateAutomergeUrl()
675
742
  const handle = charlieRepo.find<TestDoc>(url)
@@ -748,10 +815,8 @@ describe("Repo", () => {
748
815
  })
749
816
 
750
817
  it("can broadcast a message without entering into an infinite loop", async () => {
751
- const { aliceRepo, bobRepo, charlieRepo } = await setupMeshNetwork()
818
+ const { aliceRepo, bobRepo, charlieRepo } = setupMeshNetwork()
752
819
 
753
- // pause to let the network set up
754
- await pause(50)
755
820
  const message = { presence: "alex" }
756
821
 
757
822
  const aliceHandle = aliceRepo.create<TestDoc>()
@@ -765,7 +830,7 @@ describe("Repo", () => {
765
830
  }, 100)
766
831
 
767
832
  aliceHandle.on("ephemeral-message", () => {
768
- reject("alice got the message")
833
+ reject(new Error("alice got the message"))
769
834
  })
770
835
  })
771
836
 
@@ -787,14 +852,16 @@ describe("Repo", () => {
787
852
  })
788
853
 
789
854
  it("notifies peers when a document is cloned", async () => {
790
- const { bobRepo, charlieRepo } = await setupMeshNetwork()
855
+ const { bobRepo, charlieRepo } = setupMeshNetwork()
791
856
 
792
857
  // pause to let the network set up
793
858
  await pause(50)
794
859
 
795
860
  const handle = bobRepo.create<TestDoc>()
796
- handle.change(d => { d.foo = "bar" })
797
- const handle2 = bobRepo.clone(handle)
861
+ handle.change(d => {
862
+ d.foo = "bar"
863
+ })
864
+ const handle2 = bobRepo.clone(handle)
798
865
 
799
866
  // pause to let the sync happen
800
867
  await pause(50)
@@ -805,14 +872,16 @@ describe("Repo", () => {
805
872
  })
806
873
 
807
874
  it("notifies peers when a document is merged", async () => {
808
- const { bobRepo, charlieRepo } = await setupMeshNetwork()
875
+ const { bobRepo, charlieRepo } = setupMeshNetwork()
809
876
 
810
877
  // pause to let the network set up
811
878
  await pause(50)
812
879
 
813
880
  const handle = bobRepo.create<TestDoc>()
814
- handle.change(d => { d.foo = "bar" })
815
- const handle2 = bobRepo.clone(handle)
881
+ handle.change(d => {
882
+ d.foo = "bar"
883
+ })
884
+ const handle2 = bobRepo.clone(handle)
816
885
 
817
886
  // pause to let the sync happen
818
887
  await pause(50)
@@ -822,14 +891,25 @@ describe("Repo", () => {
822
891
  assert.deepStrictEqual(charlieHandle.docSync(), { foo: "bar" })
823
892
 
824
893
  // now make a change to doc2 on bobs side and merge it into doc1
825
- handle2.change(d => { d.foo = "baz" })
894
+ handle2.change(d => {
895
+ d.foo = "baz"
896
+ })
826
897
  handle.merge(handle2)
827
-
898
+
828
899
  // wait for the network to do it's thang
829
900
  await pause(50)
830
901
 
831
- await charlieHandle.doc()
902
+ await charlieHandle.doc()
832
903
  assert.deepStrictEqual(charlieHandle.docSync(), { foo: "baz" })
833
904
  })
834
905
  })
835
906
  })
907
+
908
+ const disableConsoleWarn = () => {
909
+ console["_warn"] = console.warn
910
+ console.warn = () => {}
911
+ }
912
+
913
+ const reenableConsoleWarn = () => {
914
+ console.warn = console["_warn"]
915
+ }
@@ -1,16 +1,13 @@
1
+ import { NodeFSStorageAdapter } from "@automerge/automerge-repo-storage-nodefs"
2
+ import * as A from "@automerge/automerge/next"
3
+ import assert from "assert"
1
4
  import fs from "fs"
2
5
  import os from "os"
3
6
  import path from "path"
4
-
5
- import assert from "assert"
6
-
7
- import * as A from "@automerge/automerge/next"
8
-
9
- import { DummyStorageAdapter } from "./helpers/DummyStorageAdapter.js"
10
- import { NodeFSStorageAdapter } from "@automerge/automerge-repo-storage-nodefs"
11
-
12
- import { StorageSubsystem } from "../src/storage/StorageSubsystem.js"
7
+ import { describe, it } from "vitest"
13
8
  import { generateAutomergeUrl, parseAutomergeUrl } from "../src/DocUrl.js"
9
+ import { StorageSubsystem } from "../src/storage/StorageSubsystem.js"
10
+ import { DummyStorageAdapter } from "./helpers/DummyStorageAdapter.js"
14
11
 
15
12
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "automerge-repo-tests"))
16
13
 
@@ -0,0 +1,8 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "NodeNext",
4
+ "moduleResolution": "Node16",
5
+ "noEmit": true
6
+ },
7
+ "include": ["**/*.ts"]
8
+ }
package/typedoc.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "extends": "../../typedoc.base.json",
3
- "entryPoints": ["src/index.ts"],
4
- "readme": "none"
2
+ "extends": ["../../typedoc.base.json"],
3
+ "entryPoints": ["src/index.ts"],
4
+ "readme": "none"
5
5
  }
package/.mocharc.json DELETED
@@ -1,5 +0,0 @@
1
- {
2
- "extension": ["ts"],
3
- "spec": "test/*.test.ts",
4
- "loader": "ts-node/esm"
5
- }