@mr-zwets/bchn-api-wrapper 1.0.1 → 1.0.2
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.
- package/.claude/settings.local.json +8 -0
- package/.github/workflows/ci.yaml +36 -0
- package/CLAUDE.md +70 -0
- package/README.md +121 -129
- package/dist/interfaces/interfaces.d.ts +13 -0
- package/dist/interfaces/restInterfaces/interfaces.d.ts +124 -18
- package/dist/interfaces/rpcInterfaces/blockchain.d.ts +293 -102
- package/dist/interfaces/rpcInterfaces/control.d.ts +6 -0
- package/dist/interfaces/rpcInterfaces/generating.d.ts +2 -0
- package/dist/interfaces/rpcInterfaces/mining.d.ts +9 -0
- package/dist/interfaces/rpcInterfaces/network.d.ts +18 -0
- package/dist/interfaces/rpcInterfaces/rawtransactions.d.ts +21 -0
- package/dist/interfaces/rpcInterfaces/util.d.ts +5 -0
- package/dist/interfaces/rpcInterfaces/wallet.d.ts +54 -0
- package/dist/interfaces/rpcInterfaces/zmq.d.ts +1 -0
- package/dist/restClient.d.ts +13 -1
- package/dist/restClient.js +19 -6
- package/dist/rpcClient.d.ts +7 -0
- package/dist/rpcClient.js +7 -0
- package/package.json +7 -8
- package/pnpm-lock.yaml +1279 -0
- package/src/index.ts +3 -3
- package/src/interfaces/interfaces.ts +96 -86
- package/src/interfaces/restInterfaces/interfaces.ts +235 -116
- package/src/interfaces/rpcInterfaces/blockchain.ts +932 -758
- package/src/interfaces/rpcInterfaces/control.ts +68 -62
- package/src/interfaces/rpcInterfaces/generating.ts +23 -21
- package/src/interfaces/rpcInterfaces/index.ts +13 -13
- package/src/interfaces/rpcInterfaces/mining.ts +151 -143
- package/src/interfaces/rpcInterfaces/network.ts +213 -195
- package/src/interfaces/rpcInterfaces/rawtransactions.ts +332 -314
- package/src/interfaces/rpcInterfaces/util.ts +56 -52
- package/src/interfaces/rpcInterfaces/wallet.ts +728 -674
- package/src/interfaces/rpcInterfaces/zmq.ts +12 -11
- package/src/restClient.ts +134 -119
- package/src/rpcClient.ts +100 -93
- package/src/utils/errors.ts +6 -6
- package/src/utils/utils.ts +55 -55
- package/test/restClient.test.ts +33 -31
- package/test/rpcClient.test.ts +119 -115
- package/test/setupTests.ts +56 -54
- package/test/tsconfig.json +4 -4
- package/tsconfig.json +13 -13
- package/vitest.config.ts +8 -8
- package/CHANGELOG.md +0 -7
package/test/restClient.test.ts
CHANGED
|
@@ -1,32 +1,34 @@
|
|
|
1
|
-
import { BchnRestClient } from '../src/index.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
1
|
+
import { BchnRestClient } from '../src/index.js';
|
|
2
|
+
|
|
3
|
+
const localhostUrl = 'http://localhost:8332';
|
|
4
|
+
|
|
5
|
+
describe('BchnRestClient URL validation tests', () => {
|
|
6
|
+
it('should create an instance with a valid URL', () => {
|
|
7
|
+
const client = new BchnRestClient({url: localhostUrl});
|
|
8
|
+
expect(client).toBeInstanceOf(BchnRestClient);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should throw an error for an invalid URL', () => {
|
|
12
|
+
expect(() => new BchnRestClient({url: 'invalid-url'})).toThrow('Invalid URL');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should throw an error if the URL is missing', () => {
|
|
16
|
+
expect(() => new BchnRestClient({url: ''})).toThrow('URL is required');
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('BchnRestClient Timeout Handling', () => {
|
|
21
|
+
const config = {
|
|
22
|
+
url: localhostUrl,
|
|
23
|
+
timeoutMs: 1000, // 1 second timeout
|
|
24
|
+
};
|
|
25
|
+
const restClient = new BchnRestClient(config);
|
|
26
|
+
|
|
27
|
+
it('should throw a timeout error if the request exceeds the timeout limit', async () => {
|
|
28
|
+
await expect(restClient.getChainInfo()).rejects.toThrow('Request timed out');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should not return a timeout error if the request completes in time', async () => {
|
|
32
|
+
await expect(restClient.getMempoolInfo()).resolves.toEqual({});
|
|
33
|
+
});
|
|
32
34
|
});
|
package/test/rpcClient.test.ts
CHANGED
|
@@ -1,115 +1,119 @@
|
|
|
1
|
-
import { BchnRpcClient, type GetBestBlockHash, type GetBlockCount, type GetBlockHash, type RpcClientConfig } from '../src/index.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
1
|
+
import { BchnRpcClient, type GetBestBlockHash, type GetBlockCount, type GetBlockHash, type RpcClientConfig } from '../src/index.js';
|
|
2
|
+
|
|
3
|
+
const localhostUrl = 'http://localhost:8332';
|
|
4
|
+
const testRpcUser = 'rpcUser';
|
|
5
|
+
const testRpcPassword = 'rpcPassword';
|
|
6
|
+
|
|
7
|
+
describe('BchnRpcClient should have the correct constructor arguments', () => {
|
|
8
|
+
it('should create an instance with a valid URL', () => {
|
|
9
|
+
const config = {
|
|
10
|
+
url: localhostUrl,
|
|
11
|
+
rpcUser: testRpcUser,
|
|
12
|
+
rpcPassword: testRpcPassword
|
|
13
|
+
}
|
|
14
|
+
const client = new BchnRpcClient(config);
|
|
15
|
+
expect(client).toBeInstanceOf(BchnRpcClient);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should throw an error for an invalid URL', () => {
|
|
19
|
+
const config = {
|
|
20
|
+
url: 'invalid-url',
|
|
21
|
+
rpcUser: testRpcUser,
|
|
22
|
+
rpcPassword: testRpcPassword
|
|
23
|
+
}
|
|
24
|
+
expect(() => new BchnRpcClient(config)).toThrow('Invalid URL');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should throw an error if the URL is empty', () => {
|
|
28
|
+
const config = {
|
|
29
|
+
url: '',
|
|
30
|
+
rpcUser: testRpcUser,
|
|
31
|
+
rpcPassword: testRpcPassword
|
|
32
|
+
}
|
|
33
|
+
expect(() => new BchnRpcClient(config)).toThrow('URL is required');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should throw an error if the URL is missing', () => {
|
|
37
|
+
const config = {
|
|
38
|
+
rpcUser: testRpcUser,
|
|
39
|
+
rpcPassword: testRpcPassword
|
|
40
|
+
} as RpcClientConfig
|
|
41
|
+
expect(() => new BchnRpcClient(config)).toThrow('Invalid configuration: Either provide the url or protocol/host/port');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should throw an error if rpcUser is missing', () => {
|
|
45
|
+
const config = {
|
|
46
|
+
url: localhostUrl,
|
|
47
|
+
rpcPassword: testRpcPassword
|
|
48
|
+
} as RpcClientConfig
|
|
49
|
+
expect(() => new BchnRpcClient(config)).toThrow('Need to provide rpcUser in config');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should throw an error if rpcPassword is missing', () => {
|
|
53
|
+
const config = {
|
|
54
|
+
url: localhostUrl,
|
|
55
|
+
rpcUser: testRpcUser
|
|
56
|
+
} as RpcClientConfig
|
|
57
|
+
expect(() => new BchnRpcClient(config)).toThrow('Need to provide rpcPassword in config');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('BchnRpcClient Timeout and Retry Handling', () => {
|
|
62
|
+
it('should throw a timeout error if the request exceeds the timeout limit', async () => {
|
|
63
|
+
const config = {
|
|
64
|
+
url: localhostUrl,
|
|
65
|
+
rpcUser: testRpcUser,
|
|
66
|
+
rpcPassword: testRpcPassword,
|
|
67
|
+
timeoutMs: 1000,
|
|
68
|
+
}
|
|
69
|
+
const rpcClient = new BchnRpcClient(config);
|
|
70
|
+
|
|
71
|
+
await expect(rpcClient.request("getbestblockhash")).rejects.toThrow('Request failed after 1 attempts: The operation was aborted due to timeout');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should not return a timeout error if the request completes in time', async () => {
|
|
75
|
+
const config = {
|
|
76
|
+
url: localhostUrl,
|
|
77
|
+
rpcUser: testRpcUser,
|
|
78
|
+
rpcPassword: testRpcPassword,
|
|
79
|
+
timeoutMs: 1000,
|
|
80
|
+
}
|
|
81
|
+
const rpcClient = new BchnRpcClient(config);
|
|
82
|
+
|
|
83
|
+
await expect(rpcClient.request<GetBlockCount>("getblockcount")).resolves.toEqual({});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should return an RetryLimitExceededError if all retries fail', async () => {
|
|
87
|
+
const config = {
|
|
88
|
+
url: localhostUrl,
|
|
89
|
+
rpcUser: testRpcUser,
|
|
90
|
+
rpcPassword: testRpcPassword,
|
|
91
|
+
maxRetries: 3,
|
|
92
|
+
timeoutMs: 1000,
|
|
93
|
+
}
|
|
94
|
+
const rpcClient = new BchnRpcClient(config);
|
|
95
|
+
await expect(rpcClient.request<GetBestBlockHash>("getbestblockhash")).rejects.toThrow("Request failed after 4 attempts: The operation was aborted due to timeout");
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('BchnRpcClient Handling of Parameters', () => {
|
|
100
|
+
it('should error with incorrect number of params', async () => {
|
|
101
|
+
const config = {
|
|
102
|
+
url: localhostUrl,
|
|
103
|
+
rpcUser: testRpcUser,
|
|
104
|
+
rpcPassword: testRpcPassword,
|
|
105
|
+
}
|
|
106
|
+
const rpcClient = new BchnRpcClient(config);
|
|
107
|
+
await expect(rpcClient.request("getblockhash")).rejects.toThrow("Request failed after 1 attempts: Error: Invalid Request");
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('should not error with correct number of params', async () => {
|
|
111
|
+
const config = {
|
|
112
|
+
url: localhostUrl,
|
|
113
|
+
rpcUser: testRpcUser,
|
|
114
|
+
rpcPassword: testRpcPassword,
|
|
115
|
+
}
|
|
116
|
+
const rpcClient = new BchnRpcClient(config);
|
|
117
|
+
await expect(rpcClient.request<GetBlockHash>("getblockhash", 5)).resolves.toEqual({});
|
|
118
|
+
})
|
|
119
|
+
})
|
package/test/setupTests.ts
CHANGED
|
@@ -1,54 +1,56 @@
|
|
|
1
|
-
import { http, delay, HttpResponse } from 'msw';
|
|
2
|
-
import { setupServer, SetupServerApi } from 'msw/node';
|
|
3
|
-
|
|
4
|
-
type jsonResult = { method: string; params?: unknown[] } | null | undefined
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return HttpResponse.json({"jsonrpc": "2.0", "
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
1
|
+
import { http, delay, HttpResponse } from 'msw';
|
|
2
|
+
import { setupServer, SetupServerApi } from 'msw/node';
|
|
3
|
+
|
|
4
|
+
type jsonResult = { method: string; params?: unknown[] } | null | undefined
|
|
5
|
+
|
|
6
|
+
const localhostUrl = 'http://localhost:8332';
|
|
7
|
+
|
|
8
|
+
const server: SetupServerApi = setupServer(
|
|
9
|
+
// Mock endpoint with a delay to simulate timeout
|
|
10
|
+
http.get(`${localhostUrl}/rest/chaininfo.json`, async() => {
|
|
11
|
+
// Introduce a delay longer than the timeout setting to simulate a timeout scenario
|
|
12
|
+
await delay(3000)
|
|
13
|
+
return HttpResponse.json({})
|
|
14
|
+
}),
|
|
15
|
+
|
|
16
|
+
http.get(`${localhostUrl}/rest/mempool/info.json`, async() => {
|
|
17
|
+
// Mock normally working REST endpoint
|
|
18
|
+
await delay(500)
|
|
19
|
+
return HttpResponse.json({})
|
|
20
|
+
}),
|
|
21
|
+
|
|
22
|
+
http.post(`${localhostUrl}`, async ({ request }) => {
|
|
23
|
+
const json = await request.json() as jsonResult;
|
|
24
|
+
|
|
25
|
+
if (json === null || json === undefined) {
|
|
26
|
+
throw new Error('Invalid JSON response');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Introduce a delay longer than the timeout setting to simulate a timeout scenario
|
|
30
|
+
if (json.method === 'getbestblockhash') {
|
|
31
|
+
await delay(3000)
|
|
32
|
+
return HttpResponse.json({"jsonrpc": "2.0", "result": {}, "id": 4})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (json.method === 'getblockcount') {
|
|
36
|
+
// Mock normally working RPC command
|
|
37
|
+
await delay(500)
|
|
38
|
+
return HttpResponse.json({"jsonrpc": "2.0", "result": {}, "id": 5})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (json.method === 'getblockhash') {
|
|
42
|
+
// Mock normally working RPC command
|
|
43
|
+
await delay(500)
|
|
44
|
+
if (json.params?.[0]) {
|
|
45
|
+
return HttpResponse.json({"jsonrpc": "2.0", "result": {}, "id": 6})
|
|
46
|
+
} else {
|
|
47
|
+
return HttpResponse.json({"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": 7})
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Start the server before tests and reset handlers after each test
|
|
54
|
+
beforeAll(() => server.listen());
|
|
55
|
+
afterEach(() => server.resetHandlers());
|
|
56
|
+
afterAll(() => server.close());
|
package/test/tsconfig.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../tsconfig.json",
|
|
3
|
-
"include": ["**/*"],
|
|
4
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig.json",
|
|
3
|
+
"include": ["**/*"],
|
|
4
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"module": "NodeNext",
|
|
4
|
-
"verbatimModuleSyntax": true,
|
|
5
|
-
"moduleResolution": "NodeNext",
|
|
6
|
-
"target": "ES6",
|
|
7
|
-
"strict": true,
|
|
8
|
-
"outDir": "./dist",
|
|
9
|
-
"declaration": true,
|
|
10
|
-
"types": ["vitest/globals"],
|
|
11
|
-
},
|
|
12
|
-
"include": ["src"]
|
|
13
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "NodeNext",
|
|
4
|
+
"verbatimModuleSyntax": true,
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"target": "ES6",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"outDir": "./dist",
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"types": ["vitest/globals", "node"],
|
|
11
|
+
},
|
|
12
|
+
"include": ["src"]
|
|
13
|
+
}
|
package/vitest.config.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { defineConfig } from 'vitest/config';
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
test: {
|
|
5
|
-
globals: true,
|
|
6
|
-
environment: 'node',
|
|
7
|
-
setupFiles: ['./test/setupTests.ts']
|
|
8
|
-
},
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
globals: true,
|
|
6
|
+
environment: 'node',
|
|
7
|
+
setupFiles: ['./test/setupTests.ts']
|
|
8
|
+
},
|
|
9
9
|
})
|