react-realtime-hooks 1.4.0 → 1.4.1

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 CHANGED
@@ -805,9 +805,95 @@ var resolveUrlProvider = (url) => {
805
805
  const resolved = typeof url === "function" ? url() : url;
806
806
  return normalizeResolvedUrl(resolved ?? null);
807
807
  };
808
+ var createInitialState3 = () => ({
809
+ manualClose: false,
810
+ manualOpen: false,
811
+ pendingCloseAction: null,
812
+ reconnectSuppressed: false,
813
+ skipNextCloseReconnect: false,
814
+ terminalError: null
815
+ });
816
+ var useWebSocketController = () => {
817
+ const ref = react.useRef(null);
818
+ if (ref.current === null) {
819
+ ref.current = createInitialState3();
820
+ }
821
+ const state = ref.current;
822
+ const controllerRef = react.useRef(null);
823
+ if (controllerRef.current === null) {
824
+ controllerRef.current = {
825
+ clearReconnectSuppression: () => {
826
+ state.reconnectSuppressed = false;
827
+ },
828
+ consumePendingCloseAction: () => {
829
+ const action = state.pendingCloseAction;
830
+ state.pendingCloseAction = null;
831
+ return action;
832
+ },
833
+ consumeSkipNextCloseReconnect: () => {
834
+ const skip = state.skipNextCloseReconnect;
835
+ state.skipNextCloseReconnect = false;
836
+ return skip;
837
+ },
838
+ hasManualCloseRequested: () => state.manualClose,
839
+ hasManualOpenRequested: () => state.manualOpen,
840
+ isReconnectSuppressed: () => state.reconnectSuppressed,
841
+ noteEffectInitiatedClose: () => {
842
+ state.reconnectSuppressed = true;
843
+ },
844
+ noteHeartbeatActiveSocketClose: (input) => {
845
+ state.manualOpen = false;
846
+ state.terminalError = input.reconnectTrigger === null ? input.error : null;
847
+ state.pendingCloseAction = input;
848
+ state.skipNextCloseReconnect = true;
849
+ state.reconnectSuppressed = true;
850
+ },
851
+ noteHeartbeatNoActiveSocket: ({ shouldReconnect, error }) => {
852
+ state.manualOpen = false;
853
+ state.terminalError = shouldReconnect ? null : error;
854
+ },
855
+ noteParseError: (error) => {
856
+ state.terminalError = error;
857
+ state.manualOpen = false;
858
+ state.skipNextCloseReconnect = true;
859
+ state.reconnectSuppressed = true;
860
+ },
861
+ noteSocketOpened: () => {
862
+ state.manualClose = false;
863
+ state.manualOpen = false;
864
+ state.reconnectSuppressed = false;
865
+ state.terminalError = null;
866
+ },
867
+ noteUserCloseRequested: () => {
868
+ state.manualClose = true;
869
+ state.manualOpen = false;
870
+ state.reconnectSuppressed = true;
871
+ state.terminalError = null;
872
+ },
873
+ noteUserOpenRequested: () => {
874
+ state.manualClose = false;
875
+ state.manualOpen = true;
876
+ state.reconnectSuppressed = false;
877
+ state.terminalError = null;
878
+ },
879
+ noteUserReconnectClosed: () => {
880
+ state.reconnectSuppressed = false;
881
+ },
882
+ noteUserReconnectRequested: () => {
883
+ state.manualClose = false;
884
+ state.manualOpen = true;
885
+ state.skipNextCloseReconnect = true;
886
+ state.reconnectSuppressed = true;
887
+ state.terminalError = null;
888
+ },
889
+ peekTerminalError: () => state.terminalError
890
+ };
891
+ }
892
+ return controllerRef.current;
893
+ };
808
894
 
809
895
  // src/hooks/useWebSocket.ts
810
- var createInitialState3 = (status = "idle") => ({
896
+ var createInitialState4 = (status = "idle") => ({
811
897
  bufferedAmount: 0,
812
898
  lastChangedAt: null,
813
899
  lastCloseEvent: null,
@@ -850,15 +936,10 @@ var useWebSocket = (options) => {
850
936
  const activeSocketEpochRef = react.useRef(null);
851
937
  const closingSocketEpochRef = react.useRef(null);
852
938
  const nextSocketEpochRef = react.useRef(0);
853
- const manualCloseRef = react.useRef(false);
854
- const manualOpenRef = react.useRef(false);
855
- const skipCloseReconnectRef = react.useRef(false);
856
- const suppressReconnectRef = react.useRef(false);
857
- const pendingCloseActionRef = react.useRef(null);
858
- const terminalErrorRef = react.useRef(null);
939
+ const controller = useWebSocketController();
859
940
  const [openNonce, setOpenNonce] = react.useState(0);
860
941
  const [state, setState] = react.useState(
861
- () => createInitialState3(connect ? "connecting" : "idle")
942
+ () => createInitialState4(connect ? "connecting" : "idle")
862
943
  );
863
944
  const stateRef = react.useRef(state);
864
945
  stateRef.current = state;
@@ -978,10 +1059,9 @@ var useWebSocket = (options) => {
978
1059
  return;
979
1060
  }
980
1061
  const shouldReconnect = action === "reconnect" && reconnectEnabled && (options.shouldReconnect?.(error) ?? true);
981
- manualOpenRef.current = false;
982
- terminalErrorRef.current = shouldReconnect ? null : error;
983
1062
  const socket2 = socketRef.current;
984
1063
  if (socket2 === null || !isSocketActive(socket2)) {
1064
+ controller.noteHeartbeatNoActiveSocket({ error, shouldReconnect });
985
1065
  commitState((current) => ({
986
1066
  ...current,
987
1067
  lastChangedAt: Date.now(),
@@ -993,12 +1073,10 @@ var useWebSocket = (options) => {
993
1073
  }
994
1074
  return;
995
1075
  }
996
- pendingCloseActionRef.current = {
1076
+ controller.noteHeartbeatActiveSocketClose({
997
1077
  error,
998
1078
  reconnectTrigger: shouldReconnect ? reconnectTrigger : null
999
- };
1000
- skipCloseReconnectRef.current = true;
1001
- suppressReconnectRef.current = true;
1079
+ });
1002
1080
  closeSocket({ trackClose: true });
1003
1081
  }
1004
1082
  );
@@ -1013,10 +1091,7 @@ var useWebSocket = (options) => {
1013
1091
  }));
1014
1092
  });
1015
1093
  const handleOpen = useStableCallback((event, socket2) => {
1016
- manualCloseRef.current = false;
1017
- manualOpenRef.current = false;
1018
- suppressReconnectRef.current = false;
1019
- terminalErrorRef.current = null;
1094
+ controller.noteSocketOpened();
1020
1095
  reconnect.markConnected();
1021
1096
  heartbeat.start();
1022
1097
  commitState((current) => ({
@@ -1043,10 +1118,7 @@ var useWebSocket = (options) => {
1043
1118
  cause: error,
1044
1119
  kind: "parse-error"
1045
1120
  });
1046
- terminalErrorRef.current = parseError;
1047
- manualOpenRef.current = false;
1048
- skipCloseReconnectRef.current = true;
1049
- suppressReconnectRef.current = true;
1121
+ controller.noteParseError(parseError);
1050
1122
  reconnect.cancel();
1051
1123
  heartbeat.stop();
1052
1124
  options.onError?.(parseError);
@@ -1090,13 +1162,11 @@ var useWebSocket = (options) => {
1090
1162
  }
1091
1163
  heartbeat.stop();
1092
1164
  updateBufferedAmount();
1093
- const pendingCloseAction = pendingCloseActionRef.current;
1094
- pendingCloseActionRef.current = null;
1095
- const terminalError = terminalErrorRef.current;
1096
- const skipCloseReconnect = skipCloseReconnectRef.current;
1097
- skipCloseReconnectRef.current = false;
1165
+ const pendingCloseAction = controller.consumePendingCloseAction();
1166
+ const terminalError = controller.peekTerminalError();
1167
+ const skipCloseReconnect = controller.consumeSkipNextCloseReconnect();
1098
1168
  if (pendingCloseAction !== null) {
1099
- suppressReconnectRef.current = false;
1169
+ controller.clearReconnectSuppression();
1100
1170
  commitState((current) => ({
1101
1171
  ...current,
1102
1172
  lastChangedAt: Date.now(),
@@ -1111,7 +1181,7 @@ var useWebSocket = (options) => {
1111
1181
  return;
1112
1182
  }
1113
1183
  if (terminalError !== null) {
1114
- suppressReconnectRef.current = false;
1184
+ controller.clearReconnectSuppression();
1115
1185
  commitState((current) => ({
1116
1186
  ...current,
1117
1187
  lastChangedAt: Date.now(),
@@ -1122,7 +1192,7 @@ var useWebSocket = (options) => {
1122
1192
  options.onClose?.(event);
1123
1193
  return;
1124
1194
  }
1125
- const shouldReconnect = !suppressReconnectRef.current && !skipCloseReconnect && reconnectEnabled && (options.shouldReconnect?.(event) ?? true);
1195
+ const shouldReconnect = !controller.isReconnectSuppressed() && !skipCloseReconnect && reconnectEnabled && (options.shouldReconnect?.(event) ?? true);
1126
1196
  commitState((current) => ({
1127
1197
  ...current,
1128
1198
  lastChangedAt: Date.now(),
@@ -1133,33 +1203,23 @@ var useWebSocket = (options) => {
1133
1203
  if (shouldReconnect) {
1134
1204
  reconnect.schedule("close");
1135
1205
  } else {
1136
- suppressReconnectRef.current = false;
1206
+ controller.clearReconnectSuppression();
1137
1207
  }
1138
1208
  });
1139
1209
  const open = useStableCallback(() => {
1140
- manualCloseRef.current = false;
1141
- manualOpenRef.current = true;
1142
- suppressReconnectRef.current = false;
1143
- terminalErrorRef.current = null;
1210
+ controller.noteUserOpenRequested();
1144
1211
  reconnect.cancel();
1145
1212
  setOpenNonce((current) => current + 1);
1146
1213
  });
1147
1214
  const reconnectNow = useStableCallback(() => {
1148
- manualCloseRef.current = false;
1149
- manualOpenRef.current = true;
1150
- skipCloseReconnectRef.current = true;
1151
- suppressReconnectRef.current = true;
1152
- terminalErrorRef.current = null;
1215
+ controller.noteUserReconnectRequested();
1153
1216
  heartbeat.stop();
1154
1217
  closeSocket();
1155
- suppressReconnectRef.current = false;
1218
+ controller.noteUserReconnectClosed();
1156
1219
  reconnect.schedule("manual");
1157
1220
  });
1158
1221
  const close = useStableCallback((code, reason) => {
1159
- manualCloseRef.current = true;
1160
- manualOpenRef.current = false;
1161
- suppressReconnectRef.current = true;
1162
- terminalErrorRef.current = null;
1222
+ controller.noteUserCloseRequested();
1163
1223
  reconnect.cancel();
1164
1224
  heartbeat.stop();
1165
1225
  commitState((current) => ({
@@ -1201,22 +1261,22 @@ var useWebSocket = (options) => {
1201
1261
  }));
1202
1262
  return;
1203
1263
  }
1204
- const shouldConnect = terminalErrorRef.current === null && (connect && !manualCloseRef.current || manualOpenRef.current || reconnect.status === "running");
1264
+ const shouldConnect = controller.peekTerminalError() === null && (connect && !controller.hasManualCloseRequested() || controller.hasManualOpenRequested() || reconnect.status === "running");
1205
1265
  const nextSocketKey = `${resolvedUrl}::${protocolsDependency}::${options.binaryType ?? "blob"}`;
1206
1266
  if (!shouldConnect) {
1207
1267
  if (socketRef.current !== null) {
1208
- suppressReconnectRef.current = true;
1268
+ controller.noteEffectInitiatedClose();
1209
1269
  closeSocket();
1210
1270
  }
1211
1271
  socketKeyRef.current = null;
1212
1272
  commitState((current) => ({
1213
1273
  ...current,
1214
- status: terminalErrorRef.current !== null ? "error" : manualCloseRef.current ? "closed" : "idle"
1274
+ status: controller.peekTerminalError() !== null ? "error" : controller.hasManualCloseRequested() ? "closed" : "idle"
1215
1275
  }));
1216
1276
  return;
1217
1277
  }
1218
1278
  if (socketRef.current !== null && socketKeyRef.current !== nextSocketKey) {
1219
- suppressReconnectRef.current = true;
1279
+ controller.noteEffectInitiatedClose();
1220
1280
  closeSocket();
1221
1281
  }
1222
1282
  if (socketRef.current !== null) {
@@ -1283,6 +1343,7 @@ var useWebSocket = (options) => {
1283
1343
  closeSocket,
1284
1344
  commitState,
1285
1345
  connect,
1346
+ controller,
1286
1347
  handleClose,
1287
1348
  handleError,
1288
1349
  handleMessage,
@@ -1297,11 +1358,10 @@ var useWebSocket = (options) => {
1297
1358
  supported
1298
1359
  ]);
1299
1360
  react.useEffect(() => () => {
1300
- suppressReconnectRef.current = true;
1361
+ controller.noteEffectInitiatedClose();
1301
1362
  socketKeyRef.current = null;
1302
1363
  activeSocketEpochRef.current = null;
1303
1364
  closingSocketEpochRef.current = null;
1304
- terminalErrorRef.current = null;
1305
1365
  const socket2 = socketRef.current;
1306
1366
  socketRef.current = null;
1307
1367
  if (socket2 === null) {
@@ -1310,7 +1370,7 @@ var useWebSocket = (options) => {
1310
1370
  if (isSocketActive(socket2)) {
1311
1371
  socket2.close();
1312
1372
  }
1313
- }, []);
1373
+ }, [controller]);
1314
1374
  const stopHeartbeat = heartbeat.stop;
1315
1375
  react.useEffect(() => {
1316
1376
  if (state.status !== "open") {
@@ -1415,7 +1475,7 @@ var useWebSocket = (options) => {
1415
1475
  socket
1416
1476
  };
1417
1477
  };
1418
- var createInitialState4 = (status = "idle") => ({
1478
+ var createInitialState5 = (status = "idle") => ({
1419
1479
  lastChangedAt: null,
1420
1480
  lastError: null,
1421
1481
  lastEventName: null,
@@ -1452,7 +1512,7 @@ var useEventSource = (options) => {
1452
1512
  const terminalErrorRef = react.useRef(null);
1453
1513
  const [openNonce, setOpenNonce] = react.useState(0);
1454
1514
  const [state, setState] = react.useState(
1455
- () => createInitialState4(connect ? "connecting" : "idle")
1515
+ () => createInitialState5(connect ? "connecting" : "idle")
1456
1516
  );
1457
1517
  const stateRef = react.useRef(state);
1458
1518
  stateRef.current = state;