@dbs-portal/core-api
Version:
HTTP client and API utilities for DBS Portal
206 lines • 6.27 kB
JavaScript
/**
* Retry manager for failed requests
*/
/**
* Default retry condition - retry on network errors and 5xx status codes
*/
const defaultRetryCondition = (error) => {
if (!error.response) {
// Network error
return true;
}
const status = error.response.status;
// Retry on 5xx server errors
if (status >= 500 && status < 600) {
return true;
}
// Retry on specific 4xx errors
if (status === 408 || status === 429) {
return true;
}
return false;
};
/**
* Default retry delay - exponential backoff
*/
const defaultRetryDelay = (retryCount) => {
return Math.min(1000 * Math.pow(2, retryCount), 30000); // Max 30 seconds
};
/**
* Retry manager for handling failed requests
*/
export class RetryManager {
config;
constructor(config) {
this.config = {
retries: config.retries,
retryDelay: config.retryDelay || defaultRetryDelay,
retryCondition: config.retryCondition || defaultRetryCondition,
shouldResetTimeout: config.shouldResetTimeout ?? true,
};
}
/**
* Executes a function with retry logic
*/
async execute(operation) {
let lastError;
let retryCount = 0;
while (retryCount <= this.config.retries) {
try {
return await operation();
}
catch (error) {
lastError = error;
// Check if we should retry
if (retryCount >= this.config.retries || !this.shouldRetry(error)) {
break;
}
// Calculate delay
const delay = this.calculateDelay(retryCount);
// Wait before retrying
if (delay > 0) {
await this.delay(delay);
}
retryCount++;
}
}
throw lastError;
}
/**
* Updates retry configuration
*/
updateConfig(config) {
this.config = {
...this.config,
...config,
retryDelay: config.retryDelay || this.config.retryDelay,
retryCondition: config.retryCondition || this.config.retryCondition,
};
}
/**
* Gets current configuration
*/
getConfig() {
return { ...this.config };
}
/**
* Determines if an error should trigger a retry
*/
shouldRetry(error) {
return this.config.retryCondition(error);
}
/**
* Calculates retry delay
*/
calculateDelay(retryCount) {
if (typeof this.config.retryDelay === 'function') {
return this.config.retryDelay(retryCount);
}
return this.config.retryDelay;
}
/**
* Creates a delay promise
*/
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Creates a retry manager with exponential backoff
*/
static exponentialBackoff(retries = 3, baseDelay = 1000, maxDelay = 30000) {
return new RetryManager({
retries,
retryDelay: retryCount => Math.min(baseDelay * Math.pow(2, retryCount), maxDelay),
retryCondition: defaultRetryCondition,
});
}
/**
* Creates a retry manager with linear backoff
*/
static linearBackoff(retries = 3, delay = 1000) {
return new RetryManager({
retries,
retryDelay: retryCount => delay * (retryCount + 1),
retryCondition: defaultRetryCondition,
});
}
/**
* Creates a retry manager with fixed delay
*/
static fixedDelay(retries = 3, delay = 1000) {
return new RetryManager({
retries,
retryDelay: delay,
retryCondition: defaultRetryCondition,
});
}
/**
* Creates a retry manager for network errors only
*/
static networkErrorsOnly(retries = 3) {
return new RetryManager({
retries,
retryDelay: defaultRetryDelay,
retryCondition: error => !error.response, // Only network errors
});
}
/**
* Creates a retry manager for server errors only
*/
static serverErrorsOnly(retries = 3) {
return new RetryManager({
retries,
retryDelay: defaultRetryDelay,
retryCondition: error => {
const status = error.response?.status;
return status ? status >= 500 && status < 600 : false;
},
});
}
/**
* Creates a retry manager with custom condition
*/
static custom(retries, retryCondition, retryDelay) {
return new RetryManager({
retries,
retryCondition,
retryDelay: retryDelay || defaultRetryDelay,
});
}
/**
* Creates a retry manager that respects Retry-After headers
*/
static withRetryAfter(retries = 3) {
return new RetryManager({
retries,
retryCondition: defaultRetryCondition,
retryDelay: (retryCount, error) => {
// Check for Retry-After header
const retryAfter = error?.response?.headers?.['retry-after'];
if (retryAfter) {
const seconds = Number.parseInt(retryAfter, 10);
if (!isNaN(seconds)) {
return seconds * 1000; // Convert to milliseconds
}
}
// Fallback to exponential backoff
return defaultRetryDelay(retryCount);
},
});
}
/**
* Creates a retry manager with jitter to avoid thundering herd
*/
static withJitter(retries = 3, baseDelay = 1000, maxDelay = 30000, jitterFactor = 0.1) {
return new RetryManager({
retries,
retryCondition: defaultRetryCondition,
retryDelay: retryCount => {
const exponentialDelay = Math.min(baseDelay * Math.pow(2, retryCount), maxDelay);
const jitter = exponentialDelay * jitterFactor * Math.random();
return exponentialDelay + jitter;
},
});
}
}
//# sourceMappingURL=retry-manager.js.map