text: Object.defineProperty(exports, "__esModule", { value: true });
  var tslib_1 = require("tslib");
  var utils_1 = require("@sentry/utils");
  var utils_2 = require("../utils");
  var getCLS_1 = require("./web-vitals/getCLS");
  var getFID_1 = require("./web-vitals/getFID");
  var getLCP_1 = require("./web-vitals/getLCP");
  var getTTFB_1 = require("./web-vitals/getTTFB");
  var global = utils_1.getGlobalObject();
  /** Class tracking metrics  */
  var MetricsInstrumentation = /** @class */ (function () {
      function MetricsInstrumentation() {
          this._measurements = {};
          this._performanceCursor = 0;
          if (global && global.performance) {
              if (global.performance.mark) {
                  global.performance.mark('sentry-tracing-init');
              }
              this._trackCLS();
              this._trackLCP();
              this._trackFID();
              this._trackTTFB();
          }
      }
      /** Add performance related spans to a transaction */
      MetricsInstrumentation.prototype.addPerformanceEntries = function (transaction) {
          var _this = this;
          if (!global || !global.performance || !global.performance.getEntries || !utils_1.browserPerformanceTimeOrigin) {
              // Gatekeeper if performance API not available
              return;
          }
          utils_1.logger.log('[Tracing] Adding & adjusting spans using Performance API');
          var timeOrigin = utils_2.msToSec(utils_1.browserPerformanceTimeOrigin);
          var entryScriptSrc;
          if (global.document) {
              // eslint-disable-next-line @typescript-eslint/prefer-for-of
              for (var i = 0; i < document.scripts.length; i++) {
                  // We go through all scripts on the page and look for 'data-entry'
                  // We remember the name and measure the time between this script finished loading and
                  // our mark 'sentry-tracing-init'
                  if (document.scripts[i].dataset.entry === 'true') {
                      entryScriptSrc = document.scripts[i].src;
                      break;
                  }
              }
          }
          var entryScriptStartTimestamp;
          var tracingInitMarkStartTime;
          global.performance
              .getEntries()
              .slice(this._performanceCursor)
              .forEach(function (entry) {
              var startTime = utils_2.msToSec(entry.startTime);
              var duration = utils_2.msToSec(entry.duration);
              if (transaction.op === 'navigation' && timeOrigin + startTime < transaction.startTimestamp) {
                  return;
              }
              switch (entry.entryType) {
                  case 'navigation':
                      addNavigationSpans(transaction, entry, timeOrigin);
                      break;
                  case 'mark':
                  case 'paint':
                  case 'measure': {
                      var startTimestamp = addMeasureSpans(transaction, entry, startTime, duration, timeOrigin);
                      if (tracingInitMarkStartTime === undefined && entry.name === 'sentry-tracing-init') {
                          tracingInitMarkStartTime = startTimestamp;
                      }
                      // capture web vitals
                      if (entry.name === 'first-paint') {
                          utils_1.logger.log('[Measurements] Adding FP');
                          _this._measurements['fp'] = { value: entry.startTime };
                          _this._measurements['mark.fp'] = { value: startTimestamp };
                      }
                      if (entry.name === 'first-contentful-paint') {
                          utils_1.logger.log('[Measurements] Adding FCP');
                          _this._measurements['fcp'] = { value: entry.startTime };
                          _this._measurements['mark.fcp'] = { value: startTimestamp };
                      }
                      break;
                  }
                  case 'resource': {
                      var resourceName = entry.name.replace(window.location.origin, '');
                      var endTimestamp = addResourceSpans(transaction, entry, resourceName, startTime, duration, timeOrigin);
                      // We remember the entry script end time to calculate the difference to the first init mark
                      if (entryScriptStartTimestamp === undefined && (entryScriptSrc || '').indexOf(resourceName) > -1) {
                          entryScriptStartTimestamp = endTimestamp;
                      }
                      break;
                  }
                  default:
                  // Ignore other entry types.
              }
          });
          if (entryScriptStartTimestamp !== undefined && tracingInitMarkStartTime !== undefined) {
              _startChild(transaction, {
                  description: 'evaluation',
                  endTimestamp: tracingInitMarkStartTime,
                  op: 'script',
                  startTimestamp: entryScriptStartTimestamp,
              });
          }
          this._performanceCursor = Math.max(performance.getEntries().length - 1, 0);
          // Measurements are only available for pageload transactions
          if (transaction.op === 'pageload') {
              transaction.setMeasurements(this._measurements);
          }
      };
      /** Starts tracking the Cumulative Layout Shift on the current page. */
      MetricsInstrumentation.prototype._trackCLS = function () {
          var _this = this;
          getCLS_1.getCLS(function (metric) {
              var entry = metric.entries.pop();
              if (!entry) {
                  return;
              }
              utils_1.logger.log('[Measurements] Adding CLS');
              _this._measurements['cls'] = { value: metric.value };
          });
      };
      /** Starts tracking the Largest Contentful Paint on the current page. */
      MetricsInstrumentation.prototype._trackLCP = function () {
          var _this = this;
          getLCP_1.getLCP(function (metric) {
              var entry = metric.entries.pop();
              if (!entry) {
                  return;
              }
              var timeOrigin = utils_2.msToSec(performance.timeOrigin);
              var startTime = utils_2.msToSec(entry.startTime);
              utils_1.logger.log('[Measurements] Adding LCP');
              _this._measurements['lcp'] = { value: metric.value };
              _this._measurements['mark.lcp'] = { value: timeOrigin + startTime };
          });
      };
      /** Starts tracking the First Input Delay on the current page. */
      MetricsInstrumentation.prototype._trackFID = function () {
          var _this = this;
          getFID_1.getFID(function (metric) {
              var entry = metric.entries.pop();
              if (!entry) {
                  return;
              }
              var timeOrigin = utils_2.msToSec(performance.timeOrigin);
              var startTime = utils_2.msToSec(entry.startTime);
              utils_1.logger.log('[Measurements] Adding FID');
              _this._measurements['fid'] = { value: metric.value };
              _this._measurements['mark.fid'] = { value: timeOrigin + startTime };
          });
      };
      /** Starts tracking the Time to First Byte on the current page. */
      MetricsInstrumentation.prototype._trackTTFB = function () {
          var _this = this;
          getTTFB_1.getTTFB(function (metric) {
              var _a;
              var entry = metric.entries.pop();
              if (!entry) {
                  return;
              }
              utils_1.logger.log('[Measurements] Adding TTFB');
              _this._measurements['ttfb'] = { value: metric.value };
              // Capture the time spent making the request and receiving the first byte of the response
              var requestTime = metric.value - (_a = metric.entries[0], (_a !== null && _a !== void 0 ? _a : entry)).requestStart;
              _this._measurements['ttfb.requestTime'] = { value: requestTime };
          });
      };
      return MetricsInstrumentation;
  }());
  exports.MetricsInstrumentation = MetricsInstrumentation;
  /** Instrument navigation entries */
  function addNavigationSpans(transaction, entry, timeOrigin) {
      addPerformanceNavigationTiming(transaction, entry, 'unloadEvent', timeOrigin);
      addPerformanceNavigationTiming(transaction, entry, 'domContentLoadedEvent', timeOrigin);
      addPerformanceNavigationTiming(transaction, entry, 'loadEvent', timeOrigin);
      addPerformanceNavigationTiming(transaction, entry, 'connect', timeOrigin);
      addPerformanceNavigationTiming(transaction, entry, 'domainLookup', timeOrigin);
      addRequest(transaction, entry, timeOrigin);
  }
  /** Create measure related spans */
  function addMeasureSpans(transaction, entry, startTime, duration, timeOrigin) {
      var measureStartTimestamp = timeOrigin + startTime;
      var measureEndTimestamp = measureStartTimestamp + duration;
      _startChild(transaction, {
          description: entry.name,
          endTimestamp: measureEndTimestamp,
          op: entry.entryType,
          startTimestamp: measureStartTimestamp,
      });
      return measureStartTimestamp;
  }
  /** Create resource related spans */
  function addResourceSpans(transaction, entry, resourceName, startTime, duration, timeOrigin) {
      // we already instrument based on fetch and xhr, so we don't need to
      // duplicate spans here.
      if (entry.initiatorType === 'xmlhttprequest' || entry.initiatorType === 'fetch') {
          return undefined;
      }
      var data = {};
      if ('transferSize' in entry) {
          data['Transfer Size'] = entry.transferSize;
      }
      if ('encodedBodySize' in entry) {
          data['Encoded Body Size'] = entry.encodedBodySize;
      }
      if ('decodedBodySize' in entry) {
          data['Decoded Body Size'] = entry.decodedBodySize;
      }
      var startTimestamp = timeOrigin + startTime;
      var endTimestamp = startTimestamp + duration;
      _startChild(transaction, {
          description: resourceName,
          endTimestamp: endTimestamp,
          op: entry.initiatorType ? "resource." + entry.initiatorType : 'resource',
          startTimestamp: startTimestamp,
          data: data,
      });
      return endTimestamp;
  }
  exports.addResourceSpans = addResourceSpans;
  /** Create performance navigation related spans */
  function addPerformanceNavigationTiming(transaction, entry, event, timeOrigin) {
      var end = entry[event + "End"];
      var start = entry[event + "Start"];
      if (!start || !end) {
          return;
      }
      _startChild(transaction, {
          description: event,
          endTimestamp: timeOrigin + utils_2.msToSec(end),
          op: 'browser',
          startTimestamp: timeOrigin + utils_2.msToSec(start),
      });
  }
  /** Create request and response related spans */
  function addRequest(transaction, entry, timeOrigin) {
      _startChild(transaction, {
          description: 'request',
          endTimestamp: timeOrigin + utils_2.msToSec(entry.responseEnd),
          op: 'browser',
          startTimestamp: timeOrigin + utils_2.msToSec(entry.requestStart),
      });
      _startChild(transaction, {
          description: 'response',
          endTimestamp: timeOrigin + utils_2.msToSec(entry.responseEnd),
          op: 'browser',
          startTimestamp: timeOrigin + utils_2.msToSec(entry.responseStart),
      });
  }
  /**
   * Helper function to start child on transactions. This function will make sure that the transaction will
   * use the start timestamp of the created child span if it is earlier than the transactions actual
   * start timestamp.
   */
  function _startChild(transaction, _a) {
      var startTimestamp = _a.startTimestamp, ctx = tslib_1.__rest(_a, ["startTimestamp"]);
      if (startTimestamp && transaction.startTimestamp > startTimestamp) {
          transaction.startTimestamp = startTimestamp;
      }
      return transaction.startChild(tslib_1.__assign({ startTimestamp: startTimestamp }, ctx));
  }
  exports._startChild = _startChild;
  //# sourceMappingURL=metrics.js.map