@datadog/browser-rum-core
Advanced tools
Comparing version 5.18.0 to 5.19.0
@@ -19,1 +19,8 @@ export declare function cssEscape(str: string): string; | ||
export declare function getClassList(element: Element): DOMTokenList | string[]; | ||
export declare class WeakSet<T extends object> { | ||
private map; | ||
constructor(initialValues?: T[]); | ||
add(value: T): this; | ||
delete(value: T): boolean; | ||
has(value: T): boolean; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getClassList = exports.getParentElement = exports.elementMatches = exports.cssEscape = void 0; | ||
exports.WeakSet = exports.getClassList = exports.getParentElement = exports.elementMatches = exports.cssEscape = void 0; | ||
// https://github.com/jquery/jquery/blob/a684e6ba836f7c553968d7d026ed7941e1a612d8/src/selector/escapeSelector.js | ||
@@ -53,11 +53,2 @@ function cssEscape(str) { | ||
exports.getParentElement = getParentElement; | ||
// let parentNode = node.parentNode | ||
// while (parentNode !== null && parentNode.nodeType !== Node.ELEMENT_NODE) { | ||
// parentNode = node.parentNode | ||
// } | ||
// let parentNode = document.querySelector('span').parentNode | ||
// while (parentNode !== null && parentNode.nodeType !== Node.ELEMENT_NODE) { | ||
// parentNode = node.parentNode | ||
// } | ||
// console.log(parentNode) | ||
/** | ||
@@ -79,2 +70,25 @@ * Return the classList of an element or an array of classes if classList is not supported | ||
exports.getClassList = getClassList; | ||
// ie11 supports WeakMap but not WeakSet | ||
var PLACEHOLDER = 1; | ||
var WeakSet = /** @class */ (function () { | ||
function WeakSet(initialValues) { | ||
var _this = this; | ||
this.map = new WeakMap(); | ||
if (initialValues) { | ||
initialValues.forEach(function (value) { return _this.map.set(value, PLACEHOLDER); }); | ||
} | ||
} | ||
WeakSet.prototype.add = function (value) { | ||
this.map.set(value, PLACEHOLDER); | ||
return this; | ||
}; | ||
WeakSet.prototype.delete = function (value) { | ||
return this.map.delete(value); | ||
}; | ||
WeakSet.prototype.has = function (value) { | ||
return this.map.has(value); | ||
}; | ||
return WeakSet; | ||
}()); | ||
exports.WeakSet = WeakSet; | ||
//# sourceMappingURL=polyfills.js.map |
@@ -5,2 +5,3 @@ "use strict"; | ||
var browser_core_1 = require("@datadog/browser-core"); | ||
var discardNegativeDuration_1 = require("../discardNegativeDuration"); | ||
var trackClickActions_1 = require("./trackClickActions"); | ||
@@ -30,3 +31,3 @@ function startActionCollection(lifeCycle, domMutationObservable, configuration, pageStateHistory) { | ||
id: action.id, | ||
loading_time: (0, browser_core_1.toServerDuration)(action.duration), | ||
loading_time: (0, discardNegativeDuration_1.discardNegativeDuration)((0, browser_core_1.toServerDuration)(action.duration)), | ||
frustration: { | ||
@@ -33,0 +34,0 @@ type: action.frustrationTypes, |
@@ -18,4 +18,3 @@ import type { Duration, ClocksState, RelativeTime, TimeStamp } from '@datadog/browser-core'; | ||
target?: { | ||
selector: string; | ||
selector_with_stable_attributes?: string; | ||
selector: string | undefined; | ||
width: number; | ||
@@ -22,0 +21,0 @@ height: number; |
@@ -59,3 +59,3 @@ "use strict"; | ||
}, | ||
browser_sdk_version: (0, browser_core_1.canUseEventBridge)() ? "5.18.0" : undefined, | ||
browser_sdk_version: (0, browser_core_1.canUseEventBridge)() ? "5.19.0" : undefined, | ||
}, | ||
@@ -62,0 +62,0 @@ application: { |
@@ -36,3 +36,3 @@ "use strict"; | ||
applicationId: initConfiguration.applicationId, | ||
version: initConfiguration.version, | ||
version: initConfiguration.version || undefined, | ||
actionNameAttribute: initConfiguration.actionNameAttribute, | ||
@@ -39,0 +39,0 @@ sessionReplaySampleRate: (_a = initConfiguration.sessionReplaySampleRate) !== null && _a !== void 0 ? _a : 0, |
@@ -7,3 +7,3 @@ /** | ||
export declare const STABLE_ATTRIBUTES: string[]; | ||
export declare function getSelectorFromElement(targetElement: Element, actionNameAttribute: string | undefined): string; | ||
export declare function getSelectorFromElement(targetElement: Element, actionNameAttribute: string | undefined): string | undefined; | ||
export declare function supportScopeSelector(): boolean; |
@@ -39,2 +39,7 @@ "use strict"; | ||
function getSelectorFromElement(targetElement, actionNameAttribute) { | ||
if (!isConnected(targetElement)) { | ||
// We cannot compute a selector for a detached element, as we don't have access to all of its | ||
// parents, and we cannot determine if it's unique in the document. | ||
return; | ||
} | ||
var targetElementSelector = ''; | ||
@@ -60,6 +65,5 @@ var element = targetElement; | ||
// | ||
// Here, we use the same strategy: if a the value contains a digit, we consider it generated. This | ||
// Here, we use the same strategy: if the value contains a digit, we consider it generated. This | ||
// strategy might be a bit naive and fail in some cases, but there are many fallbacks to generate | ||
// CSS selectors so it should be fine most of the time. We might want to allow customers to | ||
// provide their own `isGeneratedValue` at some point. | ||
// CSS selectors so it should be fine most of the time. | ||
return /[0-9]/.test(value); | ||
@@ -169,2 +173,13 @@ } | ||
exports.supportScopeSelector = supportScopeSelector; | ||
/** | ||
* Polyfill-utility for the `isConnected` property not supported in IE11 | ||
*/ | ||
function isConnected(element) { | ||
if ('isConnected' in | ||
// cast is to make sure `element` is not inferred as `never` after the check | ||
element) { | ||
return element.isConnected; | ||
} | ||
return element.ownerDocument.documentElement.contains(element); | ||
} | ||
//# sourceMappingURL=getSelectorFromElement.js.map |
@@ -12,5 +12,6 @@ import type { RumPerformanceResourceTiming } from '../../browser/performanceCollection'; | ||
* - from valid nested entries (with 1 ms error margin) | ||
* - if a single timing match, return the timing | ||
* - filter out timing that were already matched to a request | ||
* - then, if a single timing match, return the timing | ||
* - otherwise we can't decide, return undefined | ||
*/ | ||
export declare function matchRequestTiming(request: RequestCompleteEvent): RumPerformanceResourceTiming | undefined; |
@@ -5,3 +5,5 @@ "use strict"; | ||
var browser_core_1 = require("@datadog/browser-core"); | ||
var polyfills_1 = require("../../browser/polyfills"); | ||
var resourceUtils_1 = require("./resourceUtils"); | ||
var alreadyMatchedEntries = new polyfills_1.WeakSet(); | ||
/** | ||
@@ -16,3 +18,4 @@ * Look for corresponding timing in resource timing buffer | ||
* - from valid nested entries (with 1 ms error margin) | ||
* - if a single timing match, return the timing | ||
* - filter out timing that were already matched to a request | ||
* - then, if a single timing match, return the timing | ||
* - otherwise we can't decide, return undefined | ||
@@ -29,4 +32,4 @@ */ | ||
var candidates = sameNameEntries | ||
.map(function (entry) { return entry.toJSON(); }) | ||
.filter(resourceUtils_1.toValidEntry) | ||
.filter(function (entry) { return !alreadyMatchedEntries.has(entry); }) | ||
.filter(function (entry) { return (0, resourceUtils_1.isValidEntry)(entry); }) | ||
.filter(function (entry) { | ||
@@ -36,3 +39,4 @@ return isBetween(entry, request.startClocks.relative, endTime({ startTime: request.startClocks.relative, duration: request.duration })); | ||
if (candidates.length === 1) { | ||
return candidates[0]; | ||
alreadyMatchedEntries.add(candidates[0]); | ||
return candidates[0].toJSON(); | ||
} | ||
@@ -39,0 +43,0 @@ return; |
@@ -19,3 +19,3 @@ "use strict"; | ||
if (entry.entryType === performanceCollection_1.RumPerformanceEntryType.RESOURCE && !(0, resourceUtils_1.isRequestKind)(entry)) { | ||
var rawEvent = processResourceEntry(entry, configuration, sessionManager, pageStateHistory); | ||
var rawEvent = processResourceEntry(entry, configuration, sessionManager); | ||
if (rawEvent) { | ||
@@ -30,3 +30,2 @@ lifeCycle.notify(12 /* LifeCycleEventType.RAW_RUM_EVENT_COLLECTED */, rawEvent); | ||
function processRequest(request, configuration, sessionManager, pageStateHistory) { | ||
var _a; | ||
var matchingTiming = (0, matchRequestTiming_1.matchRequestTiming)(request); | ||
@@ -42,3 +41,2 @@ var startClocks = matchingTiming ? (0, browser_core_1.relativeToClocks)(matchingTiming.startTime) : request.startClocks; | ||
var duration = computeRequestDuration(pageStateHistory, startClocks, request.duration); | ||
var pageStateInfo = computePageStateInfo(pageStateHistory, startClocks, (_a = matchingTiming === null || matchingTiming === void 0 ? void 0 : matchingTiming.duration) !== null && _a !== void 0 ? _a : request.duration); | ||
var resourceEvent = (0, browser_core_1.combine)({ | ||
@@ -58,3 +56,3 @@ date: startClocks.timeStamp, | ||
}, | ||
}, tracingInfo, correspondingTimingOverrides, pageStateInfo); | ||
}, tracingInfo, correspondingTimingOverrides); | ||
return { | ||
@@ -74,3 +72,3 @@ startTime: startClocks.relative, | ||
} | ||
function processResourceEntry(entry, configuration, sessionManager, pageStateHistory) { | ||
function processResourceEntry(entry, configuration, sessionManager) { | ||
var startClocks = (0, browser_core_1.relativeToClocks)(entry.startTime); | ||
@@ -84,3 +82,2 @@ var shouldIndex = shouldIndexResource(configuration, sessionManager, startClocks); | ||
var entryMetrics = computePerformanceEntryMetrics(entry); | ||
var pageStateInfo = computePageStateInfo(pageStateHistory, startClocks, entry.duration); | ||
var resourceEvent = (0, browser_core_1.combine)({ | ||
@@ -98,3 +95,3 @@ date: startClocks.timeStamp, | ||
}, | ||
}, tracingInfo, entryMetrics, pageStateInfo); | ||
}, tracingInfo, entryMetrics); | ||
return { | ||
@@ -151,13 +148,2 @@ startTime: startClocks.relative, | ||
} | ||
function computePageStateInfo(pageStateHistory, startClocks, duration) { | ||
if (!(0, browser_core_1.isExperimentalFeatureEnabled)(browser_core_1.ExperimentalFeature.RESOURCE_PAGE_STATES)) { | ||
return; | ||
} | ||
return { | ||
_dd: { | ||
page_states: pageStateHistory.findAll(startClocks.relative, duration), | ||
page_was_discarded: String(document.wasDiscarded), | ||
}, | ||
}; | ||
} | ||
function computeRequestDuration(pageStateHistory, startClocks, duration) { | ||
@@ -164,0 +150,0 @@ return !pageStateHistory.wasInPageStateDuringPeriod("frozen" /* PageState.FROZEN */, startClocks.relative, duration) |
@@ -19,3 +19,3 @@ import type { ServerDuration } from '@datadog/browser-core'; | ||
export declare function computePerformanceResourceDetails(entry: RumPerformanceResourceTiming): PerformanceResourceDetails | undefined; | ||
export declare function toValidEntry(entry: RumPerformanceResourceTiming): RumPerformanceResourceTiming | undefined; | ||
export declare function isValidEntry(entry: RumPerformanceResourceTiming): boolean; | ||
export declare function computeSize(entry: RumPerformanceResourceTiming): { | ||
@@ -22,0 +22,0 @@ size: number; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.sanitizeDataUrl = exports.isLongDataUrl = exports.MAX_ATTRIBUTE_VALUE_CHAR_LENGTH = exports.isAllowedRequestUrl = exports.computeSize = exports.toValidEntry = exports.computePerformanceResourceDetails = exports.computePerformanceResourceDuration = exports.isRequestKind = exports.computeResourceKind = exports.FAKE_INITIAL_DOCUMENT = void 0; | ||
exports.sanitizeDataUrl = exports.isLongDataUrl = exports.MAX_ATTRIBUTE_VALUE_CHAR_LENGTH = exports.isAllowedRequestUrl = exports.computeSize = exports.isValidEntry = exports.computePerformanceResourceDetails = exports.computePerformanceResourceDuration = exports.isRequestKind = exports.computeResourceKind = exports.FAKE_INITIAL_DOCUMENT = void 0; | ||
var browser_core_1 = require("@datadog/browser-core"); | ||
@@ -69,7 +69,6 @@ exports.FAKE_INITIAL_DOCUMENT = 'initial_document'; | ||
function computePerformanceResourceDetails(entry) { | ||
var validEntry = toValidEntry(entry); | ||
if (!validEntry) { | ||
if (!isValidEntry(entry)) { | ||
return undefined; | ||
} | ||
var startTime = validEntry.startTime, fetchStart = validEntry.fetchStart, redirectStart = validEntry.redirectStart, redirectEnd = validEntry.redirectEnd, domainLookupStart = validEntry.domainLookupStart, domainLookupEnd = validEntry.domainLookupEnd, connectStart = validEntry.connectStart, secureConnectionStart = validEntry.secureConnectionStart, connectEnd = validEntry.connectEnd, requestStart = validEntry.requestStart, responseStart = validEntry.responseStart, responseEnd = validEntry.responseEnd; | ||
var startTime = entry.startTime, fetchStart = entry.fetchStart, redirectStart = entry.redirectStart, redirectEnd = entry.redirectEnd, domainLookupStart = entry.domainLookupStart, domainLookupEnd = entry.domainLookupEnd, connectStart = entry.connectStart, secureConnectionStart = entry.secureConnectionStart, connectEnd = entry.connectEnd, requestStart = entry.requestStart, responseStart = entry.responseStart, responseEnd = entry.responseEnd; | ||
var details = { | ||
@@ -98,5 +97,5 @@ download: formatTiming(startTime, responseStart, responseEnd), | ||
exports.computePerformanceResourceDetails = computePerformanceResourceDetails; | ||
function toValidEntry(entry) { | ||
function isValidEntry(entry) { | ||
if ((0, browser_core_1.isExperimentalFeatureEnabled)(browser_core_1.ExperimentalFeature.TOLERANT_RESOURCE_TIMINGS)) { | ||
return entry; | ||
return true; | ||
} | ||
@@ -111,7 +110,5 @@ // Ensure timings are in the right order. On top of filtering out potential invalid | ||
: true; | ||
if (areCommonTimingsInOrder && areRedirectionTimingsInOrder) { | ||
return entry; | ||
} | ||
return areCommonTimingsInOrder && areRedirectionTimingsInOrder; | ||
} | ||
exports.toValidEntry = toValidEntry; | ||
exports.isValidEntry = isValidEntry; | ||
function hasRedirection(entry) { | ||
@@ -118,0 +115,0 @@ return entry.redirectEnd > entry.startTime; |
@@ -16,3 +16,3 @@ "use strict"; | ||
findTrackedSession: function (startTime) { | ||
var session = sessionManager.findActiveSession(startTime); | ||
var session = sessionManager.findSession(startTime); | ||
if (!session || !isTypeTracked(session.trackingType)) { | ||
@@ -19,0 +19,0 @@ return; |
@@ -47,3 +47,3 @@ "use strict"; | ||
lifeCycle.subscribe(11 /* LifeCycleEventType.PAGE_EXITED */, function (pageExitEvent) { | ||
if (pageExitEvent.reason === browser_core_1.PageExitReason.UNLOADING || pageExitEvent.reason === browser_core_1.PageExitReason.PAGEHIDE) { | ||
if (pageExitEvent.reason === browser_core_1.PageExitReason.UNLOADING) { | ||
currentView.end(); | ||
@@ -50,0 +50,0 @@ } |
@@ -5,2 +5,3 @@ "use strict"; | ||
var browser_core_1 = require("@datadog/browser-core"); | ||
var discardNegativeDuration_1 = require("../discardNegativeDuration"); | ||
var trackViews_1 = require("./trackViews"); | ||
@@ -57,3 +58,3 @@ function startViewCollection(lifeCycle, configuration, location, domMutationObservable, locationChangeObservable, featureFlagContexts, pageStateHistory, recorderApi, initialViewOptions) { | ||
load_event: (0, browser_core_1.toServerDuration)((_p = view.initialViewMetrics.navigationTimings) === null || _p === void 0 ? void 0 : _p.loadEvent), | ||
loading_time: discardNegativeDuration((0, browser_core_1.toServerDuration)(view.commonViewMetrics.loadingTime)), | ||
loading_time: (0, discardNegativeDuration_1.discardNegativeDuration)((0, browser_core_1.toServerDuration)(view.commonViewMetrics.loadingTime)), | ||
loading_type: view.loadingType, | ||
@@ -98,5 +99,2 @@ long_task: { | ||
} | ||
function discardNegativeDuration(duration) { | ||
return (0, browser_core_1.isNumber)(duration) && duration < 0 ? undefined : duration; | ||
} | ||
//# sourceMappingURL=viewCollection.js.map |
@@ -28,2 +28,3 @@ import { noop } from '@datadog/browser-core'; | ||
}; | ||
export declare const MAX_WINDOW_DURATION: number; | ||
/** | ||
@@ -30,0 +31,0 @@ * Check whether `layout-shift` is supported by the browser. |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isLayoutShiftSupported = exports.trackCumulativeLayoutShift = void 0; | ||
exports.isLayoutShiftSupported = exports.MAX_WINDOW_DURATION = exports.trackCumulativeLayoutShift = void 0; | ||
var browser_core_1 = require("@datadog/browser-core"); | ||
@@ -32,2 +32,4 @@ var htmlDomUtils_1 = require("../../../browser/htmlDomUtils"); | ||
var maxClsValue = 0; | ||
// WeakRef is not supported in IE11 and Safari mobile, but so is the layout shift API, so this code won't be executed in these browsers | ||
var maxClsTarget; | ||
// if no layout shift happen the value should be reported as 0 | ||
@@ -42,16 +44,13 @@ callback({ | ||
if (entry.entryType === performanceCollection_1.RumPerformanceEntryType.LAYOUT_SHIFT && !entry.hadRecentInput) { | ||
window.update(entry); | ||
if (window.value() > maxClsValue) { | ||
maxClsValue = window.value(); | ||
var cls = (0, browser_core_1.round)(maxClsValue, 4); | ||
var clsTarget = window.largestLayoutShiftTarget(); | ||
var cslTargetSelector = void 0; | ||
if (clsTarget && | ||
// Check if the CLS target have been removed from the DOM between the time we collect the target reference and when we compute the selector | ||
clsTarget.isConnected) { | ||
cslTargetSelector = (0, getSelectorFromElement_1.getSelectorFromElement)(clsTarget, configuration.actionNameAttribute); | ||
} | ||
var _a = window.update(entry), cumulatedValue = _a.cumulatedValue, isMaxValue = _a.isMaxValue; | ||
if (isMaxValue) { | ||
var target = getTargetFromSource(entry.sources); | ||
maxClsTarget = target ? new WeakRef(target) : undefined; | ||
} | ||
if (cumulatedValue > maxClsValue) { | ||
maxClsValue = cumulatedValue; | ||
var target = maxClsTarget === null || maxClsTarget === void 0 ? void 0 : maxClsTarget.deref(); | ||
callback({ | ||
value: cls, | ||
targetSelector: cslTargetSelector, | ||
value: (0, browser_core_1.round)(maxClsValue, 4), | ||
targetSelector: target && (0, getSelectorFromElement_1.getSelectorFromElement)(target, configuration.actionNameAttribute), | ||
}); | ||
@@ -67,39 +66,40 @@ } | ||
exports.trackCumulativeLayoutShift = trackCumulativeLayoutShift; | ||
function getTargetFromSource(sources) { | ||
var _a; | ||
if (!sources) { | ||
return; | ||
} | ||
return (_a = (0, browser_core_1.find)(sources, function (source) { return !!source.node && (0, htmlDomUtils_1.isElementNode)(source.node); })) === null || _a === void 0 ? void 0 : _a.node; | ||
} | ||
exports.MAX_WINDOW_DURATION = 5 * browser_core_1.ONE_SECOND; | ||
var MAX_UPDATE_GAP = browser_core_1.ONE_SECOND; | ||
function slidingSessionWindow() { | ||
var value = 0; | ||
var cumulatedValue = 0; | ||
var startTime; | ||
var endTime; | ||
var largestLayoutShift = 0; | ||
var largestLayoutShiftTarget; | ||
var largestLayoutShiftTime; | ||
var maxValue = 0; | ||
return { | ||
update: function (entry) { | ||
var _a, _b; | ||
var shouldCreateNewWindow = startTime === undefined || | ||
entry.startTime - endTime >= browser_core_1.ONE_SECOND || | ||
entry.startTime - startTime >= 5 * browser_core_1.ONE_SECOND; | ||
entry.startTime - endTime >= MAX_UPDATE_GAP || | ||
entry.startTime - startTime >= exports.MAX_WINDOW_DURATION; | ||
var isMaxValue; | ||
if (shouldCreateNewWindow) { | ||
startTime = endTime = entry.startTime; | ||
value = entry.value; | ||
largestLayoutShift = 0; | ||
largestLayoutShiftTarget = undefined; | ||
maxValue = cumulatedValue = entry.value; | ||
isMaxValue = true; | ||
} | ||
else { | ||
value += entry.value; | ||
cumulatedValue += entry.value; | ||
endTime = entry.startTime; | ||
} | ||
if (entry.value > largestLayoutShift) { | ||
largestLayoutShift = entry.value; | ||
largestLayoutShiftTime = entry.startTime; | ||
if ((_a = entry.sources) === null || _a === void 0 ? void 0 : _a.length) { | ||
largestLayoutShiftTarget = (_b = (0, browser_core_1.find)(entry.sources, function (s) { return !!s.node && (0, htmlDomUtils_1.isElementNode)(s.node); })) === null || _b === void 0 ? void 0 : _b.node; | ||
isMaxValue = entry.value > maxValue; | ||
if (isMaxValue) { | ||
maxValue = entry.value; | ||
} | ||
else { | ||
largestLayoutShiftTarget = undefined; | ||
} | ||
} | ||
return { | ||
cumulatedValue: cumulatedValue, | ||
isMaxValue: isMaxValue, | ||
}; | ||
}, | ||
value: function () { return value; }, | ||
largestLayoutShiftTarget: function () { return largestLayoutShiftTarget; }, | ||
largestLayoutShiftTime: function () { return largestLayoutShiftTime; }, | ||
}; | ||
@@ -106,0 +106,0 @@ } |
@@ -10,4 +10,2 @@ "use strict"; | ||
exports.LCP_MAXIMUM_DELAY = 10 * browser_core_1.ONE_MINUTE; | ||
var wrongLcpReported = false; | ||
var previousLcp; | ||
/** | ||
@@ -43,7 +41,2 @@ * Track the largest contentful paint (LCP) occurring during the initial View. This can yield | ||
} | ||
if ((0, browser_core_1.isExperimentalFeatureEnabled)(browser_core_1.ExperimentalFeature.ZERO_LCP_TELEMETRY)) { | ||
monitorLcpEntries(lcpEntry, entries.filter(function (entry) { | ||
return entry.entryType === performanceCollection_1.RumPerformanceEntryType.LARGEST_CONTENTFUL_PAINT; | ||
})); | ||
} | ||
callback({ | ||
@@ -64,32 +57,2 @@ value: lcpEntry.startTime, | ||
exports.trackLargestContentfulPaint = trackLargestContentfulPaint; | ||
function monitorLcpEntries(lcpEntry, lcpEntries) { | ||
if (wrongLcpReported) { | ||
return; | ||
} | ||
var wrongLcpDetected = lcpEntry.startTime === 0 | ||
? 'LCP with startTime = 0' | ||
: previousLcp !== undefined && lcpEntry.startTime < previousLcp.startTime | ||
? 'LCP with startTime < previous LCP' | ||
: previousLcp !== undefined && lcpEntry.size < previousLcp.size | ||
? 'LCP with size < previous LCP' | ||
: undefined; | ||
if (wrongLcpDetected) { | ||
wrongLcpReported = true; | ||
(0, browser_core_1.addTelemetryDebug)(wrongLcpDetected, { | ||
debug: { | ||
entry: toSerializableLCP(lcpEntry), | ||
previousLcp: previousLcp, | ||
timeOrigin: performance.timeOrigin, | ||
now: (0, browser_core_1.relativeNow)(), | ||
lcpEntries: lcpEntries.map(toSerializableLCP), | ||
}, | ||
}); | ||
} | ||
previousLcp = toSerializableLCP(lcpEntry); | ||
} | ||
function toSerializableLCP(entry) { | ||
var jsonEntry = entry.toJSON(); | ||
delete jsonEntry.element; | ||
return jsonEntry; | ||
} | ||
//# sourceMappingURL=trackLargestContentfulPaint.js.map |
@@ -19,1 +19,8 @@ export declare function cssEscape(str: string): string; | ||
export declare function getClassList(element: Element): DOMTokenList | string[]; | ||
export declare class WeakSet<T extends object> { | ||
private map; | ||
constructor(initialValues?: T[]); | ||
add(value: T): this; | ||
delete(value: T): boolean; | ||
has(value: T): boolean; | ||
} |
@@ -47,11 +47,2 @@ // https://github.com/jquery/jquery/blob/a684e6ba836f7c553968d7d026ed7941e1a612d8/src/selector/escapeSelector.js | ||
} | ||
// let parentNode = node.parentNode | ||
// while (parentNode !== null && parentNode.nodeType !== Node.ELEMENT_NODE) { | ||
// parentNode = node.parentNode | ||
// } | ||
// let parentNode = document.querySelector('span').parentNode | ||
// while (parentNode !== null && parentNode.nodeType !== Node.ELEMENT_NODE) { | ||
// parentNode = node.parentNode | ||
// } | ||
// console.log(parentNode) | ||
/** | ||
@@ -72,2 +63,25 @@ * Return the classList of an element or an array of classes if classList is not supported | ||
} | ||
// ie11 supports WeakMap but not WeakSet | ||
var PLACEHOLDER = 1; | ||
var WeakSet = /** @class */ (function () { | ||
function WeakSet(initialValues) { | ||
var _this = this; | ||
this.map = new WeakMap(); | ||
if (initialValues) { | ||
initialValues.forEach(function (value) { return _this.map.set(value, PLACEHOLDER); }); | ||
} | ||
} | ||
WeakSet.prototype.add = function (value) { | ||
this.map.set(value, PLACEHOLDER); | ||
return this; | ||
}; | ||
WeakSet.prototype.delete = function (value) { | ||
return this.map.delete(value); | ||
}; | ||
WeakSet.prototype.has = function (value) { | ||
return this.map.has(value); | ||
}; | ||
return WeakSet; | ||
}()); | ||
export { WeakSet }; | ||
//# sourceMappingURL=polyfills.js.map |
import { noop, assign, combine, toServerDuration, generateUUID } from '@datadog/browser-core'; | ||
import { discardNegativeDuration } from '../discardNegativeDuration'; | ||
import { trackClickActions } from './trackClickActions'; | ||
@@ -25,3 +26,3 @@ export function startActionCollection(lifeCycle, domMutationObservable, configuration, pageStateHistory) { | ||
id: action.id, | ||
loading_time: toServerDuration(action.duration), | ||
loading_time: discardNegativeDuration(toServerDuration(action.duration)), | ||
frustration: { | ||
@@ -28,0 +29,0 @@ type: action.frustrationTypes, |
@@ -18,4 +18,3 @@ import type { Duration, ClocksState, RelativeTime, TimeStamp } from '@datadog/browser-core'; | ||
target?: { | ||
selector: string; | ||
selector_with_stable_attributes?: string; | ||
selector: string | undefined; | ||
width: number; | ||
@@ -22,0 +21,0 @@ height: number; |
@@ -56,3 +56,3 @@ import { combine, isEmptyObject, timeStampNow, currentDrift, display, createEventRateLimiter, canUseEventBridge, assign, round, isExperimentalFeatureEnabled, ExperimentalFeature, getConnectivity, } from '@datadog/browser-core'; | ||
}, | ||
browser_sdk_version: canUseEventBridge() ? "5.18.0" : undefined, | ||
browser_sdk_version: canUseEventBridge() ? "5.19.0" : undefined, | ||
}, | ||
@@ -59,0 +59,0 @@ application: { |
@@ -33,3 +33,3 @@ import { getType, arrayFrom, isMatchOption, serializeConfiguration, assign, DefaultPrivacyLevel, TraceContextInjection, display, isPercentage, objectHasValue, validateAndBuildConfiguration, } from '@datadog/browser-core'; | ||
applicationId: initConfiguration.applicationId, | ||
version: initConfiguration.version, | ||
version: initConfiguration.version || undefined, | ||
actionNameAttribute: initConfiguration.actionNameAttribute, | ||
@@ -36,0 +36,0 @@ sessionReplaySampleRate: (_a = initConfiguration.sessionReplaySampleRate) !== null && _a !== void 0 ? _a : 0, |
@@ -7,3 +7,3 @@ /** | ||
export declare const STABLE_ATTRIBUTES: string[]; | ||
export declare function getSelectorFromElement(targetElement: Element, actionNameAttribute: string | undefined): string; | ||
export declare function getSelectorFromElement(targetElement: Element, actionNameAttribute: string | undefined): string | undefined; | ||
export declare function supportScopeSelector(): boolean; |
@@ -36,2 +36,7 @@ import { cssEscape, getClassList, getParentElement } from '../browser/polyfills'; | ||
export function getSelectorFromElement(targetElement, actionNameAttribute) { | ||
if (!isConnected(targetElement)) { | ||
// We cannot compute a selector for a detached element, as we don't have access to all of its | ||
// parents, and we cannot determine if it's unique in the document. | ||
return; | ||
} | ||
var targetElementSelector = ''; | ||
@@ -56,6 +61,5 @@ var element = targetElement; | ||
// | ||
// Here, we use the same strategy: if a the value contains a digit, we consider it generated. This | ||
// Here, we use the same strategy: if the value contains a digit, we consider it generated. This | ||
// strategy might be a bit naive and fail in some cases, but there are many fallbacks to generate | ||
// CSS selectors so it should be fine most of the time. We might want to allow customers to | ||
// provide their own `isGeneratedValue` at some point. | ||
// CSS selectors so it should be fine most of the time. | ||
return /[0-9]/.test(value); | ||
@@ -164,2 +168,13 @@ } | ||
} | ||
/** | ||
* Polyfill-utility for the `isConnected` property not supported in IE11 | ||
*/ | ||
function isConnected(element) { | ||
if ('isConnected' in | ||
// cast is to make sure `element` is not inferred as `never` after the check | ||
element) { | ||
return element.isConnected; | ||
} | ||
return element.ownerDocument.documentElement.contains(element); | ||
} | ||
//# sourceMappingURL=getSelectorFromElement.js.map |
@@ -12,5 +12,6 @@ import type { RumPerformanceResourceTiming } from '../../browser/performanceCollection'; | ||
* - from valid nested entries (with 1 ms error margin) | ||
* - if a single timing match, return the timing | ||
* - filter out timing that were already matched to a request | ||
* - then, if a single timing match, return the timing | ||
* - otherwise we can't decide, return undefined | ||
*/ | ||
export declare function matchRequestTiming(request: RequestCompleteEvent): RumPerformanceResourceTiming | undefined; |
import { addDuration } from '@datadog/browser-core'; | ||
import { toValidEntry } from './resourceUtils'; | ||
import { WeakSet } from '../../browser/polyfills'; | ||
import { isValidEntry } from './resourceUtils'; | ||
var alreadyMatchedEntries = new WeakSet(); | ||
/** | ||
@@ -12,3 +14,4 @@ * Look for corresponding timing in resource timing buffer | ||
* - from valid nested entries (with 1 ms error margin) | ||
* - if a single timing match, return the timing | ||
* - filter out timing that were already matched to a request | ||
* - then, if a single timing match, return the timing | ||
* - otherwise we can't decide, return undefined | ||
@@ -25,4 +28,4 @@ */ | ||
var candidates = sameNameEntries | ||
.map(function (entry) { return entry.toJSON(); }) | ||
.filter(toValidEntry) | ||
.filter(function (entry) { return !alreadyMatchedEntries.has(entry); }) | ||
.filter(function (entry) { return isValidEntry(entry); }) | ||
.filter(function (entry) { | ||
@@ -32,3 +35,4 @@ return isBetween(entry, request.startClocks.relative, endTime({ startTime: request.startClocks.relative, duration: request.duration })); | ||
if (candidates.length === 1) { | ||
return candidates[0]; | ||
alreadyMatchedEntries.add(candidates[0]); | ||
return candidates[0].toJSON(); | ||
} | ||
@@ -35,0 +39,0 @@ return; |
@@ -1,2 +0,2 @@ | ||
import { combine, generateUUID, toServerDuration, relativeToClocks, assign, isNumber, isExperimentalFeatureEnabled, ExperimentalFeature, } from '@datadog/browser-core'; | ||
import { combine, generateUUID, toServerDuration, relativeToClocks, assign, isNumber, } from '@datadog/browser-core'; | ||
import { RumPerformanceEntryType } from '../../browser/performanceCollection'; | ||
@@ -16,3 +16,3 @@ import { matchRequestTiming } from './matchRequestTiming'; | ||
if (entry.entryType === RumPerformanceEntryType.RESOURCE && !isRequestKind(entry)) { | ||
var rawEvent = processResourceEntry(entry, configuration, sessionManager, pageStateHistory); | ||
var rawEvent = processResourceEntry(entry, configuration, sessionManager); | ||
if (rawEvent) { | ||
@@ -26,3 +26,2 @@ lifeCycle.notify(12 /* LifeCycleEventType.RAW_RUM_EVENT_COLLECTED */, rawEvent); | ||
function processRequest(request, configuration, sessionManager, pageStateHistory) { | ||
var _a; | ||
var matchingTiming = matchRequestTiming(request); | ||
@@ -38,3 +37,2 @@ var startClocks = matchingTiming ? relativeToClocks(matchingTiming.startTime) : request.startClocks; | ||
var duration = computeRequestDuration(pageStateHistory, startClocks, request.duration); | ||
var pageStateInfo = computePageStateInfo(pageStateHistory, startClocks, (_a = matchingTiming === null || matchingTiming === void 0 ? void 0 : matchingTiming.duration) !== null && _a !== void 0 ? _a : request.duration); | ||
var resourceEvent = combine({ | ||
@@ -54,3 +52,3 @@ date: startClocks.timeStamp, | ||
}, | ||
}, tracingInfo, correspondingTimingOverrides, pageStateInfo); | ||
}, tracingInfo, correspondingTimingOverrides); | ||
return { | ||
@@ -70,3 +68,3 @@ startTime: startClocks.relative, | ||
} | ||
function processResourceEntry(entry, configuration, sessionManager, pageStateHistory) { | ||
function processResourceEntry(entry, configuration, sessionManager) { | ||
var startClocks = relativeToClocks(entry.startTime); | ||
@@ -80,3 +78,2 @@ var shouldIndex = shouldIndexResource(configuration, sessionManager, startClocks); | ||
var entryMetrics = computePerformanceEntryMetrics(entry); | ||
var pageStateInfo = computePageStateInfo(pageStateHistory, startClocks, entry.duration); | ||
var resourceEvent = combine({ | ||
@@ -94,3 +91,3 @@ date: startClocks.timeStamp, | ||
}, | ||
}, tracingInfo, entryMetrics, pageStateInfo); | ||
}, tracingInfo, entryMetrics); | ||
return { | ||
@@ -147,13 +144,2 @@ startTime: startClocks.relative, | ||
} | ||
function computePageStateInfo(pageStateHistory, startClocks, duration) { | ||
if (!isExperimentalFeatureEnabled(ExperimentalFeature.RESOURCE_PAGE_STATES)) { | ||
return; | ||
} | ||
return { | ||
_dd: { | ||
page_states: pageStateHistory.findAll(startClocks.relative, duration), | ||
page_was_discarded: String(document.wasDiscarded), | ||
}, | ||
}; | ||
} | ||
function computeRequestDuration(pageStateHistory, startClocks, duration) { | ||
@@ -160,0 +146,0 @@ return !pageStateHistory.wasInPageStateDuringPeriod("frozen" /* PageState.FROZEN */, startClocks.relative, duration) |
@@ -19,3 +19,3 @@ import type { ServerDuration } from '@datadog/browser-core'; | ||
export declare function computePerformanceResourceDetails(entry: RumPerformanceResourceTiming): PerformanceResourceDetails | undefined; | ||
export declare function toValidEntry(entry: RumPerformanceResourceTiming): RumPerformanceResourceTiming | undefined; | ||
export declare function isValidEntry(entry: RumPerformanceResourceTiming): boolean; | ||
export declare function computeSize(entry: RumPerformanceResourceTiming): { | ||
@@ -22,0 +22,0 @@ size: number; |
@@ -63,7 +63,6 @@ import { addTelemetryDebug, elapsed, ExperimentalFeature, getPathName, includes, isExperimentalFeatureEnabled, isValidUrl, toServerDuration, } from '@datadog/browser-core'; | ||
export function computePerformanceResourceDetails(entry) { | ||
var validEntry = toValidEntry(entry); | ||
if (!validEntry) { | ||
if (!isValidEntry(entry)) { | ||
return undefined; | ||
} | ||
var startTime = validEntry.startTime, fetchStart = validEntry.fetchStart, redirectStart = validEntry.redirectStart, redirectEnd = validEntry.redirectEnd, domainLookupStart = validEntry.domainLookupStart, domainLookupEnd = validEntry.domainLookupEnd, connectStart = validEntry.connectStart, secureConnectionStart = validEntry.secureConnectionStart, connectEnd = validEntry.connectEnd, requestStart = validEntry.requestStart, responseStart = validEntry.responseStart, responseEnd = validEntry.responseEnd; | ||
var startTime = entry.startTime, fetchStart = entry.fetchStart, redirectStart = entry.redirectStart, redirectEnd = entry.redirectEnd, domainLookupStart = entry.domainLookupStart, domainLookupEnd = entry.domainLookupEnd, connectStart = entry.connectStart, secureConnectionStart = entry.secureConnectionStart, connectEnd = entry.connectEnd, requestStart = entry.requestStart, responseStart = entry.responseStart, responseEnd = entry.responseEnd; | ||
var details = { | ||
@@ -91,5 +90,5 @@ download: formatTiming(startTime, responseStart, responseEnd), | ||
} | ||
export function toValidEntry(entry) { | ||
export function isValidEntry(entry) { | ||
if (isExperimentalFeatureEnabled(ExperimentalFeature.TOLERANT_RESOURCE_TIMINGS)) { | ||
return entry; | ||
return true; | ||
} | ||
@@ -104,5 +103,3 @@ // Ensure timings are in the right order. On top of filtering out potential invalid | ||
: true; | ||
if (areCommonTimingsInOrder && areRedirectionTimingsInOrder) { | ||
return entry; | ||
} | ||
return areCommonTimingsInOrder && areRedirectionTimingsInOrder; | ||
} | ||
@@ -109,0 +106,0 @@ function hasRedirection(entry) { |
@@ -13,3 +13,3 @@ import { Observable, bridgeSupports, noop, performDraw, startSessionManager, } from '@datadog/browser-core'; | ||
findTrackedSession: function (startTime) { | ||
var session = sessionManager.findActiveSession(startTime); | ||
var session = sessionManager.findSession(startTime); | ||
if (!session || !isTypeTracked(session.trackingType)) { | ||
@@ -16,0 +16,0 @@ return; |
@@ -44,3 +44,3 @@ import { noop, PageExitReason, shallowClone, elapsed, generateUUID, ONE_MINUTE, throttle, clocksNow, clocksOrigin, timeStampNow, display, looksLikeRelativeTime, setInterval, clearInterval, setTimeout, Observable, } from '@datadog/browser-core'; | ||
lifeCycle.subscribe(11 /* LifeCycleEventType.PAGE_EXITED */, function (pageExitEvent) { | ||
if (pageExitEvent.reason === PageExitReason.UNLOADING || pageExitEvent.reason === PageExitReason.PAGEHIDE) { | ||
if (pageExitEvent.reason === PageExitReason.UNLOADING) { | ||
currentView.end(); | ||
@@ -47,0 +47,0 @@ } |
@@ -1,2 +0,3 @@ | ||
import { isEmptyObject, mapValues, toServerDuration, isNumber } from '@datadog/browser-core'; | ||
import { isEmptyObject, mapValues, toServerDuration } from '@datadog/browser-core'; | ||
import { discardNegativeDuration } from '../discardNegativeDuration'; | ||
import { trackViews } from './trackViews'; | ||
@@ -92,5 +93,2 @@ export function startViewCollection(lifeCycle, configuration, location, domMutationObservable, locationChangeObservable, featureFlagContexts, pageStateHistory, recorderApi, initialViewOptions) { | ||
} | ||
function discardNegativeDuration(duration) { | ||
return isNumber(duration) && duration < 0 ? undefined : duration; | ||
} | ||
//# sourceMappingURL=viewCollection.js.map |
@@ -28,2 +28,3 @@ import { noop } from '@datadog/browser-core'; | ||
}; | ||
export declare const MAX_WINDOW_DURATION: number; | ||
/** | ||
@@ -30,0 +31,0 @@ * Check whether `layout-shift` is supported by the browser. |
@@ -29,2 +29,4 @@ import { round, find, ONE_SECOND, noop } from '@datadog/browser-core'; | ||
var maxClsValue = 0; | ||
// WeakRef is not supported in IE11 and Safari mobile, but so is the layout shift API, so this code won't be executed in these browsers | ||
var maxClsTarget; | ||
// if no layout shift happen the value should be reported as 0 | ||
@@ -39,16 +41,13 @@ callback({ | ||
if (entry.entryType === RumPerformanceEntryType.LAYOUT_SHIFT && !entry.hadRecentInput) { | ||
window.update(entry); | ||
if (window.value() > maxClsValue) { | ||
maxClsValue = window.value(); | ||
var cls = round(maxClsValue, 4); | ||
var clsTarget = window.largestLayoutShiftTarget(); | ||
var cslTargetSelector = void 0; | ||
if (clsTarget && | ||
// Check if the CLS target have been removed from the DOM between the time we collect the target reference and when we compute the selector | ||
clsTarget.isConnected) { | ||
cslTargetSelector = getSelectorFromElement(clsTarget, configuration.actionNameAttribute); | ||
} | ||
var _a = window.update(entry), cumulatedValue = _a.cumulatedValue, isMaxValue = _a.isMaxValue; | ||
if (isMaxValue) { | ||
var target = getTargetFromSource(entry.sources); | ||
maxClsTarget = target ? new WeakRef(target) : undefined; | ||
} | ||
if (cumulatedValue > maxClsValue) { | ||
maxClsValue = cumulatedValue; | ||
var target = maxClsTarget === null || maxClsTarget === void 0 ? void 0 : maxClsTarget.deref(); | ||
callback({ | ||
value: cls, | ||
targetSelector: cslTargetSelector, | ||
value: round(maxClsValue, 4), | ||
targetSelector: target && getSelectorFromElement(target, configuration.actionNameAttribute), | ||
}); | ||
@@ -63,39 +62,40 @@ } | ||
} | ||
function getTargetFromSource(sources) { | ||
var _a; | ||
if (!sources) { | ||
return; | ||
} | ||
return (_a = find(sources, function (source) { return !!source.node && isElementNode(source.node); })) === null || _a === void 0 ? void 0 : _a.node; | ||
} | ||
export var MAX_WINDOW_DURATION = 5 * ONE_SECOND; | ||
var MAX_UPDATE_GAP = ONE_SECOND; | ||
function slidingSessionWindow() { | ||
var value = 0; | ||
var cumulatedValue = 0; | ||
var startTime; | ||
var endTime; | ||
var largestLayoutShift = 0; | ||
var largestLayoutShiftTarget; | ||
var largestLayoutShiftTime; | ||
var maxValue = 0; | ||
return { | ||
update: function (entry) { | ||
var _a, _b; | ||
var shouldCreateNewWindow = startTime === undefined || | ||
entry.startTime - endTime >= ONE_SECOND || | ||
entry.startTime - startTime >= 5 * ONE_SECOND; | ||
entry.startTime - endTime >= MAX_UPDATE_GAP || | ||
entry.startTime - startTime >= MAX_WINDOW_DURATION; | ||
var isMaxValue; | ||
if (shouldCreateNewWindow) { | ||
startTime = endTime = entry.startTime; | ||
value = entry.value; | ||
largestLayoutShift = 0; | ||
largestLayoutShiftTarget = undefined; | ||
maxValue = cumulatedValue = entry.value; | ||
isMaxValue = true; | ||
} | ||
else { | ||
value += entry.value; | ||
cumulatedValue += entry.value; | ||
endTime = entry.startTime; | ||
} | ||
if (entry.value > largestLayoutShift) { | ||
largestLayoutShift = entry.value; | ||
largestLayoutShiftTime = entry.startTime; | ||
if ((_a = entry.sources) === null || _a === void 0 ? void 0 : _a.length) { | ||
largestLayoutShiftTarget = (_b = find(entry.sources, function (s) { return !!s.node && isElementNode(s.node); })) === null || _b === void 0 ? void 0 : _b.node; | ||
isMaxValue = entry.value > maxValue; | ||
if (isMaxValue) { | ||
maxValue = entry.value; | ||
} | ||
else { | ||
largestLayoutShiftTarget = undefined; | ||
} | ||
} | ||
return { | ||
cumulatedValue: cumulatedValue, | ||
isMaxValue: isMaxValue, | ||
}; | ||
}, | ||
value: function () { return value; }, | ||
largestLayoutShiftTarget: function () { return largestLayoutShiftTarget; }, | ||
largestLayoutShiftTime: function () { return largestLayoutShiftTime; }, | ||
}; | ||
@@ -102,0 +102,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { DOM_EVENT, ExperimentalFeature, ONE_MINUTE, addEventListeners, addTelemetryDebug, findLast, isExperimentalFeatureEnabled, relativeNow, } from '@datadog/browser-core'; | ||
import { DOM_EVENT, ONE_MINUTE, addEventListeners, findLast } from '@datadog/browser-core'; | ||
import { RumPerformanceEntryType } from '../../../browser/performanceCollection'; | ||
@@ -7,4 +7,2 @@ import { getSelectorFromElement } from '../../getSelectorFromElement'; | ||
export var LCP_MAXIMUM_DELAY = 10 * ONE_MINUTE; | ||
var wrongLcpReported = false; | ||
var previousLcp; | ||
/** | ||
@@ -40,7 +38,2 @@ * Track the largest contentful paint (LCP) occurring during the initial View. This can yield | ||
} | ||
if (isExperimentalFeatureEnabled(ExperimentalFeature.ZERO_LCP_TELEMETRY)) { | ||
monitorLcpEntries(lcpEntry, entries.filter(function (entry) { | ||
return entry.entryType === RumPerformanceEntryType.LARGEST_CONTENTFUL_PAINT; | ||
})); | ||
} | ||
callback({ | ||
@@ -60,32 +53,2 @@ value: lcpEntry.startTime, | ||
} | ||
function monitorLcpEntries(lcpEntry, lcpEntries) { | ||
if (wrongLcpReported) { | ||
return; | ||
} | ||
var wrongLcpDetected = lcpEntry.startTime === 0 | ||
? 'LCP with startTime = 0' | ||
: previousLcp !== undefined && lcpEntry.startTime < previousLcp.startTime | ||
? 'LCP with startTime < previous LCP' | ||
: previousLcp !== undefined && lcpEntry.size < previousLcp.size | ||
? 'LCP with size < previous LCP' | ||
: undefined; | ||
if (wrongLcpDetected) { | ||
wrongLcpReported = true; | ||
addTelemetryDebug(wrongLcpDetected, { | ||
debug: { | ||
entry: toSerializableLCP(lcpEntry), | ||
previousLcp: previousLcp, | ||
timeOrigin: performance.timeOrigin, | ||
now: relativeNow(), | ||
lcpEntries: lcpEntries.map(toSerializableLCP), | ||
}, | ||
}); | ||
} | ||
previousLcp = toSerializableLCP(lcpEntry); | ||
} | ||
function toSerializableLCP(entry) { | ||
var jsonEntry = entry.toJSON(); | ||
delete jsonEntry.element; | ||
return jsonEntry; | ||
} | ||
//# sourceMappingURL=trackLargestContentfulPaint.js.map |
{ | ||
"name": "@datadog/browser-rum-core", | ||
"version": "5.18.0", | ||
"version": "5.19.0", | ||
"license": "Apache-2.0", | ||
@@ -15,3 +15,3 @@ "main": "cjs/index.js", | ||
"dependencies": { | ||
"@datadog/browser-core": "5.18.0" | ||
"@datadog/browser-core": "5.19.0" | ||
}, | ||
@@ -32,3 +32,3 @@ "devDependencies": { | ||
}, | ||
"gitHead": "933517cf9fc2d33f561ac54a07a1f5e8bbab154e" | ||
"gitHead": "ce92533465ebb3c3d0142f0c7d83af471572a818" | ||
} |
@@ -53,13 +53,2 @@ // https://github.com/jquery/jquery/blob/a684e6ba836f7c553968d7d026ed7941e1a612d8/src/selector/escapeSelector.js | ||
// let parentNode = node.parentNode | ||
// while (parentNode !== null && parentNode.nodeType !== Node.ELEMENT_NODE) { | ||
// parentNode = node.parentNode | ||
// } | ||
// let parentNode = document.querySelector('span').parentNode | ||
// while (parentNode !== null && parentNode.nodeType !== Node.ELEMENT_NODE) { | ||
// parentNode = node.parentNode | ||
// } | ||
// console.log(parentNode) | ||
/** | ||
@@ -80,1 +69,27 @@ * Return the classList of an element or an array of classes if classList is not supported | ||
} | ||
// ie11 supports WeakMap but not WeakSet | ||
const PLACEHOLDER = 1 | ||
export class WeakSet<T extends object> { | ||
private map = new WeakMap<T, typeof PLACEHOLDER>() | ||
constructor(initialValues?: T[]) { | ||
if (initialValues) { | ||
initialValues.forEach((value) => this.map.set(value, PLACEHOLDER)) | ||
} | ||
} | ||
add(value: T) { | ||
this.map.set(value, PLACEHOLDER) | ||
return this | ||
} | ||
delete(value: T) { | ||
return this.map.delete(value) | ||
} | ||
has(value: T) { | ||
return this.map.has(value) | ||
} | ||
} |
import type { ClocksState, Context, Observable } from '@datadog/browser-core' | ||
import { noop, assign, combine, toServerDuration, generateUUID } from '@datadog/browser-core' | ||
import { discardNegativeDuration } from '../discardNegativeDuration' | ||
import type { RawRumActionEvent } from '../../rawRumEvent.types' | ||
@@ -65,3 +66,3 @@ import { ActionType, RumEventType } from '../../rawRumEvent.types' | ||
id: action.id, | ||
loading_time: toServerDuration(action.duration), | ||
loading_time: discardNegativeDuration(toServerDuration(action.duration)), | ||
frustration: { | ||
@@ -68,0 +69,0 @@ type: action.frustrationTypes, |
@@ -41,4 +41,3 @@ import type { Duration, ClocksState, RelativeTime, TimeStamp } from '@datadog/browser-core' | ||
target?: { | ||
selector: string | ||
selector_with_stable_attributes?: string | ||
selector: string | undefined | ||
width: number | ||
@@ -45,0 +44,0 @@ height: number |
@@ -115,3 +115,3 @@ import type { Configuration, InitConfiguration, MatchOption, RawTelemetryConfiguration } from '@datadog/browser-core' | ||
applicationId: initConfiguration.applicationId, | ||
version: initConfiguration.version, | ||
version: initConfiguration.version || undefined, | ||
actionNameAttribute: initConfiguration.actionNameAttribute, | ||
@@ -118,0 +118,0 @@ sessionReplaySampleRate: initConfiguration.sessionReplaySampleRate ?? 0, |
@@ -41,3 +41,11 @@ import { cssEscape, getClassList, getParentElement } from '../browser/polyfills' | ||
export function getSelectorFromElement(targetElement: Element, actionNameAttribute: string | undefined) { | ||
export function getSelectorFromElement( | ||
targetElement: Element, | ||
actionNameAttribute: string | undefined | ||
): string | undefined { | ||
if (!isConnected(targetElement)) { | ||
// We cannot compute a selector for a detached element, as we don't have access to all of its | ||
// parents, and we cannot determine if it's unique in the document. | ||
return | ||
} | ||
let targetElementSelector = '' | ||
@@ -79,6 +87,5 @@ let element: Element | null = targetElement | ||
// | ||
// Here, we use the same strategy: if a the value contains a digit, we consider it generated. This | ||
// Here, we use the same strategy: if the value contains a digit, we consider it generated. This | ||
// strategy might be a bit naive and fail in some cases, but there are many fallbacks to generate | ||
// CSS selectors so it should be fine most of the time. We might want to allow customers to | ||
// provide their own `isGeneratedValue` at some point. | ||
// CSS selectors so it should be fine most of the time. | ||
return /[0-9]/.test(value) | ||
@@ -207,1 +214,16 @@ } | ||
} | ||
/** | ||
* Polyfill-utility for the `isConnected` property not supported in IE11 | ||
*/ | ||
function isConnected(element: Element): boolean { | ||
if ( | ||
'isConnected' in | ||
// cast is to make sure `element` is not inferred as `never` after the check | ||
(element as { isConnected?: boolean }) | ||
) { | ||
return element.isConnected | ||
} | ||
return element.ownerDocument.documentElement.contains(element) | ||
} |
@@ -5,3 +5,4 @@ import type { Duration, RelativeTime } from '@datadog/browser-core' | ||
import type { RequestCompleteEvent } from '../requestCollection' | ||
import { toValidEntry } from './resourceUtils' | ||
import { WeakSet } from '../../browser/polyfills' | ||
import { isValidEntry } from './resourceUtils' | ||
@@ -13,2 +14,4 @@ interface Timing { | ||
const alreadyMatchedEntries = new WeakSet<PerformanceEntry>() | ||
/** | ||
@@ -23,3 +26,4 @@ * Look for corresponding timing in resource timing buffer | ||
* - from valid nested entries (with 1 ms error margin) | ||
* - if a single timing match, return the timing | ||
* - filter out timing that were already matched to a request | ||
* - then, if a single timing match, return the timing | ||
* - otherwise we can't decide, return undefined | ||
@@ -31,3 +35,3 @@ */ | ||
} | ||
const sameNameEntries = performance.getEntriesByName(request.url, 'resource') | ||
const sameNameEntries = performance.getEntriesByName(request.url, 'resource') as RumPerformanceResourceTiming[] | ||
@@ -39,4 +43,4 @@ if (!sameNameEntries.length || !('toJSON' in sameNameEntries[0])) { | ||
const candidates = sameNameEntries | ||
.map((entry) => entry.toJSON() as RumPerformanceResourceTiming) | ||
.filter(toValidEntry) | ||
.filter((entry) => !alreadyMatchedEntries.has(entry)) | ||
.filter((entry) => isValidEntry(entry)) | ||
.filter((entry) => | ||
@@ -51,3 +55,5 @@ isBetween( | ||
if (candidates.length === 1) { | ||
return candidates[0] | ||
alreadyMatchedEntries.add(candidates[0]) | ||
return candidates[0].toJSON() as RumPerformanceResourceTiming | ||
} | ||
@@ -54,0 +60,0 @@ |
@@ -11,4 +11,2 @@ import type { ClocksState, Duration } from '@datadog/browser-core' | ||
isNumber, | ||
isExperimentalFeatureEnabled, | ||
ExperimentalFeature, | ||
} from '@datadog/browser-core' | ||
@@ -54,3 +52,3 @@ import type { RumConfiguration } from '../configuration' | ||
if (entry.entryType === RumPerformanceEntryType.RESOURCE && !isRequestKind(entry)) { | ||
const rawEvent = processResourceEntry(entry, configuration, sessionManager, pageStateHistory) | ||
const rawEvent = processResourceEntry(entry, configuration, sessionManager) | ||
if (rawEvent) { | ||
@@ -83,7 +81,2 @@ lifeCycle.notify(LifeCycleEventType.RAW_RUM_EVENT_COLLECTED, rawEvent) | ||
const duration = computeRequestDuration(pageStateHistory, startClocks, request.duration) | ||
const pageStateInfo = computePageStateInfo( | ||
pageStateHistory, | ||
startClocks, | ||
matchingTiming?.duration ?? request.duration | ||
) | ||
@@ -107,4 +100,3 @@ const resourceEvent = combine( | ||
tracingInfo, | ||
correspondingTimingOverrides, | ||
pageStateInfo | ||
correspondingTimingOverrides | ||
) | ||
@@ -129,4 +121,3 @@ return { | ||
configuration: RumConfiguration, | ||
sessionManager: RumSessionManager, | ||
pageStateHistory: PageStateHistory | ||
sessionManager: RumSessionManager | ||
): RawRumEventCollectedData<RawRumResourceEvent> | undefined { | ||
@@ -143,4 +134,2 @@ const startClocks = relativeToClocks(entry.startTime) | ||
const pageStateInfo = computePageStateInfo(pageStateHistory, startClocks, entry.duration) | ||
const resourceEvent = combine( | ||
@@ -161,4 +150,3 @@ { | ||
tracingInfo, | ||
entryMetrics, | ||
pageStateInfo | ||
entryMetrics | ||
) | ||
@@ -230,15 +218,2 @@ return { | ||
function computePageStateInfo(pageStateHistory: PageStateHistory, startClocks: ClocksState, duration: Duration) { | ||
if (!isExperimentalFeatureEnabled(ExperimentalFeature.RESOURCE_PAGE_STATES)) { | ||
return | ||
} | ||
return { | ||
_dd: { | ||
page_states: pageStateHistory.findAll(startClocks.relative, duration), | ||
page_was_discarded: String((document as any).wasDiscarded), | ||
}, | ||
} | ||
} | ||
function computeRequestDuration(pageStateHistory: PageStateHistory, startClocks: ClocksState, duration: Duration) { | ||
@@ -245,0 +220,0 @@ return !pageStateHistory.wasInPageStateDuringPeriod(PageState.FROZEN, startClocks.relative, duration) |
@@ -92,5 +92,3 @@ import type { RelativeTime, ServerDuration } from '@datadog/browser-core' | ||
): PerformanceResourceDetails | undefined { | ||
const validEntry = toValidEntry(entry) | ||
if (!validEntry) { | ||
if (!isValidEntry(entry)) { | ||
return undefined | ||
@@ -111,3 +109,3 @@ } | ||
responseEnd, | ||
} = validEntry | ||
} = entry | ||
@@ -142,5 +140,5 @@ const details: PerformanceResourceDetails = { | ||
export function toValidEntry(entry: RumPerformanceResourceTiming) { | ||
export function isValidEntry(entry: RumPerformanceResourceTiming) { | ||
if (isExperimentalFeatureEnabled(ExperimentalFeature.TOLERANT_RESOURCE_TIMINGS)) { | ||
return entry | ||
return true | ||
} | ||
@@ -168,5 +166,3 @@ | ||
if (areCommonTimingsInOrder && areRedirectionTimingsInOrder) { | ||
return entry | ||
} | ||
return areCommonTimingsInOrder && areRedirectionTimingsInOrder | ||
} | ||
@@ -173,0 +169,0 @@ |
@@ -55,3 +55,3 @@ import type { RelativeTime, TrackingConsentState } from '@datadog/browser-core' | ||
findTrackedSession: (startTime) => { | ||
const session = sessionManager.findActiveSession(startTime) | ||
const session = sessionManager.findSession(startTime) | ||
if (!session || !isTypeTracked(session.trackingType)) { | ||
@@ -58,0 +58,0 @@ return |
@@ -134,3 +134,3 @@ import type { Duration, ClocksState, TimeStamp, Subscription, RelativeTime } from '@datadog/browser-core' | ||
lifeCycle.subscribe(LifeCycleEventType.PAGE_EXITED, (pageExitEvent) => { | ||
if (pageExitEvent.reason === PageExitReason.UNLOADING || pageExitEvent.reason === PageExitReason.PAGEHIDE) { | ||
if (pageExitEvent.reason === PageExitReason.UNLOADING) { | ||
currentView.end() | ||
@@ -137,0 +137,0 @@ } |
import type { Duration, ServerDuration, Observable } from '@datadog/browser-core' | ||
import { isEmptyObject, mapValues, toServerDuration, isNumber } from '@datadog/browser-core' | ||
import { isEmptyObject, mapValues, toServerDuration } from '@datadog/browser-core' | ||
import { discardNegativeDuration } from '../discardNegativeDuration' | ||
import type { RecorderApi } from '../../boot/rumPublicApi' | ||
@@ -134,5 +135,1 @@ import type { RawRumViewEvent } from '../../rawRumEvent.types' | ||
} | ||
function discardNegativeDuration(duration: ServerDuration | undefined): ServerDuration | undefined { | ||
return isNumber(duration) && duration < 0 ? undefined : duration | ||
} |
import { round, find, ONE_SECOND, noop } from '@datadog/browser-core' | ||
import type { RelativeTime } from '@datadog/browser-core' | ||
import type { RelativeTime, WeakRef, WeakRefConstructor } from '@datadog/browser-core' | ||
import { isElementNode } from '../../../browser/htmlDomUtils' | ||
@@ -16,2 +16,4 @@ import type { LifeCycle } from '../../lifeCycle' | ||
declare const WeakRef: WeakRefConstructor | ||
/** | ||
@@ -46,2 +48,4 @@ * Track the cumulative layout shifts (CLS). | ||
let maxClsValue = 0 | ||
// WeakRef is not supported in IE11 and Safari mobile, but so is the layout shift API, so this code won't be executed in these browsers | ||
let maxClsTarget: WeakRef<HTMLElement> | undefined | ||
@@ -57,21 +61,16 @@ // if no layout shift happen the value should be reported as 0 | ||
if (entry.entryType === RumPerformanceEntryType.LAYOUT_SHIFT && !entry.hadRecentInput) { | ||
window.update(entry) | ||
const { cumulatedValue, isMaxValue } = window.update(entry) | ||
if (window.value() > maxClsValue) { | ||
maxClsValue = window.value() | ||
const cls = round(maxClsValue, 4) | ||
const clsTarget = window.largestLayoutShiftTarget() | ||
let cslTargetSelector | ||
if (isMaxValue) { | ||
const target = getTargetFromSource(entry.sources) | ||
maxClsTarget = target ? new WeakRef(target) : undefined | ||
} | ||
if ( | ||
clsTarget && | ||
// Check if the CLS target have been removed from the DOM between the time we collect the target reference and when we compute the selector | ||
clsTarget.isConnected | ||
) { | ||
cslTargetSelector = getSelectorFromElement(clsTarget, configuration.actionNameAttribute) | ||
} | ||
if (cumulatedValue > maxClsValue) { | ||
maxClsValue = cumulatedValue | ||
const target = maxClsTarget?.deref() | ||
callback({ | ||
value: cls, | ||
targetSelector: cslTargetSelector, | ||
value: round(maxClsValue, 4), | ||
targetSelector: target && getSelectorFromElement(target, configuration.actionNameAttribute), | ||
}) | ||
@@ -88,10 +87,19 @@ } | ||
function getTargetFromSource(sources?: Array<{ node?: Node }>) { | ||
if (!sources) { | ||
return | ||
} | ||
return find(sources, (source): source is { node: HTMLElement } => !!source.node && isElementNode(source.node))?.node | ||
} | ||
export const MAX_WINDOW_DURATION = 5 * ONE_SECOND | ||
const MAX_UPDATE_GAP = ONE_SECOND | ||
function slidingSessionWindow() { | ||
let value = 0 | ||
let cumulatedValue = 0 | ||
let startTime: RelativeTime | ||
let endTime: RelativeTime | ||
let maxValue = 0 | ||
let largestLayoutShift = 0 | ||
let largestLayoutShiftTarget: HTMLElement | undefined | ||
let largestLayoutShiftTime: RelativeTime | ||
return { | ||
@@ -101,31 +109,26 @@ update: (entry: RumLayoutShiftTiming) => { | ||
startTime === undefined || | ||
entry.startTime - endTime >= ONE_SECOND || | ||
entry.startTime - startTime >= 5 * ONE_SECOND | ||
entry.startTime - endTime >= MAX_UPDATE_GAP || | ||
entry.startTime - startTime >= MAX_WINDOW_DURATION | ||
let isMaxValue: boolean | ||
if (shouldCreateNewWindow) { | ||
startTime = endTime = entry.startTime | ||
value = entry.value | ||
largestLayoutShift = 0 | ||
largestLayoutShiftTarget = undefined | ||
maxValue = cumulatedValue = entry.value | ||
isMaxValue = true | ||
} else { | ||
value += entry.value | ||
cumulatedValue += entry.value | ||
endTime = entry.startTime | ||
} | ||
isMaxValue = entry.value > maxValue | ||
if (entry.value > largestLayoutShift) { | ||
largestLayoutShift = entry.value | ||
largestLayoutShiftTime = entry.startTime | ||
if (entry.sources?.length) { | ||
largestLayoutShiftTarget = find( | ||
entry.sources, | ||
(s): s is { node: HTMLElement } => !!s.node && isElementNode(s.node) | ||
)?.node | ||
} else { | ||
largestLayoutShiftTarget = undefined | ||
if (isMaxValue) { | ||
maxValue = entry.value | ||
} | ||
} | ||
return { | ||
cumulatedValue, | ||
isMaxValue, | ||
} | ||
}, | ||
value: () => value, | ||
largestLayoutShiftTarget: () => largestLayoutShiftTarget, | ||
largestLayoutShiftTime: () => largestLayoutShiftTime, | ||
} | ||
@@ -132,0 +135,0 @@ } |
import type { RelativeTime } from '@datadog/browser-core' | ||
import { | ||
DOM_EVENT, | ||
ExperimentalFeature, | ||
ONE_MINUTE, | ||
addEventListeners, | ||
addTelemetryDebug, | ||
findLast, | ||
isExperimentalFeatureEnabled, | ||
relativeNow, | ||
} from '@datadog/browser-core' | ||
import { DOM_EVENT, ONE_MINUTE, addEventListeners, findLast } from '@datadog/browser-core' | ||
import { LifeCycleEventType } from '../../lifeCycle' | ||
@@ -29,7 +20,2 @@ import type { LifeCycle } from '../../lifeCycle' | ||
type SerializableLCP = Omit<RumLargestContentfulPaintTiming, 'toJSON' | 'element'> | ||
let wrongLcpReported = false | ||
let previousLcp: SerializableLCP | ||
/** | ||
@@ -84,12 +70,2 @@ * Track the largest contentful paint (LCP) occurring during the initial View. This can yield | ||
if (isExperimentalFeatureEnabled(ExperimentalFeature.ZERO_LCP_TELEMETRY)) { | ||
monitorLcpEntries( | ||
lcpEntry, | ||
entries.filter( | ||
(entry): entry is RumLargestContentfulPaintTiming => | ||
entry.entryType === RumPerformanceEntryType.LARGEST_CONTENTFUL_PAINT | ||
) | ||
) | ||
} | ||
callback({ | ||
@@ -111,38 +87,1 @@ value: lcpEntry.startTime, | ||
} | ||
function monitorLcpEntries(lcpEntry: RumLargestContentfulPaintTiming, lcpEntries: RumLargestContentfulPaintTiming[]) { | ||
if (wrongLcpReported) { | ||
return | ||
} | ||
const wrongLcpDetected = | ||
lcpEntry.startTime === 0 | ||
? 'LCP with startTime = 0' | ||
: previousLcp !== undefined && lcpEntry.startTime < previousLcp.startTime | ||
? 'LCP with startTime < previous LCP' | ||
: previousLcp !== undefined && lcpEntry.size < previousLcp.size | ||
? 'LCP with size < previous LCP' | ||
: undefined | ||
if (wrongLcpDetected) { | ||
wrongLcpReported = true | ||
addTelemetryDebug(wrongLcpDetected, { | ||
debug: { | ||
entry: toSerializableLCP(lcpEntry), | ||
previousLcp, | ||
timeOrigin: performance.timeOrigin, | ||
now: relativeNow(), | ||
lcpEntries: lcpEntries.map(toSerializableLCP), | ||
}, | ||
}) | ||
} | ||
previousLcp = toSerializableLCP(lcpEntry) | ||
} | ||
function toSerializableLCP(entry: RumLargestContentfulPaintTiming): SerializableLCP { | ||
const jsonEntry = entry.toJSON() | ||
delete jsonEntry.element | ||
return jsonEntry | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
493
1314029
24073
+ Added@datadog/browser-core@5.19.0(transitive)
- Removed@datadog/browser-core@5.18.0(transitive)
Updated@datadog/browser-core@5.19.0