genesys-cloud-streaming-client 15.1.0 → 15.1.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/dist/cjs/client.d.ts +1 -0
- package/dist/cjs/client.js +75 -24
- package/dist/cjs/http-client.js +7 -3
- package/dist/cjs/ping.js +2 -1
- package/dist/cjs/types/interfaces.d.ts +6 -1
- package/dist/deploy-info.json +5 -5
- package/dist/es/client.d.ts +1 -0
- package/dist/es/client.js +117 -64
- package/dist/es/http-client.js +7 -3
- package/dist/es/index.bundle.js +126 -68
- package/dist/es/ping.js +2 -1
- package/dist/es/types/interfaces.d.ts +6 -1
- package/dist/npm/CHANGELOG.md +13 -1
- package/dist/npm/client.d.ts +1 -0
- package/dist/npm/client.js +75 -24
- package/dist/npm/http-client.js +7 -3
- package/dist/npm/ping.js +2 -1
- package/dist/npm/types/interfaces.d.ts +6 -1
- package/dist/streaming-client.browser.ie.js +2 -2
- package/dist/streaming-client.browser.js +5 -5
- package/dist/v15/streaming-client.browser.ie.js +2 -2
- package/dist/v15/streaming-client.browser.js +5 -5
- package/dist/v15.1.2/streaming-client.browser.ie.js +11 -0
- package/dist/v15.1.2/streaming-client.browser.js +40 -0
- package/package.json +1 -1
- package/dist/v15.1.0/streaming-client.browser.ie.js +0 -11
- package/dist/v15.1.0/streaming-client.browser.js +0 -40
package/dist/cjs/client.d.ts
CHANGED
package/dist/cjs/client.js
CHANGED
|
@@ -22,6 +22,7 @@ let extensions = {
|
|
|
22
22
|
};
|
|
23
23
|
const STANZA_DISCONNECTED = 'stanzaDisconnected';
|
|
24
24
|
const NO_LONGER_SUBSCRIBED = 'notify:no_longer_subscribed';
|
|
25
|
+
const MAX_CHANNEL_REUSES = 10;
|
|
25
26
|
class Client extends events_1.default {
|
|
26
27
|
constructor(options) {
|
|
27
28
|
super();
|
|
@@ -32,6 +33,7 @@ class Client extends events_1.default {
|
|
|
32
33
|
this.backgroundAssistantMode = false;
|
|
33
34
|
this.autoReconnect = true;
|
|
34
35
|
this.extensions = [];
|
|
36
|
+
this.channelReuses = 0;
|
|
35
37
|
this.http = new http_client_1.HttpClient();
|
|
36
38
|
this.reconnectOnNoLongerSubscribed = options.reconnectOnNoLongerSubscribed !== false;
|
|
37
39
|
this.config = {
|
|
@@ -179,20 +181,27 @@ class Client extends events_1.default {
|
|
|
179
181
|
this.activeStanzaInstance.disconnect();
|
|
180
182
|
}, 5000, 'disconnecting streaming service');
|
|
181
183
|
}
|
|
182
|
-
async connect(connectOpts
|
|
184
|
+
async connect(connectOpts) {
|
|
183
185
|
var _a;
|
|
184
186
|
if (this.connecting) {
|
|
185
187
|
const error = new Error('Already trying to connect streaming client');
|
|
186
188
|
return this.logger.warn(error);
|
|
187
189
|
}
|
|
188
190
|
this.connecting = true;
|
|
191
|
+
const maxDelay = (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.maxDelayBetweenConnectionAttempts) || 90000;
|
|
192
|
+
let maxAttempts = (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.maxConnectionAttempts) || 1;
|
|
193
|
+
// tslint:disable-next-line
|
|
194
|
+
if (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.keepTryingOnFailure) {
|
|
195
|
+
// this maintains the previous functionality
|
|
196
|
+
maxAttempts = Infinity;
|
|
197
|
+
}
|
|
189
198
|
try {
|
|
190
199
|
await exponential_backoff_1.backOff(() => this.makeConnectionAttempt(), {
|
|
191
200
|
jitter: 'full',
|
|
192
|
-
maxDelay
|
|
193
|
-
numOfAttempts:
|
|
201
|
+
maxDelay,
|
|
202
|
+
numOfAttempts: maxAttempts,
|
|
194
203
|
startingDelay: 2000,
|
|
195
|
-
retry: this.backoffConnectRetryHandler.bind(this,
|
|
204
|
+
retry: this.backoffConnectRetryHandler.bind(this, { maxConnectionAttempts: maxAttempts })
|
|
196
205
|
});
|
|
197
206
|
}
|
|
198
207
|
catch (err) {
|
|
@@ -222,11 +231,11 @@ class Client extends events_1.default {
|
|
|
222
231
|
throw err;
|
|
223
232
|
}
|
|
224
233
|
}
|
|
225
|
-
backoffConnectRetryHandler(connectOpts, err, connectionAttempt) {
|
|
226
|
-
var _a, _b;
|
|
234
|
+
async backoffConnectRetryHandler(connectOpts, err, connectionAttempt) {
|
|
235
|
+
var _a, _b, _c, _d, _e;
|
|
227
236
|
// if we exceed the `numOfAttempts` in the backoff config it still calls this retry fn and just ignores the result
|
|
228
237
|
// if that's the case, we just want to bail out and ignore all the extra logging here.
|
|
229
|
-
if (
|
|
238
|
+
if (connectionAttempt >= connectOpts.maxConnectionAttempts) {
|
|
230
239
|
return false;
|
|
231
240
|
}
|
|
232
241
|
const additionalErrorDetails = { connectionAttempt, error: err };
|
|
@@ -267,6 +276,22 @@ class Client extends events_1.default {
|
|
|
267
276
|
additionalErrorDetails.details = details;
|
|
268
277
|
}
|
|
269
278
|
}
|
|
279
|
+
if (err === null || err === void 0 ? void 0 : err.response) {
|
|
280
|
+
// This *should* be an axios error according to typings, but it appears this could be an AxiosError *or* and XmlHttpRequest
|
|
281
|
+
// we'll check both to be safe
|
|
282
|
+
const retryAfter = ((_c = err.response.headers) === null || _c === void 0 ? void 0 : _c['retry-after']) || ((_e = (_d = err.response).getResponseHeader) === null || _e === void 0 ? void 0 : _e.call(_d, 'retry-after'));
|
|
283
|
+
if (retryAfter) {
|
|
284
|
+
// retry after comes in seconds, we need to return milliseconds
|
|
285
|
+
let retryDelay = parseInt(retryAfter, 10) * 1000;
|
|
286
|
+
additionalErrorDetails.retryDelay = retryDelay;
|
|
287
|
+
this.logger.error('Failed streaming client connection attempt, respecting retry-after header and will retry afterwards.', additionalErrorDetails, { skipServer: err instanceof offline_error_1.default });
|
|
288
|
+
await new Promise((resolve) => {
|
|
289
|
+
setTimeout(resolve, retryDelay);
|
|
290
|
+
});
|
|
291
|
+
this.logger.debug('finished waiting for retry-after');
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
270
295
|
this.logger.error('Failed streaming client connection attempt, retrying', additionalErrorDetails, { skipServer: err instanceof offline_error_1.default });
|
|
271
296
|
return true;
|
|
272
297
|
}
|
|
@@ -274,28 +299,54 @@ class Client extends events_1.default {
|
|
|
274
299
|
if (!navigator.onLine) {
|
|
275
300
|
throw new offline_error_1.default('Browser is offline, skipping connection attempt');
|
|
276
301
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
302
|
+
let stanzaInstance;
|
|
303
|
+
let previousConnectingState = this.connecting;
|
|
304
|
+
try {
|
|
305
|
+
await this.prepareForConnect();
|
|
306
|
+
stanzaInstance = await this.connectionManager.getNewStanzaConnection();
|
|
307
|
+
this.connected = true;
|
|
308
|
+
this.connecting = false;
|
|
309
|
+
this.addInateEventHandlers(stanzaInstance);
|
|
310
|
+
this.proxyStanzaEvents(stanzaInstance);
|
|
311
|
+
stanzaInstance.pinger = new ping_1.Ping(this, stanzaInstance);
|
|
312
|
+
// handle any extension configuration
|
|
313
|
+
for (const extension of this.extensions) {
|
|
314
|
+
if (extension.configureNewStanzaInstance) {
|
|
315
|
+
await extension.configureNewStanzaInstance(stanzaInstance);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
for (const extension of this.extensions) {
|
|
319
|
+
extension.handleStanzaInstanceChange(stanzaInstance);
|
|
288
320
|
}
|
|
321
|
+
this.activeStanzaInstance = stanzaInstance;
|
|
322
|
+
this.emit('connected');
|
|
323
|
+
}
|
|
324
|
+
catch (err) {
|
|
325
|
+
if (stanzaInstance) {
|
|
326
|
+
this.logger.error('Error occurred in connection attempt, but after websocket connected. Cleaning up connection so backoff is respected', { stanzaInstanceId: stanzaInstance.id, channelId: stanzaInstance.channelId });
|
|
327
|
+
// unproxy stanza events so we don't try and reconnect
|
|
328
|
+
stanzaInstance.emit = stanzaInstance.originalEmitter;
|
|
329
|
+
stanzaInstance.pinger.stop();
|
|
330
|
+
stanzaInstance.disconnect();
|
|
331
|
+
this.connected = false;
|
|
332
|
+
this.connecting = previousConnectingState;
|
|
333
|
+
}
|
|
334
|
+
throw err;
|
|
289
335
|
}
|
|
290
|
-
this.extensions.forEach(extension => extension.handleStanzaInstanceChange(stanzaInstance));
|
|
291
|
-
this.activeStanzaInstance = stanzaInstance;
|
|
292
|
-
this.emit('connected');
|
|
293
336
|
}
|
|
294
337
|
async prepareForConnect() {
|
|
295
338
|
if (this.config.jwt) {
|
|
296
339
|
this.hardReconnectRequired = false;
|
|
297
340
|
return this.connectionManager.setConfig(this.config);
|
|
298
341
|
}
|
|
342
|
+
if (!this.hardReconnectRequired) {
|
|
343
|
+
this.channelReuses++;
|
|
344
|
+
if (this.channelReuses > MAX_CHANNEL_REUSES) {
|
|
345
|
+
this.logger.warn('Forcing a hard reconnect due to max channel reuses', { channelId: this.config.channelId, channelReuses: this.channelReuses });
|
|
346
|
+
this.channelReuses = 0;
|
|
347
|
+
this.hardReconnectRequired = true;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
299
350
|
if (this.hardReconnectRequired) {
|
|
300
351
|
let jidPromise;
|
|
301
352
|
if (this.config.jid) {
|
|
@@ -308,7 +359,7 @@ class Client extends events_1.default {
|
|
|
308
359
|
authToken: this.config.authToken,
|
|
309
360
|
logger: this.logger
|
|
310
361
|
};
|
|
311
|
-
jidPromise = this.http.
|
|
362
|
+
jidPromise = this.http.requestApi('users/me', jidRequestOpts)
|
|
312
363
|
.then(res => res.data.chat.jabberId);
|
|
313
364
|
}
|
|
314
365
|
const channelRequestOpts = {
|
|
@@ -317,7 +368,7 @@ class Client extends events_1.default {
|
|
|
317
368
|
authToken: this.config.authToken,
|
|
318
369
|
logger: this.logger
|
|
319
370
|
};
|
|
320
|
-
const channelPromise = this.http.
|
|
371
|
+
const channelPromise = this.http.requestApi('notifications/channels?connectionType=streaming', channelRequestOpts)
|
|
321
372
|
.then(res => res.data.id);
|
|
322
373
|
const [jid, channelId] = await Promise.all([jidPromise, channelPromise]);
|
|
323
374
|
this.config.jid = jid;
|
|
@@ -351,7 +402,7 @@ class Client extends events_1.default {
|
|
|
351
402
|
return Client.version;
|
|
352
403
|
}
|
|
353
404
|
static get version() {
|
|
354
|
-
return '15.1.
|
|
405
|
+
return '15.1.2';
|
|
355
406
|
}
|
|
356
407
|
}
|
|
357
408
|
exports.Client = Client;
|
package/dist/cjs/http-client.js
CHANGED
|
@@ -11,12 +11,15 @@ class HttpClient {
|
|
|
11
11
|
}
|
|
12
12
|
requestApiWithRetry(path, opts, retryInterval) {
|
|
13
13
|
const retry = utils_1.retryPromise(this.requestApi.bind(this, path, opts), (error) => {
|
|
14
|
-
var _a;
|
|
14
|
+
var _a, _b, _c;
|
|
15
15
|
let retryValue = false;
|
|
16
16
|
if (error === null || error === void 0 ? void 0 : error.response) {
|
|
17
17
|
retryValue = HttpClient.retryStatusCodes.has(error.response.status || 0);
|
|
18
|
-
|
|
18
|
+
// This *should* be an axios error according to typings, but it appears this could be an AxiosError *or* and XmlHttpRequest
|
|
19
|
+
// we'll check both to be safe
|
|
20
|
+
const retryAfter = ((_a = error.response.headers) === null || _a === void 0 ? void 0 : _a['retry-after']) || ((_c = (_b = error.response).getResponseHeader) === null || _c === void 0 ? void 0 : _c.call(_b, 'retry-after'));
|
|
19
21
|
if (retryAfter) {
|
|
22
|
+
(opts.logger || console).debug('retry-after header found on response. setting retry delay', { retryAfter });
|
|
20
23
|
// retry after comes in seconds, we need to return milliseconds
|
|
21
24
|
retryValue = parseInt(retryAfter, 10) * 1000;
|
|
22
25
|
}
|
|
@@ -51,6 +54,7 @@ class HttpClient {
|
|
|
51
54
|
.then(boundHandler, boundHandler);
|
|
52
55
|
}
|
|
53
56
|
handleResponse(logger, start, params, res) {
|
|
57
|
+
var _a;
|
|
54
58
|
let now = new Date().getTime();
|
|
55
59
|
let elapsed = (now - start) + 'ms';
|
|
56
60
|
if (res instanceof axios_1.AxiosError) {
|
|
@@ -70,7 +74,7 @@ class HttpClient {
|
|
|
70
74
|
let body = response.data;
|
|
71
75
|
let error = {
|
|
72
76
|
...res,
|
|
73
|
-
text: response.request.response
|
|
77
|
+
text: (_a = response.request) === null || _a === void 0 ? void 0 : _a.response
|
|
74
78
|
};
|
|
75
79
|
logger.debug(`request error: ${params.url}`, {
|
|
76
80
|
message: res.message,
|
package/dist/cjs/ping.js
CHANGED
|
@@ -33,7 +33,8 @@ class Ping {
|
|
|
33
33
|
catch (err) {
|
|
34
34
|
const info = {
|
|
35
35
|
channelId: this.client.config.channelId,
|
|
36
|
-
jid: this.stanzaInstance.jid
|
|
36
|
+
jid: this.stanzaInstance.jid,
|
|
37
|
+
stanzaInstanceId: this.stanzaInstance.id
|
|
37
38
|
};
|
|
38
39
|
this.client.logger.warn('Missed a ping.', Object.assign({ error: err }, info));
|
|
39
40
|
/* if we have reached max number of missed pings, disconnect */
|
|
@@ -138,7 +138,12 @@ export interface StreamingClientExtension {
|
|
|
138
138
|
expose: any;
|
|
139
139
|
}
|
|
140
140
|
export interface StreamingClientConnectOptions {
|
|
141
|
-
|
|
141
|
+
/**
|
|
142
|
+
* @deprecated since version 15.1.1. Please use maxConnectionAttempts instead
|
|
143
|
+
*/
|
|
144
|
+
keepTryingOnFailure?: boolean;
|
|
145
|
+
maxConnectionAttempts?: number;
|
|
146
|
+
maxDelayBetweenConnectionAttempts?: number;
|
|
142
147
|
}
|
|
143
148
|
export interface GenesysWebrtcJsonRpcMessage extends JsonRpcMessage {
|
|
144
149
|
id?: string;
|
package/dist/deploy-info.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "developercenter-cdn/streaming-client",
|
|
3
|
-
"version": "15.1.
|
|
3
|
+
"version": "15.1.2",
|
|
4
4
|
"ecosystem": "pc",
|
|
5
5
|
"team": "Genesys Client Media (WebRTC)",
|
|
6
6
|
"indexFiles": [
|
|
7
7
|
{
|
|
8
|
-
"file": "/v15.1.
|
|
8
|
+
"file": "/v15.1.2/streaming-client.browser.ie.js"
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
|
-
"file": "/v15.1.
|
|
11
|
+
"file": "/v15.1.2/streaming-client.browser.js"
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
"file": "/v15/streaming-client.browser.ie.js"
|
|
@@ -17,6 +17,6 @@
|
|
|
17
17
|
"file": "/v15/streaming-client.browser.js"
|
|
18
18
|
}
|
|
19
19
|
],
|
|
20
|
-
"build": "
|
|
21
|
-
"buildDate": "2023-
|
|
20
|
+
"build": "57",
|
|
21
|
+
"buildDate": "2023-03-02T21:43:00.982202Z"
|
|
22
22
|
}
|
package/dist/es/client.d.ts
CHANGED
package/dist/es/client.js
CHANGED
|
@@ -20,6 +20,7 @@ let extensions = {
|
|
|
20
20
|
};
|
|
21
21
|
const STANZA_DISCONNECTED = 'stanzaDisconnected';
|
|
22
22
|
const NO_LONGER_SUBSCRIBED = 'notify:no_longer_subscribed';
|
|
23
|
+
const MAX_CHANNEL_REUSES = 10;
|
|
23
24
|
export class Client extends EventEmitter {
|
|
24
25
|
constructor(options) {
|
|
25
26
|
super();
|
|
@@ -30,6 +31,7 @@ export class Client extends EventEmitter {
|
|
|
30
31
|
this.backgroundAssistantMode = false;
|
|
31
32
|
this.autoReconnect = true;
|
|
32
33
|
this.extensions = [];
|
|
34
|
+
this.channelReuses = 0;
|
|
33
35
|
this.http = new HttpClient();
|
|
34
36
|
this.reconnectOnNoLongerSubscribed = options.reconnectOnNoLongerSubscribed !== false;
|
|
35
37
|
this.config = {
|
|
@@ -181,7 +183,7 @@ export class Client extends EventEmitter {
|
|
|
181
183
|
}, 5000, 'disconnecting streaming service');
|
|
182
184
|
});
|
|
183
185
|
}
|
|
184
|
-
connect(connectOpts
|
|
186
|
+
connect(connectOpts) {
|
|
185
187
|
var _a;
|
|
186
188
|
return __awaiter(this, void 0, void 0, function* () {
|
|
187
189
|
if (this.connecting) {
|
|
@@ -189,13 +191,20 @@ export class Client extends EventEmitter {
|
|
|
189
191
|
return this.logger.warn(error);
|
|
190
192
|
}
|
|
191
193
|
this.connecting = true;
|
|
194
|
+
const maxDelay = (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.maxDelayBetweenConnectionAttempts) || 90000;
|
|
195
|
+
let maxAttempts = (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.maxConnectionAttempts) || 1;
|
|
196
|
+
// tslint:disable-next-line
|
|
197
|
+
if (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.keepTryingOnFailure) {
|
|
198
|
+
// this maintains the previous functionality
|
|
199
|
+
maxAttempts = Infinity;
|
|
200
|
+
}
|
|
192
201
|
try {
|
|
193
202
|
yield backOff(() => this.makeConnectionAttempt(), {
|
|
194
203
|
jitter: 'full',
|
|
195
|
-
maxDelay
|
|
196
|
-
numOfAttempts:
|
|
204
|
+
maxDelay,
|
|
205
|
+
numOfAttempts: maxAttempts,
|
|
197
206
|
startingDelay: 2000,
|
|
198
|
-
retry: this.backoffConnectRetryHandler.bind(this,
|
|
207
|
+
retry: this.backoffConnectRetryHandler.bind(this, { maxConnectionAttempts: maxAttempts })
|
|
199
208
|
});
|
|
200
209
|
}
|
|
201
210
|
catch (err) {
|
|
@@ -227,74 +236,110 @@ export class Client extends EventEmitter {
|
|
|
227
236
|
});
|
|
228
237
|
}
|
|
229
238
|
backoffConnectRetryHandler(connectOpts, err, connectionAttempt) {
|
|
230
|
-
var _a, _b;
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
const additionalErrorDetails = { connectionAttempt, error: err };
|
|
237
|
-
if (!err) {
|
|
238
|
-
additionalErrorDetails.error = new Error('streaming client backoff handler received undefined error');
|
|
239
|
-
}
|
|
240
|
-
else if (err.name === 'AxiosError') {
|
|
241
|
-
const axiosError = err;
|
|
242
|
-
const config = axiosError.config;
|
|
243
|
-
let sanitizedError = {
|
|
244
|
-
config: {
|
|
245
|
-
url: config.url,
|
|
246
|
-
method: config.method
|
|
247
|
-
},
|
|
248
|
-
status: (_a = axiosError.response) === null || _a === void 0 ? void 0 : _a.status,
|
|
249
|
-
code: axiosError.code,
|
|
250
|
-
name: axiosError.name,
|
|
251
|
-
message: axiosError.message
|
|
252
|
-
};
|
|
253
|
-
additionalErrorDetails.error = sanitizedError;
|
|
254
|
-
if ([401, 403].includes(((_b = err.response) === null || _b === void 0 ? void 0 : _b.status) || 0)) {
|
|
255
|
-
this.logger.error('Streaming client received an error that it can\'t recover from and will not attempt to reconnect', additionalErrorDetails);
|
|
239
|
+
var _a, _b, _c, _d, _e;
|
|
240
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
241
|
+
// if we exceed the `numOfAttempts` in the backoff config it still calls this retry fn and just ignores the result
|
|
242
|
+
// if that's the case, we just want to bail out and ignore all the extra logging here.
|
|
243
|
+
if (connectionAttempt >= connectOpts.maxConnectionAttempts) {
|
|
256
244
|
return false;
|
|
257
245
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if (err instanceof SaslError) {
|
|
262
|
-
this.logger.info('hardReconnectRequired set to true due to sasl error');
|
|
263
|
-
this.hardReconnectRequired = true;
|
|
264
|
-
Object.assign(additionalErrorDetails, { channelId: err.channelId, stanzaInstanceId: err.stanzaInstanceId });
|
|
265
|
-
}
|
|
266
|
-
// we don't need to log the stack for a timeout message
|
|
267
|
-
if (err instanceof TimeoutError) {
|
|
268
|
-
additionalErrorDetails.error = err.message;
|
|
269
|
-
const details = err.details;
|
|
270
|
-
if (details) {
|
|
271
|
-
additionalErrorDetails.details = details;
|
|
246
|
+
const additionalErrorDetails = { connectionAttempt, error: err };
|
|
247
|
+
if (!err) {
|
|
248
|
+
additionalErrorDetails.error = new Error('streaming client backoff handler received undefined error');
|
|
272
249
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
250
|
+
else if (err.name === 'AxiosError') {
|
|
251
|
+
const axiosError = err;
|
|
252
|
+
const config = axiosError.config;
|
|
253
|
+
let sanitizedError = {
|
|
254
|
+
config: {
|
|
255
|
+
url: config.url,
|
|
256
|
+
method: config.method
|
|
257
|
+
},
|
|
258
|
+
status: (_a = axiosError.response) === null || _a === void 0 ? void 0 : _a.status,
|
|
259
|
+
code: axiosError.code,
|
|
260
|
+
name: axiosError.name,
|
|
261
|
+
message: axiosError.message
|
|
262
|
+
};
|
|
263
|
+
additionalErrorDetails.error = sanitizedError;
|
|
264
|
+
if ([401, 403].includes(((_b = err.response) === null || _b === void 0 ? void 0 : _b.status) || 0)) {
|
|
265
|
+
this.logger.error('Streaming client received an error that it can\'t recover from and will not attempt to reconnect', additionalErrorDetails);
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// if we get a sasl error, that means we made it all the way to the point of trying to open a websocket and
|
|
270
|
+
// it was rejected for some reason. At this point we should do a hard reconnect then try again.
|
|
271
|
+
if (err instanceof SaslError) {
|
|
272
|
+
this.logger.info('hardReconnectRequired set to true due to sasl error');
|
|
273
|
+
this.hardReconnectRequired = true;
|
|
274
|
+
Object.assign(additionalErrorDetails, { channelId: err.channelId, stanzaInstanceId: err.stanzaInstanceId });
|
|
275
|
+
}
|
|
276
|
+
// we don't need to log the stack for a timeout message
|
|
277
|
+
if (err instanceof TimeoutError) {
|
|
278
|
+
additionalErrorDetails.error = err.message;
|
|
279
|
+
const details = err.details;
|
|
280
|
+
if (details) {
|
|
281
|
+
additionalErrorDetails.details = details;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (err === null || err === void 0 ? void 0 : err.response) {
|
|
285
|
+
// This *should* be an axios error according to typings, but it appears this could be an AxiosError *or* and XmlHttpRequest
|
|
286
|
+
// we'll check both to be safe
|
|
287
|
+
const retryAfter = ((_c = err.response.headers) === null || _c === void 0 ? void 0 : _c['retry-after']) || ((_e = (_d = err.response).getResponseHeader) === null || _e === void 0 ? void 0 : _e.call(_d, 'retry-after'));
|
|
288
|
+
if (retryAfter) {
|
|
289
|
+
// retry after comes in seconds, we need to return milliseconds
|
|
290
|
+
let retryDelay = parseInt(retryAfter, 10) * 1000;
|
|
291
|
+
additionalErrorDetails.retryDelay = retryDelay;
|
|
292
|
+
this.logger.error('Failed streaming client connection attempt, respecting retry-after header and will retry afterwards.', additionalErrorDetails, { skipServer: err instanceof OfflineError });
|
|
293
|
+
yield new Promise((resolve) => {
|
|
294
|
+
setTimeout(resolve, retryDelay);
|
|
295
|
+
});
|
|
296
|
+
this.logger.debug('finished waiting for retry-after');
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
this.logger.error('Failed streaming client connection attempt, retrying', additionalErrorDetails, { skipServer: err instanceof OfflineError });
|
|
301
|
+
return true;
|
|
302
|
+
});
|
|
276
303
|
}
|
|
277
304
|
makeConnectionAttempt() {
|
|
278
305
|
return __awaiter(this, void 0, void 0, function* () {
|
|
279
306
|
if (!navigator.onLine) {
|
|
280
307
|
throw new OfflineError('Browser is offline, skipping connection attempt');
|
|
281
308
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
309
|
+
let stanzaInstance;
|
|
310
|
+
let previousConnectingState = this.connecting;
|
|
311
|
+
try {
|
|
312
|
+
yield this.prepareForConnect();
|
|
313
|
+
stanzaInstance = yield this.connectionManager.getNewStanzaConnection();
|
|
314
|
+
this.connected = true;
|
|
315
|
+
this.connecting = false;
|
|
316
|
+
this.addInateEventHandlers(stanzaInstance);
|
|
317
|
+
this.proxyStanzaEvents(stanzaInstance);
|
|
318
|
+
stanzaInstance.pinger = new Ping(this, stanzaInstance);
|
|
319
|
+
// handle any extension configuration
|
|
320
|
+
for (const extension of this.extensions) {
|
|
321
|
+
if (extension.configureNewStanzaInstance) {
|
|
322
|
+
yield extension.configureNewStanzaInstance(stanzaInstance);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
for (const extension of this.extensions) {
|
|
326
|
+
extension.handleStanzaInstanceChange(stanzaInstance);
|
|
293
327
|
}
|
|
328
|
+
this.activeStanzaInstance = stanzaInstance;
|
|
329
|
+
this.emit('connected');
|
|
330
|
+
}
|
|
331
|
+
catch (err) {
|
|
332
|
+
if (stanzaInstance) {
|
|
333
|
+
this.logger.error('Error occurred in connection attempt, but after websocket connected. Cleaning up connection so backoff is respected', { stanzaInstanceId: stanzaInstance.id, channelId: stanzaInstance.channelId });
|
|
334
|
+
// unproxy stanza events so we don't try and reconnect
|
|
335
|
+
stanzaInstance.emit = stanzaInstance.originalEmitter;
|
|
336
|
+
stanzaInstance.pinger.stop();
|
|
337
|
+
stanzaInstance.disconnect();
|
|
338
|
+
this.connected = false;
|
|
339
|
+
this.connecting = previousConnectingState;
|
|
340
|
+
}
|
|
341
|
+
throw err;
|
|
294
342
|
}
|
|
295
|
-
this.extensions.forEach(extension => extension.handleStanzaInstanceChange(stanzaInstance));
|
|
296
|
-
this.activeStanzaInstance = stanzaInstance;
|
|
297
|
-
this.emit('connected');
|
|
298
343
|
});
|
|
299
344
|
}
|
|
300
345
|
prepareForConnect() {
|
|
@@ -303,6 +348,14 @@ export class Client extends EventEmitter {
|
|
|
303
348
|
this.hardReconnectRequired = false;
|
|
304
349
|
return this.connectionManager.setConfig(this.config);
|
|
305
350
|
}
|
|
351
|
+
if (!this.hardReconnectRequired) {
|
|
352
|
+
this.channelReuses++;
|
|
353
|
+
if (this.channelReuses > MAX_CHANNEL_REUSES) {
|
|
354
|
+
this.logger.warn('Forcing a hard reconnect due to max channel reuses', { channelId: this.config.channelId, channelReuses: this.channelReuses });
|
|
355
|
+
this.channelReuses = 0;
|
|
356
|
+
this.hardReconnectRequired = true;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
306
359
|
if (this.hardReconnectRequired) {
|
|
307
360
|
let jidPromise;
|
|
308
361
|
if (this.config.jid) {
|
|
@@ -315,7 +368,7 @@ export class Client extends EventEmitter {
|
|
|
315
368
|
authToken: this.config.authToken,
|
|
316
369
|
logger: this.logger
|
|
317
370
|
};
|
|
318
|
-
jidPromise = this.http.
|
|
371
|
+
jidPromise = this.http.requestApi('users/me', jidRequestOpts)
|
|
319
372
|
.then(res => res.data.chat.jabberId);
|
|
320
373
|
}
|
|
321
374
|
const channelRequestOpts = {
|
|
@@ -324,7 +377,7 @@ export class Client extends EventEmitter {
|
|
|
324
377
|
authToken: this.config.authToken,
|
|
325
378
|
logger: this.logger
|
|
326
379
|
};
|
|
327
|
-
const channelPromise = this.http.
|
|
380
|
+
const channelPromise = this.http.requestApi('notifications/channels?connectionType=streaming', channelRequestOpts)
|
|
328
381
|
.then(res => res.data.id);
|
|
329
382
|
const [jid, channelId] = yield Promise.all([jidPromise, channelPromise]);
|
|
330
383
|
this.config.jid = jid;
|
|
@@ -359,6 +412,6 @@ export class Client extends EventEmitter {
|
|
|
359
412
|
return Client.version;
|
|
360
413
|
}
|
|
361
414
|
static get version() {
|
|
362
|
-
return '15.1.
|
|
415
|
+
return '15.1.2';
|
|
363
416
|
}
|
|
364
417
|
}
|
package/dist/es/http-client.js
CHANGED
|
@@ -7,12 +7,15 @@ export class HttpClient {
|
|
|
7
7
|
}
|
|
8
8
|
requestApiWithRetry(path, opts, retryInterval) {
|
|
9
9
|
const retry = retryPromise(this.requestApi.bind(this, path, opts), (error) => {
|
|
10
|
-
var _a;
|
|
10
|
+
var _a, _b, _c;
|
|
11
11
|
let retryValue = false;
|
|
12
12
|
if (error === null || error === void 0 ? void 0 : error.response) {
|
|
13
13
|
retryValue = HttpClient.retryStatusCodes.has(error.response.status || 0);
|
|
14
|
-
|
|
14
|
+
// This *should* be an axios error according to typings, but it appears this could be an AxiosError *or* and XmlHttpRequest
|
|
15
|
+
// we'll check both to be safe
|
|
16
|
+
const retryAfter = ((_a = error.response.headers) === null || _a === void 0 ? void 0 : _a['retry-after']) || ((_c = (_b = error.response).getResponseHeader) === null || _c === void 0 ? void 0 : _c.call(_b, 'retry-after'));
|
|
15
17
|
if (retryAfter) {
|
|
18
|
+
(opts.logger || console).debug('retry-after header found on response. setting retry delay', { retryAfter });
|
|
16
19
|
// retry after comes in seconds, we need to return milliseconds
|
|
17
20
|
retryValue = parseInt(retryAfter, 10) * 1000;
|
|
18
21
|
}
|
|
@@ -47,6 +50,7 @@ export class HttpClient {
|
|
|
47
50
|
.then(boundHandler, boundHandler);
|
|
48
51
|
}
|
|
49
52
|
handleResponse(logger, start, params, res) {
|
|
53
|
+
var _a;
|
|
50
54
|
let now = new Date().getTime();
|
|
51
55
|
let elapsed = (now - start) + 'ms';
|
|
52
56
|
if (res instanceof AxiosError) {
|
|
@@ -64,7 +68,7 @@ export class HttpClient {
|
|
|
64
68
|
let status = response.status;
|
|
65
69
|
let correlationId = response.headers && response.headers[correlationIdHeaderName];
|
|
66
70
|
let body = response.data;
|
|
67
|
-
let error = Object.assign(Object.assign({}, res), { text: response.request.response });
|
|
71
|
+
let error = Object.assign(Object.assign({}, res), { text: (_a = response.request) === null || _a === void 0 ? void 0 : _a.response });
|
|
68
72
|
logger.debug(`request error: ${params.url}`, {
|
|
69
73
|
message: res.message,
|
|
70
74
|
now,
|