react-realtime-hooks 1.0.2 → 1.0.4
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/README.md +12 -6
- package/dist/index.cjs +117 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +117 -10
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -402,6 +402,7 @@ var useHeartbeat = (options) => {
|
|
|
402
402
|
const startOnMount = options.startOnMount ?? true;
|
|
403
403
|
const intervalRef = useRef(createManagedInterval());
|
|
404
404
|
const timeoutRef = useRef(createManagedTimeout());
|
|
405
|
+
const generationRef = useRef(0);
|
|
405
406
|
const [state, setState] = useState(
|
|
406
407
|
() => createInitialState2(enabled && startOnMount)
|
|
407
408
|
);
|
|
@@ -428,8 +429,7 @@ var useHeartbeat = (options) => {
|
|
|
428
429
|
handleTimeout();
|
|
429
430
|
}, options.timeoutMs);
|
|
430
431
|
});
|
|
431
|
-
const
|
|
432
|
-
const performedAt = Date.now();
|
|
432
|
+
const handleBeatSuccess = useEffectEvent((performedAt) => {
|
|
433
433
|
commitState((current) => ({
|
|
434
434
|
...current,
|
|
435
435
|
hasTimedOut: false,
|
|
@@ -437,7 +437,35 @@ var useHeartbeat = (options) => {
|
|
|
437
437
|
}));
|
|
438
438
|
scheduleTimeout();
|
|
439
439
|
options.onBeat?.();
|
|
440
|
-
|
|
440
|
+
});
|
|
441
|
+
const handleBeatError = useEffectEvent((error) => {
|
|
442
|
+
timeoutRef.current.cancel();
|
|
443
|
+
options.onError?.(error);
|
|
444
|
+
});
|
|
445
|
+
const runBeat = useEffectEvent(() => {
|
|
446
|
+
const generation = generationRef.current;
|
|
447
|
+
const completeBeat = (result) => {
|
|
448
|
+
if (generation !== generationRef.current || result === false) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
handleBeatSuccess(Date.now());
|
|
452
|
+
};
|
|
453
|
+
const failBeat = (error) => {
|
|
454
|
+
if (generation !== generationRef.current) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
handleBeatError(error);
|
|
458
|
+
};
|
|
459
|
+
try {
|
|
460
|
+
const result = options.beat?.();
|
|
461
|
+
if (result !== null && typeof result === "object" && "then" in result) {
|
|
462
|
+
void Promise.resolve(result).then(completeBeat, failBeat);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
completeBeat(result);
|
|
466
|
+
} catch (error) {
|
|
467
|
+
failBeat(error);
|
|
468
|
+
}
|
|
441
469
|
});
|
|
442
470
|
const start = () => {
|
|
443
471
|
if (!enabled) {
|
|
@@ -455,13 +483,10 @@ var useHeartbeat = (options) => {
|
|
|
455
483
|
}));
|
|
456
484
|
};
|
|
457
485
|
const stop = () => {
|
|
486
|
+
generationRef.current += 1;
|
|
458
487
|
intervalRef.current.cancel();
|
|
459
488
|
timeoutRef.current.cancel();
|
|
460
|
-
commitState((
|
|
461
|
-
...current,
|
|
462
|
-
hasTimedOut: false,
|
|
463
|
-
isRunning: false
|
|
464
|
-
}));
|
|
489
|
+
commitState(createInitialState2(false));
|
|
465
490
|
};
|
|
466
491
|
const beat = () => {
|
|
467
492
|
if (!enabled) {
|
|
@@ -500,6 +525,7 @@ var useHeartbeat = (options) => {
|
|
|
500
525
|
return void 0;
|
|
501
526
|
}, [enabled, options.intervalMs, startOnMount]);
|
|
502
527
|
useEffect(() => () => {
|
|
528
|
+
generationRef.current += 1;
|
|
503
529
|
intervalRef.current.cancel();
|
|
504
530
|
timeoutRef.current.cancel();
|
|
505
531
|
}, []);
|
|
@@ -605,6 +631,7 @@ var toProtocolsDependency = (protocols) => {
|
|
|
605
631
|
return Array.isArray(protocols) ? protocols.join("|") : protocols;
|
|
606
632
|
};
|
|
607
633
|
var toHeartbeatConfig = (heartbeat) => heartbeat === void 0 || heartbeat === false ? null : heartbeat;
|
|
634
|
+
var isSocketActive = (socket) => socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING;
|
|
608
635
|
var useWebSocket = (options) => {
|
|
609
636
|
const connect = options.connect ?? true;
|
|
610
637
|
const supported = isWebSocketSupported();
|
|
@@ -616,6 +643,7 @@ var useWebSocket = (options) => {
|
|
|
616
643
|
const manualOpenRef = useRef(false);
|
|
617
644
|
const skipCloseReconnectRef = useRef(false);
|
|
618
645
|
const suppressReconnectRef = useRef(false);
|
|
646
|
+
const pendingCloseActionRef = useRef(null);
|
|
619
647
|
const terminalErrorRef = useRef(null);
|
|
620
648
|
const [openNonce, setOpenNonce] = useState(0);
|
|
621
649
|
const [state, setState] = useState(
|
|
@@ -634,6 +662,7 @@ var useWebSocket = (options) => {
|
|
|
634
662
|
const heartbeatConfig = toHeartbeatConfig(
|
|
635
663
|
options.heartbeat
|
|
636
664
|
);
|
|
665
|
+
const defaultHeartbeatAction = options.reconnect === false ? "close" : "reconnect";
|
|
637
666
|
const heartbeatHookOptions = heartbeatConfig === null ? {
|
|
638
667
|
enabled: false,
|
|
639
668
|
intervalMs: 1e3,
|
|
@@ -669,6 +698,27 @@ var useWebSocket = (options) => {
|
|
|
669
698
|
if (heartbeatConfig !== null && heartbeatConfig.onTimeout !== void 0) {
|
|
670
699
|
heartbeatHookOptions.onTimeout = heartbeatConfig.onTimeout;
|
|
671
700
|
}
|
|
701
|
+
if (heartbeatConfig !== null) {
|
|
702
|
+
const onTimeout = heartbeatHookOptions.onTimeout;
|
|
703
|
+
heartbeatHookOptions.onTimeout = () => {
|
|
704
|
+
applyHeartbeatAction(
|
|
705
|
+
heartbeatConfig.timeoutAction ?? defaultHeartbeatAction,
|
|
706
|
+
new Event("heartbeat-timeout"),
|
|
707
|
+
"heartbeat-timeout"
|
|
708
|
+
);
|
|
709
|
+
onTimeout?.();
|
|
710
|
+
};
|
|
711
|
+
const onError = heartbeatConfig.onError;
|
|
712
|
+
heartbeatHookOptions.onError = (error) => {
|
|
713
|
+
const event = error instanceof Event ? error : new Event("heartbeat-error");
|
|
714
|
+
applyHeartbeatAction(
|
|
715
|
+
heartbeatConfig.errorAction ?? heartbeatConfig.timeoutAction ?? defaultHeartbeatAction,
|
|
716
|
+
event,
|
|
717
|
+
"error"
|
|
718
|
+
);
|
|
719
|
+
onError?.(error);
|
|
720
|
+
};
|
|
721
|
+
}
|
|
672
722
|
const heartbeat = useHeartbeat(
|
|
673
723
|
heartbeatHookOptions
|
|
674
724
|
);
|
|
@@ -684,10 +734,47 @@ var useWebSocket = (options) => {
|
|
|
684
734
|
}
|
|
685
735
|
socketRef.current = null;
|
|
686
736
|
socketKeyRef.current = null;
|
|
687
|
-
if (socket2
|
|
737
|
+
if (isSocketActive(socket2)) {
|
|
688
738
|
socket2.close(code, reason);
|
|
689
739
|
}
|
|
690
740
|
});
|
|
741
|
+
const applyHeartbeatAction = useEffectEvent(
|
|
742
|
+
(action, error, reconnectTrigger) => {
|
|
743
|
+
heartbeat.stop();
|
|
744
|
+
options.onError?.(error);
|
|
745
|
+
if (action === "none") {
|
|
746
|
+
commitState((current) => ({
|
|
747
|
+
...current,
|
|
748
|
+
lastChangedAt: Date.now(),
|
|
749
|
+
lastError: error
|
|
750
|
+
}));
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
const shouldReconnect = action === "reconnect" && reconnectEnabled && (options.shouldReconnect?.(error) ?? true);
|
|
754
|
+
manualOpenRef.current = false;
|
|
755
|
+
terminalErrorRef.current = shouldReconnect ? null : error;
|
|
756
|
+
const socket2 = socketRef.current;
|
|
757
|
+
if (socket2 === null || !isSocketActive(socket2)) {
|
|
758
|
+
commitState((current) => ({
|
|
759
|
+
...current,
|
|
760
|
+
lastChangedAt: Date.now(),
|
|
761
|
+
lastError: error,
|
|
762
|
+
status: shouldReconnect ? "reconnecting" : "error"
|
|
763
|
+
}));
|
|
764
|
+
if (shouldReconnect) {
|
|
765
|
+
reconnect.schedule(reconnectTrigger);
|
|
766
|
+
}
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
pendingCloseActionRef.current = {
|
|
770
|
+
error,
|
|
771
|
+
reconnectTrigger: shouldReconnect ? reconnectTrigger : null
|
|
772
|
+
};
|
|
773
|
+
skipCloseReconnectRef.current = true;
|
|
774
|
+
suppressReconnectRef.current = true;
|
|
775
|
+
closeSocket();
|
|
776
|
+
}
|
|
777
|
+
);
|
|
691
778
|
const parseMessage = useEffectEvent((event) => {
|
|
692
779
|
const parser = options.parseMessage ?? defaultParseMessage;
|
|
693
780
|
return parser(event);
|
|
@@ -732,6 +819,7 @@ var useWebSocket = (options) => {
|
|
|
732
819
|
suppressReconnectRef.current = true;
|
|
733
820
|
reconnect.cancel();
|
|
734
821
|
heartbeat.stop();
|
|
822
|
+
options.onError?.(parseError);
|
|
735
823
|
commitState((current) => ({
|
|
736
824
|
...current,
|
|
737
825
|
lastChangedAt: Date.now(),
|
|
@@ -745,6 +833,7 @@ var useWebSocket = (options) => {
|
|
|
745
833
|
heartbeat.stop();
|
|
746
834
|
commitState((current) => ({
|
|
747
835
|
...current,
|
|
836
|
+
lastChangedAt: Date.now(),
|
|
748
837
|
lastError: event,
|
|
749
838
|
status: "error"
|
|
750
839
|
}));
|
|
@@ -755,9 +844,26 @@ var useWebSocket = (options) => {
|
|
|
755
844
|
socketKeyRef.current = null;
|
|
756
845
|
heartbeat.stop();
|
|
757
846
|
updateBufferedAmount();
|
|
847
|
+
const pendingCloseAction = pendingCloseActionRef.current;
|
|
848
|
+
pendingCloseActionRef.current = null;
|
|
758
849
|
const terminalError = terminalErrorRef.current;
|
|
759
850
|
const skipCloseReconnect = skipCloseReconnectRef.current;
|
|
760
851
|
skipCloseReconnectRef.current = false;
|
|
852
|
+
if (pendingCloseAction !== null) {
|
|
853
|
+
suppressReconnectRef.current = false;
|
|
854
|
+
commitState((current) => ({
|
|
855
|
+
...current,
|
|
856
|
+
lastChangedAt: Date.now(),
|
|
857
|
+
lastCloseEvent: event,
|
|
858
|
+
lastError: pendingCloseAction.error ?? current.lastError,
|
|
859
|
+
status: pendingCloseAction.reconnectTrigger === null ? "error" : "reconnecting"
|
|
860
|
+
}));
|
|
861
|
+
options.onClose?.(event);
|
|
862
|
+
if (pendingCloseAction.reconnectTrigger !== null) {
|
|
863
|
+
reconnect.schedule(pendingCloseAction.reconnectTrigger);
|
|
864
|
+
}
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
761
867
|
if (terminalError !== null) {
|
|
762
868
|
suppressReconnectRef.current = false;
|
|
763
869
|
commitState((current) => ({
|
|
@@ -916,7 +1022,7 @@ var useWebSocket = (options) => {
|
|
|
916
1022
|
if (socket2 === null) {
|
|
917
1023
|
return;
|
|
918
1024
|
}
|
|
919
|
-
if (socket2
|
|
1025
|
+
if (isSocketActive(socket2)) {
|
|
920
1026
|
socket2.close();
|
|
921
1027
|
}
|
|
922
1028
|
}, []);
|
|
@@ -1074,6 +1180,7 @@ var useEventSource = (options) => {
|
|
|
1074
1180
|
suppressReconnectRef.current = true;
|
|
1075
1181
|
reconnect.cancel();
|
|
1076
1182
|
closeEventSource();
|
|
1183
|
+
options.onError?.(parseError);
|
|
1077
1184
|
commitState((current) => ({
|
|
1078
1185
|
...current,
|
|
1079
1186
|
lastChangedAt: Date.now(),
|