@automerge/automerge-repo 1.1.0-alpha.7 → 1.1.1

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 (73) hide show
  1. package/README.md +5 -3
  2. package/dist/AutomergeUrl.js +1 -1
  3. package/dist/DocHandle.d.ts +10 -4
  4. package/dist/DocHandle.d.ts.map +1 -1
  5. package/dist/DocHandle.js +21 -13
  6. package/dist/Repo.d.ts +22 -10
  7. package/dist/Repo.d.ts.map +1 -1
  8. package/dist/Repo.js +90 -76
  9. package/dist/helpers/pause.d.ts +0 -1
  10. package/dist/helpers/pause.d.ts.map +1 -1
  11. package/dist/helpers/pause.js +2 -8
  12. package/dist/helpers/tests/network-adapter-tests.d.ts +2 -2
  13. package/dist/helpers/tests/network-adapter-tests.d.ts.map +1 -1
  14. package/dist/helpers/tests/network-adapter-tests.js +16 -1
  15. package/dist/helpers/withTimeout.d.ts.map +1 -1
  16. package/dist/helpers/withTimeout.js +2 -0
  17. package/dist/index.d.ts +4 -2
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/network/NetworkAdapter.d.ts +4 -34
  21. package/dist/network/NetworkAdapter.d.ts.map +1 -1
  22. package/dist/network/NetworkAdapter.js +2 -0
  23. package/dist/network/NetworkAdapterInterface.d.ts +61 -0
  24. package/dist/network/NetworkAdapterInterface.d.ts.map +1 -0
  25. package/dist/network/NetworkAdapterInterface.js +2 -0
  26. package/dist/network/NetworkSubsystem.d.ts +3 -3
  27. package/dist/network/NetworkSubsystem.d.ts.map +1 -1
  28. package/dist/network/NetworkSubsystem.js +7 -5
  29. package/dist/network/messages.d.ts +43 -38
  30. package/dist/network/messages.d.ts.map +1 -1
  31. package/dist/network/messages.js +7 -9
  32. package/dist/storage/StorageAdapter.d.ts +3 -1
  33. package/dist/storage/StorageAdapter.d.ts.map +1 -1
  34. package/dist/storage/StorageAdapter.js +1 -0
  35. package/dist/storage/StorageAdapterInterface.d.ts +30 -0
  36. package/dist/storage/StorageAdapterInterface.d.ts.map +1 -0
  37. package/dist/storage/StorageAdapterInterface.js +1 -0
  38. package/dist/storage/StorageSubsystem.d.ts +2 -2
  39. package/dist/storage/StorageSubsystem.d.ts.map +1 -1
  40. package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
  41. package/dist/synchronizer/CollectionSynchronizer.js +1 -0
  42. package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
  43. package/dist/synchronizer/DocSynchronizer.js +13 -9
  44. package/dist/synchronizer/Synchronizer.d.ts +11 -3
  45. package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
  46. package/package.json +3 -4
  47. package/src/AutomergeUrl.ts +1 -1
  48. package/src/DocHandle.ts +40 -19
  49. package/src/Repo.ts +123 -98
  50. package/src/helpers/pause.ts +3 -11
  51. package/src/helpers/tests/network-adapter-tests.ts +30 -4
  52. package/src/helpers/withTimeout.ts +2 -0
  53. package/src/index.ts +4 -2
  54. package/src/network/NetworkAdapter.ts +9 -45
  55. package/src/network/NetworkAdapterInterface.ts +77 -0
  56. package/src/network/NetworkSubsystem.ts +16 -14
  57. package/src/network/messages.ts +60 -63
  58. package/src/storage/StorageAdapter.ts +3 -1
  59. package/src/storage/StorageAdapterInterface.ts +34 -0
  60. package/src/storage/StorageSubsystem.ts +3 -3
  61. package/src/synchronizer/CollectionSynchronizer.ts +1 -0
  62. package/src/synchronizer/DocSynchronizer.ts +22 -18
  63. package/src/synchronizer/Synchronizer.ts +11 -3
  64. package/test/CollectionSynchronizer.test.ts +7 -5
  65. package/test/DocHandle.test.ts +35 -3
  66. package/test/RemoteHeadsSubscriptions.test.ts +49 -49
  67. package/test/Repo.test.ts +71 -2
  68. package/test/StorageSubsystem.test.ts +1 -1
  69. package/test/helpers/DummyNetworkAdapter.ts +37 -5
  70. package/test/helpers/collectMessages.ts +19 -0
  71. package/test/remoteHeads.test.ts +142 -119
  72. package/.eslintrc +0 -28
  73. package/test/helpers/waitForMessages.ts +0 -22
@@ -1,6 +1,6 @@
1
1
  import assert from "assert"
2
2
  import { beforeEach, describe, it } from "vitest"
3
- import { PeerId, Repo } from "../src/index.js"
3
+ import { PeerId, Repo, SyncMessage } from "../src/index.js"
4
4
  import { CollectionSynchronizer } from "../src/synchronizer/CollectionSynchronizer.js"
5
5
 
6
6
  describe("CollectionSynchronizer", () => {
@@ -24,8 +24,9 @@ describe("CollectionSynchronizer", () => {
24
24
  synchronizer.addPeer("peer1" as PeerId)
25
25
 
26
26
  synchronizer.once("message", event => {
27
- assert(event.targetId === "peer1")
28
- assert(event.documentId === handle.documentId)
27
+ const { targetId, documentId } = event as SyncMessage
28
+ assert(targetId === "peer1")
29
+ assert(documentId === handle.documentId)
29
30
  done()
30
31
  })
31
32
 
@@ -37,8 +38,9 @@ describe("CollectionSynchronizer", () => {
37
38
  const handle = repo.create()
38
39
  synchronizer.addDocument(handle.documentId)
39
40
  synchronizer.once("message", event => {
40
- assert(event.targetId === "peer1")
41
- assert(event.documentId === handle.documentId)
41
+ const { targetId, documentId } = event as SyncMessage
42
+ assert(targetId === "peer1")
43
+ assert(documentId === handle.documentId)
42
44
  done()
43
45
  })
44
46
  synchronizer.addPeer("peer1" as PeerId)
@@ -1,11 +1,11 @@
1
1
  import * as A from "@automerge/automerge/next"
2
2
  import assert from "assert"
3
3
  import { decode } from "cbor-x"
4
- import { describe, it } from "vitest"
4
+ import { describe, it, vi } from "vitest"
5
5
  import { generateAutomergeUrl, parseAutomergeUrl } from "../src/AutomergeUrl.js"
6
6
  import { eventPromise } from "../src/helpers/eventPromise.js"
7
7
  import { pause } from "../src/helpers/pause.js"
8
- import { DocHandle, DocHandleChangePayload, PeerId } from "../src/index.js"
8
+ import { DocHandle, DocHandleChangePayload } from "../src/index.js"
9
9
  import { TestDoc } from "./types.js"
10
10
 
11
11
  describe("DocHandle", () => {
@@ -20,6 +20,15 @@ describe("DocHandle", () => {
20
20
  assert.equal(handle.documentId, TEST_ID)
21
21
  })
22
22
 
23
+ it("should take an initial value", async () => {
24
+ const handle = new DocHandle(TEST_ID, {
25
+ isNew: true,
26
+ initialValue: { foo: "bar" },
27
+ })
28
+ const doc = await handle.doc()
29
+ assert.equal(doc.foo, "bar")
30
+ })
31
+
23
32
  it("should become ready when a document is loaded", async () => {
24
33
  const handle = new DocHandle<TestDoc>(TEST_ID)
25
34
  assert.equal(handle.isReady(), false)
@@ -44,7 +53,7 @@ describe("DocHandle", () => {
44
53
  assert.deepEqual(doc, handle.docSync())
45
54
  })
46
55
 
47
- it("should return undefined if we accessing the doc before ready", async () => {
56
+ it("should return undefined if we access the doc before ready", async () => {
48
57
  const handle = new DocHandle<TestDoc>(TEST_ID)
49
58
 
50
59
  assert.equal(handle.docSync(), undefined)
@@ -63,6 +72,29 @@ describe("DocHandle", () => {
63
72
  assert.equal(doc?.foo, "bar")
64
73
  })
65
74
 
75
+ /**
76
+ * Once there's a Repo#stop API this case should be covered in accompanying
77
+ * tests and the following test removed.
78
+ */
79
+ it("no pending timers after a document is loaded", async () => {
80
+ vi.useFakeTimers()
81
+ const timerCount = vi.getTimerCount()
82
+
83
+ const handle = new DocHandle<TestDoc>(TEST_ID)
84
+ assert.equal(handle.isReady(), false)
85
+
86
+ handle.doc()
87
+
88
+ assert(vi.getTimerCount() > timerCount)
89
+
90
+ // simulate loading from storage
91
+ handle.update(doc => docFromMockStorage(doc))
92
+
93
+ assert.equal(handle.isReady(), true)
94
+ assert.equal(vi.getTimerCount(), timerCount)
95
+ vi.useRealTimers()
96
+ })
97
+
66
98
  it("should block changes until ready()", async () => {
67
99
  const handle = new DocHandle<TestDoc>(TEST_ID)
68
100
 
@@ -8,7 +8,7 @@ import {
8
8
  RemoteHeadsChanged,
9
9
  RemoteSubscriptionControlMessage,
10
10
  } from "../src/network/messages.js"
11
- import { waitForMessages } from "./helpers/waitForMessages.js"
11
+ import { collectMessages } from "./helpers/collectMessages.js"
12
12
 
13
13
  describe("RepoHeadsSubscriptions", () => {
14
14
  const storageA = "remote-a" as StorageId
@@ -86,15 +86,15 @@ describe("RepoHeadsSubscriptions", () => {
86
86
  it("should allow to subscribe and unsubscribe to storage ids", async () => {
87
87
  const remoteHeadsSubscriptions = new RemoteHeadsSubscriptions()
88
88
 
89
- const remoteHeadsMessages = waitForMessages(
90
- remoteHeadsSubscriptions,
91
- "remote-heads-changed"
92
- )
89
+ const remoteHeadsMessages = collectMessages({
90
+ emitter: remoteHeadsSubscriptions,
91
+ event: "remote-heads-changed",
92
+ })
93
93
 
94
- const changeRemoteSubsAfterSubscribe = waitForMessages(
95
- remoteHeadsSubscriptions,
96
- "change-remote-subs"
97
- )
94
+ const changeRemoteSubsAfterSubscribe = collectMessages({
95
+ emitter: remoteHeadsSubscriptions,
96
+ event: "change-remote-subs",
97
+ })
98
98
 
99
99
  // subscribe to storageB and change storageB heads
100
100
  remoteHeadsSubscriptions.subscribeToRemotes([storageB])
@@ -114,10 +114,10 @@ describe("RepoHeadsSubscriptions", () => {
114
114
  assert.deepStrictEqual(messages[0].remove, undefined)
115
115
  assert.deepStrictEqual(messages[0].peers, [])
116
116
 
117
- const remoteHeadsMessagesAfterUnsub = waitForMessages(
118
- remoteHeadsSubscriptions,
119
- "change-remote-subs"
120
- )
117
+ const remoteHeadsMessagesAfterUnsub = collectMessages({
118
+ emitter: remoteHeadsSubscriptions,
119
+ event: "change-remote-subs",
120
+ })
121
121
 
122
122
  // unsubscribe from storageB
123
123
  remoteHeadsSubscriptions.unsubscribeFromRemotes([storageB])
@@ -133,15 +133,15 @@ describe("RepoHeadsSubscriptions", () => {
133
133
  it("should forward all changes to generous peers", async () => {
134
134
  const remoteHeadsSubscriptions = new RemoteHeadsSubscriptions()
135
135
 
136
- const notifyRemoteHeadsMessagesPromise = waitForMessages(
137
- remoteHeadsSubscriptions,
138
- "notify-remote-heads"
139
- )
136
+ const notifyRemoteHeadsMessagesPromise = collectMessages({
137
+ emitter: remoteHeadsSubscriptions,
138
+ event: "notify-remote-heads",
139
+ })
140
140
 
141
- const changeRemoteSubsMessagesPromise = waitForMessages(
142
- remoteHeadsSubscriptions,
143
- "change-remote-subs"
144
- )
141
+ const changeRemoteSubsMessagesPromise = collectMessages({
142
+ emitter: remoteHeadsSubscriptions,
143
+ event: "change-remote-subs",
144
+ })
145
145
 
146
146
  remoteHeadsSubscriptions.addGenerousPeer(peerC)
147
147
  remoteHeadsSubscriptions.subscribeToRemotes([storageB])
@@ -170,10 +170,10 @@ describe("RepoHeadsSubscriptions", () => {
170
170
  assert.deepStrictEqual(messages[0].remove, undefined)
171
171
  assert.deepStrictEqual(messages[0].peers, [peerC])
172
172
 
173
- const changeRemoteSubsMessagesAfterUnsubPromise = waitForMessages(
174
- remoteHeadsSubscriptions,
175
- "change-remote-subs"
176
- )
173
+ const changeRemoteSubsMessagesAfterUnsubPromise = collectMessages({
174
+ emitter: remoteHeadsSubscriptions,
175
+ event: "change-remote-subs",
176
+ })
177
177
 
178
178
  // unsubsscribe from storage B
179
179
  remoteHeadsSubscriptions.unsubscribeFromRemotes([storageB])
@@ -189,10 +189,10 @@ describe("RepoHeadsSubscriptions", () => {
189
189
  it("should not notify generous peers of changed remote heads, if they send the heads originally", async () => {
190
190
  const remoteHeadsSubscriptions = new RemoteHeadsSubscriptions()
191
191
 
192
- const messagesPromise = waitForMessages(
193
- remoteHeadsSubscriptions,
194
- "notify-remote-heads"
195
- )
192
+ const messagesPromise = collectMessages({
193
+ emitter: remoteHeadsSubscriptions,
194
+ event: "notify-remote-heads",
195
+ })
196
196
 
197
197
  remoteHeadsSubscriptions.addGenerousPeer(peerC)
198
198
  remoteHeadsSubscriptions.subscribeToRemotes([storageB])
@@ -219,10 +219,10 @@ describe("RepoHeadsSubscriptions", () => {
219
219
 
220
220
  // subscribe peer c to storage b
221
221
  remoteHeadsSubscriptions.handleControlMessage(subscribePeerCToStorageB)
222
- const messagesAfterSubscribePromise = waitForMessages(
223
- remoteHeadsSubscriptions,
224
- "notify-remote-heads"
225
- )
222
+ const messagesAfterSubscribePromise = collectMessages({
223
+ emitter: remoteHeadsSubscriptions,
224
+ event: "notify-remote-heads",
225
+ })
226
226
  remoteHeadsSubscriptions.subscribePeerToDoc(peerC, docA)
227
227
  remoteHeadsSubscriptions.subscribePeerToDoc(peerC, docC)
228
228
 
@@ -248,10 +248,10 @@ describe("RepoHeadsSubscriptions", () => {
248
248
 
249
249
  // unsubscribe peer C
250
250
  remoteHeadsSubscriptions.handleControlMessage(unsubscribePeerCFromStorageB)
251
- const messagesAfteUnsubscribePromise = waitForMessages(
252
- remoteHeadsSubscriptions,
253
- "notify-remote-heads"
254
- )
251
+ const messagesAfteUnsubscribePromise = collectMessages({
252
+ emitter: remoteHeadsSubscriptions,
253
+ event: "notify-remote-heads",
254
+ })
255
255
 
256
256
  // heads of docB for storageB change
257
257
  remoteHeadsSubscriptions.handleRemoteHeads(docBHeadsChangedForStorageB)
@@ -267,10 +267,10 @@ describe("RepoHeadsSubscriptions", () => {
267
267
 
268
268
  // subscribe peer c to storage b
269
269
  remoteHeadsSubscriptions.handleControlMessage(subscribePeerCToStorageB)
270
- const messagesAfterSubscribePromise = waitForMessages(
271
- remoteHeadsSubscriptions,
272
- "notify-remote-heads"
273
- )
270
+ const messagesAfterSubscribePromise = collectMessages({
271
+ emitter: remoteHeadsSubscriptions,
272
+ event: "notify-remote-heads",
273
+ })
274
274
 
275
275
  // change message for docA in storageB
276
276
  remoteHeadsSubscriptions.handleRemoteHeads(docAHeadsChangedForStorageB)
@@ -290,10 +290,10 @@ describe("RepoHeadsSubscriptions", () => {
290
290
  it("should only notify of sync states with a more recent timestamp", async () => {
291
291
  const remoteHeadsSubscription = new RemoteHeadsSubscriptions()
292
292
 
293
- const messagesPromise = waitForMessages(
294
- remoteHeadsSubscription,
295
- "remote-heads-changed"
296
- )
293
+ const messagesPromise = collectMessages({
294
+ emitter: remoteHeadsSubscription,
295
+ event: "remote-heads-changed",
296
+ })
297
297
 
298
298
  remoteHeadsSubscription.subscribeToRemotes([storageB])
299
299
  remoteHeadsSubscription.handleRemoteHeads(docBHeadsChangedForStorageB2)
@@ -314,10 +314,10 @@ describe("RepoHeadsSubscriptions", () => {
314
314
  it("should remove subs of disconnected peers", async () => {
315
315
  const remoteHeadsSubscriptions = new RemoteHeadsSubscriptions()
316
316
 
317
- const messagesPromise = waitForMessages(
318
- remoteHeadsSubscriptions,
319
- "change-remote-subs"
320
- )
317
+ const messagesPromise = collectMessages({
318
+ emitter: remoteHeadsSubscriptions,
319
+ event: "change-remote-subs",
320
+ })
321
321
 
322
322
  remoteHeadsSubscriptions.handleControlMessage({
323
323
  type: "remote-subscription-change",
package/test/Repo.test.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { next as A } from "@automerge/automerge"
2
- import { MessageChannelNetworkAdapter } from "@automerge/automerge-repo-network-messagechannel"
2
+ import { MessageChannelNetworkAdapter } from "../../automerge-repo-network-messagechannel/src/index.js"
3
3
  import assert from "assert"
4
4
  import * as Uuid from "uuid"
5
5
  import { describe, expect, it } from "vitest"
6
- import { READY } from "../src/DocHandle.js"
6
+ import { HandleState, READY } from "../src/DocHandle.js"
7
7
  import { parseAutomergeUrl } from "../src/AutomergeUrl.js"
8
8
  import {
9
9
  generateAutomergeUrl,
@@ -13,6 +13,7 @@ import { Repo } from "../src/Repo.js"
13
13
  import { eventPromise } from "../src/helpers/eventPromise.js"
14
14
  import { pause } from "../src/helpers/pause.js"
15
15
  import {
16
+ AnyDocumentId,
16
17
  AutomergeUrl,
17
18
  DocHandle,
18
19
  DocumentId,
@@ -31,6 +32,15 @@ import { TestDoc } from "./types.js"
31
32
  import { StorageId } from "../src/storage/types.js"
32
33
 
33
34
  describe("Repo", () => {
35
+ describe("constructor", () => {
36
+ it("can be instantiated without network adapters", () => {
37
+ const repo = new Repo({
38
+ network: [],
39
+ })
40
+ expect(repo).toBeInstanceOf(Repo)
41
+ })
42
+ })
43
+
34
44
  describe("local only", () => {
35
45
  const setup = ({ startReady = true } = {}) => {
36
46
  const storageAdapter = new DummyStorageAdapter()
@@ -57,6 +67,13 @@ describe("Repo", () => {
57
67
  assert.equal(handle.isReady(), true)
58
68
  })
59
69
 
70
+ it("can create a document with an initial value", async () => {
71
+ const { repo } = setup()
72
+ const handle = repo.create({ foo: "bar" })
73
+ await handle.doc()
74
+ assert.equal(handle.docSync().foo, "bar")
75
+ })
76
+
60
77
  it("can find a document by url", () => {
61
78
  const { repo } = setup()
62
79
  const handle = repo.create<TestDoc>()
@@ -321,6 +338,27 @@ describe("Repo", () => {
321
338
  repo.delete(handle.documentId)
322
339
  }))
323
340
 
341
+ it("exports a document", async () => {
342
+ const { repo } = setup()
343
+ const handle = repo.create<TestDoc>()
344
+ handle.change(d => {
345
+ d.foo = "bar"
346
+ })
347
+ assert.equal(handle.isReady(), true)
348
+
349
+ const exported = await repo.export(handle.documentId)
350
+ const loaded = A.load(exported)
351
+ const doc = await handle.doc()
352
+ assert.deepEqual(doc, loaded)
353
+ })
354
+
355
+ it("rejects when exporting a document that does not exist", async () => {
356
+ const { repo } = setup()
357
+ assert.rejects(async () => {
358
+ await repo.export("foo" as AnyDocumentId)
359
+ })
360
+ })
361
+
324
362
  it("storage state doesn't change across reloads when the document hasn't changed", async () => {
325
363
  const storage = new DummyStorageAdapter()
326
364
 
@@ -985,6 +1023,37 @@ describe("Repo", () => {
985
1023
  })
986
1024
  })
987
1025
 
1026
+
1027
+ it('peer receives a document when connection is recovered', async () => {
1028
+ const alice = "alice" as PeerId;
1029
+ const bob = "bob" as PeerId;
1030
+ const [aliceAdapter, bobAdapter] = DummyNetworkAdapter.createConnectedPair();
1031
+ const aliceRepo = new Repo({
1032
+ network: [aliceAdapter],
1033
+ peerId: alice
1034
+ })
1035
+ const bobRepo = new Repo({
1036
+ network: [bobAdapter],
1037
+ peerId: bob
1038
+ })
1039
+
1040
+ const aliceDoc = aliceRepo.create();
1041
+ aliceDoc.change((doc: any) => doc.text = 'Hello world');
1042
+
1043
+ const bobDoc = bobRepo.find(aliceDoc.url);
1044
+ bobDoc.unavailable()
1045
+ await bobDoc.whenReady([HandleState.UNAVAILABLE]);
1046
+
1047
+ aliceAdapter.peerCandidate(bob);
1048
+ // Bob isn't yet connected to Alice and can't respond to her sync message
1049
+ await pause(100);
1050
+ bobAdapter.peerCandidate(alice);
1051
+
1052
+ await bobDoc.whenReady([HandleState.READY]);
1053
+
1054
+ assert.equal(bobDoc.isReady(), true);
1055
+ });
1056
+
988
1057
  describe("with peers (mesh network)", () => {
989
1058
  const setup = async () => {
990
1059
  // Set up three repos; connect Alice to Bob, Bob to Charlie, and Alice to Charlie
@@ -1,4 +1,4 @@
1
- import { NodeFSStorageAdapter } from "@automerge/automerge-repo-storage-nodefs"
1
+ import { NodeFSStorageAdapter } from "../../automerge-repo-storage-nodefs/src/index.js"
2
2
  import * as A from "@automerge/automerge/next"
3
3
  import assert from "assert"
4
4
  import fs from "fs"
@@ -1,21 +1,53 @@
1
- import { NetworkAdapter } from "../../src/index.js"
1
+ import { pause } from "../../src/helpers/pause.js";
2
+ import { Message, NetworkAdapter, PeerId } from "../../src/index.js"
2
3
 
3
4
  export class DummyNetworkAdapter extends NetworkAdapter {
4
5
  #startReady: boolean
6
+ #sendMessage?: SendMessageFn;
5
7
 
6
- constructor({ startReady = true }: Options = {}) {
8
+ constructor(opts: Options = {startReady: true}) {
7
9
  super()
8
- this.#startReady = startReady
10
+ this.#startReady = opts.startReady;
11
+ this.#sendMessage = opts.sendMessage;
9
12
  }
10
- send() {}
13
+
11
14
  connect(_: string) {
12
15
  if (this.#startReady) {
13
16
  this.emit("ready", { network: this })
14
17
  }
15
18
  }
19
+
16
20
  disconnect() {}
21
+
22
+ peerCandidate(peerId: PeerId) {
23
+ this.emit('peer-candidate', { peerId, peerMetadata: {} });
24
+ }
25
+
26
+ override send(message: Message) {
27
+ this.#sendMessage?.(message);
28
+ }
29
+
30
+ receive(message: Message) {
31
+ this.emit('message', message);
32
+ }
33
+
34
+ static createConnectedPair() {
35
+ const adapter1: DummyNetworkAdapter = new DummyNetworkAdapter({
36
+ startReady: true,
37
+ sendMessage: (message: Message) => pause(10).then(() => adapter2.receive(message)),
38
+ });
39
+ const adapter2: DummyNetworkAdapter = new DummyNetworkAdapter({
40
+ startReady: true,
41
+ sendMessage: (message: Message) => pause(10).then(() => adapter1.receive(message)),
42
+ });
43
+
44
+ return [adapter1, adapter2];
45
+ }
17
46
  }
18
47
 
48
+ type SendMessageFn = (message: Message) => void;
49
+
19
50
  type Options = {
20
- startReady?: boolean
51
+ startReady?: boolean;
52
+ sendMessage?: SendMessageFn;
21
53
  }
@@ -0,0 +1,19 @@
1
+ import { EventEmitter } from "eventemitter3"
2
+ import { pause } from "../../src/helpers/pause.js"
3
+
4
+ export async function collectMessages({
5
+ emitter,
6
+ event,
7
+ until = pause(100),
8
+ }: {
9
+ emitter: EventEmitter
10
+ event: string
11
+ until?: Promise<unknown>
12
+ }): Promise<any[]> {
13
+ const messages = []
14
+ const listener = (message: unknown) => messages.push(message)
15
+ emitter.on(event, listener)
16
+ await until
17
+ emitter.off(event)
18
+ return messages
19
+ }