{"version":3,"file":"lcp.js","sources":["../../../src/metrics/lcp.ts"],"sourcesContent":["import type { Client, SpanAttributes } from '@sentry/core';\nimport {\n browserPerformanceTimeOrigin,\n debug,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_EXCLUSIVE_TIME,\n SEMANTIC_ATTRIBUTE_SENTRY_MEASUREMENT_UNIT,\n SEMANTIC_ATTRIBUTE_SENTRY_MEASUREMENT_VALUE,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { htmlTreeAsString } from '../htmlTreeAsString';\nimport { addLcpInstrumentationHandler } from './instrument';\nimport type { WebVitalReportEvent } from './utils';\nimport { listenForWebVitalReportEvents, msToSec, startStandaloneWebVitalSpan, supportsWebVital } from './utils';\n\n/**\n * 60 seconds is the maximum for a plausible LCP value.\n */\nexport const MAX_PLAUSIBLE_LCP_DURATION = 60_000;\n\nexport function isValidLcpMetric(lcpValue: number | undefined): lcpValue is number {\n return lcpValue != null && lcpValue > 0 && lcpValue <= MAX_PLAUSIBLE_LCP_DURATION;\n}\n\n/**\n * Starts tracking the Largest Contentful Paint on the current page and collects the value once\n *\n * - the page visibility is hidden\n * - a navigation span is started (to stop LCP measurement for SPA soft navigations)\n *\n * Once either of these events triggers, the LCP value is sent as a standalone span and we stop\n * measuring LCP for subsequent routes.\n */\nexport function trackLcpAsStandaloneSpan(client: Client): void {\n let standaloneLcpValue = 0;\n let standaloneLcpEntry: LargestContentfulPaint | undefined;\n\n if (!supportsWebVital('largest-contentful-paint')) {\n return;\n }\n\n const cleanupLcpHandler = addLcpInstrumentationHandler(({ metric }) => {\n const entry = metric.entries[metric.entries.length - 1] as LargestContentfulPaint | undefined;\n if (!entry || !isValidLcpMetric(metric.value)) {\n return;\n }\n standaloneLcpValue = metric.value;\n standaloneLcpEntry = entry;\n }, true);\n\n listenForWebVitalReportEvents(client, (reportEvent, pageloadSpanId) => {\n _sendStandaloneLcpSpan(standaloneLcpValue, standaloneLcpEntry, pageloadSpanId, reportEvent);\n cleanupLcpHandler();\n });\n}\n\n/**\n * Exported only for testing!\n */\nexport function _sendStandaloneLcpSpan(\n lcpValue: number,\n entry: LargestContentfulPaint | undefined,\n pageloadSpanId: string,\n reportEvent: WebVitalReportEvent,\n) {\n if (!isValidLcpMetric(lcpValue)) {\n return;\n }\n\n DEBUG_BUILD && debug.log(`Sending LCP span (${lcpValue})`);\n\n const startTime = msToSec((browserPerformanceTimeOrigin() || 0) + (entry?.startTime || 0));\n const routeName = getCurrentScope().getScopeData().transactionName;\n\n const name = entry ? htmlTreeAsString(entry.element) : 'Largest contentful paint';\n\n const attributes: SpanAttributes = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.browser.lcp',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.webvital.lcp',\n [SEMANTIC_ATTRIBUTE_EXCLUSIVE_TIME]: 0, // LCP is a point-in-time metric\n // attach the pageload span id to the LCP span so that we can link them in the UI\n 'sentry.pageload.span_id': pageloadSpanId,\n // describes what triggered the web vital to be reported\n 'sentry.report_event': reportEvent,\n };\n\n if (entry) {\n entry.element && (attributes['lcp.element'] = htmlTreeAsString(entry.element));\n entry.id && (attributes['lcp.id'] = entry.id);\n\n entry.url && (attributes['lcp.url'] = entry.url);\n\n // loadTime is the time of LCP that's related to receiving the LCP element response..\n entry.loadTime != null && (attributes['lcp.loadTime'] = entry.loadTime);\n\n // renderTime is loadTime + rendering time\n // it's 0 if the LCP element is loaded from a 3rd party origin that doesn't send the\n // `Timing-Allow-Origin` header.\n entry.renderTime != null && (attributes['lcp.renderTime'] = entry.renderTime);\n\n entry.size != null && (attributes['lcp.size'] = entry.size);\n }\n\n const span = startStandaloneWebVitalSpan({\n name,\n transaction: routeName,\n attributes,\n startTime,\n });\n\n if (span) {\n span.addEvent('lcp', {\n [SEMANTIC_ATTRIBUTE_SENTRY_MEASUREMENT_UNIT]: 'millisecond',\n [SEMANTIC_ATTRIBUTE_SENTRY_MEASUREMENT_VALUE]: lcpValue,\n });\n\n // LCP is a point-in-time metric, so we end the span immediately\n span.end(startTime);\n }\n}\n"],"names":["supportsWebVital","addLcpInstrumentationHandler","listenForWebVitalReportEvents","DEBUG_BUILD","debug","msToSec","browserPerformanceTimeOrigin","getCurrentScope","htmlTreeAsString","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_EXCLUSIVE_TIME","startStandaloneWebVitalSpan","SEMANTIC_ATTRIBUTE_SENTRY_MEASUREMENT_UNIT","SEMANTIC_ATTRIBUTE_SENTRY_MEASUREMENT_VALUE"],"mappings":"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"}