@automerge/automerge-repo-network-websocket 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/BrowserWebSocketClientAdapter.d.ts +4 -2
- package/dist/BrowserWebSocketClientAdapter.d.ts.map +1 -1
- package/dist/BrowserWebSocketClientAdapter.js +36 -32
- package/dist/NodeWSServerAdapter.d.ts +3 -2
- package/dist/NodeWSServerAdapter.d.ts.map +1 -1
- package/dist/NodeWSServerAdapter.js +22 -40
- package/dist/messages.d.ts +26 -0
- package/dist/messages.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/BrowserWebSocketClientAdapter.ts +48 -57
- package/src/NodeWSServerAdapter.ts +35 -70
- package/src/messages.ts +31 -0
- package/test/Websocket.test.ts +45 -24
- package/test/utilities/WebSockets.ts +1 -1
- package/tsconfig.json +2 -2
- package/dist/message.d.ts +0 -9
- package/dist/message.d.ts.map +0 -1
- package/src/message.ts +0 -10
- /package/dist/{message.js → messages.js} +0 -0
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
/// <reference types="ws" />
|
|
2
|
-
import {
|
|
2
|
+
import { NetworkAdapter, PeerId } from "@automerge/automerge-repo";
|
|
3
3
|
import WebSocket from "isomorphic-ws";
|
|
4
|
+
import { FromClientMessage } from "./messages.js";
|
|
4
5
|
declare abstract class WebSocketNetworkAdapter extends NetworkAdapter {
|
|
5
6
|
socket?: WebSocket;
|
|
6
7
|
}
|
|
7
8
|
export declare class BrowserWebSocketClientAdapter extends WebSocketNetworkAdapter {
|
|
9
|
+
#private;
|
|
8
10
|
timerId?: ReturnType<typeof setTimeout>;
|
|
9
11
|
url: string;
|
|
10
12
|
constructor(url: string);
|
|
11
13
|
connect(peerId: PeerId): void;
|
|
12
14
|
join(): void;
|
|
13
15
|
leave(): void;
|
|
14
|
-
|
|
16
|
+
send(message: FromClientMessage): void;
|
|
15
17
|
announceConnection(peerId: PeerId): void;
|
|
16
18
|
receiveMessage(message: Uint8Array): void;
|
|
17
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BrowserWebSocketClientAdapter.d.ts","sourceRoot":"","sources":["../src/BrowserWebSocketClientAdapter.ts"],"names":[],"mappings":";AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"BrowserWebSocketClientAdapter.d.ts","sourceRoot":"","sources":["../src/BrowserWebSocketClientAdapter.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,EAAQ,MAAM,2BAA2B,CAAA;AACxE,OAAO,SAAS,MAAM,eAAe,CAAA;AAIrC,OAAO,EACL,iBAAiB,EAGlB,MAAM,eAAe,CAAA;AAMtB,uBAAe,uBAAwB,SAAQ,cAAc;IAC3D,MAAM,CAAC,EAAE,SAAS,CAAA;CACnB;AAED,qBAAa,6BAA8B,SAAQ,uBAAuB;;IAGxE,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAA;IAGvC,GAAG,EAAE,MAAM,CAAA;gBAEC,GAAG,EAAE,MAAM;IAKvB,OAAO,CAAC,MAAM,EAAE,MAAM;IAwCtB,IAAI;IAoBJ,KAAK;IAOL,IAAI,CAAC,OAAO,EAAE,iBAAiB;IAwB/B,kBAAkB,CAAC,MAAM,EAAE,MAAM;IAajC,cAAc,CAAC,OAAO,EAAE,UAAU;CA6BnC"}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { NetworkAdapter, } from "@automerge/automerge-repo";
|
|
2
|
-
import * as CBOR from "cbor-x";
|
|
1
|
+
import { NetworkAdapter, cbor } from "@automerge/automerge-repo";
|
|
3
2
|
import WebSocket from "isomorphic-ws";
|
|
4
3
|
import debug from "debug";
|
|
5
4
|
import { ProtocolV1 } from "./protocolVersion.js";
|
|
5
|
+
import { isValidMessage } from "@automerge/automerge-repo/dist/network/messages.js";
|
|
6
6
|
const log = debug("WebsocketClient");
|
|
7
7
|
class WebSocketNetworkAdapter extends NetworkAdapter {
|
|
8
8
|
socket;
|
|
9
9
|
}
|
|
10
10
|
export class BrowserWebSocketClientAdapter extends WebSocketNetworkAdapter {
|
|
11
|
-
// Type trickery required for platform independence,
|
|
11
|
+
// Type trickery required for platform independence,
|
|
12
12
|
// see https://stackoverflow.com/questions/45802988/typescript-use-correct-version-of-settimeout-node-vs-window
|
|
13
13
|
timerId;
|
|
14
|
+
#startupComplete = false;
|
|
14
15
|
url;
|
|
15
16
|
constructor(url) {
|
|
16
17
|
super();
|
|
@@ -38,20 +39,29 @@ export class BrowserWebSocketClientAdapter extends WebSocketNetworkAdapter {
|
|
|
38
39
|
// log("Disconnected from server")
|
|
39
40
|
});
|
|
40
41
|
this.socket.addEventListener("message", (event) => this.receiveMessage(event.data));
|
|
42
|
+
// mark this adapter as ready if we haven't received an ack in 1 second.
|
|
43
|
+
// We might hear back from the other end at some point but we shouldn't
|
|
44
|
+
// hold up marking things as unavailable for any longer
|
|
45
|
+
setTimeout(() => {
|
|
46
|
+
if (!this.#startupComplete) {
|
|
47
|
+
this.#startupComplete = true;
|
|
48
|
+
this.emit("ready", { network: this });
|
|
49
|
+
}
|
|
50
|
+
}, 1000);
|
|
41
51
|
}
|
|
42
52
|
join() {
|
|
43
53
|
if (!this.socket) {
|
|
44
54
|
throw new Error("WTF, get a socket");
|
|
45
55
|
}
|
|
46
56
|
if (this.socket.readyState === WebSocket.OPEN) {
|
|
47
|
-
this.
|
|
57
|
+
this.send(joinMessage(this.peerId));
|
|
48
58
|
}
|
|
49
59
|
else {
|
|
50
60
|
this.socket.addEventListener("open", () => {
|
|
51
61
|
if (!this.socket) {
|
|
52
62
|
throw new Error("WTF, get a socket");
|
|
53
63
|
}
|
|
54
|
-
this.
|
|
64
|
+
this.send(joinMessage(this.peerId));
|
|
55
65
|
}, { once: true });
|
|
56
66
|
}
|
|
57
67
|
}
|
|
@@ -59,31 +69,23 @@ export class BrowserWebSocketClientAdapter extends WebSocketNetworkAdapter {
|
|
|
59
69
|
if (!this.socket) {
|
|
60
70
|
throw new Error("WTF, get a socket");
|
|
61
71
|
}
|
|
62
|
-
this.
|
|
72
|
+
this.send({ type: "leave", senderId: this.peerId });
|
|
63
73
|
}
|
|
64
|
-
|
|
65
|
-
if (message.byteLength === 0) {
|
|
74
|
+
send(message) {
|
|
75
|
+
if ("data" in message && message.data.byteLength === 0) {
|
|
66
76
|
throw new Error("tried to send a zero-length message");
|
|
67
77
|
}
|
|
68
78
|
if (!this.peerId) {
|
|
69
79
|
throw new Error("Why don't we have a PeerID?");
|
|
70
80
|
}
|
|
71
|
-
const decoded = {
|
|
72
|
-
senderId: this.peerId,
|
|
73
|
-
targetId,
|
|
74
|
-
channelId,
|
|
75
|
-
type: "sync",
|
|
76
|
-
message,
|
|
77
|
-
broadcast,
|
|
78
|
-
};
|
|
79
|
-
const encoded = CBOR.encode(decoded);
|
|
80
|
-
// This incantation deals with websocket sending the whole
|
|
81
|
-
// underlying buffer even if we just have a uint8array view on it
|
|
82
|
-
const arrayBuf = encoded.buffer.slice(encoded.byteOffset, encoded.byteOffset + encoded.byteLength);
|
|
83
81
|
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
|
|
84
82
|
throw new Error("Websocket Socket not ready!");
|
|
85
83
|
}
|
|
86
|
-
|
|
84
|
+
const encoded = cbor.encode(message);
|
|
85
|
+
// This incantation deals with websocket sending the whole
|
|
86
|
+
// underlying buffer even if we just have a uint8array view on it
|
|
87
|
+
const arrayBuf = encoded.buffer.slice(encoded.byteOffset, encoded.byteOffset + encoded.byteLength);
|
|
88
|
+
this.socket?.send(arrayBuf);
|
|
87
89
|
}
|
|
88
90
|
announceConnection(peerId) {
|
|
89
91
|
// return a peer object
|
|
@@ -91,11 +93,15 @@ export class BrowserWebSocketClientAdapter extends WebSocketNetworkAdapter {
|
|
|
91
93
|
if (!myPeerId) {
|
|
92
94
|
throw new Error("we should have a peer ID by now");
|
|
93
95
|
}
|
|
96
|
+
if (!this.#startupComplete) {
|
|
97
|
+
this.#startupComplete = true;
|
|
98
|
+
this.emit("ready", { network: this });
|
|
99
|
+
}
|
|
94
100
|
this.emit("peer-candidate", { peerId });
|
|
95
101
|
}
|
|
96
102
|
receiveMessage(message) {
|
|
97
|
-
const decoded =
|
|
98
|
-
const { type, senderId
|
|
103
|
+
const decoded = cbor.decode(new Uint8Array(message));
|
|
104
|
+
const { type, senderId } = decoded;
|
|
99
105
|
const socket = this.socket;
|
|
100
106
|
if (!socket) {
|
|
101
107
|
throw new Error("Missing socket at receiveMessage");
|
|
@@ -105,19 +111,17 @@ export class BrowserWebSocketClientAdapter extends WebSocketNetworkAdapter {
|
|
|
105
111
|
}
|
|
106
112
|
switch (type) {
|
|
107
113
|
case "peer":
|
|
108
|
-
log(`peer: ${senderId}
|
|
114
|
+
log(`peer: ${senderId}`);
|
|
109
115
|
this.announceConnection(senderId);
|
|
110
116
|
break;
|
|
111
117
|
case "error":
|
|
112
|
-
log(`error: ${decoded.
|
|
118
|
+
log(`error: ${decoded.message}`);
|
|
119
|
+
break;
|
|
113
120
|
default:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
message: new Uint8Array(messageData),
|
|
119
|
-
broadcast,
|
|
120
|
-
});
|
|
121
|
+
if (!isValidMessage(decoded)) {
|
|
122
|
+
throw new Error("Invalid message received");
|
|
123
|
+
}
|
|
124
|
+
this.emit("message", decoded);
|
|
121
125
|
}
|
|
122
126
|
}
|
|
123
127
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="ws" />
|
|
2
2
|
import { WebSocket, type WebSocketServer } from "isomorphic-ws";
|
|
3
|
-
import {
|
|
3
|
+
import { NetworkAdapter, type PeerId } from "@automerge/automerge-repo";
|
|
4
|
+
import { FromServerMessage } from "./messages.js";
|
|
4
5
|
export declare class NodeWSServerAdapter extends NetworkAdapter {
|
|
5
6
|
server: WebSocketServer;
|
|
6
7
|
sockets: {
|
|
@@ -10,7 +11,7 @@ export declare class NodeWSServerAdapter extends NetworkAdapter {
|
|
|
10
11
|
connect(peerId: PeerId): void;
|
|
11
12
|
join(): void;
|
|
12
13
|
leave(): void;
|
|
13
|
-
|
|
14
|
+
send(message: FromServerMessage): void;
|
|
14
15
|
receiveMessage(message: Uint8Array, socket: WebSocket): void;
|
|
15
16
|
}
|
|
16
17
|
//# sourceMappingURL=NodeWSServerAdapter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeWSServerAdapter.d.ts","sourceRoot":"","sources":["../src/NodeWSServerAdapter.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"NodeWSServerAdapter.d.ts","sourceRoot":"","sources":["../src/NodeWSServerAdapter.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA;AAM/D,OAAO,EACL,cAAc,EAGd,KAAK,MAAM,EACZ,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAqB,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAIpE,qBAAa,mBAAoB,SAAQ,cAAc;IACrD,MAAM,EAAE,eAAe,CAAA;IACvB,OAAO,EAAE;QAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAK;gBAEjC,MAAM,EAAE,eAAe;IAKnC,OAAO,CAAC,MAAM,EAAE,MAAM;IAoBtB,IAAI;IAIJ,KAAK;IAIL,IAAI,CAAC,OAAO,EAAE,iBAAiB;IAyB/B,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS;CAuDtD"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import * as CBOR from "cbor-x";
|
|
2
1
|
import debug from "debug";
|
|
3
2
|
const log = debug("WebsocketServer");
|
|
4
|
-
import { NetworkAdapter, } from "@automerge/automerge-repo";
|
|
5
3
|
import { ProtocolV1 } from "./protocolVersion.js";
|
|
4
|
+
import { NetworkAdapter, cbor as cborHelpers, } from "@automerge/automerge-repo";
|
|
5
|
+
const { encode, decode } = cborHelpers;
|
|
6
6
|
export class NodeWSServerAdapter extends NetworkAdapter {
|
|
7
7
|
server;
|
|
8
8
|
sockets = {};
|
|
@@ -23,6 +23,7 @@ export class NodeWSServerAdapter extends NetworkAdapter {
|
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
socket.on("message", message => this.receiveMessage(message, socket));
|
|
26
|
+
this.emit("ready", { network: this });
|
|
26
27
|
});
|
|
27
28
|
}
|
|
28
29
|
join() {
|
|
@@ -31,41 +32,32 @@ export class NodeWSServerAdapter extends NetworkAdapter {
|
|
|
31
32
|
leave() {
|
|
32
33
|
// throw new Error("The server doesn't join channels.")
|
|
33
34
|
}
|
|
34
|
-
|
|
35
|
-
if (message.byteLength === 0) {
|
|
35
|
+
send(message) {
|
|
36
|
+
if ("data" in message && message.data.byteLength === 0) {
|
|
36
37
|
throw new Error("tried to send a zero-length message");
|
|
37
38
|
}
|
|
38
39
|
const senderId = this.peerId;
|
|
39
40
|
if (!senderId) {
|
|
40
41
|
throw new Error("No peerId set for the websocket server network adapter.");
|
|
41
42
|
}
|
|
42
|
-
if (this.sockets[targetId] === undefined) {
|
|
43
|
-
log(`Tried to send message to disconnected peer: ${targetId}`);
|
|
43
|
+
if (this.sockets[message.targetId] === undefined) {
|
|
44
|
+
log(`Tried to send message to disconnected peer: ${message.targetId}`);
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
46
|
-
const
|
|
47
|
-
senderId,
|
|
48
|
-
targetId,
|
|
49
|
-
channelId,
|
|
50
|
-
type: "sync",
|
|
51
|
-
message,
|
|
52
|
-
broadcast,
|
|
53
|
-
};
|
|
54
|
-
const encoded = CBOR.encode(decoded);
|
|
47
|
+
const encoded = encode(message);
|
|
55
48
|
// This incantation deals with websocket sending the whole
|
|
56
49
|
// underlying buffer even if we just have a uint8array view on it
|
|
57
50
|
const arrayBuf = encoded.buffer.slice(encoded.byteOffset, encoded.byteOffset + encoded.byteLength);
|
|
58
|
-
|
|
59
|
-
this.sockets[targetId].send(arrayBuf);
|
|
51
|
+
this.sockets[message.targetId]?.send(arrayBuf);
|
|
60
52
|
}
|
|
61
53
|
receiveMessage(message, socket) {
|
|
62
|
-
const cbor =
|
|
63
|
-
const { type,
|
|
54
|
+
const cbor = decode(message);
|
|
55
|
+
const { type, senderId } = cbor;
|
|
64
56
|
const myPeerId = this.peerId;
|
|
65
57
|
if (!myPeerId) {
|
|
66
58
|
throw new Error("Missing my peer ID.");
|
|
67
59
|
}
|
|
68
|
-
log(`[${senderId}->${myPeerId}
|
|
60
|
+
log(`[${senderId}->${myPeerId}${"documentId" in cbor ? "@" + cbor.documentId : ""}] ${type} | ${message.byteLength} bytes`);
|
|
69
61
|
switch (type) {
|
|
70
62
|
case "join":
|
|
71
63
|
// Let the rest of the system know that we have a new connection.
|
|
@@ -73,21 +65,24 @@ export class NodeWSServerAdapter extends NetworkAdapter {
|
|
|
73
65
|
this.sockets[senderId] = socket;
|
|
74
66
|
// In this client-server connection, there's only ever one peer: us!
|
|
75
67
|
// (and we pretend to be joined to every channel)
|
|
76
|
-
const selectedProtocolVersion = selectProtocol(supportedProtocolVersions);
|
|
68
|
+
const selectedProtocolVersion = selectProtocol(cbor.supportedProtocolVersions);
|
|
77
69
|
if (selectedProtocolVersion === null) {
|
|
78
|
-
|
|
70
|
+
this.send({
|
|
79
71
|
type: "error",
|
|
80
|
-
|
|
81
|
-
|
|
72
|
+
senderId: this.peerId,
|
|
73
|
+
message: "unsupported protocol version",
|
|
74
|
+
targetId: senderId,
|
|
75
|
+
});
|
|
82
76
|
this.sockets[senderId].close();
|
|
83
77
|
delete this.sockets[senderId];
|
|
84
78
|
}
|
|
85
79
|
else {
|
|
86
|
-
|
|
80
|
+
this.send({
|
|
87
81
|
type: "peer",
|
|
88
82
|
senderId: this.peerId,
|
|
89
83
|
selectedProtocolVersion: ProtocolV1,
|
|
90
|
-
|
|
84
|
+
targetId: senderId,
|
|
85
|
+
});
|
|
91
86
|
}
|
|
92
87
|
break;
|
|
93
88
|
case "leave":
|
|
@@ -96,21 +91,8 @@ export class NodeWSServerAdapter extends NetworkAdapter {
|
|
|
96
91
|
// TODO: confirm this
|
|
97
92
|
// ?
|
|
98
93
|
break;
|
|
99
|
-
// We accept both "message" and "sync" because a previous version of this
|
|
100
|
-
// codebase sent sync messages in the BrowserWebSocketClientAdapter as
|
|
101
|
-
// type "message" and we want to stay backwards compatible
|
|
102
|
-
case "message":
|
|
103
|
-
case "sync":
|
|
104
|
-
this.emit("message", {
|
|
105
|
-
senderId,
|
|
106
|
-
targetId,
|
|
107
|
-
channelId,
|
|
108
|
-
message: new Uint8Array(data),
|
|
109
|
-
broadcast,
|
|
110
|
-
});
|
|
111
|
-
break;
|
|
112
94
|
default:
|
|
113
|
-
|
|
95
|
+
this.emit("message", cbor);
|
|
114
96
|
break;
|
|
115
97
|
}
|
|
116
98
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Message, type PeerId } from "@automerge/automerge-repo";
|
|
2
|
+
import { ProtocolVersion } from "./protocolVersion.js";
|
|
3
|
+
export type LeaveMessage = {
|
|
4
|
+
type: "leave";
|
|
5
|
+
senderId: PeerId;
|
|
6
|
+
};
|
|
7
|
+
export type JoinMessage = {
|
|
8
|
+
type: "join";
|
|
9
|
+
senderId: PeerId;
|
|
10
|
+
supportedProtocolVersions: ProtocolVersion[];
|
|
11
|
+
};
|
|
12
|
+
export type PeerMessage = {
|
|
13
|
+
type: "peer";
|
|
14
|
+
senderId: PeerId;
|
|
15
|
+
selectedProtocolVersion: ProtocolVersion;
|
|
16
|
+
targetId: PeerId;
|
|
17
|
+
};
|
|
18
|
+
export type ErrorMessage = {
|
|
19
|
+
type: "error";
|
|
20
|
+
senderId: PeerId;
|
|
21
|
+
message: string;
|
|
22
|
+
targetId: PeerId;
|
|
23
|
+
};
|
|
24
|
+
export type FromClientMessage = JoinMessage | LeaveMessage | Message;
|
|
25
|
+
export type FromServerMessage = PeerMessage | ErrorMessage | Message;
|
|
26
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,2BAA2B,CAAA;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,yBAAyB,EAAE,eAAe,EAAE,CAAA;CAC7C,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,uBAAuB,EAAE,eAAe,CAAA;IACxC,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAGD,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,YAAY,GAAG,OAAO,CAAA;AACpE,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,YAAY,GAAG,OAAO,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automerge/automerge-repo-network-websocket",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.4",
|
|
4
4
|
"description": "isomorphic node/browser Websocket network adapter for Automerge Repo",
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"@automerge/automerge": "^2.1.0-alpha.
|
|
6
|
+
"@automerge/automerge": "^2.1.0-alpha.12"
|
|
7
7
|
},
|
|
8
8
|
"repository": "https://github.com/automerge/automerge-repo",
|
|
9
9
|
"author": "Peter van Hardenberg <pvh@pvh.ca>",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"test": "mocha --no-warnings --experimental-specifier-resolution=node --exit"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@automerge/automerge-repo": "^1.0.0-alpha.
|
|
20
|
+
"@automerge/automerge-repo": "^1.0.0-alpha.4",
|
|
21
21
|
"cbor-x": "^1.3.0",
|
|
22
22
|
"eventemitter3": "^4.0.7",
|
|
23
23
|
"isomorphic-ws": "^5.0.0",
|
|
@@ -34,5 +34,5 @@
|
|
|
34
34
|
"publishConfig": {
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "fbf71f0c3aaa2786a4e279f336f01d665f53ce5b"
|
|
38
38
|
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ChannelId,
|
|
3
|
-
InboundMessagePayload,
|
|
4
|
-
NetworkAdapter,
|
|
5
|
-
PeerId,
|
|
6
|
-
} from "@automerge/automerge-repo"
|
|
7
|
-
import * as CBOR from "cbor-x"
|
|
1
|
+
import { NetworkAdapter, PeerId, cbor } from "@automerge/automerge-repo"
|
|
8
2
|
import WebSocket from "isomorphic-ws"
|
|
9
3
|
|
|
10
4
|
import debug from "debug"
|
|
11
|
-
|
|
12
|
-
import {
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
FromClientMessage,
|
|
8
|
+
FromServerMessage,
|
|
9
|
+
JoinMessage,
|
|
10
|
+
} from "./messages.js"
|
|
11
|
+
import { ProtocolV1 } from "./protocolVersion.js"
|
|
12
|
+
import { isValidMessage } from "@automerge/automerge-repo/dist/network/messages.js"
|
|
13
|
+
|
|
13
14
|
const log = debug("WebsocketClient")
|
|
14
15
|
|
|
15
16
|
abstract class WebSocketNetworkAdapter extends NetworkAdapter {
|
|
@@ -17,9 +18,11 @@ abstract class WebSocketNetworkAdapter extends NetworkAdapter {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export class BrowserWebSocketClientAdapter extends WebSocketNetworkAdapter {
|
|
20
|
-
// Type trickery required for platform independence,
|
|
21
|
+
// Type trickery required for platform independence,
|
|
21
22
|
// see https://stackoverflow.com/questions/45802988/typescript-use-correct-version-of-settimeout-node-vs-window
|
|
22
23
|
timerId?: ReturnType<typeof setTimeout>
|
|
24
|
+
#startupComplete: boolean = false
|
|
25
|
+
|
|
23
26
|
url: string
|
|
24
27
|
|
|
25
28
|
constructor(url: string) {
|
|
@@ -55,6 +58,16 @@ export class BrowserWebSocketClientAdapter extends WebSocketNetworkAdapter {
|
|
|
55
58
|
this.socket.addEventListener("message", (event: WebSocket.MessageEvent) =>
|
|
56
59
|
this.receiveMessage(event.data as Uint8Array)
|
|
57
60
|
)
|
|
61
|
+
|
|
62
|
+
// mark this adapter as ready if we haven't received an ack in 1 second.
|
|
63
|
+
// We might hear back from the other end at some point but we shouldn't
|
|
64
|
+
// hold up marking things as unavailable for any longer
|
|
65
|
+
setTimeout(() => {
|
|
66
|
+
if (!this.#startupComplete) {
|
|
67
|
+
this.#startupComplete = true
|
|
68
|
+
this.emit("ready", { network: this })
|
|
69
|
+
}
|
|
70
|
+
}, 1000)
|
|
58
71
|
}
|
|
59
72
|
|
|
60
73
|
join() {
|
|
@@ -62,9 +75,7 @@ export class BrowserWebSocketClientAdapter extends WebSocketNetworkAdapter {
|
|
|
62
75
|
throw new Error("WTF, get a socket")
|
|
63
76
|
}
|
|
64
77
|
if (this.socket.readyState === WebSocket.OPEN) {
|
|
65
|
-
this.
|
|
66
|
-
CBOR.encode(joinMessage(this.peerId))
|
|
67
|
-
)
|
|
78
|
+
this.send(joinMessage(this.peerId!))
|
|
68
79
|
} else {
|
|
69
80
|
this.socket.addEventListener(
|
|
70
81
|
"open",
|
|
@@ -72,9 +83,7 @@ export class BrowserWebSocketClientAdapter extends WebSocketNetworkAdapter {
|
|
|
72
83
|
if (!this.socket) {
|
|
73
84
|
throw new Error("WTF, get a socket")
|
|
74
85
|
}
|
|
75
|
-
this.
|
|
76
|
-
CBOR.encode(joinMessage(this.peerId))
|
|
77
|
-
)
|
|
86
|
+
this.send(joinMessage(this.peerId!))
|
|
78
87
|
},
|
|
79
88
|
{ once: true }
|
|
80
89
|
)
|
|
@@ -85,43 +94,31 @@ export class BrowserWebSocketClientAdapter extends WebSocketNetworkAdapter {
|
|
|
85
94
|
if (!this.socket) {
|
|
86
95
|
throw new Error("WTF, get a socket")
|
|
87
96
|
}
|
|
88
|
-
this.
|
|
97
|
+
this.send({ type: "leave", senderId: this.peerId! })
|
|
89
98
|
}
|
|
90
99
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
channelId: ChannelId,
|
|
94
|
-
message: Uint8Array,
|
|
95
|
-
broadcast: boolean
|
|
96
|
-
) {
|
|
97
|
-
if (message.byteLength === 0) {
|
|
100
|
+
send(message: FromClientMessage) {
|
|
101
|
+
if ("data" in message && message.data.byteLength === 0) {
|
|
98
102
|
throw new Error("tried to send a zero-length message")
|
|
99
103
|
}
|
|
104
|
+
|
|
100
105
|
if (!this.peerId) {
|
|
101
106
|
throw new Error("Why don't we have a PeerID?")
|
|
102
107
|
}
|
|
103
108
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
targetId,
|
|
107
|
-
channelId,
|
|
108
|
-
type: "sync",
|
|
109
|
-
message,
|
|
110
|
-
broadcast,
|
|
109
|
+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
|
|
110
|
+
throw new Error("Websocket Socket not ready!")
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
const encoded =
|
|
114
|
-
|
|
113
|
+
const encoded = cbor.encode(message)
|
|
115
114
|
// This incantation deals with websocket sending the whole
|
|
116
115
|
// underlying buffer even if we just have a uint8array view on it
|
|
117
116
|
const arrayBuf = encoded.buffer.slice(
|
|
118
117
|
encoded.byteOffset,
|
|
119
118
|
encoded.byteOffset + encoded.byteLength
|
|
120
119
|
)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
this.socket.send(arrayBuf)
|
|
120
|
+
|
|
121
|
+
this.socket?.send(arrayBuf)
|
|
125
122
|
}
|
|
126
123
|
|
|
127
124
|
announceConnection(peerId: PeerId) {
|
|
@@ -130,21 +127,17 @@ export class BrowserWebSocketClientAdapter extends WebSocketNetworkAdapter {
|
|
|
130
127
|
if (!myPeerId) {
|
|
131
128
|
throw new Error("we should have a peer ID by now")
|
|
132
129
|
}
|
|
133
|
-
|
|
130
|
+
if (!this.#startupComplete) {
|
|
131
|
+
this.#startupComplete = true
|
|
132
|
+
this.emit("ready", { network: this })
|
|
133
|
+
}
|
|
134
134
|
this.emit("peer-candidate", { peerId })
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
receiveMessage(message: Uint8Array) {
|
|
138
|
-
const decoded:
|
|
138
|
+
const decoded: FromServerMessage = cbor.decode(new Uint8Array(message))
|
|
139
139
|
|
|
140
|
-
const {
|
|
141
|
-
type,
|
|
142
|
-
senderId,
|
|
143
|
-
targetId,
|
|
144
|
-
channelId,
|
|
145
|
-
message: messageData,
|
|
146
|
-
broadcast,
|
|
147
|
-
} = decoded
|
|
140
|
+
const { type, senderId } = decoded
|
|
148
141
|
|
|
149
142
|
const socket = this.socket
|
|
150
143
|
if (!socket) {
|
|
@@ -157,24 +150,22 @@ export class BrowserWebSocketClientAdapter extends WebSocketNetworkAdapter {
|
|
|
157
150
|
|
|
158
151
|
switch (type) {
|
|
159
152
|
case "peer":
|
|
160
|
-
log(`peer: ${senderId}
|
|
153
|
+
log(`peer: ${senderId}`)
|
|
161
154
|
this.announceConnection(senderId)
|
|
162
155
|
break
|
|
163
156
|
case "error":
|
|
164
|
-
log(`error: ${decoded.
|
|
157
|
+
log(`error: ${decoded.message}`)
|
|
158
|
+
break
|
|
165
159
|
default:
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
message: new Uint8Array(messageData),
|
|
171
|
-
broadcast,
|
|
172
|
-
})
|
|
160
|
+
if (!isValidMessage(decoded)) {
|
|
161
|
+
throw new Error("Invalid message received")
|
|
162
|
+
}
|
|
163
|
+
this.emit("message", decoded)
|
|
173
164
|
}
|
|
174
165
|
}
|
|
175
166
|
}
|
|
176
167
|
|
|
177
|
-
function joinMessage(senderId
|
|
168
|
+
function joinMessage(senderId: PeerId): JoinMessage {
|
|
178
169
|
return {
|
|
179
170
|
type: "join",
|
|
180
171
|
senderId,
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import * as CBOR from "cbor-x"
|
|
2
1
|
import { WebSocket, type WebSocketServer } from "isomorphic-ws"
|
|
3
2
|
|
|
4
3
|
import debug from "debug"
|
|
5
4
|
const log = debug("WebsocketServer")
|
|
6
5
|
|
|
6
|
+
import { ProtocolV1, ProtocolVersion } from "./protocolVersion.js"
|
|
7
7
|
import {
|
|
8
|
-
ChannelId,
|
|
9
|
-
InboundMessagePayload,
|
|
10
8
|
NetworkAdapter,
|
|
11
|
-
|
|
9
|
+
cbor as cborHelpers,
|
|
10
|
+
type NetworkAdapterMessage,
|
|
11
|
+
type PeerId,
|
|
12
12
|
} from "@automerge/automerge-repo"
|
|
13
|
-
import {
|
|
14
|
-
|
|
13
|
+
import { FromClientMessage, FromServerMessage } from "./messages.js"
|
|
14
|
+
|
|
15
|
+
const { encode, decode } = cborHelpers
|
|
15
16
|
|
|
16
17
|
export class NodeWSServerAdapter extends NetworkAdapter {
|
|
17
18
|
server: WebSocketServer
|
|
@@ -38,6 +39,7 @@ export class NodeWSServerAdapter extends NetworkAdapter {
|
|
|
38
39
|
socket.on("message", message =>
|
|
39
40
|
this.receiveMessage(message as Uint8Array, socket)
|
|
40
41
|
)
|
|
42
|
+
this.emit("ready", {network: this})
|
|
41
43
|
})
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -49,34 +51,21 @@ export class NodeWSServerAdapter extends NetworkAdapter {
|
|
|
49
51
|
// throw new Error("The server doesn't join channels.")
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
channelId: ChannelId,
|
|
55
|
-
message: Uint8Array,
|
|
56
|
-
broadcast: boolean
|
|
57
|
-
) {
|
|
58
|
-
if (message.byteLength === 0) {
|
|
54
|
+
send(message: FromServerMessage) {
|
|
55
|
+
if ("data" in message && message.data.byteLength === 0) {
|
|
59
56
|
throw new Error("tried to send a zero-length message")
|
|
60
57
|
}
|
|
61
58
|
const senderId = this.peerId
|
|
62
59
|
if (!senderId) {
|
|
63
60
|
throw new Error("No peerId set for the websocket server network adapter.")
|
|
64
61
|
}
|
|
65
|
-
if (this.sockets[targetId] === undefined) {
|
|
66
|
-
log(`Tried to send message to disconnected peer: ${targetId}`)
|
|
67
|
-
return
|
|
68
|
-
}
|
|
69
62
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
channelId,
|
|
74
|
-
type: "sync",
|
|
75
|
-
message,
|
|
76
|
-
broadcast,
|
|
63
|
+
if (this.sockets[message.targetId] === undefined) {
|
|
64
|
+
log(`Tried to send message to disconnected peer: ${message.targetId}`)
|
|
65
|
+
return
|
|
77
66
|
}
|
|
78
|
-
const encoded = CBOR.encode(decoded)
|
|
79
67
|
|
|
68
|
+
const encoded = encode(message)
|
|
80
69
|
// This incantation deals with websocket sending the whole
|
|
81
70
|
// underlying buffer even if we just have a uint8array view on it
|
|
82
71
|
const arrayBuf = encoded.buffer.slice(
|
|
@@ -84,32 +73,22 @@ export class NodeWSServerAdapter extends NetworkAdapter {
|
|
|
84
73
|
encoded.byteOffset + encoded.byteLength
|
|
85
74
|
)
|
|
86
75
|
|
|
87
|
-
|
|
88
|
-
`[${senderId}->${targetId}@${channelId}] "sync" | ${arrayBuf.byteLength} bytes`
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
this.sockets[targetId].send(arrayBuf)
|
|
76
|
+
this.sockets[message.targetId]?.send(arrayBuf)
|
|
92
77
|
}
|
|
93
78
|
|
|
94
79
|
receiveMessage(message: Uint8Array, socket: WebSocket) {
|
|
95
|
-
const cbor:
|
|
96
|
-
|
|
97
|
-
const {
|
|
98
|
-
type,
|
|
99
|
-
channelId,
|
|
100
|
-
senderId,
|
|
101
|
-
targetId,
|
|
102
|
-
message: data,
|
|
103
|
-
broadcast,
|
|
104
|
-
supportedProtocolVersions,
|
|
105
|
-
} = cbor
|
|
80
|
+
const cbor: FromClientMessage = decode(message)
|
|
81
|
+
|
|
82
|
+
const { type, senderId } = cbor
|
|
106
83
|
|
|
107
84
|
const myPeerId = this.peerId
|
|
108
85
|
if (!myPeerId) {
|
|
109
86
|
throw new Error("Missing my peer ID.")
|
|
110
87
|
}
|
|
111
88
|
log(
|
|
112
|
-
`[${senderId}->${myPeerId}
|
|
89
|
+
`[${senderId}->${myPeerId}${
|
|
90
|
+
"documentId" in cbor ? "@" + cbor.documentId : ""
|
|
91
|
+
}] ${type} | ${message.byteLength} bytes`
|
|
113
92
|
)
|
|
114
93
|
switch (type) {
|
|
115
94
|
case "join":
|
|
@@ -120,25 +99,24 @@ export class NodeWSServerAdapter extends NetworkAdapter {
|
|
|
120
99
|
// In this client-server connection, there's only ever one peer: us!
|
|
121
100
|
// (and we pretend to be joined to every channel)
|
|
122
101
|
const selectedProtocolVersion = selectProtocol(
|
|
123
|
-
supportedProtocolVersions
|
|
102
|
+
cbor.supportedProtocolVersions
|
|
124
103
|
)
|
|
125
104
|
if (selectedProtocolVersion === null) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
)
|
|
105
|
+
this.send({
|
|
106
|
+
type: "error",
|
|
107
|
+
senderId: this.peerId!,
|
|
108
|
+
message: "unsupported protocol version",
|
|
109
|
+
targetId: senderId,
|
|
110
|
+
})
|
|
132
111
|
this.sockets[senderId].close()
|
|
133
112
|
delete this.sockets[senderId]
|
|
134
113
|
} else {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
)
|
|
114
|
+
this.send({
|
|
115
|
+
type: "peer",
|
|
116
|
+
senderId: this.peerId!,
|
|
117
|
+
selectedProtocolVersion: ProtocolV1,
|
|
118
|
+
targetId: senderId,
|
|
119
|
+
})
|
|
142
120
|
}
|
|
143
121
|
break
|
|
144
122
|
case "leave":
|
|
@@ -148,21 +126,8 @@ export class NodeWSServerAdapter extends NetworkAdapter {
|
|
|
148
126
|
// ?
|
|
149
127
|
break
|
|
150
128
|
|
|
151
|
-
// We accept both "message" and "sync" because a previous version of this
|
|
152
|
-
// codebase sent sync messages in the BrowserWebSocketClientAdapter as
|
|
153
|
-
// type "message" and we want to stay backwards compatible
|
|
154
|
-
case "message":
|
|
155
|
-
case "sync":
|
|
156
|
-
this.emit("message", {
|
|
157
|
-
senderId,
|
|
158
|
-
targetId,
|
|
159
|
-
channelId,
|
|
160
|
-
message: new Uint8Array(data),
|
|
161
|
-
broadcast,
|
|
162
|
-
})
|
|
163
|
-
break
|
|
164
129
|
default:
|
|
165
|
-
|
|
130
|
+
this.emit("message", cbor)
|
|
166
131
|
break
|
|
167
132
|
}
|
|
168
133
|
}
|
package/src/messages.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type Message, type PeerId } from "@automerge/automerge-repo"
|
|
2
|
+
import { ProtocolVersion } from "./protocolVersion.js"
|
|
3
|
+
|
|
4
|
+
export type LeaveMessage = {
|
|
5
|
+
type: "leave"
|
|
6
|
+
senderId: PeerId
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type JoinMessage = {
|
|
10
|
+
type: "join"
|
|
11
|
+
senderId: PeerId
|
|
12
|
+
supportedProtocolVersions: ProtocolVersion[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type PeerMessage = {
|
|
16
|
+
type: "peer"
|
|
17
|
+
senderId: PeerId
|
|
18
|
+
selectedProtocolVersion: ProtocolVersion
|
|
19
|
+
targetId: PeerId
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type ErrorMessage = {
|
|
23
|
+
type: "error"
|
|
24
|
+
senderId: PeerId
|
|
25
|
+
message: string
|
|
26
|
+
targetId: PeerId
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// This adapter doesn't use NetworkAdapterMessage, it has its own idea of how to handle join/leave
|
|
30
|
+
export type FromClientMessage = JoinMessage | LeaveMessage | Message
|
|
31
|
+
export type FromServerMessage = PeerMessage | ErrorMessage | Message
|
package/test/Websocket.test.ts
CHANGED
|
@@ -1,19 +1,32 @@
|
|
|
1
|
-
import { runAdapterTests } from "../../automerge-repo/src/helpers/tests/network-adapter-tests"
|
|
2
|
-
import { BrowserWebSocketClientAdapter } from "../src/BrowserWebSocketClientAdapter"
|
|
3
|
-
import { NodeWSServerAdapter } from "../src/NodeWSServerAdapter"
|
|
4
|
-
import { startServer } from "./utilities/WebSockets"
|
|
1
|
+
import { runAdapterTests } from "../../automerge-repo/src/helpers/tests/network-adapter-tests.js"
|
|
2
|
+
import { BrowserWebSocketClientAdapter } from "../src/BrowserWebSocketClientAdapter.js"
|
|
3
|
+
import { NodeWSServerAdapter } from "../src/NodeWSServerAdapter.js"
|
|
4
|
+
import { startServer } from "./utilities/WebSockets.js"
|
|
5
5
|
import * as CBOR from "cbor-x"
|
|
6
|
-
import WebSocket, {AddressInfo}
|
|
6
|
+
import WebSocket, { AddressInfo } from "ws"
|
|
7
7
|
import { assert } from "chai"
|
|
8
|
-
import {
|
|
8
|
+
import { PeerId, Repo } from "@automerge/automerge-repo"
|
|
9
9
|
import { once } from "events"
|
|
10
10
|
|
|
11
11
|
describe("Websocket adapters", async () => {
|
|
12
12
|
let port = 8080
|
|
13
13
|
|
|
14
14
|
runAdapterTests(async () => {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
let socket: WebSocket.Server | undefined = undefined
|
|
16
|
+
let server: any
|
|
17
|
+
|
|
18
|
+
while (socket === undefined) {
|
|
19
|
+
try {
|
|
20
|
+
; ({ socket, server } = await startServer(port))
|
|
21
|
+
} catch (e: any) {
|
|
22
|
+
if (e.code === "EADDRINUSE") {
|
|
23
|
+
port++
|
|
24
|
+
} else {
|
|
25
|
+
throw e
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
17
30
|
const serverAdapter = new NodeWSServerAdapter(socket)
|
|
18
31
|
|
|
19
32
|
const serverUrl = `ws://localhost:${port}`
|
|
@@ -25,19 +38,19 @@ describe("Websocket adapters", async () => {
|
|
|
25
38
|
server.close()
|
|
26
39
|
}
|
|
27
40
|
|
|
28
|
-
return { adapters: [
|
|
41
|
+
return { adapters: [aliceAdapter, serverAdapter, bobAdapter], teardown }
|
|
29
42
|
})
|
|
30
43
|
})
|
|
31
44
|
|
|
32
45
|
describe("The BrowserWebSocketClientAdapter", () => {
|
|
33
46
|
it("should advertise the protocol versions it supports in its join message", async () => {
|
|
34
|
-
const
|
|
47
|
+
const { socket, server } = await startServer(0)
|
|
35
48
|
let port = (server.address()!! as AddressInfo).port
|
|
36
49
|
const serverUrl = `ws://localhost:${port}`
|
|
37
50
|
const helloPromise = firstMessage(socket)
|
|
38
51
|
|
|
39
52
|
const client = new BrowserWebSocketClientAdapter(serverUrl)
|
|
40
|
-
const repo = new Repo({network: [client], peerId: "browser" as PeerId})
|
|
53
|
+
const repo = new Repo({ network: [client], peerId: "browser" as PeerId })
|
|
41
54
|
|
|
42
55
|
const hello = await helloPromise
|
|
43
56
|
|
|
@@ -45,8 +58,8 @@ describe("The BrowserWebSocketClientAdapter", () => {
|
|
|
45
58
|
assert.deepEqual(message, {
|
|
46
59
|
type: "join",
|
|
47
60
|
senderId: "browser",
|
|
48
|
-
supportedProtocolVersions: ["1"]
|
|
49
|
-
})
|
|
61
|
+
supportedProtocolVersions: ["1"],
|
|
62
|
+
})
|
|
50
63
|
})
|
|
51
64
|
})
|
|
52
65
|
|
|
@@ -55,12 +68,13 @@ describe("The NodeWSServerAdapter", () => {
|
|
|
55
68
|
const response = await serverHelloGivenClientHello({
|
|
56
69
|
type: "join",
|
|
57
70
|
senderId: "browser",
|
|
58
|
-
supportedProtocolVersions: ["1"]
|
|
71
|
+
supportedProtocolVersions: ["1"],
|
|
59
72
|
})
|
|
60
73
|
assert.deepEqual<any>(response, {
|
|
61
74
|
type: "peer",
|
|
62
75
|
senderId: "server",
|
|
63
|
-
|
|
76
|
+
targetId: "browser",
|
|
77
|
+
selectedProtocolVersion: "1",
|
|
64
78
|
})
|
|
65
79
|
})
|
|
66
80
|
|
|
@@ -68,11 +82,13 @@ describe("The NodeWSServerAdapter", () => {
|
|
|
68
82
|
const response = await serverHelloGivenClientHello({
|
|
69
83
|
type: "join",
|
|
70
84
|
senderId: "browser",
|
|
71
|
-
supportedProtocolVersions: ["fake"]
|
|
85
|
+
supportedProtocolVersions: ["fake"],
|
|
72
86
|
})
|
|
73
87
|
assert.deepEqual<any>(response, {
|
|
74
88
|
type: "error",
|
|
75
|
-
|
|
89
|
+
senderId: "server",
|
|
90
|
+
targetId: "browser",
|
|
91
|
+
message: "unsupported protocol version",
|
|
76
92
|
})
|
|
77
93
|
})
|
|
78
94
|
|
|
@@ -84,17 +100,20 @@ describe("The NodeWSServerAdapter", () => {
|
|
|
84
100
|
assert.deepEqual<any>(response, {
|
|
85
101
|
type: "peer",
|
|
86
102
|
senderId: "server",
|
|
87
|
-
|
|
103
|
+
targetId: "browser",
|
|
104
|
+
selectedProtocolVersion: "1",
|
|
88
105
|
})
|
|
89
106
|
})
|
|
90
107
|
})
|
|
91
108
|
|
|
92
|
-
async function serverHelloGivenClientHello(
|
|
93
|
-
|
|
109
|
+
async function serverHelloGivenClientHello(
|
|
110
|
+
clientHello: Object
|
|
111
|
+
): Promise<Object | null> {
|
|
112
|
+
const { socket, server } = await startServer(0)
|
|
94
113
|
let port = (server.address()!! as AddressInfo).port
|
|
95
114
|
const serverUrl = `ws://localhost:${port}`
|
|
96
115
|
const adapter = new NodeWSServerAdapter(socket)
|
|
97
|
-
const repo = new Repo({network: [adapter], peerId: "server" as PeerId})
|
|
116
|
+
const repo = new Repo({ network: [adapter], peerId: "server" as PeerId })
|
|
98
117
|
|
|
99
118
|
const clientSocket = new WebSocket(serverUrl)
|
|
100
119
|
await once(clientSocket, "open")
|
|
@@ -107,9 +126,11 @@ async function serverHelloGivenClientHello(clientHello: Object): Promise<Object
|
|
|
107
126
|
return message
|
|
108
127
|
}
|
|
109
128
|
|
|
110
|
-
async function firstMessage(
|
|
129
|
+
async function firstMessage(
|
|
130
|
+
socket: WebSocket.Server<any>
|
|
131
|
+
): Promise<Object | null> {
|
|
111
132
|
return new Promise((resolve, reject) => {
|
|
112
|
-
socket.once("connection",
|
|
133
|
+
socket.once("connection", ws => {
|
|
113
134
|
ws.once("message", (message: any) => {
|
|
114
135
|
resolve(message)
|
|
115
136
|
})
|
|
@@ -117,7 +138,7 @@ async function firstMessage(socket: WebSocket.Server<any>): Promise<Object | nul
|
|
|
117
138
|
reject(error)
|
|
118
139
|
})
|
|
119
140
|
})
|
|
120
|
-
socket.once("error",
|
|
141
|
+
socket.once("error", error => {
|
|
121
142
|
reject(error)
|
|
122
143
|
})
|
|
123
144
|
})
|
package/tsconfig.json
CHANGED
package/dist/message.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { type InboundMessagePayload } from "@automerge/automerge-repo";
|
|
2
|
-
import { ProtocolVersion } from "./protocolVersion.js";
|
|
3
|
-
export interface InboundWebSocketMessage extends InboundMessagePayload {
|
|
4
|
-
supportedProtocolVersions?: ProtocolVersion[];
|
|
5
|
-
}
|
|
6
|
-
export interface OutboundWebSocketMessage extends InboundMessagePayload {
|
|
7
|
-
errorMessage?: string;
|
|
8
|
-
}
|
|
9
|
-
//# sourceMappingURL=message.d.ts.map
|
package/dist/message.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../src/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,2BAA2B,CAAA;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,WAAW,uBAAwB,SAAQ,qBAAqB;IACpE,yBAAyB,CAAC,EAAE,eAAe,EAAE,CAAA;CAC9C;AAED,MAAM,WAAW,wBAAyB,SAAQ,qBAAqB;IACrE,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB"}
|
package/src/message.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { type InboundMessagePayload } from "@automerge/automerge-repo"
|
|
2
|
-
import { ProtocolVersion } from "./protocolVersion.js"
|
|
3
|
-
|
|
4
|
-
export interface InboundWebSocketMessage extends InboundMessagePayload {
|
|
5
|
-
supportedProtocolVersions?: ProtocolVersion[]
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface OutboundWebSocketMessage extends InboundMessagePayload {
|
|
9
|
-
errorMessage?: string
|
|
10
|
-
}
|
|
File without changes
|