/** * Shared Query Patterns * * Consistent patterns for TanStack Query across all features * * USAGE GUIDELINES: * - Always use DISABLED_QUERY_KEY for disabled queries * - Always use STALE_TIMES constants for staleTime configuration * - Use createRetryLogic() for consistent retry behavior across the app * - Never hardcode stale times directly in hooks */ // Consistent disabled query key - use when query should not execute export const DISABLED_QUERY_KEY = ["disabled"] as const; // Consistent stale times by update frequency // Use these to ensure predictable caching behavior across the app export const STALE_TIMES = { instant: 0, // Always fresh - for real-time data like active progress realtime: 3_000, // 3 seconds - for near real-time updates frequent: 5_000, // 5 seconds - for frequently changing data normal: 30_000, // 30 seconds - standard cache time for most data rare: 300_000, // 5 minutes - for rarely changing configuration static: Infinity, // Never stale - for static data like settings } as const; // Re-export commonly used TanStack Query types for convenience export type { QueryKey, QueryOptions } from "@tanstack/react-query"; /** * Extract HTTP status code from various error objects * Handles different client libraries and error structures */ function getErrorStatus(error: unknown): number | undefined { if (!error || typeof error !== "object") return undefined; const anyErr = error as any; // Check common status properties in order of likelihood if (typeof anyErr.statusCode === "number") return anyErr.statusCode; // APIServiceError if (typeof anyErr.status === "number") return anyErr.status; // fetch Response if (typeof anyErr.response?.status === "number") return anyErr.response.status; // axios return undefined; } /** * Check if error is an abort/cancel operation that shouldn't be retried */ function isAbortError(error: unknown): boolean { if (!error || typeof error !== "object") return false; const anyErr = error as any; return anyErr?.name === "AbortError" || anyErr?.code === "ERR_CANCELED"; } /** * Unified retry logic for TanStack Query * - No retries on 4xx client errors (permanent failures) * - No retries on abort/cancel operations * - Configurable retry count for other errors */ export function createRetryLogic(maxRetries: number = 2) { return (failureCount: number, error: unknown) => { // Don't retry aborted operations if (isAbortError(error)) return false; // Don't retry 4xx client errors (400-499) const status = getErrorStatus(error); if (status && status >= 400 && status < 500) return false; // Retry up to maxRetries for other errors (5xx, network, etc) return failureCount < maxRetries; }; }