diff --git a/packages/rate-limit-controller/CHANGELOG.md b/packages/rate-limit-controller/CHANGELOG.md index de86b5806f..7996fba0ec 100644 --- a/packages/rate-limit-controller/CHANGELOG.md +++ b/packages/rate-limit-controller/CHANGELOG.md @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add and export types `RateLimitedApiMap`, `RateLimitedRequests`. ([#3963](https://github.com/MetaMask/core/pull/3963)) + - `RateLimitedApiMap` represents the type of the `RateLimitedApis` generic parameter used throughout the controller. + - `RateLimitedRequests` represents the type of the `request` property of `RateLimitState`. + +### Changed + +- **BREAKING:** Rename types to align with conventions followed by other controllers. ([#3963](https://github.com/MetaMask/core/pull/3963)) + - `GetRateLimitState` is now `RateLimitControllerGetStateAction`. + - `RateLimitStateChange` is now `RateLimitControllerStateChangeEvent`. + - `CallApi` is now `RateLimitControllerCallApiAction`. +- Add `@metamask/utils` `^8.3.0` as a dependency. ([#3963](https://github.com/MetaMask/core/pull/3963)) + ### Fixed - **BREAKING:** Fix `GetRateLimitState`, `RateLimitStateChange` types by replacing `RateLimitedApis` with `RateLimitState` as the state type passed in as generic arguments to `ControllerGetStateAction` and `ControllerStateChangeEvent` ([#3949](https://github.com/MetaMask/core/pull/3949)) diff --git a/packages/rate-limit-controller/package.json b/packages/rate-limit-controller/package.json index 254f992ebb..3594d55d65 100644 --- a/packages/rate-limit-controller/package.json +++ b/packages/rate-limit-controller/package.json @@ -42,7 +42,8 @@ }, "dependencies": { "@metamask/base-controller": "^4.1.1", - "@metamask/rpc-errors": "^6.2.1" + "@metamask/rpc-errors": "^6.2.1", + "@metamask/utils": "^8.3.0" }, "devDependencies": { "@metamask/auto-changelog": "^3.4.4", diff --git a/packages/rate-limit-controller/src/RateLimitController.ts b/packages/rate-limit-controller/src/RateLimitController.ts index 00f79f70c4..2ddcf1c8f3 100644 --- a/packages/rate-limit-controller/src/RateLimitController.ts +++ b/packages/rate-limit-controller/src/RateLimitController.ts @@ -6,9 +6,10 @@ import type { } from '@metamask/base-controller'; import { BaseController } from '@metamask/base-controller'; import { rpcErrors } from '@metamask/rpc-errors'; +import { getKnownPropertyNames } from '@metamask/utils'; /** - * @type RateLimitedApi + * A rate-limited API endpoint. * @property method - The method that is rate-limited. * @property rateLimitTimeout - The time window in which the rate limit is applied (in ms). * @property rateLimitCount - The amount of calls an origin can make in the rate limit time window. @@ -20,47 +21,61 @@ export type RateLimitedApi = { }; /** - * @type RateLimitState - * @property requests - Object containing number of requests in a given interval for each origin and api type combination + * A map of rate-limited API types to APIs. */ -export type RateLimitState< - RateLimitedApis extends Record, -> = { - requests: Record>; +export type RateLimitedApiMap = Record; + +/** + * A map of rate-limited API types to the number of requests made in a given interval for each origin and api type combination. + * @template RateLimitedApis - A {@link RateLimitedApiMap} containing the rate-limited API endpoints that is used by the {@link RateLimitController}. + */ +export type RateLimitedRequests = + Record>; + +/** + * The state of the {@link RateLimitController}. + * @template RateLimitedApis - A {@link RateLimitedApiMap} containing the rate-limited API endpoints that is used by the {@link RateLimitController}. + * @property requests - An object containing the number of requests made in a given interval for each origin and api type combination. + */ +export type RateLimitState = { + requests: RateLimitedRequests; }; const name = 'RateLimitController'; -export type RateLimitStateChange< - RateLimitedApis extends Record, +export type RateLimitControllerStateChangeEvent< + RateLimitedApis extends RateLimitedApiMap, > = ControllerStateChangeEvent>; -export type GetRateLimitState< - RateLimitedApis extends Record, +export type RateLimitControllerGetStateAction< + RateLimitedApis extends RateLimitedApiMap, > = ControllerGetStateAction>; -export type CallApi> = { +export type RateLimitControllerCallApiAction< + RateLimitedApis extends RateLimitedApiMap, +> = { type: `${typeof name}:call`; handler: RateLimitController['call']; }; export type RateLimitControllerActions< - RateLimitedApis extends Record, -> = GetRateLimitState | CallApi; + RateLimitedApis extends RateLimitedApiMap, +> = + | RateLimitControllerGetStateAction + | RateLimitControllerCallApiAction; export type RateLimitControllerEvents< - RateLimitedApis extends Record, -> = RateLimitStateChange; - -export type RateLimitMessenger< - RateLimitedApis extends Record, -> = RestrictedControllerMessenger< - typeof name, - RateLimitControllerActions, - RateLimitControllerEvents, - never, - never ->; + RateLimitedApis extends RateLimitedApiMap, +> = RateLimitControllerStateChangeEvent; + +export type RateLimitMessenger = + RestrictedControllerMessenger< + typeof name, + RateLimitControllerActions, + RateLimitControllerEvents, + never, + never + >; const metadata = { requests: { persist: false, anonymous: false }, @@ -70,7 +85,7 @@ const metadata = { * Controller with logic for rate-limiting API endpoints per requesting origin. */ export class RateLimitController< - RateLimitedApis extends Record, + RateLimitedApis extends RateLimitedApiMap, > extends BaseController< typeof name, RateLimitState, @@ -106,10 +121,9 @@ export class RateLimitController< implementations: RateLimitedApis; }) { const defaultState = { - requests: Object.keys(implementations).reduce( - (acc, key) => ({ ...acc, [key]: {} }), - {} as Record>, - ), + requests: getKnownPropertyNames(implementations).reduce< + RateLimitedRequests + >((acc, key) => ({ ...acc, [key]: {} }), {} as never), }; super({ name, @@ -122,11 +136,11 @@ export class RateLimitController< this.rateLimitCount = rateLimitCount; this.messagingSystem.registerActionHandler( - `${name}:call` as const, + `${name}:call`, ( origin: string, type: keyof RateLimitedApis, - ...args: Parameters + ...args: Parameters ) => this.call(origin, type, ...args), ); } @@ -183,17 +197,17 @@ export class RateLimitController< private recordRequest(api: keyof RateLimitedApis, origin: string) { const rateLimitTimeout = this.implementations[api].rateLimitTimeout ?? this.rateLimitTimeout; + const previous = this.state.requests[api][origin] ?? 0; this.update((state) => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const previous = (state as any).requests[api][origin] ?? 0; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (state as any).requests[api][origin] = previous + 1; - if (previous === 0) { setTimeout(() => this.resetRequestCount(api, origin), rateLimitTimeout); } + Object.assign(state, { + requests: { + ...(state.requests as RateLimitedRequests), + [api]: { [origin]: previous + 1 }, + }, + }); }); } @@ -205,9 +219,12 @@ export class RateLimitController< */ private resetRequestCount(api: keyof RateLimitedApis, origin: string) { this.update((state) => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (state as any).requests[api][origin] = 0; + Object.assign(state, { + requests: { + ...(state.requests as RateLimitedRequests), + [api]: { [origin]: 0 }, + }, + }); }); } } diff --git a/packages/rate-limit-controller/src/index.ts b/packages/rate-limit-controller/src/index.ts index 50645817b7..97cbe8505e 100644 --- a/packages/rate-limit-controller/src/index.ts +++ b/packages/rate-limit-controller/src/index.ts @@ -1 +1,13 @@ -export * from './RateLimitController'; +export type { + RateLimitedApi, + RateLimitedApiMap, + RateLimitedRequests, + RateLimitControllerActions, + RateLimitControllerCallApiAction, + RateLimitControllerGetStateAction, + RateLimitControllerEvents, + RateLimitControllerStateChangeEvent, + RateLimitMessenger, + RateLimitState, +} from './RateLimitController'; +export { RateLimitController } from './RateLimitController'; diff --git a/yarn.lock b/yarn.lock index 0617364be5..b7a0d9964e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2781,6 +2781,7 @@ __metadata: "@metamask/auto-changelog": ^3.4.4 "@metamask/base-controller": ^4.1.1 "@metamask/rpc-errors": ^6.2.1 + "@metamask/utils": ^8.3.0 "@types/jest": ^27.4.1 deepmerge: ^4.2.2 jest: ^27.5.1