@meshagent/meshagent 0.37.2 → 0.38.1
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/CHANGELOG.md +7 -0
- package/dist/browser/agent.js +74 -10
- package/dist/browser/developer-client.js +3 -0
- package/dist/browser/helpers.d.ts +2 -2
- package/dist/browser/helpers.js +1 -1
- package/dist/browser/meshagent-client.d.ts +25 -0
- package/dist/browser/meshagent-client.js +65 -0
- package/dist/browser/messaging-client.d.ts +29 -16
- package/dist/browser/messaging-client.js +256 -154
- package/dist/browser/participant.d.ts +7 -2
- package/dist/browser/participant.js +9 -9
- package/dist/browser/protocol.d.ts +85 -28
- package/dist/browser/protocol.js +356 -119
- package/dist/browser/room-client.d.ts +165 -29
- package/dist/browser/room-client.js +1114 -74
- package/dist/browser/room-event.d.ts +11 -0
- package/dist/browser/room-event.js +21 -1
- package/dist/browser/room-server-client.d.ts +2 -0
- package/dist/browser/room-server-client.js +6 -0
- package/dist/browser/runtime.d.ts +1 -1
- package/dist/browser/runtime.js +3 -1
- package/dist/browser/secrets-client.js +6 -2
- package/dist/browser/storage-client.d.ts +1 -0
- package/dist/browser/storage-client.js +9 -0
- package/dist/browser/sync-client.d.ts +16 -14
- package/dist/browser/sync-client.js +195 -116
- package/dist/esm/agent.js +74 -10
- package/dist/esm/developer-client.js +3 -0
- package/dist/esm/helpers.d.ts +2 -2
- package/dist/esm/helpers.js +1 -1
- package/dist/esm/meshagent-client.d.ts +25 -0
- package/dist/esm/meshagent-client.js +65 -0
- package/dist/esm/messaging-client.d.ts +29 -16
- package/dist/esm/messaging-client.js +256 -154
- package/dist/esm/participant.d.ts +7 -2
- package/dist/esm/participant.js +9 -9
- package/dist/esm/protocol.d.ts +85 -28
- package/dist/esm/protocol.js +352 -118
- package/dist/esm/room-client.d.ts +165 -29
- package/dist/esm/room-client.js +1112 -73
- package/dist/esm/room-event.d.ts +11 -0
- package/dist/esm/room-event.js +19 -0
- package/dist/esm/room-server-client.d.ts +2 -0
- package/dist/esm/room-server-client.js +7 -1
- package/dist/esm/runtime.d.ts +1 -1
- package/dist/esm/runtime.js +1 -1
- package/dist/esm/secrets-client.js +6 -2
- package/dist/esm/storage-client.d.ts +1 -0
- package/dist/esm/storage-client.js +9 -0
- package/dist/esm/sync-client.d.ts +16 -14
- package/dist/esm/sync-client.js +196 -117
- package/dist/node/agent.js +74 -10
- package/dist/node/developer-client.js +3 -0
- package/dist/node/helpers.d.ts +2 -2
- package/dist/node/helpers.js +1 -1
- package/dist/node/meshagent-client.d.ts +25 -0
- package/dist/node/meshagent-client.js +65 -0
- package/dist/node/messaging-client.d.ts +29 -16
- package/dist/node/messaging-client.js +256 -154
- package/dist/node/participant.d.ts +7 -2
- package/dist/node/participant.js +9 -9
- package/dist/node/protocol.d.ts +85 -28
- package/dist/node/protocol.js +356 -119
- package/dist/node/room-client.d.ts +165 -29
- package/dist/node/room-client.js +1114 -74
- package/dist/node/room-event.d.ts +11 -0
- package/dist/node/room-event.js +21 -1
- package/dist/node/room-server-client.d.ts +2 -0
- package/dist/node/room-server-client.js +6 -0
- package/dist/node/runtime.d.ts +1 -1
- package/dist/node/runtime.js +3 -1
- package/dist/node/secrets-client.js +6 -2
- package/dist/node/storage-client.d.ts +1 -0
- package/dist/node/storage-client.js +9 -0
- package/dist/node/sync-client.d.ts +16 -14
- package/dist/node/sync-client.js +195 -116
- package/package.json +6 -3
package/dist/esm/protocol.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import WebSocket from "isomorphic-ws";
|
|
2
|
-
import { mergeUint8Arrays, decoder, encoder, unpackMessage } from "./utils";
|
|
3
2
|
import { Completer } from "./completer";
|
|
3
|
+
import { decoder, encoder, mergeUint8Arrays, unpackMessage } from "./utils";
|
|
4
4
|
class ProtocolMessage {
|
|
5
5
|
constructor({ id, type, data }) {
|
|
6
6
|
this.id = id;
|
|
@@ -9,8 +9,42 @@ class ProtocolMessage {
|
|
|
9
9
|
this.sent = new Completer();
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
+
export var ProtocolCloseKind;
|
|
13
|
+
(function (ProtocolCloseKind) {
|
|
14
|
+
ProtocolCloseKind["CLIENT"] = "client";
|
|
15
|
+
ProtocolCloseKind["SERVER"] = "server";
|
|
16
|
+
ProtocolCloseKind["ERROR"] = "error";
|
|
17
|
+
})(ProtocolCloseKind || (ProtocolCloseKind = {}));
|
|
18
|
+
export class ProtocolReconnectUnsupportedException extends Error {
|
|
19
|
+
constructor(message) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = "ProtocolReconnectUnsupportedException";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export class ProtocolCloseException extends Error {
|
|
25
|
+
constructor({ closeCode, reason }) {
|
|
26
|
+
super(reason == null || reason.trim().length === 0 ? `connection closed with status ${closeCode}` : reason);
|
|
27
|
+
this.name = "ProtocolCloseException";
|
|
28
|
+
this.closeCode = closeCode;
|
|
29
|
+
this.reason = reason;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class ProtocolHandshakeException extends Error {
|
|
33
|
+
constructor({ statusCode, statusText }) {
|
|
34
|
+
const normalizedStatusText = statusText?.trim();
|
|
35
|
+
super(normalizedStatusText == null || normalizedStatusText.length === 0
|
|
36
|
+
? `websocket connect failed with status ${statusCode}`
|
|
37
|
+
: `websocket connect failed with status ${statusCode}: ${normalizedStatusText}`);
|
|
38
|
+
this.name = "ProtocolHandshakeException";
|
|
39
|
+
this.statusCode = statusCode;
|
|
40
|
+
this.statusText = normalizedStatusText;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function isNodeRuntime() {
|
|
44
|
+
return typeof process !== "undefined" && process.release?.name === "node";
|
|
45
|
+
}
|
|
12
46
|
export class StreamProtocolChannel {
|
|
13
|
-
constructor({ input, output }) {
|
|
47
|
+
constructor({ input, output, }) {
|
|
14
48
|
this.started = false;
|
|
15
49
|
this._iterator = null;
|
|
16
50
|
this.input = input;
|
|
@@ -22,29 +56,24 @@ export class StreamProtocolChannel {
|
|
|
22
56
|
}
|
|
23
57
|
this.started = true;
|
|
24
58
|
(async () => {
|
|
25
|
-
this._iterator?.return(
|
|
59
|
+
this._iterator?.return(undefined);
|
|
26
60
|
try {
|
|
27
61
|
this._iterator = this.input.stream();
|
|
28
62
|
for await (const message of this._iterator) {
|
|
29
|
-
|
|
30
|
-
onDataReceived(message);
|
|
31
|
-
}
|
|
63
|
+
onDataReceived(message);
|
|
32
64
|
}
|
|
65
|
+
onDone?.();
|
|
33
66
|
}
|
|
34
67
|
catch (error) {
|
|
35
|
-
|
|
36
|
-
onError(error);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
finally {
|
|
40
|
-
if (onDone) {
|
|
41
|
-
onDone();
|
|
42
|
-
}
|
|
68
|
+
onError?.(error);
|
|
43
69
|
}
|
|
44
|
-
})()
|
|
70
|
+
})().catch((error) => {
|
|
71
|
+
onError?.(error);
|
|
72
|
+
});
|
|
45
73
|
}
|
|
46
74
|
dispose() {
|
|
47
|
-
this.
|
|
75
|
+
this.started = false;
|
|
76
|
+
this._iterator?.return(undefined);
|
|
48
77
|
this._iterator = null;
|
|
49
78
|
this.input.close();
|
|
50
79
|
}
|
|
@@ -56,54 +85,115 @@ export class WebSocketProtocolChannel {
|
|
|
56
85
|
constructor({ url, jwt }) {
|
|
57
86
|
this.webSocket = null;
|
|
58
87
|
this._opened = new Completer();
|
|
59
|
-
this.
|
|
60
|
-
this.
|
|
88
|
+
this._finished = false;
|
|
89
|
+
this._onUnexpectedResponse = (_request, response) => {
|
|
90
|
+
const statusCode = response.statusCode;
|
|
91
|
+
if (statusCode == null) {
|
|
92
|
+
this._finish("error", new Error("websocket connect failed"));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
this._finish("error", new ProtocolHandshakeException({
|
|
96
|
+
statusCode,
|
|
97
|
+
statusText: response.statusMessage,
|
|
98
|
+
}));
|
|
99
|
+
};
|
|
100
|
+
this._onOpen = () => {
|
|
101
|
+
if (!this._opened.completed) {
|
|
102
|
+
this._opened.complete();
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
this._onMessage = (event) => {
|
|
61
106
|
const data = event.data;
|
|
62
107
|
if (data instanceof Blob) {
|
|
63
|
-
data.arrayBuffer().then((buffer) => {
|
|
64
|
-
|
|
65
|
-
this.onDataReceived(new Uint8Array(buffer));
|
|
66
|
-
}
|
|
108
|
+
void data.arrayBuffer().then((buffer) => {
|
|
109
|
+
this._onDataReceived?.(new Uint8Array(buffer));
|
|
67
110
|
});
|
|
111
|
+
return;
|
|
68
112
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
113
|
+
if (typeof data === "string") {
|
|
114
|
+
this._onDataReceived?.(encoder.encode(data));
|
|
115
|
+
return;
|
|
73
116
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
117
|
+
if (data instanceof ArrayBuffer) {
|
|
118
|
+
this._onDataReceived?.(new Uint8Array(data));
|
|
119
|
+
return;
|
|
78
120
|
}
|
|
121
|
+
if (data instanceof Uint8Array) {
|
|
122
|
+
this._onDataReceived?.(data);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (ArrayBuffer.isView(data)) {
|
|
126
|
+
this._onDataReceived?.(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
this._onClose = (event) => {
|
|
130
|
+
if (event.code === 1000) {
|
|
131
|
+
this._finish("done");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const reason = typeof event.reason === "string" ? event.reason : event.reason.toString();
|
|
135
|
+
this._finish("error", new ProtocolCloseException({ closeCode: event.code, reason }));
|
|
136
|
+
};
|
|
137
|
+
this._onError = (event) => {
|
|
138
|
+
this._finish("error", event instanceof Error ? event : new Error("websocket error"));
|
|
79
139
|
};
|
|
80
140
|
this.url = url;
|
|
81
141
|
this.jwt = jwt;
|
|
82
142
|
}
|
|
83
143
|
start(onDataReceived, { onDone, onError }) {
|
|
84
|
-
if (typeof (onDataReceived) != "function") {
|
|
85
|
-
throw new Error("onDataReceived must be a function");
|
|
86
|
-
}
|
|
87
144
|
const url = new URL(this.url);
|
|
88
145
|
url.searchParams.set("token", this.jwt);
|
|
89
|
-
this.
|
|
90
|
-
this.
|
|
91
|
-
this.
|
|
92
|
-
this.
|
|
93
|
-
|
|
94
|
-
|
|
146
|
+
this._opened = new Completer();
|
|
147
|
+
this._finished = false;
|
|
148
|
+
this._onDataReceived = onDataReceived;
|
|
149
|
+
this._doneHandler = onDone;
|
|
150
|
+
this._errorHandler = onError;
|
|
151
|
+
const socket = new WebSocket(url.toString());
|
|
152
|
+
this.webSocket = socket;
|
|
153
|
+
if (isNodeRuntime()) {
|
|
154
|
+
socket.on("unexpected-response", this._onUnexpectedResponse);
|
|
95
155
|
}
|
|
96
|
-
|
|
97
|
-
|
|
156
|
+
socket.addEventListener("open", this._onOpen);
|
|
157
|
+
socket.addEventListener("message", this._onMessage);
|
|
158
|
+
socket.addEventListener("close", this._onClose);
|
|
159
|
+
socket.addEventListener("error", this._onError);
|
|
160
|
+
}
|
|
161
|
+
_finish(kind, error) {
|
|
162
|
+
if (this._finished) {
|
|
163
|
+
return;
|
|
98
164
|
}
|
|
165
|
+
this._finished = true;
|
|
166
|
+
if (kind === "done") {
|
|
167
|
+
this._doneHandler?.();
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
this._errorHandler?.(error);
|
|
99
171
|
}
|
|
100
172
|
dispose() {
|
|
101
|
-
this.webSocket
|
|
173
|
+
const socket = this.webSocket;
|
|
102
174
|
this.webSocket = null;
|
|
175
|
+
this._onDataReceived = undefined;
|
|
176
|
+
if (socket == null) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
socket.removeEventListener("open", this._onOpen);
|
|
180
|
+
socket.removeEventListener("message", this._onMessage);
|
|
181
|
+
socket.removeEventListener("close", this._onClose);
|
|
182
|
+
socket.removeEventListener("error", this._onError);
|
|
183
|
+
if (isNodeRuntime()) {
|
|
184
|
+
socket.off("unexpected-response", this._onUnexpectedResponse);
|
|
185
|
+
}
|
|
186
|
+
if (socket.readyState === WebSocket.CONNECTING || socket.readyState === WebSocket.OPEN) {
|
|
187
|
+
socket.close(1000);
|
|
188
|
+
}
|
|
103
189
|
}
|
|
104
190
|
async sendData(data) {
|
|
105
191
|
await this._opened.fut;
|
|
106
|
-
this.webSocket
|
|
192
|
+
const socket = this.webSocket;
|
|
193
|
+
if (socket == null) {
|
|
194
|
+
throw new Error("websocket is closed");
|
|
195
|
+
}
|
|
196
|
+
socket.send(data);
|
|
107
197
|
}
|
|
108
198
|
}
|
|
109
199
|
export class ProtocolMessageStream {
|
|
@@ -112,15 +202,15 @@ export class ProtocolMessageStream {
|
|
|
112
202
|
this._messageAdded = new Completer();
|
|
113
203
|
this._closed = false;
|
|
114
204
|
}
|
|
115
|
-
|
|
205
|
+
add(message) {
|
|
116
206
|
this._messages.push(message);
|
|
117
207
|
if (!this._messageAdded.completed) {
|
|
118
208
|
this._messageAdded.complete();
|
|
119
209
|
}
|
|
120
210
|
}
|
|
121
211
|
close() {
|
|
212
|
+
this._closed = true;
|
|
122
213
|
if (!this._messageAdded.completed) {
|
|
123
|
-
this._closed = true;
|
|
124
214
|
this._messageAdded.complete();
|
|
125
215
|
}
|
|
126
216
|
}
|
|
@@ -129,9 +219,9 @@ export class ProtocolMessageStream {
|
|
|
129
219
|
await this._messageAdded.fut;
|
|
130
220
|
this._messageAdded = new Completer();
|
|
131
221
|
while (this._messages.length > 0 && !this._closed) {
|
|
132
|
-
const
|
|
133
|
-
if (
|
|
134
|
-
yield
|
|
222
|
+
const message = this._messages.shift();
|
|
223
|
+
if (message !== undefined) {
|
|
224
|
+
yield message;
|
|
135
225
|
}
|
|
136
226
|
}
|
|
137
227
|
}
|
|
@@ -142,26 +232,87 @@ export class Protocol {
|
|
|
142
232
|
this.handlers = {};
|
|
143
233
|
this._id = 0;
|
|
144
234
|
this._send = new ProtocolMessageStream();
|
|
235
|
+
this._done = new Completer();
|
|
236
|
+
this._sendLoop = null;
|
|
237
|
+
this._open = false;
|
|
238
|
+
this._closed = false;
|
|
239
|
+
this._closeKind = null;
|
|
240
|
+
this._closeReason = null;
|
|
145
241
|
this._recvPacketId = 0;
|
|
146
242
|
this._recvState = "ready";
|
|
147
243
|
this._recvPacketTotal = 0;
|
|
148
244
|
this._recvMessageId = -1;
|
|
149
245
|
this._recvType = "";
|
|
150
246
|
this._recvPackets = [];
|
|
151
|
-
this._iterator = null;
|
|
152
247
|
this.channel = channel;
|
|
153
248
|
}
|
|
249
|
+
get url() {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
get token() {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
get isOpen() {
|
|
256
|
+
return this._open;
|
|
257
|
+
}
|
|
258
|
+
get isClosed() {
|
|
259
|
+
return this._closed;
|
|
260
|
+
}
|
|
261
|
+
get closeKind() {
|
|
262
|
+
return this._closeKind;
|
|
263
|
+
}
|
|
264
|
+
get closeReason() {
|
|
265
|
+
return this._closeReason;
|
|
266
|
+
}
|
|
267
|
+
get done() {
|
|
268
|
+
return this._done.fut;
|
|
269
|
+
}
|
|
270
|
+
async waitForClose() {
|
|
271
|
+
await this.done;
|
|
272
|
+
}
|
|
273
|
+
static createFactory(...args) {
|
|
274
|
+
const ProtocolCtor = this;
|
|
275
|
+
if (ProtocolCtor === Protocol) {
|
|
276
|
+
let used = false;
|
|
277
|
+
return () => {
|
|
278
|
+
if (used) {
|
|
279
|
+
throw new ProtocolReconnectUnsupportedException("protocolFactory was not configured for reconnecting this protocol");
|
|
280
|
+
}
|
|
281
|
+
used = true;
|
|
282
|
+
return new ProtocolCtor(...args);
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
return () => new ProtocolCtor(...args);
|
|
286
|
+
}
|
|
287
|
+
_setCloseState({ kind, reason, }) {
|
|
288
|
+
if (this._closeKind == null) {
|
|
289
|
+
this._closeKind = kind;
|
|
290
|
+
}
|
|
291
|
+
if (this._closeReason == null && reason != null && reason.trim().length > 0) {
|
|
292
|
+
this._closeReason = reason.trim();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
154
295
|
addHandler(type, handler) {
|
|
296
|
+
if (this.handlers[type] !== undefined) {
|
|
297
|
+
throw new Error(`already registered handler for ${type}`);
|
|
298
|
+
}
|
|
155
299
|
this.handlers[type] = handler;
|
|
156
300
|
}
|
|
157
|
-
removeHandler(type) {
|
|
301
|
+
removeHandler(type, handler) {
|
|
302
|
+
const current = this.handlers[type];
|
|
303
|
+
if (current !== handler) {
|
|
304
|
+
throw new Error(`handler mismatch for ${type}`);
|
|
305
|
+
}
|
|
158
306
|
delete this.handlers[type];
|
|
159
307
|
}
|
|
308
|
+
getHandler(type) {
|
|
309
|
+
return this.handlers[type];
|
|
310
|
+
}
|
|
160
311
|
async handleMessage(messageId, type, data) {
|
|
161
312
|
const handler = this.handlers[type] ?? this.handlers["*"];
|
|
162
|
-
if (
|
|
163
|
-
const
|
|
164
|
-
console.warn(`No handler for message type ${type}; data
|
|
313
|
+
if (handler == null) {
|
|
314
|
+
const unpacked = unpackMessage(data);
|
|
315
|
+
console.warn(`No handler for message type ${type}; data:`, unpacked);
|
|
165
316
|
return;
|
|
166
317
|
}
|
|
167
318
|
await handler(this, messageId, type, data);
|
|
@@ -169,69 +320,139 @@ export class Protocol {
|
|
|
169
320
|
getNextMessageId() {
|
|
170
321
|
return this._id++;
|
|
171
322
|
}
|
|
323
|
+
sendNowait(type, data, { id } = {}) {
|
|
324
|
+
if (this._sendError != null) {
|
|
325
|
+
throw this._sendError;
|
|
326
|
+
}
|
|
327
|
+
if (this._closed) {
|
|
328
|
+
throw new Error("protocol is closed");
|
|
329
|
+
}
|
|
330
|
+
const message = new ProtocolMessage({ id: id ?? this.getNextMessageId(), type, data });
|
|
331
|
+
this._send.add(message);
|
|
332
|
+
return message.id;
|
|
333
|
+
}
|
|
172
334
|
async send(type, data, id) {
|
|
173
|
-
const
|
|
174
|
-
this.
|
|
175
|
-
|
|
335
|
+
const message = new ProtocolMessage({ id: id ?? this.getNextMessageId(), type, data });
|
|
336
|
+
if (this._sendError != null) {
|
|
337
|
+
throw this._sendError;
|
|
338
|
+
}
|
|
339
|
+
if (this._closed) {
|
|
340
|
+
throw new Error("protocol is closed");
|
|
341
|
+
}
|
|
342
|
+
this._send.add(message);
|
|
343
|
+
await message.sent.fut;
|
|
176
344
|
}
|
|
177
345
|
async sendJson(object) {
|
|
178
|
-
|
|
346
|
+
await this.send("application/json", encoder.encode(JSON.stringify(object)));
|
|
179
347
|
}
|
|
180
|
-
start({ onMessage, onDone, onError } = {}) {
|
|
348
|
+
start({ onMessage, onDone, onError, } = {}) {
|
|
349
|
+
if (this._sendLoop != null) {
|
|
350
|
+
throw new Error("protocol already started");
|
|
351
|
+
}
|
|
181
352
|
if (onMessage != null) {
|
|
182
353
|
this.addHandler("*", onMessage);
|
|
183
354
|
}
|
|
184
|
-
this.
|
|
185
|
-
this.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if (
|
|
190
|
-
|
|
191
|
-
const header = new Uint8Array(4 * 4);
|
|
192
|
-
const dataView = new DataView(header.buffer);
|
|
193
|
-
dataView.setUint32(0, (message.id & 0x000fffff00000000) / Math.pow(2, 32), false);
|
|
194
|
-
dataView.setUint32(4, message.id & 0xffffffff, false);
|
|
195
|
-
dataView.setUint32(8, 0, false);
|
|
196
|
-
dataView.setUint32(12, packets, false);
|
|
197
|
-
const headerPacket = mergeUint8Arrays(header, encoder.encode(message.type));
|
|
198
|
-
await this.channel.sendData(headerPacket);
|
|
199
|
-
for (var i = 0; i < packets; i++) {
|
|
200
|
-
const packetHeader = new Uint8Array(3 * 4);
|
|
201
|
-
const dataView = new DataView(packetHeader.buffer);
|
|
202
|
-
dataView.setUint32(0, (message.id & 0x000fffff00000000) / Math.pow(2, 32), false);
|
|
203
|
-
dataView.setUint32(4, message.id & 0xffffffff, false);
|
|
204
|
-
dataView.setUint32(8, i + 1, false);
|
|
205
|
-
const packet = mergeUint8Arrays(packetHeader, message.data.subarray(i * 1024, Math.min((i + 1) * 1024, message.data.length)));
|
|
206
|
-
await this.channel.sendData(packet);
|
|
207
|
-
}
|
|
208
|
-
message.sent.resolve();
|
|
355
|
+
this._open = true;
|
|
356
|
+
this.channel.start(this.onDataReceived.bind(this), {
|
|
357
|
+
onDone: () => {
|
|
358
|
+
this._setCloseState({ kind: ProtocolCloseKind.SERVER });
|
|
359
|
+
this._shutdown();
|
|
360
|
+
if (!this._done.completed) {
|
|
361
|
+
this._done.complete(null);
|
|
209
362
|
}
|
|
210
|
-
|
|
211
|
-
|
|
363
|
+
onDone?.();
|
|
364
|
+
},
|
|
365
|
+
onError: (error) => {
|
|
366
|
+
this._setCloseState({
|
|
367
|
+
kind: ProtocolCloseKind.ERROR,
|
|
368
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
369
|
+
});
|
|
370
|
+
this._shutdown();
|
|
371
|
+
if (!this._done.completed) {
|
|
372
|
+
this._done.complete(error);
|
|
373
|
+
}
|
|
374
|
+
onError?.(error);
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
this._sendLoop = this._runSendLoop(onError);
|
|
212
378
|
}
|
|
213
|
-
|
|
379
|
+
close() {
|
|
380
|
+
this._setCloseState({ kind: ProtocolCloseKind.CLIENT });
|
|
381
|
+
this._shutdown();
|
|
214
382
|
this.channel.dispose();
|
|
215
|
-
this.
|
|
216
|
-
|
|
383
|
+
if (!this._done.completed) {
|
|
384
|
+
this._done.complete(null);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
dispose() {
|
|
388
|
+
this.close();
|
|
389
|
+
}
|
|
390
|
+
_shutdown() {
|
|
391
|
+
if (this._closed) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
this._closed = true;
|
|
395
|
+
this._open = false;
|
|
396
|
+
this._send.close();
|
|
397
|
+
}
|
|
398
|
+
async _runSendLoop(onError) {
|
|
399
|
+
for await (const message of this._send.stream()) {
|
|
400
|
+
try {
|
|
401
|
+
const packets = Math.ceil(message.data.length / 1024);
|
|
402
|
+
const header = new Uint8Array(16);
|
|
403
|
+
const headerView = new DataView(header.buffer);
|
|
404
|
+
headerView.setUint32(0, Math.floor(message.id / 2 ** 32), false);
|
|
405
|
+
headerView.setUint32(4, message.id & 0xffffffff, false);
|
|
406
|
+
headerView.setUint32(8, 0, false);
|
|
407
|
+
headerView.setUint32(12, packets, false);
|
|
408
|
+
await this.channel.sendData(mergeUint8Arrays(header, encoder.encode(message.type)));
|
|
409
|
+
for (let i = 0; i < packets; i += 1) {
|
|
410
|
+
const packetHeader = new Uint8Array(12);
|
|
411
|
+
const packetHeaderView = new DataView(packetHeader.buffer);
|
|
412
|
+
packetHeaderView.setUint32(0, Math.floor(message.id / 2 ** 32), false);
|
|
413
|
+
packetHeaderView.setUint32(4, message.id & 0xffffffff, false);
|
|
414
|
+
packetHeaderView.setUint32(8, i + 1, false);
|
|
415
|
+
await this.channel.sendData(mergeUint8Arrays(packetHeader, message.data.subarray(i * 1024, Math.min((i + 1) * 1024, message.data.length))));
|
|
416
|
+
}
|
|
417
|
+
if (!message.sent.completed) {
|
|
418
|
+
message.sent.complete();
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
catch (error) {
|
|
422
|
+
this._sendError = error;
|
|
423
|
+
this._setCloseState({
|
|
424
|
+
kind: ProtocolCloseKind.ERROR,
|
|
425
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
426
|
+
});
|
|
427
|
+
if (!message.sent.completed) {
|
|
428
|
+
message.sent.completeError(error);
|
|
429
|
+
}
|
|
430
|
+
this._shutdown();
|
|
431
|
+
if (!this._done.completed) {
|
|
432
|
+
this._done.complete(error);
|
|
433
|
+
}
|
|
434
|
+
onError?.(error);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
217
438
|
}
|
|
218
439
|
onDataReceived(dataPacket) {
|
|
219
|
-
const dataView = new DataView(dataPacket.buffer);
|
|
220
|
-
const messageId = dataView.getUint32(4, false) + dataView.getUint32(0, false) *
|
|
440
|
+
const dataView = new DataView(dataPacket.buffer, dataPacket.byteOffset, dataPacket.byteLength);
|
|
441
|
+
const messageId = dataView.getUint32(4, false) + dataView.getUint32(0, false) * 2 ** 32;
|
|
221
442
|
const packet = dataView.getUint32(8, false);
|
|
222
|
-
if (packet
|
|
443
|
+
if (packet !== this._recvPacketId) {
|
|
223
444
|
this._recvState = "error";
|
|
224
445
|
}
|
|
225
|
-
if (packet
|
|
226
|
-
if (this._recvState
|
|
446
|
+
if (packet === 0) {
|
|
447
|
+
if (this._recvState === "ready" || this._recvState === "error") {
|
|
227
448
|
this._recvPacketTotal = dataView.getUint32(12, false);
|
|
228
449
|
this._recvMessageId = messageId;
|
|
229
450
|
this._recvType = decoder.decode(dataPacket.subarray(16));
|
|
230
|
-
if (this._recvPacketTotal
|
|
451
|
+
if (this._recvPacketTotal === 0) {
|
|
231
452
|
try {
|
|
232
453
|
const merged = mergeUint8Arrays(...this._recvPackets);
|
|
233
454
|
this._recvPackets.length = 0;
|
|
234
|
-
this.
|
|
455
|
+
this._dispatchMessage({ messageId, type: this._recvType, data: merged });
|
|
235
456
|
}
|
|
236
457
|
finally {
|
|
237
458
|
this._recvState = "ready";
|
|
@@ -249,39 +470,52 @@ export class Protocol {
|
|
|
249
470
|
this._recvState = "error";
|
|
250
471
|
this._recvPacketId = 0;
|
|
251
472
|
}
|
|
473
|
+
return;
|
|
252
474
|
}
|
|
253
|
-
|
|
475
|
+
if (this._recvState !== "processing") {
|
|
254
476
|
this._recvState = "error";
|
|
255
477
|
this._recvPacketId = 0;
|
|
478
|
+
return;
|
|
256
479
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
this.handleMessage(messageId, this._recvType, merged);
|
|
268
|
-
}
|
|
269
|
-
finally {
|
|
270
|
-
this._recvState = "ready";
|
|
271
|
-
this._recvPacketId = 0;
|
|
272
|
-
this._recvType = "";
|
|
273
|
-
this._recvMessageId = -1;
|
|
274
|
-
}
|
|
480
|
+
if (messageId !== this._recvMessageId) {
|
|
481
|
+
this._recvState = "error";
|
|
482
|
+
this._recvPacketId = 0;
|
|
483
|
+
}
|
|
484
|
+
this._recvPackets.push(dataPacket.subarray(12));
|
|
485
|
+
if (this._recvPacketTotal === this._recvPacketId) {
|
|
486
|
+
try {
|
|
487
|
+
const merged = mergeUint8Arrays(...this._recvPackets);
|
|
488
|
+
this._recvPackets.length = 0;
|
|
489
|
+
this._dispatchMessage({ messageId, type: this._recvType, data: merged });
|
|
275
490
|
}
|
|
276
|
-
|
|
277
|
-
this.
|
|
491
|
+
finally {
|
|
492
|
+
this._recvState = "ready";
|
|
493
|
+
this._recvPacketId = 0;
|
|
494
|
+
this._recvType = "";
|
|
495
|
+
this._recvMessageId = -1;
|
|
278
496
|
}
|
|
497
|
+
return;
|
|
279
498
|
}
|
|
499
|
+
this._recvPacketId += 1;
|
|
500
|
+
}
|
|
501
|
+
_dispatchMessage({ messageId, type, data, }) {
|
|
502
|
+
void this.handleMessage(messageId, type, data).catch((error) => {
|
|
503
|
+
console.error("unhandled protocol message handler error", error);
|
|
504
|
+
});
|
|
280
505
|
}
|
|
281
506
|
}
|
|
282
507
|
export class WebSocketClientProtocol extends Protocol {
|
|
283
508
|
constructor({ url, token }) {
|
|
284
|
-
|
|
285
|
-
|
|
509
|
+
super({
|
|
510
|
+
channel: new WebSocketProtocolChannel({ url, jwt: token }),
|
|
511
|
+
});
|
|
512
|
+
this._url = url;
|
|
513
|
+
this._token = token;
|
|
514
|
+
}
|
|
515
|
+
get url() {
|
|
516
|
+
return this._url;
|
|
517
|
+
}
|
|
518
|
+
get token() {
|
|
519
|
+
return this._token;
|
|
286
520
|
}
|
|
287
521
|
}
|