react-realtime-hooks 1.1.0 → 1.2.0
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 +71 -4
- package/dist/index.cjs +172 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.js +172 -52
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -29,6 +29,34 @@ type UsePageVisibilityHook = (options?: UsePageVisibilityOptions) => UsePageVisi
|
|
|
29
29
|
|
|
30
30
|
declare const usePageVisibility: UsePageVisibilityHook;
|
|
31
31
|
|
|
32
|
+
type ConnectionGateReason = "ready" | "manual" | "offline" | "hidden";
|
|
33
|
+
interface UseConnectionGateOptions {
|
|
34
|
+
enabled?: boolean;
|
|
35
|
+
requireOnline?: boolean;
|
|
36
|
+
requireVisible?: boolean;
|
|
37
|
+
hiddenGraceMs?: number;
|
|
38
|
+
initialOnline?: boolean;
|
|
39
|
+
initialVisible?: boolean;
|
|
40
|
+
trackTransitions?: boolean;
|
|
41
|
+
}
|
|
42
|
+
interface UseConnectionGateResult {
|
|
43
|
+
connect: boolean;
|
|
44
|
+
isBlocked: boolean;
|
|
45
|
+
isWaitingForVisibleGrace: boolean;
|
|
46
|
+
reason: ConnectionGateReason;
|
|
47
|
+
isOnline: boolean;
|
|
48
|
+
isOnlineSupported: boolean;
|
|
49
|
+
isVisible: boolean;
|
|
50
|
+
isVisibilitySupported: boolean;
|
|
51
|
+
visibilityState: DocumentVisibilityState | "visible";
|
|
52
|
+
lastChangedAt: number | null;
|
|
53
|
+
becameReadyAt: number | null;
|
|
54
|
+
becameBlockedAt: number | null;
|
|
55
|
+
}
|
|
56
|
+
type UseConnectionGateHook = (options?: UseConnectionGateOptions) => UseConnectionGateResult;
|
|
57
|
+
|
|
58
|
+
declare const useConnectionGate: UseConnectionGateHook;
|
|
59
|
+
|
|
32
60
|
type ReconnectStatus = "idle" | "scheduled" | "running" | "stopped";
|
|
33
61
|
type ReconnectTrigger = "mount" | "manual" | "close" | "error" | "heartbeat-timeout" | "offline" | "online" | "visibility";
|
|
34
62
|
interface ReconnectAttempt {
|
|
@@ -235,4 +263,4 @@ type UseEventSourceHook = <TMessage = unknown>(options: UseEventSourceOptions<TM
|
|
|
235
263
|
|
|
236
264
|
declare const useEventSource: UseEventSourceHook;
|
|
237
265
|
|
|
238
|
-
export { type ConnectionStateSnapshot, type HeartbeatAckMatcher, type HeartbeatBeatFn, type MessageParser, type MessageSerializer, type Milliseconds, type RealtimeConnectionStatus, type RealtimeTransport, type ReconnectAttempt, type ReconnectDelayContext, type ReconnectDelayStrategy, type ReconnectStatus, type ReconnectTrigger, type UrlProvider, type UseEventSourceHook, type UseEventSourceOptions, type UseEventSourceResult, type UseHeartbeatHook, type UseHeartbeatOptions, type UseHeartbeatResult, type UseOnlineStatusHook, type UseOnlineStatusOptions, type UseOnlineStatusResult, type UsePageVisibilityHook, type UsePageVisibilityOptions, type UsePageVisibilityResult, type UseReconnectHook, type UseReconnectOptions, type UseReconnectResult, type UseWebSocketHeartbeatOptions, type UseWebSocketHook, type UseWebSocketOptions, type UseWebSocketResult, type WebSocketHeartbeatAction, useEventSource, useHeartbeat, useOnlineStatus, usePageVisibility, useReconnect, useWebSocket };
|
|
266
|
+
export { type ConnectionGateReason, type ConnectionStateSnapshot, type HeartbeatAckMatcher, type HeartbeatBeatFn, type MessageParser, type MessageSerializer, type Milliseconds, type RealtimeConnectionStatus, type RealtimeTransport, type ReconnectAttempt, type ReconnectDelayContext, type ReconnectDelayStrategy, type ReconnectStatus, type ReconnectTrigger, type UrlProvider, type UseConnectionGateHook, type UseConnectionGateOptions, type UseConnectionGateResult, type UseEventSourceHook, type UseEventSourceOptions, type UseEventSourceResult, type UseHeartbeatHook, type UseHeartbeatOptions, type UseHeartbeatResult, type UseOnlineStatusHook, type UseOnlineStatusOptions, type UseOnlineStatusResult, type UsePageVisibilityHook, type UsePageVisibilityOptions, type UsePageVisibilityResult, type UseReconnectHook, type UseReconnectOptions, type UseReconnectResult, type UseWebSocketHeartbeatOptions, type UseWebSocketHook, type UseWebSocketOptions, type UseWebSocketResult, type WebSocketHeartbeatAction, useConnectionGate, useEventSource, useHeartbeat, useOnlineStatus, usePageVisibility, useReconnect, useWebSocket };
|
package/dist/index.js
CHANGED
|
@@ -136,6 +136,177 @@ var usePageVisibility = (options = {}) => {
|
|
|
136
136
|
};
|
|
137
137
|
};
|
|
138
138
|
|
|
139
|
+
// src/core/timers.ts
|
|
140
|
+
var sanitizeTimerDelay = (delayMs) => {
|
|
141
|
+
if (!Number.isFinite(delayMs)) {
|
|
142
|
+
return 0;
|
|
143
|
+
}
|
|
144
|
+
return Math.max(0, Math.round(delayMs));
|
|
145
|
+
};
|
|
146
|
+
var createManagedTimeout = () => {
|
|
147
|
+
let timeoutId = null;
|
|
148
|
+
return {
|
|
149
|
+
cancel() {
|
|
150
|
+
if (timeoutId !== null) {
|
|
151
|
+
clearTimeout(timeoutId);
|
|
152
|
+
timeoutId = null;
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
isActive() {
|
|
156
|
+
return timeoutId !== null;
|
|
157
|
+
},
|
|
158
|
+
schedule(callback, delayMs) {
|
|
159
|
+
if (timeoutId !== null) {
|
|
160
|
+
clearTimeout(timeoutId);
|
|
161
|
+
}
|
|
162
|
+
timeoutId = setTimeout(() => {
|
|
163
|
+
timeoutId = null;
|
|
164
|
+
callback();
|
|
165
|
+
}, sanitizeTimerDelay(delayMs));
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
var createManagedInterval = () => {
|
|
170
|
+
let intervalId = null;
|
|
171
|
+
return {
|
|
172
|
+
cancel() {
|
|
173
|
+
if (intervalId !== null) {
|
|
174
|
+
clearInterval(intervalId);
|
|
175
|
+
intervalId = null;
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
isActive() {
|
|
179
|
+
return intervalId !== null;
|
|
180
|
+
},
|
|
181
|
+
start(callback, intervalMs) {
|
|
182
|
+
if (intervalId !== null) {
|
|
183
|
+
clearInterval(intervalId);
|
|
184
|
+
}
|
|
185
|
+
intervalId = setInterval(callback, sanitizeTimerDelay(intervalMs));
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// src/hooks/useConnectionGate.ts
|
|
191
|
+
var createEmptyTransitionState3 = () => ({
|
|
192
|
+
becameBlockedAt: null,
|
|
193
|
+
becameReadyAt: null,
|
|
194
|
+
lastChangedAt: null
|
|
195
|
+
});
|
|
196
|
+
var normalizeHiddenGraceMs = (value) => {
|
|
197
|
+
if (value === void 0 || !Number.isFinite(value)) {
|
|
198
|
+
return 0;
|
|
199
|
+
}
|
|
200
|
+
return Math.max(0, value);
|
|
201
|
+
};
|
|
202
|
+
var useConnectionGate = (options = {}) => {
|
|
203
|
+
const enabled = options.enabled ?? true;
|
|
204
|
+
const requireOnline = options.requireOnline ?? true;
|
|
205
|
+
const requireVisible = options.requireVisible ?? false;
|
|
206
|
+
const hiddenGraceMs = normalizeHiddenGraceMs(options.hiddenGraceMs);
|
|
207
|
+
const trackTransitions = options.trackTransitions ?? true;
|
|
208
|
+
const onlineStatus = useOnlineStatus({
|
|
209
|
+
...options.initialOnline === void 0 ? {} : { initialOnline: options.initialOnline },
|
|
210
|
+
trackTransitions: false
|
|
211
|
+
});
|
|
212
|
+
const pageVisibility = usePageVisibility({
|
|
213
|
+
...options.initialVisible === void 0 ? {} : { initialVisible: options.initialVisible },
|
|
214
|
+
trackTransitions: false
|
|
215
|
+
});
|
|
216
|
+
const hiddenGraceTimeoutRef = useRef(createManagedTimeout());
|
|
217
|
+
const hiddenSinceRef = useRef(null);
|
|
218
|
+
const previousStateRef = useRef(null);
|
|
219
|
+
const [hasExceededHiddenGrace, setHasExceededHiddenGrace] = useState(false);
|
|
220
|
+
const [isWaitingForVisibleGrace, setIsWaitingForVisibleGrace] = useState(false);
|
|
221
|
+
const [transitions, setTransitions] = useState(createEmptyTransitionState3);
|
|
222
|
+
useEffect(() => () => {
|
|
223
|
+
hiddenGraceTimeoutRef.current.cancel();
|
|
224
|
+
}, []);
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
hiddenGraceTimeoutRef.current.cancel();
|
|
227
|
+
if (!requireVisible || pageVisibility.isVisible) {
|
|
228
|
+
hiddenSinceRef.current = null;
|
|
229
|
+
setHasExceededHiddenGrace(false);
|
|
230
|
+
setIsWaitingForVisibleGrace(false);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const hiddenSince = hiddenSinceRef.current ?? Date.now();
|
|
234
|
+
hiddenSinceRef.current = hiddenSince;
|
|
235
|
+
if (hiddenGraceMs <= 0) {
|
|
236
|
+
setHasExceededHiddenGrace(true);
|
|
237
|
+
setIsWaitingForVisibleGrace(false);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const elapsedMs = Date.now() - hiddenSince;
|
|
241
|
+
if (elapsedMs >= hiddenGraceMs) {
|
|
242
|
+
setHasExceededHiddenGrace(true);
|
|
243
|
+
setIsWaitingForVisibleGrace(false);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
setHasExceededHiddenGrace(false);
|
|
247
|
+
setIsWaitingForVisibleGrace(true);
|
|
248
|
+
hiddenGraceTimeoutRef.current.schedule(() => {
|
|
249
|
+
setHasExceededHiddenGrace(true);
|
|
250
|
+
setIsWaitingForVisibleGrace(false);
|
|
251
|
+
}, hiddenGraceMs - elapsedMs);
|
|
252
|
+
}, [hiddenGraceMs, pageVisibility.isVisible, requireVisible]);
|
|
253
|
+
let reason = "ready";
|
|
254
|
+
if (!enabled) {
|
|
255
|
+
reason = "manual";
|
|
256
|
+
} else if (requireOnline && !onlineStatus.isOnline) {
|
|
257
|
+
reason = "offline";
|
|
258
|
+
} else if (requireVisible && !pageVisibility.isVisible && hasExceededHiddenGrace) {
|
|
259
|
+
reason = "hidden";
|
|
260
|
+
}
|
|
261
|
+
const connect = reason === "ready";
|
|
262
|
+
const isBlocked = !connect;
|
|
263
|
+
useEffect(() => {
|
|
264
|
+
if (!trackTransitions) {
|
|
265
|
+
previousStateRef.current = {
|
|
266
|
+
connect,
|
|
267
|
+
reason
|
|
268
|
+
};
|
|
269
|
+
setTransitions(createEmptyTransitionState3);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const previousState = previousStateRef.current;
|
|
273
|
+
if (previousState === null) {
|
|
274
|
+
previousStateRef.current = {
|
|
275
|
+
connect,
|
|
276
|
+
reason
|
|
277
|
+
};
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (previousState.connect === connect && previousState.reason === reason) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const changedAt = Date.now();
|
|
284
|
+
previousStateRef.current = {
|
|
285
|
+
connect,
|
|
286
|
+
reason
|
|
287
|
+
};
|
|
288
|
+
setTransitions((current) => ({
|
|
289
|
+
becameBlockedAt: connect ? current.becameBlockedAt : changedAt,
|
|
290
|
+
becameReadyAt: connect ? changedAt : current.becameReadyAt,
|
|
291
|
+
lastChangedAt: changedAt
|
|
292
|
+
}));
|
|
293
|
+
}, [connect, reason, trackTransitions]);
|
|
294
|
+
return {
|
|
295
|
+
becameBlockedAt: transitions.becameBlockedAt,
|
|
296
|
+
becameReadyAt: transitions.becameReadyAt,
|
|
297
|
+
connect,
|
|
298
|
+
isBlocked,
|
|
299
|
+
isOnline: onlineStatus.isOnline,
|
|
300
|
+
isOnlineSupported: onlineStatus.isSupported,
|
|
301
|
+
isVisibilitySupported: pageVisibility.isSupported,
|
|
302
|
+
isVisible: pageVisibility.isVisible,
|
|
303
|
+
isWaitingForVisibleGrace,
|
|
304
|
+
lastChangedAt: transitions.lastChangedAt,
|
|
305
|
+
reason,
|
|
306
|
+
visibilityState: pageVisibility.visibilityState
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
|
|
139
310
|
// src/core/reconnect.ts
|
|
140
311
|
var DEFAULT_RECONNECT_OPTIONS = {
|
|
141
312
|
backoffFactor: 2,
|
|
@@ -272,57 +443,6 @@ var createReconnectAttempt = (attempt, trigger, options, lastDelayMs, config = {
|
|
|
272
443
|
};
|
|
273
444
|
};
|
|
274
445
|
|
|
275
|
-
// src/core/timers.ts
|
|
276
|
-
var sanitizeTimerDelay = (delayMs) => {
|
|
277
|
-
if (!Number.isFinite(delayMs)) {
|
|
278
|
-
return 0;
|
|
279
|
-
}
|
|
280
|
-
return Math.max(0, Math.round(delayMs));
|
|
281
|
-
};
|
|
282
|
-
var createManagedTimeout = () => {
|
|
283
|
-
let timeoutId = null;
|
|
284
|
-
return {
|
|
285
|
-
cancel() {
|
|
286
|
-
if (timeoutId !== null) {
|
|
287
|
-
clearTimeout(timeoutId);
|
|
288
|
-
timeoutId = null;
|
|
289
|
-
}
|
|
290
|
-
},
|
|
291
|
-
isActive() {
|
|
292
|
-
return timeoutId !== null;
|
|
293
|
-
},
|
|
294
|
-
schedule(callback, delayMs) {
|
|
295
|
-
if (timeoutId !== null) {
|
|
296
|
-
clearTimeout(timeoutId);
|
|
297
|
-
}
|
|
298
|
-
timeoutId = setTimeout(() => {
|
|
299
|
-
timeoutId = null;
|
|
300
|
-
callback();
|
|
301
|
-
}, sanitizeTimerDelay(delayMs));
|
|
302
|
-
}
|
|
303
|
-
};
|
|
304
|
-
};
|
|
305
|
-
var createManagedInterval = () => {
|
|
306
|
-
let intervalId = null;
|
|
307
|
-
return {
|
|
308
|
-
cancel() {
|
|
309
|
-
if (intervalId !== null) {
|
|
310
|
-
clearInterval(intervalId);
|
|
311
|
-
intervalId = null;
|
|
312
|
-
}
|
|
313
|
-
},
|
|
314
|
-
isActive() {
|
|
315
|
-
return intervalId !== null;
|
|
316
|
-
},
|
|
317
|
-
start(callback, intervalMs) {
|
|
318
|
-
if (intervalId !== null) {
|
|
319
|
-
clearInterval(intervalId);
|
|
320
|
-
}
|
|
321
|
-
intervalId = setInterval(callback, sanitizeTimerDelay(intervalMs));
|
|
322
|
-
}
|
|
323
|
-
};
|
|
324
|
-
};
|
|
325
|
-
|
|
326
446
|
// src/hooks/useReconnect.ts
|
|
327
447
|
var createInitialState = (enabled) => ({
|
|
328
448
|
attempt: 0,
|
|
@@ -1458,6 +1578,6 @@ var useEventSource = (options) => {
|
|
|
1458
1578
|
};
|
|
1459
1579
|
};
|
|
1460
1580
|
|
|
1461
|
-
export { useEventSource, useHeartbeat, useOnlineStatus, usePageVisibility, useReconnect, useWebSocket };
|
|
1581
|
+
export { useConnectionGate, useEventSource, useHeartbeat, useOnlineStatus, usePageVisibility, useReconnect, useWebSocket };
|
|
1462
1582
|
//# sourceMappingURL=index.js.map
|
|
1463
1583
|
//# sourceMappingURL=index.js.map
|