@tchain/tcabci-read-js-client 2.7.0 → 2.7.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/breaker.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare class Breaker {
2
+ execute(fn: () => Promise<any>): Promise<any>
3
+ }
package/breaker.js ADDED
@@ -0,0 +1,50 @@
1
+ import {
2
+ ConsecutiveBreaker,
3
+ ExponentialBackoff,
4
+ retry,
5
+ handleType,
6
+ circuitBreaker,
7
+ wrap,
8
+ } from 'cockatiel'
9
+ import { FetchError } from './errors.js'
10
+
11
+ export class Breaker {
12
+ _options = {
13
+ halfOpenAfter: 10 * 1000,
14
+ breaker: new ConsecutiveBreaker(10),
15
+ }
16
+ _retryPolicy = retry(
17
+ handleType(FetchError, (err) =>
18
+ [502, 503, 504].includes(err.status),
19
+ ).orType(Error, (err) => {
20
+ return err.message.toLowerCase().search('failed to fetch') > -1
21
+ }),
22
+ {
23
+ maxAttempts: 10,
24
+ backoff: new ExponentialBackoff({
25
+ initialDelay: 500,
26
+ maxDelay: 15000,
27
+ }),
28
+ },
29
+ )
30
+ _circuitBreakerPolicy = circuitBreaker(
31
+ handleType(FetchError, (err) =>
32
+ [502, 503, 504].includes(err.status),
33
+ ).orType(Error, (err) => {
34
+ return err.message.toLowerCase().search('failed to fetch') > -1
35
+ }),
36
+ this._options,
37
+ )
38
+ _retryWithBreaker = wrap(this._retryPolicy, this._circuitBreakerPolicy)
39
+ constructor() {}
40
+
41
+ /**
42
+ * @param {function} fn
43
+ * @return {Promise<*>}
44
+ */
45
+ execute(fn) {
46
+ return this._retryWithBreaker.execute(fn).catch((err) => {
47
+ return Promise.reject(err)
48
+ })
49
+ }
50
+ }
package/client.js CHANGED
@@ -1,8 +1,6 @@
1
1
  import {
2
- FetchError,
3
2
  ALREADY_CONNECTED,
4
3
  BLOCK_NOT_FOUND,
5
- ERR_NETWORK,
6
4
  INVALID_ARGUMENT_WITH_CS,
7
5
  INVALID_ARGUMENTS,
8
6
  NOT_CONNECTED,
@@ -23,6 +21,7 @@ import { Options } from './websocketOptions.js'
23
21
  import { TWebSocket } from './websocket.js'
24
22
  import { Transaction, TX_TYPE_LIST } from './transaction.js'
25
23
  import { Block } from './block.js'
24
+ import { HTTP } from './http.js'
26
25
 
27
26
  /**
28
27
  * @callback SuccessCallback
@@ -51,13 +50,14 @@ import { Block } from './block.js'
51
50
  */
52
51
 
53
52
  export default class TCaBCIClient {
53
+ _httpClient = new HTTP()
54
54
  _subscribed = false
55
55
  _subscribedAddresses = []
56
56
  _SubscribedSignedData = {}
57
57
  _connected = false
58
58
  _chainName = 'transferchain'
59
59
  _chainVersion = 'v1'
60
- _version = `v2.7.0`
60
+ _version = `v2.7.2`
61
61
  /**
62
62
  * @type {?SuccessCallback}
63
63
  */
@@ -110,6 +110,7 @@ export default class TCaBCIClient {
110
110
 
111
111
  this._readNodeAddress = readNodeAddresses[0]
112
112
  this._readNodeWSAddress = readNodeAddresses[1]
113
+ this._httpClient.setBaseURL(readNodeAddresses[0])
113
114
  }
114
115
 
115
116
  this._options = new Options(this._readNodeWSAddress)
@@ -309,12 +310,13 @@ export default class TCaBCIClient {
309
310
  * @return {Promise<{blocks: Transaction[], total_count: number}>}
310
311
  */
311
312
  LastBlock(chainName = null, chainVersion = null) {
312
- return this._httpClient(
313
- `/v1/blocks?chain_name=${chainName ?? this._chainName}&chain_version=${chainVersion ?? this._chainVersion}&limit=1&offset=0`,
314
- {
315
- method: 'GET',
316
- },
317
- )
313
+ return this._httpClient
314
+ .request(
315
+ `/v1/blocks?chain_name=${chainName ?? this._chainName}&chain_version=${chainVersion ?? this._chainVersion}&limit=1&offset=0`,
316
+ {
317
+ method: 'GET',
318
+ },
319
+ )
318
320
  .then((res) => {
319
321
  const data = []
320
322
 
@@ -326,7 +328,7 @@ export default class TCaBCIClient {
326
328
 
327
329
  return { blocks: data, total_count: res.total_count }
328
330
  })
329
- .catch((e) => this._handleRestError(e, new Error(BLOCK_NOT_FOUND)))
331
+ .catch((e) => this._httpClient.handleError(e, new Error(BLOCK_NOT_FOUND)))
330
332
  }
331
333
 
332
334
  /**
@@ -339,17 +341,18 @@ export default class TCaBCIClient {
339
341
  return Promise.reject(new Error(INVALID_ARGUMENTS))
340
342
  }
341
343
 
342
- return this._httpClient(`/v1/tx/${id}`, {
343
- method: 'GET',
344
- headers: { 'X-Signature': signature },
345
- })
344
+ return this._httpClient
345
+ .request(`/v1/tx/${id}`, {
346
+ method: 'GET',
347
+ headers: { 'X-Signature': signature },
348
+ })
346
349
  .then((res) => {
347
350
  const { transaction, error } = Transaction.FromObject(res.data)
348
351
  if (error) return Promise.reject(error)
349
352
 
350
353
  return { tx: transaction }
351
354
  })
352
- .catch((e) => this._handleRestError(e))
355
+ .catch((e) => this._httpClient.handleError(e))
353
356
  }
354
357
 
355
358
  /**
@@ -383,17 +386,18 @@ export default class TCaBCIClient {
383
386
  return Promise.reject(new Error(INVALID_ARGUMENTS))
384
387
  }
385
388
 
386
- return this._httpClient('/v1/tx_summary', {
387
- method: 'POST',
388
- body: JSON.stringify({
389
- chain_name: chainName ?? this._chainName,
390
- chain_version: chainVersion ?? this._chainVersion,
391
- recipient_addrs: recipientAddrs,
392
- sender_addrs: senderAddrs,
393
- signed_addrs: signedData,
394
- ...(types ? { types: types } : { typ: typ }),
395
- }),
396
- })
389
+ return this._httpClient
390
+ .request('/v1/tx_summary', {
391
+ method: 'POST',
392
+ body: JSON.stringify({
393
+ chain_name: chainName ?? this._chainName,
394
+ chain_version: chainVersion ?? this._chainVersion,
395
+ recipient_addrs: recipientAddrs,
396
+ sender_addrs: senderAddrs,
397
+ signed_addrs: signedData,
398
+ ...(types ? { types: types } : { typ: typ }),
399
+ }),
400
+ })
397
401
  .then((res) => {
398
402
  let firstTransaction, lastTransaction
399
403
 
@@ -423,7 +427,7 @@ export default class TCaBCIClient {
423
427
  total_count: res.total_count,
424
428
  }
425
429
  })
426
- .catch((e) => this._handleRestError(e))
430
+ .catch((e) => this._httpClient.handleError(e))
427
431
  }
428
432
 
429
433
  /**
@@ -463,25 +467,26 @@ export default class TCaBCIClient {
463
467
  chainName = null,
464
468
  chainVersion = null,
465
469
  }) {
466
- return this._httpClient('/v1/tx_search/p', {
467
- method: 'POST',
468
- body: JSON.stringify({
469
- chain_name: chainName ?? this._chainName,
470
- chain_version: chainVersion ?? this._chainVersion,
471
- height: `${heightOperator} ${height}`,
472
- ...(maxHeight ? { max_height: maxHeight } : {}),
473
- ...(lastOrder ? { last_order: lastOrder } : {}),
474
- ...(recipientAddrs ? { recipient_addrs: recipientAddrs } : {}),
475
- ...(senderAddrs ? { sender_addrs: senderAddrs } : {}),
476
- ...(signedData ? { signed_addrs: signedData } : {}),
477
- ...(hashes ? { hashes: hashes } : {}),
478
- ...(limit ? { limit: limit } : {}),
479
- ...(offset ? { offset: offset } : {}),
480
- ...(orderField ? { order_field: orderField } : {}),
481
- ...(orderBy ? { order_by: orderBy } : {}),
482
- ...(types ? { types: types } : typ ? { typ: typ } : {}),
483
- }),
484
- })
470
+ return this._httpClient
471
+ .request('/v1/tx_search/p', {
472
+ method: 'POST',
473
+ body: JSON.stringify({
474
+ chain_name: chainName ?? this._chainName,
475
+ chain_version: chainVersion ?? this._chainVersion,
476
+ height: `${heightOperator} ${height}`,
477
+ ...(maxHeight ? { max_height: maxHeight } : {}),
478
+ ...(lastOrder ? { last_order: lastOrder } : {}),
479
+ ...(recipientAddrs ? { recipient_addrs: recipientAddrs } : {}),
480
+ ...(senderAddrs ? { sender_addrs: senderAddrs } : {}),
481
+ ...(signedData ? { signed_addrs: signedData } : {}),
482
+ ...(hashes ? { hashes: hashes } : {}),
483
+ ...(limit ? { limit: limit } : {}),
484
+ ...(offset ? { offset: offset } : {}),
485
+ ...(orderField ? { order_field: orderField } : {}),
486
+ ...(orderBy ? { order_by: orderBy } : {}),
487
+ ...(types ? { types: types } : typ ? { typ: typ } : {}),
488
+ }),
489
+ })
485
490
  .then((res) => {
486
491
  const data = []
487
492
 
@@ -496,7 +501,7 @@ export default class TCaBCIClient {
496
501
  total_count: res.total_count,
497
502
  }
498
503
  })
499
- .catch((e) => this._handleRestError(e))
504
+ .catch((e) => this._httpClient.handleError(e))
500
505
  }
501
506
 
502
507
  /**
@@ -642,9 +647,8 @@ export default class TCaBCIClient {
642
647
  throw new Error(TRANSACTION_TYPE_NOT_VALID)
643
648
  }
644
649
 
645
- return this._httpClient(
646
- commit ? '/v1/tx/commit' : sync ? '/v1/tx/sync' : '/v1/tx',
647
- {
650
+ return this._httpClient
651
+ .request(commit ? '/v1/tx/commit' : sync ? '/v1/tx/sync' : '/v1/tx', {
648
652
  method: 'POST',
649
653
  body: JSON.stringify({
650
654
  id,
@@ -658,13 +662,14 @@ export default class TCaBCIClient {
658
662
  sign,
659
663
  fee,
660
664
  }),
661
- },
662
- )
665
+ })
663
666
  .then((res) => {
664
667
  return { data: res.data }
665
668
  })
666
669
  .catch((e) =>
667
- this._handleRestError(e, { 400: new Error(TRANSACTION_NOT_BROADCAST) }),
670
+ this._httpClient.handleError(e, {
671
+ 400: new Error(TRANSACTION_NOT_BROADCAST),
672
+ }),
668
673
  )
669
674
  }
670
675
 
@@ -684,7 +689,7 @@ export default class TCaBCIClient {
684
689
  chainName = null,
685
690
  chainVersion = null,
686
691
  ) {
687
- return this._httpClient('/v1/bulk_tx', {
692
+ return this._httpClient.request('/v1/bulk_tx', {
688
693
  method: 'POST',
689
694
  body: JSON.stringify({
690
695
  chain_name: chainName ?? this._chainName,
@@ -840,69 +845,4 @@ export default class TCaBCIClient {
840
845
  if (this._listenCb) this._listenCb(null, transaction, null)
841
846
  }
842
847
  }
843
-
844
- /**
845
- * @param {string} uri
846
- * @param {RequestInit} req
847
- * @return {Promise<Record<string, any>>}
848
- */
849
- _httpClient(uri, req) {
850
- req.cache = 'no-cache'
851
- req.headers = {
852
- Client: `tcabaci-read-js-client/${this._version}`,
853
- }
854
- req.priority = 'high'
855
-
856
- return fetch(this._readNodeAddress + uri, req).then((response) =>
857
- this._handleRestResponse(response),
858
- )
859
- }
860
-
861
- /**
862
- * @param {Response} response
863
- * @return {Promise<*>}
864
- */
865
- async _handleRestResponse(response) {
866
- if (response.status >= 200 && response.status < 400) {
867
- return response.json()
868
- }
869
-
870
- try {
871
- const data = JSON.parse(await response.text())
872
-
873
- return Promise.reject(
874
- new FetchError(data.message)
875
- .setStatus(response.status)
876
- .setResponse(data),
877
- )
878
- } catch (e) {
879
- return Promise.reject(
880
- new FetchError(response.statusText).setOriginError(e),
881
- )
882
- }
883
- }
884
-
885
- /**
886
- * @param {Error} err
887
- * @param {?Object|Error} custom
888
- * @throws Error
889
- */
890
- _handleRestError(err, custom = null) {
891
- const code = err.code
892
-
893
- if (custom && typeof custom === 'object' && custom[code]) {
894
- throw custom[code]
895
- } else if (custom instanceof Error) {
896
- throw custom
897
- }
898
-
899
- switch (code) {
900
- case 400:
901
- throw new Error(INVALID_ARGUMENTS)
902
- case 'ERR_NETWORK':
903
- throw new Error(ERR_NETWORK)
904
- default:
905
- throw err
906
- }
907
- }
908
848
  }
package/errors.js CHANGED
@@ -12,6 +12,24 @@ export const ALREADY_CONNECTED = 'Already connected',
12
12
  TRANSACTION_TYPE_NOT_VALID = 'Transaction type is not valid',
13
13
  TRANSACTION_NOT_BROADCAST = 'Transaction can not be broadcast'
14
14
 
15
+ export class CopiedError extends Error {
16
+ originName
17
+ originError
18
+ originStack
19
+
20
+ /**
21
+ * @param {Error} err
22
+ */
23
+ constructor(err) {
24
+ super(err.message)
25
+ this.name = 'CopiedError'
26
+ this.originName = err.name
27
+ this.originError = err
28
+ this.stack = err.stack
29
+ this.originStack = err.stack
30
+ }
31
+ }
32
+
15
33
  export class FetchError extends Error {
16
34
  status = -1
17
35
  response
package/http.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { Request } from 'express'
2
+
3
+ export declare class HTTP {
4
+ constructor(baseURL: string)
5
+ setBaseURL(baseURL: string): HTTP
6
+ request(uri: string, req: Request): Promise<Record<string, any>>
7
+ handleResponse(response: Response): Promise<any>
8
+ handleError(err: Error, custom: Record<string, any>|Error): Promise<any>
9
+ }
package/http.js ADDED
@@ -0,0 +1,81 @@
1
+ import { ERR_NETWORK, FetchError, INVALID_ARGUMENTS } from './errors.js'
2
+ import { Breaker } from './breaker.js'
3
+
4
+ export class HTTP {
5
+ _baseURL
6
+ _version = 'v2.7.1'
7
+ _breaker = new Breaker()
8
+ constructor(baseURL = null) {
9
+ this._baseURL = baseURL
10
+ }
11
+ setBaseURL(baseURL) {
12
+ this._baseURL = baseURL
13
+ return this
14
+ }
15
+ /**
16
+ * @param {string} uri
17
+ * @param {RequestInit} req
18
+ * @return {Promise<Record<string, any>>}
19
+ */
20
+ async request(uri, req) {
21
+ req.cache = 'no-cache'
22
+ req.headers = {
23
+ Client: `tcabaci-read-js-client/${this._version}`,
24
+ }
25
+ req.priority = 'high'
26
+
27
+ return this._breaker
28
+ .execute(() => {
29
+ return fetch(this._baseURL + uri, req)
30
+ })
31
+ .then((response) => this.handleResponse(response))
32
+ }
33
+
34
+ /**
35
+ * @param {Response} response
36
+ * @return {Promise<*>}
37
+ */
38
+ async handleResponse(response) {
39
+ if (response.status >= 200 && response.status < 400) {
40
+ return response.json()
41
+ }
42
+
43
+ try {
44
+ const data = JSON.parse(await response.text())
45
+
46
+ return Promise.reject(
47
+ new FetchError(data.message)
48
+ .setStatus(response.status)
49
+ .setResponse(data),
50
+ )
51
+ } catch (e) {
52
+ return Promise.reject(
53
+ new FetchError(response.statusText).setOriginError(e),
54
+ )
55
+ }
56
+ }
57
+
58
+ /**
59
+ * @param {Error} err
60
+ * @param {?Object|Error} custom
61
+ * @return {Promise<Error>}
62
+ */
63
+ async handleError(err, custom = null) {
64
+ const code = err.code
65
+
66
+ if (custom && typeof custom === 'object' && custom[code]) {
67
+ return Promise.reject(custom[code])
68
+ } else if (custom instanceof Error) {
69
+ return Promise.reject(custom)
70
+ }
71
+
72
+ switch (code) {
73
+ case 400:
74
+ return Promise.reject(new Error(INVALID_ARGUMENTS))
75
+ case 'ERR_NETWORK':
76
+ return Promise.reject(new Error(ERR_NETWORK))
77
+ default:
78
+ return Promise.reject(err)
79
+ }
80
+ }
81
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tchain/tcabci-read-js-client",
3
- "version": "2.7.0",
3
+ "version": "2.7.2",
4
4
  "description": "TransferChain Fastest Read Network WebSocket Client",
5
5
  "main": "client.js",
6
6
  "type": "module",
@@ -11,6 +11,8 @@
11
11
  "files": [
12
12
  "*.d.ts",
13
13
  "constants.js",
14
+ "breaker.js",
15
+ "http.js",
14
16
  "block.js",
15
17
  "client.js",
16
18
  "errors.js",
@@ -30,19 +32,20 @@
30
32
  "@eslint/js": "^10.0.1",
31
33
  "@types/chai": "^5.2.3",
32
34
  "chai": "^6.2.2",
33
- "eslint": "^10.2.0",
35
+ "eslint": "^10.5.0",
34
36
  "eslint-config-prettier": "^10.1.8",
35
- "eslint-plugin-prettier": "^5.5.5",
37
+ "eslint-plugin-prettier": "^5.5.6",
36
38
  "esm": "^3.2.25",
37
39
  "express": "^5.2.1",
38
40
  "globals": "^16.5.0",
39
- "mocha": "^11.7.5",
41
+ "mocha": "^11.7.6",
40
42
  "nyc": "^17.1.0",
41
- "prettier": "^3.8.3",
43
+ "prettier": "^3.8.4",
42
44
  "unit.js": "^2.1.1",
43
- "ws": "^8.20.0"
45
+ "ws": "^8.21.0"
44
46
  },
45
47
  "dependencies": {
48
+ "cockatiel": "^3.2.1",
46
49
  "reconnecting-websocket": "^4.4.0"
47
50
  },
48
51
  "scripts": {
@@ -13,7 +13,7 @@ export class Options {
13
13
  _protocols = []
14
14
  _customWS
15
15
  _endpoints = ['ws', 'longpoll']
16
- _version = `v2.7.0`
16
+ _version = `v2.7.2`
17
17
 
18
18
  _maxConnectionDelay = 10000
19
19
  _minReconnectionDelay = 1000 + Math.random() * 4000