@cloudbase/database 0.9.17-beta → 0.9.19

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 (218) hide show
  1. package/README.md +32 -10
  2. package/dist/commonjs/ObjectId/index.d.ts +11 -0
  3. package/dist/commonjs/ObjectId/index.js +21 -0
  4. package/dist/commonjs/aggregate.d.ts +29 -29
  5. package/dist/commonjs/aggregate.js +156 -146
  6. package/dist/commonjs/collection.d.ts +9 -9
  7. package/dist/commonjs/collection.js +46 -46
  8. package/dist/commonjs/command.d.ts +175 -175
  9. package/dist/commonjs/command.js +298 -298
  10. package/dist/commonjs/commands/logic.d.ts +24 -24
  11. package/dist/commonjs/commands/logic.js +88 -88
  12. package/dist/commonjs/commands/query.d.ts +68 -68
  13. package/dist/commonjs/commands/query.js +157 -157
  14. package/dist/commonjs/commands/update.d.ts +29 -29
  15. package/dist/commonjs/commands/update.js +50 -50
  16. package/dist/commonjs/config/app.config.d.ts +6 -6
  17. package/dist/commonjs/config/app.config.js +7 -7
  18. package/dist/commonjs/config/error.config.d.ts +3 -3
  19. package/dist/commonjs/config/error.config.js +15 -15
  20. package/dist/commonjs/const/code.d.ts +30 -30
  21. package/dist/commonjs/const/code.js +32 -32
  22. package/dist/commonjs/constant.d.ts +51 -51
  23. package/dist/commonjs/constant.js +74 -74
  24. package/dist/commonjs/document.d.ts +11 -11
  25. package/dist/commonjs/document.js +252 -263
  26. package/dist/commonjs/geo/index.d.ts +6 -6
  27. package/dist/commonjs/geo/index.js +11 -11
  28. package/dist/commonjs/geo/interface.d.ts +24 -24
  29. package/dist/commonjs/geo/interface.js +2 -2
  30. package/dist/commonjs/geo/lineString.d.ts +19 -19
  31. package/dist/commonjs/geo/lineString.js +85 -85
  32. package/dist/commonjs/geo/multiLineString.d.ts +18 -18
  33. package/dist/commonjs/geo/multiLineString.js +94 -94
  34. package/dist/commonjs/geo/multiPoint.d.ts +18 -18
  35. package/dist/commonjs/geo/multiPoint.js +78 -78
  36. package/dist/commonjs/geo/multiPolygon.d.ts +18 -18
  37. package/dist/commonjs/geo/multiPolygon.js +121 -121
  38. package/dist/commonjs/geo/point.d.ts +19 -19
  39. package/dist/commonjs/geo/point.js +49 -49
  40. package/dist/commonjs/geo/polygon.d.ts +19 -19
  41. package/dist/commonjs/geo/polygon.js +108 -108
  42. package/dist/commonjs/helper/symbol.d.ts +14 -14
  43. package/dist/commonjs/helper/symbol.js +19 -19
  44. package/dist/commonjs/index.d.ts +33 -32
  45. package/dist/commonjs/index.js +46 -46
  46. package/dist/commonjs/lib/util.d.ts +1 -1
  47. package/dist/commonjs/lib/util.js +25 -25
  48. package/dist/commonjs/operator-map.d.ts +4 -4
  49. package/dist/commonjs/operator-map.js +23 -23
  50. package/dist/commonjs/query.d.ts +22 -22
  51. package/dist/commonjs/query.js +251 -262
  52. package/dist/commonjs/realtime/error.d.ts +7 -7
  53. package/dist/commonjs/realtime/error.js +11 -27
  54. package/dist/commonjs/realtime/listener.d.ts +14 -14
  55. package/dist/commonjs/realtime/listener.js +17 -18
  56. package/dist/commonjs/realtime/message.d.ts +3 -3
  57. package/dist/commonjs/realtime/message.js +10 -11
  58. package/dist/commonjs/realtime/snapshot.d.ts +16 -16
  59. package/dist/commonjs/realtime/snapshot.js +42 -43
  60. package/dist/commonjs/realtime/virtual-websocket-client.d.ts +63 -63
  61. package/dist/commonjs/realtime/virtual-websocket-client.js +736 -990
  62. package/dist/commonjs/realtime/websocket-client.d.ts +78 -78
  63. package/dist/commonjs/realtime/websocket-client.js +567 -788
  64. package/dist/commonjs/realtime/ws-event.d.ts +126 -126
  65. package/dist/commonjs/realtime/ws-event.js +139 -139
  66. package/dist/commonjs/regexp/index.d.ts +14 -14
  67. package/dist/commonjs/regexp/index.js +32 -32
  68. package/dist/commonjs/serializer/common.d.ts +11 -11
  69. package/dist/commonjs/serializer/common.js +121 -121
  70. package/dist/commonjs/serializer/datatype.d.ts +7 -7
  71. package/dist/commonjs/serializer/datatype.js +126 -126
  72. package/dist/commonjs/serializer/query.d.ts +7 -7
  73. package/dist/commonjs/serializer/query.js +231 -231
  74. package/dist/commonjs/serializer/update.d.ts +15 -15
  75. package/dist/commonjs/serializer/update.js +141 -141
  76. package/dist/commonjs/serverDate/index.d.ts +13 -13
  77. package/dist/commonjs/serverDate/index.js +29 -29
  78. package/dist/commonjs/transaction/collection.d.ts +7 -7
  79. package/dist/commonjs/transaction/collection.js +46 -46
  80. package/dist/commonjs/transaction/document.d.ts +27 -27
  81. package/dist/commonjs/transaction/document.js +205 -205
  82. package/dist/commonjs/transaction/index.d.ts +30 -30
  83. package/dist/commonjs/transaction/index.js +214 -214
  84. package/dist/commonjs/transaction/query.d.ts +5 -5
  85. package/dist/commonjs/transaction/query.js +10 -10
  86. package/dist/commonjs/transaction.d.ts +42 -42
  87. package/dist/commonjs/transaction.js +126 -126
  88. package/dist/commonjs/typings/index.d.ts +40 -40
  89. package/dist/commonjs/typings/index.js +2 -2
  90. package/dist/commonjs/typings/realtime.d.ts +219 -219
  91. package/dist/commonjs/typings/realtime.js +2 -2
  92. package/dist/commonjs/util.d.ts +9 -9
  93. package/dist/commonjs/util.js +144 -144
  94. package/dist/commonjs/utils/error.d.ts +37 -37
  95. package/dist/commonjs/utils/error.js +85 -85
  96. package/dist/commonjs/utils/symbol.d.ts +8 -8
  97. package/dist/commonjs/utils/symbol.js +57 -57
  98. package/dist/commonjs/utils/type.d.ts +13 -13
  99. package/dist/commonjs/utils/type.js +22 -22
  100. package/dist/commonjs/utils/utils.d.ts +4 -2
  101. package/dist/commonjs/utils/utils.js +36 -14
  102. package/dist/commonjs/validate.d.ts +1 -1
  103. package/dist/commonjs/validate.js +59 -59
  104. package/dist/esm/ObjectId/index.d.ts +11 -0
  105. package/dist/esm/ObjectId/index.js +17 -0
  106. package/dist/esm/aggregate.d.ts +29 -29
  107. package/dist/esm/aggregate.js +154 -144
  108. package/dist/esm/collection.d.ts +9 -9
  109. package/dist/esm/collection.js +44 -44
  110. package/dist/esm/command.d.ts +175 -175
  111. package/dist/esm/command.js +296 -296
  112. package/dist/esm/commands/logic.d.ts +24 -24
  113. package/dist/esm/commands/logic.js +84 -84
  114. package/dist/esm/commands/query.d.ts +68 -68
  115. package/dist/esm/commands/query.js +152 -152
  116. package/dist/esm/commands/update.d.ts +29 -29
  117. package/dist/esm/commands/update.js +46 -46
  118. package/dist/esm/config/app.config.d.ts +6 -6
  119. package/dist/esm/config/app.config.js +5 -5
  120. package/dist/esm/config/error.config.d.ts +3 -3
  121. package/dist/esm/config/error.config.js +13 -13
  122. package/dist/esm/const/code.d.ts +30 -30
  123. package/dist/esm/const/code.js +30 -30
  124. package/dist/esm/constant.d.ts +51 -51
  125. package/dist/esm/constant.js +65 -65
  126. package/dist/esm/document.d.ts +11 -11
  127. package/dist/esm/document.js +250 -261
  128. package/dist/esm/geo/index.d.ts +6 -6
  129. package/dist/esm/geo/index.js +6 -6
  130. package/dist/esm/geo/interface.d.ts +24 -24
  131. package/dist/esm/geo/lineString.d.ts +19 -19
  132. package/dist/esm/geo/lineString.js +83 -83
  133. package/dist/esm/geo/multiLineString.d.ts +18 -18
  134. package/dist/esm/geo/multiLineString.js +92 -92
  135. package/dist/esm/geo/multiPoint.d.ts +18 -18
  136. package/dist/esm/geo/multiPoint.js +76 -76
  137. package/dist/esm/geo/multiPolygon.d.ts +18 -18
  138. package/dist/esm/geo/multiPolygon.js +119 -119
  139. package/dist/esm/geo/point.d.ts +19 -19
  140. package/dist/esm/geo/point.js +47 -47
  141. package/dist/esm/geo/polygon.d.ts +19 -19
  142. package/dist/esm/geo/polygon.js +106 -106
  143. package/dist/esm/helper/symbol.d.ts +14 -14
  144. package/dist/esm/helper/symbol.js +14 -14
  145. package/dist/esm/index.d.ts +33 -32
  146. package/dist/esm/index.js +41 -41
  147. package/dist/esm/lib/util.d.ts +1 -1
  148. package/dist/esm/lib/util.js +23 -23
  149. package/dist/esm/operator-map.d.ts +4 -4
  150. package/dist/esm/operator-map.js +20 -20
  151. package/dist/esm/query.d.ts +22 -22
  152. package/dist/esm/query.js +249 -260
  153. package/dist/esm/realtime/error.d.ts +7 -7
  154. package/dist/esm/realtime/error.js +8 -25
  155. package/dist/esm/realtime/listener.d.ts +14 -14
  156. package/dist/esm/realtime/listener.js +14 -16
  157. package/dist/esm/realtime/message.d.ts +3 -3
  158. package/dist/esm/realtime/message.js +6 -7
  159. package/dist/esm/realtime/snapshot.d.ts +16 -16
  160. package/dist/esm/realtime/snapshot.js +39 -41
  161. package/dist/esm/realtime/virtual-websocket-client.d.ts +63 -63
  162. package/dist/esm/realtime/virtual-websocket-client.js +733 -988
  163. package/dist/esm/realtime/websocket-client.d.ts +78 -78
  164. package/dist/esm/realtime/websocket-client.js +564 -786
  165. package/dist/esm/realtime/ws-event.d.ts +126 -126
  166. package/dist/esm/realtime/ws-event.js +137 -137
  167. package/dist/esm/regexp/index.d.ts +14 -14
  168. package/dist/esm/regexp/index.js +29 -29
  169. package/dist/esm/serializer/common.d.ts +11 -11
  170. package/dist/esm/serializer/common.js +113 -113
  171. package/dist/esm/serializer/datatype.d.ts +7 -7
  172. package/dist/esm/serializer/datatype.js +122 -122
  173. package/dist/esm/serializer/query.d.ts +7 -7
  174. package/dist/esm/serializer/query.js +229 -229
  175. package/dist/esm/serializer/update.d.ts +15 -15
  176. package/dist/esm/serializer/update.js +139 -139
  177. package/dist/esm/serverDate/index.d.ts +13 -13
  178. package/dist/esm/serverDate/index.js +26 -26
  179. package/dist/esm/transaction/collection.d.ts +7 -7
  180. package/dist/esm/transaction/collection.js +44 -44
  181. package/dist/esm/transaction/document.d.ts +27 -27
  182. package/dist/esm/transaction/document.js +203 -203
  183. package/dist/esm/transaction/index.d.ts +30 -30
  184. package/dist/esm/transaction/index.js +210 -210
  185. package/dist/esm/transaction/query.d.ts +5 -5
  186. package/dist/esm/transaction/query.js +8 -8
  187. package/dist/esm/transaction.d.ts +42 -42
  188. package/dist/esm/transaction.js +121 -121
  189. package/dist/esm/typings/index.d.ts +40 -40
  190. package/dist/esm/typings/realtime.d.ts +219 -219
  191. package/dist/esm/util.d.ts +9 -9
  192. package/dist/esm/util.js +142 -142
  193. package/dist/esm/utils/error.d.ts +37 -37
  194. package/dist/esm/utils/error.js +82 -82
  195. package/dist/esm/utils/symbol.d.ts +8 -8
  196. package/dist/esm/utils/symbol.js +55 -55
  197. package/dist/esm/utils/type.d.ts +13 -13
  198. package/dist/esm/utils/type.js +20 -20
  199. package/dist/esm/utils/utils.d.ts +4 -2
  200. package/dist/esm/utils/utils.js +33 -12
  201. package/dist/esm/validate.d.ts +1 -1
  202. package/dist/esm/validate.js +57 -57
  203. package/package.json +4 -1
  204. package/src/aggregate.ts +11 -0
  205. package/src/document.ts +3 -15
  206. package/src/index.ts +2 -0
  207. package/src/query.ts +4 -16
  208. package/src/utils/utils.ts +28 -0
  209. package/.DS_Store +0 -0
  210. package/src/.DS_Store +0 -0
  211. package/src/realtime/error.ts +0 -19
  212. package/src/realtime/listener.ts +0 -35
  213. package/src/realtime/message.ts +0 -14
  214. package/src/realtime/snapshot.ts +0 -60
  215. package/src/realtime/virtual-websocket-client.ts +0 -1195
  216. package/src/realtime/websocket-client.ts +0 -995
  217. package/src/realtime/ws-event.ts +0 -163
  218. package/src/regexp/.DS_Store +0 -0
@@ -1,988 +1,733 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- var __generator = (this && this.__generator) || function (thisArg, body) {
11
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
12
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
13
- function verb(n) { return function (v) { return step([n, v]); }; }
14
- function step(op) {
15
- if (f) throw new TypeError("Generator is already executing.");
16
- while (_) try {
17
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
18
- if (y = 0, t) op = [op[0] & 2, t.value];
19
- switch (op[0]) {
20
- case 0: case 1: t = op; break;
21
- case 4: _.label++; return { value: op[1], done: false };
22
- case 5: _.label++; y = op[1]; op = [0]; continue;
23
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
24
- default:
25
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
26
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
27
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
28
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
29
- if (t[2]) _.ops.pop();
30
- _.trys.pop(); continue;
31
- }
32
- op = body.call(thisArg, _);
33
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
34
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35
- }
36
- };
37
- var __values = (this && this.__values) || function(o) {
38
- var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
39
- if (m) return m.call(o);
40
- if (o && typeof o.length === "number") return {
41
- next: function () {
42
- if (o && i >= o.length) o = void 0;
43
- return { value: o && o[i++], done: !o };
44
- }
45
- };
46
- throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
47
- };
48
- var __read = (this && this.__read) || function (o, n) {
49
- var m = typeof Symbol === "function" && o[Symbol.iterator];
50
- if (!m) return o;
51
- var i = m.call(o), r, ar = [], e;
52
- try {
53
- while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
54
- }
55
- catch (error) { e = { error: error }; }
56
- finally {
57
- try {
58
- if (r && !r.done && (m = i["return"])) m.call(i);
59
- }
60
- finally { if (e) throw e.error; }
61
- }
62
- return ar;
63
- };
64
- var __spread = (this && this.__spread) || function () {
65
- for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
66
- return ar;
67
- };
68
- import set from 'lodash.set';
69
- import unset from 'lodash.unset';
70
- import cloneDeep from 'lodash.clonedeep';
71
- import { genRequestId } from './message';
72
- import { CloudSDKError, isTimeoutError, CancelledError, isCancelledError } from '../utils/error';
73
- import { ERR_CODE } from '../config/error.config';
74
- import { sleep } from '../utils/utils';
75
- import { RealtimeListener } from './listener';
76
- import { Snapshot } from './snapshot';
77
- import { isRealtimeErrorMessageError } from './error';
78
- var WATCH_STATUS;
79
- (function (WATCH_STATUS) {
80
- WATCH_STATUS["LOGGINGIN"] = "LOGGINGIN";
81
- WATCH_STATUS["INITING"] = "INITING";
82
- WATCH_STATUS["REBUILDING"] = "REBUILDING";
83
- WATCH_STATUS["ACTIVE"] = "ACTIVE";
84
- WATCH_STATUS["ERRORED"] = "ERRORED";
85
- WATCH_STATUS["CLOSING"] = "CLOSING";
86
- WATCH_STATUS["CLOSED"] = "CLOSED";
87
- WATCH_STATUS["PAUSED"] = "PAUSED";
88
- WATCH_STATUS["RESUMING"] = "RESUMING";
89
- })(WATCH_STATUS || (WATCH_STATUS = {}));
90
- var DEFAULT_WAIT_TIME_ON_UNKNOWN_ERROR = 100;
91
- var DEFAULT_MAX_AUTO_RETRY_ON_ERROR = 2;
92
- var DEFAULT_MAX_SEND_ACK_AUTO_RETRY_ON_ERROR = 2;
93
- var DEFAULT_SEND_ACK_DEBOUNCE_TIMEOUT = 10 * 1000;
94
- var DEFAULT_INIT_WATCH_TIMEOUT = 10 * 1000;
95
- var DEFAULT_REBUILD_WATCH_TIMEOUT = 10 * 1000;
96
- var VirtualWebSocketClient = (function () {
97
- function VirtualWebSocketClient(options) {
98
- var _this = this;
99
- this.watchStatus = WATCH_STATUS.INITING;
100
- this._login = function (envId, refresh) { return __awaiter(_this, void 0, void 0, function () {
101
- var loginResult;
102
- return __generator(this, function (_a) {
103
- switch (_a.label) {
104
- case 0:
105
- this.watchStatus = WATCH_STATUS.LOGGINGIN;
106
- return [4, this.login(envId, refresh)];
107
- case 1:
108
- loginResult = _a.sent();
109
- if (!this.envId) {
110
- this.envId = loginResult.envId;
111
- }
112
- return [2, loginResult];
113
- }
114
- });
115
- }); };
116
- this.initWatch = function (forceRefreshLogin) { return __awaiter(_this, void 0, void 0, function () {
117
- var success;
118
- var _this = this;
119
- return __generator(this, function (_a) {
120
- switch (_a.label) {
121
- case 0:
122
- if (this._initWatchPromise) {
123
- return [2, this._initWatchPromise];
124
- }
125
- this._initWatchPromise = new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
126
- var envId, initWatchMsg, initEventMsg, _a, events, currEvent, events_1, events_1_1, e, snapshot, e_1;
127
- var e_2, _b;
128
- return __generator(this, function (_c) {
129
- switch (_c.label) {
130
- case 0:
131
- _c.trys.push([0, 3, , 4]);
132
- if (this.watchStatus === WATCH_STATUS.PAUSED) {
133
- console.log('[realtime] initWatch cancelled on pause');
134
- return [2, resolve()];
135
- }
136
- return [4, this._login(this.envId, forceRefreshLogin)];
137
- case 1:
138
- envId = (_c.sent()).envId;
139
- if (this.watchStatus === WATCH_STATUS.PAUSED) {
140
- console.log('[realtime] initWatch cancelled on pause');
141
- return [2, resolve()];
142
- }
143
- this.watchStatus = WATCH_STATUS.INITING;
144
- initWatchMsg = {
145
- watchId: this.watchId,
146
- requestId: genRequestId(),
147
- msgType: 'INIT_WATCH',
148
- msgData: {
149
- envId: envId,
150
- collName: this.collectionName,
151
- query: this.query,
152
- limit: this.limit,
153
- orderBy: this.orderBy
154
- }
155
- };
156
- return [4, this.send({
157
- msg: initWatchMsg,
158
- waitResponse: true,
159
- skipOnMessage: true,
160
- timeout: DEFAULT_INIT_WATCH_TIMEOUT
161
- })];
162
- case 2:
163
- initEventMsg = _c.sent();
164
- _a = initEventMsg.msgData, events = _a.events, currEvent = _a.currEvent;
165
- this.sessionInfo = {
166
- queryID: initEventMsg.msgData.queryID,
167
- currentEventId: currEvent - 1,
168
- currentDocs: []
169
- };
170
- if (events.length > 0) {
171
- try {
172
- for (events_1 = __values(events), events_1_1 = events_1.next(); !events_1_1.done; events_1_1 = events_1.next()) {
173
- e = events_1_1.value;
174
- e.ID = currEvent;
175
- }
176
- }
177
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
178
- finally {
179
- try {
180
- if (events_1_1 && !events_1_1.done && (_b = events_1.return)) _b.call(events_1);
181
- }
182
- finally { if (e_2) throw e_2.error; }
183
- }
184
- this.handleServerEvents(initEventMsg);
185
- }
186
- else {
187
- this.sessionInfo.currentEventId = currEvent;
188
- snapshot = new Snapshot({
189
- id: currEvent,
190
- docChanges: [],
191
- docs: [],
192
- type: 'init'
193
- });
194
- this.listener.onChange(snapshot);
195
- this.scheduleSendACK();
196
- }
197
- this.onWatchStart(this, this.sessionInfo.queryID);
198
- this.watchStatus = WATCH_STATUS.ACTIVE;
199
- this._availableRetries.INIT_WATCH = DEFAULT_MAX_AUTO_RETRY_ON_ERROR;
200
- resolve();
201
- return [3, 4];
202
- case 3:
203
- e_1 = _c.sent();
204
- this.handleWatchEstablishmentError(e_1, {
205
- operationName: 'INIT_WATCH',
206
- resolve: resolve,
207
- reject: reject
208
- });
209
- return [3, 4];
210
- case 4: return [2];
211
- }
212
- });
213
- }); });
214
- success = false;
215
- _a.label = 1;
216
- case 1:
217
- _a.trys.push([1, , 3, 4]);
218
- return [4, this._initWatchPromise];
219
- case 2:
220
- _a.sent();
221
- success = true;
222
- return [3, 4];
223
- case 3:
224
- this._initWatchPromise = undefined;
225
- return [7];
226
- case 4:
227
- console.log("[realtime] initWatch " + (success ? 'success' : 'fail'));
228
- return [2];
229
- }
230
- });
231
- }); };
232
- this.rebuildWatch = function (forceRefreshLogin) { return __awaiter(_this, void 0, void 0, function () {
233
- var success;
234
- var _this = this;
235
- return __generator(this, function (_a) {
236
- switch (_a.label) {
237
- case 0:
238
- if (this._rebuildWatchPromise) {
239
- return [2, this._rebuildWatchPromise];
240
- }
241
- this._rebuildWatchPromise = new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
242
- var envId, rebuildWatchMsg, nextEventMsg, e_3;
243
- return __generator(this, function (_a) {
244
- switch (_a.label) {
245
- case 0:
246
- _a.trys.push([0, 3, , 4]);
247
- if (this.watchStatus === WATCH_STATUS.PAUSED) {
248
- console.log('[realtime] rebuildWatch cancelled on pause');
249
- return [2, resolve()];
250
- }
251
- return [4, this._login(this.envId, forceRefreshLogin)];
252
- case 1:
253
- envId = (_a.sent()).envId;
254
- if (!this.sessionInfo) {
255
- throw new Error('can not rebuildWatch without a successful initWatch (lack of sessionInfo)');
256
- }
257
- if (this.watchStatus === WATCH_STATUS.PAUSED) {
258
- console.log('[realtime] rebuildWatch cancelled on pause');
259
- return [2, resolve()];
260
- }
261
- this.watchStatus = WATCH_STATUS.REBUILDING;
262
- rebuildWatchMsg = {
263
- watchId: this.watchId,
264
- requestId: genRequestId(),
265
- msgType: 'REBUILD_WATCH',
266
- msgData: {
267
- envId: envId,
268
- collName: this.collectionName,
269
- queryID: this.sessionInfo.queryID,
270
- eventID: this.sessionInfo.currentEventId
271
- }
272
- };
273
- return [4, this.send({
274
- msg: rebuildWatchMsg,
275
- waitResponse: true,
276
- skipOnMessage: false,
277
- timeout: DEFAULT_REBUILD_WATCH_TIMEOUT
278
- })];
279
- case 2:
280
- nextEventMsg = _a.sent();
281
- this.handleServerEvents(nextEventMsg);
282
- this.watchStatus = WATCH_STATUS.ACTIVE;
283
- this._availableRetries.REBUILD_WATCH = DEFAULT_MAX_AUTO_RETRY_ON_ERROR;
284
- resolve();
285
- return [3, 4];
286
- case 3:
287
- e_3 = _a.sent();
288
- this.handleWatchEstablishmentError(e_3, {
289
- operationName: 'REBUILD_WATCH',
290
- resolve: resolve,
291
- reject: reject
292
- });
293
- return [3, 4];
294
- case 4: return [2];
295
- }
296
- });
297
- }); });
298
- success = false;
299
- _a.label = 1;
300
- case 1:
301
- _a.trys.push([1, , 3, 4]);
302
- return [4, this._rebuildWatchPromise];
303
- case 2:
304
- _a.sent();
305
- success = true;
306
- return [3, 4];
307
- case 3:
308
- this._rebuildWatchPromise = undefined;
309
- return [7];
310
- case 4:
311
- console.log("[realtime] rebuildWatch " + (success ? 'success' : 'fail'));
312
- return [2];
313
- }
314
- });
315
- }); };
316
- this.handleWatchEstablishmentError = function (e, options) { return __awaiter(_this, void 0, void 0, function () {
317
- var isInitWatch, abortWatch, retry;
318
- var _this = this;
319
- return __generator(this, function (_a) {
320
- isInitWatch = options.operationName === 'INIT_WATCH';
321
- abortWatch = function () {
322
- _this.closeWithError(new CloudSDKError({
323
- errCode: isInitWatch
324
- ? ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_INIT_WATCH_FAIL
325
- : ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_REBUILD_WATCH_FAIL,
326
- errMsg: e
327
- }));
328
- options.reject(e);
329
- };
330
- retry = function (refreshLogin) {
331
- if (_this.useRetryTicket(options.operationName)) {
332
- if (isInitWatch) {
333
- _this._initWatchPromise = undefined;
334
- options.resolve(_this.initWatch(refreshLogin));
335
- }
336
- else {
337
- _this._rebuildWatchPromise = undefined;
338
- options.resolve(_this.rebuildWatch(refreshLogin));
339
- }
340
- }
341
- else {
342
- abortWatch();
343
- }
344
- };
345
- this.handleCommonError(e, {
346
- onSignError: function () { return retry(true); },
347
- onTimeoutError: function () { return retry(false); },
348
- onNotRetryableError: abortWatch,
349
- onCancelledError: options.reject,
350
- onUnknownError: function () { return __awaiter(_this, void 0, void 0, function () {
351
- var onWSDisconnected, e_4;
352
- var _this = this;
353
- return __generator(this, function (_a) {
354
- switch (_a.label) {
355
- case 0:
356
- _a.trys.push([0, 8, , 9]);
357
- onWSDisconnected = function () { return __awaiter(_this, void 0, void 0, function () {
358
- return __generator(this, function (_a) {
359
- switch (_a.label) {
360
- case 0:
361
- this.pause();
362
- return [4, this.onceWSConnected()];
363
- case 1:
364
- _a.sent();
365
- retry(true);
366
- return [2];
367
- }
368
- });
369
- }); };
370
- if (!!this.isWSConnected()) return [3, 2];
371
- return [4, onWSDisconnected()];
372
- case 1:
373
- _a.sent();
374
- return [3, 7];
375
- case 2: return [4, sleep(DEFAULT_WAIT_TIME_ON_UNKNOWN_ERROR)];
376
- case 3:
377
- _a.sent();
378
- if (!(this.watchStatus === WATCH_STATUS.PAUSED)) return [3, 4];
379
- options.reject(new CancelledError(options.operationName + " cancelled due to pause after unknownError"));
380
- return [3, 7];
381
- case 4:
382
- if (!!this.isWSConnected()) return [3, 6];
383
- return [4, onWSDisconnected()];
384
- case 5:
385
- _a.sent();
386
- return [3, 7];
387
- case 6:
388
- retry(false);
389
- _a.label = 7;
390
- case 7: return [3, 9];
391
- case 8:
392
- e_4 = _a.sent();
393
- retry(true);
394
- return [3, 9];
395
- case 9: return [2];
396
- }
397
- });
398
- }); }
399
- });
400
- return [2];
401
- });
402
- }); };
403
- this.closeWatch = function () { return __awaiter(_this, void 0, void 0, function () {
404
- var queryId, closeWatchMsg, e_5;
405
- return __generator(this, function (_a) {
406
- switch (_a.label) {
407
- case 0:
408
- queryId = this.sessionInfo ? this.sessionInfo.queryID : '';
409
- if (this.watchStatus !== WATCH_STATUS.ACTIVE) {
410
- this.watchStatus = WATCH_STATUS.CLOSED;
411
- this.onWatchClose(this, queryId);
412
- return [2];
413
- }
414
- _a.label = 1;
415
- case 1:
416
- _a.trys.push([1, 3, 4, 5]);
417
- this.watchStatus = WATCH_STATUS.CLOSING;
418
- closeWatchMsg = {
419
- watchId: this.watchId,
420
- requestId: genRequestId(),
421
- msgType: 'CLOSE_WATCH',
422
- msgData: null
423
- };
424
- return [4, this.send({
425
- msg: closeWatchMsg
426
- })];
427
- case 2:
428
- _a.sent();
429
- this.sessionInfo = undefined;
430
- this.watchStatus = WATCH_STATUS.CLOSED;
431
- return [3, 5];
432
- case 3:
433
- e_5 = _a.sent();
434
- this.closeWithError(new CloudSDKError({
435
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_CLOSE_WATCH_FAIL,
436
- errMsg: e_5
437
- }));
438
- return [3, 5];
439
- case 4:
440
- this.onWatchClose(this, queryId);
441
- return [7];
442
- case 5: return [2];
443
- }
444
- });
445
- }); };
446
- this.scheduleSendACK = function () {
447
- _this.clearACKSchedule();
448
- _this._ackTimeoutId = setTimeout(function () {
449
- if (_this._waitExpectedTimeoutId) {
450
- _this.scheduleSendACK();
451
- }
452
- else {
453
- _this.sendACK();
454
- }
455
- }, DEFAULT_SEND_ACK_DEBOUNCE_TIMEOUT);
456
- };
457
- this.clearACKSchedule = function () {
458
- if (_this._ackTimeoutId) {
459
- clearTimeout(_this._ackTimeoutId);
460
- }
461
- };
462
- this.sendACK = function () { return __awaiter(_this, void 0, void 0, function () {
463
- var ackMsg, e_6, msg;
464
- return __generator(this, function (_a) {
465
- switch (_a.label) {
466
- case 0:
467
- _a.trys.push([0, 2, , 3]);
468
- if (this.watchStatus !== WATCH_STATUS.ACTIVE) {
469
- this.scheduleSendACK();
470
- return [2];
471
- }
472
- if (!this.sessionInfo) {
473
- console.warn('[realtime listener] can not send ack without a successful initWatch (lack of sessionInfo)');
474
- return [2];
475
- }
476
- ackMsg = {
477
- watchId: this.watchId,
478
- requestId: genRequestId(),
479
- msgType: 'CHECK_LAST',
480
- msgData: {
481
- queryID: this.sessionInfo.queryID,
482
- eventID: this.sessionInfo.currentEventId
483
- }
484
- };
485
- return [4, this.send({
486
- msg: ackMsg
487
- })];
488
- case 1:
489
- _a.sent();
490
- this.scheduleSendACK();
491
- return [3, 3];
492
- case 2:
493
- e_6 = _a.sent();
494
- if (isRealtimeErrorMessageError(e_6)) {
495
- msg = e_6.payload;
496
- switch (msg.msgData.code) {
497
- case 'CHECK_LOGIN_FAILED':
498
- case 'SIGN_EXPIRED_ERROR':
499
- case 'SIGN_INVALID_ERROR':
500
- case 'SIGN_PARAM_INVALID': {
501
- this.rebuildWatch();
502
- return [2];
503
- }
504
- case 'QUERYID_INVALID_ERROR':
505
- case 'SYS_ERR':
506
- case 'INVALIID_ENV':
507
- case 'COLLECTION_PERMISSION_DENIED': {
508
- this.closeWithError(new CloudSDKError({
509
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_CHECK_LAST_FAIL,
510
- errMsg: msg.msgData.code
511
- }));
512
- return [2];
513
- }
514
- default: {
515
- break;
516
- }
517
- }
518
- }
519
- if (this._availableRetries.CHECK_LAST &&
520
- this._availableRetries.CHECK_LAST > 0) {
521
- this._availableRetries.CHECK_LAST--;
522
- this.scheduleSendACK();
523
- }
524
- else {
525
- this.closeWithError(new CloudSDKError({
526
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_CHECK_LAST_FAIL,
527
- errMsg: e_6
528
- }));
529
- }
530
- return [3, 3];
531
- case 3: return [2];
532
- }
533
- });
534
- }); };
535
- this.handleCommonError = function (e, options) {
536
- if (isRealtimeErrorMessageError(e)) {
537
- var msg = e.payload;
538
- switch (msg.msgData.code) {
539
- case 'CHECK_LOGIN_FAILED':
540
- case 'SIGN_EXPIRED_ERROR':
541
- case 'SIGN_INVALID_ERROR':
542
- case 'SIGN_PARAM_INVALID': {
543
- options.onSignError(e);
544
- return;
545
- }
546
- case 'QUERYID_INVALID_ERROR':
547
- case 'SYS_ERR':
548
- case 'INVALIID_ENV':
549
- case 'COLLECTION_PERMISSION_DENIED': {
550
- options.onNotRetryableError(e);
551
- return;
552
- }
553
- default: {
554
- options.onNotRetryableError(e);
555
- return;
556
- }
557
- }
558
- }
559
- else if (isTimeoutError(e)) {
560
- options.onTimeoutError(e);
561
- return;
562
- }
563
- else if (isCancelledError(e)) {
564
- options.onCancelledError(e);
565
- return;
566
- }
567
- options.onUnknownError(e);
568
- };
569
- this.watchId = "watchid_" + +new Date() + "_" + Math.random();
570
- this.envId = options.envId;
571
- this.collectionName = options.collectionName;
572
- this.query = options.query;
573
- this.limit = options.limit;
574
- this.orderBy = options.orderBy;
575
- this.send = options.send;
576
- this.login = options.login;
577
- this.isWSConnected = options.isWSConnected;
578
- this.onceWSConnected = options.onceWSConnected;
579
- this.getWaitExpectedTimeoutLength = options.getWaitExpectedTimeoutLength;
580
- this.onWatchStart = options.onWatchStart;
581
- this.onWatchClose = options.onWatchClose;
582
- this.debug = options.debug;
583
- this._availableRetries = {
584
- INIT_WATCH: DEFAULT_MAX_AUTO_RETRY_ON_ERROR,
585
- REBUILD_WATCH: DEFAULT_MAX_AUTO_RETRY_ON_ERROR,
586
- CHECK_LAST: DEFAULT_MAX_SEND_ACK_AUTO_RETRY_ON_ERROR
587
- };
588
- this.listener = new RealtimeListener({
589
- close: this.closeWatch,
590
- onChange: options.onChange,
591
- onError: options.onError,
592
- debug: this.debug,
593
- virtualClient: this
594
- });
595
- this.initWatch();
596
- }
597
- VirtualWebSocketClient.prototype.useRetryTicket = function (operationName) {
598
- if (this._availableRetries[operationName] &&
599
- this._availableRetries[operationName] > 0) {
600
- this._availableRetries[operationName]--;
601
- console.log("[realtime] " + operationName + " use a retry ticket, now only " + this._availableRetries[operationName] + " retry left");
602
- return true;
603
- }
604
- return false;
605
- };
606
- VirtualWebSocketClient.prototype.handleServerEvents = function (msg) {
607
- return __awaiter(this, void 0, void 0, function () {
608
- var e_7;
609
- return __generator(this, function (_a) {
610
- switch (_a.label) {
611
- case 0:
612
- _a.trys.push([0, 2, , 3]);
613
- this.scheduleSendACK();
614
- return [4, this._handleServerEvents(msg)];
615
- case 1:
616
- _a.sent();
617
- this._postHandleServerEventsValidityCheck(msg);
618
- return [3, 3];
619
- case 2:
620
- e_7 = _a.sent();
621
- console.error('[realtime listener] internal non-fatal error: handle server events failed with error: ', e_7);
622
- throw e_7;
623
- case 3: return [2];
624
- }
625
- });
626
- });
627
- };
628
- VirtualWebSocketClient.prototype._handleServerEvents = function (msg) {
629
- return __awaiter(this, void 0, void 0, function () {
630
- var requestId, events, msgType, sessionInfo, allChangeEvents, docs, initEncountered, _loop_1, this_1, i, len, state_1;
631
- return __generator(this, function (_a) {
632
- switch (_a.label) {
633
- case 0:
634
- requestId = msg.requestId;
635
- events = msg.msgData.events;
636
- msgType = msg.msgType;
637
- if (!events.length || !this.sessionInfo) {
638
- return [2];
639
- }
640
- sessionInfo = this.sessionInfo;
641
- try {
642
- allChangeEvents = events.map(getPublicEvent);
643
- }
644
- catch (e) {
645
- this.closeWithError(new CloudSDKError({
646
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_RECEIVE_INVALID_SERVER_DATA,
647
- errMsg: e
648
- }));
649
- return [2];
650
- }
651
- docs = __spread(sessionInfo.currentDocs);
652
- initEncountered = false;
653
- _loop_1 = function (i, len) {
654
- var change, localDoc, doc, fieldPath, _a, _b, fieldPath, err, err, doc, doc, err, ind, ind, docsSnapshot, docChanges, snapshot;
655
- var e_8, _c;
656
- return __generator(this, function (_d) {
657
- switch (_d.label) {
658
- case 0:
659
- change = allChangeEvents[i];
660
- if (!(sessionInfo.currentEventId >= change.id)) return [3, 1];
661
- if (!allChangeEvents[i - 1] || change.id > allChangeEvents[i - 1].id) {
662
- console.warn("[realtime] duplicate event received, cur " + sessionInfo.currentEventId + " but got " + change.id);
663
- }
664
- else {
665
- 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 + ")");
666
- }
667
- return [2, "continue"];
668
- case 1:
669
- if (!(sessionInfo.currentEventId === change.id - 1)) return [3, 2];
670
- switch (change.dataType) {
671
- case 'update': {
672
- if (!change.doc) {
673
- switch (change.queueType) {
674
- case 'update':
675
- case 'dequeue': {
676
- localDoc = docs.find(function (doc) { return doc._id === change.docId; });
677
- if (localDoc) {
678
- doc = cloneDeep(localDoc);
679
- if (change.updatedFields) {
680
- for (fieldPath in change.updatedFields) {
681
- set(doc, fieldPath, change.updatedFields[fieldPath]);
682
- }
683
- }
684
- if (change.removedFields) {
685
- try {
686
- for (_a = (e_8 = void 0, __values(change.removedFields)), _b = _a.next(); !_b.done; _b = _a.next()) {
687
- fieldPath = _b.value;
688
- unset(doc, fieldPath);
689
- }
690
- }
691
- catch (e_8_1) { e_8 = { error: e_8_1 }; }
692
- finally {
693
- try {
694
- if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
695
- }
696
- finally { if (e_8) throw e_8.error; }
697
- }
698
- }
699
- change.doc = doc;
700
- }
701
- else {
702
- console.error('[realtime listener] internal non-fatal server error: unexpected update dataType event where no doc is associated.');
703
- }
704
- break;
705
- }
706
- case 'enqueue': {
707
- err = new CloudSDKError({
708
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR,
709
- errMsg: "HandleServerEvents: full doc is not provided with dataType=\"update\" and queueType=\"enqueue\" (requestId " + msg.requestId + ")"
710
- });
711
- this_1.closeWithError(err);
712
- throw err;
713
- }
714
- default: {
715
- break;
716
- }
717
- }
718
- }
719
- break;
720
- }
721
- case 'replace': {
722
- if (!change.doc) {
723
- err = new CloudSDKError({
724
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR,
725
- errMsg: "HandleServerEvents: full doc is not provided with dataType=\"replace\" (requestId " + msg.requestId + ")"
726
- });
727
- this_1.closeWithError(err);
728
- throw err;
729
- }
730
- break;
731
- }
732
- case 'remove': {
733
- doc = docs.find(function (doc) { return doc._id === change.docId; });
734
- if (doc) {
735
- change.doc = doc;
736
- }
737
- else {
738
- console.error('[realtime listener] internal non-fatal server error: unexpected remove event where no doc is associated.');
739
- }
740
- break;
741
- }
742
- case 'limit': {
743
- if (!change.doc) {
744
- switch (change.queueType) {
745
- case 'dequeue': {
746
- doc = docs.find(function (doc) { return doc._id === change.docId; });
747
- if (doc) {
748
- change.doc = doc;
749
- }
750
- else {
751
- console.error('[realtime listener] internal non-fatal server error: unexpected limit dataType event where no doc is associated.');
752
- }
753
- break;
754
- }
755
- case 'enqueue': {
756
- err = new CloudSDKError({
757
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR,
758
- errMsg: "HandleServerEvents: full doc is not provided with dataType=\"limit\" and queueType=\"enqueue\" (requestId " + msg.requestId + ")"
759
- });
760
- this_1.closeWithError(err);
761
- throw err;
762
- }
763
- default: {
764
- break;
765
- }
766
- }
767
- }
768
- break;
769
- }
770
- }
771
- switch (change.queueType) {
772
- case 'init': {
773
- if (!initEncountered) {
774
- initEncountered = true;
775
- docs = [change.doc];
776
- }
777
- else {
778
- docs.push(change.doc);
779
- }
780
- break;
781
- }
782
- case 'enqueue': {
783
- docs.push(change.doc);
784
- break;
785
- }
786
- case 'dequeue': {
787
- ind = docs.findIndex(function (doc) { return doc._id === change.docId; });
788
- if (ind > -1) {
789
- docs.splice(ind, 1);
790
- }
791
- else {
792
- console.error('[realtime listener] internal non-fatal server error: unexpected dequeue event where no doc is associated.');
793
- }
794
- break;
795
- }
796
- case 'update': {
797
- ind = docs.findIndex(function (doc) { return doc._id === change.docId; });
798
- if (ind > -1) {
799
- docs[ind] = change.doc;
800
- }
801
- else {
802
- console.error('[realtime listener] internal non-fatal server error: unexpected queueType update event where no doc is associated.');
803
- }
804
- break;
805
- }
806
- }
807
- if (i === len - 1 ||
808
- (allChangeEvents[i + 1] && allChangeEvents[i + 1].id !== change.id)) {
809
- docsSnapshot = __spread(docs);
810
- docChanges = allChangeEvents
811
- .slice(0, i + 1)
812
- .filter(function (c) { return c.id === change.id; });
813
- this_1.sessionInfo.currentEventId = change.id;
814
- this_1.sessionInfo.currentDocs = docs;
815
- snapshot = new Snapshot({
816
- id: change.id,
817
- docChanges: docChanges,
818
- docs: docsSnapshot,
819
- msgType: msgType
820
- });
821
- this_1.listener.onChange(snapshot);
822
- }
823
- return [3, 4];
824
- case 2:
825
- console.warn("[realtime listener] event received is out of order, cur " + this_1.sessionInfo.currentEventId + " but got " + change.id);
826
- return [4, this_1.rebuildWatch()];
827
- case 3:
828
- _d.sent();
829
- return [2, { value: void 0 }];
830
- case 4: return [2];
831
- }
832
- });
833
- };
834
- this_1 = this;
835
- i = 0, len = allChangeEvents.length;
836
- _a.label = 1;
837
- case 1:
838
- if (!(i < len)) return [3, 4];
839
- return [5, _loop_1(i, len)];
840
- case 2:
841
- state_1 = _a.sent();
842
- if (typeof state_1 === "object")
843
- return [2, state_1.value];
844
- _a.label = 3;
845
- case 3:
846
- i++;
847
- return [3, 1];
848
- case 4: return [2];
849
- }
850
- });
851
- });
852
- };
853
- VirtualWebSocketClient.prototype._postHandleServerEventsValidityCheck = function (msg) {
854
- if (!this.sessionInfo) {
855
- console.error('[realtime listener] internal non-fatal error: sessionInfo lost after server event handling, this should never occur');
856
- return;
857
- }
858
- if (this.sessionInfo.expectEventId &&
859
- this.sessionInfo.currentEventId >= this.sessionInfo.expectEventId) {
860
- this.clearWaitExpectedEvent();
861
- }
862
- if (this.sessionInfo.currentEventId < msg.msgData.currEvent) {
863
- console.warn('[realtime listener] internal non-fatal error: client eventId does not match with server event id after server event handling');
864
- return;
865
- }
866
- };
867
- VirtualWebSocketClient.prototype.clearWaitExpectedEvent = function () {
868
- if (this._waitExpectedTimeoutId) {
869
- clearTimeout(this._waitExpectedTimeoutId);
870
- this._waitExpectedTimeoutId = undefined;
871
- }
872
- };
873
- VirtualWebSocketClient.prototype.onMessage = function (msg) {
874
- var _this = this;
875
- switch (this.watchStatus) {
876
- case WATCH_STATUS.PAUSED: {
877
- if (msg.msgType !== 'ERROR') {
878
- return;
879
- }
880
- break;
881
- }
882
- case WATCH_STATUS.LOGGINGIN:
883
- case WATCH_STATUS.INITING:
884
- case WATCH_STATUS.REBUILDING: {
885
- console.warn("[realtime listener] internal non-fatal error: unexpected message received while " + this.watchStatus);
886
- return;
887
- }
888
- case WATCH_STATUS.CLOSED: {
889
- console.warn('[realtime listener] internal non-fatal error: unexpected message received when the watch has closed');
890
- return;
891
- }
892
- case WATCH_STATUS.ERRORED: {
893
- console.warn('[realtime listener] internal non-fatal error: unexpected message received when the watch has ended with error');
894
- return;
895
- }
896
- }
897
- if (!this.sessionInfo) {
898
- console.warn('[realtime listener] internal non-fatal error: sessionInfo not found while message is received.');
899
- return;
900
- }
901
- this.scheduleSendACK();
902
- switch (msg.msgType) {
903
- case 'NEXT_EVENT': {
904
- console.warn("nextevent " + msg.msgData.currEvent + " ignored", msg);
905
- this.handleServerEvents(msg);
906
- break;
907
- }
908
- case 'CHECK_EVENT': {
909
- if (this.sessionInfo.currentEventId < msg.msgData.currEvent) {
910
- this.sessionInfo.expectEventId = msg.msgData.currEvent;
911
- this.clearWaitExpectedEvent();
912
- this._waitExpectedTimeoutId = setTimeout(function () {
913
- _this.rebuildWatch();
914
- }, this.getWaitExpectedTimeoutLength());
915
- console.log("[realtime] waitExpectedTimeoutLength " + this.getWaitExpectedTimeoutLength());
916
- }
917
- break;
918
- }
919
- case 'ERROR': {
920
- this.closeWithError(new CloudSDKError({
921
- errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_SERVER_ERROR_MSG,
922
- errMsg: msg.msgData.code + " - " + msg.msgData.message
923
- }));
924
- break;
925
- }
926
- default: {
927
- console.warn("[realtime listener] virtual client receive unexpected msg " + msg.msgType + ": ", msg);
928
- break;
929
- }
930
- }
931
- };
932
- VirtualWebSocketClient.prototype.closeWithError = function (error) {
933
- this.watchStatus = WATCH_STATUS.ERRORED;
934
- this.clearACKSchedule();
935
- this.listener.onError(error);
936
- this.onWatchClose(this, (this.sessionInfo && this.sessionInfo.queryID) || '');
937
- console.log("[realtime] client closed (" + this.collectionName + " " + this.query + ") (watchId " + this.watchId + ")");
938
- };
939
- VirtualWebSocketClient.prototype.pause = function () {
940
- this.watchStatus = WATCH_STATUS.PAUSED;
941
- console.log("[realtime] client paused (" + this.collectionName + " " + this.query + ") (watchId " + this.watchId + ")");
942
- };
943
- VirtualWebSocketClient.prototype.resume = function () {
944
- return __awaiter(this, void 0, void 0, function () {
945
- var e_9;
946
- return __generator(this, function (_a) {
947
- switch (_a.label) {
948
- case 0:
949
- this.watchStatus = WATCH_STATUS.RESUMING;
950
- console.log("[realtime] client resuming with " + (this.sessionInfo ? 'REBUILD_WATCH' : 'INIT_WATCH') + " (" + this.collectionName + " " + this.query + ") (" + this.watchId + ")");
951
- _a.label = 1;
952
- case 1:
953
- _a.trys.push([1, 3, , 4]);
954
- return [4, (this.sessionInfo ? this.rebuildWatch() : this.initWatch())];
955
- case 2:
956
- _a.sent();
957
- console.log("[realtime] client successfully resumed (" + this.collectionName + " " + this.query + ") (" + this.watchId + ")");
958
- return [3, 4];
959
- case 3:
960
- e_9 = _a.sent();
961
- console.error("[realtime] client resume failed (" + this.collectionName + " " + this.query + ") (" + this.watchId + ")", e_9);
962
- return [3, 4];
963
- case 4: return [2];
964
- }
965
- });
966
- });
967
- };
968
- return VirtualWebSocketClient;
969
- }());
970
- export { VirtualWebSocketClient };
971
- function getPublicEvent(event) {
972
- var e = {
973
- id: event.ID,
974
- dataType: event.DataType,
975
- queueType: event.QueueType,
976
- docId: event.DocID,
977
- doc: event.Doc && event.Doc !== '{}' ? JSON.parse(event.Doc) : undefined
978
- };
979
- if (event.DataType === 'update') {
980
- if (event.UpdatedFields) {
981
- e.updatedFields = JSON.parse(event.UpdatedFields);
982
- }
983
- if (event.removedFields || event.RemovedFields) {
984
- e.removedFields = JSON.parse(event.removedFields);
985
- }
986
- }
987
- return e;
988
- }
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 { CloudSDKError, isTimeoutError, CancelledError, isCancelledError } from '../utils/error';
6
+ import { ERR_CODE } from '../config/error.config';
7
+ import { sleep } from '../utils/utils';
8
+ import { RealtimeListener } from './listener';
9
+ import { Snapshot } from './snapshot';
10
+ import { isRealtimeErrorMessageError } from './error';
11
+ var WATCH_STATUS;
12
+ (function (WATCH_STATUS) {
13
+ WATCH_STATUS["LOGGINGIN"] = "LOGGINGIN";
14
+ WATCH_STATUS["INITING"] = "INITING";
15
+ WATCH_STATUS["REBUILDING"] = "REBUILDING";
16
+ WATCH_STATUS["ACTIVE"] = "ACTIVE";
17
+ WATCH_STATUS["ERRORED"] = "ERRORED";
18
+ WATCH_STATUS["CLOSING"] = "CLOSING";
19
+ WATCH_STATUS["CLOSED"] = "CLOSED";
20
+ WATCH_STATUS["PAUSED"] = "PAUSED";
21
+ WATCH_STATUS["RESUMING"] = "RESUMING";
22
+ })(WATCH_STATUS || (WATCH_STATUS = {}));
23
+ const DEFAULT_WAIT_TIME_ON_UNKNOWN_ERROR = 100;
24
+ const DEFAULT_MAX_AUTO_RETRY_ON_ERROR = 2;
25
+ const DEFAULT_MAX_SEND_ACK_AUTO_RETRY_ON_ERROR = 2;
26
+ const DEFAULT_SEND_ACK_DEBOUNCE_TIMEOUT = 10 * 1000;
27
+ const DEFAULT_INIT_WATCH_TIMEOUT = 10 * 1000;
28
+ const DEFAULT_REBUILD_WATCH_TIMEOUT = 10 * 1000;
29
+ export class VirtualWebSocketClient {
30
+ constructor(options) {
31
+ this.watchStatus = WATCH_STATUS.INITING;
32
+ this._login = async (envId, refresh) => {
33
+ this.watchStatus = WATCH_STATUS.LOGGINGIN;
34
+ const loginResult = await this.login(envId, refresh);
35
+ if (!this.envId) {
36
+ this.envId = loginResult.envId;
37
+ }
38
+ return loginResult;
39
+ };
40
+ this.initWatch = async (forceRefreshLogin) => {
41
+ if (this._initWatchPromise) {
42
+ return this._initWatchPromise;
43
+ }
44
+ this._initWatchPromise = new Promise(async (resolve, reject) => {
45
+ try {
46
+ if (this.watchStatus === WATCH_STATUS.PAUSED) {
47
+ console.log('[realtime] initWatch cancelled on pause');
48
+ return resolve();
49
+ }
50
+ const { envId } = await this._login(this.envId, forceRefreshLogin);
51
+ if (this.watchStatus === WATCH_STATUS.PAUSED) {
52
+ console.log('[realtime] initWatch cancelled on pause');
53
+ return resolve();
54
+ }
55
+ this.watchStatus = WATCH_STATUS.INITING;
56
+ const initWatchMsg = {
57
+ watchId: this.watchId,
58
+ requestId: genRequestId(),
59
+ msgType: 'INIT_WATCH',
60
+ msgData: {
61
+ envId,
62
+ collName: this.collectionName,
63
+ query: this.query,
64
+ limit: this.limit,
65
+ orderBy: this.orderBy
66
+ }
67
+ };
68
+ const initEventMsg = await this.send({
69
+ msg: initWatchMsg,
70
+ waitResponse: true,
71
+ skipOnMessage: true,
72
+ timeout: DEFAULT_INIT_WATCH_TIMEOUT
73
+ });
74
+ const { events, currEvent } = initEventMsg.msgData;
75
+ this.sessionInfo = {
76
+ queryID: initEventMsg.msgData.queryID,
77
+ currentEventId: currEvent - 1,
78
+ currentDocs: []
79
+ };
80
+ if (events.length > 0) {
81
+ for (const e of events) {
82
+ e.ID = currEvent;
83
+ }
84
+ this.handleServerEvents(initEventMsg);
85
+ }
86
+ else {
87
+ this.sessionInfo.currentEventId = currEvent;
88
+ const snapshot = new Snapshot({
89
+ id: currEvent,
90
+ docChanges: [],
91
+ docs: [],
92
+ type: 'init'
93
+ });
94
+ this.listener.onChange(snapshot);
95
+ this.scheduleSendACK();
96
+ }
97
+ this.onWatchStart(this, this.sessionInfo.queryID);
98
+ this.watchStatus = WATCH_STATUS.ACTIVE;
99
+ this._availableRetries.INIT_WATCH = DEFAULT_MAX_AUTO_RETRY_ON_ERROR;
100
+ resolve();
101
+ }
102
+ catch (e) {
103
+ this.handleWatchEstablishmentError(e, {
104
+ operationName: 'INIT_WATCH',
105
+ resolve,
106
+ reject
107
+ });
108
+ }
109
+ });
110
+ let success = false;
111
+ try {
112
+ await this._initWatchPromise;
113
+ success = true;
114
+ }
115
+ finally {
116
+ this._initWatchPromise = undefined;
117
+ }
118
+ console.log(`[realtime] initWatch ${success ? 'success' : 'fail'}`);
119
+ };
120
+ this.rebuildWatch = async (forceRefreshLogin) => {
121
+ if (this._rebuildWatchPromise) {
122
+ return this._rebuildWatchPromise;
123
+ }
124
+ this._rebuildWatchPromise = new Promise(async (resolve, reject) => {
125
+ try {
126
+ if (this.watchStatus === WATCH_STATUS.PAUSED) {
127
+ console.log('[realtime] rebuildWatch cancelled on pause');
128
+ return resolve();
129
+ }
130
+ const { envId } = await this._login(this.envId, forceRefreshLogin);
131
+ if (!this.sessionInfo) {
132
+ throw new Error('can not rebuildWatch without a successful initWatch (lack of sessionInfo)');
133
+ }
134
+ if (this.watchStatus === WATCH_STATUS.PAUSED) {
135
+ console.log('[realtime] rebuildWatch cancelled on pause');
136
+ return resolve();
137
+ }
138
+ this.watchStatus = WATCH_STATUS.REBUILDING;
139
+ const rebuildWatchMsg = {
140
+ watchId: this.watchId,
141
+ requestId: genRequestId(),
142
+ msgType: 'REBUILD_WATCH',
143
+ msgData: {
144
+ envId,
145
+ collName: this.collectionName,
146
+ queryID: this.sessionInfo.queryID,
147
+ eventID: this.sessionInfo.currentEventId
148
+ }
149
+ };
150
+ const nextEventMsg = await this.send({
151
+ msg: rebuildWatchMsg,
152
+ waitResponse: true,
153
+ skipOnMessage: false,
154
+ timeout: DEFAULT_REBUILD_WATCH_TIMEOUT
155
+ });
156
+ this.handleServerEvents(nextEventMsg);
157
+ this.watchStatus = WATCH_STATUS.ACTIVE;
158
+ this._availableRetries.REBUILD_WATCH = DEFAULT_MAX_AUTO_RETRY_ON_ERROR;
159
+ resolve();
160
+ }
161
+ catch (e) {
162
+ this.handleWatchEstablishmentError(e, {
163
+ operationName: 'REBUILD_WATCH',
164
+ resolve,
165
+ reject
166
+ });
167
+ }
168
+ });
169
+ let success = false;
170
+ try {
171
+ await this._rebuildWatchPromise;
172
+ success = true;
173
+ }
174
+ finally {
175
+ this._rebuildWatchPromise = undefined;
176
+ }
177
+ console.log(`[realtime] rebuildWatch ${success ? 'success' : 'fail'}`);
178
+ };
179
+ this.handleWatchEstablishmentError = async (e, options) => {
180
+ const isInitWatch = options.operationName === 'INIT_WATCH';
181
+ const abortWatch = () => {
182
+ this.closeWithError(new CloudSDKError({
183
+ errCode: isInitWatch
184
+ ? ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_INIT_WATCH_FAIL
185
+ : ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_REBUILD_WATCH_FAIL,
186
+ errMsg: e
187
+ }));
188
+ options.reject(e);
189
+ };
190
+ const retry = (refreshLogin) => {
191
+ if (this.useRetryTicket(options.operationName)) {
192
+ if (isInitWatch) {
193
+ this._initWatchPromise = undefined;
194
+ options.resolve(this.initWatch(refreshLogin));
195
+ }
196
+ else {
197
+ this._rebuildWatchPromise = undefined;
198
+ options.resolve(this.rebuildWatch(refreshLogin));
199
+ }
200
+ }
201
+ else {
202
+ abortWatch();
203
+ }
204
+ };
205
+ this.handleCommonError(e, {
206
+ onSignError: () => retry(true),
207
+ onTimeoutError: () => retry(false),
208
+ onNotRetryableError: abortWatch,
209
+ onCancelledError: options.reject,
210
+ onUnknownError: async () => {
211
+ try {
212
+ const onWSDisconnected = async () => {
213
+ this.pause();
214
+ await this.onceWSConnected();
215
+ retry(true);
216
+ };
217
+ if (!this.isWSConnected()) {
218
+ await onWSDisconnected();
219
+ }
220
+ else {
221
+ await sleep(DEFAULT_WAIT_TIME_ON_UNKNOWN_ERROR);
222
+ if (this.watchStatus === WATCH_STATUS.PAUSED) {
223
+ options.reject(new CancelledError(`${options.operationName} cancelled due to pause after unknownError`));
224
+ }
225
+ else if (!this.isWSConnected()) {
226
+ await onWSDisconnected();
227
+ }
228
+ else {
229
+ retry(false);
230
+ }
231
+ }
232
+ }
233
+ catch (e) {
234
+ retry(true);
235
+ }
236
+ }
237
+ });
238
+ };
239
+ this.closeWatch = async () => {
240
+ const queryId = this.sessionInfo ? this.sessionInfo.queryID : '';
241
+ if (this.watchStatus !== WATCH_STATUS.ACTIVE) {
242
+ this.watchStatus = WATCH_STATUS.CLOSED;
243
+ this.onWatchClose(this, queryId);
244
+ return;
245
+ }
246
+ try {
247
+ this.watchStatus = WATCH_STATUS.CLOSING;
248
+ const closeWatchMsg = {
249
+ watchId: this.watchId,
250
+ requestId: genRequestId(),
251
+ msgType: 'CLOSE_WATCH',
252
+ msgData: null
253
+ };
254
+ await this.send({
255
+ msg: closeWatchMsg
256
+ });
257
+ this.sessionInfo = undefined;
258
+ this.watchStatus = WATCH_STATUS.CLOSED;
259
+ }
260
+ catch (e) {
261
+ this.closeWithError(new CloudSDKError({
262
+ errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_CLOSE_WATCH_FAIL,
263
+ errMsg: e
264
+ }));
265
+ }
266
+ finally {
267
+ this.onWatchClose(this, queryId);
268
+ }
269
+ };
270
+ this.scheduleSendACK = () => {
271
+ this.clearACKSchedule();
272
+ this._ackTimeoutId = setTimeout(() => {
273
+ if (this._waitExpectedTimeoutId) {
274
+ this.scheduleSendACK();
275
+ }
276
+ else {
277
+ this.sendACK();
278
+ }
279
+ }, DEFAULT_SEND_ACK_DEBOUNCE_TIMEOUT);
280
+ };
281
+ this.clearACKSchedule = () => {
282
+ if (this._ackTimeoutId) {
283
+ clearTimeout(this._ackTimeoutId);
284
+ }
285
+ };
286
+ this.sendACK = async () => {
287
+ try {
288
+ if (this.watchStatus !== WATCH_STATUS.ACTIVE) {
289
+ this.scheduleSendACK();
290
+ return;
291
+ }
292
+ if (!this.sessionInfo) {
293
+ console.warn('[realtime listener] can not send ack without a successful initWatch (lack of sessionInfo)');
294
+ return;
295
+ }
296
+ const ackMsg = {
297
+ watchId: this.watchId,
298
+ requestId: genRequestId(),
299
+ msgType: 'CHECK_LAST',
300
+ msgData: {
301
+ queryID: this.sessionInfo.queryID,
302
+ eventID: this.sessionInfo.currentEventId
303
+ }
304
+ };
305
+ await this.send({
306
+ msg: ackMsg
307
+ });
308
+ this.scheduleSendACK();
309
+ }
310
+ catch (e) {
311
+ if (isRealtimeErrorMessageError(e)) {
312
+ const msg = e.payload;
313
+ switch (msg.msgData.code) {
314
+ case 'CHECK_LOGIN_FAILED':
315
+ case 'SIGN_EXPIRED_ERROR':
316
+ case 'SIGN_INVALID_ERROR':
317
+ case 'SIGN_PARAM_INVALID': {
318
+ this.rebuildWatch();
319
+ return;
320
+ }
321
+ case 'QUERYID_INVALID_ERROR':
322
+ case 'SYS_ERR':
323
+ case 'INVALIID_ENV':
324
+ case 'COLLECTION_PERMISSION_DENIED': {
325
+ this.closeWithError(new CloudSDKError({
326
+ errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_CHECK_LAST_FAIL,
327
+ errMsg: msg.msgData.code
328
+ }));
329
+ return;
330
+ }
331
+ default: {
332
+ break;
333
+ }
334
+ }
335
+ }
336
+ if (this._availableRetries.CHECK_LAST &&
337
+ this._availableRetries.CHECK_LAST > 0) {
338
+ this._availableRetries.CHECK_LAST--;
339
+ this.scheduleSendACK();
340
+ }
341
+ else {
342
+ this.closeWithError(new CloudSDKError({
343
+ errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_CHECK_LAST_FAIL,
344
+ errMsg: e
345
+ }));
346
+ }
347
+ }
348
+ };
349
+ this.handleCommonError = (e, options) => {
350
+ if (isRealtimeErrorMessageError(e)) {
351
+ const msg = e.payload;
352
+ switch (msg.msgData.code) {
353
+ case 'CHECK_LOGIN_FAILED':
354
+ case 'SIGN_EXPIRED_ERROR':
355
+ case 'SIGN_INVALID_ERROR':
356
+ case 'SIGN_PARAM_INVALID': {
357
+ options.onSignError(e);
358
+ return;
359
+ }
360
+ case 'QUERYID_INVALID_ERROR':
361
+ case 'SYS_ERR':
362
+ case 'INVALIID_ENV':
363
+ case 'COLLECTION_PERMISSION_DENIED': {
364
+ options.onNotRetryableError(e);
365
+ return;
366
+ }
367
+ default: {
368
+ options.onNotRetryableError(e);
369
+ return;
370
+ }
371
+ }
372
+ }
373
+ else if (isTimeoutError(e)) {
374
+ options.onTimeoutError(e);
375
+ return;
376
+ }
377
+ else if (isCancelledError(e)) {
378
+ options.onCancelledError(e);
379
+ return;
380
+ }
381
+ options.onUnknownError(e);
382
+ };
383
+ this.watchId = `watchid_${+new Date()}_${Math.random()}`;
384
+ this.envId = options.envId;
385
+ this.collectionName = options.collectionName;
386
+ this.query = options.query;
387
+ this.limit = options.limit;
388
+ this.orderBy = options.orderBy;
389
+ this.send = options.send;
390
+ this.login = options.login;
391
+ this.isWSConnected = options.isWSConnected;
392
+ this.onceWSConnected = options.onceWSConnected;
393
+ this.getWaitExpectedTimeoutLength = options.getWaitExpectedTimeoutLength;
394
+ this.onWatchStart = options.onWatchStart;
395
+ this.onWatchClose = options.onWatchClose;
396
+ this.debug = options.debug;
397
+ this._availableRetries = {
398
+ INIT_WATCH: DEFAULT_MAX_AUTO_RETRY_ON_ERROR,
399
+ REBUILD_WATCH: DEFAULT_MAX_AUTO_RETRY_ON_ERROR,
400
+ CHECK_LAST: DEFAULT_MAX_SEND_ACK_AUTO_RETRY_ON_ERROR
401
+ };
402
+ this.listener = new RealtimeListener({
403
+ close: this.closeWatch,
404
+ onChange: options.onChange,
405
+ onError: options.onError,
406
+ debug: this.debug,
407
+ virtualClient: this
408
+ });
409
+ this.initWatch();
410
+ }
411
+ useRetryTicket(operationName) {
412
+ if (this._availableRetries[operationName] &&
413
+ this._availableRetries[operationName] > 0) {
414
+ this._availableRetries[operationName]--;
415
+ console.log(`[realtime] ${operationName} use a retry ticket, now only ${this._availableRetries[operationName]} retry left`);
416
+ return true;
417
+ }
418
+ return false;
419
+ }
420
+ async handleServerEvents(msg) {
421
+ try {
422
+ this.scheduleSendACK();
423
+ await this._handleServerEvents(msg);
424
+ this._postHandleServerEventsValidityCheck(msg);
425
+ }
426
+ catch (e) {
427
+ console.error('[realtime listener] internal non-fatal error: handle server events failed with error: ', e);
428
+ throw e;
429
+ }
430
+ }
431
+ async _handleServerEvents(msg) {
432
+ const { requestId } = msg;
433
+ const { events } = msg.msgData;
434
+ const { msgType } = msg;
435
+ if (!events.length || !this.sessionInfo) {
436
+ return;
437
+ }
438
+ const sessionInfo = this.sessionInfo;
439
+ let allChangeEvents;
440
+ try {
441
+ allChangeEvents = events.map(getPublicEvent);
442
+ }
443
+ catch (e) {
444
+ this.closeWithError(new CloudSDKError({
445
+ errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_RECEIVE_INVALID_SERVER_DATA,
446
+ errMsg: e
447
+ }));
448
+ return;
449
+ }
450
+ let docs = [...sessionInfo.currentDocs];
451
+ let initEncountered = false;
452
+ for (let i = 0, len = allChangeEvents.length; i < len; i++) {
453
+ const change = allChangeEvents[i];
454
+ if (sessionInfo.currentEventId >= change.id) {
455
+ if (!allChangeEvents[i - 1] || change.id > allChangeEvents[i - 1].id) {
456
+ console.warn(`[realtime] duplicate event received, cur ${sessionInfo.currentEventId} but got ${change.id}`);
457
+ }
458
+ else {
459
+ 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})`);
460
+ }
461
+ continue;
462
+ }
463
+ else if (sessionInfo.currentEventId === change.id - 1) {
464
+ switch (change.dataType) {
465
+ case 'update': {
466
+ if (!change.doc) {
467
+ switch (change.queueType) {
468
+ case 'update':
469
+ case 'dequeue': {
470
+ const localDoc = docs.find(doc => doc._id === change.docId);
471
+ if (localDoc) {
472
+ const doc = cloneDeep(localDoc);
473
+ if (change.updatedFields) {
474
+ for (const fieldPath in change.updatedFields) {
475
+ set(doc, fieldPath, change.updatedFields[fieldPath]);
476
+ }
477
+ }
478
+ if (change.removedFields) {
479
+ for (const fieldPath of change.removedFields) {
480
+ unset(doc, fieldPath);
481
+ }
482
+ }
483
+ change.doc = doc;
484
+ }
485
+ else {
486
+ console.error('[realtime listener] internal non-fatal server error: unexpected update dataType event where no doc is associated.');
487
+ }
488
+ break;
489
+ }
490
+ case 'enqueue': {
491
+ const err = new CloudSDKError({
492
+ errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR,
493
+ errMsg: `HandleServerEvents: full doc is not provided with dataType="update" and queueType="enqueue" (requestId ${msg.requestId})`
494
+ });
495
+ this.closeWithError(err);
496
+ throw err;
497
+ }
498
+ default: {
499
+ break;
500
+ }
501
+ }
502
+ }
503
+ break;
504
+ }
505
+ case 'replace': {
506
+ if (!change.doc) {
507
+ const err = new CloudSDKError({
508
+ errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR,
509
+ errMsg: `HandleServerEvents: full doc is not provided with dataType="replace" (requestId ${msg.requestId})`
510
+ });
511
+ this.closeWithError(err);
512
+ throw err;
513
+ }
514
+ break;
515
+ }
516
+ case 'remove': {
517
+ const doc = docs.find(doc => doc._id === change.docId);
518
+ if (doc) {
519
+ change.doc = doc;
520
+ }
521
+ else {
522
+ console.error('[realtime listener] internal non-fatal server error: unexpected remove event where no doc is associated.');
523
+ }
524
+ break;
525
+ }
526
+ case 'limit': {
527
+ if (!change.doc) {
528
+ switch (change.queueType) {
529
+ case 'dequeue': {
530
+ const doc = docs.find(doc => doc._id === change.docId);
531
+ if (doc) {
532
+ change.doc = doc;
533
+ }
534
+ else {
535
+ console.error('[realtime listener] internal non-fatal server error: unexpected limit dataType event where no doc is associated.');
536
+ }
537
+ break;
538
+ }
539
+ case 'enqueue': {
540
+ const err = new CloudSDKError({
541
+ errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR,
542
+ errMsg: `HandleServerEvents: full doc is not provided with dataType="limit" and queueType="enqueue" (requestId ${msg.requestId})`
543
+ });
544
+ this.closeWithError(err);
545
+ throw err;
546
+ }
547
+ default: {
548
+ break;
549
+ }
550
+ }
551
+ }
552
+ break;
553
+ }
554
+ }
555
+ switch (change.queueType) {
556
+ case 'init': {
557
+ if (!initEncountered) {
558
+ initEncountered = true;
559
+ docs = [change.doc];
560
+ }
561
+ else {
562
+ docs.push(change.doc);
563
+ }
564
+ break;
565
+ }
566
+ case 'enqueue': {
567
+ docs.push(change.doc);
568
+ break;
569
+ }
570
+ case 'dequeue': {
571
+ const ind = docs.findIndex(doc => doc._id === change.docId);
572
+ if (ind > -1) {
573
+ docs.splice(ind, 1);
574
+ }
575
+ else {
576
+ console.error('[realtime listener] internal non-fatal server error: unexpected dequeue event where no doc is associated.');
577
+ }
578
+ break;
579
+ }
580
+ case 'update': {
581
+ const ind = docs.findIndex(doc => doc._id === change.docId);
582
+ if (ind > -1) {
583
+ docs[ind] = change.doc;
584
+ }
585
+ else {
586
+ console.error('[realtime listener] internal non-fatal server error: unexpected queueType update event where no doc is associated.');
587
+ }
588
+ break;
589
+ }
590
+ }
591
+ if (i === len - 1 ||
592
+ (allChangeEvents[i + 1] && allChangeEvents[i + 1].id !== change.id)) {
593
+ const docsSnapshot = [...docs];
594
+ const docChanges = allChangeEvents
595
+ .slice(0, i + 1)
596
+ .filter(c => c.id === change.id);
597
+ this.sessionInfo.currentEventId = change.id;
598
+ this.sessionInfo.currentDocs = docs;
599
+ const snapshot = new Snapshot({
600
+ id: change.id,
601
+ docChanges,
602
+ docs: docsSnapshot,
603
+ msgType
604
+ });
605
+ this.listener.onChange(snapshot);
606
+ }
607
+ }
608
+ else {
609
+ console.warn(`[realtime listener] event received is out of order, cur ${this.sessionInfo.currentEventId} but got ${change.id}`);
610
+ await this.rebuildWatch();
611
+ return;
612
+ }
613
+ }
614
+ }
615
+ _postHandleServerEventsValidityCheck(msg) {
616
+ if (!this.sessionInfo) {
617
+ console.error('[realtime listener] internal non-fatal error: sessionInfo lost after server event handling, this should never occur');
618
+ return;
619
+ }
620
+ if (this.sessionInfo.expectEventId &&
621
+ this.sessionInfo.currentEventId >= this.sessionInfo.expectEventId) {
622
+ this.clearWaitExpectedEvent();
623
+ }
624
+ if (this.sessionInfo.currentEventId < msg.msgData.currEvent) {
625
+ console.warn('[realtime listener] internal non-fatal error: client eventId does not match with server event id after server event handling');
626
+ return;
627
+ }
628
+ }
629
+ clearWaitExpectedEvent() {
630
+ if (this._waitExpectedTimeoutId) {
631
+ clearTimeout(this._waitExpectedTimeoutId);
632
+ this._waitExpectedTimeoutId = undefined;
633
+ }
634
+ }
635
+ onMessage(msg) {
636
+ switch (this.watchStatus) {
637
+ case WATCH_STATUS.PAUSED: {
638
+ if (msg.msgType !== 'ERROR') {
639
+ return;
640
+ }
641
+ break;
642
+ }
643
+ case WATCH_STATUS.LOGGINGIN:
644
+ case WATCH_STATUS.INITING:
645
+ case WATCH_STATUS.REBUILDING: {
646
+ console.warn(`[realtime listener] internal non-fatal error: unexpected message received while ${this.watchStatus}`);
647
+ return;
648
+ }
649
+ case WATCH_STATUS.CLOSED: {
650
+ console.warn('[realtime listener] internal non-fatal error: unexpected message received when the watch has closed');
651
+ return;
652
+ }
653
+ case WATCH_STATUS.ERRORED: {
654
+ console.warn('[realtime listener] internal non-fatal error: unexpected message received when the watch has ended with error');
655
+ return;
656
+ }
657
+ }
658
+ if (!this.sessionInfo) {
659
+ console.warn('[realtime listener] internal non-fatal error: sessionInfo not found while message is received.');
660
+ return;
661
+ }
662
+ this.scheduleSendACK();
663
+ switch (msg.msgType) {
664
+ case 'NEXT_EVENT': {
665
+ console.warn(`nextevent ${msg.msgData.currEvent} ignored`, msg);
666
+ this.handleServerEvents(msg);
667
+ break;
668
+ }
669
+ case 'CHECK_EVENT': {
670
+ if (this.sessionInfo.currentEventId < msg.msgData.currEvent) {
671
+ this.sessionInfo.expectEventId = msg.msgData.currEvent;
672
+ this.clearWaitExpectedEvent();
673
+ this._waitExpectedTimeoutId = setTimeout(() => {
674
+ this.rebuildWatch();
675
+ }, this.getWaitExpectedTimeoutLength());
676
+ console.log(`[realtime] waitExpectedTimeoutLength ${this.getWaitExpectedTimeoutLength()}`);
677
+ }
678
+ break;
679
+ }
680
+ case 'ERROR': {
681
+ this.closeWithError(new CloudSDKError({
682
+ errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_SERVER_ERROR_MSG,
683
+ errMsg: `${msg.msgData.code} - ${msg.msgData.message}`
684
+ }));
685
+ break;
686
+ }
687
+ default: {
688
+ console.warn(`[realtime listener] virtual client receive unexpected msg ${msg.msgType}: `, msg);
689
+ break;
690
+ }
691
+ }
692
+ }
693
+ closeWithError(error) {
694
+ this.watchStatus = WATCH_STATUS.ERRORED;
695
+ this.clearACKSchedule();
696
+ this.listener.onError(error);
697
+ this.onWatchClose(this, (this.sessionInfo && this.sessionInfo.queryID) || '');
698
+ console.log(`[realtime] client closed (${this.collectionName} ${this.query}) (watchId ${this.watchId})`);
699
+ }
700
+ pause() {
701
+ this.watchStatus = WATCH_STATUS.PAUSED;
702
+ console.log(`[realtime] client paused (${this.collectionName} ${this.query}) (watchId ${this.watchId})`);
703
+ }
704
+ async resume() {
705
+ this.watchStatus = WATCH_STATUS.RESUMING;
706
+ console.log(`[realtime] client resuming with ${this.sessionInfo ? 'REBUILD_WATCH' : 'INIT_WATCH'} (${this.collectionName} ${this.query}) (${this.watchId})`);
707
+ try {
708
+ await (this.sessionInfo ? this.rebuildWatch() : this.initWatch());
709
+ console.log(`[realtime] client successfully resumed (${this.collectionName} ${this.query}) (${this.watchId})`);
710
+ }
711
+ catch (e) {
712
+ console.error(`[realtime] client resume failed (${this.collectionName} ${this.query}) (${this.watchId})`, e);
713
+ }
714
+ }
715
+ }
716
+ function getPublicEvent(event) {
717
+ const e = {
718
+ id: event.ID,
719
+ dataType: event.DataType,
720
+ queueType: event.QueueType,
721
+ docId: event.DocID,
722
+ doc: event.Doc && event.Doc !== '{}' ? JSON.parse(event.Doc) : undefined
723
+ };
724
+ if (event.DataType === 'update') {
725
+ if (event.UpdatedFields) {
726
+ e.updatedFields = JSON.parse(event.UpdatedFields);
727
+ }
728
+ if (event.removedFields || event.RemovedFields) {
729
+ e.removedFields = JSON.parse(event.removedFields);
730
+ }
731
+ }
732
+ return e;
733
+ }