@emeryld/rrroutes-client 2.0.9 → 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 +240 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +240 -69
- package/dist/index.mjs.map +1 -1
- package/dist/sockets/socket.client.context.d.ts +6 -6
- package/dist/sockets/socket.client.index.d.ts +144 -30
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -571,81 +571,165 @@ var SocketClient = class {
|
|
|
571
571
|
this.handlerMap = /* @__PURE__ */ new Map();
|
|
572
572
|
this.events = events;
|
|
573
573
|
this.socket = opts.socket ?? null;
|
|
574
|
-
this.roomJoinEvent = opts.roomJoinEvent ?? "room:join";
|
|
575
|
-
this.roomLeaveEvent = opts.roomLeaveEvent ?? "room:leave";
|
|
576
574
|
this.environment = opts.environment ?? "development";
|
|
577
575
|
this.debug = opts.debug ?? {};
|
|
578
|
-
|
|
576
|
+
this.sysEvents = opts.sys ?? {};
|
|
577
|
+
const hb = opts.heartbeat ?? {};
|
|
579
578
|
this.hb = {
|
|
580
|
-
pingEvent: hb.pingEvent ?? "sys:ping",
|
|
581
|
-
pongEvent: hb.pongEvent ?? "sys:pong",
|
|
582
579
|
intervalMs: hb.intervalMs ?? 15e3,
|
|
583
580
|
timeoutMs: hb.timeoutMs ?? 7500,
|
|
584
|
-
pingSchema: hb.pingSchema,
|
|
585
|
-
makePingPayload: hb.makePingPayload,
|
|
586
|
-
pongSchema: hb.pongSchema,
|
|
587
581
|
onPong: hb.onPong
|
|
588
582
|
};
|
|
589
583
|
this.onConnect = () => {
|
|
590
|
-
this.
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
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
|
+
}
|
|
597
605
|
};
|
|
598
606
|
this.onReconnect = (attempt) => {
|
|
599
|
-
this.
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
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
|
+
}
|
|
604
628
|
};
|
|
605
629
|
this.onDisconnect = (reason) => {
|
|
606
|
-
this.
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
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
|
+
}
|
|
612
652
|
};
|
|
613
653
|
this.onConnectError = (err) => {
|
|
614
|
-
this.
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
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
|
+
}
|
|
619
675
|
};
|
|
620
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
|
+
}
|
|
621
685
|
const receivedAt = Date.now();
|
|
622
|
-
const clientSentIso =
|
|
686
|
+
const clientSentIso = validated?.clientEcho?.__clientSentAt;
|
|
623
687
|
let latencyMs;
|
|
624
688
|
if (clientSentIso) {
|
|
625
689
|
const sent = Date.parse(clientSentIso);
|
|
626
690
|
if (!Number.isNaN(sent)) latencyMs = Math.max(0, receivedAt - sent);
|
|
627
691
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
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();
|
|
631
720
|
}
|
|
632
|
-
const latency = latencyMs ?? raw?.sinceMs ?? 0;
|
|
633
|
-
this.dbg({
|
|
634
|
-
type: "heartbeat",
|
|
635
|
-
action: "pong_recv",
|
|
636
|
-
latencyMs: latency,
|
|
637
|
-
payload: raw
|
|
638
|
-
});
|
|
639
|
-
this.hb.onPong?.({ latencyMs: latency, payload: raw, socket: this.socket });
|
|
640
721
|
};
|
|
641
722
|
if (this.socket) {
|
|
642
723
|
this.socket.on("connect", this.onConnect);
|
|
643
724
|
this.socket.on("reconnect", this.onReconnect);
|
|
644
725
|
this.socket.on("disconnect", this.onDisconnect);
|
|
645
726
|
this.socket.on("connect_error", this.onConnectError);
|
|
646
|
-
this.socket.on(
|
|
727
|
+
this.socket.on("sys:pong", this.onPong);
|
|
647
728
|
}
|
|
648
729
|
}
|
|
730
|
+
getSysEvent(name) {
|
|
731
|
+
return this.sysEvents[name];
|
|
732
|
+
}
|
|
649
733
|
dbg(e) {
|
|
650
734
|
const d = this.debug;
|
|
651
735
|
if (!d.logger) return;
|
|
@@ -670,33 +754,73 @@ var SocketClient = class {
|
|
|
670
754
|
toArray(rooms) {
|
|
671
755
|
return rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms];
|
|
672
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
|
+
*/
|
|
673
763
|
startHeartbeat() {
|
|
674
764
|
this.stopHeartbeat();
|
|
675
765
|
if (!this.socket) return;
|
|
766
|
+
const socket = this.socket;
|
|
676
767
|
const tick = () => {
|
|
677
|
-
if (!
|
|
678
|
-
const
|
|
679
|
-
const
|
|
680
|
-
const
|
|
681
|
-
|
|
682
|
-
if (this.environment === "development")
|
|
683
|
-
console.warn("[socket] ping schema validation failed", check.error.issues);
|
|
684
|
-
return;
|
|
685
|
-
}
|
|
686
|
-
const timer = setTimeout(() => {
|
|
687
|
-
}, this.hb.timeoutMs);
|
|
688
|
-
this.socket.timeout(this.hb.timeoutMs).emit(this.hb.pingEvent, { payload: check.data }, () => {
|
|
689
|
-
clearTimeout(timer);
|
|
690
|
-
});
|
|
691
|
-
this.dbg({
|
|
692
|
-
type: "heartbeat",
|
|
693
|
-
action: "ping_emit",
|
|
694
|
-
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() }
|
|
695
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
|
+
}
|
|
696
814
|
};
|
|
697
815
|
this.hbTimer = setInterval(tick, this.hb.intervalMs);
|
|
698
816
|
tick();
|
|
699
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
|
+
*/
|
|
700
824
|
stopHeartbeat() {
|
|
701
825
|
if (this.hbTimer) {
|
|
702
826
|
clearInterval(this.hbTimer);
|
|
@@ -738,8 +862,24 @@ var SocketClient = class {
|
|
|
738
862
|
if (next === 1) toJoin.push(r);
|
|
739
863
|
}
|
|
740
864
|
if (toJoin.length > 0 && this.socket) {
|
|
741
|
-
this.socket
|
|
742
|
-
this.
|
|
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
|
+
}
|
|
743
883
|
}
|
|
744
884
|
}
|
|
745
885
|
leaveRooms(rooms) {
|
|
@@ -753,8 +893,24 @@ var SocketClient = class {
|
|
|
753
893
|
else this.roomCounts.set(r, next);
|
|
754
894
|
}
|
|
755
895
|
if (toLeave.length > 0 && this.socket) {
|
|
756
|
-
this.socket
|
|
757
|
-
this.
|
|
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
|
+
}
|
|
758
914
|
}
|
|
759
915
|
}
|
|
760
916
|
on(event, handler) {
|
|
@@ -846,7 +1002,7 @@ var SocketClient = class {
|
|
|
846
1002
|
socket.off("reconnect", this.onReconnect);
|
|
847
1003
|
socket.off("disconnect", this.onDisconnect);
|
|
848
1004
|
socket.off("connect_error", this.onConnectError);
|
|
849
|
-
socket.off(
|
|
1005
|
+
socket.off("sys:pong", this.onPong);
|
|
850
1006
|
for (const [event, set] of this.handlerMap.entries()) {
|
|
851
1007
|
for (const entry of set) {
|
|
852
1008
|
socket.off(String(event), entry.wrapped);
|
|
@@ -857,8 +1013,23 @@ var SocketClient = class {
|
|
|
857
1013
|
this.handlerMap.clear();
|
|
858
1014
|
const toLeave = Array.from(this.roomCounts.entries()).filter(([, count]) => count > 0).map(([room]) => room);
|
|
859
1015
|
if (toLeave.length > 0 && socket) {
|
|
860
|
-
|
|
861
|
-
|
|
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
|
+
}
|
|
862
1033
|
}
|
|
863
1034
|
this.roomCounts.clear();
|
|
864
1035
|
}
|