eth-rpc-cache
Version:
A simple cache for Ethereum RPC requests extensible with different caching strategies
78 lines (77 loc) • 3.58 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
import pMemoize from 'promise-mem';
import { errors } from './error';
import { perBlockStrategy } from './strategies/per-block';
import { permanentStrategy } from './strategies/permanent';
import { getKey } from './utils/cache-key';
export var createEthRpcCache = function (rpc, options) {
if (options === void 0) { options = {}; }
var _a = options.allowOthers, allowOthers = _a === void 0 ? true : _a, _b = options.cache, cache = _b === void 0 ? new Map() : _b, _c = options.strategies, strategies = _c === void 0 ? [perBlockStrategy, permanentStrategy] : _c;
// Each strategy resolves to a cache if it has a maxAge defined.
// Index all caches into the object by strategy name
var cachesByStrategy = strategies
.filter(function (_a) {
var maxAge = _a.maxAge;
return maxAge !== undefined;
})
.map(function (_a) {
var _b;
var maxAge = _a.maxAge, name = _a.name;
return (_b = {},
_b[name] = pMemoize(rpc, __assign({ cache: cache, maxAge: maxAge, resolver: getKey }, options)),
_b);
})
.reduce(function (acc, curr) { return (__assign(__assign({}, 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
var strategyResolver = strategies
.flatMap(function (_a) {
var methods = _a.methods, name = _a.name, _b = _a.resolver, resolver = _b === void 0 ? function () { return name; } : _b;
return methods.map(function (method) {
var _a;
return (_a = {},
_a[method] = resolver,
_a);
});
})
.reduce(function (acc, curr) { return (__assign(__assign({}, 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) {
var _a;
try {
var strategyName = (_a = strategyResolver[method]) === null || _a === void 0 ? void 0 : _a.call(strategyResolver, method, params);
if (strategyName) {
return cachesByStrategy[strategyName](method, params).then(function (c) {
// can't inline on .then(structuredClone), TS fails to infer
return structuredClone(c);
});
}
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));
}
};
};