UNPKG

@hyper-fetch/react

Version:

React hooks and utils for the hyper-fetch

1 lines 124 kB
{"version":3,"file":"index.mjs","names":["r","f","t","u","a","n","v","y","d","n","o","t","r","e","u","l","c"],"sources":["../../../node_modules/@better-hooks/lifecycle/dist/index.esm.js","../../../node_modules/@better-hooks/performance/dist/index.esm.js","../src/helpers/use-request-events/use-request-events.hooks.ts","../src/helpers/use-tracked-state/use-tracked-state.constants.ts","../src/utils/deep-equal.utils.ts","../src/utils/bounce.utils.ts","../src/utils/tracked-proxy.utils.ts","../src/helpers/use-tracked-state/use-tracked-state.utils.ts","../src/helpers/use-tracked-state/use-tracked-state.hooks.ts","../src/helpers/use-socket-state/use-socket-state.hooks.ts","../src/helpers/use-socket-state/use-socket-state.constants.ts","../src/provider/provider.tsx","../src/hooks/use-fetch/use-fetch.hooks.ts","../src/hooks/use-fetch/use-fetch.utils.ts","../src/hooks/use-fetch/use-fetch.constants.ts","../src/hooks/use-submit/use-submit.hooks.ts","../src/hooks/use-submit/use-submit.constants.ts","../src/hooks/use-queue/use-queue.hooks.ts","../src/hooks/use-queue/use-queue.constants.ts","../src/hooks/use-cache/use-cache.hooks.ts","../src/hooks/use-cache/use-cache.constants.ts","../src/hooks/use-stream/use-stream.hooks.ts","../src/hooks/use-listener/use-listener.hooks.ts","../src/hooks/use-emitter/use-emitter.hooks.ts","../src/hooks/use-app-manager/use-app-manager.hooks.ts"],"sourcesContent":["import{useRef as r,useEffect as n,useState as t,useCallback as u,useLayoutEffect as c}from\"react\";var e=function(t,u,c){void 0===c&&(c=!1);var e=r(null),o=r(c);n((function(){if(o.current)return function(){t(e.current),e.current=u};o.current=!0}),u)},o=function(t){var u=r(!1);n((function(){if(!u.current)return u.current=!0,t()}),[])},i=function(t,u,c){void 0===c&&(c=!1);var e=r(c);n((function(){if(e.current)return t();e.current=!0}),u)};var f=function(){var r=function(r,n){var t=\"function\"==typeof Symbol&&r[Symbol.iterator];if(!t)return r;var u,c,e=t.call(r),o=[];try{for(;(void 0===n||n-- >0)&&!(u=e.next()).done;)o.push(u.value)}catch(r){c={error:r}}finally{try{u&&!u.done&&(t=e.return)&&t.call(e)}finally{if(c)throw c.error}}return o}(t(0),2)[1];return u((function(){r((function(r){return r+1}))}),[])},a=function(){var t=r(!0);return n((function(){return t.current=!0,function(){t.current=!1}}),[]),t},v=function(n){var t=r(!1);c((function(){if(!t.current)return t.current=!0,n()}),[])},l=function(n,t,u){void 0===u&&(u=!1);var e=r(u);c((function(){if(e.current)return n();e.current=!0}),t)},y=function(n){var t=r(!0);t.current&&n(),t.current=!1},d=function(r){n((function(){return r}),[])};export{e as useDepsChange,o as useDidMount,i as useDidUpdate,f as useForceUpdate,a as useIsMounted,v as useLayoutMount,l as useLayoutUpdate,y as useWillMount,d as useWillUnmount};\n//# sourceMappingURL=index.esm.js.map\n","import{useRef as n,useCallback as t,useEffect as r,useState as e,useMemo as u,useLayoutEffect as c}from\"react\";import{useForceUpdate as o,useDidUpdate as i,useDidMount as l}from\"@better-hooks/lifecycle\";var f=function(e){var u=(e||{}).delay,c=void 0===u?400:u,i=n(!1),l=n(null),f=o(),a=t((function(){i.current&&f()}),[f]),v=function(){null!==l.current&&clearTimeout(l.current),l.current=null},d=t((function(n,t){var r=null!=t?t:c;v(),l.current=setTimeout((function(){l.current=null,n(),a()}),r),a()}),[c,a]);return r((function(){return v}),[]),{get active(){return i.current=!0,!!l.current},debounce:d,reset:v}},a=function(n,t,e){var u=f(t).debounce;r((function(){u(n)}),e)};function v(n,t){var r=\"function\"==typeof Symbol&&n[Symbol.iterator];if(!r)return n;var e,u,c=r.call(n),o=[];try{for(;(void 0===t||t-- >0)&&!(e=c.next()).done;)o.push(e.value)}catch(n){u={error:n}}finally{try{e&&!e.done&&(r=c.return)&&r.call(c)}finally{if(u)throw u.error}}return o}var d=function(n,t){var r=f(t).debounce,u=v(e(n),2),c=u[0],o=u[1];return[c,function(n){return r((function(){return o(n)}))}]},y=function(n){var t=Object.prototype.toString.call(n);return Array.isArray(n)?!n.length:\"object\"==typeof n&&null!==n&&\"[object Object]\"===t&&!Object.keys(n).length},m=function(n,t){var r,e=Object.prototype.toString.call(n),u=Object.prototype.toString.call(t),c=typeof n,o=typeof t,i=function(n){return c===n&&o===n};return e===u&&(null===n&&null===t||(!!(i(\"number\")&&Number.isNaN(n)&&Number.isNaN(t))||(!(!y(n)||!y(t))||(Array.isArray(n)&&Array.isArray(t)?n.length===t.length&&!n.some((function(n,r){return!m(n,t[r])})):i(\"object\")&&(e===(r=\"[object Object]\")&&u===r)?Object.keys(n).length===Object.keys(t).length&&!Object.entries(n).some((function(n){var r=v(n,2),e=r[0],u=r[1];return!m(u,t[e])})):n instanceof Date&&t instanceof Date?+n==+t:n===t))))},s=function(t,e){return r(t,(o=n(c=e),i=n(0),m(c,o.current)||(o.current=c,i.current+=1),u((function(){return o.current}),[i.current])));var c,o,i},b=function(t,r,u){var c=u||{},o=c.immediate,f=void 0===o||o,a=c.disabled,d=void 0!==a&&a,y=v(e(null),2),m=y[0],s=y[1],b=n(t),p=function(){m&&clearInterval(m),s(null)},h=function(){p(),!r&&0!==r||d||s(setInterval((function(){return b.current()}),r))};return i((function(){b.current=t}),[t]),l((function(){if(b.current=t,f)return h(),p})),i((function(){return b.current=t,h(),p}),[r]),{active:null!==m,start:h,stop:p}},p=function(t){var e=t||{},u=e.interval,c=void 0===u?200:u,i=e.timeout,l=void 0===i?200:i,f=n(0),a=n(!0),v=n(!1),d=n(null),y=o(),m=function(){a.current&&(a.current=!1),v.current&&y()},s=function(){null!==d.current&&clearTimeout(d.current),d.current=null};return r((function(){return s}),[]),{get active(){return v.current=!0,!!d.current},throttle:function(n,t){var r,e,u=function(){f.current=Date.now(),n(),m()},o=null!==(r=null==t?void 0:t.interval)&&void 0!==r?r:c,i=null!==(e=null==t?void 0:t.timeout)&&void 0!==e?e:l,v=Date.now()>=f.current+o;a.current&&m(),d.current&&s(),v?u():i&&(d.current=setTimeout((function(){d.current=null,a.current=!0,u()}),i))},reset:s}},h=function(n,t,e){var u=p(t).throttle;r((function(){u(n)}),e)},j=function(n,t){var r=p(t).throttle,u=v(e(n),2),c=u[0],o=u[1];return[c,function(n){return r((function(){return o(n)}))}]},g=function(t,e){var u=n(t);w((function(){u.current=t}),[t]),r((function(){if(e||0===e){var n=setTimeout((function(){return u.current()}),e);return function(){return clearTimeout(n)}}}),[e])},w=!(\"undefined\"==typeof window||void 0===window.document||void 0===window.document.createElement)?c:r;export{f as useDebounce,a as useDebounceEffect,d as useDebounceState,s as useDeepEffect,b as useInterval,w as useIsomorphicEffect,p as useThrottle,h as useThrottleEffect,j as useThrottleState,g as useTimeout};\n//# sourceMappingURL=index.esm.js.map\n","import type {\n ExtractErrorType,\n ExtractResponseType,\n RequestInstance,\n RequestEventType,\n RequestLoadingEventType,\n ExtractAdapterType,\n RequestProgressEventType,\n RequestResponseEventType,\n ResponseType,\n OptimisticCallbackResult,\n} from \"@hyper-fetch/core\";\nimport { scopeKey } from \"@hyper-fetch/core\";\nimport { useWillUnmount } from \"@better-hooks/lifecycle\";\nimport { useRef } from \"react\";\n\nimport type {\n OnErrorCallbackType,\n OnStartCallbackType,\n OnSuccessCallbackType,\n OnProgressCallbackType,\n OnFinishedCallbackType,\n UseRequestEventsDataMap,\n UseRequestEventsReturnType,\n UseRequestEventsPropsType,\n UseRequestEventsLifecycleMap,\n} from \"helpers\";\n\n/**\n * This is helper hook that handles main Hyper-Fetch event/data flow\n * @internal\n * @param options\n * @returns\n */\nexport const useRequestEvents = <R extends RequestInstance>({\n request,\n dispatcher,\n logger,\n actions,\n setCacheData,\n getIsDataProcessing,\n}: UseRequestEventsPropsType<R>): UseRequestEventsReturnType<R> => {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n const { unstable_responseMapper } = request;\n const { cache, requestManager } = request.client;\n\n // ******************\n // Callbacks\n // ******************\n\n const onSuccessCallback = useRef<null | OnSuccessCallbackType<R>>(null);\n const onErrorCallback = useRef<null | OnErrorCallbackType<R>>(null);\n const onAbortCallback = useRef<null | OnErrorCallbackType<R>>(null);\n const onOfflineErrorCallback = useRef<null | OnErrorCallbackType<R>>(null);\n const onFinishedCallback = useRef<null | OnFinishedCallbackType<R>>(null);\n const onRequestStartCallback = useRef<null | OnStartCallbackType<R>>(null);\n const onResponseStartCallback = useRef<null | OnStartCallbackType<R>>(null);\n const onDownloadProgressCallback = useRef<null | OnProgressCallbackType>(null);\n const onUploadProgressCallback = useRef<null | OnProgressCallbackType>(null);\n\n // ******************\n // Listeners unmounting\n // ******************\n\n const lifecycleEvents = useRef<UseRequestEventsLifecycleMap>(new Map());\n const dataEvents = useRef<UseRequestEventsDataMap | null>(null);\n\n const removeLifecycleListener = (requestId: string) => {\n const event = lifecycleEvents.current.get(requestId);\n event?.unmount();\n lifecycleEvents.current.delete(requestId);\n };\n\n const clearLifecycleListeners = () => {\n const events = lifecycleEvents.current;\n const listeners = Array.from(events.values());\n listeners.forEach((value) => {\n value.unmount();\n });\n events.clear();\n };\n\n // ******************\n // Optimistic state\n // ******************\n\n const optimisticResultsRef = useRef<Map<string, OptimisticCallbackResult<any>>>(new Map());\n\n // ******************\n // Response handlers\n // ******************\n\n const handleResponseCallbacks = (values: RequestResponseEventType<R>) => {\n const { success } = values.response;\n const { isOffline, isCanceled, willRetry } = values.details;\n const opt = optimisticResultsRef.current.get(values.requestId);\n const paramsWithContext = { ...values, mutationContext: opt?.context } as any;\n\n if (request.offline && isOffline && !success) {\n logger.debug({ title: \"Performing offline error callback\", type: \"system\", extra: values });\n onOfflineErrorCallback.current?.(paramsWithContext);\n } else if (isCanceled) {\n logger.debug({ title: \"Performing abort callback\", type: \"system\", extra: values });\n try {\n opt?.rollback?.();\n } catch {\n // rollback threw — onAbort still fires\n }\n onAbortCallback.current?.(paramsWithContext);\n } else if (success) {\n logger.debug({ title: \"Performing success callback\", type: \"system\", extra: values });\n if (opt?.invalidate) {\n opt.invalidate.forEach((req) => cache.invalidate(req));\n }\n onSuccessCallback.current?.(paramsWithContext);\n } else {\n logger.debug({ title: \"Performing error callback\", type: \"system\", extra: values });\n if (!willRetry) {\n try {\n opt?.rollback?.();\n } catch {\n // rollback threw — onError still fires\n }\n }\n onErrorCallback.current?.(paramsWithContext);\n }\n onFinishedCallback.current?.(paramsWithContext);\n if (!willRetry) {\n optimisticResultsRef.current.delete(values.requestId);\n }\n };\n\n // ******************\n // Lifecycle\n // ******************\n\n const handleGetLoadingEvent = (req: R) => {\n return ({ loading }: RequestLoadingEventType<RequestInstance>) => {\n const isProcessing = getIsDataProcessing(req.cacheKey);\n\n // When we process the cache data, we don't want to change the loading state during it\n // This prevents the UI from flickering with { data: null, loading: false }\n if (isProcessing) return;\n\n const canDisableLoading = !loading && !dispatcher.hasRunningRequests(scopeKey(req.queryKey, req.scope));\n if (loading || canDisableLoading) {\n actions.setLoading(loading);\n }\n };\n };\n\n const handleDownloadProgress = (data: RequestProgressEventType<RequestInstance>) => {\n onDownloadProgressCallback.current?.(data);\n };\n\n const handleUploadProgress = (data: RequestProgressEventType<RequestInstance>) => {\n onUploadProgressCallback.current?.(data);\n };\n\n const handleRequestStart = () => {\n return (details: RequestEventType<R>) => {\n onRequestStartCallback.current?.(details);\n };\n };\n const handleResponseStart = () => {\n return (details: RequestEventType<R>) => {\n onResponseStartCallback.current?.(details);\n };\n };\n\n const handleResponse = () => {\n return (values: RequestResponseEventType<R>) => {\n const data = unstable_responseMapper\n ? unstable_responseMapper(values.response as ResponseType<any, any, ExtractAdapterType<R>>)\n : values.response;\n\n if (data instanceof Promise) {\n return (async () => {\n handleResponseCallbacks({\n ...values,\n response: (await data) as ResponseType<any, any, ExtractAdapterType<R>>,\n });\n })();\n }\n return handleResponseCallbacks(values);\n };\n };\n\n const handleRemove = ({ requestId }: RequestEventType<R>) => {\n const opt = optimisticResultsRef.current.get(requestId);\n if (opt) {\n try {\n opt.rollback?.();\n } catch {\n // swallow — queue removal rollback failure\n }\n optimisticResultsRef.current.delete(requestId);\n }\n removeLifecycleListener(requestId);\n };\n\n // ******************\n // Data Listeners\n // ******************\n\n const clearCacheDataListener = () => {\n dataEvents.current?.unmount();\n dataEvents.current = null;\n };\n\n const addCacheDataListener = (req: R) => {\n // Data handlers\n const loadingUnmount = requestManager.events.onLoadingByQueue(\n scopeKey(req.queryKey, req.scope),\n handleGetLoadingEvent(req),\n );\n const getResponseUnmount = cache.events.onDataByKey<\n ExtractResponseType<R>,\n ExtractErrorType<R>,\n ExtractAdapterType<R>\n >(req.cacheKey, setCacheData);\n\n const unmount = () => {\n loadingUnmount();\n getResponseUnmount();\n };\n\n clearCacheDataListener();\n dataEvents.current = { unmount };\n\n return unmount;\n };\n\n // ******************\n // Lifecycle Listeners\n // ******************\n\n const addLifecycleListeners = (req: R, requestId?: string, optimisticResult?: OptimisticCallbackResult<any>) => {\n /**\n * useFetch handles requesting by general keys\n * This makes it possible to deduplicate requests from different places and share data\n */\n if (!requestId) {\n // It's important to clear previously attached listeners to not cause some additional response/request\n // events to be triggered during lifecycle\n clearLifecycleListeners();\n const { queryKey, cacheKey } = req;\n const requestStartUnmount = requestManager.events.onRequestStartByQueue(queryKey, handleRequestStart());\n const responseStartUnmount = requestManager.events.onResponseStartByQueue(queryKey, handleResponseStart());\n const uploadUnmount = requestManager.events.onUploadProgressByQueue(queryKey, handleUploadProgress);\n const downloadUnmount = requestManager.events.onDownloadProgressByQueue(queryKey, handleDownloadProgress);\n const responseUnmount = requestManager.events.onResponseByCache(cacheKey, handleResponse());\n\n const unmount = () => {\n downloadUnmount();\n uploadUnmount();\n requestStartUnmount();\n responseStartUnmount();\n responseUnmount();\n };\n\n lifecycleEvents.current.set(queryKey, { unmount });\n\n return unmount;\n }\n /**\n * useSubmit handles requesting by requestIds, this makes it possible to track single requests\n */\n if (optimisticResult) {\n optimisticResultsRef.current.set(requestId, optimisticResult);\n }\n const requestRemove = requestManager.events.onRemoveById(requestId, handleRemove);\n const requestStartUnmount = requestManager.events.onRequestStartById(requestId, handleRequestStart());\n const responseStartUnmount = requestManager.events.onResponseStartById(requestId, handleResponseStart());\n const responseUnmount = requestManager.events.onResponseById(requestId, handleResponse());\n const uploadUnmount = requestManager.events.onUploadProgressById(requestId, handleUploadProgress);\n const downloadUnmount = requestManager.events.onDownloadProgressById(requestId, handleDownloadProgress);\n\n const unmount = () => {\n requestRemove();\n downloadUnmount();\n uploadUnmount();\n requestStartUnmount();\n responseStartUnmount();\n responseUnmount();\n };\n\n lifecycleEvents.current.set(requestId, { unmount });\n\n return unmount;\n };\n\n // ******************\n // Abort\n // ******************\n\n const abort = () => {\n const ak = scopeKey(request.abortKey, request.scope);\n const requests = dispatcher.getAllRunningRequests();\n requests.forEach((requestData) => {\n const reqAk = scopeKey(requestData.request.abortKey, requestData.request.scope);\n if (reqAk === ak) {\n const qk = scopeKey(requestData.request.queryKey, requestData.request.scope);\n dispatcher.delete(qk, requestData.requestId, reqAk);\n }\n });\n };\n\n // ******************\n // Lifecycle\n // ******************\n\n /**\n * On unmount we want to clear all the listeners to prevent memory leaks\n */\n useWillUnmount(() => {\n // Unmount listeners\n clearLifecycleListeners();\n clearCacheDataListener();\n });\n\n return [\n {\n abort,\n onSuccess: (callback: OnSuccessCallbackType<R>) => {\n onSuccessCallback.current = callback;\n },\n onError: (callback: OnErrorCallbackType<R>) => {\n onErrorCallback.current = callback;\n },\n onAbort: (callback: OnErrorCallbackType<R>) => {\n onAbortCallback.current = callback;\n },\n onOfflineError: (callback: OnErrorCallbackType<R>) => {\n onOfflineErrorCallback.current = callback;\n },\n onFinished: (callback: OnFinishedCallbackType<R>) => {\n onFinishedCallback.current = callback;\n },\n onRequestStart: (callback: OnStartCallbackType<R>) => {\n onRequestStartCallback.current = callback;\n },\n onResponseStart: (callback: OnStartCallbackType<R>) => {\n onResponseStartCallback.current = callback;\n },\n onDownloadProgress: (callback: OnProgressCallbackType) => {\n onDownloadProgressCallback.current = callback;\n },\n onUploadProgress: (callback: OnProgressCallbackType) => {\n onUploadProgressCallback.current = callback;\n },\n },\n {\n addCacheDataListener,\n clearCacheDataListener,\n addLifecycleListeners,\n removeLifecycleListener,\n clearLifecycleListeners,\n },\n ];\n};\n","import type { UseTrackedStateType } from \"./use-tracked-state.types\";\n\nexport const initialState: UseTrackedStateType = {\n data: null,\n error: null,\n status: null,\n extra: {},\n success: false,\n loading: false,\n retries: 0,\n responseTimestamp: null,\n requestTimestamp: null,\n};\n","/**\n * Check if value is empty\n * @param value any object or primitive\n * @returns true when value is empty\n */\nexport const isEmpty = (value: unknown): boolean => {\n const valueType = Object.prototype.toString.call(value);\n if (Array.isArray(value)) return !value.length;\n if (typeof value === \"object\" && value !== null && valueType === \"[object Object]\") return !Object.keys(value).length;\n return false;\n};\n\n/**\n * Allow to deep compare any passed values\n * @param firstValue unknown\n * @param secondValue unknown\n * @returns true when elements are equal\n */\nexport const isEqual = (firstValue: unknown, secondValue: unknown): boolean => {\n // Nulls (objects), and other primitives\n if (firstValue === secondValue) return true;\n\n try {\n const firstValueType = Object.prototype.toString.call(firstValue);\n const secondValueType = Object.prototype.toString.call(secondValue);\n\n const firstType = typeof firstValue;\n const secondType = typeof secondValue;\n\n const isType = (type: unknown) => firstType === type && secondType === type;\n const isTypeValue = (type: unknown) => firstValueType === type && secondValueType === type;\n\n // Compared types are different\n if (firstValueType !== secondValueType) return false;\n\n // NaN\n if (isType(\"number\") && Number.isNaN(firstValue) && Number.isNaN(secondValue)) return true;\n\n // Empty Array or Object\n if (isEmpty(firstValue) && isEmpty(secondValue)) return true;\n\n // Array\n if (Array.isArray(firstValue) && Array.isArray(secondValue)) {\n if (firstValue.length !== secondValue.length) return false;\n\n return !firstValue.some((element, i) => !isEqual(element, secondValue[i]));\n }\n\n // Object\n if (isType(\"object\") && isTypeValue(\"[object Object]\")) {\n if (Object.keys(firstValue as object).length !== Object.keys(secondValue as object).length) return false;\n\n return !Object.entries(firstValue as object).some(\n ([key, value]) => !isEqual(value, (secondValue as Record<string, unknown>)[key]),\n );\n }\n\n // Date\n if (firstValue instanceof Date && secondValue instanceof Date) {\n return +firstValue === +secondValue;\n }\n\n // undefined, string, number, bool\n return firstValue === secondValue;\n } catch (err) {\n console.error(err);\n return false;\n }\n};\n","export const getBounceData = (bounceData: {\n reset: () => void;\n active: boolean;\n}): { reset: () => void; active: boolean } => {\n return {\n ...bounceData,\n ...{ throttle: undefined, debounce: undefined },\n };\n};\n","/**\n * Wraps a plain object in a Proxy that calls `setRenderKey` when tracked properties are accessed.\n * Unlike getter-based tracking, Proxy-wrapped objects display their actual values in console.log,\n * making debugging significantly easier while preserving field-level dependency tracking.\n */\nexport const createTrackedProxy = <T extends Record<string, unknown>, K extends string>(\n target: T,\n trackedKeys: ReadonlyArray<K>,\n setRenderKey: (key: K) => void,\n): T => {\n return new Proxy(target, {\n get(obj, prop, receiver) {\n if (typeof prop === \"string\" && trackedKeys.includes(prop as K)) {\n setRenderKey(prop as K);\n }\n return Reflect.get(obj, prop, receiver);\n },\n });\n};\n","/* eslint-disable @typescript-eslint/naming-convention */\nimport type {\n CacheValueType,\n NullableType,\n RequestInstance,\n ExtractResponseType,\n ExtractErrorType,\n ExtractAdapterResolvedType,\n Dispatcher,\n ExtractAdapterType,\n ExtractAdapterExtraType,\n ResponseDetailsType,\n ExtractAdapterStatusType,\n ResponseType,\n} from \"@hyper-fetch/core\";\nimport { scopeKey } from \"@hyper-fetch/core\";\n\nimport type { UseTrackedStateType } from \"helpers\";\nimport { initialState } from \"helpers\";\n\n/**\n * Extracts the \"identity\" portion of a cache key (method + endpoint with resolved params),\n * stripping the query params part. Cache keys follow the format: `method_endpoint_queryParams`.\n */\nconst getCacheKeyIdentity = (cacheKey: string): string => {\n const lastUnderscoreIndex = cacheKey.lastIndexOf(\"_\");\n if (lastUnderscoreIndex === -1) return cacheKey;\n return cacheKey.substring(0, lastUnderscoreIndex);\n};\n\n/**\n * Determines whether state should be cleared when the cache key changes.\n *\n * - `\"clean\"` — always clear\n * - `\"preserve\"` — never clear\n * - `\"auto\"` — clear when the resource identity changed (URL params), preserve when only query params changed\n */\nexport const getShouldClearState = (\n mode: \"auto\" | \"preserve\" | \"clean\",\n oldCacheKey: string,\n newCacheKey: string,\n): boolean => {\n if (oldCacheKey === newCacheKey) return false;\n\n if (mode === \"clean\") return true;\n if (mode === \"preserve\") return false;\n\n // \"auto\" — compare the identity part (method + endpoint with params)\n return getCacheKeyIdentity(oldCacheKey) !== getCacheKeyIdentity(newCacheKey);\n};\n\nexport const getDetailsState = (\n state?: UseTrackedStateType<RequestInstance>,\n details?: Partial<ResponseDetailsType>,\n): ResponseDetailsType => {\n return {\n retries: state?.retries || 0,\n isCanceled: false,\n isOffline: false,\n willRetry: false,\n addedTimestamp: +new Date(),\n triggerTimestamp: +new Date(),\n requestTimestamp: +new Date(),\n responseTimestamp: +new Date(),\n ...details,\n };\n};\n\nexport const isStaleCacheData = (staleTime: number, staleTimestamp: NullableType<Date | number>) => {\n if (!staleTimestamp) return true;\n return +new Date() > +staleTimestamp + staleTime;\n};\n\nexport const getValidCacheData = <T extends RequestInstance>(\n request: T,\n initialResponse: NullableType<Partial<ExtractAdapterResolvedType<T>>>,\n cacheData: NullableType<CacheValueType<ExtractResponseType<T>, ExtractErrorType<T>, ExtractAdapterType<T>>>,\n): CacheValueType<ExtractResponseType<T>, ExtractErrorType<T>, ExtractAdapterType<T>> | null => {\n if (cacheData) {\n return cacheData;\n }\n\n if (initialResponse) {\n return {\n data: null,\n error: null,\n status: null,\n success: true,\n extra: null,\n cached: !!request.cache,\n ...(initialResponse as Partial<ExtractAdapterResolvedType<T>>),\n ...getDetailsState(),\n staleTime: 1000,\n version: request.client.cache.version,\n cacheKey: request.cacheKey,\n scope: request.scope,\n cacheTime: request.cacheTime,\n requestTimestamp: initialResponse?.requestTimestamp ?? +new Date(),\n responseTimestamp: initialResponse?.responseTimestamp ?? +new Date(),\n };\n }\n\n return null;\n};\n\nexport const getTimestamp = (timestamp?: NullableType<number | Date>) => {\n return timestamp ? new Date(timestamp) : null;\n};\n\nexport const getIsInitiallyLoading = <T extends RequestInstance>({\n queryKey,\n dispatcher,\n hasState,\n revalidate,\n disabled,\n}: {\n queryKey: string;\n dispatcher: Dispatcher<ExtractAdapterType<T>>;\n hasState: boolean;\n revalidate?: boolean;\n disabled?: boolean;\n}) => {\n if (!revalidate && hasState) {\n return false;\n }\n\n const queue = dispatcher.getQueue(queryKey);\n const isInitiallyLoading = dispatcher.hasRunningRequests(queryKey) || (!queue.stopped && disabled === false);\n\n return isInitiallyLoading;\n};\n\nexport const getInitialState = <T extends RequestInstance>({\n initialResponse,\n dispatcher,\n request,\n disabled,\n revalidate,\n}: {\n initialResponse: NullableType<Partial<ExtractAdapterResolvedType<T>>>;\n dispatcher: Dispatcher<ExtractAdapterType<T>>;\n request: T;\n disabled?: boolean; // useFetch only\n revalidate?: boolean; // useFetch only\n}): UseTrackedStateType<T> => {\n const { client, cacheKey, unstable_responseMapper } = request;\n const { cache } = client;\n\n const cacheData = cache.get<ExtractResponseType<T>, ExtractErrorType<T>>(scopeKey(cacheKey, request.scope));\n const cacheState = getValidCacheData<T>(request, initialResponse, cacheData);\n\n const initialLoading = getIsInitiallyLoading({\n queryKey: scopeKey(request.queryKey, request.scope),\n dispatcher,\n disabled,\n revalidate,\n hasState: !!cacheState,\n });\n\n if (cacheState) {\n const mappedData = unstable_responseMapper\n ? unstable_responseMapper(cacheState as ResponseType<any, any, ExtractAdapterType<T>>)\n : cacheState;\n if (mappedData instanceof Promise) {\n // For the async mapper we cannot return async values\n // So we have return the initial state instead\n return initialState;\n }\n return {\n data: mappedData.data,\n error: mappedData.error,\n status: mappedData.status as ExtractAdapterStatusType<ExtractAdapterType<T>>,\n success: mappedData.success,\n extra: (mappedData.extra || client.adapter.defaultExtra) as ExtractAdapterExtraType<ExtractAdapterType<T>>,\n retries: cacheState.retries,\n requestTimestamp: getTimestamp(cacheState.requestTimestamp),\n responseTimestamp: getTimestamp(cacheState.responseTimestamp),\n loading: initialLoading,\n };\n }\n\n return {\n data: initialState.data,\n error: initialState.error,\n status: initialState.status as ExtractAdapterStatusType<ExtractAdapterType<T>>,\n success: initialState.success,\n extra: request.client.adapter.defaultExtra as ExtractAdapterExtraType<ExtractAdapterType<T>>,\n retries: initialState.retries,\n requestTimestamp: getTimestamp(initialState.requestTimestamp),\n responseTimestamp: getTimestamp(initialState.responseTimestamp),\n loading: initialLoading,\n };\n};\n","/* eslint-disable @typescript-eslint/naming-convention */\nimport { useDidUpdate } from \"@better-hooks/lifecycle\";\nimport type {\n ExtractErrorType,\n CacheValueType,\n ExtractResponseType,\n RequestInstance,\n ExtractAdapterType,\n ExtractAdapterExtraType,\n ResponseType,\n} from \"@hyper-fetch/core\";\nimport { scopeKey } from \"@hyper-fetch/core\";\nimport { useCallback, useRef, useSyncExternalStore } from \"react\";\n\nimport { isEqual } from \"utils\";\nimport type {\n UseTrackedStateActions,\n UseTrackedStateType,\n UseTrackedStateProps,\n UseTrackedStateReturn,\n} from \"./use-tracked-state.types\";\nimport {\n getInitialState,\n getIsInitiallyLoading,\n getShouldClearState,\n getValidCacheData,\n isStaleCacheData,\n} from \"./use-tracked-state.utils\";\n\n/**\n *\n * @param request\n * @param initialResponse\n * @param dispatcher\n * @param dependencies\n * @internal\n */\nexport const useTrackedState = <T extends RequestInstance>({\n request,\n dispatcher,\n initialResponse,\n deepCompare,\n dependencyTracking,\n keepPreviousData = \"auto\",\n disabled,\n revalidate,\n}: UseTrackedStateProps<T>): UseTrackedStateReturn<T> => {\n const { client, cacheKey, queryKey, staleTime, unstable_responseMapper } = request;\n const { cache } = client;\n\n const state = useRef<UseTrackedStateType<T>>(getInitialState({ initialResponse, dispatcher, request, disabled }));\n const renderKeys = useRef<Array<keyof UseTrackedStateType<T>>>([]);\n const isProcessingData = useRef(\"\");\n const previousCacheKey = useRef(cacheKey);\n\n // ******************\n // useSyncExternalStore\n // ******************\n\n const versionRef = useRef(0);\n const listenerRef = useRef<(() => void) | null>(null);\n\n const subscribe = useCallback((listener: () => void) => {\n listenerRef.current = listener;\n return () => {\n listenerRef.current = null;\n };\n }, []);\n\n const getSnapshot = useCallback(() => versionRef.current, []);\n\n useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n const emitChange = () => {\n versionRef.current += 1;\n listenerRef.current?.();\n };\n\n // ******************\n // Utils\n // ******************\n\n const getStaleStatus = (): boolean => {\n const cacheData = cache.get(scopeKey(cacheKey, request.scope));\n return !cacheData || isStaleCacheData(staleTime, cacheData?.responseTimestamp);\n };\n\n // ******************\n // Dependency Tracking\n // ******************\n\n const renderKeyTrigger = (keys: Array<keyof UseTrackedStateType>) => {\n const shouldRerender = renderKeys.current.some((renderKey) => keys.includes(renderKey));\n if (shouldRerender) emitChange();\n };\n\n const setRenderKey = (renderKey: keyof UseTrackedStateType) => {\n if (!renderKeys.current.includes(renderKey)) {\n renderKeys.current.push(renderKey);\n }\n };\n\n // ******************\n // Cache initialization\n // ******************\n\n useDidUpdate(\n () => {\n const oldKey = previousCacheKey.current;\n previousCacheKey.current = cacheKey;\n\n // Get cache state\n const cacheData = cache.get<ExtractResponseType<T>, ExtractErrorType<T>>(cacheKey);\n const cacheState = getValidCacheData<T>(request, initialResponse, cacheData);\n\n // Determine whether to clear state based on keepPreviousData mode\n const shouldClear = getShouldClearState(keepPreviousData, oldKey, cacheKey);\n\n if (shouldClear && !cacheState) {\n // Reset state to initial values when no cached data exists for the new key\n const resetState = getInitialState({ initialResponse, dispatcher, request, disabled, revalidate });\n state.current = resetState;\n renderKeyTrigger(Object.keys(state.current) as (keyof UseTrackedStateType<T>)[]);\n } else if (cacheState) {\n state.current.loading = getIsInitiallyLoading({\n queryKey: scopeKey(request.queryKey, request.scope),\n dispatcher,\n disabled,\n revalidate,\n hasState: true,\n });\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n setCacheData(cacheState);\n }\n // When state is preserved and no cache exists (e.g. query param change in \"auto\" mode),\n // don't update loading on the ref — let the dispatcher's loading event handle it (#126)\n },\n [cacheKey, queryKey],\n true,\n );\n\n // ******************\n // Turn off dependency tracking\n // ******************\n\n useDidUpdate(\n () => {\n const handleDependencyTracking = () => {\n if (!dependencyTracking) {\n Object.keys(state.current).forEach((key) => setRenderKey(key as Parameters<typeof setRenderKey>[0]));\n }\n };\n\n handleDependencyTracking();\n },\n [dependencyTracking],\n true,\n );\n\n // ******************\n // Cache data handler\n // ******************\n\n const handleCompare = (firstValue: unknown, secondValue: unknown) => {\n if (typeof deepCompare === \"function\") {\n return deepCompare(firstValue, secondValue);\n }\n if (deepCompare) {\n return isEqual(firstValue, secondValue);\n }\n return false;\n };\n\n const handleCacheData = (\n cacheData: CacheValueType<ExtractResponseType<T>, ExtractErrorType<T>, ExtractAdapterType<T>>,\n ) => {\n const newStateValues: UseTrackedStateType<T> = {\n data: cacheData.data,\n error: cacheData.error,\n status: cacheData.status,\n success: cacheData.success,\n extra: cacheData.extra as ExtractAdapterExtraType<ExtractAdapterType<T>>,\n retries: cacheData.retries,\n responseTimestamp: new Date(cacheData.responseTimestamp),\n requestTimestamp: new Date(cacheData.requestTimestamp),\n loading: dispatcher.hasRunningRequests(queryKey),\n };\n\n const changedKeys = Object.keys(newStateValues).filter((key) => {\n const keyValue = key as keyof UseTrackedStateType<T>;\n const firstValue = state.current[keyValue];\n const secondValue = newStateValues[keyValue];\n\n return !handleCompare(firstValue, secondValue);\n }) as unknown as (keyof UseTrackedStateType<T>)[];\n\n state.current = {\n ...state.current,\n ...newStateValues,\n };\n\n renderKeyTrigger(changedKeys);\n };\n\n const setIsDataProcessing = ({\n processingCacheKey,\n isProcessing,\n }: {\n processingCacheKey: string;\n isProcessing: boolean;\n }) => {\n if (isProcessing) {\n isProcessingData.current = processingCacheKey;\n }\n // Do not turn off other keys processing\n else if (isProcessingData.current === cacheKey) {\n isProcessingData.current = \"\";\n }\n };\n\n const getIsDataProcessing = (processingCacheKey: string) => {\n return isProcessingData.current === processingCacheKey;\n };\n\n const setCacheData = (\n cacheData: CacheValueType<ExtractResponseType<T>, ExtractErrorType<T>, ExtractAdapterType<T>>,\n ): Promise<void> | void => {\n setIsDataProcessing({ processingCacheKey: cacheKey, isProcessing: true });\n const data = unstable_responseMapper\n ? unstable_responseMapper(cacheData as ResponseType<any, any, ExtractAdapterType<T>>)\n : cacheData;\n\n if (data instanceof Promise) {\n return (async () => {\n const promiseData = await data;\n handleCacheData({ ...cacheData, ...promiseData } as CacheValueType<\n ExtractResponseType<T>,\n ExtractErrorType<T>,\n ExtractAdapterType<T>\n >);\n setIsDataProcessing({ processingCacheKey: cacheKey, isProcessing: false });\n })();\n }\n setIsDataProcessing({ processingCacheKey: cacheKey, isProcessing: false });\n return handleCacheData({ ...cacheData, ...data } as CacheValueType<\n ExtractResponseType<T>,\n ExtractErrorType<T>,\n ExtractAdapterType<T>\n >);\n };\n\n // ******************\n // Actions\n // ******************\n\n const actions: UseTrackedStateActions<T> = {\n setData: (data) => {\n state.current.data = data instanceof Function ? data(state.current.data || null) : data;\n renderKeyTrigger([\"data\"]);\n },\n setError: (error) => {\n state.current.error = error instanceof Function ? error(state.current.error || null) : error;\n renderKeyTrigger([\"error\"]);\n },\n setLoading: (loading) => {\n const value = loading instanceof Function ? loading(state.current.loading) : loading;\n if (value === state.current.loading) return;\n state.current.loading = value;\n renderKeyTrigger([\"loading\"]);\n },\n setStatus: (status) => {\n const value = status instanceof Function ? status(state.current.status) : status;\n if (value === state.current.status) return;\n state.current.status = status instanceof Function ? status(state.current.status || null) : status;\n renderKeyTrigger([\"status\"]);\n },\n setSuccess: (success) => {\n const value = success instanceof Function ? success(state.current.success || false) : success;\n if (value === state.current.success) return;\n state.current.success = success instanceof Function ? success(state.current.success || false) : success;\n renderKeyTrigger([\"success\"]);\n },\n setExtra: (extra) => {\n const value = extra instanceof Function ? extra(state.current.extra) : extra;\n if (value === state.current.extra) return;\n state.current.extra = extra instanceof Function ? extra(state.current.extra) : extra;\n renderKeyTrigger([\"extra\"]);\n },\n setRetries: (retries) => {\n const value = retries instanceof Function ? retries(state.current.retries || 0) : retries;\n if (value === state.current.retries) return;\n state.current.retries = retries instanceof Function ? retries(state.current.retries || 0) : retries;\n renderKeyTrigger([\"retries\"]);\n },\n setResponseTimestamp: (timestamp) => {\n const value = timestamp instanceof Function ? timestamp(state.current.responseTimestamp) : timestamp;\n if (value === state.current.responseTimestamp) return;\n const getTimestamp = (prev: Date | null) => {\n return timestamp instanceof Function ? timestamp(prev ? new Date(prev) : null) : timestamp;\n };\n\n state.current.responseTimestamp = getTimestamp(state.current.responseTimestamp);\n renderKeyTrigger([\"responseTimestamp\"]);\n },\n setRequestTimestamp: (timestamp) => {\n const value = timestamp instanceof Function ? timestamp(state.current.requestTimestamp) : timestamp;\n if (value === state.current.requestTimestamp) return;\n const getTimestamp = (prev: Date | null) => {\n return timestamp instanceof Function ? timestamp(prev ? new Date(prev) : null) : timestamp;\n };\n\n state.current.requestTimestamp = getTimestamp(state.current.requestTimestamp);\n renderKeyTrigger([\"requestTimestamp\"]);\n },\n clearState: () => {\n state.current = {\n data: null,\n error: null,\n loading: false,\n status: null,\n success: false,\n extra: null,\n retries: 0,\n responseTimestamp: null,\n requestTimestamp: null,\n } as UseTrackedStateType<T>;\n renderKeyTrigger(Object.keys(state.current) as (keyof UseTrackedStateType<T>)[]);\n },\n };\n\n return [state.current, actions, { setRenderKey, setCacheData, getStaleStatus, getIsDataProcessing }];\n};\n","import type { SocketInstance, ExtractSocketExtraType } from \"@hyper-fetch/sockets\";\nimport { useDidMount, useDidUpdate } from \"@better-hooks/lifecycle\";\nimport { useCallback, useRef, useSyncExternalStore } from \"react\";\n\nimport type { UseSocketStateType, UseSocketStateProps } from \"helpers\";\n\nexport const useSocketState = <DataType, Socket extends SocketInstance>(\n socket: Socket,\n { dependencyTracking }: UseSocketStateProps,\n) => {\n const onDisconnectCallback = useRef<null | VoidFunction>(null);\n const onErrorCallback = useRef<null | ((event: any) => void)>(null);\n const onConnectedCallback = useRef<null | VoidFunction>(null);\n const onConnectingCallback = useRef<null | VoidFunction>(null);\n const onReconnectingCallback = useRef<null | ((data: { attempts: number }) => void)>(null);\n const onReconnectingFailedCallback = useRef<null | ((data: { attempts: number }) => void)>(null);\n\n const state = useRef<UseSocketStateType<Socket, DataType>>({\n data: null,\n extra: null,\n connected: socket.adapter.connected,\n connecting: socket.adapter.connecting,\n timestamp: null,\n });\n const renderKeys = useRef<Array<keyof UseSocketStateType<Socket, DataType>>>([]);\n\n // ******************\n // useSyncExternalStore\n // ******************\n\n const versionRef = useRef(0);\n const listenerRef = useRef<(() => void) | null>(null);\n\n const subscribe = useCallback((listener: () => void) => {\n listenerRef.current = listener;\n return () => {\n listenerRef.current = null;\n };\n }, []);\n\n const getSnapshot = useCallback(() => versionRef.current, []);\n\n useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n const emitChange = () => {\n versionRef.current += 1;\n listenerRef.current?.();\n };\n\n // ******************\n // Dependency Tracking\n // ******************\n\n const renderKeyTrigger = (keys: Array<keyof UseSocketStateType<Socket, DataType>>) => {\n const shouldRerender = renderKeys.current.some((renderKey) => keys.includes(renderKey));\n if (shouldRerender) emitChange();\n };\n\n const setRenderKey = (renderKey: keyof UseSocketStateType<Socket, DataType>) => {\n if (!renderKeys.current.includes(renderKey)) {\n renderKeys.current.push(renderKey);\n }\n };\n\n // ******************\n // Turn off dependency tracking\n // ******************\n\n useDidUpdate(\n () => {\n const handleDependencyTracking = () => {\n if (!dependencyTracking) {\n Object.keys(state.current).forEach((key) => setRenderKey(key as Parameters<typeof setRenderKey>[0]));\n }\n };\n\n handleDependencyTracking();\n },\n [dependencyTracking],\n true,\n );\n\n // ******************\n // Actions\n // ******************\n\n const actions = {\n setData: (data: DataType | null) => {\n state.current.data = data;\n renderKeyTrigger([\"data\"]);\n },\n setExtra: (extra: ExtractSocketExtraType<Socket> | null) => {\n state.current.extra = extra;\n renderKeyTrigger([\"extra\"]);\n },\n setConnected: (connected: boolean) => {\n state.current.connected = connected;\n renderKeyTrigger([\"connected\"]);\n },\n setConnecting: (connecting: boolean) => {\n state.current.connecting = connecting;\n renderKeyTrigger([\"connecting\"]);\n },\n setTimestamp: (timestamp: number | null) => {\n state.current.timestamp = timestamp;\n renderKeyTrigger([\"timestamp\"]);\n },\n clearState: () => {\n state.current = {\n data: null,\n extra: null,\n connected: false,\n connecting: false,\n timestamp: null,\n };\n renderKeyTrigger(Object.keys(state.current) as (keyof UseSocketStateType<Socket, DataType>)[]);\n },\n };\n\n const callbacks = {\n onConnected: (callback: VoidFunction) => {\n onConnectedCallback.current = callback;\n },\n onDisconnected: (callback: VoidFunction) => {\n onDisconnectCallback.current = callback;\n },\n onError: <ErrorType = Event>(callback: (event: ErrorType) => void) => {\n onErrorCallback.current = callback;\n },\n onConnecting: (callback: VoidFunction) => {\n onConnectingCallback.current = callback;\n },\n onReconnecting: (callback: (data: { attempts: number }) => void) => {\n onReconnectingCallback.current = callback;\n },\n onReconnectingFailed: (callback: (data: { attempts: number }) => void) => {\n onReconnectingFailedCallback.current = callback;\n },\n };\n\n // ******************\n // Hook to events\n // ******************\n\n useDidMount(() => {\n const umountOnError = socket.events.onError((event) => {\n onErrorCallback.current?.(event);\n });\n const umountOnConnecting = socket.events.onConnecting(({ connecting }) => {\n actions.setConnecting(connecting);\n onConnectingCallback.current?.();\n });\n const umountOnOpen = socket.events.onConnected(() => {\n actions.setConnected(true);\n onConnectedCallback.current?.();\n });\n const umountOnClose = socket.events.onDisconnected(() => {\n actions.setConnected(false);\n onDisconnectCallback.current?.();\n });\n const umountOnReconnecting = socket.events.onReconnecting(({ attempts }) => {\n onReconnectingCallback.current?.({ attempts });\n });\n const umountOnReconnectingFailed = socket.events.onReconnectingFailed(({ attempts }) => {\n onReconnectingFailedCallback.current?.({ attempts });\n });\n\n return () => {\n umountOnError();\n umountOnConnecting();\n umountOnOpen();\n umountOnClose();\n umountOnReconnecting();\n umountOnReconnectingFailed();\n };\n });\n\n return [state.current, actions, callbacks, { setRenderKey }] as const;\n};\n","import type { SocketInstance } from \"@hyper-fetch/sockets\";\n\nimport type { UseSocketStateType } from \"./use-socket-state.types\";\n\nexport const initialSocketState: UseSocketStateType<SocketInstance> = {\n data: null,\n extra: null,\n connected: false,\n connecting: false,\n timestamp: null,\n};\n","import React, { useContext, useMemo, useState } from \"react\";\n\nimport type { ProviderProps, ProviderValueType } from \"./provider.types\";\n\nconst ConfigContext = React.createContext<ProviderValueType>({\n config: {},\n setConfig: () => null,\n});\n\n/**\n * Provider with configuration for hooks\n * @param options\n * @returns\n */\nexport const Provider = ({ children, config }: ProviderProps) => {\n const [currentConfig, setConfig] = useState(config || {});\n\n const value = useMemo(() => {\n const contextValue: ProviderValueType = { config: currentConfig, setConfig };\n return contextValue;\n }, [currentConfig]);\n\n return <ConfigContext.Provider value={value}>{children}</ConfigContext.Provider>;\n};\n\n/**\n * Hook to allow reading current context config\n * @returns\n */\nexport const useProvider = (): ProviderValueType => {\n const config = useContext(ConfigContext);\n return config;\n};\n","import { useDidUpdate, useDidMount, useWillUnmount } from \"@better-hooks/lifecycle\";\nimport { useDebounce, useThrottle } from \"@better-hooks/performance\";\nimport type {\n RequestInstance,\n ExtractAdapterStatusType,\n ExtractAdapterType,\n ExtractAdapterExtraType,\n} from \"@hyper-fetch/core\";\nimport { scopeKey } from \"@hyper-fetch/core\";\nimport { useRef } from \"react\";\n\nimport { useRequestEvents, useTrackedState } from \"helpers\";\nimport type { UseFetchOptionsType, UseFetchReturnType } from \"hooks/use-fetch\";\nimport { useFetchDefaultOptions } from \"hooks/use-fetch\";\nimport { useProvider } from \"provider\";\nimport { createTrackedProxy, getBounceData } from \"utils\";\n\ntype SuspenseEntry = { promise: Promise<void>; resolve: () => void; cleanup: () => void };\nconst suspensePromiseMap = new Map<string, SuspenseEntry>();\nconst suspenseResultMap = new Map<string, { data: any; error: any; status: any; extra: any; success: boolean }>();\n\n/**\n * This hook aims to retrieve data from the server. It automatically fetches on mount and\n * refetches based on dependencies, with support for caching, polling, and suspense.\n * @param request Request instance\n * @param options Hook options\n * @returns\n */\nexport const useFetch = <R extends RequestInstance>(\n request: R,\n options?: UseFetchOptionsType<R>,\n): UseFetchReturnType<R> => {\n // Build the configuration options\n const { config: globalConfig } = useProvider();\n const {\n suspense,\n dependencies,\n disabled,\n dependencyTracking,\n revalidate,\n initialResponse,\n keepPreviousData,\n refresh,\n refreshTime,\n refetchBlurred,\n refetchOnBlur,\n refetchOnFocus,\n refetchOnReconnect,\n bounce,\n bounceType,\n bounceTime,\n bounceTimeout,\n deepCompare,\n } = {\n ...useFetchDefaultOptions,\n ...globalConfig.useFetchConfig,\n ...options,\n };\n\n const updateKey = JSON.stringify(request.toJSON());\n const requestDebounce = useDebounce({ delay: bounceTime });\n const requestThrottle = useThrottle({ interval: bounceTime, timeout: bounceTimeout });\n const refreshDebounce = useDebounce({ delay: refreshTime });\n\n const { cacheKey, queryKey, client } = request;\n const { cache, fetchDispatcher: dispatcher, appManager, loggerManager } = client;\n\n const ignoreReact18DoubleRender = useRef(true);\n