@emeryld/rrroutes-client 2.0.8 → 2.0.10
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/dist/index.cjs +297 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +297 -99
- package/dist/index.mjs.map +1 -1
- package/dist/sockets/socket.client.context.d.ts +7 -7
- package/dist/sockets/socket.client.index.d.ts +150 -47
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -488,30 +488,33 @@ function buildSocketProvider(args) {
|
|
|
488
488
|
}
|
|
489
489
|
function SocketProvider(props) {
|
|
490
490
|
const { events, baseOptions, children, fallback, providerDebug } = props;
|
|
491
|
-
const [
|
|
492
|
-
|
|
493
|
-
);
|
|
491
|
+
const [resolvedSocket, setResolvedSocket] = React.useState(null);
|
|
492
|
+
const socket = "socket" in props ? props.socket ?? null : resolvedSocket;
|
|
494
493
|
React.useEffect(() => {
|
|
494
|
+
if (!("getSocket" in props)) return;
|
|
495
495
|
let cancelled = false;
|
|
496
496
|
dbg(providerDebug, { type: "resolve", phase: "start" });
|
|
497
|
-
if (!
|
|
497
|
+
if (!resolvedSocket) {
|
|
498
498
|
Promise.resolve(props.getSocket()).then((s) => {
|
|
499
499
|
if (cancelled) {
|
|
500
500
|
dbg(providerDebug, { type: "resolve", phase: "cancelled" });
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
if (!s) {
|
|
504
|
+
dbg(providerDebug, { type: "resolve", phase: "missing" });
|
|
505
|
+
return;
|
|
501
506
|
}
|
|
502
|
-
|
|
507
|
+
setResolvedSocket(s);
|
|
503
508
|
dbg(providerDebug, { type: "resolve", phase: "ok" });
|
|
504
509
|
}).catch((err) => {
|
|
505
|
-
dbg(providerDebug, { type: "resolve", phase: "error", err: String(err) });
|
|
506
510
|
if (cancelled) return;
|
|
511
|
+
dbg(providerDebug, { type: "resolve", phase: "error", err: String(err) });
|
|
507
512
|
});
|
|
508
|
-
} else {
|
|
509
|
-
dbg(providerDebug, { type: "resolve", phase: "missing" });
|
|
510
513
|
}
|
|
511
514
|
return () => {
|
|
512
515
|
cancelled = true;
|
|
513
516
|
};
|
|
514
|
-
}, [
|
|
517
|
+
}, [resolvedSocket]);
|
|
515
518
|
const client = React.useMemo(() => {
|
|
516
519
|
if (!socket) {
|
|
517
520
|
dbg(providerDebug, { type: "client", phase: "missing" });
|
|
@@ -567,78 +570,165 @@ var SocketClient = class {
|
|
|
567
570
|
this.roomCounts = /* @__PURE__ */ new Map();
|
|
568
571
|
this.handlerMap = /* @__PURE__ */ new Map();
|
|
569
572
|
this.events = events;
|
|
570
|
-
this.socket = opts.socket;
|
|
571
|
-
this.roomJoinEvent = opts.roomJoinEvent ?? "room:join";
|
|
572
|
-
this.roomLeaveEvent = opts.roomLeaveEvent ?? "room:leave";
|
|
573
|
+
this.socket = opts.socket ?? null;
|
|
573
574
|
this.environment = opts.environment ?? "development";
|
|
574
575
|
this.debug = opts.debug ?? {};
|
|
575
|
-
|
|
576
|
+
this.sysEvents = opts.sys ?? {};
|
|
577
|
+
const hb = opts.heartbeat ?? {};
|
|
576
578
|
this.hb = {
|
|
577
|
-
pingEvent: hb.pingEvent ?? "sys:ping",
|
|
578
|
-
pongEvent: hb.pongEvent ?? "sys:pong",
|
|
579
579
|
intervalMs: hb.intervalMs ?? 15e3,
|
|
580
580
|
timeoutMs: hb.timeoutMs ?? 7500,
|
|
581
|
-
pingSchema: hb.pingSchema,
|
|
582
|
-
makePingPayload: hb.makePingPayload,
|
|
583
|
-
pongSchema: hb.pongSchema,
|
|
584
581
|
onPong: hb.onPong
|
|
585
582
|
};
|
|
586
583
|
this.onConnect = () => {
|
|
587
|
-
this.
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
584
|
+
const sys = this.getSysEvent("sys:connect");
|
|
585
|
+
const defaultHandler = () => {
|
|
586
|
+
this.dbg({
|
|
587
|
+
type: "connection",
|
|
588
|
+
phase: "connect",
|
|
589
|
+
id: this.socket?.id ?? ""
|
|
590
|
+
});
|
|
591
|
+
this.startHeartbeat();
|
|
592
|
+
};
|
|
593
|
+
if (sys?.sysHandler) {
|
|
594
|
+
sys.sysHandler({
|
|
595
|
+
name: "sys:connect",
|
|
596
|
+
phase: "connect",
|
|
597
|
+
socket: this.socket,
|
|
598
|
+
next: defaultHandler,
|
|
599
|
+
startHeartbeat: () => this.startHeartbeat(),
|
|
600
|
+
stopHeartbeat: () => this.stopHeartbeat()
|
|
601
|
+
});
|
|
602
|
+
} else {
|
|
603
|
+
defaultHandler();
|
|
604
|
+
}
|
|
593
605
|
};
|
|
594
606
|
this.onReconnect = (attempt) => {
|
|
595
|
-
this.
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
607
|
+
const sys = this.getSysEvent("sys:reconnect");
|
|
608
|
+
const defaultHandler = () => {
|
|
609
|
+
this.dbg({
|
|
610
|
+
type: "connection",
|
|
611
|
+
phase: "reconnect",
|
|
612
|
+
attempt
|
|
613
|
+
});
|
|
614
|
+
};
|
|
615
|
+
if (sys?.sysHandler) {
|
|
616
|
+
sys.sysHandler({
|
|
617
|
+
name: "sys:reconnect",
|
|
618
|
+
phase: "reconnect",
|
|
619
|
+
attempt,
|
|
620
|
+
socket: this.socket,
|
|
621
|
+
next: defaultHandler,
|
|
622
|
+
startHeartbeat: () => this.startHeartbeat(),
|
|
623
|
+
stopHeartbeat: () => this.stopHeartbeat()
|
|
624
|
+
});
|
|
625
|
+
} else {
|
|
626
|
+
defaultHandler();
|
|
627
|
+
}
|
|
600
628
|
};
|
|
601
629
|
this.onDisconnect = (reason) => {
|
|
602
|
-
this.
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
630
|
+
const sys = this.getSysEvent("sys:disconnect");
|
|
631
|
+
const defaultHandler = () => {
|
|
632
|
+
this.dbg({
|
|
633
|
+
type: "connection",
|
|
634
|
+
phase: "disconnect",
|
|
635
|
+
reason: String(reason)
|
|
636
|
+
});
|
|
637
|
+
this.stopHeartbeat();
|
|
638
|
+
};
|
|
639
|
+
if (sys?.sysHandler) {
|
|
640
|
+
sys.sysHandler({
|
|
641
|
+
name: "sys:disconnect",
|
|
642
|
+
phase: "disconnect",
|
|
643
|
+
reason: String(reason),
|
|
644
|
+
socket: this.socket,
|
|
645
|
+
next: defaultHandler,
|
|
646
|
+
startHeartbeat: () => this.startHeartbeat(),
|
|
647
|
+
stopHeartbeat: () => this.stopHeartbeat()
|
|
648
|
+
});
|
|
649
|
+
} else {
|
|
650
|
+
defaultHandler();
|
|
651
|
+
}
|
|
608
652
|
};
|
|
609
653
|
this.onConnectError = (err) => {
|
|
610
|
-
this.
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
654
|
+
const sys = this.getSysEvent("sys:connect_error");
|
|
655
|
+
const defaultHandler = () => {
|
|
656
|
+
this.dbg({
|
|
657
|
+
type: "connection",
|
|
658
|
+
phase: "connect_error",
|
|
659
|
+
err: String(err)
|
|
660
|
+
});
|
|
661
|
+
};
|
|
662
|
+
if (sys?.sysHandler) {
|
|
663
|
+
sys.sysHandler({
|
|
664
|
+
name: "sys:connect_error",
|
|
665
|
+
phase: "connect_error",
|
|
666
|
+
error: err,
|
|
667
|
+
socket: this.socket,
|
|
668
|
+
next: defaultHandler,
|
|
669
|
+
startHeartbeat: () => this.startHeartbeat(),
|
|
670
|
+
stopHeartbeat: () => this.stopHeartbeat()
|
|
671
|
+
});
|
|
672
|
+
} else {
|
|
673
|
+
defaultHandler();
|
|
674
|
+
}
|
|
615
675
|
};
|
|
616
676
|
this.onPong = (raw) => {
|
|
677
|
+
const sys = this.getSysEvent("sys:pong");
|
|
678
|
+
const schema = sys?.message;
|
|
679
|
+
let validated = raw;
|
|
680
|
+
if (schema) {
|
|
681
|
+
const ok = schema.safeParse(raw);
|
|
682
|
+
if (!ok.success) return;
|
|
683
|
+
validated = ok.data;
|
|
684
|
+
}
|
|
617
685
|
const receivedAt = Date.now();
|
|
618
|
-
const clientSentIso =
|
|
686
|
+
const clientSentIso = validated?.clientEcho?.__clientSentAt;
|
|
619
687
|
let latencyMs;
|
|
620
688
|
if (clientSentIso) {
|
|
621
689
|
const sent = Date.parse(clientSentIso);
|
|
622
690
|
if (!Number.isNaN(sent)) latencyMs = Math.max(0, receivedAt - sent);
|
|
623
691
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
692
|
+
const latency = latencyMs ?? validated?.sinceMs ?? 0;
|
|
693
|
+
const defaultHandler = () => {
|
|
694
|
+
this.dbg({
|
|
695
|
+
type: "heartbeat",
|
|
696
|
+
action: "pong_recv",
|
|
697
|
+
latencyMs: latency,
|
|
698
|
+
payload: validated
|
|
699
|
+
});
|
|
700
|
+
if (this.hb.onPong) {
|
|
701
|
+
this.hb.onPong({
|
|
702
|
+
latencyMs: latency,
|
|
703
|
+
payload: validated,
|
|
704
|
+
socket: this.socket
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
if (sys?.sysHandler) {
|
|
709
|
+
sys.sysHandler({
|
|
710
|
+
name: "sys:pong",
|
|
711
|
+
socket: this.socket,
|
|
712
|
+
raw: validated,
|
|
713
|
+
latencyMs: latency,
|
|
714
|
+
next: defaultHandler,
|
|
715
|
+
startHeartbeat: () => this.startHeartbeat(),
|
|
716
|
+
stopHeartbeat: () => this.stopHeartbeat()
|
|
717
|
+
});
|
|
718
|
+
} else {
|
|
719
|
+
defaultHandler();
|
|
627
720
|
}
|
|
628
|
-
const latency = latencyMs ?? raw?.sinceMs ?? 0;
|
|
629
|
-
this.dbg({
|
|
630
|
-
type: "heartbeat",
|
|
631
|
-
action: "pong_recv",
|
|
632
|
-
latencyMs: latency,
|
|
633
|
-
payload: raw
|
|
634
|
-
});
|
|
635
|
-
this.hb.onPong?.({ latencyMs: latency, payload: raw, socket: this.socket });
|
|
636
721
|
};
|
|
637
|
-
this.socket
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
722
|
+
if (this.socket) {
|
|
723
|
+
this.socket.on("connect", this.onConnect);
|
|
724
|
+
this.socket.on("reconnect", this.onReconnect);
|
|
725
|
+
this.socket.on("disconnect", this.onDisconnect);
|
|
726
|
+
this.socket.on("connect_error", this.onConnectError);
|
|
727
|
+
this.socket.on("sys:pong", this.onPong);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
getSysEvent(name) {
|
|
731
|
+
return this.sysEvents[name];
|
|
642
732
|
}
|
|
643
733
|
dbg(e) {
|
|
644
734
|
const d = this.debug;
|
|
@@ -664,31 +754,73 @@ var SocketClient = class {
|
|
|
664
754
|
toArray(rooms) {
|
|
665
755
|
return rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms];
|
|
666
756
|
}
|
|
757
|
+
/**
|
|
758
|
+
* Public: start the heartbeat loop manually.
|
|
759
|
+
*
|
|
760
|
+
* This is called by the default 'sys:connect' handler, but you can also
|
|
761
|
+
* call it yourself from any sysHandler to change when heartbeats start.
|
|
762
|
+
*/
|
|
667
763
|
startHeartbeat() {
|
|
668
764
|
this.stopHeartbeat();
|
|
765
|
+
if (!this.socket) return;
|
|
766
|
+
const socket = this.socket;
|
|
669
767
|
const tick = () => {
|
|
670
|
-
|
|
671
|
-
const
|
|
672
|
-
const
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
console.warn("[socket] ping schema validation failed", check.error.issues);
|
|
676
|
-
return;
|
|
677
|
-
}
|
|
678
|
-
const timer = setTimeout(() => {
|
|
679
|
-
}, this.hb.timeoutMs);
|
|
680
|
-
this.socket.timeout(this.hb.timeoutMs).emit(this.hb.pingEvent, { payload: check.data }, () => {
|
|
681
|
-
clearTimeout(timer);
|
|
682
|
-
});
|
|
683
|
-
this.dbg({
|
|
684
|
-
type: "heartbeat",
|
|
685
|
-
action: "ping_emit",
|
|
686
|
-
payload: check.data
|
|
768
|
+
if (!socket) return;
|
|
769
|
+
const sysPing = this.getSysEvent("sys:ping");
|
|
770
|
+
const schema = sysPing?.message;
|
|
771
|
+
const buildDefaultPayload = () => ({
|
|
772
|
+
clientEcho: { __clientSentAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
687
773
|
});
|
|
774
|
+
const send = (payload) => {
|
|
775
|
+
let dataToSend = payload;
|
|
776
|
+
if (schema) {
|
|
777
|
+
const check = schema.safeParse(payload);
|
|
778
|
+
if (!check.success) {
|
|
779
|
+
if (this.environment === "development") {
|
|
780
|
+
console.warn("[socket] ping schema validation failed", check.error.issues);
|
|
781
|
+
}
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
dataToSend = check.data;
|
|
785
|
+
}
|
|
786
|
+
const timer = setTimeout(() => {
|
|
787
|
+
}, this.hb.timeoutMs);
|
|
788
|
+
socket.timeout(this.hb.timeoutMs).emit("sys:ping", dataToSend, () => {
|
|
789
|
+
clearTimeout(timer);
|
|
790
|
+
});
|
|
791
|
+
this.dbg({
|
|
792
|
+
type: "heartbeat",
|
|
793
|
+
action: "ping_emit",
|
|
794
|
+
payload: dataToSend
|
|
795
|
+
});
|
|
796
|
+
};
|
|
797
|
+
const defaultHandler = () => {
|
|
798
|
+
const payload = buildDefaultPayload();
|
|
799
|
+
send(payload);
|
|
800
|
+
};
|
|
801
|
+
if (sysPing?.sysHandler) {
|
|
802
|
+
sysPing.sysHandler({
|
|
803
|
+
name: "sys:ping",
|
|
804
|
+
socket,
|
|
805
|
+
buildDefaultPayload,
|
|
806
|
+
send,
|
|
807
|
+
next: defaultHandler,
|
|
808
|
+
startHeartbeat: () => this.startHeartbeat(),
|
|
809
|
+
stopHeartbeat: () => this.stopHeartbeat()
|
|
810
|
+
});
|
|
811
|
+
} else {
|
|
812
|
+
defaultHandler();
|
|
813
|
+
}
|
|
688
814
|
};
|
|
689
815
|
this.hbTimer = setInterval(tick, this.hb.intervalMs);
|
|
690
816
|
tick();
|
|
691
817
|
}
|
|
818
|
+
/**
|
|
819
|
+
* Public: stop the heartbeat loop.
|
|
820
|
+
*
|
|
821
|
+
* This is called by the default 'sys:disconnect' handler, but you can also
|
|
822
|
+
* call it yourself from any sysHandler to fully control heartbeat lifecycle.
|
|
823
|
+
*/
|
|
692
824
|
stopHeartbeat() {
|
|
693
825
|
if (this.hbTimer) {
|
|
694
826
|
clearInterval(this.hbTimer);
|
|
@@ -699,6 +831,12 @@ var SocketClient = class {
|
|
|
699
831
|
const schema = this.events[event].message;
|
|
700
832
|
const parsed = schema.safeParse(payload);
|
|
701
833
|
if (!parsed.success) throw new Error(`Invalid payload for "${event}": ${parsed.error.message}`);
|
|
834
|
+
if (!this.socket) {
|
|
835
|
+
if (this.environment === "development") {
|
|
836
|
+
console.warn(`[socket] emit("${String(event)}") skipped because socket is null`);
|
|
837
|
+
}
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
702
840
|
if (onAck) {
|
|
703
841
|
this.socket.timeout(timeoutMs ?? this.hb.timeoutMs).emit(String(event), parsed.data, (ack) => {
|
|
704
842
|
try {
|
|
@@ -723,9 +861,25 @@ var SocketClient = class {
|
|
|
723
861
|
this.roomCounts.set(r, next);
|
|
724
862
|
if (next === 1) toJoin.push(r);
|
|
725
863
|
}
|
|
726
|
-
if (toJoin.length > 0) {
|
|
727
|
-
this.socket
|
|
728
|
-
this.
|
|
864
|
+
if (toJoin.length > 0 && this.socket) {
|
|
865
|
+
const socket = this.socket;
|
|
866
|
+
const sys = this.getSysEvent("sys:room_join");
|
|
867
|
+
const defaultHandler = () => {
|
|
868
|
+
socket.emit("sys:room_join", { rooms: toJoin });
|
|
869
|
+
this.dbg({ type: "room", action: "join", rooms: toJoin });
|
|
870
|
+
};
|
|
871
|
+
if (sys?.sysHandler) {
|
|
872
|
+
sys.sysHandler({
|
|
873
|
+
name: "sys:room_join",
|
|
874
|
+
rooms: toJoin,
|
|
875
|
+
socket,
|
|
876
|
+
next: defaultHandler,
|
|
877
|
+
startHeartbeat: () => this.startHeartbeat(),
|
|
878
|
+
stopHeartbeat: () => this.stopHeartbeat()
|
|
879
|
+
});
|
|
880
|
+
} else {
|
|
881
|
+
defaultHandler();
|
|
882
|
+
}
|
|
729
883
|
}
|
|
730
884
|
}
|
|
731
885
|
leaveRooms(rooms) {
|
|
@@ -738,14 +892,38 @@ var SocketClient = class {
|
|
|
738
892
|
if (next === 0) this.roomCounts.delete(r);
|
|
739
893
|
else this.roomCounts.set(r, next);
|
|
740
894
|
}
|
|
741
|
-
if (toLeave.length > 0) {
|
|
742
|
-
this.socket
|
|
743
|
-
this.
|
|
895
|
+
if (toLeave.length > 0 && this.socket) {
|
|
896
|
+
const socket = this.socket;
|
|
897
|
+
const sys = this.getSysEvent("sys:room_leave");
|
|
898
|
+
const defaultHandler = () => {
|
|
899
|
+
socket.emit("sys:room_leave", { rooms: toLeave });
|
|
900
|
+
this.dbg({ type: "room", action: "leave", rooms: toLeave });
|
|
901
|
+
};
|
|
902
|
+
if (sys?.sysHandler) {
|
|
903
|
+
sys.sysHandler({
|
|
904
|
+
name: "sys:room_leave",
|
|
905
|
+
rooms: toLeave,
|
|
906
|
+
socket,
|
|
907
|
+
next: defaultHandler,
|
|
908
|
+
startHeartbeat: () => this.startHeartbeat(),
|
|
909
|
+
stopHeartbeat: () => this.stopHeartbeat()
|
|
910
|
+
});
|
|
911
|
+
} else {
|
|
912
|
+
defaultHandler();
|
|
913
|
+
}
|
|
744
914
|
}
|
|
745
915
|
}
|
|
746
916
|
on(event, handler) {
|
|
747
917
|
const schema = this.events[event].message;
|
|
748
918
|
this.dbg({ type: "register", action: "register", event });
|
|
919
|
+
if (!this.socket) {
|
|
920
|
+
if (this.environment === "development") {
|
|
921
|
+
console.warn(`[socket] on("${String(event)}") skipped because socket is null`);
|
|
922
|
+
}
|
|
923
|
+
return () => {
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
const socket = this.socket;
|
|
749
927
|
const wrapped = (envelopeOrRaw, maybeAck) => {
|
|
750
928
|
const maybeEnvelope = envelopeOrRaw;
|
|
751
929
|
const rawData = maybeEnvelope?.data ?? maybeEnvelope;
|
|
@@ -764,9 +942,9 @@ var SocketClient = class {
|
|
|
764
942
|
ctx: {
|
|
765
943
|
receivedAt,
|
|
766
944
|
latencyMs: sentAt ? Math.max(0, receivedAt.getTime() - sentAt.getTime()) : void 0,
|
|
767
|
-
nsp:
|
|
768
|
-
socketId:
|
|
769
|
-
socket
|
|
945
|
+
nsp: socket.nsp,
|
|
946
|
+
socketId: socket.id,
|
|
947
|
+
socket,
|
|
770
948
|
reply: typeof maybeAck === "function" ? (d) => {
|
|
771
949
|
try {
|
|
772
950
|
maybeAck(d);
|
|
@@ -792,8 +970,8 @@ var SocketClient = class {
|
|
|
792
970
|
console.warn(`[socket] ${String(event)}:error`, e);
|
|
793
971
|
}
|
|
794
972
|
};
|
|
795
|
-
|
|
796
|
-
|
|
973
|
+
socket.on(String(event), wrapped);
|
|
974
|
+
socket.on(`${String(event)}:error`, errorWrapped);
|
|
797
975
|
let set = this.handlerMap.get(String(event));
|
|
798
976
|
if (!set) {
|
|
799
977
|
set = /* @__PURE__ */ new Set();
|
|
@@ -802,8 +980,8 @@ var SocketClient = class {
|
|
|
802
980
|
const entry = { orig: handler, wrapped, errorWrapped };
|
|
803
981
|
set.add(entry);
|
|
804
982
|
return () => {
|
|
805
|
-
|
|
806
|
-
|
|
983
|
+
socket.off(String(event), wrapped);
|
|
984
|
+
socket.off(`${String(event)}:error`, errorWrapped);
|
|
807
985
|
const s = this.handlerMap.get(String(event));
|
|
808
986
|
if (s) {
|
|
809
987
|
s.delete(entry);
|
|
@@ -818,32 +996,52 @@ var SocketClient = class {
|
|
|
818
996
|
*/
|
|
819
997
|
destroy() {
|
|
820
998
|
this.stopHeartbeat();
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
999
|
+
const socket = this.socket;
|
|
1000
|
+
if (socket) {
|
|
1001
|
+
socket.off("connect", this.onConnect);
|
|
1002
|
+
socket.off("reconnect", this.onReconnect);
|
|
1003
|
+
socket.off("disconnect", this.onDisconnect);
|
|
1004
|
+
socket.off("connect_error", this.onConnectError);
|
|
1005
|
+
socket.off("sys:pong", this.onPong);
|
|
1006
|
+
for (const [event, set] of this.handlerMap.entries()) {
|
|
1007
|
+
for (const entry of set) {
|
|
1008
|
+
socket.off(String(event), entry.wrapped);
|
|
1009
|
+
socket.off(`${String(event)}:error`, entry.errorWrapped);
|
|
1010
|
+
}
|
|
830
1011
|
}
|
|
831
1012
|
}
|
|
832
1013
|
this.handlerMap.clear();
|
|
833
1014
|
const toLeave = Array.from(this.roomCounts.entries()).filter(([, count]) => count > 0).map(([room]) => room);
|
|
834
|
-
if (toLeave.length > 0) {
|
|
835
|
-
this.
|
|
836
|
-
|
|
1015
|
+
if (toLeave.length > 0 && socket) {
|
|
1016
|
+
const sys = this.getSysEvent("sys:room_leave");
|
|
1017
|
+
const defaultHandler = () => {
|
|
1018
|
+
socket.emit("sys:room_leave", { rooms: toLeave });
|
|
1019
|
+
this.dbg({ type: "room", action: "leave", rooms: toLeave });
|
|
1020
|
+
};
|
|
1021
|
+
if (sys?.sysHandler) {
|
|
1022
|
+
sys.sysHandler({
|
|
1023
|
+
name: "sys:room_leave",
|
|
1024
|
+
rooms: toLeave,
|
|
1025
|
+
socket,
|
|
1026
|
+
next: defaultHandler,
|
|
1027
|
+
startHeartbeat: () => this.startHeartbeat(),
|
|
1028
|
+
stopHeartbeat: () => this.stopHeartbeat()
|
|
1029
|
+
});
|
|
1030
|
+
} else {
|
|
1031
|
+
defaultHandler();
|
|
1032
|
+
}
|
|
837
1033
|
}
|
|
838
1034
|
this.roomCounts.clear();
|
|
839
1035
|
}
|
|
840
1036
|
/** Pass-throughs. Managing connection is the caller’s responsibility. */
|
|
841
1037
|
disconnect() {
|
|
1038
|
+
if (!this.socket) return;
|
|
842
1039
|
this.stopHeartbeat();
|
|
843
1040
|
this.socket.disconnect();
|
|
844
1041
|
this.dbg({ type: "connection", phase: "disconnect", reason: "client_disconnect" });
|
|
845
1042
|
}
|
|
846
1043
|
connect() {
|
|
1044
|
+
if (!this.socket) return;
|
|
847
1045
|
this.socket.connect();
|
|
848
1046
|
this.dbg({ type: "connection", phase: "connect", id: this.socket.id ?? "" });
|
|
849
1047
|
}
|