@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.
Files changed (77) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/browser/agent.js +74 -10
  3. package/dist/browser/developer-client.js +3 -0
  4. package/dist/browser/helpers.d.ts +2 -2
  5. package/dist/browser/helpers.js +1 -1
  6. package/dist/browser/meshagent-client.d.ts +25 -0
  7. package/dist/browser/meshagent-client.js +65 -0
  8. package/dist/browser/messaging-client.d.ts +29 -16
  9. package/dist/browser/messaging-client.js +256 -154
  10. package/dist/browser/participant.d.ts +7 -2
  11. package/dist/browser/participant.js +9 -9
  12. package/dist/browser/protocol.d.ts +85 -28
  13. package/dist/browser/protocol.js +356 -119
  14. package/dist/browser/room-client.d.ts +165 -29
  15. package/dist/browser/room-client.js +1114 -74
  16. package/dist/browser/room-event.d.ts +11 -0
  17. package/dist/browser/room-event.js +21 -1
  18. package/dist/browser/room-server-client.d.ts +2 -0
  19. package/dist/browser/room-server-client.js +6 -0
  20. package/dist/browser/runtime.d.ts +1 -1
  21. package/dist/browser/runtime.js +3 -1
  22. package/dist/browser/secrets-client.js +6 -2
  23. package/dist/browser/storage-client.d.ts +1 -0
  24. package/dist/browser/storage-client.js +9 -0
  25. package/dist/browser/sync-client.d.ts +16 -14
  26. package/dist/browser/sync-client.js +195 -116
  27. package/dist/esm/agent.js +74 -10
  28. package/dist/esm/developer-client.js +3 -0
  29. package/dist/esm/helpers.d.ts +2 -2
  30. package/dist/esm/helpers.js +1 -1
  31. package/dist/esm/meshagent-client.d.ts +25 -0
  32. package/dist/esm/meshagent-client.js +65 -0
  33. package/dist/esm/messaging-client.d.ts +29 -16
  34. package/dist/esm/messaging-client.js +256 -154
  35. package/dist/esm/participant.d.ts +7 -2
  36. package/dist/esm/participant.js +9 -9
  37. package/dist/esm/protocol.d.ts +85 -28
  38. package/dist/esm/protocol.js +352 -118
  39. package/dist/esm/room-client.d.ts +165 -29
  40. package/dist/esm/room-client.js +1112 -73
  41. package/dist/esm/room-event.d.ts +11 -0
  42. package/dist/esm/room-event.js +19 -0
  43. package/dist/esm/room-server-client.d.ts +2 -0
  44. package/dist/esm/room-server-client.js +7 -1
  45. package/dist/esm/runtime.d.ts +1 -1
  46. package/dist/esm/runtime.js +1 -1
  47. package/dist/esm/secrets-client.js +6 -2
  48. package/dist/esm/storage-client.d.ts +1 -0
  49. package/dist/esm/storage-client.js +9 -0
  50. package/dist/esm/sync-client.d.ts +16 -14
  51. package/dist/esm/sync-client.js +196 -117
  52. package/dist/node/agent.js +74 -10
  53. package/dist/node/developer-client.js +3 -0
  54. package/dist/node/helpers.d.ts +2 -2
  55. package/dist/node/helpers.js +1 -1
  56. package/dist/node/meshagent-client.d.ts +25 -0
  57. package/dist/node/meshagent-client.js +65 -0
  58. package/dist/node/messaging-client.d.ts +29 -16
  59. package/dist/node/messaging-client.js +256 -154
  60. package/dist/node/participant.d.ts +7 -2
  61. package/dist/node/participant.js +9 -9
  62. package/dist/node/protocol.d.ts +85 -28
  63. package/dist/node/protocol.js +356 -119
  64. package/dist/node/room-client.d.ts +165 -29
  65. package/dist/node/room-client.js +1114 -74
  66. package/dist/node/room-event.d.ts +11 -0
  67. package/dist/node/room-event.js +21 -1
  68. package/dist/node/room-server-client.d.ts +2 -0
  69. package/dist/node/room-server-client.js +6 -0
  70. package/dist/node/runtime.d.ts +1 -1
  71. package/dist/node/runtime.js +3 -1
  72. package/dist/node/secrets-client.js +6 -2
  73. package/dist/node/storage-client.d.ts +1 -0
  74. package/dist/node/storage-client.js +9 -0
  75. package/dist/node/sync-client.d.ts +16 -14
  76. package/dist/node/sync-client.js +195 -116
  77. package/package.json +6 -3
@@ -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(null);
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
- if (message) {
30
- onDataReceived(message);
31
- }
63
+ onDataReceived(message);
32
64
  }
65
+ onDone?.();
33
66
  }
34
67
  catch (error) {
35
- if (onError) {
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._iterator?.return(null);
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._onOpen = () => this._opened.resolve();
60
- this._onData = (event) => {
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
- if (this.onDataReceived) {
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
- else if (typeof (data) == "string") {
70
- if (this.onDataReceived) {
71
- this.onDataReceived(new Uint8Array(encoder.encode(data)));
72
- }
113
+ if (typeof data === "string") {
114
+ this._onDataReceived?.(encoder.encode(data));
115
+ return;
73
116
  }
74
- else if (data instanceof ArrayBuffer || data instanceof Buffer) {
75
- if (this.onDataReceived) {
76
- this.onDataReceived(new Uint8Array(data));
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.onDataReceived = onDataReceived;
90
- this.webSocket = new WebSocket(url.toString());
91
- this.webSocket.addEventListener("open", this._onOpen);
92
- this.webSocket.addEventListener("message", this._onData);
93
- if (onDone) {
94
- this.webSocket.addEventListener("close", onDone);
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
- if (onError) {
97
- this.webSocket.addEventListener("error", onError);
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?.close();
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?.send(data);
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
- async add(message) {
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 msg = this._messages.shift();
133
- if (msg) {
134
- yield msg;
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 (!handler) {
163
- const d = data ? unpackMessage(data) : null;
164
- console.warn(`No handler for message type ${type}; data: ${d}`);
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 msg = new ProtocolMessage({ id: id ?? this.getNextMessageId(), type: type, data: data });
174
- this._send.add(msg);
175
- await msg.sent.fut;
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
- return await this.send("application/json", encoder.encode(JSON.stringify(object)));
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.channel.start(this.onDataReceived.bind(this), { onDone, onError });
185
- this._iterator?.return(null);
186
- (async () => {
187
- this._iterator = this._send.stream();
188
- for await (const message of this._iterator) {
189
- if (message) {
190
- const packets = Math.ceil((message.data.length / 1024));
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
- dispose() {
379
+ close() {
380
+ this._setCloseState({ kind: ProtocolCloseKind.CLIENT });
381
+ this._shutdown();
214
382
  this.channel.dispose();
215
- this._iterator?.return(null);
216
- this._iterator = null;
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) * Math.pow(2, 32);
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 != this._recvPacketId) {
443
+ if (packet !== this._recvPacketId) {
223
444
  this._recvState = "error";
224
445
  }
225
- if (packet == 0) {
226
- if (this._recvState == "ready" || this._recvState == "error") {
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 == 0) {
451
+ if (this._recvPacketTotal === 0) {
231
452
  try {
232
453
  const merged = mergeUint8Arrays(...this._recvPackets);
233
454
  this._recvPackets.length = 0;
234
- this.handleMessage(messageId, this._recvType, merged);
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
- else if (this._recvState != "processing") {
475
+ if (this._recvState !== "processing") {
254
476
  this._recvState = "error";
255
477
  this._recvPacketId = 0;
478
+ return;
256
479
  }
257
- else {
258
- if (messageId != this._recvMessageId) {
259
- this._recvState = "error";
260
- this._recvPacketId = 0;
261
- }
262
- this._recvPackets.push(dataPacket.subarray(12));
263
- if (this._recvPacketTotal == this._recvPacketId) {
264
- try {
265
- const merged = mergeUint8Arrays(...this._recvPackets);
266
- this._recvPackets.length = 0;
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
- else {
277
- this._recvPacketId += 1;
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
- const channel = new WebSocketProtocolChannel({ url, jwt: token });
285
- super({ channel });
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
  }