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