@triformine/nexis-sdk 0.1.0-next.6d96464

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.
@@ -0,0 +1,746 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: all[name]
9
+ });
10
+ }
11
+ _export(exports, {
12
+ NexisClient: function() {
13
+ return NexisClient;
14
+ },
15
+ NexisRoom: function() {
16
+ return NexisRoom;
17
+ },
18
+ connect: function() {
19
+ return connect;
20
+ },
21
+ decodeRoomBytes: function() {
22
+ return decodeRoomBytes;
23
+ },
24
+ parseMatchFoundPayload: function() {
25
+ return parseMatchFoundPayload;
26
+ }
27
+ });
28
+ const _patch = require("./patch");
29
+ const _codec = require("./codec");
30
+ const _rpc = require("./rpc");
31
+ const DEFAULT_VERSION = 1;
32
+ const DEFAULT_RECONNECT_INITIAL_DELAY_MS = 250;
33
+ const DEFAULT_RECONNECT_MAX_DELAY_MS = 3_000;
34
+ const DEFAULT_RECONNECT_MAX_ATTEMPTS = 20;
35
+ function normalizeConnectOptions(options) {
36
+ const reconnectInitialDelayMs = Math.max(50, options.reconnectInitialDelayMs ?? DEFAULT_RECONNECT_INITIAL_DELAY_MS);
37
+ const reconnectMaxDelayMs = Math.max(reconnectInitialDelayMs, options.reconnectMaxDelayMs ?? DEFAULT_RECONNECT_MAX_DELAY_MS);
38
+ const reconnectMaxAttempts = Math.max(1, options.reconnectMaxAttempts ?? DEFAULT_RECONNECT_MAX_ATTEMPTS);
39
+ return {
40
+ ...options,
41
+ codecs: options.codecs ?? [
42
+ "msgpack",
43
+ "json"
44
+ ],
45
+ autoJoinMatchedRoom: options.autoJoinMatchedRoom ?? false,
46
+ autoReconnect: options.autoReconnect ?? true,
47
+ reconnectInitialDelayMs,
48
+ reconnectMaxDelayMs,
49
+ reconnectMaxAttempts
50
+ };
51
+ }
52
+ function readCodecName(message) {
53
+ const payload = message.p;
54
+ if (payload && typeof payload === "object" && "codec" in payload && payload.codec === "msgpack") {
55
+ return "msgpack";
56
+ }
57
+ return "json";
58
+ }
59
+ function readSessionId(message) {
60
+ const payload = message.p;
61
+ if (payload && typeof payload === "object" && "session_id" in payload && typeof payload.session_id === "string") {
62
+ return payload.session_id;
63
+ }
64
+ return undefined;
65
+ }
66
+ function readErrorReason(message) {
67
+ const payload = message.p;
68
+ if (payload && typeof payload === "object" && "reason" in payload && typeof payload.reason === "string") {
69
+ return payload.reason;
70
+ }
71
+ return "server returned error";
72
+ }
73
+ function isStringArray(value) {
74
+ return Array.isArray(value) && value.every((item)=>typeof item === "string");
75
+ }
76
+ function deepEqual(left, right) {
77
+ return JSON.stringify(left) === JSON.stringify(right);
78
+ }
79
+ function toJsonValue(bytes) {
80
+ let binary = "";
81
+ for (const value of bytes){
82
+ binary += String.fromCharCode(value);
83
+ }
84
+ return btoa(binary);
85
+ }
86
+ function fromJsonValue(value) {
87
+ if (typeof value !== "string") {
88
+ return null;
89
+ }
90
+ try {
91
+ const decoded = atob(value);
92
+ const bytes = new Uint8Array(decoded.length);
93
+ for(let i = 0; i < decoded.length; i += 1){
94
+ bytes[i] = decoded.charCodeAt(i);
95
+ }
96
+ return bytes;
97
+ } catch {
98
+ return null;
99
+ }
100
+ }
101
+ function readRoomMessagePayload(payload) {
102
+ if (!payload || typeof payload !== "object") {
103
+ return null;
104
+ }
105
+ const type = payload.type;
106
+ if (typeof type !== "string" && typeof type !== "number") {
107
+ return null;
108
+ }
109
+ const data = payload.data;
110
+ return {
111
+ type,
112
+ data
113
+ };
114
+ }
115
+ function parseMatchFoundPayload(payload) {
116
+ if (!payload || typeof payload !== "object") {
117
+ return null;
118
+ }
119
+ const room = payload.room;
120
+ const roomType = payload.room_type;
121
+ const size = payload.size;
122
+ const participants = payload.participants;
123
+ if (typeof room !== "string" || typeof roomType !== "string" || typeof size !== "number" || !Number.isFinite(size) || !isStringArray(participants)) {
124
+ return null;
125
+ }
126
+ return {
127
+ room,
128
+ roomType,
129
+ size,
130
+ participants
131
+ };
132
+ }
133
+ class NexisRoom {
134
+ client;
135
+ id;
136
+ onStateChange;
137
+ constructor(client, roomId){
138
+ this.client = client;
139
+ this.id = roomId;
140
+ this.onStateChange = this.buildStateChangeSubscription();
141
+ }
142
+ get state() {
143
+ return this.client.getRoomState(this.id);
144
+ }
145
+ send(type, message) {
146
+ this.client.sendRoomMessage(this.id, type, message);
147
+ }
148
+ sendBytes(type, bytes) {
149
+ const normalized = bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes);
150
+ this.client.sendRoomMessageBytes(this.id, type, normalized);
151
+ }
152
+ onMessage(type, callback) {
153
+ return this.client.onRoomMessage(this.id, type, callback);
154
+ }
155
+ buildStateChangeSubscription() {
156
+ const subscribe = (callback)=>this.client.onRoomState(this.id, callback);
157
+ subscribe.once = (callback)=>this.client.onRoomStateOnce(this.id, callback);
158
+ subscribe.select = (path, callback)=>this.client.onRoomStateSelect(this.id, path, callback);
159
+ return subscribe;
160
+ }
161
+ }
162
+ class NexisClient {
163
+ socket;
164
+ url;
165
+ connectOptions;
166
+ rpc = new _rpc.RpcClient();
167
+ codec;
168
+ eventHandlers = new Map();
169
+ stateHandlers = new Set();
170
+ roomStateHandlers = new Map();
171
+ roomStateSelectors = new Map();
172
+ roomMessageHandlers = new Map();
173
+ roomStates = new Map();
174
+ roomSeq = new Map();
175
+ roomChecksum = new Map();
176
+ sessionId;
177
+ autoJoinMatchedRoom;
178
+ reconnecting = false;
179
+ disposed = false;
180
+ constructor(url, socket, codec, sessionId, options){
181
+ this.url = url;
182
+ this.socket = socket;
183
+ this.codec = codec;
184
+ this.sessionId = sessionId;
185
+ this.connectOptions = options;
186
+ this.autoJoinMatchedRoom = options.autoJoinMatchedRoom;
187
+ this.attachSocket(socket);
188
+ }
189
+ static connect(url, options) {
190
+ const resolved = normalizeConnectOptions(options);
191
+ return NexisClient.openSocketAndHandshake(url, resolved, resolved.sessionId).then(({ socket, codec, sessionId })=>new NexisClient(url, socket, codec, sessionId, resolved));
192
+ }
193
+ close() {
194
+ this.disposed = true;
195
+ this.socket.close();
196
+ }
197
+ attachSocket(socket) {
198
+ this.socket = socket;
199
+ this.socket.addEventListener("message", (event)=>{
200
+ void this.onRawMessage(event.data);
201
+ });
202
+ this.socket.addEventListener("close", ()=>{
203
+ this.rpc.rejectAll(new Error("socket closed"));
204
+ if (!this.disposed && this.connectOptions.autoReconnect) {
205
+ void this.tryReconnect();
206
+ }
207
+ });
208
+ }
209
+ async tryReconnect() {
210
+ if (this.reconnecting || this.disposed) {
211
+ return;
212
+ }
213
+ this.reconnecting = true;
214
+ this.dispatchEvent({
215
+ v: DEFAULT_VERSION,
216
+ t: "reconnect.start",
217
+ p: {
218
+ session_id: this.sessionId
219
+ }
220
+ });
221
+ let attempt = 0;
222
+ let delayMs = this.connectOptions.reconnectInitialDelayMs;
223
+ while(!this.disposed && attempt < this.connectOptions.reconnectMaxAttempts){
224
+ attempt += 1;
225
+ await NexisClient.wait(delayMs);
226
+ try {
227
+ const reconnect = await NexisClient.openSocketAndHandshake(this.url, this.connectOptions, this.sessionId);
228
+ this.codec = reconnect.codec;
229
+ this.sessionId = reconnect.sessionId ?? this.sessionId;
230
+ this.attachSocket(reconnect.socket);
231
+ this.dispatchEvent({
232
+ v: DEFAULT_VERSION,
233
+ t: "reconnect.ok",
234
+ p: {
235
+ attempt,
236
+ session_id: this.sessionId
237
+ }
238
+ });
239
+ this.reconnecting = false;
240
+ return;
241
+ } catch {
242
+ this.dispatchEvent({
243
+ v: DEFAULT_VERSION,
244
+ t: "reconnect.retry",
245
+ p: {
246
+ attempt
247
+ }
248
+ });
249
+ }
250
+ delayMs = Math.min(delayMs * 2, this.connectOptions.reconnectMaxDelayMs);
251
+ }
252
+ this.reconnecting = false;
253
+ this.dispatchEvent({
254
+ v: DEFAULT_VERSION,
255
+ t: "reconnect.failed",
256
+ p: {
257
+ session_id: this.sessionId
258
+ }
259
+ });
260
+ }
261
+ getSessionId() {
262
+ return this.sessionId;
263
+ }
264
+ room(roomId) {
265
+ return new NexisRoom(this, roomId);
266
+ }
267
+ async joinOrCreate(roomType, options) {
268
+ const roomId = typeof options?.roomId === "string" ? options.roomId : undefined;
269
+ const response = await this.sendRPC("room.join_or_create", {
270
+ roomType,
271
+ roomId,
272
+ options
273
+ }, roomId);
274
+ if (response && typeof response === "object" && typeof response.room === "string") {
275
+ return this.room(response.room);
276
+ }
277
+ if (roomId) {
278
+ return this.room(roomId);
279
+ }
280
+ return this.room(`${roomType}:default`);
281
+ }
282
+ listRooms(roomType) {
283
+ return this.sendRPC("room.list", roomType ? {
284
+ roomType
285
+ } : {});
286
+ }
287
+ enqueueMatchmaking(roomType, size = 2) {
288
+ return this.sendRPC("matchmaking.enqueue", {
289
+ roomType,
290
+ size
291
+ });
292
+ }
293
+ dequeueMatchmaking() {
294
+ return this.sendRPC("matchmaking.dequeue", {});
295
+ }
296
+ onStateChange(callback) {
297
+ this.stateHandlers.add(callback);
298
+ return ()=>this.stateHandlers.delete(callback);
299
+ }
300
+ onEvent(type, callback) {
301
+ const handlers = this.eventHandlers.get(type) ?? new Set();
302
+ handlers.add(callback);
303
+ this.eventHandlers.set(type, handlers);
304
+ return ()=>{
305
+ const current = this.eventHandlers.get(type);
306
+ if (!current) {
307
+ return;
308
+ }
309
+ current.delete(callback);
310
+ if (current.size === 0) {
311
+ this.eventHandlers.delete(type);
312
+ }
313
+ };
314
+ }
315
+ onMatchFound(callback) {
316
+ return this.onEvent("match.found", (message)=>{
317
+ const parsed = parseMatchFoundPayload(message.p);
318
+ if (!parsed) {
319
+ return;
320
+ }
321
+ callback(parsed, message);
322
+ });
323
+ }
324
+ sendRPC(type, payload, room) {
325
+ const { message, promise } = this.rpc.createRequest(type, payload, room);
326
+ this.sendEnvelope(message);
327
+ return promise;
328
+ }
329
+ getRoomState(roomId) {
330
+ return this.roomStates.get(roomId) ?? {};
331
+ }
332
+ sendRoomMessage(roomId, type, data) {
333
+ this.sendEnvelope({
334
+ v: DEFAULT_VERSION,
335
+ t: "room.message",
336
+ room: roomId,
337
+ p: {
338
+ type: String(type),
339
+ data
340
+ }
341
+ });
342
+ }
343
+ sendRoomMessageBytes(roomId, type, data) {
344
+ this.sendEnvelope({
345
+ v: DEFAULT_VERSION,
346
+ t: "room.message.bytes",
347
+ room: roomId,
348
+ p: {
349
+ type: String(type),
350
+ data_b64: toJsonValue(data)
351
+ }
352
+ });
353
+ }
354
+ onRoomMessage(roomId, type, callback) {
355
+ const key = String(type);
356
+ const byType = this.roomMessageHandlers.get(roomId) ?? new Map();
357
+ const handlers = byType.get(key) ?? new Set();
358
+ handlers.add(callback);
359
+ byType.set(key, handlers);
360
+ this.roomMessageHandlers.set(roomId, byType);
361
+ return ()=>{
362
+ const roomHandlers = this.roomMessageHandlers.get(roomId);
363
+ if (!roomHandlers) {
364
+ return;
365
+ }
366
+ const typeHandlers = roomHandlers.get(key);
367
+ if (!typeHandlers) {
368
+ return;
369
+ }
370
+ typeHandlers.delete(callback);
371
+ if (typeHandlers.size === 0) {
372
+ roomHandlers.delete(key);
373
+ }
374
+ if (roomHandlers.size === 0) {
375
+ this.roomMessageHandlers.delete(roomId);
376
+ }
377
+ };
378
+ }
379
+ onRoomState(roomId, callback) {
380
+ const handlers = this.roomStateHandlers.get(roomId) ?? new Set();
381
+ handlers.add(callback);
382
+ this.roomStateHandlers.set(roomId, handlers);
383
+ if (this.roomStates.has(roomId)) {
384
+ callback(this.roomStates.get(roomId) ?? {});
385
+ }
386
+ return ()=>{
387
+ const current = this.roomStateHandlers.get(roomId);
388
+ if (!current) {
389
+ return;
390
+ }
391
+ current.delete(callback);
392
+ if (current.size === 0) {
393
+ this.roomStateHandlers.delete(roomId);
394
+ }
395
+ };
396
+ }
397
+ onRoomStateOnce(roomId, callback) {
398
+ let disposed = false;
399
+ const off = this.onRoomState(roomId, (state)=>{
400
+ if (disposed) {
401
+ return;
402
+ }
403
+ disposed = true;
404
+ off();
405
+ callback(state);
406
+ });
407
+ return ()=>{
408
+ disposed = true;
409
+ off();
410
+ };
411
+ }
412
+ onRoomStateSelect(roomId, path, callback) {
413
+ const normalizedPath = path.startsWith("/") ? path.slice(1) : path;
414
+ const currentState = this.getRoomState(roomId);
415
+ const registration = {
416
+ path: normalizedPath,
417
+ callback,
418
+ lastValue: currentState[normalizedPath]
419
+ };
420
+ const selectors = this.roomStateSelectors.get(roomId) ?? new Set();
421
+ selectors.add(registration);
422
+ this.roomStateSelectors.set(roomId, selectors);
423
+ return ()=>{
424
+ const current = this.roomStateSelectors.get(roomId);
425
+ if (!current) {
426
+ return;
427
+ }
428
+ current.delete(registration);
429
+ if (current.size === 0) {
430
+ this.roomStateSelectors.delete(roomId);
431
+ }
432
+ };
433
+ }
434
+ sendEnvelope(message) {
435
+ const bytes = this.codec.encode(message);
436
+ this.socket.send(bytes);
437
+ }
438
+ dispatchEvent(message) {
439
+ const handlers = this.eventHandlers.get(message.t);
440
+ if (!handlers) {
441
+ return;
442
+ }
443
+ for (const handler of handlers){
444
+ handler(message);
445
+ }
446
+ }
447
+ dispatchState(roomId, nextState, prevState) {
448
+ for (const handler of this.stateHandlers){
449
+ handler(nextState);
450
+ }
451
+ const roomHandlers = this.roomStateHandlers.get(roomId);
452
+ if (roomHandlers) {
453
+ for (const handler of roomHandlers){
454
+ handler(nextState);
455
+ }
456
+ }
457
+ const selectors = this.roomStateSelectors.get(roomId);
458
+ if (selectors) {
459
+ for (const registration of selectors){
460
+ const nextValue = nextState[registration.path];
461
+ const prevValue = prevState[registration.path];
462
+ if (!deepEqual(nextValue, prevValue)) {
463
+ registration.lastValue = nextValue;
464
+ registration.callback(nextValue, nextState);
465
+ }
466
+ }
467
+ }
468
+ }
469
+ dispatchRoomMessage(message) {
470
+ if (!message.room) {
471
+ return;
472
+ }
473
+ const payload = readRoomMessagePayload(message.p);
474
+ if (!payload) {
475
+ return;
476
+ }
477
+ const byType = this.roomMessageHandlers.get(message.room);
478
+ if (!byType) {
479
+ return;
480
+ }
481
+ const handlers = byType.get(String(payload.type));
482
+ if (!handlers) {
483
+ return;
484
+ }
485
+ for (const handler of handlers){
486
+ handler(payload.data, message);
487
+ }
488
+ }
489
+ async onRawMessage(raw) {
490
+ const bytes = await NexisClient.toBytes(raw);
491
+ if (!bytes) {
492
+ return;
493
+ }
494
+ let message;
495
+ try {
496
+ message = this.codec.decode(bytes);
497
+ } catch {
498
+ return;
499
+ }
500
+ if (message.t === "rpc.response") {
501
+ try {
502
+ this.rpc.resolveResponse(message);
503
+ } catch (error) {
504
+ if (error instanceof _rpc.UnknownRidError) {
505
+ this.dispatchEvent({
506
+ v: DEFAULT_VERSION,
507
+ t: "error",
508
+ p: {
509
+ reason: error.message
510
+ }
511
+ });
512
+ return;
513
+ }
514
+ throw error;
515
+ }
516
+ return;
517
+ }
518
+ if (message.t === "state.snapshot") {
519
+ if (!message.room) {
520
+ return;
521
+ }
522
+ const snapshot = (0, _patch.parseSnapshotPayload)(message.p);
523
+ if (!snapshot) {
524
+ return;
525
+ }
526
+ const computedChecksum = await (0, _patch.computeStateChecksum)(snapshot.state);
527
+ if (snapshot.checksum && snapshot.checksum !== computedChecksum) {
528
+ this.sendEnvelope({
529
+ v: DEFAULT_VERSION,
530
+ t: "state.resync",
531
+ room: message.room,
532
+ p: {
533
+ since: this.roomSeq.get(message.room) ?? 0
534
+ }
535
+ });
536
+ return;
537
+ }
538
+ const checksum = snapshot.checksum ?? computedChecksum;
539
+ const prevState = this.roomStates.get(message.room) ?? {};
540
+ this.roomStates.set(message.room, snapshot.state);
541
+ this.roomSeq.set(message.room, snapshot.seq);
542
+ this.roomChecksum.set(message.room, checksum);
543
+ this.dispatchState(message.room, snapshot.state, prevState);
544
+ this.sendEnvelope({
545
+ v: DEFAULT_VERSION,
546
+ t: "state.ack",
547
+ room: message.room,
548
+ p: {
549
+ seq: snapshot.seq,
550
+ checksum
551
+ }
552
+ });
553
+ return;
554
+ }
555
+ if (message.t === "state.patch") {
556
+ if (!message.room) {
557
+ return;
558
+ }
559
+ const parsedPatch = (0, _patch.parsePatchPayload)(message.p);
560
+ if (!parsedPatch) {
561
+ return;
562
+ }
563
+ const currentSeq = this.roomSeq.get(message.room) ?? 0;
564
+ const patchSeq = parsedPatch.seq > 0 ? parsedPatch.seq : currentSeq + 1;
565
+ if (patchSeq <= currentSeq) {
566
+ return;
567
+ }
568
+ if (patchSeq > currentSeq + 1) {
569
+ this.sendEnvelope({
570
+ v: DEFAULT_VERSION,
571
+ t: "state.resync",
572
+ room: message.room,
573
+ p: {
574
+ since: currentSeq
575
+ }
576
+ });
577
+ return;
578
+ }
579
+ const currentState = this.roomStates.get(message.room) ?? {};
580
+ const nextState = (0, _patch.applyPatch)(currentState, parsedPatch.ops);
581
+ let localChecksum;
582
+ if (parsedPatch.checksum) {
583
+ localChecksum = await (0, _patch.computeStateChecksum)(nextState);
584
+ if (parsedPatch.checksum !== localChecksum) {
585
+ this.sendEnvelope({
586
+ v: DEFAULT_VERSION,
587
+ t: "state.resync",
588
+ room: message.room,
589
+ p: {
590
+ since: currentSeq,
591
+ checksum: this.roomChecksum.get(message.room)
592
+ }
593
+ });
594
+ return;
595
+ }
596
+ }
597
+ this.roomStates.set(message.room, nextState);
598
+ this.roomSeq.set(message.room, patchSeq);
599
+ if (parsedPatch.checksum) {
600
+ this.roomChecksum.set(message.room, parsedPatch.checksum);
601
+ } else if (localChecksum) {
602
+ this.roomChecksum.set(message.room, localChecksum);
603
+ }
604
+ this.dispatchState(message.room, nextState, currentState);
605
+ this.sendEnvelope({
606
+ v: DEFAULT_VERSION,
607
+ t: "state.ack",
608
+ room: message.room,
609
+ p: parsedPatch.checksum ? {
610
+ seq: patchSeq,
611
+ checksum: this.roomChecksum.get(message.room)
612
+ } : {
613
+ seq: patchSeq
614
+ }
615
+ });
616
+ return;
617
+ }
618
+ if (this.autoJoinMatchedRoom && message.t === "match.found") {
619
+ const parsed = parseMatchFoundPayload(message.p);
620
+ if (parsed) {
621
+ void this.joinOrCreate(parsed.roomType, {
622
+ roomId: parsed.room
623
+ }).catch(()=>undefined);
624
+ }
625
+ }
626
+ if (message.t === "room.message" && message.room) {
627
+ this.dispatchRoomMessage(message);
628
+ }
629
+ this.dispatchEvent(message);
630
+ }
631
+ static openSocketAndHandshake(url, options, sessionIdOverride) {
632
+ const socket = new WebSocket(url);
633
+ const jsonCodec = new _codec.JsonCodec();
634
+ const msgpackCodec = new _codec.MsgpackCodec();
635
+ return new Promise((resolve, reject)=>{
636
+ let settled = false;
637
+ const handshakeSessionId = sessionIdOverride ?? options.sessionId;
638
+ const onOpen = ()=>{
639
+ const handshake = {
640
+ v: DEFAULT_VERSION,
641
+ codecs: options.codecs,
642
+ project_id: options.projectId?.trim() || "anonymous",
643
+ token: options.token?.trim() || "",
644
+ session_id: handshakeSessionId
645
+ };
646
+ socket.send(JSON.stringify(handshake));
647
+ };
648
+ const onError = ()=>{
649
+ finishReject(new Error("socket connection failed"));
650
+ };
651
+ const onClose = ()=>{
652
+ finishReject(new Error("socket closed before handshake"));
653
+ };
654
+ const onMessage = async (event)=>{
655
+ try {
656
+ const message = await NexisClient.decodeHandshakeMessage(event.data, jsonCodec, msgpackCodec);
657
+ if (!message) {
658
+ return;
659
+ }
660
+ if (message.t === "error") {
661
+ finishReject(new Error(readErrorReason(message)));
662
+ return;
663
+ }
664
+ if (message.t !== "handshake.ok") {
665
+ return;
666
+ }
667
+ const negotiatedCodec = readCodecName(message);
668
+ const sessionId = readSessionId(message) ?? handshakeSessionId;
669
+ finishResolve({
670
+ socket,
671
+ codec: (0, _codec.codecFor)(negotiatedCodec),
672
+ sessionId
673
+ });
674
+ } catch (error) {
675
+ finishReject(new Error(`handshake decode failed: ${String(error)}`));
676
+ }
677
+ };
678
+ const cleanup = ()=>{
679
+ socket.removeEventListener("open", onOpen);
680
+ socket.removeEventListener("error", onError);
681
+ socket.removeEventListener("close", onClose);
682
+ socket.removeEventListener("message", onMessage);
683
+ };
684
+ const finishResolve = (result)=>{
685
+ if (settled) {
686
+ return;
687
+ }
688
+ settled = true;
689
+ cleanup();
690
+ resolve(result);
691
+ };
692
+ const finishReject = (error)=>{
693
+ if (settled) {
694
+ return;
695
+ }
696
+ settled = true;
697
+ cleanup();
698
+ reject(error);
699
+ };
700
+ socket.addEventListener("open", onOpen);
701
+ socket.addEventListener("error", onError);
702
+ socket.addEventListener("close", onClose);
703
+ socket.addEventListener("message", onMessage);
704
+ });
705
+ }
706
+ static wait(ms) {
707
+ return new Promise((resolve)=>setTimeout(resolve, ms));
708
+ }
709
+ static async decodeHandshakeMessage(raw, jsonCodec, msgpackCodec) {
710
+ if (typeof raw === "string") {
711
+ return JSON.parse(raw);
712
+ }
713
+ const bytes = await NexisClient.toBytes(raw);
714
+ if (!bytes) {
715
+ return null;
716
+ }
717
+ try {
718
+ return msgpackCodec.decode(bytes);
719
+ } catch {
720
+ return jsonCodec.decode(bytes);
721
+ }
722
+ }
723
+ static async toBytes(raw) {
724
+ if (raw instanceof Uint8Array) {
725
+ return raw;
726
+ }
727
+ if (raw instanceof ArrayBuffer) {
728
+ return new Uint8Array(raw);
729
+ }
730
+ if (raw instanceof Blob) {
731
+ return new Uint8Array(await raw.arrayBuffer());
732
+ }
733
+ if (typeof raw === "string") {
734
+ return new TextEncoder().encode(raw);
735
+ }
736
+ return null;
737
+ }
738
+ }
739
+ async function connect(url, options) {
740
+ return NexisClient.connect(url, options);
741
+ }
742
+ function decodeRoomBytes(payload) {
743
+ return fromJsonValue(payload);
744
+ }
745
+
746
+ //# sourceMappingURL=client.js.map