UNPKG

guardz-axios

Version:

Type-safe HTTP client built on top of Axios with runtime validation using guardz. Part of the guardz ecosystem for comprehensive TypeScript type safety.

208 lines 7.47 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.safeExtractData = safeExtractData; exports.extractPaginatedData = extractPaginatedData; exports.handleAxiosError = handleAxiosError; exports.createResponseValidator = createResponseValidator; exports.defaultRetryCondition = defaultRetryCondition; exports.calculateBackoffDelay = calculateBackoffDelay; exports.shouldTriggerAuth = shouldTriggerAuth; exports.extractErrorMessage = extractErrorMessage; const guardz_1 = require("guardz"); const axios_response_guards_1 = require("../guards/axios-response-guards"); const axios_error_guards_1 = require("../guards/axios-error-guards"); /** * Safely extract data from Axios response with type validation */ function safeExtractData(response, dataGuard) { if (!(0, axios_response_guards_1.isAxiosResponse)(response)) { return { success: false, error: "Invalid Axios response structure" }; } if (!(0, axios_response_guards_1.isSuccessResponse)(response)) { return { success: false, error: `HTTP error: ${response.status} ${response.statusText}`, }; } if (!dataGuard(response.data)) { return { success: false, error: "Response data does not match expected type", }; } return { success: true, data: response.data, response }; } /** * Validate and extract paginated response data */ function extractPaginatedData(response, itemGuard) { const paginatedResponseGuard = (0, guardz_1.isType)({ data: (value) => { if (!Array.isArray(value)) return false; return value.every((item) => itemGuard(item)); }, page: guardz_1.isNumber, total: guardz_1.isNumber, hasNext: (value) => typeof value === "boolean", }); const extraction = safeExtractData(response, paginatedResponseGuard); if (!extraction.success) { return extraction; } return { success: true, data: extraction.data.data, pagination: { page: extraction.data.page, total: extraction.data.total, hasNext: extraction.data.hasNext, }, response: extraction.response, }; } /** * Handle Axios errors with detailed categorization */ function handleAxiosError(error) { const categorization = (0, axios_error_guards_1.categorizeAxiosError)(error); if (!categorization.isAxiosError) { return { category: "unknown", message: error instanceof Error ? error.message : "Unknown error occurred", isRetryable: false, }; } const axiosError = error; let message = axiosError.message || "Axios error occurred"; let isRetryable = false; switch (categorization.category) { case "network": message = "Network error - please check your connection"; isRetryable = true; break; case "timeout": message = "Request timeout - please try again"; isRetryable = true; break; case "cancel": message = "Request was cancelled"; isRetryable = false; break; case "client": message = `Client error: ${categorization.statusCode} ${axiosError.response?.statusText || ""}`; isRetryable = categorization.statusCode === 429; // Rate limit break; case "server": message = `Server error: ${categorization.statusCode} ${axiosError.response?.statusText || ""}`; isRetryable = [500, 502, 503, 504].includes(categorization.statusCode || 0); break; default: message = axiosError.message || "Unknown error occurred"; isRetryable = false; } return { category: categorization.category, message, statusCode: categorization.statusCode, errorCode: categorization.errorCode, isRetryable, details: axiosError.response?.data, }; } /** * Create a response validator with custom error messages */ function createResponseValidator(dataGuard, options = {}) { const { allowedStatuses = [200, 201], customErrorMessages = {}, validateContentType, } = options; return function (response) { if (!(0, axios_response_guards_1.isAxiosResponse)(response)) { return { success: false, error: "Invalid response structure" }; } if (!allowedStatuses.includes(response.status)) { const customMessage = customErrorMessages[response.status]; return { success: false, error: customMessage || `Unexpected status code: ${response.status}`, statusCode: response.status, }; } if (validateContentType) { const contentType = response.headers["content-type"] || response.headers["Content-Type"]; if (!contentType || !contentType.includes(validateContentType)) { return { success: false, error: `Expected content type '${validateContentType}' but got '${contentType}'`, }; } } if (!dataGuard(response.data)) { return { success: false, error: "Response data validation failed", statusCode: response.status, }; } return { success: true, data: response.data, response, }; }; } /** * Default retry condition - retry on network errors, timeouts, and 5xx server errors */ function defaultRetryCondition(error) { if ((0, axios_error_guards_1.isNetworkError)(error) || (0, axios_error_guards_1.isTimeoutError)(error)) { return true; } if ((0, axios_error_guards_1.isAxiosError)(error) && error.response) { const status = error.response.status; return status >= 500 || status === 429; // Server errors or rate limiting } return false; } /** * Calculate exponential backoff delay */ function calculateBackoffDelay(attempt, baseDelay, maxDelay) { const delay = baseDelay * Math.pow(2, attempt - 1); return Math.min(delay, maxDelay); } /** * Utility to check if error should trigger authentication flow */ function shouldTriggerAuth(error) { if (!(0, axios_error_guards_1.isAxiosError)(error) || !error.response) { return false; } return error.response.status === 401 || error.response.status === 403; } /** * Extract error message from Axios error */ function extractErrorMessage(error, fallback = "An error occurred") { if ((0, axios_error_guards_1.isAxiosError)(error)) { // Try to extract message from response data if (error.response?.data) { const data = error.response.data; if (typeof data === "string") return data; if (typeof data === "object" && data !== null) { const obj = data; if (typeof obj.message === "string") return obj.message; if (typeof obj.error === "string") return obj.error; } } // Fallback to error message return error.message || fallback; } if (error instanceof Error) { return error.message; } return fallback; } //# sourceMappingURL=axios-utils.js.map