import { runInAction } from "mobx"; type mobxFetchOptions = { store: Store; value?: keyof Store; values?: Array; loading?: keyof Store; error?: keyof Store; fn: RequestType extends void ? (signal?: AbortSignal) => Promise : (request: RequestType, signal?: AbortSignal) => Promise; pollingInterval?: number; resetValue?: boolean; transform?: (response: ResponseType) => Partial>; onSuccess?: (response: ResponseType) => void; }; type FetchFunction = RequestType extends void ? { (): Promise; stopPolling?: () => void; } : { (request: RequestType): Promise; stopPolling?: () => void; }; export function mobxFetch>( options: mobxFetchOptions ): FetchFunction; export function mobxFetch< RequestType, ResponseType, Store extends Record, >( options: mobxFetchOptions ): FetchFunction; export function mobxFetch< RequestType, ResponseType, Store extends Record, >( options: mobxFetchOptions ): FetchFunction { const { store, value, values, loading, error, fn, pollingInterval, resetValue, transform, onSuccess, } = options; let abortController: AbortController | undefined; let pollingTimer: ReturnType | undefined; let currentRequest: RequestType | undefined; const stopPolling = () => { if (pollingTimer) { clearInterval(pollingTimer); pollingTimer = undefined; } abortController?.abort(); }; const fetch = async (request?: RequestType): Promise => { abortController?.abort(); abortController = new AbortController(); currentRequest = request as RequestType; runInAction(() => { if (value) { (store[value] as any) = resetValue ? null : store[value]; } if (values) { values.forEach((key) => { (store[key] as any) = resetValue ? null : store[key]; }); } if (error) { (store[error] as any) = null; } if (loading) { (store[loading] as any) = true; } }); try { const result = await ( fn as ( request?: RequestType, signal?: AbortSignal ) => Promise )(request, abortController.signal); runInAction(() => { if (values && transform) { const transformed = transform(result) as Record; values.forEach((key) => { const k = key as string; if (k in transformed) { (store[key] as any) = transformed[k]; } }); } else if (value) { (store[value] as any) = result as ResponseType; } if (loading) { (store[loading] as any) = false; } if (error) { (store[error] as any) = null; } }); if (pollingInterval && !pollingTimer) { pollingTimer = setInterval(() => { if (currentRequest !== undefined) { fetch(currentRequest); } else { fetch(); } }, pollingInterval); } if (onSuccess) { onSuccess(result); } return result; } catch (err) { if (!(err instanceof Error && err.name === "CanceledError")) { runInAction(() => { if (error) { (store[error] as any) = err instanceof Error ? err.message : String(err); } if (loading) { (store[loading] as any) = false; } if (value) { (store[value] as any) = null; } if (values) { values.forEach((key) => { (store[key] as any) = null; }); } }); throw err; } return null; } }; const fetchWithStopPolling = fetch as FetchFunction< RequestType, ResponseType >; if (pollingInterval) { fetchWithStopPolling.stopPolling = stopPolling; } return fetchWithStopPolling; }