@cloudbase/database 1.4.3 → 1.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/dist/commonjs/ObjectId/index.d.ts +1 -1
- package/dist/commonjs/ObjectId/index.js +16 -9
- package/dist/commonjs/aggregate.js +126 -75
- package/dist/commonjs/collection.d.ts +3 -2
- package/dist/commonjs/collection.js +131 -58
- package/dist/commonjs/command.d.ts +5 -5
- package/dist/commonjs/command.js +181 -154
- package/dist/commonjs/commands/logic.js +30 -20
- package/dist/commonjs/commands/query.d.ts +1 -1
- package/dist/commonjs/commands/query.js +85 -65
- package/dist/commonjs/commands/update.js +9 -7
- package/dist/commonjs/config/error.config.js +1 -0
- package/dist/commonjs/const/code.js +1 -0
- package/dist/commonjs/constant.d.ts +7 -7
- package/dist/commonjs/constant.js +13 -11
- package/dist/commonjs/document.d.ts +10 -6
- package/dist/commonjs/document.js +305 -193
- package/dist/commonjs/geo/index.js +20 -9
- package/dist/commonjs/geo/lineString.d.ts +1 -1
- package/dist/commonjs/geo/lineString.js +62 -33
- package/dist/commonjs/geo/multiLineString.d.ts +1 -1
- package/dist/commonjs/geo/multiLineString.js +71 -32
- package/dist/commonjs/geo/multiPoint.d.ts +1 -1
- package/dist/commonjs/geo/multiPoint.js +58 -29
- package/dist/commonjs/geo/multiPolygon.d.ts +1 -1
- package/dist/commonjs/geo/multiPolygon.js +96 -36
- package/dist/commonjs/geo/point.d.ts +1 -1
- package/dist/commonjs/geo/point.js +30 -23
- package/dist/commonjs/geo/polygon.d.ts +1 -1
- package/dist/commonjs/geo/polygon.js +78 -39
- package/dist/commonjs/helper/symbol.js +17 -5
- package/dist/commonjs/index.d.ts +8 -2
- package/dist/commonjs/index.js +73 -20
- package/dist/commonjs/lib/util.d.ts +1 -0
- package/dist/commonjs/lib/util.js +26 -0
- package/dist/commonjs/operator-map.js +7 -6
- package/dist/commonjs/query.d.ts +8 -4
- package/dist/commonjs/query.js +308 -201
- package/dist/commonjs/regexp/index.d.ts +1 -1
- package/dist/commonjs/regexp/index.js +16 -9
- package/dist/commonjs/serializer/common.d.ts +2 -2
- package/dist/commonjs/serializer/common.js +59 -22
- package/dist/commonjs/serializer/datatype.d.ts +2 -2
- package/dist/commonjs/serializer/datatype.js +58 -21
- package/dist/commonjs/serializer/query.d.ts +1 -1
- package/dist/commonjs/serializer/query.js +137 -127
- package/dist/commonjs/serializer/update.d.ts +1 -1
- package/dist/commonjs/serializer/update.js +83 -78
- package/dist/commonjs/serverDate/index.d.ts +1 -1
- package/dist/commonjs/serverDate/index.js +16 -9
- package/dist/commonjs/transaction/collection.d.ts +7 -0
- package/dist/commonjs/transaction/collection.js +49 -0
- package/dist/commonjs/transaction/document.d.ts +27 -0
- package/dist/commonjs/transaction/document.js +205 -0
- package/dist/commonjs/transaction/index.js +196 -86
- package/dist/commonjs/transaction/query.d.ts +43 -0
- package/dist/commonjs/transaction/query.js +261 -0
- package/dist/commonjs/typings/index.d.ts +4 -4
- package/dist/commonjs/typings/realtime.d.ts +16 -16
- package/dist/commonjs/util.js +123 -92
- package/dist/commonjs/utils/error.d.ts +2 -1
- package/dist/commonjs/utils/error.js +74 -38
- package/dist/commonjs/utils/symbol.js +33 -14
- package/dist/commonjs/utils/type.d.ts +1 -1
- package/dist/commonjs/utils/type.js +25 -13
- package/dist/commonjs/utils/utils.d.ts +4 -1
- package/dist/commonjs/utils/utils.js +93 -22
- package/dist/commonjs/validate.js +79 -63
- package/dist/esm/ObjectId/index.d.ts +1 -1
- package/dist/esm/ObjectId/index.js +15 -8
- package/dist/esm/aggregate.js +118 -66
- package/dist/esm/collection.d.ts +3 -2
- package/dist/esm/collection.js +125 -52
- package/dist/esm/command.d.ts +5 -5
- package/dist/esm/command.js +175 -147
- package/dist/esm/commands/logic.js +30 -20
- package/dist/esm/commands/query.d.ts +1 -1
- package/dist/esm/commands/query.js +91 -71
- package/dist/esm/commands/update.js +8 -6
- package/dist/esm/config/error.config.js +1 -1
- package/dist/esm/const/code.js +1 -1
- package/dist/esm/constant.d.ts +7 -7
- package/dist/esm/constant.js +12 -11
- package/dist/esm/document.d.ts +10 -6
- package/dist/esm/document.js +297 -185
- package/dist/esm/geo/interface.js +1 -0
- package/dist/esm/geo/lineString.d.ts +1 -1
- package/dist/esm/geo/lineString.js +57 -28
- package/dist/esm/geo/multiLineString.d.ts +1 -1
- package/dist/esm/geo/multiLineString.js +66 -27
- package/dist/esm/geo/multiPoint.d.ts +1 -1
- package/dist/esm/geo/multiPoint.js +53 -24
- package/dist/esm/geo/multiPolygon.d.ts +1 -1
- package/dist/esm/geo/multiPolygon.js +91 -31
- package/dist/esm/geo/point.d.ts +1 -1
- package/dist/esm/geo/point.js +26 -19
- package/dist/esm/geo/polygon.d.ts +1 -1
- package/dist/esm/geo/polygon.js +73 -34
- package/dist/esm/helper/symbol.js +13 -13
- package/dist/esm/index.d.ts +8 -2
- package/dist/esm/index.js +62 -9
- package/dist/esm/lib/util.d.ts +1 -0
- package/dist/esm/lib/util.js +22 -0
- package/dist/esm/operator-map.js +4 -4
- package/dist/esm/query.d.ts +8 -4
- package/dist/esm/query.js +299 -192
- package/dist/esm/regexp/index.d.ts +1 -1
- package/dist/esm/regexp/index.js +15 -8
- package/dist/esm/serializer/common.d.ts +2 -2
- package/dist/esm/serializer/common.js +49 -13
- package/dist/esm/serializer/datatype.d.ts +2 -2
- package/dist/esm/serializer/datatype.js +44 -8
- package/dist/esm/serializer/query.d.ts +1 -1
- package/dist/esm/serializer/query.js +112 -102
- package/dist/esm/serializer/update.d.ts +1 -1
- package/dist/esm/serializer/update.js +69 -64
- package/dist/esm/serverDate/index.d.ts +1 -1
- package/dist/esm/serverDate/index.js +15 -8
- package/dist/esm/transaction/collection.d.ts +7 -0
- package/dist/esm/transaction/collection.js +46 -0
- package/dist/esm/transaction/document.d.ts +27 -0
- package/dist/esm/transaction/document.js +202 -0
- package/dist/esm/transaction/index.js +191 -81
- package/dist/esm/transaction/query.d.ts +43 -0
- package/dist/esm/transaction/query.js +258 -0
- package/dist/esm/typings/index.d.ts +4 -4
- package/dist/esm/typings/index.js +1 -0
- package/dist/esm/typings/realtime.d.ts +16 -16
- package/dist/esm/typings/realtime.js +1 -0
- package/dist/esm/util.js +121 -90
- package/dist/esm/utils/error.d.ts +2 -1
- package/dist/esm/utils/error.js +70 -35
- package/dist/esm/utils/symbol.js +33 -14
- package/dist/esm/utils/type.d.ts +1 -1
- package/dist/esm/utils/type.js +12 -12
- package/dist/esm/utils/utils.d.ts +4 -1
- package/dist/esm/utils/utils.js +78 -16
- package/dist/esm/validate.js +65 -49
- package/jest.config.js +14 -0
- package/package.json +14 -10
- package/src/aggregate.ts +1 -5
- package/src/collection.ts +15 -2
- package/src/document.ts +45 -34
- package/src/index.ts +51 -4
- package/src/query.ts +33 -29
- package/src/serializer/query.ts +18 -6
- package/src/transaction/index.ts +1 -1
- package/src/typings/index.ts +0 -4
- package/src/utils/utils.ts +46 -0
- package/src/validate.ts +3 -6
- package/tests/gateway-contract.test.ts +180 -0
- package/tests/index.test.ts +283 -0
- package/tests/transaction-query.test.ts +207 -0
- package/tsconfig.esm.json +4 -2
- package/tsconfig.json +16 -6
- package/tsconfig.test.json +15 -0
- package/dist/commonjs/realtime/error.d.ts +0 -7
- package/dist/commonjs/realtime/error.js +0 -11
- package/dist/commonjs/realtime/listener.d.ts +0 -14
- package/dist/commonjs/realtime/listener.js +0 -17
- package/dist/commonjs/realtime/message.d.ts +0 -3
- package/dist/commonjs/realtime/message.js +0 -10
- package/dist/commonjs/realtime/snapshot.d.ts +0 -16
- package/dist/commonjs/realtime/snapshot.js +0 -42
- package/dist/commonjs/realtime/virtual-websocket-client.d.ts +0 -63
- package/dist/commonjs/realtime/virtual-websocket-client.js +0 -736
- package/dist/commonjs/realtime/websocket-client.d.ts +0 -78
- package/dist/commonjs/realtime/websocket-client.js +0 -567
- package/dist/commonjs/realtime/ws-event.d.ts +0 -126
- package/dist/commonjs/realtime/ws-event.js +0 -139
- package/dist/commonjs/transaction.d.ts +0 -42
- package/dist/commonjs/transaction.js +0 -126
- package/dist/esm/realtime/error.d.ts +0 -7
- package/dist/esm/realtime/error.js +0 -8
- package/dist/esm/realtime/listener.d.ts +0 -14
- package/dist/esm/realtime/listener.js +0 -14
- package/dist/esm/realtime/message.d.ts +0 -3
- package/dist/esm/realtime/message.js +0 -6
- package/dist/esm/realtime/snapshot.d.ts +0 -16
- package/dist/esm/realtime/snapshot.js +0 -39
- package/dist/esm/realtime/virtual-websocket-client.d.ts +0 -63
- package/dist/esm/realtime/virtual-websocket-client.js +0 -733
- package/dist/esm/realtime/websocket-client.d.ts +0 -78
- package/dist/esm/realtime/websocket-client.js +0 -564
- package/dist/esm/realtime/ws-event.d.ts +0 -126
- package/dist/esm/realtime/ws-event.js +0 -137
- package/dist/esm/transaction.d.ts +0 -42
- package/dist/esm/transaction.js +0 -121
- package/src/realtime/error.ts +0 -19
- package/src/realtime/listener.ts +0 -35
- package/src/realtime/message.ts +0 -14
- package/src/realtime/snapshot.ts +0 -60
- package/src/realtime/virtual-websocket-client.ts +0 -1195
- package/src/realtime/websocket-client.ts +0 -995
- package/src/realtime/ws-event.ts +0 -163
- package/src/transaction.ts +0 -190
|
@@ -1,995 +0,0 @@
|
|
|
1
|
-
import { VirtualWebSocketClient } from './virtual-websocket-client'
|
|
2
|
-
import { sleep } from '../utils/utils'
|
|
3
|
-
import { genRequestId } from './message'
|
|
4
|
-
import {
|
|
5
|
-
IDatabaseServiceContext,
|
|
6
|
-
IWatchOptions,
|
|
7
|
-
DBRealtimeListener
|
|
8
|
-
} from '../typings/index'
|
|
9
|
-
import {
|
|
10
|
-
IRequestMessage,
|
|
11
|
-
IResponseMessage,
|
|
12
|
-
IRequestMessagePingMsg,
|
|
13
|
-
IRequestMessageLoginMsg,
|
|
14
|
-
IResponseMessageLoginResMsg,
|
|
15
|
-
IRequestMessageLoginData
|
|
16
|
-
} from '../typings/realtime'
|
|
17
|
-
import {
|
|
18
|
-
CLOSE_EVENT_CODE,
|
|
19
|
-
CLOSE_EVENT_CODE_INFO,
|
|
20
|
-
getWSCloseError
|
|
21
|
-
} from './ws-event'
|
|
22
|
-
import { CloudSDKError, TimeoutError } from '../utils/error'
|
|
23
|
-
// import { CloudSDKError } from "./utils/error"
|
|
24
|
-
import { RealtimeErrorMessageError } from './error'
|
|
25
|
-
import { ERR_CODE } from '../config/error.config'
|
|
26
|
-
import { Db } from '../';
|
|
27
|
-
|
|
28
|
-
// =============== Realtime WebSocket Client (Internal) ====================
|
|
29
|
-
|
|
30
|
-
export interface IRealtimeWebSocketClientConstructorOptions {
|
|
31
|
-
maxReconnect?: number
|
|
32
|
-
reconnectInterval?: number
|
|
33
|
-
context: IDatabaseServiceContext
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface ISignature {
|
|
37
|
-
envId: string
|
|
38
|
-
secretVersion: number
|
|
39
|
-
signStr: string
|
|
40
|
-
wsUrl: string
|
|
41
|
-
expireTS: number
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface ILoginInfo {
|
|
45
|
-
loggedIn: boolean
|
|
46
|
-
loggingInPromise?: Promise<ILoginResult>
|
|
47
|
-
loginStartTS?: number
|
|
48
|
-
loginResult?: ILoginResult
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface ILoginResult {
|
|
52
|
-
envId: string
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface IWSSendOptions {
|
|
56
|
-
msg: IRequestMessage
|
|
57
|
-
waitResponse?: boolean
|
|
58
|
-
// when waitResponse is set to true, if skipOnMessage is true, general onMessage handler will be skipped
|
|
59
|
-
skipOnMessage?: boolean
|
|
60
|
-
timeout?: number
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export interface IWSWatchOptions extends IWatchOptions {
|
|
64
|
-
envId?: string
|
|
65
|
-
collectionName: string
|
|
66
|
-
query: string
|
|
67
|
-
limit?: number
|
|
68
|
-
orderBy?: Record<string, string>
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
interface IResolveReject {
|
|
72
|
-
resolve: (value?: any | PromiseLike<any> | undefined) => void
|
|
73
|
-
reject: (reason?: any) => void
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
interface IResponseWaitSpec extends IResolveReject {
|
|
77
|
-
skipOnMessage?: boolean
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
interface IWsSign {
|
|
81
|
-
signStr: string,
|
|
82
|
-
wsUrl: string,
|
|
83
|
-
secretVersion: string
|
|
84
|
-
envId: string
|
|
85
|
-
expiredTs: number
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const WS_READY_STATE = {
|
|
89
|
-
CONNECTING: 0,
|
|
90
|
-
OPEN: 1,
|
|
91
|
-
CLOSING: 2,
|
|
92
|
-
CLOSED: 3
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const MAX_RTT_OBSERVED = 3
|
|
96
|
-
const DEFAULT_EXPECTED_EVENT_WAIT_TIME = 5000
|
|
97
|
-
const DEFAULT_UNTRUSTED_RTT_THRESHOLD = 10000
|
|
98
|
-
const DEFAULT_MAX_RECONNECT = 5
|
|
99
|
-
const DEFAULT_WS_RECONNECT_INTERVAL = 10000
|
|
100
|
-
// const DEFAULT_WS_RECONNECT_MAX_VALID_INTERVAL = 3 * 60 * 1000
|
|
101
|
-
const DEFAULT_PING_FAIL_TOLERANCE = 2
|
|
102
|
-
const DEFAULT_PONG_MISS_TOLERANCE = 2
|
|
103
|
-
const DEFAULT_LOGIN_TIMEOUT = 5000
|
|
104
|
-
|
|
105
|
-
export class RealtimeWebSocketClient {
|
|
106
|
-
private _virtualWSClient: Set<VirtualWebSocketClient> = new Set()
|
|
107
|
-
// after listener initWatch, the listener has the queryID and can store it here
|
|
108
|
-
private _queryIdClientMap: Map<string, VirtualWebSocketClient> = new Map()
|
|
109
|
-
private _watchIdClientMap: Map<string, VirtualWebSocketClient> = new Map()
|
|
110
|
-
private _maxReconnect: number
|
|
111
|
-
// private _availableRetries: number
|
|
112
|
-
private _reconnectInterval: number
|
|
113
|
-
private _context: IDatabaseServiceContext
|
|
114
|
-
// private _ws?: WXNS.Socket.ISocketTask
|
|
115
|
-
private _ws?: any
|
|
116
|
-
private _lastPingSendTS?: number
|
|
117
|
-
private _pingFailed: number = 0
|
|
118
|
-
private _pongMissed: number = 0
|
|
119
|
-
private _pingTimeoutId?: number
|
|
120
|
-
private _pongTimeoutId?: number
|
|
121
|
-
private _logins: Map<string /* envId */, ILoginInfo> = new Map()
|
|
122
|
-
// private _loginInfo: ILoginInfo
|
|
123
|
-
// private _signatures: Map<string /* envId */, ISignature> = new Map()
|
|
124
|
-
private _wsInitPromise?: Promise<void>
|
|
125
|
-
private _wsReadySubsribers: IResolveReject[] = []
|
|
126
|
-
private _wsResponseWait: Map<
|
|
127
|
-
string /* requestId */,
|
|
128
|
-
IResponseWaitSpec
|
|
129
|
-
> = new Map()
|
|
130
|
-
private _rttObserved: number[] = []
|
|
131
|
-
private _reconnectState: boolean
|
|
132
|
-
// obtained from the first getSignature with no envId provided
|
|
133
|
-
// private _defaultEnvId?: string
|
|
134
|
-
private _wsSign: IWsSign
|
|
135
|
-
|
|
136
|
-
constructor(options: IRealtimeWebSocketClientConstructorOptions) {
|
|
137
|
-
this._maxReconnect = options.maxReconnect || DEFAULT_MAX_RECONNECT
|
|
138
|
-
// this._availableRetries = this._maxReconnect
|
|
139
|
-
this._reconnectInterval =
|
|
140
|
-
options.reconnectInterval || DEFAULT_WS_RECONNECT_INTERVAL
|
|
141
|
-
this._context = options.context
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
private initWebSocketConnection = async (
|
|
145
|
-
reconnect: boolean,
|
|
146
|
-
availableRetries: number = this._maxReconnect
|
|
147
|
-
): Promise<void> => {
|
|
148
|
-
// 当前处于正在重连中的状态
|
|
149
|
-
if (reconnect && this._reconnectState) {
|
|
150
|
-
return // 忽略
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (reconnect) {
|
|
154
|
-
this._reconnectState = true // 重连状态开始
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (this._wsInitPromise) {
|
|
158
|
-
// there already exists a websocket initiation, just wait for it
|
|
159
|
-
return this._wsInitPromise
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// if (process.env.DEBUG) {
|
|
163
|
-
// console.log(
|
|
164
|
-
// `[realtime] initWebSocketConnection reconnect ${reconnect} availableRetries ${availableRetries}`
|
|
165
|
-
// )
|
|
166
|
-
// }
|
|
167
|
-
|
|
168
|
-
if (reconnect) {
|
|
169
|
-
this.pauseClients()
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
this.close(CLOSE_EVENT_CODE.ReconnectWebSocket)
|
|
173
|
-
|
|
174
|
-
this._wsInitPromise = new Promise<void>(async (resolve, reject) => {
|
|
175
|
-
try {
|
|
176
|
-
// if (process.env.DEBUG) {
|
|
177
|
-
// console.log(
|
|
178
|
-
// '[realtime] initWebSocketConnection start throwErrorIfNetworkOffline'
|
|
179
|
-
// )
|
|
180
|
-
// }
|
|
181
|
-
|
|
182
|
-
// 暂不检查网络态
|
|
183
|
-
// await throwErrorIfNetworkOffline()
|
|
184
|
-
|
|
185
|
-
// if (process.env.DEBUG) {
|
|
186
|
-
// console.log('[realtime] initWebSocketConnection start getSignature')
|
|
187
|
-
// }
|
|
188
|
-
|
|
189
|
-
// const signature = await this.getSignature()
|
|
190
|
-
const wsSign = await this.getWsSign()
|
|
191
|
-
|
|
192
|
-
// if (process.env.DEBUG) {
|
|
193
|
-
// console.log('[realtime] initWebSocketConnection getSignature success')
|
|
194
|
-
// console.log('[realtime] initWebSocketConnection start connectSocket')
|
|
195
|
-
// }
|
|
196
|
-
|
|
197
|
-
await new Promise(success => {
|
|
198
|
-
// this._ws = getSDK(this._context.identifiers)
|
|
199
|
-
// ._socketSkipCheckDomainFactory()
|
|
200
|
-
// .connectSocket({
|
|
201
|
-
// url: signature.wsUrl,
|
|
202
|
-
// header: {
|
|
203
|
-
// "content-type": "application/json"
|
|
204
|
-
// },
|
|
205
|
-
// success: () => success(),
|
|
206
|
-
// fail
|
|
207
|
-
// })
|
|
208
|
-
|
|
209
|
-
const url = wsSign.wsUrl || 'wss://tcb-ws.tencentcloudapi.com';
|
|
210
|
-
this._ws = Db.wsClass ? new Db.wsClass(url) : new WebSocket(url)
|
|
211
|
-
success()
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
if(this._ws.connect){
|
|
215
|
-
await this._ws.connect()
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// if (process.env.DEBUG) {
|
|
219
|
-
// console.log(
|
|
220
|
-
// '[realtime] initWebSocketConnection connectSocket successfully fired'
|
|
221
|
-
// )
|
|
222
|
-
// }
|
|
223
|
-
|
|
224
|
-
await this.initWebSocketEvent()
|
|
225
|
-
resolve()
|
|
226
|
-
|
|
227
|
-
if (reconnect) {
|
|
228
|
-
this.resumeClients()
|
|
229
|
-
this._reconnectState = false // 重连状态结束
|
|
230
|
-
}
|
|
231
|
-
} catch (e) {
|
|
232
|
-
// if (process.env.DEBUG) {
|
|
233
|
-
console.error('[realtime] initWebSocketConnection connect fail', e)
|
|
234
|
-
// }
|
|
235
|
-
|
|
236
|
-
if (availableRetries > 0) {
|
|
237
|
-
// this is an optimization, in case of network offline, we don't need to stubbornly sleep for sometime,
|
|
238
|
-
// we only need to wait for the network to be back online, this ensures minimum downtime
|
|
239
|
-
// const { isConnected } = await getNetworkStatus()
|
|
240
|
-
const isConnected = true
|
|
241
|
-
|
|
242
|
-
// if (process.env.DEBUG) {
|
|
243
|
-
// console.log(
|
|
244
|
-
// '[realtime] initWebSocketConnection waiting for network online'
|
|
245
|
-
// )
|
|
246
|
-
// }
|
|
247
|
-
|
|
248
|
-
// auto wait until network online, cause' it would be meaningless to reconnect while network is offline
|
|
249
|
-
|
|
250
|
-
// await onceNetworkOnline()
|
|
251
|
-
|
|
252
|
-
// COMPATIBILITY: wait for ide state update
|
|
253
|
-
// if (isDevTools()) {
|
|
254
|
-
// await sleep(0)
|
|
255
|
-
// }
|
|
256
|
-
|
|
257
|
-
// if (process.env.DEBUG) {
|
|
258
|
-
// console.log('[realtime] initWebSocketConnection network online')
|
|
259
|
-
// }
|
|
260
|
-
|
|
261
|
-
this._wsInitPromise = undefined
|
|
262
|
-
|
|
263
|
-
if (isConnected) {
|
|
264
|
-
// if (process.env.DEBUG) {
|
|
265
|
-
// console.log(
|
|
266
|
-
// `[realtime] initWebSocketConnection sleep ${this._reconnectInterval}ms`
|
|
267
|
-
// )
|
|
268
|
-
// }
|
|
269
|
-
await sleep(this._reconnectInterval)
|
|
270
|
-
if (reconnect) {
|
|
271
|
-
this._reconnectState = false // 重连异常也算重连状态结束
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
resolve(this.initWebSocketConnection(reconnect, availableRetries - 1))
|
|
276
|
-
} else {
|
|
277
|
-
reject(e)
|
|
278
|
-
|
|
279
|
-
if (reconnect) {
|
|
280
|
-
this.closeAllClients(
|
|
281
|
-
new CloudSDKError({
|
|
282
|
-
errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_RECONNECT_WATCH_FAIL as string,
|
|
283
|
-
errMsg: e
|
|
284
|
-
})
|
|
285
|
-
)
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
// let success = false
|
|
292
|
-
|
|
293
|
-
try {
|
|
294
|
-
await this._wsInitPromise
|
|
295
|
-
// success = true
|
|
296
|
-
this._wsReadySubsribers.forEach(({ resolve }) => resolve())
|
|
297
|
-
} catch (e) {
|
|
298
|
-
this._wsReadySubsribers.forEach(({ reject }) => reject())
|
|
299
|
-
} finally {
|
|
300
|
-
this._wsInitPromise = undefined
|
|
301
|
-
this._wsReadySubsribers = []
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// if (process.env.DEBUG) {
|
|
305
|
-
// console.log(
|
|
306
|
-
// `[realtime] initWebSocketConnection ${success ? 'success' : 'fail'}`
|
|
307
|
-
// )
|
|
308
|
-
// }
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
private initWebSocketEvent = () =>
|
|
312
|
-
new Promise<void>((resolve, reject) => {
|
|
313
|
-
if (!this._ws) {
|
|
314
|
-
throw new Error('can not initWebSocketEvent, ws not exists')
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
let wsOpened = false
|
|
318
|
-
|
|
319
|
-
this._ws.onopen = event => {
|
|
320
|
-
// this._ws.onOpen(() => {
|
|
321
|
-
// this._ws.on("open", () => {
|
|
322
|
-
// this._context.debug &&
|
|
323
|
-
console.warn('[realtime] ws event: open', event)
|
|
324
|
-
wsOpened = true
|
|
325
|
-
resolve()
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
this._ws.onerror = event => {
|
|
329
|
-
// this._ws.on("error", error => {
|
|
330
|
-
// this._ws.onError(error => {
|
|
331
|
-
// all logins are invalid after disconnection
|
|
332
|
-
this._logins = new Map()
|
|
333
|
-
|
|
334
|
-
// error写进file
|
|
335
|
-
|
|
336
|
-
if (!wsOpened) {
|
|
337
|
-
// this._context.debug &&
|
|
338
|
-
console.error('[realtime] ws open failed with ws event: error', event)
|
|
339
|
-
// writeToFile(
|
|
340
|
-
// "wserror.txt",
|
|
341
|
-
// `${
|
|
342
|
-
// this.specialNumber
|
|
343
|
-
// } [realtime] ws open failed with ws event: error ${error} \n`
|
|
344
|
-
// )
|
|
345
|
-
|
|
346
|
-
reject(event)
|
|
347
|
-
} else {
|
|
348
|
-
// this._context.debug &&
|
|
349
|
-
console.error('[realtime] ws event: error', event)
|
|
350
|
-
|
|
351
|
-
// writeToFile(
|
|
352
|
-
// "wserror.txt",
|
|
353
|
-
// `${this.specialNumber} [realtime] ws event: error ${error} \n`
|
|
354
|
-
// )
|
|
355
|
-
|
|
356
|
-
this.clearHeartbeat()
|
|
357
|
-
this._virtualWSClient.forEach(client =>
|
|
358
|
-
client.closeWithError(
|
|
359
|
-
new CloudSDKError({
|
|
360
|
-
errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_WEBSOCKET_CONNECTION_ERROR as string,
|
|
361
|
-
errMsg: event
|
|
362
|
-
})
|
|
363
|
-
)
|
|
364
|
-
)
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
// TODO: reconnect
|
|
369
|
-
this._ws.onclose = closeEvent => {
|
|
370
|
-
// this._ws.on("close", (closeEvent, closereason) => {
|
|
371
|
-
// this._ws.onClose(closeEvent => {
|
|
372
|
-
// if (process.env.DEBUG) {
|
|
373
|
-
console.warn('[realtime] ws event: close', closeEvent)
|
|
374
|
-
// }
|
|
375
|
-
|
|
376
|
-
// writeToFile(
|
|
377
|
-
// "wsclose.txt",
|
|
378
|
-
// `${
|
|
379
|
-
// this.specialNumber
|
|
380
|
-
// } [realtime] ws event: close ${closeEvent} ${closereason} \n`
|
|
381
|
-
// )
|
|
382
|
-
|
|
383
|
-
// all logins are invalid after disconnection
|
|
384
|
-
this._logins = new Map()
|
|
385
|
-
|
|
386
|
-
this.clearHeartbeat()
|
|
387
|
-
switch (closeEvent.code) {
|
|
388
|
-
case CLOSE_EVENT_CODE.ReconnectWebSocket: {
|
|
389
|
-
// just ignore
|
|
390
|
-
break
|
|
391
|
-
}
|
|
392
|
-
case CLOSE_EVENT_CODE.NoRealtimeListeners: {
|
|
393
|
-
// quit
|
|
394
|
-
break
|
|
395
|
-
}
|
|
396
|
-
case CLOSE_EVENT_CODE.HeartbeatPingError:
|
|
397
|
-
case CLOSE_EVENT_CODE.HeartbeatPongTimeoutError:
|
|
398
|
-
case CLOSE_EVENT_CODE.NormalClosure:
|
|
399
|
-
case CLOSE_EVENT_CODE.AbnormalClosure: {
|
|
400
|
-
// Normal Closure and Abnormal Closure:
|
|
401
|
-
// expected closure, most likely dispatched by wechat client,
|
|
402
|
-
// since this is the status code dispatched in case of network failure,
|
|
403
|
-
// we should retry
|
|
404
|
-
|
|
405
|
-
if (this._maxReconnect > 0) {
|
|
406
|
-
// if (this._availableRetries > 0) {
|
|
407
|
-
this.initWebSocketConnection(true, this._maxReconnect)
|
|
408
|
-
} else {
|
|
409
|
-
this.closeAllClients(getWSCloseError(closeEvent.code))
|
|
410
|
-
}
|
|
411
|
-
break
|
|
412
|
-
}
|
|
413
|
-
case CLOSE_EVENT_CODE.NoAuthentication: {
|
|
414
|
-
this.closeAllClients(
|
|
415
|
-
getWSCloseError(closeEvent.code, closeEvent.reason)
|
|
416
|
-
)
|
|
417
|
-
break
|
|
418
|
-
}
|
|
419
|
-
default: {
|
|
420
|
-
// we should retry by default
|
|
421
|
-
if (this._maxReconnect > 0) {
|
|
422
|
-
// if (this._availableRetries > 0) {
|
|
423
|
-
this.initWebSocketConnection(true, this._maxReconnect)
|
|
424
|
-
} else {
|
|
425
|
-
this.closeAllClients(getWSCloseError(closeEvent.code))
|
|
426
|
-
}
|
|
427
|
-
// console.warn(`[realtime] unrecognize ws close event`, closeEvent)
|
|
428
|
-
// this.closeAllClients(getWSCloseError(closeEvent.code))
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
this._ws.onmessage = res => {
|
|
434
|
-
// this._ws.on("message", res => {
|
|
435
|
-
// this._ws.onMessage(res => {
|
|
436
|
-
// const rawMsg = res.data
|
|
437
|
-
const rawMsg = res.data
|
|
438
|
-
|
|
439
|
-
// reset & restart heartbeat
|
|
440
|
-
this.heartbeat()
|
|
441
|
-
|
|
442
|
-
let msg: IResponseMessage
|
|
443
|
-
|
|
444
|
-
try {
|
|
445
|
-
msg = JSON.parse(rawMsg as string)
|
|
446
|
-
} catch (e) {
|
|
447
|
-
throw new Error(`[realtime] onMessage parse res.data error: ${e}`)
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// console.log(
|
|
451
|
-
// `[realtime] onMessage ${
|
|
452
|
-
// msg.msgType
|
|
453
|
-
// } (${new Date().toLocaleString()})`,
|
|
454
|
-
// msg
|
|
455
|
-
// )
|
|
456
|
-
|
|
457
|
-
if (msg.msgType === 'ERROR') {
|
|
458
|
-
// 找到当前监听,并将error返回
|
|
459
|
-
let virtualWatch = null
|
|
460
|
-
this._virtualWSClient.forEach(item => {
|
|
461
|
-
if (item.watchId === msg.watchId) {
|
|
462
|
-
virtualWatch = item
|
|
463
|
-
}
|
|
464
|
-
})
|
|
465
|
-
|
|
466
|
-
if (virtualWatch) {
|
|
467
|
-
virtualWatch.listener.onError(msg)
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
const responseWaitSpec = this._wsResponseWait.get(msg.requestId)
|
|
472
|
-
if (responseWaitSpec) {
|
|
473
|
-
try {
|
|
474
|
-
if (msg.msgType === 'ERROR') {
|
|
475
|
-
responseWaitSpec.reject(new RealtimeErrorMessageError(msg))
|
|
476
|
-
} else {
|
|
477
|
-
responseWaitSpec.resolve(msg)
|
|
478
|
-
}
|
|
479
|
-
} catch (e) {
|
|
480
|
-
// this._context.debug &&
|
|
481
|
-
console.error(
|
|
482
|
-
'ws onMessage responseWaitSpec.resolve(msg) errored:',
|
|
483
|
-
e
|
|
484
|
-
)
|
|
485
|
-
} finally {
|
|
486
|
-
this._wsResponseWait.delete(msg.requestId)
|
|
487
|
-
}
|
|
488
|
-
if (responseWaitSpec.skipOnMessage) {
|
|
489
|
-
return
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
if (msg.msgType === 'PONG') {
|
|
494
|
-
if (this._lastPingSendTS) {
|
|
495
|
-
const rtt = Date.now() - this._lastPingSendTS
|
|
496
|
-
if (rtt > DEFAULT_UNTRUSTED_RTT_THRESHOLD) {
|
|
497
|
-
// this._context.debug &&
|
|
498
|
-
console.warn(`[realtime] untrusted rtt observed: ${rtt}`)
|
|
499
|
-
return
|
|
500
|
-
}
|
|
501
|
-
if (this._rttObserved.length >= MAX_RTT_OBSERVED) {
|
|
502
|
-
this._rttObserved.splice(
|
|
503
|
-
0,
|
|
504
|
-
this._rttObserved.length - MAX_RTT_OBSERVED + 1
|
|
505
|
-
)
|
|
506
|
-
}
|
|
507
|
-
this._rttObserved.push(rtt)
|
|
508
|
-
}
|
|
509
|
-
return
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
let client = msg.watchId && this._watchIdClientMap.get(msg.watchId)
|
|
513
|
-
if (client) {
|
|
514
|
-
client.onMessage(msg)
|
|
515
|
-
} else {
|
|
516
|
-
// TODO, this is a temporary fix done for server
|
|
517
|
-
// if (process.env.DEBUG) {
|
|
518
|
-
console.error(
|
|
519
|
-
`[realtime] no realtime listener found responsible for watchId ${msg.watchId}: `,
|
|
520
|
-
msg
|
|
521
|
-
)
|
|
522
|
-
// }
|
|
523
|
-
switch (msg.msgType) {
|
|
524
|
-
case 'INIT_EVENT':
|
|
525
|
-
case 'NEXT_EVENT':
|
|
526
|
-
case 'CHECK_EVENT': {
|
|
527
|
-
client = this._queryIdClientMap.get(msg.msgData.queryID)
|
|
528
|
-
if (client) {
|
|
529
|
-
client.onMessage(msg)
|
|
530
|
-
}
|
|
531
|
-
break
|
|
532
|
-
}
|
|
533
|
-
default: {
|
|
534
|
-
for (const [,client] of this._watchIdClientMap) {
|
|
535
|
-
// console.log('watchid*****', watchId)
|
|
536
|
-
client.onMessage(msg)
|
|
537
|
-
break
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
this.heartbeat()
|
|
545
|
-
})
|
|
546
|
-
|
|
547
|
-
private isWSConnected = (): boolean => {
|
|
548
|
-
return Boolean(this._ws && this._ws.readyState === WS_READY_STATE.OPEN)
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
private onceWSConnected = async (): Promise<void> => {
|
|
552
|
-
if (this.isWSConnected()) {
|
|
553
|
-
return
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
if (this._wsInitPromise) {
|
|
557
|
-
return this._wsInitPromise
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
return new Promise<void>((resolve, reject) => {
|
|
561
|
-
this._wsReadySubsribers.push({
|
|
562
|
-
resolve,
|
|
563
|
-
reject
|
|
564
|
-
})
|
|
565
|
-
})
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
private webLogin = async (
|
|
569
|
-
envId?: string,
|
|
570
|
-
refresh?: boolean
|
|
571
|
-
): Promise<any> => {
|
|
572
|
-
if (!refresh) {
|
|
573
|
-
// let loginInfo = this._loginInfo
|
|
574
|
-
if (envId) {
|
|
575
|
-
const loginInfo = this._logins.get(envId)
|
|
576
|
-
if (loginInfo) {
|
|
577
|
-
if (loginInfo.loggedIn && loginInfo.loginResult) {
|
|
578
|
-
// if (process.env.DEBUG) {
|
|
579
|
-
// console.log('[realtime] login: already logged in')
|
|
580
|
-
// }
|
|
581
|
-
return loginInfo.loginResult
|
|
582
|
-
} else if (loginInfo.loggingInPromise) {
|
|
583
|
-
return loginInfo.loggingInPromise
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
} else {
|
|
587
|
-
const emptyEnvLoginInfo = this._logins.get('')
|
|
588
|
-
if (emptyEnvLoginInfo && emptyEnvLoginInfo.loggingInPromise) {
|
|
589
|
-
return emptyEnvLoginInfo.loggingInPromise
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
// console.log('[realtime] login: logging in')
|
|
594
|
-
|
|
595
|
-
const promise = new Promise<ILoginResult>(async (resolve, reject) => {
|
|
596
|
-
try {
|
|
597
|
-
// const signature = await this.getSignature(envId, refresh)
|
|
598
|
-
|
|
599
|
-
const wsSign = await this.getWsSign()
|
|
600
|
-
|
|
601
|
-
// const wxVersion = getWXVersion()
|
|
602
|
-
const msgData: IRequestMessageLoginData = {
|
|
603
|
-
envId: wsSign.envId || '',
|
|
604
|
-
accessToken: '', // 已废弃字段
|
|
605
|
-
// signStr: signature.signStr,
|
|
606
|
-
// secretVersion: signature.secretVersion,
|
|
607
|
-
referrer: 'web',
|
|
608
|
-
sdkVersion: '',
|
|
609
|
-
dataVersion: Db.dataVersion||''
|
|
610
|
-
}
|
|
611
|
-
const loginMsg: IRequestMessageLoginMsg = {
|
|
612
|
-
watchId: undefined,
|
|
613
|
-
requestId: genRequestId(),
|
|
614
|
-
msgType: 'LOGIN',
|
|
615
|
-
msgData,
|
|
616
|
-
exMsgData: {
|
|
617
|
-
runtime: Db.runtime,
|
|
618
|
-
signStr: wsSign.signStr,
|
|
619
|
-
secretVersion: wsSign.secretVersion
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
const loginResMsg = await this.send<IResponseMessageLoginResMsg>({
|
|
623
|
-
msg: loginMsg,
|
|
624
|
-
waitResponse: true,
|
|
625
|
-
skipOnMessage: true,
|
|
626
|
-
timeout: DEFAULT_LOGIN_TIMEOUT
|
|
627
|
-
})
|
|
628
|
-
|
|
629
|
-
if (!loginResMsg.msgData.code) {
|
|
630
|
-
// login success
|
|
631
|
-
resolve({
|
|
632
|
-
envId: wsSign.envId
|
|
633
|
-
})
|
|
634
|
-
} else {
|
|
635
|
-
// login failed
|
|
636
|
-
reject(
|
|
637
|
-
new Error(
|
|
638
|
-
`${loginResMsg.msgData.code} ${loginResMsg.msgData.message}`
|
|
639
|
-
)
|
|
640
|
-
)
|
|
641
|
-
}
|
|
642
|
-
} catch (e) {
|
|
643
|
-
reject(e)
|
|
644
|
-
}
|
|
645
|
-
})
|
|
646
|
-
|
|
647
|
-
// let loginInfo = this._loginInfo
|
|
648
|
-
let loginInfo = envId && this._logins.get(envId)
|
|
649
|
-
|
|
650
|
-
const loginStartTS = Date.now()
|
|
651
|
-
|
|
652
|
-
if (loginInfo) {
|
|
653
|
-
loginInfo.loggedIn = false
|
|
654
|
-
loginInfo.loggingInPromise = promise
|
|
655
|
-
loginInfo.loginStartTS = loginStartTS
|
|
656
|
-
} else {
|
|
657
|
-
loginInfo = {
|
|
658
|
-
loggedIn: false,
|
|
659
|
-
loggingInPromise: promise,
|
|
660
|
-
loginStartTS
|
|
661
|
-
}
|
|
662
|
-
// this._loginInfo = loginInfo
|
|
663
|
-
this._logins.set(envId || '', loginInfo)
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// try {
|
|
667
|
-
// const loginResult = await promise
|
|
668
|
-
// loginInfo.loggedIn = true
|
|
669
|
-
// loginInfo.loggingInPromise = undefined
|
|
670
|
-
// loginInfo.loginStartTS = undefined
|
|
671
|
-
// loginInfo.loginResult = loginResult
|
|
672
|
-
// return loginResult
|
|
673
|
-
// } catch (e) {
|
|
674
|
-
// loginInfo.loggedIn = false
|
|
675
|
-
// loginInfo.loggingInPromise = undefined
|
|
676
|
-
// loginInfo.loginStartTS = undefined
|
|
677
|
-
// loginInfo.loginResult = undefined
|
|
678
|
-
// throw e
|
|
679
|
-
// }
|
|
680
|
-
|
|
681
|
-
try {
|
|
682
|
-
const loginResult = await promise
|
|
683
|
-
const curLoginInfo = envId && this._logins.get(envId)
|
|
684
|
-
if (
|
|
685
|
-
curLoginInfo &&
|
|
686
|
-
curLoginInfo === loginInfo &&
|
|
687
|
-
curLoginInfo.loginStartTS === loginStartTS
|
|
688
|
-
) {
|
|
689
|
-
loginInfo.loggedIn = true
|
|
690
|
-
loginInfo.loggingInPromise = undefined
|
|
691
|
-
loginInfo.loginStartTS = undefined
|
|
692
|
-
loginInfo.loginResult = loginResult
|
|
693
|
-
return loginResult
|
|
694
|
-
} else if (curLoginInfo) {
|
|
695
|
-
if (curLoginInfo.loggedIn && curLoginInfo.loginResult) {
|
|
696
|
-
return curLoginInfo.loginResult
|
|
697
|
-
} else if (curLoginInfo.loggingInPromise) {
|
|
698
|
-
return curLoginInfo.loggingInPromise
|
|
699
|
-
} else {
|
|
700
|
-
throw new Error('ws unexpected login info')
|
|
701
|
-
}
|
|
702
|
-
} else {
|
|
703
|
-
throw new Error('ws login info reset')
|
|
704
|
-
}
|
|
705
|
-
} catch (e) {
|
|
706
|
-
loginInfo.loggedIn = false
|
|
707
|
-
loginInfo.loggingInPromise = undefined
|
|
708
|
-
loginInfo.loginStartTS = undefined
|
|
709
|
-
loginInfo.loginResult = undefined
|
|
710
|
-
throw e
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
private getWsSign = async (): Promise<IWsSign> => {
|
|
715
|
-
if (this._wsSign && this._wsSign.expiredTs > Date.now()) {
|
|
716
|
-
return this._wsSign
|
|
717
|
-
}
|
|
718
|
-
const expiredTs = Date.now() + 60000
|
|
719
|
-
const res = await this._context.appConfig.request.send('auth.wsWebSign', {runtime: Db.runtime})
|
|
720
|
-
|
|
721
|
-
if (res.code) {
|
|
722
|
-
throw new Error(`[tcb-js-sdk] 获取实时数据推送登录票据失败: ${res.code}`)
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
if (res.data) {
|
|
726
|
-
const {signStr, wsUrl, secretVersion, envId} = res.data
|
|
727
|
-
return {
|
|
728
|
-
signStr,
|
|
729
|
-
wsUrl,
|
|
730
|
-
secretVersion,
|
|
731
|
-
envId,
|
|
732
|
-
expiredTs
|
|
733
|
-
}
|
|
734
|
-
} else {
|
|
735
|
-
throw new Error('[tcb-js-sdk] 获取实时数据推送登录票据失败')
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
private getWaitExpectedTimeoutLength = () => {
|
|
740
|
-
if (!this._rttObserved.length) {
|
|
741
|
-
return DEFAULT_EXPECTED_EVENT_WAIT_TIME
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
// 1.5 * RTT
|
|
745
|
-
return (
|
|
746
|
-
(this._rttObserved.reduce((acc, cur) => acc + cur) /
|
|
747
|
-
this._rttObserved.length) *
|
|
748
|
-
1.5
|
|
749
|
-
)
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
private heartbeat(immediate?: boolean) {
|
|
753
|
-
this.clearHeartbeat()
|
|
754
|
-
// @ts-ignore
|
|
755
|
-
this._pingTimeoutId = setTimeout(
|
|
756
|
-
async () => {
|
|
757
|
-
try {
|
|
758
|
-
if (!this._ws || this._ws.readyState !== WS_READY_STATE.OPEN) {
|
|
759
|
-
// no need to ping
|
|
760
|
-
return
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
this._lastPingSendTS = Date.now()
|
|
764
|
-
await this.ping()
|
|
765
|
-
this._pingFailed = 0
|
|
766
|
-
|
|
767
|
-
// @ts-ignore
|
|
768
|
-
this._pongTimeoutId = setTimeout(() => {
|
|
769
|
-
console.error('pong timed out')
|
|
770
|
-
if (this._pongMissed < DEFAULT_PONG_MISS_TOLERANCE) {
|
|
771
|
-
this._pongMissed++
|
|
772
|
-
this.heartbeat(true)
|
|
773
|
-
} else {
|
|
774
|
-
// logical perceived connection lost, even though websocket did not receive error or close event
|
|
775
|
-
this.initWebSocketConnection(true)
|
|
776
|
-
}
|
|
777
|
-
}, this._context.appConfig.realtimePongWaitTimeout)
|
|
778
|
-
} catch (e) {
|
|
779
|
-
if (this._pingFailed < DEFAULT_PING_FAIL_TOLERANCE) {
|
|
780
|
-
this._pingFailed++
|
|
781
|
-
this.heartbeat()
|
|
782
|
-
} else {
|
|
783
|
-
this.close(CLOSE_EVENT_CODE.HeartbeatPingError)
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
},
|
|
787
|
-
immediate ? 0 : this._context.appConfig.realtimePingInterval
|
|
788
|
-
)
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
clearHeartbeat() {
|
|
792
|
-
this._pingTimeoutId && clearTimeout(this._pingTimeoutId)
|
|
793
|
-
this._pongTimeoutId && clearTimeout(this._pongTimeoutId)
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
private ping = async () => {
|
|
797
|
-
const msg: IRequestMessagePingMsg = {
|
|
798
|
-
watchId: undefined,
|
|
799
|
-
requestId: genRequestId(),
|
|
800
|
-
msgType: 'PING',
|
|
801
|
-
msgData: null
|
|
802
|
-
}
|
|
803
|
-
await this.send({
|
|
804
|
-
msg
|
|
805
|
-
})
|
|
806
|
-
// console.log('ping sent')
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
send = async <T = any>(opts: IWSSendOptions): Promise<T> =>
|
|
810
|
-
new Promise<T>(async (_resolve, _reject) => {
|
|
811
|
-
let timeoutId: number
|
|
812
|
-
let _hasResolved = false
|
|
813
|
-
let _hasRejected = false
|
|
814
|
-
|
|
815
|
-
const resolve: typeof _resolve = (
|
|
816
|
-
value?: T | PromiseLike<T> | undefined
|
|
817
|
-
) => {
|
|
818
|
-
_hasResolved = true
|
|
819
|
-
timeoutId && clearTimeout(timeoutId)
|
|
820
|
-
_resolve(value)
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
const reject: typeof _reject = (error: any) => {
|
|
824
|
-
_hasRejected = true
|
|
825
|
-
timeoutId && clearTimeout(timeoutId)
|
|
826
|
-
_reject(error)
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
if (opts.timeout) {
|
|
830
|
-
// @ts-ignore
|
|
831
|
-
timeoutId = setTimeout(async () => {
|
|
832
|
-
if (!_hasResolved || !_hasRejected) {
|
|
833
|
-
// wait another immediate timeout to allow the success/fail callback to be invoked if ws has already got the result,
|
|
834
|
-
// this is because the timer is registered before ws.send
|
|
835
|
-
await sleep(0)
|
|
836
|
-
if (!_hasResolved || !_hasRejected) {
|
|
837
|
-
reject(new TimeoutError('wsclient.send timedout'))
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
}, opts.timeout)
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
try {
|
|
844
|
-
// if (this._context.debug) {
|
|
845
|
-
// console.log(`[realtime] ws send (${new Date()}): `, opts)
|
|
846
|
-
// console.log(
|
|
847
|
-
// `[realtime] ws send ${
|
|
848
|
-
// opts.msg.msgType
|
|
849
|
-
// } (${new Date().toLocaleString()}): `,
|
|
850
|
-
// opts
|
|
851
|
-
// )
|
|
852
|
-
// }
|
|
853
|
-
|
|
854
|
-
if (this._wsInitPromise) {
|
|
855
|
-
await this._wsInitPromise
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
if (!this._ws) {
|
|
859
|
-
reject(
|
|
860
|
-
new Error(
|
|
861
|
-
'invalid state: ws connection not exists, can not send message'
|
|
862
|
-
)
|
|
863
|
-
)
|
|
864
|
-
return
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
if (this._ws.readyState !== WS_READY_STATE.OPEN) {
|
|
868
|
-
reject(
|
|
869
|
-
new Error(
|
|
870
|
-
`ws readyState invalid: ${this._ws.readyState}, can not send message`
|
|
871
|
-
)
|
|
872
|
-
)
|
|
873
|
-
return
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
if (opts.waitResponse) {
|
|
877
|
-
this._wsResponseWait.set(opts.msg.requestId, {
|
|
878
|
-
resolve,
|
|
879
|
-
reject,
|
|
880
|
-
skipOnMessage: opts.skipOnMessage
|
|
881
|
-
} as IResponseWaitSpec)
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
// console.log('send msg:', opts.msg)
|
|
885
|
-
try {
|
|
886
|
-
await this._ws.send(JSON.stringify(opts.msg))
|
|
887
|
-
if (!opts.waitResponse) {
|
|
888
|
-
resolve()
|
|
889
|
-
}
|
|
890
|
-
} catch (err) {
|
|
891
|
-
if (err) {
|
|
892
|
-
reject(err)
|
|
893
|
-
if (opts.waitResponse) {
|
|
894
|
-
this._wsResponseWait.delete(opts.msg.requestId)
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
// this._ws.send(JSON.stringify(opts.msg), err => {
|
|
899
|
-
// if (err) {
|
|
900
|
-
// reject(err)
|
|
901
|
-
// if (opts.waitResponse) {
|
|
902
|
-
// this._wsResponseWait.delete(opts.msg.requestId)
|
|
903
|
-
// }
|
|
904
|
-
// return
|
|
905
|
-
// }
|
|
906
|
-
|
|
907
|
-
// if (!opts.waitResponse) {
|
|
908
|
-
// resolve()
|
|
909
|
-
// }
|
|
910
|
-
// })
|
|
911
|
-
|
|
912
|
-
// this._ws.send({
|
|
913
|
-
// data: JSON.stringify(opts.msg),
|
|
914
|
-
// success: res => {
|
|
915
|
-
// if (!opts.waitResponse) {
|
|
916
|
-
// resolve(res)
|
|
917
|
-
// }
|
|
918
|
-
// },
|
|
919
|
-
// fail: e => {
|
|
920
|
-
// reject(e)
|
|
921
|
-
// if (opts.waitResponse) {
|
|
922
|
-
// this._wsResponseWait.delete(opts.msg.requestId)
|
|
923
|
-
// }
|
|
924
|
-
// }
|
|
925
|
-
// })
|
|
926
|
-
} catch (e) {
|
|
927
|
-
reject(e)
|
|
928
|
-
}
|
|
929
|
-
})
|
|
930
|
-
|
|
931
|
-
close(code: CLOSE_EVENT_CODE) {
|
|
932
|
-
this.clearHeartbeat()
|
|
933
|
-
|
|
934
|
-
if (this._ws) {
|
|
935
|
-
this._ws.close(code, CLOSE_EVENT_CODE_INFO[code].name)
|
|
936
|
-
this._ws = undefined
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
closeAllClients = (error: any) => {
|
|
941
|
-
this._virtualWSClient.forEach(client => {
|
|
942
|
-
client.closeWithError(error)
|
|
943
|
-
})
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
pauseClients = (clients?: Set<VirtualWebSocketClient>) => {
|
|
947
|
-
;(clients || this._virtualWSClient).forEach(client => {
|
|
948
|
-
client.pause()
|
|
949
|
-
})
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
resumeClients = (clients?: Set<VirtualWebSocketClient>) => {
|
|
953
|
-
;(clients || this._virtualWSClient).forEach(client => {
|
|
954
|
-
client.resume()
|
|
955
|
-
})
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
private onWatchStart = (client: VirtualWebSocketClient, queryID: string) => {
|
|
959
|
-
this._queryIdClientMap.set(queryID, client)
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
private onWatchClose = (client: VirtualWebSocketClient, queryID: string) => {
|
|
963
|
-
if (queryID) {
|
|
964
|
-
this._queryIdClientMap.delete(queryID)
|
|
965
|
-
}
|
|
966
|
-
this._watchIdClientMap.delete(client.watchId)
|
|
967
|
-
this._virtualWSClient.delete(client)
|
|
968
|
-
|
|
969
|
-
if (!this._virtualWSClient.size) {
|
|
970
|
-
// no more existing watch, we should release the websocket connection
|
|
971
|
-
this.close(CLOSE_EVENT_CODE.NoRealtimeListeners)
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
watch(options: IWSWatchOptions): DBRealtimeListener {
|
|
976
|
-
if (!this._ws && !this._wsInitPromise) {
|
|
977
|
-
this.initWebSocketConnection(false)
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
const virtualClient = new VirtualWebSocketClient({
|
|
981
|
-
...options,
|
|
982
|
-
send: this.send,
|
|
983
|
-
login: this.webLogin,
|
|
984
|
-
isWSConnected: this.isWSConnected,
|
|
985
|
-
onceWSConnected: this.onceWSConnected,
|
|
986
|
-
getWaitExpectedTimeoutLength: this.getWaitExpectedTimeoutLength,
|
|
987
|
-
onWatchStart: this.onWatchStart,
|
|
988
|
-
onWatchClose: this.onWatchClose,
|
|
989
|
-
debug: true
|
|
990
|
-
})
|
|
991
|
-
this._virtualWSClient.add(virtualClient)
|
|
992
|
-
this._watchIdClientMap.set(virtualClient.watchId, virtualClient)
|
|
993
|
-
return virtualClient.listener
|
|
994
|
-
}
|
|
995
|
-
}
|