@tchain/tcabci-read-js-client 2.3.0 → 2.5.0

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/websocket.js ADDED
@@ -0,0 +1,344 @@
1
+ import { Options } from './websocketOptions.js'
2
+ import ReconnectingWebSocket from 'reconnecting-websocket'
3
+
4
+ export const MaxListenerSize = 100
5
+
6
+ export class TWebSocket {
7
+ /**
8
+ * @private
9
+ * @type {Options}
10
+ */
11
+ _options
12
+
13
+ /**
14
+ * @private
15
+ * @type {ReconnectingWebSocket}
16
+ */
17
+ _client
18
+
19
+ _openListener = () => {}
20
+ _messageListener = () => {}
21
+ _errorListener = () => {}
22
+ _closeListener = () => {}
23
+
24
+ _openCallbacks = []
25
+ _messageCallbacks = []
26
+ _errorCallbacks = []
27
+ _closeCallbacks = []
28
+
29
+ /**
30
+ * @param {Options} options
31
+ * @constructor
32
+ * @throws {Error}
33
+ */
34
+ constructor(options) {
35
+ if (!(options instanceof Options))
36
+ throw new TypeError('options must be a Options')
37
+
38
+ options.check()
39
+ this._options = options
40
+ }
41
+
42
+ /**
43
+ * @return {boolean}
44
+ */
45
+ get ready() {
46
+ if (!this._client) return false
47
+
48
+ return this._client.readyState === WebSocket.OPEN
49
+ }
50
+
51
+ /**
52
+ * @return {Array<string>}
53
+ */
54
+ get openListeners() {
55
+ return this._openCallbacks
56
+ }
57
+
58
+ /**
59
+ * @return {Array<string>}
60
+ */
61
+ get messageListeners() {
62
+ return this._messageCallbacks
63
+ }
64
+
65
+ /**
66
+ * @return {Array<string>}
67
+ */
68
+ get errorListeners() {
69
+ return this._errorCallbacks
70
+ }
71
+
72
+ /**
73
+ * @return {Array<string>}
74
+ */
75
+ get closeListeners() {
76
+ return this._closeCallbacks
77
+ }
78
+
79
+ /**
80
+ * @param {?boolean} force
81
+ * @return {Awaited<TWebSocket>}
82
+ * @throws {Error}
83
+ */
84
+ async connect(force = false) {
85
+ if (!force && this.ready)
86
+ throw new Error('already connect. please use force flag')
87
+
88
+ return this._init(force)
89
+ }
90
+
91
+ /**
92
+ * @param {?number} code
93
+ * @return {Awaited<TWebSocket>}
94
+ * @throws {Error}
95
+ */
96
+ async reconnect(code = null) {
97
+ await this._disconnect(code)
98
+ this._init(true)
99
+
100
+ return this
101
+ }
102
+
103
+ /**
104
+ * @param {number} code
105
+ * @return {Promise<void>}
106
+ */
107
+ async disconnect(code = 1000) {
108
+ return this._disconnect(code)
109
+ }
110
+
111
+ /**
112
+ * @param {string|ArrayBuffer|Blob|ArrayBufferView} msg
113
+ */
114
+ send(msg) {
115
+ if (!this._client) return
116
+
117
+ this._client.send(msg)
118
+ }
119
+
120
+ /**
121
+ * @param {function(Event)} callback
122
+ * @return {TWebSocket}
123
+ * @throws {Error}
124
+ */
125
+ addOpenListener(callback) {
126
+ return this._addListener('_openCallbacks', callback)
127
+ }
128
+
129
+ /**
130
+ * @param {function(Event)} callback
131
+ * @return {TWebSocket}
132
+ */
133
+ removeOpenListener(callback) {
134
+ return this._removeListener('_openCallbacks', callback)
135
+ }
136
+
137
+ /**
138
+ * @param {function(MessageEvent)} callback
139
+ * @return {TWebSocket}
140
+ * @throws {Error}
141
+ */
142
+ addMessageListener(callback) {
143
+ return this._addListener('_messageCallbacks', callback)
144
+ }
145
+
146
+ /**
147
+ * @param {function(MessageEvent)} callback
148
+ * @return {TWebSocket}
149
+ */
150
+ removeMessageListener(callback) {
151
+ return this._removeListener('_messageCallbacks', callback)
152
+ }
153
+
154
+ /**
155
+ * @param {function(ErrorEvent)} callback
156
+ * @return {TWebSocket}
157
+ * @throws {Error}
158
+ */
159
+ addErrorListener(callback) {
160
+ return this._addListener('_errorCallbacks', callback)
161
+ }
162
+
163
+ /**
164
+ * @param {function(ErrorEvent)} callback
165
+ * @return {TWebSocket}
166
+ */
167
+ removeErrorListener(callback) {
168
+ return this._removeListener('_errorCallbacks', callback)
169
+ }
170
+
171
+ /**
172
+ * @param {function(CloseEvent)} callback
173
+ * @return {TWebSocket}
174
+ * @throws {Error}
175
+ */
176
+ addCloseListener(callback) {
177
+ return this._addListener('_closeCallbacks', callback)
178
+ }
179
+
180
+ /**
181
+ * @param {function(CloseEvent)} callback
182
+ * @return {TWebSocket}
183
+ */
184
+ removeCloseListener(callback) {
185
+ return this._removeListener('_closeCallbacks', callback)
186
+ }
187
+
188
+ /**
189
+ * @param {string} name
190
+ * @param {function(any)} callback
191
+ * @throws {Error}
192
+ * @private
193
+ */
194
+ _addListener(name, callback) {
195
+ if (this[name].length >= MaxListenerSize)
196
+ throw new Error(`listener size must be ${MaxListenerSize}`)
197
+
198
+ if (this[name].findIndex((v) => v === callback) > -1) return this
199
+
200
+ this[name].push(callback)
201
+
202
+ return this
203
+ }
204
+
205
+ /**
206
+ * @param {string} name
207
+ * @param {function(any)} callback
208
+ * @private
209
+ */
210
+ _removeListener(name, callback) {
211
+ const idx = this[name].findIndex((v) => v === callback)
212
+
213
+ if (idx > -1) {
214
+ this[name].splice(idx, 1)
215
+ }
216
+
217
+ return this
218
+ }
219
+
220
+ _callListener(name, value) {
221
+ if (!this[name]) return
222
+
223
+ for (const callback of this[name]) {
224
+ setTimeout(
225
+ (callback, value) => {
226
+ callback(value)
227
+ },
228
+ 0,
229
+ callback,
230
+ value,
231
+ )
232
+ }
233
+ }
234
+
235
+ /**
236
+ * @private
237
+ * @param {?boolean} force
238
+ * @return {Awaited<TWebSocket>}
239
+ * @throws {Error}
240
+ */
241
+ async _init(force = false) {
242
+ if (!force && this.ready) return this
243
+
244
+ if (this.ready) {
245
+ await this.disconnect(1000)
246
+ }
247
+
248
+ this._make()
249
+
250
+ return this
251
+ }
252
+
253
+ _make() {
254
+ this._client = new ReconnectingWebSocket(
255
+ this._options.url(),
256
+ this._options.protocols(),
257
+ this._options.make(),
258
+ )
259
+
260
+ this._openListener = (e) => {
261
+ this._onOpen(e)
262
+ }
263
+ this._client.addEventListener('open', this._openListener)
264
+
265
+ this._closeListener = (e) => {
266
+ this._onClose(e)
267
+ }
268
+ this._client.addEventListener('close', this._closeListener)
269
+
270
+ this._messageListener = (e) => {
271
+ this._onMessage(e)
272
+ }
273
+ this._client.addEventListener('message', this._messageListener)
274
+
275
+ this._errorListener = (e) => {
276
+ this._onError(e)
277
+ }
278
+ this._client.addEventListener('error', this._errorListener)
279
+ }
280
+
281
+ /**
282
+ * @private
283
+ * @param {?number} code
284
+ * @return {Promise<void>}
285
+ */
286
+ async _disconnect(code = 1000) {
287
+ if (!this.ready) return
288
+
289
+ this._close(code)
290
+ }
291
+
292
+ _close(code = 1000) {
293
+ if (!code) code = 1000
294
+
295
+ this._client.removeEventListener('open', this._openListener)
296
+ this._client.removeEventListener('close', this._closeListener)
297
+ this._client.removeEventListener('message', this._messageListener)
298
+ this._client.removeEventListener('error', this._errorListener)
299
+
300
+ this._openCallbacks.length = 0
301
+ this._errorCallbacks.length = 0
302
+ this._closeCallbacks.length = 0
303
+ this._messageCallbacks.length = 0
304
+
305
+ this._openListener = () => {}
306
+ this._closeListener = () => {}
307
+ this._closeListener = () => {}
308
+ this._messageListener = () => {}
309
+
310
+ this._client.close(code)
311
+ }
312
+
313
+ /**
314
+ * @param {CloseEvent} event
315
+ * @private
316
+ */
317
+ _onClose(event) {
318
+ this._callListener('_closeCallbacks', event)
319
+ }
320
+
321
+ /**
322
+ * @param {ErrorEvent} event
323
+ * @private
324
+ */
325
+ _onError(event) {
326
+ this._callListener('_errorCallbacks', event)
327
+ }
328
+
329
+ /**
330
+ * @param {MessageEvent} msg
331
+ * @private
332
+ */
333
+ _onMessage(msg) {
334
+ this._callListener('_messageCallbacks', msg)
335
+ }
336
+
337
+ /**
338
+ * @param {Event} event
339
+ * @private
340
+ */
341
+ _onOpen(event) {
342
+ this._callListener('_openCallbacks', event)
343
+ }
344
+ }
@@ -0,0 +1,277 @@
1
+ export const HTTPSPort = 443,
2
+ HTTPPort = 80
3
+
4
+ export const DefaultTimeout = 7000,
5
+ LongTimeout = DefaultTimeout * 2
6
+
7
+ export class Options {
8
+ _host
9
+ _port
10
+ _secure
11
+ _timeout
12
+ _longpoll = false
13
+ _protocols = []
14
+ _customWS
15
+ _endpoints = ['ws', 'longpoll']
16
+
17
+ _maxConnectionDelay = 10000
18
+ _minReconnectionDelay = 1000 + Math.random() * 4000
19
+ _reconnectionDelayGrowFactor = 1.3
20
+ _minUptime = 5000
21
+ _maxRetries = 10
22
+ _maxEnqueuedMessages = Infinity
23
+ _startClosed = false
24
+ _debug = !!process.env.DEBUG
25
+
26
+ /**
27
+ * @param {string} host
28
+ * @param {?number} timeout
29
+ * @param {?boolean} secure
30
+ * @param {?number} port
31
+ * @param {?boolean} longpoll
32
+ * @constructor
33
+ */
34
+ constructor(
35
+ host,
36
+ timeout = DefaultTimeout,
37
+ secure = true,
38
+ port = HTTPSPort,
39
+ longpoll = false,
40
+ ) {
41
+ this._host = host
42
+ this._port = port
43
+ this._secure = secure
44
+ this._timeout = timeout
45
+ this._longpoll = longpoll
46
+ }
47
+
48
+ /**
49
+ * @return {boolean}
50
+ * @throws {Error}
51
+ */
52
+ check() {
53
+ if (this._timeout > LongTimeout)
54
+ throw new Error(`timeout less than LongTimeout`)
55
+
56
+ try {
57
+ new URL(this.url())
58
+
59
+ return true
60
+ } catch (e) {
61
+ throw e
62
+ }
63
+ }
64
+
65
+ /**
66
+ * @return {boolean}
67
+ */
68
+ isLongPool() {
69
+ // return this._longpoll
70
+
71
+ return false
72
+ }
73
+
74
+ /**
75
+ * @param {boolean} debug
76
+ * @return {Options}
77
+ */
78
+ setDebug(debug) {
79
+ this._debug = debug
80
+
81
+ return this
82
+ }
83
+
84
+ /**
85
+ * @return {boolean}
86
+ */
87
+ debug() {
88
+ return this._debug
89
+ }
90
+
91
+ /**
92
+ * @param {?string} wsEndpoint
93
+ * @param {?string} longpollEndpoint
94
+ * @throws {Error}
95
+ */
96
+ setEndpoints(wsEndpoint = 'ws', longpollEndpoint = 'longpool') {
97
+ if (typeof wsEndpoint !== 'string' || typeof longpollEndpoint !== 'string')
98
+ throw new TypeError('invalid endpoints')
99
+
100
+ this._endpoints = [wsEndpoint, longpollEndpoint]
101
+ }
102
+
103
+ /**
104
+ * @return {Array<string>}
105
+ */
106
+ endpoints() {
107
+ return this._endpoints
108
+ }
109
+
110
+ /**
111
+ * @param {number} delay
112
+ * @return {Options}
113
+ */
114
+ setMaxConnectionDelay(delay) {
115
+ this._maxConnectionDelay = delay
116
+
117
+ return this
118
+ }
119
+
120
+ /**
121
+ * @return {number}
122
+ */
123
+ maxConnectionDelay() {
124
+ return this._maxConnectionDelay
125
+ }
126
+
127
+ /**
128
+ * @param {number} delay
129
+ * @return {Options}
130
+ */
131
+ setMinReconnectionDelay(delay) {
132
+ this._minReconnectionDelay = delay
133
+
134
+ return this
135
+ }
136
+
137
+ /**
138
+ * @return {number}
139
+ */
140
+ minReconnectionDelay() {
141
+ return this._minReconnectionDelay
142
+ }
143
+
144
+ /**
145
+ * @param {number} gw
146
+ * @return {Options}
147
+ */
148
+ setReconnectionDelayGrowFactor(gw) {
149
+ this._reconnectionDelayGrowFactor = gw
150
+
151
+ return this
152
+ }
153
+
154
+ /**
155
+ * @return {number}
156
+ */
157
+ reconnectionDelayGrowFactor() {
158
+ return this._reconnectionDelayGrowFactor
159
+ }
160
+
161
+ /**
162
+ * @param {number} ut
163
+ * @return {Options}
164
+ */
165
+ setMinUptime(ut) {
166
+ this._minUptime = ut
167
+
168
+ return this
169
+ }
170
+
171
+ /**
172
+ * @return {number}
173
+ */
174
+ minUptime() {
175
+ return this._minUptime
176
+ }
177
+
178
+ /**
179
+ * @param {number} retries
180
+ * @return {Options}
181
+ */
182
+ setMaxRetries(retries) {
183
+ this._maxRetries = retries
184
+
185
+ return this
186
+ }
187
+
188
+ /**
189
+ * @return {number}
190
+ */
191
+ maxRetries() {
192
+ return this._maxRetries
193
+ }
194
+
195
+ /**
196
+ * @param {number} em
197
+ * @return {Options}
198
+ */
199
+ setMaxEnqueuedMessages(em) {
200
+ this._maxEnqueuedMessages = em
201
+
202
+ return this
203
+ }
204
+
205
+ /**
206
+ * @return {number}
207
+ */
208
+ maxEnqueuedMessages() {
209
+ return this._maxEnqueuedMessages
210
+ }
211
+
212
+ /**
213
+ * @param {Array<string>} protocols
214
+ * @return {Options}
215
+ * @throws {TypeError}
216
+ */
217
+ setProtocols(protocols) {
218
+ if (!Array.isArray(protocols))
219
+ throw new TypeError('protocols must be a Array')
220
+ this._protocols = protocols
221
+
222
+ return this
223
+ }
224
+
225
+ /**
226
+ * @return {Array<string>}
227
+ */
228
+ protocols() {
229
+ return this._protocols
230
+ }
231
+
232
+ /**
233
+ * @param {any} customWS
234
+ * @return {Options}
235
+ */
236
+ setCustomWS(customWS) {
237
+ this._customWS = customWS
238
+
239
+ return this
240
+ }
241
+
242
+ /**
243
+ * @return {any}
244
+ */
245
+ customWS() {
246
+ return this._customWS
247
+ }
248
+
249
+ /**
250
+ * @return {string}
251
+ */
252
+ url() {
253
+ if (this._host.length > 2 && this._host.slice(0, 2) === 'ws') {
254
+ return `${this._host}${![HTTPPort, HTTPSPort].includes(this._port) ? `:${this._port}` : ''}`
255
+ }
256
+
257
+ return `ws${this._secure ? 's' : ''}://${this._host}${![HTTPPort, HTTPSPort].includes(this._port) ? `:${this._port}` : ''}/${!this._longpoll ? this._endpoints[0] : this._endpoints[1]}`
258
+ }
259
+
260
+ /**
261
+ * @return {{WebSocket: ?any, maxReconnectionDelay: number, minReconnectionDelay: number, reconnectionDelayGrowFactor: number, minUptime: number, connectionTimeout: number, maxRetries: number, maxEnqueuedMessages: number, startClosed: boolean, debug: boolean }}
262
+ */
263
+ make() {
264
+ return {
265
+ ...(this._customWS ? { WebSocket: this._customWS } : {}),
266
+ maxReconnectionDelay: this._maxConnectionDelay,
267
+ minReconnectionDelay: this._minReconnectionDelay,
268
+ reconnectionDelayGrowFactor: this._reconnectionDelayGrowFactor,
269
+ minUptime: this._minUptime,
270
+ connectionTimeout: this._timeout,
271
+ maxRetries: this._maxRetries,
272
+ maxEnqueuedMessages: this._maxEnqueuedMessages,
273
+ startClosed: this._startClosed,
274
+ debug: this._debug,
275
+ }
276
+ }
277
+ }