@anvil-js/client 0.0.1 → 0.0.2

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,613 @@
1
+ 'use strict';
2
+
3
+ var uuid = require('uuid');
4
+ var react = require('react');
5
+
6
+ // src/ws/codec.ts
7
+ var ConnectionCodec = class {
8
+ /** typeId → packetName (frames we receive) */
9
+ incoming = /* @__PURE__ */ new Map();
10
+ /** packetName → typeId (frames we send) */
11
+ outgoing = /* @__PURE__ */ new Map();
12
+ nextOutgoingTypeId = 100;
13
+ constructor() {
14
+ this.incoming.set(0, "connection.ConnectionRegisterPacketTypeIdPacket");
15
+ this.outgoing.set("connection.ConnectionRegisterPacketTypeIdPacket", 0);
16
+ }
17
+ /**
18
+ * Decode a single binary frame. Returns null for registration
19
+ * frames (which mutate the codec in place) and for unknown
20
+ * typeIds.
21
+ */
22
+ decode(buffer) {
23
+ const view = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
24
+ if (view.byteLength < 4) return null;
25
+ const dv = new DataView(view.buffer, view.byteOffset, view.byteLength);
26
+ let off = 0;
27
+ const typeId = dv.getInt32(off);
28
+ off += 4;
29
+ const name = this.incoming.get(typeId);
30
+ if (!name) return null;
31
+ const idLen = dv.getInt32(off);
32
+ off += 4;
33
+ const id = new TextDecoder("utf-8").decode(view.subarray(off, off + idLen));
34
+ off += idLen;
35
+ const payloadLen = dv.getInt32(off);
36
+ off += 4;
37
+ const payloadJson = new TextDecoder("utf-8").decode(view.subarray(off, off + payloadLen));
38
+ if (name === "connection.ConnectionRegisterPacketTypeIdPacket") {
39
+ const reg = JSON.parse(payloadJson);
40
+ this.incoming.set(reg.b, reg.a);
41
+ return null;
42
+ }
43
+ return { type: name, id, payload: JSON.parse(payloadJson) };
44
+ }
45
+ /**
46
+ * Encode a packet to a binary frame. If the packet name has
47
+ * not been registered yet, a fresh typeId is allocated and
48
+ * a registration frame is yielded first.
49
+ */
50
+ encode(name, payload, id = uuid.v4()) {
51
+ const frames = [];
52
+ if (!this.outgoing.has(name)) {
53
+ this.outgoing.set(name, this.nextOutgoingTypeId++);
54
+ frames.push(
55
+ ...this.encode(
56
+ "connection.ConnectionRegisterPacketTypeIdPacket",
57
+ { a: name, b: this.outgoing.get(name) },
58
+ ""
59
+ )
60
+ );
61
+ }
62
+ const typeId = this.outgoing.get(name);
63
+ const payloadBytes = new TextEncoder().encode(JSON.stringify(payload));
64
+ const idBytes = id ? new TextEncoder().encode(id) : new Uint8Array(0);
65
+ const total = 4 + 4 + idBytes.length + 4 + payloadBytes.length;
66
+ const buf = new Uint8Array(total);
67
+ const dv = new DataView(buf.buffer);
68
+ let off = 0;
69
+ dv.setInt32(off, typeId);
70
+ off += 4;
71
+ dv.setInt32(off, idBytes.length);
72
+ off += 4;
73
+ buf.set(idBytes, off);
74
+ off += idBytes.length;
75
+ dv.setInt32(off, payloadBytes.length);
76
+ off += 4;
77
+ buf.set(payloadBytes, off);
78
+ frames.push(buf);
79
+ return frames;
80
+ }
81
+ /** Register a packet type up-front (returns the typeId assigned). */
82
+ registerOutgoing(name) {
83
+ if (!this.outgoing.has(name)) {
84
+ this.outgoing.set(name, this.nextOutgoingTypeId++);
85
+ }
86
+ return this.outgoing.get(name);
87
+ }
88
+ };
89
+ var AnvilWsClient = class {
90
+ ws = null;
91
+ codec = new ConnectionCodec();
92
+ config;
93
+ listeners = /* @__PURE__ */ new Map();
94
+ pendingRequests = /* @__PURE__ */ new Map();
95
+ state = "disconnected";
96
+ bootstrapReceived = false;
97
+ reconnectTimer = null;
98
+ defaultRequestTimeoutMs = 1e4;
99
+ constructor(config) {
100
+ this.config = {
101
+ maxProtocolVersion: 9,
102
+ autoReconnect: true,
103
+ reconnectDelayMs: 2e3,
104
+ authenticationToken: "",
105
+ ...config
106
+ };
107
+ }
108
+ // ----- Lifecycle -----
109
+ connect() {
110
+ return new Promise((resolve, reject) => {
111
+ if (this.state === "ready" || this.state === "connected") {
112
+ resolve();
113
+ return;
114
+ }
115
+ this.state = "connecting";
116
+ const params = new URLSearchParams();
117
+ params.set("essential-user-uuid", this.config.userUuid);
118
+ params.set("essential-user-name", this.config.userName);
119
+ params.set("essential-max-protocol-version", String(this.config.maxProtocolVersion));
120
+ if (this.config.authenticationToken) {
121
+ params.set("essential-authentication-token", this.config.authenticationToken);
122
+ }
123
+ const url = `${this.config.url}?${params.toString()}`;
124
+ this.ws = new WebSocket(url);
125
+ this.ws.binaryType = "arraybuffer";
126
+ this.ws.onopen = () => {
127
+ this.state = "connected";
128
+ this.emit("open", void 0);
129
+ this.preRegisterOutgoing();
130
+ resolve();
131
+ };
132
+ this.ws.onerror = (e) => {
133
+ this.emit("error", new Error("WebSocket error"));
134
+ reject(new Error("WebSocket error"));
135
+ };
136
+ this.ws.onclose = (ev) => {
137
+ this.state = "disconnected";
138
+ this.emit("close", { code: ev.code, reason: ev.reason });
139
+ for (const [, req] of this.pendingRequests) {
140
+ clearTimeout(req.timer);
141
+ req.reject(new Error("Connection closed"));
142
+ }
143
+ this.pendingRequests.clear();
144
+ if (this.config.autoReconnect && this.bootstrapReceived) {
145
+ this.scheduleReconnect();
146
+ }
147
+ };
148
+ this.ws.onmessage = (ev) => {
149
+ this.handleFrame(ev.data);
150
+ };
151
+ });
152
+ }
153
+ disconnect() {
154
+ this.config.autoReconnect = false;
155
+ if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
156
+ if (this.ws) {
157
+ this.ws.close(1e3, "Client disconnect");
158
+ this.ws = null;
159
+ }
160
+ }
161
+ scheduleReconnect() {
162
+ if (this.reconnectTimer) return;
163
+ this.reconnectTimer = setTimeout(() => {
164
+ this.reconnectTimer = null;
165
+ this.connect().catch(() => {
166
+ });
167
+ }, this.config.reconnectDelayMs);
168
+ }
169
+ preRegisterOutgoing() {
170
+ const TYPES = [
171
+ "chat.ClientChatChannelMessageCreatePacket",
172
+ "chat.ClientChatChannelMessagesRetrievePacket",
173
+ "chat.ClientChatChannelCreatePacket",
174
+ "chat.ClientChatChannelMessageUpdatePacket",
175
+ "chat.ChatChannelMessageDeletePacket",
176
+ "chat.ClientChatChannelMessageReportPacket",
177
+ "chat.ChatChannelMemberAddPacket",
178
+ "chat.ChatChannelMemberRemovePacket",
179
+ "chat.ClientChatChannelMutePacket",
180
+ "chat.ClientChatChannelReadStatePacket",
181
+ "chat.ClientChatChannelMessageReadStatePacket",
182
+ "relationships.ClientRelationshipCreatePacket",
183
+ "relationships.RelationshipDeletePacket",
184
+ "relationships.ClientLookupUuidByNamePacket",
185
+ "profile.ClientProfileRequestPacket",
186
+ "profile.ClientProfileActivityPacket",
187
+ "cosmetic.ClientCosmeticRequestPacket",
188
+ "cosmetic.ClientCosmeticBulkRequestUnlockStatePacket",
189
+ "cosmetic.ClientCosmeticAnimationTriggerPacket",
190
+ "skin.ClientSkinCreatePacket",
191
+ "skin.ClientSkinUpdateLastUsedStatePacket",
192
+ "skin.ClientSkinUpdateFavoriteStatePacket",
193
+ "skin.ClientSelectedSkinsRequestPacket",
194
+ "cosmetic.outfit.ClientCosmeticOutfitCreatePacket",
195
+ "cosmetic.outfit.ClientCosmeticOutfitSelectPacket",
196
+ "cosmetic.outfit.ClientCosmeticOutfitEquippedCosmeticsUpdatePacket",
197
+ "cosmetic.emote.ClientCosmeticEmoteWheelUpdatePacket",
198
+ "cosmetic.emote.ClientCosmeticEmoteWheelSelectPacket",
199
+ "wardrobe.ClientWardrobeSettingsPacket",
200
+ "checkout.ClientCheckoutCosmeticsPacket",
201
+ "coins.ClientCoinsBalancePacket",
202
+ "notices.ClientNoticeRequestPacket",
203
+ "serverdiscovery.ClientServerDiscoveryRequestPacket",
204
+ "knownservers.ClientKnownServersRequestPacket",
205
+ "multiplayer.ClientMultiplayerIceRelayPacket",
206
+ "pingproxy.ClientPingProxyRequestPacket",
207
+ "social.ClientCommunityRulesAgreedPacket"
208
+ ];
209
+ for (const t of TYPES) this.codec.registerOutgoing(t);
210
+ }
211
+ // ----- Frame handling -----
212
+ handleFrame(data) {
213
+ const packet = this.codec.decode(data);
214
+ if (!packet) return;
215
+ this.emit("raw", packet);
216
+ this.dispatch(packet);
217
+ }
218
+ dispatch(packet) {
219
+ if (packet.id && this.pendingRequests.has(packet.id)) {
220
+ const req = this.pendingRequests.get(packet.id);
221
+ this.pendingRequests.delete(packet.id);
222
+ clearTimeout(req.timer);
223
+ req.resolve(packet);
224
+ return;
225
+ }
226
+ if (!this.bootstrapReceived && packet.type === "social.ServerCommunityRulesStatePacket") {
227
+ this.bootstrapReceived = true;
228
+ this.state = "ready";
229
+ this.emit("bootstrap", void 0);
230
+ }
231
+ switch (packet.type) {
232
+ case "chat.ServerChatChannelMessagePacket": {
233
+ const msgs = packet.payload.a || [];
234
+ for (const m of msgs) {
235
+ this.emit("chat", {
236
+ id: m.a,
237
+ channelId: m.b ?? 0,
238
+ senderUuid: m.c,
239
+ content: m.d,
240
+ replyToId: m.e ?? null,
241
+ editedAt: m.f ?? null,
242
+ deleted: m.g ?? false,
243
+ createdAt: m.h ?? Date.now()
244
+ });
245
+ }
246
+ break;
247
+ }
248
+ case "relationships.ServerRelationshipPopulatePacket": {
249
+ const rels = packet.payload.a || [];
250
+ for (const r of rels) {
251
+ this.emit("friendAdded", {
252
+ userA: r.a,
253
+ userB: r.b,
254
+ type: r.c,
255
+ status: r.d,
256
+ since: r.e
257
+ });
258
+ }
259
+ break;
260
+ }
261
+ case "relationships.ServerRelationshipDeletedPacket": {
262
+ const dels = packet.payload.a || [];
263
+ for (const d of dels) {
264
+ this.emit("friendRemoved", { userA: d.a, userB: d.b, type: d.c });
265
+ }
266
+ break;
267
+ }
268
+ case "profile.ServerProfileStatusPacket": {
269
+ this.emit("profileStatus", {
270
+ uuid: packet.payload.a,
271
+ status: packet.payload.b,
272
+ lastOnlineTimestamp: packet.payload.lastOnlineTimestamp ?? 0
273
+ });
274
+ break;
275
+ }
276
+ case "profile.ServerProfileActivityPacket": {
277
+ this.emit("profileActivity", {
278
+ uuid: packet.payload.a,
279
+ activity: packet.payload.b,
280
+ metadata: packet.payload.c
281
+ });
282
+ break;
283
+ }
284
+ case "cosmetic.ServerCosmeticAnimationTriggerPacket": {
285
+ this.emit("cosmeticAnimation", {
286
+ userUuid: packet.payload.a,
287
+ cosmeticId: packet.payload.b,
288
+ animationId: packet.payload.c
289
+ });
290
+ break;
291
+ }
292
+ case "notices.ServerNoticePopulatePacket": {
293
+ const items = packet.payload.items || packet.payload.a || [];
294
+ for (const n of items) {
295
+ this.emit("notice", { id: n.a, title: n.b, body: n.c, category: n.d });
296
+ }
297
+ break;
298
+ }
299
+ case "cosmetic.ServerCosmeticsUserUnlockedPacket": {
300
+ this.emit("cosmeticsUnlocked", {
301
+ userUuid: packet.payload.c,
302
+ unlockedIds: packet.payload.a ?? [],
303
+ gifted: packet.payload.b,
304
+ unlockMap: packet.payload.d ?? {}
305
+ });
306
+ break;
307
+ }
308
+ case "cosmetic.ServerCosmeticsUserEquippedPacket": {
309
+ this.emit("equippedUpdate", {
310
+ userUuid: packet.payload.a,
311
+ equipped: packet.payload.b
312
+ });
313
+ break;
314
+ }
315
+ case "cosmetic.ServerCosmeticPlayerSettingsPacket": {
316
+ this.emit("playerSettings", {
317
+ userUuid: packet.payload.a,
318
+ settings: packet.payload.b
319
+ });
320
+ break;
321
+ }
322
+ case "cosmetic.ServerCosmeticsSkinTexturePacket": {
323
+ this.emit("skinTexture", {
324
+ userUuid: packet.payload.a,
325
+ skinTexture: packet.payload.b
326
+ });
327
+ break;
328
+ }
329
+ case "upnp.ServerUPnPSessionPopulatePacket": {
330
+ const sessions = packet.payload.a || [];
331
+ for (const s of sessions) {
332
+ this.emit("upnpSession", {
333
+ hostUuid: s.a,
334
+ ip: s.b,
335
+ port: s.c,
336
+ privacy: s.d,
337
+ worldName: s.e,
338
+ protocolVersion: s.f,
339
+ invites: s.g ?? [],
340
+ createdAt: s.h,
341
+ rawStatus: s.i
342
+ });
343
+ }
344
+ break;
345
+ }
346
+ case "upnp.ServerUPnPSessionRemovePacket": {
347
+ const removed = packet.payload.a || [];
348
+ for (const hostUuid of removed) {
349
+ this.emit("upnpSessionRemoved", { hostUuid });
350
+ }
351
+ break;
352
+ }
353
+ case "upnp.ServerUPnPSessionInviteAddPacket": {
354
+ this.emit("upnpInvite", { hostUuid: packet.payload.a });
355
+ break;
356
+ }
357
+ case "serverdiscovery.ServerServerDiscoveryResponsePacket": {
358
+ this.emit("serverList", {
359
+ recommended: packet.payload.recommended || [],
360
+ featured: packet.payload.featured || []
361
+ });
362
+ break;
363
+ }
364
+ }
365
+ }
366
+ // ----- Request/response -----
367
+ sendRequest(type, payload, timeoutMs = this.defaultRequestTimeoutMs) {
368
+ return new Promise((resolve, reject) => {
369
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
370
+ reject(new Error("WebSocket not connected"));
371
+ return;
372
+ }
373
+ const id = crypto.randomUUID();
374
+ const timer = setTimeout(() => {
375
+ this.pendingRequests.delete(id);
376
+ reject(new Error(`Request ${type} timed out`));
377
+ }, timeoutMs);
378
+ this.pendingRequests.set(id, { resolve, reject, type, timer });
379
+ for (const frame of this.codec.encode(type, payload, id)) {
380
+ this.ws.send(frame);
381
+ }
382
+ });
383
+ }
384
+ // ----- High-level API -----
385
+ // Chat
386
+ sendChatMessage(channelId, content, replyToId) {
387
+ return this.sendRequest("chat.ClientChatChannelMessageCreatePacket", {
388
+ a: channelId,
389
+ b: content,
390
+ c: replyToId ?? null
391
+ });
392
+ }
393
+ retrieveChatMessages(channelId, limit = 50, beforeMessageId) {
394
+ return this.sendRequest("chat.ClientChatChannelMessagesRetrievePacket", {
395
+ a: channelId,
396
+ b: limit,
397
+ c: beforeMessageId ?? null
398
+ });
399
+ }
400
+ createChannel(type, name, memberUuids = []) {
401
+ return this.sendRequest("chat.ClientChatChannelCreatePacket", {
402
+ a: type,
403
+ b: name,
404
+ c: memberUuids
405
+ });
406
+ }
407
+ deleteChatMessage(messageId) {
408
+ return this.sendRequest("chat.ChatChannelMessageDeletePacket", { a: messageId });
409
+ }
410
+ // Social
411
+ addFriend(targetUuid) {
412
+ return this.sendRequest("relationships.ClientRelationshipCreatePacket", {
413
+ a: targetUuid,
414
+ b: "FRIENDS"
415
+ });
416
+ }
417
+ removeFriend(targetUuid) {
418
+ return this.sendRequest("relationships.RelationshipDeletePacket", {
419
+ a: targetUuid,
420
+ b: "FRIENDS"
421
+ });
422
+ }
423
+ blockUser(targetUuid) {
424
+ return this.sendRequest("relationships.ClientRelationshipCreatePacket", {
425
+ a: targetUuid,
426
+ b: "BLOCKED"
427
+ });
428
+ }
429
+ lookupUuidByName(username) {
430
+ return this.sendRequest("relationships.ClientLookupUuidByNamePacket", { username });
431
+ }
432
+ // Profile
433
+ getProfile(uuid) {
434
+ return this.sendRequest("profile.ClientProfileRequestPacket", { a: uuid });
435
+ }
436
+ setActivity(activity, metadata = {}) {
437
+ return this.sendRequest("profile.ClientProfileActivityPacket", { a: activity, c: metadata });
438
+ }
439
+ // Cosmetics
440
+ listCosmetics() {
441
+ return this.sendRequest("cosmetic.ClientCosmeticRequestPacket", {});
442
+ }
443
+ triggerCosmeticAnimation(cosmeticId, animationId) {
444
+ return this.sendRequest("cosmetic.ClientCosmeticAnimationTriggerPacket", {
445
+ a: cosmeticId,
446
+ b: animationId
447
+ });
448
+ }
449
+ unlockCosmetics(cosmeticIds) {
450
+ return this.sendRequest("checkout.ClientCheckoutCosmeticsPacket", {
451
+ cosmetic_ids: cosmeticIds
452
+ });
453
+ }
454
+ // Skins
455
+ createSkin(name, model, hash) {
456
+ return this.sendRequest("skin.ClientSkinCreatePacket", { a: name, b: model, c: hash });
457
+ }
458
+ selectLastUsedSkin(skinId) {
459
+ return this.sendRequest("skin.ClientSkinUpdateLastUsedStatePacket", { a: skinId });
460
+ }
461
+ favoriteSkin(skinId, favorited) {
462
+ return this.sendRequest("skin.ClientSkinUpdateFavoriteStatePacket", {
463
+ a: skinId,
464
+ b: favorited
465
+ });
466
+ }
467
+ // Outfits
468
+ createOutfit(name, equippedCosmetics, settings = {}) {
469
+ return this.sendRequest("cosmetic.outfit.ClientCosmeticOutfitCreatePacket", {
470
+ name,
471
+ skin_id: null,
472
+ equipped_cosmetics: equippedCosmetics,
473
+ cosmetic_settings: settings
474
+ });
475
+ }
476
+ selectOutfit(outfitId) {
477
+ return this.sendRequest("cosmetic.outfit.ClientCosmeticOutfitSelectPacket", { a: outfitId });
478
+ }
479
+ setEquippedCosmetic(outfitId, slot, cosmeticId) {
480
+ return this.sendRequest("cosmetic.outfit.ClientCosmeticOutfitEquippedCosmeticsUpdatePacket", {
481
+ a: outfitId,
482
+ b: slot,
483
+ c: cosmeticId
484
+ });
485
+ }
486
+ // Emote wheel
487
+ updateEmoteWheel(wheelId, slots) {
488
+ return this.sendRequest("cosmetic.emote.ClientCosmeticEmoteWheelUpdatePacket", {
489
+ a: wheelId,
490
+ b: slots
491
+ });
492
+ }
493
+ selectEmoteWheel(wheelId) {
494
+ return this.sendRequest("cosmetic.emote.ClientCosmeticEmoteWheelSelectPacket", { a: wheelId });
495
+ }
496
+ // Wardrobe settings
497
+ getWardrobeSettings() {
498
+ return this.sendRequest("wardrobe.ClientWardrobeSettingsPacket", {});
499
+ }
500
+ // Discovery
501
+ listNotices() {
502
+ return this.sendRequest("notices.ClientNoticeRequestPacket", {});
503
+ }
504
+ listServerDiscovery() {
505
+ return this.sendRequest("serverdiscovery.ClientServerDiscoveryRequestPacket", {});
506
+ }
507
+ listKnownServers() {
508
+ return this.sendRequest("knownservers.ClientKnownServersRequestPacket", {});
509
+ }
510
+ // Multiplayer
511
+ relayIcePacket(targetUuid, payload) {
512
+ return this.sendRequest("multiplayer.ClientMultiplayerIceRelayPacket", {
513
+ a: targetUuid,
514
+ b: Array.from(payload)
515
+ });
516
+ }
517
+ pingServer(host, port, protocolVersion) {
518
+ return this.sendRequest("pingproxy.ClientPingProxyRequestPacket", {
519
+ a: host,
520
+ b: port,
521
+ c: protocolVersion ?? 0
522
+ });
523
+ }
524
+ // UPnP server hosting
525
+ createSession(ip, port, privacy, worldName, protocolVersion) {
526
+ return this.sendRequest("upnp.ClientUPnPSessionCreatePacket", {
527
+ a: ip,
528
+ b: port,
529
+ c: privacy,
530
+ d: protocolVersion,
531
+ e: worldName
532
+ });
533
+ }
534
+ closeSession() {
535
+ return this.sendRequest("upnp.ClientUPnPSessionClosePacket", {});
536
+ }
537
+ updateSession(patch) {
538
+ return this.sendRequest("upnp.ClientUPnPSessionUpdatePacket", {
539
+ a: patch.ip,
540
+ b: patch.port,
541
+ c: patch.privacy
542
+ });
543
+ }
544
+ inviteToSession(inviteeUuids) {
545
+ return this.sendRequest("upnp.ClientUPnPSessionInvitesAddPacket", { a: inviteeUuids });
546
+ }
547
+ revokeSessionInvites(inviteeUuids) {
548
+ return this.sendRequest("upnp.ClientUPnPSessionInvitesRemovePacket", { a: inviteeUuids });
549
+ }
550
+ pushServerStatus(rawStatus) {
551
+ return this.sendRequest("upnp.ClientUPnPSessionPingProxyUpdatePacket", { a: rawStatus });
552
+ }
553
+ // Social invite
554
+ inviteFriendToServer(targetUuid, address) {
555
+ return this.sendRequest("social.ClientSocialInviteRequestPacket", {
556
+ a: targetUuid,
557
+ b: address
558
+ });
559
+ }
560
+ // ----- Event emitter -----
561
+ on(event, listener) {
562
+ if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
563
+ this.listeners.get(event).add(listener);
564
+ return () => this.off(event, listener);
565
+ }
566
+ off(event, listener) {
567
+ this.listeners.get(event)?.delete(listener);
568
+ }
569
+ emit(event, data) {
570
+ for (const l of this.listeners.get(event) ?? []) {
571
+ try {
572
+ l(data);
573
+ } catch (e) {
574
+ }
575
+ }
576
+ }
577
+ isReady() {
578
+ return this.state === "ready";
579
+ }
580
+ isConnected() {
581
+ return this.state === "connected" || this.state === "ready";
582
+ }
583
+ };
584
+ function useWsEvent(client, event, handler) {
585
+ react.useEffect(() => {
586
+ const off = client.on(event, handler);
587
+ return off;
588
+ }, [client, event]);
589
+ }
590
+ function useWsState(client) {
591
+ const [state, setState] = react.useState(
592
+ client.isConnected() ? client.isReady() ? "ready" : "connected" : "disconnected"
593
+ );
594
+ react.useEffect(() => {
595
+ const update = () => setState(client.isConnected() ? client.isReady() ? "ready" : "connected" : "disconnected");
596
+ const off1 = client.on("open", update);
597
+ const off2 = client.on("bootstrap", update);
598
+ const off3 = client.on("close", update);
599
+ return () => {
600
+ off1();
601
+ off2();
602
+ off3();
603
+ };
604
+ }, [client]);
605
+ return state;
606
+ }
607
+
608
+ exports.AnvilWsClient = AnvilWsClient;
609
+ exports.ConnectionCodec = ConnectionCodec;
610
+ exports.useWsEvent = useWsEvent;
611
+ exports.useWsState = useWsState;
612
+ //# sourceMappingURL=index.cjs.map
613
+ //# sourceMappingURL=index.cjs.map