From e962ecc4a4c907acdd51ac3b9fe5f7ad5537140c Mon Sep 17 00:00:00 2001 From: Gonzalo D'Elia Date: Fri, 31 May 2024 18:27:20 -0300 Subject: [PATCH] Export strategies --- packages/eth-rpc-cache/src/index.ts | 82 ++--------------------------- packages/eth-rpc-cache/src/rpc.ts | 79 +++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 79 deletions(-) create mode 100644 packages/eth-rpc-cache/src/rpc.ts diff --git a/packages/eth-rpc-cache/src/index.ts b/packages/eth-rpc-cache/src/index.ts index 88098b1..11c3551 100644 --- a/packages/eth-rpc-cache/src/index.ts +++ b/packages/eth-rpc-cache/src/index.ts @@ -1,79 +1,3 @@ -import debugConstructor from 'debug' -import pMemoize from 'promise-mem' - -import { errors } from './error' -import { perBlockStrategy } from './strategies/per-block' -import { permanentStrategy } from './strategies/permanent' -import { type JsonRpcCallFn, type Strategy } from './types' -import { getKey } from './utils/cache-key' -import { clone } from './utils/clone' - -const debug = debugConstructor('eth-rpc-cache') - -type Options = { - allowOthers?: boolean - cache?: Map - strategies?: Strategy[] -} - -export const createEthRpcCache = function ( - rpc: JsonRpcCallFn, - options: Options = {} -): JsonRpcCallFn { - debug('Creating EVM RPC cache') - - const { - allowOthers = true, - cache = new Map(), - strategies = [perBlockStrategy, permanentStrategy] - } = options - - // Each strategy resolves to a cache if it has a maxAge defined. - // Index all caches into the object by strategy name - const cachesByStrategy = strategies - .filter(({ maxAge }) => maxAge !== undefined) - .map(({ name, maxAge }) => ({ - [name]: pMemoize(rpc, { - cache, - maxAge, - resolver: getKey, - ...options - }) - })) - .reduce((acc, curr) => ({ ...acc, ...curr }), {}) - - // This object indexed by method holds a function that returns which strategy (and cache) - // should be used. By default, each strategy resolves to use its own cache, but some strategies - // may resolve to other strategies' caches, depending on the method - const strategyResolver = strategies - .flatMap(({ methods, name, resolver = () => name }) => - methods.map(method => ({ - [method]: resolver - })) - ) - .reduce((acc, curr) => ({ ...acc, ...curr }), {}) - - // Return the cached `rpc` function. - // - // If an strategy defined an RPC function for the incoming method, use that. - // Otherwise call the method directly if allowed or return proper errors. - // - // To prevent user code to mutate the cached results, the cached RPC functions - // will always return a clone of the result and not the result object itself. - return function (method, params) { - try { - const strategyName = strategyResolver[method]?.(method, params) - if (strategyName) { - return cachesByStrategy[strategyName](method, params).then(clone) - } - if (allowOthers) { - // not configured to be cached, call the method directly - return rpc(method, params) - } - return Promise.reject(errors.methodNotFound()) - } catch (err) { - // @ts-expect-error error is typed as unknown by default - return Promise.reject(errors.internalServerError(err)) - } - } -} +export { createEthRpcCache } from './rpc' +export { perBlockStrategy } from './strategies/per-block' +export { permanentStrategy } from './strategies/permanent' diff --git a/packages/eth-rpc-cache/src/rpc.ts b/packages/eth-rpc-cache/src/rpc.ts new file mode 100644 index 0000000..88098b1 --- /dev/null +++ b/packages/eth-rpc-cache/src/rpc.ts @@ -0,0 +1,79 @@ +import debugConstructor from 'debug' +import pMemoize from 'promise-mem' + +import { errors } from './error' +import { perBlockStrategy } from './strategies/per-block' +import { permanentStrategy } from './strategies/permanent' +import { type JsonRpcCallFn, type Strategy } from './types' +import { getKey } from './utils/cache-key' +import { clone } from './utils/clone' + +const debug = debugConstructor('eth-rpc-cache') + +type Options = { + allowOthers?: boolean + cache?: Map + strategies?: Strategy[] +} + +export const createEthRpcCache = function ( + rpc: JsonRpcCallFn, + options: Options = {} +): JsonRpcCallFn { + debug('Creating EVM RPC cache') + + const { + allowOthers = true, + cache = new Map(), + strategies = [perBlockStrategy, permanentStrategy] + } = options + + // Each strategy resolves to a cache if it has a maxAge defined. + // Index all caches into the object by strategy name + const cachesByStrategy = strategies + .filter(({ maxAge }) => maxAge !== undefined) + .map(({ name, maxAge }) => ({ + [name]: pMemoize(rpc, { + cache, + maxAge, + resolver: getKey, + ...options + }) + })) + .reduce((acc, curr) => ({ ...acc, ...curr }), {}) + + // This object indexed by method holds a function that returns which strategy (and cache) + // should be used. By default, each strategy resolves to use its own cache, but some strategies + // may resolve to other strategies' caches, depending on the method + const strategyResolver = strategies + .flatMap(({ methods, name, resolver = () => name }) => + methods.map(method => ({ + [method]: resolver + })) + ) + .reduce((acc, curr) => ({ ...acc, ...curr }), {}) + + // Return the cached `rpc` function. + // + // If an strategy defined an RPC function for the incoming method, use that. + // Otherwise call the method directly if allowed or return proper errors. + // + // To prevent user code to mutate the cached results, the cached RPC functions + // will always return a clone of the result and not the result object itself. + return function (method, params) { + try { + const strategyName = strategyResolver[method]?.(method, params) + if (strategyName) { + return cachesByStrategy[strategyName](method, params).then(clone) + } + if (allowOthers) { + // not configured to be cached, call the method directly + return rpc(method, params) + } + return Promise.reject(errors.methodNotFound()) + } catch (err) { + // @ts-expect-error error is typed as unknown by default + return Promise.reject(errors.internalServerError(err)) + } + } +}