import type { Ref } from 'vue' interface UseAsyncActionOptions { isLoading: Ref error: Ref } interface RunOptions { /** Fallback message shown when the error has no server-provided detail */ errorMessage: string /** Whether to re-throw after recording the error (default true) */ rethrow?: boolean } /** * Wraps the isLoading/error/try-catch-finally shape shared by most store actions: * set loading, clear error, run fn, extract a message from an axios-shaped or * plain Error on failure, log it, optionally re-throw, and always clear loading. */ export function useAsyncAction({ isLoading, error }: UseAsyncActionOptions) { // Overloads so callers that don't pass `rethrow: false` keep a non-optional return type function run(fn: () => Promise, options: RunOptions & { rethrow?: true }): Promise function run(fn: () => Promise, options: RunOptions & { rethrow: false }): Promise async function run(fn: () => Promise, options: RunOptions): Promise { const { errorMessage, rethrow = true } = options try { isLoading.value = true error.value = null return await fn() } catch (err: any) { error.value = err?.response?.data?.detail || (err instanceof Error ? err.message : errorMessage) console.error(errorMessage, err) if (rethrow) throw err return undefined } finally { isLoading.value = false } } return { run } }