genesys-cloud-streaming-client 15.1.1 → 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.
@@ -22,6 +22,7 @@ export declare class Client extends EventEmitter {
22
22
  private autoReconnect;
23
23
  private extensions;
24
24
  private connectionManager;
25
+ private channelReuses;
25
26
  http: HttpClient;
26
27
  notifications: NotificationsAPI;
27
28
  _notifications: Notifications;
@@ -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 = {
@@ -186,7 +188,7 @@ class Client extends events_1.default {
186
188
  return this.logger.warn(error);
187
189
  }
188
190
  this.connecting = true;
189
- const maxDelay = (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.maxDelayBetweenConnectionAttempts) || 180000;
191
+ const maxDelay = (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.maxDelayBetweenConnectionAttempts) || 90000;
190
192
  let maxAttempts = (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.maxConnectionAttempts) || 1;
191
193
  // tslint:disable-next-line
192
194
  if (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.keepTryingOnFailure) {
@@ -229,8 +231,8 @@ class Client extends events_1.default {
229
231
  throw err;
230
232
  }
231
233
  }
232
- backoffConnectRetryHandler(connectOpts, err, connectionAttempt) {
233
- var _a, _b;
234
+ async backoffConnectRetryHandler(connectOpts, err, connectionAttempt) {
235
+ var _a, _b, _c, _d, _e;
234
236
  // if we exceed the `numOfAttempts` in the backoff config it still calls this retry fn and just ignores the result
235
237
  // if that's the case, we just want to bail out and ignore all the extra logging here.
236
238
  if (connectionAttempt >= connectOpts.maxConnectionAttempts) {
@@ -274,6 +276,22 @@ class Client extends events_1.default {
274
276
  additionalErrorDetails.details = details;
275
277
  }
276
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
+ }
277
295
  this.logger.error('Failed streaming client connection attempt, retrying', additionalErrorDetails, { skipServer: err instanceof offline_error_1.default });
278
296
  return true;
279
297
  }
@@ -281,28 +299,54 @@ class Client extends events_1.default {
281
299
  if (!navigator.onLine) {
282
300
  throw new offline_error_1.default('Browser is offline, skipping connection attempt');
283
301
  }
284
- await this.prepareForConnect();
285
- const stanzaInstance = await this.connectionManager.getNewStanzaConnection();
286
- this.connected = true;
287
- this.connecting = false;
288
- this.addInateEventHandlers(stanzaInstance);
289
- this.proxyStanzaEvents(stanzaInstance);
290
- stanzaInstance.pinger = new ping_1.Ping(this, stanzaInstance);
291
- // handle any extension configuration
292
- for (const extension of this.extensions) {
293
- if (extension.configureNewStanzaInstance) {
294
- await extension.configureNewStanzaInstance(stanzaInstance);
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);
295
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;
296
335
  }
297
- this.extensions.forEach(extension => extension.handleStanzaInstanceChange(stanzaInstance));
298
- this.activeStanzaInstance = stanzaInstance;
299
- this.emit('connected');
300
336
  }
301
337
  async prepareForConnect() {
302
338
  if (this.config.jwt) {
303
339
  this.hardReconnectRequired = false;
304
340
  return this.connectionManager.setConfig(this.config);
305
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
+ }
306
350
  if (this.hardReconnectRequired) {
307
351
  let jidPromise;
308
352
  if (this.config.jid) {
@@ -315,7 +359,7 @@ class Client extends events_1.default {
315
359
  authToken: this.config.authToken,
316
360
  logger: this.logger
317
361
  };
318
- jidPromise = this.http.requestApiWithRetry('users/me', jidRequestOpts).promise
362
+ jidPromise = this.http.requestApi('users/me', jidRequestOpts)
319
363
  .then(res => res.data.chat.jabberId);
320
364
  }
321
365
  const channelRequestOpts = {
@@ -324,7 +368,7 @@ class Client extends events_1.default {
324
368
  authToken: this.config.authToken,
325
369
  logger: this.logger
326
370
  };
327
- const channelPromise = this.http.requestApiWithRetry('notifications/channels?connectionType=streaming', channelRequestOpts).promise
371
+ const channelPromise = this.http.requestApi('notifications/channels?connectionType=streaming', channelRequestOpts)
328
372
  .then(res => res.data.id);
329
373
  const [jid, channelId] = await Promise.all([jidPromise, channelPromise]);
330
374
  this.config.jid = jid;
@@ -358,7 +402,7 @@ class Client extends events_1.default {
358
402
  return Client.version;
359
403
  }
360
404
  static get version() {
361
- return '15.1.1';
405
+ return '15.1.2';
362
406
  }
363
407
  }
364
408
  exports.Client = Client;
@@ -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
- const retryAfter = (_a = error.response.headers) === null || _a === void 0 ? void 0 : _a['retry-after'];
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 */
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "developercenter-cdn/streaming-client",
3
- "version": "15.1.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.1/streaming-client.browser.ie.js"
8
+ "file": "/v15.1.2/streaming-client.browser.ie.js"
9
9
  },
10
10
  {
11
- "file": "/v15.1.1/streaming-client.browser.js"
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": "56",
21
- "buildDate": "2023-02-27T17:03:21.071748Z"
20
+ "build": "57",
21
+ "buildDate": "2023-03-02T21:43:00.982202Z"
22
22
  }
@@ -22,6 +22,7 @@ export declare class Client extends EventEmitter {
22
22
  private autoReconnect;
23
23
  private extensions;
24
24
  private connectionManager;
25
+ private channelReuses;
25
26
  http: HttpClient;
26
27
  notifications: NotificationsAPI;
27
28
  _notifications: Notifications;
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 = {
@@ -189,7 +191,7 @@ export class Client extends EventEmitter {
189
191
  return this.logger.warn(error);
190
192
  }
191
193
  this.connecting = true;
192
- const maxDelay = (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.maxDelayBetweenConnectionAttempts) || 180000;
194
+ const maxDelay = (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.maxDelayBetweenConnectionAttempts) || 90000;
193
195
  let maxAttempts = (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.maxConnectionAttempts) || 1;
194
196
  // tslint:disable-next-line
195
197
  if (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.keepTryingOnFailure) {
@@ -234,74 +236,110 @@ export class Client extends EventEmitter {
234
236
  });
235
237
  }
236
238
  backoffConnectRetryHandler(connectOpts, err, connectionAttempt) {
237
- var _a, _b;
238
- // if we exceed the `numOfAttempts` in the backoff config it still calls this retry fn and just ignores the result
239
- // if that's the case, we just want to bail out and ignore all the extra logging here.
240
- if (connectionAttempt >= connectOpts.maxConnectionAttempts) {
241
- return false;
242
- }
243
- const additionalErrorDetails = { connectionAttempt, error: err };
244
- if (!err) {
245
- additionalErrorDetails.error = new Error('streaming client backoff handler received undefined error');
246
- }
247
- else if (err.name === 'AxiosError') {
248
- const axiosError = err;
249
- const config = axiosError.config;
250
- let sanitizedError = {
251
- config: {
252
- url: config.url,
253
- method: config.method
254
- },
255
- status: (_a = axiosError.response) === null || _a === void 0 ? void 0 : _a.status,
256
- code: axiosError.code,
257
- name: axiosError.name,
258
- message: axiosError.message
259
- };
260
- additionalErrorDetails.error = sanitizedError;
261
- if ([401, 403].includes(((_b = err.response) === null || _b === void 0 ? void 0 : _b.status) || 0)) {
262
- 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) {
263
244
  return false;
264
245
  }
265
- }
266
- // if we get a sasl error, that means we made it all the way to the point of trying to open a websocket and
267
- // it was rejected for some reason. At this point we should do a hard reconnect then try again.
268
- if (err instanceof SaslError) {
269
- this.logger.info('hardReconnectRequired set to true due to sasl error');
270
- this.hardReconnectRequired = true;
271
- Object.assign(additionalErrorDetails, { channelId: err.channelId, stanzaInstanceId: err.stanzaInstanceId });
272
- }
273
- // we don't need to log the stack for a timeout message
274
- if (err instanceof TimeoutError) {
275
- additionalErrorDetails.error = err.message;
276
- const details = err.details;
277
- if (details) {
278
- additionalErrorDetails.details = details;
246
+ const additionalErrorDetails = { connectionAttempt, error: err };
247
+ if (!err) {
248
+ additionalErrorDetails.error = new Error('streaming client backoff handler received undefined error');
279
249
  }
280
- }
281
- this.logger.error('Failed streaming client connection attempt, retrying', additionalErrorDetails, { skipServer: err instanceof OfflineError });
282
- return true;
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
+ });
283
303
  }
284
304
  makeConnectionAttempt() {
285
305
  return __awaiter(this, void 0, void 0, function* () {
286
306
  if (!navigator.onLine) {
287
307
  throw new OfflineError('Browser is offline, skipping connection attempt');
288
308
  }
289
- yield this.prepareForConnect();
290
- const stanzaInstance = yield this.connectionManager.getNewStanzaConnection();
291
- this.connected = true;
292
- this.connecting = false;
293
- this.addInateEventHandlers(stanzaInstance);
294
- this.proxyStanzaEvents(stanzaInstance);
295
- stanzaInstance.pinger = new Ping(this, stanzaInstance);
296
- // handle any extension configuration
297
- for (const extension of this.extensions) {
298
- if (extension.configureNewStanzaInstance) {
299
- yield extension.configureNewStanzaInstance(stanzaInstance);
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);
300
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;
301
342
  }
302
- this.extensions.forEach(extension => extension.handleStanzaInstanceChange(stanzaInstance));
303
- this.activeStanzaInstance = stanzaInstance;
304
- this.emit('connected');
305
343
  });
306
344
  }
307
345
  prepareForConnect() {
@@ -310,6 +348,14 @@ export class Client extends EventEmitter {
310
348
  this.hardReconnectRequired = false;
311
349
  return this.connectionManager.setConfig(this.config);
312
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
+ }
313
359
  if (this.hardReconnectRequired) {
314
360
  let jidPromise;
315
361
  if (this.config.jid) {
@@ -322,7 +368,7 @@ export class Client extends EventEmitter {
322
368
  authToken: this.config.authToken,
323
369
  logger: this.logger
324
370
  };
325
- jidPromise = this.http.requestApiWithRetry('users/me', jidRequestOpts).promise
371
+ jidPromise = this.http.requestApi('users/me', jidRequestOpts)
326
372
  .then(res => res.data.chat.jabberId);
327
373
  }
328
374
  const channelRequestOpts = {
@@ -331,7 +377,7 @@ export class Client extends EventEmitter {
331
377
  authToken: this.config.authToken,
332
378
  logger: this.logger
333
379
  };
334
- const channelPromise = this.http.requestApiWithRetry('notifications/channels?connectionType=streaming', channelRequestOpts).promise
380
+ const channelPromise = this.http.requestApi('notifications/channels?connectionType=streaming', channelRequestOpts)
335
381
  .then(res => res.data.id);
336
382
  const [jid, channelId] = yield Promise.all([jidPromise, channelPromise]);
337
383
  this.config.jid = jid;
@@ -366,6 +412,6 @@ export class Client extends EventEmitter {
366
412
  return Client.version;
367
413
  }
368
414
  static get version() {
369
- return '15.1.1';
415
+ return '15.1.2';
370
416
  }
371
417
  }
@@ -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
- const retryAfter = (_a = error.response.headers) === null || _a === void 0 ? void 0 : _a['retry-after'];
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,