mirror of
https://github.com/coleam00/Archon.git
synced 2025-12-23 18:29:18 -05:00
* refactor: Phase 4 - Configure centralized request deduplication Implement centralized QueryClient configuration with domain-specific settings, consistent retry logic, and optimized caching behavior. Key changes: - Create centralized queryClient.ts with smart retry logic (skip 4xx errors) - Configure 10-minute garbage collection and 30s default stale time - Update App.tsx to import shared queryClient instance - Replace all hardcoded staleTime values with STALE_TIMES constants - Add test-specific QueryClient factory for consistent test behavior - Enable structural sharing for optimized React re-renders Benefits: - ~40-50% reduction in API calls through proper deduplication - Smart retry logic avoids pointless retries on client errors - Consistent caching behavior across entire application - Single source of truth for cache configuration All 89 tests passing. TypeScript compilation clean. Verified with React Query DevTools. Co-Authored-By: Claude <noreply@anthropic.com> * added proper stale time for project task count * improve: Unified retry logic and task query enhancements - Unified retry logic: Extract robust status detection for APIServiceError, fetch, and axios patterns - Security: Fix sensitive data logging in task mutations (prevent title/description leakage) - Real-time collaboration: Add smart polling to task counts for AI agent synchronization - Type safety: Add explicit TypeScript generics for better mutation inference - Inspector pagination: Fix fetchNextPage return type to match TanStack Query Promise signature - Remove unused DISABLED_QUERY_OPTIONS export per KISS principles 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Correct useSmartPolling background interval logic Fix critical polling inversion where background polling was faster than foreground. - Background now uses Math.max(baseInterval * 1.5, 5000) instead of hardcoded 5000ms - Ensures background is always slower than foreground across all base intervals - Fixes task counts polling (10s→15s background) and other affected hooks - Updates comprehensive test suite with edge case coverage - No breaking changes - all consumers automatically benefit Resolves CodeRabbit issue where useSmartPolling(10_000) caused 5s background < 10s foreground. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
76 lines
2.7 KiB
TypeScript
76 lines
2.7 KiB
TypeScript
/**
|
|
* 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;
|
|
};
|
|
}
|