<commit_before>/* eslint-disable max-lines */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Measurements, SpanContext } from '@sentry/types';
import { browserPerformanceTimeOrigin, getGlobalObject, htmlTreeAsString, isNodeEnv, logger } from '@sentry/utils';

import { Span } from '../span';
import { Transaction } from '../transaction';
import { msToSec } from '../utils';
import { getCLS, LayoutShift } from './web-vitals/getCLS';
import { getFID } from './web-vitals/getFID';
import { getLCP, LargestContentfulPaint } from './web-vitals/getLCP';
import { getFirstHidden } from './web-vitals/lib/getFirstHidden';
import { NavigatorDeviceMemory, NavigatorNetworkInformation } from './web-vitals/types';

const global = getGlobalObject<Window>();

/** Class tracking metrics  */
export class MetricsInstrumentation {
  private _measurements: Measurements = {};

  private _performanceCursor: number = 0;
  private _lcpEntry: LargestContentfulPaint | undefined;
  private _clsEntry: LayoutShift | undefined;

  public constructor() {
    if (!isNodeEnv() && global?.performance) {
      if (global.performance.mark) {
        global.performance.mark('sentry-tracing-init');
      }

      this._trackCLS();
      this._trackLCP();
      this._trackFID();
    }
  }

  /** Add performance related spans to a transaction */
  public addPerformanceEntries(transaction: Transaction): void {
    if (!global || !global.performance || !global.performance.getEntries || !browserPerformanceTimeOrigin) {
      // Gatekeeper if performance API not available
      return;
    }

    logger.log('[Tracing] Adding & adjusting spans using Performance API');

    const timeOrigin = msToSec(browserPerformanceTimeOrigin);
    let entryScriptSrc: string | undefined;

    if (global.document) {
      // eslint-disable-next-line @typescript-eslint/prefer-for-of
      for (let 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;
        }
      }
    }

    let entryScriptStartTimestamp: number | undefined;
    let tracingInitMarkStartTime: number | undefined;
    let responseStartTimestamp: number | undefined;
    let requestStartTimestamp: number | undefined;

    global.performance
      .getEntries()
      .slice(this._performanceCursor)
      .forEach((entry: Record<string, any>) => {
        const startTime = msToSec(entry.startTime as number);
        const duration = msToSec(entry.duration as number);

        if (transaction.op === 'navigation' && timeOrigin + startTime < transaction.startTimestamp) {<commit_msg>fix(tracing): Add check for document.scripts in metrics (#3766)<commit_after>/* eslint-disable max-lines */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Measurements, SpanContext } from '@sentry/types';
import { browserPerformanceTimeOrigin, getGlobalObject, htmlTreeAsString, isNodeEnv, logger } from '@sentry/utils';

import { Span } from '../span';
import { Transaction } from '../transaction';
import { msToSec } from '../utils';
import { getCLS, LayoutShift } from './web-vitals/getCLS';
import { getFID } from './web-vitals/getFID';
import { getLCP, LargestContentfulPaint } from './web-vitals/getLCP';
import { getFirstHidden } from './web-vitals/lib/getFirstHidden';
import { NavigatorDeviceMemory, NavigatorNetworkInformation } from './web-vitals/types';

const global = getGlobalObject<Window>();

/** Class tracking metrics  */
export class MetricsInstrumentation {
  private _measurements: Measurements = {};

  private _performanceCursor: number = 0;
  private _lcpEntry: LargestContentfulPaint | undefined;
  private _clsEntry: LayoutShift | undefined;

  public constructor() {
    if (!isNodeEnv() && global?.performance) {
      if (global.performance.mark) {
        global.performance.mark('sentry-tracing-init');
      }

      this._trackCLS();
      this._trackLCP();
      this._trackFID();
    }
  }

  /** Add performance related spans to a transaction */
  public addPerformanceEntries(transaction: Transaction): void {
    if (!global || !global.performance || !global.performance.getEntries || !browserPerformanceTimeOrigin) {
      // Gatekeeper if performance API not available
      return;
    }

    logger.log('[Tracing] Adding & adjusting spans using Performance API');

    const timeOrigin = msToSec(browserPerformanceTimeOrigin);
    let entryScriptSrc: string | undefined;

    if (global.document && global.document.scripts) {
      // eslint-disable-next-line @typescript-eslint/prefer-for-of
      for (let i = 0; i < global.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 (global.document.scripts[i].dataset.entry === 'true') {
          entryScriptSrc = global.document.scripts[i].src;
          break;
        }
      }
    }

    let entryScriptStartTimestamp: number | undefined;
    let tracingInitMarkStartTime: number | undefined;
    let responseStartTimestamp: number | undefined;
    let requestStartTimestamp: number | undefined;

    global.performance
      .getEntries()
      .slice(this._performanceCursor)
      .forEach((entry: Record<string, any>) => {
        const startTime = msToSec(entry.startTime as number);
        const duration = msToSec(entry.duration as number);

        if (transaction.op === 'navigation' && timeOrigin + startTime < transaction.startTimestamp) {
{'fraction_non_alphanumeric': 0.11934156378600823, 'fraction_numerical': 0.0013717421124828531, 'mean_word_length': 3.5007716049382718, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4612384', 'n_tokens_mistral': 1644, 'n_tokens_neox': 1579, 'n_words': 426}