@cloudbase/database 1.4.3 → 1.5.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.
Files changed (197) hide show
  1. package/dist/commonjs/ObjectId/index.d.ts +1 -1
  2. package/dist/commonjs/ObjectId/index.js +16 -9
  3. package/dist/commonjs/aggregate.js +126 -75
  4. package/dist/commonjs/collection.d.ts +3 -2
  5. package/dist/commonjs/collection.js +131 -58
  6. package/dist/commonjs/command.d.ts +5 -5
  7. package/dist/commonjs/command.js +181 -154
  8. package/dist/commonjs/commands/logic.js +30 -20
  9. package/dist/commonjs/commands/query.d.ts +1 -1
  10. package/dist/commonjs/commands/query.js +85 -65
  11. package/dist/commonjs/commands/update.js +9 -7
  12. package/dist/commonjs/config/error.config.js +1 -0
  13. package/dist/commonjs/const/code.js +1 -0
  14. package/dist/commonjs/constant.d.ts +7 -7
  15. package/dist/commonjs/constant.js +13 -11
  16. package/dist/commonjs/document.d.ts +10 -6
  17. package/dist/commonjs/document.js +305 -193
  18. package/dist/commonjs/geo/index.js +20 -9
  19. package/dist/commonjs/geo/lineString.d.ts +1 -1
  20. package/dist/commonjs/geo/lineString.js +62 -33
  21. package/dist/commonjs/geo/multiLineString.d.ts +1 -1
  22. package/dist/commonjs/geo/multiLineString.js +71 -32
  23. package/dist/commonjs/geo/multiPoint.d.ts +1 -1
  24. package/dist/commonjs/geo/multiPoint.js +58 -29
  25. package/dist/commonjs/geo/multiPolygon.d.ts +1 -1
  26. package/dist/commonjs/geo/multiPolygon.js +96 -36
  27. package/dist/commonjs/geo/point.d.ts +1 -1
  28. package/dist/commonjs/geo/point.js +30 -23
  29. package/dist/commonjs/geo/polygon.d.ts +1 -1
  30. package/dist/commonjs/geo/polygon.js +78 -39
  31. package/dist/commonjs/helper/symbol.js +17 -5
  32. package/dist/commonjs/index.d.ts +8 -2
  33. package/dist/commonjs/index.js +73 -20
  34. package/dist/commonjs/lib/util.d.ts +1 -0
  35. package/dist/commonjs/lib/util.js +26 -0
  36. package/dist/commonjs/operator-map.js +7 -6
  37. package/dist/commonjs/query.d.ts +9 -5
  38. package/dist/commonjs/query.js +330 -205
  39. package/dist/commonjs/regexp/index.d.ts +1 -1
  40. package/dist/commonjs/regexp/index.js +16 -9
  41. package/dist/commonjs/serializer/common.d.ts +2 -2
  42. package/dist/commonjs/serializer/common.js +59 -22
  43. package/dist/commonjs/serializer/datatype.d.ts +2 -2
  44. package/dist/commonjs/serializer/datatype.js +58 -21
  45. package/dist/commonjs/serializer/query.d.ts +1 -1
  46. package/dist/commonjs/serializer/query.js +137 -127
  47. package/dist/commonjs/serializer/update.d.ts +1 -1
  48. package/dist/commonjs/serializer/update.js +83 -78
  49. package/dist/commonjs/serverDate/index.d.ts +1 -1
  50. package/dist/commonjs/serverDate/index.js +16 -9
  51. package/dist/commonjs/transaction/collection.d.ts +7 -0
  52. package/dist/commonjs/transaction/collection.js +49 -0
  53. package/dist/commonjs/transaction/document.d.ts +27 -0
  54. package/dist/commonjs/transaction/document.js +205 -0
  55. package/dist/commonjs/transaction/index.js +196 -86
  56. package/dist/commonjs/transaction/query.d.ts +43 -0
  57. package/dist/commonjs/transaction/query.js +261 -0
  58. package/dist/commonjs/typings/index.d.ts +4 -4
  59. package/dist/commonjs/typings/realtime.d.ts +16 -16
  60. package/dist/commonjs/util.js +123 -92
  61. package/dist/commonjs/utils/error.d.ts +2 -1
  62. package/dist/commonjs/utils/error.js +74 -38
  63. package/dist/commonjs/utils/symbol.js +33 -14
  64. package/dist/commonjs/utils/type.d.ts +1 -1
  65. package/dist/commonjs/utils/type.js +25 -13
  66. package/dist/commonjs/utils/utils.d.ts +5 -1
  67. package/dist/commonjs/utils/utils.js +153 -22
  68. package/dist/commonjs/validate.js +79 -63
  69. package/dist/esm/ObjectId/index.d.ts +1 -1
  70. package/dist/esm/ObjectId/index.js +15 -8
  71. package/dist/esm/aggregate.js +118 -66
  72. package/dist/esm/collection.d.ts +3 -2
  73. package/dist/esm/collection.js +125 -52
  74. package/dist/esm/command.d.ts +5 -5
  75. package/dist/esm/command.js +175 -147
  76. package/dist/esm/commands/logic.js +30 -20
  77. package/dist/esm/commands/query.d.ts +1 -1
  78. package/dist/esm/commands/query.js +91 -71
  79. package/dist/esm/commands/update.js +8 -6
  80. package/dist/esm/config/error.config.js +1 -1
  81. package/dist/esm/const/code.js +1 -1
  82. package/dist/esm/constant.d.ts +7 -7
  83. package/dist/esm/constant.js +12 -11
  84. package/dist/esm/document.d.ts +10 -6
  85. package/dist/esm/document.js +297 -185
  86. package/dist/esm/geo/interface.js +1 -0
  87. package/dist/esm/geo/lineString.d.ts +1 -1
  88. package/dist/esm/geo/lineString.js +57 -28
  89. package/dist/esm/geo/multiLineString.d.ts +1 -1
  90. package/dist/esm/geo/multiLineString.js +66 -27
  91. package/dist/esm/geo/multiPoint.d.ts +1 -1
  92. package/dist/esm/geo/multiPoint.js +53 -24
  93. package/dist/esm/geo/multiPolygon.d.ts +1 -1
  94. package/dist/esm/geo/multiPolygon.js +91 -31
  95. package/dist/esm/geo/point.d.ts +1 -1
  96. package/dist/esm/geo/point.js +26 -19
  97. package/dist/esm/geo/polygon.d.ts +1 -1
  98. package/dist/esm/geo/polygon.js +73 -34
  99. package/dist/esm/helper/symbol.js +13 -13
  100. package/dist/esm/index.d.ts +8 -2
  101. package/dist/esm/index.js +62 -9
  102. package/dist/esm/lib/util.d.ts +1 -0
  103. package/dist/esm/lib/util.js +22 -0
  104. package/dist/esm/operator-map.js +4 -4
  105. package/dist/esm/query.d.ts +9 -5
  106. package/dist/esm/query.js +321 -196
  107. package/dist/esm/regexp/index.d.ts +1 -1
  108. package/dist/esm/regexp/index.js +15 -8
  109. package/dist/esm/serializer/common.d.ts +2 -2
  110. package/dist/esm/serializer/common.js +49 -13
  111. package/dist/esm/serializer/datatype.d.ts +2 -2
  112. package/dist/esm/serializer/datatype.js +44 -8
  113. package/dist/esm/serializer/query.d.ts +1 -1
  114. package/dist/esm/serializer/query.js +112 -102
  115. package/dist/esm/serializer/update.d.ts +1 -1
  116. package/dist/esm/serializer/update.js +69 -64
  117. package/dist/esm/serverDate/index.d.ts +1 -1
  118. package/dist/esm/serverDate/index.js +15 -8
  119. package/dist/esm/transaction/collection.d.ts +7 -0
  120. package/dist/esm/transaction/collection.js +46 -0
  121. package/dist/esm/transaction/document.d.ts +27 -0
  122. package/dist/esm/transaction/document.js +202 -0
  123. package/dist/esm/transaction/index.js +191 -81
  124. package/dist/esm/transaction/query.d.ts +43 -0
  125. package/dist/esm/transaction/query.js +258 -0
  126. package/dist/esm/typings/index.d.ts +4 -4
  127. package/dist/esm/typings/index.js +1 -0
  128. package/dist/esm/typings/realtime.d.ts +16 -16
  129. package/dist/esm/typings/realtime.js +1 -0
  130. package/dist/esm/util.js +121 -90
  131. package/dist/esm/utils/error.d.ts +2 -1
  132. package/dist/esm/utils/error.js +70 -35
  133. package/dist/esm/utils/symbol.js +33 -14
  134. package/dist/esm/utils/type.d.ts +1 -1
  135. package/dist/esm/utils/type.js +12 -12
  136. package/dist/esm/utils/utils.d.ts +5 -1
  137. package/dist/esm/utils/utils.js +137 -16
  138. package/dist/esm/validate.js +65 -49
  139. package/jest.config.js +14 -0
  140. package/package.json +14 -10
  141. package/src/aggregate.ts +1 -5
  142. package/src/collection.ts +15 -2
  143. package/src/document.ts +45 -34
  144. package/src/index.ts +51 -4
  145. package/src/query.ts +48 -38
  146. package/src/serializer/query.ts +18 -6
  147. package/src/transaction/index.ts +1 -1
  148. package/src/typings/index.ts +0 -4
  149. package/src/utils/utils.ts +105 -0
  150. package/src/validate.ts +3 -6
  151. package/tests/gateway-contract.test.ts +180 -0
  152. package/tests/index.test.ts +283 -0
  153. package/tests/transaction-query.test.ts +207 -0
  154. package/tests/watch-query.test.ts +144 -0
  155. package/tsconfig.esm.json +4 -2
  156. package/tsconfig.json +16 -6
  157. package/tsconfig.test.json +15 -0
  158. package/dist/commonjs/realtime/error.d.ts +0 -7
  159. package/dist/commonjs/realtime/error.js +0 -11
  160. package/dist/commonjs/realtime/listener.d.ts +0 -14
  161. package/dist/commonjs/realtime/listener.js +0 -17
  162. package/dist/commonjs/realtime/message.d.ts +0 -3
  163. package/dist/commonjs/realtime/message.js +0 -10
  164. package/dist/commonjs/realtime/snapshot.d.ts +0 -16
  165. package/dist/commonjs/realtime/snapshot.js +0 -42
  166. package/dist/commonjs/realtime/virtual-websocket-client.d.ts +0 -63
  167. package/dist/commonjs/realtime/virtual-websocket-client.js +0 -736
  168. package/dist/commonjs/realtime/websocket-client.d.ts +0 -78
  169. package/dist/commonjs/realtime/websocket-client.js +0 -567
  170. package/dist/commonjs/realtime/ws-event.d.ts +0 -126
  171. package/dist/commonjs/realtime/ws-event.js +0 -139
  172. package/dist/commonjs/transaction.d.ts +0 -42
  173. package/dist/commonjs/transaction.js +0 -126
  174. package/dist/esm/realtime/error.d.ts +0 -7
  175. package/dist/esm/realtime/error.js +0 -8
  176. package/dist/esm/realtime/listener.d.ts +0 -14
  177. package/dist/esm/realtime/listener.js +0 -14
  178. package/dist/esm/realtime/message.d.ts +0 -3
  179. package/dist/esm/realtime/message.js +0 -6
  180. package/dist/esm/realtime/snapshot.d.ts +0 -16
  181. package/dist/esm/realtime/snapshot.js +0 -39
  182. package/dist/esm/realtime/virtual-websocket-client.d.ts +0 -63
  183. package/dist/esm/realtime/virtual-websocket-client.js +0 -733
  184. package/dist/esm/realtime/websocket-client.d.ts +0 -78
  185. package/dist/esm/realtime/websocket-client.js +0 -564
  186. package/dist/esm/realtime/ws-event.d.ts +0 -126
  187. package/dist/esm/realtime/ws-event.js +0 -137
  188. package/dist/esm/transaction.d.ts +0 -42
  189. package/dist/esm/transaction.js +0 -121
  190. package/src/realtime/error.ts +0 -19
  191. package/src/realtime/listener.ts +0 -35
  192. package/src/realtime/message.ts +0 -14
  193. package/src/realtime/snapshot.ts +0 -60
  194. package/src/realtime/virtual-websocket-client.ts +0 -1195
  195. package/src/realtime/websocket-client.ts +0 -995
  196. package/src/realtime/ws-event.ts +0 -163
  197. package/src/transaction.ts +0 -190
@@ -1,1195 +0,0 @@
1
- import set from 'lodash.set'
2
- import unset from 'lodash.unset'
3
- import cloneDeep from 'lodash.clonedeep'
4
- import { genRequestId } from './message'
5
- import {
6
- IResponseMessage,
7
- IRequestMessageInitWatchMsg,
8
- IResponseMessageInitEventMsg,
9
- IDBEvent,
10
- IRequestMessageRebuildWatchMsg,
11
- IRequestMessageCloseWatchMsg,
12
- IRequestMsgType,
13
- IResponseMessageNextEventMsg,
14
- IRequestMessageCheckLastMsg
15
- } from '../typings/realtime'
16
- import {
17
- CloudSDKError,
18
- isTimeoutError,
19
- TimeoutError,
20
- CancelledError,
21
- isCancelledError
22
- } from '../utils/error'
23
- import { ERR_CODE } from '../config/error.config'
24
- import { sleep } from '../utils/utils'
25
- // import Reporter from "./externals/public-lib/reporter"
26
- import { RealtimeListener } from './listener'
27
- import { Snapshot } from './snapshot'
28
- import { IWSSendOptions, ILoginResult } from './websocket-client'
29
- import { isRealtimeErrorMessageError, RealtimeErrorMessageError } from './error'
30
- import { IWatchOptions, ISingleDBEvent } from '../typings/index'
31
-
32
- // =============== Realtime Virtual WebSocket Client (Internal) ====================
33
-
34
- interface IVirtualWebSocketClientConstructorOptions extends IWatchOptions {
35
- // ws: RealtimeWebSocketClient
36
- envId?: string
37
- collectionName: string
38
- query: string
39
- limit?: number
40
- orderBy?: Record<string, string>
41
- send: <T = any>(opts: IWSSendOptions) => Promise<T>
42
- login: (envId?: string, refresh?: boolean) => Promise<any>
43
- isWSConnected: () => boolean
44
- onceWSConnected: () => Promise<void>
45
- getWaitExpectedTimeoutLength: () => number
46
- onWatchStart: (client: VirtualWebSocketClient, queryID: string) => void
47
- onWatchClose: (client: VirtualWebSocketClient, queryID: string) => void
48
- debug?: boolean
49
- }
50
-
51
- interface IWatchSessionInfo {
52
- queryID: string
53
- currentEventId: number
54
- currentDocs: Record<string, any>[]
55
- expectEventId?: number
56
- }
57
-
58
- interface IHandleCommonErrorOptions {
59
- onSignError: (e: RealtimeErrorMessageError) => void
60
- onTimeoutError: (e: TimeoutError) => void
61
- onCancelledError: (e: CancelledError) => void
62
- onNotRetryableError: (e: RealtimeErrorMessageError) => void
63
- onUnknownError: (e: any) => void
64
- }
65
-
66
- interface IHandleWatchEstablishmentErrorOptions {
67
- operationName: 'INIT_WATCH' | 'REBUILD_WATCH'
68
- resolve: (value?: void | PromiseLike<void> | undefined) => void
69
- reject: (e: any) => void
70
- // retry: (refreshLogin?: boolean) => void
71
- // abortWatch: (e: any) => void
72
- }
73
-
74
- enum WATCH_STATUS {
75
- LOGGINGIN = 'LOGGINGIN',
76
- INITING = 'INITING',
77
- REBUILDING = 'REBUILDING',
78
- ACTIVE = 'ACTIVE',
79
- ERRORED = 'ERRORED',
80
- CLOSING = 'CLOSING',
81
- CLOSED = 'CLOSED',
82
- PAUSED = 'PAUSED',
83
- RESUMING = 'RESUMING'
84
- }
85
-
86
- const DEFAULT_WAIT_TIME_ON_UNKNOWN_ERROR = 100
87
- const DEFAULT_MAX_AUTO_RETRY_ON_ERROR = 2
88
- const DEFAULT_MAX_SEND_ACK_AUTO_RETRY_ON_ERROR = 2
89
- const DEFAULT_SEND_ACK_DEBOUNCE_TIMEOUT = 10 * 1000
90
- const DEFAULT_INIT_WATCH_TIMEOUT = 10 * 1000
91
- const DEFAULT_REBUILD_WATCH_TIMEOUT = 10 * 1000
92
-
93
- export class VirtualWebSocketClient {
94
- // passed over
95
- watchId: string
96
- private envId?: string
97
- private collectionName: string
98
- private query: string
99
- private limit: number
100
- private orderBy: Record<string, string>
101
- private send: <T = any>(opts: IWSSendOptions) => Promise<T>
102
- private login: (envId?: string, refresh?: boolean) => Promise<any>
103
- private isWSConnected: () => boolean
104
- private onceWSConnected: () => Promise<void>
105
- private getWaitExpectedTimeoutLength: () => number
106
- private onWatchStart: (
107
- client: VirtualWebSocketClient,
108
- queryID: string
109
- ) => void
110
- private onWatchClose: (
111
- client: VirtualWebSocketClient,
112
- queryID: string
113
- ) => void
114
- private debug?: boolean
115
-
116
- // own
117
- listener: RealtimeListener
118
- private watchStatus: WATCH_STATUS = WATCH_STATUS.INITING
119
- private _availableRetries: Partial<Record<IRequestMsgType, number>>
120
- private _ackTimeoutId?: number
121
- private _initWatchPromise?: Promise<void>
122
- private _rebuildWatchPromise?: Promise<void>
123
-
124
- // obtained
125
- private sessionInfo?: IWatchSessionInfo
126
-
127
- // internal
128
- private _waitExpectedTimeoutId?: number
129
-
130
- constructor(options: IVirtualWebSocketClientConstructorOptions) {
131
- this.watchId = `watchid_${+new Date()}_${Math.random()}`
132
- this.envId = options.envId
133
- this.collectionName = options.collectionName
134
- this.query = options.query
135
- this.limit = options.limit
136
- this.orderBy = options.orderBy
137
- this.send = options.send
138
- this.login = options.login
139
- this.isWSConnected = options.isWSConnected
140
- this.onceWSConnected = options.onceWSConnected
141
- this.getWaitExpectedTimeoutLength = options.getWaitExpectedTimeoutLength
142
- this.onWatchStart = options.onWatchStart
143
- this.onWatchClose = options.onWatchClose
144
- this.debug = options.debug
145
-
146
- this._availableRetries = {
147
- INIT_WATCH: DEFAULT_MAX_AUTO_RETRY_ON_ERROR,
148
- REBUILD_WATCH: DEFAULT_MAX_AUTO_RETRY_ON_ERROR,
149
- CHECK_LAST: DEFAULT_MAX_SEND_ACK_AUTO_RETRY_ON_ERROR
150
- }
151
-
152
- this.listener = new RealtimeListener({
153
- close: this.closeWatch,
154
- onChange: options.onChange,
155
- onError: options.onError,
156
- debug: this.debug,
157
- virtualClient: this
158
- })
159
-
160
- this.initWatch()
161
- }
162
-
163
- private _login = async (
164
- envId?: string,
165
- refresh?: boolean
166
- ): Promise<ILoginResult> => {
167
- this.watchStatus = WATCH_STATUS.LOGGINGIN
168
- const loginResult = await this.login(envId, refresh)
169
- if (!this.envId) {
170
- this.envId = loginResult.envId
171
- }
172
- return loginResult
173
- }
174
-
175
- private initWatch = async (forceRefreshLogin?: boolean): Promise<void> => {
176
- if (this._initWatchPromise) {
177
- return this._initWatchPromise
178
- }
179
-
180
- this._initWatchPromise = new Promise<void>(
181
- async (resolve, reject): Promise<void> => {
182
- try {
183
- if (this.watchStatus === WATCH_STATUS.PAUSED) {
184
- // if (process.env.DEBUG) {
185
- console.log('[realtime] initWatch cancelled on pause')
186
- // }
187
- return resolve()
188
- }
189
-
190
- const { envId } = await this._login(this.envId, forceRefreshLogin)
191
-
192
- // if (!this.sessionInfo) {
193
- // throw new Error(`can not rebuildWatch without a successful initWatch (lack of sessionInfo)`)
194
- // }
195
-
196
- if ((this.watchStatus as WATCH_STATUS) === WATCH_STATUS.PAUSED) {
197
- console.log('[realtime] initWatch cancelled on pause')
198
- return resolve()
199
- }
200
-
201
- this.watchStatus = WATCH_STATUS.INITING
202
-
203
- const initWatchMsg: IRequestMessageInitWatchMsg = {
204
- watchId: this.watchId,
205
- requestId: genRequestId(),
206
- msgType: 'INIT_WATCH',
207
- msgData: {
208
- envId,
209
- collName: this.collectionName,
210
- query: this.query,
211
- limit: this.limit,
212
- orderBy: this.orderBy
213
- }
214
- }
215
-
216
- const initEventMsg = await this.send<IResponseMessageInitEventMsg>({
217
- msg: initWatchMsg,
218
- waitResponse: true,
219
- skipOnMessage: true,
220
- timeout: DEFAULT_INIT_WATCH_TIMEOUT
221
- })
222
-
223
- const { events, currEvent } = initEventMsg.msgData
224
-
225
- this.sessionInfo = {
226
- queryID: initEventMsg.msgData.queryID,
227
- currentEventId: currEvent - 1,
228
- currentDocs: []
229
- }
230
-
231
- // FIX: in initEvent message, all events have id 0, which is inconsistent with currEvent
232
- if (events.length > 0) {
233
- for (const e of events) {
234
- e.ID = currEvent
235
- }
236
- this.handleServerEvents(initEventMsg)
237
- } else {
238
- this.sessionInfo.currentEventId = currEvent
239
- const snapshot = new Snapshot({
240
- id: currEvent,
241
- docChanges: [],
242
- docs: [],
243
- type: 'init'
244
- })
245
- this.listener.onChange(snapshot)
246
- this.scheduleSendACK()
247
- }
248
- this.onWatchStart(this, this.sessionInfo.queryID)
249
- this.watchStatus = WATCH_STATUS.ACTIVE
250
- this._availableRetries.INIT_WATCH = DEFAULT_MAX_AUTO_RETRY_ON_ERROR
251
- resolve()
252
- } catch (e) {
253
- this.handleWatchEstablishmentError(e, {
254
- operationName: 'INIT_WATCH',
255
- resolve,
256
- reject
257
- })
258
- }
259
- }
260
- )
261
-
262
- let success = false
263
-
264
- try {
265
- await this._initWatchPromise
266
- success = true
267
- } finally {
268
- this._initWatchPromise = undefined
269
- }
270
-
271
- // if (process.env.DEBUG) {
272
- console.log(`[realtime] initWatch ${success ? 'success' : 'fail'}`)
273
- // }
274
- }
275
-
276
- private rebuildWatch = async (forceRefreshLogin?: boolean): Promise<void> => {
277
- if (this._rebuildWatchPromise) {
278
- return this._rebuildWatchPromise
279
- }
280
-
281
- this._rebuildWatchPromise = new Promise<void>(
282
- async (resolve, reject): Promise<void> => {
283
- try {
284
- if (this.watchStatus === WATCH_STATUS.PAUSED) {
285
- // if (process.env.DEBUG) {
286
- console.log('[realtime] rebuildWatch cancelled on pause')
287
- // }
288
- return resolve()
289
- }
290
- const { envId } = await this._login(this.envId, forceRefreshLogin)
291
-
292
- if (!this.sessionInfo) {
293
- throw new Error(
294
- 'can not rebuildWatch without a successful initWatch (lack of sessionInfo)'
295
- )
296
- }
297
-
298
- if ((this.watchStatus as WATCH_STATUS) === WATCH_STATUS.PAUSED) {
299
- console.log('[realtime] rebuildWatch cancelled on pause')
300
- return resolve()
301
- }
302
-
303
- this.watchStatus = WATCH_STATUS.REBUILDING
304
-
305
- const rebuildWatchMsg: IRequestMessageRebuildWatchMsg = {
306
- watchId: this.watchId,
307
- requestId: genRequestId(),
308
- msgType: 'REBUILD_WATCH',
309
- msgData: {
310
- envId,
311
- collName: this.collectionName,
312
- queryID: this.sessionInfo.queryID,
313
- eventID: this.sessionInfo.currentEventId
314
- }
315
- }
316
-
317
- const nextEventMsg = await this.send<IResponseMessageNextEventMsg>({
318
- msg: rebuildWatchMsg,
319
- waitResponse: true,
320
- skipOnMessage: false,
321
- timeout: DEFAULT_REBUILD_WATCH_TIMEOUT
322
- })
323
-
324
- this.handleServerEvents(nextEventMsg)
325
-
326
- this.watchStatus = WATCH_STATUS.ACTIVE
327
- this._availableRetries.REBUILD_WATCH = DEFAULT_MAX_AUTO_RETRY_ON_ERROR
328
- resolve()
329
- } catch (e) {
330
- this.handleWatchEstablishmentError(e, {
331
- operationName: 'REBUILD_WATCH',
332
- resolve,
333
- reject
334
- })
335
- }
336
- }
337
- )
338
-
339
- let success = false
340
-
341
- try {
342
- await this._rebuildWatchPromise
343
- success = true
344
- } finally {
345
- this._rebuildWatchPromise = undefined
346
- }
347
-
348
- // if (process.env.DEBUG) {
349
- console.log(`[realtime] rebuildWatch ${success ? 'success' : 'fail'}`)
350
- // }
351
- }
352
-
353
- private handleWatchEstablishmentError = async (
354
- e: any,
355
- options: IHandleWatchEstablishmentErrorOptions
356
- ) => {
357
- const isInitWatch = options.operationName === 'INIT_WATCH'
358
-
359
- const abortWatch = () => {
360
- // mock temp comment
361
- this.closeWithError(
362
- new CloudSDKError({
363
- errCode: isInitWatch
364
- ? (ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_INIT_WATCH_FAIL as string)
365
- : (ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_REBUILD_WATCH_FAIL as string),
366
- errMsg: e
367
- })
368
- )
369
- options.reject(e)
370
- }
371
-
372
- const retry = (refreshLogin?: boolean) => {
373
- if (this.useRetryTicket(options.operationName)) {
374
- if (isInitWatch) {
375
- this._initWatchPromise = undefined
376
- options.resolve(this.initWatch(refreshLogin))
377
- } else {
378
- this._rebuildWatchPromise = undefined
379
- options.resolve(this.rebuildWatch(refreshLogin))
380
- }
381
- } else {
382
- abortWatch()
383
- }
384
- }
385
-
386
- this.handleCommonError(e, {
387
- onSignError: () => retry(true),
388
- onTimeoutError: () => retry(false),
389
- onNotRetryableError: abortWatch,
390
- onCancelledError: options.reject,
391
- onUnknownError: async () => {
392
- try {
393
- const onWSDisconnected = async () => {
394
- this.pause()
395
- await this.onceWSConnected()
396
- retry(true)
397
- }
398
-
399
- if (!this.isWSConnected()) {
400
- await onWSDisconnected()
401
- } else {
402
- await sleep(DEFAULT_WAIT_TIME_ON_UNKNOWN_ERROR)
403
- if (this.watchStatus === WATCH_STATUS.PAUSED) {
404
- // cancel
405
- options.reject(
406
- new CancelledError(
407
- `${options.operationName} cancelled due to pause after unknownError`
408
- )
409
- )
410
- } else if (!this.isWSConnected()) {
411
- await onWSDisconnected()
412
- } else {
413
- retry(false)
414
- }
415
- }
416
- } catch (e) {
417
- // unexpected error while handling error, in order to provide maximum effort on SEAMINGLESS FAULT TOLERANCE, just retry
418
- retry(true)
419
- }
420
- }
421
- })
422
- }
423
-
424
- private closeWatch = async () => {
425
- const queryId = this.sessionInfo ? this.sessionInfo.queryID : ''
426
-
427
- if (this.watchStatus !== WATCH_STATUS.ACTIVE) {
428
- this.watchStatus = WATCH_STATUS.CLOSED
429
- this.onWatchClose(this, queryId)
430
- return
431
- }
432
-
433
- try {
434
- this.watchStatus = WATCH_STATUS.CLOSING
435
-
436
- const closeWatchMsg: IRequestMessageCloseWatchMsg = {
437
- watchId: this.watchId,
438
- requestId: genRequestId(),
439
- msgType: 'CLOSE_WATCH',
440
- msgData: null
441
- }
442
-
443
- await this.send<void>({
444
- msg: closeWatchMsg
445
- })
446
-
447
- this.sessionInfo = undefined
448
- this.watchStatus = WATCH_STATUS.CLOSED
449
- } catch (e) {
450
- this.closeWithError(
451
- new CloudSDKError({
452
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_CLOSE_WATCH_FAIL as string,
453
- errMsg: e
454
- })
455
- )
456
- } finally {
457
- this.onWatchClose(this, queryId)
458
- }
459
- }
460
-
461
- private scheduleSendACK = () => {
462
- this.clearACKSchedule()
463
-
464
- // TODO: should we check status after timeout
465
- // @ts-ignore
466
- this._ackTimeoutId = setTimeout(() => {
467
- if (this._waitExpectedTimeoutId) {
468
- this.scheduleSendACK()
469
- } else {
470
- this.sendACK()
471
- }
472
- }, DEFAULT_SEND_ACK_DEBOUNCE_TIMEOUT)
473
- }
474
-
475
- private clearACKSchedule = () => {
476
- if (this._ackTimeoutId) {
477
- clearTimeout(this._ackTimeoutId)
478
- }
479
- }
480
-
481
- private sendACK = async (): Promise<void> => {
482
- try {
483
- if (this.watchStatus !== WATCH_STATUS.ACTIVE) {
484
- this.scheduleSendACK()
485
- return
486
- }
487
-
488
- if (!this.sessionInfo) {
489
- console.warn(
490
- '[realtime listener] can not send ack without a successful initWatch (lack of sessionInfo)'
491
- )
492
- return
493
- }
494
-
495
- const ackMsg: IRequestMessageCheckLastMsg = {
496
- watchId: this.watchId,
497
- requestId: genRequestId(),
498
- msgType: 'CHECK_LAST',
499
- msgData: {
500
- queryID: this.sessionInfo.queryID,
501
- eventID: this.sessionInfo.currentEventId
502
- }
503
- }
504
-
505
- await this.send<void>({
506
- msg: ackMsg
507
- })
508
-
509
- this.scheduleSendACK()
510
- } catch (e) {
511
- // TODO: refactor
512
- if (isRealtimeErrorMessageError(e)) {
513
- const msg = e.payload
514
- switch (msg.msgData.code) {
515
- // signature error -> retry with refreshed signature
516
- case 'CHECK_LOGIN_FAILED':
517
- case 'SIGN_EXPIRED_ERROR':
518
- case 'SIGN_INVALID_ERROR':
519
- case 'SIGN_PARAM_INVALID': {
520
- this.rebuildWatch()
521
- return
522
- }
523
- // other -> throw
524
- case 'QUERYID_INVALID_ERROR':
525
- case 'SYS_ERR':
526
- case 'INVALIID_ENV':
527
- case 'COLLECTION_PERMISSION_DENIED': {
528
- // must throw
529
- this.closeWithError(
530
- new CloudSDKError({
531
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_CHECK_LAST_FAIL as string,
532
- errMsg: msg.msgData.code
533
- })
534
- )
535
- return
536
- }
537
- default: {
538
- break
539
- }
540
- }
541
- }
542
-
543
- // maybe retryable
544
- if (
545
- this._availableRetries.CHECK_LAST &&
546
- this._availableRetries.CHECK_LAST > 0
547
- ) {
548
- this._availableRetries.CHECK_LAST--
549
- this.scheduleSendACK()
550
- } else {
551
- this.closeWithError(
552
- new CloudSDKError({
553
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_CHECK_LAST_FAIL as string,
554
- errMsg: e
555
- })
556
- )
557
- }
558
- }
559
- }
560
-
561
- private handleCommonError = (
562
- e: any,
563
- options: IHandleCommonErrorOptions
564
- ): void => {
565
- if (isRealtimeErrorMessageError(e)) {
566
- const msg = e.payload
567
- switch (msg.msgData.code) {
568
- // signature error -> retry with refreshed signature
569
- case 'CHECK_LOGIN_FAILED':
570
- case 'SIGN_EXPIRED_ERROR':
571
- case 'SIGN_INVALID_ERROR':
572
- case 'SIGN_PARAM_INVALID': {
573
- options.onSignError(e)
574
- return
575
- }
576
- // not-retryable error -> throw
577
- case 'QUERYID_INVALID_ERROR':
578
- case 'SYS_ERR':
579
- case 'INVALIID_ENV':
580
- case 'COLLECTION_PERMISSION_DENIED': {
581
- options.onNotRetryableError(e)
582
- return
583
- }
584
- default: {
585
- options.onNotRetryableError(e)
586
- return
587
- }
588
- }
589
- } else if (isTimeoutError(e)) {
590
- // timeout error
591
- options.onTimeoutError(e)
592
- return
593
- } else if (isCancelledError(e)) {
594
- // cancelled error
595
- options.onCancelledError(e)
596
- return
597
- }
598
-
599
- // unknown error
600
- options.onUnknownError(e)
601
- }
602
-
603
- // credit a retry chance from availableRetries
604
- private useRetryTicket(operationName: IRequestMsgType): boolean {
605
- if (
606
- this._availableRetries[operationName] &&
607
- this._availableRetries[operationName]! > 0
608
- ) {
609
- this._availableRetries[operationName]!--
610
-
611
- // if (process.env.DEBUG) {
612
- console.log(
613
- `[realtime] ${operationName} use a retry ticket, now only ${this._availableRetries[operationName]} retry left`
614
- )
615
- // }
616
-
617
- return true
618
- }
619
- return false
620
- }
621
-
622
- private async handleServerEvents(
623
- msg: IResponseMessageInitEventMsg | IResponseMessageNextEventMsg
624
- ) {
625
- try {
626
- this.scheduleSendACK()
627
- await this._handleServerEvents(msg)
628
- this._postHandleServerEventsValidityCheck(msg)
629
- } catch (e) {
630
- // if (process.env.DEBUG) {
631
- // TODO: report
632
- console.error(
633
- '[realtime listener] internal non-fatal error: handle server events failed with error: ',
634
- e
635
- )
636
-
637
- // writeToFile(
638
- // "wserror.txt",
639
- // `[realtime listener] internal non-fatal error: handle server events failed with error: ${JSON.stringify(
640
- // Object.assign({}, e, {
641
- // requestId: msg.requestId,
642
- // watchId: msg.watchId
643
- // })
644
- // )} \n`
645
- // )
646
- // }
647
-
648
- throw e
649
- }
650
- }
651
-
652
- private async _handleServerEvents(
653
- msg: IResponseMessageInitEventMsg | IResponseMessageNextEventMsg
654
- ) {
655
- const { requestId } = msg
656
-
657
- const { events } = msg.msgData
658
- const { msgType } = msg
659
-
660
- if (!events.length || !this.sessionInfo) {
661
- return
662
- }
663
-
664
- const sessionInfo = this.sessionInfo
665
-
666
- let allChangeEvents: ISingleDBEvent[]
667
- try {
668
- allChangeEvents = events.map(getPublicEvent)
669
- } catch (e) {
670
- this.closeWithError(
671
- new CloudSDKError({
672
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_RECEIVE_INVALID_SERVER_DATA as string,
673
- errMsg: e
674
- })
675
- )
676
- return
677
- }
678
-
679
- // aggregate docs
680
- let docs = [...sessionInfo.currentDocs]
681
- let initEncountered = false
682
- for (let i = 0, len = allChangeEvents.length; i < len; i++) {
683
- const change = allChangeEvents[i]
684
-
685
- if (sessionInfo.currentEventId >= change.id) {
686
- if (!allChangeEvents[i - 1] || change.id > allChangeEvents[i - 1].id) {
687
- // duplicate event, dropable
688
- // TODO: report
689
- // if (process.env.DEBUG) {
690
- console.warn(
691
- `[realtime] duplicate event received, cur ${sessionInfo.currentEventId} but got ${change.id}`
692
- )
693
- // }
694
- } else {
695
- // allChangeEvents should be in ascending order according to eventId, this should never happens, must report a non-fatal error
696
- console.error(
697
- `[realtime listener] server non-fatal error: events out of order (the latter event's id is smaller than that of the former) (requestId ${requestId})`
698
- )
699
-
700
- // writeToFile(
701
- // "wserror.txt",
702
- // `[realtime listener] server non-fatal error: events out of order (the latter event's id is smaller than that of the former) ${JSON.stringify(
703
- // Object.assign(
704
- // {},
705
- // {
706
- // requestId: msg.requestId,
707
- // watchId: msg.watchId
708
- // }
709
- // )
710
- // )} \n`
711
- // )
712
- }
713
- continue
714
- } else if (sessionInfo.currentEventId === change.id - 1) {
715
- // correct sequence
716
- // first handle dataType then queueType:
717
- // 1. dataType: we ONLY populate change.doc if neccessary
718
- // 2. queueType: we build the data snapshot
719
-
720
- switch (change.dataType) {
721
- case 'update': {
722
- // only need to populate change.doc when it is not provided
723
- if (!change.doc) {
724
- switch (change.queueType) {
725
- case 'update':
726
- case 'dequeue': {
727
- const localDoc = docs.find(doc => doc._id === change.docId)
728
- if (localDoc) {
729
- // a partial update
730
- const doc = cloneDeep(localDoc)
731
-
732
- if (change.updatedFields) {
733
- for (const fieldPath in change.updatedFields) {
734
- set(doc, fieldPath, change.updatedFields[fieldPath])
735
- }
736
- }
737
-
738
- if (change.removedFields) {
739
- for (const fieldPath of change.removedFields) {
740
- unset(doc, fieldPath)
741
- }
742
- }
743
-
744
- change.doc = doc
745
- } else {
746
- // TODO report
747
- console.error(
748
- '[realtime listener] internal non-fatal server error: unexpected update dataType event where no doc is associated.'
749
- )
750
-
751
- // writeToFile(
752
- // "wserror.txt",
753
- // `[realtime listener] internal non-fatal server error: unexpected update dataType event where no doc is associated. ${JSON.stringify(
754
- // Object.assign(
755
- // {},
756
- // {
757
- // requestId: msg.requestId,
758
- // watchId: msg.watchId
759
- // }
760
- // )
761
- // )} \n`
762
- // )
763
- }
764
- break
765
- }
766
- case 'enqueue': {
767
- // doc is provided by server, this should never occur
768
- const err = new CloudSDKError({
769
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR as string,
770
- errMsg: `HandleServerEvents: full doc is not provided with dataType="update" and queueType="enqueue" (requestId ${msg.requestId})`
771
- })
772
- this.closeWithError(err)
773
- throw err
774
- }
775
- default: {
776
- break
777
- }
778
- }
779
- }
780
- break
781
- }
782
- case 'replace': {
783
- // validation
784
- if (!change.doc) {
785
- // doc is provided by server, this should never occur
786
- const err = new CloudSDKError({
787
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR as string,
788
- errMsg: `HandleServerEvents: full doc is not provided with dataType="replace" (requestId ${msg.requestId})`
789
- })
790
- this.closeWithError(err)
791
- throw err
792
- }
793
- break
794
- }
795
- case 'remove': {
796
- const doc = docs.find(doc => doc._id === change.docId)
797
- if (doc) {
798
- change.doc = doc
799
- } else {
800
- // TODO report
801
- console.error(
802
- '[realtime listener] internal non-fatal server error: unexpected remove event where no doc is associated.'
803
- )
804
-
805
- // writeToFile(
806
- // "wserror.txt",
807
- // `[realtime listener] internal non-fatal server error: unexpected remove event where no doc is associated. ${JSON.stringify(
808
- // Object.assign(
809
- // {},
810
- // {
811
- // requestId: msg.requestId,
812
- // watchId: msg.watchId
813
- // }
814
- // )
815
- // )} \n`
816
- // )
817
- }
818
- break
819
- }
820
- case 'limit': {
821
- if (!change.doc) {
822
- switch(change.queueType) {
823
- case 'dequeue': {
824
- const doc = docs.find(doc => doc._id === change.docId)
825
- if (doc) {
826
- change.doc = doc
827
- } else {
828
- console.error(
829
- '[realtime listener] internal non-fatal server error: unexpected limit dataType event where no doc is associated.'
830
- )
831
- }
832
- break
833
- }
834
- case 'enqueue': {
835
- // doc is provided by server, this should never occur
836
- const err = new CloudSDKError({
837
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR as string,
838
- errMsg: `HandleServerEvents: full doc is not provided with dataType="limit" and queueType="enqueue" (requestId ${msg.requestId})`
839
- })
840
- this.closeWithError(err)
841
- throw err
842
- }
843
- default: {
844
- break
845
- }
846
- }
847
- }
848
- break
849
- }
850
- }
851
-
852
- switch (change.queueType) {
853
- case 'init': {
854
- if (!initEncountered) {
855
- initEncountered = true
856
- docs = [change.doc]
857
- } else {
858
- docs.push(change.doc)
859
- }
860
- break
861
- }
862
- case 'enqueue': {
863
- docs.push(change.doc)
864
- break
865
- }
866
- case 'dequeue': {
867
- const ind = docs.findIndex(doc => doc._id === change.docId)
868
- if (ind > -1) {
869
- docs.splice(ind, 1)
870
- } else {
871
- // TODO report
872
- console.error(
873
- '[realtime listener] internal non-fatal server error: unexpected dequeue event where no doc is associated.'
874
- )
875
-
876
- // writeToFile(
877
- // "wserror.txt",
878
- // `[realtime listener] internal non-fatal server error: unexpected dequeue event where no doc is associated. ${JSON.stringify(
879
- // Object.assign(
880
- // {},
881
- // {
882
- // requestId: msg.requestId,
883
- // watchId: msg.watchId
884
- // }
885
- // )
886
- // )} \n`
887
- // )
888
- }
889
- break
890
- }
891
- case 'update': {
892
- // writeToFile(
893
- // "wserror.txt",
894
- // `[realtime listener] docs ${JSON.stringify(
895
- // docs
896
- // )} change doc ${JSON.stringify(change)} \n`
897
- // )
898
- const ind = docs.findIndex(doc => doc._id === change.docId)
899
- if (ind > -1) {
900
- docs[ind] = change.doc
901
- } else {
902
- // TODO report
903
- console.error(
904
- '[realtime listener] internal non-fatal server error: unexpected queueType update event where no doc is associated.'
905
- )
906
-
907
- // writeToFile(
908
- // "wserror.txt",
909
- // `[realtime listener] internal non-fatal server error: unexpected queueType update event where no doc is associated. ${JSON.stringify(
910
- // Object.assign(
911
- // {},
912
- // {
913
- // requestId: msg.requestId,
914
- // watchId: msg.watchId
915
- // }
916
- // )
917
- // )} \n`
918
- // )
919
- }
920
- break
921
- }
922
- }
923
-
924
- if (
925
- i === len - 1 ||
926
- (allChangeEvents[i + 1] && allChangeEvents[i + 1].id !== change.id)
927
- ) {
928
- // a shallow slice creates a shallow snapshot
929
- const docsSnapshot = [...docs]
930
-
931
- // we slice first cause' if there're allChangeEvents that are of the same id after this change, we don't want to involve it for it is unexpected invalid order
932
- const docChanges = allChangeEvents
933
- .slice(0, i + 1)
934
- .filter(c => c.id === change.id)
935
-
936
- // all changes of this event has been handle, we could dispatch the event now
937
- this.sessionInfo.currentEventId = change.id
938
- this.sessionInfo.currentDocs = docs
939
-
940
- const snapshot = new Snapshot({
941
- id: change.id,
942
- docChanges,
943
- docs: docsSnapshot,
944
- msgType
945
- })
946
-
947
- // Reporter.surroundThirdByTryCatch(() =>
948
- this.listener.onChange(snapshot)
949
- // )()
950
- }
951
- } else {
952
- // out-of-order event
953
- // if (process.env.DEBUG) {
954
- // TODO: report
955
- console.warn(
956
- `[realtime listener] event received is out of order, cur ${this.sessionInfo.currentEventId} but got ${change.id}`
957
- )
958
- // }
959
- // rebuild watch
960
- await this.rebuildWatch()
961
- return
962
- }
963
- }
964
- }
965
-
966
- private _postHandleServerEventsValidityCheck(
967
- msg: IResponseMessageInitEventMsg | IResponseMessageNextEventMsg
968
- ) {
969
- if (!this.sessionInfo) {
970
- console.error(
971
- '[realtime listener] internal non-fatal error: sessionInfo lost after server event handling, this should never occur'
972
- )
973
-
974
- // writeToFile(
975
- // "wserror.txt",
976
- // `[realtime listener] internal non-fatal error: sessionInfo lost after server event handling, this should never occur ${JSON.stringify(
977
- // Object.assign(
978
- // {},
979
- // {
980
- // requestId: msg.requestId,
981
- // watchId: msg.watchId
982
- // }
983
- // )
984
- // )} \n`
985
- // )
986
- return
987
- }
988
-
989
- if (
990
- this.sessionInfo.expectEventId &&
991
- this.sessionInfo.currentEventId >= this.sessionInfo.expectEventId
992
- ) {
993
- this.clearWaitExpectedEvent()
994
- }
995
-
996
- if (this.sessionInfo.currentEventId < msg.msgData.currEvent) {
997
- console.warn(
998
- '[realtime listener] internal non-fatal error: client eventId does not match with server event id after server event handling'
999
- )
1000
- return
1001
- }
1002
- }
1003
-
1004
- private clearWaitExpectedEvent() {
1005
- if (this._waitExpectedTimeoutId) {
1006
- clearTimeout(this._waitExpectedTimeoutId)
1007
- this._waitExpectedTimeoutId = undefined
1008
- }
1009
- }
1010
-
1011
- onMessage(msg: IResponseMessage) {
1012
- // watchStatus sanity check
1013
- switch (this.watchStatus) {
1014
- case WATCH_STATUS.PAUSED: {
1015
- // ignore all but error message
1016
- if (msg.msgType !== 'ERROR') {
1017
- return
1018
- }
1019
- break
1020
- }
1021
- case WATCH_STATUS.LOGGINGIN:
1022
- case WATCH_STATUS.INITING:
1023
- case WATCH_STATUS.REBUILDING: {
1024
- console.warn(
1025
- `[realtime listener] internal non-fatal error: unexpected message received while ${this.watchStatus}`
1026
- )
1027
- return
1028
- }
1029
- case WATCH_STATUS.CLOSED: {
1030
- console.warn(
1031
- '[realtime listener] internal non-fatal error: unexpected message received when the watch has closed'
1032
- )
1033
- return
1034
- }
1035
- case WATCH_STATUS.ERRORED: {
1036
- console.warn(
1037
- '[realtime listener] internal non-fatal error: unexpected message received when the watch has ended with error'
1038
- )
1039
- return
1040
- }
1041
- }
1042
-
1043
- if (!this.sessionInfo) {
1044
- console.warn(
1045
- '[realtime listener] internal non-fatal error: sessionInfo not found while message is received.'
1046
- )
1047
- return
1048
- }
1049
-
1050
- this.scheduleSendACK()
1051
-
1052
- switch (msg.msgType) {
1053
- case 'NEXT_EVENT': {
1054
- // if (process.env.DEBUG) {
1055
- // @ts-ignore
1056
- // if (wx._ignore) {
1057
- console.warn(`nextevent ${msg.msgData.currEvent} ignored`, msg)
1058
- // @ts-ignore
1059
- // wx._ignore = false
1060
- // return
1061
- // }
1062
- // }
1063
-
1064
- this.handleServerEvents(msg)
1065
- break
1066
- }
1067
- case 'CHECK_EVENT': {
1068
- if (this.sessionInfo.currentEventId < msg.msgData.currEvent) {
1069
- // client eventID < server eventID:
1070
- // there might be one or more pending events not yet received but sent by the server
1071
- this.sessionInfo.expectEventId = msg.msgData.currEvent
1072
- this.clearWaitExpectedEvent()
1073
- // @ts-ignore
1074
- this._waitExpectedTimeoutId = setTimeout(() => {
1075
- // must rebuild watch
1076
- this.rebuildWatch()
1077
- }, this.getWaitExpectedTimeoutLength())
1078
-
1079
- // if (process.env.DEBUG) {
1080
- console.log(
1081
- `[realtime] waitExpectedTimeoutLength ${this.getWaitExpectedTimeoutLength()}`
1082
- )
1083
- // }
1084
- }
1085
- break
1086
- }
1087
- case 'ERROR': {
1088
- // receive server error
1089
- this.closeWithError(
1090
- new CloudSDKError({
1091
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_SERVER_ERROR_MSG as string,
1092
- errMsg: `${msg.msgData.code} - ${msg.msgData.message}`
1093
- })
1094
- )
1095
- break
1096
- }
1097
- default: {
1098
- // if (process.env.DEBUG) {
1099
- console.warn(
1100
- `[realtime listener] virtual client receive unexpected msg ${msg.msgType}: `,
1101
- msg
1102
- )
1103
- // }
1104
- break
1105
- }
1106
- }
1107
- }
1108
-
1109
- closeWithError(error: any) {
1110
- this.watchStatus = WATCH_STATUS.ERRORED
1111
- this.clearACKSchedule()
1112
- this.listener.onError(error)
1113
- // Reporter.surroundThirdByTryCatch(() => this.listener.onError(error))
1114
- this.onWatchClose(
1115
- this,
1116
- (this.sessionInfo && this.sessionInfo.queryID) || ''
1117
- )
1118
-
1119
- // if (process.env.DEBUG) {
1120
- console.log(
1121
- `[realtime] client closed (${this.collectionName} ${this.query}) (watchId ${this.watchId})`
1122
- )
1123
- // }
1124
- }
1125
-
1126
- pause() {
1127
- this.watchStatus = WATCH_STATUS.PAUSED
1128
- // if (process.env.DEBUG) {
1129
- console.log(
1130
- `[realtime] client paused (${this.collectionName} ${this.query}) (watchId ${this.watchId})`
1131
- )
1132
- // }
1133
- }
1134
-
1135
- // resume() {
1136
- // return this.sessionInfo ? this.rebuildWatch() : this.initWatch()
1137
- // }
1138
-
1139
- async resume(): Promise<void> {
1140
- this.watchStatus = WATCH_STATUS.RESUMING
1141
-
1142
- // if (process.env.DEBUG) {
1143
- console.log(
1144
- `[realtime] client resuming with ${
1145
- this.sessionInfo ? 'REBUILD_WATCH' : 'INIT_WATCH'
1146
- } (${this.collectionName} ${this.query}) (${this.watchId})`
1147
- )
1148
- // }
1149
-
1150
- try {
1151
- await (this.sessionInfo ? this.rebuildWatch() : this.initWatch())
1152
-
1153
- // if (process.env.DEBUG) {
1154
- console.log(
1155
- `[realtime] client successfully resumed (${this.collectionName} ${this.query}) (${this.watchId})`
1156
- )
1157
- // }
1158
- } catch (e) {
1159
- // if (process.env.DEBUG) {
1160
- console.error(
1161
- `[realtime] client resume failed (${this.collectionName} ${this.query}) (${this.watchId})`,
1162
- e
1163
- )
1164
- // }
1165
- }
1166
- }
1167
- }
1168
-
1169
- function getPublicEvent(event: IDBEvent): ISingleDBEvent {
1170
- const e: ISingleDBEvent = {
1171
- id: event.ID,
1172
- dataType: event.DataType,
1173
- queueType: event.QueueType,
1174
- docId: event.DocID,
1175
- doc: event.Doc && event.Doc !== '{}' ? JSON.parse(event.Doc) : undefined
1176
- }
1177
-
1178
- if (event.DataType === 'update') {
1179
- // @ts-ignore
1180
- if (event.UpdatedFields) {
1181
- e.updatedFields = JSON.parse(event.UpdatedFields)
1182
- }
1183
- // TODO: wait for tcb to change removedFields to RemovedFields
1184
- // @ts-ignore
1185
- if (event.removedFields || event.RemovedFields) {
1186
- // @ts-ignore
1187
- // e.removedFields = event.removedFields
1188
- // ? JSON.parse(event.removedFields)
1189
- // : JSON.parse(event.RemovedFields)
1190
- e.removedFields = JSON.parse(event.removedFields)
1191
- }
1192
- }
1193
-
1194
- return e
1195
- }