react-realtime-hooks 1.3.4 → 1.4.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/dist/index.d.cts CHANGED
@@ -168,11 +168,39 @@ interface UseWebSocketHeartbeatOptions<TOutgoing = unknown, TIncoming = TOutgoin
168
168
  timeoutAction?: WebSocketHeartbeatAction;
169
169
  errorAction?: WebSocketHeartbeatAction;
170
170
  }
171
+ /**
172
+ * Controls how the hook surfaces the live `WebSocket.bufferedAmount`
173
+ * value through `state.bufferedAmount`.
174
+ *
175
+ * The native `WebSocket` does not emit any event when buffered bytes
176
+ * drain to the network, so by default `state.bufferedAmount` is only
177
+ * refreshed when the consumer calls `send(...)`, when a message
178
+ * arrives, or when the socket transitions to `open`. For UIs that need
179
+ * to display real-time backpressure (e.g. an outbound queue meter,
180
+ * a "flushing..." indicator, or a flow-control gauge), enable polling.
181
+ *
182
+ * - `false` (default): no polling. `state.bufferedAmount` updates only
183
+ * on the natural lifecycle events listed above.
184
+ * - `true`: poll once every 100ms while the socket is `open`.
185
+ * - `"raf"`: poll on every animation frame while the socket is `open`.
186
+ * Recommended for visual gauges that are tied to a render loop.
187
+ * - `{ intervalMs: N }`: poll every `N` milliseconds. `N` must be a
188
+ * positive integer; values <= 0 disable polling.
189
+ *
190
+ * Polling is automatically suspended when the socket is not in the
191
+ * `open` state and resumed when it returns to `open`. The hook diffs
192
+ * the polled value against the last committed one, so identical
193
+ * readings do not trigger React re-renders.
194
+ */
195
+ type BufferedAmountPolling = false | true | "raf" | {
196
+ intervalMs: number;
197
+ };
171
198
  interface UseWebSocketOptions<TIncoming = unknown, TOutgoing = TIncoming> {
172
199
  url: UrlProvider;
173
200
  protocols?: string | string[];
174
201
  connect?: boolean;
175
202
  binaryType?: BinaryType;
203
+ bufferedAmountPolling?: BufferedAmountPolling;
176
204
  parseMessage?: MessageParser<TIncoming>;
177
205
  serializeMessage?: MessageSerializer<TOutgoing>;
178
206
  reconnect?: false | UseReconnectOptions;
@@ -274,4 +302,4 @@ declare class RealtimeErrorEvent extends Event {
274
302
  constructor(type: string, init: RealtimeErrorEventInit);
275
303
  }
276
304
 
277
- export { type ConnectionGateReason, type ConnectionStateSnapshot, type HeartbeatAckMatcher, type HeartbeatBeatFn, type MessageParser, type MessageSerializer, type Milliseconds, type RealtimeConnectionStatus, RealtimeErrorEvent, type RealtimeErrorEventInit, type RealtimeErrorKind, 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 };
305
+ export { type BufferedAmountPolling, type ConnectionGateReason, type ConnectionStateSnapshot, type HeartbeatAckMatcher, type HeartbeatBeatFn, type MessageParser, type MessageSerializer, type Milliseconds, type RealtimeConnectionStatus, RealtimeErrorEvent, type RealtimeErrorEventInit, type RealtimeErrorKind, 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.d.ts CHANGED
@@ -168,11 +168,39 @@ interface UseWebSocketHeartbeatOptions<TOutgoing = unknown, TIncoming = TOutgoin
168
168
  timeoutAction?: WebSocketHeartbeatAction;
169
169
  errorAction?: WebSocketHeartbeatAction;
170
170
  }
171
+ /**
172
+ * Controls how the hook surfaces the live `WebSocket.bufferedAmount`
173
+ * value through `state.bufferedAmount`.
174
+ *
175
+ * The native `WebSocket` does not emit any event when buffered bytes
176
+ * drain to the network, so by default `state.bufferedAmount` is only
177
+ * refreshed when the consumer calls `send(...)`, when a message
178
+ * arrives, or when the socket transitions to `open`. For UIs that need
179
+ * to display real-time backpressure (e.g. an outbound queue meter,
180
+ * a "flushing..." indicator, or a flow-control gauge), enable polling.
181
+ *
182
+ * - `false` (default): no polling. `state.bufferedAmount` updates only
183
+ * on the natural lifecycle events listed above.
184
+ * - `true`: poll once every 100ms while the socket is `open`.
185
+ * - `"raf"`: poll on every animation frame while the socket is `open`.
186
+ * Recommended for visual gauges that are tied to a render loop.
187
+ * - `{ intervalMs: N }`: poll every `N` milliseconds. `N` must be a
188
+ * positive integer; values <= 0 disable polling.
189
+ *
190
+ * Polling is automatically suspended when the socket is not in the
191
+ * `open` state and resumed when it returns to `open`. The hook diffs
192
+ * the polled value against the last committed one, so identical
193
+ * readings do not trigger React re-renders.
194
+ */
195
+ type BufferedAmountPolling = false | true | "raf" | {
196
+ intervalMs: number;
197
+ };
171
198
  interface UseWebSocketOptions<TIncoming = unknown, TOutgoing = TIncoming> {
172
199
  url: UrlProvider;
173
200
  protocols?: string | string[];
174
201
  connect?: boolean;
175
202
  binaryType?: BinaryType;
203
+ bufferedAmountPolling?: BufferedAmountPolling;
176
204
  parseMessage?: MessageParser<TIncoming>;
177
205
  serializeMessage?: MessageSerializer<TOutgoing>;
178
206
  reconnect?: false | UseReconnectOptions;
@@ -274,4 +302,4 @@ declare class RealtimeErrorEvent extends Event {
274
302
  constructor(type: string, init: RealtimeErrorEventInit);
275
303
  }
276
304
 
277
- export { type ConnectionGateReason, type ConnectionStateSnapshot, type HeartbeatAckMatcher, type HeartbeatBeatFn, type MessageParser, type MessageSerializer, type Milliseconds, type RealtimeConnectionStatus, RealtimeErrorEvent, type RealtimeErrorEventInit, type RealtimeErrorKind, 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 };
305
+ export { type BufferedAmountPolling, type ConnectionGateReason, type ConnectionStateSnapshot, type HeartbeatAckMatcher, type HeartbeatBeatFn, type MessageParser, type MessageSerializer, type Milliseconds, type RealtimeConnectionStatus, RealtimeErrorEvent, type RealtimeErrorEventInit, type RealtimeErrorKind, 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
@@ -1315,6 +1315,58 @@ var useWebSocket = (options) => {
1315
1315
  stopHeartbeat();
1316
1316
  }
1317
1317
  }, [state.status, stopHeartbeat]);
1318
+ const rawBufferedAmountPolling = options.bufferedAmountPolling;
1319
+ const bufferedAmountPollingMode = useMemo(() => {
1320
+ if (rawBufferedAmountPolling === void 0 || rawBufferedAmountPolling === false) {
1321
+ return null;
1322
+ }
1323
+ if (rawBufferedAmountPolling === true) {
1324
+ return 100;
1325
+ }
1326
+ if (rawBufferedAmountPolling === "raf") {
1327
+ return "raf";
1328
+ }
1329
+ const intervalMs = rawBufferedAmountPolling.intervalMs;
1330
+ return Number.isFinite(intervalMs) && intervalMs > 0 ? intervalMs : null;
1331
+ }, [rawBufferedAmountPolling]);
1332
+ const pollBufferedAmount = useStableCallback(() => {
1333
+ const socket2 = socketRef.current;
1334
+ if (socket2 === null) {
1335
+ return;
1336
+ }
1337
+ const next = socket2.bufferedAmount;
1338
+ if (stateRef.current.bufferedAmount === next) {
1339
+ return;
1340
+ }
1341
+ commitState((current) => ({
1342
+ ...current,
1343
+ bufferedAmount: next
1344
+ }));
1345
+ });
1346
+ useEffect(() => {
1347
+ if (bufferedAmountPollingMode === null) {
1348
+ return;
1349
+ }
1350
+ if (state.status !== "open") {
1351
+ return;
1352
+ }
1353
+ if (bufferedAmountPollingMode === "raf") {
1354
+ if (typeof requestAnimationFrame !== "function") {
1355
+ return;
1356
+ }
1357
+ let frame = requestAnimationFrame(function loop() {
1358
+ pollBufferedAmount();
1359
+ frame = requestAnimationFrame(loop);
1360
+ });
1361
+ return () => {
1362
+ cancelAnimationFrame(frame);
1363
+ };
1364
+ }
1365
+ const intervalId = setInterval(pollBufferedAmount, bufferedAmountPollingMode);
1366
+ return () => {
1367
+ clearInterval(intervalId);
1368
+ };
1369
+ }, [bufferedAmountPollingMode, state.status, pollBufferedAmount]);
1318
1370
  const status = (reconnect.status === "scheduled" || reconnect.status === "running") && state.status !== "open" ? "reconnecting" : state.status;
1319
1371
  const snapshot = createConnectionStateSnapshot(status, {
1320
1372
  isSupported: supported,