@webex/webex-core 3.12.0-next.9 → 3.12.0-webex-services-ready.1

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.
@@ -42,6 +42,12 @@ var DEFAULT_CLUSTER_IDENTIFIER = process.env.WEBEX_CONVERSATION_DEFAULT_CLUSTER
42
42
  var CATALOG_CACHE_KEY_V2 = 'services.v2.u2cHostMap';
43
43
  var CATALOG_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
44
44
 
45
+ // Maximum time we will wait for the initial catalog collection before letting
46
+ // `services.ready` (and therefore `webex.ready`) fire anyway. A hung request
47
+ // must never leave the app on a permanent spinner - past this point downstream
48
+ // consumers must fall through to their normal error/login paths.
49
+ var SERVICES_INIT_TIMEOUT_MS = 15000;
50
+
45
51
  /* eslint-disable no-underscore-dangle */
46
52
  /**
47
53
  * @class
@@ -52,6 +58,19 @@ var Services = _webexPlugin.default.extend({
52
58
  validateDomains: ['boolean', false, true],
53
59
  initFailed: ['boolean', false, false]
54
60
  },
61
+ session: {
62
+ /**
63
+ * Becomes `true` once services initialization has completed.
64
+ * This blocks `webex.ready` until services are initialized.
65
+ * @instance
66
+ * @memberof Services
67
+ * @type {boolean}
68
+ */
69
+ ready: {
70
+ default: false,
71
+ type: 'boolean'
72
+ }
73
+ },
55
74
  _catalogs: new _weakMap.default(),
56
75
  _activeServices: {},
57
76
  _services: [],
@@ -1314,6 +1333,43 @@ var Services = _webexPlugin.default.extend({
1314
1333
  return _promise.default.resolve();
1315
1334
  });
1316
1335
  },
1336
+ /**
1337
+ * Await any in-flight credentials refresh, then flip `services.ready` so
1338
+ * `webex.ready` can fire. Closes the parallel-refresh window: if a credential
1339
+ * refresh is in flight when initial catalog collection settles, we must not
1340
+ * signal ready until the refresh has resolved - otherwise downstream
1341
+ * consumers may observe `canAuthorize`/token state that is about to change
1342
+ * under them.
1343
+ *
1344
+ * @private
1345
+ * @returns {Promise<void>}
1346
+ */
1347
+ _finalizeReady: function _finalizeReady() {
1348
+ var _this12 = this;
1349
+ return (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3() {
1350
+ var credentials;
1351
+ return _regenerator.default.wrap(function (_context3) {
1352
+ while (1) switch (_context3.prev = _context3.next) {
1353
+ case 0:
1354
+ credentials = _this12.webex.credentials;
1355
+ if (!(credentials && credentials.isRefreshing)) {
1356
+ _context3.next = 1;
1357
+ break;
1358
+ }
1359
+ _context3.next = 1;
1360
+ return new _promise.default(function (resolve) {
1361
+ credentials.once('change:isRefreshing', resolve);
1362
+ });
1363
+ case 1:
1364
+ _this12.ready = true;
1365
+ _this12.trigger('services:initialized');
1366
+ case 2:
1367
+ case "end":
1368
+ return _context3.stop();
1369
+ }
1370
+ }, _callee3);
1371
+ }))();
1372
+ },
1317
1373
  /**
1318
1374
  * Initializer
1319
1375
  *
@@ -1322,58 +1378,84 @@ var Services = _webexPlugin.default.extend({
1322
1378
  * @returns {Services}
1323
1379
  */
1324
1380
  initialize: function initialize() {
1325
- var _this12 = this;
1381
+ var _this13 = this;
1326
1382
  var catalog = new _serviceCatalog.default();
1327
1383
  this._catalogs.set(this.webex, catalog);
1328
1384
 
1329
1385
  // Listen for configuration changes once.
1330
1386
  this.listenToOnce(this.webex, 'change:config', function () {
1331
- _this12.initConfig();
1387
+ _this13.initConfig();
1332
1388
  });
1333
1389
 
1334
- // wait for webex instance to be ready before attempting
1335
- // to update the service catalogs
1336
- this.listenToOnce(this.webex, 'ready', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3() {
1337
- var warmed, supertoken, email;
1338
- return _regenerator.default.wrap(function (_context3) {
1339
- while (1) switch (_context3.prev = _context3.next) {
1390
+ // Wait for storage to be loaded before attempting to update the service catalogs.
1391
+ // We listen for 'loaded' instead of 'ready' because services.ready is a dependency
1392
+ // of webex.ready - listening to 'ready' would cause a deadlock.
1393
+ this.listenToOnce(this.webex, 'loaded', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee4() {
1394
+ var warmed, supertoken, timeout, email;
1395
+ return _regenerator.default.wrap(function (_context4) {
1396
+ while (1) switch (_context4.prev = _context4.next) {
1340
1397
  case 0:
1341
- _context3.next = 1;
1342
- return _this12._loadCatalogFromCache();
1398
+ _context4.next = 1;
1399
+ return _this13._loadCatalogFromCache();
1343
1400
  case 1:
1344
- warmed = _context3.sent;
1401
+ warmed = _context4.sent;
1345
1402
  if (!warmed) {
1346
- _context3.next = 2;
1403
+ _context4.next = 3;
1347
1404
  break;
1348
1405
  }
1349
1406
  catalog.isReady = true;
1350
- return _context3.abrupt("return");
1407
+ _context4.next = 2;
1408
+ return _this13._finalizeReady();
1351
1409
  case 2:
1352
- supertoken = _this12.webex.credentials.supertoken; // Validate if the supertoken exists.
1410
+ return _context4.abrupt("return");
1411
+ case 3:
1412
+ supertoken = _this13.webex.credentials.supertoken; // Race init against a hard timeout so a hung request never leaves
1413
+ // `services.ready` false forever - that would stall `webex.ready` and
1414
+ // leave the app on a permanent spinner.
1415
+ timeout = new _promise.default(function (_, reject) {
1416
+ setTimeout(function () {
1417
+ return reject(new Error("services: init timed out after ".concat(SERVICES_INIT_TIMEOUT_MS, "ms")));
1418
+ }, SERVICES_INIT_TIMEOUT_MS);
1419
+ }); // Validate if the supertoken exists.
1353
1420
  if (supertoken && supertoken.access_token) {
1354
- _this12.initServiceCatalogs().then(function () {
1421
+ _promise.default.race([_this13.initServiceCatalogs(), timeout]).then(function () {
1355
1422
  catalog.isReady = true;
1356
1423
  }).catch(function (error) {
1357
- _this12.initFailed = true;
1358
- _this12.logger.error("services: failed to init initial services when credentials available, ".concat(error === null || error === void 0 ? void 0 : error.message));
1424
+ _this13.initFailed = true;
1425
+ _this13.logger.error("services: failed to init initial services when credentials available, ".concat(error === null || error === void 0 ? void 0 : error.message));
1426
+ }).finally(function () {
1427
+ return _this13._finalizeReady();
1359
1428
  });
1360
1429
  } else {
1361
- email = _this12.webex.config.email;
1362
- _this12.collectPreauthCatalog(email ? {
1430
+ email = _this13.webex.config.email;
1431
+ _promise.default.race([_this13.collectPreauthCatalog(email ? {
1363
1432
  email: email
1364
- } : undefined).catch(function (error) {
1365
- _this12.initFailed = true;
1366
- _this12.logger.error("services: failed to init initial services when no credentials available, ".concat(error === null || error === void 0 ? void 0 : error.message));
1433
+ } : undefined), timeout]).catch(function (error) {
1434
+ _this13.initFailed = true;
1435
+ _this13.logger.error("services: failed to init initial services when no credentials available, ".concat(error === null || error === void 0 ? void 0 : error.message));
1436
+ }).finally(function () {
1437
+ return _this13._finalizeReady();
1438
+ });
1439
+ // Listen for when credentials become available to fetch the full catalog.
1440
+ // This handles fresh login where 'loaded' fires before OAuth completes.
1441
+ _this13.listenToOnce(_this13.webex, 'change:canAuthorize', function () {
1442
+ if (_this13.webex.canAuthorize && !catalog.status.postauth.ready) {
1443
+ _this13.initServiceCatalogs().then(function () {
1444
+ catalog.isReady = true;
1445
+ }).catch(function (error) {
1446
+ _this13.logger.error("services: failed to init service catalogs after auth, ".concat(error === null || error === void 0 ? void 0 : error.message));
1447
+ });
1448
+ }
1367
1449
  });
1368
1450
  }
1369
- case 3:
1451
+ case 4:
1370
1452
  case "end":
1371
- return _context3.stop();
1453
+ return _context4.stop();
1372
1454
  }
1373
- }, _callee3);
1455
+ }, _callee4);
1374
1456
  })));
1375
1457
  },
1376
- version: "3.12.0-next.9"
1458
+ version: "3.12.0-webex-services-ready.1"
1377
1459
  });
1378
1460
  /* eslint-enable no-underscore-dangle */
1379
1461
  var _default = exports.default = Services;