Socket
Socket
Sign inDemoInstall

@datadog/browser-rum-core

Package Overview
Dependencies
Maintainers
1
Versions
177
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@datadog/browser-rum-core - npm Package Compare versions

Comparing version 5.19.0 to 5.20.0

188

cjs/boot/rumPublicApi.d.ts

@@ -1,2 +0,2 @@

import type { Context, User, DeflateWorker, DeflateEncoderStreamId, DeflateEncoder, TrackingConsent } from '@datadog/browser-core';
import type { Context, User, DeflateWorker, DeflateEncoderStreamId, DeflateEncoder, TrackingConsent, PublicApi } from '@datadog/browser-core';
import type { LifeCycle } from '../domain/lifeCycle';

@@ -8,32 +8,12 @@ import type { ViewContexts } from '../domain/contexts/viewContexts';

import type { ViewOptions } from '../domain/view/trackViews';
import type { InternalContext } from '../domain/contexts/internalContext';
import type { StartRum, StartRumResult } from './startRum';
export type RumPublicApi = ReturnType<typeof makeRumPublicApi>;
export interface RecorderApi {
start: () => void;
stop: () => void;
onRumStart: (lifeCycle: LifeCycle, configuration: RumConfiguration, sessionManager: RumSessionManager, viewContexts: ViewContexts, deflateWorker: DeflateWorker | undefined) => void;
isRecording: () => boolean;
getReplayStats: (viewId: string) => ReplayStats | undefined;
getSessionReplayLink: () => string | undefined;
}
export interface RumPublicApiOptions {
ignoreInitIfSyntheticsWillInjectRum?: boolean;
startDeflateWorker?: (configuration: RumConfiguration, source: string, onInitializationFailure: () => void) => DeflateWorker | undefined;
createDeflateEncoder?: (configuration: RumConfiguration, worker: DeflateWorker, streamId: DeflateEncoderStreamId) => DeflateEncoder;
}
export interface Strategy {
export interface RumPublicApi extends PublicApi {
/**
* Init the RUM browser SDK.
* @param initConfiguration Configuration options of the SDK
*
* See [RUM Browser Monitoring Setup](https://docs.datadoghq.com/real_user_monitoring/browser) for further information.
*/
init: (initConfiguration: RumInitConfiguration) => void;
initConfiguration: RumInitConfiguration | undefined;
getInternalContext: StartRumResult['getInternalContext'];
stopSession: StartRumResult['stopSession'];
addTiming: StartRumResult['addTiming'];
startView: StartRumResult['startView'];
addAction: StartRumResult['addAction'];
addError: StartRumResult['addError'];
addFeatureFlagEvaluation: StartRumResult['addFeatureFlagEvaluation'];
startDurationVital: StartRumResult['startDurationVital'];
stopDurationVital: StartRumResult['stopDurationVital'];
}
export declare function makeRumPublicApi(startRumImpl: StartRum, recorderApi: RecorderApi, options?: RumPublicApiOptions): {
init: (initConfiguration: RumInitConfiguration) => void;
/**

@@ -50,12 +30,64 @@ * Set the tracking consent of the current user.

* over the one provided as initialization parameter.
*
* See [User tracking consent](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-tracking-consent) for further information.
*/
setTrackingConsent: (trackingConsent: TrackingConsent) => void;
/**
* Set the global context information to all events, stored in `@context`
*
* @param context Global context
*
* See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.
*/
setGlobalContext: (context: any) => void;
/**
* Get the global Context
*
* See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.
*/
getGlobalContext: () => Context;
/**
* Set or update a global context property, stored in `@context.<key>`
*
* @param key Key of the property
* @param property Value of the property
*
* See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.
*/
setGlobalContextProperty: (key: any, value: any) => void;
/**
* Remove a global context property
*
* See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.
*/
removeGlobalContextProperty: (key: any) => void;
getGlobalContext: () => Context;
setGlobalContext: (context: any) => void;
/**
* Clear the global context
*
* See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.
*/
clearGlobalContext: () => void;
getInternalContext: (startTime?: number) => import("../domain/contexts/internalContext").InternalContext | undefined;
/**
* [Internal API] Get the internal SDK context
*/
getInternalContext: (startTime?: number) => InternalContext | undefined;
/**
* Get the init configuration
*/
getInitConfiguration: () => RumInitConfiguration | undefined;
/**
* Add a custom action, stored in `@action`
* @param name Name of the action
* @param context Context of the action
*
* See [Send RUM Custom Actions](https://docs.datadoghq.com/real_user_monitoring/guide/send-rum-custom-actions) for further information.
*/
addAction: (name: string, context?: object) => void;
/**
* Add a custom error, stored in `@error`.
* @param error Error. Favor sending a [Javascript Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) to have a stack trace attached to the error event.
* @param context Context of the error
*
* See [Send RUM Custom Actions](https://docs.datadoghq.com/real_user_monitoring/guide/send-rum-custom-actions) for further information.
*/
addError: (error: unknown, context?: object) => void;

@@ -74,7 +106,45 @@ /**

addTiming: (name: string, time?: number) => void;
/**
* Set user information to all events, stored in `@usr`
*
* See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.
*/
setUser: (newUser: User) => void;
/**
* Get user information
*
* See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.
*/
getUser: () => Context;
/**
* Set or update the user property, stored in `@usr.<key>`
*
* @param key Key of the property
* @param property Value of the property
*
* See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.
*/
setUserProperty: (key: any, property: any) => void;
/**
* Remove a user property
*
* See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.
*/
removeUserProperty: (key: any) => void;
/**
* Clear all user information
*
* See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.
*/
clearUser: () => void;
/**
* Start a view manually.
* Enable to manual start a view, use `trackViewManually: true` init parameter and call `startView()` to create RUM views and be aligned with how you’ve defined them in your SPA application routing.
*
* @param options.name name of the view
* @param options.service service of the view
* @param options.version version of the view
*
* See [Override default RUM view names](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#override-default-rum-view-names) for further information.
*/
startView: {

@@ -84,2 +154,5 @@ (name?: string): void;

};
/**
* Stop the session. A new session will start at the next user interaction with the page.
*/
stopSession: () => void;

@@ -94,11 +167,52 @@ /**

* We recommend enabling the intake request compression when using feature flags `compressIntakeRequests: true`.
* For more information see the full [feature flag tracking guide](https://docs.datadoghq.com/real_user_monitoring/feature_flag_tracking/).
*
* See [Feature Flag Tracking](https://docs.datadoghq.com/real_user_monitoring/feature_flag_tracking/) for further information.
*/
addFeatureFlagEvaluation: (key: string, value: any) => void;
/**
* Get the Session Replay Link.
*
* See [Connect Session Replay To Your Third-Party Tools](https://docs.datadoghq.com/real_user_monitoring/guide/connect-session-replay-to-your-third-party-tools) for further information.
*/
getSessionReplayLink: () => string | undefined;
/**
* Start Session Replay recording.
* Enable to conditionally start the recording, use the `startSessionReplayRecordingManually:true` init parameter and call `startSessionReplayRecording()`
*
* See [Browser Session Replay](https://docs.datadoghq.com/real_user_monitoring/session_replay/browser) for further information.
*/
startSessionReplayRecording: () => void;
/**
* Stop Session Replay recording.
*
* See [Browser Session Replay](https://docs.datadoghq.com/real_user_monitoring/session_replay/browser) for further information.
*/
stopSessionReplayRecording: () => void;
} & {
onReady(callback: () => void): void;
version: string;
};
}
export interface RecorderApi {
start: () => void;
stop: () => void;
onRumStart: (lifeCycle: LifeCycle, configuration: RumConfiguration, sessionManager: RumSessionManager, viewContexts: ViewContexts, deflateWorker: DeflateWorker | undefined) => void;
isRecording: () => boolean;
getReplayStats: (viewId: string) => ReplayStats | undefined;
getSessionReplayLink: () => string | undefined;
}
export interface RumPublicApiOptions {
ignoreInitIfSyntheticsWillInjectRum?: boolean;
startDeflateWorker?: (configuration: RumConfiguration, source: string, onInitializationFailure: () => void) => DeflateWorker | undefined;
createDeflateEncoder?: (configuration: RumConfiguration, worker: DeflateWorker, streamId: DeflateEncoderStreamId) => DeflateEncoder;
}
export interface Strategy {
init: (initConfiguration: RumInitConfiguration) => void;
initConfiguration: RumInitConfiguration | undefined;
getInternalContext: StartRumResult['getInternalContext'];
stopSession: StartRumResult['stopSession'];
addTiming: StartRumResult['addTiming'];
startView: StartRumResult['startView'];
addAction: StartRumResult['addAction'];
addError: StartRumResult['addError'];
addFeatureFlagEvaluation: StartRumResult['addFeatureFlagEvaluation'];
startDurationVital: StartRumResult['startDurationVital'];
stopDurationVital: StartRumResult['stopDurationVital'];
}
export declare function makeRumPublicApi(startRumImpl: StartRum, recorderApi: RecorderApi, options?: RumPublicApiOptions): RumPublicApi;

63

cjs/boot/rumPublicApi.js

@@ -63,14 +63,2 @@ "use strict";

init: (0, browser_core_1.monitor)(function (initConfiguration) { return strategy.init(initConfiguration); }),
/**
* Set the tracking consent of the current user.
*
* @param {"granted" | "not-granted"} trackingConsent The user tracking consent
*
* Data will be sent only if it is set to "granted". This value won't be stored by the library
* across page loads: you will need to call this method or set the appropriate `trackingConsent`
* field in the init() method at each page load.
*
* If this method is called before the init() method, the provided value will take precedence
* over the one provided as initialization parameter.
*/
setTrackingConsent: (0, browser_core_1.monitor)(function (trackingConsent) {

@@ -80,2 +68,7 @@ trackingConsentState.update(trackingConsent);

}),
setGlobalContext: (0, browser_core_1.monitor)(function (context) {
globalContextManager.setContext(context);
(0, browser_core_1.addTelemetryUsage)({ feature: 'set-global-context' });
}),
getGlobalContext: (0, browser_core_1.monitor)(function () { return globalContextManager.getContext(); }),
setGlobalContextProperty: (0, browser_core_1.monitor)(function (key, value) {

@@ -86,19 +79,18 @@ globalContextManager.setContextProperty(key, value);

removeGlobalContextProperty: (0, browser_core_1.monitor)(function (key) { return globalContextManager.removeContextProperty(key); }),
getGlobalContext: (0, browser_core_1.monitor)(function () { return globalContextManager.getContext(); }),
setGlobalContext: (0, browser_core_1.monitor)(function (context) {
globalContextManager.setContext(context);
(0, browser_core_1.addTelemetryUsage)({ feature: 'set-global-context' });
}),
clearGlobalContext: (0, browser_core_1.monitor)(function () { return globalContextManager.clearContext(); }),
getInternalContext: (0, browser_core_1.monitor)(function (startTime) { return strategy.getInternalContext(startTime); }),
getInitConfiguration: (0, browser_core_1.monitor)(function () { return (0, browser_core_1.deepClone)(strategy.initConfiguration); }),
addAction: (0, browser_core_1.monitor)(function (name, context) {
strategy.addAction({
name: (0, browser_core_1.sanitize)(name),
context: (0, browser_core_1.sanitize)(context),
startClocks: (0, browser_core_1.clocksNow)(),
type: "custom" /* ActionType.CUSTOM */,
addAction: function (name, context) {
var handlingStack = (0, browser_core_1.createHandlingStack)();
(0, browser_core_1.callMonitored)(function () {
strategy.addAction({
name: (0, browser_core_1.sanitize)(name),
context: (0, browser_core_1.sanitize)(context),
startClocks: (0, browser_core_1.clocksNow)(),
type: "custom" /* ActionType.CUSTOM */,
handlingStack: handlingStack,
});
(0, browser_core_1.addTelemetryUsage)({ feature: 'add-action' });
});
(0, browser_core_1.addTelemetryUsage)({ feature: 'add-action' });
}),
},
addError: function (error, context) {

@@ -116,13 +108,2 @@ var handlingStack = (0, browser_core_1.createHandlingStack)();

},
/**
* Add a custom timing relative to the start of the current view,
* stored in `@view.custom_timings.<timing_name>`
*
* @param name Name of the custom timing
* @param [time] Epoch timestamp of the custom timing (if not set, will use current time)
*
* Note: passing a relative time is discouraged since it is actually used as-is but displayed relative to the view start.
* We currently don't provide a way to retrieve the view start time, so it can be challenging to provide a timing relative to the view start.
* see https://github.com/DataDog/browser-sdk/issues/2552
*/
addTiming: (0, browser_core_1.monitor)(function (name, time) {

@@ -152,12 +133,2 @@ // TODO: next major decide to drop relative time support or update its behaviour

}),
/**
* Add a feature flag evaluation,
* stored in `@feature_flags.<feature_flag_key>`
*
* @param {string} key The key of the feature flag.
* @param {any} value The value of the feature flag.
*
* We recommend enabling the intake request compression when using feature flags `compressIntakeRequests: true`.
* For more information see the full [feature flag tracking guide](https://docs.datadoghq.com/real_user_monitoring/feature_flag_tracking/).
*/
addFeatureFlagEvaluation: (0, browser_core_1.monitor)(function (key, value) {

@@ -164,0 +135,0 @@ strategy.addFeatureFlagEvaluation((0, browser_core_1.sanitize)(key), (0, browser_core_1.sanitize)(value));

@@ -82,4 +82,4 @@ "use strict";

(0, browser_core_1.addTelemetryConfiguration)((0, configuration_1.serializeRumConfiguration)(initConfiguration));
(0, longTaskCollection_1.startLongTaskCollection)(lifeCycle, configuration, session);
(0, resourceCollection_1.startResourceCollection)(lifeCycle, configuration, session, pageStateHistory);
(0, longTaskCollection_1.startLongTaskCollection)(lifeCycle, configuration);
(0, resourceCollection_1.startResourceCollection)(lifeCycle, configuration, pageStateHistory);
var _b = (0, viewCollection_1.startViewCollection)(lifeCycle, configuration, location, domMutationObservable, locationChangeObservable, featureFlagContexts, pageStateHistory, recorderApi, initialViewOptions), addTiming = _b.addTiming, startView = _b.startView, stopViewCollection = _b.stop;

@@ -86,0 +86,0 @@ cleanupTasks.push(stopViewCollection);

@@ -14,2 +14,3 @@ import type { ClocksState, Context, Observable } from '@datadog/browser-core';

context?: Context;
handlingStack?: string;
}

@@ -16,0 +17,0 @@ export type AutoAction = ClickAction;

@@ -65,2 +65,8 @@ "use strict";

}, autoActionProperties);
var domainContext = isAutoAction(action) ? { events: action.events } : {};
if (!isAutoAction(action) &&
action.handlingStack &&
(0, browser_core_1.isExperimentalFeatureEnabled)(browser_core_1.ExperimentalFeature.MICRO_FRONTEND)) {
domainContext.handlingStack = action.handlingStack;
}
return {

@@ -70,3 +76,3 @@ customerContext: customerContext,

startTime: action.startClocks.relative,
domainContext: isAutoAction(action) ? { events: action.events } : {},
domainContext: domainContext,
};

@@ -73,0 +79,0 @@ }

@@ -59,3 +59,3 @@ "use strict";

},
browser_sdk_version: (0, browser_core_1.canUseEventBridge)() ? "5.19.0" : undefined,
browser_sdk_version: (0, browser_core_1.canUseEventBridge)() ? "5.20.0" : undefined,
},

@@ -62,0 +62,0 @@ application: {

@@ -69,10 +69,14 @@ "use strict";

}
var domainContext = {
error: error.originalError,
};
if ((0, browser_core_1.isExperimentalFeatureEnabled)(browser_core_1.ExperimentalFeature.MICRO_FRONTEND)) {
domainContext.handlingStack = error.handlingStack;
}
return {
rawRumEvent: rawRumEvent,
startTime: error.startClocks.relative,
domainContext: {
error: error.originalError,
},
domainContext: domainContext,
};
}
//# sourceMappingURL=errorCollection.js.map

@@ -8,2 +8,65 @@ /**

export declare function getSelectorFromElement(targetElement: Element, actionNameAttribute: string | undefined): string | undefined;
/**
* Check whether the selector is unique among the element siblings. In other words, it returns true
* if "ELEMENT_PARENT > CHILD_SELECTOR" returns a single element.
*
* @param {Element} currentElement - the element being considered while iterating over the target
* element ancestors.
*
* @param {string} currentElementSelector - a selector that matches the current element. That
* selector is not a composed selector (i.e. it might be a single tag name, class name...).
*
* @param {string|undefined} childSelector - child selector is a selector that targets a descendant
* of the current element. When undefined, the current element is the target element.
*
* # Scope selector usage
*
* When composed together, the final selector will be joined with `>` operators to make sure we
* target direct descendants at each level. In this function, we'll use `querySelector` to check if
* a selector matches descendants of the current element. But by default, the query selector match
* elements at any level. Example:
*
* ```html
* <main>
* <div>
* <span></span>
* </div>
* <marquee>
* <div>
* <span></span>
* </div>
* </marquee>
* </main>
* ```
*
* `sibling.querySelector('DIV > SPAN')` will match both span elements, so we would consider the
* selector to be not unique, even if it is unique when we'll compose it with the parent with a `>`
* operator (`MAIN > DIV > SPAN`).
*
* To avoid this, we can use the `:scope` selector to make sure the selector starts from the current
* sibling (i.e. `sibling.querySelector('DIV:scope > SPAN')` will only match the first span).
*
* The result will be less accurate on browsers that don't support :scope (i. e. IE): it will check
* for any element matching the selector contained in the parent (in other words,
* "ELEMENT_PARENT CHILD_SELECTOR" returns a single element), regardless of whether the selector is
* a direct descendant of the element parent. This should not impact results too much: if it
* inaccurately returns false, we'll just fall back to another strategy.
*
* [1]: https://developer.mozilla.org/fr/docs/Web/CSS/:scope
*
* # Performance considerations
*
* We compute selectors in performance-critical operations (ex: during a click), so we need to make
* sure the function is as fast as possible. We observed that naively using `querySelectorAll` to
* check if the selector matches more than 1 element is quite expensive, so we want to avoid it.
*
* Because we are iterating the DOM upward and we use that function at every level, we know the
* child selector is already unique among the current element children, so we don't need to check
* for the current element subtree.
*
* Instead, we can focus on the current element siblings. If we find a single element matching the
* selector within a sibling, we know that it's not unique. This allows us to use `querySelector`
* (or `matches`, when the current element is the target element) instead of `querySelectorAll`.
*/
export declare function isSelectorUniqueAmongSiblings(currentElement: Element, currentElementSelector: string, childSelector: string | undefined): boolean;
export declare function supportScopeSelector(): boolean;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.supportScopeSelector = exports.getSelectorFromElement = exports.STABLE_ATTRIBUTES = void 0;
exports.supportScopeSelector = exports.isSelectorUniqueAmongSiblings = exports.getSelectorFromElement = exports.STABLE_ATTRIBUTES = void 0;
var polyfills_1 = require("../browser/polyfills");

@@ -44,13 +44,13 @@ var getActionNameFromElement_1 = require("./action/getActionNameFromElement");

}
var targetElementSelector = '';
var element = targetElement;
while (element && element.nodeName !== 'HTML') {
var globallyUniqueSelector = findSelector(element, GLOBALLY_UNIQUE_SELECTOR_GETTERS, isSelectorUniqueGlobally, actionNameAttribute, targetElementSelector);
var targetElementSelector;
var currentElement = targetElement;
while (currentElement && currentElement.nodeName !== 'HTML') {
var globallyUniqueSelector = findSelector(currentElement, GLOBALLY_UNIQUE_SELECTOR_GETTERS, isSelectorUniqueGlobally, actionNameAttribute, targetElementSelector);
if (globallyUniqueSelector) {
return globallyUniqueSelector;
}
var uniqueSelectorAmongChildren = findSelector(element, UNIQUE_AMONG_CHILDREN_SELECTOR_GETTERS, isSelectorUniqueAmongSiblings, actionNameAttribute, targetElementSelector);
var uniqueSelectorAmongChildren = findSelector(currentElement, UNIQUE_AMONG_CHILDREN_SELECTOR_GETTERS, isSelectorUniqueAmongSiblings, actionNameAttribute, targetElementSelector);
targetElementSelector =
uniqueSelectorAmongChildren || combineSelector(getPositionSelector(element), targetElementSelector);
element = (0, polyfills_1.getParentElement)(element);
uniqueSelectorAmongChildren || combineSelector(getPositionSelector(currentElement), targetElementSelector);
currentElement = (0, polyfills_1.getParentElement)(currentElement);
}

@@ -129,5 +129,4 @@ return targetElementSelector;

}
var fullSelector = combineSelector(elementSelector, childSelector);
if (predicate(element, fullSelector)) {
return fullSelector;
if (predicate(element, elementSelector, childSelector)) {
return combineSelector(elementSelector, childSelector);
}

@@ -139,19 +138,92 @@ }

*/
function isSelectorUniqueGlobally(element, selector) {
return element.ownerDocument.querySelectorAll(selector).length === 1;
function isSelectorUniqueGlobally(element, elementSelector, childSelector) {
return element.ownerDocument.querySelectorAll(combineSelector(elementSelector, childSelector)).length === 1;
}
/**
* Check whether the selector is unique among the element siblings. In other words, it returns true
* if "ELEMENT_PARENT > SELECTOR" returns a single element.
* if "ELEMENT_PARENT > CHILD_SELECTOR" returns a single element.
*
* @param {Element} currentElement - the element being considered while iterating over the target
* element ancestors.
*
* @param {string} currentElementSelector - a selector that matches the current element. That
* selector is not a composed selector (i.e. it might be a single tag name, class name...).
*
* @param {string|undefined} childSelector - child selector is a selector that targets a descendant
* of the current element. When undefined, the current element is the target element.
*
* # Scope selector usage
*
* When composed together, the final selector will be joined with `>` operators to make sure we
* target direct descendants at each level. In this function, we'll use `querySelector` to check if
* a selector matches descendants of the current element. But by default, the query selector match
* elements at any level. Example:
*
* ```html
* <main>
* <div>
* <span></span>
* </div>
* <marquee>
* <div>
* <span></span>
* </div>
* </marquee>
* </main>
* ```
*
* `sibling.querySelector('DIV > SPAN')` will match both span elements, so we would consider the
* selector to be not unique, even if it is unique when we'll compose it with the parent with a `>`
* operator (`MAIN > DIV > SPAN`).
*
* To avoid this, we can use the `:scope` selector to make sure the selector starts from the current
* sibling (i.e. `sibling.querySelector('DIV:scope > SPAN')` will only match the first span).
*
* The result will be less accurate on browsers that don't support :scope (i. e. IE): it will check
* for any element matching the selector contained in the parent (in other words,
* "ELEMENT_PARENT SELECTOR" returns a single element), regardless of whether the selector is a
* direct descendent of the element parent. This should not impact results too much: if it
* "ELEMENT_PARENT CHILD_SELECTOR" returns a single element), regardless of whether the selector is
* a direct descendant of the element parent. This should not impact results too much: if it
* inaccurately returns false, we'll just fall back to another strategy.
*
* [1]: https://developer.mozilla.org/fr/docs/Web/CSS/:scope
*
* # Performance considerations
*
* We compute selectors in performance-critical operations (ex: during a click), so we need to make
* sure the function is as fast as possible. We observed that naively using `querySelectorAll` to
* check if the selector matches more than 1 element is quite expensive, so we want to avoid it.
*
* Because we are iterating the DOM upward and we use that function at every level, we know the
* child selector is already unique among the current element children, so we don't need to check
* for the current element subtree.
*
* Instead, we can focus on the current element siblings. If we find a single element matching the
* selector within a sibling, we know that it's not unique. This allows us to use `querySelector`
* (or `matches`, when the current element is the target element) instead of `querySelectorAll`.
*/
function isSelectorUniqueAmongSiblings(element, selector) {
return ((0, polyfills_1.getParentElement)(element).querySelectorAll(supportScopeSelector() ? combineSelector(':scope', selector) : selector)
.length === 1);
function isSelectorUniqueAmongSiblings(currentElement, currentElementSelector, childSelector) {
var isSiblingMatching;
if (childSelector === undefined) {
// If the child selector is undefined (meaning `currentElement` is the target element, not one
// of its ancestor), we need to use `matches` to check if the sibling is matching the selector,
// as `querySelector` only returns a descendant of the element.
isSiblingMatching = function (sibling) { return (0, polyfills_1.elementMatches)(sibling, currentElementSelector); };
}
else {
var scopedSelector_1 = supportScopeSelector()
? combineSelector("".concat(currentElementSelector, ":scope"), childSelector)
: combineSelector(currentElementSelector, childSelector);
isSiblingMatching = function (sibling) { return sibling.querySelector(scopedSelector_1) !== null; };
}
var parent = (0, polyfills_1.getParentElement)(currentElement);
var sibling = parent.firstElementChild;
while (sibling) {
if (sibling !== currentElement && isSiblingMatching(sibling)) {
return false;
}
sibling = sibling.nextElementSibling;
}
return true;
}
exports.isSelectorUniqueAmongSiblings = isSelectorUniqueAmongSiblings;
function combineSelector(parent, child) {

@@ -158,0 +230,0 @@ return child ? "".concat(parent, ">").concat(child) : parent;

import type { LifeCycle } from '../lifeCycle';
import type { RumSessionManager } from '../rumSessionManager';
import type { RumConfiguration } from '../configuration';
export declare function startLongTaskCollection(lifeCycle: LifeCycle, configuration: RumConfiguration, sessionManager: RumSessionManager): void;
export declare function startLongTaskCollection(lifeCycle: LifeCycle, configuration: RumConfiguration): void;

@@ -6,3 +6,3 @@ "use strict";

var performanceCollection_1 = require("../../browser/performanceCollection");
function startLongTaskCollection(lifeCycle, configuration, sessionManager) {
function startLongTaskCollection(lifeCycle, configuration) {
lifeCycle.subscribe(0 /* LifeCycleEventType.PERFORMANCE_ENTRIES_COLLECTED */, function (entries) {

@@ -14,4 +14,3 @@ for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {

}
var session = sessionManager.findTrackedSession(entry.startTime);
if (!session || !configuration.trackLongTasks) {
if (!configuration.trackLongTasks) {
break;

@@ -18,0 +17,0 @@ }

@@ -43,2 +43,3 @@ import type { Duration, XhrCompleteContext, XhrStartContext, ClocksState, FetchStartContext, FetchResolveContext } from '@datadog/browser-core';

isAborted: boolean;
handlingStack?: string;
}

@@ -45,0 +46,0 @@ export declare function startRequestCollection(lifeCycle: LifeCycle, configuration: RumConfiguration, sessionManager: RumSessionManager): void;

@@ -44,2 +44,3 @@ "use strict";

isAborted: context.isAborted,
handlingStack: context.handlingStack,
});

@@ -86,2 +87,3 @@ break;

isAborted: context.isAborted,
handlingStack: context.handlingStack,
});

@@ -88,0 +90,0 @@ });

import type { RumConfiguration } from '../configuration';
import type { LifeCycle } from '../lifeCycle';
import type { RumSessionManager } from '../rumSessionManager';
import type { PageStateHistory } from '../contexts/pageStateHistory';
export declare function startResourceCollection(lifeCycle: LifeCycle, configuration: RumConfiguration, sessionManager: RumSessionManager, pageStateHistory: PageStateHistory): void;
export declare function startResourceCollection(lifeCycle: LifeCycle, configuration: RumConfiguration, pageStateHistory: PageStateHistory): void;

@@ -8,5 +8,5 @@ "use strict";

var resourceUtils_1 = require("./resourceUtils");
function startResourceCollection(lifeCycle, configuration, sessionManager, pageStateHistory) {
function startResourceCollection(lifeCycle, configuration, pageStateHistory) {
lifeCycle.subscribe(8 /* LifeCycleEventType.REQUEST_COMPLETED */, function (request) {
var rawEvent = processRequest(request, configuration, sessionManager, pageStateHistory);
var rawEvent = processRequest(request, configuration, pageStateHistory);
if (rawEvent) {

@@ -20,3 +20,3 @@ lifeCycle.notify(12 /* LifeCycleEventType.RAW_RUM_EVENT_COLLECTED */, rawEvent);

if (entry.entryType === performanceCollection_1.RumPerformanceEntryType.RESOURCE && !(0, resourceUtils_1.isRequestKind)(entry)) {
var rawEvent = processResourceEntry(entry, configuration, sessionManager);
var rawEvent = processResourceEntry(entry, configuration);
if (rawEvent) {

@@ -30,8 +30,7 @@ lifeCycle.notify(12 /* LifeCycleEventType.RAW_RUM_EVENT_COLLECTED */, rawEvent);

exports.startResourceCollection = startResourceCollection;
function processRequest(request, configuration, sessionManager, pageStateHistory) {
function processRequest(request, configuration, pageStateHistory) {
var matchingTiming = (0, matchRequestTiming_1.matchRequestTiming)(request);
var startClocks = matchingTiming ? (0, browser_core_1.relativeToClocks)(matchingTiming.startTime) : request.startClocks;
var shouldIndex = shouldIndexResource(configuration, sessionManager, startClocks);
var tracingInfo = computeRequestTracingInfo(request, configuration);
if (!shouldIndex && !tracingInfo) {
if (!configuration.trackResources && !tracingInfo) {
return;

@@ -54,6 +53,6 @@ }

_dd: {
discarded: !shouldIndex,
discarded: !configuration.trackResources,
},
}, tracingInfo, correspondingTimingOverrides);
return {
var collectedData = {
startTime: startClocks.relative,

@@ -71,8 +70,11 @@ rawRumEvent: resourceEvent,

};
if ((0, browser_core_1.isExperimentalFeatureEnabled)(browser_core_1.ExperimentalFeature.MICRO_FRONTEND)) {
collectedData.domainContext.handlingStack = request.handlingStack;
}
return collectedData;
}
function processResourceEntry(entry, configuration, sessionManager) {
function processResourceEntry(entry, configuration) {
var startClocks = (0, browser_core_1.relativeToClocks)(entry.startTime);
var shouldIndex = shouldIndexResource(configuration, sessionManager, startClocks);
var tracingInfo = computeEntryTracingInfo(entry, configuration);
if (!shouldIndex && !tracingInfo) {
if (!configuration.trackResources && !tracingInfo) {
return;

@@ -92,3 +94,3 @@ }

_dd: {
discarded: !shouldIndex,
discarded: !configuration.trackResources,
},

@@ -104,5 +106,2 @@ }, tracingInfo, entryMetrics);

}
function shouldIndexResource(configuration, sessionManager, resourceStart) {
return configuration.trackResources && sessionManager.findTrackedSession(resourceStart.relative);
}
function computePerformanceEntryMetrics(timing) {

@@ -109,0 +108,0 @@ var renderBlockingStatus = timing.renderBlockingStatus;

@@ -11,2 +11,3 @@ /**

events?: Event[];
handlingStack?: string;
}

@@ -20,2 +21,3 @@ export interface RumFetchResourceEventDomainContext {

isAborted: boolean;
handlingStack?: string;
}

@@ -26,2 +28,3 @@ export interface RumXhrResourceEventDomainContext {

isAborted: boolean;
handlingStack?: string;
}

@@ -33,2 +36,3 @@ export interface RumOtherResourceEventDomainContext {

error: unknown;
handlingStack?: string;
}

@@ -35,0 +39,0 @@ export interface RumLongTaskEventDomainContext {

@@ -1,2 +0,2 @@

import type { Context, User, DeflateWorker, DeflateEncoderStreamId, DeflateEncoder, TrackingConsent } from '@datadog/browser-core';
import type { Context, User, DeflateWorker, DeflateEncoderStreamId, DeflateEncoder, TrackingConsent, PublicApi } from '@datadog/browser-core';
import type { LifeCycle } from '../domain/lifeCycle';

@@ -8,32 +8,12 @@ import type { ViewContexts } from '../domain/contexts/viewContexts';

import type { ViewOptions } from '../domain/view/trackViews';
import type { InternalContext } from '../domain/contexts/internalContext';
import type { StartRum, StartRumResult } from './startRum';
export type RumPublicApi = ReturnType<typeof makeRumPublicApi>;
export interface RecorderApi {
start: () => void;
stop: () => void;
onRumStart: (lifeCycle: LifeCycle, configuration: RumConfiguration, sessionManager: RumSessionManager, viewContexts: ViewContexts, deflateWorker: DeflateWorker | undefined) => void;
isRecording: () => boolean;
getReplayStats: (viewId: string) => ReplayStats | undefined;
getSessionReplayLink: () => string | undefined;
}
export interface RumPublicApiOptions {
ignoreInitIfSyntheticsWillInjectRum?: boolean;
startDeflateWorker?: (configuration: RumConfiguration, source: string, onInitializationFailure: () => void) => DeflateWorker | undefined;
createDeflateEncoder?: (configuration: RumConfiguration, worker: DeflateWorker, streamId: DeflateEncoderStreamId) => DeflateEncoder;
}
export interface Strategy {
export interface RumPublicApi extends PublicApi {
/**
* Init the RUM browser SDK.
* @param initConfiguration Configuration options of the SDK
*
* See [RUM Browser Monitoring Setup](https://docs.datadoghq.com/real_user_monitoring/browser) for further information.
*/
init: (initConfiguration: RumInitConfiguration) => void;
initConfiguration: RumInitConfiguration | undefined;
getInternalContext: StartRumResult['getInternalContext'];
stopSession: StartRumResult['stopSession'];
addTiming: StartRumResult['addTiming'];
startView: StartRumResult['startView'];
addAction: StartRumResult['addAction'];
addError: StartRumResult['addError'];
addFeatureFlagEvaluation: StartRumResult['addFeatureFlagEvaluation'];
startDurationVital: StartRumResult['startDurationVital'];
stopDurationVital: StartRumResult['stopDurationVital'];
}
export declare function makeRumPublicApi(startRumImpl: StartRum, recorderApi: RecorderApi, options?: RumPublicApiOptions): {
init: (initConfiguration: RumInitConfiguration) => void;
/**

@@ -50,12 +30,64 @@ * Set the tracking consent of the current user.

* over the one provided as initialization parameter.
*
* See [User tracking consent](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-tracking-consent) for further information.
*/
setTrackingConsent: (trackingConsent: TrackingConsent) => void;
/**
* Set the global context information to all events, stored in `@context`
*
* @param context Global context
*
* See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.
*/
setGlobalContext: (context: any) => void;
/**
* Get the global Context
*
* See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.
*/
getGlobalContext: () => Context;
/**
* Set or update a global context property, stored in `@context.<key>`
*
* @param key Key of the property
* @param property Value of the property
*
* See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.
*/
setGlobalContextProperty: (key: any, value: any) => void;
/**
* Remove a global context property
*
* See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.
*/
removeGlobalContextProperty: (key: any) => void;
getGlobalContext: () => Context;
setGlobalContext: (context: any) => void;
/**
* Clear the global context
*
* See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.
*/
clearGlobalContext: () => void;
getInternalContext: (startTime?: number) => import("../domain/contexts/internalContext").InternalContext | undefined;
/**
* [Internal API] Get the internal SDK context
*/
getInternalContext: (startTime?: number) => InternalContext | undefined;
/**
* Get the init configuration
*/
getInitConfiguration: () => RumInitConfiguration | undefined;
/**
* Add a custom action, stored in `@action`
* @param name Name of the action
* @param context Context of the action
*
* See [Send RUM Custom Actions](https://docs.datadoghq.com/real_user_monitoring/guide/send-rum-custom-actions) for further information.
*/
addAction: (name: string, context?: object) => void;
/**
* Add a custom error, stored in `@error`.
* @param error Error. Favor sending a [Javascript Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) to have a stack trace attached to the error event.
* @param context Context of the error
*
* See [Send RUM Custom Actions](https://docs.datadoghq.com/real_user_monitoring/guide/send-rum-custom-actions) for further information.
*/
addError: (error: unknown, context?: object) => void;

@@ -74,7 +106,45 @@ /**

addTiming: (name: string, time?: number) => void;
/**
* Set user information to all events, stored in `@usr`
*
* See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.
*/
setUser: (newUser: User) => void;
/**
* Get user information
*
* See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.
*/
getUser: () => Context;
/**
* Set or update the user property, stored in `@usr.<key>`
*
* @param key Key of the property
* @param property Value of the property
*
* See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.
*/
setUserProperty: (key: any, property: any) => void;
/**
* Remove a user property
*
* See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.
*/
removeUserProperty: (key: any) => void;
/**
* Clear all user information
*
* See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.
*/
clearUser: () => void;
/**
* Start a view manually.
* Enable to manual start a view, use `trackViewManually: true` init parameter and call `startView()` to create RUM views and be aligned with how you’ve defined them in your SPA application routing.
*
* @param options.name name of the view
* @param options.service service of the view
* @param options.version version of the view
*
* See [Override default RUM view names](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#override-default-rum-view-names) for further information.
*/
startView: {

@@ -84,2 +154,5 @@ (name?: string): void;

};
/**
* Stop the session. A new session will start at the next user interaction with the page.
*/
stopSession: () => void;

@@ -94,11 +167,52 @@ /**

* We recommend enabling the intake request compression when using feature flags `compressIntakeRequests: true`.
* For more information see the full [feature flag tracking guide](https://docs.datadoghq.com/real_user_monitoring/feature_flag_tracking/).
*
* See [Feature Flag Tracking](https://docs.datadoghq.com/real_user_monitoring/feature_flag_tracking/) for further information.
*/
addFeatureFlagEvaluation: (key: string, value: any) => void;
/**
* Get the Session Replay Link.
*
* See [Connect Session Replay To Your Third-Party Tools](https://docs.datadoghq.com/real_user_monitoring/guide/connect-session-replay-to-your-third-party-tools) for further information.
*/
getSessionReplayLink: () => string | undefined;
/**
* Start Session Replay recording.
* Enable to conditionally start the recording, use the `startSessionReplayRecordingManually:true` init parameter and call `startSessionReplayRecording()`
*
* See [Browser Session Replay](https://docs.datadoghq.com/real_user_monitoring/session_replay/browser) for further information.
*/
startSessionReplayRecording: () => void;
/**
* Stop Session Replay recording.
*
* See [Browser Session Replay](https://docs.datadoghq.com/real_user_monitoring/session_replay/browser) for further information.
*/
stopSessionReplayRecording: () => void;
} & {
onReady(callback: () => void): void;
version: string;
};
}
export interface RecorderApi {
start: () => void;
stop: () => void;
onRumStart: (lifeCycle: LifeCycle, configuration: RumConfiguration, sessionManager: RumSessionManager, viewContexts: ViewContexts, deflateWorker: DeflateWorker | undefined) => void;
isRecording: () => boolean;
getReplayStats: (viewId: string) => ReplayStats | undefined;
getSessionReplayLink: () => string | undefined;
}
export interface RumPublicApiOptions {
ignoreInitIfSyntheticsWillInjectRum?: boolean;
startDeflateWorker?: (configuration: RumConfiguration, source: string, onInitializationFailure: () => void) => DeflateWorker | undefined;
createDeflateEncoder?: (configuration: RumConfiguration, worker: DeflateWorker, streamId: DeflateEncoderStreamId) => DeflateEncoder;
}
export interface Strategy {
init: (initConfiguration: RumInitConfiguration) => void;
initConfiguration: RumInitConfiguration | undefined;
getInternalContext: StartRumResult['getInternalContext'];
stopSession: StartRumResult['stopSession'];
addTiming: StartRumResult['addTiming'];
startView: StartRumResult['startView'];
addAction: StartRumResult['addAction'];
addError: StartRumResult['addError'];
addFeatureFlagEvaluation: StartRumResult['addFeatureFlagEvaluation'];
startDurationVital: StartRumResult['startDurationVital'];
stopDurationVital: StartRumResult['stopDurationVital'];
}
export declare function makeRumPublicApi(startRumImpl: StartRum, recorderApi: RecorderApi, options?: RumPublicApiOptions): RumPublicApi;

@@ -60,14 +60,2 @@ import { addTelemetryUsage, timeStampToClocks, isExperimentalFeatureEnabled, ExperimentalFeature, assign, createContextManager, deepClone, makePublicApi, monitor, clocksNow, callMonitored, createHandlingStack, checkUser, sanitizeUser, sanitize, createIdentityEncoder, createCustomerDataTrackerManager, storeContextManager, displayAlreadyInitializedError, createTrackingConsentState, } from '@datadog/browser-core';

init: monitor(function (initConfiguration) { return strategy.init(initConfiguration); }),
/**
* Set the tracking consent of the current user.
*
* @param {"granted" | "not-granted"} trackingConsent The user tracking consent
*
* Data will be sent only if it is set to "granted". This value won't be stored by the library
* across page loads: you will need to call this method or set the appropriate `trackingConsent`
* field in the init() method at each page load.
*
* If this method is called before the init() method, the provided value will take precedence
* over the one provided as initialization parameter.
*/
setTrackingConsent: monitor(function (trackingConsent) {

@@ -77,2 +65,7 @@ trackingConsentState.update(trackingConsent);

}),
setGlobalContext: monitor(function (context) {
globalContextManager.setContext(context);
addTelemetryUsage({ feature: 'set-global-context' });
}),
getGlobalContext: monitor(function () { return globalContextManager.getContext(); }),
setGlobalContextProperty: monitor(function (key, value) {

@@ -83,19 +76,18 @@ globalContextManager.setContextProperty(key, value);

removeGlobalContextProperty: monitor(function (key) { return globalContextManager.removeContextProperty(key); }),
getGlobalContext: monitor(function () { return globalContextManager.getContext(); }),
setGlobalContext: monitor(function (context) {
globalContextManager.setContext(context);
addTelemetryUsage({ feature: 'set-global-context' });
}),
clearGlobalContext: monitor(function () { return globalContextManager.clearContext(); }),
getInternalContext: monitor(function (startTime) { return strategy.getInternalContext(startTime); }),
getInitConfiguration: monitor(function () { return deepClone(strategy.initConfiguration); }),
addAction: monitor(function (name, context) {
strategy.addAction({
name: sanitize(name),
context: sanitize(context),
startClocks: clocksNow(),
type: "custom" /* ActionType.CUSTOM */,
addAction: function (name, context) {
var handlingStack = createHandlingStack();
callMonitored(function () {
strategy.addAction({
name: sanitize(name),
context: sanitize(context),
startClocks: clocksNow(),
type: "custom" /* ActionType.CUSTOM */,
handlingStack: handlingStack,
});
addTelemetryUsage({ feature: 'add-action' });
});
addTelemetryUsage({ feature: 'add-action' });
}),
},
addError: function (error, context) {

@@ -113,13 +105,2 @@ var handlingStack = createHandlingStack();

},
/**
* Add a custom timing relative to the start of the current view,
* stored in `@view.custom_timings.<timing_name>`
*
* @param name Name of the custom timing
* @param [time] Epoch timestamp of the custom timing (if not set, will use current time)
*
* Note: passing a relative time is discouraged since it is actually used as-is but displayed relative to the view start.
* We currently don't provide a way to retrieve the view start time, so it can be challenging to provide a timing relative to the view start.
* see https://github.com/DataDog/browser-sdk/issues/2552
*/
addTiming: monitor(function (name, time) {

@@ -149,12 +130,2 @@ // TODO: next major decide to drop relative time support or update its behaviour

}),
/**
* Add a feature flag evaluation,
* stored in `@feature_flags.<feature_flag_key>`
*
* @param {string} key The key of the feature flag.
* @param {any} value The value of the feature flag.
*
* We recommend enabling the intake request compression when using feature flags `compressIntakeRequests: true`.
* For more information see the full [feature flag tracking guide](https://docs.datadoghq.com/real_user_monitoring/feature_flag_tracking/).
*/
addFeatureFlagEvaluation: monitor(function (key, value) {

@@ -161,0 +132,0 @@ strategy.addFeatureFlagEvaluation(sanitize(key), sanitize(value));

@@ -79,4 +79,4 @@ import { sendToExtension, createPageExitObservable, addTelemetryConfiguration, startTelemetry, canUseEventBridge, getEventBridge, addTelemetryDebug, drainPreStartTelemetry, } from '@datadog/browser-core';

addTelemetryConfiguration(serializeRumConfiguration(initConfiguration));
startLongTaskCollection(lifeCycle, configuration, session);
startResourceCollection(lifeCycle, configuration, session, pageStateHistory);
startLongTaskCollection(lifeCycle, configuration);
startResourceCollection(lifeCycle, configuration, pageStateHistory);
var _b = startViewCollection(lifeCycle, configuration, location, domMutationObservable, locationChangeObservable, featureFlagContexts, pageStateHistory, recorderApi, initialViewOptions), addTiming = _b.addTiming, startView = _b.startView, stopViewCollection = _b.stop;

@@ -83,0 +83,0 @@ cleanupTasks.push(stopViewCollection);

@@ -14,2 +14,3 @@ import type { ClocksState, Context, Observable } from '@datadog/browser-core';

context?: Context;
handlingStack?: string;
}

@@ -16,0 +17,0 @@ export type AutoAction = ClickAction;

@@ -1,2 +0,2 @@

import { noop, assign, combine, toServerDuration, generateUUID } from '@datadog/browser-core';
import { noop, assign, combine, toServerDuration, generateUUID, isExperimentalFeatureEnabled, ExperimentalFeature, } from '@datadog/browser-core';
import { discardNegativeDuration } from '../discardNegativeDuration';

@@ -61,2 +61,8 @@ import { trackClickActions } from './trackClickActions';

}, autoActionProperties);
var domainContext = isAutoAction(action) ? { events: action.events } : {};
if (!isAutoAction(action) &&
action.handlingStack &&
isExperimentalFeatureEnabled(ExperimentalFeature.MICRO_FRONTEND)) {
domainContext.handlingStack = action.handlingStack;
}
return {

@@ -66,3 +72,3 @@ customerContext: customerContext,

startTime: action.startClocks.relative,
domainContext: isAutoAction(action) ? { events: action.events } : {},
domainContext: domainContext,
};

@@ -69,0 +75,0 @@ }

@@ -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.19.0" : undefined,
browser_sdk_version: canUseEventBridge() ? "5.20.0" : undefined,
},

@@ -59,0 +59,0 @@ application: {

@@ -1,2 +0,2 @@

import { isEmptyObject, assign, ErrorSource, generateUUID, computeRawError, computeStackTrace, Observable, trackRuntimeError, } from '@datadog/browser-core';
import { isEmptyObject, assign, ErrorSource, generateUUID, computeRawError, computeStackTrace, Observable, trackRuntimeError, isExperimentalFeatureEnabled, ExperimentalFeature, } from '@datadog/browser-core';
import { trackConsoleError } from './trackConsoleError';

@@ -64,10 +64,14 @@ import { trackReportError } from './trackReportError';

}
var domainContext = {
error: error.originalError,
};
if (isExperimentalFeatureEnabled(ExperimentalFeature.MICRO_FRONTEND)) {
domainContext.handlingStack = error.handlingStack;
}
return {
rawRumEvent: rawRumEvent,
startTime: error.startClocks.relative,
domainContext: {
error: error.originalError,
},
domainContext: domainContext,
};
}
//# sourceMappingURL=errorCollection.js.map

@@ -8,2 +8,65 @@ /**

export declare function getSelectorFromElement(targetElement: Element, actionNameAttribute: string | undefined): string | undefined;
/**
* Check whether the selector is unique among the element siblings. In other words, it returns true
* if "ELEMENT_PARENT > CHILD_SELECTOR" returns a single element.
*
* @param {Element} currentElement - the element being considered while iterating over the target
* element ancestors.
*
* @param {string} currentElementSelector - a selector that matches the current element. That
* selector is not a composed selector (i.e. it might be a single tag name, class name...).
*
* @param {string|undefined} childSelector - child selector is a selector that targets a descendant
* of the current element. When undefined, the current element is the target element.
*
* # Scope selector usage
*
* When composed together, the final selector will be joined with `>` operators to make sure we
* target direct descendants at each level. In this function, we'll use `querySelector` to check if
* a selector matches descendants of the current element. But by default, the query selector match
* elements at any level. Example:
*
* ```html
* <main>
* <div>
* <span></span>
* </div>
* <marquee>
* <div>
* <span></span>
* </div>
* </marquee>
* </main>
* ```
*
* `sibling.querySelector('DIV > SPAN')` will match both span elements, so we would consider the
* selector to be not unique, even if it is unique when we'll compose it with the parent with a `>`
* operator (`MAIN > DIV > SPAN`).
*
* To avoid this, we can use the `:scope` selector to make sure the selector starts from the current
* sibling (i.e. `sibling.querySelector('DIV:scope > SPAN')` will only match the first span).
*
* The result will be less accurate on browsers that don't support :scope (i. e. IE): it will check
* for any element matching the selector contained in the parent (in other words,
* "ELEMENT_PARENT CHILD_SELECTOR" returns a single element), regardless of whether the selector is
* a direct descendant of the element parent. This should not impact results too much: if it
* inaccurately returns false, we'll just fall back to another strategy.
*
* [1]: https://developer.mozilla.org/fr/docs/Web/CSS/:scope
*
* # Performance considerations
*
* We compute selectors in performance-critical operations (ex: during a click), so we need to make
* sure the function is as fast as possible. We observed that naively using `querySelectorAll` to
* check if the selector matches more than 1 element is quite expensive, so we want to avoid it.
*
* Because we are iterating the DOM upward and we use that function at every level, we know the
* child selector is already unique among the current element children, so we don't need to check
* for the current element subtree.
*
* Instead, we can focus on the current element siblings. If we find a single element matching the
* selector within a sibling, we know that it's not unique. This allows us to use `querySelector`
* (or `matches`, when the current element is the target element) instead of `querySelectorAll`.
*/
export declare function isSelectorUniqueAmongSiblings(currentElement: Element, currentElementSelector: string, childSelector: string | undefined): boolean;
export declare function supportScopeSelector(): boolean;

@@ -1,2 +0,2 @@

import { cssEscape, getClassList, getParentElement } from '../browser/polyfills';
import { cssEscape, elementMatches, getClassList, getParentElement } from '../browser/polyfills';
import { DEFAULT_PROGRAMMATIC_ACTION_NAME_ATTRIBUTE } from './action/getActionNameFromElement';

@@ -41,13 +41,13 @@ /**

}
var targetElementSelector = '';
var element = targetElement;
while (element && element.nodeName !== 'HTML') {
var globallyUniqueSelector = findSelector(element, GLOBALLY_UNIQUE_SELECTOR_GETTERS, isSelectorUniqueGlobally, actionNameAttribute, targetElementSelector);
var targetElementSelector;
var currentElement = targetElement;
while (currentElement && currentElement.nodeName !== 'HTML') {
var globallyUniqueSelector = findSelector(currentElement, GLOBALLY_UNIQUE_SELECTOR_GETTERS, isSelectorUniqueGlobally, actionNameAttribute, targetElementSelector);
if (globallyUniqueSelector) {
return globallyUniqueSelector;
}
var uniqueSelectorAmongChildren = findSelector(element, UNIQUE_AMONG_CHILDREN_SELECTOR_GETTERS, isSelectorUniqueAmongSiblings, actionNameAttribute, targetElementSelector);
var uniqueSelectorAmongChildren = findSelector(currentElement, UNIQUE_AMONG_CHILDREN_SELECTOR_GETTERS, isSelectorUniqueAmongSiblings, actionNameAttribute, targetElementSelector);
targetElementSelector =
uniqueSelectorAmongChildren || combineSelector(getPositionSelector(element), targetElementSelector);
element = getParentElement(element);
uniqueSelectorAmongChildren || combineSelector(getPositionSelector(currentElement), targetElementSelector);
currentElement = getParentElement(currentElement);
}

@@ -125,5 +125,4 @@ return targetElementSelector;

}
var fullSelector = combineSelector(elementSelector, childSelector);
if (predicate(element, fullSelector)) {
return fullSelector;
if (predicate(element, elementSelector, childSelector)) {
return combineSelector(elementSelector, childSelector);
}

@@ -135,18 +134,90 @@ }

*/
function isSelectorUniqueGlobally(element, selector) {
return element.ownerDocument.querySelectorAll(selector).length === 1;
function isSelectorUniqueGlobally(element, elementSelector, childSelector) {
return element.ownerDocument.querySelectorAll(combineSelector(elementSelector, childSelector)).length === 1;
}
/**
* Check whether the selector is unique among the element siblings. In other words, it returns true
* if "ELEMENT_PARENT > SELECTOR" returns a single element.
* if "ELEMENT_PARENT > CHILD_SELECTOR" returns a single element.
*
* @param {Element} currentElement - the element being considered while iterating over the target
* element ancestors.
*
* @param {string} currentElementSelector - a selector that matches the current element. That
* selector is not a composed selector (i.e. it might be a single tag name, class name...).
*
* @param {string|undefined} childSelector - child selector is a selector that targets a descendant
* of the current element. When undefined, the current element is the target element.
*
* # Scope selector usage
*
* When composed together, the final selector will be joined with `>` operators to make sure we
* target direct descendants at each level. In this function, we'll use `querySelector` to check if
* a selector matches descendants of the current element. But by default, the query selector match
* elements at any level. Example:
*
* ```html
* <main>
* <div>
* <span></span>
* </div>
* <marquee>
* <div>
* <span></span>
* </div>
* </marquee>
* </main>
* ```
*
* `sibling.querySelector('DIV > SPAN')` will match both span elements, so we would consider the
* selector to be not unique, even if it is unique when we'll compose it with the parent with a `>`
* operator (`MAIN > DIV > SPAN`).
*
* To avoid this, we can use the `:scope` selector to make sure the selector starts from the current
* sibling (i.e. `sibling.querySelector('DIV:scope > SPAN')` will only match the first span).
*
* The result will be less accurate on browsers that don't support :scope (i. e. IE): it will check
* for any element matching the selector contained in the parent (in other words,
* "ELEMENT_PARENT SELECTOR" returns a single element), regardless of whether the selector is a
* direct descendent of the element parent. This should not impact results too much: if it
* "ELEMENT_PARENT CHILD_SELECTOR" returns a single element), regardless of whether the selector is
* a direct descendant of the element parent. This should not impact results too much: if it
* inaccurately returns false, we'll just fall back to another strategy.
*
* [1]: https://developer.mozilla.org/fr/docs/Web/CSS/:scope
*
* # Performance considerations
*
* We compute selectors in performance-critical operations (ex: during a click), so we need to make
* sure the function is as fast as possible. We observed that naively using `querySelectorAll` to
* check if the selector matches more than 1 element is quite expensive, so we want to avoid it.
*
* Because we are iterating the DOM upward and we use that function at every level, we know the
* child selector is already unique among the current element children, so we don't need to check
* for the current element subtree.
*
* Instead, we can focus on the current element siblings. If we find a single element matching the
* selector within a sibling, we know that it's not unique. This allows us to use `querySelector`
* (or `matches`, when the current element is the target element) instead of `querySelectorAll`.
*/
function isSelectorUniqueAmongSiblings(element, selector) {
return (getParentElement(element).querySelectorAll(supportScopeSelector() ? combineSelector(':scope', selector) : selector)
.length === 1);
export function isSelectorUniqueAmongSiblings(currentElement, currentElementSelector, childSelector) {
var isSiblingMatching;
if (childSelector === undefined) {
// If the child selector is undefined (meaning `currentElement` is the target element, not one
// of its ancestor), we need to use `matches` to check if the sibling is matching the selector,
// as `querySelector` only returns a descendant of the element.
isSiblingMatching = function (sibling) { return elementMatches(sibling, currentElementSelector); };
}
else {
var scopedSelector_1 = supportScopeSelector()
? combineSelector("".concat(currentElementSelector, ":scope"), childSelector)
: combineSelector(currentElementSelector, childSelector);
isSiblingMatching = function (sibling) { return sibling.querySelector(scopedSelector_1) !== null; };
}
var parent = getParentElement(currentElement);
var sibling = parent.firstElementChild;
while (sibling) {
if (sibling !== currentElement && isSiblingMatching(sibling)) {
return false;
}
sibling = sibling.nextElementSibling;
}
return true;
}

@@ -153,0 +224,0 @@ function combineSelector(parent, child) {

import type { LifeCycle } from '../lifeCycle';
import type { RumSessionManager } from '../rumSessionManager';
import type { RumConfiguration } from '../configuration';
export declare function startLongTaskCollection(lifeCycle: LifeCycle, configuration: RumConfiguration, sessionManager: RumSessionManager): void;
export declare function startLongTaskCollection(lifeCycle: LifeCycle, configuration: RumConfiguration): void;
import { toServerDuration, relativeToClocks, generateUUID } from '@datadog/browser-core';
import { RumPerformanceEntryType } from '../../browser/performanceCollection';
export function startLongTaskCollection(lifeCycle, configuration, sessionManager) {
export function startLongTaskCollection(lifeCycle, configuration) {
lifeCycle.subscribe(0 /* LifeCycleEventType.PERFORMANCE_ENTRIES_COLLECTED */, function (entries) {

@@ -10,4 +10,3 @@ for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {

}
var session = sessionManager.findTrackedSession(entry.startTime);
if (!session || !configuration.trackLongTasks) {
if (!configuration.trackLongTasks) {
break;

@@ -14,0 +13,0 @@ }

@@ -43,2 +43,3 @@ import type { Duration, XhrCompleteContext, XhrStartContext, ClocksState, FetchStartContext, FetchResolveContext } from '@datadog/browser-core';

isAborted: boolean;
handlingStack?: string;
}

@@ -45,0 +46,0 @@ export declare function startRequestCollection(lifeCycle: LifeCycle, configuration: RumConfiguration, sessionManager: RumSessionManager): void;

@@ -40,2 +40,3 @@ import { initFetchObservable, initXhrObservable, readBytesFromStream, elapsed, timeStampNow, tryToClone, } from '@datadog/browser-core';

isAborted: context.isAborted,
handlingStack: context.handlingStack,
});

@@ -81,2 +82,3 @@ break;

isAborted: context.isAborted,
handlingStack: context.handlingStack,
});

@@ -83,0 +85,0 @@ });

import type { RumConfiguration } from '../configuration';
import type { LifeCycle } from '../lifeCycle';
import type { RumSessionManager } from '../rumSessionManager';
import type { PageStateHistory } from '../contexts/pageStateHistory';
export declare function startResourceCollection(lifeCycle: LifeCycle, configuration: RumConfiguration, sessionManager: RumSessionManager, pageStateHistory: PageStateHistory): void;
export declare function startResourceCollection(lifeCycle: LifeCycle, configuration: RumConfiguration, pageStateHistory: PageStateHistory): void;

@@ -1,8 +0,8 @@

import { combine, generateUUID, toServerDuration, relativeToClocks, assign, isNumber, } from '@datadog/browser-core';
import { combine, generateUUID, toServerDuration, relativeToClocks, assign, isNumber, ExperimentalFeature, isExperimentalFeatureEnabled, } from '@datadog/browser-core';
import { RumPerformanceEntryType } from '../../browser/performanceCollection';
import { matchRequestTiming } from './matchRequestTiming';
import { computePerformanceResourceDetails, computePerformanceResourceDuration, computeResourceKind, computeSize, isRequestKind, isLongDataUrl, sanitizeDataUrl, } from './resourceUtils';
export function startResourceCollection(lifeCycle, configuration, sessionManager, pageStateHistory) {
export function startResourceCollection(lifeCycle, configuration, pageStateHistory) {
lifeCycle.subscribe(8 /* LifeCycleEventType.REQUEST_COMPLETED */, function (request) {
var rawEvent = processRequest(request, configuration, sessionManager, pageStateHistory);
var rawEvent = processRequest(request, configuration, pageStateHistory);
if (rawEvent) {

@@ -16,3 +16,3 @@ lifeCycle.notify(12 /* LifeCycleEventType.RAW_RUM_EVENT_COLLECTED */, rawEvent);

if (entry.entryType === RumPerformanceEntryType.RESOURCE && !isRequestKind(entry)) {
var rawEvent = processResourceEntry(entry, configuration, sessionManager);
var rawEvent = processResourceEntry(entry, configuration);
if (rawEvent) {

@@ -25,8 +25,7 @@ lifeCycle.notify(12 /* LifeCycleEventType.RAW_RUM_EVENT_COLLECTED */, rawEvent);

}
function processRequest(request, configuration, sessionManager, pageStateHistory) {
function processRequest(request, configuration, pageStateHistory) {
var matchingTiming = matchRequestTiming(request);
var startClocks = matchingTiming ? relativeToClocks(matchingTiming.startTime) : request.startClocks;
var shouldIndex = shouldIndexResource(configuration, sessionManager, startClocks);
var tracingInfo = computeRequestTracingInfo(request, configuration);
if (!shouldIndex && !tracingInfo) {
if (!configuration.trackResources && !tracingInfo) {
return;

@@ -49,6 +48,6 @@ }

_dd: {
discarded: !shouldIndex,
discarded: !configuration.trackResources,
},
}, tracingInfo, correspondingTimingOverrides);
return {
var collectedData = {
startTime: startClocks.relative,

@@ -66,8 +65,11 @@ rawRumEvent: resourceEvent,

};
if (isExperimentalFeatureEnabled(ExperimentalFeature.MICRO_FRONTEND)) {
collectedData.domainContext.handlingStack = request.handlingStack;
}
return collectedData;
}
function processResourceEntry(entry, configuration, sessionManager) {
function processResourceEntry(entry, configuration) {
var startClocks = relativeToClocks(entry.startTime);
var shouldIndex = shouldIndexResource(configuration, sessionManager, startClocks);
var tracingInfo = computeEntryTracingInfo(entry, configuration);
if (!shouldIndex && !tracingInfo) {
if (!configuration.trackResources && !tracingInfo) {
return;

@@ -87,3 +89,3 @@ }

_dd: {
discarded: !shouldIndex,
discarded: !configuration.trackResources,
},

@@ -99,5 +101,2 @@ }, tracingInfo, entryMetrics);

}
function shouldIndexResource(configuration, sessionManager, resourceStart) {
return configuration.trackResources && sessionManager.findTrackedSession(resourceStart.relative);
}
function computePerformanceEntryMetrics(timing) {

@@ -104,0 +103,0 @@ var renderBlockingStatus = timing.renderBlockingStatus;

@@ -11,2 +11,3 @@ /**

events?: Event[];
handlingStack?: string;
}

@@ -20,2 +21,3 @@ export interface RumFetchResourceEventDomainContext {

isAborted: boolean;
handlingStack?: string;
}

@@ -26,2 +28,3 @@ export interface RumXhrResourceEventDomainContext {

isAborted: boolean;
handlingStack?: string;
}

@@ -33,2 +36,3 @@ export interface RumOtherResourceEventDomainContext {

error: unknown;
handlingStack?: string;
}

@@ -35,0 +39,0 @@ export interface RumLongTaskEventDomainContext {

{
"name": "@datadog/browser-rum-core",
"version": "5.19.0",
"version": "5.20.0",
"license": "Apache-2.0",

@@ -15,3 +15,3 @@ "main": "cjs/index.js",

"dependencies": {
"@datadog/browser-core": "5.19.0"
"@datadog/browser-core": "5.20.0"
},

@@ -32,3 +32,3 @@ "devDependencies": {

},
"gitHead": "ce92533465ebb3c3d0142f0c7d83af471572a818"
"gitHead": "1dafdcfbf43a0f0ed8a0cbd5f13d16ba68378074"
}

@@ -10,2 +10,3 @@ import type {

TrackingConsent,
PublicApi,
} from '@datadog/browser-core'

@@ -44,7 +45,205 @@ import {

import { buildCommonContext } from '../domain/contexts/commonContext'
import type { InternalContext } from '../domain/contexts/internalContext'
import { createPreStartStrategy } from './preStartRum'
import type { StartRum, StartRumResult } from './startRum'
export type RumPublicApi = ReturnType<typeof makeRumPublicApi>
export interface RumPublicApi extends PublicApi {
/**
* Init the RUM browser SDK.
* @param initConfiguration Configuration options of the SDK
*
* See [RUM Browser Monitoring Setup](https://docs.datadoghq.com/real_user_monitoring/browser) for further information.
*/
init: (initConfiguration: RumInitConfiguration) => void
/**
* Set the tracking consent of the current user.
*
* @param {"granted" | "not-granted"} trackingConsent The user tracking consent
*
* Data will be sent only if it is set to "granted". This value won't be stored by the library
* across page loads: you will need to call this method or set the appropriate `trackingConsent`
* field in the init() method at each page load.
*
* If this method is called before the init() method, the provided value will take precedence
* over the one provided as initialization parameter.
*
* See [User tracking consent](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-tracking-consent) for further information.
*/
setTrackingConsent: (trackingConsent: TrackingConsent) => void
/**
* Set the global context information to all events, stored in `@context`
*
* @param context Global context
*
* See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.
*/
setGlobalContext: (context: any) => void
/**
* Get the global Context
*
* See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.
*/
getGlobalContext: () => Context
/**
* Set or update a global context property, stored in `@context.<key>`
*
* @param key Key of the property
* @param property Value of the property
*
* See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.
*/
setGlobalContextProperty: (key: any, value: any) => void
/**
* Remove a global context property
*
* See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.
*/
removeGlobalContextProperty: (key: any) => void
/**
* Clear the global context
*
* See [Global context](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#global-context) for further information.
*/
clearGlobalContext: () => void
/**
* [Internal API] Get the internal SDK context
*/
getInternalContext: (startTime?: number) => InternalContext | undefined
/**
* Get the init configuration
*/
getInitConfiguration: () => RumInitConfiguration | undefined
/**
* Add a custom action, stored in `@action`
* @param name Name of the action
* @param context Context of the action
*
* See [Send RUM Custom Actions](https://docs.datadoghq.com/real_user_monitoring/guide/send-rum-custom-actions) for further information.
*/
addAction: (name: string, context?: object) => void
/**
* Add a custom error, stored in `@error`.
* @param error Error. Favor sending a [Javascript Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) to have a stack trace attached to the error event.
* @param context Context of the error
*
* See [Send RUM Custom Actions](https://docs.datadoghq.com/real_user_monitoring/guide/send-rum-custom-actions) for further information.
*/
addError: (error: unknown, context?: object) => void
/**
* Add a custom timing relative to the start of the current view,
* stored in `@view.custom_timings.<timing_name>`
*
* @param name Name of the custom timing
* @param [time] Epoch timestamp of the custom timing (if not set, will use current time)
*
* Note: passing a relative time is discouraged since it is actually used as-is but displayed relative to the view start.
* We currently don't provide a way to retrieve the view start time, so it can be challenging to provide a timing relative to the view start.
* see https://github.com/DataDog/browser-sdk/issues/2552
*/
addTiming: (name: string, time?: number) => void
/**
* Set user information to all events, stored in `@usr`
*
* See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.
*/
setUser: (newUser: User) => void
/**
* Get user information
*
* See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.
*/
getUser: () => Context
/**
* Set or update the user property, stored in `@usr.<key>`
*
* @param key Key of the property
* @param property Value of the property
*
* See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.
*/
setUserProperty: (key: any, property: any) => void
/**
* Remove a user property
*
* See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.
*/
removeUserProperty: (key: any) => void
/**
* Clear all user information
*
* See [User session](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#user-session) for further information.
*/
clearUser: () => void
/**
* Start a view manually.
* Enable to manual start a view, use `trackViewManually: true` init parameter and call `startView()` to create RUM views and be aligned with how you’ve defined them in your SPA application routing.
*
* @param options.name name of the view
* @param options.service service of the view
* @param options.version version of the view
*
* See [Override default RUM view names](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/#override-default-rum-view-names) for further information.
*/
startView: {
(name?: string): void
(options: ViewOptions): void
}
/**
* Stop the session. A new session will start at the next user interaction with the page.
*/
stopSession: () => void
/**
* Add a feature flag evaluation,
* stored in `@feature_flags.<feature_flag_key>`
*
* @param {string} key The key of the feature flag.
* @param {any} value The value of the feature flag.
*
* We recommend enabling the intake request compression when using feature flags `compressIntakeRequests: true`.
*
* See [Feature Flag Tracking](https://docs.datadoghq.com/real_user_monitoring/feature_flag_tracking/) for further information.
*/
addFeatureFlagEvaluation: (key: string, value: any) => void
/**
* Get the Session Replay Link.
*
* See [Connect Session Replay To Your Third-Party Tools](https://docs.datadoghq.com/real_user_monitoring/guide/connect-session-replay-to-your-third-party-tools) for further information.
*/
getSessionReplayLink: () => string | undefined
/**
* Start Session Replay recording.
* Enable to conditionally start the recording, use the `startSessionReplayRecordingManually:true` init parameter and call `startSessionReplayRecording()`
*
* See [Browser Session Replay](https://docs.datadoghq.com/real_user_monitoring/session_replay/browser) for further information.
*/
startSessionReplayRecording: () => void
/**
* Stop Session Replay recording.
*
* See [Browser Session Replay](https://docs.datadoghq.com/real_user_monitoring/session_replay/browser) for further information.
*/
stopSessionReplayRecording: () => void
}
export interface RecorderApi {

@@ -95,3 +294,7 @@ start: () => void

export function makeRumPublicApi(startRumImpl: StartRum, recorderApi: RecorderApi, options: RumPublicApiOptions = {}) {
export function makeRumPublicApi(
startRumImpl: StartRum,
recorderApi: RecorderApi,
options: RumPublicApiOptions = {}
): RumPublicApi {
const customerDataTrackerManager = createCustomerDataTrackerManager(CustomerDataCompressionStatus.Unknown)

@@ -197,18 +400,6 @@ const globalContextManager = createContextManager(

})
const rumPublicApi = makePublicApi({
init: monitor((initConfiguration: RumInitConfiguration) => strategy.init(initConfiguration)),
const rumPublicApi = makePublicApi<RumPublicApi>({
init: monitor((initConfiguration) => strategy.init(initConfiguration)),
/**
* Set the tracking consent of the current user.
*
* @param {"granted" | "not-granted"} trackingConsent The user tracking consent
*
* Data will be sent only if it is set to "granted". This value won't be stored by the library
* across page loads: you will need to call this method or set the appropriate `trackingConsent`
* field in the init() method at each page load.
*
* If this method is called before the init() method, the provided value will take precedence
* over the one provided as initialization parameter.
*/
setTrackingConsent: monitor((trackingConsent: TrackingConsent) => {
setTrackingConsent: monitor((trackingConsent) => {
trackingConsentState.update(trackingConsent)

@@ -218,33 +409,38 @@ addTelemetryUsage({ feature: 'set-tracking-consent', tracking_consent: trackingConsent })

setGlobalContextProperty: monitor((key, value) => {
globalContextManager.setContextProperty(key, value)
setGlobalContext: monitor((context) => {
globalContextManager.setContext(context)
addTelemetryUsage({ feature: 'set-global-context' })
}),
removeGlobalContextProperty: monitor((key) => globalContextManager.removeContextProperty(key)),
getGlobalContext: monitor(() => globalContextManager.getContext()),
setGlobalContext: monitor((context) => {
globalContextManager.setContext(context)
setGlobalContextProperty: monitor((key, value) => {
globalContextManager.setContextProperty(key, value)
addTelemetryUsage({ feature: 'set-global-context' })
}),
removeGlobalContextProperty: monitor((key) => globalContextManager.removeContextProperty(key)),
clearGlobalContext: monitor(() => globalContextManager.clearContext()),
getInternalContext: monitor((startTime?: number) => strategy.getInternalContext(startTime)),
getInternalContext: monitor((startTime) => strategy.getInternalContext(startTime)),
getInitConfiguration: monitor(() => deepClone(strategy.initConfiguration)),
addAction: monitor((name: string, context?: object) => {
strategy.addAction({
name: sanitize(name)!,
context: sanitize(context) as Context,
startClocks: clocksNow(),
type: ActionType.CUSTOM,
addAction: (name, context) => {
const handlingStack = createHandlingStack()
callMonitored(() => {
strategy.addAction({
name: sanitize(name)!,
context: sanitize(context) as Context,
startClocks: clocksNow(),
type: ActionType.CUSTOM,
handlingStack,
})
addTelemetryUsage({ feature: 'add-action' })
})
addTelemetryUsage({ feature: 'add-action' })
}),
},
addError: (error: unknown, context?: object) => {
addError: (error, context) => {
const handlingStack = createHandlingStack()

@@ -262,14 +458,3 @@ callMonitored(() => {

/**
* Add a custom timing relative to the start of the current view,
* stored in `@view.custom_timings.<timing_name>`
*
* @param name Name of the custom timing
* @param [time] Epoch timestamp of the custom timing (if not set, will use current time)
*
* Note: passing a relative time is discouraged since it is actually used as-is but displayed relative to the view start.
* We currently don't provide a way to retrieve the view start time, so it can be challenging to provide a timing relative to the view start.
* see https://github.com/DataDog/browser-sdk/issues/2552
*/
addTiming: monitor((name: string, time?: number) => {
addTiming: monitor((name, time) => {
// TODO: next major decide to drop relative time support or update its behaviour

@@ -279,3 +464,3 @@ strategy.addTiming(sanitize(name)!, time as RelativeTime | TimeStamp | undefined)

setUser: monitor((newUser: User) => {
setUser: monitor((newUser) => {
if (checkUser(newUser)) {

@@ -306,13 +491,3 @@ userContextManager.setContext(sanitizeUser(newUser as Context))

/**
* Add a feature flag evaluation,
* stored in `@feature_flags.<feature_flag_key>`
*
* @param {string} key The key of the feature flag.
* @param {any} value The value of the feature flag.
*
* We recommend enabling the intake request compression when using feature flags `compressIntakeRequests: true`.
* For more information see the full [feature flag tracking guide](https://docs.datadoghq.com/real_user_monitoring/feature_flag_tracking/).
*/
addFeatureFlagEvaluation: monitor((key: string, value: any) => {
addFeatureFlagEvaluation: monitor((key, value) => {
strategy.addFeatureFlagEvaluation(sanitize(key)!, sanitize(value))

@@ -323,2 +498,3 @@ addTelemetryUsage({ feature: 'add-feature-flag-evaluation' })

getSessionReplayLink: monitor(() => recorderApi.getSessionReplayLink()),
startSessionReplayRecording: monitor(() => {

@@ -328,2 +504,3 @@ recorderApi.start()

}),
stopSessionReplayRecording: monitor(() => recorderApi.stop()),

@@ -330,0 +507,0 @@ })

@@ -150,4 +150,4 @@ import type {

startLongTaskCollection(lifeCycle, configuration, session)
startResourceCollection(lifeCycle, configuration, session, pageStateHistory)
startLongTaskCollection(lifeCycle, configuration)
startResourceCollection(lifeCycle, configuration, pageStateHistory)

@@ -154,0 +154,0 @@ const {

import type { ClocksState, Context, Observable } from '@datadog/browser-core'
import { noop, assign, combine, toServerDuration, generateUUID } from '@datadog/browser-core'
import {
noop,
assign,
combine,
toServerDuration,
generateUUID,
isExperimentalFeatureEnabled,
ExperimentalFeature,
} from '@datadog/browser-core'

@@ -13,2 +21,3 @@ import { discardNegativeDuration } from '../discardNegativeDuration'

import { PageState } from '../contexts/pageStateHistory'
import type { RumActionEventDomainContext } from '../../domainContext.types'
import type { ActionContexts, ClickAction } from './trackClickActions'

@@ -24,2 +33,3 @@ import { trackClickActions } from './trackClickActions'

context?: Context
handlingStack?: string
}

@@ -107,2 +117,12 @@

const domainContext: RumActionEventDomainContext = isAutoAction(action) ? { events: action.events } : {}
if (
!isAutoAction(action) &&
action.handlingStack &&
isExperimentalFeatureEnabled(ExperimentalFeature.MICRO_FRONTEND)
) {
domainContext.handlingStack = action.handlingStack
}
return {

@@ -112,3 +132,3 @@ customerContext,

startTime: action.startClocks.relative,
domainContext: isAutoAction(action) ? { events: action.events } : {},
domainContext,
}

@@ -115,0 +135,0 @@ }

@@ -13,2 +13,4 @@ import type { Context, RawError, ClocksState } from '@datadog/browser-core'

NonErrorPrefix,
isExperimentalFeatureEnabled,
ExperimentalFeature,
} from '@datadog/browser-core'

@@ -24,2 +26,3 @@ import type { RumConfiguration } from '../configuration'

import { PageState } from '../contexts/pageStateHistory'
import type { RumErrorEventDomainContext } from '../../domainContext.types'
import { trackConsoleError } from './trackConsoleError'

@@ -124,9 +127,15 @@ import { trackReportError } from './trackReportError'

const domainContext: RumErrorEventDomainContext = {
error: error.originalError,
}
if (isExperimentalFeatureEnabled(ExperimentalFeature.MICRO_FRONTEND)) {
domainContext.handlingStack = error.handlingStack
}
return {
rawRumEvent,
startTime: error.startClocks.relative,
domainContext: {
error: error.originalError,
},
domainContext,
}
}

@@ -1,2 +0,2 @@

import { cssEscape, getClassList, getParentElement } from '../browser/polyfills'
import { cssEscape, elementMatches, getClassList, getParentElement } from '../browser/polyfills'
import { DEFAULT_PROGRAMMATIC_ACTION_NAME_ATTRIBUTE } from './action/getActionNameFromElement'

@@ -50,8 +50,8 @@

}
let targetElementSelector = ''
let element: Element | null = targetElement
let targetElementSelector: string | undefined
let currentElement: Element | null = targetElement
while (element && element.nodeName !== 'HTML') {
while (currentElement && currentElement.nodeName !== 'HTML') {
const globallyUniqueSelector = findSelector(
element,
currentElement,
GLOBALLY_UNIQUE_SELECTOR_GETTERS,

@@ -67,3 +67,3 @@ isSelectorUniqueGlobally,

const uniqueSelectorAmongChildren = findSelector(
element,
currentElement,
UNIQUE_AMONG_CHILDREN_SELECTOR_GETTERS,

@@ -75,5 +75,5 @@ isSelectorUniqueAmongSiblings,

targetElementSelector =
uniqueSelectorAmongChildren || combineSelector(getPositionSelector(element), targetElementSelector)
uniqueSelectorAmongChildren || combineSelector(getPositionSelector(currentElement), targetElementSelector)
element = getParentElement(element)
currentElement = getParentElement(currentElement)
}

@@ -159,5 +159,5 @@

selectorGetters: SelectorGetter[],
predicate: (element: Element, selector: string) => boolean,
predicate: (element: Element, elementSelector: string, childSelector: string | undefined) => boolean,
actionNameAttribute: string | undefined,
childSelector?: string
childSelector: string | undefined
) {

@@ -169,5 +169,4 @@ for (const selectorGetter of selectorGetters) {

}
const fullSelector = combineSelector(elementSelector, childSelector)
if (predicate(element, fullSelector)) {
return fullSelector
if (predicate(element, elementSelector, childSelector)) {
return combineSelector(elementSelector, childSelector)
}

@@ -180,4 +179,8 @@ }

*/
function isSelectorUniqueGlobally(element: Element, selector: string): boolean {
return element.ownerDocument.querySelectorAll(selector).length === 1
function isSelectorUniqueGlobally(
element: Element,
elementSelector: string,
childSelector: string | undefined
): boolean {
return element.ownerDocument.querySelectorAll(combineSelector(elementSelector, childSelector)).length === 1
}

@@ -187,15 +190,91 @@

* Check whether the selector is unique among the element siblings. In other words, it returns true
* if "ELEMENT_PARENT > SELECTOR" returns a single element.
* if "ELEMENT_PARENT > CHILD_SELECTOR" returns a single element.
*
* @param {Element} currentElement - the element being considered while iterating over the target
* element ancestors.
*
* @param {string} currentElementSelector - a selector that matches the current element. That
* selector is not a composed selector (i.e. it might be a single tag name, class name...).
*
* @param {string|undefined} childSelector - child selector is a selector that targets a descendant
* of the current element. When undefined, the current element is the target element.
*
* # Scope selector usage
*
* When composed together, the final selector will be joined with `>` operators to make sure we
* target direct descendants at each level. In this function, we'll use `querySelector` to check if
* a selector matches descendants of the current element. But by default, the query selector match
* elements at any level. Example:
*
* ```html
* <main>
* <div>
* <span></span>
* </div>
* <marquee>
* <div>
* <span></span>
* </div>
* </marquee>
* </main>
* ```
*
* `sibling.querySelector('DIV > SPAN')` will match both span elements, so we would consider the
* selector to be not unique, even if it is unique when we'll compose it with the parent with a `>`
* operator (`MAIN > DIV > SPAN`).
*
* To avoid this, we can use the `:scope` selector to make sure the selector starts from the current
* sibling (i.e. `sibling.querySelector('DIV:scope > SPAN')` will only match the first span).
*
* The result will be less accurate on browsers that don't support :scope (i. e. IE): it will check
* for any element matching the selector contained in the parent (in other words,
* "ELEMENT_PARENT SELECTOR" returns a single element), regardless of whether the selector is a
* direct descendent of the element parent. This should not impact results too much: if it
* "ELEMENT_PARENT CHILD_SELECTOR" returns a single element), regardless of whether the selector is
* a direct descendant of the element parent. This should not impact results too much: if it
* inaccurately returns false, we'll just fall back to another strategy.
*
* [1]: https://developer.mozilla.org/fr/docs/Web/CSS/:scope
*
* # Performance considerations
*
* We compute selectors in performance-critical operations (ex: during a click), so we need to make
* sure the function is as fast as possible. We observed that naively using `querySelectorAll` to
* check if the selector matches more than 1 element is quite expensive, so we want to avoid it.
*
* Because we are iterating the DOM upward and we use that function at every level, we know the
* child selector is already unique among the current element children, so we don't need to check
* for the current element subtree.
*
* Instead, we can focus on the current element siblings. If we find a single element matching the
* selector within a sibling, we know that it's not unique. This allows us to use `querySelector`
* (or `matches`, when the current element is the target element) instead of `querySelectorAll`.
*/
function isSelectorUniqueAmongSiblings(element: Element, selector: string): boolean {
return (
getParentElement(element)!.querySelectorAll(supportScopeSelector() ? combineSelector(':scope', selector) : selector)
.length === 1
)
export function isSelectorUniqueAmongSiblings(
currentElement: Element,
currentElementSelector: string,
childSelector: string | undefined
): boolean {
let isSiblingMatching: (sibling: Element) => boolean
if (childSelector === undefined) {
// If the child selector is undefined (meaning `currentElement` is the target element, not one
// of its ancestor), we need to use `matches` to check if the sibling is matching the selector,
// as `querySelector` only returns a descendant of the element.
isSiblingMatching = (sibling) => elementMatches(sibling, currentElementSelector)
} else {
const scopedSelector = supportScopeSelector()
? combineSelector(`${currentElementSelector}:scope`, childSelector)
: combineSelector(currentElementSelector, childSelector)
isSiblingMatching = (sibling) => sibling.querySelector(scopedSelector) !== null
}
const parent = getParentElement(currentElement)!
let sibling = parent.firstElementChild
while (sibling) {
if (sibling !== currentElement && isSiblingMatching(sibling)) {
return false
}
sibling = sibling.nextElementSibling
}
return true
}

@@ -202,0 +281,0 @@

@@ -6,11 +6,6 @@ import { toServerDuration, relativeToClocks, generateUUID } from '@datadog/browser-core'

import { LifeCycleEventType } from '../lifeCycle'
import type { RumSessionManager } from '../rumSessionManager'
import { RumPerformanceEntryType } from '../../browser/performanceCollection'
import type { RumConfiguration } from '../configuration'
export function startLongTaskCollection(
lifeCycle: LifeCycle,
configuration: RumConfiguration,
sessionManager: RumSessionManager
) {
export function startLongTaskCollection(lifeCycle: LifeCycle, configuration: RumConfiguration) {
lifeCycle.subscribe(LifeCycleEventType.PERFORMANCE_ENTRIES_COLLECTED, (entries) => {

@@ -21,4 +16,3 @@ for (const entry of entries) {

}
const session = sessionManager.findTrackedSession(entry.startTime)
if (!session || !configuration.trackLongTasks) {
if (!configuration.trackLongTasks) {
break

@@ -25,0 +19,0 @@ }

@@ -59,2 +59,3 @@ import type {

isAborted: boolean
handlingStack?: string
}

@@ -106,2 +107,3 @@

isAborted: context.isAborted,
handlingStack: context.handlingStack,
})

@@ -151,2 +153,3 @@ break

isAborted: context.isAborted,
handlingStack: context.handlingStack,
})

@@ -153,0 +156,0 @@ })

@@ -11,2 +11,4 @@ import type { ClocksState, Duration } from '@datadog/browser-core'

isNumber,
ExperimentalFeature,
isExperimentalFeatureEnabled,
} from '@datadog/browser-core'

@@ -22,3 +24,2 @@ import type { RumConfiguration } from '../configuration'

import type { RequestCompleteEvent } from '../requestCollection'
import type { RumSessionManager } from '../rumSessionManager'
import type { PageStateHistory } from '../contexts/pageStateHistory'

@@ -40,7 +41,6 @@ import { PageState } from '../contexts/pageStateHistory'

configuration: RumConfiguration,
sessionManager: RumSessionManager,
pageStateHistory: PageStateHistory
) {
lifeCycle.subscribe(LifeCycleEventType.REQUEST_COMPLETED, (request: RequestCompleteEvent) => {
const rawEvent = processRequest(request, configuration, sessionManager, pageStateHistory)
const rawEvent = processRequest(request, configuration, pageStateHistory)
if (rawEvent) {

@@ -54,3 +54,3 @@ lifeCycle.notify(LifeCycleEventType.RAW_RUM_EVENT_COLLECTED, rawEvent)

if (entry.entryType === RumPerformanceEntryType.RESOURCE && !isRequestKind(entry)) {
const rawEvent = processResourceEntry(entry, configuration, sessionManager)
const rawEvent = processResourceEntry(entry, configuration)
if (rawEvent) {

@@ -67,3 +67,2 @@ lifeCycle.notify(LifeCycleEventType.RAW_RUM_EVENT_COLLECTED, rawEvent)

configuration: RumConfiguration,
sessionManager: RumSessionManager,
pageStateHistory: PageStateHistory

@@ -73,5 +72,4 @@ ): RawRumEventCollectedData<RawRumResourceEvent> | undefined {

const startClocks = matchingTiming ? relativeToClocks(matchingTiming.startTime) : request.startClocks
const shouldIndex = shouldIndexResource(configuration, sessionManager, startClocks)
const tracingInfo = computeRequestTracingInfo(request, configuration)
if (!shouldIndex && !tracingInfo) {
if (!configuration.trackResources && !tracingInfo) {
return

@@ -99,3 +97,3 @@ }

_dd: {
discarded: !shouldIndex,
discarded: !configuration.trackResources,
},

@@ -106,3 +104,3 @@ },

)
return {
const collectedData = {
startTime: startClocks.relative,

@@ -120,2 +118,8 @@ rawRumEvent: resourceEvent,

}
if (isExperimentalFeatureEnabled(ExperimentalFeature.MICRO_FRONTEND)) {
collectedData.domainContext.handlingStack = request.handlingStack
}
return collectedData
}

@@ -125,9 +129,7 @@

entry: RumPerformanceResourceTiming,
configuration: RumConfiguration,
sessionManager: RumSessionManager
configuration: RumConfiguration
): RawRumEventCollectedData<RawRumResourceEvent> | undefined {
const startClocks = relativeToClocks(entry.startTime)
const shouldIndex = shouldIndexResource(configuration, sessionManager, startClocks)
const tracingInfo = computeEntryTracingInfo(entry, configuration)
if (!shouldIndex && !tracingInfo) {
if (!configuration.trackResources && !tracingInfo) {
return

@@ -150,3 +152,3 @@ }

_dd: {
discarded: !shouldIndex,
discarded: !configuration.trackResources,
},

@@ -166,10 +168,2 @@ },

function shouldIndexResource(
configuration: RumConfiguration,
sessionManager: RumSessionManager,
resourceStart: ClocksState
) {
return configuration.trackResources && sessionManager.findTrackedSession(resourceStart.relative)
}
function computePerformanceEntryMetrics(timing: RumPerformanceResourceTiming) {

@@ -176,0 +170,0 @@ const { renderBlockingStatus } = timing

@@ -27,2 +27,3 @@ /**

events?: Event[]
handlingStack?: string
}

@@ -37,2 +38,3 @@

isAborted: boolean
handlingStack?: string
}

@@ -44,2 +46,3 @@

isAborted: boolean
handlingStack?: string
}

@@ -53,2 +56,3 @@

error: unknown
handlingStack?: string
}

@@ -55,0 +59,0 @@

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc