@mr-zwets/bchn-api-wrapper 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/src/index.d.ts +4 -0
  2. package/dist/src/index.js +4 -0
  3. package/{src/interfaces/interfaces.ts → dist/src/interfaces/interfaces.d.ts} +51 -65
  4. package/dist/src/interfaces/interfaces.js +1 -0
  5. package/{src/interfaces/restInterfaces/interfaces.ts → dist/src/interfaces/restInterfaces/interfaces.d.ts} +145 -166
  6. package/dist/src/interfaces/restInterfaces/interfaces.js +1 -0
  7. package/dist/src/interfaces/rpcInterfaces/blockchain.d.ts +883 -0
  8. package/dist/src/interfaces/rpcInterfaces/blockchain.js +3 -0
  9. package/dist/src/interfaces/rpcInterfaces/control.d.ts +60 -0
  10. package/dist/src/interfaces/rpcInterfaces/control.js +3 -0
  11. package/dist/src/interfaces/rpcInterfaces/generating.d.ts +19 -0
  12. package/dist/src/interfaces/rpcInterfaces/generating.js +3 -0
  13. package/dist/src/interfaces/rpcInterfaces/index.d.ts +9 -0
  14. package/{src/interfaces/rpcInterfaces/index.ts → dist/src/interfaces/rpcInterfaces/index.js} +1 -3
  15. package/dist/src/interfaces/rpcInterfaces/mining.d.ts +140 -0
  16. package/dist/src/interfaces/rpcInterfaces/mining.js +3 -0
  17. package/dist/src/interfaces/rpcInterfaces/network.d.ts +197 -0
  18. package/dist/src/interfaces/rpcInterfaces/network.js +3 -0
  19. package/dist/src/interfaces/rpcInterfaces/rawtransactions.d.ts +304 -0
  20. package/dist/src/interfaces/rpcInterfaces/rawtransactions.js +3 -0
  21. package/dist/src/interfaces/rpcInterfaces/util.d.ts +49 -0
  22. package/dist/src/interfaces/rpcInterfaces/util.js +3 -0
  23. package/dist/src/interfaces/rpcInterfaces/wallet.d.ts +674 -0
  24. package/dist/src/interfaces/rpcInterfaces/wallet.js +3 -0
  25. package/dist/src/interfaces/rpcInterfaces/zmq.d.ts +9 -0
  26. package/dist/src/interfaces/rpcInterfaces/zmq.js +3 -0
  27. package/dist/src/restClient.d.ts +29 -0
  28. package/dist/src/restClient.js +113 -0
  29. package/dist/src/rpcClient.d.ts +19 -0
  30. package/dist/src/rpcClient.js +92 -0
  31. package/dist/src/utils/errors.d.ts +3 -0
  32. package/dist/src/utils/errors.js +6 -0
  33. package/dist/src/utils/utils.d.ts +11 -0
  34. package/dist/src/utils/utils.js +49 -0
  35. package/package.json +7 -3
  36. package/.claude/settings.local.json +0 -8
  37. package/.github/workflows/ci.yaml +0 -36
  38. package/CLAUDE.md +0 -70
  39. package/pnpm-lock.yaml +0 -1279
  40. package/src/index.ts +0 -4
  41. package/src/interfaces/rpcInterfaces/blockchain.ts +0 -933
  42. package/src/interfaces/rpcInterfaces/control.ts +0 -68
  43. package/src/interfaces/rpcInterfaces/generating.ts +0 -23
  44. package/src/interfaces/rpcInterfaces/mining.ts +0 -151
  45. package/src/interfaces/rpcInterfaces/network.ts +0 -213
  46. package/src/interfaces/rpcInterfaces/rawtransactions.ts +0 -332
  47. package/src/interfaces/rpcInterfaces/util.ts +0 -56
  48. package/src/interfaces/rpcInterfaces/wallet.ts +0 -728
  49. package/src/interfaces/rpcInterfaces/zmq.ts +0 -12
  50. package/src/restClient.ts +0 -134
  51. package/src/rpcClient.ts +0 -100
  52. package/src/utils/errors.ts +0 -6
  53. package/src/utils/utils.ts +0 -55
  54. package/test/restClient.test.ts +0 -34
  55. package/test/rpcClient.test.ts +0 -119
  56. package/test/setupTests.ts +0 -56
  57. package/test/tsconfig.json +0 -4
  58. package/tsconfig.json +0 -13
  59. package/vitest.config.ts +0 -9
@@ -0,0 +1,113 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { validateUrl } from "./utils/utils.js";
11
+ /** REST client for read-only BCHN blockchain access via the REST interface. */
12
+ export class BchnRestClient {
13
+ constructor(config) {
14
+ var _a, _b;
15
+ this.baseUrl = validateUrl(config.url);
16
+ this.timeoutMs = (_a = config.timeoutMs) !== null && _a !== void 0 ? _a : 5000;
17
+ this.logger = (_b = config.logger) !== null && _b !== void 0 ? _b : console;
18
+ }
19
+ fetchFromNode(endpoint, format) {
20
+ return __awaiter(this, void 0, void 0, function* () {
21
+ try {
22
+ const response = yield fetch(`${this.baseUrl}/rest/${endpoint}`, {
23
+ signal: AbortSignal.timeout(this.timeoutMs),
24
+ });
25
+ if (!response.ok) {
26
+ throw new Error(`Error fetching data from ${endpoint}: ${response.statusText}`);
27
+ }
28
+ if (format === 'json') {
29
+ return yield response.json();
30
+ }
31
+ else {
32
+ return yield response.text(); // For 'bin' and 'hex', return raw text
33
+ }
34
+ }
35
+ catch (error) {
36
+ let errorMessage;
37
+ // Check if the error is due to timeout or other fetch-related issues
38
+ if (typeof error === 'string') {
39
+ errorMessage = error;
40
+ this.logger.error(error);
41
+ }
42
+ else if (error instanceof DOMException && error.name === 'TimeoutError') {
43
+ // If error is an instance DOMException TimeoutError
44
+ errorMessage = 'Request timed out';
45
+ this.logger.error(`Request to ${endpoint} timed out after ${this.timeoutMs} ms`);
46
+ }
47
+ else {
48
+ this.logger.error(`Unknown error occurred during request to ${endpoint}`);
49
+ throw new Error(`Unknown error: ${error}`);
50
+ }
51
+ // Always rethrow the error after logging
52
+ throw new Error(errorMessage);
53
+ }
54
+ });
55
+ }
56
+ /** Returns transaction details by txid. */
57
+ getTransaction(txid_1) {
58
+ return __awaiter(this, arguments, void 0, function* (txid, format = 'json') {
59
+ return this.fetchFromNode(`tx/${txid}.${format}`, format);
60
+ });
61
+ }
62
+ // getBlock Implementation
63
+ getBlock(blockhash_1, includeTxDetails_1) {
64
+ return __awaiter(this, arguments, void 0, function* (blockhash, includeTxDetails, format = 'json') {
65
+ const path = includeTxDetails ? 'block' : 'block/notxdetails';
66
+ return this.fetchFromNode(`${path}/${blockhash}.${format}`, format);
67
+ });
68
+ }
69
+ /** Returns block headers starting from a specific block hash. */
70
+ getBlockHeaders(count_1, blockhash_1) {
71
+ return __awaiter(this, arguments, void 0, function* (count, blockhash, format = 'json') {
72
+ return this.fetchFromNode(`headers/${count}/${blockhash}.${format}`, format);
73
+ });
74
+ }
75
+ /** Returns current chain state (network, sync progress, best block). */
76
+ getChainInfo() {
77
+ return __awaiter(this, void 0, void 0, function* () {
78
+ return this.fetchFromNode('chaininfo.json', 'json');
79
+ });
80
+ }
81
+ /** Queries UTXO set for specific outpoints. Outpoints format: "txid-vout". */
82
+ getUTXOs(checkmempool_1, outpoints_1) {
83
+ return __awaiter(this, arguments, void 0, function* (checkmempool, outpoints, format = 'json') {
84
+ const path = (checkmempool ? 'checkmempool/' : '') + outpoints.join('/');
85
+ const endpoint = `getutxos/${path}.${format}`;
86
+ return this.fetchFromNode(endpoint, format);
87
+ });
88
+ }
89
+ /** Returns mempool statistics (size, bytes, fee rates). */
90
+ getMempoolInfo() {
91
+ return __awaiter(this, void 0, void 0, function* () {
92
+ return this.fetchFromNode('mempool/info.json', 'json');
93
+ });
94
+ }
95
+ /** Returns all transactions currently in the mempool. */
96
+ getMempoolContents() {
97
+ return __awaiter(this, void 0, void 0, function* () {
98
+ return this.fetchFromNode('mempool/contents.json', 'json');
99
+ });
100
+ }
101
+ /** Returns block with bytecode pattern data (v29.0.0+). */
102
+ getBlockWithPatterns(blockhash) {
103
+ return __awaiter(this, void 0, void 0, function* () {
104
+ return this.fetchFromNode(`block/withpatterns/${blockhash}.json`, 'json');
105
+ });
106
+ }
107
+ /** Returns transaction with bytecode pattern data (v29.0.0+). */
108
+ getTransactionWithPatterns(txid) {
109
+ return __awaiter(this, void 0, void 0, function* () {
110
+ return this.fetchFromNode(`tx/withpatterns/${txid}.json`, 'json');
111
+ });
112
+ }
113
+ }
@@ -0,0 +1,19 @@
1
+ import type { RpcClientConfig, RpcRequest } from "./interfaces/interfaces.js";
2
+ /** RPC client for full BCHN node interaction via JSON-RPC. */
3
+ export declare class BchnRpcClient {
4
+ private url;
5
+ private rpcUser;
6
+ private rpcPassword;
7
+ private maxRetries;
8
+ private retryDelayMs;
9
+ private logger;
10
+ private timeoutMs;
11
+ constructor(config: RpcClientConfig);
12
+ /**
13
+ * Sends a typed RPC request to the BCHN node.
14
+ * @example
15
+ * const result = await client.request<GetBlockCount>("getblockcount");
16
+ * const block = await client.request<GetBlockVerbosity1>("getblock", hash, 1);
17
+ */
18
+ request<T extends RpcRequest>(endpoint: T['method'], ...params: T['params']): Promise<T['response']>;
19
+ }
@@ -0,0 +1,92 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { getRandomId, validateAndConstructUrl } from "./utils/utils.js";
11
+ import { RetryLimitExceededError } from "./utils/errors.js";
12
+ /** RPC client for full BCHN node interaction via JSON-RPC. */
13
+ export class BchnRpcClient {
14
+ constructor(config) {
15
+ var _a, _b, _c, _d;
16
+ this.url = validateAndConstructUrl(config);
17
+ if (!config.rpcUser)
18
+ throw new Error('Need to provide rpcUser in config');
19
+ if (!config.rpcPassword)
20
+ throw new Error('Need to provide rpcPassword in config');
21
+ this.rpcUser = config.rpcUser;
22
+ this.rpcPassword = config.rpcPassword;
23
+ // optional config
24
+ this.maxRetries = (_a = config.maxRetries) !== null && _a !== void 0 ? _a : 0;
25
+ this.retryDelayMs = (_b = config.retryDelayMs) !== null && _b !== void 0 ? _b : 100;
26
+ this.logger = (_c = config.logger) !== null && _c !== void 0 ? _c : console;
27
+ this.timeoutMs = (_d = config.timeoutMs) !== null && _d !== void 0 ? _d : 5000;
28
+ }
29
+ /**
30
+ * Sends a typed RPC request to the BCHN node.
31
+ * @example
32
+ * const result = await client.request<GetBlockCount>("getblockcount");
33
+ * const block = await client.request<GetBlockVerbosity1>("getblock", hash, 1);
34
+ */
35
+ request(endpoint, ...params) {
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ var _a;
38
+ const auth = Buffer.from(`${this.rpcUser}:${this.rpcPassword}`).toString('base64');
39
+ // Retry logic
40
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
41
+ try {
42
+ // Send the request with a timeout and retries
43
+ const response = yield fetch(this.url, {
44
+ method: 'POST',
45
+ headers: {
46
+ 'Content-Type': 'application/json',
47
+ 'Authorization': `Basic ${auth}`
48
+ },
49
+ body: JSON.stringify({ jsonrpc: '2.0', method: endpoint, params, id: getRandomId() }),
50
+ signal: AbortSignal.timeout(this.timeoutMs),
51
+ });
52
+ const result = yield response.json();
53
+ // Handle response errors
54
+ if (!response.ok || result.error) {
55
+ throw new Error(`Error: ${((_a = result.error) === null || _a === void 0 ? void 0 : _a.message) || response.statusText}`);
56
+ }
57
+ return result.result; // Return the result if successful
58
+ }
59
+ catch (error) {
60
+ let errorMessage;
61
+ // Check if the error is due to timeout or other fetch-related issues
62
+ if (typeof error == 'string') {
63
+ errorMessage = error;
64
+ this.logger.error(error);
65
+ }
66
+ else if (error instanceof DOMException && error.name === 'TimeoutError') {
67
+ // If error is an instance DOMException TimeoutError
68
+ errorMessage = error.message;
69
+ this.logger.error(`Request timed out after ${this.timeoutMs} ms`);
70
+ }
71
+ else if (error instanceof Error) {
72
+ // If error is an instance of Error, you can safely access its properties
73
+ errorMessage = error.message;
74
+ this.logger.error(`Request failed with error: ${error.message}`);
75
+ }
76
+ // Retry if allowed
77
+ if (attempt < this.maxRetries) {
78
+ this.logger.warn(`Retrying request... (${attempt + 1}/${this.maxRetries})`);
79
+ yield new Promise(res => setTimeout(res, this.retryDelayMs)); // Wait before retrying
80
+ }
81
+ else {
82
+ // If no retries are left, throw the final error
83
+ throw new RetryLimitExceededError(`Request failed after ${this.maxRetries + 1} attempts: ${errorMessage}`);
84
+ }
85
+ }
86
+ }
87
+ // This line ensures TypeScript is satisfied that a value will always be returned, but
88
+ // it should never be reached if the retries fail, as the last attempt should throw an error.
89
+ throw new Error('Request failed unexpectedly');
90
+ });
91
+ }
92
+ }
@@ -0,0 +1,3 @@
1
+ export declare class RetryLimitExceededError extends Error {
2
+ constructor(message: string);
3
+ }
@@ -0,0 +1,6 @@
1
+ export class RetryLimitExceededError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = 'RetryLimitExceededError';
5
+ }
6
+ }
@@ -0,0 +1,11 @@
1
+ import type { RpcClientConfig } from "../interfaces/interfaces.js";
2
+ export declare function getRandomId(): number;
3
+ export declare function validateAndConstructUrl(config: RpcClientConfig): string;
4
+ export declare function validateUrl(url: string): string;
5
+ export declare enum BchnNetworkPort {
6
+ Mainnet = 8332,
7
+ Testnet = 18332,
8
+ Testnet4 = 28332,
9
+ Scalenet = 38332,
10
+ Regtest = 18443
11
+ }
@@ -0,0 +1,49 @@
1
+ export function getRandomId() {
2
+ return Math.floor(Math.random() * 100000);
3
+ }
4
+ // A utility function to validate and construct the URL from the RpcClientConfig object
5
+ export function validateAndConstructUrl(config) {
6
+ let url;
7
+ if (isUrlConfig(config)) {
8
+ url = validateUrl(config.url);
9
+ }
10
+ else if (isHostConfig(config)) {
11
+ const { protocol, host, port } = config;
12
+ if (protocol !== 'http' && protocol !== 'https') {
13
+ throw new Error("Protocol should be 'http' or 'https'");
14
+ }
15
+ url = validateUrl(`${protocol}://${host}:${port}`);
16
+ }
17
+ else {
18
+ throw new Error('Invalid configuration: Either provide the url or protocol/host/port');
19
+ }
20
+ return url;
21
+ }
22
+ // A utility function to validate a URL
23
+ export function validateUrl(url) {
24
+ if (!url)
25
+ throw new Error('URL is required');
26
+ try {
27
+ new URL(url);
28
+ }
29
+ catch (err) {
30
+ throw new Error('Invalid URL format');
31
+ }
32
+ return url;
33
+ }
34
+ // Type guard to check if the config is RpcClientUrlConfig
35
+ function isUrlConfig(config) {
36
+ return 'url' in config;
37
+ }
38
+ // Type guard to check if the config is RpcClientHostConfig
39
+ function isHostConfig(config) {
40
+ return 'protocol' in config && 'hostname' in config && 'port' in config;
41
+ }
42
+ export var BchnNetworkPort;
43
+ (function (BchnNetworkPort) {
44
+ BchnNetworkPort[BchnNetworkPort["Mainnet"] = 8332] = "Mainnet";
45
+ BchnNetworkPort[BchnNetworkPort["Testnet"] = 18332] = "Testnet";
46
+ BchnNetworkPort[BchnNetworkPort["Testnet4"] = 28332] = "Testnet4";
47
+ BchnNetworkPort[BchnNetworkPort["Scalenet"] = 38332] = "Scalenet";
48
+ BchnNetworkPort[BchnNetworkPort["Regtest"] = 18443] = "Regtest";
49
+ })(BchnNetworkPort || (BchnNetworkPort = {}));
package/package.json CHANGED
@@ -1,11 +1,15 @@
1
1
  {
2
2
  "name": "@mr-zwets/bchn-api-wrapper",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "a Typescript wrapper for interacting with the Bitcoin Cash Node (BCHN) API ",
5
5
  "type": "module",
6
+ "files": [
7
+ "dist"
8
+ ],
6
9
  "main": "dist/index.js",
7
10
  "author": "mr-zwets",
8
11
  "scripts": {
12
+ "prepare": "pnpm build",
9
13
  "build": "tsc",
10
14
  "test": "vitest"
11
15
  },
@@ -13,8 +17,8 @@
13
17
  "devDependencies": {
14
18
  "@types/node": "^24.10.4",
15
19
  "msw": "^2.12.4",
16
- "typescript": "^5.9.3",
17
- "vitest": "^4.0.16"
20
+ "typescript": "^6.0.2",
21
+ "vitest": "^4.1.2"
18
22
  },
19
23
  "directories": {
20
24
  "test": "test"
@@ -1,8 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "WebFetch(domain:github.com)",
5
- "WebFetch(domain:docs.bitcoincashnode.org)"
6
- ]
7
- }
8
- }
@@ -1,36 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- pull_request:
7
- branches: [main]
8
-
9
- permissions:
10
- contents: read
11
-
12
- jobs:
13
- check:
14
- runs-on: ubuntu-latest
15
-
16
- steps:
17
- - name: Checkout
18
- uses: actions/checkout@v4
19
-
20
- - name: Set up pnpm
21
- uses: pnpm/action-setup@v4
22
-
23
- - name: Set up Node.js
24
- uses: actions/setup-node@v4
25
- with:
26
- node-version: 22
27
- cache: pnpm
28
-
29
- - name: Install dependencies
30
- run: pnpm install
31
-
32
- - name: Build
33
- run: pnpm build
34
-
35
- - name: Run tests
36
- run: pnpm test run
package/CLAUDE.md DELETED
@@ -1,70 +0,0 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- ## Build & Test Commands
6
-
7
- ```bash
8
- pnpm build # Compile TypeScript to dist/
9
- pnpm test # Run all tests with Vitest
10
- pnpm test <file> # Run a specific test file
11
- ```
12
-
13
- ## Architecture
14
-
15
- TypeScript library providing type-safe wrappers for Bitcoin Cash Node (BCHN) JSON-RPC and REST interfaces. Types compatible with **BCHN v29.0.0**. Zero runtime dependencies.
16
-
17
- ### Two Client Classes
18
-
19
- **BchnRestClient** (`src/restClient.ts`)
20
- - Wraps 11 REST endpoints for read-only blockchain access-
21
- - Class with dedicated methods for each endpoint
22
- - Supports format options (`json`, `hex`, `bin`) via conditional return types
23
- - Uses function overloads for type-safe returns based on parameters (e.g., `getBlock` with `includeTxDetails`)
24
-
25
- **BchnRpcClient** (`src/rpcClient.ts`)
26
- - Wraps 136 RPC commands for full node interaction
27
- - Single generic `request<T extends RpcRequest>()` method
28
- - Type safety achieved through discriminated union interfaces
29
- - Each RPC command interface defines: `method`, `params[]`, and `response` type
30
-
31
- ### Interface Organization
32
-
33
- ```
34
- src/interfaces/
35
- ├── interfaces.ts # Config types, RpcRequest base, shared types
36
- ├── restInterfaces/ # REST response types
37
- └── rpcInterfaces/ # 136 RPC command interfaces across 9 files
38
- ├── blockchain.ts # 33 commands
39
- ├── wallet.ts # 52 commands
40
- ├── rawtransactions.ts
41
- ├── network.ts # 14 commands
42
- ├── mining.ts
43
- ├── control.ts # 6 commands
44
- ├── util.ts
45
- ├── generating.ts
46
- └── zmq.ts
47
- ```
48
-
49
- ### RPC Type Pattern
50
-
51
- RPC interfaces follow this pattern for full type inference:
52
-
53
- ```typescript
54
- export interface GetBlockVerbosity1 extends GetBlockBase {
55
- params: [blockhash: string, verbosity?: 1 | true];
56
- response: { hash: string; /* ... */ };
57
- }
58
-
59
- // Usage - params and response are fully typed:
60
- const result = await rpcClient.request<GetBlockVerbosity1>("getblock", hash, 1);
61
- ```
62
-
63
- ### Configuration Types
64
-
65
- - `RpcClientConfig`: Supports URL-based (`{url, rpcUser, rpcPassword}`) or host-based (`{protocol, host, port, rpcUser, rpcPassword}`) configuration
66
- - `RestClientConfig`: Requires `url`, optional `logger` and `timeoutMs`
67
-
68
- ### Testing
69
-
70
- Tests use Vitest with MSW (Mock Service Worker) for HTTP interception. Mock server setup is in `test/setupTests.ts`.