diff --git a/packages/core/src/browser/pageExitObservable.spec.ts b/packages/core/src/browser/pageExitObservable.spec.ts index ec6274e686..31924d6abd 100644 --- a/packages/core/src/browser/pageExitObservable.spec.ts +++ b/packages/core/src/browser/pageExitObservable.spec.ts @@ -1,5 +1,5 @@ import { createNewEvent, restorePageVisibility, setPageVisibility } from '../../test/specHelper' -import { resetExperimentalFeatures, updateExperimentalFeatures } from '../domain/configuration' +import { resetExperimentalFeatures, updateExperimentalFeatures, ExperimentalFeature } from '../domain/configuration' import type { Subscription } from '../tools/observable' import type { PageExitEvent } from './pageExitObservable' import { PageExitReason, createPageExitObservable } from './pageExitObservable' @@ -20,7 +20,7 @@ describe('createPageExitObservable', () => { }) it('notifies when the page fires pagehide if ff pagehide is enabled', () => { - updateExperimentalFeatures(['pagehide']) + updateExperimentalFeatures([ExperimentalFeature.PAGEHIDE]) onExitSpy = jasmine.createSpy() pageExitSubscription = createPageExitObservable().subscribe(onExitSpy) diff --git a/packages/core/src/browser/pageExitObservable.ts b/packages/core/src/browser/pageExitObservable.ts index 7182f5583f..f290dbce16 100644 --- a/packages/core/src/browser/pageExitObservable.ts +++ b/packages/core/src/browser/pageExitObservable.ts @@ -1,4 +1,4 @@ -import { isExperimentalFeatureEnabled } from '../domain/configuration' +import { isExperimentalFeatureEnabled, ExperimentalFeature } from '../domain/configuration' import { Observable } from '../tools/observable' import { includes, noop, objectValues } from '../tools/utils' import { addEventListeners, addEventListener, DOM_EVENT } from './addEventListener' @@ -18,7 +18,7 @@ export interface PageExitEvent { export function createPageExitObservable(): Observable { const observable = new Observable(() => { - const pagehideEnabled = isExperimentalFeatureEnabled('pagehide') + const pagehideEnabled = isExperimentalFeatureEnabled(ExperimentalFeature.PAGEHIDE) const { stop: stopListeners } = addEventListeners( window, [DOM_EVENT.VISIBILITY_CHANGE, DOM_EVENT.FREEZE, DOM_EVENT.PAGE_HIDE], diff --git a/packages/core/src/domain/configuration/configuration.spec.ts b/packages/core/src/domain/configuration/configuration.spec.ts index 4619d4df18..6c9a2cdd0d 100644 --- a/packages/core/src/domain/configuration/configuration.spec.ts +++ b/packages/core/src/domain/configuration/configuration.spec.ts @@ -2,7 +2,7 @@ import type { RumEvent } from '../../../../rum-core/src' import { display } from '../../tools/display' import type { InitConfiguration } from './configuration' import { validateAndBuildConfiguration } from './configuration' -import { isExperimentalFeatureEnabled, updateExperimentalFeatures } from './experimentalFeatures' +import { ExperimentalFeature, isExperimentalFeatureEnabled, updateExperimentalFeatures } from './experimentalFeatures' describe('validateAndBuildConfiguration', () => { const clientToken = 'some_client_token' @@ -11,9 +11,21 @@ describe('validateAndBuildConfiguration', () => { updateExperimentalFeatures([]) }) - it('updates experimental feature flags', () => { - validateAndBuildConfiguration({ clientToken, enableExperimentalFeatures: ['foo'] }) - expect(isExperimentalFeatureEnabled('foo')).toBeTrue() + describe('experimentalFeatures', () => { + const TEST_FEATURE_FLAG = 'foo' as ExperimentalFeature + + beforeEach(() => { + ;(ExperimentalFeature as any).FOO = TEST_FEATURE_FLAG + }) + + afterEach(() => { + delete (ExperimentalFeature as any).FOO + }) + + it('updates experimental feature flags', () => { + validateAndBuildConfiguration({ clientToken, enableExperimentalFeatures: ['foo'] }) + expect(isExperimentalFeatureEnabled(TEST_FEATURE_FLAG)).toBeTrue() + }) }) describe('validate init configuration', () => { diff --git a/packages/core/src/domain/configuration/configuration.ts b/packages/core/src/domain/configuration/configuration.ts index 7e124435ab..ea15379f15 100644 --- a/packages/core/src/domain/configuration/configuration.ts +++ b/packages/core/src/domain/configuration/configuration.ts @@ -2,9 +2,9 @@ import type { CookieOptions } from '../../browser/cookie' import { getCurrentSite } from '../../browser/cookie' import { catchUserErrors } from '../../tools/catchUserErrors' import { display } from '../../tools/display' -import { assign, isPercentage, ONE_KIBI_BYTE, ONE_SECOND } from '../../tools/utils' +import { assign, isPercentage, objectHasValue, ONE_KIBI_BYTE, ONE_SECOND } from '../../tools/utils' import type { RawTelemetryConfiguration } from '../telemetry' -import { updateExperimentalFeatures } from './experimentalFeatures' +import { ExperimentalFeature, updateExperimentalFeatures } from './experimentalFeatures' import type { TransportConfiguration } from './transportConfiguration' import { computeTransportConfiguration } from './transportConfiguration' @@ -112,7 +112,13 @@ export function validateAndBuildConfiguration(initConfiguration: InitConfigurati } // Set the experimental feature flags as early as possible, so we can use them in most places - updateExperimentalFeatures(initConfiguration.enableExperimentalFeatures) + if (Array.isArray(initConfiguration.enableExperimentalFeatures)) { + updateExperimentalFeatures( + initConfiguration.enableExperimentalFeatures.filter((flag): flag is ExperimentalFeature => + objectHasValue(ExperimentalFeature, flag) + ) + ) + } return assign( { diff --git a/packages/core/src/domain/configuration/endpointBuilder.spec.ts b/packages/core/src/domain/configuration/endpointBuilder.spec.ts index f70cc6b78f..f62649a644 100644 --- a/packages/core/src/domain/configuration/endpointBuilder.spec.ts +++ b/packages/core/src/domain/configuration/endpointBuilder.spec.ts @@ -2,7 +2,7 @@ import type { BuildEnvWindow } from '../../../test/specHelper' import { startsWith } from '../../tools/utils' import type { InitConfiguration } from './configuration' import { createEndpointBuilder } from './endpointBuilder' -import { resetExperimentalFeatures, updateExperimentalFeatures } from './experimentalFeatures' +import { ExperimentalFeature, resetExperimentalFeatures, updateExperimentalFeatures } from './experimentalFeatures' describe('endpointBuilder', () => { const clientToken = 'some_client_token' @@ -126,7 +126,7 @@ describe('endpointBuilder', () => { }) it('should contain flush reason when ff collect_flush_reason is enabled', () => { - updateExperimentalFeatures(['collect_flush_reason']) + updateExperimentalFeatures([ExperimentalFeature.COLLECT_FLUSH_REASON]) expect(createEndpointBuilder(initConfiguration, 'rum', []).build('xhr', 'batch_bytes_limit')).toContain( 'flush_reason%3Abatch_bytes_limit' ) diff --git a/packages/core/src/domain/configuration/endpointBuilder.ts b/packages/core/src/domain/configuration/endpointBuilder.ts index c0328fec81..34208c79b7 100644 --- a/packages/core/src/domain/configuration/endpointBuilder.ts +++ b/packages/core/src/domain/configuration/endpointBuilder.ts @@ -4,7 +4,7 @@ import { normalizeUrl } from '../../tools/urlPolyfill' import { generateUUID } from '../../tools/utils' import type { InitConfiguration } from './configuration' import { INTAKE_SITE_AP1, INTAKE_SITE_US1 } from './intakeSites' -import { isExperimentalFeatureEnabled } from './experimentalFeatures' +import { ExperimentalFeature, isExperimentalFeatureEnabled } from './experimentalFeatures' // replaced at build time declare const __BUILD_ENV__SDK_VERSION__: string @@ -104,7 +104,7 @@ function buildEndpointParameters( retry: RetryInfo | undefined ) { const tags = [`sdk_version:${__BUILD_ENV__SDK_VERSION__}`, `api:${api}`].concat(configurationTags) - if (flushReason && isExperimentalFeatureEnabled('collect_flush_reason')) { + if (flushReason && isExperimentalFeatureEnabled(ExperimentalFeature.COLLECT_FLUSH_REASON)) { tags.push(`flush_reason:${flushReason}`) } if (retry) { diff --git a/packages/core/src/domain/configuration/experimentalFeatures.spec.ts b/packages/core/src/domain/configuration/experimentalFeatures.spec.ts index 20c99e9cbf..a06c5d7c5b 100644 --- a/packages/core/src/domain/configuration/experimentalFeatures.spec.ts +++ b/packages/core/src/domain/configuration/experimentalFeatures.spec.ts @@ -1,39 +1,43 @@ +import type { ExperimentalFeature } from './experimentalFeatures' import { updateExperimentalFeatures, isExperimentalFeatureEnabled, resetExperimentalFeatures, } from './experimentalFeatures' +const TEST_FEATURE_FLAG_ONE = 'foo' as ExperimentalFeature +const TEST_FEATURE_FLAG_TWO = 'bar' as ExperimentalFeature + describe('experimentalFeatures', () => { afterEach(() => { resetExperimentalFeatures() }) it('initial state is empty', () => { - expect(isExperimentalFeatureEnabled('foo')).toBeFalse() - expect(isExperimentalFeatureEnabled('bar')).toBeFalse() + expect(isExperimentalFeatureEnabled(TEST_FEATURE_FLAG_ONE)).toBeFalse() + expect(isExperimentalFeatureEnabled(TEST_FEATURE_FLAG_TWO)).toBeFalse() }) it('should define enabled experimental features', () => { - updateExperimentalFeatures(['foo']) - expect(isExperimentalFeatureEnabled('foo')).toBeTrue() - expect(isExperimentalFeatureEnabled('bar')).toBeFalse() + updateExperimentalFeatures([TEST_FEATURE_FLAG_ONE]) + expect(isExperimentalFeatureEnabled(TEST_FEATURE_FLAG_ONE)).toBeTrue() + expect(isExperimentalFeatureEnabled(TEST_FEATURE_FLAG_TWO)).toBeFalse() }) it('should allow to be shared between products', () => { - updateExperimentalFeatures(['foo']) - updateExperimentalFeatures(['bar']) + updateExperimentalFeatures([TEST_FEATURE_FLAG_ONE]) + updateExperimentalFeatures([TEST_FEATURE_FLAG_TWO]) - expect(isExperimentalFeatureEnabled('foo')).toBeTrue() - expect(isExperimentalFeatureEnabled('bar')).toBeTrue() + expect(isExperimentalFeatureEnabled(TEST_FEATURE_FLAG_ONE)).toBeTrue() + expect(isExperimentalFeatureEnabled(TEST_FEATURE_FLAG_TWO)).toBeTrue() }) it('should support some edge cases', () => { - updateExperimentalFeatures(['foo']) + updateExperimentalFeatures([TEST_FEATURE_FLAG_ONE]) updateExperimentalFeatures(undefined) updateExperimentalFeatures([]) updateExperimentalFeatures([11 as any]) - expect(isExperimentalFeatureEnabled('foo')).toBeTrue() + expect(isExperimentalFeatureEnabled(TEST_FEATURE_FLAG_ONE)).toBeTrue() }) }) diff --git a/packages/core/src/domain/configuration/experimentalFeatures.ts b/packages/core/src/domain/configuration/experimentalFeatures.ts index 0657cd2d3f..be083ea1b6 100644 --- a/packages/core/src/domain/configuration/experimentalFeatures.ts +++ b/packages/core/src/domain/configuration/experimentalFeatures.ts @@ -3,13 +3,27 @@ * For NPM setup, this feature flag singleton is shared between RUM and Logs product. * This means that an experimental flag set on the RUM product will be set on the Logs product. * So keep in mind that in certain configurations, your experimental feature flag may affect other products. + * + * FORMAT: + * All feature flags should be snake_cased */ -import { includes } from '../../tools/utils' -import { display } from '../../tools/display' +// We want to use a real enum (i.e. not a const enum) here, to be able to check whether an arbitrary +// string is an expected feature flag +// eslint-disable-next-line no-restricted-syntax +export enum ExperimentalFeature { + PAGEHIDE = 'pagehide', + SHADOW_DOM_DEBUG = 'shadow_dom_debug', + FEATURE_FLAGS = 'feature_flags', + RESOURCE_DURATIONS = 'resource_durations', + RESOURCE_PAGE_STATES = 'resource_page_states', + CLICKMAP = 'clickmap', + COLLECT_FLUSH_REASON = 'collect_flush_reason', + SANITIZE_INPUTS = 'sanitize_inputs', +} -let enabledExperimentalFeatures: Set | undefined +let enabledExperimentalFeatures: Set | undefined -export function updateExperimentalFeatures(enabledFeatures: string[] | undefined): void { +export function updateExperimentalFeatures(enabledFeatures: ExperimentalFeature[] | undefined): void { // Safely handle external data if (!Array.isArray(enabledFeatures)) { return @@ -19,17 +33,12 @@ export function updateExperimentalFeatures(enabledFeatures: string[] | undefined enabledExperimentalFeatures = new Set(enabledFeatures) } - enabledFeatures - .filter((flag) => typeof flag === 'string') - .forEach((flag: string) => { - if (includes(flag, '-')) { - display.warn(`please use snake case for '${flag}'`) - } - enabledExperimentalFeatures!.add(flag) - }) + enabledFeatures.forEach((flag) => { + enabledExperimentalFeatures!.add(flag) + }) } -export function isExperimentalFeatureEnabled(featureName: string): boolean { +export function isExperimentalFeatureEnabled(featureName: ExperimentalFeature): boolean { return !!enabledExperimentalFeatures && enabledExperimentalFeatures.has(featureName) } diff --git a/packages/core/src/domain/configuration/index.ts b/packages/core/src/domain/configuration/index.ts index c11987ca24..533a42b861 100644 --- a/packages/core/src/domain/configuration/index.ts +++ b/packages/core/src/domain/configuration/index.ts @@ -12,5 +12,6 @@ export { updateExperimentalFeatures, resetExperimentalFeatures, getExperimentalFeatures, + ExperimentalFeature, } from './experimentalFeatures' export * from './intakeSites' diff --git a/packages/core/src/domain/console/consoleObservable.ts b/packages/core/src/domain/console/consoleObservable.ts index 3a20794256..bff1de2e7d 100644 --- a/packages/core/src/domain/console/consoleObservable.ts +++ b/packages/core/src/domain/console/consoleObservable.ts @@ -5,7 +5,7 @@ import { find, jsonStringify } from '../../tools/utils' import { ConsoleApiName } from '../../tools/display' import { callMonitored } from '../../tools/monitor' import { sanitize } from '../../tools/sanitize' -import { isExperimentalFeatureEnabled } from '../configuration' +import { ExperimentalFeature, isExperimentalFeatureEnabled } from '../configuration' export interface ConsoleLog { message: string @@ -70,10 +70,14 @@ function buildConsoleLog(params: unknown[], api: ConsoleApiName, handlingStack: function formatConsoleParameters(param: unknown) { if (typeof param === 'string') { - return isExperimentalFeatureEnabled('sanitize_inputs') ? sanitize(param) : param + return isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) ? sanitize(param) : param } if (param instanceof Error) { return formatErrorMessage(computeStackTrace(param)) } - return jsonStringify(isExperimentalFeatureEnabled('sanitize_inputs') ? sanitize(param) : param, undefined, 2) + return jsonStringify( + isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) ? sanitize(param) : param, + undefined, + 2 + ) } diff --git a/packages/core/src/domain/telemetry/telemetry.spec.ts b/packages/core/src/domain/telemetry/telemetry.spec.ts index 0dfc1a76cd..f673ed4397 100644 --- a/packages/core/src/domain/telemetry/telemetry.spec.ts +++ b/packages/core/src/domain/telemetry/telemetry.spec.ts @@ -1,6 +1,6 @@ import type { StackTrace } from '@datadog/browser-core' import { callMonitored } from '../../tools/monitor' -import type { Configuration } from '../configuration' +import type { Configuration, ExperimentalFeature } from '../configuration' import { resetExperimentalFeatures, updateExperimentalFeatures, @@ -74,7 +74,7 @@ describe('telemetry', () => { }) it('should contains feature flags', () => { - updateExperimentalFeatures(['foo']) + updateExperimentalFeatures(['foo' as ExperimentalFeature]) const { notifySpy } = startAndSpyTelemetry() callMonitored(() => { throw new Error('message') diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c32a1d9dbe..ed3e1444c0 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -6,6 +6,7 @@ export { DefaultPrivacyLevel, EndpointBuilder, isExperimentalFeatureEnabled, + ExperimentalFeature, updateExperimentalFeatures, resetExperimentalFeatures, serializeConfiguration, diff --git a/packages/core/src/tools/contextManager.ts b/packages/core/src/tools/contextManager.ts index 907977821a..ee2715d144 100644 --- a/packages/core/src/tools/contextManager.ts +++ b/packages/core/src/tools/contextManager.ts @@ -1,4 +1,4 @@ -import { isExperimentalFeatureEnabled } from '../domain/configuration' +import { ExperimentalFeature, isExperimentalFeatureEnabled } from '../domain/configuration' import { computeBytesCount, deepClone, jsonStringify } from './utils' import type { Context, ContextValue } from './context' import { sanitize } from './sanitize' @@ -40,12 +40,16 @@ export function createContextManager(computeBytesCountImpl = computeBytesCount) getContext: () => deepClone(context), setContext: (newContext: Context) => { - context = isExperimentalFeatureEnabled('sanitize_inputs') ? sanitize(newContext) : deepClone(newContext) + context = isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) + ? sanitize(newContext) + : deepClone(newContext) bytesCountCache = undefined }, setContextProperty: (key: string, property: any) => { - context[key] = isExperimentalFeatureEnabled('sanitize_inputs') ? sanitize(property) : deepClone(property) + context[key] = isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) + ? sanitize(property) + : deepClone(property) bytesCountCache = undefined }, diff --git a/packages/core/src/tools/error.ts b/packages/core/src/tools/error.ts index 54c276d524..942bfc67cb 100644 --- a/packages/core/src/tools/error.ts +++ b/packages/core/src/tools/error.ts @@ -1,4 +1,4 @@ -import { isExperimentalFeatureEnabled } from '../domain/configuration' +import { ExperimentalFeature, isExperimentalFeatureEnabled } from '../domain/configuration' import type { StackTrace } from '../domain/tracekit' import { computeStackTrace } from '../domain/tracekit' import { callMonitored } from './monitor' @@ -70,7 +70,9 @@ export function computeRawError({ handling, }: RawErrorParams): RawError { if (!stackTrace || (stackTrace.message === undefined && !(originalError instanceof Error))) { - const sanitizedError = isExperimentalFeatureEnabled('sanitize_inputs') ? sanitize(originalError) : originalError + const sanitizedError = isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) + ? sanitize(originalError) + : originalError return { startClocks, source, diff --git a/packages/core/src/tools/limitModification.spec.ts b/packages/core/src/tools/limitModification.spec.ts index d01e30b527..9e6bf8b591 100644 --- a/packages/core/src/tools/limitModification.spec.ts +++ b/packages/core/src/tools/limitModification.spec.ts @@ -1,4 +1,4 @@ -import { resetExperimentalFeatures, updateExperimentalFeatures } from '../domain/configuration' +import { ExperimentalFeature, resetExperimentalFeatures, updateExperimentalFeatures } from '../domain/configuration' import type { Context } from './context' import { limitModification } from './limitModification' @@ -158,7 +158,7 @@ describe('limitModification', () => { }) it('should call sanitize on newly provided values', () => { - updateExperimentalFeatures(['sanitize_inputs']) + updateExperimentalFeatures([ExperimentalFeature.SANITIZE_INPUTS]) const object: Context = { bar: { baz: 42 } } const modifier = (candidate: any) => { diff --git a/packages/core/src/tools/limitModification.ts b/packages/core/src/tools/limitModification.ts index b16a4129de..6d755551e4 100644 --- a/packages/core/src/tools/limitModification.ts +++ b/packages/core/src/tools/limitModification.ts @@ -1,4 +1,4 @@ -import { isExperimentalFeatureEnabled } from '../domain/configuration' +import { ExperimentalFeature, isExperimentalFeatureEnabled } from '../domain/configuration' import type { Context } from './context' import { sanitize } from './sanitize' import { deepClone, getType } from './utils' @@ -20,7 +20,11 @@ export function limitModification( const originalType = getType(originalValue) const newType = getType(newValue) if (newType === originalType) { - set(object, path, isExperimentalFeatureEnabled('sanitize_inputs') ? sanitize(newValue) : newValue) + set( + object, + path, + isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) ? sanitize(newValue) : newValue + ) } else if (originalType === 'object' && (newType === 'undefined' || newType === 'null')) { set(object, path, {}) } diff --git a/packages/logs/src/boot/logsPublicApi.ts b/packages/logs/src/boot/logsPublicApi.ts index 315b491b87..010b5179cb 100644 --- a/packages/logs/src/boot/logsPublicApi.ts +++ b/packages/logs/src/boot/logsPublicApi.ts @@ -1,5 +1,6 @@ import type { Context, InitConfiguration, User } from '@datadog/browser-core' import { + ExperimentalFeature, isExperimentalFeatureEnabled, assign, BoundedBuffer, @@ -120,10 +121,12 @@ export function makeLogsPublicApi(startLogsImpl: StartLogs) { createLogger: monitor((name: string, conf: LoggerConfiguration = {}) => { customLoggers[name] = new Logger( (...params) => handleLogStrategy(...params), - isExperimentalFeatureEnabled('sanitize_inputs') ? sanitize(name) : name, + isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) ? sanitize(name) : name, conf.handler, conf.level, - isExperimentalFeatureEnabled('sanitize_inputs') ? (sanitize(conf.context) as object) : conf.context + isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) + ? (sanitize(conf.context) as object) + : conf.context ) return customLoggers[name]! diff --git a/packages/logs/src/domain/logger.ts b/packages/logs/src/domain/logger.ts index d4cc43fbaa..9d70dbee1c 100644 --- a/packages/logs/src/domain/logger.ts +++ b/packages/logs/src/domain/logger.ts @@ -1,5 +1,6 @@ import type { Context } from '@datadog/browser-core' import { + ExperimentalFeature, isExperimentalFeatureEnabled, clocksNow, computeRawError, @@ -82,7 +83,9 @@ export class Logger { } const sanitizedMessageContext = ( - isExperimentalFeatureEnabled('sanitize_inputs') ? sanitize(messageContext) : deepClone(messageContext) + isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) + ? sanitize(messageContext) + : deepClone(messageContext) ) as Context const context = errorContext @@ -90,7 +93,11 @@ export class Logger { : sanitizedMessageContext this.handleLogStrategy( - { message: isExperimentalFeatureEnabled('sanitize_inputs') ? sanitize(message)! : message, context, status }, + { + message: isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) ? sanitize(message)! : message, + context, + status, + }, this ) } diff --git a/packages/rum-core/src/boot/rumPublicApi.ts b/packages/rum-core/src/boot/rumPublicApi.ts index 7801ece31e..a132ec5371 100644 --- a/packages/rum-core/src/boot/rumPublicApi.ts +++ b/packages/rum-core/src/boot/rumPublicApi.ts @@ -1,5 +1,6 @@ import type { Context, InitConfiguration, TimeStamp, RelativeTime, User } from '@datadog/browser-core' import { + ExperimentalFeature, noop, isExperimentalFeatureEnabled, willSyntheticsInjectRum, @@ -204,8 +205,10 @@ export function makeRumPublicApi( addAction: monitor((name: string, context?: object) => { addActionStrategy({ - name: isExperimentalFeatureEnabled('sanitize_inputs') ? sanitize(name)! : name, - context: (isExperimentalFeatureEnabled('sanitize_inputs') ? sanitize(context) : deepClone(context)) as Context, + name: isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) ? sanitize(name)! : name, + context: (isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) + ? sanitize(context) + : deepClone(context)) as Context, startClocks: clocksNow(), type: ActionType.CUSTOM, }) @@ -217,7 +220,7 @@ export function makeRumPublicApi( addErrorStrategy({ error, // Do not sanitize error here, it is needed unserialized by computeRawError() handlingStack, - context: (isExperimentalFeatureEnabled('sanitize_inputs') + context: (isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) ? sanitize(context) : deepClone(context)) as Context, startClocks: clocksNow(), @@ -227,7 +230,7 @@ export function makeRumPublicApi( addTiming: monitor((name: string, time?: number) => { addTimingStrategy( - isExperimentalFeatureEnabled('sanitize_inputs') ? sanitize(name)! : name, + isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) ? sanitize(name)! : name, time as RelativeTime | TimeStamp | undefined ) }), @@ -265,8 +268,8 @@ export function makeRumPublicApi( */ addFeatureFlagEvaluation: monitor((key: string, value: any) => { addFeatureFlagEvaluationStrategy( - isExperimentalFeatureEnabled('sanitize_inputs') ? sanitize(key)! : key, - isExperimentalFeatureEnabled('sanitize_inputs') ? sanitize(value) : value + isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) ? sanitize(key)! : key, + isExperimentalFeatureEnabled(ExperimentalFeature.SANITIZE_INPUTS) ? sanitize(value) : value ) }), }) diff --git a/packages/rum-core/src/domain/contexts/displayContext.spec.ts b/packages/rum-core/src/domain/contexts/displayContext.spec.ts index 0333a776b5..37ebf945b5 100644 --- a/packages/rum-core/src/domain/contexts/displayContext.spec.ts +++ b/packages/rum-core/src/domain/contexts/displayContext.spec.ts @@ -1,4 +1,4 @@ -import { resetExperimentalFeatures, updateExperimentalFeatures } from '@datadog/browser-core' +import { ExperimentalFeature, resetExperimentalFeatures, updateExperimentalFeatures } from '@datadog/browser-core' import { getDisplayContext, resetDisplayContext } from './displayContext' describe('displayContext', () => { @@ -8,7 +8,7 @@ describe('displayContext', () => { }) it('should return current display context when ff enabled', () => { - updateExperimentalFeatures(['clickmap']) + updateExperimentalFeatures([ExperimentalFeature.CLICKMAP]) expect(getDisplayContext()).toEqual({ viewport: { diff --git a/packages/rum-core/src/domain/contexts/displayContext.ts b/packages/rum-core/src/domain/contexts/displayContext.ts index 04737328b3..b5f4a5c9e5 100644 --- a/packages/rum-core/src/domain/contexts/displayContext.ts +++ b/packages/rum-core/src/domain/contexts/displayContext.ts @@ -1,11 +1,11 @@ -import { isExperimentalFeatureEnabled } from '@datadog/browser-core' +import { ExperimentalFeature, isExperimentalFeatureEnabled } from '@datadog/browser-core' import { getViewportDimension, initViewportObservable } from '../../browser/viewportObservable' let viewport: { width: number; height: number } | undefined let stopListeners: (() => void) | undefined export function getDisplayContext() { - if (!isExperimentalFeatureEnabled('clickmap')) { + if (!isExperimentalFeatureEnabled(ExperimentalFeature.CLICKMAP)) { return } diff --git a/packages/rum-core/src/domain/contexts/featureFlagContext.spec.ts b/packages/rum-core/src/domain/contexts/featureFlagContext.spec.ts index 1f20b5816e..f75ffd6793 100644 --- a/packages/rum-core/src/domain/contexts/featureFlagContext.spec.ts +++ b/packages/rum-core/src/domain/contexts/featureFlagContext.spec.ts @@ -1,5 +1,10 @@ import type { RelativeTime } from '@datadog/browser-core' -import { resetExperimentalFeatures, updateExperimentalFeatures, relativeToClocks } from '@datadog/browser-core' +import { + ExperimentalFeature, + resetExperimentalFeatures, + updateExperimentalFeatures, + relativeToClocks, +} from '@datadog/browser-core' import type { TestSetupBuilder } from '../../../test/testSetupBuilder' import { setup } from '../../../test/testSetupBuilder' import { LifeCycleEventType } from '../lifeCycle' @@ -32,7 +37,7 @@ describe('featureFlagContexts', () => { describe('addFeatureFlagEvaluation', () => { it('should add feature flag evaluations of any type when the ff feature_flags is enabled', () => { - updateExperimentalFeatures(['feature_flags']) + updateExperimentalFeatures([ExperimentalFeature.FEATURE_FLAGS]) const { lifeCycle } = setupBuilder.build() @@ -56,7 +61,7 @@ describe('featureFlagContexts', () => { }) it('should replace existing feature flag evaluation to the current context when the ff feature_flags is enabled', () => { - updateExperimentalFeatures(['feature_flags']) + updateExperimentalFeatures([ExperimentalFeature.FEATURE_FLAGS]) const { lifeCycle } = setupBuilder.build() @@ -94,7 +99,7 @@ describe('featureFlagContexts', () => { * (which seems unlikely) and this event would anyway be rejected by lack of view id */ it('should return undefined when no current view', () => { - updateExperimentalFeatures(['feature_flags']) + updateExperimentalFeatures([ExperimentalFeature.FEATURE_FLAGS]) setupBuilder.build() @@ -102,7 +107,7 @@ describe('featureFlagContexts', () => { }) it('should clear feature flag context on new view', () => { - updateExperimentalFeatures(['feature_flags']) + updateExperimentalFeatures([ExperimentalFeature.FEATURE_FLAGS]) const { lifeCycle } = setupBuilder.build() @@ -122,7 +127,7 @@ describe('featureFlagContexts', () => { }) it('should return the feature flag context corresponding to the start time', () => { - updateExperimentalFeatures(['feature_flags']) + updateExperimentalFeatures([ExperimentalFeature.FEATURE_FLAGS]) const { lifeCycle, clock } = setupBuilder.withFakeClock().build() @@ -149,7 +154,7 @@ describe('featureFlagContexts', () => { describe('getFeatureFlagBytesCount', () => { it('should compute the bytes count only if the context has been updated', () => { - updateExperimentalFeatures(['feature_flags']) + updateExperimentalFeatures([ExperimentalFeature.FEATURE_FLAGS]) const { lifeCycle } = setupBuilder.withFakeClock().build() lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { diff --git a/packages/rum-core/src/domain/contexts/featureFlagContext.ts b/packages/rum-core/src/domain/contexts/featureFlagContext.ts index 27cf8575fa..7fc0dd85ce 100644 --- a/packages/rum-core/src/domain/contexts/featureFlagContext.ts +++ b/packages/rum-core/src/domain/contexts/featureFlagContext.ts @@ -6,6 +6,7 @@ import { isExperimentalFeatureEnabled, SESSION_TIME_OUT_DELAY, ContextHistory, + ExperimentalFeature, } from '@datadog/browser-core' import type { LifeCycle } from '../lifeCycle' import { LifeCycleEventType } from '../lifeCycle' @@ -32,7 +33,7 @@ export function startFeatureFlagContexts( lifeCycle: LifeCycle, computeBytesCountImpl = computeBytesCount ): FeatureFlagContexts { - if (!isExperimentalFeatureEnabled('feature_flags')) { + if (!isExperimentalFeatureEnabled(ExperimentalFeature.FEATURE_FLAGS)) { return { findFeatureFlagEvaluations: () => undefined, getFeatureFlagBytesCount: () => 0, diff --git a/packages/rum-core/src/domain/rumEventsCollection/action/trackClickActions.spec.ts b/packages/rum-core/src/domain/rumEventsCollection/action/trackClickActions.spec.ts index a4b95742d2..0a645e0237 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/action/trackClickActions.spec.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/action/trackClickActions.spec.ts @@ -6,6 +6,7 @@ import { clocksNow, timeStampNow, relativeNow, + ExperimentalFeature, } from '@datadog/browser-core' import { createNewEvent } from '../../../../../core/test/specHelper' import type { TestSetupBuilder } from '../../../../test/testSetupBuilder' @@ -114,7 +115,7 @@ describe('trackClickActions', () => { describe('when clickmap ff is enabled', () => { beforeEach(() => { - updateExperimentalFeatures(['clickmap']) + updateExperimentalFeatures([ExperimentalFeature.CLICKMAP]) }) afterEach(() => { diff --git a/packages/rum-core/src/domain/rumEventsCollection/action/trackClickActions.ts b/packages/rum-core/src/domain/rumEventsCollection/action/trackClickActions.ts index 1c72a51deb..0fa45130d1 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/action/trackClickActions.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/action/trackClickActions.ts @@ -12,6 +12,7 @@ import { clocksNow, ONE_SECOND, elapsed, + ExperimentalFeature, } from '@datadog/browser-core' import type { FrustrationType } from '../../../rawRumEvent.types' import { ActionType } from '../../../rawRumEvent.types' @@ -242,7 +243,7 @@ function computeClickActionBase(event: MouseEventOnElement, actionNameAttribute? let target: ClickAction['target'] let position: ClickAction['position'] - if (isExperimentalFeatureEnabled('clickmap')) { + if (isExperimentalFeatureEnabled(ExperimentalFeature.CLICKMAP)) { const rect = event.target.getBoundingClientRect() target = { width: Math.round(rect.width), diff --git a/packages/rum-core/src/domain/rumEventsCollection/resource/resourceCollection.spec.ts b/packages/rum-core/src/domain/rumEventsCollection/resource/resourceCollection.spec.ts index df7cb3517a..ce015d15e1 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/resource/resourceCollection.spec.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/resource/resourceCollection.spec.ts @@ -6,6 +6,7 @@ import { isIE, RequestType, ResourceType, + ExperimentalFeature, } from '@datadog/browser-core' import type { RumFetchResourceEventDomainContext } from '../../../domainContext.types' import { createResourceEntry } from '../../../../test/fixtures' @@ -114,7 +115,7 @@ describe('resourceCollection', () => { }) it('should collect page states on resources when ff resource_page_states enabled', () => { - updateExperimentalFeatures(['resource_page_states']) + updateExperimentalFeatures([ExperimentalFeature.RESOURCE_PAGE_STATES]) const { lifeCycle, rawRumEvents } = setupBuilder.build() const mockPageStates = [{ state: PageState.ACTIVE, startTime: 0 as RelativeTime }] const mockXHR = createCompletedRequest() @@ -157,7 +158,7 @@ describe('resourceCollection', () => { }) it('should collect computed duration and performance entry duration when resource_durations ff is enabled', () => { - updateExperimentalFeatures(['resource_durations']) + updateExperimentalFeatures([ExperimentalFeature.RESOURCE_DURATIONS]) const match = createResourceEntry({ startTime: 200 as RelativeTime, duration: 300 as Duration }) spyOn(performance, 'getEntriesByName').and.returnValues([match] as unknown as PerformanceResourceTiming[]) diff --git a/packages/rum-core/src/domain/rumEventsCollection/resource/resourceCollection.ts b/packages/rum-core/src/domain/rumEventsCollection/resource/resourceCollection.ts index aade8b326f..abc66ecebb 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/resource/resourceCollection.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/resource/resourceCollection.ts @@ -8,6 +8,7 @@ import { assign, isNumber, isExperimentalFeatureEnabled, + ExperimentalFeature, } from '@datadog/browser-core' import type { ClocksState, ServerDuration, Duration } from '@datadog/browser-core' import type { RumConfiguration } from '../../configuration' @@ -196,7 +197,7 @@ function computeDurationOverrideInfo( computedDuration: ServerDuration, performanceEntryDuration: ServerDuration | undefined ) { - if (!isExperimentalFeatureEnabled('resource_durations')) { + if (!isExperimentalFeatureEnabled(ExperimentalFeature.RESOURCE_DURATIONS)) { return } @@ -231,7 +232,7 @@ function computeIndexingInfo(sessionManager: RumSessionManager, resourceStart: C } function computePageStateInfo(pageStateHistory: PageStateHistory, startClocks: ClocksState, duration: Duration) { - if (!isExperimentalFeatureEnabled('resource_page_states')) { + if (!isExperimentalFeatureEnabled(ExperimentalFeature.RESOURCE_PAGE_STATES)) { return } diff --git a/packages/rum/src/domain/record/shadowRootsController.ts b/packages/rum/src/domain/record/shadowRootsController.ts index 0688960369..c155e09965 100644 --- a/packages/rum/src/domain/record/shadowRootsController.ts +++ b/packages/rum/src/domain/record/shadowRootsController.ts @@ -1,4 +1,4 @@ -import { addTelemetryDebug, DOM_EVENT, isExperimentalFeatureEnabled } from '@datadog/browser-core' +import { addTelemetryDebug, DOM_EVENT, ExperimentalFeature, isExperimentalFeatureEnabled } from '@datadog/browser-core' import type { RumConfiguration } from '@datadog/browser-rum-core' import type { InputCallback, MutationCallBack } from './observers' import { initInputObserver, initMutationObserver } from './observers' @@ -58,7 +58,7 @@ export const initShadowRootsController = ( childrenLength: shadowRoot ? shadowRoot.childElementCount : '-1', controllerByShadowRootSize: controllerByShadowRoot.size, html: - shadowRoot && isExperimentalFeatureEnabled('shadow_dom_debug') + shadowRoot && isExperimentalFeatureEnabled(ExperimentalFeature.SHADOW_DOM_DEBUG) ? shadowRoot.innerHTML.substring(0, 2000) : undefined, })