UNPKG

@hyper-fetch/react

Version:

React hooks and utils for the hyper-fetch

1,434 lines (1,411 loc) 52.6 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 { useRef as useRef4 } from "react"; import { useDidUpdate as useDidUpdate3, useDidMount as useDidMount2, useWillUnmount as useWillUnmount2 } from "@better-hooks/lifecycle"; import { useDebounce, useThrottle } from "@better-hooks/performance"; import { Request, getRequestKey } from "@hyper-fetch/core"; // src/helpers/use-request-events/use-request-events.hooks.ts import { useRef } from "react"; import { useWillUnmount } from "@better-hooks/lifecycle"; var useRequestEvents = ({ request, dispatcher, logger, actions, setCacheData, getIsDataProcessing }) => { const { 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 = (cmd, response, details) => { var _a, _b, _c, _d, _e; const { data, error, success } = response; const { isOffline, isCanceled } = details; if (request.offline && isOffline && !success) { logger.debug("Performing offline error callback", { data, details }); (_a = onOfflineErrorCallback.current) == null ? void 0 : _a.call(onOfflineErrorCallback, { response: error, request: cmd, details }); } else if (isCanceled) { logger.debug("Performing abort callback", { data, details }); (_b = onAbortCallback.current) == null ? void 0 : _b.call(onAbortCallback, { response: error, request: cmd, details }); } else if (success) { logger.debug("Performing success callback", { data, details }); (_c = onSuccessCallback.current) == null ? void 0 : _c.call(onSuccessCallback, { response: data, request: cmd, details }); } else { logger.debug("Performing error callback", { data, details }); (_d = onErrorCallback.current) == null ? void 0 : _d.call(onErrorCallback, { response: error, request: cmd, details }); } (_e = onFinishedCallback.current) == null ? void 0 : _e.call(onFinishedCallback, { response, request: cmd, details }); }; const handleGetLoadingEvent = (req) => { return ({ loading }) => { const isProcessing = getIsDataProcessing(req.cacheKey); if (isProcessing) return; const canDisableLoading = !loading && !dispatcher.hasRunningRequests(req.queueKey); if (loading || canDisableLoading) { actions.setLoading(loading, false); } }; }; const handleDownloadProgress = (progress, details) => { var _a; (_a = onDownloadProgressCallback.current) == null ? void 0 : _a.call(onDownloadProgressCallback, progress, details); }; const handleUploadProgress = (progress, details) => { var _a; (_a = onUploadProgressCallback.current) == null ? void 0 : _a.call(onUploadProgressCallback, progress, details); }; const handleRequestStart = (cmd) => { return (details) => { var _a; (_a = onRequestStartCallback.current) == null ? void 0 : _a.call(onRequestStartCallback, { request: cmd, details }); }; }; const handleResponseStart = (cmd) => { return (details) => { var _a; (_a = onResponseStartCallback.current) == null ? void 0 : _a.call(onResponseStartCallback, { request: cmd, details }); }; }; const handleResponse = (req) => { return (response, details) => { const data = responseMapper ? responseMapper(response) : response; if (data instanceof Promise) { return (() => __async(void 0, null, function* () { const promiseData = yield data; handleResponseCallbacks(req, promiseData, details); }))(); } return handleResponseCallbacks(req, data, details); }; }; 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.onLoading(req.queueKey, handleGetLoadingEvent(req)); const getResponseUnmount = cache.events.onData( req.cacheKey, setCacheData ); const unmount = () => { loadingUnmount(); getResponseUnmount(); }; clearCacheDataListener(); dataEvents.current = { unmount }; return unmount; }; const addLifecycleListeners = (req, requestId) => { if (!requestId) { clearLifecycleListeners(); const { queueKey, cacheKey } = req; const requestStartUnmount2 = requestManager.events.onRequestStart(queueKey, handleRequestStart(req)); const responseStartUnmount2 = requestManager.events.onResponseStart(queueKey, handleResponseStart(req)); const uploadUnmount2 = requestManager.events.onUploadProgress(queueKey, handleUploadProgress); const downloadUnmount2 = requestManager.events.onDownloadProgress(queueKey, handleDownloadProgress); const responseUnmount2 = requestManager.events.onResponse(cacheKey, handleResponse(req)); const unmount2 = () => { downloadUnmount2(); uploadUnmount2(); requestStartUnmount2(); responseStartUnmount2(); responseUnmount2(); }; lifecycleEvents.current.set(queueKey, { unmount: unmount2 }); return unmount2; } const requestRemove = requestManager.events.onRemoveById(requestId, handleRemove); const requestStartUnmount = requestManager.events.onRequestStartById(requestId, handleRequestStart(req)); const responseStartUnmount = requestManager.events.onResponseStartById(requestId, handleResponseStart(req)); const responseUnmount = requestManager.events.onResponseById(requestId, handleResponse(req)); 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.getAllRunningRequest(); requests.forEach((requestData) => { if (requestData.request.abortKey === abortKey) { dispatcher.delete(requestData.request.queueKey, 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: null, success: null, loading: false, retries: 0, timestamp: null }; // src/helpers/use-tracked-state/use-tracked-state.hooks.ts import { useRef as useRef2 } from "react"; import { useDidUpdate, useForceUpdate } from "@better-hooks/lifecycle"; // 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 (firstValue === null && secondValue === null) return true; 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, timestamp: +new Date(), isCanceled: false, isOffline: false }, details); }; var isStaleCacheData = (cacheTime, cacheTimestamp) => { if (!cacheTimestamp) return true; return +new Date() > +cacheTimestamp + cacheTime; }; var getValidCacheData = (request, initialData, cacheData) => { const isStale = isStaleCacheData(request.cacheTime, cacheData == null ? void 0 : cacheData.timestamp); if (!isStale && cacheData) { return cacheData; } if (initialData) { return __spreadProps(__spreadValues(__spreadValues({ data: null, error: null, status: null, success: null, extra: null }, initialData || {}), getDetailsState()), { cacheTime: 1e3, clearKey: request.client.cache.clearKey, garbageCollection: request.garbageCollection }); } 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, responseMapper } = request; const { cache } = client; const cacheData = cache.get(cacheKey); const cacheState = getValidCacheData(request, initialResponse, cacheData); const initialLoading = getIsInitiallyLoading({ queryKey: request.queueKey, dispatcher, disabled, revalidate, hasState: !!cacheState }); if (cacheState) { const mappedData = responseMapper ? 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, retries: cacheState.retries, timestamp: getTimestamp(cacheState.timestamp), loading: initialLoading }; } return { data: initialState.data, error: initialState.error, status: initialState.status, success: initialState.success, extra: request.client.defaultExtra, retries: initialState.retries, timestamp: getTimestamp(initialState.timestamp), loading: initialLoading }; }; // src/helpers/use-tracked-state/use-tracked-state.hooks.ts var useTrackedState = ({ request, dispatcher, initialData, deepCompare, dependencyTracking, defaultCacheEmitting = true, disabled, revalidate }) => { const { client, cacheKey, queueKey, cacheTime, responseMapper } = request; const { cache, requestManager } = client; const forceUpdate = useForceUpdate(); const state = useRef2( getInitialState({ initialResponse: initialData, dispatcher, request, disabled }) ); const renderKeys = useRef2([]); const isProcessingData = useRef2(""); const getStaleStatus = () => { const cacheData = cache.get(cacheKey); return !cacheData || isStaleCacheData(cacheTime, cacheData == null ? void 0 : cacheData.timestamp); }; 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( () => { state.current.loading = dispatcher.hasRunningRequests(queueKey); const cacheData = cache.get(cacheKey); const cacheState = getValidCacheData(request, initialData, cacheData); state.current.loading = getIsInitiallyLoading({ queryKey: request.queueKey, dispatcher, disabled, revalidate, hasState: !!cacheState }); const hasInitialState = isEqual(initialData == null ? void 0 : initialData.data, state.current.data); const hasState = !!(state.current.data || state.current.error) && !hasInitialState; const shouldLoadInitialCache = !hasState && !!state.current.data; const shouldRemovePreviousData = hasState && !state.current.data; if (cacheState || shouldLoadInitialCache || shouldRemovePreviousData) { setCacheData(cacheState); } }, [cacheKey, queueKey], 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, timestamp: new Date(cacheData.timestamp), loading: dispatcher.hasRunningRequests(queueKey) }; 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 = responseMapper ? responseMapper(cacheData) : cacheData; if (data instanceof Promise) { return (() => __async(void 0, 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, emitToCache = defaultCacheEmitting) => { if (emitToCache) { const currentState = state.current; cache.set(request, __spreadProps(__spreadValues(__spreadValues({}, currentState), getDetailsState(state.current)), { success: true, data })); } else { state.current.data = data; renderKeyTrigger(["data"]); } }, setError: (error, emitToCache = defaultCacheEmitting) => { if (emitToCache) { const currentState = state.current; cache.set(request, __spreadProps(__spreadValues(__spreadValues({}, currentState), getDetailsState(state.current)), { success: false, error })); } else { state.current.error = error; renderKeyTrigger(["error"]); } }, setLoading: (loading, emitToHooks = true) => { if (emitToHooks) { requestManager.events.emitLoading(queueKey, "", { queueKey, requestId: "", loading, isRetry: false, isOffline: false }); } else if (loading !== state.current.loading) { state.current.loading = loading; renderKeyTrigger(["loading"]); } }, setStatus: (status, emitToCache = defaultCacheEmitting) => { if (emitToCache) { const currentState = state.current; cache.set(request, __spreadProps(__spreadValues(__spreadValues({}, currentState), getDetailsState(state.current)), { status })); } else { state.current.status = status; renderKeyTrigger(["status"]); } }, setSuccess: (success, emitToCache = defaultCacheEmitting) => { if (emitToCache) { const currentState = state.current; cache.set(request, __spreadProps(__spreadValues(__spreadValues({}, currentState), getDetailsState(state.current)), { success })); } else { state.current.success = success; renderKeyTrigger(["success"]); } }, setExtra: (extra, emitToCache = defaultCacheEmitting) => { if (emitToCache) { const currentState = state.current; cache.set(request, __spreadProps(__spreadValues(__spreadValues({}, currentState), getDetailsState(state.current)), { extra })); } else { state.current.extra = extra; renderKeyTrigger(["extra"]); } }, setRetries: (retries, emitToCache = defaultCacheEmitting) => { if (emitToCache) { const currentState = state.current; cache.set(request, __spreadValues(__spreadValues({}, currentState), getDetailsState(state.current, { retries }))); } else { state.current.retries = retries; renderKeyTrigger(["retries"]); } }, setTimestamp: (timestamp, emitToCache = defaultCacheEmitting) => { if (emitToCache) { const currentState = state.current; cache.set(request, __spreadValues(__spreadValues({}, currentState), getDetailsState(state.current, { timestamp: +timestamp }))); } else { state.current.timestamp = timestamp; renderKeyTrigger(["timestamp"]); } } }; return [state.current, actions, { setRenderKey, setCacheData, getStaleStatus, getIsDataProcessing }]; }; // src/helpers/use-socket-state/use-socket-state.hooks.ts import { useRef as useRef3 } from "react"; import { useDidMount, useDidUpdate as useDidUpdate2, useForceUpdate as useForceUpdate2 } from "@better-hooks/lifecycle"; var useSocketState = (socket, { dependencyTracking }) => { const forceUpdate = useForceUpdate2(); const onOpenCallback = useRef3(null); const onCloseCallback = useRef3(null); const onErrorCallback = useRef3(null); const onConnectingCallback = useRef3(null); const onReconnectingCallback = useRef3(null); const onReconnectingStopCallback = 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( () => { 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"]); }, 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 = { onOpen: (callback) => { onOpenCallback.current = callback; }, onClose: (callback) => { onCloseCallback.current = callback; }, onError: (callback) => { onErrorCallback.current = callback; }, onConnecting: (callback) => { onConnectingCallback.current = callback; }, onReconnecting: (callback) => { onReconnectingCallback.current = callback; }, onReconnectingStop: (callback) => { onReconnectingStopCallback.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(() => { var _a; actions.setConnecting(true); (_a = onConnectingCallback.current) == null ? void 0 : _a.call(onConnectingCallback); }); const umountOnOpen = socket.events.onOpen(() => { var _a; actions.setConnected(true); (_a = onOpenCallback.current) == null ? void 0 : _a.call(onOpenCallback); }); const umountOnClose = socket.events.onClose(() => { var _a; actions.setConnected(false); (_a = onCloseCallback.current) == null ? void 0 : _a.call(onCloseCallback); }); const umountOnReconnecting = socket.events.onReconnecting((attempts) => { var _a; (_a = onReconnectingCallback.current) == null ? void 0 : _a.call(onReconnectingCallback, attempts); }); const umountOnReconnectingStop = socket.events.onReconnectingStop((attempts) => { var _a; (_a = onReconnectingStopCallback.current) == null ? void 0 : _a.call(onReconnectingStopCallback, attempts); }); return () => { umountOnError(); umountOnConnecting(); umountOnOpen(); umountOnClose(); umountOnReconnecting(); umountOnReconnectingStop(); }; }); return [state.current, actions, callbacks, { setRenderKey }]; }; // src/helpers/use-socket-state/use-socket-state.constants.ts var initialSocketState = { data: null, connected: false, connecting: false, timestamp: null }; // src/config-provider/config-provider.tsx import React, { useContext, useMemo, useState } from "react"; import { jsx } from "react/jsx-runtime"; var ConfigContext = React.createContext([{}, () => null]); var ConfigProvider = ({ children, config }) => { const [currentConfig, setConfig] = useState(config || {}); const value = useMemo(() => { const contextValue = [currentConfig, setConfig]; return contextValue; }, [currentConfig]); return /* @__PURE__ */ jsx(ConfigContext.Provider, { value, children }); }; var useConfigProvider = () => { const config = useContext(ConfigContext); return config; }; // src/hooks/use-fetch/use-fetch.hooks.ts var useFetch = (request, options = useFetchDefaultOptions) => { const [globalConfig] = useConfigProvider(); const { dependencies = useFetchDefaultOptions.dependencies, disabled = useFetchDefaultOptions.disabled, dependencyTracking = useFetchDefaultOptions.dependencyTracking, revalidate = useFetchDefaultOptions.revalidate, initialData = useFetchDefaultOptions.initialData, refresh = useFetchDefaultOptions.refresh, refreshTime = useFetchDefaultOptions.refreshTime, refetchBlurred = useFetchDefaultOptions.refetchBlurred, refetchOnBlur = useFetchDefaultOptions.refetchOnBlur, refetchOnFocus = useFetchDefaultOptions.refetchOnFocus, refetchOnReconnect = useFetchDefaultOptions.refetchOnReconnect, bounce = useFetchDefaultOptions.bounce, bounceType = useFetchDefaultOptions.bounceType, bounceTime = useFetchDefaultOptions.bounceTime, bounceTimeout = useFetchDefaultOptions.bounceTime, deepCompare = useFetchDefaultOptions.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, queueKey, client } = request; const { cache, fetchDispatcher: dispatcher, appManager, loggerManager } = client; const ignoreReact18DoubleRender = useRef4(true); const logger = useRef4(loggerManager.init("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, initialData, 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(`Fetching data`); dispatcher.add(request); } else { logger.debug(`Cannot add to fetch queue`, { disabled }); } }; function handleRefresh() { if (!refresh) { refreshDebounce.reset(); return; } refreshDebounce.debounce(() => { const isBlurred = !appManager.isFocused; const isFetching = dispatcher.hasRunningRequests(request.queueKey); const isQueued = dispatcher.getIsActiveQueue(request.queueKey); const isActive = isFetching || isQueued; const canRefreshBlurred = isBlurred && refetchBlurred && !isActive; const canRefreshFocused = !isBlurred && !isActive; if (canRefreshBlurred || canRefreshFocused) { handleFetch(); logger.debug(`Performing refresh request`); } handleRefresh(); }); } const handleInvalidation = (invalidateKey) => { if (invalidateKey && invalidateKey instanceof Request) { cache.invalidate(getRequestKey(invalidateKey)); } else if (invalidateKey && !(invalidateKey instanceof Request)) { cache.invalidate(invalidateKey); } }; const refetch = (invalidateKey) => { if (invalidateKey && Array.isArray(invalidateKey)) { invalidateKey.forEach(handleInvalidation); } else if (invalidateKey && !Array.isArray(invalidateKey)) { handleInvalidation(invalidateKey); } else { handleFetch(); handleRefresh(); } }; const getIsFetchingIdentity = () => { return dispatcher.getRunningRequests(queueKey).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(`Bounce request with ${bounceType}`, { queueKey, 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.onInvalidate(cacheKey, handleFetch); const unmount = () => { clearCacheDataListener(); focusUnmount(); blurUnmount(); onlineUnmount(); invalidateUnmount(); }; 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 timestamp() { setRenderKey("timestamp"); return state.timestamp; }, 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 { DateInterval } from "@hyper-fetch/core"; var useFetchDefaultOptions = { dependencies: [], disabled: false, dependencyTracking: true, revalidate: true, initialData: null, refresh: false, refreshTime: DateInterval.hour, refetchBlurred: true, refetchOnBlur: false, refetchOnFocus: false, refetchOnReconnect: false, bounce: false, bounceType: "debounce", bounceTime: 400, deepCompare: true }; // src/hooks/use-submit/use-submit.hooks.ts import { useMemo as useMemo2, useRef as useRef5 } from "react"; import { Request as Request2, getRequestKey as getRequestKey2, sendRequest } from "@hyper-fetch/core"; import { useDidMount as useDidMount3 } from "@better-hooks/lifecycle"; import { useDebounce as useDebounce2, useThrottle as useThrottle2 } from "@better-hooks/performance"; var useSubmit = (request, options = useSubmitDefaultOptions) => { const [globalConfig] = useConfigProvider(); const mergedOptions = useMemo2( () => __spreadValues(__spreadValues(__spreadValues({}, useSubmitDefaultOptions), globalConfig.useSubmitConfig), options), [globalConfig.useSubmitConfig, JSON.stringify(options), options.deepCompare] ); const { disabled, dependencyTracking, initialData, bounce, bounceType, bounceTime, deepCompare } = mergedOptions; const { client } = request; const { cache, submitDispatcher: dispatcher, loggerManager } = client; const logger = useRef5(loggerManager.init("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, initialData, 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(`Cannot submit request`, { disabled, submitOptions }); return Promise.resolve({ data: null, error: new Error("Cannot submit request. Option 'disabled' is enabled"), status: null, extra: request.client.defaultExtra }); } const triggerRequest = () => { addCacheDataListener(requestClone); return sendRequest(requestClone, __spreadProps(__spreadValues({ dispatcherType: "submit" }, submitOptions), { onSettle: (requestId, cmd) => { var _a; addLifecycleListeners(requestClone, requestId); (_a = submitOptions == null ? void 0 : submitOptions.onSettle) == null ? void 0 : _a.call(submitOptions, requestId, cmd); } })); }; return new Promise((resolve) => { const performSubmit = () => __async(void 0, null, function* () { logger.debug(`Submitting request`, { disabled, submitOptions }); if (bounce) { const bouncedResolve = bounceResolver.current; bounceResolver.current = (value) => { bouncedResolve(value); resolve(value); }; bounceFunction(() => __async(void 0, null, function* () { const callback = bounceResolver.current; bounceResolver.current = () => null; const value = yield triggerRequest(); callback(value); })); } else { const value = yield triggerRequest(); resolve(value); } }); performSubmit(); }); }; const handleInvalidation = (invalidateKey) => { if (invalidateKey && invalidateKey instanceof Request2) { cache.invalidate(getRequestKey2(invalidateKey)); } else if (invalidateKey && !(invalidateKey instanceof Request2)) { cache.invalidate(invalidateKey); } }; const refetch = (invalidateKey) => { if (!invalidateKey) return; if (invalidateKey && Array.isArray(invalidateKey)) { invalidateKey.forEach(handleInvalidation); } else if (invalidateKey && !Array.isArray(invalidateKey)) { handleInvalidation(invalidateKey); } }; 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 timestamp() { setRenderKey("timestamp"); return state.timestamp; }, abort: callbacks.abort }, actions), handlers), { bounce: getBounceData(bounceData), refetch }); }; // src/hooks/use-submit/use-submit.constants.ts var useSubmitDefaultOptions = { disabled: false, dependencyTracking: true, initialData: null, bounce: false, bounceType: "debounce", bounceTime: 400, deepCompare: true }; // src/hooks/use-queue/use-queue.hooks.ts import { useState as useState2, useRef as useRef6 } from "react"; import { getRequestDispatcher } from "@hyper-fetch/core"; import { useDidMount as useDidMount4, useDidUpdate as useDidUpdate4 } from "@better-hooks/lifecycle"; var useQueue = (request, options = useQueueDefaultOptions) => { const [globalConfig] = useConfigProvider(); const { queueType = "auto" } = __spreadValues(__spreadValues(__spreadValues({}, useQueueDefaultOptions), globalConfig.useQueueConfig), options); const { abortKey, queueKey, client } = request; const { requestManager } = client; const [dispatcher] = getRequestDispatcher(request, queueType); const unmountCallbacks = useRef6(null); const [stopped, setStopped] = useState2(false); const [requests, setRequests] = useState2([]); const createRequestsArray = (queueElements) => { return queueElements.map((req) => __spreadProps(__spreadValues({}, req), { stopRequest: () => dispatcher.stopRequest(queueKey, req.requestId), startRequest: () => dispatcher.startRequest(queueKey, req.requestId), deleteRequest: () => dispatcher.delete(queueKey, req.requestId, abortKey) })); }; const mergePayloadType = (requestId, data) => { setRequests((prev) => prev.map((el) => el.requestId === requestId ? __spreadValues(__spreadValues({}, el), data) : el)); }; const getInitialState2 = () => { const requestQueue = dispatcher.getQueue(queueKey); setStopped(requestQueue.stopped); setRequests(createRequestsArray(requestQueue.requests)); }; const updateQueueState = (values) => { setStopped(values.stopped); setRequests(createRequestsArray(values.requests)); }; const mountEvents = () => { var _a; const unmountChange = dispatcher.events.onQueueChange(queueKey, updateQueueState); const unmountStatus = dispatcher.events.onQueueStatus(queueKey, updateQueueState); const unmountDownload = requestManager.events.onDownloadProgress(queueKey, (progress, { requestId }) => { mergePayloadType(requestId, { downloading: progress }); }); const unmountUpload = requestManager.events.onUploadProgress(queueKey, (progress, { requestId }) => { mergePayloadType(requestId, { uploading: progress }); }); const unmount = () => { unmountStatus(); unmountChange(); unmountDownload(); unmountUpload(); }; (_a = unmountCallbacks.current) == null ? void 0 : _a.call(unmountCallbacks); unmountCallbacks.current = unmount; return unmount; }; useDidMount4(getInitialState2); useDidUpdate4(mountEvents, [stopped, requests, setRequests, setStopped], true); return { stopped, requests, stop: () => dispatcher.stop(queueKey), pause: () => dispatcher.pause(queueKey), start: () => dispatcher.start(queueKey) }; }; // src/hooks/use-queue/use-queue.constants.ts var useQueueDefaultOptions = { queueType: "auto" }; // src/hooks/use-cache/use-cache.hooks.ts import { useRef as useRef7 } from "react"; import { useDidUpdate as useDidUpdate5 } from "@better-hooks/lifecycle"; import { getRequestDispatcher as getRequestDispatcher2, Request as Request3, getRequestKey as getRequestKey3 } from "@hyper-fetch/core"; var useCache = (request, options = useCacheDefaultOptions) => { const { cacheKey, client } = request; const { cache, loggerManager } = client; const logger = useRef7(loggerManager.init("useCache")).current; const [dispatcher] = getRequestDispatcher2(request); const updateKey = JSON.stringify(request.toJSON()); const [globalConfig] = useConfigProvider(); const { dependencyTracking, initialData, deepCompare } = __spreadValues(__spreadValues(__spreadValues({}, useCacheDefaultOptions), globalConfig.useCacheConfig), options); const [state, actions, { setRenderKey, setCacheData, getIsDataProcessing }] = useTrackedState({ logger, request, dispatcher, initialData, deepCompare, dependencyTracking }); const [callbacks, listeners] = useRequestEvents({ logger, actions, request, dispatcher, setCacheData, getIsDataProcessing }); const { addCacheDataListener, addLifecycleListeners } = listeners; const handleMountEvents = () => { addCacheDataListener(request); addLifecycleListeners(request); }; useDidUpdate5(handleMountEvents, [updateKey], true); const refetch = (invalidateKey) => { if (invalidateKey instanceof Request3) { cache.invalidate(getRequestKey3(invalidateKey, true)); } else if (invalidateKey) { cache.invalidate(invalidateKey); } else { cache.invalidate(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 timestamp() { setRenderKey("timestamp"); return state.timestamp; }, onCacheError: callbacks.onError, onCacheSuccess: callbacks.onSuccess, onCacheChange: callbacks.onFinished }, actions), { refetch }); }; // src/hooks/use-cache/use-cache.constants.ts var useCacheDefaultOptions = { dependencyTracking: true, initialData: null, deepCompare: true }; // src/hooks/use-listener/use-listener.hooks.ts import { useRef as useRef8 } from "react"; import { useDidUpdate as useDidUpdate6, useWillUnmount as useWillUnmount3 } from "@better-hooks/lifecycle"; var useListener = (listener, options) => { const [globalConfig] = useConfigProvider(); const { dependencyTracking } = __spreadValues(__spreadValues({}, globalConfig.useListener), options); const [state, actions, callbacks, { setRenderKey }] = useSocketState(listener.socket, { dependencyTracking }); const removeListenerRef = useRef8(null); const onEventCallback = useRef8(null); const stopListener = () => { var _a; (_a = removeListenerRef.current) == null ? void 0 : _a.call(removeListenerRef); }; const listen = () => { stopListener(); removeListenerRef.current = listener.listen({ callback: ({ data, extra }) => { var _a; (_a = onEventCallback.current) == null ? void 0 : _a.call(onEventCallback, { data, extra }); actions.setData(data); actions.setTimestamp(+new Date()); } }); }; const additionalCallbacks = { onEvent: (callback) => { onEventCallback.current = callback; } }; useDidUpdate6( () => { listen(); }, [listener.params, JSON.stringify(listener.options)], true ); useWillUnmount3(() => { stopListener(); }); return __spreadProps(__spreadValues(__spreadValues(__spreadValues({ get data() { setRenderKey("data"); return state.data; }, 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 { useRef as useRef9 } from "react"; var useEmitter = (emitter, options) => { const [globalConfig] = useConfigProvider(); const { dependencyTracking } = __spreadValues(__spreadValues({}, globalConfig.useEmitter), options); const onEventCallback = useRef9(null); const [state, actions, callbacks, { setRenderKey }] = useSocketState(emitter.socket, { dependencyTracking }); const additionalCallbacks = { onEvent: (callback) => { onEventCallback.current = callback; } }; const emit = (emitOptions) => { var _a; actions.setTimestamp(+new Date()); (_a = onEventCallback.current) == null ? void 0 : _a.call(onEventCallback, emitter); return emitter.emit(emitOptions); }; return __spreadProps(__spreadValues(__spreadValues(__spreadValues({ get connected() { setRenderKey("connected"); return state.connected; }, get connecting() { setRenderKey("connecting"); return state.connecting; }, get timestamp() { setRenderKey("timestamp"); return state.timestamp; } }, actions), callbacks), additionalCallbacks), { emit, reconnect: emitter.socket.reconnect }); }; // src/hooks/use-event-messages/use-event-messages.hooks.ts import { useRef as useRef10 } from "react"; import { useDidUpdate as useDidUpdate7 } from "@better-hooks/lifecycle"; var useEventMessages = (socket, options) => { const [globalConfig] = useConfigProvider(); const { dependencyTracking = false, filter } = __spreadValues(__spreadValues({}, globalConfig.useEventMessages), options); const onEventCallback = useRef10(null); const [state, actions, callbacks, { setRenderKey }] = useSocketState(socket, { dependencyTracking }); useDidUpdate7( () => { const unmountListener = socket.events.onListenerEvent(({ endpoint, data, extra }) => { var _a; const filterFn = typeof filter === "function" ? () => filter(endpoint, data) : () => filter.includes(endpoint); const isFiltered = filter ?