UNPKG

mollitia

Version:

JavaScript Resilience Library

1,146 lines (1,145 loc) 40.3 kB
var R = Object.defineProperty; var _ = (l, r, e) => r in l ? R(l, r, { enumerable: !0, configurable: !0, writable: !0, value: e }) : l[r] = e; var i = (l, r, e) => (_(l, typeof r != "symbol" ? r + "" : r, e), e); const w = [], A = (l) => { w.push(l); }; class $ { } class M { // Constructor constructor(r, e, t) { // Private Attributes i(this, "listeners"); i(this, "eventName"); i(this, "index"); this.listeners = r, this.eventName = e, this.index = t; } // Public Methods /** * Unsubscribes to the event. */ dispose() { this.listeners[this.eventName].splice(this.index, 1); } } class C { // Constructor constructor() { // Private Attributes i(this, "listeners"); this.listeners = {}; } // Public Methods /** * Subscribes to an event. * @param {string} eventName Event name. * @param {EventCallback} cb Event callback. */ on(r, e) { this.listeners[r] = this.listeners[r] || []; const t = this.listeners[r].push(e); return new M(this.listeners, r, t - 1); } /** * Unsubscribes to an event. * @param {string} eventName Event name. * @param {EventCallback} cb Event callback. */ off(r, e) { const t = this.listeners[r]; if (t) { for (let s = t.length - 1; s > 0; s--) if (t[s] === e) { t.splice(s, 1); break; } } } /** * Emits an event. * @param {string} eventName Event name. * @param args Optional parameters. */ emit(r, ...e) { const t = this.listeners[r]; return t ? (t.forEach((s) => { s(...e); }), !0) : !1; } dispose() { this.listeners = {}; } } class y extends Error { constructor() { super("Circuit has no function set"), Object.setPrototypeOf(this, y.prototype); } } class W { constructor() { /** * Module list, by order of execution. */ i(this, "modules"); } } class L { constructor() { /** * The Circuit name. */ i(this, "name"); /** * The Circuit function. */ i(this, "func"); /** * The Circuit options. */ i(this, "options"); } } const E = async () => Promise.reject(new y()), I = []; class V extends C { // Constructor constructor(e) { var t; super(); // Public Attributes /** * The Circuit name. */ i(this, "name"); /** * The Circuit function. */ i(this, "func"); /** * Module list, by order of execution. */ i(this, "modules"); this.name = e != null && e.name ? e.name : `Circuit${I.length}`; for (const s of w) s.onCircuitCreate && s.onCircuitCreate(this, e == null ? void 0 : e.options); this.func = e != null && e.func ? e.func : E, this.modules = ((t = e == null ? void 0 : e.options) == null ? void 0 : t.modules) || [], I.push(this); } // Computed get activeModules() { return this.modules.filter((e) => e.active); } // Public Methods /** * Modifies the Circuit function. * @param {CircuitFunction} func The Circuit function. */ fn(e) { return this.func = e, this; } /** * Executes the Circuit function. * @param params Eventual parameters to pass to the Circuit function. */ async execute(...e) { let t; if (this.activeModules.length) if (this.activeModules.length > 1) { const s = []; for (let a = 2; a < this.activeModules.length; a++) s.push(this, this.activeModules[a].execute.bind(this.activeModules[a])); s.push(this, this.func, ...e), t = this.activeModules[0].execute(this, this.activeModules[1].execute.bind(this.activeModules[1]), ...s); } else t = this.activeModules[0].execute(this, this.func, ...e); else t = this.func(...e); return this.emit("execute", this, t, e), t; } /** * Disposes the Circuit, to cleanup every interval and timeouts. */ dispose() { super.dispose(), this.modules && this.modules.forEach((e) => e.dispose()); } } class g { constructor() { i(this, "name"); i(this, "active"); i(this, "logger"); } } const p = []; class f extends C { // Constructor constructor(e) { super(); // Public Attributes /** * The Module name. */ i(this, "name"); /** * Whether the Module is active or not. */ i(this, "active"); /** * The Module logger, for monitoring. */ i(this, "logger"); this.active = (e == null ? void 0 : e.active) !== void 0 ? e.active : !0, this.name = (e == null ? void 0 : e.name) !== void 0 ? e.name : `Module${p.length}`; for (const t of w) t.onModuleCreate && t.onModuleCreate(this, e); this.logger = e == null ? void 0 : e.logger, p.push(this); } /** * Called when the module is executed by the circuit. * @param circuit The Circuit reference. * @param promise The Circuit function. * @param params The Eventual parameters to use with the Circuit function. * @example * // Empty code to execute the function and emit the execute event * const _exec = promise(...params); * this.emit('execute', circuit, _exec); * return _exec; */ execute(e, t, ...s) { const a = t(...s), h = this.getExecParams(e, s); return this.emit("execute", e, a, h), a; } /** * Returns params passed to the execute method. * @param circuit The Circuit reference. * @param params The Eventual parameters to use with the Circuit function. * @example * const _params = this.getExecParams(circuit, params); */ getExecParams(e, t) { const s = e.modules.findIndex((h) => h === this), a = t.length - (e.modules.length - 1 - s) * 2; return t.filter((h, n) => t.length - n <= a); } } class U extends g { constructor() { super(...arguments); /** * The amount of time before a promise is rejected. */ i(this, "delay"); } } class S extends Error { constructor() { super("Timed out"), Object.setPrototypeOf(this, S.prototype); } } class Q extends f { // Constructor constructor(e) { super(e); // Public Attributes /** * The amount of time before a promise is rejected. */ i(this, "delay"); this.delay = (e == null ? void 0 : e.delay) !== void 0 ? e.delay : 6e4; } // Public Methods async execute(e, t, ...s) { const a = this._promiseTimeout(e, this.delay, t, ...s), h = this.getExecParams(e, s); return this.emit("execute", e, a, h), a; } // Private Methods async _promiseTimeout(e, t, s, ...a) { let h; return t !== 0 && t !== 1 / 0 ? Promise.race([ s(...a), new Promise((n, u) => { h = setTimeout(() => { this.emitTimeout(e), u(new S()); }, t); }) ]).then((n) => (clearTimeout(h), n)).catch((n) => (clearTimeout(h), Promise.reject(n))) : s(...a); } emitTimeout(e) { var t; (t = this.logger) == null || t.debug(`${e.name}/${this.name} - Has timed out`), this.emit("timeout", e); } } async function P(l = 1) { return new Promise((r) => { setTimeout(() => { r(); }, l); }); } var F = /* @__PURE__ */ ((l) => (l.CONSTANT = "constant", l.LINEAR = "linear", l.EXPONENTIAL = "exponential", l.JITTER = "jitter", l))(F || {}); class J extends g { constructor() { super(...arguments); /** * The number of retry attempts (the function will be called attempts + 1 times). */ i(this, "attempts"); /** * The amount of time to wait before retrying. */ i(this, "interval"); /** * The mode for the retry (constant, linear, exponential or jitter) */ i(this, "mode"); /** * The factor to be applied for the retry mode */ i(this, "factor"); /** * The maximum amount of time to wait before retrying another time */ i(this, "maxInterval"); /** * Should the first retry be done directly after a failure occurred */ i(this, "fastFirst"); /** * The value (between 0 and 1) to adjust delay randomly based on jitter retry duration */ i(this, "jitterAdjustment"); /** * A filtering callback, to modify the retry behavior. * @returns true (retries), false (rejects), number (retries after some delay) */ i(this, "onRejection"); } } class X extends f { // Constructor constructor(e) { super(e); // Public Attributes /** * The number of retry attempts (the function will be called attempts + 1 times). */ i(this, "attempts"); /** * The amount of time to wait before retrying. */ i(this, "interval"); /** * The mode for the retry (constant, linear, exponential or jitter) */ i(this, "mode"); /** * The factor to be applied for the retry mode */ i(this, "factor"); /** * The maximum amount of time to wait before retrying another time */ i(this, "maxInterval"); /** * Should the first retry be done directly after a failure occurred */ i(this, "fastFirst"); /** * The value to adjust delay randomly based on jitter retry duration */ i(this, "_jitterAdjustment"); /** * A filtering callback, to modify the retry behavior. * @returns true (retries), false (rejects), number (retries after some delay) */ i(this, "onRejection"); this.attempts = (e == null ? void 0 : e.attempts) ?? 2, this.interval = (e == null ? void 0 : e.interval) ?? 0, this.fastFirst = (e == null ? void 0 : e.fastFirst) ?? !1, this.maxInterval = (e == null ? void 0 : e.maxInterval) ?? 1 / 0, this.mode = (e == null ? void 0 : e.mode) ?? "constant", this._jitterAdjustment = this.adjustJitterValue((e == null ? void 0 : e.jitterAdjustment) ?? 0.1); const t = this.mode === "linear" ? 1 : 2; this.factor = (e == null ? void 0 : e.factor) ?? t, this.onRejection = (e == null ? void 0 : e.onRejection) || (() => !0); } /** * The value (between 0 and 1) to adjust delay randomly based on jitter retry duration */ get jitterAdjustment() { return this._jitterAdjustment; } /** * Set the value (between 0 and 1) for jitter adjustment (to adjust delay randomly). */ set jitterAdjustment(e) { this._jitterAdjustment = this.adjustJitterValue(e); } adjustJitterValue(e) { return Math.min(1, Math.max(0, e)); } // Public Methods async execute(e, t, ...s) { const a = this._promiseRetry(e, this.attempts + 1, t, ...s), h = this.getExecParams(e, s); return this.emit("execute", e, a, h), a; } // Private Methods async waitBeforeNextTry(e, t) { let s = 0; if (this.fastFirst) { if (e === 0) return this.emit("delay-before-next-retry", t, 0), Promise.resolve(); e--; } switch (this.mode) { case "linear": { s = Math.min(this.interval + this.interval * this.factor * e, this.maxInterval); break; } case "exponential": { s = Math.min(this.interval * this.factor ** e, this.maxInterval); break; } case "jitter": { const a = Math.min(this.interval * this.factor ** e, this.maxInterval), h = a * (1 - this.jitterAdjustment), u = Math.min(a * (1 + this.jitterAdjustment), this.maxInterval) - h; s = Math.floor(Math.random() * u) + h; break; } case "constant": default: { s = Math.min(this.interval, this.maxInterval); break; } } return this.emit("delay-before-next-retry", t, s), P(s); } async _promiseRetry(e, t, s, ...a) { var h, n; return t - 1 === 0 ? (this.attempts && (this.emit("retry", e, this.attempts), (h = this.logger) == null || h.debug(`${e.name}/${this.name} - Retry: (${this.attempts}/${this.attempts})`)), s(...a).then((u) => (this.attempts > 0 ? this.emit("success-with-retry", e, this.attempts) : this.emit("success-without-retry", e), u)).catch((u) => { throw this.attempts > 0 ? this.emit("failure-with-retry", e, this.attempts) : this.emit("failure-without-retry", e), u; })) : (t !== this.attempts + 1 && (this.emit("retry", e, this.attempts - t + 1), (n = this.logger) == null || n.debug(`${e.name}/${this.name} - Retry: (${this.attempts - t + 1}/${this.attempts})`)), s(...a).then((u) => (t !== this.attempts + 1 ? this.emit("success-with-retry", e, this.attempts - t + 1) : this.emit("success-without-retry", e), u)).catch(async (u) => { const c = this.onRejection(u, this.attempts - t + 1); return c === !1 ? (t !== this.attempts + 1 ? this.emit("failure-with-retry", e, this.attempts - t + 1) : this.emit("failure-without-retry", e), Promise.reject(u)) : (typeof c == "number" ? (this.emit("delay-before-next-retry", e, c), await P(c)) : await this.waitBeforeNextTry(this.attempts - t + 1, e), this._promiseRetry(e, t - 1, s, ...a)); })); } } class K extends g { constructor() { super(...arguments); /** * The callback, called when the circuit rejects, can be used to reject another error. */ i(this, "callback", (e) => e); } } class G extends f { // Constructor constructor(e) { super(e); // Public Attributes /** * The callback, called when the circuit rejects, can be used to reject another error. */ i(this, "callback"); this.callback = (e == null ? void 0 : e.callback) || ((t) => t); } // Public Methods async execute(e, t, ...s) { const a = this._promiseFallback(e, t, ...s), h = this.getExecParams(e, s); return this.emit("execute", e, a, h), a; } // Private Methods async _promiseFallback(e, t, ...s) { return new Promise((a, h) => { t(...s).then((n) => { a(n); }).catch((n) => { h(this.callback(n)); }); }); } } class k { constructor(r, e) { i(this, "ttl"); i(this, "res"); this.ttl = r, this.res = e; } } class B { // Constructor constructor() { // Private Attributes i(this, "map"); this.map = /* @__PURE__ */ new Map(); } // Public Methods set(r, ...e) { this._setLoopMap(this.map, r, ...e); } get(...r) { return this._getLoopMap(this.map, ...r); } clear() { return this._clearLoopMap(this.map); } // Private Methods _setLoopMap(r, e, ...t) { if (t.length === 2) { const s = { map: /* @__PURE__ */ new Map(), cache: new k(Date.now() + e, t[1]) }; r.set(t[0], s); } else if (r.get(t[0])) { const s = t.splice(0, 1)[0]; this._setLoopMap(r.get(s).map, e, ...t); } else { const s = /* @__PURE__ */ new Map(); r.set(t[0], { map: s }), t.splice(0, 1), this._setLoopMap(s, e, ...t); } } _getLoopMap(r, ...e) { if (r) { if (e.length === 1) return r.get(e[0]) && r.get(e[0]).cache; { const t = e.splice(0, 1)[0]; return r.get(t) ? this._getLoopMap(r.get(t).map, ...e) : null; } } else return null; } _clearLoopMap(r) { let e = !1; return r.forEach((t) => { t.map && this._clearLoopMap(t.map) === !0 && (e = !0), t.cache && Date.now() > t.cache.ttl && (delete t.cache, e = !0); }), e; } } class Y extends g { constructor() { super(...arguments); /** * The amount of time during which a cached result is considered valid. */ i(this, "ttl"); /** * The amount of time before the cache cleans itself up. */ i(this, "cacheClearInterval"); /** * The attribute name indicating if data is retrieved from cache or not */ i(this, "getInformationFromCache"); /** * A filtering callback, to modify the parameters used for Cache Key. * @returns The modified parameters */ i(this, "adjustCacheParams"); } } class Z extends f { // Constructor constructor(e) { super(e); // Public Attributes /** * The amount of time during which a cached result is considered valid. */ i(this, "ttl"); /** * The attribute name indicating if data is retrieved from cache or not */ i(this, "getInformationFromCache"); /** * A filtering callback, to modify the parameters used for Cache Key. */ i(this, "adjustCacheParams"); // Private Attributes i(this, "cache"); i(this, "_cacheClearInterval"); i(this, "_cacheInterval"); this.ttl = (e == null ? void 0 : e.ttl) !== void 0 ? e.ttl : 6e3, this.getInformationFromCache = (e == null ? void 0 : e.getInformationFromCache) !== void 0 ? e.getInformationFromCache : !1, this.adjustCacheParams = (e == null ? void 0 : e.adjustCacheParams) || null, this._cacheInterval = null, this._cacheClearInterval = 0, this.cacheClearInterval = (e == null ? void 0 : e.cacheClearInterval) !== void 0 ? e.cacheClearInterval : 9e5, this.cache = new B(); } // Computed Attributes /** * Get the amount of time before the cache cleans itself up. */ get cacheClearInterval() { return this._cacheClearInterval; } /** * Set the amount of time before the cache cleans itself up. */ set cacheClearInterval(e) { this._cacheClearInterval = e, this._initializeInterval(); } // Public Methods async execute(e, t, ...s) { const a = this._promiseCache(e, t, ...s), h = this.getExecParams(e, s); return this.emit("execute", e, a, h), a; } dispose() { super.dispose(), this._cacheInterval && (clearTimeout(this._cacheInterval), this._cacheInterval = null); } // Private Methods async _promiseCache(e, t, ...s) { return new Promise((a, h) => { var o; const n = this.getExecParams(e, s); let u = n; this.adjustCacheParams && (u = this.adjustCacheParams(e.func, ...n)); const c = this.cache.get(e.func, ...u); if (c) { typeof c.res == "object" && this.getInformationFromCache && (c.res._mollitiaIsFromCache = !0); const m = Date.now(); this.ttl !== 1 / 0 && c.ttl < m ? t(...s).then((d) => { this.ttl > 0 && this.cache.set(this.ttl, e.func, ...u, d), typeof d == "object" && this.getInformationFromCache && (d._mollitiaIsFromCache = !1), a(d); }).catch(() => { var d; (d = this.logger) == null || d.debug(`${e.name}/${this.name} - Cache: Hit [Old]`), this.emit("cache-hit-old", e, u), a(c.res); }) : ((o = this.logger) == null || o.debug(`${e.name}/${this.name} - Cache: Hit`), this.emit("cache-hit", e, u), a(c.res)); } else t(...s).then((m) => { this.ttl > 0 && this.cache.set(this.ttl, e.func, ...u, m), typeof m == "object" && this.getInformationFromCache && (m._mollitiaIsFromCache = !1), a(m); }).catch((m) => { h(m); }); }); } _initializeInterval() { this._cacheInterval && (clearTimeout(this._cacheInterval), this._cacheInterval = null), this.cacheClearInterval !== 0 && this.cacheClearInterval !== 1 / 0 && (this._cacheInterval = setTimeout(() => { var t; this.cache.clear() && ((t = this.logger) == null || t.debug(`${this.name} - Cache: Clear`)), this._initializeInterval(); }, this.cacheClearInterval)); } } const z = 2147483647; class ee extends g { constructor() { super(...arguments); /** * The number of concurrent requests that can be running in parallel. */ i(this, "concurrentSize"); /** * The number of requests that can be queued. */ i(this, "queueSize"); /** * The amount of time before a queued request is rejected. */ i(this, "maxQueueWait"); } } class v extends Error { constructor() { super("Circuit is overloaded"), Object.setPrototypeOf(this, v.prototype); } } class T extends Error { constructor() { super("Waiting for too long in queue"), Object.setPrototypeOf(this, T.prototype); } } class H extends C { constructor(e, ...t) { super(); i(this, "promise"); i(this, "params"); this.promise = e, this.params = t; } async execute() { return new Promise((e, t) => { this.emit("execute"), this.promise(...this.params).then((s) => { this.emit("resolve", s), e(s); }).catch((s) => { this.emit("reject", s), t(s); }); }); } } class te extends f { // Constructor constructor(e) { super(e); // Public Attributes /** * The number of concurrent requests that can be running in parallel. */ i(this, "concurrentSize"); /** * The number of requests that can be queued. */ i(this, "queueSize"); /** * The amount of time before a queued request is rejected. */ i(this, "maxQueueWait"); // Private Attributes i(this, "concurrentBuffer"); i(this, "queueBuffer"); this.concurrentSize = (e == null ? void 0 : e.concurrentSize) !== void 0 ? e.concurrentSize : 10, this.queueSize = (e == null ? void 0 : e.queueSize) !== void 0 ? e.queueSize : 10, this.maxQueueWait = (e == null ? void 0 : e.maxQueueWait) !== void 0 ? e.maxQueueWait : 6e4, this.concurrentBuffer = [], this.queueBuffer = []; } // Public Methods async execute(e, t, ...s) { const a = this._promiseBulkhead(e, t, ...s), h = this.getExecParams(e, s); return this.emit("execute", e, a, h), a; } // Private Methods async _promiseBulkhead(e, t, ...s) { return new Promise((a, h) => { const n = new H(t, ...s); if (this.concurrentBuffer.length < this.concurrentSize) n.execute().then((u) => { a(u); }).catch((u) => { h(u); }).finally(() => { this.concurrentBuffer.splice(this.concurrentBuffer.indexOf(n), 1), this.emit("update-concurrent-buffer", e, this.concurrentBuffer), this._addBufferedPromise(e); }), this.concurrentBuffer.push(n), this.emit("update-concurrent-buffer", e, this.concurrentBuffer); else if (this.queueBuffer.length < this.queueSize) { this.queueBuffer.push(n), this.emit("update-queue-buffer", e, this.queueBuffer); let u; this.maxQueueWait <= z && (u = setTimeout(() => { this.queueBuffer.splice(this.queueBuffer.indexOf(n), 1), this.emit("update-queue-buffer", e, this.queueBuffer), o.dispose(), m.dispose(), h(new T()); }, this.maxQueueWait)); const c = n.on("execute", () => { c.dispose(), clearTimeout(u); }), o = n.on("resolve", (d) => { clearTimeout(u), this.concurrentBuffer.splice(this.concurrentBuffer.indexOf(n), 1), this.emit("update-concurrent-buffer", e, this.concurrentBuffer), o.dispose(), m.dispose(), this._addBufferedPromise(e), a(d); }), m = n.on("reject", (d) => { clearTimeout(u), this.concurrentBuffer.splice(this.concurrentBuffer.indexOf(n), 1), this.emit("update-concurrent-buffer", e, this.concurrentBuffer), o.dispose(), m.dispose(), this._addBufferedPromise(e), h(d); }); } else h(new v()); }); } _addBufferedPromise(e) { if (this.queueBuffer.length > 0) { const t = this.queueBuffer.splice(0, 1)[0]; this.emit("update-queue-buffer", e, this.queueBuffer), t.execute().catch(() => { }), this.concurrentBuffer.push(t), this.emit("update-concurrent-buffer", e, this.concurrentBuffer); } } } class O extends Error { constructor(e, t) { super("Ratelimited"); i(this, "remainingTimeInRatelimit"); this.name = t, this.remainingTimeInRatelimit = e, Object.setPrototypeOf(this, O.prototype); } } class se extends f { // Constructor constructor(e) { super(e); // Public Attributes /** * Specifies the time period during which the ratelimit is calculated */ i(this, "limitPeriod"); /** * Specifies the maximum number of requests during the period */ i(this, "limitForPeriod"); // Private Attributes i(this, "requestsTime"); this.limitPeriod = (e == null ? void 0 : e.limitPeriod) !== void 0 ? e.limitPeriod : 0, this.limitForPeriod = (e == null ? void 0 : e.limitForPeriod) !== void 0 ? e.limitForPeriod : 1 / 0, this.requestsTime = []; } async getState() { return new Promise((e) => { e({ requests: this.requestsTime }); }); } async setState(e, t) { return new Promise((s) => { s(); }); } async clearState() { return new Promise((e) => { this.requestsTime = [], e(); }); } // Public Methods async execute(e, t, ...s) { var u; let a; try { a = await this.getState(); } catch (c) { (u = this.logger) == null || u.warn(c); } a != null && a.requests && (this.requestsTime = a == null ? void 0 : a.requests); const h = this._promiseRatelimit(e, t, ...s), n = this.getExecParams(e, s); return this.emit("execute", e, h, n), h; } async addCurrentRequest(e) { var t; this.requestsTime.push(e); try { await this.setState([{ key: "requests", value: this.requestsTime }], this.limitPeriod); } catch (s) { (t = this.logger) == null || t.warn(s); } } // Private Methods async _promiseRatelimit(e, t, ...s) { var h; if (!this.limitPeriod) return t(...s); const a = (/* @__PURE__ */ new Date()).getTime(); if (this.requestsTime.length < this.limitForPeriod) return await this.addCurrentRequest(a), t(...s); { const n = a - this.requestsTime[0]; return n > this.limitPeriod ? (this.requestsTime.splice(0, 1), await this.addCurrentRequest(a), t(...s)) : ((h = this.logger) == null || h.debug(`${e.name}/${this.name} - Ratelimited`), this.emit("ratelimit", e), Promise.reject(new O(this.limitPeriod - n, this.name))); } } } class q extends Error { constructor() { super("Circuit is opened"), Object.setPrototypeOf(this, q.prototype); } } class b extends Error { constructor() { super("Max allowed requests reached"), Object.setPrototypeOf(this, b.prototype); } } var D = /* @__PURE__ */ ((l) => (l.CLOSED = "closed", l.HALF_OPENED = "half-opened", l.OPENED = "opened", l))(D || {}); class ie extends g { constructor() { super(...arguments); /** * Specifies the circuit state */ i(this, "state"); /** * Specifies the time (in ms) the circuit stay opened before switching to half-open */ i(this, "openStateDelay"); /** * Specifies the maximum wait (in ms) in Half Open State, before switching back to open. 0 deactivates this */ i(this, "halfOpenStateMaxDelay"); /** * Specifies the maximum number of calls (if count breaker is user), * or the sliding duration (in ms, if time breaker is used) used to calculate failure and slow call rate percentages */ i(this, "slidingWindowSize"); /** * Specifies the minimum number of calls used to calculate failure and slow call rate percentages */ i(this, "minimumNumberOfCalls"); /** * Specifies the failure rate threshold in percentage */ i(this, "failureRateThreshold"); /** * Specifies the slow call rate threshold. A call is considered as slow when the call duration is greater than slowCallDurationThreshold */ i(this, "slowCallRateThreshold"); /** * Specifies the duration (in ms) threshold above which calls are considered as slow */ i(this, "slowCallDurationThreshold"); /** * Specifies the number of permitted calls when the circuit is half open */ i(this, "permittedNumberOfCallsInHalfOpenState"); /** * Allows filtering of the error to report as a failure or not. */ i(this, "onError"); } } var x = /* @__PURE__ */ ((l) => (l[l.SUCCESS = 0] = "SUCCESS", l[l.FAILURE = 1] = "FAILURE", l[l.TIMEOUT = 2] = "TIMEOUT", l))(x || {}); class j extends f { constructor(e) { super(e); // Public Attributes /** * Specifies the circuit state */ i(this, "state"); /** * Specifies when the circuit state was set */ i(this, "stateTimestamp"); /** * Specifies the time (in ms) the circuit stay opened before switching to half-open */ i(this, "openStateDelay"); /** * Specifies the maximum wait (in ms) in Half Open State, before switching back to open. 0 deactivates this */ i(this, "halfOpenStateMaxDelay"); /** * Specifies the maximum number of calls (if count breaker is user), * or the sliding duration (in ms, if time breaker is used) used to calculate failure and slow call rate percentages */ i(this, "slidingWindowSize"); /** * Specifies the minimum number of calls used to calculate failure and slow call rate percentages */ i(this, "minimumNumberOfCalls"); /** * Specifies the failure rate threshold in percentage */ i(this, "failureRateThreshold"); /** * Specifies the slow call rate threshold. A call is considered as slow when the call duration is greater than slowCallDurationThreshold */ i(this, "slowCallRateThreshold"); /** * Specifies the duration (in ms) threshold above which calls are considered as slow */ i(this, "slowCallDurationThreshold"); /** * Specifies the number of permitted calls when the circuit is half open */ i(this, "permittedNumberOfCallsInHalfOpenState"); /** * Allows filtering of the error to report as a failure or not. */ i(this, "onError"); // Private Attributes i(this, "halfOpenMaxDelayTimeout", 0); i(this, "openTimeout", 0); i(this, "nbRequestsInHalfOpenedState"); i(this, "requests"); i(this, "isInitialized", !1); this.state = (e == null ? void 0 : e.state) !== void 0 ? e.state : "closed", this.stateTimestamp = Date.now(), this.openStateDelay = (e == null ? void 0 : e.openStateDelay) !== void 0 ? e.openStateDelay : 60 * 1e3, this.halfOpenStateMaxDelay = (e == null ? void 0 : e.halfOpenStateMaxDelay) !== void 0 ? e.halfOpenStateMaxDelay : 0, this.slidingWindowSize = (e == null ? void 0 : e.slidingWindowSize) !== void 0 ? e.slidingWindowSize : 10, this.minimumNumberOfCalls = (e == null ? void 0 : e.minimumNumberOfCalls) !== void 0 ? e.minimumNumberOfCalls : 10, this.failureRateThreshold = (e == null ? void 0 : e.failureRateThreshold) !== void 0 ? e.failureRateThreshold : 50, this.slowCallDurationThreshold = (e == null ? void 0 : e.slowCallDurationThreshold) !== void 0 ? e.slowCallDurationThreshold : 6e4, this.slowCallRateThreshold = (e == null ? void 0 : e.slowCallRateThreshold) !== void 0 ? e == null ? void 0 : e.slowCallRateThreshold : 100, this.permittedNumberOfCallsInHalfOpenState = (e == null ? void 0 : e.permittedNumberOfCallsInHalfOpenState) !== void 0 ? e.permittedNumberOfCallsInHalfOpenState : 2, this.nbRequestsInHalfOpenedState = 0, this.requests = [], this.onError = (e == null ? void 0 : e.onError) || (() => !0); } reinitializeCounters() { this.nbRequestsInHalfOpenedState = 0, this.requests = []; } onOpened() { this.reinitializeCounters(); } onClosed() { this.reinitializeCounters(); } onHalfOpened() { this.reinitializeCounters(); } isSomeEnum(e, t) { return Object.values(e).includes(t); } isValidTimestamp(e) { return !("timestamp" in e) || "timestamp" in e && typeof e.timestamp == "number"; } isValidState(e) { return !!(e && typeof e == "object" && "state" in e && this.isSomeEnum(D, e.state) && this.isValidTimestamp(e)); } isValidRequest(e) { return !!(e && typeof e == "object" && "result" in e && this.isSomeEnum(x, e.result) && this.isValidTimestamp(e)); } isValidData(e) { return (!e.state || this.isValidState(e.state)) && (!e.requests || Array.isArray(e.requests) && !e.requests.some((t) => !this.isValidRequest(t))); } async execute(e, t, ...s) { var n; try { const u = await this.getState(); this.isValidData(u) && (this.requests = u.requests ? u.requests.map((c) => c.timestamp ? { result: c.result, timestamp: c.timestamp } : { result: c.result }) : [], u.state && (this.state = u.state.state, this.stateTimestamp = u.state.timestamp)); } catch (u) { (n = this.logger) == null || n.warn(u); } this.isInitialized || (this.isInitialized = !0, this.state === "opened" ? await this.setHalfDelay() : this.state === "half-opened" && await this.setOpenDelay()); const a = this._promiseBreaker(e, t, ...s), h = this.getExecParams(e, s); return this.emit("execute", e, a, h), a; } async _promiseBreaker(e, t, ...s) { var a; switch (this.state) { case "opened": return (a = this.logger) == null || a.debug(`${e.name}/${this.name} - Circuit is opened`), Promise.reject(new q()); case "half-opened": return this.executeInHalfOpened(t, ...s); case "closed": default: return this.executeInClosed(t, ...s); } } adjustRequestResult(e, t) { return !t && e === 1 ? 0 : e; } async setStateSecure(e, t) { var s; try { await this.setState(e, t); } catch (a) { (s = this.logger) == null || s.warn(a); } } async executeInHalfOpened(e, ...t) { if (this.nbRequestsInHalfOpenedState < this.permittedNumberOfCallsInHalfOpenState) { this.nbRequestsInHalfOpenedState++; const { requestResult: s, response: a, shouldReportFailure: h } = await this.executePromise(e, ...t); return this.requests.push({ result: this.adjustRequestResult(s, h) }), await this.setStateSecure([{ key: "requests", value: this.requests }]), this.requests.length == this.permittedNumberOfCallsInHalfOpenState && this.checkCallRatesHalfOpen(this.open.bind(this), this.close.bind(this)), s === 1 ? Promise.reject(a) : Promise.resolve(a); } else return Promise.reject(new b()); } executePromise(e, ...t) { const s = (/* @__PURE__ */ new Date()).getTime(); return e(...t).then((a) => { const h = (/* @__PURE__ */ new Date()).getTime(); let n = 0; return this.slowCallDurationThreshold !== 0 && this.slowCallDurationThreshold !== 1 / 0 && h - s > this.slowCallDurationThreshold && (n = 2), { requestResult: n, response: a, shouldReportFailure: !1 }; }).catch((a) => ({ requestResult: 1, response: a, shouldReportFailure: this.onError(a) })); } checkCallRatesHalfOpen(e, t) { const { nbSlow: s, nbFailure: a } = this.requests.reduce(this.getNbSlowAndFailure, { nbSlow: 0, nbFailure: 0 }); this.checkResult(s, a, this.requests.length) ? t && t() : e(); } checkResult(e, t, s) { return !(this.slowCallRateThreshold < 100 && e / s * 100 >= this.slowCallRateThreshold || this.failureRateThreshold < 100 && t / s * 100 >= this.failureRateThreshold); } getNbSlowAndFailure(e, t) { switch (t.result) { case 1: e.nbFailure++; break; case 2: e.nbSlow++; } return e; } checkCallRatesClosed() { const { nbSlow: e, nbFailure: t } = this.requests.reduce(this.getNbSlowAndFailure, { nbSlow: 0, nbFailure: 0 }); return this.checkResult(e, t, this.requests.length); } async open() { this.state !== "opened" && (this.clearHalfOpenTimeout(), this.state = "opened", this.stateTimestamp = Date.now(), this.setHalfDelay(), this.onOpened(), await this.setStateSecure([ { key: "state", value: { state: "opened", timestamp: Date.now() } }, { key: "requests", value: "" } ]), this.emit("state-changed", this.state)); } async halfOpen() { this.state !== "half-opened" && (this.clearHalfOpenTimeout(), this.state = "half-opened", this.stateTimestamp = Date.now(), this.setOpenDelay(), this.onHalfOpened(), await this.setStateSecure([ { key: "state", value: { state: "half-opened", timestamp: Date.now() } }, { key: "requests", value: "" } ]), this.emit("state-changed", this.state)); } async close() { this.state !== "closed" && (this.clearHalfOpenTimeout(), this.state = "closed", this.stateTimestamp = Date.now(), this.onClosed(), await this.setStateSecure([ { key: "state", value: { state: "closed", timestamp: Date.now() } }, { key: "requests", value: "" } ]), this.emit("state-changed", this.state)); } async setHalfDelay() { var t; const e = Date.now() - this.stateTimestamp; e >= this.openStateDelay ? ((t = this.logger) == null || t.debug(`${this.name} - Breaker: Half Open`), await this.halfOpen()) : this.openTimeout = setTimeout(async () => { var s; (s = this.logger) == null || s.debug(`${this.name} - Breaker: Half Open`), await this.halfOpen(); }, this.openStateDelay - e); } async setOpenDelay() { if (this.halfOpenStateMaxDelay) { const e = Date.now() - this.stateTimestamp; e >= this.halfOpenStateMaxDelay ? (this.halfOpenMaxDelayTimeout = 0, await this.open()) : this.halfOpenMaxDelayTimeout = setTimeout(async () => { this.halfOpenMaxDelayTimeout = 0, await this.open(); }, this.halfOpenStateMaxDelay - e); } } clearHalfOpenTimeout() { this.halfOpenMaxDelayTimeout && (clearTimeout(this.halfOpenMaxDelayTimeout), this.halfOpenMaxDelayTimeout = 0); } dispose() { super.dispose(), this.clearHalfOpenTimeout(), this.openTimeout && (clearTimeout(this.openTimeout), this.openTimeout = 0); } async getState() { return new Promise((e) => { e({ requests: this.requests, state: { state: this.state, timestamp: this.stateTimestamp } }); }); } async setState(e, t) { return new Promise((s) => { s(); }); } async clearState() { return new Promise((e) => { this.requests = [], e(); }); } } class ae extends j { constructor(r) { super(r), this.slidingWindowSize = (r == null ? void 0 : r.slidingWindowSize) !== void 0 ? r.slidingWindowSize : 10, this.slidingWindowSize < this.minimumNumberOfCalls && (this.slidingWindowSize = this.minimumNumberOfCalls); } async executeInClosed(r, ...e) { const { requestResult: t, response: s, shouldReportFailure: a } = await this.executePromise(r, ...e), h = this.adjustRequestResult(t, a); this.requests.push({ result: h }); const n = this.requests.length; let u = !1; return n >= this.minimumNumberOfCalls && (n > this.slidingWindowSize && (this.requests.splice(0, n - this.slidingWindowSize), u = !0, await this.setStateSecure([{ key: "requests", value: this.requests }])), h !== x.SUCCESS && (this.checkCallRatesClosed() || (await this.open(), u = !0))), u || await this.setStateSecure([{ key: "requests", value: this.requests }]), t === x.FAILURE ? Promise.reject(s) : Promise.resolve(s); } } class re extends j { constructor(e) { super(e); i(this, "maxSize"); this.slidingWindowSize = (e == null ? void 0 : e.slidingWindowSize) !== void 0 ? e.slidingWindowSize : 60, this.maxSize = 1e3; } filterCalls() { let e = this.requests.length; e >= this.maxSize && (this.requests.splice(0, 1), e--); let t = !0; const s = (/* @__PURE__ */ new Date()).getTime(); for (let a = 0; a < e && t; a++) s - this.requests[0].timestamp > this.slidingWindowSize ? this.requests.splice(0, 1) : t = !1; } async executeInClosed(e, ...t) { const { requestResult: s, response: a, shouldReportFailure: h } = await this.executePromise(e, ...t); this.filterCalls(); const n = this.adjustRequestResult(s, h); this.requests.push({ result: n, timestamp: (/* @__PURE__ */ new Date()).getTime() }); let u = !1; return this.requests.length >= this.minimumNumberOfCalls && n !== x.SUCCESS && (this.checkCallRatesClosed() || (await this.open(), u = !0)), u || await this.setStateSecure([{ key: "requests", value: this.requests }], this.slidingWindowSize), s === x.FAILURE ? Promise.reject(a) : Promise.resolve(a); } } const le = "0.1.1"; export { $ as Addon, q as BreakerError, b as BreakerMaxAllowedRequestError, D as BreakerState, te as Bulkhead, ee as BulkheadOptions, v as BulkheadOverloadError, T as BulkheadQueueWaitError, Z as Cache, Y as CacheOptions, V as Circuit, L as CircuitFactory, W as CircuitOptions, G as Fallback, K as FallbackOptions, f as Module, g as ModuleOptions, y as NoFuncError, se as Ratelimit, O as RatelimitError, X as Retry, F as RetryMode, J as RetryOptions, ae as SlidingCountBreaker, re as SlidingTimeBreaker, j as SlidingWindowBreaker, ie as SlidingWindowBreakerOptions, x as SlidingWindowRequestResult, Q as Timeout, S as TimeoutError, U as TimeoutOptions, I as circuits, p as modules, A as use, le as version };