@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 +3 -0
- package/breaker.js +50 -0
- package/client.js +59 -119
- package/errors.js +18 -0
- package/http.d.ts +9 -0
- package/http.js +81 -0
- package/package.json +9 -6
- package/websocketOptions.js +1 -1
package/breaker.d.ts
ADDED
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.
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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.
|
|
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
|
|
343
|
-
|
|
344
|
-
|
|
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.
|
|
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
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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.
|
|
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
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
35
|
+
"eslint": "^10.5.0",
|
|
34
36
|
"eslint-config-prettier": "^10.1.8",
|
|
35
|
-
"eslint-plugin-prettier": "^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.
|
|
41
|
+
"mocha": "^11.7.6",
|
|
40
42
|
"nyc": "^17.1.0",
|
|
41
|
-
"prettier": "^3.8.
|
|
43
|
+
"prettier": "^3.8.4",
|
|
42
44
|
"unit.js": "^2.1.1",
|
|
43
|
-
"ws": "^8.
|
|
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": {
|
package/websocketOptions.js
CHANGED