@daiso-tech/core
Version:
The library offers flexible, framework-agnostic solutions for modern web applications, built on adaptable components that integrate seamlessly with popular frameworks like Next Js.
160 lines • 5.52 kB
JavaScript
/**
* @module RateLimiter
*/
import {} from "../../../../event-bus/contracts/_module.js";
import {} from "../../../../namespace/_module.js";
import { BlockedRateLimiterError, RATE_LIMITER_EVENTS, RATE_LIMITER_STATE, } from "../../../../rate-limiter/contracts/_module.js";
import {} from "../../../../task/contracts/_module.js";
import { Task } from "../../../../task/implementations/_module.js";
import { TimeSpan } from "../../../../time-span/implementations/_module.js";
import { callErrorPolicyOnThrow, resolveAsyncLazyable, UnexpectedError, } from "../../../../utilities/_module.js";
/**
* @internal
*/
export class RateLimiter {
_key;
_limit;
errorPolicy;
onlyError;
adapter;
eventDispatcher;
enableAsyncTracking;
constructor(settings) {
const { limit, enableAsyncTracking, eventDispatcher, key, errorPolicy, onlyError, adapter, } = settings;
this._limit = limit;
this.enableAsyncTracking = enableAsyncTracking;
this.eventDispatcher = eventDispatcher;
this._key = key;
this.errorPolicy = errorPolicy;
this.onlyError = onlyError;
this.adapter = adapter;
}
toRateLimiterState(state) {
if (state === null) {
return {
type: RATE_LIMITER_STATE.ALLOWED,
usedAttempts: 0,
reaminingAttemps: this.limit,
limit: this.limit,
};
}
if (state.success) {
return {
type: RATE_LIMITER_STATE.ALLOWED,
usedAttempts: state.attempt,
reaminingAttemps: this.limit - state.attempt,
limit: this.limit,
};
}
if (state.resetTime !== null) {
return {
type: RATE_LIMITER_STATE.BLOCKED,
limit: this.limit,
totalAttempts: state.attempt,
exceedAttempts: state.attempt - this.limit,
resetTime: TimeSpan.fromTimeSpan(state.resetTime),
};
}
throw new UnexpectedError("1_!!__MESSAGE__!!");
}
getState() {
return new Task(async () => {
const state = await this.adapter.getState(this._key.toString());
return this.toRateLimiterState(state);
});
}
get key() {
return this._key.get();
}
get limit() {
return this._limit;
}
async trackErrorWrapper(asyncFn) {
const state = this.toRateLimiterState(await this.adapter.getState(this.key.toString()));
if (state.type === RATE_LIMITER_STATE.BLOCKED) {
this.eventDispatcher
.dispatch(RATE_LIMITER_EVENTS.BLOCKED, {
rateLimiter: this,
})
.detach();
const { type: _type, ...rest } = state;
throw new BlockedRateLimiterError(rest, "2_!!__MESSAGE__!!");
}
try {
this.eventDispatcher
.dispatch(RATE_LIMITER_EVENTS.ALLOWED, {
rateLimiter: this,
})
.detach();
return await resolveAsyncLazyable(asyncFn);
}
catch (error) {
const isErrorMatching = await callErrorPolicyOnThrow(this.errorPolicy, error);
if (isErrorMatching) {
this.eventDispatcher
.dispatch(RATE_LIMITER_EVENTS.TRACKED_FAILURE, {
rateLimiter: this,
error,
})
.detach();
}
else {
this.eventDispatcher
.dispatch(RATE_LIMITER_EVENTS.UNTRACKED_FAILURE, {
rateLimiter: this,
error,
})
.detach();
}
if (isErrorMatching) {
const task = new Task(async () => {
await this.adapter.updateState(this.key.toString(), this.limit);
});
if (this.enableAsyncTracking) {
task.detach();
}
else {
await task;
}
}
throw error;
}
}
async trackWrapper(asyncFn) {
const state = this.toRateLimiterState(await this.adapter.updateState(this.key.toString(), this.limit));
if (state.type === RATE_LIMITER_STATE.BLOCKED) {
this.eventDispatcher
.dispatch(RATE_LIMITER_EVENTS.BLOCKED, {
rateLimiter: this,
})
.detach();
const { type: _type, ...rest } = state;
throw new BlockedRateLimiterError(rest, "3_!!__MESSAGE__!!");
}
this.eventDispatcher
.dispatch(RATE_LIMITER_EVENTS.ALLOWED, {
rateLimiter: this,
})
.detach();
return await resolveAsyncLazyable(asyncFn);
}
runOrFail(asyncFn) {
return new Task(async () => {
if (this.onlyError) {
return await this.trackErrorWrapper(asyncFn);
}
return await this.trackWrapper(asyncFn);
});
}
reset() {
return new Task(async () => {
this.eventDispatcher
.dispatch(RATE_LIMITER_EVENTS.RESETED, {
rateLimiter: this,
})
.detach();
await this.adapter.reset(this._key.toString());
});
}
}
//# sourceMappingURL=rate-limiter.js.map