@mr-zwets/bchn-api-wrapper 1.0.1

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 (60) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +129 -0
  3. package/dist/index.d.ts +4 -0
  4. package/dist/index.js +4 -0
  5. package/dist/interfaces/interfaces.d.ts +70 -0
  6. package/dist/interfaces/interfaces.js +1 -0
  7. package/dist/interfaces/restInterfaces/interfaces.d.ts +109 -0
  8. package/dist/interfaces/restInterfaces/interfaces.js +1 -0
  9. package/dist/interfaces/rpcInterfaces/blockchain.d.ts +692 -0
  10. package/dist/interfaces/rpcInterfaces/blockchain.js +3 -0
  11. package/dist/interfaces/rpcInterfaces/control.d.ts +54 -0
  12. package/dist/interfaces/rpcInterfaces/control.js +3 -0
  13. package/dist/interfaces/rpcInterfaces/generating.d.ts +17 -0
  14. package/dist/interfaces/rpcInterfaces/generating.js +3 -0
  15. package/dist/interfaces/rpcInterfaces/index.d.ts +9 -0
  16. package/dist/interfaces/rpcInterfaces/index.js +12 -0
  17. package/dist/interfaces/rpcInterfaces/mining.d.ts +131 -0
  18. package/dist/interfaces/rpcInterfaces/mining.js +3 -0
  19. package/dist/interfaces/rpcInterfaces/network.d.ts +179 -0
  20. package/dist/interfaces/rpcInterfaces/network.js +3 -0
  21. package/dist/interfaces/rpcInterfaces/rawtransactions.d.ts +283 -0
  22. package/dist/interfaces/rpcInterfaces/rawtransactions.js +3 -0
  23. package/dist/interfaces/rpcInterfaces/util.d.ts +44 -0
  24. package/dist/interfaces/rpcInterfaces/util.js +3 -0
  25. package/dist/interfaces/rpcInterfaces/wallet.d.ts +620 -0
  26. package/dist/interfaces/rpcInterfaces/wallet.js +3 -0
  27. package/dist/interfaces/rpcInterfaces/zmq.d.ts +8 -0
  28. package/dist/interfaces/rpcInterfaces/zmq.js +3 -0
  29. package/dist/restClient.d.ts +17 -0
  30. package/dist/restClient.js +100 -0
  31. package/dist/rpcClient.d.ts +12 -0
  32. package/dist/rpcClient.js +85 -0
  33. package/dist/utils/errors.d.ts +3 -0
  34. package/dist/utils/errors.js +6 -0
  35. package/dist/utils/utils.d.ts +11 -0
  36. package/dist/utils/utils.js +49 -0
  37. package/package.json +40 -0
  38. package/src/index.ts +4 -0
  39. package/src/interfaces/interfaces.ts +87 -0
  40. package/src/interfaces/restInterfaces/interfaces.ts +117 -0
  41. package/src/interfaces/rpcInterfaces/blockchain.ts +759 -0
  42. package/src/interfaces/rpcInterfaces/control.ts +62 -0
  43. package/src/interfaces/rpcInterfaces/generating.ts +21 -0
  44. package/src/interfaces/rpcInterfaces/index.ts +14 -0
  45. package/src/interfaces/rpcInterfaces/mining.ts +143 -0
  46. package/src/interfaces/rpcInterfaces/network.ts +195 -0
  47. package/src/interfaces/rpcInterfaces/rawtransactions.ts +314 -0
  48. package/src/interfaces/rpcInterfaces/util.ts +52 -0
  49. package/src/interfaces/rpcInterfaces/wallet.ts +674 -0
  50. package/src/interfaces/rpcInterfaces/zmq.ts +11 -0
  51. package/src/restClient.ts +119 -0
  52. package/src/rpcClient.ts +93 -0
  53. package/src/utils/errors.ts +6 -0
  54. package/src/utils/utils.ts +55 -0
  55. package/test/restClient.test.ts +32 -0
  56. package/test/rpcClient.test.ts +115 -0
  57. package/test/setupTests.ts +54 -0
  58. package/test/tsconfig.json +4 -0
  59. package/tsconfig.json +13 -0
  60. package/vitest.config.ts +9 -0
@@ -0,0 +1,119 @@
1
+ import type { RestClientConfig, formatOptions, ResponseType } from "./interfaces/interfaces.js";
2
+ import type {
3
+ BlockInfoNoTxDetails,
4
+ BlockInfoTxDetails,
5
+ ChainInfo,
6
+ HeaderInfo,
7
+ MempoolContent,
8
+ MempoolInfo,
9
+ TxDetails,
10
+ UtxosInfo
11
+ } from "./interfaces/restInterfaces/interfaces.js";
12
+ import { validateUrl } from "./utils/utils.js";
13
+
14
+ export class BchnRestClient {
15
+ private baseUrl: string;
16
+ private timeoutMs: number;
17
+ private logger: Console;
18
+
19
+ constructor(config: RestClientConfig) {
20
+ this.baseUrl = validateUrl(config.url);
21
+ this.timeoutMs = config.timeoutMs ?? 5000;
22
+ this.logger = config.logger ?? console;
23
+ }
24
+
25
+ private async fetchFromNode<T, TFormat extends formatOptions>(
26
+ endpoint: string,
27
+ format: TFormat
28
+ ): Promise<ResponseType<TFormat, T>> {
29
+ try {
30
+ const response = await fetch(`${this.baseUrl}/rest/${endpoint}`, {
31
+ signal: AbortSignal.timeout(this.timeoutMs),
32
+ });
33
+
34
+ if (!response.ok) {
35
+ throw new Error(`Error fetching data from ${endpoint}: ${response.statusText}`);
36
+ }
37
+
38
+ if (format === 'json') {
39
+ return await response.json() as ResponseType<TFormat, T>;
40
+ } else {
41
+ return await response.text() as ResponseType<TFormat, T>; // For 'bin' and 'hex', return raw text
42
+ }
43
+ } catch(error) {
44
+ let errorMessage: string | undefined
45
+
46
+ // Check if the error is due to timeout or other fetch-related issues
47
+ if (typeof error === 'string') {
48
+ errorMessage = error;
49
+ this.logger.error(error);
50
+ } else if (error instanceof DOMException && error.name === 'TimeoutError') {
51
+ // If error is an instance DOMException TimeoutError
52
+ errorMessage = 'Request timed out';
53
+ this.logger.error(`Request to ${endpoint} timed out after ${this.timeoutMs} ms`);
54
+ } else {
55
+ this.logger.error(`Unknown error occurred during request to ${endpoint}`);
56
+ throw new Error(`Unknown error: ${error}`);
57
+ }
58
+
59
+ // Always rethrow the error after logging
60
+ throw new Error(errorMessage);
61
+ }
62
+ }
63
+
64
+ // Get transaction details by transaction hash
65
+ async getTransaction<TFormat extends formatOptions = 'json'>(
66
+ txid: string, format:TFormat = 'json' as TFormat
67
+ ) {
68
+ return this.fetchFromNode<TxDetails, TFormat>(`tx/${txid}.${format}`, format);
69
+ }
70
+
71
+ // getBlock overload signatures
72
+ // This is needed so the getBlock return type can depend on the 'includeTxDetails' boolean flag
73
+ async getBlock<TFormat extends formatOptions = 'json'>(
74
+ blockhash: string, includeTxDetails: true, format?:TFormat
75
+ ): Promise<TFormat extends 'json' ? BlockInfoTxDetails : string>;
76
+
77
+ async getBlock<TFormat extends formatOptions = 'json'>(
78
+ blockhash: string, includeTxDetails: false, format?:TFormat
79
+ ): Promise<TFormat extends 'json' ? BlockInfoNoTxDetails : string>;
80
+
81
+ // getBlock Implementation
82
+ async getBlock<TFormat extends formatOptions = 'json'>(
83
+ blockhash: string, includeTxDetails: boolean, format:TFormat = 'json' as TFormat
84
+ ): Promise<any> {
85
+ const path = includeTxDetails ? 'block' : 'block/notxdetails';
86
+ return this.fetchFromNode(`${path}/${blockhash}.${format}`, format);
87
+ }
88
+
89
+ // Get block headers starting from a specific block hash
90
+ async getBlockHeaders<TFormat extends formatOptions = 'json'>(
91
+ count: number, blockhash: string, format:TFormat = 'json' as TFormat
92
+ ) {
93
+ return this.fetchFromNode<HeaderInfo, TFormat>(`headers/${count}/${blockhash}.${format}`, format);
94
+ }
95
+
96
+ // Get chain info (chain state details)
97
+ async getChainInfo() {
98
+ return this.fetchFromNode<ChainInfo, 'json'>('chaininfo.json', 'json');
99
+ }
100
+
101
+ // Query UTXO set based on specific outpoints (txid and vout)
102
+ async getUTXOs<TFormat extends formatOptions = 'json'>(
103
+ checkmempool: boolean, outpoints: string[], format:TFormat = 'json' as TFormat
104
+ ) {
105
+ const path = (checkmempool ? 'checkmempool/' : '') + outpoints.join('/');
106
+ const endpoint = `getutxos/${path}.${format}`;
107
+ return this.fetchFromNode<UtxosInfo, TFormat>(endpoint, format);
108
+ }
109
+
110
+ // Get mempool information (basic)
111
+ async getMempoolInfo() {
112
+ return this.fetchFromNode<MempoolInfo, 'json'>('mempool/info.json', 'json');
113
+ }
114
+
115
+ // Get mempool contents (transactions currently in the mempool)
116
+ async getMempoolContents() {
117
+ return this.fetchFromNode<MempoolContent, 'json'>('mempool/contents.json', 'json');
118
+ }
119
+ }
@@ -0,0 +1,93 @@
1
+ import type { RpcClientConfig, RpcRequest } from "./interfaces/interfaces.js";
2
+ import { getRandomId, validateAndConstructUrl } from "./utils/utils.js";
3
+ import { RetryLimitExceededError } from "./utils/errors.js";
4
+
5
+ export class BchnRpcClient {
6
+ private url: string
7
+ private rpcUser: string
8
+ private rpcPassword: string
9
+
10
+ private maxRetries: number // number of retries before throwing an exception
11
+ private retryDelayMs: number // delay between each retry
12
+ private logger: typeof console // logger
13
+ private timeoutMs: number // max timeout for each retry
14
+
15
+ constructor(config: RpcClientConfig){
16
+ this.url = validateAndConstructUrl(config)
17
+ if(!config.rpcUser) throw new Error('Need to provide rpcUser in config')
18
+ if(!config.rpcPassword) throw new Error('Need to provide rpcPassword in config')
19
+ this.rpcUser = config.rpcUser;
20
+ this.rpcPassword= config.rpcPassword;
21
+
22
+ // optional config
23
+ this.maxRetries = config.maxRetries ?? 0;
24
+ this.retryDelayMs= config.retryDelayMs ?? 100;
25
+ this.logger = config.logger ?? console;
26
+ this.timeoutMs = config.timeoutMs ?? 5000;
27
+ }
28
+
29
+ async request<T extends RpcRequest>(
30
+ endpoint: T['method'],
31
+ ...params: T['params']
32
+ ): Promise<T['response']> {
33
+ const auth = Buffer.from(`${this.rpcUser}:${this.rpcPassword}`).toString('base64');
34
+
35
+ // Retry logic
36
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
37
+ try {
38
+ // Send the request with a timeout and retries
39
+ const response = await fetch(this.url, {
40
+ method: 'POST',
41
+ headers: {
42
+ 'Content-Type': 'application/json',
43
+ 'Authorization': `Basic ${auth}`
44
+ },
45
+ body: JSON.stringify({ jsonrpc: '2.0', method: endpoint, params, id: getRandomId() }),
46
+ signal: AbortSignal.timeout(this.timeoutMs),
47
+ });
48
+
49
+ const result = await response.json();
50
+
51
+ // Handle response errors
52
+ if (!response.ok || result.error) {
53
+ throw new Error(`Error: ${result.error?.message || response.statusText}`);
54
+ }
55
+
56
+ return result.result as T['response']; // Return the result if successful
57
+
58
+ } catch (error) {
59
+ let errorMessage: string | undefined
60
+
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
+ }
77
+
78
+ // Retry if allowed
79
+ if (attempt < this.maxRetries) {
80
+ this.logger.warn(`Retrying request... (${attempt + 1}/${this.maxRetries})`);
81
+ await new Promise(res => setTimeout(res, this.retryDelayMs)); // Wait before retrying
82
+ } else {
83
+ // If no retries are left, throw the final error
84
+ throw new RetryLimitExceededError(`Request failed after ${this.maxRetries + 1} attempts: ${errorMessage}`);
85
+ }
86
+ }
87
+ }
88
+
89
+ // This line ensures TypeScript is satisfied that a value will always be returned, but
90
+ // it should never be reached if the retries fail, as the last attempt should throw an error.
91
+ throw new Error('Request failed unexpectedly');
92
+ }
93
+ }
@@ -0,0 +1,6 @@
1
+ export class RetryLimitExceededError extends Error {
2
+ constructor(message: string) {
3
+ super(message);
4
+ this.name = 'RetryLimitExceededError';
5
+ }
6
+ }
@@ -0,0 +1,55 @@
1
+ import type {
2
+ RpcClientConfig,
3
+ RpcClientUrlConfig,
4
+ RpcClientHostConfig,
5
+ } from "../interfaces/interfaces.js";
6
+
7
+ export function getRandomId(): number {
8
+ return Math.floor(Math.random() * 100000);
9
+ }
10
+
11
+ // A utility function to validate and construct the URL from the RpcClientConfig object
12
+ export function validateAndConstructUrl(config: RpcClientConfig): string {
13
+ let url: string
14
+ if (isUrlConfig(config)) {
15
+ url = validateUrl(config.url)
16
+ } else if (isHostConfig(config)) {
17
+ const { protocol, host, port } = config;
18
+ if (protocol !== 'http' && protocol !== 'https') {
19
+ throw new Error("Protocol should be 'http' or 'https'");
20
+ }
21
+ url = validateUrl(`${protocol}://${host}:${port}`)
22
+ } else {
23
+ throw new Error('Invalid configuration: Either provide the url or protocol/host/port');
24
+ }
25
+ return url
26
+ }
27
+
28
+ // A utility function to validate a URL
29
+ export function validateUrl(url: string) {
30
+ if(!url) throw new Error('URL is required');
31
+ try {
32
+ new URL(url);
33
+ } catch (err) {
34
+ throw new Error('Invalid URL format');
35
+ }
36
+ return url
37
+ }
38
+
39
+ // Type guard to check if the config is RpcClientUrlConfig
40
+ function isUrlConfig(config: RpcClientConfig): config is RpcClientUrlConfig {
41
+ return 'url' in config;
42
+ }
43
+
44
+ // Type guard to check if the config is RpcClientHostConfig
45
+ function isHostConfig(config: RpcClientConfig): config is RpcClientHostConfig {
46
+ return 'protocol' in config && 'hostname' in config && 'port' in config;
47
+ }
48
+
49
+ export enum BchnNetworkPort {
50
+ Mainnet = 8332,
51
+ Testnet = 18332,
52
+ Testnet4 = 28332,
53
+ Scalenet = 38332,
54
+ Regtest = 18443
55
+ }
@@ -0,0 +1,32 @@
1
+ import { BchnRestClient } from '../src/index.js';
2
+
3
+ describe('BchnRestClient URL validation tests', () => {
4
+ it('should create an instance with a valid URL', () => {
5
+ const client = new BchnRestClient({url: 'http://localhost:8332'});
6
+ expect(client).toBeInstanceOf(BchnRestClient);
7
+ });
8
+
9
+ it('should throw an error for an invalid URL', () => {
10
+ expect(() => new BchnRestClient({url: 'invalid-url'})).toThrow('Invalid URL');
11
+ });
12
+
13
+ it('should throw an error if the URL is missing', () => {
14
+ expect(() => new BchnRestClient({url: ''})).toThrow('URL is required');
15
+ });
16
+ });
17
+
18
+ describe('BchnRestClient Timeout Handling', () => {
19
+ const config = {
20
+ url: 'http://localhost:8332',
21
+ timeoutMs: 1000, // 1 second timeout
22
+ };
23
+ const restClient = new BchnRestClient(config);
24
+
25
+ it('should throw a timeout error if the request exceeds the timeout limit', async () => {
26
+ await expect(restClient.getChainInfo()).rejects.toThrow('Request timed out');
27
+ });
28
+
29
+ it('should not return a timeout error if the request completes in time', async () => {
30
+ await expect(restClient.getMempoolInfo()).resolves.toEqual({});
31
+ });
32
+ });
@@ -0,0 +1,115 @@
1
+ import { BchnRpcClient, type GetBestBlockHash, type GetBlockCount, type GetBlockHash, type RpcClientConfig } from '../src/index.js';
2
+
3
+ describe('BchnRpcClient should have the correct constructor arguments', () => {
4
+ it('should create an instance with a valid URL', () => {
5
+ const config = {
6
+ url: 'http://localhost:8332',
7
+ rpcUser: 'rpcUser',
8
+ rpcPassword: 'rpcPassword'
9
+ }
10
+ const client = new BchnRpcClient(config);
11
+ expect(client).toBeInstanceOf(BchnRpcClient);
12
+ });
13
+
14
+ it('should throw an error for an invalid URL', () => {
15
+ const config = {
16
+ url: 'invalid-url',
17
+ rpcUser: 'rpcUser',
18
+ rpcPassword: 'rpcPassword'
19
+ }
20
+ expect(() => new BchnRpcClient(config)).toThrow('Invalid URL');
21
+ });
22
+
23
+ it('should throw an error if the URL is empty', () => {
24
+ const config = {
25
+ url: '',
26
+ rpcUser: 'rpcUser',
27
+ rpcPassword: 'rpcPassword'
28
+ }
29
+ expect(() => new BchnRpcClient(config)).toThrow('URL is required');
30
+ });
31
+
32
+ it('should throw an error if the URL is missing', () => {
33
+ const config = {
34
+ rpcUser: 'rpcUser',
35
+ rpcPassword: 'rpcPassword'
36
+ } as RpcClientConfig
37
+ expect(() => new BchnRpcClient(config)).toThrow('Invalid configuration: Either provide the url or protocol/host/port');
38
+ });
39
+
40
+ it('should throw an error if rpcUser is missing', () => {
41
+ const config = {
42
+ url: 'http://localhost:8332',
43
+ rpcPassword: 'rpcPassword'
44
+ } as RpcClientConfig
45
+ expect(() => new BchnRpcClient(config)).toThrow('Need to provide rpcUser in config');
46
+ });
47
+
48
+ it('should throw an error if rpcPassword is missing', () => {
49
+ const config = {
50
+ url: 'http://localhost:8332',
51
+ rpcUser: 'rpcUser'
52
+ } as RpcClientConfig
53
+ expect(() => new BchnRpcClient(config)).toThrow('Need to provide rpcPassword in config');
54
+ });
55
+ });
56
+
57
+ describe('BchnRpcClient Timeout and Retry Handling', () => {
58
+ it('should throw a timeout error if the request exceeds the timeout limit', async () => {
59
+ const config = {
60
+ url: 'http://localhost:8332',
61
+ rpcUser: 'rpcUser',
62
+ rpcPassword: 'rpcPassword',
63
+ timeoutMs: 1000,
64
+ }
65
+ const rpcClient = new BchnRpcClient(config);
66
+
67
+ await expect(rpcClient.request("getbestblockhash")).rejects.toThrow('Request failed after 1 attempts: The operation was aborted due to timeout');
68
+ });
69
+
70
+ it('should not return a timeout error if the request completes in time', async () => {
71
+ const config = {
72
+ url: 'http://localhost:8332',
73
+ rpcUser: 'rpcUser',
74
+ rpcPassword: 'rpcPassword',
75
+ timeoutMs: 1000,
76
+ }
77
+ const rpcClient = new BchnRpcClient(config);
78
+
79
+ await expect(rpcClient.request<GetBlockCount>("getblockcount")).resolves.toEqual({});
80
+ });
81
+
82
+ it('should return an RetryLimitExceededError if all retries fail', async () => {
83
+ const config = {
84
+ url: 'http://localhost:8332',
85
+ rpcUser: 'rpcUser',
86
+ rpcPassword: 'rpcPassword',
87
+ maxRetries: 3,
88
+ timeoutMs: 1000,
89
+ }
90
+ const rpcClient = new BchnRpcClient(config);
91
+ await expect(rpcClient.request<GetBestBlockHash>("getbestblockhash")).rejects.toThrow("Request failed after 4 attempts: The operation was aborted due to timeout");
92
+ });
93
+ });
94
+
95
+ describe('BchnRpcClient Handling of Parameters', () => {
96
+ it('should error with incorrect number of params', async () => {
97
+ const config = {
98
+ url: 'http://localhost:8332',
99
+ rpcUser: 'rpcUser',
100
+ rpcPassword: 'rpcPassword',
101
+ }
102
+ const rpcClient = new BchnRpcClient(config);
103
+ await expect(rpcClient.request("getblockhash")).rejects.toThrow("Request failed after 1 attempts: Error: Invalid Request");
104
+ })
105
+
106
+ it('should not error with correct number of params', async () => {
107
+ const config = {
108
+ url: 'http://localhost:8332',
109
+ rpcUser: 'rpcUser',
110
+ rpcPassword: 'rpcPassword',
111
+ }
112
+ const rpcClient = new BchnRpcClient(config);
113
+ await expect(rpcClient.request<GetBlockHash>("getblockhash", 5)).resolves.toEqual({});
114
+ })
115
+ })
@@ -0,0 +1,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 server: SetupServerApi = setupServer(
7
+ // Mock endpoint with a delay to simulate timeout
8
+ http.get('http://localhost:8332/rest/chaininfo.json', async() => {
9
+ // Introduce a delay longer than the timeout setting to simulate a timeout scenario
10
+ await delay(3000)
11
+ return HttpResponse.json({})
12
+ }),
13
+
14
+ http.get('http://localhost:8332/rest/mempool/info.json', async() => {
15
+ // Mock normally working REST endpoint
16
+ await delay(500)
17
+ return HttpResponse.json({})
18
+ }),
19
+
20
+ http.post('http://localhost:8332', async ({ request }) => {
21
+ const json = await request.json() as jsonResult;
22
+
23
+ if (json === null || json === undefined) {
24
+ throw new Error('Invalid JSON response');
25
+ }
26
+
27
+ // Introduce a delay longer than the timeout setting to simulate a timeout scenario
28
+ if (json.method === 'getbestblockhash') {
29
+ await delay(3000)
30
+ return HttpResponse.json({"jsonrpc": "2.0", "result": {}, "id": 4})
31
+ }
32
+
33
+ if (json.method === 'getblockcount') {
34
+ // Mock normally working RPC command
35
+ await delay(500)
36
+ return HttpResponse.json({"jsonrpc": "2.0", "result": {}, "id": 5})
37
+ }
38
+
39
+ if (json.method === 'getblockhash') {
40
+ // Mock normally working RPC command
41
+ await delay(500)
42
+ if (json.params?.[0]) {
43
+ return HttpResponse.json({"jsonrpc": "2.0", "result": {}, "id": 6})
44
+ } else {
45
+ return HttpResponse.json({"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": 7})
46
+ }
47
+ }
48
+ })
49
+ );
50
+
51
+ // Start the server before tests and reset handlers after each test
52
+ beforeAll(() => server.listen());
53
+ afterEach(() => server.resetHandlers());
54
+ afterAll(() => server.close());
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "include": ["**/*"],
4
+ }
package/tsconfig.json ADDED
@@ -0,0 +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
+ }
@@ -0,0 +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
+ },
9
+ })