@mr-zwets/bchn-api-wrapper
Version:
a Typescript wrapper for interacting with the Bitcoin Cash Node (BCHN) API
86 lines (85 loc) • 4.83 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { getRandomId, validateAndConstructUrl } from "./utils/utils.js";
import { RetryLimitExceededError } from "./utils/errors.js";
export class BchnRpcClient {
constructor(config) {
var _a, _b, _c, _d;
this.url = validateAndConstructUrl(config);
if (!config.rpcUser)
throw new Error('Need to provide rpcUser in config');
if (!config.rpcPassword)
throw new Error('Need to provide rpcPassword in config');
this.rpcUser = config.rpcUser;
this.rpcPassword = config.rpcPassword;
// optional config
this.maxRetries = (_a = config.maxRetries) !== null && _a !== void 0 ? _a : 0;
this.retryDelayMs = (_b = config.retryDelayMs) !== null && _b !== void 0 ? _b : 100;
this.logger = (_c = config.logger) !== null && _c !== void 0 ? _c : console;
this.timeoutMs = (_d = config.timeoutMs) !== null && _d !== void 0 ? _d : 5000;
}
request(endpoint, ...params) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const auth = Buffer.from(`${this.rpcUser}:${this.rpcPassword}`).toString('base64');
// Retry logic
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
try {
// Send the request with a timeout and retries
const response = yield fetch(this.url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${auth}`
},
body: JSON.stringify({ jsonrpc: '2.0', method: endpoint, params, id: getRandomId() }),
signal: AbortSignal.timeout(this.timeoutMs),
});
const result = yield response.json();
// Handle response errors
if (!response.ok || result.error) {
throw new Error(`Error: ${((_a = result.error) === null || _a === void 0 ? void 0 : _a.message) || response.statusText}`);
}
return result.result; // Return the result if successful
}
catch (error) {
let errorMessage;
// Check if the error is due to timeout or other fetch-related issues
if (typeof error == 'string') {
errorMessage = error;
this.logger.error(error);
}
else if (error instanceof DOMException && error.name === 'TimeoutError') {
// If error is an instance DOMException TimeoutError
errorMessage = error.message;
this.logger.error(`Request timed out after ${this.timeoutMs} ms`);
}
else if (error instanceof Error) {
// If error is an instance of Error, you can safely access its properties
errorMessage = error.message;
this.logger.error(`Request failed with error: ${error.message}`);
}
// Retry if allowed
if (attempt < this.maxRetries) {
this.logger.warn(`Retrying request... (${attempt + 1}/${this.maxRetries})`);
yield new Promise(res => setTimeout(res, this.retryDelayMs)); // Wait before retrying
}
else {
// If no retries are left, throw the final error
throw new RetryLimitExceededError(`Request failed after ${this.maxRetries + 1} attempts: ${errorMessage}`);
}
}
}
// This line ensures TypeScript is satisfied that a value will always be returned, but
// it should never be reached if the retries fail, as the last attempt should throw an error.
throw new Error('Request failed unexpectedly');
});
}
}