@orderly.network/net 1.0.112-alpha.2 → 1.0.112

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/src/ws/ws.ts DELETED
@@ -1,734 +0,0 @@
1
- import { messageHandlers } from "./handler/handler";
2
-
3
- export type NetworkId = "testnet" | "mainnet";
4
-
5
- export type WSOptions = {
6
- privateUrl: string;
7
- publicUrl: string;
8
- networkId?: NetworkId;
9
- accountId?: string;
10
-
11
- onSigntureRequest?: (accountId: string) => Promise<any>;
12
- };
13
-
14
- export type unsubscribe = () => void;
15
-
16
- export enum WebSocketEvent {
17
- OPEN = "open",
18
- CLOSE = "close",
19
- ERROR = "error",
20
- MESSAGE = "message",
21
- CONNECTING = "connecting",
22
- RECONNECTING = "reconnecting",
23
- }
24
-
25
- export type MessageParams = {
26
- event: string;
27
- topic: string;
28
-
29
- onMessage?: (message: any) => any;
30
-
31
- params?: any;
32
- // [key: string]: any;
33
- };
34
-
35
- type WSMessageHandler = {
36
- onMessage: (message: any) => void;
37
- onError?: (error: any) => void;
38
- onClose?: (event: any) => void;
39
- onUnsubscribe: (event: any) => any;
40
- formatter?: (message: any) => any;
41
- };
42
-
43
- type Topics = {
44
- params: MessageParams;
45
- isPrivate?: boolean;
46
- isOnce?: boolean;
47
- callback: WSMessageHandler[];
48
- };
49
-
50
- const defaultMessageFormatter = (message: any) => message.data;
51
- const COMMON_ID = "OqdphuyCtYWxwzhxyLLjOWNdFP7sQt8RPWzmb5xY";
52
-
53
- const TIME_OUT = 1000 * 60 * 2;
54
- const CONNECT_LIMIT = 5;
55
-
56
- export class WS {
57
- private _publicSocket!: WebSocket;
58
- private privateSocket?: WebSocket;
59
-
60
- private _eventContainer: Map<string, Set<Function>> = new Map();
61
-
62
- private publicIsReconnecting: boolean = false;
63
- private privateIsReconnecting: boolean = false;
64
-
65
- private reconnectInterval: number = 1000;
66
-
67
- private authenticated: boolean = false;
68
-
69
- private _pendingPrivateSubscribe: any[][] = [];
70
- private _pendingPublicSubscribe: any[][] = [];
71
-
72
- // all message handlers
73
- private _eventHandlers: Map<string, Topics> = new Map();
74
- private _eventPrivateHandlers: Map<string, Topics> = new Map();
75
-
76
- private _publicHeartbeatTime?: number;
77
- private _privateHeartbeatTime?: number;
78
-
79
- private _publicRetryCount: number = 0;
80
- private _privateRetryCount: number = 0;
81
-
82
- constructor(private options: WSOptions) {
83
- this.createPublicSC(options);
84
-
85
- if (!!options.accountId) {
86
- this.createPrivateSC(options);
87
- }
88
-
89
- this.bindEvents();
90
- }
91
-
92
- private bindEvents() {
93
- if (typeof document !== "undefined") {
94
- document.addEventListener(
95
- "visibilitychange",
96
- this.onVisibilityChange.bind(this)
97
- );
98
- }
99
-
100
- if (typeof window !== "undefined") {
101
- window.addEventListener("online", this.onNetworkStatusChange.bind(this));
102
- // window.addEventListener("offline", this.onNetworkStatusChange);
103
- }
104
- }
105
-
106
- private onVisibilityChange() {
107
- if (document.visibilityState === "visible") {
108
- this.checkSocketStatus();
109
- }
110
- // else {
111
- // this.publicSocket.close();
112
- // this.privateSocket?.close();
113
- // }
114
- }
115
-
116
- private onNetworkStatusChange() {
117
- if (navigator.onLine) {
118
- this.checkSocketStatus();
119
- }
120
- }
121
-
122
- /**
123
- * Determine the current connection status,
124
- * 1. If it is disconnected, reconnect
125
- * 2. If no message is received for too long, disconnect and reconnect actively
126
- * 3. When returning from the background and the network status changes, the following process is followed
127
- */
128
- private checkSocketStatus() {
129
- const now = Date.now();
130
-
131
- //
132
- // "👀👀 checkNetworkStatus 👀👀",
133
- // this._publicHeartbeatTime,
134
- // this._privateHeartbeatTime,
135
- // now,
136
- // this.publicSocket.readyState,
137
- // this.privateSocket?.readyState
138
- // );
139
-
140
- // check the last time
141
- // If the view is not visible, do not process it
142
- if (document.visibilityState !== "visible") return;
143
- // If the network is not available, do not process it
144
- if (!navigator.onLine) return;
145
-
146
- // 如果已断开,则重连
147
- // public
148
- if (!this.publicIsReconnecting) {
149
- if (this._publicSocket.readyState === WebSocket.CLOSED) {
150
- this.reconnectPublic();
151
- } else {
152
- if (now - this._publicHeartbeatTime! > TIME_OUT) {
153
- //unsubscribe all public topic
154
- this._publicSocket.close();
155
- }
156
- }
157
- }
158
-
159
- if (!this.privateIsReconnecting) {
160
- // private
161
- if (this.privateSocket?.readyState === WebSocket.CLOSED) {
162
- this.reconnectPrivate();
163
- } else {
164
- if (
165
- this._privateHeartbeatTime &&
166
- now - this._privateHeartbeatTime! > TIME_OUT
167
- ) {
168
- // unsubscribe all private topic
169
- this.privateSocket?.close();
170
- }
171
- }
172
- }
173
- }
174
-
175
- public openPrivate(accountId: string) {
176
- if (this.privateSocket?.readyState === WebSocket.OPEN) {
177
- return;
178
- }
179
- this.createPrivateSC({
180
- ...this.options,
181
- accountId,
182
- });
183
- }
184
-
185
- public closePrivate() {
186
- this.authenticated = false;
187
- this._pendingPrivateSubscribe = [];
188
-
189
- this._eventPrivateHandlers.clear();
190
-
191
- this.privateSocket?.close();
192
- }
193
-
194
- private createPublicSC(options: WSOptions) {
195
- if (this._publicSocket && this._publicSocket.readyState === WebSocket.OPEN)
196
- return;
197
- this._publicSocket = new WebSocket(
198
- `${this.options.publicUrl}/ws/stream/${COMMON_ID}`
199
- );
200
- this._publicSocket.onopen = this.onOpen.bind(this);
201
- // this.publicSocket.onmessage = this.onPublicMessage.bind(this);
202
- this._publicSocket.addEventListener(
203
- "message",
204
- this.onPublicMessage.bind(this)
205
- );
206
- this._publicSocket.addEventListener("close", this.onPublicClose.bind(this));
207
- this._publicSocket.addEventListener("error", this.onPublicError.bind(this));
208
- // this.publicSocket.onclose = this.onPublicClose.bind(this);
209
- // this.publicSocket.onerror = this.onPublicError.bind(this);
210
- }
211
-
212
- private createPrivateSC(options: WSOptions) {
213
- if (this.privateSocket && this.privateSocket.readyState === WebSocket.OPEN)
214
- return;
215
-
216
- this.options = options;
217
-
218
- this.privateSocket = new WebSocket(
219
- `${this.options.privateUrl}/v2/ws/private/stream/${options.accountId}`
220
- );
221
- this.privateSocket.onopen = this.onPrivateOpen.bind(this);
222
- this.privateSocket.onmessage = this.onPrivateMessage.bind(this);
223
- this.privateSocket.onclose = this.onPrivateClose.bind(this);
224
- this.privateSocket.onerror = this.onPrivateError.bind(this);
225
- }
226
-
227
- private onOpen(event: Event) {
228
- //
229
- if (this._pendingPublicSubscribe.length > 0) {
230
- this._pendingPublicSubscribe.forEach(([params, cb, isOnce]) => {
231
- this.subscribe(params, cb, isOnce);
232
- });
233
- this._pendingPublicSubscribe = [];
234
- }
235
-
236
- this.publicIsReconnecting = false;
237
- this._publicRetryCount = 0;
238
-
239
- this.emit("status:change", { type: WebSocketEvent.OPEN, isPrivate: false });
240
- }
241
-
242
- private onPrivateOpen(event: Event) {
243
- //auth
244
- this.authenticate(this.options.accountId!);
245
- this.privateIsReconnecting = false;
246
- this._privateRetryCount = 0;
247
-
248
- this.emit("status:change", { type: WebSocketEvent.OPEN, isPrivate: true });
249
- }
250
-
251
- private onMessage(
252
- event: MessageEvent,
253
- socket: WebSocket,
254
- handlerMap: Map<string, Topics>
255
- ) {
256
- try {
257
- const message = JSON.parse(event.data);
258
-
259
- const commoneHandler = messageHandlers.get(message.event);
260
-
261
- if (message.event === "auth" && message.success) {
262
- this.authenticated = true;
263
- this.handlePendingPrivateTopic();
264
- return;
265
- }
266
-
267
- if (commoneHandler) {
268
- commoneHandler.handle(message, socket);
269
- } else {
270
- const topicKey = this.getTopicKeyFromMessage(message);
271
-
272
- const eventhandler = handlerMap.get(topicKey);
273
- //
274
- if (eventhandler?.callback) {
275
- // console.log("👀👀👀👀👀👀👀👀👀", topicKey, eventhandler?.callback);
276
- eventhandler.callback.forEach((cb) => {
277
- const data = cb.formatter
278
- ? cb.formatter(message)
279
- : defaultMessageFormatter(message);
280
-
281
- if (data) {
282
- cb.onMessage(data);
283
- }
284
- });
285
- }
286
-
287
- // emit event
288
- this._eventContainer.forEach((_, key) => {
289
- const reg = new RegExp(key);
290
- if (reg.test(topicKey)) {
291
- this.emit(key, message);
292
- }
293
- });
294
- }
295
-
296
- //
297
- } catch (e) {}
298
-
299
- // You can process the received message here
300
- }
301
-
302
- private onPublicMessage(event: MessageEvent) {
303
- this.onMessage(event, this._publicSocket, this._eventHandlers);
304
- // update last message time for public
305
- this._publicHeartbeatTime = Date.now();
306
- }
307
-
308
- private onPrivateMessage(event: MessageEvent) {
309
- this.onMessage(event, this.privateSocket!, this._eventPrivateHandlers);
310
- // update last message time for private
311
- this._privateHeartbeatTime = Date.now();
312
- }
313
-
314
- private handlePendingPrivateTopic() {
315
- if (this._pendingPrivateSubscribe.length > 0) {
316
- this._pendingPrivateSubscribe.forEach(([params, cb]) => {
317
- this.privateSubscribe(params, cb);
318
- });
319
- this._pendingPrivateSubscribe = [];
320
- }
321
- }
322
-
323
- private onPublicClose(event: CloseEvent) {
324
- // move handler to pending
325
- this._eventHandlers.forEach((value, key) => {
326
- value.callback.forEach((cb) => {
327
- this._pendingPublicSubscribe.push([value.params, cb, value.isOnce]);
328
- });
329
-
330
- this._eventHandlers.delete(key);
331
- });
332
-
333
- this.emit("status:change", {
334
- type: WebSocketEvent.CLOSE,
335
- isPrivate: false,
336
- });
337
-
338
- setTimeout(() => this.checkSocketStatus(), 0);
339
- }
340
-
341
- private onPrivateClose(event: CloseEvent) {
342
- if (this.privateIsReconnecting) return;
343
- this._eventPrivateHandlers.forEach((value, key) => {
344
- value.callback.forEach((cb) => {
345
- this._pendingPrivateSubscribe.push([value.params, cb, value.isOnce]);
346
- });
347
-
348
- this._eventPrivateHandlers.delete(key);
349
- });
350
- this.authenticated = false;
351
-
352
- this.emit("status:change", { type: WebSocketEvent.CLOSE, isPrivate: true });
353
-
354
- setTimeout(() => this.checkSocketStatus(), 0);
355
- }
356
-
357
- private onPublicError(event: Event) {
358
- console.error("public WebSocket error:", event);
359
- this.publicIsReconnecting = false;
360
-
361
- if (this._publicSocket.readyState === WebSocket.OPEN) {
362
- this._publicSocket.close();
363
- } else {
364
- // retry connect
365
- if (this._publicRetryCount > CONNECT_LIMIT) return;
366
- setTimeout(() => {
367
- // this.createPublicSC(this.options);
368
- this.reconnectPublic();
369
- this._publicRetryCount++;
370
- }, this._publicRetryCount * 1000);
371
- }
372
-
373
- this.errorBoardscast(event, this._eventHandlers);
374
- this.emit("status:change", {
375
- type: WebSocketEvent.ERROR,
376
- isPrivate: false,
377
- });
378
- }
379
-
380
- private onPrivateError(event: Event) {
381
- console.error("Private WebSocket error:", event);
382
- this.privateIsReconnecting = false;
383
-
384
- if (this.privateSocket?.readyState === WebSocket.OPEN) {
385
- this.privateSocket.close();
386
- } else {
387
- // retry connect
388
- if (this._privateRetryCount > CONNECT_LIMIT) return;
389
- setTimeout(() => {
390
- // this.createPublicSC(this.options);
391
- this.reconnectPrivate();
392
- this._privateRetryCount++;
393
- }, this._privateRetryCount * 1000);
394
- }
395
-
396
- this.errorBoardscast(event, this._eventPrivateHandlers);
397
- this.emit("status:change", { type: WebSocketEvent.ERROR, isPrivate: true });
398
- }
399
-
400
- private errorBoardscast(error: any, eventHandlers: Map<string, Topics>) {
401
- eventHandlers.forEach((value) => {
402
- value.callback.forEach((cb) => {
403
- cb.onError?.(error);
404
- });
405
- });
406
- }
407
-
408
- send = (message: any) => {
409
- if (typeof message !== "string") {
410
- message = JSON.stringify(message);
411
- }
412
- if (typeof message === "undefined") return;
413
- if (this._publicSocket.readyState === WebSocket.OPEN) {
414
- this._publicSocket.send(message);
415
- //
416
- } else {
417
- console.warn("WebSocket connection is not open. Cannot send message.");
418
- }
419
- };
420
-
421
- close() {
422
- this._publicSocket.close();
423
- this.privateSocket?.close();
424
- }
425
-
426
- set accountId(accountId: string) {}
427
-
428
- private async authenticate(accountId: string) {
429
- if (this.authenticated) return;
430
- if (!this.privateSocket) {
431
- console.error("private ws not connected");
432
- return;
433
- }
434
-
435
- if (this.privateSocket.readyState !== WebSocket.OPEN) {
436
- return;
437
- }
438
-
439
- const message = await this.options.onSigntureRequest?.(accountId);
440
-
441
- this.privateSocket.send(
442
- JSON.stringify({
443
- id: "auth",
444
- event: "auth",
445
- params: {
446
- orderly_key: message.publicKey,
447
- sign: message.signature,
448
- timestamp: message.timestamp,
449
- },
450
- })
451
- );
452
- // this.wsSubject.next({ type: "authenticate" });
453
- // this.authenticated = true;
454
- }
455
-
456
- privateSubscribe(
457
- params: any,
458
- callback: WSMessageHandler | Omit<WSMessageHandler, "onUnsubscribe">
459
- ) {
460
- const [subscribeMessage, onUnsubscribe] = this.generateMessage(
461
- params,
462
- (callback as WSMessageHandler).onUnsubscribe
463
- );
464
-
465
- if (this.privateSocket?.readyState !== WebSocket.OPEN) {
466
- this._pendingPrivateSubscribe.push([params, callback]);
467
- return () => {
468
- this.unsubscribePrivate(subscribeMessage);
469
- };
470
- }
471
-
472
- const topic = subscribeMessage.topic || subscribeMessage.event;
473
-
474
- const handler = this._eventPrivateHandlers.get(topic);
475
- const callbacks = {
476
- ...callback,
477
- onUnsubscribe,
478
- };
479
-
480
- if (!handler) {
481
- this._eventPrivateHandlers.set(topic, {
482
- params,
483
- callback: [callbacks],
484
- });
485
- this.privateSocket.send(JSON.stringify(subscribeMessage));
486
- } else {
487
- handler.callback.push(callbacks);
488
- }
489
-
490
- return () => {
491
- this.unsubscribePrivate(subscribeMessage);
492
- };
493
- }
494
-
495
- subscribe(
496
- params: any,
497
- callback: WSMessageHandler | Omit<WSMessageHandler, "onUnsubscribe">,
498
- once?: boolean,
499
- id?: string
500
- ): unsubscribe | undefined {
501
- //
502
-
503
- const [subscribeMessage, onUnsubscribe] = this.generateMessage(
504
- params,
505
- (callback as WSMessageHandler).onUnsubscribe,
506
- (callback as WSMessageHandler).onMessage
507
- );
508
-
509
- //
510
-
511
- if (this._publicSocket.readyState !== WebSocket.OPEN) {
512
- this._pendingPublicSubscribe.push([params, callback, once, id]);
513
-
514
- if (!once) {
515
- return () => {
516
- this.unsubscribePublic(subscribeMessage);
517
- };
518
- }
519
- return;
520
- }
521
-
522
- let topic = this.getTopicKeyFromParams(subscribeMessage);
523
- // const topic = subscribeMessage.topic || subscribeMessage.event;
524
-
525
- const handler = this._eventHandlers.get(topic);
526
- const callbacks = {
527
- ...callback,
528
- onUnsubscribe,
529
- };
530
-
531
- if (!handler) {
532
- this._eventHandlers.set(topic, {
533
- params,
534
- isOnce: once,
535
- callback: [callbacks],
536
- });
537
- this._publicSocket.send(JSON.stringify(subscribeMessage));
538
- } else {
539
- // 是否once,如果是once,则替换掉之前的callback
540
- if (once) {
541
- handler.callback = [callbacks];
542
- this._publicSocket.send(JSON.stringify(subscribeMessage));
543
- } else {
544
- handler.callback.push(callbacks);
545
- }
546
- }
547
-
548
- // this._subscriptionPublicTopics.push({params, cb: [cb]});
549
-
550
- if (!once) {
551
- return () => {
552
- this.unsubscribePublic(subscribeMessage);
553
- };
554
- }
555
- }
556
-
557
- private getTopicKeyFromParams(params: any): string {
558
- let topic;
559
-
560
- if (params.topic) {
561
- topic = params.topic;
562
- } else {
563
- const eventName = params.event;
564
- topic = params.event;
565
-
566
- if (params.id) {
567
- topic += `_${params.id}`;
568
- }
569
- }
570
-
571
- return topic;
572
- }
573
-
574
- private getTopicKeyFromMessage(message: any): string {
575
- let topic;
576
- if (message.topic) {
577
- topic = message.topic;
578
- } else {
579
- if (message.event) {
580
- topic = `${message.event}`;
581
-
582
- if (message.id) {
583
- topic += `_${message.id}`;
584
- }
585
- }
586
- }
587
-
588
- return topic;
589
- }
590
-
591
- // sendPublicMessage(){
592
- // if(this.publicSocket.readyState !== )
593
- // }
594
-
595
- onceSubscribe(
596
- params: any,
597
- callback: Omit<WSMessageHandler, "onUnsubscribe">
598
- ) {
599
- this.subscribe(params, callback, true);
600
- }
601
-
602
- private unsubscribe(
603
- parmas: MessageParams,
604
- webSocket: WebSocket,
605
- handlerMap: Map<string, Topics>
606
- ) {
607
- const topic = parmas.topic || parmas.event;
608
- const handler = handlerMap.get(topic);
609
-
610
- // console.log("unsubscribe", topic);
611
-
612
- if (!!handler && Array.isArray(handler?.callback)) {
613
- if (handler!.callback.length === 1) {
614
- const unsubscribeMessage = handler!.callback[0].onUnsubscribe(topic);
615
-
616
- //
617
- webSocket.send(JSON.stringify(unsubscribeMessage));
618
- handlerMap.delete(topic);
619
- //post unsubscribe message
620
- } else {
621
- const index = handler.callback.findIndex(
622
- (cb) => cb.onMessage === parmas.onMessage
623
- );
624
-
625
- // console.log(index, handler.callback.length);
626
-
627
- if (index === -1) return;
628
-
629
- handler.callback.splice(index, 1);
630
-
631
- // handlerMap.set(topic, {
632
- // ...handler,
633
- // callback: handler.callback.splice(index, 1),
634
- // });
635
- }
636
- }
637
- }
638
-
639
- private unsubscribePrivate(parmas: MessageParams) {
640
- this.unsubscribe(parmas, this.privateSocket!, this._eventPrivateHandlers);
641
- }
642
-
643
- private unsubscribePublic(parmas: MessageParams) {
644
- this.unsubscribe(parmas, this._publicSocket, this._eventHandlers);
645
- }
646
-
647
- private generateMessage(
648
- params: any,
649
- onUnsubscribe?: (event: string) => any,
650
- onMessage?: (message: any) => any
651
- ): [MessageParams, (event: string) => any] {
652
- let subscribeMessage;
653
-
654
- if (typeof params === "string") {
655
- subscribeMessage = { event: "subscribe", topic: params };
656
- } else {
657
- subscribeMessage = params;
658
- }
659
-
660
- if (typeof onUnsubscribe !== "function") {
661
- if (typeof params === "string") {
662
- onUnsubscribe = () => ({ event: "unsubscribe", topic: params });
663
- } else {
664
- onUnsubscribe = () => ({ event: "unsubscribe", topic: params.topic });
665
- }
666
- }
667
-
668
- return [{ ...subscribeMessage, onMessage }, onUnsubscribe];
669
- }
670
-
671
- private reconnectPublic() {
672
- if (this.publicIsReconnecting) return;
673
- this.publicIsReconnecting = true;
674
-
675
- window.setTimeout(() => {
676
- this.createPublicSC(this.options);
677
- // this.emit("reconnect:public", { count: this._publicRetryCount });
678
- this.emit("status:change", {
679
- type: WebSocketEvent.RECONNECTING,
680
- isPrivate: false,
681
- count: this._publicRetryCount,
682
- });
683
- }, this.reconnectInterval);
684
- }
685
-
686
- private reconnectPrivate() {
687
- if (!this.options.accountId) return;
688
- if (this.privateIsReconnecting) return;
689
- this.privateIsReconnecting = true;
690
-
691
- window.setTimeout(() => {
692
- this.createPrivateSC(this.options);
693
-
694
- this.emit("status:change", {
695
- type: WebSocketEvent.RECONNECTING,
696
- isPrivate: true,
697
- count: this._privateRetryCount,
698
- });
699
- }, this.reconnectInterval);
700
- }
701
-
702
- // get publicSocket(): WebSocket {
703
- // return this._publicSocket;
704
- // }
705
-
706
- get client(): {
707
- public: WebSocket;
708
- private?: WebSocket;
709
- } {
710
- return {
711
- public: this._publicSocket,
712
- private: this.privateSocket,
713
- };
714
- }
715
-
716
- on(eventName: string, callback: (message: any) => any) {
717
- if (this._eventContainer.has(eventName)) {
718
- this._eventContainer.get(eventName)?.add(callback);
719
- }
720
- this._eventContainer.set(eventName, new Set([callback]));
721
- }
722
-
723
- off(eventName: string, callback: (message: any) => any) {
724
- if (this._eventContainer.has(eventName)) {
725
- this._eventContainer.get(eventName)?.delete(callback);
726
- }
727
- }
728
-
729
- emit(eventName: string, message: any) {
730
- if (this._eventContainer.has(eventName)) {
731
- this._eventContainer.get(eventName)?.forEach((cb) => cb(message));
732
- }
733
- }
734
- }