UNPKG

@bigmi/core

Version:

TypeScript library for Bitcoin apps.

188 lines 7.54 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.fallback = fallback; exports.shouldThrow = shouldThrow; exports.rankTransports = rankTransports; const rpc_js_1 = require("../errors/rpc.js"); const transport_js_1 = require("../errors/transport.js"); const utxo_js_1 = require("../errors/utxo.js"); const createTransport_js_1 = require("../factories/createTransport.js"); const wait_js_1 = require("../utils/wait.js"); function fallback(transports_, config = {}) { const { key = 'fallback', name = 'Fallback', rank = false, shouldThrow: shouldThrow_ = shouldThrow, retryCount, retryDelay, } = config; return (({ chain, pollingInterval = 4000, timeout, ...rest }) => { let transports = transports_; let onResponse = () => { }; const transport = (0, createTransport_js_1.createTransport)({ key, name, async request({ method, params }) { const supportedTransports = transports.reduce((supportedTransports, transport) => { const instance = transport({ ...rest, chain, retryCount: 0, timeout, }); const { include, exclude } = instance.config.methods || {}; if (include) { if (include.includes(method)) { supportedTransports.push(instance); } return supportedTransports; } if (exclude) { if (!exclude.includes(method)) { supportedTransports.push(instance); } return supportedTransports; } supportedTransports.push(instance); return supportedTransports; }, []); if (!supportedTransports.length) { throw new transport_js_1.TransportMethodNotSupportedError({ method }); } const collectedErrors = []; const fetch = async (i = 0) => { const transport = supportedTransports[i]; try { const response = await transport.request({ method, params, }); onResponse({ method, params: params, response, transport, status: 'success', }); return response; } catch (err) { onResponse({ error: err, method, params: params, transport, status: 'error', }); if (shouldThrow_(err)) { throw err; } collectedErrors.push({ transport: transport.config.name, error: err, attempt: i + 1, }); if (i === supportedTransports.length - 1) { throw new transport_js_1.AllTransportsFailedError({ method, params, errors: collectedErrors, totalAttempts: i + 1, }); } return fetch(i + 1); } }; return fetch(); }, retryCount, retryDelay, type: 'fallback', }, { onResponse: (fn) => { onResponse = fn; }, transports: transports.map((fn) => fn({ chain, retryCount: 0 })), }); if (rank) { const rankOptions = (typeof rank === 'object' ? rank : {}); rankTransports({ chain, interval: rankOptions.interval ?? pollingInterval, onTransports: (transports_) => { transports = transports_; }, ping: rankOptions.ping, sampleCount: rankOptions.sampleCount, timeout: rankOptions.timeout, transports, weights: rankOptions.weights, }); } return transport; }); } function shouldThrow(error) { if (error instanceof utxo_js_1.InsufficientUTXOBalanceError) { return true; } if ('code' in error && typeof error.code === 'number') { if (error.code === rpc_js_1.RpcErrorCode.INTERNAL_ERROR || error.code === rpc_js_1.RpcErrorCode.USER_REJECTION || error.code === 5000) { return true; } } return false; } function rankTransports({ chain, interval = 4000, onTransports, ping, sampleCount = 10, timeout = 1000, transports, weights = {}, }) { const { stability: stabilityWeight = 0.7, latency: latencyWeight = 0.3 } = weights; const samples = []; const rankTransports_ = async () => { const sample = await Promise.all(transports.map(async (transport) => { const transport_ = transport({ chain, retryCount: 0, timeout }); const start = Date.now(); let end; let success; try { await (ping ? ping({ transport: transport_ }) : transport_.request({ method: 'net_listening', params: undefined, })); success = 1; } catch { success = 0; } finally { end = Date.now(); } const latency = end - start; return { latency, success }; })); samples.push(sample); if (samples.length > sampleCount) { samples.shift(); } const maxLatency = Math.max(...samples.map((sample) => Math.max(...sample.map(({ latency }) => latency)))); const scores = transports .map((_, i) => { const latencies = samples.map((sample) => sample[i].latency); const meanLatency = latencies.reduce((acc, latency) => acc + latency, 0) / latencies.length; const latencyScore = 1 - meanLatency / maxLatency; const successes = samples.map((sample) => sample[i].success); const stabilityScore = successes.reduce((acc, success) => acc + success, 0) / successes.length; if (stabilityScore === 0) { return [0, i]; } return [ latencyWeight * latencyScore + stabilityWeight * stabilityScore, i, ]; }) .sort((a, b) => b[0] - a[0]); onTransports(scores.map(([, i]) => transports[i])); await (0, wait_js_1.wait)(interval); rankTransports_(); }; rankTransports_(); } //# sourceMappingURL=fallback.js.map