UNPKG

24.3 kBJavaScriptView Raw
1define(function () { 'use strict';
2
3 /*! *****************************************************************************
4 Copyright (c) Microsoft Corporation. All rights reserved.
5 Licensed under the Apache License, Version 2.0 (the "License"); you may not use
6 this file except in compliance with the License. You may obtain a copy of the
7 License at http://www.apache.org/licenses/LICENSE-2.0
8
9 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
10 KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
11 WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
12 MERCHANTABLITY OR NON-INFRINGEMENT.
13
14 See the Apache Version 2.0 License for specific language governing permissions
15 and limitations under the License.
16 ***************************************************************************** */
17 /* global Reflect, Promise */
18
19 var extendStatics = function(d, b) {
20 extendStatics = Object.setPrototypeOf ||
21 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
22 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
23 return extendStatics(d, b);
24 };
25
26 function __extends(d, b) {
27 extendStatics(d, b);
28 function __() { this.constructor = d; }
29 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
30 }
31
32 function __values(o) {
33 var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;
34 if (m) return m.call(o);
35 return {
36 next: function () {
37 if (o && i >= o.length) o = void 0;
38 return { value: o && o[i++], done: !o };
39 }
40 };
41 }
42
43 function __read(o, n) {
44 var m = typeof Symbol === "function" && o[Symbol.iterator];
45 if (!m) return o;
46 var i = m.call(o), r, ar = [], e;
47 try {
48 while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
49 }
50 catch (error) { e = { error: error }; }
51 finally {
52 try {
53 if (r && !r.done && (m = i["return"])) m.call(i);
54 }
55 finally { if (e) throw e.error; }
56 }
57 return ar;
58 }
59
60 function __spread() {
61 for (var ar = [], i = 0; i < arguments.length; i++)
62 ar = ar.concat(__read(arguments[i]));
63 return ar;
64 }
65
66 var Event = /** @class */ (function () {
67 function Event(type, target) {
68 this.target = target;
69 this.type = type;
70 }
71 return Event;
72 }());
73 var ErrorEvent = /** @class */ (function (_super) {
74 __extends(ErrorEvent, _super);
75 function ErrorEvent(error, target) {
76 var _this = _super.call(this, 'error', target) || this;
77 _this.message = error.message;
78 _this.error = error;
79 return _this;
80 }
81 return ErrorEvent;
82 }(Event));
83 var CloseEvent = /** @class */ (function (_super) {
84 __extends(CloseEvent, _super);
85 function CloseEvent(code, reason, target) {
86 if (code === void 0) { code = 1000; }
87 if (reason === void 0) { reason = ''; }
88 var _this = _super.call(this, 'close', target) || this;
89 _this.wasClean = true;
90 _this.code = code;
91 _this.reason = reason;
92 return _this;
93 }
94 return CloseEvent;
95 }(Event));
96
97 /*!
98 * Reconnecting WebSocket
99 * by Pedro Ladaria <pedro.ladaria@gmail.com>
100 * https://github.com/pladaria/reconnecting-websocket
101 * License MIT
102 */
103 var getGlobalWebSocket = function () {
104 if (typeof WebSocket !== 'undefined') {
105 // @ts-ignore
106 return WebSocket;
107 }
108 };
109 /**
110 * Returns true if given argument looks like a WebSocket class
111 */
112 var isWebSocket = function (w) { return typeof w !== 'undefined' && !!w && w.CLOSING === 2; };
113 var DEFAULT = {
114 maxReconnectionDelay: 10000,
115 minReconnectionDelay: 1000 + Math.random() * 4000,
116 minUptime: 5000,
117 reconnectionDelayGrowFactor: 1.3,
118 connectionTimeout: 4000,
119 maxRetries: Infinity,
120 maxEnqueuedMessages: Infinity,
121 startClosed: false,
122 debug: false,
123 };
124 var ReconnectingWebSocket = /** @class */ (function () {
125 function ReconnectingWebSocket(url, protocols, options) {
126 var _this = this;
127 if (options === void 0) { options = {}; }
128 this._listeners = {
129 error: [],
130 message: [],
131 open: [],
132 close: [],
133 };
134 this._retryCount = -1;
135 this._shouldReconnect = true;
136 this._connectLock = false;
137 this._binaryType = 'blob';
138 this._closeCalled = false;
139 this._messageQueue = [];
140 /**
141 * An event listener to be called when the WebSocket connection's readyState changes to CLOSED
142 */
143 this.onclose = null;
144 /**
145 * An event listener to be called when an error occurs
146 */
147 this.onerror = null;
148 /**
149 * An event listener to be called when a message is received from the server
150 */
151 this.onmessage = null;
152 /**
153 * An event listener to be called when the WebSocket connection's readyState changes to OPEN;
154 * this indicates that the connection is ready to send and receive data
155 */
156 this.onopen = null;
157 this._handleOpen = function (event) {
158 _this._debug('open event');
159 var _a = _this._options.minUptime, minUptime = _a === void 0 ? DEFAULT.minUptime : _a;
160 clearTimeout(_this._connectTimeout);
161 _this._uptimeTimeout = setTimeout(function () { return _this._acceptOpen(); }, minUptime);
162 _this._ws.binaryType = _this._binaryType;
163 // send enqueued messages (messages sent before websocket open event)
164 _this._messageQueue.forEach(function (message) { return _this._ws.send(message); });
165 _this._messageQueue = [];
166 if (_this.onopen) {
167 _this.onopen(event);
168 }
169 _this._listeners.open.forEach(function (listener) { return _this._callEventListener(event, listener); });
170 };
171 this._handleMessage = function (event) {
172 _this._debug('message event');
173 if (_this.onmessage) {
174 _this.onmessage(event);
175 }
176 _this._listeners.message.forEach(function (listener) { return _this._callEventListener(event, listener); });
177 };
178 this._handleError = function (event) {
179 _this._debug('error event', event.message);
180 _this._disconnect(undefined, event.message === 'TIMEOUT' ? 'timeout' : undefined);
181 if (_this.onerror) {
182 _this.onerror(event);
183 }
184 _this._debug('exec error listeners');
185 _this._listeners.error.forEach(function (listener) { return _this._callEventListener(event, listener); });
186 _this._connect();
187 };
188 this._handleClose = function (event) {
189 _this._debug('close event');
190 _this._clearTimeouts();
191 if (_this._shouldReconnect) {
192 _this._connect();
193 }
194 if (_this.onclose) {
195 _this.onclose(event);
196 }
197 _this._listeners.close.forEach(function (listener) { return _this._callEventListener(event, listener); });
198 };
199 this._url = url;
200 this._protocols = protocols;
201 this._options = options;
202 if (this._options.startClosed) {
203 this._shouldReconnect = false;
204 }
205 this._connect();
206 }
207 Object.defineProperty(ReconnectingWebSocket, "CONNECTING", {
208 get: function () {
209 return 0;
210 },
211 enumerable: true,
212 configurable: true
213 });
214 Object.defineProperty(ReconnectingWebSocket, "OPEN", {
215 get: function () {
216 return 1;
217 },
218 enumerable: true,
219 configurable: true
220 });
221 Object.defineProperty(ReconnectingWebSocket, "CLOSING", {
222 get: function () {
223 return 2;
224 },
225 enumerable: true,
226 configurable: true
227 });
228 Object.defineProperty(ReconnectingWebSocket, "CLOSED", {
229 get: function () {
230 return 3;
231 },
232 enumerable: true,
233 configurable: true
234 });
235 Object.defineProperty(ReconnectingWebSocket.prototype, "CONNECTING", {
236 get: function () {
237 return ReconnectingWebSocket.CONNECTING;
238 },
239 enumerable: true,
240 configurable: true
241 });
242 Object.defineProperty(ReconnectingWebSocket.prototype, "OPEN", {
243 get: function () {
244 return ReconnectingWebSocket.OPEN;
245 },
246 enumerable: true,
247 configurable: true
248 });
249 Object.defineProperty(ReconnectingWebSocket.prototype, "CLOSING", {
250 get: function () {
251 return ReconnectingWebSocket.CLOSING;
252 },
253 enumerable: true,
254 configurable: true
255 });
256 Object.defineProperty(ReconnectingWebSocket.prototype, "CLOSED", {
257 get: function () {
258 return ReconnectingWebSocket.CLOSED;
259 },
260 enumerable: true,
261 configurable: true
262 });
263 Object.defineProperty(ReconnectingWebSocket.prototype, "binaryType", {
264 get: function () {
265 return this._ws ? this._ws.binaryType : this._binaryType;
266 },
267 set: function (value) {
268 this._binaryType = value;
269 if (this._ws) {
270 this._ws.binaryType = value;
271 }
272 },
273 enumerable: true,
274 configurable: true
275 });
276 Object.defineProperty(ReconnectingWebSocket.prototype, "retryCount", {
277 /**
278 * Returns the number or connection retries
279 */
280 get: function () {
281 return Math.max(this._retryCount, 0);
282 },
283 enumerable: true,
284 configurable: true
285 });
286 Object.defineProperty(ReconnectingWebSocket.prototype, "bufferedAmount", {
287 /**
288 * The number of bytes of data that have been queued using calls to send() but not yet
289 * transmitted to the network. This value resets to zero once all queued data has been sent.
290 * This value does not reset to zero when the connection is closed; if you keep calling send(),
291 * this will continue to climb. Read only
292 */
293 get: function () {
294 var bytes = this._messageQueue.reduce(function (acc, message) {
295 if (typeof message === 'string') {
296 acc += message.length; // not byte size
297 }
298 else if (message instanceof Blob) {
299 acc += message.size;
300 }
301 else {
302 acc += message.byteLength;
303 }
304 return acc;
305 }, 0);
306 return bytes + (this._ws ? this._ws.bufferedAmount : 0);
307 },
308 enumerable: true,
309 configurable: true
310 });
311 Object.defineProperty(ReconnectingWebSocket.prototype, "extensions", {
312 /**
313 * The extensions selected by the server. This is currently only the empty string or a list of
314 * extensions as negotiated by the connection
315 */
316 get: function () {
317 return this._ws ? this._ws.extensions : '';
318 },
319 enumerable: true,
320 configurable: true
321 });
322 Object.defineProperty(ReconnectingWebSocket.prototype, "protocol", {
323 /**
324 * A string indicating the name of the sub-protocol the server selected;
325 * this will be one of the strings specified in the protocols parameter when creating the
326 * WebSocket object
327 */
328 get: function () {
329 return this._ws ? this._ws.protocol : '';
330 },
331 enumerable: true,
332 configurable: true
333 });
334 Object.defineProperty(ReconnectingWebSocket.prototype, "readyState", {
335 /**
336 * The current state of the connection; this is one of the Ready state constants
337 */
338 get: function () {
339 if (this._ws) {
340 return this._ws.readyState;
341 }
342 return this._options.startClosed
343 ? ReconnectingWebSocket.CLOSED
344 : ReconnectingWebSocket.CONNECTING;
345 },
346 enumerable: true,
347 configurable: true
348 });
349 Object.defineProperty(ReconnectingWebSocket.prototype, "url", {
350 /**
351 * The URL as resolved by the constructor
352 */
353 get: function () {
354 return this._ws ? this._ws.url : '';
355 },
356 enumerable: true,
357 configurable: true
358 });
359 /**
360 * Closes the WebSocket connection or connection attempt, if any. If the connection is already
361 * CLOSED, this method does nothing
362 */
363 ReconnectingWebSocket.prototype.close = function (code, reason) {
364 if (code === void 0) { code = 1000; }
365 this._closeCalled = true;
366 this._shouldReconnect = false;
367 this._clearTimeouts();
368 if (!this._ws) {
369 this._debug('close enqueued: no ws instance');
370 return;
371 }
372 if (this._ws.readyState === this.CLOSED) {
373 this._debug('close: already closed');
374 return;
375 }
376 this._ws.close(code, reason);
377 };
378 /**
379 * Closes the WebSocket connection or connection attempt and connects again.
380 * Resets retry counter;
381 */
382 ReconnectingWebSocket.prototype.reconnect = function (code, reason) {
383 this._shouldReconnect = true;
384 this._closeCalled = false;
385 this._retryCount = -1;
386 if (!this._ws || this._ws.readyState === this.CLOSED) {
387 this._connect();
388 }
389 else {
390 this._disconnect(code, reason);
391 this._connect();
392 }
393 };
394 /**
395 * Enqueue specified data to be transmitted to the server over the WebSocket connection
396 */
397 ReconnectingWebSocket.prototype.send = function (data) {
398 if (this._ws && this._ws.readyState === this.OPEN) {
399 this._debug('send', data);
400 this._ws.send(data);
401 }
402 else {
403 var _a = this._options.maxEnqueuedMessages, maxEnqueuedMessages = _a === void 0 ? DEFAULT.maxEnqueuedMessages : _a;
404 if (this._messageQueue.length < maxEnqueuedMessages) {
405 this._debug('enqueue', data);
406 this._messageQueue.push(data);
407 }
408 }
409 };
410 /**
411 * Register an event handler of a specific event type
412 */
413 ReconnectingWebSocket.prototype.addEventListener = function (type, listener) {
414 if (this._listeners[type]) {
415 // @ts-ignore
416 this._listeners[type].push(listener);
417 }
418 };
419 ReconnectingWebSocket.prototype.dispatchEvent = function (event) {
420 var e_1, _a;
421 var listeners = this._listeners[event.type];
422 if (listeners) {
423 try {
424 for (var listeners_1 = __values(listeners), listeners_1_1 = listeners_1.next(); !listeners_1_1.done; listeners_1_1 = listeners_1.next()) {
425 var listener = listeners_1_1.value;
426 this._callEventListener(event, listener);
427 }
428 }
429 catch (e_1_1) { e_1 = { error: e_1_1 }; }
430 finally {
431 try {
432 if (listeners_1_1 && !listeners_1_1.done && (_a = listeners_1.return)) _a.call(listeners_1);
433 }
434 finally { if (e_1) throw e_1.error; }
435 }
436 }
437 return true;
438 };
439 /**
440 * Removes an event listener
441 */
442 ReconnectingWebSocket.prototype.removeEventListener = function (type, listener) {
443 if (this._listeners[type]) {
444 // @ts-ignore
445 this._listeners[type] = this._listeners[type].filter(function (l) { return l !== listener; });
446 }
447 };
448 ReconnectingWebSocket.prototype._debug = function () {
449 var args = [];
450 for (var _i = 0; _i < arguments.length; _i++) {
451 args[_i] = arguments[_i];
452 }
453 if (this._options.debug) {
454 // not using spread because compiled version uses Symbols
455 // tslint:disable-next-line
456 console.log.apply(console, __spread(['RWS>'], args));
457 }
458 };
459 ReconnectingWebSocket.prototype._getNextDelay = function () {
460 var _a = this._options, _b = _a.reconnectionDelayGrowFactor, reconnectionDelayGrowFactor = _b === void 0 ? DEFAULT.reconnectionDelayGrowFactor : _b, _c = _a.minReconnectionDelay, minReconnectionDelay = _c === void 0 ? DEFAULT.minReconnectionDelay : _c, _d = _a.maxReconnectionDelay, maxReconnectionDelay = _d === void 0 ? DEFAULT.maxReconnectionDelay : _d;
461 var delay = 0;
462 if (this._retryCount > 0) {
463 delay =
464 minReconnectionDelay * Math.pow(reconnectionDelayGrowFactor, this._retryCount - 1);
465 if (delay > maxReconnectionDelay) {
466 delay = maxReconnectionDelay;
467 }
468 }
469 this._debug('next delay', delay);
470 return delay;
471 };
472 ReconnectingWebSocket.prototype._wait = function () {
473 var _this = this;
474 return new Promise(function (resolve) {
475 setTimeout(resolve, _this._getNextDelay());
476 });
477 };
478 ReconnectingWebSocket.prototype._getNextUrl = function (urlProvider) {
479 if (typeof urlProvider === 'string') {
480 return Promise.resolve(urlProvider);
481 }
482 if (typeof urlProvider === 'function') {
483 var url = urlProvider();
484 if (typeof url === 'string') {
485 return Promise.resolve(url);
486 }
487 if (!!url.then) {
488 return url;
489 }
490 }
491 throw Error('Invalid URL');
492 };
493 ReconnectingWebSocket.prototype._connect = function () {
494 var _this = this;
495 if (this._connectLock || !this._shouldReconnect) {
496 return;
497 }
498 this._connectLock = true;
499 var _a = this._options, _b = _a.maxRetries, maxRetries = _b === void 0 ? DEFAULT.maxRetries : _b, _c = _a.connectionTimeout, connectionTimeout = _c === void 0 ? DEFAULT.connectionTimeout : _c, _d = _a.WebSocket, WebSocket = _d === void 0 ? getGlobalWebSocket() : _d;
500 if (this._retryCount >= maxRetries) {
501 this._debug('max retries reached', this._retryCount, '>=', maxRetries);
502 return;
503 }
504 this._retryCount++;
505 this._debug('connect', this._retryCount);
506 this._removeListeners();
507 if (!isWebSocket(WebSocket)) {
508 throw Error('No valid WebSocket class provided');
509 }
510 this._wait()
511 .then(function () { return _this._getNextUrl(_this._url); })
512 .then(function (url) {
513 // close could be called before creating the ws
514 if (_this._closeCalled) {
515 return;
516 }
517 _this._debug('connect', { url: url, protocols: _this._protocols });
518 _this._ws = _this._protocols
519 ? new WebSocket(url, _this._protocols)
520 : new WebSocket(url);
521 _this._ws.binaryType = _this._binaryType;
522 _this._connectLock = false;
523 _this._addListeners();
524 _this._connectTimeout = setTimeout(function () { return _this._handleTimeout(); }, connectionTimeout);
525 });
526 };
527 ReconnectingWebSocket.prototype._handleTimeout = function () {
528 this._debug('timeout event');
529 this._handleError(new ErrorEvent(Error('TIMEOUT'), this));
530 };
531 ReconnectingWebSocket.prototype._disconnect = function (code, reason) {
532 if (code === void 0) { code = 1000; }
533 this._clearTimeouts();
534 if (!this._ws) {
535 return;
536 }
537 this._removeListeners();
538 try {
539 this._ws.close(code, reason);
540 this._handleClose(new CloseEvent(code, reason, this));
541 }
542 catch (error) {
543 // ignore
544 }
545 };
546 ReconnectingWebSocket.prototype._acceptOpen = function () {
547 this._debug('accept open');
548 this._retryCount = 0;
549 };
550 ReconnectingWebSocket.prototype._callEventListener = function (event, listener) {
551 if ('handleEvent' in listener) {
552 // @ts-ignore
553 listener.handleEvent(event);
554 }
555 else {
556 // @ts-ignore
557 listener(event);
558 }
559 };
560 ReconnectingWebSocket.prototype._removeListeners = function () {
561 if (!this._ws) {
562 return;
563 }
564 this._debug('removeListeners');
565 this._ws.removeEventListener('open', this._handleOpen);
566 this._ws.removeEventListener('close', this._handleClose);
567 this._ws.removeEventListener('message', this._handleMessage);
568 // @ts-ignore
569 this._ws.removeEventListener('error', this._handleError);
570 };
571 ReconnectingWebSocket.prototype._addListeners = function () {
572 if (!this._ws) {
573 return;
574 }
575 this._debug('addListeners');
576 this._ws.addEventListener('open', this._handleOpen);
577 this._ws.addEventListener('close', this._handleClose);
578 this._ws.addEventListener('message', this._handleMessage);
579 // @ts-ignore
580 this._ws.addEventListener('error', this._handleError);
581 };
582 ReconnectingWebSocket.prototype._clearTimeouts = function () {
583 clearTimeout(this._connectTimeout);
584 clearTimeout(this._uptimeTimeout);
585 };
586 return ReconnectingWebSocket;
587 }());
588
589 return ReconnectingWebSocket;
590
591});