@orderly.network/net 1.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/.turbo/turbo-build.log +22 -0
- package/CHANGELOG.md +7 -0
- package/README.md +3 -0
- package/__test__/createWebSocket.test.ts +11 -0
- package/__test__/fetch.test.ts +13 -0
- package/babel.config.js +6 -0
- package/dist/index.d.mts +37 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +410 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +383 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +33 -0
- package/src/constants.ts +1 -0
- package/src/fetch/index.ts +70 -0
- package/src/index.ts +4 -0
- package/src/sum.test.ts +5 -0
- package/src/sum.ts +1 -0
- package/src/types/ws.ts +15 -0
- package/src/utils/netObserve.ts +20 -0
- package/src/ws/contants.ts +13 -0
- package/src/ws/handler/baseHandler.ts +9 -0
- package/src/ws/handler/handler.ts +17 -0
- package/src/ws/handler/ping.ts +8 -0
- package/src/ws/index.ts +329 -0
- package/tsconfig.json +13 -0
- package/tsup.config.ts +11 -0
package/src/ws/index.ts
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { type WebSocketSubject, webSocket } from "rxjs/webSocket";
|
|
2
|
+
|
|
3
|
+
import { WS_URL } from "./contants";
|
|
4
|
+
import { Observable, Observer, Subject, tap } from "rxjs";
|
|
5
|
+
import { messageHandlers } from "@/ws/handler/handler";
|
|
6
|
+
|
|
7
|
+
export type NetworkId = "testnet" | "mainnet";
|
|
8
|
+
|
|
9
|
+
export type WSOptions = {
|
|
10
|
+
url?: string;
|
|
11
|
+
networkId?: NetworkId;
|
|
12
|
+
accountId?: string;
|
|
13
|
+
|
|
14
|
+
onSigntureRequest?: (accountId: string) => Promise<any>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
class WebSocketClient {
|
|
18
|
+
// the topic reference count;
|
|
19
|
+
private static __topicRefCountMap: Map<string, number> = new Map();
|
|
20
|
+
private wsSubject: WebSocketSubject<any>;
|
|
21
|
+
private privateWsSubject?: WebSocketSubject<any>;
|
|
22
|
+
|
|
23
|
+
private authenticated: boolean = false;
|
|
24
|
+
|
|
25
|
+
private _pendingPrivateSubscribe: any[] = [];
|
|
26
|
+
|
|
27
|
+
constructor(options: WSOptions) {
|
|
28
|
+
this.wsSubject = this.createSubject(options);
|
|
29
|
+
|
|
30
|
+
if (!!options.accountId) {
|
|
31
|
+
this.privateWsSubject = this.createPrivateSubject(options);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.bindSubscribe();
|
|
35
|
+
}
|
|
36
|
+
private createSubject(options: WSOptions): WebSocketSubject<any> {
|
|
37
|
+
let url;
|
|
38
|
+
if (typeof options.url === "string") {
|
|
39
|
+
url = options.url;
|
|
40
|
+
} else {
|
|
41
|
+
url = WS_URL[options.networkId || "testnet"].public;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return webSocket({
|
|
45
|
+
url: `${url}${options.accountId || ""}`,
|
|
46
|
+
openObserver: {
|
|
47
|
+
next: () => {
|
|
48
|
+
console.log("Connection ok");
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
closeObserver: {
|
|
52
|
+
next: () => {
|
|
53
|
+
console.log("Connection closed");
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private createPrivateSubject(options: WSOptions): WebSocketSubject<any> {
|
|
60
|
+
const url = WS_URL[options.networkId || "testnet"].private;
|
|
61
|
+
const ws = webSocket({
|
|
62
|
+
url: `${url}${options.accountId}`,
|
|
63
|
+
openObserver: {
|
|
64
|
+
next: () => {
|
|
65
|
+
console.log("Private connection ok");
|
|
66
|
+
if (this.authenticated || !options.accountId) return;
|
|
67
|
+
|
|
68
|
+
options.onSigntureRequest?.(options.accountId).then((signature) => {
|
|
69
|
+
this.authenticate(options.accountId!, signature);
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
closeObserver: {
|
|
74
|
+
next: () => {
|
|
75
|
+
console.log("Private connection closed");
|
|
76
|
+
this.authenticated = false;
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
// authenticate
|
|
81
|
+
|
|
82
|
+
return ws;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private bindSubscribe() {
|
|
86
|
+
/// 处理ping,auth等消息
|
|
87
|
+
|
|
88
|
+
this.wsSubject.subscribe({
|
|
89
|
+
next: (message) => {
|
|
90
|
+
const handler = messageHandlers.get(message.event);
|
|
91
|
+
if (handler) {
|
|
92
|
+
handler.handle(message, this.send);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
error(err) {
|
|
96
|
+
console.log("WS Error: ", err);
|
|
97
|
+
},
|
|
98
|
+
complete() {
|
|
99
|
+
console.log("WS Connection closed");
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (!this.privateWsSubject) return;
|
|
104
|
+
|
|
105
|
+
this.privateWsSubject.subscribe({
|
|
106
|
+
next: (message) => {
|
|
107
|
+
if (message.event === "auth") {
|
|
108
|
+
this.authenticated = true;
|
|
109
|
+
// 认证成功后,发送之前的订阅消息
|
|
110
|
+
this._sendPendingPrivateMessage();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const handler = messageHandlers.get(message.event);
|
|
115
|
+
|
|
116
|
+
if (handler) {
|
|
117
|
+
handler.handle(message, this.privateSend);
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
error(err) {
|
|
121
|
+
console.log("WS Error: ", err);
|
|
122
|
+
},
|
|
123
|
+
complete() {
|
|
124
|
+
console.log("WS Connection closed");
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private authenticate(
|
|
130
|
+
accountId: string,
|
|
131
|
+
message: {
|
|
132
|
+
publicKey: string;
|
|
133
|
+
signature: string;
|
|
134
|
+
timestamp: number;
|
|
135
|
+
}
|
|
136
|
+
) {
|
|
137
|
+
if (this.authenticated) return;
|
|
138
|
+
if (!this.privateWsSubject) {
|
|
139
|
+
console.error("private ws not connected");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.log("push auth message:", message);
|
|
144
|
+
this.privateWsSubject?.next({
|
|
145
|
+
id: "auth",
|
|
146
|
+
event: "auth",
|
|
147
|
+
params: {
|
|
148
|
+
orderly_key: message.publicKey,
|
|
149
|
+
sign: message.signature,
|
|
150
|
+
timestamp: message.timestamp,
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
// this.wsSubject.next({ type: "authenticate" });
|
|
154
|
+
// this.authenticated = true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
send = (message: any) => {
|
|
158
|
+
this.wsSubject.next(message);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
privateSend = (message: any) => {
|
|
162
|
+
if (!this.privateWsSubject) {
|
|
163
|
+
console.warn("private ws not connected");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
this.privateWsSubject.next(message);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
get isAuthed() {
|
|
171
|
+
return this.authenticated;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// observe<T>(topic: string): Observable<T>;
|
|
175
|
+
// observe<T>(topic: string, unsubscribe?: () => any): Observable<T>;
|
|
176
|
+
// observe<T>(
|
|
177
|
+
// params: {
|
|
178
|
+
// event: string;
|
|
179
|
+
// } & Record<string, any>,
|
|
180
|
+
// unsubscribe?: () => any
|
|
181
|
+
// ): Observable<T>;
|
|
182
|
+
observe<T>(
|
|
183
|
+
params: any,
|
|
184
|
+
unsubscribe?: () => any,
|
|
185
|
+
messageFilter?: (value: T) => boolean
|
|
186
|
+
): Observable<T> {
|
|
187
|
+
return this._observe(false, params, unsubscribe, messageFilter);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// privateObserve<T>(topic: string): Observable<T>;
|
|
191
|
+
// privateObserve<T>(topic: string, unsubscribe?: () => any): Observable<T>;
|
|
192
|
+
// privateObserve<T>(
|
|
193
|
+
// params: {
|
|
194
|
+
// event: string;
|
|
195
|
+
// } & Record<string, any>,
|
|
196
|
+
// unsubscribe?: () => any
|
|
197
|
+
// ): Observable<T>;
|
|
198
|
+
privateObserve<T>(
|
|
199
|
+
params: any,
|
|
200
|
+
unsubscribe?: () => any,
|
|
201
|
+
messageFilter?: (value: T) => boolean
|
|
202
|
+
): Observable<T> {
|
|
203
|
+
return this._observe(true, params, unsubscribe, messageFilter);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private _observe<T>(
|
|
207
|
+
isPrivate: boolean,
|
|
208
|
+
params: any,
|
|
209
|
+
unsubscribe?: () => any,
|
|
210
|
+
messageFilter?: (value: T) => boolean
|
|
211
|
+
) {
|
|
212
|
+
if (isPrivate && !this.privateWsSubject) {
|
|
213
|
+
throw new Error("private ws not connected");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const subject = isPrivate ? this.privateWsSubject : this.wsSubject;
|
|
217
|
+
const sendFunc = isPrivate ? this.privateSend : this.send;
|
|
218
|
+
|
|
219
|
+
const [subscribeMessage, unsubscribeMessage, filter, messageFormatter] =
|
|
220
|
+
this.generateMessage(params, unsubscribe, messageFilter);
|
|
221
|
+
|
|
222
|
+
return new Observable((observer: Observer<T>) => {
|
|
223
|
+
// 如果是private ws, 但是没有连接,就先缓存起来,等连接上了再订阅
|
|
224
|
+
if (isPrivate && !this.authenticated) {
|
|
225
|
+
this._pendingPrivateSubscribe.push(params);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
//TODO: add ref count, only send subscribe message when ref count is 0
|
|
231
|
+
// 如果已经订阅过了,就不再发送订阅消息
|
|
232
|
+
const refCount =
|
|
233
|
+
WebSocketClient.__topicRefCountMap.get(subscribeMessage.topic) || 0;
|
|
234
|
+
if (refCount === 0) {
|
|
235
|
+
// WS.__topicRefCountMap.set(subscribeMessage.topic, WS.__topicRefCountMap.get(subscribeMessage.topic) + 1);
|
|
236
|
+
// this.send(subscribeMessage);
|
|
237
|
+
sendFunc(subscribeMessage);
|
|
238
|
+
WebSocketClient.__topicRefCountMap.set(
|
|
239
|
+
subscribeMessage.topic,
|
|
240
|
+
refCount + 1
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
} catch (err) {
|
|
244
|
+
observer.error(err);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const subscription = subject!.subscribe({
|
|
248
|
+
next: (x) => {
|
|
249
|
+
try {
|
|
250
|
+
if (filter(x)) {
|
|
251
|
+
observer.next(messageFormatter(x));
|
|
252
|
+
}
|
|
253
|
+
} catch (err) {
|
|
254
|
+
observer.error(err);
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
error: (err) => observer.error(err),
|
|
258
|
+
complete: () => observer.complete(),
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
return () => {
|
|
262
|
+
try {
|
|
263
|
+
// console.log("******* unsubscribe", unsubscribeMessage);
|
|
264
|
+
const refCount =
|
|
265
|
+
WebSocketClient.__topicRefCountMap.get(subscribeMessage.topic) || 0;
|
|
266
|
+
if (refCount > 1) {
|
|
267
|
+
WebSocketClient.__topicRefCountMap.set(
|
|
268
|
+
subscribeMessage.topic,
|
|
269
|
+
refCount - 1
|
|
270
|
+
);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (!!unsubscribeMessage) {
|
|
274
|
+
this.send(unsubscribeMessage);
|
|
275
|
+
}
|
|
276
|
+
WebSocketClient.__topicRefCountMap.delete(subscribeMessage.topic);
|
|
277
|
+
} catch (err) {
|
|
278
|
+
observer.error(err);
|
|
279
|
+
}
|
|
280
|
+
subscription.unsubscribe();
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private generateMessage(
|
|
286
|
+
params: any,
|
|
287
|
+
unsubscribe?: () => any,
|
|
288
|
+
messageFilter?: (value: any) => boolean
|
|
289
|
+
): [
|
|
290
|
+
Record<string, any>,
|
|
291
|
+
Record<string, any>,
|
|
292
|
+
(message: any) => boolean,
|
|
293
|
+
(message: any) => any
|
|
294
|
+
] {
|
|
295
|
+
let subscribeMessage: Record<string, any>,
|
|
296
|
+
unsubscribeMessage: Record<string, any>;
|
|
297
|
+
let filter: (message: any) => boolean,
|
|
298
|
+
messageFormatter: (message: any) => any = (message: any) => message.data;
|
|
299
|
+
|
|
300
|
+
if (typeof params === "string") {
|
|
301
|
+
subscribeMessage = { event: "subscribe", topic: params };
|
|
302
|
+
unsubscribeMessage = { event: "unsubscribe", topic: params };
|
|
303
|
+
filter = (message: any) => message.topic === params;
|
|
304
|
+
} else {
|
|
305
|
+
subscribeMessage = params;
|
|
306
|
+
unsubscribeMessage =
|
|
307
|
+
typeof unsubscribe === "function" ? unsubscribe() : unsubscribe;
|
|
308
|
+
filter = messageFilter || ((message: any) => true);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return [subscribeMessage, unsubscribeMessage, filter, messageFormatter];
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private _sendPendingPrivateMessage() {
|
|
315
|
+
if (this._pendingPrivateSubscribe.length === 0) return;
|
|
316
|
+
this._pendingPrivateSubscribe.forEach((params) => {
|
|
317
|
+
this.privateObserve(params).subscribe();
|
|
318
|
+
});
|
|
319
|
+
this._pendingPrivateSubscribe = [];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// 取消所有订阅
|
|
323
|
+
desotry() {
|
|
324
|
+
this.wsSubject.unsubscribe();
|
|
325
|
+
this.privateWsSubject?.unsubscribe();
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export default WebSocketClient;
|
package/tsconfig.json
ADDED