@webex/internal-plugin-metrics 3.12.0-next.4 → 3.12.0-next.40

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 (48) hide show
  1. package/dist/automated-user.js +19 -0
  2. package/dist/automated-user.js.map +1 -0
  3. package/dist/batcher.js +3 -0
  4. package/dist/batcher.js.map +1 -1
  5. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +638 -65
  6. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
  7. package/dist/call-diagnostic/call-diagnostic-metrics.js +167 -76
  8. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
  9. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +5 -0
  10. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
  11. package/dist/call-diagnostic/config.js +16 -3
  12. package/dist/call-diagnostic/config.js.map +1 -1
  13. package/dist/config.js +1 -0
  14. package/dist/config.js.map +1 -1
  15. package/dist/index.js +15 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/metrics.js +1 -1
  18. package/dist/metrics.types.js +4 -0
  19. package/dist/metrics.types.js.map +1 -1
  20. package/dist/new-metrics.js +16 -6
  21. package/dist/new-metrics.js.map +1 -1
  22. package/dist/types/automated-user.d.ts +2 -0
  23. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +208 -5
  24. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +67 -14
  25. package/dist/types/call-diagnostic/config.d.ts +4 -0
  26. package/dist/types/config.d.ts +1 -0
  27. package/dist/types/index.d.ts +5 -3
  28. package/dist/types/metrics.types.d.ts +4 -2
  29. package/dist/types/new-metrics.d.ts +4 -0
  30. package/package.json +12 -11
  31. package/src/automated-user.ts +16 -0
  32. package/src/batcher.js +4 -0
  33. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +800 -73
  34. package/src/call-diagnostic/call-diagnostic-metrics.ts +101 -15
  35. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +5 -0
  36. package/src/call-diagnostic/config.ts +14 -0
  37. package/src/config.js +1 -0
  38. package/src/index.ts +5 -0
  39. package/src/metrics.types.ts +16 -3
  40. package/src/new-metrics.ts +12 -4
  41. package/test/unit/spec/automated-user.ts +43 -0
  42. package/test/unit/spec/batcher.js +43 -0
  43. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +14 -2
  44. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +1434 -303
  45. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +852 -159
  46. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +27 -0
  47. package/test/unit/spec/new-metrics.ts +39 -2
  48. package/test/unit/spec/prelogin-metrics-batcher.ts +71 -36
@@ -1,6 +1,11 @@
1
1
  "use strict";
2
2
 
3
3
  var _Reflect$construct = require("@babel/runtime-corejs2/core-js/reflect/construct");
4
+ var _Object$keys = require("@babel/runtime-corejs2/core-js/object/keys");
5
+ var _Object$getOwnPropertySymbols = require("@babel/runtime-corejs2/core-js/object/get-own-property-symbols");
6
+ var _Object$getOwnPropertyDescriptor = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptor");
7
+ var _Object$getOwnPropertyDescriptors = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptors");
8
+ var _Object$defineProperties = require("@babel/runtime-corejs2/core-js/object/define-properties");
4
9
  var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/define-property");
5
10
  var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
6
11
  _Object$defineProperty(exports, "__esModule", {
@@ -8,6 +13,7 @@ _Object$defineProperty(exports, "__esModule", {
8
13
  });
9
14
  exports.default = void 0;
10
15
  var _map = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/map"));
16
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/toConsumableArray"));
11
17
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/classCallCheck"));
12
18
  var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/createClass"));
13
19
  var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/possibleConstructorReturn"));
@@ -16,9 +22,17 @@ var _inherits2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/
16
22
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
17
23
  var _webexCore = require("@webex/webex-core");
18
24
  var _lodash = require("lodash");
25
+ var _metrics = require("../metrics.types");
26
+ function ownKeys(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
27
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; }
19
28
  function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? _Reflect$construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
20
29
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } /* eslint-disable class-methods-use-this */ /* eslint-disable valid-jsdoc */
21
30
  // we only care about client event and feature event for now
31
+ /**
32
+ * Container for the latencies tracked for a single meeting. Locus sync is currently the only
33
+ * meeting-specific latency we track, but this wrapper lets us add other latency records in the
34
+ * future without changing the shape of `meetingLatencies`.
35
+ */
22
36
  /**
23
37
  * @description Helper class to store latencies timestamp and to calculate various latencies for CA.
24
38
  * @exports
@@ -37,11 +51,23 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
37
51
  _this = _callSuper(this, CallDiagnosticLatencies, [].concat(args));
38
52
  (0, _defineProperty2.default)(_this, "latencyTimestamps", void 0);
39
53
  (0, _defineProperty2.default)(_this, "precomputedLatencies", void 0);
54
+ (0, _defineProperty2.default)(_this, "meetingLatencies", void 0);
40
55
  // meetingId that the current latencies are for
41
56
  (0, _defineProperty2.default)(_this, "meetingId", void 0);
42
57
  (0, _defineProperty2.default)(_this, "MAX_INTEGER", 2147483647);
58
+ // Aligned with UCF desktop (MekleFlowSyncMetricsTracker::kSleepWakeSkewThresholdMs): if any
59
+ // measured Locus sync segment exceeds this threshold the record is assumed to be corrupted by a
60
+ // clock jump (sleep/wake) and is discarded instead of being reported.
61
+ (0, _defineProperty2.default)(_this, "LOCUS_SYNC_SKEW_THRESHOLD_MS", 10 * 60 * 1000);
62
+ // Incomplete Locus sync records (missing the /sync response and/or the LLM message) are kept for
63
+ // this long so a late milestone can still complete them, then pruned as stale.
64
+ (0, _defineProperty2.default)(_this, "LOCUS_SYNC_PENDING_RECORD_TTL_MS", 5 * 60 * 1000);
65
+ // Safety cap on the number of records kept per meeting + dataset, to bound memory if syncs churn
66
+ // faster than the TTL can clean them up.
67
+ (0, _defineProperty2.default)(_this, "LOCUS_SYNC_MAX_RECORDS_PER_DATASET", 20);
43
68
  _this.latencyTimestamps = new _map.default();
44
69
  _this.precomputedLatencies = new _map.default();
70
+ _this.meetingLatencies = new _map.default();
45
71
  return _this;
46
72
  }
47
73
 
@@ -54,6 +80,502 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
54
80
  value: function clearTimestamps() {
55
81
  this.latencyTimestamps.clear();
56
82
  this.precomputedLatencies.clear();
83
+ this.meetingLatencies.clear();
84
+ }
85
+
86
+ /**
87
+ * Clear tracked Locus sync latency state for a dataset.
88
+ *
89
+ * When a `trackingId` is supplied only the single record matching it is dropped, mirroring the
90
+ * native client which discards the individual in-progress record on a sync error (by syncId).
91
+ * When no `trackingId` is supplied every record for the dataset is dropped (legacy behavior).
92
+ *
93
+ * @param dataSetName dataset name
94
+ * @param meetingId meeting id
95
+ * @param trackingId optional sync tracking id to drop a single record instead of the whole dataset
96
+ */
97
+ }, {
98
+ key: "clearLocusSyncLatency",
99
+ value: function clearLocusSyncLatency(dataSetName, meetingId, trackingId) {
100
+ var records = this.meetingLatencies.get(meetingId);
101
+ if (!records) {
102
+ return;
103
+ }
104
+ var remainingRecords = trackingId ? records.filter(function (record) {
105
+ return record.locusSync.trackingId !== trackingId;
106
+ }) : records.filter(function (record) {
107
+ return record.locusSync.dataSetName !== dataSetName;
108
+ });
109
+ this.setMeetingLatencyRecords(meetingId, remainingRecords);
110
+ }
111
+
112
+ /**
113
+ * Calculates Locus sync latency values from stored milestone timestamps.
114
+ * @param meetingId meeting id
115
+ * @param trackingId sync tracking id used to match the record
116
+ * @returns sync latency metrics
117
+ */
118
+ }, {
119
+ key: "getLocusSyncLatency",
120
+ value: function getLocusSyncLatency(meetingId, trackingId) {
121
+ var _this$getDiffBetweenL,
122
+ _this$getDiffBetweenL2,
123
+ _this2 = this;
124
+ if (!trackingId) {
125
+ return undefined;
126
+ }
127
+ var record = this.getLocusSyncLatencyRecord(meetingId, trackingId);
128
+ if (!record) {
129
+ return undefined;
130
+ }
131
+
132
+ // Some sync flows skip the /hashtree request, for example single-leaf data sets or cases
133
+ // where we already know which leaves to sync. Treat the missing hashtree segment as 0,
134
+ // while still requiring the sync request/response/message milestones below.
135
+ var hashtreePrepTime = (_this$getDiffBetweenL = this.getDiffBetweenLocusSyncTimestamps(record, 'syncStart', 'hashTreeRequest')) !== null && _this$getDiffBetweenL !== void 0 ? _this$getDiffBetweenL : 0;
136
+ var hashtreeResponseTime = (_this$getDiffBetweenL2 = this.getDiffBetweenLocusSyncTimestamps(record, 'hashTreeRequest', 'hashTreeResponse')) !== null && _this$getDiffBetweenL2 !== void 0 ? _this$getDiffBetweenL2 : 0;
137
+ var syncPrepStart = this.getLocusSyncPrepStart(record);
138
+ var syncPrepTime = this.getDiffBetweenLocusSyncTimestamps(record, syncPrepStart, 'syncRequest');
139
+ var syncResponseTime = this.getDiffBetweenLocusSyncTimestamps(record, 'syncRequest', 'syncResponse');
140
+ // Aligned with UCF: syncMessageReceiveTime is measured from the sync request start (not the
141
+ // sync response), i.e. how long after we asked Locus to sync the resulting state update arrived.
142
+ var syncMessageReceiveTime = this.getDiffBetweenLocusSyncTimestamps(record, 'syncRequest', 'messageReceived');
143
+ var totalTimeFromMessageReceived = this.getDiffBetweenLocusSyncTimestamps(record, 'syncStart', 'messageReceived');
144
+ var totalTimeFromSyncResponse = this.getDiffBetweenLocusSyncTimestamps(record, 'syncStart', 'syncResponse');
145
+
146
+ // messageReceived can occasionally be out of order due to race conditions relative to the
147
+ // /sync response. Use the larger of the two so totalTime is never less than syncStart->syncResponse.
148
+ var totalTime = typeof totalTimeFromMessageReceived === 'number' && typeof totalTimeFromSyncResponse === 'number' ? Math.max(totalTimeFromMessageReceived, totalTimeFromSyncResponse) : totalTimeFromMessageReceived !== null && totalTimeFromMessageReceived !== void 0 ? totalTimeFromMessageReceived : totalTimeFromSyncResponse;
149
+ if (typeof syncPrepTime !== 'number' || typeof totalTime !== 'number') {
150
+ return undefined;
151
+ }
152
+
153
+ // Aligned with UCF desktop (MekleFlowSyncMetricsTracker kSleepWakeSkewThresholdMs): if any
154
+ // individual segment is larger than the skew threshold the timestamps were almost certainly
155
+ // distorted by a clock jump (device sleep/wake), so discard the whole record.
156
+ var skewedSegment = [record.randomBackoffTime, hashtreePrepTime, hashtreeResponseTime, syncPrepTime, syncResponseTime, syncMessageReceiveTime, totalTime].some(function (segment) {
157
+ return typeof segment === 'number' && segment > _this2.LOCUS_SYNC_SKEW_THRESHOLD_MS;
158
+ });
159
+ if (skewedSegment) {
160
+ return undefined;
161
+ }
162
+
163
+ // Emit whenever at least one measured segment is non-zero. The LLM broadcast itself is gated
164
+ // in completeLocusSyncLatency(); this only guards against an all-zero (unmeasurable) record.
165
+ var hasMeaningfulSegment = hashtreeResponseTime > 0 || typeof syncResponseTime === 'number' && syncResponseTime > 0 || typeof syncMessageReceiveTime === 'number' && syncMessageReceiveTime > 0;
166
+ if (!hasMeaningfulSegment) {
167
+ return undefined;
168
+ }
169
+ return _objectSpread(_objectSpread(_objectSpread({
170
+ randomBackoffTime: this.getClampedLocusSyncLatency(record.randomBackoffTime),
171
+ hashtreePrepTime: hashtreePrepTime,
172
+ hashtreeResponseTime: hashtreeResponseTime,
173
+ syncPrepTime: syncPrepTime
174
+ }, typeof syncResponseTime === 'number' ? {
175
+ syncResponseTime: syncResponseTime
176
+ } : {}), typeof syncMessageReceiveTime === 'number' ? {
177
+ syncMessageReceiveTime: syncMessageReceiveTime
178
+ } : {}), {}, {
179
+ totalTime: totalTime
180
+ });
181
+ }
182
+
183
+ /**
184
+ * Records the time the LLM state-update message for a Locus sync arrived. This is the milestone
185
+ * that gates the client.locus.sync.complete metric: the metric is emitted only for flows where
186
+ * the update came over LLM (aligned with the agreed requirement that body/http-only syncs do not
187
+ * emit). The record is matched by meeting id + tracking id, regardless of which other milestones
188
+ * are present yet, so the real arrival time is captured even if the /sync response has not landed.
189
+ * @param meetingId meeting id
190
+ * @param trackingId sync tracking id used to match the pending record
191
+ * @returns void
192
+ */
193
+ }, {
194
+ key: "recordLocusSyncMessageReceived",
195
+ value: function recordLocusSyncMessageReceived(meetingId, trackingId) {
196
+ if (!trackingId) {
197
+ return;
198
+ }
199
+ var record = this.getLocusSyncLatencyRecord(meetingId, trackingId);
200
+ if (record && typeof record.messageReceived !== 'number') {
201
+ record.messageReceived = new Date().getTime();
202
+ var records = this.meetingLatencies.get(meetingId);
203
+ if (records) {
204
+ this.setMeetingLatencyRecords(meetingId, records, {
205
+ changedRecord: record
206
+ });
207
+ }
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Complete and remove the Locus sync latency record for a meeting + tracking id.
213
+ *
214
+ * Both milestones are required, in any arrival order: the metric is emitted only once BOTH the
215
+ * matching LLM broadcast (messageReceived) AND the /sync response have arrived. The LLM broadcast
216
+ * confirms the client that issued the /sync received the resulting state update for its tracking
217
+ * id (so sync cancel, or a sync storm where the final broadcast echoed another client's tracking
218
+ * id, never emit); the /sync response is always waited for too, since the LLM message can arrive
219
+ * before the HTTP response. If either milestone is missing the record is kept so the other can
220
+ * still arrive; it is only consumed when the metric is actually emitted.
221
+ * @param meetingId meeting id
222
+ * @param trackingId sync tracking id used to match the pending record
223
+ * @returns completed sync latency metric payload, or undefined when not completed
224
+ */
225
+ }, {
226
+ key: "completeLocusSyncLatency",
227
+ value: function completeLocusSyncLatency(meetingId, trackingId) {
228
+ if (!trackingId) {
229
+ return undefined;
230
+ }
231
+ var record = this.getLocusSyncLatencyRecord(meetingId, trackingId);
232
+ if (!record) {
233
+ return undefined;
234
+ }
235
+ var hasMessageReceived = typeof record.messageReceived === 'number';
236
+ var hasSyncResponse = typeof record.syncResponse === 'number';
237
+
238
+ // Wait until both milestones are present before emitting. The LLM broadcast and the /sync
239
+ // response can arrive in either order, so we keep the record until the second one lands.
240
+ if (!hasMessageReceived || !hasSyncResponse) {
241
+ return undefined;
242
+ }
243
+ var syncLatency = this.getLocusSyncLatency(meetingId, trackingId);
244
+ this.removeLocusSyncLatencyRecord(meetingId, trackingId);
245
+ if (!syncLatency) {
246
+ return undefined;
247
+ }
248
+ var completed = {
249
+ dataSet: record.dataSetName,
250
+ syncLatency: syncLatency
251
+ };
252
+ return completed;
253
+ }
254
+
255
+ /**
256
+ * Helper to calculate end - start for Locus sync milestones.
257
+ * @param record tracked milestone timestamps
258
+ * @param a start milestone
259
+ * @param b end milestone
260
+ * @returns latency
261
+ */
262
+ }, {
263
+ key: "getDiffBetweenLocusSyncTimestamps",
264
+ value: function getDiffBetweenLocusSyncTimestamps(record, a, b) {
265
+ var start = record[a];
266
+ var end = record[b];
267
+ if (typeof start !== 'number' || typeof end !== 'number') {
268
+ return undefined;
269
+ }
270
+ return this.getClampedLocusSyncLatency(end - start);
271
+ }
272
+
273
+ /**
274
+ * Get the timestamp that starts the sync prep segment.
275
+ * @param record tracked milestone timestamps
276
+ * @returns sync prep start milestone
277
+ */
278
+ }, {
279
+ key: "getLocusSyncPrepStart",
280
+ value: function getLocusSyncPrepStart(record) {
281
+ return typeof record.hashTreeResponse === 'number' ? 'hashTreeResponse' : 'syncStart';
282
+ }
283
+
284
+ /**
285
+ * Round and clamp Locus sync latency values.
286
+ * @param latency latency value
287
+ * @returns rounded latency
288
+ */
289
+ }, {
290
+ key: "getClampedLocusSyncLatency",
291
+ value: function getClampedLocusSyncLatency(latency) {
292
+ return Math.round((0, _lodash.clamp)(latency, 0, this.MAX_INTEGER));
293
+ }
294
+
295
+ /**
296
+ * Finds a Locus sync latency record for a meeting using a predicate run against each stored
297
+ * locusSync record.
298
+ * @param meetingId meeting id
299
+ * @param predicate matcher run against each locusSync record
300
+ * @param options.searchFromLatest when true, iterate from the most recently added record first
301
+ * @returns matching Locus sync latency record
302
+ */
303
+ }, {
304
+ key: "findLocusSyncRecord",
305
+ value: function findLocusSyncRecord(meetingId, predicate) {
306
+ var _orderedRecords$find;
307
+ var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
308
+ _ref$searchFromLatest = _ref.searchFromLatest,
309
+ searchFromLatest = _ref$searchFromLatest === void 0 ? false : _ref$searchFromLatest;
310
+ var records = this.meetingLatencies.get(meetingId);
311
+ if (!records) {
312
+ return undefined;
313
+ }
314
+ var orderedRecords = searchFromLatest ? (0, _toConsumableArray2.default)(records).reverse() : records;
315
+ return (_orderedRecords$find = orderedRecords.find(function (record) {
316
+ return predicate(record.locusSync);
317
+ })) === null || _orderedRecords$find === void 0 ? void 0 : _orderedRecords$find.locusSync;
318
+ }
319
+
320
+ /**
321
+ * Get the Locus sync latency record for a meeting and tracking id.
322
+ * @param meetingId meeting id
323
+ * @param trackingId /sync response tracking id
324
+ * @returns matching Locus sync latency record
325
+ */
326
+ }, {
327
+ key: "getLocusSyncLatencyRecord",
328
+ value: function getLocusSyncLatencyRecord(meetingId, trackingId) {
329
+ return this.findLocusSyncRecord(meetingId, function (locusSync) {
330
+ return locusSync.trackingId === trackingId;
331
+ });
332
+ }
333
+
334
+ /**
335
+ * Remove the Locus sync latency record for a meeting and tracking id.
336
+ * @param meetingId meeting id
337
+ * @param trackingId sync tracking id used to match the record
338
+ * @returns void
339
+ */
340
+ }, {
341
+ key: "removeLocusSyncLatencyRecord",
342
+ value: function removeLocusSyncLatencyRecord(meetingId, trackingId) {
343
+ var records = this.meetingLatencies.get(meetingId);
344
+ if (!records) {
345
+ return;
346
+ }
347
+ var remainingRecords = records.filter(function (_ref2) {
348
+ var locusSync = _ref2.locusSync;
349
+ return locusSync.trackingId !== trackingId;
350
+ });
351
+ this.setMeetingLatencyRecords(meetingId, remainingRecords);
352
+ }
353
+
354
+ /**
355
+ * Prune stale never-completed Locus sync latency records for the same dataset as the record that
356
+ * just changed. Records are dropped when they have been pending longer than the TTL, or to keep
357
+ * the number of records per dataset under the safety cap (oldest pending candidates first).
358
+ * @param records current records for the meeting
359
+ * @param changedRecord the record that was just updated, used as the reference point
360
+ * @returns the records to keep
361
+ */
362
+ }, {
363
+ key: "pruneStaleLocusSyncLatencyRecords",
364
+ value: function pruneStaleLocusSyncLatencyRecords(records, changedRecord) {
365
+ var _this3 = this;
366
+ var currentTimestamp = this.getLatestLocusSyncTimestamp(changedRecord);
367
+ if (typeof currentTimestamp !== 'number') {
368
+ return records;
369
+ }
370
+
371
+ // A record is only a cleanup candidate when it belongs to the same dataset as the record that
372
+ // just changed, is not that record itself, and has never completed. Everything else is kept.
373
+ var isPrunableCandidate = function isPrunableCandidate(locusSync) {
374
+ if (locusSync.dataSetName !== changedRecord.dataSetName) {
375
+ return false;
376
+ }
377
+ if (changedRecord.trackingId && locusSync.trackingId === changedRecord.trackingId) {
378
+ return false;
379
+ }
380
+ var hasCompleted = typeof locusSync.syncResponse === 'number' && typeof locusSync.messageReceived === 'number';
381
+ return !hasCompleted;
382
+ };
383
+
384
+ // 1) Drop candidates that have been pending longer than the TTL.
385
+ var recordsAfterTtl = records.filter(function (_ref3) {
386
+ var locusSync = _ref3.locusSync;
387
+ if (!isPrunableCandidate(locusSync)) {
388
+ return true;
389
+ }
390
+ var lastTimestamp = _this3.getLatestLocusSyncTimestamp(locusSync);
391
+ if (typeof lastTimestamp !== 'number') {
392
+ return true;
393
+ }
394
+ return currentTimestamp - lastTimestamp < _this3.LOCUS_SYNC_PENDING_RECORD_TTL_MS;
395
+ });
396
+
397
+ // 2) Cap the number of records kept per dataset, dropping the oldest pending candidates first.
398
+ var overflowCount = Math.max(0, recordsAfterTtl.filter(function (_ref4) {
399
+ var locusSync = _ref4.locusSync;
400
+ return locusSync.dataSetName === changedRecord.dataSetName;
401
+ }).length - this.LOCUS_SYNC_MAX_RECORDS_PER_DATASET);
402
+ if (overflowCount === 0) {
403
+ return recordsAfterTtl;
404
+ }
405
+ return recordsAfterTtl.filter(function (_ref5) {
406
+ var locusSync = _ref5.locusSync;
407
+ if (overflowCount <= 0 || !isPrunableCandidate(locusSync)) {
408
+ return true;
409
+ }
410
+ overflowCount -= 1;
411
+ return false;
412
+ });
413
+ }
414
+
415
+ /**
416
+ * Get the most recent milestone timestamp stored on a Locus sync latency record, checking
417
+ * milestones from latest to earliest so the newest available one is returned.
418
+ * @param record tracked milestone timestamps
419
+ * @returns latest milestone timestamp, or undefined when none are set
420
+ */
421
+ }, {
422
+ key: "getLatestLocusSyncTimestamp",
423
+ value: function getLatestLocusSyncTimestamp(record) {
424
+ var _ref6, _ref7, _ref8, _ref9, _record$messageReceiv;
425
+ return (_ref6 = (_ref7 = (_ref8 = (_ref9 = (_record$messageReceiv = record.messageReceived) !== null && _record$messageReceiv !== void 0 ? _record$messageReceiv : record.syncResponse) !== null && _ref9 !== void 0 ? _ref9 : record.syncRequest) !== null && _ref8 !== void 0 ? _ref8 : record.hashTreeResponse) !== null && _ref7 !== void 0 ? _ref7 : record.hashTreeRequest) !== null && _ref6 !== void 0 ? _ref6 : record.syncStart;
426
+ }
427
+
428
+ /**
429
+ * Persist the Locus sync latency records for a meeting, deleting the meeting entry when no
430
+ * records remain. When pruneOptions is provided, stale records are pruned before saving.
431
+ * @param meetingId meeting id
432
+ * @param records records to save
433
+ * @param pruneOptions when provided, triggers stale record pruning relative to changedRecord
434
+ * @returns void
435
+ */
436
+ }, {
437
+ key: "setMeetingLatencyRecords",
438
+ value: function setMeetingLatencyRecords(meetingId, records, pruneOptions) {
439
+ var recordsToSave = pruneOptions ? this.pruneStaleLocusSyncLatencyRecords(records, pruneOptions.changedRecord) : records;
440
+ if (recordsToSave.length > 0) {
441
+ this.meetingLatencies.set(meetingId, recordsToSave);
442
+ } else {
443
+ this.meetingLatencies.delete(meetingId);
444
+ }
445
+ }
446
+
447
+ /**
448
+ * Get the latest Locus sync latency record waiting for sync.start.
449
+ * @param meetingId meeting id
450
+ * @param dataSetName dataset name
451
+ * @returns latest pending Locus sync latency record
452
+ */
453
+ }, {
454
+ key: "getLatestPendingLocusSyncLatencyRecord",
455
+ value: function getLatestPendingLocusSyncLatencyRecord(meetingId, dataSetName) {
456
+ return this.findLocusSyncRecord(meetingId, function (locusSync) {
457
+ return locusSync.dataSetName === dataSetName && locusSync.syncStart === undefined;
458
+ }, {
459
+ searchFromLatest: true
460
+ });
461
+ }
462
+
463
+ /**
464
+ * Store random backoff latency for the current or next Locus sync latency record.
465
+ * @param meetingId meeting id
466
+ * @param dataSetName dataset name
467
+ * @param randomBackoffTime random backoff latency value
468
+ * @returns void
469
+ */
470
+ }, {
471
+ key: "saveLocusSyncBackoffLatency",
472
+ value: function saveLocusSyncBackoffLatency(_ref0) {
473
+ var _this$meetingLatencie;
474
+ var meetingId = _ref0.meetingId,
475
+ dataSetName = _ref0.dataSetName,
476
+ randomBackoffTime = _ref0.randomBackoffTime;
477
+ var pendingRecord = this.getLatestPendingLocusSyncLatencyRecord(meetingId, dataSetName);
478
+ if (pendingRecord) {
479
+ pendingRecord.randomBackoffTime = randomBackoffTime;
480
+ return;
481
+ }
482
+ var records = (_this$meetingLatencie = this.meetingLatencies.get(meetingId)) !== null && _this$meetingLatencie !== void 0 ? _this$meetingLatencie : [];
483
+ records.push({
484
+ locusSync: {
485
+ meetingId: meetingId,
486
+ dataSetName: dataSetName,
487
+ randomBackoffTime: randomBackoffTime
488
+ }
489
+ });
490
+ this.meetingLatencies.set(meetingId, records);
491
+ }
492
+
493
+ /**
494
+ * Checks if metric event name is a Locus sync latency milestone.
495
+ * @param key event name
496
+ * @returns whether event is Locus sync latency milestone
497
+ */
498
+ }, {
499
+ key: "isLocusSyncLatencyEvent",
500
+ value: function isLocusSyncLatencyEvent(key) {
501
+ return _metrics.LOCUS_SYNC_LATENCY_EVENT_NAMES.includes(key);
502
+ }
503
+
504
+ /**
505
+ * Stores a Locus sync latency milestone timestamp.
506
+ * @param options options
507
+ */
508
+ }, {
509
+ key: "saveLocusSyncLatencyTimestamp",
510
+ value: function saveLocusSyncLatencyTimestamp(_ref1) {
511
+ var _this$meetingLatencie4;
512
+ var meetingId = _ref1.meetingId,
513
+ dataSetName = _ref1.dataSetName,
514
+ key = _ref1.key,
515
+ value = _ref1.value,
516
+ trackingId = _ref1.trackingId;
517
+ if (!trackingId) {
518
+ // @ts-ignore
519
+ this.webex.logger.warn("CallDiagnosticLatencies: saveLocusSyncLatencyTimestamp called without a trackingId for key \"".concat(key, "\"; skipping Locus sync milestone"));
520
+ return;
521
+ }
522
+ if (key === 'internal.client.locus.sync.start') {
523
+ var _this$meetingLatencie3;
524
+ var pendingRecord = this.getLatestPendingLocusSyncLatencyRecord(meetingId, dataSetName);
525
+ if (pendingRecord) {
526
+ var _this$meetingLatencie2;
527
+ pendingRecord.trackingId = trackingId;
528
+ pendingRecord.syncStart = value;
529
+ this.setMeetingLatencyRecords(meetingId, (_this$meetingLatencie2 = this.meetingLatencies.get(meetingId)) !== null && _this$meetingLatencie2 !== void 0 ? _this$meetingLatencie2 : [], {
530
+ changedRecord: pendingRecord
531
+ });
532
+ return;
533
+ }
534
+ var records = (_this$meetingLatencie3 = this.meetingLatencies.get(meetingId)) !== null && _this$meetingLatencie3 !== void 0 ? _this$meetingLatencie3 : [];
535
+ records.push({
536
+ locusSync: {
537
+ meetingId: meetingId,
538
+ dataSetName: dataSetName,
539
+ randomBackoffTime: 0,
540
+ trackingId: trackingId,
541
+ syncStart: value
542
+ }
543
+ });
544
+ this.setMeetingLatencyRecords(meetingId, records, {
545
+ changedRecord: records[records.length - 1].locusSync
546
+ });
547
+ return;
548
+ }
549
+
550
+ // Every sync milestone after sync.start is stamped with the same tracking id that was
551
+ // generated up-front and forced onto the /sync request, so the record is located by
552
+ // meeting id + tracking id.
553
+ var record = this.getLocusSyncLatencyRecord(meetingId, trackingId);
554
+ if (!record) {
555
+ return;
556
+ }
557
+ switch (key) {
558
+ case 'internal.client.locus.hashtree.request':
559
+ record.hashTreeRequest = value;
560
+ break;
561
+ case 'internal.client.locus.hashtree.response':
562
+ record.hashTreeResponse = value;
563
+ break;
564
+ case 'internal.client.locus.sync.request':
565
+ record.syncRequest = value;
566
+ break;
567
+ case 'internal.client.locus.sync.response':
568
+ record.syncResponse = value;
569
+ break;
570
+ case 'internal.client.locus.sync.message.received':
571
+ record.messageReceived = value;
572
+ break;
573
+ default:
574
+ break;
575
+ }
576
+ this.setMeetingLatencyRecords(meetingId, (_this$meetingLatencie4 = this.meetingLatencies.get(meetingId)) !== null && _this$meetingLatencie4 !== void 0 ? _this$meetingLatencie4 : [], {
577
+ changedRecord: record
578
+ });
57
579
  }
58
580
 
59
581
  /**
@@ -90,17 +612,28 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
90
612
  */
91
613
  }, {
92
614
  key: "saveTimestamp",
93
- value: function saveTimestamp(_ref) {
94
- var key = _ref.key,
95
- _ref$value = _ref.value,
96
- value = _ref$value === void 0 ? new Date().getTime() : _ref$value,
97
- _ref$options = _ref.options,
98
- options = _ref$options === void 0 ? {} : _ref$options;
615
+ value: function saveTimestamp(_ref10) {
616
+ var key = _ref10.key,
617
+ _ref10$value = _ref10.value,
618
+ value = _ref10$value === void 0 ? new Date().getTime() : _ref10$value,
619
+ _ref10$options = _ref10.options,
620
+ options = _ref10$options === void 0 ? {} : _ref10$options;
99
621
  // save the meetingId so we can use the meeting object in latency calculations if needed
100
622
  var meetingId = options.meetingId;
101
623
  if (meetingId) {
102
624
  this.setMeetingId(meetingId);
103
625
  }
626
+ if (this.isLocusSyncLatencyEvent(key) && options.dataSetName && options.meetingId) {
627
+ this.saveLocusSyncLatencyTimestamp({
628
+ meetingId: options.meetingId,
629
+ dataSetName: options.dataSetName,
630
+ key: key,
631
+ value: value,
632
+ trackingId: options.trackingId
633
+ });
634
+ return;
635
+ }
636
+
104
637
  // for some events we're only interested in the first timestamp not last
105
638
  // as these events can happen multiple times
106
639
  if (key === 'client.media.rx.start' || key === 'client.media.tx.start' || key === 'internal.client.meetinginfo.request' || key === 'internal.client.meetinginfo.response' || key === 'client.media-engine.remote-sdp-received') {
@@ -118,14 +651,35 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
118
651
  * Store precomputed latency value
119
652
  * @param key - key
120
653
  * @param value - value
121
- * @param accumulate - when it is true, it overwrites existing value with sum of the current value and the new measurement otherwise just store the new measurement
654
+ * @param options - store options (a legacy boolean `accumulate` flag is also accepted)
655
+ * @param options.accumulate - when it is true, it overwrites existing value with sum of the current value and the new measurement otherwise just store the new measurement
656
+ * @param options.meetingId - meeting id, only used for Locus sync latency records
657
+ * @param options.dataSetName - dataset name, only used for Locus sync latency records
122
658
  * @throws
123
659
  * @returns
124
660
  */
125
661
  }, {
126
662
  key: "saveLatency",
127
663
  value: function saveLatency(key, value) {
128
- var accumulate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
664
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
665
+ // Older (untyped) callers may still pass a boolean `accumulate` as the third argument
666
+ // (saveLatency(key, value, true)). Normalize it to the options object before destructuring,
667
+ // otherwise accumulation is silently lost and legacy SDK/plugin consumers underreport latencies.
668
+ var normalizedOptions = typeof options === 'boolean' ? {
669
+ accumulate: options
670
+ } : options;
671
+ var _normalizedOptions$ac = normalizedOptions.accumulate,
672
+ accumulate = _normalizedOptions$ac === void 0 ? false : _normalizedOptions$ac,
673
+ meetingId = normalizedOptions.meetingId,
674
+ dataSetName = normalizedOptions.dataSetName;
675
+ if (key === 'internal.client.locus.sync.random.backoff' && meetingId && dataSetName) {
676
+ this.saveLocusSyncBackoffLatency({
677
+ meetingId: meetingId,
678
+ dataSetName: dataSetName,
679
+ randomBackoffTime: value
680
+ });
681
+ return;
682
+ }
129
683
  var existingValue = accumulate ? this.precomputedLatencies.get(key) || 0 : 0;
130
684
  this.precomputedLatencies.set(key, value + existingValue);
131
685
  }
@@ -140,11 +694,13 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
140
694
  }, {
141
695
  key: "measureLatency",
142
696
  value: function measureLatency(callback, key) {
143
- var _this2 = this;
697
+ var _this4 = this;
144
698
  var accumulate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
145
699
  var start = performance.now();
146
700
  return callback().finally(function () {
147
- _this2.saveLatency(key, performance.now() - start, accumulate);
701
+ _this4.saveLatency(key, performance.now() - start, {
702
+ accumulate: accumulate
703
+ });
148
704
  });
149
705
  }
150
706
 
@@ -180,11 +736,11 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
180
736
  return undefined;
181
737
  }
182
738
  var diff = end - start;
183
- var _ref2 = clampValues || {},
184
- _ref2$minimum = _ref2.minimum,
185
- minimum = _ref2$minimum === void 0 ? 0 : _ref2$minimum,
186
- _ref2$maximum = _ref2.maximum,
187
- maximum = _ref2$maximum === void 0 ? this.MAX_INTEGER : _ref2$maximum;
739
+ var _ref11 = clampValues || {},
740
+ _ref11$minimum = _ref11.minimum,
741
+ minimum = _ref11$minimum === void 0 ? 0 : _ref11$minimum,
742
+ _ref11$maximum = _ref11.maximum,
743
+ maximum = _ref11$maximum === void 0 ? this.MAX_INTEGER : _ref11$maximum;
188
744
  return (0, _lodash.clamp)(diff, minimum, maximum);
189
745
  }
190
746
 
@@ -244,9 +800,12 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
244
800
  }, {
245
801
  key: "getCallInitJoinReq",
246
802
  value: function getCallInitJoinReq() {
247
- return this.getDiffBetweenTimestamps('internal.client.interstitial-window.click.joinbutton', 'client.locus.join.request', {
248
- maximum: 1200000
249
- });
803
+ var interstitialShowedToJoinReq = this.getDiffBetweenTimestamps('internal.client.meeting.interstitial-window.showed', 'client.locus.join.request');
804
+ var showInterstitialTime = this.getShowInterstitialTime() || 0;
805
+ if (typeof interstitialShowedToJoinReq !== 'number') {
806
+ return undefined;
807
+ }
808
+ return (0, _lodash.clamp)(interstitialShowedToJoinReq - showInterstitialTime, 0, 1200000);
250
809
  }
251
810
 
252
811
  /**
@@ -332,7 +891,35 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
332
891
  }, {
333
892
  key: "getStayLobbyTime",
334
893
  value: function getStayLobbyTime() {
335
- return this.getDiffBetweenTimestamps('client.locus.join.response', 'client.lobby.exited');
894
+ return this.getDiffBetweenTimestamps('client.lobby.entered', 'client.lobby.exited');
895
+ }
896
+
897
+ /**
898
+ * Stay lobby time capped by a certain timestamp.
899
+ * This is to handle the case where the target end timestamp could happen before the lobby is exited,
900
+ * for example media-engine.ready or client.ice.end
901
+ * This is supposed to be called AFTER the end timestamp happens
902
+ * @param endTimestampKey name of the target end event
903
+ * @returns - latency
904
+ */
905
+ }, {
906
+ key: "getStayLobbyTimeCappedBy",
907
+ value: function getStayLobbyTimeCappedBy(endTimestampKey) {
908
+ var lobbyStartTimestamp = this.latencyTimestamps.get('client.lobby.entered'); // might not exist (some meetings don't have lobby)
909
+
910
+ if (typeof lobbyStartTimestamp !== 'number') {
911
+ // no lobby in the meeting, stayLobbyTime is 0
912
+ return 0;
913
+ }
914
+ var lobbyEndTimestamp = this.latencyTimestamps.get('client.lobby.exited'); // might not exist (if user still in lobby at the time of measurement)
915
+ var maximumEndTimestamp = this.latencyTimestamps.get(endTimestampKey); // must exist
916
+
917
+ if (typeof maximumEndTimestamp !== 'number') {
918
+ // the provided timestamp to be used as a cap should exist, return undefined if it doesn't
919
+ return undefined;
920
+ }
921
+ var endTimestamp = typeof lobbyEndTimestamp === 'number' ? Math.min(lobbyEndTimestamp, maximumEndTimestamp) : maximumEndTimestamp;
922
+ return (0, _lodash.clamp)(endTimestamp - lobbyStartTimestamp, 0, this.MAX_INTEGER);
336
923
  }
337
924
 
338
925
  /**
@@ -364,10 +951,6 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
364
951
  }, {
365
952
  key: "getClickToInterstitial",
366
953
  value: function getClickToInterstitial() {
367
- // for normal join (where green join button exists before interstitial, i.e reminder, space list etc)
368
- if (this.latencyTimestamps.get('internal.client.meeting.click.joinbutton')) {
369
- return this.getDiffBetweenTimestamps('internal.client.meeting.click.joinbutton', 'internal.client.meeting.interstitial-window.showed');
370
- }
371
954
  var clickToInterstitialLatency = this.precomputedLatencies.get('internal.click.to.interstitial');
372
955
  if (typeof clickToInterstitialLatency === 'number') {
373
956
  return (0, _lodash.clamp)(clickToInterstitialLatency, 0, this.MAX_INTEGER);
@@ -382,10 +965,6 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
382
965
  }, {
383
966
  key: "getClickToInterstitialWithUserDelay",
384
967
  value: function getClickToInterstitialWithUserDelay() {
385
- // for normal join (where green join button exists before interstitial, i.e reminder, space list etc)
386
- if (this.latencyTimestamps.get('internal.client.meeting.click.joinbutton')) {
387
- return this.getDiffBetweenTimestamps('internal.client.meeting.click.joinbutton', 'internal.client.meeting.interstitial-window.showed');
388
- }
389
968
  var clickToInterstitialWithUserDelayLatency = this.precomputedLatencies.get('internal.click.to.interstitial.with.user.delay');
390
969
  if (typeof clickToInterstitialWithUserDelayLatency === 'number') {
391
970
  return (0, _lodash.clamp)(clickToInterstitialWithUserDelayLatency, 0, this.MAX_INTEGER);
@@ -400,7 +979,12 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
400
979
  }, {
401
980
  key: "getInterstitialToJoinOK",
402
981
  value: function getInterstitialToJoinOK() {
403
- return this.getDiffBetweenTimestamps('internal.client.interstitial-window.click.joinbutton', 'client.locus.join.response');
982
+ var interstitialShowedToJoinResp = this.getDiffBetweenTimestamps('internal.client.meeting.interstitial-window.showed', 'client.locus.join.response');
983
+ var showInterstitialTime = this.getShowInterstitialTime() || 0;
984
+ if (typeof interstitialShowedToJoinResp !== 'number') {
985
+ return undefined;
986
+ }
987
+ return (0, _lodash.clamp)(interstitialShowedToJoinResp - showInterstitialTime, 0, this.MAX_INTEGER);
404
988
  }
405
989
 
406
990
  /**
@@ -410,9 +994,7 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
410
994
  }, {
411
995
  key: "getCallInitMediaEngineReady",
412
996
  value: function getCallInitMediaEngineReady() {
413
- return this.getDiffBetweenTimestamps('internal.client.interstitial-window.click.joinbutton', 'client.media-engine.ready', {
414
- maximum: 1200000
415
- });
997
+ return this.getInterstitialToMediaOKJMT();
416
998
  }
417
999
 
418
1000
  /**
@@ -422,15 +1004,11 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
422
1004
  }, {
423
1005
  key: "getInterstitialToMediaOKJMT",
424
1006
  value: function getInterstitialToMediaOKJMT() {
425
- var interstitialJoinClickTimestamp = this.latencyTimestamps.get('internal.client.interstitial-window.click.joinbutton');
426
-
427
- // get the first timestamp
428
- var connectedMedia = this.latencyTimestamps.get('client.ice.end');
429
- var lobbyTimeLatency = this.getStayLobbyTime();
430
- var lobbyTime = typeof lobbyTimeLatency === 'number' ? lobbyTimeLatency : 0;
431
- if (interstitialJoinClickTimestamp && connectedMedia) {
432
- var interstitialToMediaOKJmt = connectedMedia - interstitialJoinClickTimestamp - lobbyTime;
433
- return (0, _lodash.clamp)(interstitialToMediaOKJmt, 0, this.MAX_INTEGER);
1007
+ var interstitialShowedToIceEnd = this.getDiffBetweenTimestamps('internal.client.meeting.interstitial-window.showed', 'client.ice.end');
1008
+ var showInterstitialTime = this.getShowInterstitialTime() || 0;
1009
+ var stayLobbyTimeCappedByIceEnd = this.getStayLobbyTimeCappedBy('client.ice.end');
1010
+ if (typeof interstitialShowedToIceEnd === 'number' && typeof stayLobbyTimeCappedByIceEnd === 'number') {
1011
+ return (0, _lodash.clamp)(interstitialShowedToIceEnd - showInterstitialTime - stayLobbyTimeCappedByIceEnd, 0, this.MAX_INTEGER);
434
1012
  }
435
1013
  return undefined;
436
1014
  }
@@ -443,9 +1021,10 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
443
1021
  key: "getTotalJMT",
444
1022
  value: function getTotalJMT() {
445
1023
  var clickToInterstitial = this.getClickToInterstitial();
446
- var interstitialToJoinOk = this.getInterstitialToJoinOK();
447
- if (typeof clickToInterstitial === 'number' && typeof interstitialToJoinOk === 'number') {
448
- return (0, _lodash.clamp)(clickToInterstitial + interstitialToJoinOk, 0, this.MAX_INTEGER);
1024
+ var interstitialShowedToJoinLocusResponse = this.getDiffBetweenTimestamps('internal.client.meeting.interstitial-window.showed', 'client.locus.join.response');
1025
+ var showInterstitialTime = this.getShowInterstitialTime() || 0;
1026
+ if (typeof clickToInterstitial === 'number' && typeof interstitialShowedToJoinLocusResponse === 'number') {
1027
+ return (0, _lodash.clamp)(clickToInterstitial + interstitialShowedToJoinLocusResponse - showInterstitialTime, 0, this.MAX_INTEGER);
449
1028
  }
450
1029
  return undefined;
451
1030
  }
@@ -458,9 +1037,9 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
458
1037
  key: "getTotalJMTWithUserDelay",
459
1038
  value: function getTotalJMTWithUserDelay() {
460
1039
  var clickToInterstitialWithUserDelay = this.getClickToInterstitialWithUserDelay();
461
- var interstitialToJoinOk = this.getInterstitialToJoinOK();
462
- if (typeof clickToInterstitialWithUserDelay === 'number' && typeof interstitialToJoinOk === 'number') {
463
- return (0, _lodash.clamp)(clickToInterstitialWithUserDelay + interstitialToJoinOk, 0, this.MAX_INTEGER);
1040
+ var interstitialShowedToJoinLocusResponse = this.getDiffBetweenTimestamps('internal.client.meeting.interstitial-window.showed', 'client.locus.join.response');
1041
+ if (typeof clickToInterstitialWithUserDelay === 'number' && typeof interstitialShowedToJoinLocusResponse === 'number') {
1042
+ return (0, _lodash.clamp)(clickToInterstitialWithUserDelay + interstitialShowedToJoinLocusResponse, 0, this.MAX_INTEGER);
464
1043
  }
465
1044
  return undefined;
466
1045
  }
@@ -488,17 +1067,11 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
488
1067
  key: "getTotalMediaJMT",
489
1068
  value: function getTotalMediaJMT() {
490
1069
  var clickToInterstitial = this.getClickToInterstitial();
491
- var interstitialToJoinOk = this.getInterstitialToJoinOK();
492
- var joinConfJMT = this.getJoinConfJMT();
493
- var lobbyTimeLatency = this.getStayLobbyTime();
494
- var lobbyTime = typeof lobbyTimeLatency === 'number' ? lobbyTimeLatency : 0;
495
- if (clickToInterstitial && interstitialToJoinOk && joinConfJMT) {
496
- var _this$getMeeting;
497
- var totalMediaJMT = (0, _lodash.clamp)(clickToInterstitial + interstitialToJoinOk + joinConfJMT, 0, Infinity);
498
- if ((_this$getMeeting = this.getMeeting()) !== null && _this$getMeeting !== void 0 && _this$getMeeting.allowMediaInLobby) {
499
- return (0, _lodash.clamp)(totalMediaJMT, 0, this.MAX_INTEGER);
500
- }
501
- return (0, _lodash.clamp)(totalMediaJMT - lobbyTime, 0, this.MAX_INTEGER);
1070
+ var interstitialShowedToMediaEngineReady = this.getDiffBetweenTimestamps('internal.client.meeting.interstitial-window.showed', 'client.media-engine.ready');
1071
+ var showInterstitialTime = this.getShowInterstitialTime() || 0;
1072
+ var stayLobbyTimeCappedByMediaEngineReady = this.getStayLobbyTimeCappedBy('client.media-engine.ready');
1073
+ if (typeof clickToInterstitial === 'number' && typeof interstitialShowedToMediaEngineReady === 'number' && typeof stayLobbyTimeCappedByMediaEngineReady === 'number') {
1074
+ return (0, _lodash.clamp)(clickToInterstitial + interstitialShowedToMediaEngineReady - showInterstitialTime - stayLobbyTimeCappedByMediaEngineReady, 0, this.MAX_INTEGER);
502
1075
  }
503
1076
  return undefined;
504
1077
  }
@@ -511,10 +1084,9 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
511
1084
  key: "getTotalMediaJMTWithUserDelay",
512
1085
  value: function getTotalMediaJMTWithUserDelay() {
513
1086
  var clickToInterstitialWithUserDelay = this.getClickToInterstitialWithUserDelay();
514
- var interstitialToJoinOk = this.getInterstitialToJoinOK();
515
- var joinConfJMT = this.getJoinConfJMT();
516
- if (clickToInterstitialWithUserDelay && interstitialToJoinOk && joinConfJMT) {
517
- return (0, _lodash.clamp)(clickToInterstitialWithUserDelay + interstitialToJoinOk + joinConfJMT, 0, this.MAX_INTEGER);
1087
+ var interstitialShowedToMediaEngineReady = this.getDiffBetweenTimestamps('internal.client.meeting.interstitial-window.showed', 'client.media-engine.ready');
1088
+ if (typeof clickToInterstitialWithUserDelay === 'number' && typeof interstitialShowedToMediaEngineReady === 'number') {
1089
+ return (0, _lodash.clamp)(clickToInterstitialWithUserDelay + interstitialShowedToMediaEngineReady, 0, this.MAX_INTEGER);
518
1090
  }
519
1091
  return undefined;
520
1092
  }
@@ -526,10 +1098,11 @@ var CallDiagnosticLatencies = exports.default = /*#__PURE__*/function (_WebexPlu
526
1098
  }, {
527
1099
  key: "getClientJMT",
528
1100
  value: function getClientJMT() {
529
- var interstitialToJoinOk = this.getInterstitialToJoinOK();
530
- var joinConfJMT = this.getJoinConfJMT();
531
- if (typeof interstitialToJoinOk === 'number' && typeof joinConfJMT === 'number') {
532
- return (0, _lodash.clamp)(interstitialToJoinOk - joinConfJMT, 0, this.MAX_INTEGER);
1101
+ var clickToInterstitialForClientJmt = this.precomputedLatencies.get('internal.click.to.interstitial.for.client.jmt');
1102
+ var interstitialShowedToLocusJoinRequest = this.getDiffBetweenTimestamps('internal.client.meeting.interstitial-window.showed', 'client.locus.join.request');
1103
+ var showInterstitialTime = this.getShowInterstitialTime() || 0;
1104
+ if (typeof clickToInterstitialForClientJmt === 'number' && typeof interstitialShowedToLocusJoinRequest === 'number') {
1105
+ return (0, _lodash.clamp)(clickToInterstitialForClientJmt + interstitialShowedToLocusJoinRequest - showInterstitialTime, 0, this.MAX_INTEGER);
533
1106
  }
534
1107
  return undefined;
535
1108
  }