@automerge/automerge-repo 2.0.0-alpha.20 → 2.0.0-alpha.23

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.
package/test/Repo.test.ts CHANGED
@@ -8,9 +8,6 @@ import {
8
8
  getHeadsFromUrl,
9
9
  isValidAutomergeUrl,
10
10
  parseAutomergeUrl,
11
- UrlHeads,
12
- } from "../src/AutomergeUrl.js"
13
- import {
14
11
  generateAutomergeUrl,
15
12
  stringifyAutomergeUrl,
16
13
  } from "../src/AutomergeUrl.js"
@@ -19,6 +16,7 @@ import { eventPromise } from "../src/helpers/eventPromise.js"
19
16
  import { pause } from "../src/helpers/pause.js"
20
17
  import {
21
18
  AnyDocumentId,
19
+ UrlHeads,
22
20
  AutomergeUrl,
23
21
  DocHandle,
24
22
  DocumentId,
@@ -78,35 +76,34 @@ describe("Repo", () => {
78
76
  it("can create a document with an initial value", async () => {
79
77
  const { repo } = setup()
80
78
  const handle = repo.create({ foo: "bar" })
81
- await handle.doc()
82
- assert.equal(handle.docSync().foo, "bar")
79
+ assert.equal(handle.doc().foo, "bar")
83
80
  })
84
81
 
85
- it("can find a document by url", () => {
82
+ it("can find a document by url", async () => {
86
83
  const { repo } = setup()
87
84
  const handle = repo.create<TestDoc>()
88
85
  handle.change((d: TestDoc) => {
89
86
  d.foo = "bar"
90
87
  })
91
88
 
92
- const handle2 = repo.find(handle.url)
89
+ const handle2 = await repo.find(handle.url)
93
90
  assert.equal(handle, handle2)
94
- assert.deepEqual(handle2.docSync(), { foo: "bar" })
91
+ assert.deepEqual(handle2.doc(), { foo: "bar" })
95
92
  })
96
93
 
97
- it("can find a document by its unprefixed document ID", () => {
94
+ it("can find a document by its unprefixed document ID", async () => {
98
95
  const { repo } = setup()
99
96
  const handle = repo.create<TestDoc>()
100
97
  handle.change((d: TestDoc) => {
101
98
  d.foo = "bar"
102
99
  })
103
100
 
104
- const handle2 = repo.find(handle.documentId)
101
+ const handle2 = await repo.find(handle.documentId)
105
102
  assert.equal(handle, handle2)
106
- assert.deepEqual(handle2.docSync(), { foo: "bar" })
103
+ assert.deepEqual(handle2.doc(), { foo: "bar" })
107
104
  })
108
105
 
109
- it("can find a document by legacy UUID (for now)", () => {
106
+ it("can find a document by legacy UUID (for now)", async () => {
110
107
  disableConsoleWarn()
111
108
 
112
109
  const { repo } = setup()
@@ -119,9 +116,9 @@ describe("Repo", () => {
119
116
  const { binaryDocumentId } = parseAutomergeUrl(url)
120
117
  const legacyDocId = Uuid.stringify(binaryDocumentId) as LegacyDocumentId
121
118
 
122
- const handle2 = repo.find(legacyDocId)
119
+ const handle2 = await repo.find(legacyDocId)
123
120
  assert.equal(handle, handle2)
124
- assert.deepEqual(handle2.docSync(), { foo: "bar" })
121
+ assert.deepEqual(handle2.doc(), { foo: "bar" })
125
122
 
126
123
  reenableConsoleWarn()
127
124
  })
@@ -132,7 +129,7 @@ describe("Repo", () => {
132
129
  handle.change(d => {
133
130
  d.foo = "bar"
134
131
  })
135
- const v = await handle.doc()
132
+ const v = handle.doc()
136
133
  assert.equal(handle.isReady(), true)
137
134
  assert.equal(v.foo, "bar")
138
135
  })
@@ -146,8 +143,8 @@ describe("Repo", () => {
146
143
  const handle2 = repo.clone(handle)
147
144
  assert.equal(handle2.isReady(), true)
148
145
  assert.notEqual(handle.documentId, handle2.documentId)
149
- assert.deepStrictEqual(handle.docSync(), handle2.docSync())
150
- assert.deepStrictEqual(handle2.docSync(), { foo: "bar" })
146
+ assert.deepStrictEqual(handle.doc(), handle2.doc())
147
+ assert.deepStrictEqual(handle2.doc(), { foo: "bar" })
151
148
  })
152
149
 
153
150
  it("the cloned documents are distinct", () => {
@@ -165,9 +162,9 @@ describe("Repo", () => {
165
162
  d.baz = "baz"
166
163
  })
167
164
 
168
- assert.notDeepStrictEqual(handle.docSync(), handle2.docSync())
169
- assert.deepStrictEqual(handle.docSync(), { foo: "bar", bar: "bif" })
170
- assert.deepStrictEqual(handle2.docSync(), { foo: "bar", baz: "baz" })
165
+ assert.notDeepStrictEqual(handle.doc(), handle2.doc())
166
+ assert.deepStrictEqual(handle.doc(), { foo: "bar", bar: "bif" })
167
+ assert.deepStrictEqual(handle2.doc(), { foo: "bar", baz: "baz" })
171
168
  })
172
169
 
173
170
  it("the cloned documents can merge", () => {
@@ -187,59 +184,47 @@ describe("Repo", () => {
187
184
 
188
185
  handle.merge(handle2)
189
186
 
190
- assert.deepStrictEqual(handle.docSync(), {
187
+ assert.deepStrictEqual(handle.doc(), {
191
188
  foo: "bar",
192
189
  bar: "bif",
193
190
  baz: "baz",
194
191
  })
195
192
  // only the one handle should be changed
196
- assert.deepStrictEqual(handle2.docSync(), { foo: "bar", baz: "baz" })
193
+ assert.deepStrictEqual(handle2.doc(), { foo: "bar", baz: "baz" })
197
194
  })
198
195
 
199
196
  it("throws an error if we try to find a handle with an invalid AutomergeUrl", async () => {
200
197
  const { repo } = setup()
201
- try {
202
- repo.find<TestDoc>("invalid-url" as unknown as AutomergeUrl)
203
- } catch (e: any) {
204
- assert.equal(e.message, "Invalid AutomergeUrl: 'invalid-url'")
205
- }
198
+ await expect(async () => {
199
+ await repo.find<TestDoc>("invalid-url" as unknown as AutomergeUrl)
200
+ }).rejects.toThrow("Invalid AutomergeUrl: 'invalid-url'")
206
201
  })
207
202
 
208
203
  it("doesn't find a document that doesn't exist", async () => {
209
204
  const { repo } = setup()
210
- const handle = repo.find<TestDoc>(generateAutomergeUrl())
211
-
212
- await handle.whenReady(["ready", "unavailable"])
213
-
214
- assert.equal(handle.isReady(), false)
215
- assert.equal(handle.state, "unavailable")
216
- const doc = await handle.doc()
217
- assert.equal(doc, undefined)
218
- })
219
-
220
- it("emits an unavailable event when you don't have the document locally and are not connected to anyone", async () => {
221
- const { repo } = setup()
222
- const url = generateAutomergeUrl()
223
- const handle = repo.find<TestDoc>(url)
224
- assert.equal(handle.isReady(), false)
225
- await eventPromise(handle, "unavailable")
205
+ await expect(async () => {
206
+ await repo.find<TestDoc>(generateAutomergeUrl())
207
+ }).rejects.toThrow(/Document (.*) is unavailable/)
226
208
  })
227
209
 
228
210
  it("doesn't mark a document as unavailable until network adapters are ready", async () => {
229
211
  const { repo, networkAdapter } = setup({ startReady: false })
230
212
  const url = generateAutomergeUrl()
231
- const handle = repo.find<TestDoc>(url)
232
213
 
233
- let wasUnavailable = false
234
- handle.on("unavailable", () => {
235
- wasUnavailable = true
236
- })
214
+ const attemptedFind = repo.find<TestDoc>(url)
237
215
 
238
- await pause(50)
239
- assert.equal(wasUnavailable, false)
216
+ // First verify it stays pending for 50ms
217
+ await expect(
218
+ Promise.race([attemptedFind, pause(50)])
219
+ ).resolves.toBeUndefined()
240
220
 
221
+ // Trigger the rejection
241
222
  networkAdapter.forceReady()
242
- await eventPromise(handle, "unavailable")
223
+
224
+ // Now verify it rejects
225
+ await expect(attemptedFind).rejects.toThrow(
226
+ /Document (.*) is unavailable/
227
+ )
243
228
  })
244
229
 
245
230
  it("can find a created document", async () => {
@@ -250,18 +235,18 @@ describe("Repo", () => {
250
235
  })
251
236
  assert.equal(handle.isReady(), true)
252
237
 
253
- const bobHandle = repo.find<TestDoc>(handle.url)
238
+ const bobHandle = await repo.find<TestDoc>(handle.url)
254
239
 
255
240
  assert.equal(handle, bobHandle)
256
241
  assert.equal(handle.isReady(), true)
257
242
 
258
- const v = await bobHandle.doc()
243
+ const v = bobHandle.doc()
259
244
  assert.equal(v?.foo, "bar")
260
245
  })
261
246
 
262
247
  it("saves the document when creating it", async () => {
263
248
  const { repo, storageAdapter } = setup()
264
- const handle = repo.create<TestDoc>()
249
+ const handle = repo.create<TestDoc>({ foo: "saved" })
265
250
 
266
251
  const repo2 = new Repo({
267
252
  storage: storageAdapter,
@@ -269,9 +254,8 @@ describe("Repo", () => {
269
254
 
270
255
  await repo.flush()
271
256
 
272
- const bobHandle = repo2.find<TestDoc>(handle.url)
273
- await bobHandle.whenReady()
274
- assert.equal(bobHandle.isReady(), true)
257
+ const bobHandle = await repo2.find<TestDoc>(handle.url)
258
+ assert.deepEqual(bobHandle.doc(), { foo: "saved" })
275
259
  })
276
260
 
277
261
  it("saves the document when changed and can find it again", async () => {
@@ -290,9 +274,9 @@ describe("Repo", () => {
290
274
  storage: storageAdapter,
291
275
  })
292
276
 
293
- const bobHandle = repo2.find<TestDoc>(handle.url)
277
+ const bobHandle = await repo2.find<TestDoc>(handle.url)
294
278
 
295
- const v = await bobHandle.doc()
279
+ const v = bobHandle.doc()
296
280
  assert.equal(v?.foo, "bar")
297
281
  })
298
282
 
@@ -304,7 +288,7 @@ describe("Repo", () => {
304
288
  })
305
289
  // we now have a snapshot and an incremental change in storage
306
290
  assert.equal(handle.isReady(), true)
307
- const foo = await handle.doc()
291
+ const foo = handle.doc()
308
292
  assert.equal(foo?.foo, "bar")
309
293
 
310
294
  await pause()
@@ -321,7 +305,6 @@ describe("Repo", () => {
321
305
  d.foo = "bar"
322
306
  })
323
307
  assert.equal(handle.isReady(), true)
324
- await handle.doc()
325
308
 
326
309
  await pause()
327
310
  repo.delete(handle.url)
@@ -358,7 +341,7 @@ describe("Repo", () => {
358
341
 
359
342
  const exported = await repo.export(handle.documentId)
360
343
  const loaded = A.load(exported)
361
- const doc = await handle.doc()
344
+ const doc = handle.doc()
362
345
  assert.deepEqual(doc, loaded)
363
346
  })
364
347
 
@@ -392,9 +375,7 @@ describe("Repo", () => {
392
375
  const repo2 = new Repo({
393
376
  storage,
394
377
  })
395
- const handle2 = repo2.find(handle.url)
396
- await handle2.doc()
397
-
378
+ const handle2 = await repo2.find(handle.url)
398
379
  assert.deepEqual(storage.keys(), initialKeys)
399
380
  })
400
381
 
@@ -420,9 +401,7 @@ describe("Repo", () => {
420
401
  const repo2 = new Repo({
421
402
  storage,
422
403
  })
423
- const handle2 = repo2.find(handle.url)
424
- await handle2.doc()
425
-
404
+ const handle2 = await repo2.find(handle.url)
426
405
  assert(storage.keys().length !== 0)
427
406
  }
428
407
  })
@@ -462,7 +441,7 @@ describe("Repo", () => {
462
441
 
463
442
  const handle = repo.import<TestDoc>(saved)
464
443
  assert.equal(handle.isReady(), true)
465
- const v = await handle.doc()
444
+ const v = handle.doc()
466
445
  assert.equal(v?.foo, "bar")
467
446
 
468
447
  expect(A.getHistory(v)).toEqual(A.getHistory(updatedDoc))
@@ -481,7 +460,7 @@ describe("Repo", () => {
481
460
  const { repo } = setup()
482
461
  // @ts-ignore - passing something other than UInt8Array
483
462
  const handle = repo.import<TestDoc>(A.from({ foo: 123 }))
484
- const doc = await handle.doc()
463
+ const doc = handle.doc()
485
464
  expect(doc).toEqual({})
486
465
  })
487
466
 
@@ -489,7 +468,7 @@ describe("Repo", () => {
489
468
  const { repo } = setup()
490
469
  // @ts-ignore - passing something other than UInt8Array
491
470
  const handle = repo.import<TestDoc>({ foo: 123 })
492
- const doc = await handle.doc()
471
+ const doc = handle.doc()
493
472
  expect(doc).toEqual({})
494
473
  })
495
474
 
@@ -497,14 +476,12 @@ describe("Repo", () => {
497
476
  it("contains doc handle", async () => {
498
477
  const { repo } = setup()
499
478
  const handle = repo.create({ foo: "bar" })
500
- await handle.doc()
501
479
  assert(repo.handles[handle.documentId])
502
480
  })
503
481
 
504
482
  it("delete removes doc handle", async () => {
505
483
  const { repo } = setup()
506
484
  const handle = repo.create({ foo: "bar" })
507
- await handle.doc()
508
485
  await repo.delete(handle.documentId)
509
486
  assert(repo.handles[handle.documentId] === undefined)
510
487
  })
@@ -512,7 +489,6 @@ describe("Repo", () => {
512
489
  it("removeFromCache removes doc handle", async () => {
513
490
  const { repo } = setup()
514
491
  const handle = repo.create({ foo: "bar" })
515
- await handle.doc()
516
492
  await repo.removeFromCache(handle.documentId)
517
493
  assert(repo.handles[handle.documentId] === undefined)
518
494
  })
@@ -571,8 +547,8 @@ describe("Repo", () => {
571
547
 
572
548
  it("should not be in a new repo yet because the storage is slow", async () => {
573
549
  const { pausedStorage, repo, handle, handle2 } = setup()
574
- expect((await handle.doc()).foo).toEqual("first")
575
- expect((await handle2.doc()).foo).toEqual("second")
550
+ expect((await handle).doc().foo).toEqual("first")
551
+ expect((await handle2).doc().foo).toEqual("second")
576
552
 
577
553
  // Reload repo
578
554
  const repo2 = new Repo({
@@ -580,9 +556,10 @@ describe("Repo", () => {
580
556
  })
581
557
 
582
558
  // Could not find the document that is not yet saved because of slow storage.
583
- const reloadedHandle = repo2.find<{ foo: string }>(handle.url)
559
+ await expect(async () => {
560
+ const reloadedHandle = await repo2.find<{ foo: string }>(handle.url)
561
+ }).rejects.toThrow(/Document (.*) is unavailable/)
584
562
  expect(pausedStorage.keys()).to.deep.equal([])
585
- expect(await reloadedHandle.doc()).toEqual(undefined)
586
563
  })
587
564
 
588
565
  it("should be visible to a new repo after flush()", async () => {
@@ -602,10 +579,10 @@ describe("Repo", () => {
602
579
  })
603
580
 
604
581
  expect(
605
- (await repo.find<{ foo: string }>(handle.documentId).doc()).foo
582
+ (await repo.find<{ foo: string }>(handle.documentId)).doc().foo
606
583
  ).toEqual("first")
607
584
  expect(
608
- (await repo.find<{ foo: string }>(handle2.documentId).doc()).foo
585
+ (await repo.find<{ foo: string }>(handle2.documentId)).doc().foo
609
586
  ).toEqual("second")
610
587
  }
611
588
  })
@@ -627,13 +604,13 @@ describe("Repo", () => {
627
604
  })
628
605
 
629
606
  expect(
630
- (await repo.find<{ foo: string }>(handle.documentId).doc()).foo
607
+ (await repo.find<{ foo: string }>(handle.documentId)).doc().foo
631
608
  ).toEqual("first")
632
609
  // Really, it's okay if the second one is also flushed but I'm forcing the issue
633
610
  // in the test storage engine above to make sure the behaviour is as documented
634
- expect(
635
- await repo.find<{ foo: string }>(handle2.documentId).doc()
636
- ).toEqual(undefined)
611
+ await expect(async () => {
612
+ ;(await repo.find<{ foo: string }>(handle2.documentId)).doc()
613
+ }).rejects.toThrow(/Document (.*) is unavailable/)
637
614
  }
638
615
  })
639
616
 
@@ -681,7 +658,7 @@ describe("Repo", () => {
681
658
 
682
659
  if (idx < numberOfPeers - 1) {
683
660
  network.push(pair[0])
684
- pair[0].whenReady()
661
+ networkReady.push(pair[0].whenReady())
685
662
  }
686
663
 
687
664
  const repo = new Repo({
@@ -712,7 +689,6 @@ describe("Repo", () => {
712
689
  }
713
690
 
714
691
  await connectedPromise
715
-
716
692
  return { repos }
717
693
  }
718
694
 
@@ -724,10 +700,14 @@ describe("Repo", () => {
724
700
  d.foo = "bar"
725
701
  })
726
702
 
727
- const handleN = repos[numberOfPeers - 1].find<TestDoc>(handle0.url)
703
+ const handleN = await repos[numberOfPeers - 1].find<TestDoc>(handle0.url)
704
+ assert.deepStrictEqual(handleN.doc(), { foo: "bar" })
728
705
 
729
- await handleN.whenReady()
730
- assert.deepStrictEqual(handleN.docSync(), { foo: "bar" })
706
+ const handleNBack = repos[numberOfPeers - 1].create({
707
+ foo: "reverse-trip",
708
+ })
709
+ const handle0Back = await repos[0].find<TestDoc>(handleNBack.url)
710
+ assert.deepStrictEqual(handle0Back.doc(), { foo: "reverse-trip" })
731
711
  })
732
712
 
733
713
  const setup = async ({
@@ -854,9 +834,8 @@ describe("Repo", () => {
854
834
  it("changes are replicated from aliceRepo to bobRepo", async () => {
855
835
  const { bobRepo, aliceHandle, teardown } = await setup()
856
836
 
857
- const bobHandle = bobRepo.find<TestDoc>(aliceHandle.url)
858
- await eventPromise(bobHandle, "change")
859
- const bobDoc = await bobHandle.doc()
837
+ const bobHandle = await bobRepo.find<TestDoc>(aliceHandle.url)
838
+ const bobDoc = bobHandle.doc()
860
839
  assert.deepStrictEqual(bobDoc, { foo: "bar" })
861
840
  teardown()
862
841
  })
@@ -864,9 +843,8 @@ describe("Repo", () => {
864
843
  it("can load a document from aliceRepo on charlieRepo", async () => {
865
844
  const { charlieRepo, aliceHandle, teardown } = await setup()
866
845
 
867
- const handle3 = charlieRepo.find<TestDoc>(aliceHandle.url)
868
- await eventPromise(handle3, "change")
869
- const doc3 = await handle3.doc()
846
+ const handle3 = await charlieRepo.find<TestDoc>(aliceHandle.url)
847
+ const doc3 = handle3.doc()
870
848
  assert.deepStrictEqual(doc3, { foo: "bar" })
871
849
  teardown()
872
850
  })
@@ -885,12 +863,11 @@ describe("Repo", () => {
885
863
  await bobRepo2.flush()
886
864
 
887
865
  // Now, let's load it on the original bob repo (which shares a "disk")
888
- const bobFoundIt = bobRepo.find<TestDoc>(inStorageHandle.url)
889
- await bobFoundIt.whenReady()
866
+ const bobFoundIt = await bobRepo.find<TestDoc>(inStorageHandle.url)
890
867
 
891
868
  // Before checking if it syncs, make sure we have it!
892
869
  // (This behaviour is mostly test-validation, we are already testing load/save elsewhere.)
893
- assert.deepStrictEqual(await bobFoundIt.doc(), { foo: "foundOnFakeDisk" })
870
+ assert.deepStrictEqual(bobFoundIt.doc(), { foo: "foundOnFakeDisk" })
894
871
 
895
872
  await pause(10)
896
873
 
@@ -930,11 +907,8 @@ describe("Repo", () => {
930
907
  it("charlieRepo can request a document not initially shared with it", async () => {
931
908
  const { charlieRepo, notForCharlie, teardown } = await setup()
932
909
 
933
- const handle = charlieRepo.find<TestDoc>(notForCharlie)
934
-
935
- await pause(50)
936
-
937
- const doc = await handle.doc()
910
+ const handle = await charlieRepo.find<TestDoc>(notForCharlie)
911
+ const doc = handle.doc()
938
912
 
939
913
  assert.deepStrictEqual(doc, { foo: "baz" })
940
914
 
@@ -944,11 +918,11 @@ describe("Repo", () => {
944
918
  it("charlieRepo can request a document across a network of multiple peers", async () => {
945
919
  const { charlieRepo, notForBob, teardown } = await setup()
946
920
 
947
- const handle = charlieRepo.find<TestDoc>(notForBob)
921
+ const handle = await charlieRepo.find<TestDoc>(notForBob)
948
922
 
949
923
  await pause(50)
950
924
 
951
- const doc = await handle.doc()
925
+ const doc = handle.doc()
952
926
  assert.deepStrictEqual(doc, { foo: "bap" })
953
927
 
954
928
  teardown()
@@ -957,42 +931,10 @@ describe("Repo", () => {
957
931
  it("doesn't find a document which doesn't exist anywhere on the network", async () => {
958
932
  const { charlieRepo, teardown } = await setup()
959
933
  const url = generateAutomergeUrl()
960
- const handle = charlieRepo.find<TestDoc>(url)
961
- assert.equal(handle.isReady(), false)
962
-
963
- const doc = await handle.doc()
964
- assert.equal(doc, undefined)
965
-
966
- teardown()
967
- })
968
-
969
- it("emits an unavailable event when it's not found on the network", async () => {
970
- const { aliceRepo, teardown } = await setup()
971
- const url = generateAutomergeUrl()
972
- const handle = aliceRepo.find(url)
973
- assert.equal(handle.isReady(), false)
974
- await eventPromise(handle, "unavailable")
975
- teardown()
976
- })
977
-
978
- it("emits an unavailable event every time an unavailable doc is requested", async () => {
979
- const { charlieRepo, teardown } = await setup()
980
- const url = generateAutomergeUrl()
981
- const handle = charlieRepo.find<TestDoc>(url)
982
- assert.equal(handle.isReady(), false)
983
-
984
- await Promise.all([
985
- eventPromise(handle, "unavailable"),
986
- eventPromise(charlieRepo, "unavailable-document"),
987
- ])
988
934
 
989
- // make sure it emits a second time if the doc is still unavailable
990
- const handle2 = charlieRepo.find<TestDoc>(url)
991
- assert.equal(handle2.isReady(), false)
992
- await Promise.all([
993
- eventPromise(handle, "unavailable"),
994
- eventPromise(charlieRepo, "unavailable-document"),
995
- ])
935
+ await expect(charlieRepo.find<TestDoc>(url)).rejects.toThrow(
936
+ /Document (.*) is unavailable/
937
+ )
996
938
 
997
939
  teardown()
998
940
  })
@@ -1007,21 +949,23 @@ describe("Repo", () => {
1007
949
  } = await setup({ connectAlice: false })
1008
950
 
1009
951
  const url = stringifyAutomergeUrl({ documentId: notForCharlie })
1010
- const handle = charlieRepo.find<TestDoc>(url)
1011
- assert.equal(handle.isReady(), false)
1012
-
1013
- await eventPromise(handle, "unavailable")
952
+ await expect(charlieRepo.find<TestDoc>(url)).rejects.toThrow(
953
+ /Document (.*) is unavailable/
954
+ )
1014
955
 
1015
956
  connectAliceToBob()
1016
957
 
1017
958
  await eventPromise(aliceRepo.networkSubsystem, "peer")
1018
959
 
1019
- const doc = await handle.doc(["ready"])
960
+ // Not sure why we need this pause here, but... we do.
961
+ await pause(150)
962
+ const handle = await charlieRepo.find<TestDoc>(url)
963
+ const doc = handle.doc()
1020
964
  assert.deepStrictEqual(doc, { foo: "baz" })
1021
965
 
1022
966
  // an additional find should also return the correct resolved document
1023
- const handle2 = charlieRepo.find<TestDoc>(url)
1024
- const doc2 = await handle2.doc()
967
+ const handle2 = await charlieRepo.find<TestDoc>(url)
968
+ const doc2 = handle2.doc()
1025
969
  assert.deepStrictEqual(doc2, { foo: "baz" })
1026
970
 
1027
971
  teardown()
@@ -1057,11 +1001,9 @@ describe("Repo", () => {
1057
1001
  sharePolicy: async () => true,
1058
1002
  })
1059
1003
 
1060
- const handle = a.find(url)
1061
-
1062
- // We expect this to be unavailable as there is no connected peer and
1063
- // the repo has no storage.
1064
- await eventPromise(handle, "unavailable")
1004
+ await expect(a.find<TestDoc>(url)).rejects.toThrow(
1005
+ /Document (.*) is unavailable/
1006
+ )
1065
1007
 
1066
1008
  // Now create a repo pointing at the storage containing the document and
1067
1009
  // connect it to the other end of the MessageChannel
@@ -1071,9 +1013,14 @@ describe("Repo", () => {
1071
1013
  network: [new MessageChannelNetworkAdapter(ba)],
1072
1014
  })
1073
1015
 
1016
+ // We need a proper peer status API so we can tell when the
1017
+ // peer is connected. For now we just wait a bit.
1018
+ await pause(50)
1019
+
1074
1020
  // The empty repo should be notified of the new peer, send it a request
1075
1021
  // and eventually resolve the handle to "READY"
1076
- await handle.whenReady()
1022
+ const handle = await a.find<TestDoc>(url)
1023
+ expect(handle.state).toBe("ready")
1077
1024
  })
1078
1025
 
1079
1026
  it("a deleted document from charlieRepo can be refetched", async () => {
@@ -1089,9 +1036,8 @@ describe("Repo", () => {
1089
1036
  })
1090
1037
  await changePromise
1091
1038
 
1092
- const handle3 = charlieRepo.find<TestDoc>(aliceHandle.url)
1093
- await eventPromise(handle3, "change")
1094
- const doc3 = await handle3.doc()
1039
+ const handle3 = await charlieRepo.find<TestDoc>(aliceHandle.url)
1040
+ const doc3 = handle3.doc()
1095
1041
 
1096
1042
  assert.deepStrictEqual(doc3, { foo: "baz" })
1097
1043
 
@@ -1115,11 +1061,6 @@ describe("Repo", () => {
1115
1061
  : // tails, pick a random doc
1116
1062
  (getRandomItem(docs) as DocHandle<TestDoc>)
1117
1063
 
1118
- // make sure the doc is ready
1119
- if (!doc.isReady()) {
1120
- await doc.doc()
1121
- }
1122
-
1123
1064
  // make a random change to it
1124
1065
  doc.change(d => {
1125
1066
  d.foo = Math.random().toString()
@@ -1135,10 +1076,10 @@ describe("Repo", () => {
1135
1076
 
1136
1077
  const data = { presence: "alice" }
1137
1078
 
1138
- const aliceHandle = aliceRepo.find<TestDoc>(
1079
+ const aliceHandle = await aliceRepo.find<TestDoc>(
1139
1080
  stringifyAutomergeUrl({ documentId: notForCharlie })
1140
1081
  )
1141
- const bobHandle = bobRepo.find<TestDoc>(
1082
+ const bobHandle = await bobRepo.find<TestDoc>(
1142
1083
  stringifyAutomergeUrl({ documentId: notForCharlie })
1143
1084
  )
1144
1085
 
@@ -1291,8 +1232,7 @@ describe("Repo", () => {
1291
1232
  })
1292
1233
  })
1293
1234
 
1294
- const charlieHandle = charlieRepo.find<TestDoc>(handle.url)
1295
- await charlieHandle.whenReady()
1235
+ const charlieHandle = await charlieRepo.find<TestDoc>(handle.url)
1296
1236
 
1297
1237
  // make a change on charlie
1298
1238
  charlieHandle.change(d => {
@@ -1329,34 +1269,6 @@ describe("Repo", () => {
1329
1269
  })
1330
1270
  })
1331
1271
 
1332
- it("peer receives a document when connection is recovered", async () => {
1333
- const alice = "alice" as PeerId
1334
- const bob = "bob" as PeerId
1335
- const [aliceAdapter, bobAdapter] = DummyNetworkAdapter.createConnectedPair()
1336
- const aliceRepo = new Repo({
1337
- network: [aliceAdapter],
1338
- peerId: alice,
1339
- })
1340
- const bobRepo = new Repo({
1341
- network: [bobAdapter],
1342
- peerId: bob,
1343
- })
1344
- const aliceDoc = aliceRepo.create()
1345
- aliceDoc.change((doc: any) => (doc.text = "Hello world"))
1346
-
1347
- const bobDoc = bobRepo.find(aliceDoc.url)
1348
- await eventPromise(bobDoc, "unavailable")
1349
-
1350
- aliceAdapter.peerCandidate(bob)
1351
- // Bob isn't yet connected to Alice and can't respond to her sync message
1352
- await pause(100)
1353
- bobAdapter.peerCandidate(alice)
1354
-
1355
- await bobDoc.whenReady()
1356
-
1357
- assert.equal(bobDoc.isReady(), true)
1358
- })
1359
-
1360
1272
  describe("with peers (mesh network)", () => {
1361
1273
  const setup = async () => {
1362
1274
  // Set up three repos; connect Alice to Bob, Bob to Charlie, and Alice to Charlie
@@ -1418,8 +1330,8 @@ describe("Repo", () => {
1418
1330
 
1419
1331
  const aliceHandle = aliceRepo.create<TestDoc>()
1420
1332
 
1421
- const bobHandle = bobRepo.find(aliceHandle.url)
1422
- const charlieHandle = charlieRepo.find(aliceHandle.url)
1333
+ const bobHandle = await bobRepo.find(aliceHandle.url)
1334
+ const charlieHandle = await charlieRepo.find(aliceHandle.url)
1423
1335
 
1424
1336
  // Alice should not receive her own ephemeral message
1425
1337
  aliceHandle.on("ephemeral-message", () => {
@@ -1457,9 +1369,8 @@ describe("Repo", () => {
1457
1369
  // pause to let the sync happen
1458
1370
  await pause(50)
1459
1371
 
1460
- const charlieHandle = charlieRepo.find(handle2.url)
1461
- await charlieHandle.doc()
1462
- assert.deepStrictEqual(charlieHandle.docSync(), { foo: "bar" })
1372
+ const charlieHandle = await charlieRepo.find(handle2.url)
1373
+ assert.deepStrictEqual(charlieHandle.doc(), { foo: "bar" })
1463
1374
 
1464
1375
  teardown()
1465
1376
  })
@@ -1476,9 +1387,8 @@ describe("Repo", () => {
1476
1387
  // pause to let the sync happen
1477
1388
  await pause(50)
1478
1389
 
1479
- const charlieHandle = charlieRepo.find(handle2.url)
1480
- await charlieHandle.doc()
1481
- assert.deepStrictEqual(charlieHandle.docSync(), { foo: "bar" })
1390
+ const charlieHandle = await charlieRepo.find(handle2.url)
1391
+ assert.deepStrictEqual(charlieHandle.doc(), { foo: "bar" })
1482
1392
 
1483
1393
  // now make a change to doc2 on bobs side and merge it into doc1
1484
1394
  handle2.change(d => {
@@ -1489,8 +1399,7 @@ describe("Repo", () => {
1489
1399
  // wait for the network to do it's thang
1490
1400
  await pause(350)
1491
1401
 
1492
- await charlieHandle.doc()
1493
- assert.deepStrictEqual(charlieHandle.docSync(), { foo: "baz" })
1402
+ assert.deepStrictEqual(charlieHandle.doc(), { foo: "baz" })
1494
1403
 
1495
1404
  teardown()
1496
1405
  })
@@ -1525,9 +1434,9 @@ describe("Repo", () => {
1525
1434
  eventPromise(client.networkSubsystem, "peer"),
1526
1435
  ])
1527
1436
 
1528
- const clientDoc = client.find(doc.url)
1529
- await pause(100)
1530
- assert.strictEqual(clientDoc.docSync(), undefined)
1437
+ await expect(async () => {
1438
+ const clientDoc = await client.find(doc.url)
1439
+ }).rejects.toThrow(/Document (.*) is unavailable/)
1531
1440
 
1532
1441
  const openDocs = Object.keys(server.metrics().documents).length
1533
1442
  assert.deepEqual(openDocs, 0)
@@ -1547,8 +1456,8 @@ describe("Repo heads-in-URLs functionality", () => {
1547
1456
  const { repo, handle } = setup()
1548
1457
  const heads = handle.heads()!
1549
1458
  const url = stringifyAutomergeUrl({ documentId: handle.documentId, heads })
1550
- const view = repo.find(url)
1551
- expect(view.docSync()).toEqual({ title: "Hello World" })
1459
+ const view = await repo.find(url)
1460
+ expect(view.doc()).toEqual({ title: "Hello World" })
1552
1461
  })
1553
1462
 
1554
1463
  it("returns a view, not the actual handle, when finding by URL with heads", async () => {
@@ -1556,35 +1465,35 @@ describe("Repo heads-in-URLs functionality", () => {
1556
1465
  const heads = handle.heads()!
1557
1466
  await handle.change((doc: any) => (doc.title = "Changed"))
1558
1467
  const url = stringifyAutomergeUrl({ documentId: handle.documentId, heads })
1559
- const view = repo.find(url)
1560
- expect(view.docSync()).toEqual({ title: "Hello World" })
1561
- expect(handle.docSync()).toEqual({ title: "Changed" })
1468
+ const view = await repo.find(url)
1469
+ expect(view.doc()).toEqual({ title: "Hello World" })
1470
+ expect(handle.doc()).toEqual({ title: "Changed" })
1562
1471
  })
1563
1472
 
1564
1473
  it("changes to a document view do not affect the original", async () => {
1565
1474
  const { repo, handle } = setup()
1566
1475
  const heads = handle.heads()!
1567
1476
  const url = stringifyAutomergeUrl({ documentId: handle.documentId, heads })
1568
- const view = repo.find(url)
1477
+ const view = await repo.find(url)
1569
1478
  expect(() =>
1570
1479
  view.change((doc: any) => (doc.title = "Changed in View"))
1571
1480
  ).toThrow()
1572
- expect(handle.docSync()).toEqual({ title: "Hello World" })
1481
+ expect(handle.doc()).toEqual({ title: "Hello World" })
1573
1482
  })
1574
1483
 
1575
1484
  it("document views are read-only", async () => {
1576
1485
  const { repo, handle } = setup()
1577
1486
  const heads = handle.heads()!
1578
1487
  const url = stringifyAutomergeUrl({ documentId: handle.documentId, heads })
1579
- const view = repo.find(url)
1488
+ const view = await repo.find(url)
1580
1489
  expect(() => view.change((doc: any) => (doc.title = "Changed"))).toThrow()
1581
1490
  })
1582
1491
 
1583
1492
  it("finds the latest document when given a URL without heads", async () => {
1584
1493
  const { repo, handle } = setup()
1585
1494
  await handle.change((doc: any) => (doc.title = "Changed"))
1586
- const found = repo.find(handle.url)
1587
- expect(found.docSync()).toEqual({ title: "Changed" })
1495
+ const found = await repo.find(handle.url)
1496
+ expect(found.doc()).toEqual({ title: "Changed" })
1588
1497
  })
1589
1498
 
1590
1499
  it("getHeadsFromUrl returns heads array if present or undefined", () => {
@@ -1640,6 +1549,50 @@ describe("Repo heads-in-URLs functionality", () => {
1640
1549
  })
1641
1550
  })
1642
1551
 
1552
+ describe("Repo.find() abort behavior", () => {
1553
+ it("aborts immediately if signal is already aborted", async () => {
1554
+ const repo = new Repo()
1555
+ const controller = new AbortController()
1556
+ controller.abort()
1557
+
1558
+ await expect(
1559
+ repo.find(generateAutomergeUrl(), { signal: controller.signal })
1560
+ ).rejects.toThrow("Operation aborted")
1561
+ })
1562
+
1563
+ it("can abort while waiting for ready state", async () => {
1564
+ // Create a repo with no network adapters so document can't become ready
1565
+ const repo = new Repo()
1566
+ const url = generateAutomergeUrl()
1567
+
1568
+ const controller = new AbortController()
1569
+
1570
+ // Start find and abort after a moment
1571
+ const findPromise = repo.find(url, { signal: controller.signal })
1572
+ controller.abort()
1573
+
1574
+ await expect(findPromise).rejects.toThrow("Operation aborted")
1575
+ await expect(findPromise).rejects.not.toThrow("unavailable")
1576
+ })
1577
+
1578
+ it("returns handle immediately when allow unavailable is true, even with abort signal", async () => {
1579
+ const repo = new Repo()
1580
+ const controller = new AbortController()
1581
+ const url = generateAutomergeUrl()
1582
+
1583
+ const handle = await repo.find(url, {
1584
+ allowableStates: ["unavailable"],
1585
+ signal: controller.signal,
1586
+ })
1587
+
1588
+ expect(handle).toBeDefined()
1589
+
1590
+ // Abort shouldn't affect the result since we skipped ready
1591
+ controller.abort()
1592
+ expect(handle.url).toBe(url)
1593
+ })
1594
+ })
1595
+
1643
1596
  const warn = console.warn
1644
1597
  const NO_OP = () => {}
1645
1598