@automerge/automerge-repo 1.0.0-alpha.2 → 1.0.0-alpha.4
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/dist/DocCollection.d.ts +4 -2
- package/dist/DocCollection.d.ts.map +1 -1
- package/dist/DocCollection.js +20 -11
- package/dist/DocHandle.d.ts +34 -6
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +69 -9
- package/dist/DocUrl.d.ts +4 -4
- package/dist/DocUrl.d.ts.map +1 -1
- package/dist/DocUrl.js +9 -9
- package/dist/EphemeralData.d.ts +8 -16
- package/dist/EphemeralData.d.ts.map +1 -1
- package/dist/EphemeralData.js +1 -28
- package/dist/Repo.d.ts +0 -2
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +37 -39
- package/dist/helpers/cbor.d.ts +4 -0
- package/dist/helpers/cbor.d.ts.map +1 -0
- package/dist/helpers/cbor.js +8 -0
- package/dist/helpers/eventPromise.d.ts +1 -1
- package/dist/helpers/eventPromise.d.ts.map +1 -1
- package/dist/helpers/headsAreSame.d.ts +0 -1
- package/dist/helpers/headsAreSame.d.ts.map +1 -1
- package/dist/helpers/tests/network-adapter-tests.d.ts.map +1 -1
- package/dist/helpers/tests/network-adapter-tests.js +15 -13
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/network/NetworkAdapter.d.ts +6 -15
- package/dist/network/NetworkAdapter.d.ts.map +1 -1
- package/dist/network/NetworkAdapter.js +1 -1
- package/dist/network/NetworkSubsystem.d.ts +9 -6
- package/dist/network/NetworkSubsystem.d.ts.map +1 -1
- package/dist/network/NetworkSubsystem.js +69 -32
- package/dist/network/messages.d.ts +57 -0
- package/dist/network/messages.d.ts.map +1 -0
- package/dist/network/messages.js +21 -0
- package/dist/storage/StorageSubsystem.d.ts +1 -1
- package/dist/storage/StorageSubsystem.d.ts.map +1 -1
- package/dist/storage/StorageSubsystem.js +2 -2
- package/dist/synchronizer/CollectionSynchronizer.d.ts +3 -2
- package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.js +19 -13
- package/dist/synchronizer/DocSynchronizer.d.ts +9 -3
- package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.js +149 -34
- package/dist/synchronizer/Synchronizer.d.ts +4 -5
- package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
- package/dist/synchronizer/Synchronizer.js +1 -1
- package/dist/types.d.ts +1 -3
- package/dist/types.d.ts.map +1 -1
- package/fuzz/fuzz.ts +5 -5
- package/package.json +3 -3
- package/src/DocCollection.ts +23 -12
- package/src/DocHandle.ts +120 -13
- package/src/DocUrl.ts +10 -10
- package/src/EphemeralData.ts +6 -36
- package/src/Repo.ts +37 -55
- package/src/helpers/cbor.ts +10 -0
- package/src/helpers/eventPromise.ts +1 -1
- package/src/helpers/headsAreSame.ts +1 -1
- package/src/helpers/tests/network-adapter-tests.ts +18 -14
- package/src/index.ts +14 -2
- package/src/network/NetworkAdapter.ts +6 -22
- package/src/network/NetworkSubsystem.ts +94 -44
- package/src/network/messages.ts +123 -0
- package/src/storage/StorageSubsystem.ts +2 -2
- package/src/synchronizer/CollectionSynchronizer.ts +38 -19
- package/src/synchronizer/DocSynchronizer.ts +201 -43
- package/src/synchronizer/Synchronizer.ts +4 -9
- package/src/types.ts +4 -1
- package/test/CollectionSynchronizer.test.ts +6 -7
- package/test/DocCollection.test.ts +2 -2
- package/test/DocHandle.test.ts +32 -17
- package/test/DocSynchronizer.test.ts +85 -9
- package/test/Repo.test.ts +267 -63
- package/test/StorageSubsystem.test.ts +4 -5
- package/test/helpers/DummyNetworkAdapter.ts +12 -3
- package/test/helpers/DummyStorageAdapter.ts +1 -1
- package/tsconfig.json +4 -3
- package/test/EphemeralData.test.ts +0 -44
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
import EventEmitter from "eventemitter3"
|
|
2
|
-
import { PeerId
|
|
1
|
+
import { EventEmitter } from "eventemitter3"
|
|
2
|
+
import { PeerId } from "../types.js"
|
|
3
|
+
import { Message } from "./messages.js"
|
|
3
4
|
|
|
4
5
|
export abstract class NetworkAdapter extends EventEmitter<NetworkAdapterEvents> {
|
|
5
6
|
peerId?: PeerId // hmmm, maybe not
|
|
6
7
|
|
|
7
8
|
abstract connect(url?: string): void
|
|
8
9
|
|
|
9
|
-
abstract
|
|
10
|
-
peerId: PeerId,
|
|
11
|
-
channelId: ChannelId,
|
|
12
|
-
message: Uint8Array,
|
|
13
|
-
broadcast: boolean
|
|
14
|
-
): void
|
|
10
|
+
abstract send(message: Message): void
|
|
15
11
|
|
|
16
12
|
abstract join(): void
|
|
17
13
|
|
|
@@ -21,11 +17,11 @@ export abstract class NetworkAdapter extends EventEmitter<NetworkAdapterEvents>
|
|
|
21
17
|
// events & payloads
|
|
22
18
|
|
|
23
19
|
export interface NetworkAdapterEvents {
|
|
24
|
-
|
|
20
|
+
ready: (payload: OpenPayload) => void
|
|
25
21
|
close: () => void
|
|
26
22
|
"peer-candidate": (payload: PeerCandidatePayload) => void
|
|
27
23
|
"peer-disconnected": (payload: PeerDisconnectedPayload) => void
|
|
28
|
-
message: (payload:
|
|
24
|
+
message: (payload: Message) => void
|
|
29
25
|
}
|
|
30
26
|
|
|
31
27
|
export interface OpenPayload {
|
|
@@ -36,18 +32,6 @@ export interface PeerCandidatePayload {
|
|
|
36
32
|
peerId: PeerId
|
|
37
33
|
}
|
|
38
34
|
|
|
39
|
-
export interface MessagePayload {
|
|
40
|
-
targetId: PeerId
|
|
41
|
-
channelId: ChannelId
|
|
42
|
-
message: Uint8Array
|
|
43
|
-
broadcast: boolean
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface InboundMessagePayload extends MessagePayload {
|
|
47
|
-
type?: string
|
|
48
|
-
senderId: PeerId
|
|
49
|
-
}
|
|
50
|
-
|
|
51
35
|
export interface PeerDisconnectedPayload {
|
|
52
36
|
peerId: PeerId
|
|
53
37
|
}
|
|
@@ -1,28 +1,51 @@
|
|
|
1
|
-
import EventEmitter from "eventemitter3"
|
|
1
|
+
import { EventEmitter } from "eventemitter3"
|
|
2
|
+
import { PeerId } from "../types.js"
|
|
3
|
+
import { NetworkAdapter, PeerDisconnectedPayload } from "./NetworkAdapter.js"
|
|
4
|
+
|
|
2
5
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
EphemeralMessage,
|
|
7
|
+
isEphemeralMessage,
|
|
8
|
+
isValidMessage,
|
|
9
|
+
Message,
|
|
10
|
+
MessageContents,
|
|
11
|
+
} from "./messages.js"
|
|
8
12
|
|
|
9
13
|
import debug from "debug"
|
|
14
|
+
import { SessionId } from "../EphemeralData.js"
|
|
15
|
+
|
|
16
|
+
type EphemeralMessageSource = `${PeerId}:${SessionId}`
|
|
17
|
+
|
|
18
|
+
const getEphemeralMessageSource = (message: EphemeralMessage) =>
|
|
19
|
+
`${message.senderId}:${message.sessionId}` as EphemeralMessageSource
|
|
10
20
|
|
|
11
21
|
export class NetworkSubsystem extends EventEmitter<NetworkSubsystemEvents> {
|
|
12
22
|
#log: debug.Debugger
|
|
13
23
|
#adaptersByPeer: Record<PeerId, NetworkAdapter> = {}
|
|
14
24
|
|
|
25
|
+
#count = 0
|
|
26
|
+
#sessionId: SessionId = Math.random().toString(36).slice(2) as SessionId
|
|
27
|
+
#ephemeralSessionCounts: Record<EphemeralMessageSource, number> = {}
|
|
28
|
+
#readyAdapterCount = 0
|
|
29
|
+
#adapters: NetworkAdapter[] = []
|
|
30
|
+
|
|
15
31
|
constructor(
|
|
16
|
-
|
|
32
|
+
adapters: NetworkAdapter[],
|
|
17
33
|
public peerId = randomPeerId()
|
|
18
34
|
) {
|
|
19
35
|
super()
|
|
20
36
|
this.#log = debug(`automerge-repo:network:${this.peerId}`)
|
|
21
|
-
|
|
37
|
+
adapters.forEach(a => this.addNetworkAdapter(a))
|
|
22
38
|
}
|
|
23
39
|
|
|
24
40
|
addNetworkAdapter(networkAdapter: NetworkAdapter) {
|
|
25
|
-
|
|
41
|
+
this.#adapters.push(networkAdapter)
|
|
42
|
+
networkAdapter.once("ready", () => {
|
|
43
|
+
this.#readyAdapterCount++
|
|
44
|
+
this.#log("Adapters ready: ", this.#readyAdapterCount, "/", this.#adapters.length)
|
|
45
|
+
if (this.#readyAdapterCount === this.#adapters.length) {
|
|
46
|
+
this.emit("ready")
|
|
47
|
+
}
|
|
48
|
+
})
|
|
26
49
|
|
|
27
50
|
networkAdapter.on("peer-candidate", ({ peerId }) => {
|
|
28
51
|
this.#log(`peer candidate: ${peerId} `)
|
|
@@ -44,20 +67,24 @@ export class NetworkSubsystem extends EventEmitter<NetworkSubsystemEvents> {
|
|
|
44
67
|
})
|
|
45
68
|
|
|
46
69
|
networkAdapter.on("message", msg => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
.
|
|
59
|
-
|
|
60
|
-
|
|
70
|
+
if (!isValidMessage(msg)) {
|
|
71
|
+
this.#log(`invalid message: ${JSON.stringify(msg)}`)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.#log(`message from ${msg.senderId}`)
|
|
76
|
+
|
|
77
|
+
if (isEphemeralMessage(msg)) {
|
|
78
|
+
const source = getEphemeralMessageSource(msg)
|
|
79
|
+
if (
|
|
80
|
+
this.#ephemeralSessionCounts[source] === undefined ||
|
|
81
|
+
msg.count > this.#ephemeralSessionCounts[source]
|
|
82
|
+
) {
|
|
83
|
+
this.#ephemeralSessionCounts[source] = msg.count
|
|
84
|
+
this.emit("message", msg)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return
|
|
61
88
|
}
|
|
62
89
|
|
|
63
90
|
this.emit("message", msg)
|
|
@@ -72,39 +99,61 @@ export class NetworkSubsystem extends EventEmitter<NetworkSubsystemEvents> {
|
|
|
72
99
|
})
|
|
73
100
|
})
|
|
74
101
|
|
|
102
|
+
networkAdapter.connect(this.peerId)
|
|
75
103
|
networkAdapter.join()
|
|
76
104
|
}
|
|
77
105
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
106
|
+
send(message: MessageContents) {
|
|
107
|
+
const peer = this.#adaptersByPeer[message.targetId]
|
|
108
|
+
if (!peer) {
|
|
109
|
+
this.#log(`Tried to send message but peer not found: ${message.targetId}`)
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
this.#log(`Sending message to ${message.targetId}`)
|
|
113
|
+
|
|
114
|
+
if (isEphemeralMessage(message)) {
|
|
115
|
+
const outbound =
|
|
116
|
+
"count" in message
|
|
117
|
+
? message
|
|
118
|
+
: {
|
|
119
|
+
...message,
|
|
120
|
+
count: ++this.#count,
|
|
121
|
+
sessionId: this.#sessionId,
|
|
122
|
+
senderId: this.peerId,
|
|
123
|
+
}
|
|
124
|
+
this.#log("Ephemeral message", outbound)
|
|
125
|
+
peer.send(outbound)
|
|
89
126
|
} else {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return
|
|
94
|
-
}
|
|
95
|
-
this.#log(`Sending message to ${peerId}`)
|
|
96
|
-
peer.sendMessage(peerId, channelId, message, false)
|
|
127
|
+
const outbound = { ...message, senderId: this.peerId }
|
|
128
|
+
this.#log("Sync message", outbound)
|
|
129
|
+
peer.send(outbound)
|
|
97
130
|
}
|
|
98
131
|
}
|
|
99
132
|
|
|
100
133
|
join() {
|
|
101
134
|
this.#log(`Joining network`)
|
|
102
|
-
this
|
|
135
|
+
this.#adapters.forEach(a => a.join())
|
|
103
136
|
}
|
|
104
137
|
|
|
105
138
|
leave() {
|
|
106
139
|
this.#log(`Leaving network`)
|
|
107
|
-
this
|
|
140
|
+
this.#adapters.forEach(a => a.leave())
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
isReady = () => {
|
|
144
|
+
return this.#readyAdapterCount === this.#adapters.length
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
whenReady = async () => {
|
|
148
|
+
if (this.isReady()) {
|
|
149
|
+
return
|
|
150
|
+
} else {
|
|
151
|
+
return new Promise<void>(resolve => {
|
|
152
|
+
this.once("ready", () => {
|
|
153
|
+
resolve()
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
}
|
|
108
157
|
}
|
|
109
158
|
}
|
|
110
159
|
|
|
@@ -117,7 +166,8 @@ function randomPeerId() {
|
|
|
117
166
|
export interface NetworkSubsystemEvents {
|
|
118
167
|
peer: (payload: PeerPayload) => void
|
|
119
168
|
"peer-disconnected": (payload: PeerDisconnectedPayload) => void
|
|
120
|
-
message: (payload:
|
|
169
|
+
message: (payload: Message) => void
|
|
170
|
+
"ready": () => void
|
|
121
171
|
}
|
|
122
172
|
|
|
123
173
|
export interface PeerPayload {
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// utilities
|
|
2
|
+
import { SessionId } from "../EphemeralData.js"
|
|
3
|
+
import { DocumentId, PeerId } from "../types.js"
|
|
4
|
+
|
|
5
|
+
export function isValidMessage(
|
|
6
|
+
message: NetworkAdapterMessage
|
|
7
|
+
): message is
|
|
8
|
+
| SyncMessage
|
|
9
|
+
| EphemeralMessage
|
|
10
|
+
| RequestMessage
|
|
11
|
+
| DocumentUnavailableMessage {
|
|
12
|
+
return (
|
|
13
|
+
typeof message === "object" &&
|
|
14
|
+
typeof message.type === "string" &&
|
|
15
|
+
typeof message.senderId === "string" &&
|
|
16
|
+
(isSyncMessage(message) ||
|
|
17
|
+
isEphemeralMessage(message) ||
|
|
18
|
+
isRequestMessage(message) ||
|
|
19
|
+
isDocumentUnavailableMessage(message))
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function isDocumentUnavailableMessage(
|
|
24
|
+
message: NetworkAdapterMessage
|
|
25
|
+
): message is DocumentUnavailableMessage {
|
|
26
|
+
return message.type === "doc-unavailable"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function isRequestMessage(
|
|
30
|
+
message: NetworkAdapterMessage
|
|
31
|
+
): message is RequestMessage {
|
|
32
|
+
return message.type === "request"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function isSyncMessage(
|
|
36
|
+
message: NetworkAdapterMessage
|
|
37
|
+
): message is SyncMessage {
|
|
38
|
+
return message.type === "sync"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function isEphemeralMessage(
|
|
42
|
+
message: NetworkAdapterMessage | MessageContents
|
|
43
|
+
): message is EphemeralMessage | EphemeralMessageContents {
|
|
44
|
+
return message.type === "ephemeral"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface SyncMessageEnvelope {
|
|
48
|
+
senderId: PeerId
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface SyncMessageContents {
|
|
52
|
+
type: "sync"
|
|
53
|
+
data: Uint8Array
|
|
54
|
+
targetId: PeerId
|
|
55
|
+
documentId: DocumentId
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type SyncMessage = SyncMessageEnvelope & SyncMessageContents
|
|
59
|
+
|
|
60
|
+
export interface EphemeralMessageEnvelope {
|
|
61
|
+
senderId: PeerId
|
|
62
|
+
count: number
|
|
63
|
+
sessionId: SessionId
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface EphemeralMessageContents {
|
|
67
|
+
type: "ephemeral"
|
|
68
|
+
targetId: PeerId
|
|
69
|
+
documentId: DocumentId
|
|
70
|
+
data: Uint8Array
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export type EphemeralMessage = EphemeralMessageEnvelope &
|
|
74
|
+
EphemeralMessageContents
|
|
75
|
+
|
|
76
|
+
export interface DocumentUnavailableMessageContents {
|
|
77
|
+
type: "doc-unavailable"
|
|
78
|
+
documentId: DocumentId
|
|
79
|
+
targetId: PeerId
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export type DocumentUnavailableMessage = SyncMessageEnvelope &
|
|
83
|
+
DocumentUnavailableMessageContents
|
|
84
|
+
|
|
85
|
+
export interface RequestMessageContents {
|
|
86
|
+
type: "request"
|
|
87
|
+
data: Uint8Array
|
|
88
|
+
targetId: PeerId
|
|
89
|
+
documentId: DocumentId
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type RequestMessage = SyncMessageEnvelope & RequestMessageContents
|
|
93
|
+
|
|
94
|
+
export type MessageContents =
|
|
95
|
+
| SyncMessageContents
|
|
96
|
+
| EphemeralMessageContents
|
|
97
|
+
| RequestMessageContents
|
|
98
|
+
| DocumentUnavailableMessageContents
|
|
99
|
+
|
|
100
|
+
export type Message =
|
|
101
|
+
| SyncMessage
|
|
102
|
+
| EphemeralMessage
|
|
103
|
+
| RequestMessage
|
|
104
|
+
| DocumentUnavailableMessage
|
|
105
|
+
|
|
106
|
+
export type SynchronizerMessage =
|
|
107
|
+
| SyncMessage
|
|
108
|
+
| RequestMessage
|
|
109
|
+
| DocumentUnavailableMessage
|
|
110
|
+
| EphemeralMessage
|
|
111
|
+
|
|
112
|
+
type ArriveMessage = {
|
|
113
|
+
senderId: PeerId
|
|
114
|
+
type: "arrive"
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
type WelcomeMessage = {
|
|
118
|
+
senderId: PeerId
|
|
119
|
+
targetId: PeerId
|
|
120
|
+
type: "welcome"
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export type NetworkAdapterMessage = ArriveMessage | WelcomeMessage | Message
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as A from "@automerge/automerge"
|
|
1
|
+
import * as A from "@automerge/automerge/next"
|
|
2
2
|
import { StorageAdapter, StorageKey } from "./StorageAdapter.js"
|
|
3
3
|
import * as sha256 from "fast-sha256"
|
|
4
4
|
import { type DocumentId } from "../types.js"
|
|
@@ -25,7 +25,7 @@ function keyHash(binary: Uint8Array) {
|
|
|
25
25
|
|
|
26
26
|
function headsHash(heads: A.Heads): string {
|
|
27
27
|
let encoder = new TextEncoder()
|
|
28
|
-
let headsbinary = mergeArrays(heads.map(h => encoder.encode(h)))
|
|
28
|
+
let headsbinary = mergeArrays(heads.map((h: string) => encoder.encode(h)))
|
|
29
29
|
return keyHash(headsbinary)
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -5,11 +5,17 @@ import {
|
|
|
5
5
|
binaryToDocumentId,
|
|
6
6
|
stringifyAutomergeUrl,
|
|
7
7
|
} from "../DocUrl.js"
|
|
8
|
-
import {
|
|
8
|
+
import { PeerId, DocumentId } from "../types.js"
|
|
9
9
|
import { DocSynchronizer } from "./DocSynchronizer.js"
|
|
10
10
|
import { Synchronizer } from "./Synchronizer.js"
|
|
11
11
|
|
|
12
12
|
import debug from "debug"
|
|
13
|
+
import {
|
|
14
|
+
DocumentUnavailableMessage,
|
|
15
|
+
RequestMessage,
|
|
16
|
+
SynchronizerMessage,
|
|
17
|
+
SyncMessage,
|
|
18
|
+
} from "../network/messages.js"
|
|
13
19
|
const log = debug("automerge-repo:collectionsync")
|
|
14
20
|
|
|
15
21
|
/** A CollectionSynchronizer is responsible for synchronizing a DocCollection with peers. */
|
|
@@ -20,6 +26,9 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
20
26
|
/** A map of documentIds to their synchronizers */
|
|
21
27
|
#docSynchronizers: Record<DocumentId, DocSynchronizer> = {}
|
|
22
28
|
|
|
29
|
+
/** Used to determine if the document is know to the Collection and a synchronizer exists or is being set up */
|
|
30
|
+
#docSetUp: Record<DocumentId, boolean> = {}
|
|
31
|
+
|
|
23
32
|
constructor(private repo: DocCollection) {
|
|
24
33
|
super()
|
|
25
34
|
}
|
|
@@ -57,37 +66,42 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
57
66
|
* When we receive a sync message for a document we haven't got in memory, we
|
|
58
67
|
* register it with the repo and start synchronizing
|
|
59
68
|
*/
|
|
60
|
-
async
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const documentId =
|
|
69
|
+
async receiveMessage(message: SynchronizerMessage) {
|
|
70
|
+
log(
|
|
71
|
+
`onSyncMessage: ${message.senderId}, ${message.documentId}, ${
|
|
72
|
+
"data" in message ? message.data.byteLength + "bytes" : ""
|
|
73
|
+
}`
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
const documentId = message.documentId
|
|
68
77
|
if (!documentId) {
|
|
69
78
|
throw new Error("received a message with an invalid documentId")
|
|
70
79
|
}
|
|
71
|
-
const docSynchronizer = await this.#fetchDocSynchronizer(documentId)
|
|
72
80
|
|
|
73
|
-
|
|
81
|
+
this.#docSetUp[documentId] = true
|
|
82
|
+
|
|
83
|
+
const docSynchronizer = this.#fetchDocSynchronizer(documentId)
|
|
84
|
+
|
|
85
|
+
docSynchronizer.receiveMessage(message)
|
|
74
86
|
|
|
75
87
|
// Initiate sync with any new peers
|
|
76
88
|
const peers = await this.#documentGenerousPeers(documentId)
|
|
77
|
-
|
|
78
|
-
.filter(peerId => !docSynchronizer.hasPeer(peerId))
|
|
79
|
-
|
|
89
|
+
docSynchronizer.beginSync(
|
|
90
|
+
peers.filter(peerId => !docSynchronizer.hasPeer(peerId))
|
|
91
|
+
)
|
|
80
92
|
}
|
|
81
93
|
|
|
82
94
|
/**
|
|
83
95
|
* Starts synchronizing the given document with all peers that we share it generously with.
|
|
84
96
|
*/
|
|
85
97
|
addDocument(documentId: DocumentId) {
|
|
98
|
+
// HACK: this is a hack to prevent us from adding the same document twice
|
|
99
|
+
if (this.#docSetUp[documentId]) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
86
102
|
const docSynchronizer = this.#fetchDocSynchronizer(documentId)
|
|
87
103
|
void this.#documentGenerousPeers(documentId).then(peers => {
|
|
88
|
-
|
|
89
|
-
docSynchronizer.beginSync(peerId)
|
|
90
|
-
})
|
|
104
|
+
docSynchronizer.beginSync(peers)
|
|
91
105
|
})
|
|
92
106
|
}
|
|
93
107
|
|
|
@@ -99,11 +113,16 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
99
113
|
/** Adds a peer and maybe starts synchronizing with them */
|
|
100
114
|
addPeer(peerId: PeerId) {
|
|
101
115
|
log(`adding ${peerId} & synchronizing with them`)
|
|
116
|
+
|
|
117
|
+
if (this.#peers.has(peerId)) {
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
102
121
|
this.#peers.add(peerId)
|
|
103
122
|
for (const docSynchronizer of Object.values(this.#docSynchronizers)) {
|
|
104
123
|
const { documentId } = docSynchronizer
|
|
105
|
-
|
|
106
|
-
if (okToShare) docSynchronizer.beginSync(peerId)
|
|
124
|
+
this.repo.sharePolicy(peerId, documentId).then(okToShare => {
|
|
125
|
+
if (okToShare) docSynchronizer.beginSync([peerId])
|
|
107
126
|
})
|
|
108
127
|
}
|
|
109
128
|
}
|