@automerge/automerge-repo-network-websocket 1.0.12 → 1.0.15
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 +192 -0
- package/dist/messages.d.ts +4 -4
- package/dist/messages.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/messages.ts +4 -4
package/README.md
CHANGED
|
@@ -3,3 +3,195 @@
|
|
|
3
3
|
Includes two implementations, a Websocket client and a Websocket server. These are used by the example sync-server.
|
|
4
4
|
|
|
5
5
|
The package uses isomorphic-ws to share code between node and the browser, but the server code is node only due to lack of browser support.
|
|
6
|
+
|
|
7
|
+
## Wire Protocol
|
|
8
|
+
|
|
9
|
+
### Peer naming
|
|
10
|
+
|
|
11
|
+
Whilst currently this wire protocol is only used for the websocket, it is
|
|
12
|
+
probably generically useful for any stream oriented transport (e.g. TCP or
|
|
13
|
+
QUIC). To make translating this spec to other transports easier we refer to the
|
|
14
|
+
two parties in the protocol as the "initiating" and "receiving" peers. In the
|
|
15
|
+
WebSocket case the "initiating" peer is the client and the "receiving" peer
|
|
16
|
+
is the server.
|
|
17
|
+
|
|
18
|
+
### Overview
|
|
19
|
+
|
|
20
|
+
The websocket wire protocol consists of a handshake where each peer tells the
|
|
21
|
+
other what their peer ID is followed by the sync loop where each peer can send
|
|
22
|
+
the other sync messages and ephemeral messages.
|
|
23
|
+
|
|
24
|
+
### Handshake
|
|
25
|
+
|
|
26
|
+
Before sync can continue each peer needs to exchange peer IDs and agree on the
|
|
27
|
+
protocol version they are using (although currently there is only one version).
|
|
28
|
+
Handshake is the following steps:
|
|
29
|
+
|
|
30
|
+
* Once a connection is established the initiating peer sends a
|
|
31
|
+
[join](#join) message with the `senderId` set to the initiating peers ID and
|
|
32
|
+
the `protocolVersion` set to "1"
|
|
33
|
+
* The receiving peer waits until it receives a message from the initiating
|
|
34
|
+
peer, if the initiating peer receives a message before sending the join message
|
|
35
|
+
the initiating peer SHOULD terminate the connection.
|
|
36
|
+
* When the receiving peer receives the join message
|
|
37
|
+
* if the `protocolVersion` is not "1" the receiving peer sends an
|
|
38
|
+
[error](#error) message and terminates the connection
|
|
39
|
+
* otherwise
|
|
40
|
+
* store the `senderId` as the peer ID of the initiating peer
|
|
41
|
+
* emit a "peer-candidate" event with the sender ID as the peer
|
|
42
|
+
* respond with a [peer](#peer) message with the `targetId` set to the
|
|
43
|
+
initiating peers peer ID, the `senderId` set to the receiving peers
|
|
44
|
+
peer ID and the `selectedProtocolVersion` set to "1"
|
|
45
|
+
* begin the sync phase
|
|
46
|
+
* Once the initiating peer has sent the join message it waits for a peer message
|
|
47
|
+
in response. If it receives any other message type before the join message
|
|
48
|
+
the receiving peer should send an [error](#error) message and terminates the
|
|
49
|
+
connection
|
|
50
|
+
* When the initiating peer receives the peer message
|
|
51
|
+
* it stores the `senderId` as the peer ID of the receiving peer.
|
|
52
|
+
* it emits a "peer-candidate" event with the sender ID as the peer
|
|
53
|
+
* if the `selectedProtocolVersion` is anything other than "1" the initiating
|
|
54
|
+
peer sends an [error](#error) message and terminates the connection
|
|
55
|
+
* it begins the sync phase
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
### Sync Phase
|
|
59
|
+
|
|
60
|
+
In the sync phase either side may send a [request](#request), [sync](#sync),
|
|
61
|
+
[unavailable](#unavailable), or [ephemeral](#ephemeral) message. Sending these
|
|
62
|
+
corresponds to implementing
|
|
63
|
+
[`NetworkAdapter.send`](https://automerge.org/automerge-repo/classes/_automerge_automerge_repo.NetworkAdapter.html#send)
|
|
64
|
+
and receiving is emitting the [corresponding
|
|
65
|
+
event](https://automerge.org/automerge-repo/interfaces/_automerge_automerge_repo.NetworkAdapterEvents.html)
|
|
66
|
+
from the `NetworkAdapter` on receipt.
|
|
67
|
+
|
|
68
|
+
### Message Types
|
|
69
|
+
|
|
70
|
+
All messages are encoded using CBOR and are described in this document using
|
|
71
|
+
[cddl](https://datatracker.ietf.org/doc/html/rfc8610)
|
|
72
|
+
|
|
73
|
+
#### Preamble
|
|
74
|
+
|
|
75
|
+
These type definitions are used in every message type
|
|
76
|
+
|
|
77
|
+
```cddl
|
|
78
|
+
; The base64 encoded bytes of a Peer ID
|
|
79
|
+
peer_id = str
|
|
80
|
+
; The possible protocol versions (currently always the string "1")
|
|
81
|
+
protocol_version = "1"
|
|
82
|
+
; The bytes of an automerge sync message
|
|
83
|
+
sync_message = bstr
|
|
84
|
+
; The base64 encoded bytes of a document ID
|
|
85
|
+
document_id = str
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### Join
|
|
89
|
+
|
|
90
|
+
Sent by the initiating peer in the [handshake](#handshake) phase.
|
|
91
|
+
|
|
92
|
+
```cddl
|
|
93
|
+
{
|
|
94
|
+
type: "join",
|
|
95
|
+
senderId: peer_id,
|
|
96
|
+
supportedProtocolVersions: protocol_version
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### Peer
|
|
101
|
+
|
|
102
|
+
Sent by the receiving peer in response to the join message in the
|
|
103
|
+
[handshake](#handshake) phase,
|
|
104
|
+
|
|
105
|
+
```cddl
|
|
106
|
+
{
|
|
107
|
+
type: "peer",
|
|
108
|
+
senderId: peer_id,
|
|
109
|
+
selectedProtocolVersion: protocol_version,
|
|
110
|
+
targetId: peer_id,
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### Request
|
|
115
|
+
|
|
116
|
+
Sent when the `senderId` is asking to begin sync for the given `documentid`.
|
|
117
|
+
Identical to [sync](#sync) but indicates that the `senderId` would like an
|
|
118
|
+
[unavailable](#unavailable) message if the `targetId` does not have the
|
|
119
|
+
document.
|
|
120
|
+
|
|
121
|
+
```cddl
|
|
122
|
+
{
|
|
123
|
+
type: "request",
|
|
124
|
+
documentId: document_id,
|
|
125
|
+
; The peer requesting to begin sync
|
|
126
|
+
senderId: peer_id,
|
|
127
|
+
targetId: peer_id,
|
|
128
|
+
; The initial automerge sync message from the sender
|
|
129
|
+
data: sync_message
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### Sync
|
|
134
|
+
|
|
135
|
+
Sent any time either peer wants to send a sync message about a given document
|
|
136
|
+
|
|
137
|
+
```cddl
|
|
138
|
+
{
|
|
139
|
+
type: "sync",
|
|
140
|
+
documentId: document_id,
|
|
141
|
+
; The peer requesting to begin sync
|
|
142
|
+
senderId: peer_id,
|
|
143
|
+
targetId: peer_id,
|
|
144
|
+
; The initial automerge sync message from the sender
|
|
145
|
+
data: sync_message
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
#### Unavailable
|
|
150
|
+
|
|
151
|
+
Sent when a peer wants to indicate to the `targetId` that it doesn't have a
|
|
152
|
+
given document and all of it's peers have also indicated that they don't have
|
|
153
|
+
it
|
|
154
|
+
|
|
155
|
+
```cddl
|
|
156
|
+
{
|
|
157
|
+
type: "doc-unavailable",
|
|
158
|
+
senderId: peer_id,
|
|
159
|
+
targetId: peer_id,
|
|
160
|
+
documentId: document_id,
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### Ephemeral
|
|
165
|
+
|
|
166
|
+
Sent when a peer wants to send an ephemeral message to another peer
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
```cddl
|
|
170
|
+
{
|
|
171
|
+
type: "ephemeral",
|
|
172
|
+
; The peer who sent this message
|
|
173
|
+
senderId: peer_id,
|
|
174
|
+
; The target of this message
|
|
175
|
+
targetId: peer_id,
|
|
176
|
+
; The sequence number of this message within its session
|
|
177
|
+
count: uint,
|
|
178
|
+
; The unique session identifying this stream of ephemeral messages
|
|
179
|
+
sessionId: str,
|
|
180
|
+
; The document ID this ephemera relates to
|
|
181
|
+
documentId: document_id,
|
|
182
|
+
; The data of this message (in practice this is arbitrary CBOR)
|
|
183
|
+
data: bstr
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### Error
|
|
188
|
+
|
|
189
|
+
Sent to inform the other end that there has been a protocol error and the
|
|
190
|
+
connection will close
|
|
191
|
+
|
|
192
|
+
```cddl
|
|
193
|
+
{
|
|
194
|
+
type: "error",
|
|
195
|
+
message: str,
|
|
196
|
+
}
|
|
197
|
+
```
|
package/dist/messages.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ProtocolVersion } from "./protocolVersion.js";
|
|
1
|
+
import type { Message, PeerId } from "@automerge/automerge-repo";
|
|
2
|
+
import type { ProtocolVersion } from "./protocolVersion.js";
|
|
3
3
|
/** The sender is disconnecting */
|
|
4
4
|
export type LeaveMessage = {
|
|
5
5
|
type: "leave";
|
|
@@ -34,7 +34,7 @@ export type ErrorMessage = {
|
|
|
34
34
|
targetId: PeerId;
|
|
35
35
|
};
|
|
36
36
|
/** A message from the client to the server */
|
|
37
|
-
export type FromClientMessage = JoinMessage | LeaveMessage |
|
|
37
|
+
export type FromClientMessage = JoinMessage | LeaveMessage | Message;
|
|
38
38
|
/** A message from the server to the client */
|
|
39
|
-
export type FromServerMessage = PeerMessage | ErrorMessage |
|
|
39
|
+
export type FromServerMessage = PeerMessage | ErrorMessage | Message;
|
|
40
40
|
//# sourceMappingURL=messages.d.ts.map
|
package/dist/messages.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAA;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAE3D,kCAAkC;AAClC,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,6EAA6E;AAC7E,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,+CAA+C;IAC/C,yBAAyB,EAAE,eAAe,EAAE,CAAA;CAC7C,CAAA;AAED,yFAAyF;AACzF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,mEAAmE;IACnE,uBAAuB,EAAE,eAAe,CAAA;IACxC,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,gGAAgG;AAChG,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAA;IAChB,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAKD,8CAA8C;AAC9C,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,YAAY,GAAG,OAAO,CAAA;AAEpE,8CAA8C;AAC9C,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,YAAY,GAAG,OAAO,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automerge/automerge-repo-network-websocket",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "isomorphic node/browser Websocket network adapter for Automerge Repo",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@automerge/automerge": "^2.1.0"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"test": "vitest"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@automerge/automerge-repo": "^1.0.
|
|
19
|
+
"@automerge/automerge-repo": "^1.0.15",
|
|
20
20
|
"cbor-x": "^1.3.0",
|
|
21
21
|
"eventemitter3": "^5.0.1",
|
|
22
22
|
"isomorphic-ws": "^5.0.0",
|
|
@@ -33,5 +33,5 @@
|
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "f60bd2a4594cc23b0ee6debed62aea61fb48ea56"
|
|
37
37
|
}
|
package/src/messages.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ProtocolVersion } from "./protocolVersion.js"
|
|
1
|
+
import type { Message, PeerId } from "@automerge/automerge-repo"
|
|
2
|
+
import type { ProtocolVersion } from "./protocolVersion.js"
|
|
3
3
|
|
|
4
4
|
/** The sender is disconnecting */
|
|
5
5
|
export type LeaveMessage = {
|
|
@@ -42,7 +42,7 @@ export type ErrorMessage = {
|
|
|
42
42
|
// join/leave
|
|
43
43
|
|
|
44
44
|
/** A message from the client to the server */
|
|
45
|
-
export type FromClientMessage = JoinMessage | LeaveMessage |
|
|
45
|
+
export type FromClientMessage = JoinMessage | LeaveMessage | Message
|
|
46
46
|
|
|
47
47
|
/** A message from the server to the client */
|
|
48
|
-
export type FromServerMessage = PeerMessage | ErrorMessage |
|
|
48
|
+
export type FromServerMessage = PeerMessage | ErrorMessage | Message
|