@rc-ex/ws 1.3.14 → 1.3.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/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
- var SdkExtension_1 = __importDefault(require("@rc-ex/core/SdkExtension"));
59
- var isomorphic_ws_1 = __importDefault(require("isomorphic-ws"));
60
- var hyperid_1 = __importDefault(require("hyperid"));
61
- var events_1 = require("events");
62
- var wait_for_async_1 = __importDefault(require("wait-for-async"));
63
- var RestException_1 = __importDefault(require("@rc-ex/core/RestException"));
64
- var rest_js_1 = require("./rest.js");
65
- var subscription_js_1 = __importDefault(require("./subscription.js"));
66
- var ConnectionException_js_1 = __importDefault(require("./exceptions/ConnectionException.js"));
67
- var utils_js_1 = __importDefault(require("./utils.js"));
68
- var CONNECTING = 0;
69
- var OPEN = 1;
70
- var uuid = (0, hyperid_1.default)();
7
+ const SdkExtension_1 = __importDefault(require("@rc-ex/core/SdkExtension"));
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_1 = __importDefault(require("@rc-ex/core/RestException"));
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,438 +26,308 @@ var Events;
77
26
  Events["newWsc"] = "newWsc";
78
27
  Events["connectionReady"] = "connectionReady";
79
28
  })(Events || (exports.Events = Events = {}));
80
- var WebSocketExtension = /** @class */ (function (_super) {
81
- __extends(WebSocketExtension, _super);
82
- function WebSocketExtension(options) {
83
- if (options === void 0) { options = {}; }
84
- var _a, _b, _c, _d, _e;
85
- var _f, _g, _h, _j, _k;
86
- var _this = _super.call(this) || this;
87
- _this.eventEmitter = new events_1.EventEmitter();
88
- _this.wsTokenExpiresAt = 0;
89
- _this.request = rest_js_1.request; // request method was moved to another file to keep this file short
90
- _this.options = options;
91
- (_a = (_f = _this.options).restOverWebSocket) !== null && _a !== void 0 ? _a : (_f.restOverWebSocket = false);
92
- (_b = (_g = _this.options).debugMode) !== null && _b !== void 0 ? _b : (_g.debugMode = false);
93
- (_c = (_h = _this.options).autoRecover) !== null && _c !== void 0 ? _c : (_h.autoRecover = {
29
+ class WebSocketExtension extends SdkExtension_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
- (_d = (_j = _this.options.autoRecover).checkInterval) !== null && _d !== void 0 ? _d : (_j.checkInterval = function (retriesAttempted) {
97
- var interval = 2000 + 2000 * retriesAttempted;
53
+ };
54
+ this.options.autoRecover.checkInterval ??= (retriesAttempted) => {
55
+ const interval = 2000 + 2000 * retriesAttempted;
98
56
  return Math.min(8000, interval);
99
- });
100
- (_e = (_k = _this.options.autoRecover).pingServerInterval) !== null && _e !== void 0 ? _e : (_k.pingServerInterval = 60000);
101
- return _this;
57
+ };
58
+ this.options.autoRecover.pingServerInterval ??= 60000;
102
59
  }
103
- WebSocketExtension.prototype.disable = function () {
104
- _super.prototype.disable.call(this);
60
+ disable() {
61
+ super.disable();
105
62
  if (this.subscription) {
106
63
  this.subscription.enabled = false;
107
64
  }
108
- };
109
- WebSocketExtension.prototype.install = function (rc) {
110
- return __awaiter(this, void 0, void 0, function () {
111
- var request_1, connectMethod, e_1, retriesAttempted, checking, check;
112
- var _this = this;
113
- return __generator(this, function (_a) {
114
- switch (_a.label) {
115
- case 0:
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_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_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
- WebSocketExtension.prototype.recover = function () {
241
- return __awaiter(this, void 0, void 0, function () {
242
- return __generator(this, function (_a) {
243
- switch (_a.label) {
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
- WebSocketExtension.prototype._recover = function () {
265
- return __awaiter(this, void 0, void 0, function () {
266
- var _a, _b;
267
- return __generator(this, function (_c) {
268
- switch (_c.label) {
269
- case 0:
270
- if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === OPEN || ((_b = this.ws) === null || _b === void 0 ? void 0 : _b.readyState) === CONNECTING) {
271
- return [2 /*return*/];
272
- }
273
- if (!(!this.wsc || !this.wsc.token)) return [3 /*break*/, 2];
274
- return [4 /*yield*/, this.connect(false)];
275
- case 1:
276
- _c.sent(); // connect to WSG but do not recover
277
- return [2 /*return*/];
278
- case 2:
279
- if (this.recoverTimestamp === undefined) {
280
- this.recoverTimestamp = Date.now();
281
- }
282
- if (!(this.connectionDetails !== undefined &&
283
- Date.now() - this.recoverTimestamp >
284
- this.connectionDetails.recoveryTimeout * 1000)) return [3 /*break*/, 4];
285
- if (this.options.debugMode) {
286
- console.debug("connect to WSG but do not recover");
287
- }
288
- return [4 /*yield*/, this.connect(false)];
289
- case 3:
290
- _c.sent(); // connect to WSG but do not recover
291
- return [3 /*break*/, 6];
292
- case 4:
293
- if (this.options.debugMode) {
294
- console.debug("connect to WSG and recover");
295
- }
296
- return [4 /*yield*/, this.connect(true)];
297
- case 5:
298
- _c.sent(); // connect to WSG and recover
299
- _c.label = 6;
300
- case 6:
301
- this.recoverTimestamp = undefined;
302
- this.enable();
303
- return [2 /*return*/];
304
- }
305
- });
306
- });
307
- };
308
- WebSocketExtension.prototype.pingServer = function () {
309
- return __awaiter(this, void 0, void 0, function () {
310
- var e_3;
311
- var _a, _b;
312
- return __generator(this, function (_c) {
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_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
- WebSocketExtension.prototype.connect = function (recoverSession) {
343
- return __awaiter(this, void 0, void 0, function () {
344
- return __generator(this, function (_a) {
345
- switch (_a.label) {
346
- case 0:
347
- if (this._connectPromise) {
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_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
- WebSocketExtension.prototype._connect = function () {
367
- return __awaiter(this, arguments, void 0, function (recoverSession) {
368
- var r, wsUri, send, _a, meta, body, event;
369
- var _this = this;
370
- var _b;
371
- if (recoverSession === void 0) { recoverSession = false; }
372
- return __generator(this, function (_c) {
373
- switch (_c.label) {
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
- WebSocketExtension.prototype.subscribe = function (eventFilters_1, callback_1) {
487
- return __awaiter(this, arguments, void 0, function (eventFilters, callback, cache) {
488
- var subscription;
489
- if (cache === void 0) { cache = undefined; }
490
- return __generator(this, function (_a) {
491
- switch (_a.label) {
492
- case 0:
493
- subscription = new subscription_js_1.default(this, eventFilters, callback);
494
- if (!(cache === undefined || cache === null)) return [3 /*break*/, 2];
495
- return [4 /*yield*/, subscription.subscribe()];
496
- case 1:
497
- _a.sent();
498
- return [3 /*break*/, 4];
499
- case 2:
500
- subscription.subscriptionInfo = cache;
501
- return [4 /*yield*/, subscription.refresh()];
502
- case 3:
503
- _a.sent();
504
- _a.label = 4;
505
- case 4:
506
- this.subscription = subscription;
507
- return [2 /*return*/, subscription];
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
- return WebSocketExtension;
513
- }(SdkExtension_1.default));
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;