204 lines
8.8 KiB
JavaScript
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;
|