mollitia
Version:
JavaScript Resilience Library
1,146 lines (1,145 loc) • 40.3 kB
JavaScript
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
};