@dbs-portal/core-api
Version:
HTTP client and API utilities for DBS Portal
279 lines • 8.17 kB
JavaScript
/**
* Query utilities and helpers
*/
const { VITE_NODE_ENV: NODE_ENV } = import.meta.env;
/**
* Error handling utilities
*/
export const queryErrorUtils = {
/**
* Check if error is a network error
*/
isNetworkError: (error) => {
return error instanceof Error && error.message.includes('fetch');
},
/**
* Check if error is an API error with status
*/
isApiError: (error) => {
return (typeof error === 'object' &&
error !== null &&
'status' in error &&
typeof error.status === 'number');
},
/**
* Check if error is a client error (4xx)
*/
isClientError: (error) => {
if (!queryErrorUtils.isApiError(error))
return false;
const status = error.response?.status;
return typeof status === 'number' && status >= 400 && status < 500;
},
/**
* Check if error is a server error (5xx)
*/
isServerError: (error) => {
if (!queryErrorUtils.isApiError(error))
return false;
const status = error.response?.status;
return typeof status === 'number' && status >= 500;
},
/**
* Check if error is unauthorized (401)
*/
isUnauthorized: (error) => {
if (!queryErrorUtils.isApiError(error))
return false;
return error.response?.status === 401;
},
/**
* Check if error is forbidden (403)
*/
isForbidden: (error) => {
if (!queryErrorUtils.isApiError(error))
return false;
return error.response?.status === 403;
},
/**
* Check if error is not found (404)
*/
isNotFound: (error) => {
if (!queryErrorUtils.isApiError(error))
return false;
return error.response?.status === 404;
},
/**
* Get error message from various error types
*/
getErrorMessage: (error) => {
if (queryErrorUtils.isApiError(error)) {
const status = error.response?.status;
return error.message || `API Error: ${status || 'Unknown'}`;
}
if (error instanceof Error) {
return error.message;
}
if (typeof error === 'string') {
return error;
}
return 'An unknown error occurred';
},
};
/**
* Cache management utilities
*/
export const cacheUtils = {
/**
* Invalidate all queries matching a pattern
*/
invalidateByPattern: (queryClient, pattern) => {
return queryClient.invalidateQueries({
queryKey: pattern,
exact: false,
});
},
/**
* Remove all queries matching a pattern
*/
removeByPattern: (queryClient, pattern) => {
return queryClient.removeQueries({
queryKey: pattern,
exact: false,
});
},
/**
* Prefetch multiple queries
*/
prefetchMultiple: async (queryClient, queries) => {
const promises = queries.map(({ queryKey, queryFn, staleTime }) => queryClient.prefetchQuery({
queryKey,
queryFn,
...(staleTime !== undefined && { staleTime }),
}));
return Promise.allSettled(promises);
},
/**
* Get cache statistics
*/
getCacheStats: (queryClient) => {
const cache = queryClient.getQueryCache();
const queries = cache.getAll();
const stats = {
totalQueries: queries.length,
activeQueries: queries.filter(q => q.getObserversCount() > 0).length,
staleQueries: queries.filter(q => q.isStale()).length,
errorQueries: queries.filter(q => q.state.status === 'error').length,
loadingQueries: queries.filter(q => q.state.status === 'pending').length,
successQueries: queries.filter(q => q.state.status === 'success').length,
};
return stats;
},
/**
* Clear all cache data
*/
clearAll: (queryClient) => {
queryClient.clear();
},
/**
* Reset all queries to initial state
*/
resetAll: (queryClient) => {
return queryClient.resetQueries();
},
};
/**
* Query key utilities
*/
export const keyUtils = {
/**
* Create a hierarchical query key
*/
createHierarchical: (...segments) => {
return segments.filter(segment => segment !== undefined && segment !== null);
},
/**
* Match query keys by pattern
*/
matchesPattern: (queryKey, pattern) => {
if (pattern.length > queryKey.length)
return false;
return pattern.every((segment, index) => {
const keySegment = queryKey[index];
// Exact match
if (segment === keySegment)
return true;
// Object match (partial)
if (typeof segment === 'object' &&
typeof keySegment === 'object' &&
segment !== null &&
keySegment !== null) {
return Object.entries(segment).every(([key, value]) => {
return keySegment[key] === value;
});
}
return false;
});
},
/**
* Extract parameters from query key
*/
extractParams: (queryKey, paramIndex) => {
const param = queryKey[paramIndex];
if (typeof param === 'object' && param !== null) {
return param;
}
return null;
},
};
/**
* Retry utilities
*/
export const retryUtils = {
/**
* Exponential backoff with jitter
*/
exponentialBackoff: (attemptIndex, baseDelay = 1000) => {
const delay = baseDelay * Math.pow(2, attemptIndex);
const jitter = Math.random() * 0.1 * delay;
return Math.min(delay + jitter, 30000); // Max 30 seconds
},
/**
* Linear backoff
*/
linearBackoff: (attemptIndex, baseDelay = 1000) => {
return Math.min(baseDelay * (attemptIndex + 1), 10000); // Max 10 seconds
},
/**
* Fixed delay
*/
fixedDelay: (delay = 1000) => {
return delay;
},
/**
* Should retry based on error type
*/
shouldRetry: (failureCount, error, maxRetries = 3) => {
// Don't retry if max attempts reached
if (failureCount >= maxRetries)
return false;
// Don't retry client errors (4xx)
if (queryErrorUtils.isClientError(error))
return false;
// Don't retry unauthorized/forbidden
if (queryErrorUtils.isUnauthorized(error) || queryErrorUtils.isForbidden(error)) {
return false;
}
// Retry server errors and network errors
return queryErrorUtils.isServerError(error) || queryErrorUtils.isNetworkError(error);
},
};
/**
* Performance utilities
*/
export const performanceUtils = {
/**
* Measure query performance
*/
measureQuery: (queryFn, queryKey) => {
const startTime = performance.now();
return queryFn().then(data => {
const endTime = performance.now();
const duration = endTime - startTime;
// Log slow queries in development
if (NODE_ENV === 'development' && duration > 1000) {
console.warn(`Slow query detected (${duration.toFixed(2)}ms):`, queryKey);
}
return { data, duration };
});
},
/**
* Create a debounced query function
*/
debounce: (fn, delay) => {
let timeoutId;
return ((...args) => {
clearTimeout(timeoutId);
return new Promise(resolve => {
timeoutId = setTimeout(() => {
resolve(fn(...args));
}, delay);
});
});
},
/**
* Create a throttled query function
*/
throttle: (fn, delay) => {
let lastCall = 0;
return ((...args) => {
const now = Date.now();
if (now - lastCall >= delay) {
lastCall = now;
return fn(...args);
}
return Promise.resolve();
});
},
};
//# sourceMappingURL=utils.js.map