@webex/internal-plugin-metrics 3.12.0-next.5 → 3.12.0-next.50

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 (84) hide show
  1. package/README.md +274 -0
  2. package/dist/automated-user.js +19 -0
  3. package/dist/automated-user.js.map +1 -0
  4. package/dist/batcher.js +3 -0
  5. package/dist/batcher.js.map +1 -1
  6. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +635 -62
  7. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
  8. package/dist/call-diagnostic/call-diagnostic-metrics.js +188 -78
  9. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
  10. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +5 -0
  11. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
  12. package/dist/call-diagnostic/config.js +16 -3
  13. package/dist/call-diagnostic/config.js.map +1 -1
  14. package/dist/config.js +8 -0
  15. package/dist/config.js.map +1 -1
  16. package/dist/index.js +25 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/metrics.js +118 -5
  19. package/dist/metrics.js.map +1 -1
  20. package/dist/metrics.types.js +4 -0
  21. package/dist/metrics.types.js.map +1 -1
  22. package/dist/network-telemetry.js +610 -0
  23. package/dist/network-telemetry.js.map +1 -0
  24. package/dist/new-metrics.js +63 -17
  25. package/dist/new-metrics.js.map +1 -1
  26. package/dist/privacy-and-security-permission-enricher/constants.js +18 -0
  27. package/dist/privacy-and-security-permission-enricher/constants.js.map +1 -0
  28. package/dist/privacy-and-security-permission-enricher/index.js +143 -0
  29. package/dist/privacy-and-security-permission-enricher/index.js.map +1 -0
  30. package/dist/privacy-and-security-permission-enricher/types.js +7 -0
  31. package/dist/privacy-and-security-permission-enricher/types.js.map +1 -0
  32. package/dist/privacy-and-security-permission-enricher/utils.js +71 -0
  33. package/dist/privacy-and-security-permission-enricher/utils.js.map +1 -0
  34. package/dist/types/automated-user.d.ts +2 -0
  35. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +208 -5
  36. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +73 -14
  37. package/dist/types/call-diagnostic/config.d.ts +4 -0
  38. package/dist/types/config.d.ts +9 -0
  39. package/dist/types/index.d.ts +5 -3
  40. package/dist/types/metrics.types.d.ts +7 -2
  41. package/dist/types/network-telemetry.d.ts +108 -0
  42. package/dist/types/new-metrics.d.ts +17 -1
  43. package/dist/types/privacy-and-security-permission-enricher/constants.d.ts +6 -0
  44. package/dist/types/privacy-and-security-permission-enricher/index.d.ts +40 -0
  45. package/dist/types/privacy-and-security-permission-enricher/types.d.ts +14 -0
  46. package/dist/types/privacy-and-security-permission-enricher/utils.d.ts +5 -0
  47. package/dist/types/unhandled-exception-telemetry/index.d.ts +60 -0
  48. package/dist/types/unhandled-exception-telemetry/utils.d.ts +6 -0
  49. package/dist/unhandled-exception-telemetry/index.js +330 -0
  50. package/dist/unhandled-exception-telemetry/index.js.map +1 -0
  51. package/dist/unhandled-exception-telemetry/utils.js +105 -0
  52. package/dist/unhandled-exception-telemetry/utils.js.map +1 -0
  53. package/package.json +12 -11
  54. package/src/automated-user.ts +16 -0
  55. package/src/batcher.js +4 -0
  56. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +781 -71
  57. package/src/call-diagnostic/call-diagnostic-metrics.ts +119 -16
  58. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +5 -0
  59. package/src/call-diagnostic/config.ts +14 -0
  60. package/src/config.js +8 -0
  61. package/src/index.ts +16 -0
  62. package/src/metrics.js +123 -5
  63. package/src/metrics.types.ts +25 -3
  64. package/src/network-telemetry.ts +757 -0
  65. package/src/new-metrics.ts +56 -6
  66. package/src/privacy-and-security-permission-enricher/constants.ts +33 -0
  67. package/src/privacy-and-security-permission-enricher/index.ts +142 -0
  68. package/src/privacy-and-security-permission-enricher/types.ts +21 -0
  69. package/src/privacy-and-security-permission-enricher/utils.ts +82 -0
  70. package/src/unhandled-exception-telemetry/index.ts +403 -0
  71. package/src/unhandled-exception-telemetry/utils.ts +101 -0
  72. package/test/unit/spec/automated-user.ts +43 -0
  73. package/test/unit/spec/batcher.js +43 -0
  74. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +14 -2
  75. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +1424 -293
  76. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +906 -159
  77. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +27 -0
  78. package/test/unit/spec/metrics.js +14 -5
  79. package/test/unit/spec/network-telemetry.ts +610 -0
  80. package/test/unit/spec/new-metrics.ts +203 -50
  81. package/test/unit/spec/prelogin-metrics-batcher.ts +71 -36
  82. package/test/unit/spec/privacy-and-security-permission-enricher.ts +318 -0
  83. package/test/unit/spec/unhandled-exception-telemetry/utils.ts +109 -0
  84. package/test/unit/spec/unhandled-exception-telemetry.ts +688 -0
@@ -0,0 +1,610 @@
1
+ "use strict";
2
+
3
+ var _Object$keys = require("@babel/runtime-corejs2/core-js/object/keys");
4
+ var _Object$getOwnPropertySymbols = require("@babel/runtime-corejs2/core-js/object/get-own-property-symbols");
5
+ var _Object$getOwnPropertyDescriptor = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptor");
6
+ var _Object$getOwnPropertyDescriptors = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptors");
7
+ var _Object$defineProperties = require("@babel/runtime-corejs2/core-js/object/define-properties");
8
+ var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/define-property");
9
+ var _Array$from = require("@babel/runtime-corejs2/core-js/array/from");
10
+ var _Symbol = require("@babel/runtime-corejs2/core-js/symbol");
11
+ var _Symbol$iterator = require("@babel/runtime-corejs2/core-js/symbol/iterator");
12
+ var _Array$isArray = require("@babel/runtime-corejs2/core-js/array/is-array");
13
+ var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
14
+ _Object$defineProperty(exports, "__esModule", {
15
+ value: true
16
+ });
17
+ exports.NETWORK_TELEMETRY_INTERVAL_MS = exports.NETWORK_REQUEST_SUMMARY_METRIC = void 0;
18
+ exports.createNetworkErrorMetric = createNetworkErrorMetric;
19
+ exports.createNetworkTelemetryCollector = createNetworkTelemetryCollector;
20
+ exports.getOperation = getOperation;
21
+ exports.shouldTrackNetworkRequest = shouldTrackNetworkRequest;
22
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/toConsumableArray"));
23
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
24
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/typeof"));
25
+ var _isFinite = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/number/is-finite"));
26
+ var _isInteger = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/number/is-integer"));
27
+ var _set = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/set"));
28
+ var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
29
+ var _commonTimers = require("@webex/common-timers");
30
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof _Symbol && r[_Symbol$iterator] || r["@@iterator"]; if (!t) { if (_Array$isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
31
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? _Array$from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
32
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
33
+ 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; }
34
+ 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; } // TypeScript signatures own the types; JSDoc contains descriptions only.
35
+ /* eslint valid-jsdoc: ["error", {"requireParamType": false, "requireReturnType": false}] */
36
+ var NETWORK_REQUEST_SUMMARY_METRIC = exports.NETWORK_REQUEST_SUMMARY_METRIC = 'JS_SDK_NETWORK_REQUEST_SUMMARY';
37
+ var NETWORK_TELEMETRY_INTERVAL_MS = exports.NETWORK_TELEMETRY_INTERVAL_MS = 10 * 60 * 1000;
38
+
39
+ // Only segments that follow the SDK's static route naming convention are kept.
40
+ // Numeric, mixed-case, encoded, and otherwise dynamic-looking values are redacted.
41
+ // Lowercase alphabetic identifiers remain ambiguous because requests do not provide route templates.
42
+ // Examples: `rooms/12345/messages` becomes `rooms/:id/messages`, and
43
+ // `v1/rooms/65f81b6e-19dc-4a99-9175-59e9b34a5d42` becomes `v1/rooms/:id`.
44
+ // `reports/a%2Fb/download` becomes `reports/:id/download`; `rooms`, `meeting-info`, and `v1` stay unchanged.
45
+ var SAFE_ROUTE_SEGMENT = /^(?:[a-z]+(?:-[a-z]+)*|v[1-9]\d?)$/;
46
+ var MAX_TRACKING_IDS = 10;
47
+ var UNKNOWN = 'unknown';
48
+
49
+ // Request events come from JavaScript in webex-core and have no shared TypeScript
50
+ // contract. This local type intentionally contains only fields used by telemetry.
51
+
52
+ /**
53
+ * Converts a non-empty string or number to a string.
54
+ * @param value Value to convert.
55
+ * @returns The converted string, or undefined for unsupported values.
56
+ */
57
+ function toString(value) {
58
+ if (typeof value === 'number') {
59
+ return String(value);
60
+ }
61
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
62
+ }
63
+
64
+ /**
65
+ * Returns the normalized SDK service name.
66
+ * @param options Request options containing the service or API name.
67
+ * @returns The normalized service name.
68
+ */
69
+ function getService(options) {
70
+ var service = options.service || options.api;
71
+ return typeof service === 'string' ? service.trim().toLowerCase() : '';
72
+ }
73
+
74
+ /**
75
+ * Replaces values that may be resource identifiers with a stable placeholder.
76
+ * @param segment Endpoint path segment.
77
+ * @returns The original static segment or the identifier placeholder.
78
+ */
79
+ function normalizeRouteSegment(segment) {
80
+ return SAFE_ROUTE_SEGMENT.test(segment) ? segment : ':id';
81
+ }
82
+
83
+ /**
84
+ * Removes query data and redacts dynamic-looking endpoint segments.
85
+ * @param resource SDK resource path.
86
+ * @returns The normalized endpoint.
87
+ */
88
+ function getOperation(resource) {
89
+ if (typeof resource !== 'string' || resource.length === 0) {
90
+ return UNKNOWN;
91
+ }
92
+ var path = resource.split(/[?#]/, 1)[0];
93
+ var segments = path.split('/').filter(Boolean).map(normalizeRouteSegment);
94
+ return segments.length > 0 ? segments.join('/') : 'root';
95
+ }
96
+
97
+ /**
98
+ * Extracts only the direct URL parts allowed in telemetry. URL credentials,
99
+ * query parameters, and fragments are not returned, while the pathname is
100
+ * converted to a sanitized endpoint.
101
+ * @param options Request options containing a direct URL.
102
+ * @returns The sanitized request identity, or undefined without a valid URL.
103
+ */
104
+ function getDirectRequestIdentity(options) {
105
+ var value = options.uri || options.url;
106
+ if (typeof value !== 'string') {
107
+ return undefined;
108
+ }
109
+ try {
110
+ var requestUrl = new URL(value);
111
+ return {
112
+ host: requestUrl.host.toLowerCase() || UNKNOWN,
113
+ endPoint: getOperation(requestUrl.pathname)
114
+ };
115
+ } catch (_unused) {
116
+ return undefined;
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Uses the logical service as the host because request:start may run before the
122
+ * service interceptor resolves a physical URL. Direct-URL requests use URL.host.
123
+ * @param options Request options used to resolve the identity.
124
+ * @returns The request host and endpoint.
125
+ */
126
+ function getRequestIdentity(options) {
127
+ var directRequestIdentity = getDirectRequestIdentity(options);
128
+ var service = getService(options);
129
+ return {
130
+ host: service || (directRequestIdentity === null || directRequestIdentity === void 0 ? void 0 : directRequestIdentity.host) || UNKNOWN,
131
+ endPoint: options.resource ? getOperation(options.resource) : (directRequestIdentity === null || directRequestIdentity === void 0 ? void 0 : directRequestIdentity.endPoint) || UNKNOWN
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Returns the measured network duration when both timing points are valid.
137
+ * @param options Request options containing network timing metadata.
138
+ * @returns The duration in milliseconds, or undefined when timing is unavailable.
139
+ */
140
+ function getNetworkDuration(options) {
141
+ var _options$$timings, _options$$timings2;
142
+ var start = (_options$$timings = options.$timings) === null || _options$$timings === void 0 ? void 0 : _options$$timings.networkStart;
143
+ var end = (_options$$timings2 = options.$timings) === null || _options$$timings2 === void 0 ? void 0 : _options$$timings2.networkEnd;
144
+ return typeof start === 'number' && (0, _isFinite.default)(start) && typeof end === 'number' && (0, _isFinite.default)(end) && end >= start ? end - start : undefined;
145
+ }
146
+
147
+ /**
148
+ * Adds a completed request's network duration to its endpoint aggregate.
149
+ * @param metric Endpoint aggregate to update.
150
+ * @param options Request options containing network timing metadata.
151
+ * @returns Nothing.
152
+ */
153
+ function recordNetworkDuration(metric, options) {
154
+ var duration = getNetworkDuration(options);
155
+ if (duration === undefined) {
156
+ return;
157
+ }
158
+ metric.totalNetworkDurationMs += duration;
159
+ metric.networkDurationsMs.push(duration);
160
+ }
161
+
162
+ /**
163
+ * Returns a normalized uppercase HTTP method.
164
+ * @param method HTTP method from the request options.
165
+ * @returns The normalized method.
166
+ */
167
+ function getMethod(method) {
168
+ return typeof method === 'string' && method.length > 0 ? method.toUpperCase() : 'UNKNOWN';
169
+ }
170
+
171
+ /**
172
+ * Status zero represents a failure for which no valid HTTP response was received.
173
+ * @param reason Request failure.
174
+ * @returns A valid HTTP status code, or zero when no response was received.
175
+ */
176
+ function getStatusCode(reason) {
177
+ var _reason$statusCode;
178
+ var statusCode = (_reason$statusCode = reason.statusCode) !== null && _reason$statusCode !== void 0 ? _reason$statusCode : reason.status;
179
+ return typeof statusCode === 'number' && (0, _isInteger.default)(statusCode) && statusCode >= 100 && statusCode <= 599 ? statusCode : 0;
180
+ }
181
+
182
+ /**
183
+ * Returns an error nested in a Webex HTTP error body.
184
+ * @param reason Request failure.
185
+ * @returns The wrapped error, when present.
186
+ */
187
+ function getWrappedError(reason) {
188
+ return reason.body && (0, _typeof2.default)(reason.body) === 'object' ? reason.body : undefined;
189
+ }
190
+
191
+ /**
192
+ * Checks the timeout forms emitted by browser and Node transports.
193
+ * @param value Possible timeout signal.
194
+ * @returns Whether the value represents a timeout.
195
+ */
196
+ function isTimeoutSignal(value) {
197
+ if (typeof value !== 'string') {
198
+ return false;
199
+ }
200
+ var signal = value.toLowerCase();
201
+ return signal.includes('timeout') || signal === 'etimedout' || signal === 'esockettimedout';
202
+ }
203
+
204
+ /**
205
+ * Detects direct errors, transport errors wrapped in body, and requests whose
206
+ * measured network duration reached the configured timeout.
207
+ * @param reason Request failure.
208
+ * @param options Request options containing timeout metadata.
209
+ * @returns Whether the request timed out.
210
+ */
211
+ function isTimeoutFailure(reason, options) {
212
+ var _options$$timings3, _options$$timings4;
213
+ var wrappedError = getWrappedError(reason);
214
+ if (isTimeoutSignal(reason.name) || isTimeoutSignal(reason.code) || isTimeoutSignal(reason.message) || isTimeoutSignal(reason.body) || isTimeoutSignal(wrappedError === null || wrappedError === void 0 ? void 0 : wrappedError.name) || isTimeoutSignal(wrappedError === null || wrappedError === void 0 ? void 0 : wrappedError.code) || isTimeoutSignal(wrappedError === null || wrappedError === void 0 ? void 0 : wrappedError.message)) {
215
+ return true;
216
+ }
217
+ var start = (_options$$timings3 = options.$timings) === null || _options$$timings3 === void 0 ? void 0 : _options$$timings3.networkStart;
218
+ var end = (_options$$timings4 = options.$timings) === null || _options$$timings4 === void 0 ? void 0 : _options$$timings4.networkEnd;
219
+ return typeof options.timeout === 'number' && options.timeout > 0 && typeof start === 'number' && typeof end === 'number' && end >= start && end - start >= options.timeout;
220
+ }
221
+
222
+ /**
223
+ * Converts errors into a bounded set of categories for aggregation.
224
+ * @param reason Request failure.
225
+ * @param options Request options.
226
+ * @param statusCode Normalized status code.
227
+ * @returns The aggregate error category.
228
+ */
229
+ function getErrorType(reason, options, statusCode) {
230
+ var name = typeof reason.name === 'string' ? reason.name.toLowerCase() : '';
231
+ if (name === 'aborterror') {
232
+ return 'aborted';
233
+ }
234
+ if (statusCode === 408 || statusCode === 504 || isTimeoutFailure(reason, options)) {
235
+ return 'timeout';
236
+ }
237
+ if (statusCode === 429) {
238
+ return 'rate_limited';
239
+ }
240
+ if (statusCode >= 500) {
241
+ return 'server_error';
242
+ }
243
+ if (statusCode >= 400) {
244
+ return 'client_error';
245
+ }
246
+ return statusCode === 0 || name !== null && name !== void 0 && name.includes('network') ? 'network_error' : 'request_error';
247
+ }
248
+
249
+ /**
250
+ * Returns the most specific available error code.
251
+ * @param reason Request failure.
252
+ * @returns The error code or the unknown fallback.
253
+ */
254
+ function getErrorCode(reason) {
255
+ var wrappedError = getWrappedError(reason);
256
+ return toString(wrappedError === null || wrappedError === void 0 ? void 0 : wrappedError.errorCode) || toString(wrappedError === null || wrappedError === void 0 ? void 0 : wrappedError.code) || toString(reason.errorCode) || toString(reason.code) || UNKNOWN;
257
+ }
258
+
259
+ /**
260
+ * Returns the most specific available error message.
261
+ * @param reason Request failure.
262
+ * @returns The error message or the unknown fallback.
263
+ */
264
+ function getErrorMessage(reason) {
265
+ var wrappedError = getWrappedError(reason);
266
+ return toString(wrappedError === null || wrappedError === void 0 ? void 0 : wrappedError.message) || toString(reason.body) || toString(reason.message) || UNKNOWN;
267
+ }
268
+
269
+ /**
270
+ * Excludes the transports used to submit telemetry, preventing self-reporting.
271
+ * @param options Request options.
272
+ * @returns Whether the request should be tracked.
273
+ */
274
+ function shouldTrackNetworkRequest() {
275
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
276
+ var service = getService(options);
277
+ return service !== 'metrics' && service !== 'unifiedtelemetry';
278
+ }
279
+
280
+ /**
281
+ * Creates the error entry produced by one failed request.
282
+ * @param options Request options.
283
+ * @param reason Request failure.
284
+ * @returns The normalized error metric.
285
+ */
286
+ function createNetworkErrorMetric() {
287
+ var _options$headers;
288
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
289
+ var reason = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
290
+ var identity = getRequestIdentity(options);
291
+ var statusCode = getStatusCode(reason);
292
+ // WebexTrackingIdInterceptor stores the request ID under this lowercase key.
293
+ var trackingId = toString((_options$headers = options.headers) === null || _options$headers === void 0 ? void 0 : _options$headers.trackingid);
294
+ return _objectSpread(_objectSpread({}, identity), {}, {
295
+ statusCode: statusCode,
296
+ errorCode: getErrorCode(reason),
297
+ method: getMethod(options.method),
298
+ errorType: getErrorType(reason, options, statusCode),
299
+ errorMessage: getErrorMessage(reason),
300
+ trackingIds: trackingId ? [trackingId] : [],
301
+ countError: 1
302
+ });
303
+ }
304
+
305
+ /**
306
+ * Creates an empty ten-minute telemetry window.
307
+ * @returns An empty telemetry aggregate.
308
+ */
309
+ function createEmptyNetworkTelemetry() {
310
+ return {
311
+ metricsSummary: {
312
+ totalSendRequest: 0,
313
+ totalFailedRequest: 0,
314
+ totalRecvdResponse: 0,
315
+ totalFailedResponse: 0
316
+ },
317
+ metrics: [],
318
+ errorMetrics: [],
319
+ errorMetricsSummary: {}
320
+ };
321
+ }
322
+
323
+ /**
324
+ * Finds or creates the request counters for one host and endpoint.
325
+ * @param telemetry Current telemetry aggregate.
326
+ * @param options Request options.
327
+ * @returns The matching endpoint counters.
328
+ */
329
+ function getRequestMetric(telemetry, options) {
330
+ var identity = getRequestIdentity(options);
331
+ var _iterator = _createForOfIteratorHelper(telemetry.metrics),
332
+ _step;
333
+ try {
334
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
335
+ var _metric = _step.value;
336
+ if (_metric.host === identity.host && _metric.endPoint === identity.endPoint) {
337
+ return _metric;
338
+ }
339
+ }
340
+ } catch (err) {
341
+ _iterator.e(err);
342
+ } finally {
343
+ _iterator.f();
344
+ }
345
+ var metric = _objectSpread(_objectSpread({}, identity), {}, {
346
+ countSendRequest: 0,
347
+ countFailedRequest: 0,
348
+ countRecvdResponse: 0,
349
+ countFailedResponse: 0,
350
+ totalNetworkDurationMs: 0,
351
+ networkDurationsMs: []
352
+ });
353
+ telemetry.metrics.push(metric);
354
+ return metric;
355
+ }
356
+
357
+ /**
358
+ * Error entries aggregate only when every identifying field matches.
359
+ * @param first First error metric.
360
+ * @param second Second error metric.
361
+ * @returns Whether both errors belong to the same aggregate.
362
+ */
363
+ function isSameError(first, second) {
364
+ return first.host === second.host && first.endPoint === second.endPoint && first.statusCode === second.statusCode && first.errorCode === second.errorCode && first.method === second.method && first.errorType === second.errorType && first.errorMessage === second.errorMessage;
365
+ }
366
+
367
+ /**
368
+ * Adds a failure to its error aggregate and retains at most ten unique IDs.
369
+ * @param telemetry Current telemetry aggregate.
370
+ * @param errorMetric Error metric to record.
371
+ * @returns Nothing.
372
+ */
373
+ function recordError(telemetry, errorMetric) {
374
+ var _iterator2 = _createForOfIteratorHelper(telemetry.errorMetrics),
375
+ _step2;
376
+ try {
377
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
378
+ var existingMetric = _step2.value;
379
+ if (isSameError(existingMetric, errorMetric)) {
380
+ existingMetric.countError += 1;
381
+ var trackingId = errorMetric.trackingIds[0];
382
+ if (trackingId && existingMetric.trackingIds.length < MAX_TRACKING_IDS && !existingMetric.trackingIds.includes(trackingId)) {
383
+ existingMetric.trackingIds.push(trackingId);
384
+ }
385
+ return;
386
+ }
387
+ }
388
+ } catch (err) {
389
+ _iterator2.e(err);
390
+ } finally {
391
+ _iterator2.f();
392
+ }
393
+ telemetry.errorMetrics.push(errorMetric);
394
+ }
395
+
396
+ /**
397
+ * Returns a nearest-rank percentile from the measured durations.
398
+ * @param durations Measured network durations in milliseconds.
399
+ * @param percentile Percentile to calculate, between zero and one.
400
+ * @returns The percentile duration, or zero when no durations were measured.
401
+ */
402
+ function getDurationPercentile(durations, percentile) {
403
+ if (durations.length === 0) {
404
+ return 0;
405
+ }
406
+ var sortedDurations = (0, _toConsumableArray2.default)(durations).sort(function (first, second) {
407
+ return first - second;
408
+ });
409
+ var rank = Math.max(1, Math.ceil(sortedDurations.length * percentile));
410
+ return sortedDurations[rank - 1];
411
+ }
412
+
413
+ /**
414
+ * Converts internal duration samples into the public endpoint metric shape.
415
+ * @param metric Internal endpoint aggregate.
416
+ * @returns Endpoint aggregate with average and p90 duration statistics.
417
+ */
418
+ function serializeRequestMetric(metric) {
419
+ var measuredRequestCount = metric.networkDurationsMs.length;
420
+ return {
421
+ host: metric.host,
422
+ endPoint: metric.endPoint,
423
+ countSendRequest: metric.countSendRequest,
424
+ countFailedRequest: metric.countFailedRequest,
425
+ countRecvdResponse: metric.countRecvdResponse,
426
+ countFailedResponse: metric.countFailedResponse,
427
+ averageNetworkDurationMs: measuredRequestCount > 0 ? metric.totalNetworkDurationMs / measuredRequestCount : 0,
428
+ maxNetworkDurationP90Ms: getDurationPercentile(metric.networkDurationsMs, 0.9),
429
+ maxNetworkDurationP99Ms: getDurationPercentile(metric.networkDurationsMs, 0.99)
430
+ };
431
+ }
432
+
433
+ /**
434
+ * Converts the internal telemetry state into the submitted payload shape.
435
+ * @param telemetry Internal telemetry aggregate.
436
+ * @returns Telemetry payload with no internal duration samples.
437
+ */
438
+ function serializeNetworkTelemetry(telemetry) {
439
+ return {
440
+ metricsSummary: telemetry.metricsSummary,
441
+ metrics: telemetry.metrics.map(serializeRequestMetric),
442
+ errorMetrics: telemetry.errorMetrics,
443
+ errorMetricsSummary: telemetry.errorMetricsSummary
444
+ };
445
+ }
446
+
447
+ /**
448
+ * Checks whether the current window contains anything worth flushing.
449
+ * @param telemetry Current telemetry aggregate.
450
+ * @returns Whether the aggregate contains request data.
451
+ */
452
+ function hasNetworkTelemetry(telemetry) {
453
+ return telemetry.metrics.length > 0 || telemetry.errorMetrics.length > 0;
454
+ }
455
+
456
+ /**
457
+ * Creates a network telemetry collector and starts its periodic timer.
458
+ * @param options Submission callbacks.
459
+ * @returns The network telemetry collector.
460
+ */
461
+ function createNetworkTelemetryCollector(_ref) {
462
+ var intervalMs = _ref.intervalMs,
463
+ onSubmissionFailure = _ref.onSubmissionFailure,
464
+ submitMetric = _ref.submitMetric;
465
+ var telemetry = createEmptyNetworkTelemetry();
466
+ var inFlightSubmissions = new _set.default();
467
+
468
+ /**
469
+ * Tracks a submission until it settles so shutdown can wait for requests
470
+ * whose telemetry window has already been reset.
471
+ * @param submission Submission promise.
472
+ * @returns The tracked submission promise.
473
+ */
474
+ function trackSubmission(submission) {
475
+ inFlightSubmissions.add(submission);
476
+ submission.then(function () {
477
+ return inFlightSubmissions.delete(submission);
478
+ }, function () {
479
+ return inFlightSubmissions.delete(submission);
480
+ });
481
+ return submission;
482
+ }
483
+
484
+ /**
485
+ * Records a request being sent.
486
+ * @param options Request options.
487
+ * @returns Nothing.
488
+ */
489
+ function recordRequest() {
490
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
491
+ if (!shouldTrackNetworkRequest(options)) {
492
+ return;
493
+ }
494
+ telemetry.metricsSummary.totalSendRequest += 1;
495
+ getRequestMetric(telemetry, options).countSendRequest += 1;
496
+ }
497
+
498
+ /**
499
+ * Records a successful response.
500
+ * @param options Request options.
501
+ * @returns Nothing.
502
+ */
503
+ function recordResponse() {
504
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
505
+ if (!shouldTrackNetworkRequest(options)) {
506
+ return;
507
+ }
508
+ telemetry.metricsSummary.totalRecvdResponse += 1;
509
+ var requestMetric = getRequestMetric(telemetry, options);
510
+ requestMetric.countRecvdResponse += 1;
511
+ recordNetworkDuration(requestMetric, options);
512
+ }
513
+
514
+ /**
515
+ * A real HTTP status is a failed response. Status zero means the request
516
+ * failed before the SDK received a valid HTTP response.
517
+ * @param options Request options.
518
+ * @param reason Request failure.
519
+ * @returns Nothing.
520
+ */
521
+ function recordFailure() {
522
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
523
+ var reason = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
524
+ if (!shouldTrackNetworkRequest(options)) {
525
+ return;
526
+ }
527
+ var errorMetric = createNetworkErrorMetric(options, reason);
528
+ var requestMetric = getRequestMetric(telemetry, options);
529
+ recordNetworkDuration(requestMetric, options);
530
+ if (errorMetric.statusCode === 0) {
531
+ telemetry.metricsSummary.totalFailedRequest += 1;
532
+ requestMetric.countFailedRequest += 1;
533
+ } else {
534
+ telemetry.metricsSummary.totalRecvdResponse += 1;
535
+ telemetry.metricsSummary.totalFailedResponse += 1;
536
+ requestMetric.countRecvdResponse += 1;
537
+ requestMetric.countFailedResponse += 1;
538
+ }
539
+ recordError(telemetry, errorMetric);
540
+ var statusCode = String(errorMetric.statusCode);
541
+ var statusSummary = telemetry.errorMetricsSummary[statusCode];
542
+ if (statusSummary) {
543
+ statusSummary.count += 1;
544
+ } else {
545
+ telemetry.errorMetricsSummary[statusCode] = {
546
+ count: 1
547
+ };
548
+ }
549
+ }
550
+
551
+ /**
552
+ * Submits the completed window and starts collecting the next one.
553
+ * @returns A promise that settles after submission succeeds or is reported as failed.
554
+ */
555
+ function submitSummary() {
556
+ var completedTelemetry = telemetry;
557
+ var payload = serializeNetworkTelemetry(completedTelemetry);
558
+
559
+ // Reset before submission so requests emitted by the telemetry transport,
560
+ // or while it is pending, cannot mutate the completed window.
561
+ telemetry = createEmptyNetworkTelemetry();
562
+ try {
563
+ var submission = _promise.default.resolve(submitMetric(NETWORK_REQUEST_SUMMARY_METRIC, {
564
+ type: 'operational',
565
+ tags: {},
566
+ fields: completedTelemetry.metricsSummary,
567
+ eventPayload: payload
568
+ })).then(function () {
569
+ return undefined;
570
+ }, function () {
571
+ onSubmissionFailure();
572
+ });
573
+ return trackSubmission(submission);
574
+ } catch (_unused2) {
575
+ // submitMetric may throw before returning a promise.
576
+ onSubmissionFailure();
577
+ return _promise.default.resolve();
578
+ }
579
+ }
580
+ var telemetryIntervalMs = typeof intervalMs === 'number' && (0, _isFinite.default)(intervalMs) && intervalMs > 0 ? intervalMs : NETWORK_TELEMETRY_INTERVAL_MS;
581
+ var telemetryInterval = (0, _commonTimers.safeSetInterval)(submitSummary, telemetryIntervalMs);
582
+
583
+ /**
584
+ * Submits the current non-empty window immediately.
585
+ * @returns A promise that settles after submission succeeds or is reported as failed.
586
+ */
587
+ function flush() {
588
+ return hasNetworkTelemetry(telemetry) ? submitSummary() : _promise.default.resolve();
589
+ }
590
+
591
+ /**
592
+ * Stops periodic submission and releases data from the current window.
593
+ * @returns A promise that settles after the final window is submitted or failure is reported.
594
+ */
595
+ function stop() {
596
+ clearInterval(telemetryInterval);
597
+ flush();
598
+ return _promise.default.all(inFlightSubmissions).then(function () {
599
+ return undefined;
600
+ });
601
+ }
602
+ return {
603
+ recordRequest: recordRequest,
604
+ recordResponse: recordResponse,
605
+ recordFailure: recordFailure,
606
+ flush: flush,
607
+ stop: stop
608
+ };
609
+ }
610
+ //# sourceMappingURL=network-telemetry.js.map