@datadog/browser-rum-core
Advanced tools
Comparing version 4.42.2 to 4.43.0
@@ -46,8 +46,6 @@ "use strict"; | ||
} | ||
if ((0, browser_core_1.canUseEventBridge)()) { | ||
var eventBridgeAvailable = (0, browser_core_1.canUseEventBridge)(); | ||
if (eventBridgeAvailable) { | ||
initConfiguration = overrideInitConfigurationForBridge(initConfiguration); | ||
} | ||
else if (!canHandleSession(initConfiguration)) { | ||
return; | ||
} | ||
if (!canInitRum(initConfiguration)) { | ||
@@ -60,2 +58,6 @@ return; | ||
} | ||
if (!eventBridgeAvailable && !configuration.sessionStoreStrategyType) { | ||
browser_core_1.display.warn('No storage available for session. We will not send any data.'); | ||
return; | ||
} | ||
if (!configuration.trackViewsManually) { | ||
@@ -159,13 +161,2 @@ doStartRum(initConfiguration, configuration); | ||
return rumPublicApi; | ||
function canHandleSession(initConfiguration) { | ||
if (!(0, browser_core_1.areCookiesAuthorized)((0, browser_core_1.buildCookieOptions)(initConfiguration))) { | ||
browser_core_1.display.warn('Cookies are not authorized, we will not send any data.'); | ||
return false; | ||
} | ||
if (isLocalFile()) { | ||
browser_core_1.display.error('Execution is not allowed in the current context.'); | ||
return false; | ||
} | ||
return true; | ||
} | ||
function canInitRum(initConfiguration) { | ||
@@ -187,7 +178,4 @@ if (isAlreadyInitialized) { | ||
} | ||
function isLocalFile() { | ||
return window.location.protocol === 'file:'; | ||
} | ||
} | ||
exports.makeRumPublicApi = makeRumPublicApi; | ||
//# sourceMappingURL=rumPublicApi.js.map |
@@ -24,2 +24,3 @@ import type { Observable, RawError, ContextManager } from '@datadog/browser-core'; | ||
foregroundContexts: import("../domain/contexts/foregroundContexts").ForegroundContexts; | ||
pageStateHistory: import("../domain/contexts/pageStateHistory").PageStateHistory; | ||
urlContexts: { | ||
@@ -26,0 +27,0 @@ findUrl: (startTime?: import("@datadog/browser-core").RelativeTime | undefined) => import("../domain/contexts/urlContexts").UrlContext | undefined; |
@@ -67,8 +67,7 @@ "use strict"; | ||
var locationChangeObservable = (0, locationChangeObservable_1.createLocationChangeObservable)(location); | ||
var _a = startRumEventCollection(lifeCycle, configuration, location, session, locationChangeObservable, domMutationObservable, function () { return (0, commonContext_1.buildCommonContext)(globalContextManager, userContextManager, recorderApi); }, reportError), viewContexts = _a.viewContexts, foregroundContexts = _a.foregroundContexts, urlContexts = _a.urlContexts, actionContexts = _a.actionContexts, addAction = _a.addAction; | ||
var _a = startRumEventCollection(lifeCycle, configuration, location, session, locationChangeObservable, domMutationObservable, function () { return (0, commonContext_1.buildCommonContext)(globalContextManager, userContextManager, recorderApi); }, reportError), viewContexts = _a.viewContexts, foregroundContexts = _a.foregroundContexts, pageStateHistory = _a.pageStateHistory, urlContexts = _a.urlContexts, actionContexts = _a.actionContexts, addAction = _a.addAction; | ||
(0, browser_core_1.addTelemetryConfiguration)((0, configuration_1.serializeRumConfiguration)(initConfiguration)); | ||
(0, longTaskCollection_1.startLongTaskCollection)(lifeCycle, session); | ||
var pageStateHistory = (0, pageStateHistory_1.startPageStateHistory)(); | ||
(0, resourceCollection_1.startResourceCollection)(lifeCycle, configuration, session, pageStateHistory); | ||
var _b = (0, viewCollection_1.startViewCollection)(lifeCycle, configuration, location, domMutationObservable, locationChangeObservable, foregroundContexts, featureFlagContexts, recorderApi, initialViewOptions), addTiming = _b.addTiming, startView = _b.startView; | ||
var _b = (0, viewCollection_1.startViewCollection)(lifeCycle, configuration, location, domMutationObservable, locationChangeObservable, foregroundContexts, featureFlagContexts, pageStateHistory, recorderApi, initialViewOptions), addTiming = _b.addTiming, startView = _b.startView; | ||
var addError = (0, errorCollection_1.startErrorCollection)(lifeCycle, foregroundContexts, featureFlagContexts).addError; | ||
@@ -104,2 +103,3 @@ (0, requestCollection_1.startRequestCollection)(lifeCycle, configuration, session); | ||
var foregroundContexts = (0, foregroundContexts_1.startForegroundContexts)(); | ||
var pageStateHistory = (0, pageStateHistory_1.startPageStateHistory)(); | ||
var _a = (0, actionCollection_1.startActionCollection)(lifeCycle, domMutationObservable, configuration, foregroundContexts), addAction = _a.addAction, actionContexts = _a.actionContexts; | ||
@@ -110,2 +110,3 @@ (0, assembly_1.startRumAssembly)(configuration, lifeCycle, sessionManager, viewContexts, urlContexts, actionContexts, buildCommonContext, reportError); | ||
foregroundContexts: foregroundContexts, | ||
pageStateHistory: pageStateHistory, | ||
urlContexts: urlContexts, | ||
@@ -112,0 +113,0 @@ addAction: addAction, |
@@ -56,3 +56,3 @@ "use strict"; | ||
}, | ||
browser_sdk_version: (0, browser_core_1.canUseEventBridge)() ? "4.42.2" : undefined, | ||
browser_sdk_version: (0, browser_core_1.canUseEventBridge)() ? "4.43.0" : undefined, | ||
}, | ||
@@ -59,0 +59,0 @@ application: { |
import type { Duration, RelativeTime } from '@datadog/browser-core'; | ||
export declare const MAX_PAGE_STATE_ENTRIES = 500; | ||
import type { PageStateServerEntry } from '../../rawRumEvent.types'; | ||
export declare const MAX_PAGE_STATE_ENTRIES = 4000; | ||
export declare const MAX_PAGE_STATE_ENTRIES_SELECTABLE = 500; | ||
export declare const PAGE_STATE_CONTEXT_TIME_OUT_DELAY: number; | ||
export declare const enum PageState { | ||
@@ -15,7 +18,6 @@ ACTIVE = "active", | ||
export interface PageStateHistory { | ||
findAll: (startTime: RelativeTime, duration: Duration) => PageStateEntry[] | undefined; | ||
findAll: (startTime: RelativeTime, duration: Duration) => PageStateServerEntry[] | undefined; | ||
addPageState(nextPageState: PageState, startTime?: RelativeTime): void; | ||
stop: () => void; | ||
} | ||
export declare function startPageStateHistory(): PageStateHistory; | ||
export declare function addPageState(nextPageState: PageState, maxPageStateEntries?: number): void; | ||
export declare function resetPageStates(): void; | ||
export declare function startPageStateHistory(maxPageStateEntriesSelectable?: number): PageStateHistory; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.resetPageStates = exports.addPageState = exports.startPageStateHistory = exports.MAX_PAGE_STATE_ENTRIES = void 0; | ||
exports.startPageStateHistory = exports.PAGE_STATE_CONTEXT_TIME_OUT_DELAY = exports.MAX_PAGE_STATE_ENTRIES_SELECTABLE = exports.MAX_PAGE_STATE_ENTRIES = void 0; | ||
var browser_core_1 = require("@datadog/browser-core"); | ||
exports.MAX_PAGE_STATE_ENTRIES = 500; | ||
var pageStateEntries = []; | ||
var currentPageState; | ||
function startPageStateHistory() { | ||
addPageState(getPageState()); | ||
var stop = (0, browser_core_1.addEventListeners)(window, [ | ||
// Arbitrary value to cap number of element for memory consumption in the browser | ||
exports.MAX_PAGE_STATE_ENTRIES = 4000; | ||
// Arbitrary value to cap number of element for backend & to save bandwidth | ||
exports.MAX_PAGE_STATE_ENTRIES_SELECTABLE = 500; | ||
exports.PAGE_STATE_CONTEXT_TIME_OUT_DELAY = browser_core_1.SESSION_TIME_OUT_DELAY; | ||
function startPageStateHistory(maxPageStateEntriesSelectable) { | ||
if (maxPageStateEntriesSelectable === void 0) { maxPageStateEntriesSelectable = exports.MAX_PAGE_STATE_ENTRIES_SELECTABLE; } | ||
var pageStateHistory = new browser_core_1.ValueHistory(exports.PAGE_STATE_CONTEXT_TIME_OUT_DELAY, exports.MAX_PAGE_STATE_ENTRIES); | ||
var currentPageState; | ||
addPageState(getPageState(), (0, browser_core_1.relativeNow)()); | ||
var stopEventListeners = (0, browser_core_1.addEventListeners)(window, [ | ||
"pageshow" /* DOM_EVENT.PAGE_SHOW */, | ||
@@ -21,35 +26,53 @@ "focus" /* DOM_EVENT.FOCUS */, | ||
// cf: developer extension auto flush: https://github.com/DataDog/browser-sdk/blob/2f72bf05a672794c9e33965351964382a94c72ba/developer-extension/src/panel/flushEvents.ts#L11-L12 | ||
if (!event.isTrusted) { | ||
if (event.isTrusted) { | ||
addPageState(computePageState(event), event.timeStamp); | ||
} | ||
}, { capture: true }).stop; | ||
function addPageState(nextPageState, startTime) { | ||
if (startTime === void 0) { startTime = (0, browser_core_1.relativeNow)(); } | ||
if (nextPageState === currentPageState) { | ||
return; | ||
} | ||
if (event.type === "freeze" /* DOM_EVENT.FREEZE */) { | ||
addPageState("frozen" /* PageState.FROZEN */); | ||
} | ||
else if (event.type === "pagehide" /* DOM_EVENT.PAGE_HIDE */) { | ||
addPageState(event.persisted ? "frozen" /* PageState.FROZEN */ : "terminated" /* PageState.TERMINATED */); | ||
} | ||
else { | ||
addPageState(getPageState()); | ||
} | ||
}, { capture: true }).stop; | ||
currentPageState = nextPageState; | ||
pageStateHistory.closeActive(startTime); | ||
pageStateHistory.add({ state: currentPageState, startTime: startTime }, startTime); | ||
} | ||
return { | ||
findAll: function (startTime, duration) { | ||
var entries = []; | ||
var endTime = (0, browser_core_1.addDuration)(startTime, duration); | ||
for (var i = pageStateEntries.length - 1; i >= 0; i--) { | ||
var stateStartTime = pageStateEntries[i].startTime; | ||
if (stateStartTime >= endTime) { | ||
continue; | ||
} | ||
entries.unshift(pageStateEntries[i]); | ||
if (stateStartTime < startTime) { | ||
break; | ||
} | ||
findAll: function (eventStartTime, duration) { | ||
var pageStateEntries = pageStateHistory.findAll(eventStartTime, duration); | ||
if (pageStateEntries.length === 0) { | ||
return; | ||
} | ||
return entries.length ? entries : undefined; | ||
var pageStateServerEntries = []; | ||
// limit the number of entries to return | ||
var limit = Math.max(0, pageStateEntries.length - maxPageStateEntriesSelectable); | ||
// loop page state entries backward to return the selected ones in desc order | ||
for (var index = pageStateEntries.length - 1; index >= limit; index--) { | ||
var pageState = pageStateEntries[index]; | ||
// compute the start time relative to the event start time (ex: to be relative to the view start time) | ||
var relativeStartTime = (0, browser_core_1.elapsed)(eventStartTime, pageState.startTime); | ||
pageStateServerEntries.push({ | ||
state: pageState.state, | ||
start: (0, browser_core_1.toServerDuration)(relativeStartTime), | ||
}); | ||
} | ||
return pageStateServerEntries; | ||
}, | ||
stop: stop, | ||
addPageState: addPageState, | ||
stop: function () { | ||
stopEventListeners(); | ||
pageStateHistory.stop(); | ||
}, | ||
}; | ||
} | ||
exports.startPageStateHistory = startPageStateHistory; | ||
function computePageState(event) { | ||
if (event.type === "freeze" /* DOM_EVENT.FREEZE */) { | ||
return "frozen" /* PageState.FROZEN */; | ||
} | ||
else if (event.type === "pagehide" /* DOM_EVENT.PAGE_HIDE */) { | ||
return event.persisted ? "frozen" /* PageState.FROZEN */ : "terminated" /* PageState.TERMINATED */; | ||
} | ||
return getPageState(); | ||
} | ||
function getPageState() { | ||
@@ -64,19 +87,2 @@ if (document.visibilityState === 'hidden') { | ||
} | ||
function addPageState(nextPageState, maxPageStateEntries) { | ||
if (maxPageStateEntries === void 0) { maxPageStateEntries = exports.MAX_PAGE_STATE_ENTRIES; } | ||
if (nextPageState === currentPageState) { | ||
return; | ||
} | ||
currentPageState = nextPageState; | ||
if (pageStateEntries.length === maxPageStateEntries) { | ||
pageStateEntries.shift(); | ||
} | ||
pageStateEntries.push({ state: currentPageState, startTime: (0, browser_core_1.relativeNow)() }); | ||
} | ||
exports.addPageState = addPageState; | ||
function resetPageStates() { | ||
pageStateEntries = []; | ||
currentPageState = undefined; | ||
} | ||
exports.resetPageStates = resetPageStates; | ||
//# sourceMappingURL=pageStateHistory.js.map |
@@ -29,3 +29,3 @@ "use strict"; | ||
var indexingInfo = computeIndexingInfo(sessionManager, startClocks); | ||
var duration = (0, browser_core_1.toServerDuration)(request.duration); | ||
var duration = computeRequestDuration(pageStateHistory, startClocks, request.duration); | ||
var pageStateInfo = computePageStateInfo(pageStateHistory, startClocks, (_a = matchingTiming === null || matchingTiming === void 0 ? void 0 : matchingTiming.duration) !== null && _a !== void 0 ? _a : request.duration); | ||
@@ -143,2 +143,12 @@ var resourceEvent = (0, browser_core_1.combine)({ | ||
} | ||
function computeRequestDuration(pageStateHistory, startClocks, duration) { | ||
var _a; | ||
// TODO remove FF in next major | ||
if (!(0, browser_core_1.isExperimentalFeatureEnabled)(browser_core_1.ExperimentalFeature.NO_RESOURCE_DURATION_FROZEN_STATE)) { | ||
return (0, browser_core_1.toServerDuration)(duration); | ||
} | ||
var requestCrossedFrozenState = (_a = pageStateHistory | ||
.findAll(startClocks.relative, duration)) === null || _a === void 0 ? void 0 : _a.some(function (pageState) { return pageState.state === "frozen" /* PageState.FROZEN */; }); | ||
return !requestCrossedFrozenState ? (0, browser_core_1.toServerDuration)(duration) : undefined; | ||
} | ||
//# sourceMappingURL=resourceCollection.js.map |
@@ -8,4 +8,5 @@ import type { Observable } from '@datadog/browser-core'; | ||
import type { FeatureFlagContexts } from '../../contexts/featureFlagContext'; | ||
import type { PageStateHistory } from '../../contexts/pageStateHistory'; | ||
import type { ViewOptions } from './trackViews'; | ||
export declare function startViewCollection(lifeCycle: LifeCycle, configuration: RumConfiguration, location: Location, domMutationObservable: Observable<void>, locationChangeObservable: Observable<LocationChange>, foregroundContexts: ForegroundContexts, featureFlagContexts: FeatureFlagContexts, recorderApi: RecorderApi, initialViewOptions?: ViewOptions): { | ||
export declare function startViewCollection(lifeCycle: LifeCycle, configuration: RumConfiguration, location: Location, domMutationObservable: Observable<void>, locationChangeObservable: Observable<LocationChange>, foregroundContexts: ForegroundContexts, featureFlagContexts: FeatureFlagContexts, pageStateHistory: PageStateHistory, recorderApi: RecorderApi, initialViewOptions?: ViewOptions): { | ||
addTiming: (name: string, time?: import("@datadog/browser-core").TimeStamp | import("@datadog/browser-core").RelativeTime) => void; | ||
@@ -12,0 +13,0 @@ startView: (options?: ViewOptions | undefined, startClocks?: import("@datadog/browser-core").ClocksState | undefined) => void; |
@@ -6,12 +6,14 @@ "use strict"; | ||
var trackViews_1 = require("./trackViews"); | ||
function startViewCollection(lifeCycle, configuration, location, domMutationObservable, locationChangeObservable, foregroundContexts, featureFlagContexts, recorderApi, initialViewOptions) { | ||
function startViewCollection(lifeCycle, configuration, location, domMutationObservable, locationChangeObservable, foregroundContexts, featureFlagContexts, pageStateHistory, recorderApi, initialViewOptions) { | ||
lifeCycle.subscribe(3 /* LifeCycleEventType.VIEW_UPDATED */, function (view) { | ||
return lifeCycle.notify(10 /* LifeCycleEventType.RAW_RUM_EVENT_COLLECTED */, processViewUpdate(view, foregroundContexts, featureFlagContexts, recorderApi)); | ||
return lifeCycle.notify(10 /* LifeCycleEventType.RAW_RUM_EVENT_COLLECTED */, processViewUpdate(view, foregroundContexts, featureFlagContexts, recorderApi, pageStateHistory)); | ||
}); | ||
return (0, trackViews_1.trackViews)(location, lifeCycle, domMutationObservable, configuration, locationChangeObservable, !configuration.trackViewsManually, initialViewOptions); | ||
var trackViewResult = (0, trackViews_1.trackViews)(location, lifeCycle, domMutationObservable, configuration, locationChangeObservable, !configuration.trackViewsManually, initialViewOptions); | ||
return trackViewResult; | ||
} | ||
exports.startViewCollection = startViewCollection; | ||
function processViewUpdate(view, foregroundContexts, featureFlagContexts, recorderApi) { | ||
function processViewUpdate(view, foregroundContexts, featureFlagContexts, recorderApi, pageStateHistory) { | ||
var replayStats = recorderApi.getReplayStats(view.id); | ||
var featureFlagContext = featureFlagContexts.findFeatureFlagEvaluations(view.startClocks.relative); | ||
var pageStatesEnabled = (0, browser_core_1.isExperimentalFeatureEnabled)(browser_core_1.ExperimentalFeature.PAGE_STATES); | ||
var viewEvent = { | ||
@@ -21,2 +23,3 @@ _dd: { | ||
replay_stats: replayStats, | ||
page_states: pageStatesEnabled ? pageStateHistory.findAll(view.startClocks.relative, view.duration) : undefined, | ||
}, | ||
@@ -56,3 +59,5 @@ date: view.startClocks.timeStamp, | ||
time_spent: (0, browser_core_1.toServerDuration)(view.duration), | ||
in_foreground_periods: foregroundContexts.selectInForegroundPeriodsFor(view.startClocks.relative, view.duration), | ||
in_foreground_periods: !pageStatesEnabled | ||
? foregroundContexts.selectInForegroundPeriodsFor(view.startClocks.relative, view.duration) | ||
: undefined, | ||
}, | ||
@@ -59,0 +64,0 @@ feature_flags: featureFlagContext && !(0, browser_core_1.isEmptyObject)(featureFlagContext) ? featureFlagContext : undefined, |
@@ -7,5 +7,5 @@ "use strict"; | ||
function startRumSessionManager(configuration, lifeCycle) { | ||
var sessionManager = (0, browser_core_1.startSessionManager)(configuration.cookieOptions, exports.RUM_SESSION_KEY, function (rawTrackingType) { | ||
return computeSessionState(configuration, rawTrackingType); | ||
}); | ||
var sessionManager = (0, browser_core_1.startSessionManager)( | ||
// TODO - Improve configuration type and remove assertion | ||
configuration.sessionStoreStrategyType, exports.RUM_SESSION_KEY, function (rawTrackingType) { return computeSessionState(configuration, rawTrackingType); }); | ||
sessionManager.expireObservable.subscribe(function () { | ||
@@ -12,0 +12,0 @@ lifeCycle.notify(7 /* LifeCycleEventType.SESSION_EXPIRED */); |
import type { Context, Duration, ErrorSource, ErrorHandling, ResourceType, ServerDuration, TimeStamp, RawErrorCause } from '@datadog/browser-core'; | ||
import type { PageStateEntry } from './domain/contexts/pageStateHistory'; | ||
import type { PageState } from './domain/contexts/pageStateHistory'; | ||
import type { RumSessionPlan } from './domain/rumSessionManager'; | ||
@@ -17,3 +17,3 @@ export declare const enum RumEventType { | ||
id: string; | ||
duration: ServerDuration; | ||
duration?: ServerDuration; | ||
url: string; | ||
@@ -35,3 +35,3 @@ method?: string; | ||
discarded: boolean; | ||
page_states?: PageStateEntry[]; | ||
page_states?: PageStateServerEntry[]; | ||
}; | ||
@@ -100,2 +100,3 @@ } | ||
replay_stats?: ReplayStats; | ||
page_states?: PageStateServerEntry[]; | ||
}; | ||
@@ -107,2 +108,6 @@ } | ||
} | ||
export type PageStateServerEntry = { | ||
state: PageState; | ||
start: ServerDuration; | ||
}; | ||
export declare const enum ViewLoadingType { | ||
@@ -109,0 +114,0 @@ INITIAL_LOAD = "initial_load", |
@@ -353,3 +353,3 @@ /** | ||
*/ | ||
readonly duration: number; | ||
readonly duration?: number; | ||
/** | ||
@@ -699,2 +699,6 @@ * Size in octet of the resource response body | ||
readonly is_active?: boolean; | ||
/** | ||
* Whether this session has been sampled for replay | ||
*/ | ||
readonly sampled_for_replay?: boolean; | ||
[k: string]: unknown; | ||
@@ -716,2 +720,16 @@ }; | ||
readonly document_version: number; | ||
/** | ||
* List of the page states during the view | ||
*/ | ||
readonly page_states?: { | ||
/** | ||
* Page state name | ||
*/ | ||
readonly state: 'active' | 'passive' | 'hidden' | 'frozen' | 'terminated'; | ||
/** | ||
* Duration in ns between start of the view and start of the page state | ||
*/ | ||
readonly start: number; | ||
[k: string]: unknown; | ||
}[]; | ||
[k: string]: unknown; | ||
@@ -942,5 +960,5 @@ }; | ||
/** | ||
* Session plan: 1 is the plan without replay, 2 is the plan with replay | ||
* Session plan: 1 is the plan without replay, 2 is the plan with replay (deprecated) | ||
*/ | ||
plan: 1 | 2; | ||
plan?: 1 | 2; | ||
[k: string]: unknown; | ||
@@ -947,0 +965,0 @@ }; |
@@ -1,2 +0,2 @@ | ||
import { noop, willSyntheticsInjectRum, assign, BoundedBuffer, buildCookieOptions, createContextManager, deepClone, makePublicApi, monitor, clocksNow, timeStampNow, display, callMonitored, createHandlingStack, canUseEventBridge, areCookiesAuthorized, checkUser, sanitizeUser, sanitize, } from '@datadog/browser-core'; | ||
import { noop, willSyntheticsInjectRum, assign, BoundedBuffer, createContextManager, deepClone, makePublicApi, monitor, clocksNow, timeStampNow, display, callMonitored, createHandlingStack, canUseEventBridge, checkUser, sanitizeUser, sanitize, } from '@datadog/browser-core'; | ||
import { validateAndBuildRumConfiguration } from '../domain/configuration'; | ||
@@ -43,8 +43,6 @@ import { buildCommonContext } from '../domain/contexts/commonContext'; | ||
} | ||
if (canUseEventBridge()) { | ||
var eventBridgeAvailable = canUseEventBridge(); | ||
if (eventBridgeAvailable) { | ||
initConfiguration = overrideInitConfigurationForBridge(initConfiguration); | ||
} | ||
else if (!canHandleSession(initConfiguration)) { | ||
return; | ||
} | ||
if (!canInitRum(initConfiguration)) { | ||
@@ -57,2 +55,6 @@ return; | ||
} | ||
if (!eventBridgeAvailable && !configuration.sessionStoreStrategyType) { | ||
display.warn('No storage available for session. We will not send any data.'); | ||
return; | ||
} | ||
if (!configuration.trackViewsManually) { | ||
@@ -156,13 +158,2 @@ doStartRum(initConfiguration, configuration); | ||
return rumPublicApi; | ||
function canHandleSession(initConfiguration) { | ||
if (!areCookiesAuthorized(buildCookieOptions(initConfiguration))) { | ||
display.warn('Cookies are not authorized, we will not send any data.'); | ||
return false; | ||
} | ||
if (isLocalFile()) { | ||
display.error('Execution is not allowed in the current context.'); | ||
return false; | ||
} | ||
return true; | ||
} | ||
function canInitRum(initConfiguration) { | ||
@@ -184,6 +175,3 @@ if (isAlreadyInitialized) { | ||
} | ||
function isLocalFile() { | ||
return window.location.protocol === 'file:'; | ||
} | ||
} | ||
//# sourceMappingURL=rumPublicApi.js.map |
@@ -24,2 +24,3 @@ import type { Observable, RawError, ContextManager } from '@datadog/browser-core'; | ||
foregroundContexts: import("../domain/contexts/foregroundContexts").ForegroundContexts; | ||
pageStateHistory: import("../domain/contexts/pageStateHistory").PageStateHistory; | ||
urlContexts: { | ||
@@ -26,0 +27,0 @@ findUrl: (startTime?: import("@datadog/browser-core").RelativeTime | undefined) => import("../domain/contexts/urlContexts").UrlContext | undefined; |
@@ -64,8 +64,7 @@ import { sendToExtension, createPageExitObservable, addTelemetryConfiguration, startTelemetry, canUseEventBridge, getEventBridge, } from '@datadog/browser-core'; | ||
var locationChangeObservable = createLocationChangeObservable(location); | ||
var _a = startRumEventCollection(lifeCycle, configuration, location, session, locationChangeObservable, domMutationObservable, function () { return buildCommonContext(globalContextManager, userContextManager, recorderApi); }, reportError), viewContexts = _a.viewContexts, foregroundContexts = _a.foregroundContexts, urlContexts = _a.urlContexts, actionContexts = _a.actionContexts, addAction = _a.addAction; | ||
var _a = startRumEventCollection(lifeCycle, configuration, location, session, locationChangeObservable, domMutationObservable, function () { return buildCommonContext(globalContextManager, userContextManager, recorderApi); }, reportError), viewContexts = _a.viewContexts, foregroundContexts = _a.foregroundContexts, pageStateHistory = _a.pageStateHistory, urlContexts = _a.urlContexts, actionContexts = _a.actionContexts, addAction = _a.addAction; | ||
addTelemetryConfiguration(serializeRumConfiguration(initConfiguration)); | ||
startLongTaskCollection(lifeCycle, session); | ||
var pageStateHistory = startPageStateHistory(); | ||
startResourceCollection(lifeCycle, configuration, session, pageStateHistory); | ||
var _b = startViewCollection(lifeCycle, configuration, location, domMutationObservable, locationChangeObservable, foregroundContexts, featureFlagContexts, recorderApi, initialViewOptions), addTiming = _b.addTiming, startView = _b.startView; | ||
var _b = startViewCollection(lifeCycle, configuration, location, domMutationObservable, locationChangeObservable, foregroundContexts, featureFlagContexts, pageStateHistory, recorderApi, initialViewOptions), addTiming = _b.addTiming, startView = _b.startView; | ||
var addError = startErrorCollection(lifeCycle, foregroundContexts, featureFlagContexts).addError; | ||
@@ -100,2 +99,3 @@ startRequestCollection(lifeCycle, configuration, session); | ||
var foregroundContexts = startForegroundContexts(); | ||
var pageStateHistory = startPageStateHistory(); | ||
var _a = startActionCollection(lifeCycle, domMutationObservable, configuration, foregroundContexts), addAction = _a.addAction, actionContexts = _a.actionContexts; | ||
@@ -106,2 +106,3 @@ startRumAssembly(configuration, lifeCycle, sessionManager, viewContexts, urlContexts, actionContexts, buildCommonContext, reportError); | ||
foregroundContexts: foregroundContexts, | ||
pageStateHistory: pageStateHistory, | ||
urlContexts: urlContexts, | ||
@@ -108,0 +109,0 @@ addAction: addAction, |
@@ -53,3 +53,3 @@ import { combine, isEmptyObject, timeStampNow, currentDrift, display, createEventRateLimiter, canUseEventBridge, assign, } from '@datadog/browser-core'; | ||
}, | ||
browser_sdk_version: canUseEventBridge() ? "4.42.2" : undefined, | ||
browser_sdk_version: canUseEventBridge() ? "4.43.0" : undefined, | ||
}, | ||
@@ -56,0 +56,0 @@ application: { |
import type { Duration, RelativeTime } from '@datadog/browser-core'; | ||
export declare const MAX_PAGE_STATE_ENTRIES = 500; | ||
import type { PageStateServerEntry } from '../../rawRumEvent.types'; | ||
export declare const MAX_PAGE_STATE_ENTRIES = 4000; | ||
export declare const MAX_PAGE_STATE_ENTRIES_SELECTABLE = 500; | ||
export declare const PAGE_STATE_CONTEXT_TIME_OUT_DELAY: number; | ||
export declare const enum PageState { | ||
@@ -15,7 +18,6 @@ ACTIVE = "active", | ||
export interface PageStateHistory { | ||
findAll: (startTime: RelativeTime, duration: Duration) => PageStateEntry[] | undefined; | ||
findAll: (startTime: RelativeTime, duration: Duration) => PageStateServerEntry[] | undefined; | ||
addPageState(nextPageState: PageState, startTime?: RelativeTime): void; | ||
stop: () => void; | ||
} | ||
export declare function startPageStateHistory(): PageStateHistory; | ||
export declare function addPageState(nextPageState: PageState, maxPageStateEntries?: number): void; | ||
export declare function resetPageStates(): void; | ||
export declare function startPageStateHistory(maxPageStateEntriesSelectable?: number): PageStateHistory; |
@@ -1,8 +0,13 @@ | ||
import { addDuration, addEventListeners, relativeNow } from '@datadog/browser-core'; | ||
export var MAX_PAGE_STATE_ENTRIES = 500; | ||
var pageStateEntries = []; | ||
var currentPageState; | ||
export function startPageStateHistory() { | ||
addPageState(getPageState()); | ||
var stop = addEventListeners(window, [ | ||
import { elapsed, ValueHistory, SESSION_TIME_OUT_DELAY, toServerDuration, addEventListeners, relativeNow, } from '@datadog/browser-core'; | ||
// Arbitrary value to cap number of element for memory consumption in the browser | ||
export var MAX_PAGE_STATE_ENTRIES = 4000; | ||
// Arbitrary value to cap number of element for backend & to save bandwidth | ||
export var MAX_PAGE_STATE_ENTRIES_SELECTABLE = 500; | ||
export var PAGE_STATE_CONTEXT_TIME_OUT_DELAY = SESSION_TIME_OUT_DELAY; | ||
export function startPageStateHistory(maxPageStateEntriesSelectable) { | ||
if (maxPageStateEntriesSelectable === void 0) { maxPageStateEntriesSelectable = MAX_PAGE_STATE_ENTRIES_SELECTABLE; } | ||
var pageStateHistory = new ValueHistory(PAGE_STATE_CONTEXT_TIME_OUT_DELAY, MAX_PAGE_STATE_ENTRIES); | ||
var currentPageState; | ||
addPageState(getPageState(), relativeNow()); | ||
var stopEventListeners = addEventListeners(window, [ | ||
"pageshow" /* DOM_EVENT.PAGE_SHOW */, | ||
@@ -18,34 +23,52 @@ "focus" /* DOM_EVENT.FOCUS */, | ||
// cf: developer extension auto flush: https://github.com/DataDog/browser-sdk/blob/2f72bf05a672794c9e33965351964382a94c72ba/developer-extension/src/panel/flushEvents.ts#L11-L12 | ||
if (!event.isTrusted) { | ||
if (event.isTrusted) { | ||
addPageState(computePageState(event), event.timeStamp); | ||
} | ||
}, { capture: true }).stop; | ||
function addPageState(nextPageState, startTime) { | ||
if (startTime === void 0) { startTime = relativeNow(); } | ||
if (nextPageState === currentPageState) { | ||
return; | ||
} | ||
if (event.type === "freeze" /* DOM_EVENT.FREEZE */) { | ||
addPageState("frozen" /* PageState.FROZEN */); | ||
} | ||
else if (event.type === "pagehide" /* DOM_EVENT.PAGE_HIDE */) { | ||
addPageState(event.persisted ? "frozen" /* PageState.FROZEN */ : "terminated" /* PageState.TERMINATED */); | ||
} | ||
else { | ||
addPageState(getPageState()); | ||
} | ||
}, { capture: true }).stop; | ||
currentPageState = nextPageState; | ||
pageStateHistory.closeActive(startTime); | ||
pageStateHistory.add({ state: currentPageState, startTime: startTime }, startTime); | ||
} | ||
return { | ||
findAll: function (startTime, duration) { | ||
var entries = []; | ||
var endTime = addDuration(startTime, duration); | ||
for (var i = pageStateEntries.length - 1; i >= 0; i--) { | ||
var stateStartTime = pageStateEntries[i].startTime; | ||
if (stateStartTime >= endTime) { | ||
continue; | ||
} | ||
entries.unshift(pageStateEntries[i]); | ||
if (stateStartTime < startTime) { | ||
break; | ||
} | ||
findAll: function (eventStartTime, duration) { | ||
var pageStateEntries = pageStateHistory.findAll(eventStartTime, duration); | ||
if (pageStateEntries.length === 0) { | ||
return; | ||
} | ||
return entries.length ? entries : undefined; | ||
var pageStateServerEntries = []; | ||
// limit the number of entries to return | ||
var limit = Math.max(0, pageStateEntries.length - maxPageStateEntriesSelectable); | ||
// loop page state entries backward to return the selected ones in desc order | ||
for (var index = pageStateEntries.length - 1; index >= limit; index--) { | ||
var pageState = pageStateEntries[index]; | ||
// compute the start time relative to the event start time (ex: to be relative to the view start time) | ||
var relativeStartTime = elapsed(eventStartTime, pageState.startTime); | ||
pageStateServerEntries.push({ | ||
state: pageState.state, | ||
start: toServerDuration(relativeStartTime), | ||
}); | ||
} | ||
return pageStateServerEntries; | ||
}, | ||
stop: stop, | ||
addPageState: addPageState, | ||
stop: function () { | ||
stopEventListeners(); | ||
pageStateHistory.stop(); | ||
}, | ||
}; | ||
} | ||
function computePageState(event) { | ||
if (event.type === "freeze" /* DOM_EVENT.FREEZE */) { | ||
return "frozen" /* PageState.FROZEN */; | ||
} | ||
else if (event.type === "pagehide" /* DOM_EVENT.PAGE_HIDE */) { | ||
return event.persisted ? "frozen" /* PageState.FROZEN */ : "terminated" /* PageState.TERMINATED */; | ||
} | ||
return getPageState(); | ||
} | ||
function getPageState() { | ||
@@ -60,17 +83,2 @@ if (document.visibilityState === 'hidden') { | ||
} | ||
export function addPageState(nextPageState, maxPageStateEntries) { | ||
if (maxPageStateEntries === void 0) { maxPageStateEntries = MAX_PAGE_STATE_ENTRIES; } | ||
if (nextPageState === currentPageState) { | ||
return; | ||
} | ||
currentPageState = nextPageState; | ||
if (pageStateEntries.length === maxPageStateEntries) { | ||
pageStateEntries.shift(); | ||
} | ||
pageStateEntries.push({ state: currentPageState, startTime: relativeNow() }); | ||
} | ||
export function resetPageStates() { | ||
pageStateEntries = []; | ||
currentPageState = undefined; | ||
} | ||
//# sourceMappingURL=pageStateHistory.js.map |
@@ -25,3 +25,3 @@ import { combine, generateUUID, toServerDuration, relativeToClocks, assign, isNumber, isExperimentalFeatureEnabled, ExperimentalFeature, } from '@datadog/browser-core'; | ||
var indexingInfo = computeIndexingInfo(sessionManager, startClocks); | ||
var duration = toServerDuration(request.duration); | ||
var duration = computeRequestDuration(pageStateHistory, startClocks, request.duration); | ||
var pageStateInfo = computePageStateInfo(pageStateHistory, startClocks, (_a = matchingTiming === null || matchingTiming === void 0 ? void 0 : matchingTiming.duration) !== null && _a !== void 0 ? _a : request.duration); | ||
@@ -139,2 +139,12 @@ var resourceEvent = combine({ | ||
} | ||
function computeRequestDuration(pageStateHistory, startClocks, duration) { | ||
var _a; | ||
// TODO remove FF in next major | ||
if (!isExperimentalFeatureEnabled(ExperimentalFeature.NO_RESOURCE_DURATION_FROZEN_STATE)) { | ||
return toServerDuration(duration); | ||
} | ||
var requestCrossedFrozenState = (_a = pageStateHistory | ||
.findAll(startClocks.relative, duration)) === null || _a === void 0 ? void 0 : _a.some(function (pageState) { return pageState.state === "frozen" /* PageState.FROZEN */; }); | ||
return !requestCrossedFrozenState ? toServerDuration(duration) : undefined; | ||
} | ||
//# sourceMappingURL=resourceCollection.js.map |
@@ -8,4 +8,5 @@ import type { Observable } from '@datadog/browser-core'; | ||
import type { FeatureFlagContexts } from '../../contexts/featureFlagContext'; | ||
import type { PageStateHistory } from '../../contexts/pageStateHistory'; | ||
import type { ViewOptions } from './trackViews'; | ||
export declare function startViewCollection(lifeCycle: LifeCycle, configuration: RumConfiguration, location: Location, domMutationObservable: Observable<void>, locationChangeObservable: Observable<LocationChange>, foregroundContexts: ForegroundContexts, featureFlagContexts: FeatureFlagContexts, recorderApi: RecorderApi, initialViewOptions?: ViewOptions): { | ||
export declare function startViewCollection(lifeCycle: LifeCycle, configuration: RumConfiguration, location: Location, domMutationObservable: Observable<void>, locationChangeObservable: Observable<LocationChange>, foregroundContexts: ForegroundContexts, featureFlagContexts: FeatureFlagContexts, pageStateHistory: PageStateHistory, recorderApi: RecorderApi, initialViewOptions?: ViewOptions): { | ||
addTiming: (name: string, time?: import("@datadog/browser-core").TimeStamp | import("@datadog/browser-core").RelativeTime) => void; | ||
@@ -12,0 +13,0 @@ startView: (options?: ViewOptions | undefined, startClocks?: import("@datadog/browser-core").ClocksState | undefined) => void; |
@@ -1,12 +0,14 @@ | ||
import { isEmptyObject, mapValues, toServerDuration, isNumber } from '@datadog/browser-core'; | ||
import { isExperimentalFeatureEnabled, ExperimentalFeature, isEmptyObject, mapValues, toServerDuration, isNumber, } from '@datadog/browser-core'; | ||
import { trackViews } from './trackViews'; | ||
export function startViewCollection(lifeCycle, configuration, location, domMutationObservable, locationChangeObservable, foregroundContexts, featureFlagContexts, recorderApi, initialViewOptions) { | ||
export function startViewCollection(lifeCycle, configuration, location, domMutationObservable, locationChangeObservable, foregroundContexts, featureFlagContexts, pageStateHistory, recorderApi, initialViewOptions) { | ||
lifeCycle.subscribe(3 /* LifeCycleEventType.VIEW_UPDATED */, function (view) { | ||
return lifeCycle.notify(10 /* LifeCycleEventType.RAW_RUM_EVENT_COLLECTED */, processViewUpdate(view, foregroundContexts, featureFlagContexts, recorderApi)); | ||
return lifeCycle.notify(10 /* LifeCycleEventType.RAW_RUM_EVENT_COLLECTED */, processViewUpdate(view, foregroundContexts, featureFlagContexts, recorderApi, pageStateHistory)); | ||
}); | ||
return trackViews(location, lifeCycle, domMutationObservable, configuration, locationChangeObservable, !configuration.trackViewsManually, initialViewOptions); | ||
var trackViewResult = trackViews(location, lifeCycle, domMutationObservable, configuration, locationChangeObservable, !configuration.trackViewsManually, initialViewOptions); | ||
return trackViewResult; | ||
} | ||
function processViewUpdate(view, foregroundContexts, featureFlagContexts, recorderApi) { | ||
function processViewUpdate(view, foregroundContexts, featureFlagContexts, recorderApi, pageStateHistory) { | ||
var replayStats = recorderApi.getReplayStats(view.id); | ||
var featureFlagContext = featureFlagContexts.findFeatureFlagEvaluations(view.startClocks.relative); | ||
var pageStatesEnabled = isExperimentalFeatureEnabled(ExperimentalFeature.PAGE_STATES); | ||
var viewEvent = { | ||
@@ -16,2 +18,3 @@ _dd: { | ||
replay_stats: replayStats, | ||
page_states: pageStatesEnabled ? pageStateHistory.findAll(view.startClocks.relative, view.duration) : undefined, | ||
}, | ||
@@ -51,3 +54,5 @@ date: view.startClocks.timeStamp, | ||
time_spent: toServerDuration(view.duration), | ||
in_foreground_periods: foregroundContexts.selectInForegroundPeriodsFor(view.startClocks.relative, view.duration), | ||
in_foreground_periods: !pageStatesEnabled | ||
? foregroundContexts.selectInForegroundPeriodsFor(view.startClocks.relative, view.duration) | ||
: undefined, | ||
}, | ||
@@ -54,0 +59,0 @@ feature_flags: featureFlagContext && !isEmptyObject(featureFlagContext) ? featureFlagContext : undefined, |
import { Observable, noop, performDraw, startSessionManager } from '@datadog/browser-core'; | ||
export var RUM_SESSION_KEY = 'rum'; | ||
export function startRumSessionManager(configuration, lifeCycle) { | ||
var sessionManager = startSessionManager(configuration.cookieOptions, RUM_SESSION_KEY, function (rawTrackingType) { | ||
return computeSessionState(configuration, rawTrackingType); | ||
}); | ||
var sessionManager = startSessionManager( | ||
// TODO - Improve configuration type and remove assertion | ||
configuration.sessionStoreStrategyType, RUM_SESSION_KEY, function (rawTrackingType) { return computeSessionState(configuration, rawTrackingType); }); | ||
sessionManager.expireObservable.subscribe(function () { | ||
@@ -8,0 +8,0 @@ lifeCycle.notify(7 /* LifeCycleEventType.SESSION_EXPIRED */); |
import type { Context, Duration, ErrorSource, ErrorHandling, ResourceType, ServerDuration, TimeStamp, RawErrorCause } from '@datadog/browser-core'; | ||
import type { PageStateEntry } from './domain/contexts/pageStateHistory'; | ||
import type { PageState } from './domain/contexts/pageStateHistory'; | ||
import type { RumSessionPlan } from './domain/rumSessionManager'; | ||
@@ -17,3 +17,3 @@ export declare const enum RumEventType { | ||
id: string; | ||
duration: ServerDuration; | ||
duration?: ServerDuration; | ||
url: string; | ||
@@ -35,3 +35,3 @@ method?: string; | ||
discarded: boolean; | ||
page_states?: PageStateEntry[]; | ||
page_states?: PageStateServerEntry[]; | ||
}; | ||
@@ -100,2 +100,3 @@ } | ||
replay_stats?: ReplayStats; | ||
page_states?: PageStateServerEntry[]; | ||
}; | ||
@@ -107,2 +108,6 @@ } | ||
} | ||
export type PageStateServerEntry = { | ||
state: PageState; | ||
start: ServerDuration; | ||
}; | ||
export declare const enum ViewLoadingType { | ||
@@ -109,0 +114,0 @@ INITIAL_LOAD = "initial_load", |
@@ -353,3 +353,3 @@ /** | ||
*/ | ||
readonly duration: number; | ||
readonly duration?: number; | ||
/** | ||
@@ -699,2 +699,6 @@ * Size in octet of the resource response body | ||
readonly is_active?: boolean; | ||
/** | ||
* Whether this session has been sampled for replay | ||
*/ | ||
readonly sampled_for_replay?: boolean; | ||
[k: string]: unknown; | ||
@@ -716,2 +720,16 @@ }; | ||
readonly document_version: number; | ||
/** | ||
* List of the page states during the view | ||
*/ | ||
readonly page_states?: { | ||
/** | ||
* Page state name | ||
*/ | ||
readonly state: 'active' | 'passive' | 'hidden' | 'frozen' | 'terminated'; | ||
/** | ||
* Duration in ns between start of the view and start of the page state | ||
*/ | ||
readonly start: number; | ||
[k: string]: unknown; | ||
}[]; | ||
[k: string]: unknown; | ||
@@ -942,5 +960,5 @@ }; | ||
/** | ||
* Session plan: 1 is the plan without replay, 2 is the plan with replay | ||
* Session plan: 1 is the plan without replay, 2 is the plan with replay (deprecated) | ||
*/ | ||
plan: 1 | 2; | ||
plan?: 1 | 2; | ||
[k: string]: unknown; | ||
@@ -947,0 +965,0 @@ }; |
{ | ||
"name": "@datadog/browser-rum-core", | ||
"version": "4.42.2", | ||
"version": "4.43.0", | ||
"license": "Apache-2.0", | ||
@@ -15,3 +15,3 @@ "main": "cjs/index.js", | ||
"dependencies": { | ||
"@datadog/browser-core": "4.42.2" | ||
"@datadog/browser-core": "4.43.0" | ||
}, | ||
@@ -29,3 +29,3 @@ "devDependencies": { | ||
}, | ||
"gitHead": "4c15587221b8f3c0c111500d9b65b679f4cf05b0" | ||
"gitHead": "27baf6471a3e48028c52af9c6282e00662687cc2" | ||
} |
@@ -8,3 +8,2 @@ import type { Context, InitConfiguration, TimeStamp, RelativeTime, User } from '@datadog/browser-core' | ||
BoundedBuffer, | ||
buildCookieOptions, | ||
createContextManager, | ||
@@ -20,3 +19,2 @@ deepClone, | ||
canUseEventBridge, | ||
areCookiesAuthorized, | ||
checkUser, | ||
@@ -115,6 +113,5 @@ sanitizeUser, | ||
if (canUseEventBridge()) { | ||
const eventBridgeAvailable = canUseEventBridge() | ||
if (eventBridgeAvailable) { | ||
initConfiguration = overrideInitConfigurationForBridge(initConfiguration) | ||
} else if (!canHandleSession(initConfiguration)) { | ||
return | ||
} | ||
@@ -131,2 +128,7 @@ | ||
if (!eventBridgeAvailable && !configuration.sessionStoreStrategyType) { | ||
display.warn('No storage available for session. We will not send any data.') | ||
return | ||
} | ||
if (!configuration.trackViewsManually) { | ||
@@ -280,15 +282,2 @@ doStartRum(initConfiguration, configuration) | ||
function canHandleSession(initConfiguration: RumInitConfiguration): boolean { | ||
if (!areCookiesAuthorized(buildCookieOptions(initConfiguration))) { | ||
display.warn('Cookies are not authorized, we will not send any data.') | ||
return false | ||
} | ||
if (isLocalFile()) { | ||
display.error('Execution is not allowed in the current context.') | ||
return false | ||
} | ||
return true | ||
} | ||
function canInitRum(initConfiguration: RumInitConfiguration) { | ||
@@ -311,6 +300,2 @@ if (isAlreadyInitialized) { | ||
} | ||
function isLocalFile() { | ||
return window.location.protocol === 'file:' | ||
} | ||
} |
@@ -105,12 +105,13 @@ import type { Observable, TelemetryEvent, RawError, ContextManager } from '@datadog/browser-core' | ||
const { viewContexts, foregroundContexts, urlContexts, actionContexts, addAction } = startRumEventCollection( | ||
lifeCycle, | ||
configuration, | ||
location, | ||
session, | ||
locationChangeObservable, | ||
domMutationObservable, | ||
() => buildCommonContext(globalContextManager, userContextManager, recorderApi), | ||
reportError | ||
) | ||
const { viewContexts, foregroundContexts, pageStateHistory, urlContexts, actionContexts, addAction } = | ||
startRumEventCollection( | ||
lifeCycle, | ||
configuration, | ||
location, | ||
session, | ||
locationChangeObservable, | ||
domMutationObservable, | ||
() => buildCommonContext(globalContextManager, userContextManager, recorderApi), | ||
reportError | ||
) | ||
@@ -120,3 +121,2 @@ addTelemetryConfiguration(serializeRumConfiguration(initConfiguration)) | ||
startLongTaskCollection(lifeCycle, session) | ||
const pageStateHistory = startPageStateHistory() | ||
startResourceCollection(lifeCycle, configuration, session, pageStateHistory) | ||
@@ -131,2 +131,3 @@ const { addTiming, startView } = startViewCollection( | ||
featureFlagContexts, | ||
pageStateHistory, | ||
recorderApi, | ||
@@ -185,2 +186,4 @@ initialViewOptions | ||
const foregroundContexts = startForegroundContexts() | ||
const pageStateHistory = startPageStateHistory() | ||
const { addAction, actionContexts } = startActionCollection( | ||
@@ -207,2 +210,3 @@ lifeCycle, | ||
foregroundContexts, | ||
pageStateHistory, | ||
urlContexts, | ||
@@ -209,0 +213,0 @@ addAction, |
import type { Duration, RelativeTime } from '@datadog/browser-core' | ||
import { addDuration, addEventListeners, relativeNow, DOM_EVENT } from '@datadog/browser-core' | ||
import { | ||
elapsed, | ||
ValueHistory, | ||
SESSION_TIME_OUT_DELAY, | ||
toServerDuration, | ||
addEventListeners, | ||
relativeNow, | ||
DOM_EVENT, | ||
} from '@datadog/browser-core' | ||
import type { PageStateServerEntry } from '../../rawRumEvent.types' | ||
export const MAX_PAGE_STATE_ENTRIES = 500 | ||
// Arbitrary value to cap number of element for memory consumption in the browser | ||
export const MAX_PAGE_STATE_ENTRIES = 4000 | ||
// Arbitrary value to cap number of element for backend & to save bandwidth | ||
export const MAX_PAGE_STATE_ENTRIES_SELECTABLE = 500 | ||
export const PAGE_STATE_CONTEXT_TIME_OUT_DELAY = SESSION_TIME_OUT_DELAY | ||
export const enum PageState { | ||
@@ -13,14 +27,20 @@ ACTIVE = 'active', | ||
} | ||
export type PageStateEntry = { state: PageState; startTime: RelativeTime } | ||
export interface PageStateHistory { | ||
findAll: (startTime: RelativeTime, duration: Duration) => PageStateEntry[] | undefined | ||
findAll: (startTime: RelativeTime, duration: Duration) => PageStateServerEntry[] | undefined | ||
addPageState(nextPageState: PageState, startTime?: RelativeTime): void | ||
stop: () => void | ||
} | ||
let pageStateEntries: PageStateEntry[] = [] | ||
let currentPageState: PageState | undefined | ||
export function startPageStateHistory(): PageStateHistory { | ||
addPageState(getPageState()) | ||
export function startPageStateHistory( | ||
maxPageStateEntriesSelectable = MAX_PAGE_STATE_ENTRIES_SELECTABLE | ||
): PageStateHistory { | ||
const pageStateHistory = new ValueHistory<PageStateEntry>(PAGE_STATE_CONTEXT_TIME_OUT_DELAY, MAX_PAGE_STATE_ENTRIES) | ||
const { stop } = addEventListeners( | ||
let currentPageState: PageState | ||
addPageState(getPageState(), relativeNow()) | ||
const { stop: stopEventListeners } = addEventListeners( | ||
window, | ||
@@ -39,13 +59,5 @@ [ | ||
// cf: developer extension auto flush: https://github.com/DataDog/browser-sdk/blob/2f72bf05a672794c9e33965351964382a94c72ba/developer-extension/src/panel/flushEvents.ts#L11-L12 | ||
if (!event.isTrusted) { | ||
return | ||
if (event.isTrusted) { | ||
addPageState(computePageState(event), event.timeStamp as RelativeTime) | ||
} | ||
if (event.type === DOM_EVENT.FREEZE) { | ||
addPageState(PageState.FROZEN) | ||
} else if (event.type === DOM_EVENT.PAGE_HIDE) { | ||
addPageState((event as PageTransitionEvent).persisted ? PageState.FROZEN : PageState.TERMINATED) | ||
} else { | ||
addPageState(getPageState()) | ||
} | ||
}, | ||
@@ -55,53 +67,65 @@ { capture: true } | ||
function addPageState(nextPageState: PageState, startTime = relativeNow()) { | ||
if (nextPageState === currentPageState) { | ||
return | ||
} | ||
currentPageState = nextPageState | ||
pageStateHistory.closeActive(startTime) | ||
pageStateHistory.add({ state: currentPageState, startTime }, startTime) | ||
} | ||
return { | ||
findAll(startTime: RelativeTime, duration: Duration) { | ||
const entries: PageStateEntry[] = [] | ||
const endTime = addDuration(startTime, duration) | ||
for (let i = pageStateEntries.length - 1; i >= 0; i--) { | ||
const { startTime: stateStartTime } = pageStateEntries[i] | ||
findAll: (eventStartTime: RelativeTime, duration: Duration): PageStateServerEntry[] | undefined => { | ||
const pageStateEntries = pageStateHistory.findAll(eventStartTime, duration) | ||
if (stateStartTime >= endTime) { | ||
continue | ||
} | ||
if (pageStateEntries.length === 0) { | ||
return | ||
} | ||
entries.unshift(pageStateEntries[i]) | ||
const pageStateServerEntries = [] | ||
// limit the number of entries to return | ||
const limit = Math.max(0, pageStateEntries.length - maxPageStateEntriesSelectable) | ||
if (stateStartTime < startTime) { | ||
break | ||
} | ||
// loop page state entries backward to return the selected ones in desc order | ||
for (let index = pageStateEntries.length - 1; index >= limit; index--) { | ||
const pageState = pageStateEntries[index] | ||
// compute the start time relative to the event start time (ex: to be relative to the view start time) | ||
const relativeStartTime = elapsed(eventStartTime, pageState.startTime) | ||
pageStateServerEntries.push({ | ||
state: pageState.state, | ||
start: toServerDuration(relativeStartTime), | ||
}) | ||
} | ||
return entries.length ? entries : undefined | ||
return pageStateServerEntries | ||
}, | ||
stop, | ||
addPageState, | ||
stop: () => { | ||
stopEventListeners() | ||
pageStateHistory.stop() | ||
}, | ||
} | ||
} | ||
function getPageState(): PageState { | ||
function computePageState(event: Event) { | ||
if (event.type === DOM_EVENT.FREEZE) { | ||
return PageState.FROZEN | ||
} else if (event.type === DOM_EVENT.PAGE_HIDE) { | ||
return (event as PageTransitionEvent).persisted ? PageState.FROZEN : PageState.TERMINATED | ||
} | ||
return getPageState() | ||
} | ||
function getPageState() { | ||
if (document.visibilityState === 'hidden') { | ||
return PageState.HIDDEN | ||
} | ||
if (document.hasFocus()) { | ||
return PageState.ACTIVE | ||
} | ||
return PageState.PASSIVE | ||
} | ||
export function addPageState(nextPageState: PageState, maxPageStateEntries = MAX_PAGE_STATE_ENTRIES) { | ||
if (nextPageState === currentPageState) { | ||
return | ||
} | ||
currentPageState = nextPageState | ||
if (pageStateEntries.length === maxPageStateEntries) { | ||
pageStateEntries.shift() | ||
} | ||
pageStateEntries.push({ state: currentPageState, startTime: relativeNow() }) | ||
} | ||
export function resetPageStates() { | ||
pageStateEntries = [] | ||
currentPageState = undefined | ||
} |
@@ -28,2 +28,3 @@ import { | ||
import type { PageStateHistory } from '../../contexts/pageStateHistory' | ||
import { PageState } from '../../contexts/pageStateHistory' | ||
import { matchRequestTiming } from './matchRequestTiming' | ||
@@ -78,3 +79,3 @@ import { | ||
const duration = toServerDuration(request.duration) | ||
const duration = computeRequestDuration(pageStateHistory, startClocks, request.duration) | ||
const pageStateInfo = computePageStateInfo( | ||
@@ -229,1 +230,14 @@ pageStateHistory, | ||
} | ||
function computeRequestDuration(pageStateHistory: PageStateHistory, startClocks: ClocksState, duration: Duration) { | ||
// TODO remove FF in next major | ||
if (!isExperimentalFeatureEnabled(ExperimentalFeature.NO_RESOURCE_DURATION_FROZEN_STATE)) { | ||
return toServerDuration(duration) | ||
} | ||
const requestCrossedFrozenState = pageStateHistory | ||
.findAll(startClocks.relative, duration) | ||
?.some((pageState) => pageState.state === PageState.FROZEN) | ||
return !requestCrossedFrozenState ? toServerDuration(duration) : undefined | ||
} |
import type { Duration, ServerDuration, Observable } from '@datadog/browser-core' | ||
import { isEmptyObject, mapValues, toServerDuration, isNumber } from '@datadog/browser-core' | ||
import { | ||
isExperimentalFeatureEnabled, | ||
ExperimentalFeature, | ||
isEmptyObject, | ||
mapValues, | ||
toServerDuration, | ||
isNumber, | ||
} from '@datadog/browser-core' | ||
import type { RecorderApi } from '../../../boot/rumPublicApi' | ||
@@ -12,2 +19,3 @@ import type { RawRumViewEvent } from '../../../rawRumEvent.types' | ||
import type { FeatureFlagContexts } from '../../contexts/featureFlagContext' | ||
import type { PageStateHistory } from '../../contexts/pageStateHistory' | ||
import type { ViewEvent, ViewOptions } from './trackViews' | ||
@@ -24,2 +32,3 @@ import { trackViews } from './trackViews' | ||
featureFlagContexts: FeatureFlagContexts, | ||
pageStateHistory: PageStateHistory, | ||
recorderApi: RecorderApi, | ||
@@ -31,7 +40,6 @@ initialViewOptions?: ViewOptions | ||
LifeCycleEventType.RAW_RUM_EVENT_COLLECTED, | ||
processViewUpdate(view, foregroundContexts, featureFlagContexts, recorderApi) | ||
processViewUpdate(view, foregroundContexts, featureFlagContexts, recorderApi, pageStateHistory) | ||
) | ||
) | ||
return trackViews( | ||
const trackViewResult = trackViews( | ||
location, | ||
@@ -45,2 +53,4 @@ lifeCycle, | ||
) | ||
return trackViewResult | ||
} | ||
@@ -52,7 +62,8 @@ | ||
featureFlagContexts: FeatureFlagContexts, | ||
recorderApi: RecorderApi | ||
recorderApi: RecorderApi, | ||
pageStateHistory: PageStateHistory | ||
): RawRumEventCollectedData<RawRumViewEvent> { | ||
const replayStats = recorderApi.getReplayStats(view.id) | ||
const featureFlagContext = featureFlagContexts.findFeatureFlagEvaluations(view.startClocks.relative) | ||
const pageStatesEnabled = isExperimentalFeatureEnabled(ExperimentalFeature.PAGE_STATES) | ||
const viewEvent: RawRumViewEvent = { | ||
@@ -62,2 +73,3 @@ _dd: { | ||
replay_stats: replayStats, | ||
page_states: pageStatesEnabled ? pageStateHistory.findAll(view.startClocks.relative, view.duration) : undefined, | ||
}, | ||
@@ -97,3 +109,5 @@ date: view.startClocks.timeStamp, | ||
time_spent: toServerDuration(view.duration), | ||
in_foreground_periods: foregroundContexts.selectInForegroundPeriodsFor(view.startClocks.relative, view.duration), | ||
in_foreground_periods: !pageStatesEnabled | ||
? foregroundContexts.selectInForegroundPeriodsFor(view.startClocks.relative, view.duration) | ||
: undefined, | ||
}, | ||
@@ -100,0 +114,0 @@ feature_flags: featureFlagContext && !isEmptyObject(featureFlagContext) ? featureFlagContext : undefined, |
@@ -38,4 +38,7 @@ import type { RelativeTime } from '@datadog/browser-core' | ||
export function startRumSessionManager(configuration: RumConfiguration, lifeCycle: LifeCycle): RumSessionManager { | ||
const sessionManager = startSessionManager(configuration.cookieOptions, RUM_SESSION_KEY, (rawTrackingType) => | ||
computeSessionState(configuration, rawTrackingType) | ||
const sessionManager = startSessionManager( | ||
// TODO - Improve configuration type and remove assertion | ||
configuration.sessionStoreStrategyType!, | ||
RUM_SESSION_KEY, | ||
(rawTrackingType) => computeSessionState(configuration, rawTrackingType) | ||
) | ||
@@ -42,0 +45,0 @@ |
@@ -11,3 +11,3 @@ import type { | ||
} from '@datadog/browser-core' | ||
import type { PageStateEntry } from './domain/contexts/pageStateHistory' | ||
import type { PageState } from './domain/contexts/pageStateHistory' | ||
import type { RumSessionPlan } from './domain/rumSessionManager' | ||
@@ -29,3 +29,3 @@ | ||
id: string | ||
duration: ServerDuration | ||
duration?: ServerDuration | ||
url: string | ||
@@ -47,3 +47,3 @@ method?: string | ||
discarded: boolean | ||
page_states?: PageStateEntry[] | ||
page_states?: PageStateServerEntry[] | ||
} | ||
@@ -116,2 +116,3 @@ } | ||
replay_stats?: ReplayStats | ||
page_states?: PageStateServerEntry[] | ||
} | ||
@@ -125,2 +126,4 @@ } | ||
export type PageStateServerEntry = { state: PageState; start: ServerDuration } | ||
export const enum ViewLoadingType { | ||
@@ -127,0 +130,0 @@ INITIAL_LOAD = 'initial_load', |
@@ -383,3 +383,3 @@ /* eslint-disable */ | ||
*/ | ||
readonly duration: number | ||
readonly duration?: number | ||
/** | ||
@@ -756,2 +756,6 @@ * Size in octet of the resource response body | ||
readonly is_active?: boolean | ||
/** | ||
* Whether this session has been sampled for replay | ||
*/ | ||
readonly sampled_for_replay?: boolean | ||
[k: string]: unknown | ||
@@ -773,2 +777,16 @@ } | ||
readonly document_version: number | ||
/** | ||
* List of the page states during the view | ||
*/ | ||
readonly page_states?: { | ||
/** | ||
* Page state name | ||
*/ | ||
readonly state: 'active' | 'passive' | 'hidden' | 'frozen' | 'terminated' | ||
/** | ||
* Duration in ns between start of the view and start of the page state | ||
*/ | ||
readonly start: number | ||
[k: string]: unknown | ||
}[] | ||
[k: string]: unknown | ||
@@ -1010,5 +1028,5 @@ } | ||
/** | ||
* Session plan: 1 is the plan without replay, 2 is the plan with replay | ||
* Session plan: 1 is the plan without replay, 2 is the plan with replay (deprecated) | ||
*/ | ||
plan: 1 | 2 | ||
plan?: 1 | 2 | ||
[k: string]: unknown | ||
@@ -1015,0 +1033,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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1093201
20189
+ Added@datadog/browser-core@4.43.0(transitive)
- Removed@datadog/browser-core@4.42.2(transitive)
Updated@datadog/browser-core@4.43.0