@webex/internal-plugin-metrics 3.8.1 → 3.9.0-multipleLLM.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.
Files changed (35) hide show
  1. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +92 -14
  2. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
  3. package/dist/call-diagnostic/call-diagnostic-metrics.js +351 -48
  4. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
  5. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +21 -0
  6. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
  7. package/dist/call-diagnostic/config.js +3 -1
  8. package/dist/call-diagnostic/config.js.map +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/metrics.js +1 -1
  11. package/dist/metrics.types.js.map +1 -1
  12. package/dist/new-metrics.js +43 -1
  13. package/dist/new-metrics.js.map +1 -1
  14. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +23 -1
  15. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +177 -10
  16. package/dist/types/call-diagnostic/config.d.ts +2 -0
  17. package/dist/types/index.d.ts +2 -2
  18. package/dist/types/metrics.types.d.ts +19 -7
  19. package/dist/types/new-metrics.d.ts +19 -2
  20. package/package.json +11 -12
  21. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +104 -14
  22. package/src/call-diagnostic/call-diagnostic-metrics.ts +368 -25
  23. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +20 -0
  24. package/src/call-diagnostic/config.ts +3 -0
  25. package/src/index.ts +2 -0
  26. package/src/metrics.types.ts +26 -6
  27. package/src/new-metrics.ts +52 -1
  28. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +20 -1
  29. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +255 -0
  30. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +864 -39
  31. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +6 -0
  32. package/test/unit/spec/new-metrics.ts +67 -2
  33. package/test/unit/spec/prelogin-metrics-batcher.ts +72 -3
  34. package/dist/call-diagnostic-events-batcher.js +0 -60
  35. package/dist/call-diagnostic-events-batcher.js.map +0 -1
@@ -41,6 +41,9 @@ import {
41
41
  ClientSubServiceType,
42
42
  BrowserLaunchMethodType,
43
43
  DelayedClientEvent,
44
+ DelayedClientFeatureEvent,
45
+ FeatureEvent,
46
+ ClientFeatureEventPayload,
44
47
  } from '../metrics.types';
45
48
  import CallDiagnosticEventsBatcher from './call-diagnostic-metrics-batcher';
46
49
  import PreLoginMetricsBatcher from '../prelogin-metrics-batcher';
@@ -58,6 +61,8 @@ import {
58
61
  AUTHENTICATION_FAILED_CODE,
59
62
  WEBEX_SUB_SERVICE_TYPES,
60
63
  SDP_OFFER_CREATION_ERROR_MAP,
64
+ CALL_FEATURE_LOG_IDENTIFIER,
65
+ CALL_FEATURE_EVENT_FAILED_TO_SEND,
61
66
  } from './config';
62
67
 
63
68
  const {getOSVersion, getBrowserName, getBrowserVersion} = BrowserDetection();
@@ -70,6 +75,7 @@ type GetOriginOptions = {
70
75
  browserLaunchMethod?: BrowserLaunchMethodType;
71
76
  environment?: EnvironmentType;
72
77
  newEnvironment?: NewEnvironmentType;
78
+ vendorId?: string;
73
79
  };
74
80
 
75
81
  type GetIdentifiersOptions = {
@@ -97,7 +103,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
97
103
  private hasLoggedBrowserSerial: boolean;
98
104
  private device: any;
99
105
  private delayedClientEvents: DelayedClientEvent[] = [];
106
+ private delayedClientFeatureEvents: DelayedClientFeatureEvent[] = [];
100
107
  private eventErrorCache: WeakMap<any, any> = new WeakMap();
108
+ private isMercuryConnected = false;
109
+ private eventLimitTracker: Map<string, number> = new Map();
110
+ private eventLimitWarningsLogged: Set<string> = new Set();
101
111
 
102
112
  // the default validator before piping an event to the batcher
103
113
  // this function can be overridden by the user
@@ -150,6 +160,16 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
150
160
  return undefined;
151
161
  }
152
162
 
163
+ /**
164
+ * Sets mercury connected status for event data object in CA events
165
+ * @public
166
+ * @param status - boolean value indicating mercury connection status
167
+ * @return {void}
168
+ */
169
+ public setMercuryConnectedStatus(status: boolean): void {
170
+ this.isMercuryConnected = status;
171
+ }
172
+
153
173
  /**
154
174
  * Returns meeting's subServiceType
155
175
  * @param meeting
@@ -278,6 +298,10 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
278
298
  origin.clientInfo.browserLaunchMethod = options.browserLaunchMethod;
279
299
  }
280
300
 
301
+ if (options?.vendorId) {
302
+ origin.clientInfo.vendorId = options.vendorId;
303
+ }
304
+
281
305
  return origin;
282
306
  }
283
307
 
@@ -416,12 +440,97 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
416
440
  }
417
441
 
418
442
  /**
419
- * TODO: NOT IMPLEMENTED
443
+ * Create feature event
444
+ * @param name
445
+ * @param payload
446
+ * @param options
447
+ * @returns
448
+ */
449
+ private prepareClientFeatureEvent({
450
+ name,
451
+ payload,
452
+ options,
453
+ }: {
454
+ name: FeatureEvent['name'];
455
+ payload?: ClientFeatureEventPayload;
456
+ options?: SubmitClientEventOptions;
457
+ }) {
458
+ const {meetingId, correlationId} = options;
459
+ let featureEventObject: FeatureEvent['payload'];
460
+
461
+ // events that will most likely happen in join phase
462
+ if (meetingId) {
463
+ featureEventObject = this.createFeatureEventObjectInMeeting({name, options});
464
+ } else {
465
+ throw new Error('Not implemented');
466
+ }
467
+
468
+ // merge any new properties, or override existing ones
469
+ featureEventObject = merge(featureEventObject, payload);
470
+
471
+ // append client event data to the call diagnostic event
472
+ const featureEvent = this.prepareDiagnosticEvent(featureEventObject, options);
473
+
474
+ return featureEvent;
475
+ }
476
+
477
+ /**
420
478
  * Submit Feature Event
479
+ * submit to business_ucf
421
480
  * @returns
422
481
  */
423
- public submitFeatureEvent() {
424
- throw Error('Not implemented');
482
+ public submitFeatureEvent({
483
+ name,
484
+ payload,
485
+ options,
486
+ delaySubmitEvent,
487
+ }: {
488
+ name: FeatureEvent['name'];
489
+ payload?: ClientFeatureEventPayload;
490
+ options?: SubmitClientEventOptions;
491
+ delaySubmitEvent?: boolean;
492
+ }) {
493
+ if (delaySubmitEvent) {
494
+ // Preserve the time when the event was triggered if delaying the submission to Call Features
495
+ const delayedOptions = {
496
+ ...options,
497
+ triggeredTime: new Date().toISOString(),
498
+ };
499
+
500
+ this.delayedClientFeatureEvents.push({
501
+ name,
502
+ payload,
503
+ options: delayedOptions,
504
+ });
505
+
506
+ return Promise.resolve();
507
+ }
508
+
509
+ this.logger.log(
510
+ CALL_FEATURE_LOG_IDENTIFIER,
511
+ 'CallFeatureMetrics: @submitFeatureEvent. Submit Client Feature Event CA event.',
512
+ `name: ${name}`
513
+ );
514
+ const featureEvent = this.prepareClientFeatureEvent({name, payload, options});
515
+
516
+ this.validator({type: 'ce', event: featureEvent});
517
+
518
+ return this.submitToCallFeatures(featureEvent);
519
+ }
520
+
521
+ /**
522
+ * Submit Feature Event
523
+ * type is business
524
+ * @param event
525
+ */
526
+ submitToCallFeatures(event: Event): Promise<any> {
527
+ // build metrics-a event type
528
+ const finalEvent = {
529
+ eventPayload: event,
530
+ type: ['business'],
531
+ };
532
+
533
+ return this.callDiagnosticEventsBatcher.request(finalEvent);
425
534
  }
426
535
 
427
536
  /**
@@ -563,6 +672,144 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
563
672
  this.eventErrorCache = new WeakMap();
564
673
  }
565
674
 
675
+ /**
676
+ * Checks if an event should be limited based on criteria defined in the event dictionary.
677
+ * Returns true if the event should be sent, false if it has reached its limit.
678
+ * @param event - The diagnostic event object
679
+ * @returns boolean indicating whether the event should be sent
680
+ */
681
+ private shouldSendEvent({event}: Event): boolean {
682
+ const eventName = event?.name as string;
683
+ const correlationId = event?.identifiers?.correlationId;
684
+
685
+ if (!correlationId || correlationId === 'unknown') {
686
+ return true;
687
+ }
688
+
689
+ const limitKeyPrefix = `${eventName}:${correlationId}`;
690
+
691
+ switch (eventName) {
692
+ case 'client.media.render.start':
693
+ case 'client.media.render.stop':
694
+ case 'client.media.rx.start':
695
+ case 'client.media.rx.stop':
696
+ case 'client.media.tx.start':
697
+ case 'client.media.tx.stop': {
698
+ // Send only once per mediaType-correlationId pair (or mediaType-correlationId-shareInstanceId for share/share_audio)
699
+ const mediaType = event?.mediaType;
700
+ if (mediaType) {
701
+ if (mediaType === 'share' || mediaType === 'share_audio') {
702
+ const shareInstanceId = event?.shareInstanceId;
703
+ if (shareInstanceId) {
704
+ const limitKey = `${limitKeyPrefix}:${mediaType}:${shareInstanceId}`;
705
+
706
+ return this.checkAndIncrementEventCount(
707
+ limitKey,
708
+ 1,
709
+ `${eventName} for ${mediaType} instance ${shareInstanceId}`
710
+ );
711
+ }
712
+ } else {
713
+ const limitKey = `${limitKeyPrefix}:${mediaType}`;
714
+
715
+ return this.checkAndIncrementEventCount(
716
+ limitKey,
717
+ 1,
718
+ `${eventName} for mediaType ${mediaType}`
719
+ );
720
+ }
721
+ }
722
+ break;
723
+ }
724
+
725
+ case 'client.roap-message.received':
726
+ case 'client.roap-message.sent': {
727
+ // Send only once per correlationId and roap.messageType/roap.type
728
+ const roapMessageType = event?.roap?.messageType || event?.roap?.type;
729
+ if (roapMessageType) {
730
+ const limitKey = `${limitKeyPrefix}:${roapMessageType}`;
731
+
732
+ return this.checkAndIncrementEventCount(
733
+ limitKey,
734
+ 1,
735
+ `${eventName} for ROAP type ${roapMessageType}`
736
+ );
737
+ }
738
+ break;
739
+ }
740
+
741
+ default:
742
+ return true;
743
+ }
744
+
745
+ return true;
746
+ }
747
+
748
+ /**
749
+ * Checks the current count for a limit key and increments if under limit.
750
+ * @param limitKey - The unique key for this limit combination
751
+ * @param maxCount - Maximum allowed count
752
+ * @param eventDescription - Description for logging
753
+ * @returns true if under limit and incremented, false if at/over limit
754
+ */
755
+ private checkAndIncrementEventCount(
756
+ limitKey: string,
757
+ maxCount: number,
758
+ eventDescription: string
759
+ ): boolean {
760
+ const currentCount = this.eventLimitTracker.get(limitKey) || 0;
761
+
762
+ if (currentCount >= maxCount) {
763
+ // Log warning only once per limit key
764
+ if (!this.eventLimitWarningsLogged.has(limitKey)) {
765
+ this.logger.log(
766
+ CALL_DIAGNOSTIC_LOG_IDENTIFIER,
767
+ `CallDiagnosticMetrics: Event limit reached for ${eventDescription}. ` +
768
+ `Max count ${maxCount} exceeded. Event will not be sent.`,
769
+ `limitKey: ${limitKey}`
770
+ );
771
+ this.eventLimitWarningsLogged.add(limitKey);
772
+ }
773
+
774
+ return false;
775
+ }
776
+
777
+ // Increment count and allow event
778
+ this.eventLimitTracker.set(limitKey, currentCount + 1);
779
+
780
+ return true;
781
+ }
782
+
783
+ /**
784
+ * Clears event limit tracking
785
+ */
786
+ public clearEventLimits(): void {
787
+ this.eventLimitTracker.clear();
788
+ this.eventLimitWarningsLogged.clear();
789
+ }
790
+
791
+ /**
792
+ * Clears event limit tracking for a specific correlationId only.
793
+ * Keeps limits for other meetings intact.
794
+ */
795
+ public clearEventLimitsForCorrelationId(correlationId: string): void {
796
+ if (!correlationId) {
797
+ return;
798
+ }
799
+ // Keys are formatted as "eventName:correlationId:..." across all limiters.
800
+ const hasCorrIdAtSecondToken = (key: string) => key.split(':')[1] === correlationId;
801
+ for (const key of Array.from(this.eventLimitTracker.keys())) {
802
+ if (hasCorrIdAtSecondToken(key)) {
803
+ this.eventLimitTracker.delete(key);
804
+ }
805
+ }
806
+ for (const key of Array.from(this.eventLimitWarningsLogged.values())) {
807
+ if (hasCorrIdAtSecondToken(key)) {
808
+ this.eventLimitWarningsLogged.delete(key);
809
+ }
810
+ }
811
+ }
812
+
566
813
  /**
567
814
  * Generate error payload for Client Event
568
815
  * @param rawError
@@ -680,20 +927,20 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
680
927
  }
681
928
 
682
929
  /**
683
- * Create client event object for in meeting events
684
- * @param arg - create args
685
- * @param arg.event - event key
686
- * @param arg.options - options
930
+ * Create common object for in meeting events
931
+ * @param name
932
+ * @param options
933
+ * @param eventType - 'client' | 'feature'
687
934
  * @returns object
688
935
  */
689
- private createClientEventObjectInMeeting({
936
+ private createCommonEventObjectInMeeting({
690
937
  name,
691
938
  options,
692
- errors,
939
+ eventType = 'client',
693
940
  }: {
694
- name: ClientEvent['name'];
941
+ name: string;
695
942
  options?: SubmitClientEventOptions;
696
- errors?: ClientEventPayloadError;
943
+ eventType?: 'client' | 'feature';
697
944
  }) {
698
945
  const {
699
946
  meetingId,
@@ -708,16 +955,21 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
708
955
 
709
956
  if (!meeting) {
710
957
  console.warn(
711
- 'Attempt to send client event but no meeting was found...',
958
+ 'Attempt to send common event but no meeting was found...',
712
959
  `name: ${name}, meetingId: ${meetingId}`
713
960
  );
714
961
  // @ts-ignore
715
- this.webex.internal.metrics.submitClientMetrics(CALL_DIAGNOSTIC_EVENT_FAILED_TO_SEND, {
716
- fields: {
717
- meetingId,
718
- name,
719
- },
720
- });
962
+ this.webex.internal.metrics.submitClientMetrics(
963
+ eventType === 'feature'
964
+ ? CALL_FEATURE_EVENT_FAILED_TO_SEND
965
+ : CALL_DIAGNOSTIC_EVENT_FAILED_TO_SEND,
966
+ {
967
+ fields: {
968
+ meetingId,
969
+ name,
970
+ },
971
+ }
972
+ );
721
973
 
722
974
  return undefined;
723
975
  }
@@ -731,12 +983,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
731
983
  sessionCorrelationId,
732
984
  });
733
985
 
734
- // create client event object
735
- const clientEventObject: ClientEvent['payload'] = {
986
+ // create common event object structur
987
+ const commonEventObject = {
736
988
  name,
737
989
  canProceed: true,
738
990
  identifiers,
739
- errors,
740
991
  eventData: {
741
992
  webClientDomain: window.location.hostname,
742
993
  },
@@ -757,18 +1008,80 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
757
1008
 
758
1009
  const joinFlowVersion = options.joinFlowVersion ?? meeting.callStateForMetrics?.joinFlowVersion;
759
1010
  if (joinFlowVersion) {
760
- clientEventObject.joinFlowVersion = joinFlowVersion;
1011
+ // @ts-ignore
1012
+ commonEventObject.joinFlowVersion = joinFlowVersion;
761
1013
  }
762
1014
  const meetingJoinedTime = meeting.isoLocalClientMeetingJoinTime;
763
1015
  if (meetingJoinedTime) {
764
- clientEventObject.meetingJoinedTime = meetingJoinedTime;
1016
+ // @ts-ignore
1017
+ commonEventObject.meetingJoinedTime = meetingJoinedTime;
765
1018
  }
766
1019
 
767
1020
  if (options.meetingJoinPhase) {
768
- clientEventObject.meetingJoinPhase = options.meetingJoinPhase;
1021
+ // @ts-ignore
1022
+ commonEventObject.meetingJoinPhase = options.meetingJoinPhase;
769
1023
  }
770
1024
 
771
- return clientEventObject;
1025
+ return commonEventObject;
1026
+ }
1027
+
1028
+ /**
1029
+ * Create client event object for in meeting events
1030
+ * @param arg - create args
1031
+ * @param arg.event - event key
1032
+ * @param arg.options - options
1033
+ * @returns object
1034
+ */
1035
+ private createClientEventObjectInMeeting({
1036
+ name,
1037
+ options,
1038
+ errors,
1039
+ }: {
1040
+ name: ClientEvent['name'];
1041
+ options?: SubmitClientEventOptions;
1042
+ errors?: ClientEventPayloadError;
1043
+ }) {
1044
+ const commonObject = this.createCommonEventObjectInMeeting({
1045
+ name,
1046
+ options,
1047
+ eventType: 'client',
1048
+ });
1049
+ if (!commonObject) return undefined;
1050
+
1051
+ return {
1052
+ ...commonObject,
1053
+ errors,
1054
+ eventData: {
1055
+ ...commonObject.eventData,
1056
+ isMercuryConnected: this.isMercuryConnected,
1057
+ },
1058
+ } as ClientEvent['payload'];
1059
+ }
1060
+
1061
+ /**
1062
+ * Create feature event object for in meeting function event
1063
+ * @param name
1064
+ * @param options
1065
+ * @returns object
1066
+ */
1067
+ private createFeatureEventObjectInMeeting({
1068
+ name,
1069
+ options,
1070
+ }: {
1071
+ name: FeatureEvent['name'];
1072
+ options?: SubmitClientEventOptions;
1073
+ }) {
1074
+ const commonObject = this.createCommonEventObjectInMeeting({
1075
+ name,
1076
+ options,
1077
+ eventType: 'feature',
1078
+ });
1079
+ if (!commonObject) return undefined;
1080
+
1081
+ return {
1082
+ ...commonObject,
1083
+ key: 'UcfFeatureUsage',
1084
+ } as FeatureEvent['payload'];
772
1085
  }
773
1086
 
774
1087
  /**
@@ -807,6 +1120,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
807
1120
  identifiers,
808
1121
  eventData: {
809
1122
  webClientDomain: window.location.hostname,
1123
+ isMercuryConnected: this.isMercuryConnected,
810
1124
  },
811
1125
  loginType: this.getCurLoginType(),
812
1126
  // @ts-ignore
@@ -930,6 +1244,10 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
930
1244
  );
931
1245
  const diagnosticEvent = this.prepareClientEvent({name, payload, options});
932
1246
 
1247
+ if (!this.shouldSendEvent(diagnosticEvent)) {
1248
+ return Promise.resolve();
1249
+ }
1250
+
933
1251
  if (options?.preLoginId) {
934
1252
  return this.submitToCallDiagnosticsPreLogin(diagnosticEvent, options?.preLoginId);
935
1253
  }
@@ -964,6 +1282,31 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
964
1282
  return Promise.all(promises);
965
1283
  }
966
1284
 
1285
+ /**
1286
+ * Submit Delayed feature Event CA events. Clears submitDelayedClientFeatureEvents array after submission.
1287
+ */
1288
+ public submitDelayedClientFeatureEvents(overrides?: Partial<DelayedClientEvent['options']>) {
1289
+ this.logger.log(
1290
+ CALL_FEATURE_LOG_IDENTIFIER,
1291
+ 'CallDiagnosticMetrics: @submitDelayedClientFeatureEvents. Submitting delayed feature events.'
1292
+ );
1293
+
1294
+ if (this.delayedClientFeatureEvents.length === 0) {
1295
+ return Promise.resolve();
1296
+ }
1297
+
1298
+ const promises = this.delayedClientFeatureEvents.map((delayedSubmitClientEventParams) => {
1299
+ const {name, payload, options} = delayedSubmitClientEventParams;
1300
+ const optionsWithOverrides: DelayedClientEvent['options'] = {...options, ...overrides};
1301
+
1302
+ return this.submitFeatureEvent({name, payload, options: optionsWithOverrides});
1303
+ });
1304
+
1305
+ this.delayedClientFeatureEvents = [];
1306
+
1307
+ return Promise.all(promises);
1308
+ }
1309
+
967
1310
  /**
968
1311
  * Prepare the event and send the request to metrics-a service.
969
1312
  * @param event
@@ -239,6 +239,20 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
239
239
 
240
240
  // Set upgradeChannel to 'gold' if buildType is 'prod', otherwise to the buildType value
241
241
  const upgradeChannel = buildType === 'prod' ? 'gold' : buildType;
242
+ if (webex.devicemanager) {
243
+ const pairedDevice = webex.devicemanager.getPairedDevice();
244
+ if (pairedDevice) {
245
+ const devicePayload = {
246
+ deviceId: pairedDevice.deviceInfo?.id,
247
+ devicePairingType: webex.devicemanager.getPairedMethod(),
248
+ deviceURL: pairedDevice.url,
249
+ isPersonalDevice: pairedDevice.mode === 'personal',
250
+ productName: pairedDevice.devices[0]?.productName,
251
+ };
252
+ item.eventPayload.event.pairingState = 'paired';
253
+ item.eventPayload.event.pairedDevice = devicePayload;
254
+ }
255
+ }
242
256
 
243
257
  const origin: Partial<Event['origin']> = {
244
258
  buildType,
@@ -257,6 +271,7 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
257
271
  switch (eventName) {
258
272
  case 'client.webexapp.launched':
259
273
  joinTimes.downloadTime = cdl.getDownloadTimeJMT();
274
+ joinTimes.pageJmt = cdl.getPageJMT();
260
275
  break;
261
276
  case 'client.login.end':
262
277
  joinTimes.otherAppApiReqResp = cdl.getOtherAppApiReqResp();
@@ -267,6 +282,7 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
267
282
  joinTimes.clickToInterstitial = cdl.getClickToInterstitial();
268
283
  joinTimes.refreshCaptchaServiceReqResp = cdl.getRefreshCaptchaReqResp();
269
284
  joinTimes.downloadIntelligenceModelsReqResp = cdl.getDownloadIntelligenceModelsReqResp();
285
+ joinTimes.clickToInterstitialWithUserDelay = cdl.getClickToInterstitialWithUserDelay();
270
286
  break;
271
287
 
272
288
  case 'client.call.initiated':
@@ -287,6 +303,8 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
287
303
  joinTimes.totalJmt = cdl.getTotalJMT();
288
304
  joinTimes.clientJmt = cdl.getClientJMT();
289
305
  joinTimes.downloadTime = cdl.getDownloadTimeJMT();
306
+ joinTimes.clickToInterstitialWithUserDelay = cdl.getClickToInterstitialWithUserDelay();
307
+ joinTimes.totalJMTWithUserDelay = cdl.getTotalJMTWithUserDelay();
290
308
  break;
291
309
 
292
310
  case 'client.ice.end':
@@ -307,6 +325,8 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
307
325
  joinTimes.interstitialToMediaOKJMT = cdl.getInterstitialToMediaOKJMT();
308
326
  joinTimes.callInitMediaEngineReady = cdl.getCallInitMediaEngineReady();
309
327
  joinTimes.stayLobbyTime = cdl.getStayLobbyTime();
328
+ joinTimes.totalMediaJMTWithUserDelay = cdl.getTotalMediaJMTWithUserDelay();
329
+ joinTimes.totalJMTWithUserDelay = cdl.getTotalJMTWithUserDelay();
310
330
  break;
311
331
 
312
332
  case 'client.media.tx.start':
@@ -5,6 +5,7 @@
5
5
  import {ClientEventError, ClientSubServiceType} from '../metrics.types';
6
6
 
7
7
  export const CALL_DIAGNOSTIC_LOG_IDENTIFIER = 'call-diagnostic-events -> ';
8
+ export const CALL_FEATURE_LOG_IDENTIFIER = 'call-diagnostic-events-feature -> ';
8
9
 
9
10
  export const AUTHENTICATION_FAILED_CODE = 1010;
10
11
  export const NETWORK_ERROR = 1026;
@@ -738,3 +739,5 @@ export const CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD: Record<number, Partial<ClientEv
738
739
  };
739
740
 
740
741
  export const CALL_DIAGNOSTIC_EVENT_FAILED_TO_SEND = 'js_sdk_call_diagnostic_event_failed_to_send';
742
+
743
+ export const CALL_FEATURE_EVENT_FAILED_TO_SEND = 'js_sdk_call_feature_event_failed_to_send';
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  SubmitBusinessEvent,
19
19
  SubmitMQE,
20
20
  PreComputedLatencies,
21
+ SubmitFeatureEvent,
21
22
  } from './metrics.types';
22
23
  import * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';
23
24
  import * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';
@@ -61,4 +62,5 @@ export type {
61
62
  SubmitOperationalEvent,
62
63
  SubmitBusinessEvent,
63
64
  PreComputedLatencies,
65
+ SubmitFeatureEvent,
64
66
  };
@@ -2,9 +2,12 @@ import {
2
2
  ClientEvent as RawClientEvent,
3
3
  Event as RawEvent,
4
4
  MediaQualityEvent as RawMediaQualityEvent,
5
+ FeatureEvent as RawFeatureEvent,
5
6
  } from '@webex/event-dictionary-ts';
6
7
 
7
- export type Event = Omit<RawEvent, 'event'> & {event: RawClientEvent | RawMediaQualityEvent};
8
+ export type Event = Omit<RawEvent, 'event'> & {
9
+ event: RawClientEvent | RawMediaQualityEvent | RawFeatureEvent;
10
+ };
8
11
 
9
12
  export type ClientEventError = NonNullable<RawClientEvent['errors']>[0];
10
13
 
@@ -135,6 +138,7 @@ export type SubmitClientEventOptions = {
135
138
  triggeredTime?: string;
136
139
  emailInput?: ClientEmailInput;
137
140
  userNameInput?: ClientUserNameInput;
141
+ vendorId?: string;
138
142
  };
139
143
 
140
144
  export type SubmitMQEOptions = {
@@ -157,7 +161,9 @@ export type InternalEvent = {
157
161
  | 'internal.client.meeting.interstitial-window.showed'
158
162
  | 'internal.client.interstitial-window.click.joinbutton'
159
163
  | 'internal.client.add-media.turn-discovery.start'
160
- | 'internal.client.add-media.turn-discovery.end';
164
+ | 'internal.client.add-media.turn-discovery.end'
165
+ | 'internal.client.share.initiated'
166
+ | 'internal.client.share.stopped';
161
167
 
162
168
  payload?: never;
163
169
  options?: never;
@@ -212,10 +218,9 @@ export type BehavioralEvent = TaggedEvent;
212
218
  export type OperationalEvent = TaggedEvent;
213
219
 
214
220
  export interface FeatureEvent {
215
- // TODO: not implemented
216
- name: never;
217
- payload?: never;
218
- options?: never;
221
+ name: RawFeatureEvent['name'];
222
+ payload?: RawFeatureEvent;
223
+ options?: SubmitClientEventOptions;
219
224
  }
220
225
 
221
226
  export interface MediaQualityEvent {
@@ -251,6 +256,8 @@ export type ClientEventPayload = RecursivePartial<ClientEvent['payload']>;
251
256
  export type ClientEventLeaveReason = ClientEvent['payload']['leaveReason'];
252
257
  export type ClientEventPayloadError = ClientEvent['payload']['errors'];
253
258
 
259
+ export type ClientFeatureEventPayload = RecursivePartial<FeatureEvent['payload']>;
260
+
254
261
  export type MediaQualityEventAudioSetupDelayPayload = NonNullable<
255
262
  MediaQualityEvent['payload']
256
263
  >['audioSetupDelay'];
@@ -311,6 +318,7 @@ export type PreComputedLatencies =
311
318
  | 'internal.download.time'
312
319
  | 'internal.get.cluster.time'
313
320
  | 'internal.click.to.interstitial'
321
+ | 'internal.click.to.interstitial.with.user.delay'
314
322
  | 'internal.refresh.captcha.time'
315
323
  | 'internal.exchange.ci.token.time'
316
324
  | 'internal.get.u2c.time'
@@ -339,3 +347,15 @@ export interface DelayedClientEvent {
339
347
  payload?: RecursivePartial<ClientEvent['payload']>;
340
348
  options?: SubmitClientEventOptions;
341
349
  }
350
+
351
+ export type SubmitFeatureEvent = (args: {
352
+ name: FeatureEvent['name'];
353
+ payload?: RecursivePartial<FeatureEvent['payload']>;
354
+ options?: SubmitClientEventOptions;
355
+ }) => Promise<any>;
356
+
357
+ export interface DelayedClientFeatureEvent {
358
+ name: FeatureEvent['name'];
359
+ payload?: RecursivePartial<FeatureEvent['payload']>;
360
+ options?: SubmitClientEventOptions;
361
+ }