@hyper-fetch/react
Version:
React hooks and utils for the hyper-fetch
1,432 lines (1,410 loc) • 53.2 kB
JavaScript
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