@automerge/automerge-repo 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +1 -1
- package/dist/DocHandle.d.ts +7 -7
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +3 -7
- package/dist/EphemeralData.d.ts +2 -2
- package/dist/EphemeralData.d.ts.map +1 -1
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +7 -11
- package/dist/helpers/cbor.d.ts +2 -2
- package/dist/helpers/cbor.d.ts.map +1 -1
- package/dist/helpers/cbor.js +1 -1
- package/dist/helpers/pause.d.ts.map +1 -1
- package/dist/helpers/pause.js +3 -1
- package/dist/helpers/tests/network-adapter-tests.d.ts.map +1 -1
- package/dist/helpers/tests/network-adapter-tests.js +2 -2
- package/dist/index.d.ts +11 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/network/NetworkAdapter.d.ts +3 -3
- package/dist/network/NetworkAdapter.d.ts.map +1 -1
- package/dist/network/NetworkSubsystem.d.ts +2 -2
- package/dist/network/NetworkSubsystem.d.ts.map +1 -1
- package/dist/network/NetworkSubsystem.js +30 -18
- package/dist/network/messages.d.ts +38 -68
- package/dist/network/messages.d.ts.map +1 -1
- package/dist/network/messages.js +13 -21
- package/dist/storage/StorageSubsystem.js +7 -7
- package/dist/synchronizer/CollectionSynchronizer.d.ts +3 -3
- package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.js +2 -2
- package/dist/synchronizer/DocSynchronizer.d.ts +3 -3
- package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.js +22 -29
- package/dist/synchronizer/Synchronizer.d.ts +2 -2
- package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -13
- package/src/DocHandle.ts +9 -11
- package/src/EphemeralData.ts +2 -2
- package/src/Repo.ts +10 -14
- package/src/helpers/cbor.ts +4 -4
- package/src/helpers/pause.ts +7 -2
- package/src/helpers/tests/network-adapter-tests.ts +3 -3
- package/src/helpers/withTimeout.ts +2 -2
- package/src/index.ts +36 -29
- package/src/network/NetworkAdapter.ts +7 -3
- package/src/network/NetworkSubsystem.ts +31 -23
- package/src/network/messages.ts +88 -151
- package/src/storage/StorageSubsystem.ts +8 -8
- package/src/synchronizer/CollectionSynchronizer.ts +6 -15
- package/src/synchronizer/DocSynchronizer.ts +34 -48
- package/src/synchronizer/Synchronizer.ts +2 -2
- package/src/types.ts +8 -3
- package/test/CollectionSynchronizer.test.ts +58 -53
- package/test/DocHandle.test.ts +35 -36
- package/test/DocSynchronizer.test.ts +9 -8
- package/test/Network.test.ts +1 -0
- package/test/Repo.test.ts +177 -97
- package/test/StorageSubsystem.test.ts +6 -9
- package/test/tsconfig.json +8 -0
- package/typedoc.json +3 -3
- package/.mocharc.json +0 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from "eventemitter3"
|
|
2
2
|
import { PeerId } from "../types.js"
|
|
3
|
-
import {
|
|
3
|
+
import { RepoMessage } from "./messages.js"
|
|
4
4
|
|
|
5
5
|
/** An interface representing some way to connect to other peers
|
|
6
6
|
*
|
|
@@ -22,7 +22,7 @@ export abstract class NetworkAdapter extends EventEmitter<NetworkAdapterEvents>
|
|
|
22
22
|
*
|
|
23
23
|
* @argument message - the message to send
|
|
24
24
|
*/
|
|
25
|
-
abstract send(message:
|
|
25
|
+
abstract send(message: RepoMessage): void
|
|
26
26
|
|
|
27
27
|
/** Called by the {@link Repo} to disconnect from the network */
|
|
28
28
|
abstract disconnect(): void
|
|
@@ -33,14 +33,18 @@ export abstract class NetworkAdapter extends EventEmitter<NetworkAdapterEvents>
|
|
|
33
33
|
export interface NetworkAdapterEvents {
|
|
34
34
|
/** Emitted when the network is ready to be used */
|
|
35
35
|
ready: (payload: OpenPayload) => void
|
|
36
|
+
|
|
36
37
|
/** Emitted when the network is closed */
|
|
37
38
|
close: () => void
|
|
39
|
+
|
|
38
40
|
/** Emitted when the network adapter learns about a new peer */
|
|
39
41
|
"peer-candidate": (payload: PeerCandidatePayload) => void
|
|
42
|
+
|
|
40
43
|
/** Emitted when the network adapter learns that a peer has disconnected */
|
|
41
44
|
"peer-disconnected": (payload: PeerDisconnectedPayload) => void
|
|
45
|
+
|
|
42
46
|
/** Emitted when the network adapter receives a message from a peer */
|
|
43
|
-
message: (payload:
|
|
47
|
+
message: (payload: RepoMessage) => void
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
export interface OpenPayload {
|
|
@@ -1,18 +1,15 @@
|
|
|
1
|
+
import debug from "debug"
|
|
1
2
|
import { EventEmitter } from "eventemitter3"
|
|
2
|
-
import { PeerId } from "../types.js"
|
|
3
|
+
import { PeerId, SessionId } from "../types.js"
|
|
3
4
|
import { NetworkAdapter, PeerDisconnectedPayload } from "./NetworkAdapter.js"
|
|
4
|
-
|
|
5
5
|
import {
|
|
6
6
|
EphemeralMessage,
|
|
7
|
-
|
|
8
|
-
isValidMessage,
|
|
9
|
-
Message,
|
|
7
|
+
RepoMessage,
|
|
10
8
|
MessageContents,
|
|
9
|
+
isEphemeralMessage,
|
|
10
|
+
isValidRepoMessage,
|
|
11
11
|
} from "./messages.js"
|
|
12
12
|
|
|
13
|
-
import debug from "debug"
|
|
14
|
-
import { SessionId } from "../EphemeralData.js"
|
|
15
|
-
|
|
16
13
|
type EphemeralMessageSource = `${PeerId}:${SessionId}`
|
|
17
14
|
|
|
18
15
|
const getEphemeralMessageSource = (message: EphemeralMessage) =>
|
|
@@ -69,7 +66,7 @@ export class NetworkSubsystem extends EventEmitter<NetworkSubsystemEvents> {
|
|
|
69
66
|
})
|
|
70
67
|
|
|
71
68
|
networkAdapter.on("message", msg => {
|
|
72
|
-
if (!
|
|
69
|
+
if (!isValidRepoMessage(msg)) {
|
|
73
70
|
this.#log(`invalid message: ${JSON.stringify(msg)}`)
|
|
74
71
|
return
|
|
75
72
|
}
|
|
@@ -110,25 +107,36 @@ export class NetworkSubsystem extends EventEmitter<NetworkSubsystemEvents> {
|
|
|
110
107
|
this.#log(`Tried to send message but peer not found: ${message.targetId}`)
|
|
111
108
|
return
|
|
112
109
|
}
|
|
113
|
-
this.#log(`Sending message to ${message.targetId}`)
|
|
114
110
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
111
|
+
/** Messages come in without a senderId and other required information; this is where we make
|
|
112
|
+
* sure they have everything they need.
|
|
113
|
+
*/
|
|
114
|
+
const prepareMessage = (message: MessageContents): RepoMessage => {
|
|
115
|
+
if (message.type === "ephemeral") {
|
|
116
|
+
if ("count" in message) {
|
|
117
|
+
// existing ephemeral message from another peer; pass on without changes
|
|
118
|
+
return message as EphemeralMessage
|
|
119
|
+
} else {
|
|
120
|
+
// new ephemeral message from us; add our senderId as well as a counter and session id
|
|
121
|
+
return {
|
|
120
122
|
...message,
|
|
121
123
|
count: ++this.#count,
|
|
122
124
|
sessionId: this.#sessionId,
|
|
123
125
|
senderId: this.peerId,
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
} as EphemeralMessage
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
// other message type; just add our senderId
|
|
130
|
+
return {
|
|
131
|
+
...message,
|
|
132
|
+
senderId: this.peerId,
|
|
133
|
+
} as RepoMessage
|
|
134
|
+
}
|
|
131
135
|
}
|
|
136
|
+
|
|
137
|
+
const outbound = prepareMessage(message)
|
|
138
|
+
this.#log("sending message", outbound)
|
|
139
|
+
peer.send(outbound as RepoMessage)
|
|
132
140
|
}
|
|
133
141
|
|
|
134
142
|
isReady = () => {
|
|
@@ -157,7 +165,7 @@ function randomPeerId() {
|
|
|
157
165
|
export interface NetworkSubsystemEvents {
|
|
158
166
|
peer: (payload: PeerPayload) => void
|
|
159
167
|
"peer-disconnected": (payload: PeerDisconnectedPayload) => void
|
|
160
|
-
message: (payload:
|
|
168
|
+
message: (payload: RepoMessage) => void
|
|
161
169
|
ready: () => void
|
|
162
170
|
}
|
|
163
171
|
|
package/src/network/messages.ts
CHANGED
|
@@ -1,161 +1,70 @@
|
|
|
1
|
-
|
|
2
|
-
import { SessionId } from "../EphemeralData.js"
|
|
3
|
-
export { type SessionId } from "../EphemeralData.js"
|
|
4
|
-
import { DocumentId, PeerId } from "../types.js"
|
|
5
|
-
|
|
6
|
-
export function isValidMessage(
|
|
7
|
-
message: NetworkAdapterMessage
|
|
8
|
-
): message is
|
|
9
|
-
| SyncMessage
|
|
10
|
-
| EphemeralMessage
|
|
11
|
-
| RequestMessage
|
|
12
|
-
| DocumentUnavailableMessage {
|
|
13
|
-
return (
|
|
14
|
-
typeof message === "object" &&
|
|
15
|
-
typeof message.type === "string" &&
|
|
16
|
-
typeof message.senderId === "string" &&
|
|
17
|
-
(isSyncMessage(message) ||
|
|
18
|
-
isEphemeralMessage(message) ||
|
|
19
|
-
isRequestMessage(message) ||
|
|
20
|
-
isDocumentUnavailableMessage(message))
|
|
21
|
-
)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function isDocumentUnavailableMessage(
|
|
25
|
-
message: NetworkAdapterMessage
|
|
26
|
-
): message is DocumentUnavailableMessage {
|
|
27
|
-
return message.type === "doc-unavailable"
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function isRequestMessage(
|
|
31
|
-
message: NetworkAdapterMessage
|
|
32
|
-
): message is RequestMessage {
|
|
33
|
-
return message.type === "request"
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function isSyncMessage(
|
|
37
|
-
message: NetworkAdapterMessage
|
|
38
|
-
): message is SyncMessage {
|
|
39
|
-
return message.type === "sync"
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function isEphemeralMessage(
|
|
43
|
-
message: NetworkAdapterMessage | MessageContents
|
|
44
|
-
): message is EphemeralMessage | EphemeralMessageContents {
|
|
45
|
-
return message.type === "ephemeral"
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface SyncMessageEnvelope {
|
|
49
|
-
senderId: PeerId
|
|
50
|
-
}
|
|
1
|
+
import { DocumentId, PeerId, SessionId } from "../types.js"
|
|
51
2
|
|
|
52
|
-
export interface SyncMessageContents {
|
|
53
|
-
type: "sync"
|
|
54
|
-
data: Uint8Array
|
|
55
|
-
targetId: PeerId
|
|
56
|
-
documentId: DocumentId
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
type static_assert<T extends true> = never
|
|
60
|
-
|
|
61
|
-
// export type SyncMessage = SyncMessageEnvelope & SyncMessageContents
|
|
62
|
-
// we inline the definitions here rather than using the above type alias because
|
|
63
|
-
// otherwise typedoc can't produce nice docs. We use this `static_assert` thing
|
|
64
|
-
// to make sure the types continue to line up though.
|
|
65
|
-
type _check_sync_message = static_assert<SyncMessage extends SyncMessageEnvelope & SyncMessageContents ? true : false>
|
|
66
3
|
/**
|
|
67
4
|
* A sync message for a particular document
|
|
68
5
|
*/
|
|
69
6
|
export type SyncMessage = {
|
|
7
|
+
type: "sync"
|
|
8
|
+
|
|
70
9
|
/** The peer ID of the sender of this message */
|
|
71
10
|
senderId: PeerId
|
|
72
|
-
|
|
73
|
-
/** The automerge sync message */
|
|
74
|
-
data: Uint8Array
|
|
11
|
+
|
|
75
12
|
/** The peer ID of the recipient of this message */
|
|
76
13
|
targetId: PeerId
|
|
77
|
-
/** The document ID of the document this message is for */
|
|
78
|
-
documentId: DocumentId
|
|
79
|
-
}
|
|
80
14
|
|
|
15
|
+
/** The automerge sync message */
|
|
16
|
+
data: Uint8Array
|
|
81
17
|
|
|
82
|
-
|
|
83
|
-
senderId: PeerId
|
|
84
|
-
count: number
|
|
85
|
-
sessionId: SessionId
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export interface EphemeralMessageContents {
|
|
89
|
-
type: "ephemeral"
|
|
90
|
-
targetId: PeerId
|
|
18
|
+
/** The document ID of the document this message is for */
|
|
91
19
|
documentId: DocumentId
|
|
92
|
-
data: Uint8Array
|
|
93
20
|
}
|
|
94
21
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
type _check_ephemeral_message = static_assert<EphemeralMessage extends EphemeralMessageEnvelope & EphemeralMessageContents ? true : false>
|
|
98
|
-
|
|
99
|
-
/** An ephemeral message
|
|
100
|
-
*
|
|
22
|
+
/** An ephemeral message
|
|
23
|
+
*
|
|
101
24
|
* @remarks
|
|
102
25
|
* Ephemeral messages are not persisted anywhere and have no particular
|
|
103
|
-
* structure. `automerge-repo` will gossip them around, in order to avoid
|
|
26
|
+
* structure. `automerge-repo` will gossip them around, in order to avoid
|
|
104
27
|
* eternal loops of ephemeral messages every message has a session ID, which
|
|
105
28
|
* is a random number generated by the sender at startup time, and a sequence
|
|
106
29
|
* number. The combination of these two things allows us to discard messages
|
|
107
30
|
* we have already seen.
|
|
108
31
|
* */
|
|
109
32
|
export type EphemeralMessage = {
|
|
110
|
-
|
|
33
|
+
type: "ephemeral"
|
|
34
|
+
|
|
35
|
+
/** The peer ID of the sender of this message */
|
|
111
36
|
senderId: PeerId
|
|
37
|
+
|
|
38
|
+
/** The peer ID of the recipient of this message */
|
|
39
|
+
targetId: PeerId
|
|
40
|
+
|
|
112
41
|
/** A sequence number which must be incremented for each message sent by this peer */
|
|
113
42
|
count: number
|
|
43
|
+
|
|
114
44
|
/** The ID of the session this message is part of. The sequence number for a given session always increases */
|
|
115
45
|
sessionId: SessionId
|
|
116
|
-
|
|
117
|
-
/** The peer this message is for */
|
|
118
|
-
targetId: PeerId
|
|
46
|
+
|
|
119
47
|
/** The document ID this message pertains to */
|
|
120
48
|
documentId: DocumentId
|
|
49
|
+
|
|
121
50
|
/** The actual data of the message */
|
|
122
51
|
data: Uint8Array
|
|
123
52
|
}
|
|
124
53
|
|
|
125
|
-
export interface DocumentUnavailableMessageContents {
|
|
126
|
-
type: "doc-unavailable"
|
|
127
|
-
documentId: DocumentId
|
|
128
|
-
targetId: PeerId
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
// export type DocumentUnavailableMessage = SyncMessageEnvelope & DocumentUnavailableMessageContents
|
|
133
|
-
// Inline definitions to get good docs, but check the types line up
|
|
134
|
-
type _check_doc_unavailable = static_assert<DocumentUnavailableMessage extends SyncMessageEnvelope & DocumentUnavailableMessageContents ? true : false>
|
|
135
54
|
/** Sent by a {@link Repo} to indicate that it does not have the document and none of it's connected peers do either */
|
|
136
55
|
export type DocumentUnavailableMessage = {
|
|
137
|
-
/** The peer who sent this message */
|
|
138
|
-
senderId: PeerId
|
|
139
56
|
type: "doc-unavailable"
|
|
140
|
-
/** The document which the peer claims it doesn't have */
|
|
141
|
-
documentId: DocumentId
|
|
142
|
-
/** The peer this message is for */
|
|
143
|
-
targetId: PeerId
|
|
144
|
-
}
|
|
145
57
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
58
|
+
/** The peer ID of the sender of this message */
|
|
59
|
+
senderId: PeerId
|
|
60
|
+
|
|
61
|
+
/** The peer ID of the recipient of this message */
|
|
149
62
|
targetId: PeerId
|
|
63
|
+
|
|
64
|
+
/** The document which the peer claims it doesn't have */
|
|
150
65
|
documentId: DocumentId
|
|
151
66
|
}
|
|
152
67
|
|
|
153
|
-
// export type RequestMessage = SyncMessageEnvelope & RequestMessageContents
|
|
154
|
-
// Inline definitions to get good docs, but check the types line up
|
|
155
|
-
type _check_request_message = static_assert<RequestMessage extends SyncMessageEnvelope & RequestMessageContents ? true : false>
|
|
156
|
-
// We inline the definitions here rather than using the above type alias because
|
|
157
|
-
// otherwise typedoc can't produce nice docs without exporting SyncMessageEnvelope
|
|
158
|
-
// and RequestMessageContents
|
|
159
68
|
/** Sent by a {@link Repo} to request a document from a peer
|
|
160
69
|
*
|
|
161
70
|
* @remarks
|
|
@@ -163,58 +72,86 @@ type _check_request_message = static_assert<RequestMessage extends SyncMessageEn
|
|
|
163
72
|
* as the initial sync message when asking the other peer if it has the document.
|
|
164
73
|
* */
|
|
165
74
|
export type RequestMessage = {
|
|
166
|
-
/** The peer who sent this message */
|
|
167
|
-
senderId: PeerId
|
|
168
75
|
type: "request"
|
|
76
|
+
|
|
77
|
+
/** The peer ID of the sender of this message */
|
|
78
|
+
senderId: PeerId
|
|
79
|
+
|
|
80
|
+
/** The peer ID of the recipient of this message */
|
|
81
|
+
targetId: PeerId
|
|
82
|
+
|
|
169
83
|
/** The initial automerge sync message */
|
|
170
84
|
data: Uint8Array
|
|
171
|
-
|
|
172
|
-
targetId: PeerId
|
|
85
|
+
|
|
173
86
|
/** The document ID this message requests */
|
|
174
87
|
documentId: DocumentId
|
|
175
88
|
}
|
|
176
89
|
|
|
177
|
-
export type MessageContents =
|
|
178
|
-
| SyncMessageContents
|
|
179
|
-
| EphemeralMessageContents
|
|
180
|
-
| RequestMessageContents
|
|
181
|
-
| DocumentUnavailableMessageContents
|
|
182
|
-
|
|
183
|
-
/** The type of messages that {@link Repo} sends and receive to {@link NetworkAdapter}s */
|
|
184
|
-
export type Message =
|
|
185
|
-
| SyncMessage
|
|
186
|
-
| EphemeralMessage
|
|
187
|
-
| RequestMessage
|
|
188
|
-
| DocumentUnavailableMessage
|
|
189
|
-
|
|
190
|
-
export type SynchronizerMessage =
|
|
191
|
-
| SyncMessage
|
|
192
|
-
| RequestMessage
|
|
193
|
-
| DocumentUnavailableMessage
|
|
194
|
-
| EphemeralMessage
|
|
195
|
-
|
|
196
|
-
|
|
197
90
|
/** Notify the network that we have arrived so everyone knows our peer ID */
|
|
198
91
|
export type ArriveMessage = {
|
|
199
|
-
/** Our peer ID */
|
|
200
|
-
senderId: PeerId
|
|
201
92
|
type: "arrive"
|
|
93
|
+
|
|
94
|
+
/** The peer ID of the sender of this message */
|
|
95
|
+
senderId: PeerId
|
|
96
|
+
|
|
97
|
+
/** Arrive messages don't have a targetId */
|
|
98
|
+
targetId: never
|
|
202
99
|
}
|
|
203
100
|
|
|
204
101
|
/** Respond to an arriving peer with our peer ID */
|
|
205
102
|
export type WelcomeMessage = {
|
|
206
|
-
|
|
103
|
+
type: "welcome"
|
|
104
|
+
|
|
105
|
+
/** The peer ID of the recipient sender this message */
|
|
207
106
|
senderId: PeerId
|
|
208
|
-
|
|
107
|
+
|
|
108
|
+
/** The peer ID of the recipient of this message */
|
|
209
109
|
targetId: PeerId
|
|
210
|
-
type: "welcome"
|
|
211
110
|
}
|
|
212
111
|
|
|
213
|
-
/**
|
|
112
|
+
/** These are message types that a {@link NetworkAdapter} surfaces to a {@link Repo}. */
|
|
113
|
+
export type RepoMessage =
|
|
114
|
+
| SyncMessage
|
|
115
|
+
| EphemeralMessage
|
|
116
|
+
| RequestMessage
|
|
117
|
+
| DocumentUnavailableMessage
|
|
118
|
+
|
|
119
|
+
/** These are all the message types that a {@link NetworkAdapter} might see.
|
|
214
120
|
*
|
|
215
121
|
* @remarks
|
|
216
|
-
* It is not _required_ that a {@link NetworkAdapter} use
|
|
217
|
-
*
|
|
218
|
-
* transport. However, this type is a useful default.
|
|
122
|
+
* It is not _required_ that a {@link NetworkAdapter} use these types: They are free to use
|
|
123
|
+
* whatever message type makes sense for their transport. However, this type is a useful default.
|
|
219
124
|
* */
|
|
220
|
-
export type
|
|
125
|
+
export type Message = RepoMessage | ArriveMessage | WelcomeMessage
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The contents of a message, without the sender ID or other properties added by the {@link NetworkSubsystem})
|
|
129
|
+
*/
|
|
130
|
+
export type MessageContents<T extends Message = Message> =
|
|
131
|
+
T extends EphemeralMessage
|
|
132
|
+
? Omit<T, "senderId" | "count" | "sessionId">
|
|
133
|
+
: Omit<T, "senderId">
|
|
134
|
+
|
|
135
|
+
// TYPE GUARDS
|
|
136
|
+
|
|
137
|
+
export const isValidRepoMessage = (message: Message): message is RepoMessage =>
|
|
138
|
+
typeof message === "object" &&
|
|
139
|
+
typeof message.type === "string" &&
|
|
140
|
+
typeof message.senderId === "string" &&
|
|
141
|
+
(isSyncMessage(message) ||
|
|
142
|
+
isEphemeralMessage(message) ||
|
|
143
|
+
isRequestMessage(message) ||
|
|
144
|
+
isDocumentUnavailableMessage(message))
|
|
145
|
+
|
|
146
|
+
// prettier-ignore
|
|
147
|
+
export const isDocumentUnavailableMessage = (msg: Message): msg is DocumentUnavailableMessage =>
|
|
148
|
+
msg.type === "doc-unavailable"
|
|
149
|
+
|
|
150
|
+
export const isRequestMessage = (msg: Message): msg is RequestMessage =>
|
|
151
|
+
msg.type === "request"
|
|
152
|
+
|
|
153
|
+
export const isSyncMessage = (msg: Message): msg is SyncMessage =>
|
|
154
|
+
msg.type === "sync"
|
|
155
|
+
|
|
156
|
+
export const isEphemeralMessage = (msg: Message): msg is EphemeralMessage =>
|
|
157
|
+
msg.type === "ephemeral"
|
|
@@ -24,8 +24,8 @@ function keyHash(binary: Uint8Array) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function headsHash(heads: A.Heads): string {
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
const encoder = new TextEncoder()
|
|
28
|
+
const headsbinary = mergeArrays(heads.map((h: string) => encoder.encode(h)))
|
|
29
29
|
return keyHash(headsbinary)
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -53,7 +53,7 @@ export class StorageSubsystem {
|
|
|
53
53
|
if (!this.#chunkInfos.has(documentId)) {
|
|
54
54
|
this.#chunkInfos.set(documentId, [])
|
|
55
55
|
}
|
|
56
|
-
this.#chunkInfos.get(documentId)
|
|
56
|
+
this.#chunkInfos.get(documentId)!.push({
|
|
57
57
|
key,
|
|
58
58
|
type: "incremental",
|
|
59
59
|
size: binary.length,
|
|
@@ -122,18 +122,18 @@ export class StorageSubsystem {
|
|
|
122
122
|
if (!this.#shouldSave(documentId, doc)) {
|
|
123
123
|
return
|
|
124
124
|
}
|
|
125
|
-
|
|
125
|
+
const sourceChunks = this.#chunkInfos.get(documentId) ?? []
|
|
126
126
|
if (this.#shouldCompact(sourceChunks)) {
|
|
127
|
-
this.#saveTotal(documentId, doc, sourceChunks)
|
|
127
|
+
void this.#saveTotal(documentId, doc, sourceChunks)
|
|
128
128
|
} else {
|
|
129
|
-
this.#saveIncremental(documentId, doc)
|
|
129
|
+
void this.#saveIncremental(documentId, doc)
|
|
130
130
|
}
|
|
131
131
|
this.#storedHeads.set(documentId, A.getHeads(doc))
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
async remove(documentId: DocumentId) {
|
|
135
|
-
this.#storageAdapter.removeRange([documentId, "snapshot"])
|
|
136
|
-
this.#storageAdapter.removeRange([documentId, "incremental"])
|
|
135
|
+
void this.#storageAdapter.removeRange([documentId, "snapshot"])
|
|
136
|
+
void this.#storageAdapter.removeRange([documentId, "incremental"])
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
#shouldSave(documentId: DocumentId, doc: A.Doc<unknown>): boolean {
|
|
@@ -1,21 +1,12 @@
|
|
|
1
|
-
import { Repo } from "../Repo.js"
|
|
2
1
|
import { DocHandle } from "../DocHandle.js"
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
stringifyAutomergeUrl,
|
|
7
|
-
} from "../DocUrl.js"
|
|
8
|
-
import { PeerId, DocumentId } from "../types.js"
|
|
2
|
+
import { stringifyAutomergeUrl } from "../DocUrl.js"
|
|
3
|
+
import { Repo } from "../Repo.js"
|
|
4
|
+
import { DocumentId, PeerId } from "../types.js"
|
|
9
5
|
import { DocSynchronizer } from "./DocSynchronizer.js"
|
|
10
6
|
import { Synchronizer } from "./Synchronizer.js"
|
|
11
7
|
|
|
12
8
|
import debug from "debug"
|
|
13
|
-
import {
|
|
14
|
-
DocumentUnavailableMessage,
|
|
15
|
-
RequestMessage,
|
|
16
|
-
SynchronizerMessage,
|
|
17
|
-
SyncMessage,
|
|
18
|
-
} from "../network/messages.js"
|
|
9
|
+
import { RepoMessage } from "../network/messages.js"
|
|
19
10
|
const log = debug("automerge-repo:collectionsync")
|
|
20
11
|
|
|
21
12
|
/** A CollectionSynchronizer is responsible for synchronizing a DocCollection with peers. */
|
|
@@ -66,7 +57,7 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
66
57
|
* When we receive a sync message for a document we haven't got in memory, we
|
|
67
58
|
* register it with the repo and start synchronizing
|
|
68
59
|
*/
|
|
69
|
-
async receiveMessage(message:
|
|
60
|
+
async receiveMessage(message: RepoMessage) {
|
|
70
61
|
log(
|
|
71
62
|
`onSyncMessage: ${message.senderId}, ${message.documentId}, ${
|
|
72
63
|
"data" in message ? message.data.byteLength + "bytes" : ""
|
|
@@ -121,7 +112,7 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
121
112
|
this.#peers.add(peerId)
|
|
122
113
|
for (const docSynchronizer of Object.values(this.#docSynchronizers)) {
|
|
123
114
|
const { documentId } = docSynchronizer
|
|
124
|
-
this.repo.sharePolicy(peerId, documentId).then(okToShare => {
|
|
115
|
+
void this.repo.sharePolicy(peerId, documentId).then(okToShare => {
|
|
125
116
|
if (okToShare) docSynchronizer.beginSync([peerId])
|
|
126
117
|
})
|
|
127
118
|
}
|