@sentry/browser-utils 11.0.0-rc.0 → 11.0.0
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.
- package/build/cjs/instrumentation/performanceObserver.js.map +1 -1
- package/build/cjs/performance/entries.js +33 -22
- package/build/cjs/performance/entries.js.map +1 -1
- package/build/cjs/performance/interactions.js +38 -11
- package/build/cjs/performance/interactions.js.map +1 -1
- package/build/cjs/web-vitals/emitSpan.js +1 -14
- package/build/cjs/web-vitals/emitSpan.js.map +1 -1
- package/build/cjs/web-vitals/inp.js +1 -0
- package/build/cjs/web-vitals/inp.js.map +1 -1
- package/build/cjs/web-vitals/reportEvents.js +4 -4
- package/build/cjs/web-vitals/reportEvents.js.map +1 -1
- package/build/cjs/web-vitals/softNavs.js +25 -5
- package/build/cjs/web-vitals/softNavs.js.map +1 -1
- package/build/cjs/web-vitals/spans.js +61 -29
- package/build/cjs/web-vitals/spans.js.map +1 -1
- package/build/cjs/web-vitals/tracking.js +7 -0
- package/build/cjs/web-vitals/tracking.js.map +1 -1
- package/build/esm/instrumentation/performanceObserver.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/performance/entries.js +35 -25
- package/build/esm/performance/entries.js.map +1 -1
- package/build/esm/performance/interactions.js +40 -13
- package/build/esm/performance/interactions.js.map +1 -1
- package/build/esm/web-vitals/emitSpan.js +1 -14
- package/build/esm/web-vitals/emitSpan.js.map +1 -1
- package/build/esm/web-vitals/inp.js +1 -1
- package/build/esm/web-vitals/inp.js.map +1 -1
- package/build/esm/web-vitals/reportEvents.js +4 -4
- package/build/esm/web-vitals/reportEvents.js.map +1 -1
- package/build/esm/web-vitals/softNavs.js +25 -5
- package/build/esm/web-vitals/softNavs.js.map +1 -1
- package/build/esm/web-vitals/spans.js +54 -22
- package/build/esm/web-vitals/spans.js.map +1 -1
- package/build/esm/web-vitals/tracking.js +8 -1
- package/build/esm/web-vitals/tracking.js.map +1 -1
- package/build/types/instrumentation/performanceObserver.d.ts +1 -2
- package/build/types/instrumentation/performanceObserver.d.ts.map +1 -1
- package/build/types/performance/entries.d.ts +6 -1
- package/build/types/performance/entries.d.ts.map +1 -1
- package/build/types/performance/interactions.d.ts.map +1 -1
- package/build/types/web-vitals/emitSpan.d.ts +0 -2
- package/build/types/web-vitals/emitSpan.d.ts.map +1 -1
- package/build/types/web-vitals/inp.d.ts +2 -0
- package/build/types/web-vitals/inp.d.ts.map +1 -1
- package/build/types/web-vitals/reportEvents.d.ts +4 -7
- package/build/types/web-vitals/reportEvents.d.ts.map +1 -1
- package/build/types/web-vitals/softNavs.d.ts.map +1 -1
- package/build/types/web-vitals/spans.d.ts +2 -3
- package/build/types/web-vitals/spans.d.ts.map +1 -1
- package/build/types/web-vitals/tracking.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -12,6 +12,7 @@ const utils$1 = require('./utils.js');
|
|
|
12
12
|
let _measurements = {};
|
|
13
13
|
let _lcpEntry;
|
|
14
14
|
let _clsEntry;
|
|
15
|
+
let _navigationType;
|
|
15
16
|
function startTrackingWebVitals({ trackCls, trackLcp }) {
|
|
16
17
|
const performance = utils.getBrowserPerformanceAPI();
|
|
17
18
|
if (performance && core.browserPerformanceTimeOrigin()) {
|
|
@@ -52,6 +53,7 @@ function _trackLCP() {
|
|
|
52
53
|
}
|
|
53
54
|
function _trackTtfb() {
|
|
54
55
|
return performanceObserver.addTtfbInstrumentationHandler(({ metric }) => {
|
|
56
|
+
_navigationType = metric.navigationType;
|
|
55
57
|
const entry = metric.entries[metric.entries.length - 1];
|
|
56
58
|
if (!entry) {
|
|
57
59
|
return;
|
|
@@ -61,6 +63,7 @@ function _trackTtfb() {
|
|
|
61
63
|
}
|
|
62
64
|
function _trackFcp() {
|
|
63
65
|
return performanceObserver.addFcpInstrumentationHandler(({ metric }) => {
|
|
66
|
+
_navigationType = metric.navigationType;
|
|
64
67
|
_measurements["fcp"] = { value: metric.value, unit: "millisecond" };
|
|
65
68
|
});
|
|
66
69
|
}
|
|
@@ -111,6 +114,9 @@ function addWebVitalsToSpan(span, options) {
|
|
|
111
114
|
});
|
|
112
115
|
_setWebVitalAttributes(span, options);
|
|
113
116
|
}
|
|
117
|
+
if (_navigationType) {
|
|
118
|
+
span.setAttribute(attributes.BROWSER_NAVIGATION_TYPE, _navigationType);
|
|
119
|
+
}
|
|
114
120
|
span.setAttribute(spanStreamingEnabled ? "browser.performance.time_origin" : "performance.timeOrigin", timeOrigin);
|
|
115
121
|
span.setAttribute(
|
|
116
122
|
spanStreamingEnabled ? "browser.performance.navigation.activation_start" : "performance.activationStart",
|
|
@@ -122,6 +128,7 @@ function addWebVitalsToSpan(span, options) {
|
|
|
122
128
|
function resetWebVitalState() {
|
|
123
129
|
_lcpEntry = void 0;
|
|
124
130
|
_clsEntry = void 0;
|
|
131
|
+
_navigationType = void 0;
|
|
125
132
|
_measurements = {};
|
|
126
133
|
}
|
|
127
134
|
function _setWebVitalAttributes(span, options) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracking.js","sources":["../../../src/web-vitals/tracking.ts"],"sourcesContent":["import type { Client, Measurements, Span } from '@sentry/core';\nimport { browserPerformanceTimeOrigin, debug, setMeasurement, spanToJSON } from '@sentry/core';\nimport { SENTRY_OP } from '@sentry/conventions/attributes';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { htmlTreeAsString } from '../htmlTreeAsString';\nimport {\n addClsInstrumentationHandler,\n addFcpInstrumentationHandler,\n addLcpInstrumentationHandler,\n addPerformanceInstrumentationHandler,\n addTtfbInstrumentationHandler,\n} from '../instrumentation/performanceObserver';\nimport { getBrowserPerformanceAPI, msToSec } from '../performance/utils';\nimport { isValidLcpMetric } from './lcp';\nimport { getActivationStart, getNavigationEntry, getVisibilityWatcher } from './utils';\n\nlet _measurements: Measurements = {};\nlet _lcpEntry: LargestContentfulPaint | undefined;\nlet _clsEntry: LayoutShift | undefined;\n\ninterface StartTrackingWebVitalsOptions {\n trackCls: boolean;\n trackLcp: boolean;\n client: Client;\n}\n\n/**\n * Start tracking web vitals.\n * The callback returned by this function can be used to stop tracking & ensure all measurements are final & captured.\n *\n * @returns A function that forces web vitals collection\n */\nexport function startTrackingWebVitals({ trackCls, trackLcp }: StartTrackingWebVitalsOptions): () => void {\n const performance = getBrowserPerformanceAPI();\n if (performance && browserPerformanceTimeOrigin()) {\n const lcpCleanupCallback = trackLcp ? _trackLCP() : undefined;\n const clsCleanupCallback = trackCls ? _trackCLS() : undefined;\n const ttfbCleanupCallback = _trackTtfb();\n const fcpCleanupCallback = _trackFcp();\n const fpCleanupCallback = _trackFp();\n\n return (): void => {\n ttfbCleanupCallback();\n fcpCleanupCallback();\n fpCleanupCallback();\n lcpCleanupCallback?.();\n clsCleanupCallback?.();\n };\n }\n\n return () => undefined;\n}\n\nexport { registerInpInteractionListener } from './inp';\n\n/**\n * Starts tracking the Cumulative Layout Shift on the current page and collects the value and last entry\n * to the `_measurements` object which ultimately is applied to the pageload span's measurements.\n */\nfunction _trackCLS(): () => void {\n return addClsInstrumentationHandler(({ metric }) => {\n const entry = metric.entries[metric.entries.length - 1] as LayoutShift | undefined;\n if (!entry) {\n return;\n }\n _measurements['cls'] = { value: metric.value, unit: '' };\n _clsEntry = entry;\n }, true);\n}\n\n/** Starts tracking the Largest Contentful Paint on the current page. */\nfunction _trackLCP(): () => void {\n return addLcpInstrumentationHandler(({ metric }) => {\n const entry = metric.entries[metric.entries.length - 1];\n if (!entry || !isValidLcpMetric(metric.value)) {\n return;\n }\n\n _measurements['lcp'] = { value: metric.value, unit: 'millisecond' };\n _lcpEntry = entry as LargestContentfulPaint;\n }, true);\n}\n\nfunction _trackTtfb(): () => void {\n return addTtfbInstrumentationHandler(({ metric }) => {\n const entry = metric.entries[metric.entries.length - 1];\n if (!entry) {\n return;\n }\n\n _measurements['ttfb'] = { value: metric.value, unit: 'millisecond' };\n });\n}\n\n/** Starts tracking the First Contentful Paint on the current page. */\nfunction _trackFcp(): () => void {\n return addFcpInstrumentationHandler(({ metric }) => {\n _measurements['fcp'] = { value: metric.value, unit: 'millisecond' };\n });\n}\n\n/**\n * Starts tracking First Paint on the current page.\n *\n * web-vitals has no `onFP`, so this stays on the raw paint observer, and mirrors the one thing\n * `onFCP` does inline: skip the vital if the page was hidden before it. The raw `startTime` is\n * stored as-is; rebasing it against `activationStart` happens in `_rebaseFpAgainstActivationStart`\n * when the span ends, because this observer can run before the page is activated.\n */\nfunction _trackFp(): () => void {\n return addPerformanceInstrumentationHandler('paint', ({ entries }) => {\n const firstHidden = getVisibilityWatcher();\n for (const entry of entries) {\n if (entry.name === 'first-paint' && entry.startTime < firstHidden.firstHiddenTime) {\n _measurements['fp'] = { value: entry.startTime, unit: 'millisecond' };\n }\n }\n });\n}\n\ninterface AddWebVitalsToSpanOptions {\n /**\n * Flag to determine if CLS should be recorded as a measurement on the pageload span or\n * sent as a standalone span instead.\n * Sending it as a standalone span will yield more accurate LCP values.\n *\n * Default: `false` for backwards compatibility.\n */\n recordClsOnPageloadSpan: boolean;\n\n /**\n * Flag to determine if LCP should be recorded as a measurement on the pageload span or\n * sent as a standalone span instead.\n * Sending it as a standalone span will yield more accurate LCP values.\n *\n * Default: `false` for backwards compatibility.\n */\n recordLcpOnPageloadSpan: boolean;\n\n /**\n * Whether span streaming is enabled.\n */\n spanStreamingEnabled?: boolean;\n}\n\n/**\n * Writes the collected web vitals (LCP, CLS, INP, TTFB, FP, FCP) onto the pageload span,\n * either as measurements/attributes (v1) or as web vital attributes (span streaming).\n *\n * This should be called when the pageload span ends, after the web vitals have been finalized.\n * It is a no-op for non-pageload spans, but always resets the collected web vital state so it\n * doesn't leak into a subsequent navigation.\n */\nexport function addWebVitalsToSpan(span: Span, options: AddWebVitalsToSpanOptions): void {\n const origin = browserPerformanceTimeOrigin();\n if (!getBrowserPerformanceAPI()?.getEntries || !origin) {\n // Gatekeeper if performance API not available\n resetWebVitalState();\n return;\n }\n\n const { spanStreamingEnabled, recordClsOnPageloadSpan, recordLcpOnPageloadSpan } = options;\n const timeOrigin = msToSec(origin);\n\n // Measurements are only available for pageload transactions\n if (spanToJSON(span).attributes[SENTRY_OP] === 'pageload') {\n _addTtfbRequestTimeToMeasurements(_measurements);\n _rebaseFpAgainstActivationStart(_measurements);\n\n if (spanStreamingEnabled) {\n const setAttr = (shortWebVitalName: string, value: number, customAttrName?: string) => {\n const attrKey = customAttrName ?? `browser.web_vital.${shortWebVitalName}.value`;\n span.setAttribute(attrKey, value);\n DEBUG_BUILD && debug.log('Setting web vital attribute', { [attrKey]: value }, 'on pageload span');\n };\n // for streamed pageload spans, we add the web vital measurements as attributes.\n // We omit LCP, CLS and INP because they're tracked separately as spans\n ['ttfb', 'fp', 'fcp'].forEach(measurementName => {\n if (_measurements[measurementName]) {\n setAttr(measurementName, _measurements[measurementName].value);\n }\n });\n if (_measurements['ttfb.requestTime']) {\n setAttr('ttfb.requestTime', _measurements['ttfb.requestTime'].value, 'browser.web_vital.ttfb.request_time');\n }\n } else {\n // If CLS is tracked as a span (span streaming), don't record CLS as a measurement\n if (!recordClsOnPageloadSpan) {\n delete _measurements.cls;\n }\n\n // If LCP is tracked as a span (span streaming), don't record LCP as a measurement\n if (!recordLcpOnPageloadSpan) {\n delete _measurements.lcp;\n }\n\n Object.entries(_measurements).forEach(([measurementName, measurement]) => {\n setMeasurement(measurementName, measurement.value, measurement.unit, span);\n });\n\n _setWebVitalAttributes(span, options);\n }\n\n // Set timeOrigin which denotes the timestamp which to base the LCP/FCP/FP/TTFB measurements on\n span.setAttribute(spanStreamingEnabled ? 'browser.performance.time_origin' : 'performance.timeOrigin', timeOrigin);\n\n // In prerendering scenarios, where a page might be prefetched and pre-rendered before the user clicks the link,\n // the navigation starts earlier than when the user clicks it. Web Vitals should always be based on the\n // user-perceived time, so they are not reported from the actual start of the navigation, but rather from the\n // time where the user actively started the navigation, for example by clicking a link.\n // This is user action is called \"activation\" and the time between navigation and activation is stored in\n // the `activationStart` attribute of the \"navigation\" PerformanceEntry.\n span.setAttribute(\n spanStreamingEnabled ? 'browser.performance.navigation.activation_start' : 'performance.activationStart',\n getActivationStart(),\n );\n }\n\n resetWebVitalState();\n}\n\nfunction resetWebVitalState(): void {\n _lcpEntry = undefined;\n _clsEntry = undefined;\n _measurements = {};\n}\n\n/** Add LCP / CLS data to span to allow debugging */\nfunction _setWebVitalAttributes(span: Span, options: AddWebVitalsToSpanOptions): void {\n // Only add LCP attributes if LCP is being recorded on the pageload span\n if (_lcpEntry && options.recordLcpOnPageloadSpan) {\n // Capture Properties of the LCP element that contributes to the LCP.\n\n if (_lcpEntry.element) {\n span.setAttribute('lcp.element', htmlTreeAsString(_lcpEntry.element));\n }\n\n if (_lcpEntry.id) {\n span.setAttribute('lcp.id', _lcpEntry.id);\n }\n\n if (_lcpEntry.url) {\n // Trim URL to the first 200 characters.\n span.setAttribute('lcp.url', _lcpEntry.url.trim().slice(0, 200));\n }\n\n if (_lcpEntry.loadTime != null) {\n // loadTime is the time of LCP that's related to receiving the LCP element response..\n span.setAttribute('lcp.loadTime', _lcpEntry.loadTime);\n }\n\n if (_lcpEntry.renderTime != null) {\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 span.setAttribute('lcp.renderTime', _lcpEntry.renderTime);\n }\n\n span.setAttribute('lcp.size', _lcpEntry.size);\n }\n\n // Only add CLS attributes if CLS is being recorded on the pageload span\n if (_clsEntry?.sources && options.recordClsOnPageloadSpan) {\n _clsEntry.sources.forEach((source, index) =>\n span.setAttribute(`cls.source.${index + 1}`, htmlTreeAsString(source.node)),\n );\n }\n}\n\n/**\n * Rebases First Paint against `activationStart`, so a prerendered page reports time-to-paint from\n * the moment the user activated it rather than from the (much earlier) prerender navigation start.\n *\n * The vitals that come from web-vitals do this themselves, when they compute their value: they only\n * start observing once the page is activated, so `activationStart` is known by then. FP has no\n * web-vitals equivalent and is read off a `buffered` paint observer registered at SDK init, which\n * on a prerendered page runs while `document.prerendering` is still true and `activationStart` is\n * still 0. Correcting there would be a no-op, so it happens here, at span end, by which point the\n * activation time is known.\n */\nfunction _rebaseFpAgainstActivationStart(measurements: Measurements): void {\n const fp = measurements['fp'];\n if (fp) {\n fp.value = Math.max(fp.value - getActivationStart(), 0);\n }\n}\n\n/**\n * Add ttfb request time information to measurements.\n *\n * ttfb information is added via the web vitals library.\n */\nfunction _addTtfbRequestTimeToMeasurements(_measurements: Measurements): void {\n const navEntry = getNavigationEntry(false);\n if (!navEntry) {\n return;\n }\n\n const { responseStart, requestStart } = navEntry;\n\n if (requestStart <= responseStart) {\n _measurements['ttfb.requestTime'] = {\n value: responseStart - requestStart,\n unit: 'millisecond',\n };\n }\n}\n"],"names":["getBrowserPerformanceAPI","browserPerformanceTimeOrigin","addClsInstrumentationHandler","addLcpInstrumentationHandler","isValidLcpMetric","addTtfbInstrumentationHandler","addFcpInstrumentationHandler","addPerformanceInstrumentationHandler","getVisibilityWatcher","msToSec","spanToJSON","SENTRY_OP","DEBUG_BUILD","debug","setMeasurement","getActivationStart","htmlTreeAsString","_measurements","getNavigationEntry"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tracking.js","sources":["../../../src/web-vitals/tracking.ts"],"sourcesContent":["import type { Client, Measurements, Span } from '@sentry/core';\nimport { browserPerformanceTimeOrigin, debug, setMeasurement, spanToJSON } from '@sentry/core';\nimport { BROWSER_NAVIGATION_TYPE, SENTRY_OP } from '@sentry/conventions/attributes';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { htmlTreeAsString } from '../htmlTreeAsString';\nimport type { MetricNavigationType } from '../instrumentation/performanceObserver';\nimport {\n addClsInstrumentationHandler,\n addFcpInstrumentationHandler,\n addLcpInstrumentationHandler,\n addPerformanceInstrumentationHandler,\n addTtfbInstrumentationHandler,\n} from '../instrumentation/performanceObserver';\nimport { getBrowserPerformanceAPI, msToSec } from '../performance/utils';\nimport { isValidLcpMetric } from './lcp';\nimport { getActivationStart, getNavigationEntry, getVisibilityWatcher } from './utils';\n\nlet _measurements: Measurements = {};\nlet _lcpEntry: LargestContentfulPaint | undefined;\nlet _clsEntry: LayoutShift | undefined;\nlet _navigationType: MetricNavigationType | undefined;\n\ninterface StartTrackingWebVitalsOptions {\n trackCls: boolean;\n trackLcp: boolean;\n client: Client;\n}\n\n/**\n * Start tracking web vitals.\n * The callback returned by this function can be used to stop tracking & ensure all measurements are final & captured.\n *\n * @returns A function that forces web vitals collection\n */\nexport function startTrackingWebVitals({ trackCls, trackLcp }: StartTrackingWebVitalsOptions): () => void {\n const performance = getBrowserPerformanceAPI();\n if (performance && browserPerformanceTimeOrigin()) {\n const lcpCleanupCallback = trackLcp ? _trackLCP() : undefined;\n const clsCleanupCallback = trackCls ? _trackCLS() : undefined;\n const ttfbCleanupCallback = _trackTtfb();\n const fcpCleanupCallback = _trackFcp();\n const fpCleanupCallback = _trackFp();\n\n return (): void => {\n ttfbCleanupCallback();\n fcpCleanupCallback();\n fpCleanupCallback();\n lcpCleanupCallback?.();\n clsCleanupCallback?.();\n };\n }\n\n return () => undefined;\n}\n\nexport { registerInpInteractionListener } from './inp';\n\n/**\n * Starts tracking the Cumulative Layout Shift on the current page and collects the value and last entry\n * to the `_measurements` object which ultimately is applied to the pageload span's measurements.\n */\nfunction _trackCLS(): () => void {\n return addClsInstrumentationHandler(({ metric }) => {\n const entry = metric.entries[metric.entries.length - 1] as LayoutShift | undefined;\n if (!entry) {\n return;\n }\n _measurements['cls'] = { value: metric.value, unit: '' };\n _clsEntry = entry;\n }, true);\n}\n\n/** Starts tracking the Largest Contentful Paint on the current page. */\nfunction _trackLCP(): () => void {\n return addLcpInstrumentationHandler(({ metric }) => {\n const entry = metric.entries[metric.entries.length - 1];\n if (!entry || !isValidLcpMetric(metric.value)) {\n return;\n }\n\n _measurements['lcp'] = { value: metric.value, unit: 'millisecond' };\n _lcpEntry = entry as LargestContentfulPaint;\n }, true);\n}\n\nfunction _trackTtfb(): () => void {\n return addTtfbInstrumentationHandler(({ metric }) => {\n _navigationType = metric.navigationType;\n\n const entry = metric.entries[metric.entries.length - 1];\n if (!entry) {\n return;\n }\n\n _measurements['ttfb'] = { value: metric.value, unit: 'millisecond' };\n });\n}\n\n/** Starts tracking the First Contentful Paint on the current page. */\nfunction _trackFcp(): () => void {\n return addFcpInstrumentationHandler(({ metric }) => {\n _navigationType = metric.navigationType;\n _measurements['fcp'] = { value: metric.value, unit: 'millisecond' };\n });\n}\n\n/**\n * Starts tracking First Paint on the current page.\n *\n * web-vitals has no `onFP`, so this stays on the raw paint observer, and mirrors the one thing\n * `onFCP` does inline: skip the vital if the page was hidden before it. The raw `startTime` is\n * stored as-is; rebasing it against `activationStart` happens in `_rebaseFpAgainstActivationStart`\n * when the span ends, because this observer can run before the page is activated.\n */\nfunction _trackFp(): () => void {\n return addPerformanceInstrumentationHandler('paint', ({ entries }) => {\n const firstHidden = getVisibilityWatcher();\n for (const entry of entries) {\n if (entry.name === 'first-paint' && entry.startTime < firstHidden.firstHiddenTime) {\n _measurements['fp'] = { value: entry.startTime, unit: 'millisecond' };\n }\n }\n });\n}\n\ninterface AddWebVitalsToSpanOptions {\n /**\n * Flag to determine if CLS should be recorded as a measurement on the pageload span or\n * sent as a standalone span instead.\n * Sending it as a standalone span will yield more accurate LCP values.\n *\n * Default: `false` for backwards compatibility.\n */\n recordClsOnPageloadSpan: boolean;\n\n /**\n * Flag to determine if LCP should be recorded as a measurement on the pageload span or\n * sent as a standalone span instead.\n * Sending it as a standalone span will yield more accurate LCP values.\n *\n * Default: `false` for backwards compatibility.\n */\n recordLcpOnPageloadSpan: boolean;\n\n /**\n * Whether span streaming is enabled.\n */\n spanStreamingEnabled?: boolean;\n}\n\n/**\n * Writes the collected web vitals (LCP, CLS, INP, TTFB, FP, FCP) onto the pageload span,\n * either as measurements/attributes (v1) or as web vital attributes (span streaming).\n *\n * This should be called when the pageload span ends, after the web vitals have been finalized.\n * It is a no-op for non-pageload spans, but always resets the collected web vital state so it\n * doesn't leak into a subsequent navigation.\n */\nexport function addWebVitalsToSpan(span: Span, options: AddWebVitalsToSpanOptions): void {\n const origin = browserPerformanceTimeOrigin();\n if (!getBrowserPerformanceAPI()?.getEntries || !origin) {\n // Gatekeeper if performance API not available\n resetWebVitalState();\n return;\n }\n\n const { spanStreamingEnabled, recordClsOnPageloadSpan, recordLcpOnPageloadSpan } = options;\n const timeOrigin = msToSec(origin);\n\n // Measurements are only available for pageload transactions\n if (spanToJSON(span).attributes[SENTRY_OP] === 'pageload') {\n _addTtfbRequestTimeToMeasurements(_measurements);\n _rebaseFpAgainstActivationStart(_measurements);\n\n if (spanStreamingEnabled) {\n const setAttr = (shortWebVitalName: string, value: number, customAttrName?: string) => {\n const attrKey = customAttrName ?? `browser.web_vital.${shortWebVitalName}.value`;\n span.setAttribute(attrKey, value);\n DEBUG_BUILD && debug.log('Setting web vital attribute', { [attrKey]: value }, 'on pageload span');\n };\n // for streamed pageload spans, we add the web vital measurements as attributes.\n // We omit LCP, CLS and INP because they're tracked separately as spans\n ['ttfb', 'fp', 'fcp'].forEach(measurementName => {\n if (_measurements[measurementName]) {\n setAttr(measurementName, _measurements[measurementName].value);\n }\n });\n if (_measurements['ttfb.requestTime']) {\n setAttr('ttfb.requestTime', _measurements['ttfb.requestTime'].value, 'browser.web_vital.ttfb.request_time');\n }\n } else {\n // If CLS is tracked as a span (span streaming), don't record CLS as a measurement\n if (!recordClsOnPageloadSpan) {\n delete _measurements.cls;\n }\n\n // If LCP is tracked as a span (span streaming), don't record LCP as a measurement\n if (!recordLcpOnPageloadSpan) {\n delete _measurements.lcp;\n }\n\n Object.entries(_measurements).forEach(([measurementName, measurement]) => {\n setMeasurement(measurementName, measurement.value, measurement.unit, span);\n });\n\n _setWebVitalAttributes(span, options);\n }\n\n // TTFB, FP and FCP are attributes of this span rather than spans of their own, so the navigation\n // type they were measured on is reported here for them to be read against.\n if (_navigationType) {\n span.setAttribute(BROWSER_NAVIGATION_TYPE, _navigationType);\n }\n\n // Set timeOrigin which denotes the timestamp which to base the LCP/FCP/FP/TTFB measurements on\n span.setAttribute(spanStreamingEnabled ? 'browser.performance.time_origin' : 'performance.timeOrigin', timeOrigin);\n\n // In prerendering scenarios, where a page might be prefetched and pre-rendered before the user clicks the link,\n // the navigation starts earlier than when the user clicks it. Web Vitals should always be based on the\n // user-perceived time, so they are not reported from the actual start of the navigation, but rather from the\n // time where the user actively started the navigation, for example by clicking a link.\n // This is user action is called \"activation\" and the time between navigation and activation is stored in\n // the `activationStart` attribute of the \"navigation\" PerformanceEntry.\n span.setAttribute(\n spanStreamingEnabled ? 'browser.performance.navigation.activation_start' : 'performance.activationStart',\n getActivationStart(),\n );\n }\n\n resetWebVitalState();\n}\n\nfunction resetWebVitalState(): void {\n _lcpEntry = undefined;\n _clsEntry = undefined;\n _navigationType = undefined;\n _measurements = {};\n}\n\n/** Add LCP / CLS data to span to allow debugging */\nfunction _setWebVitalAttributes(span: Span, options: AddWebVitalsToSpanOptions): void {\n // Only add LCP attributes if LCP is being recorded on the pageload span\n if (_lcpEntry && options.recordLcpOnPageloadSpan) {\n // Capture Properties of the LCP element that contributes to the LCP.\n\n if (_lcpEntry.element) {\n span.setAttribute('lcp.element', htmlTreeAsString(_lcpEntry.element));\n }\n\n if (_lcpEntry.id) {\n span.setAttribute('lcp.id', _lcpEntry.id);\n }\n\n if (_lcpEntry.url) {\n // Trim URL to the first 200 characters.\n span.setAttribute('lcp.url', _lcpEntry.url.trim().slice(0, 200));\n }\n\n if (_lcpEntry.loadTime != null) {\n // loadTime is the time of LCP that's related to receiving the LCP element response..\n span.setAttribute('lcp.loadTime', _lcpEntry.loadTime);\n }\n\n if (_lcpEntry.renderTime != null) {\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 span.setAttribute('lcp.renderTime', _lcpEntry.renderTime);\n }\n\n span.setAttribute('lcp.size', _lcpEntry.size);\n }\n\n // Only add CLS attributes if CLS is being recorded on the pageload span\n if (_clsEntry?.sources && options.recordClsOnPageloadSpan) {\n _clsEntry.sources.forEach((source, index) =>\n span.setAttribute(`cls.source.${index + 1}`, htmlTreeAsString(source.node)),\n );\n }\n}\n\n/**\n * Rebases First Paint against `activationStart`, so a prerendered page reports time-to-paint from\n * the moment the user activated it rather than from the (much earlier) prerender navigation start.\n *\n * The vitals that come from web-vitals do this themselves, when they compute their value: they only\n * start observing once the page is activated, so `activationStart` is known by then. FP has no\n * web-vitals equivalent and is read off a `buffered` paint observer registered at SDK init, which\n * on a prerendered page runs while `document.prerendering` is still true and `activationStart` is\n * still 0. Correcting there would be a no-op, so it happens here, at span end, by which point the\n * activation time is known.\n */\nfunction _rebaseFpAgainstActivationStart(measurements: Measurements): void {\n const fp = measurements['fp'];\n if (fp) {\n fp.value = Math.max(fp.value - getActivationStart(), 0);\n }\n}\n\n/**\n * Add ttfb request time information to measurements.\n *\n * ttfb information is added via the web vitals library.\n */\nfunction _addTtfbRequestTimeToMeasurements(_measurements: Measurements): void {\n const navEntry = getNavigationEntry(false);\n if (!navEntry) {\n return;\n }\n\n const { responseStart, requestStart } = navEntry;\n\n if (requestStart <= responseStart) {\n _measurements['ttfb.requestTime'] = {\n value: responseStart - requestStart,\n unit: 'millisecond',\n };\n }\n}\n"],"names":["getBrowserPerformanceAPI","browserPerformanceTimeOrigin","addClsInstrumentationHandler","addLcpInstrumentationHandler","isValidLcpMetric","addTtfbInstrumentationHandler","addFcpInstrumentationHandler","addPerformanceInstrumentationHandler","getVisibilityWatcher","msToSec","spanToJSON","SENTRY_OP","DEBUG_BUILD","debug","setMeasurement","BROWSER_NAVIGATION_TYPE","getActivationStart","htmlTreeAsString","_measurements","getNavigationEntry"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"performanceObserver.js","sources":["../../../src/instrumentation/performanceObserver.ts"],"sourcesContent":["import { debug, getFunctionName } from '@sentry/core';\nimport { onCLS, onFCP, onINP, onLCP, onTTFB } from 'web-vitals';\nimport { DEBUG_BUILD } from '../debug-build';\n\ntype InstrumentHandlerTypePerformanceObserver =\n | 'longtask'\n | 'event'\n | 'navigation'\n | 'paint'\n | 'resource'\n | 'element'\n | 'soft-navigation'\n // fist-input is still needed for INP\n | 'first-input';\n\ntype InstrumentHandlerTypeMetric = 'cls' | 'lcp' | 'ttfb' | 'inp' | 'fcp';\n\n// We provide this here manually instead of relying on a global, as this is not available in non-browser environements\n// And we do not want to expose such types\ninterface PerformanceEntry {\n readonly duration: number;\n readonly entryType: string;\n readonly name: string;\n readonly startTime: number;\n toJSON(): Record<string, unknown>;\n}\nexport interface PerformanceEventTiming extends PerformanceEntry {\n processingStart: number;\n processingEnd: number;\n duration: number;\n cancelable?: boolean;\n target?: unknown | null;\n interactionId?: number;\n}\n\n/**\n * A `soft-navigation` entry, minted by the browser once a history change is followed by a\n * confirming paint. `interactionId` is the id of the `PerformanceEventTiming` entry for the\n * interaction that drove the navigation, which is how we join it back to a Sentry navigation span.\n */\nexport interface PerformanceSoftNavigation extends PerformanceEntry {\n readonly interactionId: number;\n readonly navigationId: number;\n}\n\ninterface PerformanceScriptTiming extends PerformanceEntry {\n sourceURL: string;\n sourceFunctionName: string;\n sourceCharPosition: number;\n invoker: string;\n invokerType: string;\n}\nexport interface PerformanceLongAnimationFrameTiming extends PerformanceEntry {\n scripts: PerformanceScriptTiming[];\n}\n\n// Locally-defined to match web-vitals' `Metric` shape without importing it: web-vitals' type\n// entrypoint carries a `declare global` block that references DOM globals not present in every\n// TypeScript lib version (e.g. `NavigationType`), which leaks into and breaks consumers on older\n// TS. Keeping this local keeps web-vitals' global augmentations out of our published types.\n/**\n * The navigation types web-vitals reports a metric for. Wider than the set the\n * `browser.navigation.type` attribute uses - see `toBrowserNavigationType`.\n */\nexport type MetricNavigationType =\n | 'navigate'\n | 'reload'\n | 'back-forward'\n | 'back-forward-cache'\n | 'prerender'\n | 'restore'\n | 'soft-navigation';\n\ninterface Metric {\n /**\n * The name of the metric (in acronym form).\n */\n name: 'CLS' | 'FCP' | 'INP' | 'LCP' | 'TTFB';\n\n /**\n * The current value of the metric.\n */\n value: number;\n\n /**\n * The rating as to whether the metric value is within the \"good\",\n * \"needs improvement\", or \"poor\" thresholds of the metric.\n */\n rating: 'good' | 'needs-improvement' | 'poor';\n\n /**\n * The delta between the current value and the last-reported value.\n * On the first report, `delta` and `value` will always be the same.\n */\n delta: number;\n\n /**\n * A unique ID representing this particular metric instance. This ID can\n * be used by an analytics tool to dedupe multiple values sent for the same\n * metric instance, or to group multiple deltas together and calculate a\n * total. It can also be used to differentiate multiple different metric\n * instances sent from the same page, which can happen if the page is\n * restored from the back/forward cache (in that case new metrics object\n * get created).\n */\n id: string;\n\n /**\n * Any performance entries relevant to the metric value calculation.\n * The array may also be empty if the metric value was not based on any\n * entries (e.g. a CLS value of 0 given no layout shifts).\n */\n entries: PerformanceEntry[];\n\n /**\n * The type of navigation.\n *\n * Navigation Timing API (or `undefined` if the browser doesn't\n * support that API). For pages that are restored from the bfcache, this\n * value will be 'back-forward-cache'.\n */\n navigationType: MetricNavigationType;\n\n /**\n * The id of the navigation the metric belongs to. For soft navigations this is the\n * `navigationId` of the `soft-navigation` entry, otherwise it's the id of the hard navigation.\n */\n navigationId: number;\n\n /**\n * For soft navigations, the `interactionId` of the interaction that triggered the navigation.\n */\n navigationInteractionId?: number;\n\n /**\n * The start time the metric value is relative to. Non-zero for soft navigations, where the\n * time origin is the triggering interaction rather than the start of the document.\n */\n navigationStartTime?: number;\n\n /**\n * The URL the metric was recorded for. Relevant for soft navigations, where a metric can be\n * reported long after the URL has moved on.\n */\n navigationURL?: string;\n}\n\ntype InstrumentHandlerType = InstrumentHandlerTypeMetric | InstrumentHandlerTypePerformanceObserver;\n\ntype StopListening = undefined | void | (() => void);\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InstrumentHandlerCallback = (data: any) => void;\n\ntype CleanupHandlerCallback = () => void;\n\nconst handlers: { [key in InstrumentHandlerType]?: InstrumentHandlerCallback[] } = {};\nconst instrumented: { [key in InstrumentHandlerType]?: boolean } = {};\n\nlet _previousCls: Metric | undefined;\nlet _previousLcp: Metric | undefined;\nlet _previousTtfb: Metric | undefined;\nlet _previousInp: Metric | undefined;\nlet _previousFcp: Metric | undefined;\n\nconst stopListeners: Partial<Record<InstrumentHandlerType, StopListening>> = {};\n\nlet _reportSoftNavs = false;\nlet _reportBfcache = false;\n\n/**\n * Opt the CLS, LCP and INP observers into reporting metrics for soft navigations.\n *\n * This also turns `reportAllChanges` off for CLS and LCP. web-vitals force-reports a metric when\n * the navigation it belongs to is over, so without the intermediate updates every value a handler\n * receives is already the final one for its navigation. That only holds because soft navigations\n * are limited to span streaming, where CLS and LCP are sent as their own spans - the static\n * lifecycle instead writes them onto the pageload span as it ends, which is what `reportAllChanges`\n * was originally added for (#11934, #12360).\n *\n * Each observer is instrumented lazily, on its first handler, and web-vitals takes its options at\n * that point only. So this has to be called before any of the `add*InstrumentationHandler`\n * functions, otherwise it won't take effect for observers that are already running.\n *\n * On browsers without the Soft Navigation API this is a no-op: web-vitals feature-detects the API\n * and keeps reporting hard-navigation metrics as usual.\n */\nexport function enableSoftNavigationReporting(): void {\n _reportSoftNavs = true;\n}\n\n/**\n * Opt the CLS, LCP and INP observers into reporting metrics for back/forward-cache restores.\n *\n * web-vitals re-reports each metric after a restore, tagged with a `back-forward-cache` navigation\n * type. A restore is a new page view measured against a document that was never reloaded, so the\n * values only mean anything if there is a fresh root span for them to belong to. Without one they\n * would attach to the span the page had before it was frozen, which is why this is off by default.\n *\n * Like `enableSoftNavigationReporting`, this only affects observers instrumented after it is\n * called.\n */\nexport function enableBfcacheReporting(): void {\n _reportBfcache = true;\n}\n\n/**\n * Add a callback that will be triggered when a CLS metric is available.\n * Returns a cleanup callback which can be called to remove the instrumentation handler.\n *\n * Pass `stopOnCallback = true` to stop listening for CLS when the cleanup callback is called.\n * This will lead to the CLS being finalized and frozen.\n */\nexport function addClsInstrumentationHandler(\n callback: (data: { metric: Metric }) => void,\n stopOnCallback = false,\n): CleanupHandlerCallback {\n return addMetricObserver('cls', callback, instrumentCls, _previousCls, stopOnCallback);\n}\n\n/**\n * Add a callback that will be triggered when a LCP metric is available.\n * Returns a cleanup callback which can be called to remove the instrumentation handler.\n *\n * Pass `stopOnCallback = true` to stop listening for LCP when the cleanup callback is called.\n * This will lead to the LCP being finalized and frozen.\n */\nexport function addLcpInstrumentationHandler(\n callback: (data: { metric: Metric }) => void,\n stopOnCallback = false,\n): CleanupHandlerCallback {\n return addMetricObserver('lcp', callback, instrumentLcp, _previousLcp, stopOnCallback);\n}\n\n/**\n * Add a callback that will be triggered when a TTFD metric is available.\n */\nexport function addTtfbInstrumentationHandler(callback: (data: { metric: Metric }) => void): CleanupHandlerCallback {\n return addMetricObserver('ttfb', callback, instrumentTtfb, _previousTtfb);\n}\n\n/**\n * Add a callback that will be triggered when a FCP metric is available.\n */\nexport function addFcpInstrumentationHandler(callback: (data: { metric: Metric }) => void): CleanupHandlerCallback {\n return addMetricObserver('fcp', callback, instrumentFcp, _previousFcp);\n}\n\nexport type InstrumentationHandlerCallback = (data: {\n metric: Omit<Metric, 'entries'> & {\n entries: PerformanceEventTiming[];\n };\n}) => void;\n\n/**\n * Add a callback that will be triggered when a INP metric is available.\n * Returns a cleanup callback which can be called to remove the instrumentation handler.\n */\nexport function addInpInstrumentationHandler(callback: InstrumentationHandlerCallback): CleanupHandlerCallback {\n return addMetricObserver('inp', callback, instrumentInp, _previousInp);\n}\n\nexport function addPerformanceInstrumentationHandler(\n type: 'event',\n callback: (data: { entries: ((PerformanceEntry & { target?: unknown | null }) | PerformanceEventTiming)[] }) => void,\n): CleanupHandlerCallback;\nexport function addPerformanceInstrumentationHandler(\n type: InstrumentHandlerTypePerformanceObserver,\n callback: (data: { entries: PerformanceEntry[] }) => void,\n): CleanupHandlerCallback;\n\n/**\n * Add a callback that will be triggered when a performance observer is triggered,\n * and receives the entries of the observer.\n * Returns a cleanup callback which can be called to remove the instrumentation handler.\n */\nexport function addPerformanceInstrumentationHandler(\n type: InstrumentHandlerTypePerformanceObserver,\n callback: (data: { entries: PerformanceEntry[] }) => void,\n): CleanupHandlerCallback {\n addHandler(type, callback);\n\n if (!instrumented[type]) {\n instrumentPerformanceObserver(type);\n instrumented[type] = true;\n }\n\n return getCleanupCallback(type, callback);\n}\n\n/** Trigger all handlers of a given type. */\nfunction triggerHandlers(type: InstrumentHandlerType, data: unknown): void {\n const typeHandlers = handlers[type];\n\n if (!typeHandlers?.length) {\n return;\n }\n\n for (const handler of typeHandlers) {\n try {\n handler(data);\n } catch (e) {\n DEBUG_BUILD &&\n debug.error(\n `Error while triggering instrumentation handler.\\nType: ${type}\\nName: ${getFunctionName(handler)}\\nError:`,\n e,\n );\n }\n }\n}\n\n/**\n * Wraps a metric callback so that metrics reported after a back/forward-cache restore are dropped\n * unless `enableBfcacheReporting` was called. See there for why they are off by default.\n */\nfunction unlessBfcacheDisabled(callback: (metric: Metric) => void): (metric: Metric) => void {\n return metric => {\n if (!_reportBfcache && metric.navigationType === 'back-forward-cache') {\n return;\n }\n callback(metric);\n };\n}\n\nfunction instrumentCls(): StopListening {\n return onCLS(\n unlessBfcacheDisabled(metric => {\n triggerHandlers('cls', {\n metric,\n });\n _previousCls = metric;\n }),\n // We want the callback to be called whenever the CLS value updates.\n // By default, the callback is only called when the tab goes to the background.\n { reportAllChanges: !_reportSoftNavs && !_reportBfcache, reportSoftNavs: _reportSoftNavs },\n );\n}\n\nfunction instrumentLcp(): StopListening {\n return onLCP(\n unlessBfcacheDisabled(metric => {\n triggerHandlers('lcp', {\n metric,\n });\n _previousLcp = metric;\n }),\n // We want the callback to be called whenever the LCP value updates.\n // By default, the callback is only called when the tab goes to the background.\n { reportAllChanges: !_reportSoftNavs && !_reportBfcache, reportSoftNavs: _reportSoftNavs },\n );\n}\n\nfunction instrumentTtfb(): StopListening {\n return onTTFB(\n unlessBfcacheDisabled(metric => {\n triggerHandlers('ttfb', {\n metric,\n });\n _previousTtfb = metric;\n }),\n );\n}\n\nfunction instrumentFcp(): StopListening {\n return onFCP(\n unlessBfcacheDisabled(metric => {\n triggerHandlers('fcp', {\n metric,\n });\n _previousFcp = metric;\n }),\n );\n}\n\nfunction instrumentInp(): StopListening {\n return onINP(\n unlessBfcacheDisabled(metric => {\n triggerHandlers('inp', {\n metric,\n });\n _previousInp = metric;\n }),\n { reportSoftNavs: _reportSoftNavs },\n );\n}\n\nfunction addMetricObserver(\n type: InstrumentHandlerTypeMetric,\n callback: InstrumentHandlerCallback,\n instrumentFn: () => StopListening,\n previousValue: Metric | undefined,\n stopOnCallback = false,\n): CleanupHandlerCallback {\n addHandler(type, callback);\n\n if (!instrumented[type]) {\n instrumented[type] = true;\n // Deferred by a microtask rather than started here, because web-vitals reads its options once,\n // when the observer is created. Registering a handler would otherwise pin those options for\n // every other consumer of this observer, so whichever integration happened to run first would\n // decide whether soft navigations and bfcache restores are reported. Client setup is\n // synchronous, so every `enable*Reporting()` call has landed by the time this runs, and the\n // observers are buffered so no entries are missed in the meantime.\n void Promise.resolve().then(() => {\n stopListeners[type] = instrumentFn();\n });\n }\n\n if (previousValue) {\n callback({ metric: previousValue });\n }\n\n return getCleanupCallback(type, callback, stopOnCallback);\n}\n\nfunction instrumentPerformanceObserver(type: InstrumentHandlerTypePerformanceObserver): void {\n const options: PerformanceObserverInit = { type, buffered: true };\n\n // Special per-type options we want to use\n if (type === 'event') {\n (options as PerformanceObserverInit & { durationThreshold?: number }).durationThreshold = 0;\n }\n\n try {\n if (PerformanceObserver.supportedEntryTypes.includes(type)) {\n const po = new PerformanceObserver(list => {\n // Delay by a microtask to work around a bug in Safari where the\n // callback is invoked synchronously rather than in a separate task.\n // See: https://github.com/GoogleChrome/web-vitals/issues/277\n void Promise.resolve().then(() => {\n triggerHandlers(type, { entries: list.getEntries() });\n });\n });\n po.observe(options);\n }\n } catch {\n // Unsupported entry type; nothing to observe.\n }\n}\n\nfunction addHandler(type: InstrumentHandlerType, handler: InstrumentHandlerCallback): void {\n handlers[type] = handlers[type] || [];\n handlers[type].push(handler);\n}\n\n// Get a callback which can be called to remove the instrumentation handler\nfunction getCleanupCallback(\n type: InstrumentHandlerType,\n callback: InstrumentHandlerCallback,\n stopOnCleanup = false,\n): CleanupHandlerCallback {\n return () => {\n // Looked up rather than captured: the observer is started in a microtask, so its stop function\n // does not exist yet when this callback is built.\n if (stopOnCleanup) {\n stopListeners[type]?.();\n }\n\n const typeHandlers = handlers[type];\n\n if (!typeHandlers) {\n return;\n }\n\n const index = typeHandlers.indexOf(callback);\n if (index !== -1) {\n typeHandlers.splice(index, 1);\n }\n };\n}\n\n/**\n * Check if a PerformanceEntry is a PerformanceEventTiming by checking for the `duration` property.\n */\nexport function isPerformanceEventTiming(entry: PerformanceEntry): entry is PerformanceEventTiming {\n return 'duration' in entry;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"performanceObserver.js","sources":["../../../src/instrumentation/performanceObserver.ts"],"sourcesContent":["import { debug, getFunctionName } from '@sentry/core';\nimport { onCLS, onFCP, onINP, onLCP, onTTFB } from 'web-vitals';\nimport { DEBUG_BUILD } from '../debug-build';\n\ntype InstrumentHandlerTypePerformanceObserver =\n | 'longtask'\n | 'event'\n | 'navigation'\n | 'paint'\n | 'resource'\n | 'element'\n | 'soft-navigation'\n // fist-input is still needed for INP\n | 'first-input';\n\ntype InstrumentHandlerTypeMetric = 'cls' | 'lcp' | 'ttfb' | 'inp' | 'fcp';\n\n// We provide this here manually instead of relying on a global, as this is not available in non-browser environements\n// And we do not want to expose such types\ninterface PerformanceEntry {\n readonly duration: number;\n readonly entryType: string;\n readonly name: string;\n readonly startTime: number;\n toJSON(): Record<string, unknown>;\n}\nexport interface PerformanceEventTiming extends PerformanceEntry {\n processingStart: number;\n processingEnd: number;\n duration: number;\n cancelable?: boolean;\n target?: unknown | null;\n interactionId?: number;\n}\n\n/**\n * A `soft-navigation` entry, minted by the browser once a history change is followed by a\n * confirming paint. `interactionId` is the id of the `PerformanceEventTiming` entry for the\n * interaction that drove the navigation, which is how we join it back to a Sentry navigation span.\n */\nexport interface PerformanceSoftNavigation extends PerformanceEntry {\n readonly interactionId: number;\n readonly navigationId: number;\n}\n\ninterface PerformanceScriptTiming extends PerformanceEntry {\n sourceURL: string;\n sourceFunctionName: string;\n sourceCharPosition: number;\n invoker: string;\n invokerType: string;\n}\nexport interface PerformanceLongAnimationFrameTiming extends PerformanceEntry {\n scripts: PerformanceScriptTiming[];\n}\n\n// Locally-defined to match web-vitals' `Metric` shape without importing it: web-vitals' type\n// entrypoint carries a `declare global` block that references DOM globals not present in every\n// TypeScript lib version (e.g. `NavigationType`), which leaks into and breaks consumers on older\n// TS. Keeping this local keeps web-vitals' global augmentations out of our published types.\n/**\n * The navigation types web-vitals reports a metric for, reported as-is on `browser.navigation.type`.\n */\nexport type MetricNavigationType =\n | 'navigate'\n | 'reload'\n | 'back-forward'\n | 'back-forward-cache'\n | 'prerender'\n | 'restore'\n | 'soft-navigation';\n\ninterface Metric {\n /**\n * The name of the metric (in acronym form).\n */\n name: 'CLS' | 'FCP' | 'INP' | 'LCP' | 'TTFB';\n\n /**\n * The current value of the metric.\n */\n value: number;\n\n /**\n * The rating as to whether the metric value is within the \"good\",\n * \"needs improvement\", or \"poor\" thresholds of the metric.\n */\n rating: 'good' | 'needs-improvement' | 'poor';\n\n /**\n * The delta between the current value and the last-reported value.\n * On the first report, `delta` and `value` will always be the same.\n */\n delta: number;\n\n /**\n * A unique ID representing this particular metric instance. This ID can\n * be used by an analytics tool to dedupe multiple values sent for the same\n * metric instance, or to group multiple deltas together and calculate a\n * total. It can also be used to differentiate multiple different metric\n * instances sent from the same page, which can happen if the page is\n * restored from the back/forward cache (in that case new metrics object\n * get created).\n */\n id: string;\n\n /**\n * Any performance entries relevant to the metric value calculation.\n * The array may also be empty if the metric value was not based on any\n * entries (e.g. a CLS value of 0 given no layout shifts).\n */\n entries: PerformanceEntry[];\n\n /**\n * The type of navigation.\n *\n * Navigation Timing API (or `undefined` if the browser doesn't\n * support that API). For pages that are restored from the bfcache, this\n * value will be 'back-forward-cache'.\n */\n navigationType: MetricNavigationType;\n\n /**\n * The id of the navigation the metric belongs to. For soft navigations this is the\n * `navigationId` of the `soft-navigation` entry, otherwise it's the id of the hard navigation.\n */\n navigationId: number;\n\n /**\n * For soft navigations, the `interactionId` of the interaction that triggered the navigation.\n */\n navigationInteractionId?: number;\n\n /**\n * The start time the metric value is relative to. Non-zero for soft navigations, where the\n * time origin is the triggering interaction rather than the start of the document.\n */\n navigationStartTime?: number;\n\n /**\n * The URL the metric was recorded for. Relevant for soft navigations, where a metric can be\n * reported long after the URL has moved on.\n */\n navigationURL?: string;\n}\n\ntype InstrumentHandlerType = InstrumentHandlerTypeMetric | InstrumentHandlerTypePerformanceObserver;\n\ntype StopListening = undefined | void | (() => void);\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InstrumentHandlerCallback = (data: any) => void;\n\ntype CleanupHandlerCallback = () => void;\n\nconst handlers: { [key in InstrumentHandlerType]?: InstrumentHandlerCallback[] } = {};\nconst instrumented: { [key in InstrumentHandlerType]?: boolean } = {};\n\nlet _previousCls: Metric | undefined;\nlet _previousLcp: Metric | undefined;\nlet _previousTtfb: Metric | undefined;\nlet _previousInp: Metric | undefined;\nlet _previousFcp: Metric | undefined;\n\nconst stopListeners: Partial<Record<InstrumentHandlerType, StopListening>> = {};\n\nlet _reportSoftNavs = false;\nlet _reportBfcache = false;\n\n/**\n * Opt the CLS, LCP and INP observers into reporting metrics for soft navigations.\n *\n * This also turns `reportAllChanges` off for CLS and LCP. web-vitals force-reports a metric when\n * the navigation it belongs to is over, so without the intermediate updates every value a handler\n * receives is already the final one for its navigation. That only holds because soft navigations\n * are limited to span streaming, where CLS and LCP are sent as their own spans - the static\n * lifecycle instead writes them onto the pageload span as it ends, which is what `reportAllChanges`\n * was originally added for (#11934, #12360).\n *\n * Each observer is instrumented lazily, on its first handler, and web-vitals takes its options at\n * that point only. So this has to be called before any of the `add*InstrumentationHandler`\n * functions, otherwise it won't take effect for observers that are already running.\n *\n * On browsers without the Soft Navigation API this is a no-op: web-vitals feature-detects the API\n * and keeps reporting hard-navigation metrics as usual.\n */\nexport function enableSoftNavigationReporting(): void {\n _reportSoftNavs = true;\n}\n\n/**\n * Opt the CLS, LCP and INP observers into reporting metrics for back/forward-cache restores.\n *\n * web-vitals re-reports each metric after a restore, tagged with a `back-forward-cache` navigation\n * type. A restore is a new page view measured against a document that was never reloaded, so the\n * values only mean anything if there is a fresh root span for them to belong to. Without one they\n * would attach to the span the page had before it was frozen, which is why this is off by default.\n *\n * Like `enableSoftNavigationReporting`, this only affects observers instrumented after it is\n * called.\n */\nexport function enableBfcacheReporting(): void {\n _reportBfcache = true;\n}\n\n/**\n * Add a callback that will be triggered when a CLS metric is available.\n * Returns a cleanup callback which can be called to remove the instrumentation handler.\n *\n * Pass `stopOnCallback = true` to stop listening for CLS when the cleanup callback is called.\n * This will lead to the CLS being finalized and frozen.\n */\nexport function addClsInstrumentationHandler(\n callback: (data: { metric: Metric }) => void,\n stopOnCallback = false,\n): CleanupHandlerCallback {\n return addMetricObserver('cls', callback, instrumentCls, _previousCls, stopOnCallback);\n}\n\n/**\n * Add a callback that will be triggered when a LCP metric is available.\n * Returns a cleanup callback which can be called to remove the instrumentation handler.\n *\n * Pass `stopOnCallback = true` to stop listening for LCP when the cleanup callback is called.\n * This will lead to the LCP being finalized and frozen.\n */\nexport function addLcpInstrumentationHandler(\n callback: (data: { metric: Metric }) => void,\n stopOnCallback = false,\n): CleanupHandlerCallback {\n return addMetricObserver('lcp', callback, instrumentLcp, _previousLcp, stopOnCallback);\n}\n\n/**\n * Add a callback that will be triggered when a TTFD metric is available.\n */\nexport function addTtfbInstrumentationHandler(callback: (data: { metric: Metric }) => void): CleanupHandlerCallback {\n return addMetricObserver('ttfb', callback, instrumentTtfb, _previousTtfb);\n}\n\n/**\n * Add a callback that will be triggered when a FCP metric is available.\n */\nexport function addFcpInstrumentationHandler(callback: (data: { metric: Metric }) => void): CleanupHandlerCallback {\n return addMetricObserver('fcp', callback, instrumentFcp, _previousFcp);\n}\n\nexport type InstrumentationHandlerCallback = (data: {\n metric: Omit<Metric, 'entries'> & {\n entries: PerformanceEventTiming[];\n };\n}) => void;\n\n/**\n * Add a callback that will be triggered when a INP metric is available.\n * Returns a cleanup callback which can be called to remove the instrumentation handler.\n */\nexport function addInpInstrumentationHandler(callback: InstrumentationHandlerCallback): CleanupHandlerCallback {\n return addMetricObserver('inp', callback, instrumentInp, _previousInp);\n}\n\nexport function addPerformanceInstrumentationHandler(\n type: 'event',\n callback: (data: { entries: ((PerformanceEntry & { target?: unknown | null }) | PerformanceEventTiming)[] }) => void,\n): CleanupHandlerCallback;\nexport function addPerformanceInstrumentationHandler(\n type: InstrumentHandlerTypePerformanceObserver,\n callback: (data: { entries: PerformanceEntry[] }) => void,\n): CleanupHandlerCallback;\n\n/**\n * Add a callback that will be triggered when a performance observer is triggered,\n * and receives the entries of the observer.\n * Returns a cleanup callback which can be called to remove the instrumentation handler.\n */\nexport function addPerformanceInstrumentationHandler(\n type: InstrumentHandlerTypePerformanceObserver,\n callback: (data: { entries: PerformanceEntry[] }) => void,\n): CleanupHandlerCallback {\n addHandler(type, callback);\n\n if (!instrumented[type]) {\n instrumentPerformanceObserver(type);\n instrumented[type] = true;\n }\n\n return getCleanupCallback(type, callback);\n}\n\n/** Trigger all handlers of a given type. */\nfunction triggerHandlers(type: InstrumentHandlerType, data: unknown): void {\n const typeHandlers = handlers[type];\n\n if (!typeHandlers?.length) {\n return;\n }\n\n for (const handler of typeHandlers) {\n try {\n handler(data);\n } catch (e) {\n DEBUG_BUILD &&\n debug.error(\n `Error while triggering instrumentation handler.\\nType: ${type}\\nName: ${getFunctionName(handler)}\\nError:`,\n e,\n );\n }\n }\n}\n\n/**\n * Wraps a metric callback so that metrics reported after a back/forward-cache restore are dropped\n * unless `enableBfcacheReporting` was called. See there for why they are off by default.\n */\nfunction unlessBfcacheDisabled(callback: (metric: Metric) => void): (metric: Metric) => void {\n return metric => {\n if (!_reportBfcache && metric.navigationType === 'back-forward-cache') {\n return;\n }\n callback(metric);\n };\n}\n\nfunction instrumentCls(): StopListening {\n return onCLS(\n unlessBfcacheDisabled(metric => {\n triggerHandlers('cls', {\n metric,\n });\n _previousCls = metric;\n }),\n // We want the callback to be called whenever the CLS value updates.\n // By default, the callback is only called when the tab goes to the background.\n { reportAllChanges: !_reportSoftNavs && !_reportBfcache, reportSoftNavs: _reportSoftNavs },\n );\n}\n\nfunction instrumentLcp(): StopListening {\n return onLCP(\n unlessBfcacheDisabled(metric => {\n triggerHandlers('lcp', {\n metric,\n });\n _previousLcp = metric;\n }),\n // We want the callback to be called whenever the LCP value updates.\n // By default, the callback is only called when the tab goes to the background.\n { reportAllChanges: !_reportSoftNavs && !_reportBfcache, reportSoftNavs: _reportSoftNavs },\n );\n}\n\nfunction instrumentTtfb(): StopListening {\n return onTTFB(\n unlessBfcacheDisabled(metric => {\n triggerHandlers('ttfb', {\n metric,\n });\n _previousTtfb = metric;\n }),\n );\n}\n\nfunction instrumentFcp(): StopListening {\n return onFCP(\n unlessBfcacheDisabled(metric => {\n triggerHandlers('fcp', {\n metric,\n });\n _previousFcp = metric;\n }),\n );\n}\n\nfunction instrumentInp(): StopListening {\n return onINP(\n unlessBfcacheDisabled(metric => {\n triggerHandlers('inp', {\n metric,\n });\n _previousInp = metric;\n }),\n { reportSoftNavs: _reportSoftNavs },\n );\n}\n\nfunction addMetricObserver(\n type: InstrumentHandlerTypeMetric,\n callback: InstrumentHandlerCallback,\n instrumentFn: () => StopListening,\n previousValue: Metric | undefined,\n stopOnCallback = false,\n): CleanupHandlerCallback {\n addHandler(type, callback);\n\n if (!instrumented[type]) {\n instrumented[type] = true;\n // Deferred by a microtask rather than started here, because web-vitals reads its options once,\n // when the observer is created. Registering a handler would otherwise pin those options for\n // every other consumer of this observer, so whichever integration happened to run first would\n // decide whether soft navigations and bfcache restores are reported. Client setup is\n // synchronous, so every `enable*Reporting()` call has landed by the time this runs, and the\n // observers are buffered so no entries are missed in the meantime.\n void Promise.resolve().then(() => {\n stopListeners[type] = instrumentFn();\n });\n }\n\n if (previousValue) {\n callback({ metric: previousValue });\n }\n\n return getCleanupCallback(type, callback, stopOnCallback);\n}\n\nfunction instrumentPerformanceObserver(type: InstrumentHandlerTypePerformanceObserver): void {\n const options: PerformanceObserverInit = { type, buffered: true };\n\n // Special per-type options we want to use\n if (type === 'event') {\n (options as PerformanceObserverInit & { durationThreshold?: number }).durationThreshold = 0;\n }\n\n try {\n if (PerformanceObserver.supportedEntryTypes.includes(type)) {\n const po = new PerformanceObserver(list => {\n // Delay by a microtask to work around a bug in Safari where the\n // callback is invoked synchronously rather than in a separate task.\n // See: https://github.com/GoogleChrome/web-vitals/issues/277\n void Promise.resolve().then(() => {\n triggerHandlers(type, { entries: list.getEntries() });\n });\n });\n po.observe(options);\n }\n } catch {\n // Unsupported entry type; nothing to observe.\n }\n}\n\nfunction addHandler(type: InstrumentHandlerType, handler: InstrumentHandlerCallback): void {\n handlers[type] = handlers[type] || [];\n handlers[type].push(handler);\n}\n\n// Get a callback which can be called to remove the instrumentation handler\nfunction getCleanupCallback(\n type: InstrumentHandlerType,\n callback: InstrumentHandlerCallback,\n stopOnCleanup = false,\n): CleanupHandlerCallback {\n return () => {\n // Looked up rather than captured: the observer is started in a microtask, so its stop function\n // does not exist yet when this callback is built.\n if (stopOnCleanup) {\n stopListeners[type]?.();\n }\n\n const typeHandlers = handlers[type];\n\n if (!typeHandlers) {\n return;\n }\n\n const index = typeHandlers.indexOf(callback);\n if (index !== -1) {\n typeHandlers.splice(index, 1);\n }\n };\n}\n\n/**\n * Check if a PerformanceEntry is a PerformanceEventTiming by checking for the `duration` property.\n */\nexport function isPerformanceEventTiming(entry: PerformanceEntry): entry is PerformanceEventTiming {\n return 'duration' in entry;\n}\n"],"names":[],"mappings":"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"}
|
package/build/esm/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"11.0.0
|
|
1
|
+
{"type":"module","version":"11.0.0","sideEffects":false}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { browserPerformanceTimeOrigin, spanToJSON, getActiveSpan, parseUrl, filterCollectedUrl, RESOURCE_SPAN_NAME_FALLBACK, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, setMeasurement } from '@sentry/core';
|
|
2
|
-
import { SENTRY_OP, CODE_FILE_PATH, CODE_FUNCTION_NAME, NETWORK_CONNECTION_EFFECTIVE_TYPE, NETWORK_CONNECTION_TYPE, NETWORK_CONNECTION_RTT, URL_SCHEME, SERVER_ADDRESS, URL_DOMAIN, HTTP_REQUEST_SAME_ORIGIN,
|
|
1
|
+
import { browserPerformanceTimeOrigin, spanToJSON, getActiveSpan, UI_LONG_TASK_SPAN_NAME_FALLBACK, parseUrl, filterCollectedUrl, RESOURCE_SPAN_NAME_FALLBACK, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, setMeasurement, BROWSER_NAVIGATION_TIMING_SPAN_NAMES } from '@sentry/core';
|
|
2
|
+
import { SENTRY_OP, CODE_FILE_PATH, CODE_FUNCTION_NAME, BROWSER_PAINT_TYPE, NETWORK_CONNECTION_EFFECTIVE_TYPE, NETWORK_CONNECTION_TYPE, NETWORK_CONNECTION_RTT, URL_FULL, URL_SCHEME, SERVER_ADDRESS, URL_DOMAIN, HTTP_REQUEST_SAME_ORIGIN, HTTP_RESPONSE_STATUS_CODE, HTTP_RESPONSE_SIZE, HTTP_RESPONSE_BODY_SIZE } from '@sentry/conventions/attributes';
|
|
3
3
|
import { UI_LONG_ANIMATION_FRAME, RESOURCE_OTHER, BROWSER_PAINT, BROWSER_LOAD_EVENT, BROWSER_DOM_CONTENT_LOADED_EVENT, BROWSER_CONNECT, BROWSER_REDIRECT, BROWSER_UNLOAD_EVENT, BROWSER_DNS, BROWSER_CACHE, BROWSER_TLS_SSL, BROWSER_REQUEST, BROWSER_RESPONSE, UI_LONG_TASK } from '@sentry/conventions/op';
|
|
4
4
|
import { addPerformanceInstrumentationHandler } from '../instrumentation/performanceObserver.js';
|
|
5
5
|
import { WINDOW } from '../types.js';
|
|
@@ -22,7 +22,7 @@ function startTrackingLongTasks() {
|
|
|
22
22
|
continue;
|
|
23
23
|
}
|
|
24
24
|
startAndEndSpan(parent, startTime, startTime + duration, {
|
|
25
|
-
name:
|
|
25
|
+
name: UI_LONG_TASK_SPAN_NAME_FALLBACK,
|
|
26
26
|
op: UI_LONG_TASK,
|
|
27
27
|
attributes: {
|
|
28
28
|
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.browser.metrics"
|
|
@@ -67,7 +67,7 @@ function startTrackingLongAnimationFrames() {
|
|
|
67
67
|
attributes["browser.script.source_char_position"] = sourceCharPosition;
|
|
68
68
|
}
|
|
69
69
|
startAndEndSpan(parent, startTime, startTime + duration, {
|
|
70
|
-
name:
|
|
70
|
+
name: UI_LONG_TASK_SPAN_NAME_FALLBACK,
|
|
71
71
|
op: UI_LONG_ANIMATION_FRAME,
|
|
72
72
|
attributes
|
|
73
73
|
});
|
|
@@ -99,7 +99,7 @@ function addPerformanceEntries(span, options) {
|
|
|
99
99
|
}
|
|
100
100
|
switch (entry.entryType) {
|
|
101
101
|
case "navigation": {
|
|
102
|
-
_addNavigationSpans(span, entry, timeOrigin);
|
|
102
|
+
_addNavigationSpans(span, entry, timeOrigin, spanStreamingEnabled);
|
|
103
103
|
break;
|
|
104
104
|
}
|
|
105
105
|
case "paint": {
|
|
@@ -127,23 +127,26 @@ function addPerformanceEntries(span, options) {
|
|
|
127
127
|
function _addPaintSpan(span, entry, startTime, duration, timeOrigin) {
|
|
128
128
|
const startTimestamp = timeOrigin + startTime;
|
|
129
129
|
startAndEndSpan(span, startTimestamp, startTimestamp + duration, {
|
|
130
|
+
// The entry name (`first-paint`, `first-contentful-paint`) is already the low-cardinality name
|
|
131
|
+
// the conventions ask for, so only the attribute backing it has to be added.
|
|
130
132
|
name: entry.name,
|
|
131
133
|
attributes: {
|
|
132
134
|
[SENTRY_OP]: BROWSER_PAINT,
|
|
133
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.resource.browser.metrics"
|
|
135
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.resource.browser.metrics",
|
|
136
|
+
[BROWSER_PAINT_TYPE]: entry.name
|
|
134
137
|
}
|
|
135
138
|
});
|
|
136
139
|
}
|
|
137
|
-
function _addNavigationSpans(span, entry, timeOrigin) {
|
|
138
|
-
_addPerformanceNavigationTiming(span, entry, "unloadEvent", timeOrigin);
|
|
139
|
-
_addPerformanceNavigationTiming(span, entry, "redirect", timeOrigin);
|
|
140
|
-
_addPerformanceNavigationTiming(span, entry, "domContentLoadedEvent", timeOrigin);
|
|
141
|
-
_addPerformanceNavigationTiming(span, entry, "loadEvent", timeOrigin);
|
|
142
|
-
_addPerformanceNavigationTiming(span, entry, "connect", timeOrigin);
|
|
143
|
-
_addPerformanceNavigationTiming(span, entry, "secureConnection", timeOrigin);
|
|
144
|
-
_addPerformanceNavigationTiming(span, entry, "fetch", timeOrigin);
|
|
145
|
-
_addPerformanceNavigationTiming(span, entry, "domainLookup", timeOrigin);
|
|
146
|
-
_addRequest(span, entry, timeOrigin);
|
|
140
|
+
function _addNavigationSpans(span, entry, timeOrigin, spanStreamingEnabled) {
|
|
141
|
+
_addPerformanceNavigationTiming(span, entry, "unloadEvent", timeOrigin, spanStreamingEnabled);
|
|
142
|
+
_addPerformanceNavigationTiming(span, entry, "redirect", timeOrigin, spanStreamingEnabled);
|
|
143
|
+
_addPerformanceNavigationTiming(span, entry, "domContentLoadedEvent", timeOrigin, spanStreamingEnabled);
|
|
144
|
+
_addPerformanceNavigationTiming(span, entry, "loadEvent", timeOrigin, spanStreamingEnabled);
|
|
145
|
+
_addPerformanceNavigationTiming(span, entry, "connect", timeOrigin, spanStreamingEnabled);
|
|
146
|
+
_addPerformanceNavigationTiming(span, entry, "secureConnection", timeOrigin, spanStreamingEnabled);
|
|
147
|
+
_addPerformanceNavigationTiming(span, entry, "fetch", timeOrigin, spanStreamingEnabled);
|
|
148
|
+
_addPerformanceNavigationTiming(span, entry, "domainLookup", timeOrigin, spanStreamingEnabled);
|
|
149
|
+
_addRequest(span, entry, timeOrigin, spanStreamingEnabled);
|
|
147
150
|
}
|
|
148
151
|
const NAVIGATION_TIMING_SPAN_OPS = {
|
|
149
152
|
secureConnection: BROWSER_TLS_SSL,
|
|
@@ -155,18 +158,22 @@ const NAVIGATION_TIMING_SPAN_OPS = {
|
|
|
155
158
|
domContentLoadedEvent: BROWSER_DOM_CONTENT_LOADED_EVENT,
|
|
156
159
|
loadEvent: BROWSER_LOAD_EVENT
|
|
157
160
|
};
|
|
158
|
-
function _addPerformanceNavigationTiming(span, entry, event, timeOrigin) {
|
|
161
|
+
function _addPerformanceNavigationTiming(span, entry, event, timeOrigin, spanStreamingEnabled) {
|
|
159
162
|
const eventEnd = _getEndPropertyNameForNavigationTiming(event);
|
|
160
163
|
const end = entry[eventEnd];
|
|
161
164
|
const start = entry[`${event}Start`];
|
|
162
165
|
if (!start || !end) {
|
|
163
166
|
return;
|
|
164
167
|
}
|
|
168
|
+
const op = NAVIGATION_TIMING_SPAN_OPS[event];
|
|
165
169
|
startAndEndSpan(span, timeOrigin + msToSec(start), timeOrigin + msToSec(end), {
|
|
166
|
-
|
|
170
|
+
// With span streaming, span names have to be low cardinality, so we can't fall back to the
|
|
171
|
+
// document URL. `url.full` keeps it, and is what Relay derives the description from.
|
|
172
|
+
name: spanStreamingEnabled ? BROWSER_NAVIGATION_TIMING_SPAN_NAMES[op] : entry.name,
|
|
167
173
|
attributes: {
|
|
168
|
-
[SENTRY_OP]:
|
|
174
|
+
[SENTRY_OP]: op,
|
|
169
175
|
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.browser.metrics",
|
|
176
|
+
[URL_FULL]: filterCollectedUrl(entry.name),
|
|
170
177
|
...event === "redirect" && entry.redirectCount != null ? { "http.redirect_count": entry.redirectCount } : {}
|
|
171
178
|
}
|
|
172
179
|
});
|
|
@@ -180,23 +187,26 @@ function _getEndPropertyNameForNavigationTiming(event) {
|
|
|
180
187
|
}
|
|
181
188
|
return `${event}End`;
|
|
182
189
|
}
|
|
183
|
-
function _addRequest(span, entry, timeOrigin) {
|
|
190
|
+
function _addRequest(span, entry, timeOrigin, spanStreamingEnabled) {
|
|
184
191
|
const requestStartTimestamp = timeOrigin + msToSec(entry.requestStart);
|
|
185
192
|
const responseEndTimestamp = timeOrigin + msToSec(entry.responseEnd);
|
|
186
193
|
const responseStartTimestamp = timeOrigin + msToSec(entry.responseStart);
|
|
187
194
|
if (entry.responseEnd) {
|
|
195
|
+
const url = filterCollectedUrl(entry.name);
|
|
188
196
|
startAndEndSpan(span, requestStartTimestamp, responseEndTimestamp, {
|
|
189
|
-
name: entry.name,
|
|
197
|
+
name: spanStreamingEnabled ? BROWSER_NAVIGATION_TIMING_SPAN_NAMES[BROWSER_REQUEST] : entry.name,
|
|
190
198
|
attributes: {
|
|
191
199
|
[SENTRY_OP]: BROWSER_REQUEST,
|
|
192
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.browser.metrics"
|
|
200
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.browser.metrics",
|
|
201
|
+
[URL_FULL]: url
|
|
193
202
|
}
|
|
194
203
|
});
|
|
195
204
|
startAndEndSpan(span, responseStartTimestamp, responseEndTimestamp, {
|
|
196
|
-
name: entry.name,
|
|
205
|
+
name: spanStreamingEnabled ? BROWSER_NAVIGATION_TIMING_SPAN_NAMES[BROWSER_RESPONSE] : entry.name,
|
|
197
206
|
attributes: {
|
|
198
207
|
[SENTRY_OP]: BROWSER_RESPONSE,
|
|
199
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.browser.metrics"
|
|
208
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.browser.metrics",
|
|
209
|
+
[URL_FULL]: url
|
|
200
210
|
}
|
|
201
211
|
});
|
|
202
212
|
}
|
|
@@ -295,5 +305,5 @@ function _setResourceRequestAttributes(entry, attributes, properties) {
|
|
|
295
305
|
});
|
|
296
306
|
}
|
|
297
307
|
|
|
298
|
-
export { _addNavigationSpans, _addResourceSpans, _setResourceRequestAttributes, addPerformanceEntries, startTrackingLongAnimationFrames, startTrackingLongTasks };
|
|
308
|
+
export { _addNavigationSpans, _addPaintSpan, _addResourceSpans, _setResourceRequestAttributes, addPerformanceEntries, startTrackingLongAnimationFrames, startTrackingLongTasks };
|
|
299
309
|
//# sourceMappingURL=entries.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entries.js","sources":["../../../src/performance/entries.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport type { Span, SpanAttributes } from '@sentry/core';\nimport {\n browserPerformanceTimeOrigin,\n getActiveSpan,\n parseUrl,\n RESOURCE_SPAN_NAME_FALLBACK,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n setMeasurement,\n spanToJSON,\n filterCollectedUrl,\n} from '@sentry/core';\nimport {\n CODE_FILE_PATH,\n CODE_FUNCTION_NAME,\n HTTP_REQUEST_SAME_ORIGIN,\n HTTP_RESPONSE_BODY_SIZE,\n HTTP_RESPONSE_SIZE,\n HTTP_RESPONSE_STATUS_CODE,\n NETWORK_CONNECTION_EFFECTIVE_TYPE,\n NETWORK_CONNECTION_RTT,\n NETWORK_CONNECTION_TYPE,\n SENTRY_OP,\n SERVER_ADDRESS,\n URL_DOMAIN,\n URL_FULL,\n URL_SCHEME,\n} from '@sentry/conventions/attributes';\nimport {\n BROWSER_CACHE,\n BROWSER_CONNECT,\n BROWSER_DNS,\n BROWSER_DOM_CONTENT_LOADED_EVENT,\n BROWSER_LOAD_EVENT,\n BROWSER_PAINT,\n BROWSER_REDIRECT,\n BROWSER_REQUEST,\n BROWSER_RESPONSE,\n BROWSER_TLS_SSL,\n BROWSER_UNLOAD_EVENT,\n RESOURCE_OTHER,\n UI_LONG_ANIMATION_FRAME,\n UI_LONG_TASK,\n} from '@sentry/conventions/op';\nimport {\n addPerformanceInstrumentationHandler,\n type PerformanceLongAnimationFrameTiming,\n} from '../instrumentation/performanceObserver';\nimport { WINDOW } from '../types';\nimport { resourceTimingToSpanAttributes } from './resourceTiming';\nimport { getBrowserPerformanceAPI, isMeasurementValue, msToSec, startAndEndSpan } from './utils';\n\ninterface NavigatorNetworkInformation {\n readonly connection?: NetworkInformation;\n}\n\n// http://wicg.github.io/netinfo/#connection-types\ntype ConnectionType = 'bluetooth' | 'cellular' | 'ethernet' | 'mixed' | 'none' | 'other' | 'unknown' | 'wifi' | 'wimax';\n\n// http://wicg.github.io/netinfo/#effectiveconnectiontype-enum\ntype EffectiveConnectionType = '2g' | '3g' | '4g' | 'slow-2g';\n\n// http://wicg.github.io/netinfo/#dom-megabit\ntype Megabit = number;\n// http://wicg.github.io/netinfo/#dom-millisecond\ntype Millisecond = number;\n\n// http://wicg.github.io/netinfo/#networkinformation-interface\ninterface NetworkInformation extends EventTarget {\n // http://wicg.github.io/netinfo/#type-attribute\n readonly type?: ConnectionType;\n // http://wicg.github.io/netinfo/#effectivetype-attribute\n readonly effectiveType?: EffectiveConnectionType;\n // http://wicg.github.io/netinfo/#downlinkmax-attribute\n readonly downlinkMax?: Megabit;\n // http://wicg.github.io/netinfo/#downlink-attribute\n readonly downlink?: Megabit;\n // http://wicg.github.io/netinfo/#rtt-attribute\n readonly rtt?: Millisecond;\n // http://wicg.github.io/netinfo/#savedata-attribute\n readonly saveData?: boolean;\n // http://wicg.github.io/netinfo/#handling-changes-to-the-underlying-connection\n onchange?: EventListener;\n}\n\n// https://w3c.github.io/device-memory/#sec-device-memory-js-api\ninterface NavigatorDeviceMemory {\n readonly deviceMemory?: number;\n}\n\nconst MAX_INT_AS_BYTES = 2147483647;\n\nlet _performanceCursor: number = 0;\n\n/**\n * Start tracking long tasks.\n */\nexport function startTrackingLongTasks(): void {\n addPerformanceInstrumentationHandler('longtask', ({ entries }) => {\n const parent = getActiveSpan();\n if (!parent) {\n return;\n }\n\n const { attributes: parentAttributes, start_timestamp: parentStartTimestamp } = spanToJSON(parent);\n\n for (const entry of entries) {\n const startTime = msToSec((browserPerformanceTimeOrigin() as number) + entry.startTime);\n const duration = msToSec(entry.duration);\n\n if (parentAttributes[SENTRY_OP] === 'navigation' && parentStartTimestamp && startTime < parentStartTimestamp) {\n // Skip adding a span if the long task started before the navigation started.\n // `startAndEndSpan` will otherwise adjust the parent's start time to the span's start\n // time, potentially skewing the duration of the actual navigation as reported via our\n // routing instrumentations\n continue;\n }\n\n startAndEndSpan(parent, startTime, startTime + duration, {\n name: 'Main UI thread blocked',\n op: UI_LONG_TASK,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n },\n });\n }\n });\n}\n\n/**\n * Start tracking long animation frames.\n */\nexport function startTrackingLongAnimationFrames(): void {\n // NOTE: the current web-vitals version (3.5.2) does not support long-animation-frame, so\n // we directly observe `long-animation-frame` events instead of through the web-vitals\n // `observe` helper function.\n const observer = new PerformanceObserver(list => {\n const parent = getActiveSpan();\n if (!parent) {\n return;\n }\n for (const entry of list.getEntries() as PerformanceLongAnimationFrameTiming[]) {\n if (!entry.scripts[0]) {\n continue;\n }\n\n const startTime = msToSec((browserPerformanceTimeOrigin() as number) + entry.startTime);\n\n const {\n start_timestamp: parentStartTimestamp,\n attributes: { [SENTRY_OP]: parentOp },\n } = spanToJSON(parent);\n\n if (parentOp === 'navigation' && parentStartTimestamp && startTime < parentStartTimestamp) {\n // Skip adding the span if the long animation frame started before the navigation started.\n // `startAndEndSpan` will otherwise adjust the parent's start time to the span's start\n // time, potentially skewing the duration of the actual navigation as reported via our\n // routing instrumentations\n continue;\n }\n const duration = msToSec(entry.duration);\n\n const attributes: SpanAttributes = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n };\n\n const initialScript = entry.scripts[0];\n const { invoker, invokerType, sourceURL, sourceFunctionName, sourceCharPosition } = initialScript;\n attributes['browser.script.invoker'] = invoker;\n attributes['browser.script.invoker_type'] = invokerType;\n if (sourceURL) {\n attributes[CODE_FILE_PATH] = sourceURL;\n }\n if (sourceFunctionName) {\n attributes[CODE_FUNCTION_NAME] = sourceFunctionName;\n }\n if (sourceCharPosition !== -1) {\n attributes['browser.script.source_char_position'] = sourceCharPosition;\n }\n\n startAndEndSpan(parent, startTime, startTime + duration, {\n name: 'Main UI thread blocked',\n op: UI_LONG_ANIMATION_FRAME,\n attributes,\n });\n }\n });\n\n observer.observe({ type: 'long-animation-frame', buffered: true });\n}\n\ninterface AddPerformanceEntriesOptions {\n /**\n * Resource spans with `op`s matching strings in the array will not be emitted.\n *\n * Default: []\n */\n ignoreResourceSpans: Array<'resource.script' | 'resource.css' | 'resource.img' | 'resource.other' | string>;\n\n /**\n * Whether span streaming is enabled.\n */\n spanStreamingEnabled?: boolean;\n}\n\n/** Add performance related spans to a transaction */\nexport function addPerformanceEntries(span: Span, options: AddPerformanceEntriesOptions): void {\n const performance = getBrowserPerformanceAPI();\n const origin = browserPerformanceTimeOrigin();\n if (!performance?.getEntries || !origin) {\n // Gatekeeper if performance API not available\n return;\n }\n\n const { spanStreamingEnabled, ignoreResourceSpans } = options;\n\n const timeOrigin = msToSec(origin);\n\n const performanceEntries = performance.getEntries();\n\n const { attributes, start_timestamp: transactionStartTime } = spanToJSON(span);\n\n performanceEntries.slice(_performanceCursor).forEach(entry => {\n const startTime = msToSec(entry.startTime);\n const duration = msToSec(\n // Inexplicably, Chrome sometimes emits a negative duration. We need to work around this.\n // There is a SO post attempting to explain this, but it leaves one with open questions: https://stackoverflow.com/questions/23191918/peformance-getentries-and-negative-duration-display\n // The way we clamp the value is probably not accurate, since we have observed this happen for things that may take a while to load, like for example the replay worker.\n // TODO: Investigate why this happens and how to properly mitigate. For now, this is a workaround to prevent transactions being dropped due to negative duration spans.\n Math.max(0, entry.duration),\n );\n\n if (\n attributes[SENTRY_OP] === 'navigation' &&\n transactionStartTime &&\n timeOrigin + startTime < transactionStartTime\n ) {\n return;\n }\n\n switch (entry.entryType) {\n case 'navigation': {\n _addNavigationSpans(span, entry as PerformanceNavigationTiming, timeOrigin);\n break;\n }\n case 'paint': {\n _addPaintSpan(span, entry, startTime, duration, timeOrigin);\n break;\n }\n case 'resource': {\n _addResourceSpans(\n span,\n entry as PerformanceResourceTiming,\n entry.name,\n startTime,\n duration,\n timeOrigin,\n ignoreResourceSpans,\n spanStreamingEnabled,\n );\n break;\n }\n // Ignore other entry types.\n }\n });\n\n _performanceCursor = Math.max(performanceEntries.length - 1, 0);\n\n _trackNavigator(span, spanStreamingEnabled);\n}\n\n/** Create a span for a browser paint performance entry. */\nfunction _addPaintSpan(\n span: Span,\n entry: PerformanceEntry,\n startTime: number,\n duration: number,\n timeOrigin: number,\n): void {\n const startTimestamp = timeOrigin + startTime;\n\n startAndEndSpan(span, startTimestamp, startTimestamp + duration, {\n name: entry.name,\n attributes: {\n [SENTRY_OP]: BROWSER_PAINT,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.resource.browser.metrics',\n },\n });\n}\n\n/**\n * Instrument navigation entries\n * exported only for tests\n */\nexport function _addNavigationSpans(span: Span, entry: PerformanceNavigationTiming, timeOrigin: number): void {\n _addPerformanceNavigationTiming(span, entry, 'unloadEvent', timeOrigin);\n _addPerformanceNavigationTiming(span, entry, 'redirect', timeOrigin);\n _addPerformanceNavigationTiming(span, entry, 'domContentLoadedEvent', timeOrigin);\n _addPerformanceNavigationTiming(span, entry, 'loadEvent', timeOrigin);\n _addPerformanceNavigationTiming(span, entry, 'connect', timeOrigin);\n _addPerformanceNavigationTiming(span, entry, 'secureConnection', timeOrigin);\n _addPerformanceNavigationTiming(span, entry, 'fetch', timeOrigin);\n _addPerformanceNavigationTiming(span, entry, 'domainLookup', timeOrigin);\n\n _addRequest(span, entry, timeOrigin);\n}\n\ntype StartEventName =\n | 'secureConnection'\n | 'fetch'\n | 'domainLookup'\n | 'unloadEvent'\n | 'redirect'\n | 'connect'\n | 'domContentLoadedEvent'\n | 'loadEvent';\n\nconst NAVIGATION_TIMING_SPAN_OPS: Record<StartEventName, string> = {\n secureConnection: BROWSER_TLS_SSL,\n fetch: BROWSER_CACHE,\n domainLookup: BROWSER_DNS,\n unloadEvent: BROWSER_UNLOAD_EVENT,\n redirect: BROWSER_REDIRECT,\n connect: BROWSER_CONNECT,\n domContentLoadedEvent: BROWSER_DOM_CONTENT_LOADED_EVENT,\n loadEvent: BROWSER_LOAD_EVENT,\n};\n\ntype EndEventName =\n | 'domainLookupStart'\n | 'domainLookupEnd'\n | 'unloadEventEnd'\n | 'redirectEnd'\n | 'connectEnd'\n | 'domContentLoadedEventEnd'\n | 'loadEventEnd';\n\n/** Create performance navigation related spans */\nfunction _addPerformanceNavigationTiming(\n span: Span,\n entry: PerformanceNavigationTiming,\n event: StartEventName,\n timeOrigin: number,\n): void {\n const eventEnd = _getEndPropertyNameForNavigationTiming(event) satisfies keyof PerformanceNavigationTiming;\n const end = entry[eventEnd];\n const start = entry[`${event}Start`];\n if (!start || !end) {\n return;\n }\n startAndEndSpan(span, timeOrigin + msToSec(start), timeOrigin + msToSec(end), {\n name: entry.name,\n attributes: {\n [SENTRY_OP]: NAVIGATION_TIMING_SPAN_OPS[event],\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n ...(event === 'redirect' && entry.redirectCount != null ? { 'http.redirect_count': entry.redirectCount } : {}),\n },\n });\n}\n\nfunction _getEndPropertyNameForNavigationTiming(event: StartEventName): EndEventName {\n if (event === 'secureConnection') {\n return 'connectEnd';\n }\n if (event === 'fetch') {\n return 'domainLookupStart';\n }\n return `${event}End`;\n}\n\n/** Create request and response related spans */\nfunction _addRequest(span: Span, entry: PerformanceNavigationTiming, timeOrigin: number): void {\n const requestStartTimestamp = timeOrigin + msToSec(entry.requestStart);\n const responseEndTimestamp = timeOrigin + msToSec(entry.responseEnd);\n const responseStartTimestamp = timeOrigin + msToSec(entry.responseStart);\n if (entry.responseEnd) {\n // It is possible that we are collecting these metrics when the page hasn't finished loading yet, for example when the HTML slowly streams in.\n // In this case, ie. when the document request hasn't finished yet, `entry.responseEnd` will be 0.\n // In order not to produce faulty spans, where the end timestamp is before the start timestamp, we will only collect\n // these spans when the responseEnd value is available. The backend (Relay) would drop the entire span if it contained faulty spans.\n startAndEndSpan(span, requestStartTimestamp, responseEndTimestamp, {\n name: entry.name,\n attributes: {\n [SENTRY_OP]: BROWSER_REQUEST,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n },\n });\n\n startAndEndSpan(span, responseStartTimestamp, responseEndTimestamp, {\n name: entry.name,\n attributes: {\n [SENTRY_OP]: BROWSER_RESPONSE,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n },\n });\n }\n}\n\n/**\n * Create resource-related spans.\n * Exported only for tests.\n */\nexport function _addResourceSpans(\n span: Span,\n entry: PerformanceResourceTiming,\n resourceUrl: string,\n startTime: number,\n duration: number,\n timeOrigin: number,\n ignoredResourceSpanOps?: Array<string>,\n spanStreamingEnabled?: boolean,\n): void {\n // we already instrument based on fetch and xhr, so we don't need to\n // duplicate spans here.\n if (entry.initiatorType === 'xmlhttprequest' || entry.initiatorType === 'fetch') {\n return;\n }\n\n const op = entry.initiatorType ? `resource.${entry.initiatorType}` : RESOURCE_OTHER;\n if (ignoredResourceSpanOps?.includes(op)) {\n return;\n }\n\n const attributes: SpanAttributes = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.resource.browser.metrics',\n };\n\n const parsedUrl = parseUrl(resourceUrl);\n\n if (parsedUrl.protocol) {\n attributes[URL_SCHEME] = parsedUrl.protocol.split(':').pop(); // the protocol returned by parseUrl includes a :, but OTEL spec does not, so we remove it.\n }\n\n // `host` is the URL authority, so it can carry userinfo, which doesn't belong on either attribute.\n const host = parsedUrl.host?.replace(/^.*@/, '');\n\n if (host) {\n attributes[SERVER_ADDRESS] = host;\n }\n\n // Unlike `server.address`, `url.domain` excludes the port.\n const domain = host?.replace(/:\\d+$/, '');\n\n if (domain) {\n attributes[URL_DOMAIN] = domain;\n }\n\n attributes[HTTP_REQUEST_SAME_ORIGIN] = resourceUrl.includes(WINDOW.location.origin);\n\n attributes[URL_FULL] = filterCollectedUrl(resourceUrl);\n\n _setResourceRequestAttributes(entry, attributes, [\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus\n ['responseStatus', HTTP_RESPONSE_STATUS_CODE],\n\n ['transferSize', HTTP_RESPONSE_SIZE],\n ['encodedBodySize', HTTP_RESPONSE_BODY_SIZE],\n ['decodedBodySize', 'http.response.body.decoded_size'],\n\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/renderBlockingStatus\n ['renderBlockingStatus', 'resource.render_blocking_status'],\n\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/deliveryType\n ['deliveryType', 'http.response_delivery_type'],\n ]);\n\n const attributesWithResourceTiming: SpanAttributes = { ...attributes, ...resourceTimingToSpanAttributes(entry) };\n\n const startTimestamp = timeOrigin + startTime;\n const endTimestamp = startTimestamp + duration;\n\n startAndEndSpan(span, startTimestamp, endTimestamp, {\n // With span streaming, span names have to be low cardinality, so we can't fall back to the URL.\n name: spanStreamingEnabled\n ? domain || RESOURCE_SPAN_NAME_FALLBACK\n : resourceUrl.replace(WINDOW.location.origin, ''),\n op,\n attributes: attributesWithResourceTiming,\n });\n}\n\n/**\n * Capture the information of the user agent.\n * TODO(v12): Remove non-span-streaming attributes and measurements once the static trace lifecycle is removed\n */\nfunction _trackNavigator(span: Span, spanStreamingEnabled: boolean | undefined): void {\n const navigator = WINDOW.navigator as null | (Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory);\n if (!navigator) {\n return;\n }\n\n // track network connectivity\n const connection = navigator.connection;\n if (connection) {\n if (connection.effectiveType) {\n span.setAttribute(\n spanStreamingEnabled ? NETWORK_CONNECTION_EFFECTIVE_TYPE : 'effectiveConnectionType',\n connection.effectiveType,\n );\n }\n\n if (connection.type) {\n span.setAttribute(spanStreamingEnabled ? NETWORK_CONNECTION_TYPE : 'connectionType', connection.type);\n }\n\n if (isMeasurementValue(connection.rtt)) {\n if (spanStreamingEnabled) {\n span.setAttribute(NETWORK_CONNECTION_RTT, connection.rtt);\n } else if (spanToJSON(span).attributes[SENTRY_OP] === 'pageload') {\n // Measurements are only recorded on the pageload span, matching the historical\n // behavior where `connection.rtt` was only flushed for pageload transactions.\n setMeasurement('connection.rtt', connection.rtt, 'millisecond');\n }\n }\n }\n\n if (isMeasurementValue(navigator.deviceMemory)) {\n if (spanStreamingEnabled) {\n span.setAttribute('device.memory.estimated_capacity', navigator.deviceMemory);\n } else {\n span.setAttribute('deviceMemory', `${navigator.deviceMemory} GB`);\n }\n }\n\n if (isMeasurementValue(navigator.hardwareConcurrency)) {\n if (spanStreamingEnabled) {\n span.setAttribute('device.processor_count', navigator.hardwareConcurrency);\n } else {\n span.setAttribute('hardwareConcurrency', String(navigator.hardwareConcurrency));\n }\n }\n}\n\ntype ExperimentalResourceTimingProperty =\n | 'renderBlockingStatus'\n | 'deliveryType'\n // For some reason, TS during build, errors on `responseStatus` not being a property of\n // PerformanceResourceTiming while it actually is. Hence, we're adding it here.\n // Perhaps because response status is not yet available in Webkit/Safari.\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus\n | 'responseStatus';\n\n/**\n * Use this to set any attributes we can take directly form the PerformanceResourceTiming entry.\n *\n * This is just a mapping function for entry->attribute to keep bundle-size minimal.\n * Experimental properties are also accepted (see {@link ExperimentalResourceTimingProperty}).\n * Assumes that all entry properties might be undefined for browser-specific differences.\n * Only accepts string and number values for now and also sets 0-values.\n */\nexport function _setResourceRequestAttributes(\n entry: Partial<PerformanceResourceTiming> & Partial<Record<ExperimentalResourceTimingProperty, number | string>>,\n attributes: SpanAttributes,\n properties: [keyof PerformanceResourceTiming | ExperimentalResourceTimingProperty, string][],\n): void {\n properties.forEach(([entryKey, attributeKey]) => {\n const entryVal = entry[entryKey];\n if (\n entryVal != null &&\n ((typeof entryVal === 'number' && entryVal < MAX_INT_AS_BYTES) || typeof entryVal === 'string')\n ) {\n attributes[attributeKey] = entryVal;\n }\n });\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"entries.js","sources":["../../../src/performance/entries.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport type { Span, SpanAttributes } from '@sentry/core';\nimport {\n BROWSER_NAVIGATION_TIMING_SPAN_NAMES,\n browserPerformanceTimeOrigin,\n getActiveSpan,\n parseUrl,\n RESOURCE_SPAN_NAME_FALLBACK,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n setMeasurement,\n spanToJSON,\n filterCollectedUrl,\n UI_LONG_TASK_SPAN_NAME_FALLBACK,\n} from '@sentry/core';\nimport {\n BROWSER_PAINT_TYPE,\n CODE_FILE_PATH,\n CODE_FUNCTION_NAME,\n HTTP_REQUEST_SAME_ORIGIN,\n HTTP_RESPONSE_BODY_SIZE,\n HTTP_RESPONSE_SIZE,\n HTTP_RESPONSE_STATUS_CODE,\n NETWORK_CONNECTION_EFFECTIVE_TYPE,\n NETWORK_CONNECTION_RTT,\n NETWORK_CONNECTION_TYPE,\n SENTRY_OP,\n SERVER_ADDRESS,\n URL_DOMAIN,\n URL_FULL,\n URL_SCHEME,\n} from '@sentry/conventions/attributes';\nimport {\n BROWSER_CACHE,\n BROWSER_CONNECT,\n BROWSER_DNS,\n BROWSER_DOM_CONTENT_LOADED_EVENT,\n BROWSER_LOAD_EVENT,\n BROWSER_PAINT,\n BROWSER_REDIRECT,\n BROWSER_REQUEST,\n BROWSER_RESPONSE,\n BROWSER_TLS_SSL,\n BROWSER_UNLOAD_EVENT,\n RESOURCE_OTHER,\n UI_LONG_ANIMATION_FRAME,\n UI_LONG_TASK,\n} from '@sentry/conventions/op';\nimport {\n addPerformanceInstrumentationHandler,\n type PerformanceLongAnimationFrameTiming,\n} from '../instrumentation/performanceObserver';\nimport { WINDOW } from '../types';\nimport { resourceTimingToSpanAttributes } from './resourceTiming';\nimport { getBrowserPerformanceAPI, isMeasurementValue, msToSec, startAndEndSpan } from './utils';\n\ninterface NavigatorNetworkInformation {\n readonly connection?: NetworkInformation;\n}\n\n// http://wicg.github.io/netinfo/#connection-types\ntype ConnectionType = 'bluetooth' | 'cellular' | 'ethernet' | 'mixed' | 'none' | 'other' | 'unknown' | 'wifi' | 'wimax';\n\n// http://wicg.github.io/netinfo/#effectiveconnectiontype-enum\ntype EffectiveConnectionType = '2g' | '3g' | '4g' | 'slow-2g';\n\n// http://wicg.github.io/netinfo/#dom-megabit\ntype Megabit = number;\n// http://wicg.github.io/netinfo/#dom-millisecond\ntype Millisecond = number;\n\n// http://wicg.github.io/netinfo/#networkinformation-interface\ninterface NetworkInformation extends EventTarget {\n // http://wicg.github.io/netinfo/#type-attribute\n readonly type?: ConnectionType;\n // http://wicg.github.io/netinfo/#effectivetype-attribute\n readonly effectiveType?: EffectiveConnectionType;\n // http://wicg.github.io/netinfo/#downlinkmax-attribute\n readonly downlinkMax?: Megabit;\n // http://wicg.github.io/netinfo/#downlink-attribute\n readonly downlink?: Megabit;\n // http://wicg.github.io/netinfo/#rtt-attribute\n readonly rtt?: Millisecond;\n // http://wicg.github.io/netinfo/#savedata-attribute\n readonly saveData?: boolean;\n // http://wicg.github.io/netinfo/#handling-changes-to-the-underlying-connection\n onchange?: EventListener;\n}\n\n// https://w3c.github.io/device-memory/#sec-device-memory-js-api\ninterface NavigatorDeviceMemory {\n readonly deviceMemory?: number;\n}\n\nconst MAX_INT_AS_BYTES = 2147483647;\n\nlet _performanceCursor: number = 0;\n\n/**\n * Start tracking long tasks.\n */\nexport function startTrackingLongTasks(): void {\n addPerformanceInstrumentationHandler('longtask', ({ entries }) => {\n const parent = getActiveSpan();\n if (!parent) {\n return;\n }\n\n const { attributes: parentAttributes, start_timestamp: parentStartTimestamp } = spanToJSON(parent);\n\n for (const entry of entries) {\n const startTime = msToSec((browserPerformanceTimeOrigin() as number) + entry.startTime);\n const duration = msToSec(entry.duration);\n\n if (parentAttributes[SENTRY_OP] === 'navigation' && parentStartTimestamp && startTime < parentStartTimestamp) {\n // Skip adding a span if the long task started before the navigation started.\n // `startAndEndSpan` will otherwise adjust the parent's start time to the span's start\n // time, potentially skewing the duration of the actual navigation as reported via our\n // routing instrumentations\n continue;\n }\n\n startAndEndSpan(parent, startTime, startTime + duration, {\n name: UI_LONG_TASK_SPAN_NAME_FALLBACK,\n op: UI_LONG_TASK,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n },\n });\n }\n });\n}\n\n/**\n * Start tracking long animation frames.\n */\nexport function startTrackingLongAnimationFrames(): void {\n // NOTE: the current web-vitals version (3.5.2) does not support long-animation-frame, so\n // we directly observe `long-animation-frame` events instead of through the web-vitals\n // `observe` helper function.\n const observer = new PerformanceObserver(list => {\n const parent = getActiveSpan();\n if (!parent) {\n return;\n }\n for (const entry of list.getEntries() as PerformanceLongAnimationFrameTiming[]) {\n if (!entry.scripts[0]) {\n continue;\n }\n\n const startTime = msToSec((browserPerformanceTimeOrigin() as number) + entry.startTime);\n\n const {\n start_timestamp: parentStartTimestamp,\n attributes: { [SENTRY_OP]: parentOp },\n } = spanToJSON(parent);\n\n if (parentOp === 'navigation' && parentStartTimestamp && startTime < parentStartTimestamp) {\n // Skip adding the span if the long animation frame started before the navigation started.\n // `startAndEndSpan` will otherwise adjust the parent's start time to the span's start\n // time, potentially skewing the duration of the actual navigation as reported via our\n // routing instrumentations\n continue;\n }\n const duration = msToSec(entry.duration);\n\n const attributes: SpanAttributes = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n };\n\n const initialScript = entry.scripts[0];\n const { invoker, invokerType, sourceURL, sourceFunctionName, sourceCharPosition } = initialScript;\n attributes['browser.script.invoker'] = invoker;\n attributes['browser.script.invoker_type'] = invokerType;\n if (sourceURL) {\n attributes[CODE_FILE_PATH] = sourceURL;\n }\n if (sourceFunctionName) {\n attributes[CODE_FUNCTION_NAME] = sourceFunctionName;\n }\n if (sourceCharPosition !== -1) {\n attributes['browser.script.source_char_position'] = sourceCharPosition;\n }\n\n startAndEndSpan(parent, startTime, startTime + duration, {\n name: UI_LONG_TASK_SPAN_NAME_FALLBACK,\n op: UI_LONG_ANIMATION_FRAME,\n attributes,\n });\n }\n });\n\n observer.observe({ type: 'long-animation-frame', buffered: true });\n}\n\ninterface AddPerformanceEntriesOptions {\n /**\n * Resource spans with `op`s matching strings in the array will not be emitted.\n *\n * Default: []\n */\n ignoreResourceSpans: Array<'resource.script' | 'resource.css' | 'resource.img' | 'resource.other' | string>;\n\n /**\n * Whether span streaming is enabled.\n */\n spanStreamingEnabled?: boolean;\n}\n\n/** Add performance related spans to a transaction */\nexport function addPerformanceEntries(span: Span, options: AddPerformanceEntriesOptions): void {\n const performance = getBrowserPerformanceAPI();\n const origin = browserPerformanceTimeOrigin();\n if (!performance?.getEntries || !origin) {\n // Gatekeeper if performance API not available\n return;\n }\n\n const { spanStreamingEnabled, ignoreResourceSpans } = options;\n\n const timeOrigin = msToSec(origin);\n\n const performanceEntries = performance.getEntries();\n\n const { attributes, start_timestamp: transactionStartTime } = spanToJSON(span);\n\n performanceEntries.slice(_performanceCursor).forEach(entry => {\n const startTime = msToSec(entry.startTime);\n const duration = msToSec(\n // Inexplicably, Chrome sometimes emits a negative duration. We need to work around this.\n // There is a SO post attempting to explain this, but it leaves one with open questions: https://stackoverflow.com/questions/23191918/peformance-getentries-and-negative-duration-display\n // The way we clamp the value is probably not accurate, since we have observed this happen for things that may take a while to load, like for example the replay worker.\n // TODO: Investigate why this happens and how to properly mitigate. For now, this is a workaround to prevent transactions being dropped due to negative duration spans.\n Math.max(0, entry.duration),\n );\n\n if (\n attributes[SENTRY_OP] === 'navigation' &&\n transactionStartTime &&\n timeOrigin + startTime < transactionStartTime\n ) {\n return;\n }\n\n switch (entry.entryType) {\n case 'navigation': {\n _addNavigationSpans(span, entry as PerformanceNavigationTiming, timeOrigin, spanStreamingEnabled);\n break;\n }\n case 'paint': {\n _addPaintSpan(span, entry, startTime, duration, timeOrigin);\n break;\n }\n case 'resource': {\n _addResourceSpans(\n span,\n entry as PerformanceResourceTiming,\n entry.name,\n startTime,\n duration,\n timeOrigin,\n ignoreResourceSpans,\n spanStreamingEnabled,\n );\n break;\n }\n // Ignore other entry types.\n }\n });\n\n _performanceCursor = Math.max(performanceEntries.length - 1, 0);\n\n _trackNavigator(span, spanStreamingEnabled);\n}\n\n/**\n * Create a span for a browser paint performance entry.\n * Exported only for tests.\n */\nexport function _addPaintSpan(\n span: Span,\n entry: PerformanceEntry,\n startTime: number,\n duration: number,\n timeOrigin: number,\n): void {\n const startTimestamp = timeOrigin + startTime;\n\n startAndEndSpan(span, startTimestamp, startTimestamp + duration, {\n // The entry name (`first-paint`, `first-contentful-paint`) is already the low-cardinality name\n // the conventions ask for, so only the attribute backing it has to be added.\n name: entry.name,\n attributes: {\n [SENTRY_OP]: BROWSER_PAINT,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.resource.browser.metrics',\n [BROWSER_PAINT_TYPE]: entry.name,\n },\n });\n}\n\n/**\n * Instrument navigation entries\n * exported only for tests\n */\nexport function _addNavigationSpans(\n span: Span,\n entry: PerformanceNavigationTiming,\n timeOrigin: number,\n spanStreamingEnabled?: boolean,\n): void {\n _addPerformanceNavigationTiming(span, entry, 'unloadEvent', timeOrigin, spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'redirect', timeOrigin, spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'domContentLoadedEvent', timeOrigin, spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'loadEvent', timeOrigin, spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'connect', timeOrigin, spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'secureConnection', timeOrigin, spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'fetch', timeOrigin, spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'domainLookup', timeOrigin, spanStreamingEnabled);\n\n _addRequest(span, entry, timeOrigin, spanStreamingEnabled);\n}\n\ntype StartEventName =\n | 'secureConnection'\n | 'fetch'\n | 'domainLookup'\n | 'unloadEvent'\n | 'redirect'\n | 'connect'\n | 'domContentLoadedEvent'\n | 'loadEvent';\n\nconst NAVIGATION_TIMING_SPAN_OPS = {\n secureConnection: BROWSER_TLS_SSL,\n fetch: BROWSER_CACHE,\n domainLookup: BROWSER_DNS,\n unloadEvent: BROWSER_UNLOAD_EVENT,\n redirect: BROWSER_REDIRECT,\n connect: BROWSER_CONNECT,\n domContentLoadedEvent: BROWSER_DOM_CONTENT_LOADED_EVENT,\n loadEvent: BROWSER_LOAD_EVENT,\n} as const satisfies Record<StartEventName, string>;\n\ntype EndEventName =\n | 'domainLookupStart'\n | 'domainLookupEnd'\n | 'unloadEventEnd'\n | 'redirectEnd'\n | 'connectEnd'\n | 'domContentLoadedEventEnd'\n | 'loadEventEnd';\n\n/** Create performance navigation related spans */\nfunction _addPerformanceNavigationTiming(\n span: Span,\n entry: PerformanceNavigationTiming,\n event: StartEventName,\n timeOrigin: number,\n spanStreamingEnabled: boolean | undefined,\n): void {\n const eventEnd = _getEndPropertyNameForNavigationTiming(event) satisfies keyof PerformanceNavigationTiming;\n const end = entry[eventEnd];\n const start = entry[`${event}Start`];\n if (!start || !end) {\n return;\n }\n const op = NAVIGATION_TIMING_SPAN_OPS[event];\n startAndEndSpan(span, timeOrigin + msToSec(start), timeOrigin + msToSec(end), {\n // With span streaming, span names have to be low cardinality, so we can't fall back to the\n // document URL. `url.full` keeps it, and is what Relay derives the description from.\n name: spanStreamingEnabled ? BROWSER_NAVIGATION_TIMING_SPAN_NAMES[op] : entry.name,\n attributes: {\n [SENTRY_OP]: op,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n [URL_FULL]: filterCollectedUrl(entry.name),\n ...(event === 'redirect' && entry.redirectCount != null ? { 'http.redirect_count': entry.redirectCount } : {}),\n },\n });\n}\n\nfunction _getEndPropertyNameForNavigationTiming(event: StartEventName): EndEventName {\n if (event === 'secureConnection') {\n return 'connectEnd';\n }\n if (event === 'fetch') {\n return 'domainLookupStart';\n }\n return `${event}End`;\n}\n\n/** Create request and response related spans */\nfunction _addRequest(\n span: Span,\n entry: PerformanceNavigationTiming,\n timeOrigin: number,\n spanStreamingEnabled: boolean | undefined,\n): void {\n const requestStartTimestamp = timeOrigin + msToSec(entry.requestStart);\n const responseEndTimestamp = timeOrigin + msToSec(entry.responseEnd);\n const responseStartTimestamp = timeOrigin + msToSec(entry.responseStart);\n if (entry.responseEnd) {\n // It is possible that we are collecting these metrics when the page hasn't finished loading yet, for example when the HTML slowly streams in.\n // In this case, ie. when the document request hasn't finished yet, `entry.responseEnd` will be 0.\n // In order not to produce faulty spans, where the end timestamp is before the start timestamp, we will only collect\n // these spans when the responseEnd value is available. The backend (Relay) would drop the entire span if it contained faulty spans.\n\n // With span streaming, span names have to be low cardinality, so we can't fall back to the\n // document URL. `url.full` keeps it, and is what Relay derives the description from.\n const url = filterCollectedUrl(entry.name);\n\n startAndEndSpan(span, requestStartTimestamp, responseEndTimestamp, {\n name: spanStreamingEnabled ? BROWSER_NAVIGATION_TIMING_SPAN_NAMES[BROWSER_REQUEST] : entry.name,\n attributes: {\n [SENTRY_OP]: BROWSER_REQUEST,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n [URL_FULL]: url,\n },\n });\n\n startAndEndSpan(span, responseStartTimestamp, responseEndTimestamp, {\n name: spanStreamingEnabled ? BROWSER_NAVIGATION_TIMING_SPAN_NAMES[BROWSER_RESPONSE] : entry.name,\n attributes: {\n [SENTRY_OP]: BROWSER_RESPONSE,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n [URL_FULL]: url,\n },\n });\n }\n}\n\n/**\n * Create resource-related spans.\n * Exported only for tests.\n */\nexport function _addResourceSpans(\n span: Span,\n entry: PerformanceResourceTiming,\n resourceUrl: string,\n startTime: number,\n duration: number,\n timeOrigin: number,\n ignoredResourceSpanOps?: Array<string>,\n spanStreamingEnabled?: boolean,\n): void {\n // we already instrument based on fetch and xhr, so we don't need to\n // duplicate spans here.\n if (entry.initiatorType === 'xmlhttprequest' || entry.initiatorType === 'fetch') {\n return;\n }\n\n const op = entry.initiatorType ? `resource.${entry.initiatorType}` : RESOURCE_OTHER;\n if (ignoredResourceSpanOps?.includes(op)) {\n return;\n }\n\n const attributes: SpanAttributes = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.resource.browser.metrics',\n };\n\n const parsedUrl = parseUrl(resourceUrl);\n\n if (parsedUrl.protocol) {\n attributes[URL_SCHEME] = parsedUrl.protocol.split(':').pop(); // the protocol returned by parseUrl includes a :, but OTEL spec does not, so we remove it.\n }\n\n // `host` is the URL authority, so it can carry userinfo, which doesn't belong on either attribute.\n const host = parsedUrl.host?.replace(/^.*@/, '');\n\n if (host) {\n attributes[SERVER_ADDRESS] = host;\n }\n\n // Unlike `server.address`, `url.domain` excludes the port.\n const domain = host?.replace(/:\\d+$/, '');\n\n if (domain) {\n attributes[URL_DOMAIN] = domain;\n }\n\n attributes[HTTP_REQUEST_SAME_ORIGIN] = resourceUrl.includes(WINDOW.location.origin);\n\n attributes[URL_FULL] = filterCollectedUrl(resourceUrl);\n\n _setResourceRequestAttributes(entry, attributes, [\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus\n ['responseStatus', HTTP_RESPONSE_STATUS_CODE],\n\n ['transferSize', HTTP_RESPONSE_SIZE],\n ['encodedBodySize', HTTP_RESPONSE_BODY_SIZE],\n ['decodedBodySize', 'http.response.body.decoded_size'],\n\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/renderBlockingStatus\n ['renderBlockingStatus', 'resource.render_blocking_status'],\n\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/deliveryType\n ['deliveryType', 'http.response_delivery_type'],\n ]);\n\n const attributesWithResourceTiming: SpanAttributes = { ...attributes, ...resourceTimingToSpanAttributes(entry) };\n\n const startTimestamp = timeOrigin + startTime;\n const endTimestamp = startTimestamp + duration;\n\n startAndEndSpan(span, startTimestamp, endTimestamp, {\n // With span streaming, span names have to be low cardinality, so we can't fall back to the URL.\n name: spanStreamingEnabled\n ? domain || RESOURCE_SPAN_NAME_FALLBACK\n : resourceUrl.replace(WINDOW.location.origin, ''),\n op,\n attributes: attributesWithResourceTiming,\n });\n}\n\n/**\n * Capture the information of the user agent.\n * TODO(v12): Remove non-span-streaming attributes and measurements once the static trace lifecycle is removed\n */\nfunction _trackNavigator(span: Span, spanStreamingEnabled: boolean | undefined): void {\n const navigator = WINDOW.navigator as null | (Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory);\n if (!navigator) {\n return;\n }\n\n // track network connectivity\n const connection = navigator.connection;\n if (connection) {\n if (connection.effectiveType) {\n span.setAttribute(\n spanStreamingEnabled ? NETWORK_CONNECTION_EFFECTIVE_TYPE : 'effectiveConnectionType',\n connection.effectiveType,\n );\n }\n\n if (connection.type) {\n span.setAttribute(spanStreamingEnabled ? NETWORK_CONNECTION_TYPE : 'connectionType', connection.type);\n }\n\n if (isMeasurementValue(connection.rtt)) {\n if (spanStreamingEnabled) {\n span.setAttribute(NETWORK_CONNECTION_RTT, connection.rtt);\n } else if (spanToJSON(span).attributes[SENTRY_OP] === 'pageload') {\n // Measurements are only recorded on the pageload span, matching the historical\n // behavior where `connection.rtt` was only flushed for pageload transactions.\n setMeasurement('connection.rtt', connection.rtt, 'millisecond');\n }\n }\n }\n\n if (isMeasurementValue(navigator.deviceMemory)) {\n if (spanStreamingEnabled) {\n span.setAttribute('device.memory.estimated_capacity', navigator.deviceMemory);\n } else {\n span.setAttribute('deviceMemory', `${navigator.deviceMemory} GB`);\n }\n }\n\n if (isMeasurementValue(navigator.hardwareConcurrency)) {\n if (spanStreamingEnabled) {\n span.setAttribute('device.processor_count', navigator.hardwareConcurrency);\n } else {\n span.setAttribute('hardwareConcurrency', String(navigator.hardwareConcurrency));\n }\n }\n}\n\ntype ExperimentalResourceTimingProperty =\n | 'renderBlockingStatus'\n | 'deliveryType'\n // For some reason, TS during build, errors on `responseStatus` not being a property of\n // PerformanceResourceTiming while it actually is. Hence, we're adding it here.\n // Perhaps because response status is not yet available in Webkit/Safari.\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus\n | 'responseStatus';\n\n/**\n * Use this to set any attributes we can take directly form the PerformanceResourceTiming entry.\n *\n * This is just a mapping function for entry->attribute to keep bundle-size minimal.\n * Experimental properties are also accepted (see {@link ExperimentalResourceTimingProperty}).\n * Assumes that all entry properties might be undefined for browser-specific differences.\n * Only accepts string and number values for now and also sets 0-values.\n */\nexport function _setResourceRequestAttributes(\n entry: Partial<PerformanceResourceTiming> & Partial<Record<ExperimentalResourceTimingProperty, number | string>>,\n attributes: SpanAttributes,\n properties: [keyof PerformanceResourceTiming | ExperimentalResourceTimingProperty, string][],\n): void {\n properties.forEach(([entryKey, attributeKey]) => {\n const entryVal = entry[entryKey];\n if (\n entryVal != null &&\n ((typeof entryVal === 'number' && entryVal < MAX_INT_AS_BYTES) || typeof entryVal === 'string')\n ) {\n attributes[attributeKey] = entryVal;\n }\n });\n}\n"],"names":[],"mappings":"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"}
|