@webex/internal-plugin-metrics 3.8.1-web-workers-keepalive.1 → 3.9.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.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"names":["_webexCore","require","_metrics","_interopRequireWildcard","_config","_interopRequireDefault","_newMetrics","Utils","exports","CALL_DIAGNOSTIC_CONFIG","CallDiagnosticUtils","_callDiagnosticMetrics2","_callDiagnosticMetricsLatencies","_behavioralMetrics","_operationalMetrics","_businessMetrics","_rtcMetrics","_getRequireWildcardCache","e","_WeakMap","r","t","__esModule","_typeof","default","has","get","n","__proto__","a","_Object$defineProperty","_Object$getOwnPropertyDescriptor","u","Object","prototype","hasOwnProperty","call","i","set","registerInternalPlugin","Metrics","config","NewMetrics"],"sources":["index.ts"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\nimport {registerInternalPlugin} from '@webex/webex-core';\n\nimport Metrics from './metrics';\nimport config from './config';\nimport NewMetrics from './new-metrics';\nimport * as Utils from './utils';\nimport {\n ClientEvent,\n ClientEventLeaveReason,\n SubmitBehavioralEvent,\n SubmitClientEvent,\n SubmitInternalEvent,\n SubmitOperationalEvent,\n SubmitBusinessEvent,\n SubmitMQE,\n PreComputedLatencies,\n} from './metrics.types';\nimport * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';\nimport * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';\nimport CallDiagnosticMetrics from './call-diagnostic/call-diagnostic-metrics';\nimport CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-latencies';\nimport BehavioralMetrics from './behavioral-metrics';\nimport OperationalMetrics from './operational-metrics';\nimport BusinessMetrics from './business-metrics';\nimport RtcMetrics from './rtcMetrics';\n\nregisterInternalPlugin('metrics', Metrics, {\n config,\n});\n\nregisterInternalPlugin('newMetrics', NewMetrics, {\n config,\n});\n\nexport {default, getOSNameInternal} from './metrics';\n\nexport {\n config,\n CALL_DIAGNOSTIC_CONFIG,\n NewMetrics,\n Utils,\n CallDiagnosticUtils,\n CallDiagnosticLatencies,\n CallDiagnosticMetrics,\n BehavioralMetrics,\n OperationalMetrics,\n BusinessMetrics,\n RtcMetrics,\n};\nexport type {\n ClientEvent,\n ClientEventLeaveReason,\n SubmitBehavioralEvent,\n SubmitClientEvent,\n SubmitInternalEvent,\n SubmitMQE,\n SubmitOperationalEvent,\n SubmitBusinessEvent,\n PreComputedLatencies,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,IAAAA,UAAA,GAAAC,OAAA;AAEA,IAAAC,QAAA,GAAAC,uBAAA,CAAAF,OAAA;AACA,IAAAG,OAAA,GAAAC,sBAAA,CAAAJ,OAAA;AACA,IAAAK,WAAA,GAAAD,sBAAA,CAAAJ,OAAA;AACA,IAAAM,KAAA,GAAAJ,uBAAA,CAAAF,OAAA;AAAiCO,OAAA,CAAAD,KAAA,GAAAA,KAAA;AAYjC,IAAAE,sBAAA,GAAAN,uBAAA,CAAAF,OAAA;AAAmEO,OAAA,CAAAC,sBAAA,GAAAA,sBAAA;AACnE,IAAAC,mBAAA,GAAAP,uBAAA,CAAAF,OAAA;AAAsFO,OAAA,CAAAE,mBAAA,GAAAA,mBAAA;AACtF,IAAAC,uBAAA,GAAAN,sBAAA,CAAAJ,OAAA;AACA,IAAAW,+BAAA,GAAAP,sBAAA,CAAAJ,OAAA;AACA,IAAAY,kBAAA,GAAAR,sBAAA,CAAAJ,OAAA;AACA,IAAAa,mBAAA,GAAAT,sBAAA,CAAAJ,OAAA;AACA,IAAAc,gBAAA,GAAAV,sBAAA,CAAAJ,OAAA;AACA,IAAAe,WAAA,GAAAX,sBAAA,CAAAJ,OAAA;AAAsC,SAAAgB,yBAAAC,CAAA,6BAAAC,QAAA,mBAAAC,CAAA,OAAAD,QAAA,IAAAE,CAAA,OAAAF,QAAA,YAAAF,wBAAA,YAAAA,yBAAAC,CAAA,WAAAA,CAAA,GAAAG,CAAA,GAAAD,CAAA,KAAAF,CAAA;AAAA,SAAAf,wBAAAe,CAAA,EAAAE,CAAA,SAAAA,CAAA,IAAAF,CAAA,IAAAA,CAAA,CAAAI,UAAA,SAAAJ,CAAA,eAAAA,CAAA,gBAAAK,OAAA,CAAAL,CAAA,0BAAAA,CAAA,WAAAM,OAAA,EAAAN,CAAA,QAAAG,CAAA,GAAAJ,wBAAA,CAAAG,CAAA,OAAAC,CAAA,IAAAA,CAAA,CAAAI,GAAA,CAAAP,CAAA,UAAAG,CAAA,CAAAK,GAAA,CAAAR,CAAA,OAAAS,CAAA,KAAAC,SAAA,UAAAC,CAAA,GAAAC,sBAAA,IAAAC,gCAAA,WAAAC,CAAA,IAAAd,CAAA,oBAAAc,CAAA,IAAAC,MAAA,CAAAC,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAlB,CAAA,EAAAc,CAAA,SAAAK,CAAA,GAAAR,CAAA,GAAAE,gCAAA,CAAAb,CAAA,EAAAc,CAAA,UAAAK,CAAA,KAAAA,CAAA,CAAAX,GAAA,IAAAW,CAAA,CAAAC,GAAA,IAAAR,sBAAA,CAAAH,CAAA,EAAAK,CAAA,EAAAK,CAAA,IAAAV,CAAA,CAAAK,CAAA,IAAAd,CAAA,CAAAc,CAAA,YAAAL,CAAA,CAAAH,OAAA,GAAAN,CAAA,EAAAG,CAAA,IAAAA,CAAA,CAAAiB,GAAA,CAAApB,CAAA,EAAAS,CAAA,GAAAA,CAAA;AA5BtC;AACA;AACA;;AA4BA,IAAAY,iCAAsB,EAAC,SAAS,EAAEC,gBAAO,EAAE;EACzCC,MAAM,EAANA;AACF,CAAC,CAAC;AAEF,IAAAF,iCAAsB,EAAC,YAAY,EAAEG,mBAAU,EAAE;EAC/CD,MAAM,EAANA;AACF,CAAC,CAAC"}
1
+ {"version":3,"names":["_webexCore","require","_metrics","_interopRequireWildcard","_config","_interopRequireDefault","_newMetrics","Utils","exports","CALL_DIAGNOSTIC_CONFIG","CallDiagnosticUtils","_callDiagnosticMetrics2","_callDiagnosticMetricsLatencies","_behavioralMetrics","_operationalMetrics","_businessMetrics","_rtcMetrics","_getRequireWildcardCache","e","_WeakMap","r","t","__esModule","_typeof","default","has","get","n","__proto__","a","_Object$defineProperty","_Object$getOwnPropertyDescriptor","u","Object","prototype","hasOwnProperty","call","i","set","registerInternalPlugin","Metrics","config","NewMetrics"],"sources":["index.ts"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\nimport {registerInternalPlugin} from '@webex/webex-core';\n\nimport Metrics from './metrics';\nimport config from './config';\nimport NewMetrics from './new-metrics';\nimport * as Utils from './utils';\nimport {\n ClientEvent,\n ClientEventLeaveReason,\n SubmitBehavioralEvent,\n SubmitClientEvent,\n SubmitInternalEvent,\n SubmitOperationalEvent,\n SubmitBusinessEvent,\n SubmitMQE,\n PreComputedLatencies,\n SubmitFeatureEvent,\n} from './metrics.types';\nimport * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';\nimport * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';\nimport CallDiagnosticMetrics from './call-diagnostic/call-diagnostic-metrics';\nimport CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-latencies';\nimport BehavioralMetrics from './behavioral-metrics';\nimport OperationalMetrics from './operational-metrics';\nimport BusinessMetrics from './business-metrics';\nimport RtcMetrics from './rtcMetrics';\n\nregisterInternalPlugin('metrics', Metrics, {\n config,\n});\n\nregisterInternalPlugin('newMetrics', NewMetrics, {\n config,\n});\n\nexport {default, getOSNameInternal} from './metrics';\n\nexport {\n config,\n CALL_DIAGNOSTIC_CONFIG,\n NewMetrics,\n Utils,\n CallDiagnosticUtils,\n CallDiagnosticLatencies,\n CallDiagnosticMetrics,\n BehavioralMetrics,\n OperationalMetrics,\n BusinessMetrics,\n RtcMetrics,\n};\nexport type {\n ClientEvent,\n ClientEventLeaveReason,\n SubmitBehavioralEvent,\n SubmitClientEvent,\n SubmitInternalEvent,\n SubmitMQE,\n SubmitOperationalEvent,\n SubmitBusinessEvent,\n PreComputedLatencies,\n SubmitFeatureEvent,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,IAAAA,UAAA,GAAAC,OAAA;AAEA,IAAAC,QAAA,GAAAC,uBAAA,CAAAF,OAAA;AACA,IAAAG,OAAA,GAAAC,sBAAA,CAAAJ,OAAA;AACA,IAAAK,WAAA,GAAAD,sBAAA,CAAAJ,OAAA;AACA,IAAAM,KAAA,GAAAJ,uBAAA,CAAAF,OAAA;AAAiCO,OAAA,CAAAD,KAAA,GAAAA,KAAA;AAajC,IAAAE,sBAAA,GAAAN,uBAAA,CAAAF,OAAA;AAAmEO,OAAA,CAAAC,sBAAA,GAAAA,sBAAA;AACnE,IAAAC,mBAAA,GAAAP,uBAAA,CAAAF,OAAA;AAAsFO,OAAA,CAAAE,mBAAA,GAAAA,mBAAA;AACtF,IAAAC,uBAAA,GAAAN,sBAAA,CAAAJ,OAAA;AACA,IAAAW,+BAAA,GAAAP,sBAAA,CAAAJ,OAAA;AACA,IAAAY,kBAAA,GAAAR,sBAAA,CAAAJ,OAAA;AACA,IAAAa,mBAAA,GAAAT,sBAAA,CAAAJ,OAAA;AACA,IAAAc,gBAAA,GAAAV,sBAAA,CAAAJ,OAAA;AACA,IAAAe,WAAA,GAAAX,sBAAA,CAAAJ,OAAA;AAAsC,SAAAgB,yBAAAC,CAAA,6BAAAC,QAAA,mBAAAC,CAAA,OAAAD,QAAA,IAAAE,CAAA,OAAAF,QAAA,YAAAF,wBAAA,YAAAA,yBAAAC,CAAA,WAAAA,CAAA,GAAAG,CAAA,GAAAD,CAAA,KAAAF,CAAA;AAAA,SAAAf,wBAAAe,CAAA,EAAAE,CAAA,SAAAA,CAAA,IAAAF,CAAA,IAAAA,CAAA,CAAAI,UAAA,SAAAJ,CAAA,eAAAA,CAAA,gBAAAK,OAAA,CAAAL,CAAA,0BAAAA,CAAA,WAAAM,OAAA,EAAAN,CAAA,QAAAG,CAAA,GAAAJ,wBAAA,CAAAG,CAAA,OAAAC,CAAA,IAAAA,CAAA,CAAAI,GAAA,CAAAP,CAAA,UAAAG,CAAA,CAAAK,GAAA,CAAAR,CAAA,OAAAS,CAAA,KAAAC,SAAA,UAAAC,CAAA,GAAAC,sBAAA,IAAAC,gCAAA,WAAAC,CAAA,IAAAd,CAAA,oBAAAc,CAAA,IAAAC,MAAA,CAAAC,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAlB,CAAA,EAAAc,CAAA,SAAAK,CAAA,GAAAR,CAAA,GAAAE,gCAAA,CAAAb,CAAA,EAAAc,CAAA,UAAAK,CAAA,KAAAA,CAAA,CAAAX,GAAA,IAAAW,CAAA,CAAAC,GAAA,IAAAR,sBAAA,CAAAH,CAAA,EAAAK,CAAA,EAAAK,CAAA,IAAAV,CAAA,CAAAK,CAAA,IAAAd,CAAA,CAAAc,CAAA,YAAAL,CAAA,CAAAH,OAAA,GAAAN,CAAA,EAAAG,CAAA,IAAAA,CAAA,CAAAiB,GAAA,CAAApB,CAAA,EAAAS,CAAA,GAAAA,CAAA;AA7BtC;AACA;AACA;;AA6BA,IAAAY,iCAAsB,EAAC,SAAS,EAAEC,gBAAO,EAAE;EACzCC,MAAM,EAANA;AACF,CAAC,CAAC;AAEF,IAAAF,iCAAsB,EAAC,YAAY,EAAEG,mBAAU,EAAE;EAC/CD,MAAM,EAANA;AACF,CAAC,CAAC"}
package/dist/metrics.js CHANGED
@@ -148,7 +148,7 @@ var Metrics = _webexCore.WebexPlugin.extend({
148
148
  }
149
149
  });
150
150
  },
151
- version: "3.8.1-web-workers-keepalive.1"
151
+ version: "3.9.0"
152
152
  });
153
153
  var _default = exports.default = Metrics;
154
154
  //# sourceMappingURL=metrics.js.map
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["metrics.types.ts"],"sourcesContent":["import {\n ClientEvent as RawClientEvent,\n Event as RawEvent,\n MediaQualityEvent as RawMediaQualityEvent,\n FeatureEvent as RawFeatureEvent,\n} from '@webex/event-dictionary-ts';\n\nexport type Event = Omit<RawEvent, 'event'> & {\n event: RawClientEvent | RawMediaQualityEvent | RawFeatureEvent;\n};\n\nexport type ClientEventError = NonNullable<RawClientEvent['errors']>[0];\n\nexport type EnvironmentType = NonNullable<RawEvent['origin']['environment']>;\n\nexport type NewEnvironmentType = NonNullable<RawEvent['origin']['newEnvironment']>;\n\nexport type ClientLaunchMethodType = NonNullable<\n RawEvent['origin']['clientInfo']\n>['clientLaunchMethod'];\n\nexport type ClientUserNameInput = NonNullable<RawClientEvent['userNameInput']>;\n\nexport type ClientEmailInput = NonNullable<RawClientEvent['emailInput']>;\n\nexport type BrowserLaunchMethodType = NonNullable<\n RawEvent['origin']['clientInfo']\n>['browserLaunchMethod'];\n\nexport type MetricEventProduct = 'webex' | 'wxcc_desktop' | 'wxcc_crm' | 'wxcc_sdk';\n\nexport type MetricEventAgent = 'user' | 'browser' | 'system' | 'sdk' | 'redux' | 'service' | 'api';\n\nexport type MetricEventVerb =\n | 'abort'\n | 'accept'\n | 'activate'\n | 'apply'\n | 'answer'\n | 'authorize'\n | 'build'\n | 'cancel'\n | 'change'\n | 'click'\n | 'close'\n | 'complete'\n | 'connect'\n | 'create'\n | 'deactivate'\n | 'decrypt'\n | 'delete'\n | 'deliver'\n | 'destroy'\n | 'disable'\n | 'disconnect'\n | 'dismiss'\n | 'display'\n | 'download'\n | 'edit'\n | 'enable'\n | 'encrypt'\n | 'end'\n | 'expire'\n | 'fail'\n | 'fetch'\n | 'fire'\n | 'generate'\n | 'get'\n | 'hide'\n | 'hover'\n | 'ignore'\n | 'initialize'\n | 'initiate'\n | 'invalidate'\n | 'join'\n | 'list'\n | 'load'\n | 'login'\n | 'logout'\n | 'notify'\n | 'offer'\n | 'open'\n | 'press'\n | 'receive'\n | 'refer'\n | 'refresh'\n | 'register'\n | 'release'\n | 'reload'\n | 'reject'\n | 'request'\n | 'reset'\n | 'resize'\n | 'respond'\n | 'retry'\n | 'revoke'\n | 'save'\n | 'search'\n | 'select'\n | 'send'\n | 'set'\n | 'sign'\n | 'start'\n | 'submit'\n | 'switch'\n | 'sync'\n | 'toggle'\n | 'transfer'\n | 'unregister'\n | 'update'\n | 'upload'\n | 'use'\n | 'validate'\n | 'view'\n | 'visit'\n | 'wait'\n | 'warn'\n | 'exit';\n\nexport type MetricEventJoinFlowVersion = 'Other' | 'NewFTE';\nexport type MetricEventMeetingJoinPhase = 'pre-join' | 'join' | 'in-meeting';\n\nexport type SubmitClientEventOptions = {\n meetingId?: string;\n mediaConnections?: any[];\n rawError?: any;\n correlationId?: string;\n sessionCorrelationId?: string;\n preLoginId?: string;\n environment?: EnvironmentType;\n newEnvironmentType?: NewEnvironmentType;\n clientLaunchMethod?: ClientLaunchMethodType;\n browserLaunchMethod?: BrowserLaunchMethodType;\n webexConferenceIdStr?: string;\n globalMeetingId?: string;\n joinFlowVersion?: MetricEventJoinFlowVersion;\n meetingJoinPhase?: MetricEventMeetingJoinPhase;\n triggeredTime?: string;\n emailInput?: ClientEmailInput;\n userNameInput?: ClientUserNameInput;\n};\n\nexport type SubmitMQEOptions = {\n meetingId: string;\n mediaConnections?: any[];\n networkType?: Event['origin']['networkType'];\n webexConferenceIdStr?: string;\n globalMeetingId?: string;\n};\n\nexport type InternalEvent = {\n name:\n | 'internal.client.meetinginfo.request'\n | 'internal.client.meetinginfo.response'\n | 'internal.register.device.request'\n | 'internal.register.device.response'\n | 'internal.reset.join.latencies'\n | 'internal.client.meeting.click.joinbutton'\n | 'internal.host.meeting.participant.admitted'\n | 'internal.client.meeting.interstitial-window.showed'\n | 'internal.client.interstitial-window.click.joinbutton'\n | 'internal.client.add-media.turn-discovery.start'\n | 'internal.client.add-media.turn-discovery.end';\n\n payload?: never;\n options?: never;\n};\n\nexport interface ClientEvent {\n name: RawClientEvent['name'];\n payload?: RawClientEvent;\n options?: SubmitClientEventOptions;\n}\n\nexport interface DeviceContext {\n app: {version: string};\n device: {id: string};\n locale: string;\n os: {\n name: string;\n version: string;\n };\n}\n\nexport type MetricType = 'behavioral' | 'operational' | 'business';\n\nexport type Table = 'wbxapp_callend_metrics' | 'business_metrics' | 'business_ucf' | 'default';\n\ntype InternalEventPayload = string | number | boolean;\nexport type EventPayload = Record<string, InternalEventPayload>;\nexport type BehavioralEventPayload = EventPayload; // for compatibilty, can be remove after wxcc-desktop did change their imports.\n\nexport interface BusinessEventPayload {\n metricName: string;\n timestamp: number;\n context: DeviceContext;\n browserDetails: EventPayload;\n value: EventPayload;\n}\n\nexport interface BusinessEvent {\n type: string[];\n eventPayload: BusinessEventPayload;\n}\n\nexport interface TaggedEvent {\n context: DeviceContext;\n metricName: string;\n tags: EventPayload;\n timestamp: number;\n type: [MetricType];\n}\n\nexport type BehavioralEvent = TaggedEvent;\nexport type OperationalEvent = TaggedEvent;\n\nexport interface FeatureEvent {\n name: RawFeatureEvent['name'];\n payload?: RawFeatureEvent;\n options?: SubmitClientEventOptions;\n}\n\nexport interface MediaQualityEvent {\n name: RawMediaQualityEvent['name'];\n payload?: RawMediaQualityEvent;\n options: SubmitMQEOptions;\n}\n\nexport type RecursivePartial<T> = {\n [P in keyof T]?: T[P] extends (infer U)[]\n ? RecursivePartial<U>[]\n : T[P] extends object\n ? RecursivePartial<T[P]>\n : T[P];\n};\n\nexport type MetricEventNames =\n | InternalEvent['name']\n | ClientEvent['name']\n | BehavioralEvent['metricName']\n | OperationalEvent['metricName']\n | BusinessEvent['eventPayload']['metricName']\n | FeatureEvent['name']\n | MediaQualityEvent['name'];\n\nexport type ClientInfo = NonNullable<RawEvent['origin']['clientInfo']>;\nexport type ClientType = NonNullable<RawEvent['origin']['clientInfo']>['clientType'];\nexport type SubClientType = NonNullable<RawEvent['origin']['clientInfo']>['subClientType'];\nexport type NetworkType = NonNullable<RawEvent['origin']>['networkType'];\n\nexport type ClientSubServiceType = ClientEvent['payload']['webexSubServiceType'];\nexport type ClientEventPayload = RecursivePartial<ClientEvent['payload']>;\nexport type ClientEventLeaveReason = ClientEvent['payload']['leaveReason'];\nexport type ClientEventPayloadError = ClientEvent['payload']['errors'];\n\nexport type ClientFeatureEventPayload = RecursivePartial<FeatureEvent['payload']>;\n\nexport type MediaQualityEventAudioSetupDelayPayload = NonNullable<\n MediaQualityEvent['payload']\n>['audioSetupDelay'];\nexport type MediaQualityEventVideoSetupDelayPayload = NonNullable<\n MediaQualityEvent['payload']\n>['videoSetupDelay'];\n\nexport type SubmitMQEPayload = RecursivePartial<MediaQualityEvent['payload']> & {\n intervals: NonNullable<MediaQualityEvent['payload']>['intervals'];\n};\n\nexport type SubmitInternalEvent = (args: {\n name: InternalEvent['name'];\n payload?: RecursivePartial<InternalEvent['payload']>;\n options?: any;\n}) => void;\n\nexport type SubmitBehavioralEvent = (args: {\n product: MetricEventProduct;\n agent: MetricEventAgent;\n target: string;\n verb: MetricEventVerb;\n payload?: EventPayload;\n}) => void;\n\nexport type SubmitClientEvent = (args: {\n name: ClientEvent['name'];\n payload?: RecursivePartial<ClientEvent['payload']>;\n options?: SubmitClientEventOptions;\n}) => Promise<any>;\n\nexport type SubmitOperationalEvent = (args: {\n name: OperationalEvent['metricName'];\n payload: EventPayload;\n}) => void;\n\nexport type SubmitBusinessEvent = (args: {\n name: OperationalEvent['metricName'];\n payload: EventPayload;\n metadata?: EventPayload;\n table?: Table;\n}) => void;\n\nexport type SubmitMQE = (args: {\n name: MediaQualityEvent['name'];\n payload: SubmitMQEPayload;\n options: any;\n}) => void;\n\nexport type BuildClientEventFetchRequestOptions = (args: {\n name: ClientEvent['name'];\n payload?: RecursivePartial<ClientEvent['payload']>;\n options?: SubmitClientEventOptions;\n}) => Promise<any>;\n\nexport type PreComputedLatencies =\n | 'internal.client.pageJMT'\n | 'internal.download.time'\n | 'internal.get.cluster.time'\n | 'internal.click.to.interstitial'\n | 'internal.click.to.interstitial.with.user.delay'\n | 'internal.refresh.captcha.time'\n | 'internal.exchange.ci.token.time'\n | 'internal.get.u2c.time'\n | 'internal.call.init.join.req'\n | 'internal.other.app.api.time'\n | 'internal.api.fetch.intelligence.models';\n\nexport interface IdType {\n meetingId?: string;\n callId?: string;\n}\n\nexport interface IMetricsAttributes {\n type: string;\n version: string;\n userId: string;\n correlationId: string;\n connectionId: string;\n data: any[];\n meetingId?: string;\n callId?: string;\n}\n\nexport interface DelayedClientEvent {\n name: ClientEvent['name'];\n payload?: RecursivePartial<ClientEvent['payload']>;\n options?: SubmitClientEventOptions;\n}\n\nexport type SubmitFeatureEvent = (args: {\n name: FeatureEvent['name'];\n payload?: RecursivePartial<FeatureEvent['payload']>;\n options?: SubmitClientEventOptions;\n}) => Promise<any>;\n\nexport interface DelayedClientFeatureEvent {\n name: FeatureEvent['name'];\n payload?: RecursivePartial<FeatureEvent['payload']>;\n options?: SubmitClientEventOptions;\n}\n"],"mappings":""}
1
+ {"version":3,"names":[],"sources":["metrics.types.ts"],"sourcesContent":["import {\n ClientEvent as RawClientEvent,\n Event as RawEvent,\n MediaQualityEvent as RawMediaQualityEvent,\n FeatureEvent as RawFeatureEvent,\n} from '@webex/event-dictionary-ts';\n\nexport type Event = Omit<RawEvent, 'event'> & {\n event: RawClientEvent | RawMediaQualityEvent | RawFeatureEvent;\n};\n\nexport type ClientEventError = NonNullable<RawClientEvent['errors']>[0];\n\nexport type EnvironmentType = NonNullable<RawEvent['origin']['environment']>;\n\nexport type NewEnvironmentType = NonNullable<RawEvent['origin']['newEnvironment']>;\n\nexport type ClientLaunchMethodType = NonNullable<\n RawEvent['origin']['clientInfo']\n>['clientLaunchMethod'];\n\nexport type ClientUserNameInput = NonNullable<RawClientEvent['userNameInput']>;\n\nexport type ClientEmailInput = NonNullable<RawClientEvent['emailInput']>;\n\nexport type BrowserLaunchMethodType = NonNullable<\n RawEvent['origin']['clientInfo']\n>['browserLaunchMethod'];\n\nexport type MetricEventProduct = 'webex' | 'wxcc_desktop' | 'wxcc_crm' | 'wxcc_sdk';\n\nexport type MetricEventAgent = 'user' | 'browser' | 'system' | 'sdk' | 'redux' | 'service' | 'api';\n\nexport type MetricEventVerb =\n | 'abort'\n | 'accept'\n | 'activate'\n | 'apply'\n | 'answer'\n | 'authorize'\n | 'build'\n | 'cancel'\n | 'change'\n | 'click'\n | 'close'\n | 'complete'\n | 'connect'\n | 'create'\n | 'deactivate'\n | 'decrypt'\n | 'delete'\n | 'deliver'\n | 'destroy'\n | 'disable'\n | 'disconnect'\n | 'dismiss'\n | 'display'\n | 'download'\n | 'edit'\n | 'enable'\n | 'encrypt'\n | 'end'\n | 'expire'\n | 'fail'\n | 'fetch'\n | 'fire'\n | 'generate'\n | 'get'\n | 'hide'\n | 'hover'\n | 'ignore'\n | 'initialize'\n | 'initiate'\n | 'invalidate'\n | 'join'\n | 'list'\n | 'load'\n | 'login'\n | 'logout'\n | 'notify'\n | 'offer'\n | 'open'\n | 'press'\n | 'receive'\n | 'refer'\n | 'refresh'\n | 'register'\n | 'release'\n | 'reload'\n | 'reject'\n | 'request'\n | 'reset'\n | 'resize'\n | 'respond'\n | 'retry'\n | 'revoke'\n | 'save'\n | 'search'\n | 'select'\n | 'send'\n | 'set'\n | 'sign'\n | 'start'\n | 'submit'\n | 'switch'\n | 'sync'\n | 'toggle'\n | 'transfer'\n | 'unregister'\n | 'update'\n | 'upload'\n | 'use'\n | 'validate'\n | 'view'\n | 'visit'\n | 'wait'\n | 'warn'\n | 'exit';\n\nexport type MetricEventJoinFlowVersion = 'Other' | 'NewFTE';\nexport type MetricEventMeetingJoinPhase = 'pre-join' | 'join' | 'in-meeting';\n\nexport type SubmitClientEventOptions = {\n meetingId?: string;\n mediaConnections?: any[];\n rawError?: any;\n correlationId?: string;\n sessionCorrelationId?: string;\n preLoginId?: string;\n environment?: EnvironmentType;\n newEnvironmentType?: NewEnvironmentType;\n clientLaunchMethod?: ClientLaunchMethodType;\n browserLaunchMethod?: BrowserLaunchMethodType;\n webexConferenceIdStr?: string;\n globalMeetingId?: string;\n joinFlowVersion?: MetricEventJoinFlowVersion;\n meetingJoinPhase?: MetricEventMeetingJoinPhase;\n triggeredTime?: string;\n emailInput?: ClientEmailInput;\n userNameInput?: ClientUserNameInput;\n};\n\nexport type SubmitMQEOptions = {\n meetingId: string;\n mediaConnections?: any[];\n networkType?: Event['origin']['networkType'];\n webexConferenceIdStr?: string;\n globalMeetingId?: string;\n};\n\nexport type InternalEvent = {\n name:\n | 'internal.client.meetinginfo.request'\n | 'internal.client.meetinginfo.response'\n | 'internal.register.device.request'\n | 'internal.register.device.response'\n | 'internal.reset.join.latencies'\n | 'internal.client.meeting.click.joinbutton'\n | 'internal.host.meeting.participant.admitted'\n | 'internal.client.meeting.interstitial-window.showed'\n | 'internal.client.interstitial-window.click.joinbutton'\n | 'internal.client.add-media.turn-discovery.start'\n | 'internal.client.add-media.turn-discovery.end'\n | 'internal.client.share.initiated'\n | 'internal.client.share.stopped';\n\n payload?: never;\n options?: never;\n};\n\nexport interface ClientEvent {\n name: RawClientEvent['name'];\n payload?: RawClientEvent;\n options?: SubmitClientEventOptions;\n}\n\nexport interface DeviceContext {\n app: {version: string};\n device: {id: string};\n locale: string;\n os: {\n name: string;\n version: string;\n };\n}\n\nexport type MetricType = 'behavioral' | 'operational' | 'business';\n\nexport type Table = 'wbxapp_callend_metrics' | 'business_metrics' | 'business_ucf' | 'default';\n\ntype InternalEventPayload = string | number | boolean;\nexport type EventPayload = Record<string, InternalEventPayload>;\nexport type BehavioralEventPayload = EventPayload; // for compatibilty, can be remove after wxcc-desktop did change their imports.\n\nexport interface BusinessEventPayload {\n metricName: string;\n timestamp: number;\n context: DeviceContext;\n browserDetails: EventPayload;\n value: EventPayload;\n}\n\nexport interface BusinessEvent {\n type: string[];\n eventPayload: BusinessEventPayload;\n}\n\nexport interface TaggedEvent {\n context: DeviceContext;\n metricName: string;\n tags: EventPayload;\n timestamp: number;\n type: [MetricType];\n}\n\nexport type BehavioralEvent = TaggedEvent;\nexport type OperationalEvent = TaggedEvent;\n\nexport interface FeatureEvent {\n name: RawFeatureEvent['name'];\n payload?: RawFeatureEvent;\n options?: SubmitClientEventOptions;\n}\n\nexport interface MediaQualityEvent {\n name: RawMediaQualityEvent['name'];\n payload?: RawMediaQualityEvent;\n options: SubmitMQEOptions;\n}\n\nexport type RecursivePartial<T> = {\n [P in keyof T]?: T[P] extends (infer U)[]\n ? RecursivePartial<U>[]\n : T[P] extends object\n ? RecursivePartial<T[P]>\n : T[P];\n};\n\nexport type MetricEventNames =\n | InternalEvent['name']\n | ClientEvent['name']\n | BehavioralEvent['metricName']\n | OperationalEvent['metricName']\n | BusinessEvent['eventPayload']['metricName']\n | FeatureEvent['name']\n | MediaQualityEvent['name'];\n\nexport type ClientInfo = NonNullable<RawEvent['origin']['clientInfo']>;\nexport type ClientType = NonNullable<RawEvent['origin']['clientInfo']>['clientType'];\nexport type SubClientType = NonNullable<RawEvent['origin']['clientInfo']>['subClientType'];\nexport type NetworkType = NonNullable<RawEvent['origin']>['networkType'];\n\nexport type ClientSubServiceType = ClientEvent['payload']['webexSubServiceType'];\nexport type ClientEventPayload = RecursivePartial<ClientEvent['payload']>;\nexport type ClientEventLeaveReason = ClientEvent['payload']['leaveReason'];\nexport type ClientEventPayloadError = ClientEvent['payload']['errors'];\n\nexport type ClientFeatureEventPayload = RecursivePartial<FeatureEvent['payload']>;\n\nexport type MediaQualityEventAudioSetupDelayPayload = NonNullable<\n MediaQualityEvent['payload']\n>['audioSetupDelay'];\nexport type MediaQualityEventVideoSetupDelayPayload = NonNullable<\n MediaQualityEvent['payload']\n>['videoSetupDelay'];\n\nexport type SubmitMQEPayload = RecursivePartial<MediaQualityEvent['payload']> & {\n intervals: NonNullable<MediaQualityEvent['payload']>['intervals'];\n};\n\nexport type SubmitInternalEvent = (args: {\n name: InternalEvent['name'];\n payload?: RecursivePartial<InternalEvent['payload']>;\n options?: any;\n}) => void;\n\nexport type SubmitBehavioralEvent = (args: {\n product: MetricEventProduct;\n agent: MetricEventAgent;\n target: string;\n verb: MetricEventVerb;\n payload?: EventPayload;\n}) => void;\n\nexport type SubmitClientEvent = (args: {\n name: ClientEvent['name'];\n payload?: RecursivePartial<ClientEvent['payload']>;\n options?: SubmitClientEventOptions;\n}) => Promise<any>;\n\nexport type SubmitOperationalEvent = (args: {\n name: OperationalEvent['metricName'];\n payload: EventPayload;\n}) => void;\n\nexport type SubmitBusinessEvent = (args: {\n name: OperationalEvent['metricName'];\n payload: EventPayload;\n metadata?: EventPayload;\n table?: Table;\n}) => void;\n\nexport type SubmitMQE = (args: {\n name: MediaQualityEvent['name'];\n payload: SubmitMQEPayload;\n options: any;\n}) => void;\n\nexport type BuildClientEventFetchRequestOptions = (args: {\n name: ClientEvent['name'];\n payload?: RecursivePartial<ClientEvent['payload']>;\n options?: SubmitClientEventOptions;\n}) => Promise<any>;\n\nexport type PreComputedLatencies =\n | 'internal.client.pageJMT'\n | 'internal.download.time'\n | 'internal.get.cluster.time'\n | 'internal.click.to.interstitial'\n | 'internal.click.to.interstitial.with.user.delay'\n | 'internal.refresh.captcha.time'\n | 'internal.exchange.ci.token.time'\n | 'internal.get.u2c.time'\n | 'internal.call.init.join.req'\n | 'internal.other.app.api.time'\n | 'internal.api.fetch.intelligence.models';\n\nexport interface IdType {\n meetingId?: string;\n callId?: string;\n}\n\nexport interface IMetricsAttributes {\n type: string;\n version: string;\n userId: string;\n correlationId: string;\n connectionId: string;\n data: any[];\n meetingId?: string;\n callId?: string;\n}\n\nexport interface DelayedClientEvent {\n name: ClientEvent['name'];\n payload?: RecursivePartial<ClientEvent['payload']>;\n options?: SubmitClientEventOptions;\n}\n\nexport type SubmitFeatureEvent = (args: {\n name: FeatureEvent['name'];\n payload?: RecursivePartial<FeatureEvent['payload']>;\n options?: SubmitClientEventOptions;\n}) => Promise<any>;\n\nexport interface DelayedClientFeatureEvent {\n name: FeatureEvent['name'];\n payload?: RecursivePartial<FeatureEvent['payload']>;\n options?: SubmitClientEventOptions;\n}\n"],"mappings":""}
@@ -73,7 +73,10 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
73
73
  * @param b end
74
74
  * @returns latency
75
75
  */
76
- getDiffBetweenTimestamps(a: MetricEventNames, b: MetricEventNames): number;
76
+ getDiffBetweenTimestamps(a: MetricEventNames, b: MetricEventNames, clampValues?: {
77
+ minimum?: number;
78
+ maximum?: number;
79
+ }): number;
77
80
  /**
78
81
  * Meeting Info Request
79
82
  * @note Meeting Info request happen not just in the join phase. CA requires
@@ -230,6 +233,10 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
230
233
  * Video setup delay transmit
231
234
  */
232
235
  getVideoJoinRespTxStart(): number;
236
+ /**
237
+ * Time from share initiation to share stop (ms).
238
+ */
239
+ getShareDuration(): number;
233
240
  /**
234
241
  * Total latency for all exchange ci token.
235
242
  */
@@ -33,6 +33,8 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
33
33
  private delayedClientFeatureEvents;
34
34
  private eventErrorCache;
35
35
  private isMercuryConnected;
36
+ private eventLimitTracker;
37
+ private eventLimitWarningsLogged;
36
38
  validator: (options: {
37
39
  type: 'mqe' | 'ce';
38
40
  event: Event;
@@ -111,7 +113,12 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
111
113
  joinFirstUpdateLater?: "ep-enabled" | "sp-enabled" | "not-enabled";
112
114
  standbyUsed?: boolean;
113
115
  prefetchDocShowUsed?: boolean;
114
- fastJoinUsed?: boolean;
116
+ fastJoinUsed?: boolean; /**
117
+ * Sets mercury connected status for event data object in CA events
118
+ * @public
119
+ * @param status - boolean value indicating mercury connection status
120
+ * @return {void}
121
+ */
115
122
  clientDownloadSize?: number;
116
123
  clientDownloadFileCount?: number;
117
124
  nodeId?: number;
@@ -482,6 +489,30 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
482
489
  * Clear the error cache
483
490
  */
484
491
  clearErrorCache(): void;
492
+ /**
493
+ * Checks if an event should be limited based on criteria defined in the event dictionary.
494
+ * Returns true if the event should be sent, false if it has reached its limit.
495
+ * @param event - The diagnostic event object
496
+ * @returns boolean indicating whether the event should be sent
497
+ */
498
+ private shouldSendEvent;
499
+ /**
500
+ * Checks the current count for a limit key and increments if under limit.
501
+ * @param limitKey - The unique key for this limit combination
502
+ * @param maxCount - Maximum allowed count
503
+ * @param eventDescription - Description for logging
504
+ * @returns true if under limit and incremented, false if at/over limit
505
+ */
506
+ private checkAndIncrementEventCount;
507
+ /**
508
+ * Clears event limit tracking
509
+ */
510
+ clearEventLimits(): void;
511
+ /**
512
+ * Clears event limit tracking for a specific correlationId only.
513
+ * Keeps limits for other meetings intact.
514
+ */
515
+ clearEventLimitsForCorrelationId(correlationId: string): void;
485
516
  /**
486
517
  * Generate error payload for Client Event
487
518
  * @param rawError
@@ -4,7 +4,7 @@
4
4
  import config from './config';
5
5
  import NewMetrics from './new-metrics';
6
6
  import * as Utils from './utils';
7
- import { ClientEvent, ClientEventLeaveReason, SubmitBehavioralEvent, SubmitClientEvent, SubmitInternalEvent, SubmitOperationalEvent, SubmitBusinessEvent, SubmitMQE, PreComputedLatencies } from './metrics.types';
7
+ import { ClientEvent, ClientEventLeaveReason, SubmitBehavioralEvent, SubmitClientEvent, SubmitInternalEvent, SubmitOperationalEvent, SubmitBusinessEvent, SubmitMQE, PreComputedLatencies, SubmitFeatureEvent } from './metrics.types';
8
8
  import * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';
9
9
  import * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';
10
10
  import CallDiagnosticMetrics from './call-diagnostic/call-diagnostic-metrics';
@@ -15,4 +15,4 @@ import BusinessMetrics from './business-metrics';
15
15
  import RtcMetrics from './rtcMetrics';
16
16
  export { default, getOSNameInternal } from './metrics';
17
17
  export { config, CALL_DIAGNOSTIC_CONFIG, NewMetrics, Utils, CallDiagnosticUtils, CallDiagnosticLatencies, CallDiagnosticMetrics, BehavioralMetrics, OperationalMetrics, BusinessMetrics, RtcMetrics, };
18
- export type { ClientEvent, ClientEventLeaveReason, SubmitBehavioralEvent, SubmitClientEvent, SubmitInternalEvent, SubmitMQE, SubmitOperationalEvent, SubmitBusinessEvent, PreComputedLatencies, };
18
+ export type { ClientEvent, ClientEventLeaveReason, SubmitBehavioralEvent, SubmitClientEvent, SubmitInternalEvent, SubmitMQE, SubmitOperationalEvent, SubmitBusinessEvent, PreComputedLatencies, SubmitFeatureEvent, };
@@ -41,7 +41,7 @@ export type SubmitMQEOptions = {
41
41
  globalMeetingId?: string;
42
42
  };
43
43
  export type InternalEvent = {
44
- name: 'internal.client.meetinginfo.request' | 'internal.client.meetinginfo.response' | 'internal.register.device.request' | 'internal.register.device.response' | 'internal.reset.join.latencies' | 'internal.client.meeting.click.joinbutton' | 'internal.host.meeting.participant.admitted' | 'internal.client.meeting.interstitial-window.showed' | 'internal.client.interstitial-window.click.joinbutton' | 'internal.client.add-media.turn-discovery.start' | 'internal.client.add-media.turn-discovery.end';
44
+ name: 'internal.client.meetinginfo.request' | 'internal.client.meetinginfo.response' | 'internal.register.device.request' | 'internal.register.device.response' | 'internal.reset.join.latencies' | 'internal.client.meeting.click.joinbutton' | 'internal.host.meeting.participant.admitted' | 'internal.client.meeting.interstitial-window.showed' | 'internal.client.interstitial-window.click.joinbutton' | 'internal.client.add-media.turn-discovery.start' | 'internal.client.add-media.turn-discovery.end' | 'internal.client.share.initiated' | 'internal.client.share.stopped';
45
45
  payload?: never;
46
46
  options?: never;
47
47
  };
package/package.json CHANGED
@@ -26,22 +26,21 @@
26
26
  "@webex/eslint-config-legacy": "0.0.0",
27
27
  "@webex/jest-config-legacy": "0.0.0",
28
28
  "@webex/legacy-tools": "0.0.0",
29
- "@webex/test-helper-chai": "3.8.1-web-workers-keepalive.1",
30
- "@webex/test-helper-mocha": "3.8.1-web-workers-keepalive.1",
31
- "@webex/test-helper-mock-webex": "3.8.1-web-workers-keepalive.1",
32
- "@webex/test-helper-test-users": "3.8.1-web-workers-keepalive.1",
29
+ "@webex/test-helper-chai": "3.9.0",
30
+ "@webex/test-helper-mocha": "3.9.0",
31
+ "@webex/test-helper-mock-webex": "3.9.0",
32
+ "@webex/test-helper-test-users": "3.9.0",
33
33
  "eslint": "^8.24.0",
34
34
  "prettier": "^2.7.1",
35
35
  "sinon": "^9.2.4"
36
36
  },
37
37
  "dependencies": {
38
- "@webex/common": "3.8.1-web-workers-keepalive.1",
39
- "@webex/common-timers": "3.8.1-web-workers-keepalive.1",
38
+ "@webex/common": "3.9.0",
39
+ "@webex/common-timers": "3.9.0",
40
40
  "@webex/event-dictionary-ts": "^1.0.1819",
41
- "@webex/internal-plugin-metrics": "3.8.1-web-workers-keepalive.1",
42
- "@webex/test-helper-chai": "3.8.1-web-workers-keepalive.1",
43
- "@webex/test-helper-mock-webex": "3.8.1-web-workers-keepalive.1",
44
- "@webex/webex-core": "3.8.1-web-workers-keepalive.1",
41
+ "@webex/test-helper-chai": "3.9.0",
42
+ "@webex/test-helper-mock-webex": "3.9.0",
43
+ "@webex/webex-core": "3.9.0",
45
44
  "ip-anonymize": "^0.1.0",
46
45
  "lodash": "^4.17.21",
47
46
  "uuid": "^3.3.2"
@@ -54,5 +53,5 @@
54
53
  "test:style": "eslint ./src/**/*.*",
55
54
  "test:unit": "webex-legacy-tools test --unit --runner mocha"
56
55
  },
57
- "version": "3.8.1-web-workers-keepalive.1"
56
+ "version": "3.9.0"
58
57
  }
@@ -148,15 +148,27 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
148
148
  * @param b end
149
149
  * @returns latency
150
150
  */
151
- public getDiffBetweenTimestamps(a: MetricEventNames, b: MetricEventNames) {
151
+ public getDiffBetweenTimestamps(
152
+ a: MetricEventNames,
153
+ b: MetricEventNames,
154
+ clampValues?: {minimum?: number; maximum?: number}
155
+ ) {
152
156
  const start = this.latencyTimestamps.get(a);
153
157
  const end = this.latencyTimestamps.get(b);
154
158
 
155
- if (typeof start === 'number' && typeof end === 'number') {
156
- return end - start;
159
+ if (typeof start !== 'number' || typeof end !== 'number') {
160
+ return undefined;
157
161
  }
158
162
 
159
- return undefined;
163
+ const diff = end - start;
164
+
165
+ if (!clampValues) {
166
+ return diff;
167
+ }
168
+
169
+ const {minimum = 0, maximum} = clampValues;
170
+
171
+ return Math.min(maximum ?? Infinity, Math.max(diff, minimum));
160
172
  }
161
173
 
162
174
  /**
@@ -172,7 +184,8 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
172
184
  public getMeetingInfoReqResp() {
173
185
  return this.getDiffBetweenTimestamps(
174
186
  'internal.client.meetinginfo.request',
175
- 'internal.client.meetinginfo.response'
187
+ 'internal.client.meetinginfo.response',
188
+ {maximum: 1200000}
176
189
  );
177
190
  }
178
191
 
@@ -215,7 +228,8 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
215
228
  public getCallInitJoinReq() {
216
229
  return this.getDiffBetweenTimestamps(
217
230
  'internal.client.interstitial-window.click.joinbutton',
218
- 'client.locus.join.request'
231
+ 'client.locus.join.request',
232
+ {maximum: 1200000}
219
233
  );
220
234
  }
221
235
 
@@ -224,7 +238,11 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
224
238
  * @returns - latency
225
239
  */
226
240
  public getJoinReqResp() {
227
- return this.getDiffBetweenTimestamps('client.locus.join.request', 'client.locus.join.response');
241
+ return this.getDiffBetweenTimestamps(
242
+ 'client.locus.join.request',
243
+ 'client.locus.join.response',
244
+ {maximum: 1200000}
245
+ );
228
246
  }
229
247
 
230
248
  /**
@@ -245,7 +263,8 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
245
263
  public getLocalSDPGenRemoteSDPRecv() {
246
264
  return this.getDiffBetweenTimestamps(
247
265
  'client.media-engine.local-sdp-generated',
248
- 'client.media-engine.remote-sdp-received'
266
+ 'client.media-engine.remote-sdp-received',
267
+ {maximum: 1200000}
249
268
  );
250
269
  }
251
270
 
@@ -254,7 +273,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
254
273
  * @returns - latency
255
274
  */
256
275
  public getICESetupTime() {
257
- return this.getDiffBetweenTimestamps('client.ice.start', 'client.ice.end');
276
+ return this.getDiffBetweenTimestamps('client.ice.start', 'client.ice.end', {maximum: 1200000});
258
277
  }
259
278
 
260
279
  /**
@@ -378,7 +397,8 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
378
397
  public getCallInitMediaEngineReady() {
379
398
  return this.getDiffBetweenTimestamps(
380
399
  'internal.client.interstitial-window.click.joinbutton',
381
- 'client.media-engine.ready'
400
+ 'client.media-engine.ready',
401
+ {maximum: 1200000}
382
402
  );
383
403
  }
384
404
 
@@ -398,7 +418,9 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
398
418
  const lobbyTime = typeof lobbyTimeLatency === 'number' ? lobbyTimeLatency : 0;
399
419
 
400
420
  if (interstitialJoinClickTimestamp && connectedMedia) {
401
- return connectedMedia - interstitialJoinClickTimestamp - lobbyTime;
421
+ const interstitialToMediaOKJmt = connectedMedia - interstitialJoinClickTimestamp - lobbyTime;
422
+
423
+ return Math.max(0, interstitialToMediaOKJmt);
402
424
  }
403
425
 
404
426
  return undefined;
@@ -463,12 +485,12 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
463
485
  const lobbyTime = this.getStayLobbyTime();
464
486
 
465
487
  if (clickToInterstitial && interstitialToJoinOk && joinConfJMT) {
466
- const totalMediaJMT = clickToInterstitial + interstitialToJoinOk + joinConfJMT;
488
+ const totalMediaJMT = Math.max(0, clickToInterstitial + interstitialToJoinOk + joinConfJMT);
467
489
  if (this.getMeeting()?.allowMediaInLobby) {
468
490
  return totalMediaJMT;
469
491
  }
470
492
 
471
- return totalMediaJMT - lobbyTime;
493
+ return Math.max(0, totalMediaJMT - lobbyTime);
472
494
  }
473
495
 
474
496
  return undefined;
@@ -484,7 +506,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
484
506
  const joinConfJMT = this.getJoinConfJMT();
485
507
 
486
508
  if (clickToInterstitialWithUserDelay && interstitialToJoinOk && joinConfJMT) {
487
- return clickToInterstitialWithUserDelay + interstitialToJoinOk + joinConfJMT;
509
+ return Math.max(0, clickToInterstitialWithUserDelay + interstitialToJoinOk + joinConfJMT);
488
510
  }
489
511
 
490
512
  return undefined;
@@ -499,7 +521,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
499
521
  const joinConfJMT = this.getJoinConfJMT();
500
522
 
501
523
  if (typeof interstitialToJoinOk === 'number' && typeof joinConfJMT === 'number') {
502
- return interstitialToJoinOk - joinConfJMT;
524
+ return Math.max(0, interstitialToJoinOk - joinConfJMT);
503
525
  }
504
526
 
505
527
  return undefined;
@@ -544,6 +566,16 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
544
566
  return this.getDiffBetweenTimestamps('client.locus.join.response', 'client.media.tx.start');
545
567
  }
546
568
 
569
+ /**
570
+ * Time from share initiation to share stop (ms).
571
+ */
572
+ public getShareDuration() {
573
+ return this.getDiffBetweenTimestamps(
574
+ 'internal.client.share.initiated',
575
+ 'internal.client.share.stopped'
576
+ );
577
+ }
578
+
547
579
  /**
548
580
  * Total latency for all exchange ci token.
549
581
  */
@@ -105,6 +105,8 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
105
105
  private delayedClientFeatureEvents: DelayedClientFeatureEvent[] = [];
106
106
  private eventErrorCache: WeakMap<any, any> = new WeakMap();
107
107
  private isMercuryConnected = false;
108
+ private eventLimitTracker: Map<string, number> = new Map();
109
+ private eventLimitWarningsLogged: Set<string> = new Set();
108
110
 
109
111
  // the default validator before piping an event to the batcher
110
112
  // this function can be overridden by the user
@@ -665,6 +667,144 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
665
667
  this.eventErrorCache = new WeakMap();
666
668
  }
667
669
 
670
+ /**
671
+ * Checks if an event should be limited based on criteria defined in the event dictionary.
672
+ * Returns true if the event should be sent, false if it has reached its limit.
673
+ * @param event - The diagnostic event object
674
+ * @returns boolean indicating whether the event should be sent
675
+ */
676
+ private shouldSendEvent({event}: Event): boolean {
677
+ const eventName = event?.name as string;
678
+ const correlationId = event?.identifiers?.correlationId;
679
+
680
+ if (!correlationId || correlationId === 'unknown') {
681
+ return true;
682
+ }
683
+
684
+ const limitKeyPrefix = `${eventName}:${correlationId}`;
685
+
686
+ switch (eventName) {
687
+ case 'client.media.render.start':
688
+ case 'client.media.render.stop':
689
+ case 'client.media.rx.start':
690
+ case 'client.media.rx.stop':
691
+ case 'client.media.tx.start':
692
+ case 'client.media.tx.stop': {
693
+ // Send only once per mediaType-correlationId pair (or mediaType-correlationId-shareInstanceId for share/share_audio)
694
+ const mediaType = event?.mediaType;
695
+ if (mediaType) {
696
+ if (mediaType === 'share' || mediaType === 'share_audio') {
697
+ const shareInstanceId = event?.shareInstanceId;
698
+ if (shareInstanceId) {
699
+ const limitKey = `${limitKeyPrefix}:${mediaType}:${shareInstanceId}`;
700
+
701
+ return this.checkAndIncrementEventCount(
702
+ limitKey,
703
+ 1,
704
+ `${eventName} for ${mediaType} instance ${shareInstanceId}`
705
+ );
706
+ }
707
+ } else {
708
+ const limitKey = `${limitKeyPrefix}:${mediaType}`;
709
+
710
+ return this.checkAndIncrementEventCount(
711
+ limitKey,
712
+ 1,
713
+ `${eventName} for mediaType ${mediaType}`
714
+ );
715
+ }
716
+ }
717
+ break;
718
+ }
719
+
720
+ case 'client.roap-message.received':
721
+ case 'client.roap-message.sent': {
722
+ // Send only once per correlationId and roap.messageType/roap.type
723
+ const roapMessageType = event?.roap?.messageType || event?.roap?.type;
724
+ if (roapMessageType) {
725
+ const limitKey = `${limitKeyPrefix}:${roapMessageType}`;
726
+
727
+ return this.checkAndIncrementEventCount(
728
+ limitKey,
729
+ 1,
730
+ `${eventName} for ROAP type ${roapMessageType}`
731
+ );
732
+ }
733
+ break;
734
+ }
735
+
736
+ default:
737
+ return true;
738
+ }
739
+
740
+ return true;
741
+ }
742
+
743
+ /**
744
+ * Checks the current count for a limit key and increments if under limit.
745
+ * @param limitKey - The unique key for this limit combination
746
+ * @param maxCount - Maximum allowed count
747
+ * @param eventDescription - Description for logging
748
+ * @returns true if under limit and incremented, false if at/over limit
749
+ */
750
+ private checkAndIncrementEventCount(
751
+ limitKey: string,
752
+ maxCount: number,
753
+ eventDescription: string
754
+ ): boolean {
755
+ const currentCount = this.eventLimitTracker.get(limitKey) || 0;
756
+
757
+ if (currentCount >= maxCount) {
758
+ // Log warning only once per limit key
759
+ if (!this.eventLimitWarningsLogged.has(limitKey)) {
760
+ this.logger.log(
761
+ CALL_DIAGNOSTIC_LOG_IDENTIFIER,
762
+ `CallDiagnosticMetrics: Event limit reached for ${eventDescription}. ` +
763
+ `Max count ${maxCount} exceeded. Event will not be sent.`,
764
+ `limitKey: ${limitKey}`
765
+ );
766
+ this.eventLimitWarningsLogged.add(limitKey);
767
+ }
768
+
769
+ return false;
770
+ }
771
+
772
+ // Increment count and allow event
773
+ this.eventLimitTracker.set(limitKey, currentCount + 1);
774
+
775
+ return true;
776
+ }
777
+
778
+ /**
779
+ * Clears event limit tracking
780
+ */
781
+ public clearEventLimits(): void {
782
+ this.eventLimitTracker.clear();
783
+ this.eventLimitWarningsLogged.clear();
784
+ }
785
+
786
+ /**
787
+ * Clears event limit tracking for a specific correlationId only.
788
+ * Keeps limits for other meetings intact.
789
+ */
790
+ public clearEventLimitsForCorrelationId(correlationId: string): void {
791
+ if (!correlationId) {
792
+ return;
793
+ }
794
+ // Keys are formatted as "eventName:correlationId:..." across all limiters.
795
+ const hasCorrIdAtSecondToken = (key: string) => key.split(':')[1] === correlationId;
796
+ for (const key of Array.from(this.eventLimitTracker.keys())) {
797
+ if (hasCorrIdAtSecondToken(key)) {
798
+ this.eventLimitTracker.delete(key);
799
+ }
800
+ }
801
+ for (const key of Array.from(this.eventLimitWarningsLogged.values())) {
802
+ if (hasCorrIdAtSecondToken(key)) {
803
+ this.eventLimitWarningsLogged.delete(key);
804
+ }
805
+ }
806
+ }
807
+
668
808
  /**
669
809
  * Generate error payload for Client Event
670
810
  * @param rawError
@@ -1099,6 +1239,10 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
1099
1239
  );
1100
1240
  const diagnosticEvent = this.prepareClientEvent({name, payload, options});
1101
1241
 
1242
+ if (!this.shouldSendEvent(diagnosticEvent)) {
1243
+ return Promise.resolve();
1244
+ }
1245
+
1102
1246
  if (options?.preLoginId) {
1103
1247
  return this.submitToCallDiagnosticsPreLogin(diagnosticEvent, options?.preLoginId);
1104
1248
  }
@@ -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,
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
  };
@@ -160,7 +160,9 @@ export type InternalEvent = {
160
160
  | 'internal.client.meeting.interstitial-window.showed'
161
161
  | 'internal.client.interstitial-window.click.joinbutton'
162
162
  | 'internal.client.add-media.turn-discovery.start'
163
- | 'internal.client.add-media.turn-discovery.end';
163
+ | 'internal.client.add-media.turn-discovery.end'
164
+ | 'internal.client.share.initiated'
165
+ | 'internal.client.share.stopped';
164
166
 
165
167
  payload?: never;
166
168
  options?: never;