mirror of
https://github.com/MarSeventh/CloudFlare-ImgBed.git
synced 2026-04-26 23:25:11 +00:00
init
This commit is contained in:
39
node_modules/@sentry-internal/tracing/esm/browser/backgroundtab.js
generated
vendored
Normal file
39
node_modules/@sentry-internal/tracing/esm/browser/backgroundtab.js
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
import { getActiveTransaction, spanToJSON } from '@sentry/core';
|
||||
import { logger } from '@sentry/utils';
|
||||
import { DEBUG_BUILD } from '../common/debug-build.js';
|
||||
import { WINDOW } from './types.js';
|
||||
|
||||
/**
|
||||
* Add a listener that cancels and finishes a transaction when the global
|
||||
* document is hidden.
|
||||
*/
|
||||
function registerBackgroundTabDetection() {
|
||||
if (WINDOW.document) {
|
||||
WINDOW.document.addEventListener('visibilitychange', () => {
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const activeTransaction = getActiveTransaction() ;
|
||||
if (WINDOW.document.hidden && activeTransaction) {
|
||||
const statusType = 'cancelled';
|
||||
|
||||
const { op, status } = spanToJSON(activeTransaction);
|
||||
|
||||
DEBUG_BUILD &&
|
||||
logger.log(`[Tracing] Transaction: ${statusType} -> since tab moved to the background, op: ${op}`);
|
||||
// We should not set status if it is already set, this prevent important statuses like
|
||||
// error or data loss from being overwritten on transaction.
|
||||
if (!status) {
|
||||
activeTransaction.setStatus(statusType);
|
||||
}
|
||||
// TODO: Can we rewrite this to an attribute?
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
activeTransaction.setTag('visibilitychange', 'document.hidden');
|
||||
activeTransaction.end();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
DEBUG_BUILD && logger.warn('[Tracing] Could not set up background tab detection due to lack of global document');
|
||||
}
|
||||
}
|
||||
|
||||
export { registerBackgroundTabDetection };
|
||||
//# sourceMappingURL=backgroundtab.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/backgroundtab.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/backgroundtab.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"backgroundtab.js","sources":["../../../src/browser/backgroundtab.ts"],"sourcesContent":["import type { IdleTransaction, SpanStatusType } from '@sentry/core';\nimport { getActiveTransaction, spanToJSON } from '@sentry/core';\nimport { logger } from '@sentry/utils';\n\nimport { DEBUG_BUILD } from '../common/debug-build';\nimport { WINDOW } from './types';\n\n/**\n * Add a listener that cancels and finishes a transaction when the global\n * document is hidden.\n */\nexport function registerBackgroundTabDetection(): void {\n if (WINDOW.document) {\n WINDOW.document.addEventListener('visibilitychange', () => {\n // eslint-disable-next-line deprecation/deprecation\n const activeTransaction = getActiveTransaction() as IdleTransaction;\n if (WINDOW.document!.hidden && activeTransaction) {\n const statusType: SpanStatusType = 'cancelled';\n\n const { op, status } = spanToJSON(activeTransaction);\n\n DEBUG_BUILD &&\n logger.log(`[Tracing] Transaction: ${statusType} -> since tab moved to the background, op: ${op}`);\n // We should not set status if it is already set, this prevent important statuses like\n // error or data loss from being overwritten on transaction.\n if (!status) {\n activeTransaction.setStatus(statusType);\n }\n // TODO: Can we rewrite this to an attribute?\n // eslint-disable-next-line deprecation/deprecation\n activeTransaction.setTag('visibilitychange', 'document.hidden');\n activeTransaction.end();\n }\n });\n } else {\n DEBUG_BUILD && logger.warn('[Tracing] Could not set up background tab detection due to lack of global document');\n }\n}\n"],"names":[],"mappings":";;;;;AAOA;AACA;AACA;AACA;AACO,SAAS,8BAA8B,GAAS;AACvD,EAAE,IAAI,MAAM,CAAC,QAAQ,EAAE;AACvB,IAAI,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,MAAM;AAC/D;AACA,MAAM,MAAM,iBAAA,GAAoB,oBAAoB,EAAG,EAAA;AACvD,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAE,MAAA,IAAU,iBAAiB,EAAE;AACxD,QAAQ,MAAM,UAAU,GAAmB,WAAW,CAAA;AACtD;AACA,QAAQ,MAAM,EAAE,EAAE,EAAE,MAAA,KAAW,UAAU,CAAC,iBAAiB,CAAC,CAAA;AAC5D;AACA,QAAQ,WAAY;AACpB,UAAU,MAAM,CAAC,GAAG,CAAC,CAAC,uBAAuB,EAAE,UAAU,CAAC,2CAA2C,EAAE,EAAE,CAAC,CAAA,CAAA,CAAA;AACA;AACA;AACA,QAAA,IAAA,CAAA,MAAA,EAAA;AACA,UAAA,iBAAA,CAAA,SAAA,CAAA,UAAA,CAAA,CAAA;AACA,SAAA;AACA;AACA;AACA,QAAA,iBAAA,CAAA,MAAA,CAAA,kBAAA,EAAA,iBAAA,CAAA,CAAA;AACA,QAAA,iBAAA,CAAA,GAAA,EAAA,CAAA;AACA,OAAA;AACA,KAAA,CAAA,CAAA;AACA,GAAA,MAAA;AACA,IAAA,WAAA,IAAA,MAAA,CAAA,IAAA,CAAA,oFAAA,CAAA,CAAA;AACA,GAAA;AACA;;;;"}
|
||||
506
node_modules/@sentry-internal/tracing/esm/browser/browserTracingIntegration.js
generated
vendored
Normal file
506
node_modules/@sentry-internal/tracing/esm/browser/browserTracingIntegration.js
generated
vendored
Normal file
@@ -0,0 +1,506 @@
|
||||
import { TRACING_DEFAULTS, addTracingExtensions, spanToJSON, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, getActiveSpan, getCurrentHub, startIdleTransaction, getActiveTransaction, getClient, getCurrentScope } from '@sentry/core';
|
||||
import { logger, browserPerformanceTimeOrigin, addHistoryInstrumentationHandler, propagationContextFromHeaders, getDomElement } from '@sentry/utils';
|
||||
import { DEBUG_BUILD } from '../common/debug-build.js';
|
||||
import { registerBackgroundTabDetection } from './backgroundtab.js';
|
||||
import { addPerformanceInstrumentationHandler } from './instrument.js';
|
||||
import { startTrackingWebVitals, startTrackingINP, startTrackingLongTasks, startTrackingInteractions, addPerformanceEntries } from './metrics/index.js';
|
||||
import { defaultRequestInstrumentationOptions, instrumentOutgoingRequests } from './request.js';
|
||||
import { WINDOW } from './types.js';
|
||||
|
||||
const BROWSER_TRACING_INTEGRATION_ID = 'BrowserTracing';
|
||||
|
||||
/** Options for Browser Tracing integration */
|
||||
|
||||
const DEFAULT_BROWSER_TRACING_OPTIONS = {
|
||||
...TRACING_DEFAULTS,
|
||||
instrumentNavigation: true,
|
||||
instrumentPageLoad: true,
|
||||
markBackgroundSpan: true,
|
||||
enableLongTask: true,
|
||||
enableInp: false,
|
||||
interactionsSampleRate: 1,
|
||||
_experiments: {},
|
||||
...defaultRequestInstrumentationOptions,
|
||||
};
|
||||
|
||||
/**
|
||||
* The Browser Tracing integration automatically instruments browser pageload/navigation
|
||||
* actions as transactions, and captures requests, metrics and errors as spans.
|
||||
*
|
||||
* The integration can be configured with a variety of options, and can be extended to use
|
||||
* any routing library. This integration uses {@see IdleTransaction} to create transactions.
|
||||
*
|
||||
* We explicitly export the proper type here, as this has to be extended in some cases.
|
||||
*/
|
||||
const browserTracingIntegration = ((_options = {}) => {
|
||||
const _hasSetTracePropagationTargets = DEBUG_BUILD
|
||||
? !!(
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
(_options.tracePropagationTargets || _options.tracingOrigins)
|
||||
)
|
||||
: false;
|
||||
|
||||
addTracingExtensions();
|
||||
|
||||
// TODO (v8): remove this block after tracingOrigins is removed
|
||||
// Set tracePropagationTargets to tracingOrigins if specified by the user
|
||||
// In case both are specified, tracePropagationTargets takes precedence
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
if (!_options.tracePropagationTargets && _options.tracingOrigins) {
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
_options.tracePropagationTargets = _options.tracingOrigins;
|
||||
}
|
||||
|
||||
const options = {
|
||||
...DEFAULT_BROWSER_TRACING_OPTIONS,
|
||||
..._options,
|
||||
};
|
||||
|
||||
const _collectWebVitals = startTrackingWebVitals();
|
||||
|
||||
/** Stores a mapping of interactionIds from PerformanceEventTimings to the origin interaction path */
|
||||
const interactionIdToRouteNameMapping = {};
|
||||
if (options.enableInp) {
|
||||
startTrackingINP(interactionIdToRouteNameMapping, options.interactionsSampleRate);
|
||||
}
|
||||
|
||||
if (options.enableLongTask) {
|
||||
startTrackingLongTasks();
|
||||
}
|
||||
if (options._experiments.enableInteractions) {
|
||||
startTrackingInteractions();
|
||||
}
|
||||
|
||||
const latestRoute
|
||||
|
||||
= {
|
||||
name: undefined,
|
||||
context: undefined,
|
||||
};
|
||||
|
||||
/** Create routing idle transaction. */
|
||||
function _createRouteTransaction(context) {
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const hub = getCurrentHub();
|
||||
|
||||
const { beforeStartSpan, idleTimeout, finalTimeout, heartbeatInterval } = options;
|
||||
|
||||
const isPageloadTransaction = context.op === 'pageload';
|
||||
|
||||
let expandedContext;
|
||||
if (isPageloadTransaction) {
|
||||
const sentryTrace = isPageloadTransaction ? getMetaContent('sentry-trace') : '';
|
||||
const baggage = isPageloadTransaction ? getMetaContent('baggage') : undefined;
|
||||
const { traceId, dsc, parentSpanId, sampled } = propagationContextFromHeaders(sentryTrace, baggage);
|
||||
expandedContext = {
|
||||
traceId,
|
||||
parentSpanId,
|
||||
parentSampled: sampled,
|
||||
...context,
|
||||
metadata: {
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
...context.metadata,
|
||||
dynamicSamplingContext: dsc,
|
||||
},
|
||||
trimEnd: true,
|
||||
};
|
||||
} else {
|
||||
expandedContext = {
|
||||
trimEnd: true,
|
||||
...context,
|
||||
};
|
||||
}
|
||||
|
||||
const finalContext = beforeStartSpan ? beforeStartSpan(expandedContext) : expandedContext;
|
||||
|
||||
// If `beforeStartSpan` set a custom name, record that fact
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
finalContext.metadata =
|
||||
finalContext.name !== expandedContext.name
|
||||
? // eslint-disable-next-line deprecation/deprecation
|
||||
{ ...finalContext.metadata, source: 'custom' }
|
||||
: // eslint-disable-next-line deprecation/deprecation
|
||||
finalContext.metadata;
|
||||
|
||||
latestRoute.name = finalContext.name;
|
||||
latestRoute.context = finalContext;
|
||||
|
||||
if (finalContext.sampled === false) {
|
||||
DEBUG_BUILD && logger.log(`[Tracing] Will not send ${finalContext.op} transaction because of beforeNavigate.`);
|
||||
}
|
||||
|
||||
DEBUG_BUILD && logger.log(`[Tracing] Starting ${finalContext.op} transaction on scope`);
|
||||
|
||||
const { location } = WINDOW;
|
||||
|
||||
const idleTransaction = startIdleTransaction(
|
||||
hub,
|
||||
finalContext,
|
||||
idleTimeout,
|
||||
finalTimeout,
|
||||
true,
|
||||
{ location }, // for use in the tracesSampler
|
||||
heartbeatInterval,
|
||||
isPageloadTransaction, // should wait for finish signal if it's a pageload transaction
|
||||
);
|
||||
|
||||
if (isPageloadTransaction && WINDOW.document) {
|
||||
WINDOW.document.addEventListener('readystatechange', () => {
|
||||
if (['interactive', 'complete'].includes(WINDOW.document.readyState)) {
|
||||
idleTransaction.sendAutoFinishSignal();
|
||||
}
|
||||
});
|
||||
|
||||
if (['interactive', 'complete'].includes(WINDOW.document.readyState)) {
|
||||
idleTransaction.sendAutoFinishSignal();
|
||||
}
|
||||
}
|
||||
|
||||
idleTransaction.registerBeforeFinishCallback(transaction => {
|
||||
_collectWebVitals();
|
||||
addPerformanceEntries(transaction);
|
||||
});
|
||||
|
||||
return idleTransaction ;
|
||||
}
|
||||
|
||||
return {
|
||||
name: BROWSER_TRACING_INTEGRATION_ID,
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
setupOnce: () => {},
|
||||
afterAllSetup(client) {
|
||||
const clientOptions = client.getOptions();
|
||||
|
||||
const { markBackgroundSpan, traceFetch, traceXHR, shouldCreateSpanForRequest, enableHTTPTimings, _experiments } =
|
||||
options;
|
||||
|
||||
const clientOptionsTracePropagationTargets = clientOptions && clientOptions.tracePropagationTargets;
|
||||
// There are three ways to configure tracePropagationTargets:
|
||||
// 1. via top level client option `tracePropagationTargets`
|
||||
// 2. via BrowserTracing option `tracePropagationTargets`
|
||||
// 3. via BrowserTracing option `tracingOrigins` (deprecated)
|
||||
//
|
||||
// To avoid confusion, favour top level client option `tracePropagationTargets`, and fallback to
|
||||
// BrowserTracing option `tracePropagationTargets` and then `tracingOrigins` (deprecated).
|
||||
// This is done as it minimizes bundle size (we don't have to have undefined checks).
|
||||
//
|
||||
// If both 1 and either one of 2 or 3 are set (from above), we log out a warning.
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const tracePropagationTargets = clientOptionsTracePropagationTargets || options.tracePropagationTargets;
|
||||
if (DEBUG_BUILD && _hasSetTracePropagationTargets && clientOptionsTracePropagationTargets) {
|
||||
logger.warn(
|
||||
'[Tracing] The `tracePropagationTargets` option was set in the BrowserTracing integration and top level `Sentry.init`. The top level `Sentry.init` value is being used.',
|
||||
);
|
||||
}
|
||||
|
||||
let activeSpan;
|
||||
let startingUrl = WINDOW.location && WINDOW.location.href;
|
||||
|
||||
if (client.on) {
|
||||
client.on('startNavigationSpan', (context) => {
|
||||
if (activeSpan) {
|
||||
DEBUG_BUILD && logger.log(`[Tracing] Finishing current transaction with op: ${spanToJSON(activeSpan).op}`);
|
||||
// If there's an open transaction on the scope, we need to finish it before creating an new one.
|
||||
activeSpan.end();
|
||||
}
|
||||
activeSpan = _createRouteTransaction({
|
||||
op: 'navigation',
|
||||
...context,
|
||||
});
|
||||
});
|
||||
|
||||
client.on('startPageLoadSpan', (context) => {
|
||||
if (activeSpan) {
|
||||
DEBUG_BUILD && logger.log(`[Tracing] Finishing current transaction with op: ${spanToJSON(activeSpan).op}`);
|
||||
// If there's an open transaction on the scope, we need to finish it before creating an new one.
|
||||
activeSpan.end();
|
||||
}
|
||||
activeSpan = _createRouteTransaction({
|
||||
op: 'pageload',
|
||||
...context,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (options.instrumentPageLoad && client.emit && WINDOW.location) {
|
||||
const context = {
|
||||
name: WINDOW.location.pathname,
|
||||
// pageload should always start at timeOrigin (and needs to be in s, not ms)
|
||||
startTimestamp: browserPerformanceTimeOrigin ? browserPerformanceTimeOrigin / 1000 : undefined,
|
||||
origin: 'auto.pageload.browser',
|
||||
attributes: {
|
||||
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
|
||||
},
|
||||
};
|
||||
startBrowserTracingPageLoadSpan(client, context);
|
||||
}
|
||||
|
||||
if (options.instrumentNavigation && client.emit && WINDOW.location) {
|
||||
addHistoryInstrumentationHandler(({ to, from }) => {
|
||||
/**
|
||||
* This early return is there to account for some cases where a navigation transaction starts right after
|
||||
* long-running pageload. We make sure that if `from` is undefined and a valid `startingURL` exists, we don't
|
||||
* create an uneccessary navigation transaction.
|
||||
*
|
||||
* This was hard to duplicate, but this behavior stopped as soon as this fix was applied. This issue might also
|
||||
* only be caused in certain development environments where the usage of a hot module reloader is causing
|
||||
* errors.
|
||||
*/
|
||||
if (from === undefined && startingUrl && startingUrl.indexOf(to) !== -1) {
|
||||
startingUrl = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
if (from !== to) {
|
||||
startingUrl = undefined;
|
||||
const context = {
|
||||
name: WINDOW.location.pathname,
|
||||
origin: 'auto.navigation.browser',
|
||||
attributes: {
|
||||
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
|
||||
},
|
||||
};
|
||||
|
||||
startBrowserTracingNavigationSpan(client, context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (markBackgroundSpan) {
|
||||
registerBackgroundTabDetection();
|
||||
}
|
||||
|
||||
if (_experiments.enableInteractions) {
|
||||
registerInteractionListener(options, latestRoute);
|
||||
}
|
||||
|
||||
if (options.enableInp) {
|
||||
registerInpInteractionListener(interactionIdToRouteNameMapping, latestRoute);
|
||||
}
|
||||
|
||||
instrumentOutgoingRequests({
|
||||
traceFetch,
|
||||
traceXHR,
|
||||
tracePropagationTargets,
|
||||
shouldCreateSpanForRequest,
|
||||
enableHTTPTimings,
|
||||
});
|
||||
},
|
||||
// TODO v8: Remove this again
|
||||
// This is private API that we use to fix converted BrowserTracing integrations in Next.js & SvelteKit
|
||||
options,
|
||||
};
|
||||
}) ;
|
||||
|
||||
/**
|
||||
* Manually start a page load span.
|
||||
* This will only do something if the BrowserTracing integration has been setup.
|
||||
*/
|
||||
function startBrowserTracingPageLoadSpan(client, spanOptions) {
|
||||
if (!client.emit) {
|
||||
return;
|
||||
}
|
||||
|
||||
client.emit('startPageLoadSpan', spanOptions);
|
||||
|
||||
const span = getActiveSpan();
|
||||
const op = span && spanToJSON(span).op;
|
||||
return op === 'pageload' ? span : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually start a navigation span.
|
||||
* This will only do something if the BrowserTracing integration has been setup.
|
||||
*/
|
||||
function startBrowserTracingNavigationSpan(client, spanOptions) {
|
||||
if (!client.emit) {
|
||||
return;
|
||||
}
|
||||
|
||||
client.emit('startNavigationSpan', spanOptions);
|
||||
|
||||
const span = getActiveSpan();
|
||||
const op = span && spanToJSON(span).op;
|
||||
return op === 'navigation' ? span : undefined;
|
||||
}
|
||||
|
||||
/** Returns the value of a meta tag */
|
||||
function getMetaContent(metaName) {
|
||||
// Can't specify generic to `getDomElement` because tracing can be used
|
||||
// in a variety of environments, have to disable `no-unsafe-member-access`
|
||||
// as a result.
|
||||
const metaTag = getDomElement(`meta[name=${metaName}]`);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
return metaTag ? metaTag.getAttribute('content') : undefined;
|
||||
}
|
||||
|
||||
/** Start listener for interaction transactions */
|
||||
function registerInteractionListener(
|
||||
options,
|
||||
latestRoute
|
||||
|
||||
,
|
||||
) {
|
||||
let inflightInteractionTransaction;
|
||||
const registerInteractionTransaction = () => {
|
||||
const { idleTimeout, finalTimeout, heartbeatInterval } = options;
|
||||
const op = 'ui.action.click';
|
||||
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const currentTransaction = getActiveTransaction();
|
||||
if (currentTransaction && currentTransaction.op && ['navigation', 'pageload'].includes(currentTransaction.op)) {
|
||||
DEBUG_BUILD &&
|
||||
logger.warn(
|
||||
`[Tracing] Did not create ${op} transaction because a pageload or navigation transaction is in progress.`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (inflightInteractionTransaction) {
|
||||
inflightInteractionTransaction.setFinishReason('interactionInterrupted');
|
||||
inflightInteractionTransaction.end();
|
||||
inflightInteractionTransaction = undefined;
|
||||
}
|
||||
|
||||
if (!latestRoute.name) {
|
||||
DEBUG_BUILD && logger.warn(`[Tracing] Did not create ${op} transaction because _latestRouteName is missing.`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { location } = WINDOW;
|
||||
|
||||
const context = {
|
||||
name: latestRoute.name,
|
||||
op,
|
||||
trimEnd: true,
|
||||
data: {
|
||||
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: latestRoute.context ? getSource(latestRoute.context) : 'url',
|
||||
},
|
||||
};
|
||||
|
||||
inflightInteractionTransaction = startIdleTransaction(
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
getCurrentHub(),
|
||||
context,
|
||||
idleTimeout,
|
||||
finalTimeout,
|
||||
true,
|
||||
{ location }, // for use in the tracesSampler
|
||||
heartbeatInterval,
|
||||
);
|
||||
};
|
||||
|
||||
['click'].forEach(type => {
|
||||
if (WINDOW.document) {
|
||||
addEventListener(type, registerInteractionTransaction, { once: false, capture: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function isPerformanceEventTiming(entry) {
|
||||
return 'duration' in entry;
|
||||
}
|
||||
|
||||
/** We store up to 10 interaction candidates max to cap memory usage. This is the same cap as getINP from web-vitals */
|
||||
const MAX_INTERACTIONS = 10;
|
||||
|
||||
/** Creates a listener on interaction entries, and maps interactionIds to the origin path of the interaction */
|
||||
function registerInpInteractionListener(
|
||||
interactionIdToRouteNameMapping,
|
||||
latestRoute
|
||||
|
||||
,
|
||||
) {
|
||||
const handleEntries = ({ entries }) => {
|
||||
const client = getClient();
|
||||
// We need to get the replay, user, and activeTransaction from the current scope
|
||||
// so that we can associate replay id, profile id, and a user display to the span
|
||||
const replay =
|
||||
client !== undefined && client.getIntegrationByName !== undefined
|
||||
? (client.getIntegrationByName('Replay') )
|
||||
: undefined;
|
||||
const replayId = replay !== undefined ? replay.getReplayId() : undefined;
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const activeTransaction = getActiveTransaction();
|
||||
const currentScope = getCurrentScope();
|
||||
const user = currentScope !== undefined ? currentScope.getUser() : undefined;
|
||||
entries.forEach(entry => {
|
||||
if (isPerformanceEventTiming(entry)) {
|
||||
const interactionId = entry.interactionId;
|
||||
if (interactionId === undefined) {
|
||||
return;
|
||||
}
|
||||
const existingInteraction = interactionIdToRouteNameMapping[interactionId];
|
||||
const duration = entry.duration;
|
||||
const startTime = entry.startTime;
|
||||
const keys = Object.keys(interactionIdToRouteNameMapping);
|
||||
const minInteractionId =
|
||||
keys.length > 0
|
||||
? keys.reduce((a, b) => {
|
||||
return interactionIdToRouteNameMapping[a].duration < interactionIdToRouteNameMapping[b].duration
|
||||
? a
|
||||
: b;
|
||||
})
|
||||
: undefined;
|
||||
// For a first input event to be considered, we must check that an interaction event does not already exist with the same duration and start time.
|
||||
// This is also checked in the web-vitals library.
|
||||
if (entry.entryType === 'first-input') {
|
||||
const matchingEntry = keys
|
||||
.map(key => interactionIdToRouteNameMapping[key])
|
||||
.some(interaction => {
|
||||
return interaction.duration === duration && interaction.startTime === startTime;
|
||||
});
|
||||
if (matchingEntry) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Interactions with an id of 0 and are not first-input are not valid.
|
||||
if (!interactionId) {
|
||||
return;
|
||||
}
|
||||
// If the interaction already exists, we want to use the duration of the longest entry, since that is what the INP metric uses.
|
||||
if (existingInteraction) {
|
||||
existingInteraction.duration = Math.max(existingInteraction.duration, duration);
|
||||
} else if (
|
||||
keys.length < MAX_INTERACTIONS ||
|
||||
minInteractionId === undefined ||
|
||||
duration > interactionIdToRouteNameMapping[minInteractionId].duration
|
||||
) {
|
||||
// If the interaction does not exist, we want to add it to the mapping if there is space, or if the duration is longer than the shortest entry.
|
||||
const routeName = latestRoute.name;
|
||||
const parentContext = latestRoute.context;
|
||||
if (routeName && parentContext) {
|
||||
if (minInteractionId && Object.keys(interactionIdToRouteNameMapping).length >= MAX_INTERACTIONS) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete interactionIdToRouteNameMapping[minInteractionId];
|
||||
}
|
||||
interactionIdToRouteNameMapping[interactionId] = {
|
||||
routeName,
|
||||
duration,
|
||||
parentContext,
|
||||
user,
|
||||
activeTransaction,
|
||||
replayId,
|
||||
startTime,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
addPerformanceInstrumentationHandler('event', handleEntries);
|
||||
addPerformanceInstrumentationHandler('first-input', handleEntries);
|
||||
}
|
||||
|
||||
function getSource(context) {
|
||||
const sourceFromAttributes = context.attributes && context.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const sourceFromData = context.data && context.data[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const sourceFromMetadata = context.metadata && context.metadata.source;
|
||||
|
||||
return sourceFromAttributes || sourceFromData || sourceFromMetadata;
|
||||
}
|
||||
|
||||
export { BROWSER_TRACING_INTEGRATION_ID, browserTracingIntegration, getMetaContent, startBrowserTracingNavigationSpan, startBrowserTracingPageLoadSpan };
|
||||
//# sourceMappingURL=browserTracingIntegration.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/browserTracingIntegration.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/browserTracingIntegration.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
457
node_modules/@sentry-internal/tracing/esm/browser/browsertracing.js
generated
vendored
Normal file
457
node_modules/@sentry-internal/tracing/esm/browser/browsertracing.js
generated
vendored
Normal file
@@ -0,0 +1,457 @@
|
||||
import { TRACING_DEFAULTS, addTracingExtensions, startIdleTransaction, getActiveTransaction, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, getClient, getCurrentScope } from '@sentry/core';
|
||||
import { logger, propagationContextFromHeaders, getDomElement } from '@sentry/utils';
|
||||
import { DEBUG_BUILD } from '../common/debug-build.js';
|
||||
import { registerBackgroundTabDetection } from './backgroundtab.js';
|
||||
import { addPerformanceInstrumentationHandler } from './instrument.js';
|
||||
import { startTrackingWebVitals, startTrackingINP, startTrackingLongTasks, startTrackingInteractions, addPerformanceEntries } from './metrics/index.js';
|
||||
import { defaultRequestInstrumentationOptions, instrumentOutgoingRequests } from './request.js';
|
||||
import { instrumentRoutingWithDefaults } from './router.js';
|
||||
import { WINDOW } from './types.js';
|
||||
|
||||
const BROWSER_TRACING_INTEGRATION_ID = 'BrowserTracing';
|
||||
|
||||
/** Options for Browser Tracing integration */
|
||||
|
||||
const DEFAULT_BROWSER_TRACING_OPTIONS = {
|
||||
...TRACING_DEFAULTS,
|
||||
markBackgroundTransactions: true,
|
||||
routingInstrumentation: instrumentRoutingWithDefaults,
|
||||
startTransactionOnLocationChange: true,
|
||||
startTransactionOnPageLoad: true,
|
||||
enableLongTask: true,
|
||||
enableInp: false,
|
||||
interactionsSampleRate: 1,
|
||||
_experiments: {},
|
||||
...defaultRequestInstrumentationOptions,
|
||||
};
|
||||
|
||||
/** We store up to 10 interaction candidates max to cap memory usage. This is the same cap as getINP from web-vitals */
|
||||
const MAX_INTERACTIONS = 10;
|
||||
|
||||
/**
|
||||
* The Browser Tracing integration automatically instruments browser pageload/navigation
|
||||
* actions as transactions, and captures requests, metrics and errors as spans.
|
||||
*
|
||||
* The integration can be configured with a variety of options, and can be extended to use
|
||||
* any routing library. This integration uses {@see IdleTransaction} to create transactions.
|
||||
*
|
||||
* @deprecated Use `browserTracingIntegration()` instead.
|
||||
*/
|
||||
class BrowserTracing {
|
||||
// This class currently doesn't have a static `id` field like the other integration classes, because it prevented
|
||||
// @sentry/tracing from being treeshaken. Tree shakers do not like static fields, because they behave like side effects.
|
||||
// TODO: Come up with a better plan, than using static fields on integration classes, and use that plan on all
|
||||
// integrations.
|
||||
|
||||
/** Browser Tracing integration options */
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
|
||||
constructor(_options) {
|
||||
this.name = BROWSER_TRACING_INTEGRATION_ID;
|
||||
this._hasSetTracePropagationTargets = false;
|
||||
|
||||
addTracingExtensions();
|
||||
|
||||
if (DEBUG_BUILD) {
|
||||
this._hasSetTracePropagationTargets = !!(
|
||||
_options &&
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
(_options.tracePropagationTargets || _options.tracingOrigins)
|
||||
);
|
||||
}
|
||||
|
||||
this.options = {
|
||||
...DEFAULT_BROWSER_TRACING_OPTIONS,
|
||||
..._options,
|
||||
};
|
||||
|
||||
// Special case: enableLongTask can be set in _experiments
|
||||
// TODO (v8): Remove this in v8
|
||||
if (this.options._experiments.enableLongTask !== undefined) {
|
||||
this.options.enableLongTask = this.options._experiments.enableLongTask;
|
||||
}
|
||||
|
||||
// TODO (v8): remove this block after tracingOrigins is removed
|
||||
// Set tracePropagationTargets to tracingOrigins if specified by the user
|
||||
// In case both are specified, tracePropagationTargets takes precedence
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
if (_options && !_options.tracePropagationTargets && _options.tracingOrigins) {
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
this.options.tracePropagationTargets = _options.tracingOrigins;
|
||||
}
|
||||
|
||||
this._collectWebVitals = startTrackingWebVitals();
|
||||
/** Stores a mapping of interactionIds from PerformanceEventTimings to the origin interaction path */
|
||||
this._interactionIdToRouteNameMapping = {};
|
||||
|
||||
if (this.options.enableInp) {
|
||||
startTrackingINP(this._interactionIdToRouteNameMapping, this.options.interactionsSampleRate);
|
||||
}
|
||||
if (this.options.enableLongTask) {
|
||||
startTrackingLongTasks();
|
||||
}
|
||||
if (this.options._experiments.enableInteractions) {
|
||||
startTrackingInteractions();
|
||||
}
|
||||
|
||||
this._latestRoute = {
|
||||
name: undefined,
|
||||
context: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
setupOnce(_, getCurrentHub) {
|
||||
this._getCurrentHub = getCurrentHub;
|
||||
const hub = getCurrentHub();
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const client = hub.getClient();
|
||||
const clientOptions = client && client.getOptions();
|
||||
|
||||
const {
|
||||
routingInstrumentation: instrumentRouting,
|
||||
startTransactionOnLocationChange,
|
||||
startTransactionOnPageLoad,
|
||||
markBackgroundTransactions,
|
||||
traceFetch,
|
||||
traceXHR,
|
||||
shouldCreateSpanForRequest,
|
||||
enableHTTPTimings,
|
||||
_experiments,
|
||||
} = this.options;
|
||||
|
||||
const clientOptionsTracePropagationTargets = clientOptions && clientOptions.tracePropagationTargets;
|
||||
// There are three ways to configure tracePropagationTargets:
|
||||
// 1. via top level client option `tracePropagationTargets`
|
||||
// 2. via BrowserTracing option `tracePropagationTargets`
|
||||
// 3. via BrowserTracing option `tracingOrigins` (deprecated)
|
||||
//
|
||||
// To avoid confusion, favour top level client option `tracePropagationTargets`, and fallback to
|
||||
// BrowserTracing option `tracePropagationTargets` and then `tracingOrigins` (deprecated).
|
||||
// This is done as it minimizes bundle size (we don't have to have undefined checks).
|
||||
//
|
||||
// If both 1 and either one of 2 or 3 are set (from above), we log out a warning.
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const tracePropagationTargets = clientOptionsTracePropagationTargets || this.options.tracePropagationTargets;
|
||||
if (DEBUG_BUILD && this._hasSetTracePropagationTargets && clientOptionsTracePropagationTargets) {
|
||||
logger.warn(
|
||||
'[Tracing] The `tracePropagationTargets` option was set in the BrowserTracing integration and top level `Sentry.init`. The top level `Sentry.init` value is being used.',
|
||||
);
|
||||
}
|
||||
|
||||
instrumentRouting(
|
||||
(context) => {
|
||||
const transaction = this._createRouteTransaction(context);
|
||||
|
||||
this.options._experiments.onStartRouteTransaction &&
|
||||
this.options._experiments.onStartRouteTransaction(transaction, context, getCurrentHub);
|
||||
|
||||
return transaction;
|
||||
},
|
||||
startTransactionOnPageLoad,
|
||||
startTransactionOnLocationChange,
|
||||
);
|
||||
|
||||
if (markBackgroundTransactions) {
|
||||
registerBackgroundTabDetection();
|
||||
}
|
||||
|
||||
if (_experiments.enableInteractions) {
|
||||
this._registerInteractionListener();
|
||||
}
|
||||
|
||||
if (this.options.enableInp) {
|
||||
this._registerInpInteractionListener();
|
||||
}
|
||||
|
||||
instrumentOutgoingRequests({
|
||||
traceFetch,
|
||||
traceXHR,
|
||||
tracePropagationTargets,
|
||||
shouldCreateSpanForRequest,
|
||||
enableHTTPTimings,
|
||||
});
|
||||
}
|
||||
|
||||
/** Create routing idle transaction. */
|
||||
_createRouteTransaction(context) {
|
||||
if (!this._getCurrentHub) {
|
||||
DEBUG_BUILD &&
|
||||
logger.warn(`[Tracing] Did not create ${context.op} transaction because _getCurrentHub is invalid.`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const hub = this._getCurrentHub();
|
||||
|
||||
const { beforeNavigate, idleTimeout, finalTimeout, heartbeatInterval } = this.options;
|
||||
|
||||
const isPageloadTransaction = context.op === 'pageload';
|
||||
|
||||
let expandedContext;
|
||||
if (isPageloadTransaction) {
|
||||
const sentryTrace = isPageloadTransaction ? getMetaContent('sentry-trace') : '';
|
||||
const baggage = isPageloadTransaction ? getMetaContent('baggage') : undefined;
|
||||
const { traceId, dsc, parentSpanId, sampled } = propagationContextFromHeaders(sentryTrace, baggage);
|
||||
expandedContext = {
|
||||
traceId,
|
||||
parentSpanId,
|
||||
parentSampled: sampled,
|
||||
...context,
|
||||
metadata: {
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
...context.metadata,
|
||||
dynamicSamplingContext: dsc,
|
||||
},
|
||||
trimEnd: true,
|
||||
};
|
||||
} else {
|
||||
expandedContext = {
|
||||
trimEnd: true,
|
||||
...context,
|
||||
};
|
||||
}
|
||||
|
||||
const modifiedContext = typeof beforeNavigate === 'function' ? beforeNavigate(expandedContext) : expandedContext;
|
||||
|
||||
// For backwards compatibility reasons, beforeNavigate can return undefined to "drop" the transaction (prevent it
|
||||
// from being sent to Sentry).
|
||||
const finalContext = modifiedContext === undefined ? { ...expandedContext, sampled: false } : modifiedContext;
|
||||
|
||||
// If `beforeNavigate` set a custom name, record that fact
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
finalContext.metadata =
|
||||
finalContext.name !== expandedContext.name
|
||||
? // eslint-disable-next-line deprecation/deprecation
|
||||
{ ...finalContext.metadata, source: 'custom' }
|
||||
: // eslint-disable-next-line deprecation/deprecation
|
||||
finalContext.metadata;
|
||||
|
||||
this._latestRoute.name = finalContext.name;
|
||||
this._latestRoute.context = finalContext;
|
||||
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
if (finalContext.sampled === false) {
|
||||
DEBUG_BUILD && logger.log(`[Tracing] Will not send ${finalContext.op} transaction because of beforeNavigate.`);
|
||||
}
|
||||
|
||||
DEBUG_BUILD && logger.log(`[Tracing] Starting ${finalContext.op} transaction on scope`);
|
||||
|
||||
const { location } = WINDOW;
|
||||
|
||||
const idleTransaction = startIdleTransaction(
|
||||
hub,
|
||||
finalContext,
|
||||
idleTimeout,
|
||||
finalTimeout,
|
||||
true,
|
||||
{ location }, // for use in the tracesSampler
|
||||
heartbeatInterval,
|
||||
isPageloadTransaction, // should wait for finish signal if it's a pageload transaction
|
||||
);
|
||||
|
||||
if (isPageloadTransaction) {
|
||||
if (WINDOW.document) {
|
||||
WINDOW.document.addEventListener('readystatechange', () => {
|
||||
if (['interactive', 'complete'].includes(WINDOW.document.readyState)) {
|
||||
idleTransaction.sendAutoFinishSignal();
|
||||
}
|
||||
});
|
||||
|
||||
if (['interactive', 'complete'].includes(WINDOW.document.readyState)) {
|
||||
idleTransaction.sendAutoFinishSignal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idleTransaction.registerBeforeFinishCallback(transaction => {
|
||||
this._collectWebVitals();
|
||||
addPerformanceEntries(transaction);
|
||||
});
|
||||
|
||||
return idleTransaction ;
|
||||
}
|
||||
|
||||
/** Start listener for interaction transactions */
|
||||
_registerInteractionListener() {
|
||||
let inflightInteractionTransaction;
|
||||
const registerInteractionTransaction = () => {
|
||||
const { idleTimeout, finalTimeout, heartbeatInterval } = this.options;
|
||||
const op = 'ui.action.click';
|
||||
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const currentTransaction = getActiveTransaction();
|
||||
if (currentTransaction && currentTransaction.op && ['navigation', 'pageload'].includes(currentTransaction.op)) {
|
||||
DEBUG_BUILD &&
|
||||
logger.warn(
|
||||
`[Tracing] Did not create ${op} transaction because a pageload or navigation transaction is in progress.`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (inflightInteractionTransaction) {
|
||||
inflightInteractionTransaction.setFinishReason('interactionInterrupted');
|
||||
inflightInteractionTransaction.end();
|
||||
inflightInteractionTransaction = undefined;
|
||||
}
|
||||
|
||||
if (!this._getCurrentHub) {
|
||||
DEBUG_BUILD && logger.warn(`[Tracing] Did not create ${op} transaction because _getCurrentHub is invalid.`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!this._latestRoute.name) {
|
||||
DEBUG_BUILD && logger.warn(`[Tracing] Did not create ${op} transaction because _latestRouteName is missing.`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const hub = this._getCurrentHub();
|
||||
const { location } = WINDOW;
|
||||
|
||||
const context = {
|
||||
name: this._latestRoute.name,
|
||||
op,
|
||||
trimEnd: true,
|
||||
data: {
|
||||
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: this._latestRoute.context
|
||||
? getSource(this._latestRoute.context)
|
||||
: 'url',
|
||||
},
|
||||
};
|
||||
|
||||
inflightInteractionTransaction = startIdleTransaction(
|
||||
hub,
|
||||
context,
|
||||
idleTimeout,
|
||||
finalTimeout,
|
||||
true,
|
||||
{ location }, // for use in the tracesSampler
|
||||
heartbeatInterval,
|
||||
);
|
||||
};
|
||||
|
||||
['click'].forEach(type => {
|
||||
if (WINDOW.document) {
|
||||
addEventListener(type, registerInteractionTransaction, { once: false, capture: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Creates a listener on interaction entries, and maps interactionIds to the origin path of the interaction */
|
||||
_registerInpInteractionListener() {
|
||||
const handleEntries = ({ entries }) => {
|
||||
const client = getClient();
|
||||
// We need to get the replay, user, and activeTransaction from the current scope
|
||||
// so that we can associate replay id, profile id, and a user display to the span
|
||||
const replay =
|
||||
client !== undefined && client.getIntegrationByName !== undefined
|
||||
? (client.getIntegrationByName('Replay') )
|
||||
: undefined;
|
||||
const replayId = replay !== undefined ? replay.getReplayId() : undefined;
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const activeTransaction = getActiveTransaction();
|
||||
const currentScope = getCurrentScope();
|
||||
const user = currentScope !== undefined ? currentScope.getUser() : undefined;
|
||||
entries.forEach(entry => {
|
||||
if (isPerformanceEventTiming(entry)) {
|
||||
const interactionId = entry.interactionId;
|
||||
if (interactionId === undefined) {
|
||||
return;
|
||||
}
|
||||
const existingInteraction = this._interactionIdToRouteNameMapping[interactionId];
|
||||
const duration = entry.duration;
|
||||
const startTime = entry.startTime;
|
||||
const keys = Object.keys(this._interactionIdToRouteNameMapping);
|
||||
const minInteractionId =
|
||||
keys.length > 0
|
||||
? keys.reduce((a, b) => {
|
||||
return this._interactionIdToRouteNameMapping[a].duration <
|
||||
this._interactionIdToRouteNameMapping[b].duration
|
||||
? a
|
||||
: b;
|
||||
})
|
||||
: undefined;
|
||||
// For a first input event to be considered, we must check that an interaction event does not already exist with the same duration and start time.
|
||||
// This is also checked in the web-vitals library.
|
||||
if (entry.entryType === 'first-input') {
|
||||
const matchingEntry = keys
|
||||
.map(key => this._interactionIdToRouteNameMapping[key])
|
||||
.some(interaction => {
|
||||
return interaction.duration === duration && interaction.startTime === startTime;
|
||||
});
|
||||
if (matchingEntry) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Interactions with an id of 0 and are not first-input are not valid.
|
||||
if (!interactionId) {
|
||||
return;
|
||||
}
|
||||
// If the interaction already exists, we want to use the duration of the longest entry, since that is what the INP metric uses.
|
||||
if (existingInteraction) {
|
||||
existingInteraction.duration = Math.max(existingInteraction.duration, duration);
|
||||
} else if (
|
||||
keys.length < MAX_INTERACTIONS ||
|
||||
minInteractionId === undefined ||
|
||||
duration > this._interactionIdToRouteNameMapping[minInteractionId].duration
|
||||
) {
|
||||
// If the interaction does not exist, we want to add it to the mapping if there is space, or if the duration is longer than the shortest entry.
|
||||
const routeName = this._latestRoute.name;
|
||||
const parentContext = this._latestRoute.context;
|
||||
if (routeName && parentContext) {
|
||||
if (minInteractionId && Object.keys(this._interactionIdToRouteNameMapping).length >= MAX_INTERACTIONS) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete this._interactionIdToRouteNameMapping[minInteractionId];
|
||||
}
|
||||
this._interactionIdToRouteNameMapping[interactionId] = {
|
||||
routeName,
|
||||
duration,
|
||||
parentContext,
|
||||
user,
|
||||
activeTransaction,
|
||||
replayId,
|
||||
startTime,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
addPerformanceInstrumentationHandler('event', handleEntries);
|
||||
addPerformanceInstrumentationHandler('first-input', handleEntries);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the value of a meta tag */
|
||||
function getMetaContent(metaName) {
|
||||
// Can't specify generic to `getDomElement` because tracing can be used
|
||||
// in a variety of environments, have to disable `no-unsafe-member-access`
|
||||
// as a result.
|
||||
const metaTag = getDomElement(`meta[name=${metaName}]`);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
return metaTag ? metaTag.getAttribute('content') : undefined;
|
||||
}
|
||||
|
||||
function getSource(context) {
|
||||
const sourceFromAttributes = context.attributes && context.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const sourceFromData = context.data && context.data[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const sourceFromMetadata = context.metadata && context.metadata.source;
|
||||
|
||||
return sourceFromAttributes || sourceFromData || sourceFromMetadata;
|
||||
}
|
||||
|
||||
function isPerformanceEventTiming(entry) {
|
||||
return 'duration' in entry;
|
||||
}
|
||||
|
||||
export { BROWSER_TRACING_INTEGRATION_ID, BrowserTracing, getMetaContent };
|
||||
//# sourceMappingURL=browsertracing.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/browsertracing.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/browsertracing.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
230
node_modules/@sentry-internal/tracing/esm/browser/instrument.js
generated
vendored
Normal file
230
node_modules/@sentry-internal/tracing/esm/browser/instrument.js
generated
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
import { logger, getFunctionName } from '@sentry/utils';
|
||||
import { DEBUG_BUILD } from '../common/debug-build.js';
|
||||
import { onCLS } from './web-vitals/getCLS.js';
|
||||
import { onFID } from './web-vitals/getFID.js';
|
||||
import { onINP } from './web-vitals/getINP.js';
|
||||
import { onLCP } from './web-vitals/getLCP.js';
|
||||
import { observe } from './web-vitals/lib/observe.js';
|
||||
import { onTTFB } from './web-vitals/onTTFB.js';
|
||||
|
||||
const handlers = {};
|
||||
const instrumented = {};
|
||||
|
||||
let _previousCls;
|
||||
let _previousFid;
|
||||
let _previousLcp;
|
||||
let _previousTtfb;
|
||||
let _previousInp;
|
||||
|
||||
/**
|
||||
* Add a callback that will be triggered when a CLS metric is available.
|
||||
* Returns a cleanup callback which can be called to remove the instrumentation handler.
|
||||
*
|
||||
* Pass `stopOnCallback = true` to stop listening for CLS when the cleanup callback is called.
|
||||
* This will lead to the CLS being finalized and frozen.
|
||||
*/
|
||||
function addClsInstrumentationHandler(
|
||||
callback,
|
||||
stopOnCallback = false,
|
||||
) {
|
||||
return addMetricObserver('cls', callback, instrumentCls, _previousCls, stopOnCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback that will be triggered when a LCP metric is available.
|
||||
* Returns a cleanup callback which can be called to remove the instrumentation handler.
|
||||
*
|
||||
* Pass `stopOnCallback = true` to stop listening for LCP when the cleanup callback is called.
|
||||
* This will lead to the LCP being finalized and frozen.
|
||||
*/
|
||||
function addLcpInstrumentationHandler(
|
||||
callback,
|
||||
stopOnCallback = false,
|
||||
) {
|
||||
return addMetricObserver('lcp', callback, instrumentLcp, _previousLcp, stopOnCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback that will be triggered when a FID metric is available.
|
||||
*/
|
||||
function addTtfbInstrumentationHandler(callback) {
|
||||
return addMetricObserver('ttfb', callback, instrumentTtfb, _previousTtfb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback that will be triggered when a FID metric is available.
|
||||
* Returns a cleanup callback which can be called to remove the instrumentation handler.
|
||||
*/
|
||||
function addFidInstrumentationHandler(callback) {
|
||||
return addMetricObserver('fid', callback, instrumentFid, _previousFid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback that will be triggered when a INP metric is available.
|
||||
* Returns a cleanup callback which can be called to remove the instrumentation handler.
|
||||
*/
|
||||
function addInpInstrumentationHandler(
|
||||
callback,
|
||||
) {
|
||||
return addMetricObserver('inp', callback, instrumentInp, _previousInp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback that will be triggered when a performance observer is triggered,
|
||||
* and receives the entries of the observer.
|
||||
* Returns a cleanup callback which can be called to remove the instrumentation handler.
|
||||
*/
|
||||
function addPerformanceInstrumentationHandler(
|
||||
type,
|
||||
callback,
|
||||
) {
|
||||
addHandler(type, callback);
|
||||
|
||||
if (!instrumented[type]) {
|
||||
instrumentPerformanceObserver(type);
|
||||
instrumented[type] = true;
|
||||
}
|
||||
|
||||
return getCleanupCallback(type, callback);
|
||||
}
|
||||
|
||||
/** Trigger all handlers of a given type. */
|
||||
function triggerHandlers(type, data) {
|
||||
const typeHandlers = handlers[type];
|
||||
|
||||
if (!typeHandlers || !typeHandlers.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const handler of typeHandlers) {
|
||||
try {
|
||||
handler(data);
|
||||
} catch (e) {
|
||||
DEBUG_BUILD &&
|
||||
logger.error(
|
||||
`Error while triggering instrumentation handler.\nType: ${type}\nName: ${getFunctionName(handler)}\nError:`,
|
||||
e,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function instrumentCls() {
|
||||
return onCLS(
|
||||
metric => {
|
||||
triggerHandlers('cls', {
|
||||
metric,
|
||||
});
|
||||
_previousCls = metric;
|
||||
},
|
||||
{ reportAllChanges: true },
|
||||
);
|
||||
}
|
||||
|
||||
function instrumentFid() {
|
||||
return onFID(metric => {
|
||||
triggerHandlers('fid', {
|
||||
metric,
|
||||
});
|
||||
_previousFid = metric;
|
||||
});
|
||||
}
|
||||
|
||||
function instrumentLcp() {
|
||||
return onLCP(metric => {
|
||||
triggerHandlers('lcp', {
|
||||
metric,
|
||||
});
|
||||
_previousLcp = metric;
|
||||
});
|
||||
}
|
||||
|
||||
function instrumentTtfb() {
|
||||
return onTTFB(metric => {
|
||||
triggerHandlers('ttfb', {
|
||||
metric,
|
||||
});
|
||||
_previousTtfb = metric;
|
||||
});
|
||||
}
|
||||
|
||||
function instrumentInp() {
|
||||
return onINP(metric => {
|
||||
triggerHandlers('inp', {
|
||||
metric,
|
||||
});
|
||||
_previousInp = metric;
|
||||
});
|
||||
}
|
||||
|
||||
function addMetricObserver(
|
||||
type,
|
||||
callback,
|
||||
instrumentFn,
|
||||
previousValue,
|
||||
stopOnCallback = false,
|
||||
) {
|
||||
addHandler(type, callback);
|
||||
|
||||
let stopListening;
|
||||
|
||||
if (!instrumented[type]) {
|
||||
stopListening = instrumentFn();
|
||||
instrumented[type] = true;
|
||||
}
|
||||
|
||||
if (previousValue) {
|
||||
callback({ metric: previousValue });
|
||||
}
|
||||
|
||||
return getCleanupCallback(type, callback, stopOnCallback ? stopListening : undefined);
|
||||
}
|
||||
|
||||
function instrumentPerformanceObserver(type) {
|
||||
const options = {};
|
||||
|
||||
// Special per-type options we want to use
|
||||
if (type === 'event') {
|
||||
options.durationThreshold = 0;
|
||||
}
|
||||
|
||||
observe(
|
||||
type,
|
||||
entries => {
|
||||
triggerHandlers(type, { entries });
|
||||
},
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
function addHandler(type, handler) {
|
||||
handlers[type] = handlers[type] || [];
|
||||
(handlers[type] ).push(handler);
|
||||
}
|
||||
|
||||
// Get a callback which can be called to remove the instrumentation handler
|
||||
function getCleanupCallback(
|
||||
type,
|
||||
callback,
|
||||
stopListening,
|
||||
) {
|
||||
return () => {
|
||||
if (stopListening) {
|
||||
stopListening();
|
||||
}
|
||||
|
||||
const typeHandlers = handlers[type];
|
||||
|
||||
if (!typeHandlers) {
|
||||
return;
|
||||
}
|
||||
|
||||
const index = typeHandlers.indexOf(callback);
|
||||
if (index !== -1) {
|
||||
typeHandlers.splice(index, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { addClsInstrumentationHandler, addFidInstrumentationHandler, addInpInstrumentationHandler, addLcpInstrumentationHandler, addPerformanceInstrumentationHandler, addTtfbInstrumentationHandler };
|
||||
//# sourceMappingURL=instrument.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/instrument.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/instrument.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
706
node_modules/@sentry-internal/tracing/esm/browser/metrics/index.js
generated
vendored
Normal file
706
node_modules/@sentry-internal/tracing/esm/browser/metrics/index.js
generated
vendored
Normal file
@@ -0,0 +1,706 @@
|
||||
import { getActiveTransaction, spanToJSON, setMeasurement, getClient, Span, createSpanEnvelope, hasTracingEnabled, isValidSampleRate } from '@sentry/core';
|
||||
import { browserPerformanceTimeOrigin, htmlTreeAsString, getComponentName, logger, parseUrl } from '@sentry/utils';
|
||||
import { DEBUG_BUILD } from '../../common/debug-build.js';
|
||||
import { addPerformanceInstrumentationHandler, addClsInstrumentationHandler, addLcpInstrumentationHandler, addFidInstrumentationHandler, addTtfbInstrumentationHandler, addInpInstrumentationHandler } from '../instrument.js';
|
||||
import { WINDOW } from '../types.js';
|
||||
import { getVisibilityWatcher } from '../web-vitals/lib/getVisibilityWatcher.js';
|
||||
import { _startChild, isMeasurementValue } from './utils.js';
|
||||
import { getNavigationEntry } from '../web-vitals/lib/getNavigationEntry.js';
|
||||
|
||||
const MAX_INT_AS_BYTES = 2147483647;
|
||||
|
||||
/**
|
||||
* Converts from milliseconds to seconds
|
||||
* @param time time in ms
|
||||
*/
|
||||
function msToSec(time) {
|
||||
return time / 1000;
|
||||
}
|
||||
|
||||
function getBrowserPerformanceAPI() {
|
||||
// @ts-expect-error we want to make sure all of these are available, even if TS is sure they are
|
||||
return WINDOW && WINDOW.addEventListener && WINDOW.performance;
|
||||
}
|
||||
|
||||
let _performanceCursor = 0;
|
||||
|
||||
let _measurements = {};
|
||||
let _lcpEntry;
|
||||
let _clsEntry;
|
||||
|
||||
/**
|
||||
* Start tracking web vitals.
|
||||
* The callback returned by this function can be used to stop tracking & ensure all measurements are final & captured.
|
||||
*
|
||||
* @returns A function that forces web vitals collection
|
||||
*/
|
||||
function startTrackingWebVitals() {
|
||||
const performance = getBrowserPerformanceAPI();
|
||||
if (performance && browserPerformanceTimeOrigin) {
|
||||
// @ts-expect-error we want to make sure all of these are available, even if TS is sure they are
|
||||
if (performance.mark) {
|
||||
WINDOW.performance.mark('sentry-tracing-init');
|
||||
}
|
||||
const fidCallback = _trackFID();
|
||||
const clsCallback = _trackCLS();
|
||||
const lcpCallback = _trackLCP();
|
||||
const ttfbCallback = _trackTtfb();
|
||||
|
||||
return () => {
|
||||
fidCallback();
|
||||
clsCallback();
|
||||
lcpCallback();
|
||||
ttfbCallback();
|
||||
};
|
||||
}
|
||||
|
||||
return () => undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start tracking long tasks.
|
||||
*/
|
||||
function startTrackingLongTasks() {
|
||||
addPerformanceInstrumentationHandler('longtask', ({ entries }) => {
|
||||
for (const entry of entries) {
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const transaction = getActiveTransaction() ;
|
||||
if (!transaction) {
|
||||
return;
|
||||
}
|
||||
const startTime = msToSec((browserPerformanceTimeOrigin ) + entry.startTime);
|
||||
const duration = msToSec(entry.duration);
|
||||
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
transaction.startChild({
|
||||
description: 'Main UI thread blocked',
|
||||
op: 'ui.long-task',
|
||||
origin: 'auto.ui.browser.metrics',
|
||||
startTimestamp: startTime,
|
||||
endTimestamp: startTime + duration,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Start tracking interaction events.
|
||||
*/
|
||||
function startTrackingInteractions() {
|
||||
addPerformanceInstrumentationHandler('event', ({ entries }) => {
|
||||
for (const entry of entries) {
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const transaction = getActiveTransaction() ;
|
||||
if (!transaction) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.name === 'click') {
|
||||
const startTime = msToSec((browserPerformanceTimeOrigin ) + entry.startTime);
|
||||
const duration = msToSec(entry.duration);
|
||||
|
||||
const span = {
|
||||
description: htmlTreeAsString(entry.target),
|
||||
op: `ui.interaction.${entry.name}`,
|
||||
origin: 'auto.ui.browser.metrics',
|
||||
startTimestamp: startTime,
|
||||
endTimestamp: startTime + duration,
|
||||
};
|
||||
|
||||
const componentName = getComponentName(entry.target);
|
||||
if (componentName) {
|
||||
span.attributes = { 'ui.component_name': componentName };
|
||||
}
|
||||
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
transaction.startChild(span);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Start tracking INP webvital events.
|
||||
*/
|
||||
function startTrackingINP(
|
||||
interactionIdtoRouteNameMapping,
|
||||
interactionsSampleRate,
|
||||
) {
|
||||
const performance = getBrowserPerformanceAPI();
|
||||
if (performance && browserPerformanceTimeOrigin) {
|
||||
const inpCallback = _trackINP(interactionIdtoRouteNameMapping, interactionsSampleRate);
|
||||
|
||||
return () => {
|
||||
inpCallback();
|
||||
};
|
||||
}
|
||||
|
||||
return () => undefined;
|
||||
}
|
||||
|
||||
/** Starts tracking the Cumulative Layout Shift on the current page. */
|
||||
function _trackCLS() {
|
||||
return addClsInstrumentationHandler(({ metric }) => {
|
||||
const entry = metric.entries[metric.entries.length - 1];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_BUILD && logger.log('[Measurements] Adding CLS');
|
||||
_measurements['cls'] = { value: metric.value, unit: '' };
|
||||
_clsEntry = entry ;
|
||||
}, true);
|
||||
}
|
||||
|
||||
/** Starts tracking the Largest Contentful Paint on the current page. */
|
||||
function _trackLCP() {
|
||||
return addLcpInstrumentationHandler(({ metric }) => {
|
||||
const entry = metric.entries[metric.entries.length - 1];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_BUILD && logger.log('[Measurements] Adding LCP');
|
||||
_measurements['lcp'] = { value: metric.value, unit: 'millisecond' };
|
||||
_lcpEntry = entry ;
|
||||
}, true);
|
||||
}
|
||||
|
||||
/** Starts tracking the First Input Delay on the current page. */
|
||||
function _trackFID() {
|
||||
return addFidInstrumentationHandler(({ metric }) => {
|
||||
const entry = metric.entries[metric.entries.length - 1];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timeOrigin = msToSec(browserPerformanceTimeOrigin );
|
||||
const startTime = msToSec(entry.startTime);
|
||||
DEBUG_BUILD && logger.log('[Measurements] Adding FID');
|
||||
_measurements['fid'] = { value: metric.value, unit: 'millisecond' };
|
||||
_measurements['mark.fid'] = { value: timeOrigin + startTime, unit: 'second' };
|
||||
});
|
||||
}
|
||||
|
||||
function _trackTtfb() {
|
||||
return addTtfbInstrumentationHandler(({ metric }) => {
|
||||
const entry = metric.entries[metric.entries.length - 1];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_BUILD && logger.log('[Measurements] Adding TTFB');
|
||||
_measurements['ttfb'] = { value: metric.value, unit: 'millisecond' };
|
||||
});
|
||||
}
|
||||
|
||||
const INP_ENTRY_MAP = {
|
||||
click: 'click',
|
||||
pointerdown: 'click',
|
||||
pointerup: 'click',
|
||||
mousedown: 'click',
|
||||
mouseup: 'click',
|
||||
touchstart: 'click',
|
||||
touchend: 'click',
|
||||
mouseover: 'hover',
|
||||
mouseout: 'hover',
|
||||
mouseenter: 'hover',
|
||||
mouseleave: 'hover',
|
||||
pointerover: 'hover',
|
||||
pointerout: 'hover',
|
||||
pointerenter: 'hover',
|
||||
pointerleave: 'hover',
|
||||
dragstart: 'drag',
|
||||
dragend: 'drag',
|
||||
drag: 'drag',
|
||||
dragenter: 'drag',
|
||||
dragleave: 'drag',
|
||||
dragover: 'drag',
|
||||
drop: 'drag',
|
||||
keydown: 'press',
|
||||
keyup: 'press',
|
||||
keypress: 'press',
|
||||
input: 'press',
|
||||
};
|
||||
|
||||
/** Starts tracking the Interaction to Next Paint on the current page. */
|
||||
function _trackINP(
|
||||
interactionIdToRouteNameMapping,
|
||||
interactionsSampleRate,
|
||||
) {
|
||||
return addInpInstrumentationHandler(({ metric }) => {
|
||||
if (metric.value === undefined) {
|
||||
return;
|
||||
}
|
||||
const entry = metric.entries.find(
|
||||
entry => entry.duration === metric.value && INP_ENTRY_MAP[entry.name] !== undefined,
|
||||
);
|
||||
const client = getClient();
|
||||
if (!entry || !client) {
|
||||
return;
|
||||
}
|
||||
const interactionType = INP_ENTRY_MAP[entry.name];
|
||||
const options = client.getOptions();
|
||||
/** Build the INP span, create an envelope from the span, and then send the envelope */
|
||||
const startTime = msToSec((browserPerformanceTimeOrigin ) + entry.startTime);
|
||||
const duration = msToSec(metric.value);
|
||||
const interaction =
|
||||
entry.interactionId !== undefined ? interactionIdToRouteNameMapping[entry.interactionId] : undefined;
|
||||
if (interaction === undefined) {
|
||||
return;
|
||||
}
|
||||
const { routeName, parentContext, activeTransaction, user, replayId } = interaction;
|
||||
const userDisplay = user !== undefined ? user.email || user.id || user.ip_address : undefined;
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const profileId = activeTransaction !== undefined ? activeTransaction.getProfileId() : undefined;
|
||||
const span = new Span({
|
||||
startTimestamp: startTime,
|
||||
endTimestamp: startTime + duration,
|
||||
op: `ui.interaction.${interactionType}`,
|
||||
name: htmlTreeAsString(entry.target),
|
||||
attributes: {
|
||||
release: options.release,
|
||||
environment: options.environment,
|
||||
transaction: routeName,
|
||||
...(userDisplay !== undefined && userDisplay !== '' ? { user: userDisplay } : {}),
|
||||
...(profileId !== undefined ? { profile_id: profileId } : {}),
|
||||
...(replayId !== undefined ? { replay_id: replayId } : {}),
|
||||
},
|
||||
exclusiveTime: metric.value,
|
||||
measurements: {
|
||||
inp: { value: metric.value, unit: 'millisecond' },
|
||||
},
|
||||
});
|
||||
|
||||
/** Check to see if the span should be sampled */
|
||||
const sampleRate = getSampleRate(parentContext, options, interactionsSampleRate);
|
||||
|
||||
if (!sampleRate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.random() < (sampleRate )) {
|
||||
const envelope = span ? createSpanEnvelope([span], client.getDsn()) : undefined;
|
||||
const transport = client && client.getTransport();
|
||||
if (transport && envelope) {
|
||||
transport.send(envelope).then(null, reason => {
|
||||
DEBUG_BUILD && logger.error('Error while sending interaction:', reason);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Add performance related spans to a transaction */
|
||||
function addPerformanceEntries(transaction) {
|
||||
const performance = getBrowserPerformanceAPI();
|
||||
if (!performance || !WINDOW.performance.getEntries || !browserPerformanceTimeOrigin) {
|
||||
// Gatekeeper if performance API not available
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_BUILD && logger.log('[Tracing] Adding & adjusting spans using Performance API');
|
||||
const timeOrigin = msToSec(browserPerformanceTimeOrigin);
|
||||
|
||||
const performanceEntries = performance.getEntries();
|
||||
|
||||
const { op, start_timestamp: transactionStartTime } = spanToJSON(transaction);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
performanceEntries.slice(_performanceCursor).forEach((entry) => {
|
||||
const startTime = msToSec(entry.startTime);
|
||||
const duration = msToSec(entry.duration);
|
||||
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
if (transaction.op === 'navigation' && transactionStartTime && timeOrigin + startTime < transactionStartTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (entry.entryType) {
|
||||
case 'navigation': {
|
||||
_addNavigationSpans(transaction, entry, timeOrigin);
|
||||
break;
|
||||
}
|
||||
case 'mark':
|
||||
case 'paint':
|
||||
case 'measure': {
|
||||
_addMeasureSpans(transaction, entry, startTime, duration, timeOrigin);
|
||||
|
||||
// capture web vitals
|
||||
const firstHidden = getVisibilityWatcher();
|
||||
// Only report if the page wasn't hidden prior to the web vital.
|
||||
const shouldRecord = entry.startTime < firstHidden.firstHiddenTime;
|
||||
|
||||
if (entry.name === 'first-paint' && shouldRecord) {
|
||||
DEBUG_BUILD && logger.log('[Measurements] Adding FP');
|
||||
_measurements['fp'] = { value: entry.startTime, unit: 'millisecond' };
|
||||
}
|
||||
if (entry.name === 'first-contentful-paint' && shouldRecord) {
|
||||
DEBUG_BUILD && logger.log('[Measurements] Adding FCP');
|
||||
_measurements['fcp'] = { value: entry.startTime, unit: 'millisecond' };
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'resource': {
|
||||
_addResourceSpans(transaction, entry, entry.name , startTime, duration, timeOrigin);
|
||||
break;
|
||||
}
|
||||
// Ignore other entry types.
|
||||
}
|
||||
});
|
||||
|
||||
_performanceCursor = Math.max(performanceEntries.length - 1, 0);
|
||||
|
||||
_trackNavigator(transaction);
|
||||
|
||||
// Measurements are only available for pageload transactions
|
||||
if (op === 'pageload') {
|
||||
_addTtfbRequestTimeToMeasurements(_measurements);
|
||||
|
||||
['fcp', 'fp', 'lcp'].forEach(name => {
|
||||
if (!_measurements[name] || !transactionStartTime || timeOrigin >= transactionStartTime) {
|
||||
return;
|
||||
}
|
||||
// The web vitals, fcp, fp, lcp, and ttfb, all measure relative to timeOrigin.
|
||||
// Unfortunately, timeOrigin is not captured within the transaction span data, so these web vitals will need
|
||||
// to be adjusted to be relative to transaction.startTimestamp.
|
||||
const oldValue = _measurements[name].value;
|
||||
const measurementTimestamp = timeOrigin + msToSec(oldValue);
|
||||
|
||||
// normalizedValue should be in milliseconds
|
||||
const normalizedValue = Math.abs((measurementTimestamp - transactionStartTime) * 1000);
|
||||
const delta = normalizedValue - oldValue;
|
||||
|
||||
DEBUG_BUILD && logger.log(`[Measurements] Normalized ${name} from ${oldValue} to ${normalizedValue} (${delta})`);
|
||||
_measurements[name].value = normalizedValue;
|
||||
});
|
||||
|
||||
const fidMark = _measurements['mark.fid'];
|
||||
if (fidMark && _measurements['fid']) {
|
||||
// create span for FID
|
||||
_startChild(transaction, {
|
||||
description: 'first input delay',
|
||||
endTimestamp: fidMark.value + msToSec(_measurements['fid'].value),
|
||||
op: 'ui.action',
|
||||
origin: 'auto.ui.browser.metrics',
|
||||
startTimestamp: fidMark.value,
|
||||
});
|
||||
|
||||
// Delete mark.fid as we don't want it to be part of final payload
|
||||
delete _measurements['mark.fid'];
|
||||
}
|
||||
|
||||
// If FCP is not recorded we should not record the cls value
|
||||
// according to the new definition of CLS.
|
||||
if (!('fcp' in _measurements)) {
|
||||
delete _measurements.cls;
|
||||
}
|
||||
|
||||
Object.keys(_measurements).forEach(measurementName => {
|
||||
setMeasurement(measurementName, _measurements[measurementName].value, _measurements[measurementName].unit);
|
||||
});
|
||||
|
||||
_tagMetricInfo(transaction);
|
||||
}
|
||||
|
||||
_lcpEntry = undefined;
|
||||
_clsEntry = undefined;
|
||||
_measurements = {};
|
||||
}
|
||||
|
||||
/** Create measure related spans */
|
||||
function _addMeasureSpans(
|
||||
transaction,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
entry,
|
||||
startTime,
|
||||
duration,
|
||||
timeOrigin,
|
||||
) {
|
||||
const measureStartTimestamp = timeOrigin + startTime;
|
||||
const measureEndTimestamp = measureStartTimestamp + duration;
|
||||
|
||||
_startChild(transaction, {
|
||||
description: entry.name ,
|
||||
endTimestamp: measureEndTimestamp,
|
||||
op: entry.entryType ,
|
||||
origin: 'auto.resource.browser.metrics',
|
||||
startTimestamp: measureStartTimestamp,
|
||||
});
|
||||
|
||||
return measureStartTimestamp;
|
||||
}
|
||||
|
||||
/** Instrument navigation entries */
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function _addNavigationSpans(transaction, entry, timeOrigin) {
|
||||
['unloadEvent', 'redirect', 'domContentLoadedEvent', 'loadEvent', 'connect'].forEach(event => {
|
||||
_addPerformanceNavigationTiming(transaction, entry, event, timeOrigin);
|
||||
});
|
||||
_addPerformanceNavigationTiming(transaction, entry, 'secureConnection', timeOrigin, 'TLS/SSL', 'connectEnd');
|
||||
_addPerformanceNavigationTiming(transaction, entry, 'fetch', timeOrigin, 'cache', 'domainLookupStart');
|
||||
_addPerformanceNavigationTiming(transaction, entry, 'domainLookup', timeOrigin, 'DNS');
|
||||
_addRequest(transaction, entry, timeOrigin);
|
||||
}
|
||||
|
||||
/** Create performance navigation related spans */
|
||||
function _addPerformanceNavigationTiming(
|
||||
transaction,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
entry,
|
||||
event,
|
||||
timeOrigin,
|
||||
description,
|
||||
eventEnd,
|
||||
) {
|
||||
const end = eventEnd ? (entry[eventEnd] ) : (entry[`${event}End`] );
|
||||
const start = entry[`${event}Start`] ;
|
||||
if (!start || !end) {
|
||||
return;
|
||||
}
|
||||
_startChild(transaction, {
|
||||
op: 'browser',
|
||||
origin: 'auto.browser.browser.metrics',
|
||||
description: description || event,
|
||||
startTimestamp: timeOrigin + msToSec(start),
|
||||
endTimestamp: timeOrigin + msToSec(end),
|
||||
});
|
||||
}
|
||||
|
||||
/** Create request and response related spans */
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function _addRequest(transaction, entry, timeOrigin) {
|
||||
if (entry.responseEnd) {
|
||||
// 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.
|
||||
// In this case, ie. when the document request hasn't finished yet, `entry.responseEnd` will be 0.
|
||||
// In order not to produce faulty spans, where the end timestamp is before the start timestamp, we will only collect
|
||||
// these spans when the responseEnd value is available. The backend (Relay) would drop the entire transaction if it contained faulty spans.
|
||||
_startChild(transaction, {
|
||||
op: 'browser',
|
||||
origin: 'auto.browser.browser.metrics',
|
||||
description: 'request',
|
||||
startTimestamp: timeOrigin + msToSec(entry.requestStart ),
|
||||
endTimestamp: timeOrigin + msToSec(entry.responseEnd ),
|
||||
});
|
||||
|
||||
_startChild(transaction, {
|
||||
op: 'browser',
|
||||
origin: 'auto.browser.browser.metrics',
|
||||
description: 'response',
|
||||
startTimestamp: timeOrigin + msToSec(entry.responseStart ),
|
||||
endTimestamp: timeOrigin + msToSec(entry.responseEnd ),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Create resource-related spans */
|
||||
function _addResourceSpans(
|
||||
transaction,
|
||||
entry,
|
||||
resourceUrl,
|
||||
startTime,
|
||||
duration,
|
||||
timeOrigin,
|
||||
) {
|
||||
// we already instrument based on fetch and xhr, so we don't need to
|
||||
// duplicate spans here.
|
||||
if (entry.initiatorType === 'xmlhttprequest' || entry.initiatorType === 'fetch') {
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedUrl = parseUrl(resourceUrl);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const data = {};
|
||||
setResourceEntrySizeData(data, entry, 'transferSize', 'http.response_transfer_size');
|
||||
setResourceEntrySizeData(data, entry, 'encodedBodySize', 'http.response_content_length');
|
||||
setResourceEntrySizeData(data, entry, 'decodedBodySize', 'http.decoded_response_content_length');
|
||||
|
||||
if ('renderBlockingStatus' in entry) {
|
||||
data['resource.render_blocking_status'] = entry.renderBlockingStatus;
|
||||
}
|
||||
if (parsedUrl.protocol) {
|
||||
data['url.scheme'] = parsedUrl.protocol.split(':').pop(); // the protocol returned by parseUrl includes a :, but OTEL spec does not, so we remove it.
|
||||
}
|
||||
|
||||
if (parsedUrl.host) {
|
||||
data['server.address'] = parsedUrl.host;
|
||||
}
|
||||
|
||||
data['url.same_origin'] = resourceUrl.includes(WINDOW.location.origin);
|
||||
|
||||
const startTimestamp = timeOrigin + startTime;
|
||||
const endTimestamp = startTimestamp + duration;
|
||||
|
||||
_startChild(transaction, {
|
||||
description: resourceUrl.replace(WINDOW.location.origin, ''),
|
||||
endTimestamp,
|
||||
op: entry.initiatorType ? `resource.${entry.initiatorType}` : 'resource.other',
|
||||
origin: 'auto.resource.browser.metrics',
|
||||
startTimestamp,
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture the information of the user agent.
|
||||
*/
|
||||
function _trackNavigator(transaction) {
|
||||
const navigator = WINDOW.navigator ;
|
||||
if (!navigator) {
|
||||
return;
|
||||
}
|
||||
|
||||
// track network connectivity
|
||||
const connection = navigator.connection;
|
||||
if (connection) {
|
||||
if (connection.effectiveType) {
|
||||
// TODO: Can we rewrite this to an attribute?
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
transaction.setTag('effectiveConnectionType', connection.effectiveType);
|
||||
}
|
||||
|
||||
if (connection.type) {
|
||||
// TODO: Can we rewrite this to an attribute?
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
transaction.setTag('connectionType', connection.type);
|
||||
}
|
||||
|
||||
if (isMeasurementValue(connection.rtt)) {
|
||||
_measurements['connection.rtt'] = { value: connection.rtt, unit: 'millisecond' };
|
||||
}
|
||||
}
|
||||
|
||||
if (isMeasurementValue(navigator.deviceMemory)) {
|
||||
// TODO: Can we rewrite this to an attribute?
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
transaction.setTag('deviceMemory', `${navigator.deviceMemory} GB`);
|
||||
}
|
||||
|
||||
if (isMeasurementValue(navigator.hardwareConcurrency)) {
|
||||
// TODO: Can we rewrite this to an attribute?
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
transaction.setTag('hardwareConcurrency', String(navigator.hardwareConcurrency));
|
||||
}
|
||||
}
|
||||
|
||||
/** Add LCP / CLS data to transaction to allow debugging */
|
||||
function _tagMetricInfo(transaction) {
|
||||
if (_lcpEntry) {
|
||||
DEBUG_BUILD && logger.log('[Measurements] Adding LCP Data');
|
||||
|
||||
// Capture Properties of the LCP element that contributes to the LCP.
|
||||
|
||||
if (_lcpEntry.element) {
|
||||
// TODO: Can we rewrite this to an attribute?
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
transaction.setTag('lcp.element', htmlTreeAsString(_lcpEntry.element));
|
||||
}
|
||||
|
||||
if (_lcpEntry.id) {
|
||||
// TODO: Can we rewrite this to an attribute?
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
transaction.setTag('lcp.id', _lcpEntry.id);
|
||||
}
|
||||
|
||||
if (_lcpEntry.url) {
|
||||
// Trim URL to the first 200 characters.
|
||||
// TODO: Can we rewrite this to an attribute?
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
transaction.setTag('lcp.url', _lcpEntry.url.trim().slice(0, 200));
|
||||
}
|
||||
|
||||
// TODO: Can we rewrite this to an attribute?
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
transaction.setTag('lcp.size', _lcpEntry.size);
|
||||
}
|
||||
|
||||
// See: https://developer.mozilla.org/en-US/docs/Web/API/LayoutShift
|
||||
if (_clsEntry && _clsEntry.sources) {
|
||||
DEBUG_BUILD && logger.log('[Measurements] Adding CLS Data');
|
||||
_clsEntry.sources.forEach((source, index) =>
|
||||
// TODO: Can we rewrite this to an attribute?
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
transaction.setTag(`cls.source.${index + 1}`, htmlTreeAsString(source.node)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function setResourceEntrySizeData(
|
||||
data,
|
||||
entry,
|
||||
key,
|
||||
dataKey,
|
||||
) {
|
||||
const entryVal = entry[key];
|
||||
if (entryVal != null && entryVal < MAX_INT_AS_BYTES) {
|
||||
data[dataKey] = entryVal;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add ttfb request time information to measurements.
|
||||
*
|
||||
* ttfb information is added via vendored web vitals library.
|
||||
*/
|
||||
function _addTtfbRequestTimeToMeasurements(_measurements) {
|
||||
const navEntry = getNavigationEntry();
|
||||
if (!navEntry) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { responseStart, requestStart } = navEntry;
|
||||
|
||||
if (requestStart <= responseStart) {
|
||||
DEBUG_BUILD && logger.log('[Measurements] Adding TTFB Request Time');
|
||||
_measurements['ttfb.requestTime'] = {
|
||||
value: responseStart - requestStart,
|
||||
unit: 'millisecond',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** Taken from @sentry/core sampling.ts */
|
||||
function getSampleRate(
|
||||
transactionContext,
|
||||
options,
|
||||
interactionsSampleRate,
|
||||
) {
|
||||
if (!hasTracingEnabled(options)) {
|
||||
return false;
|
||||
}
|
||||
let sampleRate;
|
||||
if (transactionContext !== undefined && typeof options.tracesSampler === 'function') {
|
||||
sampleRate = options.tracesSampler({
|
||||
transactionContext,
|
||||
name: transactionContext.name,
|
||||
parentSampled: transactionContext.parentSampled,
|
||||
attributes: {
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
...transactionContext.data,
|
||||
...transactionContext.attributes,
|
||||
},
|
||||
location: WINDOW.location,
|
||||
});
|
||||
} else if (transactionContext !== undefined && transactionContext.sampled !== undefined) {
|
||||
sampleRate = transactionContext.sampled;
|
||||
} else if (typeof options.tracesSampleRate !== 'undefined') {
|
||||
sampleRate = options.tracesSampleRate;
|
||||
} else {
|
||||
sampleRate = 1;
|
||||
}
|
||||
if (!isValidSampleRate(sampleRate)) {
|
||||
DEBUG_BUILD && logger.warn('[Tracing] Discarding interaction span because of invalid sample rate.');
|
||||
return false;
|
||||
}
|
||||
if (sampleRate === true) {
|
||||
return interactionsSampleRate;
|
||||
} else if (sampleRate === false) {
|
||||
return 0;
|
||||
}
|
||||
return sampleRate * interactionsSampleRate;
|
||||
}
|
||||
|
||||
export { _addMeasureSpans, _addResourceSpans, addPerformanceEntries, startTrackingINP, startTrackingInteractions, startTrackingLongTasks, startTrackingWebVitals };
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/metrics/index.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/metrics/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
31
node_modules/@sentry-internal/tracing/esm/browser/metrics/utils.js
generated
vendored
Normal file
31
node_modules/@sentry-internal/tracing/esm/browser/metrics/utils.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Checks if a given value is a valid measurement value.
|
||||
*/
|
||||
function isMeasurementValue(value) {
|
||||
return typeof value === 'number' && isFinite(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to start child on transactions. This function will make sure that the transaction will
|
||||
* use the start timestamp of the created child span if it is earlier than the transactions actual
|
||||
* start timestamp.
|
||||
*
|
||||
* Note: this will not be possible anymore in v8,
|
||||
* unless we do some special handling for browser here...
|
||||
*/
|
||||
function _startChild(transaction, { startTimestamp, ...ctx }) {
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
if (startTimestamp && transaction.startTimestamp > startTimestamp) {
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
transaction.startTimestamp = startTimestamp;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
return transaction.startChild({
|
||||
startTimestamp,
|
||||
...ctx,
|
||||
});
|
||||
}
|
||||
|
||||
export { _startChild, isMeasurementValue };
|
||||
//# sourceMappingURL=utils.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/metrics/utils.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/metrics/utils.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"utils.js","sources":["../../../../src/browser/metrics/utils.ts"],"sourcesContent":["import type { Transaction } from '@sentry/core';\nimport type { Span, SpanContext } from '@sentry/types';\n\n/**\n * Checks if a given value is a valid measurement value.\n */\nexport function isMeasurementValue(value: unknown): value is number {\n return typeof value === 'number' && isFinite(value);\n}\n\n/**\n * Helper function to start child on transactions. This function will make sure that the transaction will\n * use the start timestamp of the created child span if it is earlier than the transactions actual\n * start timestamp.\n *\n * Note: this will not be possible anymore in v8,\n * unless we do some special handling for browser here...\n */\nexport function _startChild(transaction: Transaction, { startTimestamp, ...ctx }: SpanContext): Span {\n // eslint-disable-next-line deprecation/deprecation\n if (startTimestamp && transaction.startTimestamp > startTimestamp) {\n // eslint-disable-next-line deprecation/deprecation\n transaction.startTimestamp = startTimestamp;\n }\n\n // eslint-disable-next-line deprecation/deprecation\n return transaction.startChild({\n startTimestamp,\n ...ctx,\n });\n}\n"],"names":[],"mappings":"AAGA;AACA;AACA;AACO,SAAS,kBAAkB,CAAC,KAAK,EAA4B;AACpE,EAAE,OAAO,OAAO,KAAM,KAAI,YAAY,QAAQ,CAAC,KAAK,CAAC,CAAA;AACrD,CAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,WAAW,CAAC,WAAW,EAAe,EAAE,cAAc,EAAE,GAAG,GAAA,EAAK,EAAqB;AACrG;AACA,EAAE,IAAI,cAAe,IAAG,WAAW,CAAC,cAAA,GAAiB,cAAc,EAAE;AACrE;AACA,IAAI,WAAW,CAAC,cAAe,GAAE,cAAc,CAAA;AAC/C,GAAE;AACF;AACA;AACA,EAAE,OAAO,WAAW,CAAC,UAAU,CAAC;AAChC,IAAI,cAAc;AAClB,IAAI,GAAG,GAAG;AACV,GAAG,CAAC,CAAA;AACJ;;;;"}
|
||||
302
node_modules/@sentry-internal/tracing/esm/browser/request.js
generated
vendored
Normal file
302
node_modules/@sentry-internal/tracing/esm/browser/request.js
generated
vendored
Normal file
@@ -0,0 +1,302 @@
|
||||
import { spanToJSON, hasTracingEnabled, setHttpStatus, getCurrentScope, getIsolationScope, startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, getClient, spanToTraceHeader, getDynamicSamplingContextFromSpan, getDynamicSamplingContextFromClient } from '@sentry/core';
|
||||
import { addFetchInstrumentationHandler, parseUrl, addXhrInstrumentationHandler, SENTRY_XHR_DATA_KEY, generateSentryTraceHeader, dynamicSamplingContextToSentryBaggageHeader, BAGGAGE_HEADER_NAME, browserPerformanceTimeOrigin, stringMatchesSomePattern } from '@sentry/utils';
|
||||
import { instrumentFetchRequest } from '../common/fetch.js';
|
||||
import { addPerformanceInstrumentationHandler } from './instrument.js';
|
||||
import { WINDOW } from './types.js';
|
||||
|
||||
/* eslint-disable max-lines */
|
||||
|
||||
const DEFAULT_TRACE_PROPAGATION_TARGETS = ['localhost', /^\/(?!\/)/];
|
||||
|
||||
/** Options for Request Instrumentation */
|
||||
|
||||
const defaultRequestInstrumentationOptions = {
|
||||
traceFetch: true,
|
||||
traceXHR: true,
|
||||
enableHTTPTimings: true,
|
||||
// TODO (v8): Remove this property
|
||||
tracingOrigins: DEFAULT_TRACE_PROPAGATION_TARGETS,
|
||||
tracePropagationTargets: DEFAULT_TRACE_PROPAGATION_TARGETS,
|
||||
};
|
||||
|
||||
/** Registers span creators for xhr and fetch requests */
|
||||
function instrumentOutgoingRequests(_options) {
|
||||
const {
|
||||
traceFetch,
|
||||
traceXHR,
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
tracePropagationTargets,
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
tracingOrigins,
|
||||
shouldCreateSpanForRequest,
|
||||
enableHTTPTimings,
|
||||
} = {
|
||||
traceFetch: defaultRequestInstrumentationOptions.traceFetch,
|
||||
traceXHR: defaultRequestInstrumentationOptions.traceXHR,
|
||||
..._options,
|
||||
};
|
||||
|
||||
const shouldCreateSpan =
|
||||
typeof shouldCreateSpanForRequest === 'function' ? shouldCreateSpanForRequest : (_) => true;
|
||||
|
||||
// TODO(v8) Remove tracingOrigins here
|
||||
// The only reason we're passing it in here is because this instrumentOutgoingRequests function is publicly exported
|
||||
// and we don't want to break the API. We can remove it in v8.
|
||||
const shouldAttachHeadersWithTargets = (url) =>
|
||||
shouldAttachHeaders(url, tracePropagationTargets || tracingOrigins);
|
||||
|
||||
const spans = {};
|
||||
|
||||
if (traceFetch) {
|
||||
addFetchInstrumentationHandler(handlerData => {
|
||||
const createdSpan = instrumentFetchRequest(handlerData, shouldCreateSpan, shouldAttachHeadersWithTargets, spans);
|
||||
// We cannot use `window.location` in the generic fetch instrumentation,
|
||||
// but we need it for reliable `server.address` attribute.
|
||||
// so we extend this in here
|
||||
if (createdSpan) {
|
||||
const fullUrl = getFullURL(handlerData.fetchData.url);
|
||||
const host = fullUrl ? parseUrl(fullUrl).host : undefined;
|
||||
createdSpan.setAttributes({
|
||||
'http.url': fullUrl,
|
||||
'server.address': host,
|
||||
});
|
||||
}
|
||||
|
||||
if (enableHTTPTimings && createdSpan) {
|
||||
addHTTPTimings(createdSpan);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (traceXHR) {
|
||||
addXhrInstrumentationHandler(handlerData => {
|
||||
const createdSpan = xhrCallback(handlerData, shouldCreateSpan, shouldAttachHeadersWithTargets, spans);
|
||||
if (enableHTTPTimings && createdSpan) {
|
||||
addHTTPTimings(createdSpan);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function isPerformanceResourceTiming(entry) {
|
||||
return (
|
||||
entry.entryType === 'resource' &&
|
||||
'initiatorType' in entry &&
|
||||
typeof (entry ).nextHopProtocol === 'string' &&
|
||||
(entry.initiatorType === 'fetch' || entry.initiatorType === 'xmlhttprequest')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a temporary observer to listen to the next fetch/xhr resourcing timings,
|
||||
* so that when timings hit their per-browser limit they don't need to be removed.
|
||||
*
|
||||
* @param span A span that has yet to be finished, must contain `url` on data.
|
||||
*/
|
||||
function addHTTPTimings(span) {
|
||||
const { url } = spanToJSON(span).data || {};
|
||||
|
||||
if (!url || typeof url !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
const cleanup = addPerformanceInstrumentationHandler('resource', ({ entries }) => {
|
||||
entries.forEach(entry => {
|
||||
if (isPerformanceResourceTiming(entry) && entry.name.endsWith(url)) {
|
||||
const spanData = resourceTimingEntryToSpanData(entry);
|
||||
spanData.forEach(data => span.setAttribute(...data));
|
||||
// In the next tick, clean this handler up
|
||||
// We have to wait here because otherwise this cleans itself up before it is fully done
|
||||
setTimeout(cleanup);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts ALPN protocol ids to name and version.
|
||||
*
|
||||
* (https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids)
|
||||
* @param nextHopProtocol PerformanceResourceTiming.nextHopProtocol
|
||||
*/
|
||||
function extractNetworkProtocol(nextHopProtocol) {
|
||||
let name = 'unknown';
|
||||
let version = 'unknown';
|
||||
let _name = '';
|
||||
for (const char of nextHopProtocol) {
|
||||
// http/1.1 etc.
|
||||
if (char === '/') {
|
||||
[name, version] = nextHopProtocol.split('/');
|
||||
break;
|
||||
}
|
||||
// h2, h3 etc.
|
||||
if (!isNaN(Number(char))) {
|
||||
name = _name === 'h' ? 'http' : _name;
|
||||
version = nextHopProtocol.split(_name)[1];
|
||||
break;
|
||||
}
|
||||
_name += char;
|
||||
}
|
||||
if (_name === nextHopProtocol) {
|
||||
// webrtc, ftp, etc.
|
||||
name = _name;
|
||||
}
|
||||
return { name, version };
|
||||
}
|
||||
|
||||
function getAbsoluteTime(time = 0) {
|
||||
return ((browserPerformanceTimeOrigin || performance.timeOrigin) + time) / 1000;
|
||||
}
|
||||
|
||||
function resourceTimingEntryToSpanData(resourceTiming) {
|
||||
const { name, version } = extractNetworkProtocol(resourceTiming.nextHopProtocol);
|
||||
|
||||
const timingSpanData = [];
|
||||
|
||||
timingSpanData.push(['network.protocol.version', version], ['network.protocol.name', name]);
|
||||
|
||||
if (!browserPerformanceTimeOrigin) {
|
||||
return timingSpanData;
|
||||
}
|
||||
return [
|
||||
...timingSpanData,
|
||||
['http.request.redirect_start', getAbsoluteTime(resourceTiming.redirectStart)],
|
||||
['http.request.fetch_start', getAbsoluteTime(resourceTiming.fetchStart)],
|
||||
['http.request.domain_lookup_start', getAbsoluteTime(resourceTiming.domainLookupStart)],
|
||||
['http.request.domain_lookup_end', getAbsoluteTime(resourceTiming.domainLookupEnd)],
|
||||
['http.request.connect_start', getAbsoluteTime(resourceTiming.connectStart)],
|
||||
['http.request.secure_connection_start', getAbsoluteTime(resourceTiming.secureConnectionStart)],
|
||||
['http.request.connection_end', getAbsoluteTime(resourceTiming.connectEnd)],
|
||||
['http.request.request_start', getAbsoluteTime(resourceTiming.requestStart)],
|
||||
['http.request.response_start', getAbsoluteTime(resourceTiming.responseStart)],
|
||||
['http.request.response_end', getAbsoluteTime(resourceTiming.responseEnd)],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that determines whether to attach tracing headers to a request.
|
||||
* This was extracted from `instrumentOutgoingRequests` to make it easier to test shouldAttachHeaders.
|
||||
* We only export this fuction for testing purposes.
|
||||
*/
|
||||
function shouldAttachHeaders(url, tracePropagationTargets) {
|
||||
return stringMatchesSomePattern(url, tracePropagationTargets || DEFAULT_TRACE_PROPAGATION_TARGETS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and track xhr request spans
|
||||
*
|
||||
* @returns Span if a span was created, otherwise void.
|
||||
*/
|
||||
// eslint-disable-next-line complexity
|
||||
function xhrCallback(
|
||||
handlerData,
|
||||
shouldCreateSpan,
|
||||
shouldAttachHeaders,
|
||||
spans,
|
||||
) {
|
||||
const xhr = handlerData.xhr;
|
||||
const sentryXhrData = xhr && xhr[SENTRY_XHR_DATA_KEY];
|
||||
|
||||
if (!hasTracingEnabled() || !xhr || xhr.__sentry_own_request__ || !sentryXhrData) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const shouldCreateSpanResult = shouldCreateSpan(sentryXhrData.url);
|
||||
|
||||
// check first if the request has finished and is tracked by an existing span which should now end
|
||||
if (handlerData.endTimestamp && shouldCreateSpanResult) {
|
||||
const spanId = xhr.__sentry_xhr_span_id__;
|
||||
if (!spanId) return;
|
||||
|
||||
const span = spans[spanId];
|
||||
if (span && sentryXhrData.status_code !== undefined) {
|
||||
setHttpStatus(span, sentryXhrData.status_code);
|
||||
span.end();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete spans[spanId];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const scope = getCurrentScope();
|
||||
const isolationScope = getIsolationScope();
|
||||
|
||||
const fullUrl = getFullURL(sentryXhrData.url);
|
||||
const host = fullUrl ? parseUrl(fullUrl).host : undefined;
|
||||
|
||||
const span = shouldCreateSpanResult
|
||||
? startInactiveSpan({
|
||||
name: `${sentryXhrData.method} ${sentryXhrData.url}`,
|
||||
onlyIfParent: true,
|
||||
attributes: {
|
||||
type: 'xhr',
|
||||
'http.method': sentryXhrData.method,
|
||||
'http.url': fullUrl,
|
||||
url: sentryXhrData.url,
|
||||
'server.address': host,
|
||||
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.browser',
|
||||
},
|
||||
op: 'http.client',
|
||||
})
|
||||
: undefined;
|
||||
|
||||
if (span) {
|
||||
xhr.__sentry_xhr_span_id__ = span.spanContext().spanId;
|
||||
spans[xhr.__sentry_xhr_span_id__] = span;
|
||||
}
|
||||
|
||||
const client = getClient();
|
||||
|
||||
if (xhr.setRequestHeader && shouldAttachHeaders(sentryXhrData.url) && client) {
|
||||
const { traceId, spanId, sampled, dsc } = {
|
||||
...isolationScope.getPropagationContext(),
|
||||
...scope.getPropagationContext(),
|
||||
};
|
||||
|
||||
const sentryTraceHeader = span ? spanToTraceHeader(span) : generateSentryTraceHeader(traceId, spanId, sampled);
|
||||
|
||||
const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(
|
||||
dsc ||
|
||||
(span ? getDynamicSamplingContextFromSpan(span) : getDynamicSamplingContextFromClient(traceId, client, scope)),
|
||||
);
|
||||
|
||||
setHeaderOnXhr(xhr, sentryTraceHeader, sentryBaggageHeader);
|
||||
}
|
||||
|
||||
return span;
|
||||
}
|
||||
|
||||
function setHeaderOnXhr(
|
||||
xhr,
|
||||
sentryTraceHeader,
|
||||
sentryBaggageHeader,
|
||||
) {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
xhr.setRequestHeader('sentry-trace', sentryTraceHeader);
|
||||
if (sentryBaggageHeader) {
|
||||
// From MDN: "If this method is called several times with the same header, the values are merged into one single request header."
|
||||
// We can therefore simply set a baggage header without checking what was there before
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
xhr.setRequestHeader(BAGGAGE_HEADER_NAME, sentryBaggageHeader);
|
||||
}
|
||||
} catch (_) {
|
||||
// Error: InvalidStateError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED.
|
||||
}
|
||||
}
|
||||
|
||||
function getFullURL(url) {
|
||||
try {
|
||||
// By adding a base URL to new URL(), this will also work for relative urls
|
||||
// If `url` is a full URL, the base URL is ignored anyhow
|
||||
const parsed = new URL(url, WINDOW.location.origin);
|
||||
return parsed.href;
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export { DEFAULT_TRACE_PROPAGATION_TARGETS, defaultRequestInstrumentationOptions, extractNetworkProtocol, instrumentOutgoingRequests, shouldAttachHeaders, xhrCallback };
|
||||
//# sourceMappingURL=request.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/request.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/request.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
67
node_modules/@sentry-internal/tracing/esm/browser/router.js
generated
vendored
Normal file
67
node_modules/@sentry-internal/tracing/esm/browser/router.js
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
import { logger, browserPerformanceTimeOrigin, addHistoryInstrumentationHandler } from '@sentry/utils';
|
||||
import { DEBUG_BUILD } from '../common/debug-build.js';
|
||||
import { WINDOW } from './types.js';
|
||||
|
||||
/**
|
||||
* Default function implementing pageload and navigation transactions
|
||||
*/
|
||||
function instrumentRoutingWithDefaults(
|
||||
customStartTransaction,
|
||||
startTransactionOnPageLoad = true,
|
||||
startTransactionOnLocationChange = true,
|
||||
) {
|
||||
if (!WINDOW || !WINDOW.location) {
|
||||
DEBUG_BUILD && logger.warn('Could not initialize routing instrumentation due to invalid location');
|
||||
return;
|
||||
}
|
||||
|
||||
let startingUrl = WINDOW.location.href;
|
||||
|
||||
let activeTransaction;
|
||||
if (startTransactionOnPageLoad) {
|
||||
activeTransaction = customStartTransaction({
|
||||
name: WINDOW.location.pathname,
|
||||
// pageload should always start at timeOrigin (and needs to be in s, not ms)
|
||||
startTimestamp: browserPerformanceTimeOrigin ? browserPerformanceTimeOrigin / 1000 : undefined,
|
||||
op: 'pageload',
|
||||
origin: 'auto.pageload.browser',
|
||||
metadata: { source: 'url' },
|
||||
});
|
||||
}
|
||||
|
||||
if (startTransactionOnLocationChange) {
|
||||
addHistoryInstrumentationHandler(({ to, from }) => {
|
||||
/**
|
||||
* This early return is there to account for some cases where a navigation transaction starts right after
|
||||
* long-running pageload. We make sure that if `from` is undefined and a valid `startingURL` exists, we don't
|
||||
* create an uneccessary navigation transaction.
|
||||
*
|
||||
* This was hard to duplicate, but this behavior stopped as soon as this fix was applied. This issue might also
|
||||
* only be caused in certain development environments where the usage of a hot module reloader is causing
|
||||
* errors.
|
||||
*/
|
||||
if (from === undefined && startingUrl && startingUrl.indexOf(to) !== -1) {
|
||||
startingUrl = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
if (from !== to) {
|
||||
startingUrl = undefined;
|
||||
if (activeTransaction) {
|
||||
DEBUG_BUILD && logger.log(`[Tracing] Finishing current transaction with op: ${activeTransaction.op}`);
|
||||
// If there's an open transaction on the scope, we need to finish it before creating an new one.
|
||||
activeTransaction.end();
|
||||
}
|
||||
activeTransaction = customStartTransaction({
|
||||
name: WINDOW.location.pathname,
|
||||
op: 'navigation',
|
||||
origin: 'auto.navigation.browser',
|
||||
metadata: { source: 'url' },
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { instrumentRoutingWithDefaults };
|
||||
//# sourceMappingURL=router.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/router.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/router.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"router.js","sources":["../../../src/browser/router.ts"],"sourcesContent":["import type { Transaction, TransactionContext } from '@sentry/types';\nimport { addHistoryInstrumentationHandler, browserPerformanceTimeOrigin, logger } from '@sentry/utils';\n\nimport { DEBUG_BUILD } from '../common/debug-build';\nimport { WINDOW } from './types';\n\n/**\n * Default function implementing pageload and navigation transactions\n */\nexport function instrumentRoutingWithDefaults<T extends Transaction>(\n customStartTransaction: (context: TransactionContext) => T | undefined,\n startTransactionOnPageLoad: boolean = true,\n startTransactionOnLocationChange: boolean = true,\n): void {\n if (!WINDOW || !WINDOW.location) {\n DEBUG_BUILD && logger.warn('Could not initialize routing instrumentation due to invalid location');\n return;\n }\n\n let startingUrl: string | undefined = WINDOW.location.href;\n\n let activeTransaction: T | undefined;\n if (startTransactionOnPageLoad) {\n activeTransaction = customStartTransaction({\n name: WINDOW.location.pathname,\n // pageload should always start at timeOrigin (and needs to be in s, not ms)\n startTimestamp: browserPerformanceTimeOrigin ? browserPerformanceTimeOrigin / 1000 : undefined,\n op: 'pageload',\n origin: 'auto.pageload.browser',\n metadata: { source: 'url' },\n });\n }\n\n if (startTransactionOnLocationChange) {\n addHistoryInstrumentationHandler(({ to, from }) => {\n /**\n * This early return is there to account for some cases where a navigation transaction starts right after\n * long-running pageload. We make sure that if `from` is undefined and a valid `startingURL` exists, we don't\n * create an uneccessary navigation transaction.\n *\n * This was hard to duplicate, but this behavior stopped as soon as this fix was applied. This issue might also\n * only be caused in certain development environments where the usage of a hot module reloader is causing\n * errors.\n */\n if (from === undefined && startingUrl && startingUrl.indexOf(to) !== -1) {\n startingUrl = undefined;\n return;\n }\n\n if (from !== to) {\n startingUrl = undefined;\n if (activeTransaction) {\n DEBUG_BUILD && logger.log(`[Tracing] Finishing current transaction with op: ${activeTransaction.op}`);\n // If there's an open transaction on the scope, we need to finish it before creating an new one.\n activeTransaction.end();\n }\n activeTransaction = customStartTransaction({\n name: WINDOW.location.pathname,\n op: 'navigation',\n origin: 'auto.navigation.browser',\n metadata: { source: 'url' },\n });\n }\n });\n }\n}\n"],"names":[],"mappings":";;;;AAMA;AACA;AACA;AACO,SAAS,6BAA6B;AAC7C,EAAE,sBAAsB;AACxB,EAAE,0BAA0B,GAAY,IAAI;AAC5C,EAAE,gCAAgC,GAAY,IAAI;AAClD,EAAQ;AACR,EAAE,IAAI,CAAC,MAAA,IAAU,CAAC,MAAM,CAAC,QAAQ,EAAE;AACnC,IAAI,eAAe,MAAM,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAA;AACtG,IAAI,OAAM;AACV,GAAE;AACF;AACA,EAAE,IAAI,WAAW,GAAuB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAA;AAC5D;AACA,EAAE,IAAI,iBAAiB,CAAA;AACvB,EAAE,IAAI,0BAA0B,EAAE;AAClC,IAAI,iBAAA,GAAoB,sBAAsB,CAAC;AAC/C,MAAM,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ;AACpC;AACA,MAAM,cAAc,EAAE,4BAA6B,GAAE,+BAA+B,IAAA,GAAO,SAAS;AACpG,MAAM,EAAE,EAAE,UAAU;AACpB,MAAM,MAAM,EAAE,uBAAuB;AACrC,MAAM,QAAQ,EAAE,EAAE,MAAM,EAAE,OAAO;AACjC,KAAK,CAAC,CAAA;AACN,GAAE;AACF;AACA,EAAE,IAAI,gCAAgC,EAAE;AACxC,IAAI,gCAAgC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAA,EAAM,KAAK;AACvD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,IAAI,IAAA,KAAS,SAAA,IAAa,WAAY,IAAG,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE;AAC/E,QAAQ,WAAA,GAAc,SAAS,CAAA;AAC/B,QAAQ,OAAM;AACd,OAAM;AACN;AACA,MAAM,IAAI,IAAK,KAAI,EAAE,EAAE;AACvB,QAAQ,WAAA,GAAc,SAAS,CAAA;AAC/B,QAAQ,IAAI,iBAAiB,EAAE;AAC/B,UAAU,WAAY,IAAG,MAAM,CAAC,GAAG,CAAC,CAAC,iDAAiD,EAAE,iBAAiB,CAAC,EAAE,CAAC,CAAA,CAAA,CAAA;AACA;AACA,UAAA,iBAAA,CAAA,GAAA,EAAA,CAAA;AACA,SAAA;AACA,QAAA,iBAAA,GAAA,sBAAA,CAAA;AACA,UAAA,IAAA,EAAA,MAAA,CAAA,QAAA,CAAA,QAAA;AACA,UAAA,EAAA,EAAA,YAAA;AACA,UAAA,MAAA,EAAA,yBAAA;AACA,UAAA,QAAA,EAAA,EAAA,MAAA,EAAA,KAAA,EAAA;AACA,SAAA,CAAA,CAAA;AACA,OAAA;AACA,KAAA,CAAA,CAAA;AACA,GAAA;AACA;;;;"}
|
||||
8
node_modules/@sentry-internal/tracing/esm/browser/types.js
generated
vendored
Normal file
8
node_modules/@sentry-internal/tracing/esm/browser/types.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import { GLOBAL_OBJ } from '@sentry/utils';
|
||||
|
||||
const WINDOW = GLOBAL_OBJ
|
||||
|
||||
;
|
||||
|
||||
export { WINDOW };
|
||||
//# sourceMappingURL=types.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/types.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/types.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","sources":["../../../src/browser/types.ts"],"sourcesContent":["import { GLOBAL_OBJ } from '@sentry/utils';\n\nexport const WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ &\n // document is not available in all browser environments (webworkers). We make it optional so you have to explicitly check for it\n Omit<Window, 'document'> &\n Partial<Pick<Window, 'document'>>;\n"],"names":[],"mappings":";;AAEO,MAAM,MAAO,GAAE,UAAW;;;;;;"}
|
||||
108
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getCLS.js
generated
vendored
Normal file
108
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getCLS.js
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
import { bindReporter } from './lib/bindReporter.js';
|
||||
import { initMetric } from './lib/initMetric.js';
|
||||
import { observe } from './lib/observe.js';
|
||||
import { onHidden } from './lib/onHidden.js';
|
||||
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Calculates the [CLS](https://web.dev/cls/) value for the current page and
|
||||
* calls the `callback` function once the value is ready to be reported, along
|
||||
* with all `layout-shift` performance entries that were used in the metric
|
||||
* value calculation. The reported value is a `double` (corresponding to a
|
||||
* [layout shift score](https://web.dev/cls/#layout-shift-score)).
|
||||
*
|
||||
* If the `reportAllChanges` configuration option is set to `true`, the
|
||||
* `callback` function will be called as soon as the value is initially
|
||||
* determined as well as any time the value changes throughout the page
|
||||
* lifespan.
|
||||
*
|
||||
* _**Important:** CLS should be continually monitored for changes throughout
|
||||
* the entire lifespan of a page—including if the user returns to the page after
|
||||
* it's been hidden/backgrounded. However, since browsers often [will not fire
|
||||
* additional callbacks once the user has backgrounded a
|
||||
* page](https://developer.chrome.com/blog/page-lifecycle-api/#advice-hidden),
|
||||
* `callback` is always called when the page's visibility state changes to
|
||||
* hidden. As a result, the `callback` function might be called multiple times
|
||||
* during the same page load._
|
||||
*/
|
||||
const onCLS = (
|
||||
onReport,
|
||||
options = {},
|
||||
) => {
|
||||
const metric = initMetric('CLS', 0);
|
||||
let report;
|
||||
|
||||
let sessionValue = 0;
|
||||
let sessionEntries = [];
|
||||
|
||||
// const handleEntries = (entries: Metric['entries']) => {
|
||||
const handleEntries = (entries) => {
|
||||
entries.forEach(entry => {
|
||||
// Only count layout shifts without recent user input.
|
||||
if (!entry.hadRecentInput) {
|
||||
const firstSessionEntry = sessionEntries[0];
|
||||
const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
|
||||
|
||||
// If the entry occurred less than 1 second after the previous entry and
|
||||
// less than 5 seconds after the first entry in the session, include the
|
||||
// entry in the current session. Otherwise, start a new session.
|
||||
if (
|
||||
sessionValue &&
|
||||
sessionEntries.length !== 0 &&
|
||||
entry.startTime - lastSessionEntry.startTime < 1000 &&
|
||||
entry.startTime - firstSessionEntry.startTime < 5000
|
||||
) {
|
||||
sessionValue += entry.value;
|
||||
sessionEntries.push(entry);
|
||||
} else {
|
||||
sessionValue = entry.value;
|
||||
sessionEntries = [entry];
|
||||
}
|
||||
|
||||
// If the current session value is larger than the current CLS value,
|
||||
// update CLS and the entries contributing to it.
|
||||
if (sessionValue > metric.value) {
|
||||
metric.value = sessionValue;
|
||||
metric.entries = sessionEntries;
|
||||
if (report) {
|
||||
report();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const po = observe('layout-shift', handleEntries);
|
||||
if (po) {
|
||||
report = bindReporter(onReport, metric, options.reportAllChanges);
|
||||
|
||||
const stopListening = () => {
|
||||
handleEntries(po.takeRecords() );
|
||||
report(true);
|
||||
};
|
||||
|
||||
onHidden(stopListening);
|
||||
|
||||
return stopListening;
|
||||
}
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
export { onCLS };
|
||||
//# sourceMappingURL=getCLS.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getCLS.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getCLS.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
63
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getFID.js
generated
vendored
Normal file
63
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getFID.js
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
import { bindReporter } from './lib/bindReporter.js';
|
||||
import { getVisibilityWatcher } from './lib/getVisibilityWatcher.js';
|
||||
import { initMetric } from './lib/initMetric.js';
|
||||
import { observe } from './lib/observe.js';
|
||||
import { onHidden } from './lib/onHidden.js';
|
||||
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Calculates the [FID](https://web.dev/fid/) value for the current page and
|
||||
* calls the `callback` function once the value is ready, along with the
|
||||
* relevant `first-input` performance entry used to determine the value. The
|
||||
* reported value is a `DOMHighResTimeStamp`.
|
||||
*
|
||||
* _**Important:** since FID is only reported after the user interacts with the
|
||||
* page, it's possible that it will not be reported for some page loads._
|
||||
*/
|
||||
const onFID = (onReport) => {
|
||||
const visibilityWatcher = getVisibilityWatcher();
|
||||
const metric = initMetric('FID');
|
||||
// eslint-disable-next-line prefer-const
|
||||
let report;
|
||||
|
||||
const handleEntry = (entry) => {
|
||||
// Only report if the page wasn't hidden prior to the first input.
|
||||
if (entry.startTime < visibilityWatcher.firstHiddenTime) {
|
||||
metric.value = entry.processingStart - entry.startTime;
|
||||
metric.entries.push(entry);
|
||||
report(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEntries = (entries) => {
|
||||
(entries ).forEach(handleEntry);
|
||||
};
|
||||
|
||||
const po = observe('first-input', handleEntries);
|
||||
report = bindReporter(onReport, metric);
|
||||
|
||||
if (po) {
|
||||
onHidden(() => {
|
||||
handleEntries(po.takeRecords() );
|
||||
po.disconnect();
|
||||
}, true);
|
||||
}
|
||||
};
|
||||
|
||||
export { onFID };
|
||||
//# sourceMappingURL=getFID.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getFID.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getFID.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"getFID.js","sources":["../../../../src/browser/web-vitals/getFID.ts"],"sourcesContent":["/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { bindReporter } from './lib/bindReporter';\nimport { getVisibilityWatcher } from './lib/getVisibilityWatcher';\nimport { initMetric } from './lib/initMetric';\nimport { observe } from './lib/observe';\nimport { onHidden } from './lib/onHidden';\nimport type { FIDMetric, PerformanceEventTiming, ReportCallback } from './types';\n\n/**\n * Calculates the [FID](https://web.dev/fid/) value for the current page and\n * calls the `callback` function once the value is ready, along with the\n * relevant `first-input` performance entry used to determine the value. The\n * reported value is a `DOMHighResTimeStamp`.\n *\n * _**Important:** since FID is only reported after the user interacts with the\n * page, it's possible that it will not be reported for some page loads._\n */\nexport const onFID = (onReport: ReportCallback): void => {\n const visibilityWatcher = getVisibilityWatcher();\n const metric = initMetric('FID');\n // eslint-disable-next-line prefer-const\n let report: ReturnType<typeof bindReporter>;\n\n const handleEntry = (entry: PerformanceEventTiming): void => {\n // Only report if the page wasn't hidden prior to the first input.\n if (entry.startTime < visibilityWatcher.firstHiddenTime) {\n metric.value = entry.processingStart - entry.startTime;\n metric.entries.push(entry);\n report(true);\n }\n };\n\n const handleEntries = (entries: FIDMetric['entries']): void => {\n (entries as PerformanceEventTiming[]).forEach(handleEntry);\n };\n\n const po = observe('first-input', handleEntries);\n report = bindReporter(onReport, metric);\n\n if (po) {\n onHidden(() => {\n handleEntries(po.takeRecords() as FIDMetric['entries']);\n po.disconnect();\n }, true);\n }\n};\n"],"names":[],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AASA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACa,MAAA,KAAA,GAAQ,CAAC,QAAQ,KAA2B;AACzD,EAAE,MAAM,iBAAA,GAAoB,oBAAoB,EAAE,CAAA;AAClD,EAAE,MAAM,MAAO,GAAE,UAAU,CAAC,KAAK,CAAC,CAAA;AAClC;AACA,EAAE,IAAI,MAAM,CAAA;AACZ;AACA,EAAE,MAAM,WAAA,GAAc,CAAC,KAAK,KAAmC;AAC/D;AACA,IAAI,IAAI,KAAK,CAAC,YAAY,iBAAiB,CAAC,eAAe,EAAE;AAC7D,MAAM,MAAM,CAAC,KAAA,GAAQ,KAAK,CAAC,eAAgB,GAAE,KAAK,CAAC,SAAS,CAAA;AAC5D,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AAChC,MAAM,MAAM,CAAC,IAAI,CAAC,CAAA;AAClB,KAAI;AACJ,GAAG,CAAA;AACH;AACA,EAAE,MAAM,aAAA,GAAgB,CAAC,OAAO,KAAiC;AACjE,IAAI,CAAC,OAAQ,GAA6B,OAAO,CAAC,WAAW,CAAC,CAAA;AAC9D,GAAG,CAAA;AACH;AACA,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,EAAE,aAAa,CAAC,CAAA;AAClD,EAAE,SAAS,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;AACzC;AACA,EAAE,IAAI,EAAE,EAAE;AACV,IAAI,QAAQ,CAAC,MAAM;AACnB,MAAM,aAAa,CAAC,EAAE,CAAC,WAAW,IAA2B,CAAA;AAC7D,MAAM,EAAE,CAAC,UAAU,EAAE,CAAA;AACrB,KAAK,EAAE,IAAI,CAAC,CAAA;AACZ,GAAE;AACF;;;;"}
|
||||
210
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getINP.js
generated
vendored
Normal file
210
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getINP.js
generated
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
import { bindReporter } from './lib/bindReporter.js';
|
||||
import { initMetric } from './lib/initMetric.js';
|
||||
import { observe } from './lib/observe.js';
|
||||
import { onHidden } from './lib/onHidden.js';
|
||||
import { initInteractionCountPolyfill, getInteractionCount } from './lib/polyfills/interactionCountPolyfill.js';
|
||||
|
||||
/*
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the interaction count since the last bfcache restore (or for the
|
||||
* full page lifecycle if there were no bfcache restores).
|
||||
*/
|
||||
const getInteractionCountForNavigation = () => {
|
||||
return getInteractionCount();
|
||||
};
|
||||
|
||||
// To prevent unnecessary memory usage on pages with lots of interactions,
|
||||
// store at most 10 of the longest interactions to consider as INP candidates.
|
||||
const MAX_INTERACTIONS_TO_CONSIDER = 10;
|
||||
|
||||
// A list of longest interactions on the page (by latency) sorted so the
|
||||
// longest one is first. The list is as most MAX_INTERACTIONS_TO_CONSIDER long.
|
||||
const longestInteractionList = [];
|
||||
|
||||
// A mapping of longest interactions by their interaction ID.
|
||||
// This is used for faster lookup.
|
||||
const longestInteractionMap = {};
|
||||
|
||||
/**
|
||||
* Takes a performance entry and adds it to the list of worst interactions
|
||||
* if its duration is long enough to make it among the worst. If the
|
||||
* entry is part of an existing interaction, it is merged and the latency
|
||||
* and entries list is updated as needed.
|
||||
*/
|
||||
const processEntry = (entry) => {
|
||||
// The least-long of the 10 longest interactions.
|
||||
const minLongestInteraction = longestInteractionList[longestInteractionList.length - 1];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const existingInteraction = longestInteractionMap[entry.interactionId];
|
||||
|
||||
// Only process the entry if it's possibly one of the ten longest,
|
||||
// or if it's part of an existing interaction.
|
||||
if (
|
||||
existingInteraction ||
|
||||
longestInteractionList.length < MAX_INTERACTIONS_TO_CONSIDER ||
|
||||
entry.duration > minLongestInteraction.latency
|
||||
) {
|
||||
// If the interaction already exists, update it. Otherwise create one.
|
||||
if (existingInteraction) {
|
||||
existingInteraction.entries.push(entry);
|
||||
existingInteraction.latency = Math.max(existingInteraction.latency, entry.duration);
|
||||
} else {
|
||||
const interaction = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
id: entry.interactionId,
|
||||
latency: entry.duration,
|
||||
entries: [entry],
|
||||
};
|
||||
longestInteractionMap[interaction.id] = interaction;
|
||||
longestInteractionList.push(interaction);
|
||||
}
|
||||
|
||||
// Sort the entries by latency (descending) and keep only the top ten.
|
||||
longestInteractionList.sort((a, b) => b.latency - a.latency);
|
||||
longestInteractionList.splice(MAX_INTERACTIONS_TO_CONSIDER).forEach(i => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete longestInteractionMap[i.id];
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the estimated p98 longest interaction based on the stored
|
||||
* interaction candidates and the interaction count for the current page.
|
||||
*/
|
||||
const estimateP98LongestInteraction = () => {
|
||||
const candidateInteractionIndex = Math.min(
|
||||
longestInteractionList.length - 1,
|
||||
Math.floor(getInteractionCountForNavigation() / 50),
|
||||
);
|
||||
|
||||
return longestInteractionList[candidateInteractionIndex];
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates the [INP](https://web.dev/responsiveness/) value for the current
|
||||
* page and calls the `callback` function once the value is ready, along with
|
||||
* the `event` performance entries reported for that interaction. The reported
|
||||
* value is a `DOMHighResTimeStamp`.
|
||||
*
|
||||
* A custom `durationThreshold` configuration option can optionally be passed to
|
||||
* control what `event-timing` entries are considered for INP reporting. The
|
||||
* default threshold is `40`, which means INP scores of less than 40 are
|
||||
* reported as 0. Note that this will not affect your 75th percentile INP value
|
||||
* unless that value is also less than 40 (well below the recommended
|
||||
* [good](https://web.dev/inp/#what-is-a-good-inp-score) threshold).
|
||||
*
|
||||
* If the `reportAllChanges` configuration option is set to `true`, the
|
||||
* `callback` function will be called as soon as the value is initially
|
||||
* determined as well as any time the value changes throughout the page
|
||||
* lifespan.
|
||||
*
|
||||
* _**Important:** INP should be continually monitored for changes throughout
|
||||
* the entire lifespan of a page—including if the user returns to the page after
|
||||
* it's been hidden/backgrounded. However, since browsers often [will not fire
|
||||
* additional callbacks once the user has backgrounded a
|
||||
* page](https://developer.chrome.com/blog/page-lifecycle-api/#advice-hidden),
|
||||
* `callback` is always called when the page's visibility state changes to
|
||||
* hidden. As a result, the `callback` function might be called multiple times
|
||||
* during the same page load._
|
||||
*/
|
||||
const onINP = (onReport, opts) => {
|
||||
// Set defaults
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
opts = opts || {};
|
||||
|
||||
// https://web.dev/inp/#what's-a-%22good%22-inp-value
|
||||
// const thresholds = [200, 500];
|
||||
|
||||
// TODO(philipwalton): remove once the polyfill is no longer needed.
|
||||
initInteractionCountPolyfill();
|
||||
|
||||
const metric = initMetric('INP');
|
||||
// eslint-disable-next-line prefer-const
|
||||
let report;
|
||||
|
||||
const handleEntries = (entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.interactionId) {
|
||||
processEntry(entry);
|
||||
}
|
||||
|
||||
// Entries of type `first-input` don't currently have an `interactionId`,
|
||||
// so to consider them in INP we have to first check that an existing
|
||||
// entry doesn't match the `duration` and `startTime`.
|
||||
// Note that this logic assumes that `event` entries are dispatched
|
||||
// before `first-input` entries. This is true in Chrome but it is not
|
||||
// true in Firefox; however, Firefox doesn't support interactionId, so
|
||||
// it's not an issue at the moment.
|
||||
// TODO(philipwalton): remove once crbug.com/1325826 is fixed.
|
||||
if (entry.entryType === 'first-input') {
|
||||
const noMatchingEntry = !longestInteractionList.some(interaction => {
|
||||
return interaction.entries.some(prevEntry => {
|
||||
return entry.duration === prevEntry.duration && entry.startTime === prevEntry.startTime;
|
||||
});
|
||||
});
|
||||
if (noMatchingEntry) {
|
||||
processEntry(entry);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const inp = estimateP98LongestInteraction();
|
||||
|
||||
if (inp && inp.latency !== metric.value) {
|
||||
metric.value = inp.latency;
|
||||
metric.entries = inp.entries;
|
||||
report();
|
||||
}
|
||||
};
|
||||
|
||||
const po = observe('event', handleEntries, {
|
||||
// Event Timing entries have their durations rounded to the nearest 8ms,
|
||||
// so a duration of 40ms would be any event that spans 2.5 or more frames
|
||||
// at 60Hz. This threshold is chosen to strike a balance between usefulness
|
||||
// and performance. Running this callback for any interaction that spans
|
||||
// just one or two frames is likely not worth the insight that could be
|
||||
// gained.
|
||||
durationThreshold: opts.durationThreshold || 40,
|
||||
} );
|
||||
|
||||
report = bindReporter(onReport, metric, opts.reportAllChanges);
|
||||
|
||||
if (po) {
|
||||
// Also observe entries of type `first-input`. This is useful in cases
|
||||
// where the first interaction is less than the `durationThreshold`.
|
||||
po.observe({ type: 'first-input', buffered: true });
|
||||
|
||||
onHidden(() => {
|
||||
handleEntries(po.takeRecords() );
|
||||
|
||||
// If the interaction count shows that there were interactions but
|
||||
// none were captured by the PerformanceObserver, report a latency of 0.
|
||||
if (metric.value < 0 && getInteractionCountForNavigation() > 0) {
|
||||
metric.value = 0;
|
||||
metric.entries = [];
|
||||
}
|
||||
|
||||
report(true);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export { onINP };
|
||||
//# sourceMappingURL=getINP.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getINP.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getINP.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
88
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getLCP.js
generated
vendored
Normal file
88
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getLCP.js
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
import { WINDOW } from '../types.js';
|
||||
import { bindReporter } from './lib/bindReporter.js';
|
||||
import { getActivationStart } from './lib/getActivationStart.js';
|
||||
import { getVisibilityWatcher } from './lib/getVisibilityWatcher.js';
|
||||
import { initMetric } from './lib/initMetric.js';
|
||||
import { observe } from './lib/observe.js';
|
||||
import { onHidden } from './lib/onHidden.js';
|
||||
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const reportedMetricIDs = {};
|
||||
|
||||
/**
|
||||
* Calculates the [LCP](https://web.dev/lcp/) value for the current page and
|
||||
* calls the `callback` function once the value is ready (along with the
|
||||
* relevant `largest-contentful-paint` performance entry used to determine the
|
||||
* value). The reported value is a `DOMHighResTimeStamp`.
|
||||
*/
|
||||
const onLCP = (onReport) => {
|
||||
const visibilityWatcher = getVisibilityWatcher();
|
||||
const metric = initMetric('LCP');
|
||||
let report;
|
||||
|
||||
const handleEntries = (entries) => {
|
||||
const lastEntry = entries[entries.length - 1] ;
|
||||
if (lastEntry) {
|
||||
// The startTime attribute returns the value of the renderTime if it is
|
||||
// not 0, and the value of the loadTime otherwise. The activationStart
|
||||
// reference is used because LCP should be relative to page activation
|
||||
// rather than navigation start if the page was prerendered.
|
||||
const value = Math.max(lastEntry.startTime - getActivationStart(), 0);
|
||||
|
||||
// Only report if the page wasn't hidden prior to LCP.
|
||||
if (value < visibilityWatcher.firstHiddenTime) {
|
||||
metric.value = value;
|
||||
metric.entries = [lastEntry];
|
||||
report();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const po = observe('largest-contentful-paint', handleEntries);
|
||||
|
||||
if (po) {
|
||||
report = bindReporter(onReport, metric);
|
||||
|
||||
const stopListening = () => {
|
||||
if (!reportedMetricIDs[metric.id]) {
|
||||
handleEntries(po.takeRecords() );
|
||||
po.disconnect();
|
||||
reportedMetricIDs[metric.id] = true;
|
||||
report(true);
|
||||
}
|
||||
};
|
||||
|
||||
// Stop listening after input. Note: while scrolling is an input that
|
||||
// stop LCP observation, it's unreliable since it can be programmatically
|
||||
// generated. See: https://github.com/GoogleChrome/web-vitals/issues/75
|
||||
['keydown', 'click'].forEach(type => {
|
||||
if (WINDOW.document) {
|
||||
addEventListener(type, stopListening, { once: true, capture: true });
|
||||
}
|
||||
});
|
||||
|
||||
onHidden(stopListening, true);
|
||||
|
||||
return stopListening;
|
||||
}
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
export { onLCP };
|
||||
//# sourceMappingURL=getLCP.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getLCP.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/getLCP.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
28
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/bindReporter.js
generated
vendored
Normal file
28
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/bindReporter.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
const bindReporter = (
|
||||
callback,
|
||||
metric,
|
||||
reportAllChanges,
|
||||
) => {
|
||||
let prevValue;
|
||||
let delta;
|
||||
return (forceReport) => {
|
||||
if (metric.value >= 0) {
|
||||
if (forceReport || reportAllChanges) {
|
||||
delta = metric.value - (prevValue || 0);
|
||||
|
||||
// Report the metric if there's a non-zero delta or if no previous
|
||||
// value exists (which can happen in the case of the document becoming
|
||||
// hidden when the metric value is 0).
|
||||
// See: https://github.com/GoogleChrome/web-vitals/issues/14
|
||||
if (delta || prevValue === undefined) {
|
||||
prevValue = metric.value;
|
||||
metric.delta = delta;
|
||||
callback(metric);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export { bindReporter };
|
||||
//# sourceMappingURL=bindReporter.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/bindReporter.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/bindReporter.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"bindReporter.js","sources":["../../../../../src/browser/web-vitals/lib/bindReporter.ts"],"sourcesContent":["/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Metric, ReportCallback } from '../types';\n\nexport const bindReporter = (\n callback: ReportCallback,\n metric: Metric,\n reportAllChanges?: boolean,\n): ((forceReport?: boolean) => void) => {\n let prevValue: number;\n let delta: number;\n return (forceReport?: boolean) => {\n if (metric.value >= 0) {\n if (forceReport || reportAllChanges) {\n delta = metric.value - (prevValue || 0);\n\n // Report the metric if there's a non-zero delta or if no previous\n // value exists (which can happen in the case of the document becoming\n // hidden when the metric value is 0).\n // See: https://github.com/GoogleChrome/web-vitals/issues/14\n if (delta || prevValue === undefined) {\n prevValue = metric.value;\n metric.delta = delta;\n callback(metric);\n }\n }\n }\n };\n};\n"],"names":[],"mappings":"AAkBO,MAAM,eAAe;AAC5B,EAAE,QAAQ;AACV,EAAE,MAAM;AACR,EAAE,gBAAgB;AAClB,KAAwC;AACxC,EAAE,IAAI,SAAS,CAAA;AACf,EAAE,IAAI,KAAK,CAAA;AACX,EAAE,OAAO,CAAC,WAAW,KAAe;AACpC,IAAI,IAAI,MAAM,CAAC,KAAM,IAAG,CAAC,EAAE;AAC3B,MAAM,IAAI,WAAY,IAAG,gBAAgB,EAAE;AAC3C,QAAQ,KAAA,GAAQ,MAAM,CAAC,KAAA,IAAS,SAAA,IAAa,CAAC,CAAC,CAAA;AAC/C;AACA;AACA;AACA;AACA;AACA,QAAQ,IAAI,KAAA,IAAS,SAAU,KAAI,SAAS,EAAE;AAC9C,UAAU,SAAU,GAAE,MAAM,CAAC,KAAK,CAAA;AAClC,UAAU,MAAM,CAAC,KAAM,GAAE,KAAK,CAAA;AAC9B,UAAU,QAAQ,CAAC,MAAM,CAAC,CAAA;AAC1B,SAAQ;AACR,OAAM;AACN,KAAI;AACJ,GAAG,CAAA;AACH;;;;"}
|
||||
27
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/generateUniqueID.js
generated
vendored
Normal file
27
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/generateUniqueID.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Performantly generate a unique, 30-char string by combining a version
|
||||
* number, the current timestamp with a 13-digit number integer.
|
||||
* @return {string}
|
||||
*/
|
||||
const generateUniqueID = () => {
|
||||
return `v3-${Date.now()}-${Math.floor(Math.random() * (9e12 - 1)) + 1e12}`;
|
||||
};
|
||||
|
||||
export { generateUniqueID };
|
||||
//# sourceMappingURL=generateUniqueID.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/generateUniqueID.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/generateUniqueID.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"generateUniqueID.js","sources":["../../../../../src/browser/web-vitals/lib/generateUniqueID.ts"],"sourcesContent":["/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Performantly generate a unique, 30-char string by combining a version\n * number, the current timestamp with a 13-digit number integer.\n * @return {string}\n */\nexport const generateUniqueID = (): string => {\n return `v3-${Date.now()}-${Math.floor(Math.random() * (9e12 - 1)) + 1e12}`;\n};\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACa,MAAA,gBAAA,GAAmB,MAAc;AAC9C,EAAE,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAC,IAAK,IAAA,GAAO,CAAC,CAAC,CAAA,GAAI,IAAI,CAAC,CAAA,CAAA;AACA;;;;"}
|
||||
25
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/getActivationStart.js
generated
vendored
Normal file
25
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/getActivationStart.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import { getNavigationEntry } from './getNavigationEntry.js';
|
||||
|
||||
/*
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const getActivationStart = () => {
|
||||
const navEntry = getNavigationEntry();
|
||||
return (navEntry && navEntry.activationStart) || 0;
|
||||
};
|
||||
|
||||
export { getActivationStart };
|
||||
//# sourceMappingURL=getActivationStart.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/getActivationStart.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/getActivationStart.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"getActivationStart.js","sources":["../../../../../src/browser/web-vitals/lib/getActivationStart.ts"],"sourcesContent":["/*\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getNavigationEntry } from './getNavigationEntry';\n\nexport const getActivationStart = (): number => {\n const navEntry = getNavigationEntry();\n return (navEntry && navEntry.activationStart) || 0;\n};\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACa,MAAA,kBAAA,GAAqB,MAAc;AAChD,EAAE,MAAM,QAAA,GAAW,kBAAkB,EAAE,CAAA;AACvC,EAAE,OAAO,CAAC,QAAS,IAAG,QAAQ,CAAC,eAAe,KAAK,CAAC,CAAA;AACpD;;;;"}
|
||||
53
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/getNavigationEntry.js
generated
vendored
Normal file
53
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/getNavigationEntry.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
import { WINDOW } from '../../types.js';
|
||||
|
||||
/*
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const getNavigationEntryFromPerformanceTiming = () => {
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const timing = WINDOW.performance.timing;
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
const type = WINDOW.performance.navigation.type;
|
||||
|
||||
const navigationEntry = {
|
||||
entryType: 'navigation',
|
||||
startTime: 0,
|
||||
type: type == 2 ? 'back_forward' : type === 1 ? 'reload' : 'navigate',
|
||||
};
|
||||
|
||||
for (const key in timing) {
|
||||
if (key !== 'navigationStart' && key !== 'toJSON') {
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
navigationEntry[key] = Math.max((timing[key ] ) - timing.navigationStart, 0);
|
||||
}
|
||||
}
|
||||
return navigationEntry ;
|
||||
};
|
||||
|
||||
const getNavigationEntry = () => {
|
||||
if (WINDOW.__WEB_VITALS_POLYFILL__) {
|
||||
return (
|
||||
WINDOW.performance &&
|
||||
((performance.getEntriesByType && performance.getEntriesByType('navigation')[0]) ||
|
||||
getNavigationEntryFromPerformanceTiming())
|
||||
);
|
||||
} else {
|
||||
return WINDOW.performance && performance.getEntriesByType && performance.getEntriesByType('navigation')[0];
|
||||
}
|
||||
};
|
||||
|
||||
export { getNavigationEntry };
|
||||
//# sourceMappingURL=getNavigationEntry.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/getNavigationEntry.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/getNavigationEntry.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"getNavigationEntry.js","sources":["../../../../../src/browser/web-vitals/lib/getNavigationEntry.ts"],"sourcesContent":["/*\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { WINDOW } from '../../types';\nimport type { NavigationTimingPolyfillEntry } from '../types';\n\nconst getNavigationEntryFromPerformanceTiming = (): NavigationTimingPolyfillEntry => {\n // eslint-disable-next-line deprecation/deprecation\n const timing = WINDOW.performance.timing;\n // eslint-disable-next-line deprecation/deprecation\n const type = WINDOW.performance.navigation.type;\n\n const navigationEntry: { [key: string]: number | string } = {\n entryType: 'navigation',\n startTime: 0,\n type: type == 2 ? 'back_forward' : type === 1 ? 'reload' : 'navigate',\n };\n\n for (const key in timing) {\n if (key !== 'navigationStart' && key !== 'toJSON') {\n // eslint-disable-next-line deprecation/deprecation\n navigationEntry[key] = Math.max((timing[key as keyof PerformanceTiming] as number) - timing.navigationStart, 0);\n }\n }\n return navigationEntry as unknown as NavigationTimingPolyfillEntry;\n};\n\nexport const getNavigationEntry = (): PerformanceNavigationTiming | NavigationTimingPolyfillEntry | undefined => {\n if (WINDOW.__WEB_VITALS_POLYFILL__) {\n return (\n WINDOW.performance &&\n ((performance.getEntriesByType && performance.getEntriesByType('navigation')[0]) ||\n getNavigationEntryFromPerformanceTiming())\n );\n } else {\n return WINDOW.performance && performance.getEntriesByType && performance.getEntriesByType('navigation')[0];\n }\n};\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAKA,MAAM,uCAAA,GAA0C,MAAqC;AACrF;AACA,EAAE,MAAM,MAAO,GAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAA;AAC1C;AACA,EAAE,MAAM,OAAO,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAA;AACjD;AACA,EAAE,MAAM,eAAe,GAAuC;AAC9D,IAAI,SAAS,EAAE,YAAY;AAC3B,IAAI,SAAS,EAAE,CAAC;AAChB,IAAI,IAAI,EAAE,IAAK,IAAG,IAAI,cAAA,GAAiB,IAAA,KAAS,CAAA,GAAI,QAAA,GAAW,UAAU;AACzE,GAAG,CAAA;AACH;AACA,EAAE,KAAK,MAAM,GAAI,IAAG,MAAM,EAAE;AAC5B,IAAI,IAAI,GAAI,KAAI,qBAAqB,GAAA,KAAQ,QAAQ,EAAE;AACvD;AACA,MAAM,eAAe,CAAC,GAAG,CAAA,GAAI,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,GAAA,OAA6C,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC,CAAA;AACrH,KAAI;AACJ,GAAE;AACF,EAAE,OAAO,eAAgB,EAAA;AACzB,CAAC,CAAA;AACD;AACa,MAAA,kBAAA,GAAqB,MAA+E;AACjH,EAAE,IAAI,MAAM,CAAC,uBAAuB,EAAE;AACtC,IAAI;AACJ,MAAM,MAAM,CAAC,WAAY;AACzB,OAAO,CAAC,WAAW,CAAC,oBAAoB,WAAW,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AACrF,QAAQ,uCAAuC,EAAE,CAAA;AACjD,MAAK;AACL,SAAS;AACT,IAAI,OAAO,MAAM,CAAC,WAAY,IAAG,WAAW,CAAC,gBAAA,IAAoB,WAAW,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAA;AAC9G,GAAE;AACF;;;;"}
|
||||
56
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/getVisibilityWatcher.js
generated
vendored
Normal file
56
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/getVisibilityWatcher.js
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
import { WINDOW } from '../../types.js';
|
||||
import { onHidden } from './onHidden.js';
|
||||
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
let firstHiddenTime = -1;
|
||||
|
||||
const initHiddenTime = () => {
|
||||
// If the document is hidden and not prerendering, assume it was always
|
||||
// hidden and the page was loaded in the background.
|
||||
if (WINDOW.document && WINDOW.document.visibilityState) {
|
||||
firstHiddenTime = WINDOW.document.visibilityState === 'hidden' && !WINDOW.document.prerendering ? 0 : Infinity;
|
||||
}
|
||||
};
|
||||
|
||||
const trackChanges = () => {
|
||||
// Update the time if/when the document becomes hidden.
|
||||
onHidden(({ timeStamp }) => {
|
||||
firstHiddenTime = timeStamp;
|
||||
}, true);
|
||||
};
|
||||
|
||||
const getVisibilityWatcher = (
|
||||
|
||||
) => {
|
||||
if (firstHiddenTime < 0) {
|
||||
// If the document is hidden when this code runs, assume it was hidden
|
||||
// since navigation start. This isn't a perfect heuristic, but it's the
|
||||
// best we can do until an API is available to support querying past
|
||||
// visibilityState.
|
||||
initHiddenTime();
|
||||
trackChanges();
|
||||
}
|
||||
return {
|
||||
get firstHiddenTime() {
|
||||
return firstHiddenTime;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export { getVisibilityWatcher };
|
||||
//# sourceMappingURL=getVisibilityWatcher.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/getVisibilityWatcher.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/getVisibilityWatcher.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"getVisibilityWatcher.js","sources":["../../../../../src/browser/web-vitals/lib/getVisibilityWatcher.ts"],"sourcesContent":["/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { WINDOW } from '../../types';\nimport { onHidden } from './onHidden';\n\nlet firstHiddenTime = -1;\n\nconst initHiddenTime = (): void => {\n // If the document is hidden and not prerendering, assume it was always\n // hidden and the page was loaded in the background.\n if (WINDOW.document && WINDOW.document.visibilityState) {\n firstHiddenTime = WINDOW.document.visibilityState === 'hidden' && !WINDOW.document.prerendering ? 0 : Infinity;\n }\n};\n\nconst trackChanges = (): void => {\n // Update the time if/when the document becomes hidden.\n onHidden(({ timeStamp }) => {\n firstHiddenTime = timeStamp;\n }, true);\n};\n\nexport const getVisibilityWatcher = (): {\n readonly firstHiddenTime: number;\n} => {\n if (firstHiddenTime < 0) {\n // If the document is hidden when this code runs, assume it was hidden\n // since navigation start. This isn't a perfect heuristic, but it's the\n // best we can do until an API is available to support querying past\n // visibilityState.\n initHiddenTime();\n trackChanges();\n }\n return {\n get firstHiddenTime() {\n return firstHiddenTime;\n },\n };\n};\n"],"names":[],"mappings":";;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA,IAAI,eAAA,GAAkB,CAAC,CAAC,CAAA;AACxB;AACA,MAAM,cAAA,GAAiB,MAAY;AACnC;AACA;AACA,EAAE,IAAI,MAAM,CAAC,QAAA,IAAY,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE;AAC1D,IAAI,kBAAkB,MAAM,CAAC,QAAQ,CAAC,oBAAoB,QAAA,IAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAA,GAAI,QAAQ,CAAA;AAClH,GAAE;AACF,CAAC,CAAA;AACD;AACA,MAAM,YAAA,GAAe,MAAY;AACjC;AACA,EAAE,QAAQ,CAAC,CAAC,EAAE,SAAU,EAAC,KAAK;AAC9B,IAAI,eAAA,GAAkB,SAAS,CAAA;AAC/B,GAAG,EAAE,IAAI,CAAC,CAAA;AACV,CAAC,CAAA;AACD;AACO,MAAM,oBAAqB,GAAE;AAClC;AACF,KAAK;AACL,EAAE,IAAI,eAAgB,GAAE,CAAC,EAAE;AAC3B;AACA;AACA;AACA;AACA,IAAI,cAAc,EAAE,CAAA;AACpB,IAAI,YAAY,EAAE,CAAA;AAClB,GAAE;AACF,EAAE,OAAO;AACT,IAAI,IAAI,eAAe,GAAG;AAC1B,MAAM,OAAO,eAAe,CAAA;AAC5B,KAAK;AACL,GAAG,CAAA;AACH;;;;"}
|
||||
46
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/initMetric.js
generated
vendored
Normal file
46
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/initMetric.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
import { WINDOW } from '../../types.js';
|
||||
import { generateUniqueID } from './generateUniqueID.js';
|
||||
import { getActivationStart } from './getActivationStart.js';
|
||||
import { getNavigationEntry } from './getNavigationEntry.js';
|
||||
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const initMetric = (name, value) => {
|
||||
const navEntry = getNavigationEntry();
|
||||
let navigationType = 'navigate';
|
||||
|
||||
if (navEntry) {
|
||||
if ((WINDOW.document && WINDOW.document.prerendering) || getActivationStart() > 0) {
|
||||
navigationType = 'prerender';
|
||||
} else {
|
||||
navigationType = navEntry.type.replace(/_/g, '-') ;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
value: typeof value === 'undefined' ? -1 : value,
|
||||
rating: 'good', // Will be updated if the value changes.
|
||||
delta: 0,
|
||||
entries: [],
|
||||
id: generateUniqueID(),
|
||||
navigationType,
|
||||
};
|
||||
};
|
||||
|
||||
export { initMetric };
|
||||
//# sourceMappingURL=initMetric.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/initMetric.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/initMetric.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"initMetric.js","sources":["../../../../../src/browser/web-vitals/lib/initMetric.ts"],"sourcesContent":["/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { WINDOW } from '../../types';\nimport type { Metric } from '../types';\nimport { generateUniqueID } from './generateUniqueID';\nimport { getActivationStart } from './getActivationStart';\nimport { getNavigationEntry } from './getNavigationEntry';\n\nexport const initMetric = (name: Metric['name'], value?: number): Metric => {\n const navEntry = getNavigationEntry();\n let navigationType: Metric['navigationType'] = 'navigate';\n\n if (navEntry) {\n if ((WINDOW.document && WINDOW.document.prerendering) || getActivationStart() > 0) {\n navigationType = 'prerender';\n } else {\n navigationType = navEntry.type.replace(/_/g, '-') as Metric['navigationType'];\n }\n }\n\n return {\n name,\n value: typeof value === 'undefined' ? -1 : value,\n rating: 'good', // Will be updated if the value changes.\n delta: 0,\n entries: [],\n id: generateUniqueID(),\n navigationType,\n };\n};\n"],"names":[],"mappings":";;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;MACa,UAAW,GAAE,CAAC,IAAI,EAAkB,KAAK,KAAsB;AAC5E,EAAE,MAAM,QAAA,GAAW,kBAAkB,EAAE,CAAA;AACvC,EAAE,IAAI,cAAc,GAA6B,UAAU,CAAA;AAC3D;AACA,EAAE,IAAI,QAAQ,EAAE;AAChB,IAAI,IAAI,CAAC,MAAM,CAAC,QAAA,IAAY,MAAM,CAAC,QAAQ,CAAC,YAAY,KAAK,kBAAkB,EAAG,GAAE,CAAC,EAAE;AACvF,MAAM,cAAA,GAAiB,WAAW,CAAA;AAClC,WAAW;AACX,MAAM,cAAA,GAAiB,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAE,EAAA;AACxD,KAAI;AACJ,GAAE;AACF;AACA,EAAE,OAAO;AACT,IAAI,IAAI;AACR,IAAI,KAAK,EAAE,OAAO,KAAM,KAAI,cAAc,CAAC,CAAE,GAAE,KAAK;AACpD,IAAI,MAAM,EAAE,MAAM;AAClB,IAAI,KAAK,EAAE,CAAC;AACZ,IAAI,OAAO,EAAE,EAAE;AACf,IAAI,EAAE,EAAE,gBAAgB,EAAE;AAC1B,IAAI,cAAc;AAClB,GAAG,CAAA;AACH;;;;"}
|
||||
37
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/observe.js
generated
vendored
Normal file
37
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/observe.js
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Takes a performance entry type and a callback function, and creates a
|
||||
* `PerformanceObserver` instance that will observe the specified entry type
|
||||
* with buffering enabled and call the callback _for each entry_.
|
||||
*
|
||||
* This function also feature-detects entry support and wraps the logic in a
|
||||
* try/catch to avoid errors in unsupporting browsers.
|
||||
*/
|
||||
const observe = (
|
||||
type,
|
||||
callback,
|
||||
opts,
|
||||
) => {
|
||||
try {
|
||||
if (PerformanceObserver.supportedEntryTypes.includes(type)) {
|
||||
const po = new PerformanceObserver(list => {
|
||||
callback(list.getEntries() );
|
||||
});
|
||||
po.observe(
|
||||
Object.assign(
|
||||
{
|
||||
type,
|
||||
buffered: true,
|
||||
},
|
||||
opts || {},
|
||||
) ,
|
||||
);
|
||||
return po;
|
||||
}
|
||||
} catch (e) {
|
||||
// Do nothing.
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
export { observe };
|
||||
//# sourceMappingURL=observe.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/observe.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/observe.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"observe.js","sources":["../../../../../src/browser/web-vitals/lib/observe.ts"],"sourcesContent":["/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { FirstInputPolyfillEntry, NavigationTimingPolyfillEntry, PerformancePaintTiming } from '../types';\n\nexport interface PerformanceEntryHandler {\n (entry: PerformanceEntry): void;\n}\n\ninterface PerformanceEntryMap {\n event: PerformanceEventTiming[];\n paint: PerformancePaintTiming[];\n 'layout-shift': LayoutShift[];\n 'largest-contentful-paint': LargestContentfulPaint[];\n 'first-input': PerformanceEventTiming[] | FirstInputPolyfillEntry[];\n navigation: PerformanceNavigationTiming[] | NavigationTimingPolyfillEntry[];\n resource: PerformanceResourceTiming[];\n longtask: PerformanceEntry[];\n}\n\n/**\n * Takes a performance entry type and a callback function, and creates a\n * `PerformanceObserver` instance that will observe the specified entry type\n * with buffering enabled and call the callback _for each entry_.\n *\n * This function also feature-detects entry support and wraps the logic in a\n * try/catch to avoid errors in unsupporting browsers.\n */\nexport const observe = <K extends keyof PerformanceEntryMap>(\n type: K,\n callback: (entries: PerformanceEntryMap[K]) => void,\n opts?: PerformanceObserverInit,\n): PerformanceObserver | undefined => {\n try {\n if (PerformanceObserver.supportedEntryTypes.includes(type)) {\n const po = new PerformanceObserver(list => {\n callback(list.getEntries() as PerformanceEntryMap[K]);\n });\n po.observe(\n Object.assign(\n {\n type,\n buffered: true,\n },\n opts || {},\n ) as PerformanceObserverInit,\n );\n return po;\n }\n } catch (e) {\n // Do nothing.\n }\n return;\n};\n"],"names":[],"mappings":"AAiCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAM,UAAU;AACvB,EAAE,IAAI;AACN,EAAE,QAAQ;AACV,EAAE,IAAI;AACN,KAAsC;AACtC,EAAE,IAAI;AACN,IAAI,IAAI,mBAAmB,CAAC,mBAAmB,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;AAChE,MAAM,MAAM,EAAG,GAAE,IAAI,mBAAmB,CAAC,QAAQ;AACjD,QAAQ,QAAQ,CAAC,IAAI,CAAC,UAAU,IAA6B,CAAA;AAC7D,OAAO,CAAC,CAAA;AACR,MAAM,EAAE,CAAC,OAAO;AAChB,QAAQ,MAAM,CAAC,MAAM;AACrB,UAAU;AACV,YAAY,IAAI;AAChB,YAAY,QAAQ,EAAE,IAAI;AAC1B,WAAW;AACX,UAAU,IAAA,IAAQ,EAAE;AACpB,SAAU;AACV,OAAO,CAAA;AACP,MAAM,OAAO,EAAE,CAAA;AACf,KAAI;AACJ,GAAI,CAAA,OAAO,CAAC,EAAE;AACd;AACA,GAAE;AACF,EAAE,OAAM;AACR;;;;"}
|
||||
39
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/onHidden.js
generated
vendored
Normal file
39
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/onHidden.js
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
import { WINDOW } from '../../types.js';
|
||||
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const onHidden = (cb, once) => {
|
||||
const onHiddenOrPageHide = (event) => {
|
||||
if (event.type === 'pagehide' || WINDOW.document.visibilityState === 'hidden') {
|
||||
cb(event);
|
||||
if (once) {
|
||||
removeEventListener('visibilitychange', onHiddenOrPageHide, true);
|
||||
removeEventListener('pagehide', onHiddenOrPageHide, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (WINDOW.document) {
|
||||
addEventListener('visibilitychange', onHiddenOrPageHide, true);
|
||||
// Some browsers have buggy implementations of visibilitychange,
|
||||
// so we use pagehide in addition, just to be safe.
|
||||
addEventListener('pagehide', onHiddenOrPageHide, true);
|
||||
}
|
||||
};
|
||||
|
||||
export { onHidden };
|
||||
//# sourceMappingURL=onHidden.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/onHidden.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/onHidden.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"onHidden.js","sources":["../../../../../src/browser/web-vitals/lib/onHidden.ts"],"sourcesContent":["/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { WINDOW } from '../../types';\n\nexport interface OnHiddenCallback {\n (event: Event): void;\n}\n\nexport const onHidden = (cb: OnHiddenCallback, once?: boolean): void => {\n const onHiddenOrPageHide = (event: Event): void => {\n if (event.type === 'pagehide' || WINDOW.document!.visibilityState === 'hidden') {\n cb(event);\n if (once) {\n removeEventListener('visibilitychange', onHiddenOrPageHide, true);\n removeEventListener('pagehide', onHiddenOrPageHide, true);\n }\n }\n };\n\n if (WINDOW.document) {\n addEventListener('visibilitychange', onHiddenOrPageHide, true);\n // Some browsers have buggy implementations of visibilitychange,\n // so we use pagehide in addition, just to be safe.\n addEventListener('pagehide', onHiddenOrPageHide, true);\n }\n};\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;MAQa,QAAS,GAAE,CAAC,EAAE,EAAoB,IAAI,KAAqB;AACxE,EAAE,MAAM,kBAAA,GAAqB,CAAC,KAAK,KAAkB;AACrD,IAAI,IAAI,KAAK,CAAC,SAAS,UAAA,IAAc,MAAM,CAAC,QAAQ,CAAE,eAAgB,KAAI,QAAQ,EAAE;AACpF,MAAM,EAAE,CAAC,KAAK,CAAC,CAAA;AACf,MAAM,IAAI,IAAI,EAAE;AAChB,QAAQ,mBAAmB,CAAC,kBAAkB,EAAE,kBAAkB,EAAE,IAAI,CAAC,CAAA;AACzE,QAAQ,mBAAmB,CAAC,UAAU,EAAE,kBAAkB,EAAE,IAAI,CAAC,CAAA;AACjE,OAAM;AACN,KAAI;AACJ,GAAG,CAAA;AACH;AACA,EAAE,IAAI,MAAM,CAAC,QAAQ,EAAE;AACvB,IAAI,gBAAgB,CAAC,kBAAkB,EAAE,kBAAkB,EAAE,IAAI,CAAC,CAAA;AAClE;AACA;AACA,IAAI,gBAAgB,CAAC,UAAU,EAAE,kBAAkB,EAAE,IAAI,CAAC,CAAA;AAC1D,GAAE;AACF;;;;"}
|
||||
42
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/polyfills/interactionCountPolyfill.js
generated
vendored
Normal file
42
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/polyfills/interactionCountPolyfill.js
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
import { observe } from '../observe.js';
|
||||
|
||||
let interactionCountEstimate = 0;
|
||||
let minKnownInteractionId = Infinity;
|
||||
let maxKnownInteractionId = 0;
|
||||
|
||||
const updateEstimate = (entries) => {
|
||||
(entries ).forEach(e => {
|
||||
if (e.interactionId) {
|
||||
minKnownInteractionId = Math.min(minKnownInteractionId, e.interactionId);
|
||||
maxKnownInteractionId = Math.max(maxKnownInteractionId, e.interactionId);
|
||||
|
||||
interactionCountEstimate = maxKnownInteractionId ? (maxKnownInteractionId - minKnownInteractionId) / 7 + 1 : 0;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let po;
|
||||
|
||||
/**
|
||||
* Returns the `interactionCount` value using the native API (if available)
|
||||
* or the polyfill estimate in this module.
|
||||
*/
|
||||
const getInteractionCount = () => {
|
||||
return po ? interactionCountEstimate : performance.interactionCount || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Feature detects native support or initializes the polyfill if needed.
|
||||
*/
|
||||
const initInteractionCountPolyfill = () => {
|
||||
if ('interactionCount' in performance || po) return;
|
||||
|
||||
po = observe('event', updateEstimate, {
|
||||
type: 'event',
|
||||
buffered: true,
|
||||
durationThreshold: 0,
|
||||
} );
|
||||
};
|
||||
|
||||
export { getInteractionCount, initInteractionCountPolyfill };
|
||||
//# sourceMappingURL=interactionCountPolyfill.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/polyfills/interactionCountPolyfill.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/lib/polyfills/interactionCountPolyfill.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"interactionCountPolyfill.js","sources":["../../../../../../src/browser/web-vitals/lib/polyfills/interactionCountPolyfill.ts"],"sourcesContent":["/*\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Metric } from '../../types';\nimport { observe } from '../observe';\n\ndeclare global {\n interface Performance {\n interactionCount: number;\n }\n}\n\nlet interactionCountEstimate = 0;\nlet minKnownInteractionId = Infinity;\nlet maxKnownInteractionId = 0;\n\nconst updateEstimate = (entries: Metric['entries']): void => {\n (entries as PerformanceEventTiming[]).forEach(e => {\n if (e.interactionId) {\n minKnownInteractionId = Math.min(minKnownInteractionId, e.interactionId);\n maxKnownInteractionId = Math.max(maxKnownInteractionId, e.interactionId);\n\n interactionCountEstimate = maxKnownInteractionId ? (maxKnownInteractionId - minKnownInteractionId) / 7 + 1 : 0;\n }\n });\n};\n\nlet po: PerformanceObserver | undefined;\n\n/**\n * Returns the `interactionCount` value using the native API (if available)\n * or the polyfill estimate in this module.\n */\nexport const getInteractionCount = (): number => {\n return po ? interactionCountEstimate : performance.interactionCount || 0;\n};\n\n/**\n * Feature detects native support or initializes the polyfill if needed.\n */\nexport const initInteractionCountPolyfill = (): void => {\n if ('interactionCount' in performance || po) return;\n\n po = observe('event', updateEstimate, {\n type: 'event',\n buffered: true,\n durationThreshold: 0,\n } as PerformanceObserverInit);\n};\n"],"names":[],"mappings":";;AAyBA,IAAI,wBAAA,GAA2B,CAAC,CAAA;AAChC,IAAI,qBAAA,GAAwB,QAAQ,CAAA;AACpC,IAAI,qBAAA,GAAwB,CAAC,CAAA;AAC7B;AACA,MAAM,cAAe,GAAE,CAAC,OAAO,KAA8B;AAC7D,EAAE,CAAC,OAAQ,GAA6B,OAAO,CAAC,KAAK;AACrD,IAAI,IAAI,CAAC,CAAC,aAAa,EAAE;AACzB,MAAM,qBAAA,GAAwB,IAAI,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAAC,CAAC,aAAa,CAAC,CAAA;AAC9E,MAAM,qBAAA,GAAwB,IAAI,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAAC,CAAC,aAAa,CAAC,CAAA;AAC9E;AACA,MAAM,wBAAyB,GAAE,qBAAsB,GAAE,CAAC,qBAAsB,GAAE,qBAAqB,IAAI,CAAA,GAAI,CAAA,GAAI,CAAC,CAAA;AACpH,KAAI;AACJ,GAAG,CAAC,CAAA;AACJ,CAAC,CAAA;AACD;AACA,IAAI,EAAE,CAAA;AACN;AACA;AACA;AACA;AACA;AACa,MAAA,mBAAA,GAAsB,MAAc;AACjD,EAAE,OAAO,KAAK,wBAAA,GAA2B,WAAW,CAAC,gBAAiB,IAAG,CAAC,CAAA;AAC1E,EAAC;AACD;AACA;AACA;AACA;AACa,MAAA,4BAAA,GAA+B,MAAY;AACxD,EAAE,IAAI,kBAAmB,IAAG,eAAe,EAAE,EAAE,OAAM;AACrD;AACA,EAAE,KAAK,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE;AACxC,IAAI,IAAI,EAAE,OAAO;AACjB,IAAI,QAAQ,EAAE,IAAI;AAClB,IAAI,iBAAiB,EAAE,CAAC;AACxB,KAA+B,CAAA;AAC/B;;;;"}
|
||||
92
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/onTTFB.js
generated
vendored
Normal file
92
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/onTTFB.js
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
import { WINDOW } from '../types.js';
|
||||
import { bindReporter } from './lib/bindReporter.js';
|
||||
import { getActivationStart } from './lib/getActivationStart.js';
|
||||
import { getNavigationEntry } from './lib/getNavigationEntry.js';
|
||||
import { initMetric } from './lib/initMetric.js';
|
||||
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Runs in the next task after the page is done loading and/or prerendering.
|
||||
* @param callback
|
||||
*/
|
||||
const whenReady = (callback) => {
|
||||
if (!WINDOW.document) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (WINDOW.document.prerendering) {
|
||||
addEventListener('prerenderingchange', () => whenReady(callback), true);
|
||||
} else if (WINDOW.document.readyState !== 'complete') {
|
||||
addEventListener('load', () => whenReady(callback), true);
|
||||
} else {
|
||||
// Queue a task so the callback runs after `loadEventEnd`.
|
||||
setTimeout(callback, 0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates the [TTFB](https://web.dev/time-to-first-byte/) value for the
|
||||
* current page and calls the `callback` function once the page has loaded,
|
||||
* along with the relevant `navigation` performance entry used to determine the
|
||||
* value. The reported value is a `DOMHighResTimeStamp`.
|
||||
*
|
||||
* Note, this function waits until after the page is loaded to call `callback`
|
||||
* in order to ensure all properties of the `navigation` entry are populated.
|
||||
* This is useful if you want to report on other metrics exposed by the
|
||||
* [Navigation Timing API](https://w3c.github.io/navigation-timing/). For
|
||||
* example, the TTFB metric starts from the page's [time
|
||||
* origin](https://www.w3.org/TR/hr-time-2/#sec-time-origin), which means it
|
||||
* includes time spent on DNS lookup, connection negotiation, network latency,
|
||||
* and server processing time.
|
||||
*/
|
||||
const onTTFB = (onReport, opts) => {
|
||||
// Set defaults
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
opts = opts || {};
|
||||
|
||||
// https://web.dev/ttfb/#what-is-a-good-ttfb-score
|
||||
// const thresholds = [800, 1800];
|
||||
|
||||
const metric = initMetric('TTFB');
|
||||
const report = bindReporter(onReport, metric, opts.reportAllChanges);
|
||||
|
||||
whenReady(() => {
|
||||
const navEntry = getNavigationEntry() ;
|
||||
|
||||
if (navEntry) {
|
||||
// The activationStart reference is used because TTFB should be
|
||||
// relative to page activation rather than navigation start if the
|
||||
// page was prerendered. But in cases where `activationStart` occurs
|
||||
// after the first byte is received, this time should be clamped at 0.
|
||||
metric.value = Math.max(navEntry.responseStart - getActivationStart(), 0);
|
||||
|
||||
// In some cases the value reported is negative or is larger
|
||||
// than the current page time. Ignore these cases:
|
||||
// https://github.com/GoogleChrome/web-vitals/issues/137
|
||||
// https://github.com/GoogleChrome/web-vitals/issues/162
|
||||
if (metric.value < 0 || metric.value > performance.now()) return;
|
||||
|
||||
metric.entries = [navEntry];
|
||||
|
||||
report(true);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export { onTTFB };
|
||||
//# sourceMappingURL=onTTFB.js.map
|
||||
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/onTTFB.js.map
generated
vendored
Normal file
1
node_modules/@sentry-internal/tracing/esm/browser/web-vitals/onTTFB.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user