morphit/node_modules/@beblurt/dblurt/lib/experimental_client.js

204 lines
8.8 KiB
JavaScript

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExperimentalClient = void 0;
/**
* @file Experimental dblurt client adapter backed by blurt-rpc-core transport.
*/
const assert = __importStar(require("assert"));
const version_1 = __importDefault(require("./version"));
const account_history_1 = require("./helpers/account_history");
const blockchain_1 = require("./helpers/blockchain");
const broadcast_1 = require("./helpers/broadcast");
const index_browser_1 = require("./index-browser");
const database_1 = require("./helpers/database");
const nexus_1 = require("./helpers/nexus");
const tools_1 = require("./helpers/tools");
const errors_1 = require("./errors");
const client_1 = require("./client");
const rpc_transport_1 = require("./transports/rpc_transport");
/**
* Experimental RPC Client backed by blurt-rpc-core.
*
* This class intentionally does not replace or extend the historic Client. It mirrors the
* dblurt helper surface while delegating JSON-RPC transport, retry and failover to
* blurt-rpc-core so compatibility can be measured before any migration decision.
*/
class ExperimentalClient {
/**
* @param address The address to the Blurt RPC server,
* e.g. `https://rpc.blurt.blog`. or [`https://rpc.blurt.blog`, `https://another.api.com`]
* @param options Experimental client options.
*/
constructor(address, options = {}) {
this.currentAddress = Array.isArray(address) ? address[0] ? address[0] : 'https://rpc.blurt.blog' : address;
this.address = address;
this.options = options;
this.chainId = options.chainId ? Buffer.from(options.chainId, 'hex') : client_1.DEFAULT_CHAIN_ID;
assert.strictEqual(this.chainId.length, 32, 'invalid chain id');
this.addressPrefix = options.addressPrefix || client_1.DEFAULT_ADDRESS_PREFIX;
this.timeout = options.timeout !== undefined ? options.timeout : 60 * 1000;
this.backoff = options.backoff || ((tries) => Math.min(Math.pow(tries * 10, 2), 10 * 1000));
this.failoverThreshold = options.failoverThreshold !== undefined ? options.failoverThreshold : 3;
this.coreClient = options.coreClient;
this.coreModule = options.coreModule;
this.accountHistory = new account_history_1.AccountHistoryAPI(this);
this.blockchain = new blockchain_1.Blockchain(this);
this.broadcast = new broadcast_1.BroadcastAPI(this);
this.condenser = new index_browser_1.CondenserAPI(this);
this.database = new database_1.DatabaseAPI(this);
this.nexus = new nexus_1.Nexus(this);
this.tools = new tools_1.Tools(this);
}
/** Make a RPC call to the server using blurt-rpc-core transport. */
async call(api, method, params = []) {
const core = await this.getCoreClient();
const request = { id: 0, jsonrpc: '2.0', method: `${api}.${method}`, params: this.legacySerializeParams(params) };
const rawResponse = core.callRaw
? await core.callRaw(request, this.callOptions())
: await this.callViaResultMode(core, request);
const response = (0, rpc_transport_1.validateRpcResponse)(rawResponse);
this.syncCurrentAddress(core);
if (response.error) {
throw (0, errors_1.rpcErrorFromResponse)(response.error);
}
assert.strictEqual(response.id, request.id, 'got invalid response id');
return response.result;
}
/** Close the underlying blurt-rpc-core client when it exposes close(). */
async close() {
const core = this.coreClient || this.loadedCoreClient;
await core?.close?.();
}
async callViaResultMode(core, request) {
if (!core.call) {
throw new Error('blurt-rpc-core client must expose callRaw or call');
}
try {
const result = await core.call(request.method, request.params, { ...this.callOptions(), id: request.id });
return { id: request.id, result };
}
catch (error) {
throw error;
}
}
callOptions() {
const options = {
retry: {
backoff: { delayMs: (attempt) => this.backoff(attempt) },
maxAttempts: this.failoverThreshold === 0 ? Number.MAX_SAFE_INTEGER : this.failoverThreshold
}
};
if (this.timeout > 0) {
options.timeoutMs = this.timeout;
}
return options;
}
async getCoreClient() {
if (this.coreClient) {
return this.coreClient;
}
if (this.loadedCoreClient) {
return this.loadedCoreClient;
}
const core = this.coreModule || await this.loadCoreModule();
const options = this.coreOptions();
if (core.createRpcClient) {
this.loadedCoreClient = core.createRpcClient(this.address, options);
}
else if (core.RpcClient) {
this.loadedCoreClient = new core.RpcClient(this.address, options);
}
else {
throw new Error('blurt-rpc-core module must expose createRpcClient or RpcClient');
}
return this.loadedCoreClient;
}
coreOptions() {
const headers = {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
};
if (typeof self === 'undefined') {
headers['User-Agent'] = this.options.userAgent || `dblurt/${version_1.default}`;
}
return {
backoff: { delayMs: (attempt) => this.backoff(attempt) },
headers,
maxRetries: this.failoverThreshold === 0 ? Number.MAX_SAFE_INTEGER : Math.max(0, this.failoverThreshold - 1),
strategy: this.options.nodeSelectionStrategy || this.legacyStickyStrategy(),
...(this.timeout > 0 ? { timeoutMs: this.timeout } : {})
};
}
legacyStickyStrategy() {
let currentKey;
const endpointKey = (state) => state.endpoint.id || String(state.endpoint.url);
return {
name: 'dblurt-legacy-sticky',
onFailure: (state) => {
if (currentKey && endpointKey(state) === currentKey) {
currentKey = undefined;
}
},
select: (states) => {
const enabled = states.filter(state => state.endpoint.enabled !== false && state.status !== 'disabled');
if (currentKey) {
const current = enabled.find(state => endpointKey(state) === currentKey);
if (current && current.consecutiveFailures === 0) {
return current;
}
}
const selected = enabled.find(state => state.consecutiveFailures === 0) || enabled[0];
currentKey = selected ? endpointKey(selected) : undefined;
return selected;
}
};
}
legacySerializeParams(params) {
return JSON.parse(JSON.stringify(params, (_key, value) => {
if (value && typeof value === 'object' && value.type === 'Buffer' && Array.isArray(value.data)) {
return Buffer.from(value.data).toString('hex');
}
return value;
}));
}
async loadCoreModule() {
return import('@beblurt/blurt-rpc-core');
}
syncCurrentAddress(core) {
const state = core.getCurrentEndpoint?.();
const url = state && state.endpoint && state.endpoint.url;
if (typeof url === 'string') {
this.currentAddress = url;
}
else if (url && typeof url.toString === 'function') {
this.currentAddress = url.toString();
}
}
}
exports.ExperimentalClient = ExperimentalClient;