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.76 kB
/** * @module RateLimiter */ import {} from "../../../../event-bus/contracts/_module.js"; import {} from "../../../../execution-context/contracts/_module.js"; import {} from "../../../../namespace/contracts/_module.js"; import { BlockedRateLimiterError, RATE_LIMITER_EVENTS, RATE_LIMITER_STATE, } from "../../../../rate-limiter/contracts/_module.js"; import { callErrorPolicyOnThrow, callInvokable, resolveAsyncLazyable, } from "../../../../utilities/_module.js"; /** * @internal */ export class RateLimiter { /** * @internal */ static _serialize(deserializedValue) { return { version: "1", key: deserializedValue._key.get(), limit: deserializedValue._limit, }; } waitUntil; _key; _limit; errorPolicy; onlyError; adapter; eventDispatcher; enableAsyncTracking; serdeTransformerName; namespace; executionContext; constructor(settings) { const { limit, enableAsyncTracking, eventDispatcher, key, errorPolicy, onlyError, adapter, serdeTransformerName, namespace, waitUntil, executionContext, } = settings; this.executionContext = executionContext; this.waitUntil = waitUntil; this.namespace = namespace; this.serdeTransformerName = serdeTransformerName; this._limit = limit; this.enableAsyncTracking = enableAsyncTracking; this.eventDispatcher = eventDispatcher; this._key = key; this.errorPolicy = errorPolicy; this.onlyError = onlyError; this.adapter = adapter; } _getNamespace() { return this.namespace; } _getSerdeTransformerName() { return this.serdeTransformerName; } _getAdapter() { return this.adapter; } toRateLimiterState(state) { if (state === null) { return { type: RATE_LIMITER_STATE.EXPIRED, }; } if (state.success) { return { type: RATE_LIMITER_STATE.ALLOWED, usedAttempts: state.attempt, remainingAttempts: this.limit - state.attempt, limit: this.limit, resetAfter: state.resetTime, }; } return { type: RATE_LIMITER_STATE.BLOCKED, limit: this.limit, totalAttempts: state.attempt, exceedAttempts: state.attempt - this.limit, retryAfter: state.resetTime, }; } async getState() { const state = await this.adapter.getState(this.executionContext, this._key.toString()); return this.toRateLimiterState(state); } get key() { return this._key; } get limit() { return this._limit; } async trackErrorWrapper(asyncFn) { const state = this.toRateLimiterState(await this.adapter.getState(this.executionContext, this._key.toString())); if (state.type === RATE_LIMITER_STATE.BLOCKED) { callInvokable(this.waitUntil, this.eventDispatcher.dispatch(RATE_LIMITER_EVENTS.BLOCKED, { rateLimiter: this, })); const { type: _type, ...rest } = state; throw BlockedRateLimiterError.create(rest, this._key); } try { callInvokable(this.waitUntil, this.eventDispatcher.dispatch(RATE_LIMITER_EVENTS.ALLOWED, { rateLimiter: this, })); return await resolveAsyncLazyable(asyncFn); } catch (error) { const isErrorMatching = await callErrorPolicyOnThrow(this.errorPolicy, error); if (isErrorMatching) { callInvokable(this.waitUntil, this.eventDispatcher.dispatch(RATE_LIMITER_EVENTS.TRACKED_FAILURE, { rateLimiter: this, error, })); } else { callInvokable(this.waitUntil, this.eventDispatcher.dispatch(RATE_LIMITER_EVENTS.UNTRACKED_FAILURE, { rateLimiter: this, error, })); } if (isErrorMatching) { const fn = async () => { await this.adapter.updateState(this.executionContext, this._key.toString(), this.limit); }; if (this.enableAsyncTracking) { callInvokable(this.waitUntil, fn()); } else { await fn(); } } throw error; } } async trackWrapper(asyncFn) { const state = this.toRateLimiterState(await this.adapter.updateState(this.executionContext, this._key.toString(), this.limit)); if (state.type === RATE_LIMITER_STATE.BLOCKED) { callInvokable(this.waitUntil, this.eventDispatcher.dispatch(RATE_LIMITER_EVENTS.BLOCKED, { rateLimiter: this, })); const { type: _type, ...rest } = state; throw BlockedRateLimiterError.create(rest, this._key); } callInvokable(this.waitUntil, this.eventDispatcher.dispatch(RATE_LIMITER_EVENTS.ALLOWED, { rateLimiter: this, })); return await resolveAsyncLazyable(asyncFn); } async runOrFail(asyncFn) { if (this.onlyError) { return await this.trackErrorWrapper(asyncFn); } return await this.trackWrapper(asyncFn); } async reset() { await this.adapter.reset(this.executionContext, this._key.toString()); callInvokable(this.waitUntil, this.eventDispatcher.dispatch(RATE_LIMITER_EVENTS.RESETED, { rateLimiter: this, })); } } //# sourceMappingURL=rate-limiter.js.map