@automerge/automerge-repo 1.1.0-alpha.6 → 1.1.0
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/README.md +12 -7
- package/dist/AutomergeUrl.js +2 -2
- package/dist/DocHandle.d.ts +10 -4
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +17 -8
- package/dist/RemoteHeadsSubscriptions.js +3 -3
- package/dist/Repo.d.ts +23 -6
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +104 -71
- package/dist/helpers/debounce.js +1 -1
- package/dist/helpers/pause.d.ts +0 -1
- package/dist/helpers/pause.d.ts.map +1 -1
- package/dist/helpers/pause.js +2 -8
- package/dist/helpers/throttle.js +1 -1
- package/dist/helpers/withTimeout.d.ts.map +1 -1
- package/dist/helpers/withTimeout.js +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/network/NetworkAdapter.d.ts.map +1 -1
- package/dist/network/NetworkAdapter.js +2 -1
- package/dist/network/NetworkSubsystem.d.ts.map +1 -1
- package/dist/network/NetworkSubsystem.js +5 -3
- package/dist/network/messages.d.ts +43 -38
- package/dist/network/messages.d.ts.map +1 -1
- package/dist/network/messages.js +7 -9
- package/dist/storage/StorageSubsystem.js +1 -1
- package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.js +1 -0
- package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.js +13 -5
- package/dist/synchronizer/Synchronizer.d.ts +11 -3
- package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/AutomergeUrl.ts +2 -2
- package/src/DocHandle.ts +34 -12
- package/src/RemoteHeadsSubscriptions.ts +3 -3
- package/src/Repo.ts +130 -81
- package/src/helpers/debounce.ts +1 -1
- package/src/helpers/pause.ts +3 -11
- package/src/helpers/throttle.ts +1 -1
- package/src/helpers/withTimeout.ts +2 -0
- package/src/index.ts +1 -1
- package/src/network/NetworkAdapter.ts +5 -3
- package/src/network/NetworkSubsystem.ts +5 -4
- package/src/network/messages.ts +60 -63
- package/src/storage/StorageSubsystem.ts +1 -1
- package/src/synchronizer/CollectionSynchronizer.ts +2 -1
- package/src/synchronizer/DocSynchronizer.ts +19 -11
- package/src/synchronizer/Synchronizer.ts +11 -3
- package/test/CollectionSynchronizer.test.ts +7 -5
- package/test/DocHandle.test.ts +11 -2
- package/test/RemoteHeadsSubscriptions.test.ts +53 -50
- package/test/Repo.test.ts +64 -2
- package/test/StorageSubsystem.test.ts +1 -1
- package/test/helpers/collectMessages.ts +19 -0
- package/test/remoteHeads.test.ts +141 -120
- package/.eslintrc +0 -28
- package/test/helpers/waitForMessages.ts +0 -22
package/src/network/messages.ts
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import { SyncState } from "@automerge/automerge"
|
|
2
|
-
import { DocumentId, PeerId, SessionId } from "../types.js"
|
|
3
2
|
import { StorageId } from "../storage/types.js"
|
|
3
|
+
import { DocumentId, PeerId, SessionId } from "../types.js"
|
|
4
|
+
|
|
5
|
+
export type Message = {
|
|
6
|
+
type: string
|
|
7
|
+
|
|
8
|
+
/** The peer ID of the sender of this message */
|
|
9
|
+
senderId: PeerId
|
|
10
|
+
|
|
11
|
+
/** The peer ID of the recipient of this message */
|
|
12
|
+
targetId: PeerId
|
|
13
|
+
|
|
14
|
+
data?: Uint8Array
|
|
15
|
+
|
|
16
|
+
documentId?: DocumentId
|
|
17
|
+
}
|
|
4
18
|
|
|
5
19
|
/**
|
|
6
20
|
* A sync message for a particular document
|
|
7
21
|
*/
|
|
8
22
|
export type SyncMessage = {
|
|
9
23
|
type: "sync"
|
|
10
|
-
|
|
11
|
-
/** The peer ID of the sender of this message */
|
|
12
24
|
senderId: PeerId
|
|
13
|
-
|
|
14
|
-
/** The peer ID of the recipient of this message */
|
|
15
25
|
targetId: PeerId
|
|
16
26
|
|
|
17
27
|
/** The automerge sync message */
|
|
@@ -21,53 +31,50 @@ export type SyncMessage = {
|
|
|
21
31
|
documentId: DocumentId
|
|
22
32
|
}
|
|
23
33
|
|
|
24
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* An ephemeral message.
|
|
25
36
|
*
|
|
26
37
|
* @remarks
|
|
27
|
-
* Ephemeral messages are not persisted anywhere
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* number
|
|
32
|
-
* we have already seen.
|
|
38
|
+
* Ephemeral messages are not persisted anywhere. The data property can be used by the application
|
|
39
|
+
* as needed. The repo gossips these around.
|
|
40
|
+
*
|
|
41
|
+
* In order to avoid infinite loops of ephemeral messages, every message has (a) a session ID, which
|
|
42
|
+
* is a random number generated by the sender at startup time; and (b) a sequence number. The
|
|
43
|
+
* combination of these two things allows us to discard messages we have already seen.
|
|
33
44
|
* */
|
|
34
45
|
export type EphemeralMessage = {
|
|
35
46
|
type: "ephemeral"
|
|
36
|
-
|
|
37
|
-
/** The peer ID of the sender of this message */
|
|
38
47
|
senderId: PeerId
|
|
39
|
-
|
|
40
|
-
/** The peer ID of the recipient of this message */
|
|
41
48
|
targetId: PeerId
|
|
42
49
|
|
|
43
|
-
/** A sequence number which must be incremented for each message sent by this peer */
|
|
50
|
+
/** A sequence number which must be incremented for each message sent by this peer. */
|
|
44
51
|
count: number
|
|
45
52
|
|
|
46
|
-
/** The ID of the session this message is part of. The sequence number for a given session always increases */
|
|
53
|
+
/** The ID of the session this message is part of. The sequence number for a given session always increases. */
|
|
47
54
|
sessionId: SessionId
|
|
48
55
|
|
|
49
|
-
/** The document ID this message pertains to */
|
|
56
|
+
/** The document ID this message pertains to. */
|
|
50
57
|
documentId: DocumentId
|
|
51
58
|
|
|
52
|
-
/** The actual data of the message */
|
|
59
|
+
/** The actual data of the message. */
|
|
53
60
|
data: Uint8Array
|
|
54
61
|
}
|
|
55
62
|
|
|
56
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* Sent by a {@link Repo} to indicate that it does not have the document and none of its connected
|
|
65
|
+
* peers do either.
|
|
66
|
+
*/
|
|
57
67
|
export type DocumentUnavailableMessage = {
|
|
58
68
|
type: "doc-unavailable"
|
|
59
|
-
|
|
60
|
-
/** The peer ID of the sender of this message */
|
|
61
69
|
senderId: PeerId
|
|
62
|
-
|
|
63
|
-
/** The peer ID of the recipient of this message */
|
|
64
70
|
targetId: PeerId
|
|
65
71
|
|
|
66
72
|
/** The document which the peer claims it doesn't have */
|
|
67
73
|
documentId: DocumentId
|
|
68
74
|
}
|
|
69
75
|
|
|
70
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* Sent by a {@link Repo} to request a document from a peer.
|
|
71
78
|
*
|
|
72
79
|
* @remarks
|
|
73
80
|
* This is identical to a {@link SyncMessage} except that it is sent by a {@link Repo}
|
|
@@ -75,47 +82,43 @@ export type DocumentUnavailableMessage = {
|
|
|
75
82
|
* */
|
|
76
83
|
export type RequestMessage = {
|
|
77
84
|
type: "request"
|
|
78
|
-
|
|
79
|
-
/** The peer ID of the sender of this message */
|
|
80
85
|
senderId: PeerId
|
|
81
|
-
|
|
82
|
-
/** The peer ID of the recipient of this message */
|
|
83
86
|
targetId: PeerId
|
|
84
87
|
|
|
85
|
-
/** The
|
|
88
|
+
/** The automerge sync message */
|
|
86
89
|
data: Uint8Array
|
|
87
90
|
|
|
88
|
-
/** The document ID this message
|
|
91
|
+
/** The document ID of the document this message is for */
|
|
89
92
|
documentId: DocumentId
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
/**
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
/** The peer ID of the sender of this message */
|
|
97
|
-
senderId: PeerId
|
|
98
|
-
|
|
99
|
-
/** The peer ID of the recipient of this message */
|
|
100
|
-
targetId: PeerId
|
|
101
|
-
|
|
102
|
-
/** The payload of the auth message (up to the specific auth provider) */
|
|
103
|
-
payload: TPayload
|
|
104
|
-
}
|
|
105
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Sent by a {@link Repo} to add or remove storage IDs from a remote peer's subscription.
|
|
97
|
+
*/
|
|
106
98
|
export type RemoteSubscriptionControlMessage = {
|
|
107
99
|
type: "remote-subscription-change"
|
|
108
100
|
senderId: PeerId
|
|
109
101
|
targetId: PeerId
|
|
102
|
+
|
|
103
|
+
/** The storage IDs to add to the subscription */
|
|
110
104
|
add?: StorageId[]
|
|
105
|
+
|
|
106
|
+
/** The storage IDs to remove from the subscription */
|
|
111
107
|
remove?: StorageId[]
|
|
112
108
|
}
|
|
113
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Sent by a {@link Repo} to indicate that the heads of a document have changed on a remote peer.
|
|
112
|
+
*/
|
|
114
113
|
export type RemoteHeadsChanged = {
|
|
115
114
|
type: "remote-heads-changed"
|
|
116
115
|
senderId: PeerId
|
|
117
116
|
targetId: PeerId
|
|
117
|
+
|
|
118
|
+
/** The document ID of the document that has changed */
|
|
118
119
|
documentId: DocumentId
|
|
120
|
+
|
|
121
|
+
/** The document's new heads */
|
|
119
122
|
newHeads: { [key: StorageId]: { heads: string[]; timestamp: number } }
|
|
120
123
|
}
|
|
121
124
|
|
|
@@ -128,19 +131,17 @@ export type RepoMessage =
|
|
|
128
131
|
| RemoteSubscriptionControlMessage
|
|
129
132
|
| RemoteHeadsChanged
|
|
130
133
|
|
|
134
|
+
/** These are message types that are handled by the {@link CollectionSynchronizer}.*/
|
|
131
135
|
export type DocMessage =
|
|
132
136
|
| SyncMessage
|
|
133
137
|
| EphemeralMessage
|
|
134
138
|
| RequestMessage
|
|
135
139
|
| DocumentUnavailableMessage
|
|
136
140
|
|
|
137
|
-
/** These are all the message types that a {@link NetworkAdapter} might see. */
|
|
138
|
-
export type Message = RepoMessage | AuthMessage
|
|
139
|
-
|
|
140
141
|
/**
|
|
141
142
|
* The contents of a message, without the sender ID or other properties added by the {@link NetworkSubsystem})
|
|
142
143
|
*/
|
|
143
|
-
export type MessageContents<T extends Message =
|
|
144
|
+
export type MessageContents<T extends Message = RepoMessage> =
|
|
144
145
|
T extends EphemeralMessage
|
|
145
146
|
? Omit<T, "senderId" | "count" | "sessionId">
|
|
146
147
|
: Omit<T, "senderId">
|
|
@@ -160,16 +161,13 @@ export interface OpenDocMessage {
|
|
|
160
161
|
|
|
161
162
|
// TYPE GUARDS
|
|
162
163
|
|
|
163
|
-
export const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
isDocumentUnavailableMessage(message) ||
|
|
171
|
-
isRemoteSubscriptionControlMessage(message) ||
|
|
172
|
-
isRemoteHeadsChanged(message))
|
|
164
|
+
export const isRepoMessage = (message: Message): message is RepoMessage =>
|
|
165
|
+
isSyncMessage(message) ||
|
|
166
|
+
isEphemeralMessage(message) ||
|
|
167
|
+
isRequestMessage(message) ||
|
|
168
|
+
isDocumentUnavailableMessage(message) ||
|
|
169
|
+
isRemoteSubscriptionControlMessage(message) ||
|
|
170
|
+
isRemoteHeadsChanged(message)
|
|
173
171
|
|
|
174
172
|
// prettier-ignore
|
|
175
173
|
export const isDocumentUnavailableMessage = (msg: Message): msg is DocumentUnavailableMessage =>
|
|
@@ -184,9 +182,8 @@ export const isSyncMessage = (msg: Message): msg is SyncMessage =>
|
|
|
184
182
|
export const isEphemeralMessage = (msg: Message): msg is EphemeralMessage =>
|
|
185
183
|
msg.type === "ephemeral"
|
|
186
184
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
): msg is RemoteSubscriptionControlMessage =>
|
|
185
|
+
// prettier-ignore
|
|
186
|
+
export const isRemoteSubscriptionControlMessage = (msg: Message): msg is RemoteSubscriptionControlMessage =>
|
|
190
187
|
msg.type === "remote-subscription-change"
|
|
191
188
|
|
|
192
189
|
export const isRemoteHeadsChanged = (msg: Message): msg is RemoteHeadsChanged =>
|
|
@@ -33,7 +33,7 @@ export class StorageSubsystem {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
async id(): Promise<StorageId> {
|
|
36
|
-
|
|
36
|
+
const storedId = await this.#storageAdapter.load(["storage-adapter-id"])
|
|
37
37
|
|
|
38
38
|
let id: StorageId
|
|
39
39
|
if (storedId) {
|
|
@@ -2,7 +2,7 @@ import debug from "debug"
|
|
|
2
2
|
import { DocHandle } from "../DocHandle.js"
|
|
3
3
|
import { stringifyAutomergeUrl } from "../AutomergeUrl.js"
|
|
4
4
|
import { Repo } from "../Repo.js"
|
|
5
|
-
import { DocMessage
|
|
5
|
+
import { DocMessage } from "../network/messages.js"
|
|
6
6
|
import { DocumentId, PeerId } from "../types.js"
|
|
7
7
|
import { DocSynchronizer } from "./DocSynchronizer.js"
|
|
8
8
|
import { Synchronizer } from "./Synchronizer.js"
|
|
@@ -117,6 +117,7 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
// TODO: implement this
|
|
120
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
120
121
|
removeDocument(documentId: DocumentId) {
|
|
121
122
|
throw new Error("not implemented")
|
|
122
123
|
}
|
|
@@ -137,9 +137,13 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
137
137
|
|
|
138
138
|
let pendingCallbacks = this.#pendingSyncStateCallbacks[peerId]
|
|
139
139
|
if (!pendingCallbacks) {
|
|
140
|
-
this.#onLoadSyncState(peerId)
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
this.#onLoadSyncState(peerId)
|
|
141
|
+
.then(syncState => {
|
|
142
|
+
this.#initSyncState(peerId, syncState ?? A.initSyncState())
|
|
143
|
+
})
|
|
144
|
+
.catch(err => {
|
|
145
|
+
this.#log(`Error loading sync state for ${peerId}: ${err}`)
|
|
146
|
+
})
|
|
143
147
|
pendingCallbacks = this.#pendingSyncStateCallbacks[peerId] = []
|
|
144
148
|
}
|
|
145
149
|
|
|
@@ -260,11 +264,15 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
260
264
|
)
|
|
261
265
|
this.#setSyncState(peerId, reparsedSyncState)
|
|
262
266
|
|
|
263
|
-
docPromise
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
267
|
+
docPromise
|
|
268
|
+
.then(doc => {
|
|
269
|
+
if (doc) {
|
|
270
|
+
this.#sendSyncMessage(peerId, doc)
|
|
271
|
+
}
|
|
272
|
+
})
|
|
273
|
+
.catch(err => {
|
|
274
|
+
this.#log(`Error loading doc for ${peerId}: ${err}`)
|
|
275
|
+
})
|
|
268
276
|
})
|
|
269
277
|
})
|
|
270
278
|
}
|
|
@@ -326,10 +334,10 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
326
334
|
}
|
|
327
335
|
|
|
328
336
|
this.#processAllPendingSyncMessages()
|
|
329
|
-
this.#processSyncMessage(message
|
|
337
|
+
this.#processSyncMessage(message)
|
|
330
338
|
}
|
|
331
339
|
|
|
332
|
-
#processSyncMessage(message: SyncMessage | RequestMessage
|
|
340
|
+
#processSyncMessage(message: SyncMessage | RequestMessage) {
|
|
333
341
|
if (isRequestMessage(message)) {
|
|
334
342
|
this.#peerDocumentStatuses[message.senderId] = "wants"
|
|
335
343
|
}
|
|
@@ -388,7 +396,7 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
388
396
|
|
|
389
397
|
#processAllPendingSyncMessages() {
|
|
390
398
|
for (const message of this.#pendingSyncMessages) {
|
|
391
|
-
this.#processSyncMessage(message.message
|
|
399
|
+
this.#processSyncMessage(message.message)
|
|
392
400
|
}
|
|
393
401
|
|
|
394
402
|
this.#pendingSyncMessages = []
|
|
@@ -3,15 +3,23 @@ import {
|
|
|
3
3
|
MessageContents,
|
|
4
4
|
OpenDocMessage,
|
|
5
5
|
RepoMessage,
|
|
6
|
-
SyncStateMessage,
|
|
7
6
|
} from "../network/messages.js"
|
|
7
|
+
import { SyncState } from "@automerge/automerge"
|
|
8
|
+
import { PeerId, DocumentId } from "../types.js"
|
|
8
9
|
|
|
9
10
|
export abstract class Synchronizer extends EventEmitter<SynchronizerEvents> {
|
|
10
11
|
abstract receiveMessage(message: RepoMessage): void
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export interface SynchronizerEvents {
|
|
14
|
-
message: (
|
|
15
|
-
"sync-state": (
|
|
15
|
+
message: (payload: MessageContents) => void
|
|
16
|
+
"sync-state": (payload: SyncStatePayload) => void
|
|
16
17
|
"open-doc": (arg: OpenDocMessage) => void
|
|
17
18
|
}
|
|
19
|
+
|
|
20
|
+
/** Notify the repo that the sync state has changed */
|
|
21
|
+
export interface SyncStatePayload {
|
|
22
|
+
peerId: PeerId
|
|
23
|
+
documentId: DocumentId
|
|
24
|
+
syncState: SyncState
|
|
25
|
+
}
|
|
@@ -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
|
-
|
|
28
|
-
assert(
|
|
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
|
-
|
|
41
|
-
assert(
|
|
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)
|
package/test/DocHandle.test.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { describe, it } 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
|
|
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
|
|
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)
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
RemoteHeadsChanged,
|
|
9
9
|
RemoteSubscriptionControlMessage,
|
|
10
10
|
} from "../src/network/messages.js"
|
|
11
|
-
import {
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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)
|
|
@@ -287,17 +287,20 @@ describe("RepoHeadsSubscriptions", () => {
|
|
|
287
287
|
assert.strictEqual(messages.length, 0)
|
|
288
288
|
})
|
|
289
289
|
|
|
290
|
-
it("should
|
|
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 =
|
|
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)
|
|
300
300
|
|
|
301
|
+
// send same message
|
|
302
|
+
remoteHeadsSubscription.handleRemoteHeads(docBHeadsChangedForStorageB2)
|
|
303
|
+
|
|
301
304
|
// send message with old heads
|
|
302
305
|
remoteHeadsSubscription.handleRemoteHeads(docBHeadsChangedForStorageB)
|
|
303
306
|
|
|
@@ -311,10 +314,10 @@ describe("RepoHeadsSubscriptions", () => {
|
|
|
311
314
|
it("should remove subs of disconnected peers", async () => {
|
|
312
315
|
const remoteHeadsSubscriptions = new RemoteHeadsSubscriptions()
|
|
313
316
|
|
|
314
|
-
const messagesPromise =
|
|
315
|
-
remoteHeadsSubscriptions,
|
|
316
|
-
"change-remote-subs"
|
|
317
|
-
)
|
|
317
|
+
const messagesPromise = collectMessages({
|
|
318
|
+
emitter: remoteHeadsSubscriptions,
|
|
319
|
+
event: "change-remote-subs",
|
|
320
|
+
})
|
|
318
321
|
|
|
319
322
|
remoteHeadsSubscriptions.handleControlMessage({
|
|
320
323
|
type: "remote-subscription-change",
|