axios-retryer
Version:
TypeScript-first Axios retry library with concurrency limits, request priority, token refresh, response caching, and circuit breaker plugins.
2 lines (1 loc) • 39.5 kB
JavaScript
import e,{AxiosError as t}from"axios";const r={AUTOMATIC:"automatic",MANUAL:"manual"},s={GET:"GET",POST:"POST",PUT:"PUT",PATCH:"PATCH",DELETE:"DELETE",HEAD:"HEAD",OPTIONS:"OPTIONS",PURGE:"PURGE",LINK:"LINK",UNLINK:"UNLINK"},i={CRITICAL:4,HIGHEST:3,HIGH:2,MEDIUM:1,LOW:0},n={STATIC:0,LINEAR:1,EXPONENTIAL:2};class o{constructor(e=!1){this.debugMode=e}log(e,t){console.log(`[AXIOS_RETRYER] ${e}`,...void 0!==t?[t]:[])}error(e,t){console.error(`[AXIOS_RETRYER] ${e}`,...void 0!==t?[t]:[])}warn(e,t){console.warn(`[AXIOS_RETRYER] ${e}`,...void 0!==t?[t]:[])}debug(e,t){this.debugMode&&console.debug(`[AXIOS_RETRYER] ${e}`,...void 0!==t?[t]:[])}}const u=new Set(["retryAttempt","requestRetries","requestMode","requestId","correlationId","isRetrying","priority","timestamp","backoffType","retryableStatuses","extra","isRetryRefreshRequest","manualReplayAttempt","retryAfterMs","silentlyCancelled","cachingOptions"]);function l(e){return u.has(e)&&"__proto__"!==e&&"constructor"!==e&&"prototype"!==e}function a(e){const t={};if(Object.defineProperty(t,"toJSON",{value:()=>{},enumerable:!1,writable:!1,configurable:!1}),e)for(const r of Object.keys(e))l(r)&&(t[r]=e[r]);return t}function c(e){const t=e;return t.__axiosRetryer?"function"!=typeof t.__axiosRetryer.toJSON&&(t.__axiosRetryer=a(t.__axiosRetryer)):t.__axiosRetryer=a(),t.__axiosRetryer}function h(e){if(e)return e.__axiosRetryer}function d(e,t,r){const s=c(e);return void 0===r?delete s[t]:s[t]=r,s}function g(e,t){const r=c(e);for(const e of Object.keys(t)){if(!l(e))continue;const s=t[e];void 0===s?delete r[e]:r[e]=s}return r}const y=[408,429,500,502,503,504,[520,527]],p=[s.GET,s.HEAD,s.OPTIONS];class v{constructor(e=y,t=p,r=n.EXPONENTIAL,s=["Idempotency-Key"],i,o){this.retryableStatuses=e,this.retryableMethods=t,this.backoffType=r,this.idempotencyHeaders=s,this.logger=i,this.maxBackoffDelayMs=o,this.overrideCache=new WeakMap,this.getIsRetryable=e=>{var t,r,s,i,n,o,u,l;if("TOKEN_REFRESH_FAILED"===e.code)return!1;if(!e.response)return null===(t=this.logger)||void 0===t||t.debug("Retrying due to network error"),!0;const a=e.config,c=null===(r=null==a?void 0:a.method)||void 0===r?void 0:r.toLowerCase(),d=e.response.status,g=null!==(i=null===(s=h(a))||void 0===s?void 0:s.retryableStatuses)&&void 0!==i?i:this.retryableStatuses;if(c&&this.retryableMethodsLower.has(c)&&this.isRetryableStatus(d,g))return null===(n=this.logger)||void 0===n||n.debug(`Retrying request with status ${d} and method ${c}`),!0;if("post"===c||"put"===c||"patch"===c){const e=Object.keys(null!==(o=a.headers)&&void 0!==o?o:{}).map(e=>e.toLowerCase());if(this.idempotencyHeadersLower.some(t=>e.includes(t)))return null===(u=this.logger)||void 0===u||u.debug(`Retrying idempotent request with method ${c}`),!0}return null===(l=this.logger)||void 0===l||l.debug(`Not retrying request with method ${c} and status ${d}`),!1},this.shouldRetry=(e,t,r)=>this.getIsRetryable(e)&&t<=r,this.getDelay=(e,t,r)=>{var s;const i=function(e,t,r){let s=0;if(e<=0)return s;switch(t){case n.STATIC:s=1e3;break;case n.LINEAR:s=1e3*e;break;default:s=1e3*2**(e-1)}const i=Number.isFinite(r)&&r>0?r:6e4;return s=Math.min(s,i),Math.floor(Math.random()*(s+1))}(e,null!=r?r:this.backoffType,this.maxBackoffDelayMs);return null===(s=this.logger)||void 0===s||s.debug(`Retry delay for attempt ${e}: ${i}ms; MaxRetries: ${t}`),i},this.retryableMethodsLower=new Set(this.retryableMethods.map(e=>e.toLowerCase())),this.idempotencyHeadersLower=this.idempotencyHeaders.map(e=>e.toLowerCase()),this.defaultStatusSet=new Set,this.defaultRanges=[];for(const t of e)"number"==typeof t?this.defaultStatusSet.add(t):Array.isArray(t)&&this.defaultRanges.push(t)}isRetryableStatus(e,t){if(t===this.retryableStatuses)return this.defaultStatusSet.has(e)||this.defaultRanges.some(([t,r])=>e>=t&&e<=r);let r=this.overrideCache.get(t);if(!r){const e=new Set,s=[];for(const r of t)"number"==typeof r?e.add(r):Array.isArray(r)&&s.push(r);r={set:e,ranges:s},this.overrideCache.set(t,r)}return r.set.has(e)||r.ranges.some(([t,r])=>e>=t&&e<=r)}}class m{constructor(e,t={}){var r,s;this.logger=e,this.listeners={},"number"==typeof t?(this.maxListenersPerEvent=t,this.strictListenerLimit=!1):(this.maxListenersPerEvent=null!==(r=t.maxListenersPerEvent)&&void 0!==r?r:50,this.strictListenerLimit=null!==(s=t.strictListenerLimit)&&void 0!==s&&s)}emit(e,...t){const r=this.listeners[e];null==r||r.forEach(r=>{try{r(...t)}catch(t){this.logger.error(`Error in "${String(e)}" listener:`,t)}})}triggerAndEmit(e,...t){this.emit(e,...t)}on(e,t){var r;const s=null!==(r=this.listeners[e])&&void 0!==r?r:[];if(s.length>=this.maxListenersPerEvent){const t=`EventBus: listener limit (${this.maxListenersPerEvent}) reached for event "${String(e)}". This may indicate a listener leak. Call off() to remove unused listeners.`;if(this.strictListenerLimit)throw new Error(t);return void this.logger.warn(t,{event:e,count:s.length})}s.push(t),this.listeners[e]=s,this.logger.debug("Event listener added",{event:e})}off(e,t){const r=this.listeners[e];if(!r)return!1;const s=r.indexOf(t);return-1!==s&&(r.splice(s,1),0===r.length&&delete this.listeners[e],this.logger.debug("Event listener removed",{event:e}),!0)}clear(){this.listeners={}}hasListeners(e){var t;return Boolean(null===(t=this.listeners[e])||void 0===t?void 0:t.length)}}class R extends Error{constructor(e,t){super(e),this.name=new.target.name,this.code=t,Object.setPrototypeOf(this,new.target.prototype)}}class f extends R{constructor(e,t,r,s){super(e,t),this.pluginName=r,this.pluginVersion=s}}class q{constructor(e){this.logger=e,this.plugins=new Map}getPlugins(){return this.plugins.values()}list(){return Array.from(this.plugins.values()).map(({name:e,version:t})=>({name:e,version:t}))}use(e,t,r,s=!0){const i=e;if(this.plugins.has(e.name))throw this.logger.error("Plugin already registered",{plugin:e.name}),new f(`Plugin "${e.name}" is already registered.`,"EPLUGIN_ALREADY_REGISTERED",e.name,e.version);if(!this.validatePluginVersion(e.version))throw this.logger.error("Invalid plugin version",{version:e.version}),new f(`Invalid plugin version format: ${e.version}`,"EINVALID_PLUGIN_VERSION",e.name,e.version);this.plugins.set(e.name,i);try{s&&r.ejectRetryerInterceptors(),i.initialize(t),s&&r.installRetryerInterceptors(),this.logger.log("Plugin registered",{name:e.name,version:e.version})}catch(t){throw this.plugins.delete(e.name),s&&r.installRetryerInterceptors(),t}}unuse(e,t){const r=this.plugins.get(e);return r?("function"==typeof r.onBeforeDestroyed&&r.onBeforeDestroyed(t),this.plugins.delete(e),this.logger.log("Plugin removed",{name:r.name,version:r.version}),!0):(this.logger.debug("Plugin removal failed - not found",{pluginName:e}),!1)}cleanup(e){this.plugins.forEach(t=>{"function"==typeof t.onBeforeDestroyed&&t.onBeforeDestroyed(e)}),this.plugins.clear()}validatePluginVersion(e){return/^\d+\.\d+\.\d+$/.test(e)}}class I{constructor(e){this.options=e,this.activeRequests=new Map,this.activeConfigs=new Map,this.requestIndex=0}beginRequest(e){var t,r,s,n;const o=new AbortController,u=h(e),l=null!==(t=null==u?void 0:u.requestId)&&void 0!==t?t:this.generateRequestId(),a=null!==(r=null==u?void 0:u.priority)&&void 0!==r?r:i.MEDIUM,c=null!==(n=null!==(s=null==u?void 0:u.correlationId)&&void 0!==s?s:function(e){const t=e.headers;if(!t)return;const r=t;if("function"!=typeof r.get){for(const[e,r]of Object.entries(t))if(E.includes(e.toLowerCase())){if("string"==typeof r&&r.length>0)return r;if("number"==typeof r)return String(r)}}else for(const e of E){const t=r.get(e);if("string"==typeof t&&t.length>0)return t;if("number"==typeof t)return String(t)}}(e))&&void 0!==n?n:l,d=e.signal;return g(e,{requestId:l,correlationId:c,timestamp:Date.now(),priority:a}),e.signal=o.signal,d&&this.linkCallerAbortSignal(l,o,d),o.signal.aborted||(this.activeRequests.set(l,o),this.activeConfigs.set(l,e)),{requestId:l,priority:a,controller:o,callerAborted:o.signal.aborted}}release(e){var t;const r=null===(t=h(e))||void 0===t?void 0:t.requestId;return r?{requestId:r,released:this.removeById(r)}:{released:!1}}removeById(e){const t=this.activeRequests.get(e);return!!t&&(this.releaseAbortSignalLink(t),this.activeRequests.delete(e),this.activeConfigs.delete(e),!0)}cancelRequest(e){const t=this.activeRequests.get(e);if(t){const r=this.options.requestQueue.cancelQueuedRequest(e);this.options.logger.debug("Cancelling request",{requestId:e,wasActive:!0,wasQueued:r}),t.abort(),this.removeById(e),this.options.onRequestCancelled(e)}this.options.retryScheduler.cancelRetryTimer(e)}cancelAllRequests(e={}){var t;const r=null===(t=e.includeQueued)||void 0===t||t,s=e.preservedQueuedRequestIds,i=this.options.retryScheduler.getTimerStats();this.options.logger.warn("Cancelling all requests",{activeCount:this.activeRequests.size,queuedCount:this.options.requestQueue.getWaitingCount(),activeRetryTimers:i.activeRetryTimers,includeQueued:r}),this.activeRequests.forEach((e,t)=>{(r||!(null==s?void 0:s.has(t))&&!this.options.requestQueue.hasQueuedRequest(t))&&(this.releaseAbortSignalLink(e),e.abort(),r&&this.options.requestQueue.cancelQueuedRequest(t),this.options.onRequestCancelled(t))}),this.activeRequests.clear(),this.activeConfigs.clear(),this.options.retryScheduler.cancelAllRetryTimers()}cancelQueuedRequests(){this.activeRequests.forEach((e,t)=>{this.options.requestQueue.cancelQueuedRequest(t)&&this.options.onRequestCancelled(t)})}getActiveCount(){return this.activeRequests.size}getActiveRequests(){return this.activeRequests}generateRequestId(){const e=++this.requestIndex,t="undefined"!=typeof globalThis&&void 0!==globalThis.crypto?globalThis.crypto:void 0;if("function"==typeof(null==t?void 0:t.randomUUID))return`req_${t.randomUUID()}`;if("function"==typeof(null==t?void 0:t.getRandomValues)){const r=t.getRandomValues(new Uint32Array(2));return`req_${r[0].toString(36)}${r[1].toString(36)}_${e.toString(36)}`}return`req_${Date.now().toString(36)}_${e.toString(36)}`}linkCallerAbortSignal(e,t,r){const s=()=>{t.abort(r.reason),this.options.requestQueue.cancelQueuedRequest(e),this.options.retryScheduler.cancelRetryTimer(e)};if(r.aborted)return void s();if("function"!=typeof r.addEventListener||"function"!=typeof r.removeEventListener)return;const i=r.addEventListener.bind(r),n=r.removeEventListener.bind(r);i("abort",s,{once:!0}),t.__disposeSignalLink=()=>{n("abort",s),delete t.__disposeSignalLink}}releaseAbortSignalLink(e){var t;null===(t=e.__disposeSignalLink)||void 0===t||t.call(e)}}const E=["x-correlation-id","x-request-id"];class b{constructor(e){this.options=e}destroy(e){const t=this.options.retryScheduler.getTimerStats(),r=new Set(this.options.requestQueue.getQueuedRequestIds());this.options.logger.warn("Destroying RetryManager",{activeRequests:this.options.requestLifecycle.getActiveCount(),queuedRequests:r.size,activeRetryTimers:t.activeRetryTimers,activeTimers:t.activeTimers}),this.options.requestQueue.destroy(),this.options.requestLifecycle.cancelAllRequests({includeQueued:!1,preservedQueuedRequestIds:r}),this.options.retryScheduler.destroy(),this.options.ejectRetryerInterceptors(),this.options.pluginRegistry.cleanup(e),this.options.eventBus.clear(),this.options.logger.log("RetryManager destroyed successfully")}}class C extends R{constructor(e,t,r){super(e,"EINVALID_CONFIG"),this.optionName=t,this.optionValue=r}}class S extends t{constructor(e){super("Queue cleared","QUEUE_CLEARED",e),this.name="QueueClearedError",Object.setPrototypeOf(this,new.target.prototype)}}class w extends t{constructor(e){super("Queue has been destroyed","QUEUE_DESTROYED",e),this.name="QueueDestroyedError",Object.setPrototypeOf(this,new.target.prototype)}}class T extends t{constructor(e){super("Request queue is full. The maximum queue size has been reached.","EQUEUE_FULL",e),this.name="QueueFullError",Object.setPrototypeOf(this,new.target.prototype)}}class A extends t{constructor(e,t){super(`Request is cancelled ID: ${e}`,"REQUEST_CANCELED",t),this.name="QueuedRequestCanceledError",this.requestId=e,Object.setPrototypeOf(this,new.target.prototype)}}class L{constructor(e,t){this.heap=[],this.insertionCounter=0,this.sortedCache=null,this.compareFn=e,this.idExtractor=null!=t?t:null}get length(){return this.heap.length}push(e){e.insertionOrder=this.insertionCounter++,this.heap.push(e),this.heapifyUp(this.heap.length-1),this.sortedCache=null}shift(){if(0===this.heap.length)return;if(1===this.heap.length)return this.sortedCache=null,this.heap.pop();const e=this.heap[0];return this.heap[0]=this.heap.pop(),this.heapifyDown(0),this.sortedCache=null,e}peek(){return this.heap[0]}removeByRequestId(e){if(!this.idExtractor)return;const t=this.idExtractor,r=this.heap.findIndex(r=>t(r)===e);if(-1===r)return;const s=this.heap[r];return this.sortedCache=null,r===this.heap.length-1?this.heap.pop():(this.heap[r]=this.heap.pop(),this.heapifyUp(r),this.heapifyDown(r),s)}clear(){const e=[...this.heap];return this.heap.length=0,this.insertionCounter=0,this.sortedCache=null,e}getAll(){return this.sortedCache||(this.sortedCache=[...this.heap].sort(this.compareFn)),[...this.sortedCache]}heapifyUp(e){for(;e>0;){const t=Math.floor((e-1)/2);if(this.compareFn(this.heap[t],this.heap[e])<=0)break;[this.heap[t],this.heap[e]]=[this.heap[e],this.heap[t]],e=t}}heapifyDown(e){for(;;){const t=2*e+1,r=2*e+2;let s=e;if(t<this.heap.length&&this.compareFn(this.heap[t],this.heap[s])<0&&(s=t),r<this.heap.length&&this.compareFn(this.heap[r],this.heap[s])<0&&(s=r),s===e)break;[this.heap[e],this.heap[s]]=[this.heap[s],this.heap[e]],e=s}}}class P{constructor(e={}){this.processingGates=new Map,this.inFlightRequestIds=new Set,this.inProgressCount=0,this.isDestroyed=!1,this.dequeueTimer=null,this.microtaskScheduled=!1,this.tryDequeue=()=>{if(!(this.isDestroyed||0===this.waiting.length||this.inProgressCount>=this.maxConcurrent)){if(this.queueDelay<=0){if(this.microtaskScheduled)return;return this.microtaskScheduled=!0,void queueMicrotask(()=>{this.microtaskScheduled=!1,this.isDestroyed||this.drainQueue()})}this.dequeueTimer||(this.dequeueTimer=setTimeout(()=>{this.dequeueTimer=null,this.isDestroyed||this.drainQueue()},this.queueDelay))}};const{maxConcurrent:t=5,queueDelay:r=100,maxQueueSize:s,canProcess:i,logger:n}=e;if(t<1)throw new C(`maxConcurrent must be >= 1. Received: ${t}`,"maxConcurrent",t);if(!Number.isInteger(r)||r<0)throw new C(`queueDelay must be >= 0. Received: ${r}`,"queueDelay",r);if(void 0!==s&&(!Number.isInteger(s)||s<1))throw new C(`maxQueueSize must be >= 1 when provided. Received: ${s}`,"maxQueueSize",s);this.maxConcurrent=t,this.queueDelay=r,this.maxQueueSize=s,this.baseCanProcess=null!=i?i:()=>!0,this.logger=null!=n?n:null,this.waiting=new L(this.comparePriority.bind(this),e=>{var t;return null===(t=h(e.config))||void 0===t?void 0:t.requestId})}enqueue(e){return c(e),this.isDestroyed?Promise.reject(new w(e)):void 0!==this.maxQueueSize&&this.waiting.length>=this.maxQueueSize?Promise.reject(new T(e)):new Promise((t,r)=>{this.waiting.push({config:e,resolve:t,reject:r}),this.tryDequeue()})}markComplete(e){(void 0===e||this.inFlightRequestIds.delete(e))&&(this.inProgressCount=Math.max(0,this.inProgressCount-1),this.tryDequeue())}getWaitingCount(){return this.waiting.length}getWaiting(){return this.waiting.getAll()}get isBusy(){return this.waiting.length>0||this.inProgressCount>0}registerProcessingGate(e,t){this.processingGates.set(e,t),this.refresh()}unregisterProcessingGate(e){const t=this.processingGates.delete(e);return t&&this.refresh(),t}refresh(){this.tryDequeue()}hasQueuedRequest(e){return this.waiting.getAll().some(t=>{var r;return(null===(r=h(t.config))||void 0===r?void 0:r.requestId)===e})}getQueuedRequestIds(){return this.waiting.getAll().map(e=>{var t;return null===(t=h(e.config))||void 0===t?void 0:t.requestId}).filter(e=>"string"==typeof e)}cancelQueuedRequest(e){const t=this.waiting.removeByRequestId(e);return!!t&&(t.reject(new A(e,t.config)),this.cleanupRequest(t),!0)}clear(){this.rejectWaitingRequests(e=>new S(e.config))}destroy(){this.dequeueTimer&&(clearTimeout(this.dequeueTimer),this.dequeueTimer=null),this.microtaskScheduled=!1,this.isDestroyed=!0,this.rejectWaitingRequests(e=>new w(e.config)),this.inProgressCount=0,this.inFlightRequestIds.clear()}drainQueue(){for(var e;this.inProgressCount<this.maxConcurrent&&this.waiting.length>0;){const t=this.waiting.peek();if(!t)return;if(!this.canProcess(t.config))return;const r=this.waiting.shift();this.inProgressCount++;const s=null===(e=h(r.config))||void 0===e?void 0:e.requestId;s&&this.inFlightRequestIds.add(s),r.resolve(r.config),this.cleanupRequest(r)}}canProcess(e){if(!this.evaluateGate(this.baseCanProcess,e))return!1;for(const t of Array.from(this.processingGates.values()))if(!this.evaluateGate(t,e))return!1;return!0}comparePriority(e,t){var r,s,n,o,u,l,a,c,d,g;const y=null!==(s=null===(r=h(e.config))||void 0===r?void 0:r.priority)&&void 0!==s?s:i.MEDIUM,p=null!==(o=null===(n=h(t.config))||void 0===n?void 0:n.priority)&&void 0!==o?o:i.MEDIUM;if(y!==p)return p-y;const v=null!==(l=null===(u=h(e.config))||void 0===u?void 0:u.timestamp)&&void 0!==l?l:0,m=null!==(c=null===(a=h(t.config))||void 0===a?void 0:a.timestamp)&&void 0!==c?c:0;return v!==m?v-m:(null!==(d=e.insertionOrder)&&void 0!==d?d:0)-(null!==(g=t.insertionOrder)&&void 0!==g?g:0)}cleanupRequest(e){e.resolve=()=>{},e.reject=()=>{}}rejectWaitingRequests(e){const t=this.waiting.clear();for(const r of t)r.reject(e(r)),this.cleanupRequest(r)}evaluateGate(e,t){var r,s;try{return e(t)}catch(e){return null===(r=this.logger)||void 0===r||r.error("Queue gate threw; treating as not-ready",{requestId:null===(s=h(t))||void 0===s?void 0:s.requestId,error:e instanceof Error?e.message:String(e)}),!1}}}const D=3e5;function x(e){return Array.isArray(e)?e.length>0?x(e[0]):void 0:"string"==typeof e?e:"number"==typeof e?String(e):void 0}class Q{constructor(){this.activeTimers=new Set,this.activeSleepRejects=new Set,this.isDestroyed=!1}createTimeout(e,t){if(this.isDestroyed)return{timerId:null,cancel:()=>{}};const r=setTimeout(()=>{this.activeTimers.delete(r),this.isDestroyed||e()},t);return this.activeTimers.add(r),{timerId:r,cancel:()=>{this.activeTimers.has(r)&&(clearTimeout(r),this.activeTimers.delete(r))}}}createSleep(e){let t=()=>{};return{promise:new Promise((r,s)=>{if(this.isDestroyed)return void s(new R("Sleep cancelled","ETIMER_CANCELLED"));const i=e=>{this.activeSleepRejects.delete(i),s(e)};this.activeSleepRejects.add(i);const{cancel:n}=this.createTimeout(()=>{this.activeSleepRejects.delete(i),r()},e);t=()=>{n(),i(new R("Sleep cancelled","ETIMER_CANCELLED"))}}),cancel:t}}getActiveTimerCount(){return this.activeTimers.size}destroy(){this.isDestroyed=!0,this.activeTimers.forEach(e=>{clearTimeout(e)}),this.activeTimers.clear();const e=new R("Sleep cancelled","ETIMER_CANCELLED");this.activeSleepRejects.forEach(t=>t(e)),this.activeSleepRejects.clear()}}class k{constructor(e,t,r){this.logger=e,this.retryStrategy=t,this.emitEvent=r,this.timerManager=new Q,this.activeRetryTimers=new Map}getRetryDelay(e,t,r){const s=h(e);let i=this.retryStrategy.getDelay(t,r,null==s?void 0:s.backoffType);return(null==s?void 0:s.retryAfterMs)&&s.retryAfterMs>i&&(i=s.retryAfterMs),i}async waitForRetryDelay(e,t){var r;const{promise:s,cancel:i}=this.timerManager.createSleep(t),n=null===(r=h(e))||void 0===r?void 0:r.requestId;n&&this.activeRetryTimers.set(n,i);try{return await s,!0}catch(e){return this.logger.warn("Retry sleep cancelled",{requestId:n}),!1}finally{n&&this.activeRetryTimers.delete(n)}}async wait(e){await new Promise(t=>{this.timerManager.createTimeout(t,e)})}cancelRetryTimer(e,t="user"){var r;const s=this.activeRetryTimers.get(e);return!!s&&(s(),this.activeRetryTimers.delete(e),this.logger.debug("Cancelled retry timer",{requestId:e,source:t}),null===(r=this.emitEvent)||void 0===r||r.call(this,"onRetryTimerCancelled",{requestId:e,source:t}),!0)}cancelAllRetryTimers(){this.activeRetryTimers.forEach((e,t)=>{var r;e(),this.logger.debug("Cancelled retry timer",{requestId:t,source:"system"}),null===(r=this.emitEvent)||void 0===r||r.call(this,"onRetryTimerCancelled",{requestId:t,source:"system"})}),this.activeRetryTimers.clear()}getTimerStats(){return{activeTimers:this.timerManager.getActiveTimerCount(),activeRetryTimers:this.activeRetryTimers.size}}destroy(){this.cancelAllRetryTimers(),this.timerManager.destroy()}}class _{constructor(e){this.blockingRequestIds=new Set,this.blockingPriorityThreshold=e.blockingPriorityThreshold,this.cancelPendingOnDependencyFailure=e.cancelPendingOnDependencyFailure,this.requestQueue=e.requestQueue,this.requestLifecycle=e.requestLifecycle,this.emitEvent=e.emitEvent,void 0!==this.blockingPriorityThreshold&&this.requestQueue.registerProcessingGate("__blocking",e=>0===this.blockingRequestIds.size||this.isBlockingRequest(e))}trackIfBlocking(e){var t;if(this.isBlockingRequest(e)){const r=null===(t=h(e))||void 0===t?void 0:t.requestId;r&&this.blockingRequestIds.add(r)}}handleRequestCancelled(e){this.blockingRequestIds.delete(e)&&0===this.blockingRequestIds.size&&this.requestQueue.refresh()}finishBlockingRequest(e,t){var r;const s=null===(r=h(e))||void 0===r?void 0:r.requestId;if(!s)return;const i=this.blockingRequestIds.delete(s);(i||"failure"===t)&&("failure"===t&&this.isBlockingRequest(e)&&(this.emitEvent("onBlockingRequestFailed",e),this.cancelPendingOnDependencyFailure&&this.requestLifecycle.cancelQueuedRequests()),i&&0===this.blockingRequestIds.size&&(this.requestQueue.refresh(),"success"===t&&this.emitEvent("onAllBlockingRequestsResolved")))}isBlockingRequest(e){var t;if(void 0===this.blockingPriorityThreshold)return!1;const r=null===(t=h(e))||void 0===t?void 0:t.priority;return void 0!==r&&r>=this.blockingPriorityThreshold}}class O extends R{constructor(e){super(`Request aborted. ID: ${e}`,"EREQUEST_ABORTED"),this.requestId=e}}class M{constructor(e){this.handleRequest=async e=>{var t;const{requestId:r,priority:s,callerAborted:i}=this.requestLifecycle.beginRequest(e);if(i)return this.logger.warn("Request aborted before queueing",{requestId:r,source:"caller"}),this.throwErrorOnCancelRequest?Promise.reject(new O(r)):Promise.resolve(this.createSilentCancelConfig(e,r));this.dependencyGatekeeper.trackIfBlocking(e),this.logger.debug("New request created",this.buildRequestLogMeta(e,r));try{const t=Date.now(),i=this.requestQueue.getWaitingCount()+1;this.emitEvent("onRequestQueued",{requestId:r,config:e,priority:s,queueSize:i});const n=await this.requestQueue.enqueue(e),o=Date.now()-t;return this.emitEvent("onRequestDispatched",{requestId:r,config:n,priority:s,queuedForMs:o}),n}catch(s){if(this.requestLifecycle.removeById(r),null===(t=e.signal)||void 0===t?void 0:t.aborted)return this.throwErrorOnCancelRequest?Promise.reject(new O(r)):Promise.resolve(this.createSilentCancelConfig(e,r));throw this.logger.error("Queue error when enqueuing request",{requestId:r,error:s}),s}},this.logger=e.logger,this.requestLifecycle=e.requestLifecycle,this.dependencyGatekeeper=e.dependencyGatekeeper,this.requestQueue=e.requestQueue,this.throwErrorOnCancelRequest=e.throwErrorOnCancelRequest,this.createSilentCancelConfig=e.createSilentCancelConfig,this.emitEvent=e.emitEvent}buildRequestLogMeta(e,t){var r;const s=h(e);return{requestId:t,correlationId:null==s?void 0:s.correlationId,url:this.getLogUrl(e.url),method:null===(r=e.method)||void 0===r?void 0:r.toUpperCase(),priority:null==s?void 0:s.priority}}getLogUrl(e){if(!e)return e;const t=e.indexOf("?"),r=e.indexOf("#");return t<0&&r<0?e:e.slice(0,t<0?r:r<0?t:Math.min(t,r))}}class N{constructor(e){this.handleResponse=e=>{var t,r;const s=e.config,i=h(s);if(null==i?void 0:i.silentlyCancelled)return this.logger.debug("Request cancelled without throwing",{requestId:i.requestId}),this.handleRetryProcessFinish(),null;const n=this.requestLifecycle.release(s);return this.requestQueue.markComplete(null==i?void 0:i.requestId),this.dependencyGatekeeper.finishBlockingRequest(s,"success"),this.logger.debug("Request succeeded",{requestId:n.requestId,status:e.status,retrying:null===(t=h(s))||void 0===t?void 0:t.isRetrying}),(null==i?void 0:i.isRetrying)&&void 0!==i.priority&&(this.emitEvent("afterRetry",s,!0),d(s,"isRetrying",!1)),this.emitEvent("onRequestSucceeded",{requestId:n.requestId,config:s,status:e.status,attempts:(null!==(r=null==i?void 0:i.retryAttempt)&&void 0!==r?r:0)+1}),this.handleRetryProcessFinish(),e},this.logger=e.logger,this.requestLifecycle=e.requestLifecycle,this.dependencyGatekeeper=e.dependencyGatekeeper,this.requestQueue=e.requestQueue,this.emitEvent=e.emitEvent,this.handleRetryProcessFinish=e.handleRetryProcessFinish}}const U=new Set(["REQUEST_CANCELED","EREQUEST_ABORTED","QUEUE_DESTROYED","QUEUE_CLEARED","EQUEUE_FULL"]);class F{constructor(e){this.handleError=async e=>{var t,s,n,o;let u=!1;const l=e.config;if(!l)return this.options.logger.error("Handling error without valid config",{error:e.message}),Promise.reject(e);"REQUEST_CANCELED"===e.code&&(u=!0),this.options.requestQueue.markComplete(null===(t=h(l))||void 0===t?void 0:t.requestId),this.options.logger.error("Request failed",this.buildErrorMeta(l,e));const a=c(l);!u&&a.isRetrying&&void 0!==a.priority&&this.options.emitEvent("afterRetry",l,!1,e),g(l,{priority:null!==(s=a.priority)&&void 0!==s?s:i.MEDIUM});const y=h(l),p=function(e){var t;const{error:s,metadata:i,defaultMaxRetries:n,defaultMode:o,strategy:u,cancelledInQueue:l}=e,a=void 0!==i.requestRetries?i.requestRetries:n,c=i.requestMode||o,h=(i.retryAttempt||0)+1;return l||function(e){return void 0!==e.code&&U.has(e.code)}(s)?{kind:"no-retry",retryable:u.getIsRetryable(s)}:c===r.AUTOMATIC&&u.shouldRetry(s,h,a)?{kind:"retry",attempt:h,maxRetries:a,retryAfterMs:function(e){if(!e)return 0;const t=Number(e);if(Number.isFinite(t)&&t>0)return Math.min(Math.ceil(1e3*t),D);const r=Date.parse(e);return Number.isNaN(r)?0:Math.min(Math.max(0,r-Date.now()),D)}(function(e){if(e){if("function"==typeof e.get)return x(e.get("retry-after"));for(const[t,r]of Object.entries(e))if("retry-after"===t.toLowerCase())return x(r)}}(null===(t=s.response)||void 0===t?void 0:t.headers))}:{kind:"no-retry",retryable:u.getIsRetryable(s)}}({error:e,metadata:y,defaultMaxRetries:this.options.retries,defaultMode:this.options.mode,strategy:this.options.retryStrategy,cancelledInQueue:u});return"retry"===p.kind?(p.retryAfterMs>0&&d(l,"retryAfterMs",p.retryAfterMs),this.options.logger.debug("Auto-retrying request",{requestId:y.requestId,attempt:p.attempt,maxRetries:p.maxRetries,...(null===(n=h(l))||void 0===n?void 0:n.retryAfterMs)?{retryAfterMs:null===(o=h(l))||void 0===o?void 0:o.retryAfterMs}:{}}),this.scheduleRetry(l,p.attempt,p.maxRetries,u)):this.handleNoRetriesAction(e,p.retryable)},this.options=e}async scheduleRetry(e,t,r,s=!1){var i,n,o;this.options.markRetryProcessStart(),g(e,{retryAttempt:t,isRetrying:!0});const u=h(e),l=this.options.retryScheduler.getRetryDelay(e,Number(null!==(i=null==u?void 0:u.retryAttempt)&&void 0!==i?i:t),r);return this.options.logger.debug("Scheduling retry attempt",{requestId:null==u?void 0:u.requestId,attempt:t,maxRetries:r,delayMs:l,backoffType:null!==(n=null==u?void 0:u.backoffType)&&void 0!==n?n:"default"}),this.options.emitEvent("onRetryScheduled",l,e),await this.options.retryScheduler.waitForRetryDelay(e,l)?(this.options.logger.debug("Executing retry attempt",{requestId:null==u?void 0:u.requestId,timeSinceFirstAttempt:Date.now()-((null==u?void 0:u.timestamp)||0)}),(null==u?void 0:u.requestId)&&this.options.requestLifecycle.removeById(u.requestId),s||(null===(o=e.signal)||void 0===o?void 0:o.aborted)?(this.options.logger.warn("Retry cancelled",{requestId:null==u?void 0:u.requestId,source:s?"queue":"user"}),this.handleCancelAction(e)):(this.options.emitEvent("beforeRetry",e),this.options.axiosInstance.request(e))):this.handleCancelAction(e)}handleCancelAction(e){var t,r;return d(e,"isRetrying",!1),this.options.dependencyGatekeeper.finishBlockingRequest(e,"cancel"),this.options.logger.warn("Handling request cancellation",{requestId:null===(t=h(e))||void 0===t?void 0:t.requestId}),this.options.handleRetryProcessFinish(),this.options.throwErrorOnCancelRequest?Promise.reject(new O(null===(r=h(e))||void 0===r?void 0:r.requestId)):Promise.resolve(null)}handleNoRetriesAction(e,t=!1){var r,s,i;const n=e.config;d(n,"isRetrying",!1);const o=h(n),u=(null!==(r=null==o?void 0:o.retryAttempt)&&void 0!==r?r:0)+1;return this.options.logger.warn("Final request failure",{requestId:null==o?void 0:o.requestId,finalAttempt:(null==o?void 0:o.retryAttempt)||0,attempts:u,retryable:t}),this.options.emitEvent("onFailure",n),this.options.emitEvent("onRequestError",{error:e,config:n,status:null!==(i=null===(s=e.response)||void 0===s?void 0:s.status)&&void 0!==i?i:null,requestId:null==o?void 0:o.requestId,attempts:u,retryable:t}),this.options.dependencyGatekeeper.finishBlockingRequest(n,"failure"),(null==o?void 0:o.requestId)&&this.options.requestLifecycle.removeById(o.requestId),this.options.handleRetryProcessFinish(),e.response||this.options.emitEvent("onInternetConnectionError",n),this.options.throwErrorOnFailedRetries?Promise.reject(e):Promise.resolve(null)}buildErrorMeta(e,t){var r,s,i;const n=h(e);return{requestId:null==n?void 0:n.requestId,correlationId:null==n?void 0:n.correlationId,url:this.getLogUrl(e.url),method:null===(r=e.method)||void 0===r?void 0:r.toUpperCase(),status:null===(s=t.response)||void 0===s?void 0:s.status,statusText:null===(i=t.response)||void 0===i?void 0:i.statusText,code:t.code,message:t.message,retrying:null==n?void 0:n.isRetrying}}getLogUrl(e){if(!e)return e;const t=e.indexOf("?"),r=e.indexOf("#");return t<0&&r<0?e:e.slice(0,t<0?r:r<0?t:Math.min(t,r))}}const B={MODE:r.AUTOMATIC,RETRIES:3,THROW_ON_FAILED_RETRIES:!0,THROW_ON_CANCEL:!0,DEBUG:!1,MAX_CONCURRENT_REQUESTS:5,MAX_QUEUE_SIZE:1e3,CANCEL_PENDING_ON_DEPENDENCY_FAILURE:!0},G={totalRequests:0,successfulRetries:0,failedRetries:0,completelyFailedRequests:0,canceledRequests:0,completelyFailedCriticalRequests:0,errorTypesDistribution:{network:0,server5xx:0,client4xx:0,cancelled:0},retryAttemptsDistribution:{},requestCountsByPriority:{},avgQueueWait:0,avgRetryDelay:0,priorityMetrics:[],timerHealth:{activeTimers:0,activeRetryTimers:0,healthScore:0}};class j{constructor(e={}){var t,r,s,i,n,u,l,a,c,h,d;this._metricsRecorder=null,this.inRetryProgress=!1,this.requestInterceptorId=null,this.responseInterceptorId=null,this.setupInterceptors=()=>{this.logger.debug("Setting up Axios interceptors"),this.requestInterceptorId=this._axiosInstance.interceptors.request.use(this.requestInterceptorHandler.handleRequest,e=>(this.logger.error("Request interceptor error",{message:e.message,code:e.code,...this.debug?{stack:e.stack}:{}}),Promise.reject(e))),this.responseInterceptorId=this._axiosInstance.interceptors.response.use(this.responseInterceptorHandler.handleResponse,this.errorInterceptorHandler.handleError)},this.ejectRetryerInterceptors=()=>{null!==this.requestInterceptorId&&(this._axiosInstance.interceptors.request.eject(this.requestInterceptorId),this.requestInterceptorId=null),null!==this.responseInterceptorId&&(this._axiosInstance.interceptors.response.eject(this.responseInterceptorId),this.responseInterceptorId=null)},this.handleRetryProcessFinish=()=>{0===this.requestLifecycle.getActiveCount()&&this.inRetryProgress&&(this.logger.debug("Retry process finished"),this.triggerAndEmitInternal("onRetryProcessFinished"),this.inRetryProgress=!1)},this.triggerAndEmitInternal=(e,...t)=>{this.eventBus.triggerAndEmit(e,...t)},this.emit=(e,...t)=>{this.eventBus.emit(e,...t)},this.triggerAndEmit=(e,...t)=>{this.eventBus.triggerAndEmit(e,...t)},this.use=(e,t)=>(this.pluginRegistry.use(e,this._pluginContext,{ejectRetryerInterceptors:this.ejectRetryerInterceptors,installRetryerInterceptors:this.setupInterceptors},null!=t?t:"afterRetryer"!==e.interceptorPlacement),this),this.unuse=e=>this.pluginRegistry.unuse(e,this._pluginContext),this.listPlugins=()=>this.pluginRegistry.list(),this.on=(e,t)=>(this.eventBus.on(e,t),this),this.off=(e,t)=>this.eventBus.off(e,t),this.cancelRequest=e=>{const t=this.requestLifecycle.getActiveRequests().has(e);this.requestLifecycle.cancelRequest(e),t&&this.requestQueue.markComplete(e)},this.cancelAllRequests=()=>{this.requestLifecycle.cancelAllRequests()},this.cancelQueuedRequests=()=>{this.requestLifecycle.cancelQueuedRequests()},this.destroy=()=>{this.disposer.destroy(this._pluginContext)},this.getMetrics=()=>{this.logger.debug("Generating metrics snapshot");const e=this.retryScheduler.getTimerStats();return this._metricsRecorder?this._metricsRecorder.buildDetailedMetrics(e):{...G,timerHealth:{...e,healthScore:0}}},this.resetMetrics=()=>{var e,t,r;this.logger.debug("Resetting metrics state"),null===(e=this._metricsRecorder)||void 0===e||e.reset(),null===(r=null===(t=this._metricsRecorder)||void 0===t?void 0:t.emitMetricsUpdated)||void 0===r||r.call(t)},this.debug=null!==(t=e.debug)&&void 0!==t?t:B.DEBUG,this.logger=null!==(r=e.logger)&&void 0!==r?r:new o(this.debug),this.validateOptions(e),this.logger.debug("Initializing RetryManager",{options:{mode:e.mode,retries:e.retries,maxConcurrent:e.maxConcurrentRequests,maxQueueSize:e.maxQueueSize}}),this.mode=null!==(s=e.mode)&&void 0!==s?s:B.MODE,this.retries=null!==(i=e.retries)&&void 0!==i?i:B.RETRIES,this.throwErrorOnFailedRetries=null!==(n=e.throwErrorOnFailedRetries)&&void 0!==n?n:B.THROW_ON_FAILED_RETRIES,this.throwErrorOnCancelRequest=null!==(u=e.throwErrorOnCancelRequest)&&void 0!==u?u:B.THROW_ON_CANCEL,this.blockingPriorityThreshold=e.blockingPriorityThreshold,this.cancelPendingOnDependencyFailure=null!==(l=e.cancelPendingOnDependencyFailure)&&void 0!==l?l:B.CANCEL_PENDING_ON_DEPENDENCY_FAILURE,this.retryStrategy=null!==(a=e.retryStrategy)&&void 0!==a?a:new v(e.retryableStatuses,e.retryableMethods,e.backoffType,void 0,this.logger,e.maxBackoffDelayMs),this.requestQueue=new P({maxConcurrent:null!==(c=e.maxConcurrentRequests)&&void 0!==c?c:B.MAX_CONCURRENT_REQUESTS,queueDelay:e.queueDelay,maxQueueSize:null!==(h=e.maxQueueSize)&&void 0!==h?h:B.MAX_QUEUE_SIZE,logger:this.logger}),this._axiosInstance=e.axiosInstance||this.createAxiosInstance(),this.pluginRegistry=new q(this.logger),this.eventBus=new m(this.logger,{strictListenerLimit:null!==(d=e.strictListenerLimit)&&void 0!==d&&d}),this.retryScheduler=new k(this.logger,this.retryStrategy,this.triggerAndEmitInternal),this.requestLifecycle=new I({logger:this.logger,requestQueue:this.requestQueue,retryScheduler:this.retryScheduler,onRequestCancelled:e=>{this.dependencyGatekeeper.handleRequestCancelled(e),this.triggerAndEmitInternal("onRequestCancelled",e)}}),this.dependencyGatekeeper=new _({blockingPriorityThreshold:this.blockingPriorityThreshold,cancelPendingOnDependencyFailure:this.cancelPendingOnDependencyFailure,requestQueue:this.requestQueue,requestLifecycle:this.requestLifecycle,emitEvent:this.triggerAndEmitInternal}),this.requestInterceptorHandler=new M({logger:this.logger,requestLifecycle:this.requestLifecycle,dependencyGatekeeper:this.dependencyGatekeeper,requestQueue:this.requestQueue,throwErrorOnCancelRequest:this.throwErrorOnCancelRequest,createSilentCancelConfig:(e,t)=>this.createSilentCancelConfig(e,t),emitEvent:this.triggerAndEmitInternal}),this.responseInterceptorHandler=new N({logger:this.logger,requestLifecycle:this.requestLifecycle,dependencyGatekeeper:this.dependencyGatekeeper,requestQueue:this.requestQueue,emitEvent:this.triggerAndEmitInternal,handleRetryProcessFinish:this.handleRetryProcessFinish}),this.errorInterceptorHandler=new F({axiosInstance:this._axiosInstance,logger:this.logger,requestLifecycle:this.requestLifecycle,dependencyGatekeeper:this.dependencyGatekeeper,requestQueue:this.requestQueue,retryScheduler:this.retryScheduler,retryStrategy:this.retryStrategy,emitEvent:this.triggerAndEmitInternal,markRetryProcessStart:()=>{this.inRetryProgress||(this.logger.debug("Starting retry process"),this.triggerAndEmitInternal("onRetryProcessStarted"),this.inRetryProgress=!0)},handleRetryProcessFinish:this.handleRetryProcessFinish,retries:this.retries,mode:this.mode,throwErrorOnFailedRetries:this.throwErrorOnFailedRetries,throwErrorOnCancelRequest:this.throwErrorOnCancelRequest}),this._pluginContext=this.createPluginContext(),this.disposer=new b({logger:this.logger,requestLifecycle:this.requestLifecycle,requestQueue:this.requestQueue,retryScheduler:this.retryScheduler,ejectRetryerInterceptors:this.ejectRetryerInterceptors,pluginRegistry:this.pluginRegistry,eventBus:this.eventBus}),this.setupInterceptors(),this.logger.debug("RetryManager initialized successfully")}validateOptions(e){if(void 0!==e.retries&&e.retries<0)throw this.logger.error("Invalid retries configuration",{retries:e.retries}),new C("Retries must be a non-negative number","retries",e.retries);this.assertPositiveIntegerOption(e.maxConcurrentRequests,"maxConcurrentRequests"),this.assertPositiveIntegerOption(e.maxQueueSize,"maxQueueSize"),this.assertNonNegativeIntegerOption(e.queueDelay,"queueDelay"),this.assertPositiveIntegerOption(e.maxBackoffDelayMs,"maxBackoffDelayMs")}assertPositiveIntegerOption(e,t){if(void 0!==e&&(!Number.isInteger(e)||e<1))throw this.logger.error(`Invalid ${t} configuration`,{[t]:e}),new C(`${t} must be a positive integer`,t,e)}assertNonNegativeIntegerOption(e,t){if(void 0!==e&&(!Number.isInteger(e)||e<0))throw this.logger.error(`Invalid ${t} configuration`,{[t]:e}),new C(`${t} must be a non-negative integer`,t,e)}createAxiosInstance(){return this.logger.debug("Creating default Axios instance"),e.create({timeout:3e4,validateStatus:e=>e>=200&&e<300})}createSilentCancelConfig(e,t){const r={...e,cancelToken:void 0,signal:void 0},s=h(e);return s&&g(r,s),g(r,{requestId:t,isRetrying:!1,silentlyCancelled:!0}),r.adapter=async()=>this.createSilentCancelResponse(r),r}createSilentCancelResponse(e){return{config:e,data:null,headers:{},status:204,statusText:"Request Aborted"}}getLogger(){return this.logger}get axiosInstance(){return this._axiosInstance}createPluginContext(){const e=this._axiosInstance;return{get axiosInstance(){return e},getLogger:()=>this.logger,on:(e,t)=>{this.eventBus.on(e,t)},off:(e,t)=>this.eventBus.off(e,t),emit:(e,...t)=>{this.eventBus.emit(e,...t)},triggerAndEmit:(e,...t)=>{this.eventBus.triggerAndEmit(e,...t)},cancelRequest:e=>this.requestLifecycle.cancelRequest(e),cancelAllRequests:()=>this.requestLifecycle.cancelAllRequests(),cancelQueuedRequests:()=>this.requestLifecycle.cancelQueuedRequests(),registerQueueGate:(e,t)=>this.requestQueue.registerProcessingGate(e,t),unregisterQueueGate:e=>this.requestQueue.unregisterProcessingGate(e),refreshQueue:()=>this.requestQueue.refresh(),registerMetricsRecorder:e=>{this._metricsRecorder=e},getTimerStats:()=>this.retryScheduler.getTimerStats(),releaseRequestTracking:e=>{const t=this.requestLifecycle.release(e);t.released&&this.requestQueue.markComplete(t.requestId)}}}}function $(e){return new j(e)}function H(e={}){const t=new v;return{getIsRetryable:r=>e.isRetryable?e.isRetryable(r):t.getIsRetryable(r),shouldRetry:(r,s,i)=>e.shouldRetry?e.shouldRetry(r,s,i):t.shouldRetry(r,s,i),getDelay:(r,s,i)=>e.getDelay?e.getDelay(r,s,i):t.getDelay(r,s,i)}}export{n as AXIOS_RETRYER_BACKOFF_TYPES,s as AXIOS_RETRYER_HTTP_METHODS,i as AXIOS_RETRYER_REQUEST_PRIORITIES,R as AxiosRetryerError,v as DefaultRetryStrategy,f as PluginRegistrationError,S as QueueClearedError,w as QueueDestroyedError,T as QueueFullError,A as QueuedRequestCanceledError,r as RETRY_MODES,O as RequestAbortedError,j as RetryManager,C as RetryerConfigError,H as createRetryStrategy,$ as createRetryer};