@webex/webex-core 3.10.0 → 3.11.0

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.
Files changed (118) hide show
  1. package/dist/config.js +14 -0
  2. package/dist/config.js.map +1 -1
  3. package/dist/credentials-config.js.map +1 -1
  4. package/dist/index.js +1 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/interceptors/auth.js +6 -8
  7. package/dist/interceptors/auth.js.map +1 -1
  8. package/dist/interceptors/default-options.js +6 -8
  9. package/dist/interceptors/default-options.js.map +1 -1
  10. package/dist/interceptors/embargo.js +6 -8
  11. package/dist/interceptors/embargo.js.map +1 -1
  12. package/dist/interceptors/network-timing.js +6 -8
  13. package/dist/interceptors/network-timing.js.map +1 -1
  14. package/dist/interceptors/payload-transformer.js +6 -8
  15. package/dist/interceptors/payload-transformer.js.map +1 -1
  16. package/dist/interceptors/proxy.js +7 -10
  17. package/dist/interceptors/proxy.js.map +1 -1
  18. package/dist/interceptors/rate-limit.js +7 -10
  19. package/dist/interceptors/rate-limit.js.map +1 -1
  20. package/dist/interceptors/redirect.js +9 -8
  21. package/dist/interceptors/redirect.js.map +1 -1
  22. package/dist/interceptors/request-event.js +6 -8
  23. package/dist/interceptors/request-event.js.map +1 -1
  24. package/dist/interceptors/request-logger.js +6 -8
  25. package/dist/interceptors/request-logger.js.map +1 -1
  26. package/dist/interceptors/request-timing.js +6 -8
  27. package/dist/interceptors/request-timing.js.map +1 -1
  28. package/dist/interceptors/response-logger.js +6 -8
  29. package/dist/interceptors/response-logger.js.map +1 -1
  30. package/dist/interceptors/user-agent.js +8 -11
  31. package/dist/interceptors/user-agent.js.map +1 -1
  32. package/dist/interceptors/webex-tracking-id.js +6 -8
  33. package/dist/interceptors/webex-tracking-id.js.map +1 -1
  34. package/dist/interceptors/webex-user-agent.js +7 -10
  35. package/dist/interceptors/webex-user-agent.js.map +1 -1
  36. package/dist/lib/batcher.js +1 -1
  37. package/dist/lib/batcher.js.map +1 -1
  38. package/dist/lib/constants.js.map +1 -1
  39. package/dist/lib/credentials/credentials.js +4 -6
  40. package/dist/lib/credentials/credentials.js.map +1 -1
  41. package/dist/lib/credentials/grant-errors.js +18 -26
  42. package/dist/lib/credentials/grant-errors.js.map +1 -1
  43. package/dist/lib/credentials/index.js.map +1 -1
  44. package/dist/lib/credentials/scope.js.map +1 -1
  45. package/dist/lib/credentials/token-collection.js.map +1 -1
  46. package/dist/lib/credentials/token.js +5 -5
  47. package/dist/lib/credentials/token.js.map +1 -1
  48. package/dist/lib/interceptors/hostmap.js +6 -8
  49. package/dist/lib/interceptors/hostmap.js.map +1 -1
  50. package/dist/lib/interceptors/server-error.js +6 -8
  51. package/dist/lib/interceptors/server-error.js.map +1 -1
  52. package/dist/lib/interceptors/service.js +6 -8
  53. package/dist/lib/interceptors/service.js.map +1 -1
  54. package/dist/lib/metrics.js.map +1 -1
  55. package/dist/lib/page.js +5 -6
  56. package/dist/lib/page.js.map +1 -1
  57. package/dist/lib/services/index.js.map +1 -1
  58. package/dist/lib/services/service-catalog.js +3 -3
  59. package/dist/lib/services/service-catalog.js.map +1 -1
  60. package/dist/lib/services/service-fed-ramp.js.map +1 -1
  61. package/dist/lib/services/service-host.js +1 -2
  62. package/dist/lib/services/service-host.js.map +1 -1
  63. package/dist/lib/services/service-registry.js +1 -2
  64. package/dist/lib/services/service-registry.js.map +1 -1
  65. package/dist/lib/services/service-state.js +1 -2
  66. package/dist/lib/services/service-state.js.map +1 -1
  67. package/dist/lib/services/service-url.js +11 -1
  68. package/dist/lib/services/service-url.js.map +1 -1
  69. package/dist/lib/services/services.js +485 -127
  70. package/dist/lib/services/services.js.map +1 -1
  71. package/dist/lib/services-v2/index.js.map +1 -1
  72. package/dist/lib/services-v2/metrics.js.map +1 -1
  73. package/dist/lib/services-v2/service-catalog.js +7 -7
  74. package/dist/lib/services-v2/service-catalog.js.map +1 -1
  75. package/dist/lib/services-v2/service-detail.js.map +1 -1
  76. package/dist/lib/services-v2/service-fed-ramp.js.map +1 -1
  77. package/dist/lib/services-v2/services-v2.js +379 -51
  78. package/dist/lib/services-v2/services-v2.js.map +1 -1
  79. package/dist/lib/services-v2/types.js.map +1 -1
  80. package/dist/lib/stateless-webex-plugin.js +3 -4
  81. package/dist/lib/stateless-webex-plugin.js.map +1 -1
  82. package/dist/lib/storage/decorators.js.map +1 -1
  83. package/dist/lib/storage/errors.js +7 -9
  84. package/dist/lib/storage/errors.js.map +1 -1
  85. package/dist/lib/storage/index.js.map +1 -1
  86. package/dist/lib/storage/make-webex-plugin-store.js +14 -5
  87. package/dist/lib/storage/make-webex-plugin-store.js.map +1 -1
  88. package/dist/lib/storage/make-webex-store.js +13 -5
  89. package/dist/lib/storage/make-webex-store.js.map +1 -1
  90. package/dist/lib/storage/memory-store-adapter.js.map +1 -1
  91. package/dist/lib/webex-core-plugin-mixin.js.map +1 -1
  92. package/dist/lib/webex-http-error.js +8 -11
  93. package/dist/lib/webex-http-error.js.map +1 -1
  94. package/dist/lib/webex-internal-core-plugin-mixin.js.map +1 -1
  95. package/dist/lib/webex-plugin.js.map +1 -1
  96. package/dist/plugins/logger.js +1 -1
  97. package/dist/plugins/logger.js.map +1 -1
  98. package/dist/webex-core.js +11 -11
  99. package/dist/webex-core.js.map +1 -1
  100. package/dist/webex-internal-core.js.map +1 -1
  101. package/package.json +13 -13
  102. package/src/config.js +15 -0
  103. package/src/interceptors/redirect.js +4 -0
  104. package/src/lib/services/service-url.js +9 -1
  105. package/src/lib/services/services.js +315 -7
  106. package/src/lib/services-v2/index.ts +0 -1
  107. package/src/lib/services-v2/service-catalog.ts +4 -4
  108. package/src/lib/services-v2/services-v2.ts +307 -7
  109. package/src/lib/services-v2/types.ts +13 -0
  110. package/test/fixtures/host-catalog-v2.ts +1 -1
  111. package/test/integration/spec/services/service-catalog.js +10 -4
  112. package/test/integration/spec/services/services.js +65 -9
  113. package/test/integration/spec/services-v2/service-catalog.js +2 -2
  114. package/test/integration/spec/services-v2/services-v2.js +56 -6
  115. package/test/unit/spec/interceptors/redirect.js +98 -0
  116. package/test/unit/spec/services/service-url.js +110 -0
  117. package/test/unit/spec/services/services.js +411 -2
  118. package/test/unit/spec/services-v2/services-v2.ts +316 -0
@@ -11,11 +11,16 @@ _Object$defineProperty(exports, "__esModule", {
11
11
  value: true
12
12
  });
13
13
  exports.default = exports.DEFAULT_CLUSTER_SERVICE = exports.DEFAULT_CLUSTER = void 0;
14
- var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/slicedToArray"));
14
+ var _regenerator = _interopRequireDefault(require("@babel/runtime-corejs2/regenerator"));
15
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/asyncToGenerator"));
15
16
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
17
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/slicedToArray"));
16
18
  var _weakMap = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/weak-map"));
19
+ var _entries = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/entries"));
17
20
  var _keys = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/keys"));
18
21
  var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
22
+ var _now = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/date/now"));
23
+ var _stringify = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/json/stringify"));
19
24
  var _sha = _interopRequireDefault(require("crypto-js/sha256"));
20
25
  var _lodash = require("lodash");
21
26
  var _webexPlugin = _interopRequireDefault(require("../webex-plugin"));
@@ -35,6 +40,8 @@ var DEFAULT_CLUSTER = exports.DEFAULT_CLUSTER = 'urn:TEAM:us-east-2_a';
35
40
  var DEFAULT_CLUSTER_SERVICE = exports.DEFAULT_CLUSTER_SERVICE = 'identityLookup';
36
41
  var CLUSTER_SERVICE = process.env.WEBEX_CONVERSATION_CLUSTER_SERVICE || DEFAULT_CLUSTER_SERVICE;
37
42
  var DEFAULT_CLUSTER_IDENTIFIER = process.env.WEBEX_CONVERSATION_DEFAULT_CLUSTER || "".concat(DEFAULT_CLUSTER, ":").concat(CLUSTER_SERVICE);
43
+ var CATALOG_CACHE_KEY_V1 = 'services.v1.u2cHostMap';
44
+ var CATALOG_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
38
45
 
39
46
  /* eslint-disable no-underscore-dangle */
40
47
  /**
@@ -69,6 +76,8 @@ var Services = _webexPlugin.default.extend({
69
76
  _catalogs: new _weakMap.default(),
70
77
  _serviceUrls: null,
71
78
  _hostCatalog: null,
79
+ // Map of active cluster ids per service, e.g. { wdm: 'urn:TEAM:ap-southeast-2_m:wdm' }
80
+ _activeServices: {},
72
81
  /**
73
82
  * Get the registry associated with this webex instance.
74
83
  *
@@ -98,6 +107,45 @@ var Services = _webexPlugin.default.extend({
98
107
  _getCatalog: function _getCatalog() {
99
108
  return this._catalogs.get(this.webex);
100
109
  },
110
+ /**
111
+ * Safely access localStorage if available; returns the Storage or null.
112
+ * @returns {Storage|null}
113
+ */
114
+ _getLocalStorageSafe: function _getLocalStorageSafe() {
115
+ if (typeof window !== 'undefined' && window.localStorage) {
116
+ return window.localStorage;
117
+ }
118
+ return null;
119
+ },
120
+ /**
121
+ * Determine the intended preauth selection based on the current context.
122
+ * @param {string|undefined} currentOrgId
123
+ * @returns {{selectionType: string, selectionValue: string}}
124
+ */
125
+ getIntendedPreauthSelection: function getIntendedPreauthSelection(currentOrgId) {
126
+ var _this$webex$credentia;
127
+ if ((_this$webex$credentia = this.webex.credentials) !== null && _this$webex$credentia !== void 0 && _this$webex$credentia.canAuthorize) {
128
+ if (currentOrgId) {
129
+ return {
130
+ selectionType: 'orgId',
131
+ selectionValue: currentOrgId
132
+ };
133
+ }
134
+ }
135
+ var emailConfig = this.webex.config && this.webex.config.email;
136
+ if (typeof emailConfig === 'string' && emailConfig.trim()) {
137
+ return {
138
+ selectionType: 'emailhash',
139
+ selectionValue: (0, _sha.default)(emailConfig.toLowerCase()).toString()
140
+ };
141
+ }
142
+
143
+ // fall back to proximity mode when no orgId or email available
144
+ return {
145
+ selectionType: 'mode',
146
+ selectionValue: 'DEFAULT_BY_PROXIMITY'
147
+ };
148
+ },
101
149
  /**
102
150
  * Get a service url from the current services list by name
103
151
  * from the associated instance catalog.
@@ -156,6 +204,53 @@ var Services = _webexPlugin.default.extend({
156
204
  var catalog = this._getCatalog();
157
205
  return catalog.markFailedUrl(url, noPriorityHosts);
158
206
  },
207
+ /**
208
+ * Get all Mobius cluster host entries from the legacy host catalog.
209
+ * @returns {Array<{host: string, id: string, ttl: number, priority: number}>}
210
+ */
211
+ getMobiusClusters: function getMobiusClusters() {
212
+ this.logger.info('services: fetching mobius clusters');
213
+ var clusters = [];
214
+ var hostCatalog = this._hostCatalog || {};
215
+ (0, _entries.default)(hostCatalog).forEach(function (_ref) {
216
+ var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
217
+ host = _ref2[0],
218
+ entries = _ref2[1];
219
+ (entries || []).forEach(function (entry) {
220
+ if (typeof (entry === null || entry === void 0 ? void 0 : entry.id) === 'string' && entry.id.endsWith(':mobius')) {
221
+ // Ensure host is included; prefer entry.host if present, else use the map key
222
+ var withHost = entry.host ? entry.host : host;
223
+ // Skip duplicates for the same host
224
+ if (!clusters.find(function (c) {
225
+ return c && c.host === withHost;
226
+ })) {
227
+ clusters.push(_objectSpread(_objectSpread({}, entry), {}, {
228
+ host: withHost
229
+ }));
230
+ }
231
+ }
232
+ });
233
+ });
234
+ return clusters;
235
+ },
236
+ /**
237
+ * Check is valid host from the legacy host catalog.
238
+ * @param {string} host
239
+ * @returns {Boolean}
240
+ */
241
+ isValidHost: function isValidHost(host) {
242
+ var _hostCatalog$host;
243
+ var hostCatalog = this._hostCatalog || {};
244
+ return !!((_hostCatalog$host = hostCatalog[host]) !== null && _hostCatalog$host !== void 0 && _hostCatalog$host.length);
245
+ },
246
+ /**
247
+ * Merge provided active cluster mappings into current state.
248
+ * @param {Record<string,string>} activeServices
249
+ * @returns {void}
250
+ */
251
+ _updateActiveServices: function _updateActiveServices(activeServices) {
252
+ this._activeServices = _objectSpread(_objectSpread({}, this._activeServices), activeServices);
253
+ },
159
254
  /**
160
255
  * saves all the services from the pre and post catalog service
161
256
  * @param {Object} serviceUrls
@@ -186,63 +281,88 @@ var Services = _webexPlugin.default.extend({
186
281
  * @returns {Promise<object>}
187
282
  */
188
283
  updateServices: function updateServices() {
189
- var _this = this;
190
- var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
191
- from = _ref.from,
192
- query = _ref.query,
193
- token = _ref.token,
194
- forceRefresh = _ref.forceRefresh;
195
- var catalog = this._getCatalog();
196
- var formattedQuery;
197
- var serviceGroup;
198
-
199
- // map catalog name to service group name.
200
- switch (from) {
201
- case 'limited':
202
- serviceGroup = 'preauth';
203
- break;
204
- case 'signin':
205
- serviceGroup = 'signin';
206
- break;
207
- default:
208
- serviceGroup = 'postauth';
209
- break;
210
- }
211
-
212
- // confirm catalog update for group is not in progress.
213
- if (catalog.status[serviceGroup].collecting) {
214
- return this.waitForCatalog(serviceGroup);
215
- }
216
- catalog.status[serviceGroup].collecting = true;
217
- if (serviceGroup === 'preauth') {
218
- var queryKey = query && (0, _keys.default)(query)[0];
219
- if (!['email', 'emailhash', 'userId', 'orgId', 'mode'].includes(queryKey)) {
220
- return _promise.default.reject(new Error('a query param of email, emailhash, userId, orgId, or mode is required'));
221
- }
222
- }
223
- // encode email when query key is email
224
- if (serviceGroup === 'preauth' || serviceGroup === 'signin') {
225
- var _queryKey = (0, _keys.default)(query)[0];
226
- formattedQuery = {};
227
- if (_queryKey === 'email' && query.email) {
228
- formattedQuery.emailhash = (0, _sha.default)(query.email.toLowerCase()).toString();
229
- } else {
230
- formattedQuery[_queryKey] = query[_queryKey];
231
- }
232
- }
233
- return this._fetchNewServiceHostmap({
234
- from: from,
235
- token: token,
236
- query: formattedQuery,
237
- forceRefresh: forceRefresh
238
- }).then(function (serviceHostMap) {
239
- catalog.updateServiceUrls(serviceGroup, serviceHostMap);
240
- _this.updateCredentialsConfig();
241
- catalog.status[serviceGroup].collecting = false;
242
- }).catch(function (error) {
243
- catalog.status[serviceGroup].collecting = false;
244
- return _promise.default.reject(error);
245
- });
284
+ var _arguments = arguments,
285
+ _this = this;
286
+ return (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee() {
287
+ var _ref3, from, query, token, forceRefresh, catalog, formattedQuery, serviceGroup, queryKey, _queryKey, _t;
288
+ return _regenerator.default.wrap(function (_context) {
289
+ while (1) switch (_context.prev = _context.next) {
290
+ case 0:
291
+ _ref3 = _arguments.length > 0 && _arguments[0] !== undefined ? _arguments[0] : {}, from = _ref3.from, query = _ref3.query, token = _ref3.token, forceRefresh = _ref3.forceRefresh;
292
+ catalog = _this._getCatalog();
293
+ _t = from;
294
+ _context.next = _t === 'limited' ? 1 : _t === 'signin' ? 2 : 3;
295
+ break;
296
+ case 1:
297
+ serviceGroup = 'preauth';
298
+ return _context.abrupt("continue", 4);
299
+ case 2:
300
+ serviceGroup = 'signin';
301
+ return _context.abrupt("continue", 4);
302
+ case 3:
303
+ serviceGroup = 'postauth';
304
+ return _context.abrupt("continue", 4);
305
+ case 4:
306
+ if (!catalog.status[serviceGroup].collecting) {
307
+ _context.next = 5;
308
+ break;
309
+ }
310
+ return _context.abrupt("return", _this.waitForCatalog(serviceGroup));
311
+ case 5:
312
+ catalog.status[serviceGroup].collecting = true;
313
+ if (!(serviceGroup === 'preauth')) {
314
+ _context.next = 6;
315
+ break;
316
+ }
317
+ queryKey = query && (0, _keys.default)(query)[0];
318
+ if (['email', 'emailhash', 'userId', 'orgId', 'mode'].includes(queryKey)) {
319
+ _context.next = 6;
320
+ break;
321
+ }
322
+ return _context.abrupt("return", _promise.default.reject(new Error('a query param of email, emailhash, userId, orgId, or mode is required')));
323
+ case 6:
324
+ // encode email when query key is email
325
+ if (serviceGroup === 'preauth' || serviceGroup === 'signin') {
326
+ _queryKey = (0, _keys.default)(query)[0];
327
+ formattedQuery = {};
328
+ if (_queryKey === 'email' && query.email) {
329
+ formattedQuery.emailhash = (0, _sha.default)(query.email.toLowerCase()).toString();
330
+ } else {
331
+ formattedQuery[_queryKey] = query[_queryKey];
332
+ }
333
+ }
334
+ return _context.abrupt("return", _this._fetchNewServiceHostmap({
335
+ from: from,
336
+ token: token,
337
+ query: formattedQuery,
338
+ forceRefresh: forceRefresh
339
+ }).then(function (serviceHostMap) {
340
+ var formattedServiceHostMap = _this._formatReceivedHostmap(serviceHostMap);
341
+ // Build selection metadata for caching discrimination
342
+ var selectionMeta;
343
+ if (serviceGroup === 'preauth' || serviceGroup === 'signin') {
344
+ var key = formattedQuery && (0, _keys.default)(formattedQuery || {})[0];
345
+ if (key) {
346
+ selectionMeta = {
347
+ selectionType: key,
348
+ selectionValue: formattedQuery[key]
349
+ };
350
+ }
351
+ }
352
+ _this._cacheCatalog(serviceGroup, serviceHostMap, selectionMeta);
353
+ catalog.updateServiceUrls(serviceGroup, formattedServiceHostMap);
354
+ _this.updateCredentialsConfig();
355
+ catalog.status[serviceGroup].collecting = false;
356
+ }).catch(function (error) {
357
+ catalog.status[serviceGroup].collecting = false;
358
+ return _promise.default.reject(error);
359
+ }));
360
+ case 7:
361
+ case "end":
362
+ return _context.stop();
363
+ }
364
+ }, _callee);
365
+ }))();
246
366
  },
247
367
  /**
248
368
  * User validation parameter transfer object for {@link validateUser}.
@@ -267,16 +387,16 @@ var Services = _webexPlugin.default.extend({
267
387
  * @param {ValidateUserPTO} - The parameter transfer object.
268
388
  * @returns {ValidateUserRTO} - The return transfer object.
269
389
  */
270
- validateUser: function validateUser(_ref2) {
390
+ validateUser: function validateUser(_ref4) {
271
391
  var _this2 = this;
272
- var email = _ref2.email,
273
- _ref2$reqId = _ref2.reqId,
274
- reqId = _ref2$reqId === void 0 ? 'WEBCLIENT' : _ref2$reqId,
275
- _ref2$forceRefresh = _ref2.forceRefresh,
276
- forceRefresh = _ref2$forceRefresh === void 0 ? false : _ref2$forceRefresh,
277
- _ref2$activationOptio = _ref2.activationOptions,
278
- activationOptions = _ref2$activationOptio === void 0 ? {} : _ref2$activationOptio,
279
- preloginUserId = _ref2.preloginUserId;
392
+ var email = _ref4.email,
393
+ _ref4$reqId = _ref4.reqId,
394
+ reqId = _ref4$reqId === void 0 ? 'WEBCLIENT' : _ref4$reqId,
395
+ _ref4$forceRefresh = _ref4.forceRefresh,
396
+ forceRefresh = _ref4$forceRefresh === void 0 ? false : _ref4$forceRefresh,
397
+ _ref4$activationOptio = _ref4.activationOptions,
398
+ activationOptions = _ref4$activationOptio === void 0 ? {} : _ref4$activationOptio,
399
+ preloginUserId = _ref4.preloginUserId;
280
400
  this.logger.info('services: validating a user');
281
401
 
282
402
  // Validate that an email parameter key was provided.
@@ -313,9 +433,9 @@ var Services = _webexPlugin.default.extend({
313
433
 
314
434
  // Destructure the client authorization details.
315
435
  /* eslint-disable camelcase */
316
- var _this$webex$credentia = this.webex.credentials.config,
317
- client_id = _this$webex$credentia.client_id,
318
- client_secret = _this$webex$credentia.client_secret;
436
+ var _this$webex$credentia2 = this.webex.credentials.config,
437
+ client_id = _this$webex$credentia2.client_id,
438
+ client_secret = _this$webex$credentia2.client_secret;
319
439
 
320
440
  // Validate that client authentication details exist.
321
441
  if (!client_id || !client_secret) {
@@ -374,10 +494,10 @@ var Services = _webexPlugin.default.extend({
374
494
  activationOptions: activationOptions,
375
495
  preloginUserId: preloginUserId
376
496
  })]);
377
- }).then(function (_ref3) {
378
- var _ref4 = (0, _slicedToArray2.default)(_ref3, 2),
379
- rto = _ref4[0],
380
- user = _ref4[1];
497
+ }).then(function (_ref5) {
498
+ var _ref6 = (0, _slicedToArray2.default)(_ref5, 2),
499
+ rto = _ref6[0],
500
+ user = _ref6[1];
381
501
  return _objectSpread(_objectSpread({}, rto), {}, {
382
502
  user: user
383
503
  });
@@ -447,28 +567,31 @@ var Services = _webexPlugin.default.extend({
447
567
  * @param {SendUserActivationPTO} - The Parameter transfer object.
448
568
  * @returns {LicenseDTO} - The DTO returned from the **License** service.
449
569
  */
450
- sendUserActivation: function sendUserActivation(_ref5) {
570
+ sendUserActivation: function sendUserActivation(_ref7) {
451
571
  var _this5 = this;
452
- var email = _ref5.email,
453
- reqId = _ref5.reqId,
454
- token = _ref5.token,
455
- activationOptions = _ref5.activationOptions,
456
- preloginUserId = _ref5.preloginUserId;
572
+ var email = _ref7.email,
573
+ reqId = _ref7.reqId,
574
+ token = _ref7.token,
575
+ activationOptions = _ref7.activationOptions,
576
+ preloginUserId = _ref7.preloginUserId;
457
577
  this.logger.info('services: sending user activation request');
458
578
  var countryCode;
459
579
  var timezone;
460
580
 
461
581
  // try to fetch client region info first
462
582
  return this.fetchClientRegionInfo().then(function (clientRegionInfo) {
583
+ var _this5$webex$config$s;
463
584
  if (clientRegionInfo) {
464
585
  countryCode = clientRegionInfo.countryCode;
465
586
  timezone = clientRegionInfo.timezone;
466
587
  }
467
588
 
468
- // Send the user activation request to the **License** service.
589
+ // Send the user activation request.
590
+ // Use user-onboarding service if configured, otherwise use license service.
591
+ var useUserOnboarding = (_this5$webex$config$s = _this5.webex.config.services) === null || _this5$webex$config$s === void 0 ? void 0 : _this5$webex$config$s.useUserOnboardingServiceForActivations;
469
592
  return _this5.request({
470
- service: 'license',
471
- resource: 'users/activations',
593
+ service: useUserOnboarding ? 'user-onboarding' : 'license',
594
+ resource: useUserOnboarding ? 'api/v1/users/activations' : 'users/activations',
472
595
  method: 'POST',
473
596
  headers: {
474
597
  accept: 'application/json',
@@ -485,8 +608,8 @@ var Services = _webexPlugin.default.extend({
485
608
  });
486
609
  })
487
610
  // On success, return the **License** user object.
488
- .then(function (_ref6) {
489
- var body = _ref6.body;
611
+ .then(function (_ref8) {
612
+ var body = _ref8.body;
490
613
  return body;
491
614
  })
492
615
  // On failure, reject with error from **License**.
@@ -539,10 +662,10 @@ var Services = _webexPlugin.default.extend({
539
662
  * @returns {Promise<void>}
540
663
  */
541
664
  collectSigninCatalog: function collectSigninCatalog() {
542
- var _ref7 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
543
- email = _ref7.email,
544
- token = _ref7.token,
545
- forceRefresh = _ref7.forceRefresh;
665
+ var _ref9 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
666
+ email = _ref9.email,
667
+ token = _ref9.token,
668
+ forceRefresh = _ref9.forceRefresh;
546
669
  if (!email) {
547
670
  return _promise.default.reject(new Error('`email` is required'));
548
671
  }
@@ -580,7 +703,6 @@ var Services = _webexPlugin.default.extend({
580
703
  idbroker: {
581
704
  url: idbroker.replace(trailingSlashes, '') // remove trailing slash
582
705
  },
583
-
584
706
  identity: {
585
707
  url: identity.replace(trailingSlashes, '') // remove trailing slash
586
708
  }
@@ -621,12 +743,12 @@ var Services = _webexPlugin.default.extend({
621
743
  * @param {WaitForServicePTO} - The parameter transfer object.
622
744
  * @returns {Promise<string>} - Resolves to the priority host of a service.
623
745
  */
624
- waitForService: function waitForService(_ref8) {
746
+ waitForService: function waitForService(_ref0) {
625
747
  var _this6 = this;
626
- var name = _ref8.name,
627
- _ref8$timeout = _ref8.timeout,
628
- timeout = _ref8$timeout === void 0 ? 5 : _ref8$timeout,
629
- url = _ref8.url;
748
+ var name = _ref0.name,
749
+ _ref0$timeout = _ref0.timeout,
750
+ timeout = _ref0$timeout === void 0 ? 5 : _ref0$timeout,
751
+ url = _ref0.url;
630
752
  var services = this.webex.config.services;
631
753
 
632
754
  // Save memory by grabbing the catalog after there isn't a priortyURL
@@ -804,9 +926,9 @@ var Services = _webexPlugin.default.extend({
804
926
  * @returns {String} url of the service
805
927
  */
806
928
  getServiceUrlFromClusterId: function getServiceUrlFromClusterId() {
807
- var _ref9 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
808
- _ref9$cluster = _ref9.cluster,
809
- cluster = _ref9$cluster === void 0 ? 'us' : _ref9$cluster;
929
+ var _ref1 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
930
+ _ref1$cluster = _ref1.cluster,
931
+ cluster = _ref1$cluster === void 0 ? 'us' : _ref1$cluster;
810
932
  var clusterId = cluster === 'us' ? DEFAULT_CLUSTER_IDENTIFIER : cluster;
811
933
 
812
934
  // Determine if cluster has service name (non-US clusters from hydra do not)
@@ -924,9 +1046,223 @@ var Services = _webexPlugin.default.extend({
924
1046
  return _this7.request(requestObject);
925
1047
  }, 'internal.get.u2c.time').then(function (_ref12) {
926
1048
  var body = _ref12.body;
927
- return _this7._formatReceivedHostmap(body);
1049
+ return body;
928
1050
  });
929
1051
  },
1052
+ /**
1053
+ * Cache the catalog in the bounded storage.
1054
+ * @param {string} serviceGroup - preauth, signin, postauth
1055
+ * @param {object} hostMap - The hostmap to cache
1056
+ * @param {object} [meta] - Optional selection metadata used to validate cache reuse
1057
+ * @returns {Promise<void>}
1058
+ *
1059
+ */
1060
+ _cacheCatalog: function _cacheCatalog(serviceGroup, hostMap, meta) {
1061
+ var _this8 = this;
1062
+ return (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee2() {
1063
+ var current, orgId, _this8$webex$config, _this8$webex$config$c, _this8$webex, _this8$webex$config2, _this8$webex2, _this8$webex2$config, _this8$webex2$config$, _this8$webex2$config$2, _ls, cachedJson, credentials, _current, env, fedramp, u2cDiscoveryUrl, updated, ls, _t2;
1064
+ return _regenerator.default.wrap(function (_context2) {
1065
+ while (1) switch (_context2.prev = _context2.next) {
1066
+ case 0:
1067
+ current = {};
1068
+ _context2.prev = 1;
1069
+ if ((_this8$webex$config = _this8.webex.config) !== null && _this8$webex$config !== void 0 && (_this8$webex$config$c = _this8$webex$config.calling) !== null && _this8$webex$config$c !== void 0 && _this8$webex$config$c.cacheU2C) {
1070
+ _context2.next = 2;
1071
+ break;
1072
+ }
1073
+ _this8.logger.info("services: skipping cache write for ".concat(serviceGroup, " as per the config"));
1074
+ return _context2.abrupt("return");
1075
+ case 2:
1076
+ // Persist to localStorage to survive browser refresh
1077
+ try {
1078
+ _ls = _this8._getLocalStorageSafe();
1079
+ cachedJson = _ls ? _ls.getItem(CATALOG_CACHE_KEY_V1) : null;
1080
+ current = cachedJson ? JSON.parse(cachedJson) : {};
1081
+ } catch (e) {
1082
+ current = {};
1083
+ }
1084
+ try {
1085
+ credentials = _this8.webex.credentials;
1086
+ orgId = credentials.getOrgId();
1087
+ } catch (e) {
1088
+ orgId = current.orgId;
1089
+ }
1090
+
1091
+ // Capture environment fingerprint to invalidate cache across env changes
1092
+ _current = current, env = _current.env;
1093
+ fedramp = !!((_this8$webex = _this8.webex) !== null && _this8$webex !== void 0 && (_this8$webex$config2 = _this8$webex.config) !== null && _this8$webex$config2 !== void 0 && _this8$webex$config2.fedramp);
1094
+ u2cDiscoveryUrl = (_this8$webex2 = _this8.webex) === null || _this8$webex2 === void 0 ? void 0 : (_this8$webex2$config = _this8$webex2.config) === null || _this8$webex2$config === void 0 ? void 0 : (_this8$webex2$config$ = _this8$webex2$config.services) === null || _this8$webex2$config$ === void 0 ? void 0 : (_this8$webex2$config$2 = _this8$webex2$config$.discovery) === null || _this8$webex2$config$2 === void 0 ? void 0 : _this8$webex2$config$2.u2c;
1095
+ env = {
1096
+ fedramp: fedramp,
1097
+ u2cDiscoveryUrl: u2cDiscoveryUrl
1098
+ };
1099
+ updated = _objectSpread(_objectSpread({}, current), {}, (0, _defineProperty2.default)((0, _defineProperty2.default)({
1100
+ orgId: orgId || current.orgId,
1101
+ env: env || current.env
1102
+ }, serviceGroup, meta ? {
1103
+ hostMap: hostMap,
1104
+ meta: meta
1105
+ } : hostMap), "cachedAt", (0, _now.default)()));
1106
+ ls = _this8._getLocalStorageSafe();
1107
+ if (ls) {
1108
+ ls.setItem(CATALOG_CACHE_KEY_V1, (0, _stringify.default)(updated));
1109
+ }
1110
+ _context2.next = 4;
1111
+ break;
1112
+ case 3:
1113
+ _context2.prev = 3;
1114
+ _t2 = _context2["catch"](1);
1115
+ _this8.logger.warn('services: error caching catalog', _t2);
1116
+ case 4:
1117
+ case "end":
1118
+ return _context2.stop();
1119
+ }
1120
+ }, _callee2, null, [[1, 3]]);
1121
+ }))();
1122
+ },
1123
+ /**
1124
+ * Load the catalog from cache and hydrate the in-memory ServiceCatalog.
1125
+ * @returns {Promise<boolean>} true if cache was loaded, false otherwise
1126
+ */
1127
+ _loadCatalogFromCache: function _loadCatalogFromCache() {
1128
+ var _this9 = this;
1129
+ return (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3() {
1130
+ var currentOrgId, _this9$webex$config, _this9$webex$config$c, _this9$webex$config2, _this9$webex$config3, _this9$webex$config3$, _this9$webex$config3$2, ls, cachedJson, cached, cachedAt, _this9$webex$credenti, credentials, fedramp, u2cDiscoveryUrl, currentEnv, sameEnv, catalog, groups, _t3, _t4;
1131
+ return _regenerator.default.wrap(function (_context3) {
1132
+ while (1) switch (_context3.prev = _context3.next) {
1133
+ case 0:
1134
+ _context3.prev = 0;
1135
+ if ((_this9$webex$config = _this9.webex.config) !== null && _this9$webex$config !== void 0 && (_this9$webex$config$c = _this9$webex$config.calling) !== null && _this9$webex$config$c !== void 0 && _this9$webex$config$c.cacheU2C) {
1136
+ _context3.next = 1;
1137
+ break;
1138
+ }
1139
+ _this9.logger.info('services: skipping cache warm-up as per the cache config');
1140
+ return _context3.abrupt("return", false);
1141
+ case 1:
1142
+ ls = _this9._getLocalStorageSafe();
1143
+ if (ls) {
1144
+ _context3.next = 2;
1145
+ break;
1146
+ }
1147
+ _this9.logger.info('services: skipping cache warm-up as no localStorage is available');
1148
+ return _context3.abrupt("return", false);
1149
+ case 2:
1150
+ cachedJson = ls.getItem(CATALOG_CACHE_KEY_V1);
1151
+ cached = cachedJson ? JSON.parse(cachedJson) : undefined;
1152
+ if (cached) {
1153
+ _context3.next = 3;
1154
+ break;
1155
+ }
1156
+ return _context3.abrupt("return", false);
1157
+ case 3:
1158
+ // TTL enforcement: clear if older than 24 hours
1159
+ cachedAt = Number(cached.cachedAt) || 0;
1160
+ if (!(!cachedAt || (0, _now.default)() - cachedAt > CATALOG_TTL_MS)) {
1161
+ _context3.next = 4;
1162
+ break;
1163
+ }
1164
+ _this9.clearCatalogCache();
1165
+ return _context3.abrupt("return", false);
1166
+ case 4:
1167
+ _context3.prev = 4;
1168
+ if (!((_this9$webex$credenti = _this9.webex.credentials) !== null && _this9$webex$credenti !== void 0 && _this9$webex$credenti.canAuthorize)) {
1169
+ _context3.next = 5;
1170
+ break;
1171
+ }
1172
+ credentials = _this9.webex.credentials;
1173
+ currentOrgId = credentials.getOrgId();
1174
+ if (!(cached.orgId && cached.orgId !== currentOrgId)) {
1175
+ _context3.next = 5;
1176
+ break;
1177
+ }
1178
+ return _context3.abrupt("return", false);
1179
+ case 5:
1180
+ _context3.next = 7;
1181
+ break;
1182
+ case 6:
1183
+ _context3.prev = 6;
1184
+ _t3 = _context3["catch"](4);
1185
+ _this9.logger.warn('services: error checking orgId', _t3);
1186
+ case 7:
1187
+ // Ensure cached environment matches current environment
1188
+ fedramp = !!((_this9$webex$config2 = _this9.webex.config) !== null && _this9$webex$config2 !== void 0 && _this9$webex$config2.fedramp);
1189
+ u2cDiscoveryUrl = (_this9$webex$config3 = _this9.webex.config) === null || _this9$webex$config3 === void 0 ? void 0 : (_this9$webex$config3$ = _this9$webex$config3.services) === null || _this9$webex$config3$ === void 0 ? void 0 : (_this9$webex$config3$2 = _this9$webex$config3$.discovery) === null || _this9$webex$config3$2 === void 0 ? void 0 : _this9$webex$config3$2.u2c;
1190
+ currentEnv = {
1191
+ fedramp: fedramp,
1192
+ u2cDiscoveryUrl: u2cDiscoveryUrl
1193
+ };
1194
+ if (!cached.env) {
1195
+ _context3.next = 8;
1196
+ break;
1197
+ }
1198
+ sameEnv = cached.env.fedramp === currentEnv.fedramp && cached.env.u2cDiscoveryUrl === currentEnv.u2cDiscoveryUrl;
1199
+ if (sameEnv) {
1200
+ _context3.next = 8;
1201
+ break;
1202
+ }
1203
+ _this9.logger.info('services: skipping cache warm due to environment mismatch');
1204
+ return _context3.abrupt("return", false);
1205
+ case 8:
1206
+ catalog = _this9._getCatalog(); // Apply any cached groups (with preauth selection validation if available)
1207
+ groups = ['preauth', 'signin', 'postauth'];
1208
+ groups.forEach(function (serviceGroup) {
1209
+ var cachedGroup = cached[serviceGroup];
1210
+ if (!cachedGroup) {
1211
+ return;
1212
+ }
1213
+
1214
+ // Support legacy (hostMap) and new ({hostMap, meta}) shapes
1215
+ var hostMap = cachedGroup && cachedGroup.hostMap ? cachedGroup.hostMap : cachedGroup;
1216
+ var meta = cachedGroup === null || cachedGroup === void 0 ? void 0 : cachedGroup.meta;
1217
+ if (serviceGroup === 'preauth' && meta) {
1218
+ // For proximity-based selection, always fetch fresh to respect IP/region changes
1219
+ if (meta.selectionType === 'mode') {
1220
+ _this9.logger.info('services: skipping preauth cache warm for proximity mode');
1221
+ return;
1222
+ }
1223
+ var intended = _this9.getIntendedPreauthSelection(currentOrgId);
1224
+ var matches = intended && intended.selectionType === meta.selectionType && intended.selectionValue === meta.selectionValue;
1225
+ if (!matches) {
1226
+ _this9.logger.info('services: skipping preauth cache warm due to selection mismatch');
1227
+ return;
1228
+ }
1229
+ }
1230
+ if (hostMap) {
1231
+ var formatted = _this9._formatReceivedHostmap(hostMap);
1232
+ catalog.updateServiceUrls(serviceGroup, formatted);
1233
+ }
1234
+ });
1235
+
1236
+ // Align credentials against warmed catalog
1237
+ _this9.updateCredentialsConfig();
1238
+ return _context3.abrupt("return", true);
1239
+ case 9:
1240
+ _context3.prev = 9;
1241
+ _t4 = _context3["catch"](0);
1242
+ _this9.logger.warn('services: error loading catalog from cache', _t4);
1243
+ return _context3.abrupt("return", false);
1244
+ case 10:
1245
+ case "end":
1246
+ return _context3.stop();
1247
+ }
1248
+ }, _callee3, null, [[0, 9], [4, 6]]);
1249
+ }))();
1250
+ },
1251
+ /**
1252
+ * Clear the catalog cache from the bounded storage.
1253
+ * @returns {Promise<void>}
1254
+ */
1255
+ clearCatalogCache: function clearCatalogCache() {
1256
+ try {
1257
+ var ls = this._getLocalStorageSafe();
1258
+ if (ls) {
1259
+ ls.removeItem(CATALOG_CACHE_KEY_V1);
1260
+ }
1261
+ } catch (e) {
1262
+ this.logger.warn('services: error clearing catalog cache', e);
1263
+ }
1264
+ return _promise.default.resolve();
1265
+ },
930
1266
  /**
931
1267
  * Initialize the discovery services and the whitelisted services.
932
1268
  *
@@ -991,7 +1327,7 @@ var Services = _webexPlugin.default.extend({
991
1327
  * @returns {Promise<void, Error>} - Errors if the token is unavailable.
992
1328
  */
993
1329
  initServiceCatalogs: function initServiceCatalogs() {
994
- var _this8 = this;
1330
+ var _this0 = this;
995
1331
  this.logger.info('services: initializing initial service catalogs');
996
1332
 
997
1333
  // Destructure the credentials plugin.
@@ -1006,16 +1342,17 @@ var Services = _webexPlugin.default.extend({
1006
1342
  })
1007
1343
  // Begin collecting the preauth/limited catalog.
1008
1344
  .then(function (orgId) {
1009
- return _this8.collectPreauthCatalog({
1345
+ return _this0.collectPreauthCatalog({
1010
1346
  orgId: orgId
1011
1347
  });
1012
1348
  }).then(function () {
1013
1349
  // Validate if the token is authorized.
1014
1350
  if (credentials.canAuthorize) {
1015
1351
  // Attempt to collect the postauth catalog.
1016
- return _this8.updateServices().catch(function () {
1017
- _this8.initFailed = true;
1018
- _this8.logger.warn('services: cannot retrieve postauth catalog');
1352
+
1353
+ return _this0.updateServices().catch(function () {
1354
+ _this0.initFailed = true;
1355
+ _this0.logger.warn('services: cannot retrieve postauth catalog');
1019
1356
  });
1020
1357
  }
1021
1358
 
@@ -1031,7 +1368,7 @@ var Services = _webexPlugin.default.extend({
1031
1368
  * @returns {Services}
1032
1369
  */
1033
1370
  initialize: function initialize() {
1034
- var _this9 = this;
1371
+ var _this1 = this;
1035
1372
  var catalog = new _serviceCatalog.default();
1036
1373
  var registry = new _serviceRegistry.default();
1037
1374
  var state = new _serviceState.default();
@@ -1041,33 +1378,54 @@ var Services = _webexPlugin.default.extend({
1041
1378
 
1042
1379
  // Listen for configuration changes once.
1043
1380
  this.listenToOnce(this.webex, 'change:config', function () {
1044
- _this9.initConfig();
1381
+ _this1.initConfig();
1045
1382
  });
1046
1383
 
1047
1384
  // wait for webex instance to be ready before attempting
1048
1385
  // to update the service catalogs
1049
- this.listenToOnce(this.webex, 'ready', function () {
1050
- var supertoken = _this9.webex.credentials.supertoken;
1051
- // Validate if the supertoken exists.
1052
- if (supertoken && supertoken.access_token) {
1053
- _this9.initServiceCatalogs().then(function () {
1054
- catalog.isReady = true;
1055
- }).catch(function (error) {
1056
- _this9.initFailed = true;
1057
- _this9.logger.error("services: failed to init initial services when credentials available, ".concat(error === null || error === void 0 ? void 0 : error.message));
1058
- });
1059
- } else {
1060
- var email = _this9.webex.config.email;
1061
- _this9.collectPreauthCatalog(email ? {
1062
- email: email
1063
- } : undefined).catch(function (error) {
1064
- _this9.initFailed = true;
1065
- _this9.logger.error("services: failed to init initial services when no credentials available, ".concat(error === null || error === void 0 ? void 0 : error.message));
1066
- });
1067
- }
1068
- });
1386
+ // this can cause a race condition because credentials may
1387
+ // not be valid when services is initialized
1388
+ this.listenToOnce(this.webex, 'ready', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee4() {
1389
+ var cachedCatalog, supertoken, email;
1390
+ return _regenerator.default.wrap(function (_context4) {
1391
+ while (1) switch (_context4.prev = _context4.next) {
1392
+ case 0:
1393
+ _context4.next = 1;
1394
+ return _this1._loadCatalogFromCache();
1395
+ case 1:
1396
+ cachedCatalog = _context4.sent;
1397
+ if (!cachedCatalog) {
1398
+ _context4.next = 2;
1399
+ break;
1400
+ }
1401
+ catalog.isReady = true;
1402
+ return _context4.abrupt("return");
1403
+ case 2:
1404
+ supertoken = _this1.webex.credentials.supertoken; // Validate if the supertoken exists.
1405
+ if (supertoken && supertoken.access_token) {
1406
+ _this1.initServiceCatalogs().then(function () {
1407
+ catalog.isReady = true;
1408
+ }).catch(function (error) {
1409
+ _this1.initFailed = true;
1410
+ _this1.logger.error("services: failed to init initial services when credentials available, ".concat(error === null || error === void 0 ? void 0 : error.message));
1411
+ });
1412
+ } else {
1413
+ email = _this1.webex.config.email;
1414
+ _this1.collectPreauthCatalog(email ? {
1415
+ email: email
1416
+ } : undefined).catch(function (error) {
1417
+ _this1.initFailed = true;
1418
+ _this1.logger.error("services: failed to init initial services when no credentials available, ".concat(error === null || error === void 0 ? void 0 : error.message));
1419
+ });
1420
+ }
1421
+ case 3:
1422
+ case "end":
1423
+ return _context4.stop();
1424
+ }
1425
+ }, _callee4);
1426
+ })));
1069
1427
  },
1070
- version: "3.10.0"
1428
+ version: "0.0.0"
1071
1429
  });
1072
1430
  /* eslint-enable no-underscore-dangle */
1073
1431
  var _default = exports.default = Services;