@rc-ex/ws 1.3.1 → 1.3.2
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/lib/exceptions/ClosedException.js +4 -21
- package/lib/exceptions/ClosedException.js.map +1 -1
- package/lib/exceptions/ConnectionException.js +10 -27
- package/lib/exceptions/ConnectionException.js.map +1 -1
- package/lib/exceptions/TimeoutException.js +4 -21
- package/lib/exceptions/TimeoutException.js.map +1 -1
- package/lib/index.js +301 -482
- package/lib/index.js.map +1 -1
- package/lib/rest.js +46 -96
- package/lib/rest.js.map +1 -1
- package/lib/subscription.js +69 -151
- package/lib/subscription.js.map +1 -1
- package/lib/utils.js +33 -77
- package/lib/utils.js.map +1 -1
- package/package.json +3 -3
- package/tsconfig.json +5 -1
package/lib/index.js
CHANGED
|
@@ -1,73 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __extends = (this && this.__extends) || (function () {
|
|
3
|
-
var extendStatics = function (d, b) {
|
|
4
|
-
extendStatics = Object.setPrototypeOf ||
|
|
5
|
-
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
-
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
7
|
-
return extendStatics(d, b);
|
|
8
|
-
};
|
|
9
|
-
return function (d, b) {
|
|
10
|
-
if (typeof b !== "function" && b !== null)
|
|
11
|
-
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
12
|
-
extendStatics(d, b);
|
|
13
|
-
function __() { this.constructor = d; }
|
|
14
|
-
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
15
|
-
};
|
|
16
|
-
})();
|
|
17
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
18
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
19
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
20
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
21
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
22
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
23
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
24
|
-
});
|
|
25
|
-
};
|
|
26
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
27
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
28
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
29
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
30
|
-
function step(op) {
|
|
31
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
32
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
33
|
-
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;
|
|
34
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
35
|
-
switch (op[0]) {
|
|
36
|
-
case 0: case 1: t = op; break;
|
|
37
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
38
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
39
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
40
|
-
default:
|
|
41
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
42
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
43
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
44
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
45
|
-
if (t[2]) _.ops.pop();
|
|
46
|
-
_.trys.pop(); continue;
|
|
47
|
-
}
|
|
48
|
-
op = body.call(thisArg, _);
|
|
49
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
50
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
54
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
55
4
|
};
|
|
56
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
6
|
exports.Events = void 0;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
7
|
+
const SdkExtension_js_1 = __importDefault(require("@rc-ex/core/lib/esm/SdkExtension.js"));
|
|
8
|
+
const isomorphic_ws_1 = __importDefault(require("isomorphic-ws"));
|
|
9
|
+
const hyperid_1 = __importDefault(require("hyperid"));
|
|
10
|
+
const events_1 = require("events");
|
|
11
|
+
const wait_for_async_1 = __importDefault(require("wait-for-async"));
|
|
12
|
+
const RestException_js_1 = __importDefault(require("@rc-ex/core/lib/esm/RestException.js"));
|
|
13
|
+
const rest_js_1 = require("./rest.js");
|
|
14
|
+
const subscription_js_1 = __importDefault(require("./subscription.js"));
|
|
15
|
+
const ConnectionException_js_1 = __importDefault(require("./exceptions/ConnectionException.js"));
|
|
16
|
+
const utils_js_1 = __importDefault(require("./utils.js"));
|
|
17
|
+
const CONNECTING = 0;
|
|
18
|
+
const OPEN = 1;
|
|
19
|
+
const uuid = (0, hyperid_1.default)();
|
|
71
20
|
var Events;
|
|
72
21
|
(function (Events) {
|
|
73
22
|
Events["autoRecoverSuccess"] = "autoRecoverSuccess";
|
|
@@ -77,439 +26,309 @@ var Events;
|
|
|
77
26
|
Events["newWsc"] = "newWsc";
|
|
78
27
|
Events["connectionReady"] = "connectionReady";
|
|
79
28
|
})(Events || (exports.Events = Events = {}));
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
29
|
+
class WebSocketExtension extends SdkExtension_js_1.default {
|
|
30
|
+
eventEmitter = new events_1.EventEmitter();
|
|
31
|
+
options;
|
|
32
|
+
rc;
|
|
33
|
+
wsToken;
|
|
34
|
+
wsTokenExpiresAt = 0;
|
|
35
|
+
ws;
|
|
36
|
+
connectionDetails;
|
|
37
|
+
wsc;
|
|
38
|
+
subscription;
|
|
39
|
+
// for auto recover
|
|
40
|
+
intervalHandle;
|
|
41
|
+
recoverTimestamp;
|
|
42
|
+
pingServerHandle;
|
|
43
|
+
_recoverPromise;
|
|
44
|
+
_connectPromise;
|
|
45
|
+
request = rest_js_1.request; // request method was moved to another file to keep this file short
|
|
46
|
+
constructor(options = {}) {
|
|
47
|
+
super();
|
|
48
|
+
this.options = options;
|
|
49
|
+
this.options.restOverWebSocket ??= false;
|
|
50
|
+
this.options.debugMode ??= false;
|
|
51
|
+
this.options.autoRecover ??= {
|
|
94
52
|
enabled: true,
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
53
|
+
};
|
|
54
|
+
this.options.autoRecover.checkInterval ??= (retriesAttempted) => {
|
|
55
|
+
const interval = 2000 + 2000 * retriesAttempted;
|
|
98
56
|
return Math.min(8000, interval);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return _this;
|
|
57
|
+
};
|
|
58
|
+
this.options.autoRecover.pingServerInterval ??= 60000;
|
|
102
59
|
}
|
|
103
|
-
|
|
104
|
-
|
|
60
|
+
disable() {
|
|
61
|
+
super.disable();
|
|
105
62
|
if (this.subscription) {
|
|
106
63
|
this.subscription.enabled = false;
|
|
107
64
|
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
this.rc = rc;
|
|
117
|
-
if (this.options.restOverWebSocket) {
|
|
118
|
-
request_1 = rc.request.bind(rc);
|
|
119
|
-
rc.request = function (method, endpoint, content, queryParams, config) { return __awaiter(_this, void 0, void 0, function () {
|
|
120
|
-
var _a, _b, _c;
|
|
121
|
-
return __generator(this, function (_d) {
|
|
122
|
-
if (!this.enabled || !this.options.restOverWebSocket) {
|
|
123
|
-
return [2 /*return*/, request_1(method, endpoint, content, queryParams, config)];
|
|
124
|
-
}
|
|
125
|
-
if (
|
|
126
|
-
// the following cannot be done with WebSocket
|
|
127
|
-
((_c = (_b = (_a = config === null || config === void 0 ? void 0 : config.headers) === null || _a === void 0 ? void 0 : _a.getContentType) === null || _b === void 0 ? void 0 : _b.toString()) === null || _c === void 0 ? void 0 : _c.includes("multipart/form-data")) ||
|
|
128
|
-
(config === null || config === void 0 ? void 0 : config.responseType) === "arraybuffer" ||
|
|
129
|
-
endpoint.startsWith("/restapi/oauth/") // token, revoke, wstoken
|
|
130
|
-
) {
|
|
131
|
-
return [2 /*return*/, request_1(method, endpoint, content, queryParams, config)];
|
|
132
|
-
}
|
|
133
|
-
return [2 /*return*/, this.request(method, endpoint, content, queryParams, config)];
|
|
134
|
-
});
|
|
135
|
-
}); };
|
|
136
|
-
}
|
|
137
|
-
connectMethod = this.connect.bind(this);
|
|
138
|
-
if (this.options.wscToken) {
|
|
139
|
-
this.wsc = {
|
|
140
|
-
token: this.options.wscToken,
|
|
141
|
-
sequence: 0,
|
|
142
|
-
};
|
|
143
|
-
connectMethod = this.recover.bind(this);
|
|
144
|
-
}
|
|
145
|
-
if (!!this.options.autoRecover.enabled) return [3 /*break*/, 2];
|
|
146
|
-
return [4 /*yield*/, connectMethod()];
|
|
147
|
-
case 1:
|
|
148
|
-
_a.sent();
|
|
149
|
-
return [2 /*return*/];
|
|
150
|
-
case 2:
|
|
151
|
-
_a.trys.push([2, 4, , 5]);
|
|
152
|
-
return [4 /*yield*/, connectMethod()];
|
|
153
|
-
case 3:
|
|
154
|
-
_a.sent();
|
|
155
|
-
return [3 /*break*/, 5];
|
|
156
|
-
case 4:
|
|
157
|
-
e_1 = _a.sent();
|
|
158
|
-
if (e_1 instanceof RestException_js_1.default) {
|
|
159
|
-
throw e_1; // such as InsufficientPermissions
|
|
160
|
-
}
|
|
161
|
-
if (this.options.debugMode) {
|
|
162
|
-
console.debug("Initial connect failed:", e_1);
|
|
163
|
-
}
|
|
164
|
-
return [3 /*break*/, 5];
|
|
165
|
-
case 5:
|
|
166
|
-
retriesAttempted = 0;
|
|
167
|
-
checking = false;
|
|
168
|
-
check = function () { return __awaiter(_this, void 0, void 0, function () {
|
|
169
|
-
var e_2;
|
|
170
|
-
var _a, _b, _c;
|
|
171
|
-
return __generator(this, function (_d) {
|
|
172
|
-
switch (_d.label) {
|
|
173
|
-
case 0:
|
|
174
|
-
if (!this.enabled) {
|
|
175
|
-
return [2 /*return*/];
|
|
176
|
-
}
|
|
177
|
-
if (((_a = this.options.autoRecover) === null || _a === void 0 ? void 0 : _a.enabled) !== true) {
|
|
178
|
-
return [2 /*return*/];
|
|
179
|
-
}
|
|
180
|
-
if (checking) {
|
|
181
|
-
return [2 /*return*/];
|
|
182
|
-
}
|
|
183
|
-
checking = true;
|
|
184
|
-
if (!(((_b = this.ws) === null || _b === void 0 ? void 0 : _b.readyState) !== OPEN && ((_c = this.ws) === null || _c === void 0 ? void 0 : _c.readyState) !== CONNECTING)) return [3 /*break*/, 5];
|
|
185
|
-
clearInterval(this.intervalHandle);
|
|
186
|
-
_d.label = 1;
|
|
187
|
-
case 1:
|
|
188
|
-
_d.trys.push([1, 3, , 4]);
|
|
189
|
-
return [4 /*yield*/, this.recover()];
|
|
190
|
-
case 2:
|
|
191
|
-
_d.sent();
|
|
192
|
-
retriesAttempted = 0;
|
|
193
|
-
if (this.options.debugMode) {
|
|
194
|
-
console.debug("Auto recover done, recoveryState: ".concat(this.connectionDetails.recoveryState));
|
|
195
|
-
}
|
|
196
|
-
this.eventEmitter.emit(this.connectionDetails.recoveryState === "Successful"
|
|
197
|
-
? Events.autoRecoverSuccess
|
|
198
|
-
: Events.autoRecoverFailed, this.ws);
|
|
199
|
-
return [3 /*break*/, 4];
|
|
200
|
-
case 3:
|
|
201
|
-
e_2 = _d.sent();
|
|
202
|
-
if (e_2 instanceof RestException_js_1.default) {
|
|
203
|
-
throw e_2; // such as InsufficientPermissions
|
|
204
|
-
}
|
|
205
|
-
retriesAttempted += 1;
|
|
206
|
-
if (this.options.debugMode) {
|
|
207
|
-
console.debug("Auto recover error:", e_2);
|
|
208
|
-
}
|
|
209
|
-
this.eventEmitter.emit(Events.autoRecoverError, e_2);
|
|
210
|
-
return [3 /*break*/, 4];
|
|
211
|
-
case 4:
|
|
212
|
-
this.intervalHandle = setInterval(check, this.options.autoRecover.checkInterval(retriesAttempted));
|
|
213
|
-
_d.label = 5;
|
|
214
|
-
case 5:
|
|
215
|
-
checking = false;
|
|
216
|
-
return [2 /*return*/];
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
}); };
|
|
220
|
-
this.intervalHandle = setInterval(check, this.options.autoRecover.checkInterval(retriesAttempted));
|
|
221
|
-
// browser only code start
|
|
222
|
-
if (typeof globalThis.window !== "undefined" &&
|
|
223
|
-
globalThis.window.addEventListener) {
|
|
224
|
-
globalThis.window.addEventListener("offline", function () {
|
|
225
|
-
var _a;
|
|
226
|
-
if (_this.pingServerHandle) {
|
|
227
|
-
clearTimeout(_this.pingServerHandle);
|
|
228
|
-
}
|
|
229
|
-
(_a = _this.ws) === null || _a === void 0 ? void 0 : _a.close();
|
|
230
|
-
});
|
|
231
|
-
globalThis.window.addEventListener("online", function () {
|
|
232
|
-
check();
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
return [2 /*return*/];
|
|
65
|
+
}
|
|
66
|
+
async install(rc) {
|
|
67
|
+
this.rc = rc;
|
|
68
|
+
if (this.options.restOverWebSocket) {
|
|
69
|
+
const request = rc.request.bind(rc);
|
|
70
|
+
rc.request = async (method, endpoint, content, queryParams, config) => {
|
|
71
|
+
if (!this.enabled || !this.options.restOverWebSocket) {
|
|
72
|
+
return request(method, endpoint, content, queryParams, config);
|
|
236
73
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
case 0:
|
|
245
|
-
if (this._recoverPromise) {
|
|
246
|
-
return [2 /*return*/, this._recoverPromise];
|
|
247
|
-
}
|
|
248
|
-
this._recoverPromise = this._recover();
|
|
249
|
-
_a.label = 1;
|
|
250
|
-
case 1:
|
|
251
|
-
_a.trys.push([1, , 3, 4]);
|
|
252
|
-
return [4 /*yield*/, this._recoverPromise];
|
|
253
|
-
case 2:
|
|
254
|
-
_a.sent();
|
|
255
|
-
return [3 /*break*/, 4];
|
|
256
|
-
case 3:
|
|
257
|
-
this._recoverPromise = undefined;
|
|
258
|
-
return [7 /*endfinally*/];
|
|
259
|
-
case 4: return [2 /*return*/, undefined];
|
|
74
|
+
if (
|
|
75
|
+
// the following cannot be done with WebSocket
|
|
76
|
+
config?.headers?.getContentType?.toString()?.includes("multipart/form-data") ||
|
|
77
|
+
config?.responseType === "arraybuffer" ||
|
|
78
|
+
endpoint.startsWith("/restapi/oauth/") // token, revoke, wstoken
|
|
79
|
+
) {
|
|
80
|
+
return request(method, endpoint, content, queryParams, config);
|
|
260
81
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
switch (_c.label) {
|
|
314
|
-
case 0:
|
|
315
|
-
if (((_a = this.options.autoRecover) === null || _a === void 0 ? void 0 : _a.enabled) !== true) {
|
|
316
|
-
return [2 /*return*/];
|
|
317
|
-
}
|
|
318
|
-
if (((_b = this.ws) === null || _b === void 0 ? void 0 : _b.readyState) !== OPEN) {
|
|
319
|
-
return [2 /*return*/];
|
|
320
|
-
}
|
|
321
|
-
_c.label = 1;
|
|
322
|
-
case 1:
|
|
323
|
-
_c.trys.push([1, 3, , 4]);
|
|
324
|
-
return [4 /*yield*/, this.ws.send(JSON.stringify([
|
|
325
|
-
{
|
|
326
|
-
type: "Heartbeat",
|
|
327
|
-
messageId: uuid(),
|
|
328
|
-
},
|
|
329
|
-
]))];
|
|
330
|
-
case 2:
|
|
331
|
-
_c.sent();
|
|
332
|
-
return [3 /*break*/, 4];
|
|
333
|
-
case 3:
|
|
334
|
-
e_3 = _c.sent();
|
|
335
|
-
this.ws.close(); // Explicitly mark WS as closed
|
|
336
|
-
return [3 /*break*/, 4];
|
|
337
|
-
case 4: return [2 /*return*/];
|
|
82
|
+
return this.request(method, endpoint, content, queryParams, config);
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// should recover if this.options.wscToken
|
|
86
|
+
let connectMethod = this.connect.bind(this);
|
|
87
|
+
if (this.options.wscToken) {
|
|
88
|
+
this.wsc = {
|
|
89
|
+
token: this.options.wscToken,
|
|
90
|
+
sequence: 0,
|
|
91
|
+
};
|
|
92
|
+
connectMethod = this.recover.bind(this);
|
|
93
|
+
}
|
|
94
|
+
if (!this.options.autoRecover.enabled) {
|
|
95
|
+
await connectMethod();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// code after is for auto recover
|
|
99
|
+
try {
|
|
100
|
+
await connectMethod();
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
if (e instanceof RestException_js_1.default) {
|
|
104
|
+
throw e; // such as InsufficientPermissions
|
|
105
|
+
}
|
|
106
|
+
if (this.options.debugMode) {
|
|
107
|
+
console.debug("Initial connect failed:", e);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
let retriesAttempted = 0;
|
|
111
|
+
let checking = false;
|
|
112
|
+
const check = async () => {
|
|
113
|
+
if (!this.enabled) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (this.options.autoRecover?.enabled !== true) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (checking) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
checking = true;
|
|
123
|
+
if (this.ws?.readyState !== OPEN && this.ws?.readyState !== CONNECTING) {
|
|
124
|
+
clearInterval(this.intervalHandle);
|
|
125
|
+
try {
|
|
126
|
+
await this.recover();
|
|
127
|
+
retriesAttempted = 0;
|
|
128
|
+
if (this.options.debugMode) {
|
|
129
|
+
console.debug(`Auto recover done, recoveryState: ${this.connectionDetails.recoveryState}`);
|
|
130
|
+
}
|
|
131
|
+
this.eventEmitter.emit(this.connectionDetails.recoveryState === "Successful"
|
|
132
|
+
? Events.autoRecoverSuccess
|
|
133
|
+
: Events.autoRecoverFailed, this.ws);
|
|
338
134
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
return [2 /*return*/, this._connectPromise];
|
|
349
|
-
}
|
|
350
|
-
this._connectPromise = this._connect(recoverSession);
|
|
351
|
-
_a.label = 1;
|
|
352
|
-
case 1:
|
|
353
|
-
_a.trys.push([1, , 3, 4]);
|
|
354
|
-
return [4 /*yield*/, this._connectPromise];
|
|
355
|
-
case 2:
|
|
356
|
-
_a.sent();
|
|
357
|
-
return [3 /*break*/, 4];
|
|
358
|
-
case 3:
|
|
359
|
-
this._connectPromise = undefined;
|
|
360
|
-
return [7 /*endfinally*/];
|
|
361
|
-
case 4: return [2 /*return*/, undefined];
|
|
135
|
+
catch (e) {
|
|
136
|
+
if (e instanceof RestException_js_1.default) {
|
|
137
|
+
throw e; // such as InsufficientPermissions
|
|
138
|
+
}
|
|
139
|
+
retriesAttempted += 1;
|
|
140
|
+
if (this.options.debugMode) {
|
|
141
|
+
console.debug("Auto recover error:", e);
|
|
142
|
+
}
|
|
143
|
+
this.eventEmitter.emit(Events.autoRecoverError, e);
|
|
362
144
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
case 0:
|
|
375
|
-
if (!(!this.wsToken || Date.now() > this.wsTokenExpiresAt)) return [3 /*break*/, 2];
|
|
376
|
-
return [4 /*yield*/, this.rc.post("/restapi/oauth/wstoken")];
|
|
377
|
-
case 1:
|
|
378
|
-
r = _c.sent();
|
|
379
|
-
this.wsToken = r.data;
|
|
380
|
-
// `expires_in` default value is 600 seconds. That's why we `* 0.8`
|
|
381
|
-
this.wsTokenExpiresAt = Date.now() + this.wsToken.expires_in * 0.8 * 1000;
|
|
382
|
-
_c.label = 2;
|
|
383
|
-
case 2:
|
|
384
|
-
wsUri = "".concat(this.wsToken.uri, "?access_token=").concat(this.wsToken.ws_access_token);
|
|
385
|
-
if (recoverSession && this.wsc) {
|
|
386
|
-
wsUri += "&wsc=".concat(this.wsc.token);
|
|
387
|
-
}
|
|
388
|
-
this.ws = new isomorphic_ws_1.default(wsUri);
|
|
389
|
-
this.eventEmitter.emit(Events.newWebSocketObject, this.ws);
|
|
390
|
-
send = this.ws.send.bind(this.ws);
|
|
391
|
-
this.ws.send = function (s) { return __awaiter(_this, void 0, void 0, function () {
|
|
392
|
-
var _this = this;
|
|
393
|
-
return __generator(this, function (_a) {
|
|
394
|
-
switch (_a.label) {
|
|
395
|
-
case 0:
|
|
396
|
-
if (!(this.ws.readyState === CONNECTING)) return [3 /*break*/, 2];
|
|
397
|
-
return [4 /*yield*/, (0, wait_for_async_1.default)({
|
|
398
|
-
interval: 100,
|
|
399
|
-
condition: function () { return _this.ws.readyState !== CONNECTING; },
|
|
400
|
-
})];
|
|
401
|
-
case 1:
|
|
402
|
-
_a.sent();
|
|
403
|
-
_a.label = 2;
|
|
404
|
-
case 2: return [4 /*yield*/, send(s)];
|
|
405
|
-
case 3:
|
|
406
|
-
_a.sent();
|
|
407
|
-
return [2 /*return*/];
|
|
408
|
-
}
|
|
409
|
-
});
|
|
410
|
-
}); };
|
|
411
|
-
if ((_b = this.options.autoRecover) === null || _b === void 0 ? void 0 : _b.enabled) {
|
|
412
|
-
this.ws.addEventListener("message", function () {
|
|
413
|
-
if (_this.pingServerHandle) {
|
|
414
|
-
clearTimeout(_this.pingServerHandle);
|
|
415
|
-
}
|
|
416
|
-
_this.pingServerHandle = setTimeout(function () { return _this.pingServer(); }, _this.options.autoRecover.pingServerInterval);
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
// debug mode to print all WebSocket traffic
|
|
420
|
-
if (this.options.debugMode) {
|
|
421
|
-
utils_js_1.default.debugWebSocket(this.ws);
|
|
422
|
-
}
|
|
423
|
-
// listen for new wsc data
|
|
424
|
-
this.ws.addEventListener("message", function (mEvent) {
|
|
425
|
-
var event = mEvent;
|
|
426
|
-
var _a = utils_js_1.default.splitWsgData(event.data), meta = _a[0], body = _a[1];
|
|
427
|
-
if (meta.wsc &&
|
|
428
|
-
(!_this.wsc ||
|
|
429
|
-
(meta.type === "ConnectionDetails" && body.recoveryState) ||
|
|
430
|
-
_this.wsc.sequence < meta.wsc.sequence)) {
|
|
431
|
-
_this.wsc = meta.wsc;
|
|
432
|
-
_this.eventEmitter.emit(Events.newWsc, _this.wsc);
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
return [4 /*yield*/, utils_js_1.default.waitForWebSocketMessage(this.ws, function (meta) { return meta.type === "ConnectionDetails" || meta.type === "Error"; })];
|
|
436
|
-
case 3:
|
|
437
|
-
_a = _c.sent(), meta = _a[0], body = _a[1], event = _a[2];
|
|
438
|
-
if (meta.type === "Error") {
|
|
439
|
-
throw new ConnectionException_js_1.default(event);
|
|
440
|
-
}
|
|
441
|
-
this.connectionDetails = body;
|
|
442
|
-
// fired when ws connection is ready for creating subscription
|
|
443
|
-
this.eventEmitter.emit(Events.connectionReady, this.ws);
|
|
444
|
-
if (!(this.subscription && this.subscription.enabled)) return [3 /*break*/, 5];
|
|
445
|
-
// because we have a new ws object
|
|
446
|
-
this.subscription.setupWsEventListener();
|
|
447
|
-
if (!(!recoverSession || this.connectionDetails.recoveryState === "Failed")) return [3 /*break*/, 5];
|
|
448
|
-
// create new subscription if don't recover existing one
|
|
449
|
-
return [4 /*yield*/, this.subscription.subscribe()];
|
|
450
|
-
case 4:
|
|
451
|
-
// create new subscription if don't recover existing one
|
|
452
|
-
_c.sent();
|
|
453
|
-
_c.label = 5;
|
|
454
|
-
case 5: return [2 /*return*/];
|
|
145
|
+
this.intervalHandle = setInterval(check, this.options.autoRecover.checkInterval(retriesAttempted));
|
|
146
|
+
}
|
|
147
|
+
checking = false;
|
|
148
|
+
};
|
|
149
|
+
this.intervalHandle = setInterval(check, this.options.autoRecover.checkInterval(retriesAttempted));
|
|
150
|
+
// browser only code start
|
|
151
|
+
if (typeof globalThis.window !== "undefined" &&
|
|
152
|
+
globalThis.window.addEventListener) {
|
|
153
|
+
globalThis.window.addEventListener("offline", () => {
|
|
154
|
+
if (this.pingServerHandle) {
|
|
155
|
+
clearTimeout(this.pingServerHandle);
|
|
455
156
|
}
|
|
157
|
+
this.ws?.close();
|
|
456
158
|
});
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
// keepInterval means we do not clear the interval
|
|
460
|
-
WebSocketExtension.prototype.revoke = function () {
|
|
461
|
-
return __awaiter(this, arguments, void 0, function (keepInterval) {
|
|
462
|
-
var _a, _b;
|
|
463
|
-
if (keepInterval === void 0) { keepInterval = false; }
|
|
464
|
-
return __generator(this, function (_c) {
|
|
465
|
-
switch (_c.label) {
|
|
466
|
-
case 0: return [4 /*yield*/, ((_a = this.subscription) === null || _a === void 0 ? void 0 : _a.revoke())];
|
|
467
|
-
case 1:
|
|
468
|
-
_c.sent();
|
|
469
|
-
this.subscription = undefined;
|
|
470
|
-
if (!keepInterval && this.intervalHandle) {
|
|
471
|
-
clearInterval(this.intervalHandle);
|
|
472
|
-
}
|
|
473
|
-
if (this.pingServerHandle) {
|
|
474
|
-
clearTimeout(this.pingServerHandle);
|
|
475
|
-
}
|
|
476
|
-
(_b = this.ws) === null || _b === void 0 ? void 0 : _b.close();
|
|
477
|
-
this.wsc = undefined;
|
|
478
|
-
this.wsToken = undefined;
|
|
479
|
-
this.wsTokenExpiresAt = 0;
|
|
480
|
-
this.disable();
|
|
481
|
-
return [2 /*return*/];
|
|
482
|
-
}
|
|
159
|
+
globalThis.window.addEventListener("online", () => {
|
|
160
|
+
check();
|
|
483
161
|
});
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
162
|
+
}
|
|
163
|
+
// browser only code end
|
|
164
|
+
}
|
|
165
|
+
async recover() {
|
|
166
|
+
if (this._recoverPromise) {
|
|
167
|
+
return this._recoverPromise;
|
|
168
|
+
}
|
|
169
|
+
this._recoverPromise = this._recover();
|
|
170
|
+
try {
|
|
171
|
+
await this._recoverPromise;
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
this._recoverPromise = undefined;
|
|
175
|
+
}
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
async _recover() {
|
|
179
|
+
if (this.ws?.readyState === OPEN || this.ws?.readyState === CONNECTING) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (!this.wsc || !this.wsc.token) {
|
|
183
|
+
await this.connect(false); // connect to WSG but do not recover
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (this.recoverTimestamp === undefined) {
|
|
187
|
+
this.recoverTimestamp = Date.now();
|
|
188
|
+
}
|
|
189
|
+
if (this.connectionDetails !== undefined &&
|
|
190
|
+
Date.now() - this.recoverTimestamp >
|
|
191
|
+
this.connectionDetails.recoveryTimeout * 1000) {
|
|
192
|
+
if (this.options.debugMode) {
|
|
193
|
+
console.debug("connect to WSG but do not recover");
|
|
194
|
+
}
|
|
195
|
+
await this.connect(false); // connect to WSG but do not recover
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
if (this.options.debugMode) {
|
|
199
|
+
console.debug("connect to WSG and recover");
|
|
200
|
+
}
|
|
201
|
+
await this.connect(true); // connect to WSG and recover
|
|
202
|
+
}
|
|
203
|
+
this.recoverTimestamp = undefined;
|
|
204
|
+
this.enable();
|
|
205
|
+
}
|
|
206
|
+
async pingServer() {
|
|
207
|
+
if (this.options.autoRecover?.enabled !== true) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (this.ws?.readyState !== OPEN) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
await this.ws.send(JSON.stringify([
|
|
215
|
+
{
|
|
216
|
+
type: "Heartbeat",
|
|
217
|
+
messageId: uuid(),
|
|
218
|
+
},
|
|
219
|
+
]));
|
|
220
|
+
}
|
|
221
|
+
catch (e) {
|
|
222
|
+
this.ws.close(); // Explicitly mark WS as closed
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async connect(recoverSession) {
|
|
226
|
+
if (this._connectPromise) {
|
|
227
|
+
return this._connectPromise;
|
|
228
|
+
}
|
|
229
|
+
this._connectPromise = this._connect(recoverSession);
|
|
230
|
+
try {
|
|
231
|
+
await this._connectPromise;
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
this._connectPromise = undefined;
|
|
235
|
+
}
|
|
236
|
+
return undefined;
|
|
237
|
+
}
|
|
238
|
+
async _connect(recoverSession = false) {
|
|
239
|
+
if (!this.wsToken || Date.now() > this.wsTokenExpiresAt) {
|
|
240
|
+
const r = await this.rc.post("/restapi/oauth/wstoken");
|
|
241
|
+
this.wsToken = r.data;
|
|
242
|
+
// `expires_in` default value is 600 seconds. That's why we `* 0.8`
|
|
243
|
+
this.wsTokenExpiresAt = Date.now() + this.wsToken.expires_in * 0.8 * 1000;
|
|
244
|
+
}
|
|
245
|
+
let wsUri = `${this.wsToken.uri}?access_token=${this.wsToken.ws_access_token}`;
|
|
246
|
+
if (recoverSession && this.wsc) {
|
|
247
|
+
wsUri += `&wsc=${this.wsc.token}`;
|
|
248
|
+
}
|
|
249
|
+
this.ws = new isomorphic_ws_1.default(wsUri);
|
|
250
|
+
this.eventEmitter.emit(Events.newWebSocketObject, this.ws);
|
|
251
|
+
// override send method to wait for connecting
|
|
252
|
+
const send = this.ws.send.bind(this.ws);
|
|
253
|
+
this.ws.send = async (s) => {
|
|
254
|
+
if (this.ws.readyState === CONNECTING) {
|
|
255
|
+
await (0, wait_for_async_1.default)({
|
|
256
|
+
interval: 100,
|
|
257
|
+
condition: () => this.ws.readyState !== CONNECTING,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
await send(s);
|
|
261
|
+
};
|
|
262
|
+
if (this.options.autoRecover?.enabled) {
|
|
263
|
+
this.ws.addEventListener("message", () => {
|
|
264
|
+
if (this.pingServerHandle) {
|
|
265
|
+
clearTimeout(this.pingServerHandle);
|
|
508
266
|
}
|
|
267
|
+
this.pingServerHandle = setTimeout(() => this.pingServer(), this.options.autoRecover.pingServerInterval);
|
|
509
268
|
});
|
|
269
|
+
}
|
|
270
|
+
// debug mode to print all WebSocket traffic
|
|
271
|
+
if (this.options.debugMode) {
|
|
272
|
+
utils_js_1.default.debugWebSocket(this.ws);
|
|
273
|
+
}
|
|
274
|
+
// listen for new wsc data
|
|
275
|
+
this.ws.addEventListener("message", (mEvent) => {
|
|
276
|
+
const event = mEvent;
|
|
277
|
+
const [meta, body] = utils_js_1.default.splitWsgData(event.data);
|
|
278
|
+
if (meta.wsc &&
|
|
279
|
+
(!this.wsc ||
|
|
280
|
+
(meta.type === "ConnectionDetails" && body.recoveryState) ||
|
|
281
|
+
this.wsc.sequence < meta.wsc.sequence)) {
|
|
282
|
+
this.wsc = meta.wsc;
|
|
283
|
+
this.eventEmitter.emit(Events.newWsc, this.wsc);
|
|
284
|
+
}
|
|
510
285
|
});
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
286
|
+
// get initial ConnectionDetails data
|
|
287
|
+
const [meta, body, event] = await utils_js_1.default.waitForWebSocketMessage(this.ws, (meta) => meta.type === "ConnectionDetails" || meta.type === "Error");
|
|
288
|
+
if (meta.type === "Error") {
|
|
289
|
+
throw new ConnectionException_js_1.default(event);
|
|
290
|
+
}
|
|
291
|
+
this.connectionDetails = body;
|
|
292
|
+
// fired when ws connection is ready for creating subscription
|
|
293
|
+
this.eventEmitter.emit(Events.connectionReady, this.ws);
|
|
294
|
+
// recover the subscription, if it exists and enabled
|
|
295
|
+
if (this.subscription && this.subscription.enabled) {
|
|
296
|
+
// because we have a new ws object
|
|
297
|
+
this.subscription.setupWsEventListener();
|
|
298
|
+
if (!recoverSession || this.connectionDetails.recoveryState === "Failed") {
|
|
299
|
+
// create new subscription if don't recover existing one
|
|
300
|
+
await this.subscription.subscribe();
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// keepInterval means we do not clear the interval
|
|
305
|
+
async revoke(keepInterval = false) {
|
|
306
|
+
await this.subscription?.revoke();
|
|
307
|
+
this.subscription = undefined;
|
|
308
|
+
if (!keepInterval && this.intervalHandle) {
|
|
309
|
+
clearInterval(this.intervalHandle);
|
|
310
|
+
}
|
|
311
|
+
if (this.pingServerHandle) {
|
|
312
|
+
clearTimeout(this.pingServerHandle);
|
|
313
|
+
}
|
|
314
|
+
this.ws?.close();
|
|
315
|
+
this.wsc = undefined;
|
|
316
|
+
this.wsToken = undefined;
|
|
317
|
+
this.wsTokenExpiresAt = 0;
|
|
318
|
+
this.disable();
|
|
319
|
+
}
|
|
320
|
+
async subscribe(eventFilters, callback, cache = undefined) {
|
|
321
|
+
const subscription = new subscription_js_1.default(this, eventFilters, callback);
|
|
322
|
+
if (cache === undefined || cache === null) {
|
|
323
|
+
await subscription.subscribe();
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
subscription.subscriptionInfo = cache;
|
|
327
|
+
await subscription.refresh();
|
|
328
|
+
}
|
|
329
|
+
this.subscription = subscription;
|
|
330
|
+
return subscription;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
514
333
|
exports.default = WebSocketExtension;
|
|
515
334
|
//# sourceMappingURL=index.js.map
|