UNPKG

@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
/** * @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