@webex/webex-core 3.12.0-llmrefactor.1 → 3.12.0-llmrefactor.10

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 (44) hide show
  1. package/dist/config.js +23 -0
  2. package/dist/config.js.map +1 -1
  3. package/dist/index.js +7 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/interceptors/catalog-url.js +87 -0
  6. package/dist/interceptors/catalog-url.js.map +1 -0
  7. package/dist/interceptors/payload-transformer.js +25 -4
  8. package/dist/interceptors/payload-transformer.js.map +1 -1
  9. package/dist/lib/batcher.js +1 -1
  10. package/dist/lib/credentials/credentials.js +56 -1
  11. package/dist/lib/credentials/credentials.js.map +1 -1
  12. package/dist/lib/credentials/token.js +1 -1
  13. package/dist/lib/services/service-catalog.js +102 -8
  14. package/dist/lib/services/service-catalog.js.map +1 -1
  15. package/dist/lib/services/services.js +74 -44
  16. package/dist/lib/services/services.js.map +1 -1
  17. package/dist/lib/services-v2/service-catalog.js +8 -1
  18. package/dist/lib/services-v2/service-catalog.js.map +1 -1
  19. package/dist/lib/services-v2/services-v2.js +76 -45
  20. package/dist/lib/services-v2/services-v2.js.map +1 -1
  21. package/dist/plugins/logger.js +1 -1
  22. package/dist/webex-core.js +11 -3
  23. package/dist/webex-core.js.map +1 -1
  24. package/package.json +12 -12
  25. package/src/config.js +25 -0
  26. package/src/index.js +1 -0
  27. package/src/interceptors/catalog-url.js +66 -0
  28. package/src/interceptors/payload-transformer.js +23 -1
  29. package/src/lib/credentials/credentials.js +43 -0
  30. package/src/lib/services/service-catalog.js +106 -6
  31. package/src/lib/services/services.js +65 -30
  32. package/src/lib/services-v2/service-catalog.ts +10 -1
  33. package/src/lib/services-v2/services-v2.ts +70 -30
  34. package/src/webex-core.js +8 -0
  35. package/test/integration/spec/services/services.js +148 -113
  36. package/test/integration/spec/services-v2/services-v2.js +134 -99
  37. package/test/unit/spec/credentials/credentials.js +114 -1
  38. package/test/unit/spec/interceptors/catalog-url.js +224 -0
  39. package/test/unit/spec/interceptors/payload-transformer.js +119 -1
  40. package/test/unit/spec/services/service-catalog.js +275 -0
  41. package/test/unit/spec/services/services.js +135 -2
  42. package/test/unit/spec/services-v2/service-catalog.ts +41 -0
  43. package/test/unit/spec/services-v2/services-v2.ts +158 -2
  44. package/test/unit/spec/webex-core.js +77 -1
@@ -1103,9 +1103,16 @@ const Services = WebexPlugin.extend({
1103
1103
  requestObject.headers = {authorization: token};
1104
1104
  }
1105
1105
 
1106
- return this.webex.internal.newMetrics.callDiagnosticLatencies
1107
- .measureLatency(() => this.request(requestObject), 'internal.get.u2c.time')
1108
- .then(({body}) => body);
1106
+ const sendRequest = () => this.request(requestObject);
1107
+
1108
+ const responsePromise = this.webex.internal.newMetrics
1109
+ ? this.webex.internal.newMetrics.callDiagnosticLatencies.measureLatency(
1110
+ sendRequest,
1111
+ 'internal.get.u2c.time'
1112
+ )
1113
+ : sendRequest();
1114
+
1115
+ return responsePromise.then(({body}) => body);
1109
1116
  },
1110
1117
 
1111
1118
  /**
@@ -1418,6 +1425,28 @@ const Services = WebexPlugin.extend({
1418
1425
  this.ready = true;
1419
1426
  },
1420
1427
 
1428
+ /**
1429
+ * Build a promise that rejects once the catalog init timeout elapses. Race
1430
+ * this against catalog collection so a hung request never leaves
1431
+ * `services.ready` false forever - that would stall `webex.ready` and leave
1432
+ * consumers waiting on it indefinitely. Timeout is configurable via
1433
+ * `config.services.catalogInitTimeout` (defaults to 15s in config). Created
1434
+ * lazily so paths that skip catalog collection never schedule a stray timer.
1435
+ *
1436
+ * @private
1437
+ * @returns {Promise<never>}
1438
+ */
1439
+ _makeInitTimeout() {
1440
+ const initTimeoutMs = this.webex.config?.services?.catalogInitTimeout;
1441
+
1442
+ return new Promise((_, reject) => {
1443
+ setTimeout(
1444
+ () => reject(new Error(`services: init timed out after ${initTimeoutMs}ms`)),
1445
+ initTimeoutMs
1446
+ );
1447
+ });
1448
+ },
1449
+
1421
1450
  /**
1422
1451
  * Initializer
1423
1452
  *
@@ -1492,6 +1521,14 @@ const Services = WebexPlugin.extend({
1492
1521
  } else {
1493
1522
  const {email} = this.webex.config;
1494
1523
 
1524
+ if (this.webex.config?.services?.skipPreauthCatalogOnUnauthenticated === true) {
1525
+ this.logger.info(
1526
+ 'services: skipping preauth catalog collection while unauthenticated as per the config'
1527
+ );
1528
+
1529
+ return;
1530
+ }
1531
+
1495
1532
  this.collectPreauthCatalog(email ? {email} : undefined).catch((error) => {
1496
1533
  this.initFailed = true;
1497
1534
  this.logger.error(
@@ -1526,25 +1563,12 @@ const Services = WebexPlugin.extend({
1526
1563
  }
1527
1564
  const {supertoken} = this.webex.credentials;
1528
1565
 
1529
- // Race init against a hard timeout so a hung request never leaves
1530
- // `services.ready` false forever - that would stall `webex.ready` and
1531
- // leave consumers waiting on it indefinitely. Timeout is configurable via
1532
- // `config.services.catalogInitTimeout` (defaults to 15s in config).
1533
- const initTimeoutMs = this.webex.config?.services?.catalogInitTimeout;
1534
-
1535
- const initServiceCatalogsTimeout = new Promise((_, reject) => {
1536
- setTimeout(
1537
- () => reject(new Error(`services: init timed out after ${initTimeoutMs}ms`)),
1538
- initTimeoutMs
1539
- );
1540
- });
1541
-
1542
1566
  // Validate if the supertoken exists.
1543
1567
  if (supertoken && supertoken.access_token) {
1544
1568
  // `initServiceCatalogs` marks the catalog ready internally once the
1545
1569
  // postauth catalog is collected - even if it loses the timeout race
1546
- // above, so a slow fetch still eventually flips `catalog.isReady`.
1547
- Promise.race([this.initServiceCatalogs(), initServiceCatalogsTimeout])
1570
+ // below, so a slow fetch still eventually flips `catalog.isReady`.
1571
+ Promise.race([this.initServiceCatalogs(), this._makeInitTimeout()])
1548
1572
  .catch((error) => {
1549
1573
  this.initFailed = true;
1550
1574
  this.logger.error(
@@ -1555,18 +1579,6 @@ const Services = WebexPlugin.extend({
1555
1579
  } else {
1556
1580
  const {email} = this.webex.config;
1557
1581
 
1558
- Promise.race([
1559
- this.collectPreauthCatalog(email ? {email} : undefined),
1560
- initServiceCatalogsTimeout,
1561
- ])
1562
- .catch((error) => {
1563
- this.initFailed = true;
1564
- this.logger.error(
1565
- `services: failed to init initial services when no credentials available, ${error?.message}`
1566
- );
1567
- })
1568
- .finally(() => this._finalizeReady());
1569
-
1570
1582
  // Handle fresh login: 'loaded' fires before OAuth completes, so listen
1571
1583
  // for `canAuthorize` flipping true and then collect the postauth catalog.
1572
1584
  this.listenToOnce(this.webex, 'change:canAuthorize', () => {
@@ -1579,6 +1591,29 @@ const Services = WebexPlugin.extend({
1579
1591
  });
1580
1592
  }
1581
1593
  });
1594
+
1595
+ if (this.webex.config?.services?.skipPreauthCatalogOnUnauthenticated === true) {
1596
+ // Skip the preauth catalog fetch (it will be collected manually
1597
+ // later), but still finalize `services.ready` so `webex.ready` is not
1598
+ // stalled while unauthenticated. No timeout is created here so there
1599
+ // is no stray timer or unhandled rejection.
1600
+ this.logger.info(
1601
+ 'services: skipping preauth catalog collection while unauthenticated as per the config'
1602
+ );
1603
+ this._finalizeReady();
1604
+ } else {
1605
+ Promise.race([
1606
+ this.collectPreauthCatalog(email ? {email} : undefined),
1607
+ this._makeInitTimeout(),
1608
+ ])
1609
+ .catch((error) => {
1610
+ this.initFailed = true;
1611
+ this.logger.error(
1612
+ `services: failed to init initial services when no credentials available, ${error?.message}`
1613
+ );
1614
+ })
1615
+ .finally(() => this._finalizeReady());
1616
+ }
1582
1617
  }
1583
1618
  });
1584
1619
  },
@@ -4,6 +4,7 @@ import {union} from 'lodash';
4
4
  import ServiceDetail from './service-detail';
5
5
  import {IServiceDetail, ServiceGroup} from './types';
6
6
  import {matchAllowedDomain, normalizeAllowedDomains} from '../domains';
7
+ import {matchesParsedCatalogUrl, parseCatalogUrl} from '../services/service-catalog';
7
8
 
8
9
  /**
9
10
  * @class
@@ -199,9 +200,17 @@ const ServiceCatalog = AmpState.extend({
199
200
  findServiceDetailFromUrl(url: string): IServiceDetail | undefined {
200
201
  const serviceDetails = this._getAllServiceDetails();
201
202
 
203
+ let candidateUrl: URL;
204
+
205
+ try {
206
+ candidateUrl = new URL(url);
207
+ } catch {
208
+ return undefined;
209
+ }
210
+
202
211
  return serviceDetails.find(({serviceUrls}) => {
203
212
  for (const serviceUrl of serviceUrls) {
204
- if (url.startsWith(serviceUrl.baseUrl)) {
213
+ if (matchesParsedCatalogUrl(candidateUrl, parseCatalogUrl(serviceUrl.baseUrl))) {
205
214
  return true;
206
215
  }
207
216
  }
@@ -7,6 +7,7 @@ import METRICS from '../metrics';
7
7
  import ServiceCatalog from './service-catalog';
8
8
  import fedRampServices from './service-fed-ramp';
9
9
  import {COMMERCIAL_ALLOWED_DOMAINS} from '../constants';
10
+ import {matchesCatalogUrl} from '../services/service-catalog';
10
11
  import {
11
12
  ActiveServices,
12
13
  IServiceCatalog,
@@ -15,6 +16,7 @@ import {
15
16
  ServiceHostmap,
16
17
  ServiceGroup,
17
18
  ServiceHost,
19
+ ServiceUrl,
18
20
  SelectionMeta,
19
21
  } from './types';
20
22
 
@@ -988,7 +990,9 @@ const Services = WebexPlugin.extend({
988
990
 
989
991
  const priorityUrl = service.get();
990
992
  const defaultUrl = new URL(
991
- service.serviceUrls.find((serviceUrl) => url.startsWith(serviceUrl.baseUrl)).baseUrl
993
+ service.serviceUrls.find((serviceUrl: ServiceUrl) =>
994
+ matchesCatalogUrl(url, serviceUrl.baseUrl)
995
+ ).baseUrl
992
996
  ).href;
993
997
 
994
998
  return {
@@ -1257,9 +1261,16 @@ const Services = WebexPlugin.extend({
1257
1261
  requestObject.headers = {authorization: token};
1258
1262
  }
1259
1263
 
1260
- return this.webex.internal.newMetrics.callDiagnosticLatencies
1261
- .measureLatency(() => this.request(requestObject), 'internal.get.u2c.time')
1262
- .then(({body}) => this._formatReceivedHostmap(body || {}));
1264
+ const sendRequest = () => this.request(requestObject);
1265
+
1266
+ const responsePromise = this.webex.internal.newMetrics
1267
+ ? this.webex.internal.newMetrics.callDiagnosticLatencies.measureLatency(
1268
+ sendRequest,
1269
+ 'internal.get.u2c.time'
1270
+ )
1271
+ : sendRequest();
1272
+
1273
+ return responsePromise.then(({body}) => this._formatReceivedHostmap(body || {}));
1263
1274
  },
1264
1275
 
1265
1276
  /**
@@ -1388,6 +1399,28 @@ const Services = WebexPlugin.extend({
1388
1399
  this.ready = true;
1389
1400
  },
1390
1401
 
1402
+ /**
1403
+ * Build a promise that rejects once the catalog init timeout elapses. Race
1404
+ * this against catalog collection so a hung request never leaves
1405
+ * `services.ready` false forever - that would stall `webex.ready` and leave
1406
+ * consumers waiting on it indefinitely. Timeout is configurable via
1407
+ * `config.services.catalogInitTimeout` (defaults to 15s in config). Created
1408
+ * lazily so paths that skip catalog collection never schedule a stray timer.
1409
+ *
1410
+ * @private
1411
+ * @returns {Promise<never>}
1412
+ */
1413
+ _makeInitTimeout(): Promise<never> {
1414
+ const initTimeoutMs = this.webex.config?.services?.catalogInitTimeout;
1415
+
1416
+ return new Promise<never>((_, reject) => {
1417
+ setTimeout(
1418
+ () => reject(new Error(`services: init timed out after ${initTimeoutMs}ms`)),
1419
+ initTimeoutMs
1420
+ );
1421
+ });
1422
+ },
1423
+
1391
1424
  /**
1392
1425
  * Initializer
1393
1426
  *
@@ -1455,6 +1488,14 @@ const Services = WebexPlugin.extend({
1455
1488
  } else {
1456
1489
  const {email} = this.webex.config;
1457
1490
 
1491
+ if (this.webex.config?.services?.skipPreauthCatalogOnUnauthenticated === true) {
1492
+ this.logger.info(
1493
+ 'services: skipping preauth catalog collection while unauthenticated as per the config'
1494
+ );
1495
+
1496
+ return;
1497
+ }
1498
+
1458
1499
  this.collectPreauthCatalog(email ? {email} : undefined).catch((error) => {
1459
1500
  this.initFailed = true;
1460
1501
  this.logger.error(
@@ -1489,24 +1530,12 @@ const Services = WebexPlugin.extend({
1489
1530
  }
1490
1531
  const {supertoken} = this.webex.credentials;
1491
1532
 
1492
- // Race init against a hard timeout so a hung request never leaves
1493
- // `services.ready` false forever - that would stall `webex.ready` and
1494
- // leave consumers waiting on it indefinitely. Timeout is configurable via
1495
- // `config.services.catalogInitTimeout` (defaults to 15s in config).
1496
- const initTimeoutMs = this.webex.config?.services?.catalogInitTimeout;
1497
- const initServiceCatalogsTimeout = new Promise<never>((_, reject) => {
1498
- setTimeout(
1499
- () => reject(new Error(`services: init timed out after ${initTimeoutMs}ms`)),
1500
- initTimeoutMs
1501
- );
1502
- });
1503
-
1504
1533
  // Validate if the supertoken exists.
1505
1534
  if (supertoken && supertoken.access_token) {
1506
1535
  // `initServiceCatalogs` marks the catalog ready internally once the
1507
1536
  // postauth catalog is collected - even if it loses the timeout race
1508
- // above, so a slow fetch still eventually flips `catalog.isReady`.
1509
- Promise.race([this.initServiceCatalogs(), initServiceCatalogsTimeout])
1537
+ // below, so a slow fetch still eventually flips `catalog.isReady`.
1538
+ Promise.race([this.initServiceCatalogs(), this._makeInitTimeout()])
1510
1539
  .catch((error) => {
1511
1540
  this.initFailed = true;
1512
1541
  this.logger.error(
@@ -1517,18 +1546,6 @@ const Services = WebexPlugin.extend({
1517
1546
  } else {
1518
1547
  const {email} = this.webex.config;
1519
1548
 
1520
- Promise.race([
1521
- this.collectPreauthCatalog(email ? {email} : undefined),
1522
- initServiceCatalogsTimeout,
1523
- ])
1524
- .catch((error) => {
1525
- this.initFailed = true;
1526
- this.logger.error(
1527
- `services: failed to init initial services when no credentials available, ${error?.message}`
1528
- );
1529
- })
1530
- .finally(() => this._finalizeReady());
1531
-
1532
1549
  // Handle fresh login: 'loaded' fires before OAuth completes, so listen
1533
1550
  // for `canAuthorize` flipping true and then collect the postauth catalog.
1534
1551
  this.listenToOnce(this.webex, 'change:canAuthorize', () => {
@@ -1541,6 +1558,29 @@ const Services = WebexPlugin.extend({
1541
1558
  });
1542
1559
  }
1543
1560
  });
1561
+
1562
+ if (this.webex.config?.services?.skipPreauthCatalogOnUnauthenticated === true) {
1563
+ // Skip the preauth catalog fetch (it will be collected manually
1564
+ // later), but still finalize `services.ready` so `webex.ready` is not
1565
+ // stalled while unauthenticated. No timeout is created here so there
1566
+ // is no stray timer or unhandled rejection.
1567
+ this.logger.info(
1568
+ 'services: skipping preauth catalog collection while unauthenticated as per the config'
1569
+ );
1570
+ this._finalizeReady();
1571
+ } else {
1572
+ Promise.race([
1573
+ this.collectPreauthCatalog(email ? {email} : undefined),
1574
+ this._makeInitTimeout(),
1575
+ ])
1576
+ .catch((error) => {
1577
+ this.initFailed = true;
1578
+ this.logger.error(
1579
+ `services: failed to init initial services when no credentials available, ${error?.message}`
1580
+ );
1581
+ })
1582
+ .finally(() => this._finalizeReady());
1583
+ }
1544
1584
  }
1545
1585
  });
1546
1586
  },
package/src/webex-core.js CHANGED
@@ -17,6 +17,7 @@ import AmpState from 'ampersand-state';
17
17
  import uuid from 'uuid';
18
18
 
19
19
  import AuthInterceptor from './interceptors/auth';
20
+ import CatalogUrlInterceptor from './interceptors/catalog-url';
20
21
  import NetworkTimingInterceptor from './interceptors/network-timing';
21
22
  import PayloadTransformerInterceptor from './interceptors/payload-transformer';
22
23
  import RedirectInterceptor from './interceptors/redirect';
@@ -58,6 +59,7 @@ const interceptors = {
58
59
  /* eslint-enable no-extra-parens */
59
60
  RequestTimingInterceptor: RequestTimingInterceptor.create,
60
61
  ServiceInterceptor: undefined,
62
+ CatalogUrlInterceptor: CatalogUrlInterceptor.create,
61
63
  UserAgentInterceptor: UserAgentInterceptor.create,
62
64
  ProxyInterceptor: ProxyInterceptor.create,
63
65
  WebexUserAgentInterceptor: WebexUserAgentInterceptor.create,
@@ -83,6 +85,7 @@ const preInterceptors = [
83
85
  'RequestEventInterceptor',
84
86
  'WebexTrackingIdInterceptor',
85
87
  'RateLimitInterceptor',
88
+ 'CatalogUrlInterceptor',
86
89
  ];
87
90
 
88
91
  const postInterceptors = [
@@ -393,6 +396,11 @@ const WebexCore = AmpState.extend({
393
396
  return ints;
394
397
  }
395
398
 
399
+ // CatalogUrlInterceptor is opt-in via config.services.validateCatalogUrls
400
+ if (key === 'CatalogUrlInterceptor' && this.config.services?.validateCatalogUrls !== true) {
401
+ return ints;
402
+ }
403
+
396
404
  ints.push(Reflect.apply(interceptor, this, []));
397
405
 
398
406
  return ints;