UNPKG

@hyper-fetch/react

Version:

React hooks and utils for the hyper-fetch

1,432 lines (1,410 loc) 53.2 kB
var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/hooks/use-fetch/use-fetch.hooks.ts import { useDidUpdate as useDidUpdate3, useDidMount as useDidMount2, useWillUnmount as useWillUnmount2 } from "@better-hooks/lifecycle"; import { useDebounce, useThrottle } from "@better-hooks/performance"; import { useRef as useRef4 } from "react"; // src/helpers/use-request-events/use-request-events.hooks.ts import { useWillUnmount } from "@better-hooks/lifecycle"; import { useRef } from "react"; var useRequestEvents = ({ request, dispatcher, logger, actions, setCacheData, getIsDataProcessing }) => { const { unstable_responseMapper } = request; const { cache, requestManager } = request.client; const onSuccessCallback = useRef(null); const onErrorCallback = useRef(null); const onAbortCallback = useRef(null); const onOfflineErrorCallback = useRef(null); const onFinishedCallback = useRef(null); const onRequestStartCallback = useRef(null); const onResponseStartCallback = useRef(null); const onDownloadProgressCallback = useRef(null); const onUploadProgressCallback = useRef(null); const lifecycleEvents = useRef(/* @__PURE__ */ new Map()); const dataEvents = useRef(null); const removeLifecycleListener = (requestId) => { const event = lifecycleEvents.current.get(requestId); event == null ? void 0 : event.unmount(); lifecycleEvents.current.delete(requestId); }; const clearLifecycleListeners = () => { const events = lifecycleEvents.current; const listeners = Array.from(events.values()); listeners.forEach((value) => { value.unmount(); }); events.clear(); }; const handleResponseCallbacks = (values) => { var _a, _b, _c, _d, _e; const { success } = values.response; const { isOffline, isCanceled } = values.details; if (request.offline && isOffline && !success) { logger.debug({ title: "Performing offline error callback", type: "system", extra: values }); (_a = onOfflineErrorCallback.current) == null ? void 0 : _a.call(onOfflineErrorCallback, values); } else if (isCanceled) { logger.debug({ title: "Performing abort callback", type: "system", extra: values }); (_b = onAbortCallback.current) == null ? void 0 : _b.call(onAbortCallback, values); } else if (success) { logger.debug({ title: "Performing success callback", type: "system", extra: values }); (_c = onSuccessCallback.current) == null ? void 0 : _c.call(onSuccessCallback, values); } else { logger.debug({ title: "Performing error callback", type: "system", extra: values }); (_d = onErrorCallback.current) == null ? void 0 : _d.call(onErrorCallback, values); } (_e = onFinishedCallback.current) == null ? void 0 : _e.call(onFinishedCallback, values); }; const handleGetLoadingEvent = (req) => { return ({ loading }) => { const isProcessing = getIsDataProcessing(req.cacheKey); if (isProcessing) return; const canDisableLoading = !loading && !dispatcher.hasRunningRequests(req.queryKey); if (loading || canDisableLoading) { actions.setLoading(loading); } }; }; const handleDownloadProgress = (data) => { var _a; (_a = onDownloadProgressCallback.current) == null ? void 0 : _a.call(onDownloadProgressCallback, data); }; const handleUploadProgress = (data) => { var _a; (_a = onUploadProgressCallback.current) == null ? void 0 : _a.call(onUploadProgressCallback, data); }; const handleRequestStart = () => { return (details) => { var _a; (_a = onRequestStartCallback.current) == null ? void 0 : _a.call(onRequestStartCallback, details); }; }; const handleResponseStart = () => { return (details) => { var _a; (_a = onResponseStartCallback.current) == null ? void 0 : _a.call(onResponseStartCallback, details); }; }; const handleResponse = () => { return (values) => { const data = unstable_responseMapper ? unstable_responseMapper(values.response) : values.response; if (data instanceof Promise) { return (() => __async(null, null, function* () { handleResponseCallbacks(__spreadProps(__spreadValues({}, values), { response: yield data })); }))(); } return handleResponseCallbacks(values); }; }; const handleRemove = ({ requestId }) => { removeLifecycleListener(requestId); }; const clearCacheDataListener = () => { var _a; (_a = dataEvents.current) == null ? void 0 : _a.unmount(); dataEvents.current = null; }; const addCacheDataListener = (req) => { const loadingUnmount = requestManager.events.onLoadingByQueue(req.queryKey, handleGetLoadingEvent(req)); const getResponseUnmount = cache.events.onDataByKey(req.cacheKey, setCacheData); const unmount = () => { loadingUnmount(); getResponseUnmount(); }; clearCacheDataListener(); dataEvents.current = { unmount }; return unmount; }; const addLifecycleListeners = (req, requestId) => { if (!requestId) { clearLifecycleListeners(); const { queryKey, cacheKey } = req; const requestStartUnmount2 = requestManager.events.onRequestStartByQueue(queryKey, handleRequestStart()); const responseStartUnmount2 = requestManager.events.onResponseStartByQueue(queryKey, handleResponseStart()); const uploadUnmount2 = requestManager.events.onUploadProgressByQueue(queryKey, handleUploadProgress); const downloadUnmount2 = requestManager.events.onDownloadProgressByQueue(queryKey, handleDownloadProgress); const responseUnmount2 = requestManager.events.onResponseByCache(cacheKey, handleResponse()); const unmount2 = () => { downloadUnmount2(); uploadUnmount2(); requestStartUnmount2(); responseStartUnmount2(); responseUnmount2(); }; lifecycleEvents.current.set(queryKey, { unmount: unmount2 }); return unmount2; } const requestRemove = requestManager.events.onRemoveById(requestId, handleRemove); const requestStartUnmount = requestManager.events.onRequestStartById(requestId, handleRequestStart()); const responseStartUnmount = requestManager.events.onResponseStartById(requestId, handleResponseStart()); const responseUnmount = requestManager.events.onResponseById(requestId, handleResponse()); const uploadUnmount = requestManager.events.onUploadProgressById(requestId, handleUploadProgress); const downloadUnmount = requestManager.events.onDownloadProgressById(requestId, handleDownloadProgress); const unmount = () => { requestRemove(); downloadUnmount(); uploadUnmount(); requestStartUnmount(); responseStartUnmount(); responseUnmount(); }; lifecycleEvents.current.set(requestId, { unmount }); return unmount; }; const abort = () => { const { abortKey } = request; const requests = dispatcher.getAllRunningRequests(); requests.forEach((requestData) => { if (requestData.request.abortKey === abortKey) { dispatcher.delete(requestData.request.queryKey, requestData.requestId, abortKey); } }); }; useWillUnmount(() => { clearLifecycleListeners(); clearCacheDataListener(); }); return [ { abort, onSuccess: (callback) => { onSuccessCallback.current = callback; }, onError: (callback) => { onErrorCallback.current = callback; }, onAbort: (callback) => { onAbortCallback.current = callback; }, onOfflineError: (callback) => { onOfflineErrorCallback.current = callback; }, onFinished: (callback) => { onFinishedCallback.current = callback; }, onRequestStart: (callback) => { onRequestStartCallback.current = callback; }, onResponseStart: (callback) => { onResponseStartCallback.current = callback; }, onDownloadProgress: (callback) => { onDownloadProgressCallback.current = callback; }, onUploadProgress: (callback) => { onUploadProgressCallback.current = callback; } }, { addCacheDataListener, clearCacheDataListener, addLifecycleListeners, removeLifecycleListener, clearLifecycleListeners } ]; }; // src/helpers/use-tracked-state/use-tracked-state.constants.ts var initialState = { data: null, error: null, status: null, extra: {}, success: false, loading: false, retries: 0, responseTimestamp: null, requestTimestamp: null }; // src/helpers/use-tracked-state/use-tracked-state.hooks.ts import { useDidUpdate, useForceUpdate } from "@better-hooks/lifecycle"; import { useRef as useRef2 } from "react"; // src/utils/deep-equal.utils.ts var isEmpty = (value) => { const valueType = Object.prototype.toString.call(value); if (Array.isArray(value)) return !value.length; if (typeof value === "object" && value !== null && valueType === "[object Object]") return !Object.keys(value).length; return false; }; var isEqual = (firstValue, secondValue) => { if (firstValue === secondValue) return true; try { const firstValueType = Object.prototype.toString.call(firstValue); const secondValueType = Object.prototype.toString.call(secondValue); const firstType = typeof firstValue; const secondType = typeof secondValue; const isType = (type) => firstType === type && secondType === type; const isTypeValue = (type) => firstValueType === type && secondValueType === type; if (firstValueType !== secondValueType) return false; if (isType("number") && Number.isNaN(firstValue) && Number.isNaN(secondValue)) return true; if (isEmpty(firstValue) && isEmpty(secondValue)) return true; if (Array.isArray(firstValue) && Array.isArray(secondValue)) { if (firstValue.length !== secondValue.length) return false; return !firstValue.some((element, i) => !isEqual(element, secondValue[i])); } if (isType("object") && isTypeValue("[object Object]")) { if (Object.keys(firstValue).length !== Object.keys(secondValue).length) return false; return !Object.entries(firstValue).some( ([key, value]) => !isEqual(value, secondValue[key]) ); } if (firstValue instanceof Date && secondValue instanceof Date) { return +firstValue === +secondValue; } return firstValue === secondValue; } catch (err) { console.error(err); return false; } }; // src/utils/bounce.utils.ts var getBounceData = (bounceData) => { return __spreadValues(__spreadValues({}, bounceData), { throttle: void 0, debounce: void 0 }); }; // src/helpers/use-tracked-state/use-tracked-state.utils.ts var getDetailsState = (state, details) => { return __spreadValues({ retries: (state == null ? void 0 : state.retries) || 0, isCanceled: false, isOffline: false, addedTimestamp: +/* @__PURE__ */ new Date(), triggerTimestamp: +/* @__PURE__ */ new Date(), requestTimestamp: +/* @__PURE__ */ new Date(), responseTimestamp: +/* @__PURE__ */ new Date() }, details); }; var isStaleCacheData = (staleTime, staleTimestamp) => { if (!staleTimestamp) return true; return +/* @__PURE__ */ new Date() > +staleTimestamp + staleTime; }; var getValidCacheData = (request, initialResponse, cacheData) => { var _a, _b; const isStale = isStaleCacheData(request.staleTime, cacheData == null ? void 0 : cacheData.responseTimestamp); if (!isStale && cacheData) { return cacheData; } if (initialResponse) { return __spreadProps(__spreadValues(__spreadValues({ data: null, error: null, status: null, success: true, extra: null, cached: !!request.cache }, initialResponse), getDetailsState()), { staleTime: 1e3, version: request.client.cache.version, cacheKey: request.cacheKey, cacheTime: request.cacheTime, requestTimestamp: (_a = initialResponse == null ? void 0 : initialResponse.requestTimestamp) != null ? _a : +/* @__PURE__ */ new Date(), responseTimestamp: (_b = initialResponse == null ? void 0 : initialResponse.responseTimestamp) != null ? _b : +/* @__PURE__ */ new Date() }); } return null; }; var getTimestamp = (timestamp) => { return timestamp ? new Date(timestamp) : null; }; var getIsInitiallyLoading = ({ queryKey, dispatcher, hasState, revalidate, disabled }) => { if (!revalidate && hasState) { return false; } const queue = dispatcher.getQueue(queryKey); const isInitiallyLoading = dispatcher.hasRunningRequests(queryKey) || !queue.stopped && disabled === false; return isInitiallyLoading; }; var getInitialState = ({ initialResponse, dispatcher, request, disabled, revalidate }) => { const { client, cacheKey, unstable_responseMapper } = request; const { cache } = client; const cacheData = cache.get(cacheKey); const cacheState = getValidCacheData(request, initialResponse, cacheData); const initialLoading = getIsInitiallyLoading({ queryKey: request.queryKey, dispatcher, disabled, revalidate, hasState: !!cacheState }); if (cacheState) { const mappedData = unstable_responseMapper ? unstable_responseMapper(cacheState) : cacheState; if (mappedData instanceof Promise) { return initialState; } return { data: mappedData.data, error: mappedData.error, status: mappedData.status, success: mappedData.success, extra: mappedData.extra || client.adapter.defaultExtra, retries: cacheState.retries, requestTimestamp: getTimestamp(cacheState.requestTimestamp), responseTimestamp: getTimestamp(cacheState.responseTimestamp), loading: initialLoading }; } return { data: initialState.data, error: initialState.error, status: initialState.status, success: initialState.success, extra: request.client.adapter.defaultExtra, retries: initialState.retries, requestTimestamp: getTimestamp(initialState.requestTimestamp), responseTimestamp: getTimestamp(initialState.responseTimestamp), loading: initialLoading }; }; // src/helpers/use-tracked-state/use-tracked-state.hooks.ts var useTrackedState = ({ request, dispatcher, initialResponse, deepCompare, dependencyTracking, disabled, revalidate }) => { const { client, cacheKey, queryKey, staleTime, unstable_responseMapper } = request; const { cache } = client; const forceUpdate = useForceUpdate(); const state = useRef2(getInitialState({ initialResponse, dispatcher, request, disabled })); const renderKeys = useRef2([]); const isProcessingData = useRef2(""); const getStaleStatus = () => { const cacheData = cache.get(cacheKey); return !cacheData || isStaleCacheData(staleTime, cacheData == null ? void 0 : cacheData.responseTimestamp); }; const renderKeyTrigger = (keys) => { const shouldRerender = renderKeys.current.some((renderKey) => keys.includes(renderKey)); if (shouldRerender) forceUpdate(); }; const setRenderKey = (renderKey) => { if (!renderKeys.current.includes(renderKey)) { renderKeys.current.push(renderKey); } }; useDidUpdate( () => { const cacheData = cache.get(cacheKey); const cacheState = getValidCacheData(request, initialResponse, cacheData); state.current.loading = getIsInitiallyLoading({ queryKey: request.queryKey, dispatcher, disabled, revalidate, hasState: !!cacheState }); if (cacheState) { setCacheData(cacheState); } }, [cacheKey, queryKey], true ); useDidUpdate( () => { const handleDependencyTracking = () => { if (!dependencyTracking) { Object.keys(state.current).forEach((key) => setRenderKey(key)); } }; handleDependencyTracking(); }, [dependencyTracking], true ); const handleCompare = (firstValue, secondValue) => { if (typeof deepCompare === "function") { return deepCompare(firstValue, secondValue); } if (deepCompare) { return isEqual(firstValue, secondValue); } return false; }; const handleCacheData = (cacheData) => { const newStateValues = { data: cacheData.data, error: cacheData.error, status: cacheData.status, success: cacheData.success, extra: cacheData.extra, retries: cacheData.retries, responseTimestamp: new Date(cacheData.responseTimestamp), requestTimestamp: new Date(cacheData.requestTimestamp), loading: dispatcher.hasRunningRequests(queryKey) }; const changedKeys = Object.keys(newStateValues).filter((key) => { const keyValue = key; const firstValue = state.current[keyValue]; const secondValue = newStateValues[keyValue]; return !handleCompare(firstValue, secondValue); }); state.current = __spreadValues(__spreadValues({}, state.current), newStateValues); renderKeyTrigger(changedKeys); }; const setIsDataProcessing = ({ processingCacheKey, isProcessing }) => { if (isProcessing) { isProcessingData.current = processingCacheKey; } else if (isProcessingData.current === cacheKey) { isProcessingData.current = ""; } }; const getIsDataProcessing = (processingCacheKey) => { return isProcessingData.current === processingCacheKey; }; const setCacheData = (cacheData) => { setIsDataProcessing({ processingCacheKey: cacheKey, isProcessing: true }); const data = unstable_responseMapper ? unstable_responseMapper(cacheData) : cacheData; if (data instanceof Promise) { return (() => __async(null, null, function* () { const promiseData = yield data; handleCacheData(__spreadValues(__spreadValues({}, cacheData), promiseData)); setIsDataProcessing({ processingCacheKey: cacheKey, isProcessing: false }); }))(); } setIsDataProcessing({ processingCacheKey: cacheKey, isProcessing: false }); return handleCacheData(__spreadValues(__spreadValues({}, cacheData), data)); }; const actions = { setData: (data) => { state.current.data = data instanceof Function ? data(state.current.data || null) : data; renderKeyTrigger(["data"]); }, setError: (error) => { state.current.error = error instanceof Function ? error(state.current.error || null) : error; renderKeyTrigger(["error"]); }, setLoading: (loading) => { const value = loading instanceof Function ? loading(state.current.loading) : loading; if (value === state.current.loading) return; state.current.loading = value; renderKeyTrigger(["loading"]); }, setStatus: (status) => { const value = status instanceof Function ? status(state.current.status) : status; if (value === state.current.status) return; state.current.status = status instanceof Function ? status(state.current.status || null) : status; renderKeyTrigger(["status"]); }, setSuccess: (success) => { const value = success instanceof Function ? success(state.current.success || false) : success; if (value === state.current.success) return; state.current.success = success instanceof Function ? success(state.current.success || false) : success; renderKeyTrigger(["success"]); }, setExtra: (extra) => { const value = extra instanceof Function ? extra(state.current.extra) : extra; if (value === state.current.extra) return; state.current.extra = extra instanceof Function ? extra(state.current.extra) : extra; renderKeyTrigger(["extra"]); }, setRetries: (retries) => { const value = retries instanceof Function ? retries(state.current.retries || 0) : retries; if (value === state.current.retries) return; state.current.retries = retries instanceof Function ? retries(state.current.retries || 0) : retries; renderKeyTrigger(["retries"]); }, setResponseTimestamp: (timestamp) => { const value = timestamp instanceof Function ? timestamp(state.current.responseTimestamp) : timestamp; if (value === state.current.responseTimestamp) return; const getTimestamp2 = (prev) => { return timestamp instanceof Function ? timestamp(prev ? new Date(prev) : null) : timestamp; }; state.current.responseTimestamp = getTimestamp2(state.current.responseTimestamp); renderKeyTrigger(["responseTimestamp"]); }, setRequestTimestamp: (timestamp) => { const value = timestamp instanceof Function ? timestamp(state.current.requestTimestamp) : timestamp; if (value === state.current.requestTimestamp) return; const getTimestamp2 = (prev) => { return timestamp instanceof Function ? timestamp(prev ? new Date(prev) : null) : timestamp; }; state.current.requestTimestamp = getTimestamp2(state.current.requestTimestamp); renderKeyTrigger(["requestTimestamp"]); } }; return [state.current, actions, { setRenderKey, setCacheData, getStaleStatus, getIsDataProcessing }]; }; // src/helpers/use-socket-state/use-socket-state.hooks.ts import { useDidMount, useDidUpdate as useDidUpdate2, useForceUpdate as useForceUpdate2 } from "@better-hooks/lifecycle"; import { useRef as useRef3 } from "react"; var useSocketState = (socket, { dependencyTracking }) => { const forceUpdate = useForceUpdate2(); const onDisconnectCallback = useRef3(null); const onErrorCallback = useRef3(null); const onConnectedCallback = useRef3(null); const onConnectingCallback = useRef3(null); const onReconnectingCallback = useRef3(null); const onReconnectingFailedCallback = useRef3(null); const state = useRef3(initialSocketState); const renderKeys = useRef3([]); const renderKeyTrigger = (keys) => { const shouldRerender = renderKeys.current.some((renderKey) => keys.includes(renderKey)); if (shouldRerender) forceUpdate(); }; const setRenderKey = (renderKey) => { if (!renderKeys.current.includes(renderKey)) { renderKeys.current.push(renderKey); } }; useDidUpdate2( () => { state.current.connected = socket.adapter.connected; state.current.connecting = socket.adapter.connecting; const handleDependencyTracking = () => { if (!dependencyTracking) { Object.keys(state.current).forEach((key) => setRenderKey(key)); } }; handleDependencyTracking(); }, [dependencyTracking], true ); const actions = { setData: (data) => { state.current.data = data; renderKeyTrigger(["data"]); }, setExtra: (extra) => { state.current.extra = extra; renderKeyTrigger(["extra"]); }, setConnected: (connected) => { state.current.connected = connected; renderKeyTrigger(["data"]); }, setConnecting: (connecting) => { state.current.connecting = connecting; renderKeyTrigger(["data"]); }, setTimestamp: (timestamp) => { state.current.timestamp = timestamp; renderKeyTrigger(["timestamp"]); } }; const callbacks = { onConnected: (callback) => { onConnectedCallback.current = callback; }, onDisconnected: (callback) => { onDisconnectCallback.current = callback; }, onError: (callback) => { onErrorCallback.current = callback; }, onConnecting: (callback) => { onConnectingCallback.current = callback; }, onReconnecting: (callback) => { onReconnectingCallback.current = callback; }, onReconnectingFailed: (callback) => { onReconnectingFailedCallback.current = callback; } }; useDidMount(() => { const umountOnError = socket.events.onError((event) => { var _a; (_a = onErrorCallback.current) == null ? void 0 : _a.call(onErrorCallback, event); }); const umountOnConnecting = socket.events.onConnecting(({ connecting }) => { var _a; actions.setConnecting(connecting); (_a = onConnectingCallback.current) == null ? void 0 : _a.call(onConnectingCallback); }); const umountOnOpen = socket.events.onConnected(() => { var _a; actions.setConnected(true); (_a = onConnectedCallback.current) == null ? void 0 : _a.call(onConnectedCallback); }); const umountOnClose = socket.events.onDisconnected(() => { var _a; actions.setConnected(false); (_a = onDisconnectCallback.current) == null ? void 0 : _a.call(onDisconnectCallback); }); const umountOnReconnecting = socket.events.onReconnecting(({ attempts }) => { var _a; (_a = onReconnectingCallback.current) == null ? void 0 : _a.call(onReconnectingCallback, { attempts }); }); const umountOnReconnectingFailed = socket.events.onReconnectingFailed(({ attempts }) => { var _a; (_a = onReconnectingFailedCallback.current) == null ? void 0 : _a.call(onReconnectingFailedCallback, { attempts }); }); return () => { umountOnError(); umountOnConnecting(); umountOnOpen(); umountOnClose(); umountOnReconnecting(); umountOnReconnectingFailed(); }; }); return [state.current, actions, callbacks, { setRenderKey }]; }; // src/helpers/use-socket-state/use-socket-state.constants.ts var initialSocketState = { data: null, extra: null, connected: false, connecting: false, timestamp: null }; // src/provider/provider.tsx import React, { useContext, useMemo, useState } from "react"; import { jsx } from "react/jsx-runtime"; var ConfigContext = React.createContext({ config: {}, setConfig: () => null }); var Provider = ({ children, config }) => { const [currentConfig, setConfig] = useState(config || {}); const value = useMemo(() => { const contextValue = { config: currentConfig, setConfig }; return contextValue; }, [currentConfig]); return /* @__PURE__ */ jsx(ConfigContext.Provider, { value, children }); }; var useProvider = () => { const config = useContext(ConfigContext); return config; }; // src/hooks/use-fetch/use-fetch.hooks.ts var useFetch = (request, options) => { const { config: globalConfig } = useProvider(); const { dependencies, disabled, dependencyTracking, revalidate, initialResponse, refresh, refreshTime, refetchBlurred, refetchOnBlur, refetchOnFocus, refetchOnReconnect, bounce, bounceType, bounceTime, bounceTimeout, deepCompare } = __spreadValues(__spreadValues(__spreadValues({}, useFetchDefaultOptions), globalConfig.useFetchConfig), options); const updateKey = JSON.stringify(request.toJSON()); const requestDebounce = useDebounce({ delay: bounceTime }); const requestThrottle = useThrottle({ interval: bounceTime, timeout: bounceTimeout }); const refreshDebounce = useDebounce({ delay: refreshTime }); const { cacheKey, queryKey, client } = request; const { cache, fetchDispatcher: dispatcher, appManager, loggerManager } = client; const ignoreReact18DoubleRender = useRef4(true); const logger = useRef4(loggerManager.initialize(client, "useFetch")).current; const bounceData = bounceType === "throttle" ? requestThrottle : requestDebounce; const bounceFunction = bounceType === "throttle" ? requestThrottle.throttle : requestDebounce.debounce; const [state, actions, { setRenderKey, setCacheData, getStaleStatus, getIsDataProcessing }] = useTrackedState({ logger, request, dispatcher, initialResponse, deepCompare, dependencyTracking, disabled, revalidate }); const [callbacks, listeners] = useRequestEvents({ logger, actions, request, dispatcher, setCacheData, getIsDataProcessing }); const { addCacheDataListener, addLifecycleListeners, clearCacheDataListener } = listeners; const handleFetch = () => { if (!disabled) { logger.debug({ title: `Fetching data`, type: "system", extra: { request } }); dispatcher.add(request); } else { logger.debug({ title: `Cannot add to fetch queue`, type: "system", extra: { disabled } }); } }; function handleRefresh() { if (!refresh) { refreshDebounce.reset(); return; } refreshDebounce.debounce(() => { const isBlurred = !appManager.isFocused; const isFetching = dispatcher.hasRunningRequests(request.queryKey); const isQueued = dispatcher.getIsActiveQueue(request.queryKey); const isActive = isFetching || isQueued; const canRefreshBlurred = isBlurred && refetchBlurred && !isActive; const canRefreshFocused = !isBlurred && !isActive; if (canRefreshBlurred || canRefreshFocused) { handleFetch(); logger.debug({ title: `Performing refresh request`, type: "system", extra: { request } }); } handleRefresh(); }); } const refetch = () => { handleFetch(); handleRefresh(); }; const getIsFetchingIdentity = () => { return dispatcher.getRunningRequests(queryKey).some((running) => running.request.cacheKey === cacheKey); }; const initialFetchData = () => { const hasStaleData = getStaleStatus(); const isFetching = getIsFetchingIdentity(); if ((revalidate || hasStaleData) && !isFetching) { handleFetch(); } }; const updateFetchData = () => { const hasStaleData = getStaleStatus(); const shouldUpdate = !revalidate ? hasStaleData : true; if (!ignoreReact18DoubleRender.current && shouldUpdate) { if (bounce) { logger.debug({ title: `Bounce request with ${bounceType}`, type: "system", extra: { queryKey, request } }); bounceFunction(() => handleFetch()); } else { handleFetch(); } } else { ignoreReact18DoubleRender.current = false; } }; const handleMountEvents = () => { addCacheDataListener(request); addLifecycleListeners(request); const focusUnmount = appManager.events.onFocus(() => { if (refetchOnFocus) { handleFetch(); handleRefresh(); } }); const blurUnmount = appManager.events.onBlur(() => { if (refetchOnBlur) { handleFetch(); handleRefresh(); } }); const onlineUnmount = appManager.events.onOnline(() => { if (refetchOnReconnect) { handleFetch(); handleRefresh(); } }); const invalidateUnmount = cache.events.onInvalidateByKey(cacheKey, handleFetch); const deletionUnmount = cache.events.onDeleteByKey(cacheKey, handleFetch); const unmount = () => { clearCacheDataListener(); focusUnmount(); blurUnmount(); onlineUnmount(); invalidateUnmount(); deletionUnmount(); }; return unmount; }; useDidUpdate3(handleMountEvents, [updateKey], true); useDidMount2(initialFetchData); useDidUpdate3(updateFetchData, [updateKey, disabled, ...dependencies], true); useDidUpdate3(handleRefresh, [updateKey, ...dependencies, disabled, refresh, refreshTime], true); useWillUnmount2(() => { ignoreReact18DoubleRender.current = true; }); return __spreadProps(__spreadValues(__spreadValues({ get data() { setRenderKey("data"); return state.data; }, get error() { setRenderKey("error"); return state.error; }, get loading() { setRenderKey("loading"); return state.loading; }, get status() { setRenderKey("status"); return state.status; }, get success() { setRenderKey("success"); return state.success; }, get extra() { setRenderKey("extra"); return state.extra; }, get retries() { setRenderKey("retries"); return state.retries; }, get responseTimestamp() { setRenderKey("responseTimestamp"); return state.responseTimestamp; }, get requestTimestamp() { setRenderKey("requestTimestamp"); return state.requestTimestamp; }, bounce: getBounceData(bounceData) }, actions), callbacks), { refetch }); }; // src/hooks/use-fetch/use-fetch.utils.ts var getRefreshTime = (refreshTime, dataTimestamp) => { if (dataTimestamp) { const timeDiff = Date.now() - +dataTimestamp; return timeDiff < refreshTime ? refreshTime - timeDiff : refreshTime; } return refreshTime; }; // src/hooks/use-fetch/use-fetch.constants.ts import { Time } from "@hyper-fetch/core"; var useFetchDefaultOptions = { dependencies: [], disabled: false, dependencyTracking: true, revalidate: true, initialResponse: null, refresh: false, refreshTime: Time.HOUR, refetchBlurred: true, refetchOnBlur: false, refetchOnFocus: false, refetchOnReconnect: false, bounce: false, bounceType: "debounce", bounceTime: 400, bounceTimeout: 400, deepCompare: true }; // src/hooks/use-submit/use-submit.hooks.ts import { sendRequest } from "@hyper-fetch/core"; import { useDidMount as useDidMount3 } from "@better-hooks/lifecycle"; import { useDebounce as useDebounce2, useThrottle as useThrottle2 } from "@better-hooks/performance"; import { useMemo as useMemo2, useRef as useRef5 } from "react"; var useSubmit = (request, options) => { const { config: globalConfig } = useProvider(); const mergedOptions = useMemo2( () => __spreadValues(__spreadValues(__spreadValues({}, useSubmitDefaultOptions), globalConfig.useSubmitConfig), options), // eslint-disable-next-line react-hooks/exhaustive-deps [globalConfig.useSubmitConfig, JSON.stringify(options), options == null ? void 0 : options.deepCompare] ); const { disabled, dependencyTracking, initialResponse, bounce, bounceType, bounceTime, deepCompare } = mergedOptions; const { client } = request; const { cache, submitDispatcher: dispatcher, loggerManager } = client; const logger = useRef5(loggerManager.initialize(client, "useSubmit")).current; const requestDebounce = useDebounce2({ delay: bounceTime }); const requestThrottle = useThrottle2({ interval: bounceTime, timeout: "bounceTimeout" in mergedOptions ? mergedOptions.bounceTimeout : bounceTime }); const bounceResolver = useRef5(() => null); const bounceData = bounceType === "throttle" ? requestThrottle : requestDebounce; const bounceFunction = bounceType === "throttle" ? requestThrottle.throttle : requestDebounce.debounce; const [state, actions, { setRenderKey, setCacheData, getIsDataProcessing }] = useTrackedState({ logger, request, dispatcher, initialResponse, deepCompare, dependencyTracking }); const [callbacks, listeners] = useRequestEvents({ logger, actions, request, dispatcher, setCacheData, getIsDataProcessing }); const { addCacheDataListener, addLifecycleListeners } = listeners; const handleSubmit = (submitOptions) => { const requestClone = request.clone(submitOptions); if (disabled) { logger.warning({ title: `Cannot submit request`, type: "system", extra: { disabled, submitOptions } }); return Promise.resolve({ data: null, error: new Error("Cannot submit request. Option 'disabled' is enabled"), status: null, extra: request.client.adapter.defaultExtra }); } const triggerRequest = () => { addCacheDataListener(requestClone); const configuration = __spreadProps(__spreadValues({ dispatcherType: "submit" }, submitOptions), { onBeforeSent: (data) => { var _a; addLifecycleListeners(requestClone, data.requestId); (_a = submitOptions == null ? void 0 : submitOptions.onBeforeSent) == null ? void 0 : _a.call(submitOptions, data); } }); return sendRequest(requestClone, configuration); }; return new Promise((resolve) => { const performSubmit = () => __async(null, null, function* () { logger.debug({ title: `Submitting request`, type: "system", extra: { disabled, submitOptions } }); if (bounce) { const bouncedResolve = bounceResolver.current; bounceResolver.current = (value) => { bouncedResolve(value); resolve(value); }; bounceFunction(() => __async(null, null, function* () { const callback = bounceResolver.current; bounceResolver.current = () => null; const value = yield triggerRequest(); callback(value); })); } else { const value = yield triggerRequest(); resolve(value); } }); performSubmit(); }); }; const refetch = () => { cache.invalidate(request); }; const handlers = { onSubmitSuccess: callbacks.onSuccess, onSubmitError: callbacks.onError, onSubmitFinished: callbacks.onFinished, onSubmitRequestStart: callbacks.onRequestStart, onSubmitResponseStart: callbacks.onResponseStart, onSubmitDownloadProgress: callbacks.onDownloadProgress, onSubmitUploadProgress: callbacks.onUploadProgress, onSubmitOfflineError: callbacks.onOfflineError, onSubmitAbort: callbacks.onAbort }; useDidMount3(() => { addCacheDataListener(request); }); return __spreadProps(__spreadValues(__spreadValues({ submit: handleSubmit, get data() { setRenderKey("data"); return state.data; }, get error() { setRenderKey("error"); return state.error; }, get submitting() { setRenderKey("loading"); return state.loading; }, get status() { setRenderKey("status"); return state.status; }, get success() { setRenderKey("success"); return state.success; }, get extra() { setRenderKey("extra"); return state.extra; }, get retries() { setRenderKey("retries"); return state.retries; }, get responseTimestamp() { setRenderKey("responseTimestamp"); return state.responseTimestamp; }, get requestTimestamp() { setRenderKey("requestTimestamp"); return state.requestTimestamp; }, abort: callbacks.abort }, actions), handlers), { bounce: getBounceData(bounceData), refetch }); }; // src/hooks/use-submit/use-submit.constants.ts var useSubmitDefaultOptions = { disabled: false, dependencyTracking: true, initialResponse: null, bounce: false, bounceType: "debounce", bounceTime: 400, deepCompare: true }; // src/hooks/use-queue/use-queue.hooks.ts import { getRequestDispatcher } from "@hyper-fetch/core"; import { useState as useState2, useEffect, useCallback } from "react"; var canUpdate = (item) => { if (item.success || item.failed) { return false; } return true; }; var useQueue = (request, options) => { const { config: globalConfig } = useProvider(); const { dispatcherType, keepFinishedRequests } = __spreadValues(__spreadValues(__spreadValues({}, useQueueDefaultOptions), globalConfig.useQueueConfig), options); const { abortKey, queryKey, client } = request; const { requestManager } = client; const [dispatcher] = getRequestDispatcher(request, dispatcherType); const [stopped, setStopped] = useState2(false); const [requests, setRequests] = useState2([]); const createRequestsArray = useCallback( (queueElements, prevRequests) => { const newRequests = queueElements.filter((el) => !(prevRequests == null ? void 0 : prevRequests.some((prevEl) => prevEl.requestId === el.requestId))).map((req) => __spreadProps(__spreadValues({ failed: false, canceled: false, removed: false, success: false }, req), { downloading: { progress: 0, timeLeft: 0, sizeLeft: 0, total: 0, loaded: 0, startTimestamp: 0 }, uploading: { progress: 0, timeLeft: 0, sizeLeft: 0, total: 0, loaded: 0, startTimestamp: 0 }, stopRequest: () => dispatcher.stopRequest(queryKey, req.requestId), startRequest: () => dispatcher.startRequest(queryKey, req.requestId), deleteRequest: () => dispatcher.delete(queryKey, req.requestId, abortKey) })); if (keepFinishedRequests && prevRequests) { return [...prevRequests, ...newRequests]; } return newRequests; }, [abortKey, dispatcher, queryKey, keepFinishedRequests] ); const mergePayloadType = useCallback((requestId, data) => { setRequests( (prev) => prev.map((el) => { if (el.requestId === requestId && canUpdate(el)) { return __spreadValues(__spreadValues({}, el), data); } return el; }) ); }, []); const getInitialState2 = () => { const requestQueue = dispatcher.getQueue(queryKey); setStopped(requestQueue.stopped); setRequests(createRequestsArray(requestQueue.requests)); }; const updateQueueState = useCallback( (values) => { setStopped(values.stopped); setRequests((prev) => createRequestsArray(values.requests, prev)); }, [createRequestsArray] ); const mountEvents = () => { const unmountChange = dispatcher.events.onQueueChangeByKey(queryKey, updateQueueState); const unmountStatus = dispatcher.events.onQueueStatusChangeByKey(queryKey, updateQueueState); const unmountFailed = requestManager.events.onResponse(({ requestId, response }) => { if (!response.success) { setRequests((prev) => prev.map((el) => el.requestId === requestId ? __spreadProps(__spreadValues({}, el), { failed: true }) : el)); } else { setRequests((prev) => prev.map((el) => el.requestId === requestId ? __spreadProps(__spreadValues({}, el), { success: true }) : el)); } }); const unmountCanceled = requestManager.events.onAbort(({ requestId }) => { setRequests((prev) => prev.map((el) => el.requestId === requestId ? __spreadProps(__spreadValues({}, el), { canceled: true }) : el)); }); const unmountRemoved = requestManager.events.onRemove(({ requestId }) => { setRequests( (prev) => prev.map((el) => el.requestId === requestId && canUpdate(el) ? __spreadProps(__spreadValues({}, el), { removed: true }) : el) ); }); const unmountDownload = requestManager.events.onDownloadProgress( ({ progress, timeLeft, sizeLeft, total, loaded, startTimestamp, requestId }) => { mergePayloadType(requestId, { downloading: { progress, timeLeft, sizeLeft, total, loaded, startTimestamp } }); } ); const unmountUpload = requestManager.events.onUploadProgress( ({ progress, timeLeft, sizeLeft, total, loaded, startTimestamp, requestId }) => { mergePayloadType(requestId, { uploading: { progress, timeLeft, sizeLeft, total, loaded, startTimestamp } }); } ); const unmount = () => { unmountStatus(); unmountChange(); unmountDownload(); unmountUpload(); unmountFailed(); unmountCanceled(); unmountRemoved(); }; return unmount; }; useEffect(getInitialState2, [createRequestsArray, dispatcher, queryKey]); useEffect(mountEvents, [ stopped, requests, setRequests, setStopped, queryKey, dispatcher.events, requestManager.events, updateQueueState, mergePayloadType ]); return { stopped, requests, dispatcher, stop: () => dispatcher.stop(queryKey), pause: () => dispatcher.pause(queryKey), start: () => dispatcher.start(queryKey) }; }; // src/hooks/use-queue/use-queue.constants.ts var useQueueDefaultOptions = { dispatcherType: "auto", keepFinishedRequests: false }; // src/hooks/use-cache/use-cache.hooks.ts import { useDidUpdate as useDidUpdate4 } from "@better-hooks/lifecycle"; import { getRequestDispatcher as getRequestDispatcher2 } from "@hyper-fetch/core"; import { useRef as useRef6 } from "react"; var useCache = (request, options) => { const { cacheKey, client } = request; const { cache, loggerManager } = client; const logger = useRef6(loggerManager.initialize(client, "useCache")).current; const [dispatcher] = getRequestDispatcher2(request); const updateKey = JSON.stringify(request.toJSON()); const { config: globalConfig } = useProvider(); const { dependencyTracking, initialResponse, deepCompare } = __spreadValues(__spreadValues(__spreadValues({}, useCacheDefaultOptions), globalConfig.useCacheConfig), options); const [state, actions, { setRenderKey, setCacheData, getIsDataProcessing }] = useTrackedState({ logger, request, dispatcher, initialResponse, deepCompare, dependencyTracking }); const [, listeners] = useRequestEvents({ logger, actions, request, dispatcher, setCacheData, getIsDataProcessing }); const { addCacheDataListener, addLifecycleListeners } = listeners; const handleMountEvents = () => { const unmountDataListener = addCacheDataListener(request); const unmountLifecycleListener = addLifecycleListeners(request); return () => { unmountDataListener(); unmountLifecycleListener(); }; }; useDidUpdate4( () => { return handleMountEvents(); }, [updateKey], true ); const invalidate = (cacheKeys) => { cache.invalidate(cacheKeys != null ? cacheKeys : cacheKey); }; return __spreadProps(__spreadValues({ get data() { setRenderKey("data"); return state.data; }, get error() { setRenderKey("error"); return state.error; }, get loading() { setRenderKey("loading"); return state.loading; }, get status() { setRenderKey("status"); return state.status; }, get success() { setRenderKey("success"); return state.success; }, get extra() { setRenderKey("extra"); return state.extra; }, get retries() { setRenderKey("retries"); return state.retries; }, get responseTimestamp() { setRenderKey("responseTimestamp"); return state.responseTimestamp; }, get requestTimestamp() { setRenderKey("requestTimestamp"); return state.requestTimestamp; } }, actions), { invalidate }); }; // src/hooks/use-cache/use-cache.constants.ts var useCacheDefaultOptions = { dependencyTracking: true, initialResponse: null, deepCompare: true }; // src/hooks/use-listener/use-listener.hooks.ts import { useDidUpdate as useDidUpdate5, useWillUnmount as useWillUnmount3 } from "@better-hooks/lifecycle"; import { useRef as useRef7 } from "react"; var useListener = (listener, options) => { const { config: globalConfig } = useProvider(); const { dependencyTracking } = __spreadValues(__spreadValues({}, globalConfig.useListener), options); const [state, actions, callbacks, { setRenderKey }] = useSocketState(listener.socket, { dependencyTracking }); const removeListenerRef = useRef7(null); const onEventCallback = useRef7(null); const stopListener = () => { var _a; (_a = removeListenerRef.current) == null ? void 0 : _a.call(removeListenerRef); }; const listen = () => { stopListener(); removeListenerRef.current = listener.listen(({ data, extra }) => { var _a; (_a = onEventCallback.current) == null ? void 0 : _a.call(onEventCallback, { data, extra }); actions.setData(data); actions.setExtra(extra); actions.setTimestamp(+/* @__PURE__ */ new Date()); }); }; const additionalCallbacks = { onEvent: (callback) => { onEventCallback.current = callback; } }; useDidUpdate5( () => { listen(); }, [listener.params, JSON.stringify(listener.options)], true ); useWillUnmount3(() => { stopListener(); }); return __spreadProps(__spreadValues(__spreadValues(__spreadValues({ get data() { setRenderKey("data"); return state.data; }, get extra() { setRenderKey("extra"); return state.extra; }, get connected() { setRenderKey("connected"); return state.connected; }, get connecting() { setRenderKey("connecting"); return state.connecting; }, get timestamp() { setRenderKey("timestamp"); return state.timestamp; } }, actions), callbacks), additionalCallbacks), { listen }); }; // src/hooks/use-emitter/use-emitter.hooks.ts import { useDidUpdate as useDidUpdate6 } from "@better-hooks/lifecycle"; import { useRef as useRef8 } from "react"; var useEmitter = (emitter, options) => { const { config: globalConfig } = useProvider(); const { dependencyTracking } = __spreadValues(__spreadValues({}, globalConfig.useEmitter), options); const [state, actions, callbacks, { setRenderKey }] = useSocketState(emitter.socket, { dependencyTra