@tchain/tcabci-read-js-client 1.3.13 → 2.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.
package/client.js CHANGED
@@ -1,296 +1,430 @@
1
- const ReconnectingWebSocket = require('reconnecting-websocket/dist/reconnecting-websocket-cjs')
2
- const {
3
- NOT_CONNECTED,
4
- ERR_NETWORK,
5
- ALREADY_CONNECTED,
6
- INVALID_ARGUMENTS,
7
- NOT_SUBSCRIBED,
8
- ADDRESSES_IS_EMPTY,
9
- BLOCK_NOT_FOUND,
10
- TRANSACTION_TYPE_NOT_VALID,
11
- TRANSACTION_NOT_BROADCAST
12
- } = require("./errors")
13
- const {MESSAGE_TYPE, READ_NODE_ADDRESS, READ_NODE_WS_ADDRESS, TX_TYPE} = require("./constants")
14
- const Message = require("./message")
15
- const {toJSON} = require("./util")
16
- const axios = require("axios")
17
-
18
- class TCAbciClient {
19
- subscribed = false
20
- subscribedAddresses = []
21
- connected = false
22
- version = "v1.3.13"
23
- errorCb = null
24
- listenCb = null
25
- ws = null
26
- httpClient = null
27
- readNodeAddress = READ_NODE_ADDRESS
28
- readNodeWSAddress = READ_NODE_WS_ADDRESS
29
-
30
- /**
31
- *
32
- * @param {Array<String>} readNodeAddresses
33
- * @param axiosAdapter Axios Adapter
34
- */
35
- constructor(readNodeAddresses = [], axiosAdapter = null) {
36
- if (readNodeAddresses.length === 2) {
37
- this.readNodeAddress = readNodeAddresses[0]
38
- this.readNodeWSAddress = readNodeAddresses[1]
39
- }
40
- this.httpClient = axios.create({
41
- baseURL: this.readNodeAddress,
42
- timeout: 10000,
43
- headers: {'Client': `tcabaci-read-js-client${this.version}`},
44
- ...(axiosAdapter ? { adapter: axiosAdapter } : {})
45
- })
46
- }
47
- Socket() {
48
- return this.ws
49
- }
50
- SetError(cb) {
51
- this.errorCb = cb
52
- }
53
- SetListen(cb) {
54
- this.listenCb = cb
1
+ import ReconnectingWebSocket from 'reconnecting-websocket'
2
+ import {
3
+ ADDRESSES_IS_EMPTY,
4
+ ALREADY_CONNECTED,
5
+ BLOCK_NOT_FOUND,
6
+ ERR_NETWORK,
7
+ FetchError,
8
+ INVALID_ARGUMENTS,
9
+ NOT_CONNECTED,
10
+ NOT_SUBSCRIBED,
11
+ TRANSACTION_NOT_BROADCAST,
12
+ TRANSACTION_TYPE_NOT_VALID,
13
+ } from './errors.js'
14
+ import {
15
+ MESSAGE_TYPE,
16
+ READ_NODE_ADDRESS,
17
+ READ_NODE_WS_ADDRESS,
18
+ TX_TYPE,
19
+ } from './constants.js'
20
+ import Message from './message.js'
21
+ import { toJSON } from './util.js'
22
+
23
+ export default class TCAbciClient {
24
+ subscribed = false
25
+ subscribedAddresses = []
26
+ connected = false
27
+ version = 'v2.0.1'
28
+ errorCb = null
29
+ listenCb = null
30
+ ws = null
31
+ readNodeAddress = READ_NODE_ADDRESS
32
+ readNodeWSAddress = READ_NODE_WS_ADDRESS
33
+
34
+ /**
35
+ *
36
+ * @param {Array<String>} readNodeAddresses
37
+ */
38
+ constructor(readNodeAddresses = []) {
39
+ if (readNodeAddresses.length === 2) {
40
+ this.readNodeAddress = readNodeAddresses[0]
41
+ this.readNodeWSAddress = readNodeAddresses[1]
42
+
43
+ if (!readNodeAddresses[0].startsWith('http')) throw INVALID_ARGUMENTS
44
+ if (!readNodeAddresses[1].startsWith('ws')) throw INVALID_ARGUMENTS
55
45
  }
56
- Start() {
57
- return this.connect()
46
+ }
47
+
48
+ /**
49
+ * @param {string} uri
50
+ * @param {RequestInit} req
51
+ * @return {Promise<any>}
52
+ */
53
+ httpClient(uri, req) {
54
+ req.cache = 'no-cache'
55
+ req.headers = {
56
+ Client: `tcabaci-read-js-client${this.version}`,
57
+ 'X-Client': `tcabaci-read-js-client${this.version}`,
58
58
  }
59
- Stop() {
60
- if (!this.getConnected()) {
61
- throw NOT_CONNECTED
62
- }
63
- this.Disconnect(1000)
64
- this.setConnected(false)
65
- this.setSubscribed(false)
59
+
60
+ if (typeof AbortSignal !== 'undefined') {
61
+ req.signal = AbortSignal.timeout(10000)
66
62
  }
67
- Subscribe(addresses = [], txTypes = []) {
68
- if (!Array.isArray(addresses)) {
69
- throw INVALID_ARGUMENTS
70
- }
71
63
 
72
- if (txTypes && !Array.isArray(addresses)) {
73
- throw INVALID_ARGUMENTS
74
- }
64
+ req.priority = 'high'
75
65
 
76
- if (txTypes.length && txTypes.length > Object.values(TX_TYPE).length) {
77
- throw INVALID_ARGUMENTS
78
- }
66
+ return fetch(this.readNodeAddress + uri, req).then((response) => {
67
+ if (response.ok) return response.json()
79
68
 
80
- if (!this.getConnected()) {
81
- throw NOT_CONNECTED
82
- }
69
+ return Promise.reject(
70
+ new FetchError(response.statusText, response.status),
71
+ )
72
+ })
73
+ }
83
74
 
84
- let addrs = []
75
+ Socket() {
76
+ return this.ws
77
+ }
85
78
 
86
- if (addresses.length === 0) {
87
- throw ADDRESSES_IS_EMPTY
88
- }
79
+ SetErrorCallback(cb) {
80
+ this.errorCb = cb
81
+ }
89
82
 
90
- addrs = addresses
83
+ SetListenCallback(cb) {
84
+ this.listenCb = cb
85
+ }
91
86
 
92
- if (this.getSubscribeAddresses().length > 0) {
93
- let newAddress = []
94
- for (let i = 0; i < addresses.length; i++) {
95
- if (this.getSubscribeAddresses().indexOf(addresses[i]) === -1 ){
96
- newAddress.push(addresses[i])
97
- }
98
- }
99
- addrs = newAddress
100
- }
87
+ async Start() {
88
+ return this.connect()
89
+ }
101
90
 
102
- const message = new Message(true, MESSAGE_TYPE.SUBSCRIBE, addrs, txTypes)
103
- this.ws.send(message.ToJSONString())
104
- this.setSubscribeAddresses(addrs, true)
105
- this.setSubscribed(true)
91
+ Stop() {
92
+ if (!this.getConnected()) {
93
+ throw NOT_CONNECTED
106
94
  }
107
- Unsubscribe() {
108
- if (!this.getSubscribed()) {
109
- throw NOT_SUBSCRIBED
110
- }
111
- this.ws.send(new Message(true, MESSAGE_TYPE.UNSUBSCRIBE, this.getSubscribeAddresses()).ToJSONString())
112
- this.setSubscribed(false)
113
- this.setSubscribeAddresses([])
95
+ this.Disconnect(1000)
96
+ this.setConnected(false)
97
+ this.setSubscribed(false)
98
+ }
99
+
100
+ Subscribe(addresses = [], txTypes = []) {
101
+ if (!Array.isArray(addresses)) {
102
+ throw INVALID_ARGUMENTS
114
103
  }
115
- LastBlock() {
116
- return this.httpClient.get("/v1/blocks?limit=1&offset=0")
117
- .then(res => { return { blocks: res.data.data, total_count: res.data.total_count } } )
118
- .catch(e => {
119
- throw BLOCK_NOT_FOUND
120
- })
104
+
105
+ if (txTypes && !Array.isArray(addresses)) {
106
+ throw INVALID_ARGUMENTS
121
107
  }
122
- TxSummary({recipientAddrs, senderAddrs, typ}) {
123
- return this.httpClient.post(
124
- "/v1/tx_summary",
125
- {
126
- recipient_addrs: recipientAddrs,
127
- sender_addrs: senderAddrs,
128
- typ: typ,
129
- })
130
- .then((res) => {
131
- return {
132
- first_block_height: res.data.data.first_block_height,
133
- first_transaction: res.data.data.first_transaction,
134
- last_block_height: res.data.data.last_block_height,
135
- last_transaction: res.data.data.last_transaction,
136
- total_count: res.data.total_count,
137
- }
138
- })
139
- .catch(e => {
140
- switch (e?.response?.status ?? e?.status ?? e?.code) {
141
- case 400:
142
- throw INVALID_ARGUMENTS
143
- case 'ERR_NETWORK':
144
- throw ERR_NETWORK
145
- default:
146
- throw e
147
- }
148
- })
108
+
109
+ if (txTypes.length && txTypes.length > Object.values(TX_TYPE).length) {
110
+ throw INVALID_ARGUMENTS
149
111
  }
150
- TxSearch({heightOperator, height, recipientAddrs, senderAddrs, hashes, typ, limit, offset, orderField, orderBy}) {
151
- return this.httpClient.post(
152
- "/v1/tx_search/p",
153
- {
154
- height: `${heightOperator} ${height}`,
155
- recipient_addrs: recipientAddrs,
156
- sender_addrs: senderAddrs,
157
- hashes: hashes,
158
- typ,
159
- limit,
160
- offset,
161
- order_field: orderField,
162
- order_by: orderBy
163
- })
164
- .then(res => {
165
- return {
166
- txs: res.data.data, total_count: res.data.total_count
167
- }
168
- })
169
- .catch(e => {
170
- switch (e?.response?.status ?? e?.status ?? e?.code) {
171
- case 400:
172
- throw INVALID_ARGUMENTS
173
- case 'ERR_NETWORK':
174
- throw ERR_NETWORK
175
- default:
176
- throw e
177
- }
178
- })
112
+
113
+ if (!this.getConnected()) {
114
+ throw NOT_CONNECTED
179
115
  }
180
- Status() {
181
- return {
182
- connected: this.connected,
183
- subscribed: this.subscribed,
184
- }
116
+
117
+ let addrs = []
118
+
119
+ if (addresses.length === 0) {
120
+ throw ADDRESSES_IS_EMPTY
185
121
  }
186
- Broadcast({id, version, type, data, sender_addr, recipient_addr, sign, fee}) {
187
- if(Object.values(TX_TYPE).indexOf(type) < 0) {
188
- throw TRANSACTION_TYPE_NOT_VALID
122
+
123
+ addrs = addresses
124
+
125
+ if (this.getSubscribeAddresses().length > 0) {
126
+ let newAddress = []
127
+
128
+ for (let i = 0; i < addresses.length; i++) {
129
+ if (this.getSubscribeAddresses().indexOf(addresses[i]) === -1) {
130
+ newAddress.push(addresses[i])
189
131
  }
132
+ }
133
+ addrs = newAddress
134
+ }
190
135
 
191
- return this.httpClient.post(
192
- "/v1/tx",
193
- {
194
- id,
195
- version,
196
- type,
197
- data,
198
- sender_addr,
199
- recipient_addr,
200
- sign,
201
- fee
202
- }
203
- ).then(res => { return { data: res.data.data } })
204
- .catch(e => { throw TRANSACTION_NOT_BROADCAST })
136
+ const message = new Message(true, MESSAGE_TYPE.SUBSCRIBE, addrs, txTypes)
137
+
138
+ this.ws.send(message.ToJSONString())
139
+ this.setSubscribeAddresses(addrs, true)
140
+ this.setSubscribed(true)
141
+ }
142
+
143
+ Unsubscribe() {
144
+ if (!this.getSubscribed()) {
145
+ throw NOT_SUBSCRIBED
205
146
  }
147
+ this.ws.send(
148
+ new Message(
149
+ true,
150
+ MESSAGE_TYPE.UNSUBSCRIBE,
151
+ this.getSubscribeAddresses(),
152
+ ).ToJSONString(),
153
+ )
154
+ this.setSubscribed(false)
155
+ this.setSubscribeAddresses([])
156
+ }
206
157
 
207
- Bulk(addresses = []) {
208
- return this.httpClient.post('/v1/bulk_tx', { addresses: addresses })
158
+ /**
159
+ * @return {Promise<any>}
160
+ */
161
+ LastBlock() {
162
+ return this.httpClient('/v1/blocks?limit=1&offset=0', {
163
+ method: 'GET',
164
+ })
165
+ .then((res) => {
166
+ return { blocks: res.data, total_count: res.total_count }
167
+ })
168
+ .catch((e) => this.handleRestError(e, BLOCK_NOT_FOUND))
169
+ }
170
+
171
+ /**
172
+ * @param {?Array<string>} recipientAddrs
173
+ * @param {?Array<string>} senderAddrs
174
+ * @param {?string} typ
175
+ * @param {?Array<string>}
176
+ * @return {Promise<any>}
177
+ */
178
+ TxSummary({ recipientAddrs, senderAddrs, typ, types }) {
179
+ if (!recipientAddrs && !senderAddrs) {
180
+ return Promise.reject(INVALID_ARGUMENTS)
209
181
  }
210
182
 
211
- Disconnect(code = 1000) {
212
- if (this.getConnected()) {
213
- this.ws.close(code)
183
+ return this.httpClient('/v1/tx_summary', {
184
+ method: 'POST',
185
+ body: JSON.stringify({
186
+ recipient_addrs: recipientAddrs,
187
+ sender_addrs: senderAddrs,
188
+ ...(types ? { types: types } : { typ: typ }),
189
+ }),
190
+ })
191
+ .then((res) => {
192
+ return {
193
+ first_block_height: res.data.first_block_height,
194
+ first_transaction: res.data.first_transaction,
195
+ last_block_height: res.data.last_block_height,
196
+ last_transaction: res.data.last_transaction,
197
+ total_count: res.total_count,
214
198
  }
215
- }
199
+ })
200
+ .catch((e) => this.handleRestError(e))
201
+ }
216
202
 
217
- Reconnect(code = 1000) {
218
- if (this.getConnected()) {
219
- this.ws.reconnect(code)
203
+ /**
204
+ * @param {string} heightOperator
205
+ * @param {number} height
206
+ * @param {?Array<string>} recipientAddrs
207
+ * @param {?Array<string>} senderAddrs
208
+ * @param {?Array<string>} hashes
209
+ * @param {string} typ
210
+ * @param {?Array<string>} types
211
+ * @param {number} limit
212
+ * @param {number} offset
213
+ * @param {string} orderField
214
+ * @param {string} orderBy
215
+ * @return {Promise<any>}
216
+ */
217
+ TxSearch({
218
+ heightOperator,
219
+ height,
220
+ recipientAddrs,
221
+ senderAddrs,
222
+ hashes,
223
+ typ,
224
+ types,
225
+ limit,
226
+ offset,
227
+ orderField,
228
+ orderBy,
229
+ }) {
230
+ return this.httpClient('/v1/tx_search/p', {
231
+ method: 'POST',
232
+ body: JSON.stringify({
233
+ height: `${heightOperator} ${height}`,
234
+ recipient_addrs: recipientAddrs,
235
+ sender_addrs: senderAddrs,
236
+ hashes: hashes,
237
+ limit,
238
+ offset,
239
+ order_field: orderField,
240
+ order_by: orderBy,
241
+ ...(types ? { types: types } : { typ: typ }),
242
+ }),
243
+ })
244
+ .then((res) => {
245
+ return {
246
+ txs: res.data,
247
+ total_count: res.total_count,
220
248
  }
221
- }
249
+ })
250
+ .catch((e) => this.handleRestError(e))
251
+ }
222
252
 
223
- connect() {
224
- return new Promise((resolve, reject) => {
225
- if (this.getConnected()) {
226
- reject(ALREADY_CONNECTED)
227
- }
228
-
229
- const options = {
230
- WebSocket: this.wsClient(),
231
- connectionTimeout: 1000,
232
- maxRetries: 10,
233
- }
234
-
235
- this.ws = new ReconnectingWebSocket(this.readNodeWSAddress, [], options)
236
-
237
- this.ws.onerror = (event) => {
238
- this.setConnected(false)
239
- if (this.errorCb) {
240
- this.errorCb(event.error)
241
- }
242
- reject(event)
243
- }
244
- this.ws.onopen = (event) => {
245
- this.setConnected(true)
246
- resolve(event)
247
- }
248
- this.ws.onmessage = (message) => {
249
- if (message.data === "OK" && message.data.length < 10) {
250
- return { status: message.data }
251
- }
252
- if (this.listenCb) {
253
- this.listenCb(toJSON(message.data))
254
- }
255
- }
256
- this.ws.onclose = (event) => {
257
- this.setConnected(false)
258
- if (this.errorCb) {
259
- this.errorCb(event.error)
260
- }
261
- }
262
- })
253
+ Status() {
254
+ return {
255
+ connected: this.connected,
256
+ subscribed: this.subscribed,
263
257
  }
258
+ }
264
259
 
265
- wsClient() {
266
- if (typeof window !== "undefined" && typeof window.WebSocket !== "undefined") {
267
- return window.WebSocket
268
- }
269
- const { WebSocket } = require("ws")
270
- return WebSocket
260
+ /**
261
+ * @param {string} id
262
+ * @param {number} version
263
+ * @param {string} type
264
+ * @param {string} data
265
+ * @param {string} sender_addr
266
+ * @param {string} recipient_addr
267
+ * @param {string} sign
268
+ * @param {number} fee
269
+ * @return {Promise<any>}
270
+ */
271
+ Broadcast({
272
+ id,
273
+ version,
274
+ type,
275
+ data,
276
+ sender_addr,
277
+ recipient_addr,
278
+ sign,
279
+ fee,
280
+ }) {
281
+ if (Object.values(TX_TYPE).indexOf(type) < 0) {
282
+ throw TRANSACTION_TYPE_NOT_VALID
271
283
  }
272
- getConnected() {
273
- return this.connected
284
+
285
+ return this.httpClient('/v1/tx', {
286
+ method: 'POST',
287
+ body: JSON.stringify({
288
+ id,
289
+ version,
290
+ type,
291
+ data,
292
+ sender_addr,
293
+ recipient_addr,
294
+ sign,
295
+ fee,
296
+ }),
297
+ })
298
+ .then((res) => {
299
+ return { data: res.data.data }
300
+ })
301
+ .catch((e) => this.handleRestError(e, { 400: TRANSACTION_NOT_BROADCAST }))
302
+ }
303
+
304
+ Bulk(addresses = []) {
305
+ return this.httpClient('/v1/bulk_tx', {
306
+ method: 'POST',
307
+ body: JSON.stringify({ addresses: addresses }),
308
+ })
309
+ }
310
+
311
+ Disconnect(code = 1000) {
312
+ if (this.getConnected()) {
313
+ this.ws.close(code)
274
314
  }
275
- setConnected(value) {
276
- this.connected = value
315
+ }
316
+
317
+ Reconnect(code = 1000) {
318
+ if (this.getConnected()) {
319
+ this.ws.reconnect(code)
277
320
  }
278
- getSubscribed() {
279
- return this.subscribed
321
+ }
322
+
323
+ async connect() {
324
+ const wsClient = await this.wsClient()
325
+
326
+ return new Promise((resolve, reject) => {
327
+ if (this.getConnected()) {
328
+ reject(ALREADY_CONNECTED)
329
+ }
330
+
331
+ const options = {
332
+ WebSocket: wsClient,
333
+ connectionTimeout: 1000,
334
+ maxRetries: 10,
335
+ }
336
+
337
+ this.ws = new ReconnectingWebSocket(this.readNodeWSAddress, [], options)
338
+
339
+ this.ws.onerror = (event) => {
340
+ this.setConnected(false)
341
+ if (this.errorCb) {
342
+ this.errorCb(event.error)
343
+ }
344
+ reject(event.error)
345
+ }
346
+ this.ws.onopen = (event) => {
347
+ this.setConnected(true)
348
+ resolve(event)
349
+ }
350
+ this.ws.onmessage = (message) => {
351
+ if (message.data === 'OK' && message.data.length < 10) {
352
+ return { status: message.data }
353
+ }
354
+ if (this.listenCb) {
355
+ this.listenCb(toJSON(message.data))
356
+ }
357
+ }
358
+ this.ws.onclose = (event) => {
359
+ this.setConnected(false)
360
+ if (this.errorCb) {
361
+ this.errorCb(event.error)
362
+ }
363
+ }
364
+ })
365
+ }
366
+
367
+ async wsClient() {
368
+ if (
369
+ typeof window !== 'undefined' &&
370
+ typeof window.WebSocket !== 'undefined'
371
+ ) {
372
+ return window.WebSocket
280
373
  }
281
- setSubscribed(value) {
282
- this.subscribed = value
374
+ const { WebSocket } = await import('ws')
375
+
376
+ return WebSocket
377
+ }
378
+
379
+ getConnected() {
380
+ return this.connected
381
+ }
382
+
383
+ setConnected(value) {
384
+ this.connected = value
385
+ }
386
+
387
+ getSubscribed() {
388
+ return this.subscribed
389
+ }
390
+
391
+ setSubscribed(value) {
392
+ this.subscribed = value
393
+ }
394
+
395
+ getSubscribeAddresses() {
396
+ return this.subscribedAddresses
397
+ }
398
+
399
+ setSubscribeAddresses(addresses, push = false) {
400
+ if (push) {
401
+ this.subscribedAddresses.push(...addresses)
402
+
403
+ return
283
404
  }
284
- getSubscribeAddresses() {
285
- return this.subscribedAddresses
405
+ this.subscribedAddresses = addresses
406
+ }
407
+
408
+ /**
409
+ * @param {Error} err
410
+ * @param {?Object|Error} custom
411
+ */
412
+ handleRestError(err, custom = null) {
413
+ const code = err.code
414
+
415
+ if (custom && typeof custom === 'object' && custom[code]) {
416
+ throw custom[code]
417
+ } else if (custom instanceof Error) {
418
+ throw custom
286
419
  }
287
- setSubscribeAddresses(addresses, push = false) {
288
- if (push) {
289
- this.subscribedAddresses.push(...addresses)
290
- return
291
- }
292
- this.subscribedAddresses = addresses
420
+
421
+ switch (code) {
422
+ case 400:
423
+ throw INVALID_ARGUMENTS
424
+ case 'ERR_NETWORK':
425
+ throw ERR_NETWORK
426
+ default:
427
+ throw err
293
428
  }
429
+ }
294
430
  }
295
-
296
- module.exports = TCAbciClient