-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathencoding.ts
111 lines (100 loc) · 3.13 KB
/
encoding.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { z } from "zod";
export type RouterMessageHeader = z.infer<typeof routerMessageHeader>;
export const routerMessageHeader = z.union([
z.tuple([
// The kind of message
z.literal("join"),
// The DID of the peer involved
z.string(),
// The connection ID involved
z.string(),
// The document ID that they are joining
z.string(),
]),
z.tuple([
// The kind of message
z.literal("leave"),
// The DID of the peer involved
z.string(),
// The connection that has left ( or all of them if null)
z.string(),
// The document ID that they are leaving ( or all of them if null )
z.string(),
]),
z.tuple([
// The kind of message
z.literal("send"),
// The DID of the peer involved
z.string(),
// The connection ID involved
z.string(),
// The document ID the message is about
z.string(),
]),
]);
export type PeerMessageHeader = z.infer<typeof peerMessageHeader>;
export const peerMessageHeader = z.union([
// Sets the DocIds that this peer is interested in getting "join" and "leave" messages from.
z.tuple([z.literal("listen")]).rest(z.string()),
// Sends a message to another peer's ( did, connectionId )
z.tuple([
z.literal("send"),
// The other peer's DID
z.string(),
// The other peer's connection ID
z.string(),
// The DocID the message is about
z.string(),
]),
]);
export type RawMessage<T> = {
header: T;
body: Uint8Array;
};
const sizeOfU32 = 4;
export function parseRawMessage<T>(data: ArrayBuffer): RawMessage<T> {
const headerLength = new DataView(data).getUint32(0, true);
const headerSlice = data.slice(sizeOfU32, sizeOfU32 + headerLength);
const headerTxt = new TextDecoder().decode(headerSlice);
const header = JSON.parse(headerTxt) as T;
return {
header,
body: new Uint8Array(data.slice(sizeOfU32 + headerLength)),
};
}
export function encodeRawMessage<T>(message: RawMessage<T>): ArrayBuffer {
const header = new TextEncoder().encode(JSON.stringify(message.header));
const headerLength = header.length;
const encodedLength = sizeOfU32 + headerLength + message.body.length;
const encoded = new Uint8Array(encodedLength);
new DataView(encoded.buffer).setUint32(0, headerLength, true);
encoded.set(header, sizeOfU32);
encoded.set(message.body, sizeOfU32 + headerLength);
return encoded.buffer;
}
export function parseMessageWithSchema<T>(
schema: z.ZodType<T>,
data: ArrayBuffer
): [T, Uint8Array] | Error {
const errorMsg = "Error parsing peer message";
let rawMessage: RawMessage<T>;
try {
rawMessage = parseRawMessage(data);
} catch (_) {
return new Error(errorMsg);
}
const header = schema.safeParse(rawMessage.header);
if (header.error || !header.data)
return new Error(`${errorMsg}: ${header.error}`);
return [header.data, rawMessage.body];
}
export function parsePeerMessage(
data: ArrayBuffer
): [PeerMessageHeader, Uint8Array] | Error {
return parseMessageWithSchema(peerMessageHeader, data);
}
export function parseRouterMessage(
data: ArrayBuffer
): [RouterMessageHeader, Uint8Array] | Error {
return parseMessageWithSchema(routerMessageHeader, data);
}