diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b70b0a9e92..ed72b2873f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - CURRENT_STAGING: staging-16 + CURRENT_STAGING: staging-17 APP: 'browser-sdk' CURRENT_CI_IMAGE: 63 BUILD_STABLE_REGISTRY: '486234852809.dkr.ecr.us-east-1.amazonaws.com' @@ -140,7 +140,17 @@ build-and-lint: - yarn build - yarn lint - node scripts/check-packages.js - - node scripts/report-bundle-size/index.js + +test-performance: + extends: + - .base-configuration + - .test-allowed-branches + interruptible: true + script: + - yarn + - yarn build:bundle + - node ./scripts/deploy/deploy.js staging pull-request pull-request + - node scripts/performance/index.js build-bundle: extends: @@ -211,6 +221,15 @@ check-release: - BUILD_MODE=release yarn build - node scripts/release/check-release.js +check-schemas: + extends: + - .base-configuration + - .test-allowed-branches + interruptible: true + script: + - yarn + - node scripts/check-schemas.js + unit-bs: stage: browserstack extends: diff --git a/Dockerfile b/Dockerfile index e8634080b2..24d0f82629 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.1-buster-slim +FROM node:20.12.2-buster-slim ARG CHROME_PACKAGE_VERSION diff --git a/developer-extension/package.json b/developer-extension/package.json index 218114d2d1..7d50401d1e 100644 --- a/developer-extension/package.json +++ b/developer-extension/package.json @@ -7,23 +7,23 @@ "dev": "webpack --mode development --watch" }, "devDependencies": { - "@tabler/icons-react": "3.1.0", + "@tabler/icons-react": "3.2.0", "@types/chrome": "0.0.266", - "@types/react": "18.2.74", - "@types/react-dom": "18.2.24", + "@types/react": "18.2.79", + "@types/react-dom": "18.2.25", "@webextension-toolbox/webpack-webextension-plugin": "3.3.1", "copy-webpack-plugin": "12.0.2", - "css-loader": "7.1.0", + "css-loader": "6.11.0", "html-webpack-plugin": "5.6.0", - "style-loader": "3.3.4", + "style-loader": "4.0.0", "webpack": "5.91.0" }, "dependencies": { "@datadog/browser-core": "workspace:*", "@datadog/browser-logs": "workspace:*", "@datadog/browser-rum": "workspace:*", - "@mantine/core": "7.7.1", - "@mantine/hooks": "7.7.1", + "@mantine/core": "7.8.0", + "@mantine/hooks": "7.8.0", "clsx": "2.1.0", "react": "18.2.0", "react-dom": "18.2.0" diff --git a/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx b/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx index fac86ccf06..99daf31526 100644 --- a/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx +++ b/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx @@ -280,6 +280,13 @@ function TelemetryDescription({ event }: { event: TelemetryEvent }) { if (event.telemetry.type === 'configuration') { return Configuration } + if (event.telemetry.type === 'usage') { + return ( + <> + Usage of {event.telemetry.usage.feature} + + ) + } return <>{event.telemetry.message} } diff --git a/package.json b/package.json index dfc9e5190e..850b010cfd 100644 --- a/package.json +++ b/package.json @@ -35,14 +35,14 @@ "@types/cors": "2.8.17", "@types/express": "4.17.21", "@types/jasmine": "3.10.18", - "@typescript-eslint/eslint-plugin": "7.5.0", - "@typescript-eslint/parser": "7.5.0", - "@wdio/browserstack-service": "8.35.1", - "@wdio/cli": "8.35.1", - "@wdio/jasmine-framework": "8.35.1", - "@wdio/junit-reporter": "8.32.4", - "@wdio/local-runner": "8.35.1", - "@wdio/spec-reporter": "8.32.4", + "@typescript-eslint/eslint-plugin": "7.7.0", + "@typescript-eslint/parser": "7.7.0", + "@wdio/browserstack-service": "8.36.0", + "@wdio/cli": "8.36.0", + "@wdio/jasmine-framework": "8.36.0", + "@wdio/junit-reporter": "8.36.0", + "@wdio/local-runner": "8.36.0", + "@wdio/spec-reporter": "8.36.0", "ajv": "6.12.6", "browserstack-local": "1.5.5", "chrome-webstore-upload": "3.0.3", @@ -79,8 +79,8 @@ "ts-loader": "9.5.1", "ts-node": "10.9.2", "tsconfig-paths-webpack-plugin": "4.1.0", - "typescript": "5.4.4", - "webdriverio": "8.35.1", + "typescript": "5.4.5", + "webdriverio": "8.36.0", "webpack": "5.91.0", "webpack-cli": "5.1.4", "webpack-dev-middleware": "7.2.1" @@ -90,7 +90,7 @@ "@mantine/core/type-fest": "4.15.0" }, "volta": { - "node": "20.12.1", + "node": "20.12.2", "yarn": "1.22.22" }, "packageManager": "yarn@3.8.1" diff --git a/packages/core/src/domain/configuration/configuration.ts b/packages/core/src/domain/configuration/configuration.ts index 839cc4fecc..895ba7e337 100644 --- a/packages/core/src/domain/configuration/configuration.ts +++ b/packages/core/src/domain/configuration/configuration.ts @@ -66,6 +66,7 @@ export interface InitConfiguration { internalAnalyticsSubdomain?: string telemetryConfigurationSampleRate?: number + telemetryUsageSampleRate?: number } // This type is only used to build the core configuration. Logs and RUM SDKs are using a proper type @@ -90,6 +91,7 @@ export interface Configuration extends TransportConfiguration { sessionSampleRate: number telemetrySampleRate: number telemetryConfigurationSampleRate: number + telemetryUsageSampleRate: number service: string | undefined silentMultipleInit: boolean allowUntrustedEvents: boolean @@ -130,6 +132,14 @@ export function validateAndBuildConfiguration(initConfiguration: InitConfigurati return } + if ( + initConfiguration.telemetryUsageSampleRate !== undefined && + !isPercentage(initConfiguration.telemetryUsageSampleRate) + ) { + display.error('Telemetry Usage Sample Rate should be a number between 0 and 100') + return + } + if ( initConfiguration.trackingConsent !== undefined && !objectHasValue(TrackingConsent, initConfiguration.trackingConsent) @@ -155,6 +165,7 @@ export function validateAndBuildConfiguration(initConfiguration: InitConfigurati sessionSampleRate: initConfiguration.sessionSampleRate ?? 100, telemetrySampleRate: initConfiguration.telemetrySampleRate ?? 20, telemetryConfigurationSampleRate: initConfiguration.telemetryConfigurationSampleRate ?? 5, + telemetryUsageSampleRate: initConfiguration.telemetryUsageSampleRate ?? 5, service: initConfiguration.service, silentMultipleInit: !!initConfiguration.silentMultipleInit, allowUntrustedEvents: !!initConfiguration.allowUntrustedEvents, @@ -190,6 +201,7 @@ export function serializeConfiguration(initConfiguration: InitConfiguration) { session_sample_rate: initConfiguration.sessionSampleRate, telemetry_sample_rate: initConfiguration.telemetrySampleRate, telemetry_configuration_sample_rate: initConfiguration.telemetryConfigurationSampleRate, + telemetry_usage_sample_rate: initConfiguration.telemetryUsageSampleRate, use_before_send: !!initConfiguration.beforeSend, use_cross_site_session_cookie: initConfiguration.useCrossSiteSessionCookie, use_partitioned_cross_site_session_cookie: initConfiguration.usePartitionedCrossSiteSessionCookie, diff --git a/packages/core/src/domain/telemetry/index.ts b/packages/core/src/domain/telemetry/index.ts index 6b1999601c..abbd10bbe2 100644 --- a/packages/core/src/domain/telemetry/index.ts +++ b/packages/core/src/domain/telemetry/index.ts @@ -8,6 +8,7 @@ export { startTelemetry, isTelemetryReplicationAllowed, addTelemetryConfiguration, + addTelemetryUsage, } from './telemetry' export * from './rawTelemetryEvent.types' diff --git a/packages/core/src/domain/telemetry/rawTelemetryEvent.types.ts b/packages/core/src/domain/telemetry/rawTelemetryEvent.types.ts index 1f016805e6..663be9a253 100644 --- a/packages/core/src/domain/telemetry/rawTelemetryEvent.types.ts +++ b/packages/core/src/domain/telemetry/rawTelemetryEvent.types.ts @@ -1,8 +1,9 @@ -import type { TelemetryEvent, TelemetryConfigurationEvent } from './telemetryEvent.types' +import type { TelemetryEvent, TelemetryConfigurationEvent, TelemetryUsageEvent } from './telemetryEvent.types' export const TelemetryType = { log: 'log', configuration: 'configuration', + usage: 'usage', } as const export const enum StatusType { @@ -17,3 +18,4 @@ export interface RuntimeEnvInfo { export type RawTelemetryEvent = TelemetryEvent['telemetry'] export type RawTelemetryConfiguration = TelemetryConfigurationEvent['telemetry']['configuration'] +export type RawTelemetryUsage = TelemetryUsageEvent['telemetry']['usage'] diff --git a/packages/core/src/domain/telemetry/telemetry.spec.ts b/packages/core/src/domain/telemetry/telemetry.spec.ts index ac52ab19f5..27befba09b 100644 --- a/packages/core/src/domain/telemetry/telemetry.spec.ts +++ b/packages/core/src/domain/telemetry/telemetry.spec.ts @@ -12,6 +12,7 @@ import { scrubCustomerFrames, formatError, addTelemetryConfiguration, + addTelemetryUsage, TelemetryService, } from './telemetry' @@ -72,6 +73,32 @@ describe('telemetry', () => { }) }) + describe('addTelemetryUsage', () => { + it('should collects usage when sampled', () => { + const { notifySpy } = startAndSpyTelemetry({ telemetrySampleRate: 100, telemetryUsageSampleRate: 100 }) + + addTelemetryUsage({ feature: 'set-tracking-consent', tracking_consent: 'granted' }) + + expect(notifySpy).toHaveBeenCalled() + }) + + it('should not notify usage when not sampled', () => { + const { notifySpy } = startAndSpyTelemetry({ telemetrySampleRate: 100, telemetryUsageSampleRate: 0 }) + + addTelemetryUsage({ feature: 'set-tracking-consent', tracking_consent: 'granted' }) + + expect(notifySpy).not.toHaveBeenCalled() + }) + + it('should not notify usage when telemetrySampleRate is 0', () => { + const { notifySpy } = startAndSpyTelemetry({ telemetrySampleRate: 0, telemetryUsageSampleRate: 100 }) + + addTelemetryUsage({ feature: 'set-tracking-consent', tracking_consent: 'granted' }) + + expect(notifySpy).not.toHaveBeenCalled() + }) + }) + it('should contains feature flags', () => { addExperimentalFeatures(['foo' as ExperimentalFeature]) const { notifySpy } = startAndSpyTelemetry() diff --git a/packages/core/src/domain/telemetry/telemetry.ts b/packages/core/src/domain/telemetry/telemetry.ts index dae8c490ae..c34bc9d98c 100644 --- a/packages/core/src/domain/telemetry/telemetry.ts +++ b/packages/core/src/domain/telemetry/telemetry.ts @@ -17,7 +17,12 @@ import type { StackTrace } from '../error/computeStackTrace' import { computeStackTrace } from '../error/computeStackTrace' import { getConnectivity } from '../connectivity' import type { TelemetryEvent } from './telemetryEvent.types' -import type { RawTelemetryConfiguration, RawTelemetryEvent, RuntimeEnvInfo } from './rawTelemetryEvent.types' +import type { + RawTelemetryConfiguration, + RawTelemetryEvent, + RuntimeEnvInfo, + RawTelemetryUsage, +} from './rawTelemetryEvent.types' import { StatusType, TelemetryType } from './rawTelemetryEvent.types' // replaced at build time @@ -48,9 +53,10 @@ const TELEMETRY_EXCLUDED_SITES: string[] = [INTAKE_SITE_US1_FED] const telemetryConfiguration: { maxEventsPerPage: number sentEventCount: number - telemetryEnabled: boolean - telemetryConfigurationEnabled: boolean -} = { maxEventsPerPage: 0, sentEventCount: 0, telemetryEnabled: false, telemetryConfigurationEnabled: false } +} = { + maxEventsPerPage: 0, + sentEventCount: 0, +} let onRawTelemetryEventCollected: ((event: RawTelemetryEvent) => void) | undefined @@ -58,14 +64,18 @@ export function startTelemetry(telemetryService: TelemetryService, configuration let contextProvider: () => Context const observable = new Observable() - telemetryConfiguration.telemetryEnabled = + const telemetryEnabled = !includes(TELEMETRY_EXCLUDED_SITES, configuration.site) && performDraw(configuration.telemetrySampleRate) - telemetryConfiguration.telemetryConfigurationEnabled = - telemetryConfiguration.telemetryEnabled && performDraw(configuration.telemetryConfigurationSampleRate) + + const telemetryEnabledPerType = { + [TelemetryType.log]: telemetryEnabled, + [TelemetryType.configuration]: telemetryEnabled && performDraw(configuration.telemetryConfigurationSampleRate), + [TelemetryType.usage]: telemetryEnabled && performDraw(configuration.telemetryUsageSampleRate), + } const runtimeEnvInfo = getRuntimeEnvInfo() onRawTelemetryEventCollected = (rawEvent: RawTelemetryEvent) => { - if (telemetryConfiguration.telemetryEnabled) { + if (telemetryEnabledPerType[rawEvent.type!]) { const event = toTelemetryEvent(telemetryService, rawEvent, runtimeEnvInfo) observable.notify(event) sendToExtension('telemetry', event) @@ -108,7 +118,7 @@ export function startTelemetry(telemetryService: TelemetryService, configuration contextProvider = provider }, observable, - enabled: telemetryConfiguration.telemetryEnabled, + enabled: telemetryEnabled, } } function getRuntimeEnvInfo(): RuntimeEnvInfo { @@ -172,12 +182,17 @@ export function addTelemetryError(e: unknown, context?: Context) { } export function addTelemetryConfiguration(configuration: RawTelemetryConfiguration) { - if (telemetryConfiguration.telemetryConfigurationEnabled) { - addTelemetry({ - type: TelemetryType.configuration, - configuration, - }) - } + addTelemetry({ + type: TelemetryType.configuration, + configuration, + }) +} + +export function addTelemetryUsage(usage: RawTelemetryUsage) { + addTelemetry({ + type: TelemetryType.usage, + usage, + }) } function addTelemetry(event: RawTelemetryEvent) { diff --git a/packages/core/src/domain/telemetry/telemetryEvent.types.ts b/packages/core/src/domain/telemetry/telemetryEvent.types.ts index cb0fc2e50e..b389f1d7d2 100644 --- a/packages/core/src/domain/telemetry/telemetryEvent.types.ts +++ b/packages/core/src/domain/telemetry/telemetryEvent.types.ts @@ -6,7 +6,11 @@ /** * Schema of all properties of a telemetry event */ -export type TelemetryEvent = TelemetryErrorEvent | TelemetryDebugEvent | TelemetryConfigurationEvent +export type TelemetryEvent = + | TelemetryErrorEvent + | TelemetryDebugEvent + | TelemetryConfigurationEvent + | TelemetryUsageEvent /** * Schema of all properties of a telemetry error event */ @@ -97,6 +101,10 @@ export type TelemetryConfigurationEvent = CommonTelemetryProperties & { * The percentage of telemetry configuration events sent after being sampled by telemetry_sample_rate */ telemetry_configuration_sample_rate?: number + /** + * The percentage of telemetry usage events sent after being sampled by telemetry_sample_rate + */ + telemetry_usage_sample_rate?: number /** * The percentage of requests traced */ @@ -120,7 +128,7 @@ export type TelemetryConfigurationEvent = CommonTelemetryProperties & { /** * The initial tracking consent value */ - tracking_consent?: string + tracking_consent?: 'granted' | 'not-granted' | 'pending' /** * Whether the session replay start is handled manually */ @@ -351,6 +359,55 @@ export type TelemetryConfigurationEvent = CommonTelemetryProperties & { } [k: string]: unknown } +/** + * Schema of all properties of a telemetry usage event + */ +export type TelemetryUsageEvent = CommonTelemetryProperties & { + /** + * The telemetry usage information + */ + telemetry: { + /** + * Telemetry type + */ + type: 'usage' + usage: TelemetryCommonFeaturesUsage | TelemetryBrowserFeaturesUsage + [k: string]: unknown + } + [k: string]: unknown +} +/** + * Schema of features usage common across SDKs + */ +export type TelemetryCommonFeaturesUsage = + | { + /** + * setTrackingConsent API + */ + feature: 'set-tracking-consent' + /** + * The tracking consent value set by the user + */ + tracking_consent: 'granted' | 'not-granted' | 'pending' + [k: string]: unknown + } + | { + /** + * stopSession API + */ + feature: 'stop-session' + [k: string]: unknown + } +/** + * Schema of browser specific features usage + */ +export type TelemetryBrowserFeaturesUsage = { + /** + * startSessionReplayRecording API + */ + feature: 'start-session-replay-recording' + [k: string]: unknown +} /** * Schema of common properties of Telemetry events diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index cbd049e324..dd39171ee0 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -40,6 +40,7 @@ export { TelemetryService, isTelemetryReplicationAllowed, addTelemetryConfiguration, + addTelemetryUsage, } from './domain/telemetry' export { monitored, monitor, callMonitored, setDebugMode } from './tools/monitor' export { Observable, Subscription } from './tools/observable' diff --git a/packages/core/test/coreConfiguration.ts b/packages/core/test/coreConfiguration.ts index 9c32befc3f..a81216aa96 100644 --- a/packages/core/test/coreConfiguration.ts +++ b/packages/core/test/coreConfiguration.ts @@ -35,12 +35,14 @@ export const EXHAUSTIVE_INIT_CONFIGURATION: Required = { datacenter: 'datacenter', internalAnalyticsSubdomain: 'internal-analytics-subdomain.com', telemetryConfigurationSampleRate: 70, + telemetryUsageSampleRate: 80, } export const SERIALIZED_EXHAUSTIVE_INIT_CONFIGURATION = { session_sample_rate: 50, telemetry_sample_rate: 60, telemetry_configuration_sample_rate: 70, + telemetry_usage_sample_rate: 80, use_before_send: true, use_cross_site_session_cookie: true, use_partitioned_cross_site_session_cookie: true, @@ -51,7 +53,7 @@ export const SERIALIZED_EXHAUSTIVE_INIT_CONFIGURATION = { allow_fallback_to_local_storage: true, store_contexts_across_pages: true, allow_untrusted_events: true, - tracking_consent: 'not-granted', + tracking_consent: 'not-granted' as const, } /** diff --git a/packages/logs/src/boot/logsPublicApi.ts b/packages/logs/src/boot/logsPublicApi.ts index 77a0e86f83..83e8ad076b 100644 --- a/packages/logs/src/boot/logsPublicApi.ts +++ b/packages/logs/src/boot/logsPublicApi.ts @@ -1,5 +1,6 @@ import type { Context, TrackingConsent, User } from '@datadog/browser-core' import { + addTelemetryUsage, CustomerDataType, assign, createContextManager, @@ -86,7 +87,10 @@ export function makeLogsPublicApi(startLogsImpl: StartLogs) { * If this method is called before the init() method, the provided value will take precedence * over the one provided as initialization parameter. */ - setTrackingConsent: monitor((trackingConsent: TrackingConsent) => trackingConsentState.update(trackingConsent)), + setTrackingConsent: monitor((trackingConsent: TrackingConsent) => { + trackingConsentState.update(trackingConsent) + addTelemetryUsage({ feature: 'set-tracking-consent', tracking_consent: trackingConsent }) + }), getGlobalContext: monitor(() => globalContextManager.getContext()), diff --git a/packages/rum-core/src/boot/rumPublicApi.ts b/packages/rum-core/src/boot/rumPublicApi.ts index e88333ecd3..45619ab18b 100644 --- a/packages/rum-core/src/boot/rumPublicApi.ts +++ b/packages/rum-core/src/boot/rumPublicApi.ts @@ -9,6 +9,7 @@ import type { TrackingConsent, } from '@datadog/browser-core' import { + addTelemetryUsage, timeStampToClocks, isExperimentalFeatureEnabled, ExperimentalFeature, @@ -204,7 +205,10 @@ export function makeRumPublicApi(startRumImpl: StartRum, recorderApi: RecorderAp * If this method is called before the init() method, the provided value will take precedence * over the one provided as initialization parameter. */ - setTrackingConsent: monitor((trackingConsent: TrackingConsent) => trackingConsentState.update(trackingConsent)), + setTrackingConsent: monitor((trackingConsent: TrackingConsent) => { + trackingConsentState.update(trackingConsent) + addTelemetryUsage({ feature: 'set-tracking-consent', tracking_consent: trackingConsent }) + }), setGlobalContextProperty: monitor((key, value) => globalContextManager.setContextProperty(key, value)), @@ -278,6 +282,7 @@ export function makeRumPublicApi(startRumImpl: StartRum, recorderApi: RecorderAp stopSession: monitor(() => { strategy.stopSession() + addTelemetryUsage({ feature: 'stop-session' }) }), /** @@ -288,7 +293,10 @@ export function makeRumPublicApi(startRumImpl: StartRum, recorderApi: RecorderAp }), getSessionReplayLink: monitor(() => recorderApi.getSessionReplayLink()), - startSessionReplayRecording: monitor(() => recorderApi.start()), + startSessionReplayRecording: monitor(() => { + recorderApi.start() + addTelemetryUsage({ feature: 'start-session-replay-recording' }) + }), stopSessionReplayRecording: monitor(() => recorderApi.stop()), }) diff --git a/packages/rum/src/domain/record/record.spec.ts b/packages/rum/src/domain/record/record.spec.ts index f13b824838..1e36b26c0d 100644 --- a/packages/rum/src/domain/record/record.spec.ts +++ b/packages/rum/src/domain/record/record.spec.ts @@ -3,7 +3,7 @@ import type { RumConfiguration, ViewCreatedEvent } from '@datadog/browser-rum-co import { LifeCycle, LifeCycleEventType } from '@datadog/browser-rum-core' import type { Clock } from '@datadog/browser-core/test' import { createNewEvent, collectAsyncCalls } from '@datadog/browser-core/test' -import { findFullSnapshot, findNode, recordsPerFullSnapshot } from '../../../test' +import { findElement, findFullSnapshot, findNode, recordsPerFullSnapshot } from '../../../test' import type { BrowserIncrementalSnapshotRecord, BrowserMutationData, @@ -11,6 +11,7 @@ import type { DocumentFragmentNode, ElementNode, FocusRecord, + ScrollData, } from '../../types' import { NodeType, RecordType, IncrementalSource } from '../../types' import { appendElement } from '../../../../rum-core/test' @@ -334,6 +335,28 @@ describe('record', () => { expect(inputRecords.length).toBe(1) }) + it('should record the scroll event inside a shadow root', () => { + const div = appendElement('
', createShadow()) as HTMLDivElement + startRecording() + expect(getEmittedRecords().length).toBe(recordsPerFullSnapshot()) + + div.dispatchEvent(createNewEvent('scroll', { target: div, composed: false })) + + recordApi.flushMutations() + + const scrollRecords = getEmittedRecords().filter( + (record) => record.type === RecordType.IncrementalSnapshot && record.data.source === IncrementalSource.Scroll + ) + expect(scrollRecords.length).toBe(1) + + const scrollData = getLastIncrementalSnapshotData(getEmittedRecords(), IncrementalSource.Scroll) + + const fs = findFullSnapshot({ records: getEmittedRecords() })! + const scrollableNode = findElement(fs.data.node, (node) => node.attributes['unique-selector'] === 'enabled')! + + expect(scrollData.id).toBe(scrollableNode.id) + }) + it('should clean the state once the shadow dom is removed to avoid memory leak', () => { const shadowRoot = createShadow() appendElement('
', shadowRoot) diff --git a/packages/rum/src/domain/record/record.ts b/packages/rum/src/domain/record/record.ts index fd156cdaba..f5b027d1ea 100644 --- a/packages/rum/src/domain/record/record.ts +++ b/packages/rum/src/domain/record/record.ts @@ -51,10 +51,7 @@ export function record(options: RecordOptions): RecordAPI { const elementsScrollPositions = createElementsScrollPositions() - const shadowRootsController = initShadowRootsController(configuration, { - mutationCb: emitAndComputeStats, - inputCb: emitAndComputeStats, - }) + const shadowRootsController = initShadowRootsController(configuration, emitAndComputeStats, elementsScrollPositions) const { stop: stopFullSnapshots } = startFullSnapshots( elementsScrollPositions, @@ -76,7 +73,7 @@ export function record(options: RecordOptions): RecordAPI { mutationTracker, trackMove(configuration, emitAndComputeStats), trackMouseInteraction(configuration, emitAndComputeStats, recordIds), - trackScroll(configuration, emitAndComputeStats, elementsScrollPositions), + trackScroll(configuration, emitAndComputeStats, elementsScrollPositions, document), trackViewportResize(configuration, emitAndComputeStats), trackInput(configuration, emitAndComputeStats), trackMediaInteraction(configuration, emitAndComputeStats), diff --git a/packages/rum/src/domain/record/serialization/serializeAttribute.spec.ts b/packages/rum/src/domain/record/serialization/serializeAttribute.spec.ts index 6034ca5199..5163bb2ee7 100644 --- a/packages/rum/src/domain/record/serialization/serializeAttribute.spec.ts +++ b/packages/rum/src/domain/record/serialization/serializeAttribute.spec.ts @@ -144,4 +144,13 @@ describe('serializeAttribute', () => { ) }) }) + + describe('iframe srcdoc masking', () => { + it('should mask the srcdoc when privacy override set to mask', () => { + const node = document.createElement('iframe') + node.srcdoc = 'data-foo">' + node.setAttribute(PRIVACY_ATTR_NAME, 'mask') + expect(serializeAttribute(node, NodePrivacyLevel.MASK, 'srcdoc', DEFAULT_CONFIGURATION)).toBe('***') + }) + }) }) diff --git a/packages/rum/src/domain/record/serialization/serializeAttribute.ts b/packages/rum/src/domain/record/serialization/serializeAttribute.ts index 4c6991c5e1..dcbbcfc65b 100644 --- a/packages/rum/src/domain/record/serialization/serializeAttribute.ts +++ b/packages/rum/src/domain/record/serialization/serializeAttribute.ts @@ -68,6 +68,11 @@ export function serializeAttribute( // Exception: it's safe to reveal the `${PRIVACY_ATTR_NAME}` attr return CENSORED_STRING_MARK } + + // mask iframe srcdoc + if (tagName === 'IFRAME' && attributeName === 'srcdoc') { + return CENSORED_STRING_MARK + } } if (!attributeValue || typeof attributeValue !== 'string') { diff --git a/packages/rum/src/domain/record/shadowRootsController.ts b/packages/rum/src/domain/record/shadowRootsController.ts index 5a5ad60125..d505b19b84 100644 --- a/packages/rum/src/domain/record/shadowRootsController.ts +++ b/packages/rum/src/domain/record/shadowRootsController.ts @@ -1,6 +1,7 @@ import type { RumConfiguration } from '@datadog/browser-rum-core' -import type { InputCallback, MutationCallBack } from './trackers' -import { trackInput, trackMutation } from './trackers' +import type { BrowserIncrementalSnapshotRecord } from '../../types' +import { trackInput, trackMutation, trackScroll } from './trackers' +import type { ElementsScrollPositions } from './elementsScrollPositions' interface ShadowRootController { stop: () => void @@ -18,13 +19,8 @@ export interface ShadowRootsController { export const initShadowRootsController = ( configuration: RumConfiguration, - { - mutationCb, - inputCb, - }: { - mutationCb: MutationCallBack - inputCb: InputCallback - } + callback: (record: BrowserIncrementalSnapshotRecord) => void, + elementsScrollPositions: ElementsScrollPositions ): ShadowRootsController => { const controllerByShadowRoot = new Map() @@ -33,14 +29,17 @@ export const initShadowRootsController = ( if (controllerByShadowRoot.has(shadowRoot)) { return } - const mutationTracker = trackMutation(mutationCb, configuration, shadowRootsController, shadowRoot) - // the change event no do bubble up across the shadow root, we have to listen on the shadow root - const inputTracker = trackInput(configuration, inputCb, shadowRoot) + const mutationTracker = trackMutation(callback, configuration, shadowRootsController, shadowRoot) + // The change event does not bubble up across the shadow root, we have to listen on the shadow root + const inputTracker = trackInput(configuration, callback, shadowRoot) + // The scroll event does not bubble up across the shadow root, we have to listen on the shadow root + const scrollTracker = trackScroll(configuration, callback, elementsScrollPositions, shadowRoot) controllerByShadowRoot.set(shadowRoot, { flush: () => mutationTracker.flush(), stop: () => { mutationTracker.stop() inputTracker.stop() + scrollTracker.stop() }, }) }, diff --git a/packages/rum/src/domain/record/trackers/index.ts b/packages/rum/src/domain/record/trackers/index.ts index e0ef802644..4a4fb792f4 100644 --- a/packages/rum/src/domain/record/trackers/index.ts +++ b/packages/rum/src/domain/record/trackers/index.ts @@ -8,5 +8,6 @@ export { trackFocus } from './trackFocus' export { trackFrustration } from './trackFrustration' export { trackViewEnd } from './trackViewEnd' export { InputCallback, trackInput } from './trackInput' +export { ScrollCallback } from './trackScroll' export { trackMutation, MutationCallBack, RumMutationRecord } from './trackMutation' export { Tracker } from './types' diff --git a/packages/rum/src/domain/record/trackers/trackScroll.ts b/packages/rum/src/domain/record/trackers/trackScroll.ts index df00846722..28aa1ac25e 100644 --- a/packages/rum/src/domain/record/trackers/trackScroll.ts +++ b/packages/rum/src/domain/record/trackers/trackScroll.ts @@ -16,7 +16,8 @@ export type ScrollCallback = (incrementalSnapshotRecord: BrowserIncrementalSnaps export function trackScroll( configuration: RumConfiguration, scrollCb: ScrollCallback, - elementsScrollPositions: ElementsScrollPositions + elementsScrollPositions: ElementsScrollPositions, + target: Document | ShadowRoot = document ): Tracker { const { throttled: updatePosition, cancel: cancelThrottle } = throttle((event: Event) => { const target = getEventTarget(event) as HTMLElement | Document @@ -48,7 +49,7 @@ export function trackScroll( ) }, SCROLL_OBSERVER_THRESHOLD) - const { stop: removeListener } = addEventListener(configuration, document, DOM_EVENT.SCROLL, updatePosition, { + const { stop: removeListener } = addEventListener(configuration, target, DOM_EVENT.SCROLL, updatePosition, { capture: true, passive: true, }) diff --git a/performances/package.json b/performances/package.json index 72e40d4c46..988552f535 100644 --- a/performances/package.json +++ b/performances/package.json @@ -6,10 +6,10 @@ "start": "ts-node ./src/main.ts" }, "dependencies": { - "@types/node": "20.12.5", + "@types/node": "20.12.7", "@types/node-forge": "1.3.11", "node-forge": "1.3.1", - "puppeteer": "22.6.3", + "puppeteer": "22.6.5", "ts-node": "10.9.2" }, "volta": { diff --git a/sandbox/index.html b/sandbox/index.html index 6c187bffd7..20c03da615 100644 --- a/sandbox/index.html +++ b/sandbox/index.html @@ -14,12 +14,14 @@ trackLongTasks: true, telemetrySampleRate: 100, telemetryConfigurationSampleRate: 100, + telemetryUsageSampleRate: 100, enableExperimentalFeatures: [], }) DD_LOGS.init({ clientToken: 'xxx', telemetrySampleRate: 100, telemetryConfigurationSampleRate: 100, + telemetryUsageSampleRate: 100, enableExperimentalFeatures: [], }) diff --git a/scripts/check-schemas.js b/scripts/check-schemas.js new file mode 100644 index 0000000000..fc96e7366b --- /dev/null +++ b/scripts/check-schemas.js @@ -0,0 +1,17 @@ +const { printLog, printError, runMain } = require('./lib/execution-utils') +const { command } = require('./lib/command') + +runMain(() => { + printLog('Regenerating schemas...') + command`scripts/cli build_json2type`.run() + command`node scripts/generate-schema-types.js`.run() + + printLog('Checking untracked changes...') + const diff = command`git diff --color`.run() + + if (diff) { + printLog(diff) + printError('\nUntracked changes detected, ensure that schemas and types are in-sync.\n') + process.exit(1) + } +}) diff --git a/scripts/deploy/deploy.js b/scripts/deploy/deploy.js index 0c96d738dc..b0fa7750d5 100644 --- a/scripts/deploy/deploy.js +++ b/scripts/deploy/deploy.js @@ -1,12 +1,15 @@ 'use strict' const { printLog, runMain } = require('../lib/execution-utils') +const { fetchPR, LOCAL_BRANCH } = require('../lib/git-utils') const { command } = require('../lib/command') + const { buildRootUploadPath, buildDatacenterUploadPath, buildBundleFolder, buildBundleFileName, + buildPullRequestUploadPath, packages, } = require('./lib/deployment-utils') @@ -27,20 +30,27 @@ const AWS_CONFIG = { /** * Deploy SDK files to CDN * Usage: - * node deploy.js staging|prod staging|canary|vXXX root,us1,eu1,... + * node deploy.js staging|prod staging|canary|pull-request|vXXX root,pull-request,us1,eu1,... */ const env = process.argv[2] const version = process.argv[3] const uploadPathTypes = process.argv[4].split(',') -runMain(() => { +runMain(async () => { const awsConfig = AWS_CONFIG[env] let cloudfrontPathsToInvalidate = [] for (const { packageName } of packages) { const bundleFolder = buildBundleFolder(packageName) for (const uploadPathType of uploadPathTypes) { let uploadPath - if (uploadPathType === 'root') { + if (uploadPathType === 'pull-request') { + const pr = await fetchPR(LOCAL_BRANCH) + if (!pr) { + console.log('No pull requests found for the branch') + return + } + uploadPath = buildPullRequestUploadPath(packageName, pr.number) + } else if (uploadPathType === 'root') { uploadPath = buildRootUploadPath(packageName, version) } else { uploadPath = buildDatacenterUploadPath(uploadPathType, packageName, version) @@ -58,7 +68,9 @@ function uploadToS3(awsConfig, bundlePath, uploadPath) { const accessToS3 = generateEnvironmentForRole(awsConfig.accountId, 'build-stable-browser-agent-artifacts-s3-write') const browserCache = - version === 'staging' || version === 'canary' ? 15 * ONE_MINUTE_IN_SECOND : 4 * ONE_HOUR_IN_SECOND + version === 'staging' || version === 'canary' || version === 'pull-request' + ? 15 * ONE_MINUTE_IN_SECOND + : 4 * ONE_HOUR_IN_SECOND const cacheControl = `max-age=${browserCache}, s-maxage=60` printLog(`Upload ${bundlePath} to s3://${awsConfig.bucketName}/${uploadPath}`) diff --git a/scripts/deploy/lib/deployment-utils.js b/scripts/deploy/lib/deployment-utils.js index 5306158b17..c26acca3f5 100644 --- a/scripts/deploy/lib/deployment-utils.js +++ b/scripts/deploy/lib/deployment-utils.js @@ -14,6 +14,10 @@ const buildDatacenterUploadPath = (datacenter, packageName, version, extension = // ex: datadog-rum.js const buildBundleFileName = (packageName, extension = 'js') => `datadog-${packageName}.${extension}` +// ex: pull-request/2781/datadog-rum.js +function buildPullRequestUploadPath(packageName, version, extension = 'js') { + return `pull-request/${version}/datadog-${packageName}.${extension}` +} // ex: packages/rum/bundle const buildBundleFolder = (packageName) => `packages/${packageName}/bundle` @@ -23,4 +27,5 @@ module.exports = { buildDatacenterUploadPath, buildBundleFileName, buildBundleFolder, + buildPullRequestUploadPath, } diff --git a/scripts/lib/git-utils.js b/scripts/lib/git-utils.js index 0c9090ccd6..b4133f3120 100644 --- a/scripts/lib/git-utils.js +++ b/scripts/lib/git-utils.js @@ -2,7 +2,38 @@ const os = require('os') const fs = require('fs') const { command } = require('../lib/command') -const { getGithubDeployKey } = require('./secrets') +const { getGithubDeployKey, getGithubAccessToken } = require('./secrets') +const { fetchHandlingError } = require('./execution-utils') + +const GITHUB_TOKEN = getGithubAccessToken() + +async function fetchPR(localBranch) { + const response = await fetchHandlingError( + `https://api.github.com/repos/DataDog/browser-sdk/pulls?head=DataDog:${localBranch}`, + { + method: 'GET', + headers: { + Authorization: `token ${GITHUB_TOKEN}`, + }, + } + ) + const pr = response.body ? await response.json() : null + if (pr && pr.length > 1) { + throw new Error('Multiple pull requests found for the branch') + } + return pr ? pr[0] : null +} + +function getLastCommonCommit(baseBranch) { + try { + command`git fetch --depth=100 origin ${baseBranch}`.run() + const commandOutput = command`git merge-base origin/${baseBranch} HEAD`.run() + // SHA commit is truncated to 8 characters as bundle sizes commit are exported in short format to logs for convenience and readability. + return commandOutput.trim().substring(0, 8) + } catch (error) { + throw new Error('Failed to get last common commit', { cause: error }) + } +} function initGitConfig(repository) { const homedir = os.homedir() @@ -21,4 +52,9 @@ function initGitConfig(repository) { module.exports = { initGitConfig, + fetchPR, + getLastCommonCommit, + BASE_BRANCH: process.env.MAIN_BRANCH, + LOCAL_BRANCH: process.env.CI_COMMIT_REF_NAME, + GITHUB_TOKEN, } diff --git a/scripts/performance/bundle-size/compute-bundle-size.js b/scripts/performance/bundle-size/compute-bundle-size.js new file mode 100644 index 0000000000..2cc1c722a8 --- /dev/null +++ b/scripts/performance/bundle-size/compute-bundle-size.js @@ -0,0 +1,29 @@ +const path = require('path') +const fs = require('fs') + +const rumPath = path.join(__dirname, '../../../packages/rum/bundle/datadog-rum.js') +const logsPath = path.join(__dirname, '../../../packages/logs/bundle/datadog-logs.js') +const rumSlimPath = path.join(__dirname, '../../../packages/rum-slim/bundle/datadog-rum-slim.js') +const workerPath = path.join(__dirname, '../../../packages/worker/bundle/worker.js') + +function getBundleSize(pathBundle) { + try { + const file = fs.statSync(pathBundle) + return file.size + } catch (error) { + throw new Error('Failed to get bundle size', { cause: error }) + } +} + +function calculateBundleSizes() { + return { + rum: getBundleSize(rumPath), + logs: getBundleSize(logsPath), + rum_slim: getBundleSize(rumSlimPath), + worker: getBundleSize(workerPath), + } +} + +module.exports = { + calculateBundleSizes, +} diff --git a/scripts/performance/cpu-performance/compute-cpu-performance.js b/scripts/performance/cpu-performance/compute-cpu-performance.js new file mode 100644 index 0000000000..e9a4e22b70 --- /dev/null +++ b/scripts/performance/cpu-performance/compute-cpu-performance.js @@ -0,0 +1,68 @@ +const { fetchHandlingError } = require('../../lib/execution-utils') +const { getOrg2ApiKey, getOrg2AppKey } = require('../../lib/secrets') +const { timeout } = require('../../lib/execution-utils') +const { fetchPR, LOCAL_BRANCH } = require('../../lib/git-utils') +const { LOCAL_COMMIT_SHA } = require('../report-as-a-pr-comment') +const API_KEY = getOrg2ApiKey() +const APP_KEY = getOrg2AppKey() +const TIMEOUT_IN_MS = 10000 +const TEST_PUBLIC_ID = 'vcg-7rk-5av' +const RETRIES_NUMBER = 6 + +async function computeCpuPerformance() { + const pr = await fetchPR(LOCAL_BRANCH) + if (!pr) { + console.log('No pull requests found for the branch') + return + } + const resultId = await triggerSyntheticsTest(pr.number, LOCAL_COMMIT_SHA) + await waitForSyntheticsTestToFinish(resultId, RETRIES_NUMBER) +} + +async function triggerSyntheticsTest(prNumber, commitId) { + const body = { + tests: [ + { + public_id: `${TEST_PUBLIC_ID}`, + startUrl: `https://datadoghq.dev/browser-sdk-test-playground/performance/?prNumber=${prNumber}&commitId=${commitId}`, + }, + ], + } + const url = 'https://api.datadoghq.com/api/v1/synthetics/tests/trigger/ci' + const response = await fetchHandlingError(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'DD-API-KEY': API_KEY, + 'DD-APPLICATION-KEY': APP_KEY, + }, + body: JSON.stringify(body), + }) + const data = await response.json() + return data.results[0].result_id +} + +async function waitForSyntheticsTestToFinish(resultId, RETRIES_NUMBER) { + const url = `https://api.datadoghq.com/api/v1/synthetics/tests/${TEST_PUBLIC_ID}/results/${resultId}` + for (let i = 0; i < RETRIES_NUMBER; i++) { + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'DD-API-KEY': API_KEY, + 'DD-APPLICATION-KEY': APP_KEY, + }, + }) + const data = await response.json() + if (data.length !== 0 && data.status === 0) { + await timeout(TIMEOUT_IN_MS) // Wait for logs ingestion + return + } + await timeout(TIMEOUT_IN_MS) + } + throw new Error('Synthetics test did not finish within the specified number of retries') +} + +module.exports = { + computeCpuPerformance, +} diff --git a/scripts/performance/fetch-performance-metrics.js b/scripts/performance/fetch-performance-metrics.js new file mode 100644 index 0000000000..8a59b5d902 --- /dev/null +++ b/scripts/performance/fetch-performance-metrics.js @@ -0,0 +1,43 @@ +const { getOrg2ApiKey, getOrg2AppKey } = require('../lib/secrets') +const { fetchHandlingError } = require('../lib/execution-utils') +const ONE_DAY_IN_SECOND = 24 * 60 * 60 + +function fetchPerformanceMetrics(type, names, commitId) { + return Promise.all(names.map((name) => fetchMetric(type, name, commitId))) +} + +async function fetchMetric(type, name, commitId) { + const now = Math.floor(Date.now() / 1000) + const date = now - 30 * ONE_DAY_IN_SECOND + let query = '' + + if (type === 'bundle') { + query = `avg:bundle_sizes.${name}{commit:${commitId}}&from=${date}&to=${now}` + } else if (type === 'cpu') { + query = `avg:cpu.sdk.${name}.performance.average{commitid:${commitId}}&from=${date}&to=${now}` + } + + const response = await fetchHandlingError(`https://api.datadoghq.com/api/v1/query?query=${query}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'DD-API-KEY': getOrg2ApiKey(), + 'DD-APPLICATION-KEY': getOrg2AppKey(), + }, + }) + const data = await response.json() + if (data.series && data.series.length > 0 && data.series[0].pointlist && data.series[0].pointlist.length > 0) { + return { + name, + value: data.series[0].pointlist[0][1], + } + } + return { + name, + value: null, + } +} + +module.exports = { + fetchPerformanceMetrics, +} diff --git a/scripts/performance/index.js b/scripts/performance/index.js new file mode 100644 index 0000000000..8ff5d103b3 --- /dev/null +++ b/scripts/performance/index.js @@ -0,0 +1,12 @@ +const { runMain } = require('../lib/execution-utils') +const { reportAsPrComment } = require('./report-as-a-pr-comment') +const { reportToDatadog } = require('./report-to-datadog') +const { calculateBundleSizes } = require('./bundle-size/compute-bundle-size') +const { computeCpuPerformance } = require('./cpu-performance/compute-cpu-performance') + +runMain(async () => { + const localBundleSizes = calculateBundleSizes() + await computeCpuPerformance() + await reportToDatadog(localBundleSizes) + await reportAsPrComment(localBundleSizes) +}) diff --git a/scripts/performance/report-as-a-pr-comment.js b/scripts/performance/report-as-a-pr-comment.js new file mode 100644 index 0000000000..1f66d7e4fe --- /dev/null +++ b/scripts/performance/report-as-a-pr-comment.js @@ -0,0 +1,170 @@ +const { command } = require('../lib/command') +const { fetchHandlingError } = require('../lib/execution-utils') +const { LOCAL_BRANCH, BASE_BRANCH, GITHUB_TOKEN, getLastCommonCommit, fetchPR } = require('../lib/git-utils') +const { fetchPerformanceMetrics } = require('./fetch-performance-metrics') +const PR_COMMENT_HEADER = 'Bundles Sizes Evolution' +const PR_COMMENTER_AUTH_TOKEN = command`authanywhere`.run().split(' ')[2].trim() +// The value is set to 5% as it's around 10 times the average value for small PRs. +const SIZE_INCREASE_THRESHOLD = 5 +const LOCAL_COMMIT_SHA = process.env.CI_COMMIT_SHORT_SHA +const ACTION_NAMES = [ + 'adderror', + 'addaction', + 'logmessage', + 'startview', + 'startstopsessionreplayrecording', + 'addtiming', + 'addglobalcontext', +] + +async function reportAsPrComment(localBundleSizes) { + const lastCommonCommit = getLastCommonCommit(BASE_BRANCH, LOCAL_BRANCH) + const pr = await fetchPR(LOCAL_BRANCH) + if (!pr) { + console.log('No pull requests found for the branch') + return + } + const packageNames = Object.keys(localBundleSizes) + const baseBundleSizes = await fetchPerformanceMetrics('bundle', packageNames, lastCommonCommit) + const cpuBasePerformance = await fetchPerformanceMetrics('cpu', ACTION_NAMES, lastCommonCommit) + const cpuLocalPerformance = await fetchPerformanceMetrics('cpu', ACTION_NAMES, LOCAL_COMMIT_SHA) + const differenceBundle = compare(baseBundleSizes, localBundleSizes) + const differenceCpu = compare(cpuBasePerformance, cpuLocalPerformance) + const commentId = await retrieveExistingCommentId(pr.number) + const message = createMessage( + differenceBundle, + differenceCpu, + baseBundleSizes, + localBundleSizes, + cpuBasePerformance, + cpuLocalPerformance + ) + await updateOrAddComment(message, pr.number, commentId) +} + +function compare(baseResults, localResults) { + return baseResults.map((baseResult) => { + let localResult = null + + if (Array.isArray(localResults)) { + const localResultObj = localResults.find((result) => result.name === baseResult.name) + localResult = localResultObj ? localResultObj.value : null + } else { + localResult = localResults[baseResult.name] + } + + let change = null + let percentageChange = null + + if (baseResult.value && localResult) { + change = localResult - baseResult.value + percentageChange = ((change / baseResult.value) * 100).toFixed(2) + } else if (localResult) { + change = localResult + percentageChange = 'N/A' + } + + return { + name: baseResult.name, + change, + percentageChange, + } + }) +} + +async function retrieveExistingCommentId(prNumber) { + const response = await fetchHandlingError( + `https://api.github.com/repos/DataDog/browser-sdk/issues/${prNumber}/comments`, + { + method: 'GET', + headers: { + Authorization: `token ${GITHUB_TOKEN}`, + }, + } + ) + const comments = await response.json() + const targetComment = comments.find((comment) => comment.body.startsWith(`## ${PR_COMMENT_HEADER}`)) + if (targetComment !== undefined) { + return targetComment.id + } +} +async function updateOrAddComment(message, prNumber, commentId) { + const method = commentId ? 'PATCH' : 'POST' + const payload = { + pr_url: `https://github.com/DataDog/browser-sdk/pull/${prNumber}`, + message, + header: PR_COMMENT_HEADER, + org: 'DataDog', + repo: 'browser-sdk', + } + await fetchHandlingError('https://pr-commenter.us1.ddbuild.io/internal/cit/pr-comment', { + method, + headers: { + Authorization: `Bearer ${PR_COMMENTER_AUTH_TOKEN}`, + }, + body: JSON.stringify(payload), + }) +} + +function createMessage( + differenceBundle, + differenceCpu, + baseBundleSizes, + localBundleSizes, + cpuBasePerformance, + cpuLocalPerformance +) { + let message = + '| šŸ“¦ Bundle Name| Base Size | Local Size | šš« | šš«% | Status |\n| --- | --- | --- | --- | --- | :---: |\n' + let highIncreaseDetected = false + differenceBundle.forEach((diff, index) => { + const baseSize = formatSize(baseBundleSizes[index].value) + const localSize = formatSize(localBundleSizes[diff.name]) + const diffSize = formatSize(diff.change) + const sign = diff.percentageChange > 0 ? '+' : '' + let status = 'āœ…' + if (diff.percentageChange > SIZE_INCREASE_THRESHOLD) { + status = 'āš ļø' + highIncreaseDetected = true + } + message += `| ${formatBundleName(diff.name)} | ${baseSize} | ${localSize} | ${diffSize} | ${sign}${diff.percentageChange}% | ${status} |\n` + }) + + if (highIncreaseDetected) { + message += `\nāš ļø The increase is particularly high and exceeds ${SIZE_INCREASE_THRESHOLD}%. Please check the changes.` + } + message += '\n\n
\nšŸš€ CPU Performance\n\n\n' + message += + '| Action Name | Base Average Cpu Time (ms) | Local Average Cpu Time (ms) | šš« |\n| --- | --- | --- | --- |\n' + cpuBasePerformance.forEach((cpuActionPerformance, index) => { + const localCpuPerf = cpuLocalPerformance[index] + const diffCpuPerf = differenceCpu[index] + const baseCpuTaskValue = cpuActionPerformance.value !== null ? cpuActionPerformance.value.toFixed(3) : 'N/A' + const localCpuTaskValue = localCpuPerf.value !== null ? localCpuPerf.value.toFixed(3) : 'N/A' + const diffCpuTaskValue = diffCpuPerf.change !== null ? diffCpuPerf.change.toFixed(3) : 'N/A' + message += `| ${cpuActionPerformance.name} | ${baseCpuTaskValue} | ${localCpuTaskValue} | ${diffCpuTaskValue} |\n` + }) + message += '\n
\n' + + return message +} + +function formatBundleName(bundleName) { + return bundleName + .split('_') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' ') +} + +function formatSize(bytes) { + if (bytes < 1024) { + return `${bytes} B` + } + + return `${(bytes / 1024).toFixed(2)} KiB` +} + +module.exports = { + LOCAL_COMMIT_SHA, + reportAsPrComment, +} diff --git a/scripts/report-bundle-size/report-to-datadog.js b/scripts/performance/report-to-datadog.js similarity index 90% rename from scripts/report-bundle-size/report-to-datadog.js rename to scripts/performance/report-to-datadog.js index 0e83b24e12..9c99dc3363 100644 --- a/scripts/report-bundle-size/report-to-datadog.js +++ b/scripts/performance/report-to-datadog.js @@ -1,14 +1,13 @@ const { fetchHandlingError } = require('../lib/execution-utils') const { getOrg2ApiKey } = require('../lib/secrets') const { browserSdkVersion } = require('../lib/browser-sdk-version') - const LOG_INTAKE_URL = 'https://http-intake.logs.datadoghq.com/api/v2/logs' const LOG_INTAKE_REQUEST_HEADERS = { 'DD-API-KEY': getOrg2ApiKey(), 'Content-Type': 'application/json', } -async function reportBundleSizesToDatadog(bundleSizes) { +async function reportToDatadog(bundleSizes) { const logData = createLogData(bundleSizes, browserSdkVersion) await sendLogToOrg2(logData) } @@ -27,7 +26,6 @@ function createLogData(bundleSizes, browserSdkVersion) { }, ] } - async function sendLogToOrg2(bundleData = {}) { await fetchHandlingError(LOG_INTAKE_URL, { method: 'POST', @@ -36,4 +34,6 @@ async function sendLogToOrg2(bundleData = {}) { }) } -module.exports = { reportBundleSizesToDatadog } +module.exports = { + reportToDatadog, +} diff --git a/scripts/report-bundle-size/index.js b/scripts/report-bundle-size/index.js deleted file mode 100644 index dab2160943..0000000000 --- a/scripts/report-bundle-size/index.js +++ /dev/null @@ -1,30 +0,0 @@ -const path = require('path') -const fs = require('fs') -const { runMain } = require('../lib/execution-utils') -const { reportBundleSizesAsPrComment } = require('./report-as-a-pr-comment') -const { reportBundleSizesToDatadog } = require('./report-to-datadog') - -const rumPath = path.join(__dirname, '../../packages/rum/bundle/datadog-rum.js') -const logsPath = path.join(__dirname, '../../packages/logs/bundle/datadog-logs.js') -const rumSlimPath = path.join(__dirname, '../../packages/rum-slim/bundle/datadog-rum-slim.js') -const workerPath = path.join(__dirname, '../../packages/worker/bundle/worker.js') - -runMain(async () => { - const bundleSizes = { - rum: getBundleSize(rumPath), - logs: getBundleSize(logsPath), - rum_slim: getBundleSize(rumSlimPath), - worker: getBundleSize(workerPath), - } - await reportBundleSizesToDatadog(bundleSizes) - await reportBundleSizesAsPrComment(bundleSizes) -}) - -function getBundleSize(pathBundle) { - try { - const file = fs.statSync(pathBundle) - return file.size - } catch (error) { - throw new Error('Failed to get bundle size', { cause: error }) - } -} diff --git a/scripts/report-bundle-size/report-as-a-pr-comment.js b/scripts/report-bundle-size/report-as-a-pr-comment.js deleted file mode 100644 index 062f0a4968..0000000000 --- a/scripts/report-bundle-size/report-as-a-pr-comment.js +++ /dev/null @@ -1,181 +0,0 @@ -const { command } = require('../lib/command') -const { fetchHandlingError } = require('../lib/execution-utils') -const { getOrg2ApiKey, getGithubAccessToken, getOrg2AppKey } = require('../lib/secrets') - -const PR_COMMENT_HEADER = 'Bundles Sizes Evolution' -const BASE_BRANCH = process.env.MAIN_BRANCH -const LOCAL_BRANCH = process.env.CI_COMMIT_REF_NAME -const PR_COMMENTER_AUTH_TOKEN = command`authanywhere`.run().split(' ')[2].trim() -const GITHUB_TOKEN = getGithubAccessToken() -const ONE_DAY_IN_SECOND = 24 * 60 * 60 -// The value is set to 5% as it's around 10 times the average value for small PRs. -const SIZE_INCREASE_THRESHOLD = 5 - -async function reportBundleSizesAsPrComment(localBundleSizes) { - const lastCommonCommit = getLastCommonCommit(BASE_BRANCH, LOCAL_BRANCH) - const pr = await fetchPR(LOCAL_BRANCH) - if (!pr) { - console.log('No pull requests found for the branch') - return - } - const packageNames = Object.keys(localBundleSizes) - const mainBranchBundleSizes = await fetchAllPackagesBaseBundleSize(packageNames, lastCommonCommit) - const difference = compare(mainBranchBundleSizes, localBundleSizes) - const commentId = await retrieveExistingCommentId(pr.number) - await updateOrAddComment(difference, mainBranchBundleSizes, localBundleSizes, pr.number, commentId) -} - -function getLastCommonCommit(baseBranch) { - try { - command`git fetch --depth=100 origin ${baseBranch}`.run() - const commandOutput = command`git merge-base origin/${baseBranch} HEAD`.run() - // SHA commit is truncated to 8 characters as bundle sizes commit are exported in short format to logs for convenience and readability. - return commandOutput.trim().substring(0, 8) - } catch (error) { - throw new Error('Failed to get last common commit', { cause: error }) - } -} - -async function fetchPR(localBranch) { - const response = await fetchHandlingError( - `https://api.github.com/repos/DataDog/browser-sdk/pulls?head=DataDog:${localBranch}`, - { - method: 'GET', - headers: { - Authorization: `token ${GITHUB_TOKEN}`, - }, - } - ) - const pr = response.body ? await response.json() : null - if (pr && pr.length > 1) { - throw new Error('Multiple pull requests found for the branch') - } - return pr ? pr[0] : null -} - -function fetchAllPackagesBaseBundleSize(packageNames, commitSha) { - return Promise.all(packageNames.map((packageName) => fetchBundleSizesMetric(packageName, commitSha))) -} - -async function fetchBundleSizesMetric(packageName, commitSha) { - const now = Math.floor(Date.now() / 1000) - const date = now - 30 * ONE_DAY_IN_SECOND - const query = `avg:bundle_sizes.${packageName}{commit:${commitSha}}&from=${date}&to=${now}` - - const response = await fetchHandlingError(`https://api.datadoghq.com/api/v1/query?query=${query}`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - 'DD-API-KEY': getOrg2ApiKey(), - 'DD-APPLICATION-KEY': getOrg2AppKey(), - }, - }) - const data = await response.json() - if (data.series && data.series.length > 0 && data.series[0].pointlist && data.series[0].pointlist.length > 0) { - return { - name: packageName, - size: data.series[0].pointlist[0][1], - } - } - return { - name: packageName, - size: null, - } -} - -function compare(resultsBaseQuery, localBundleSizes) { - return resultsBaseQuery.map((baseResult) => { - const localSize = localBundleSizes[baseResult.name] - let change = null - let percentageChange = null - - if (baseResult.size && localSize) { - change = localSize - baseResult.size - percentageChange = ((change / baseResult.size) * 100).toFixed(2) - } - - return { - name: baseResult.name, - change, - percentageChange, - } - }) -} - -async function retrieveExistingCommentId(prNumber) { - const response = await fetchHandlingError( - `https://api.github.com/repos/DataDog/browser-sdk/issues/${prNumber}/comments`, - { - method: 'GET', - headers: { - Authorization: `token ${GITHUB_TOKEN}`, - }, - } - ) - const comments = await response.json() - const targetComment = comments.find((comment) => comment.body.startsWith(`## ${PR_COMMENT_HEADER}`)) - if (targetComment !== undefined) { - return targetComment.id - } -} -async function updateOrAddComment(difference, resultsBaseQuery, localBundleSizes, prNumber, commentId) { - const message = createMessage(difference, resultsBaseQuery, localBundleSizes) - const method = commentId ? 'PATCH' : 'POST' - const payload = { - pr_url: `https://github.com/DataDog/browser-sdk/pull/${prNumber}`, - message, - header: PR_COMMENT_HEADER, - org: 'DataDog', - repo: 'browser-sdk', - } - await fetchHandlingError('https://pr-commenter.us1.ddbuild.io/internal/cit/pr-comment', { - method, - headers: { - Authorization: `Bearer ${PR_COMMENTER_AUTH_TOKEN}`, - }, - body: JSON.stringify(payload), - }) -} - -function createMessage(difference, resultsBaseQuery, localBundleSizes) { - let message = - '| šŸ“¦ Bundle Name| Base Size | Local Size | šš« | šš«% | Status |\n| --- | --- | --- | --- | --- | :---: |\n' - let highIncreaseDetected = false - difference.forEach((diff, index) => { - const baseSize = formatSize(resultsBaseQuery[index].size) - const localSize = formatSize(localBundleSizes[diff.name]) - const diffSize = formatSize(diff.change) - const sign = diff.percentageChange > 0 ? '+' : '' - let status = 'āœ…' - if (diff.percentageChange > SIZE_INCREASE_THRESHOLD) { - status = 'āš ļø' - highIncreaseDetected = true - } - message += `| ${formatBundleName(diff.name)} | ${baseSize} | ${localSize} | ${diffSize} | ${sign}${diff.percentageChange}% | ${status} |\n` - }) - - if (highIncreaseDetected) { - message += `\nāš ļø The increase is particularly high and exceeds ${SIZE_INCREASE_THRESHOLD}%. Please check the changes.` - } - - return message -} - -function formatBundleName(bundleName) { - return bundleName - .split('_') - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' ') -} - -function formatSize(bytes) { - if (bytes < 1024) { - return `${bytes} B` - } - - return `${(bytes / 1024).toFixed(2)} KiB` -} - -module.exports = { - reportBundleSizesAsPrComment, -} diff --git a/test/e2e/scenario/recorder/shadowDom.scenario.ts b/test/e2e/scenario/recorder/shadowDom.scenario.ts index 8318727383..3d56264ddd 100644 --- a/test/e2e/scenario/recorder/shadowDom.scenario.ts +++ b/test/e2e/scenario/recorder/shadowDom.scenario.ts @@ -1,11 +1,17 @@ -import type { DocumentFragmentNode, MouseInteractionData, SerializedNodeWithId } from '@datadog/browser-rum/src/types' -import { MouseInteractionType, NodeType } from '@datadog/browser-rum/src/types' +import type { + DocumentFragmentNode, + MouseInteractionData, + ScrollData, + SerializedNodeWithId, +} from '@datadog/browser-rum/src/types' +import { IncrementalSource, MouseInteractionType, NodeType } from '@datadog/browser-rum/src/types' import { createMutationPayloadValidatorFromSegment, findElementWithIdAttribute, findElementWithTagName, findFullSnapshot, + findIncrementalSnapshot, findMouseInteractionRecords, findNode, findTextContent, @@ -80,6 +86,49 @@ const divShadowDom = ` ` +/** Will generate the following HTML + * ```html + * + * #shadow-root + *
+ *
+ *
+ * + *
+ *``` + when called like `` + */ +const scrollableDivShadowDom = ` + ` + /** Will generate the following HTML * ```html * @@ -244,6 +293,31 @@ describe('recorder with shadow DOM', () => { ], }) }) + + createTest('can record scroll from inside the shadow root') + .withRum({}) + .withSetup(bundleSetup) + .withBody(html` + ${scrollableDivShadowDom} + + `) + .run(async ({ intakeRegistry }) => { + const button = await getNodeInsideShadowDom('my-scrollable-div', 'button') + + // Triggering scrollTo from the test itself is not allowed + // Thus, a callback to scroll the div was added to the button 'click' event + await button.click() + + await flushEvents() + expect(intakeRegistry.replaySegments.length).toBe(1) + const scrollRecord = findIncrementalSnapshot(intakeRegistry.replaySegments[0], IncrementalSource.Scroll) + const fullSnapshot = findFullSnapshot(intakeRegistry.replaySegments[0])! + const divNode = findElementWithIdAttribute(fullSnapshot.data.node, 'scrollable-div')! + + expect(scrollRecord).toBeTruthy() + expect((scrollRecord?.data as ScrollData).id).toBe(divNode.id) + expect((scrollRecord?.data as ScrollData).y).toBe(250) + }) }) function findElementsInShadowDom(node: SerializedNodeWithId, id: string) { diff --git a/yarn.lock b/yarn.lock index 0fcf7c016d..acc459371e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -372,20 +372,20 @@ __metadata: "@datadog/browser-core": "workspace:*" "@datadog/browser-logs": "workspace:*" "@datadog/browser-rum": "workspace:*" - "@mantine/core": 7.7.1 - "@mantine/hooks": 7.7.1 - "@tabler/icons-react": 3.1.0 + "@mantine/core": 7.8.0 + "@mantine/hooks": 7.8.0 + "@tabler/icons-react": 3.2.0 "@types/chrome": 0.0.266 - "@types/react": 18.2.74 - "@types/react-dom": 18.2.24 + "@types/react": 18.2.79 + "@types/react-dom": 18.2.25 "@webextension-toolbox/webpack-webextension-plugin": 3.3.1 clsx: 2.1.0 copy-webpack-plugin: 12.0.2 - css-loader: 7.1.0 + css-loader: 6.11.0 html-webpack-plugin: 5.6.0 react: 18.2.0 react-dom: 18.2.0 - style-loader: 3.3.4 + style-loader: 4.0.0 webpack: 5.91.0 languageName: unknown linkType: soft @@ -428,10 +428,10 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.5.1, @eslint-community/regexpp@npm:^4.6.1": - version: 4.8.0 - resolution: "@eslint-community/regexpp@npm:4.8.0" - checksum: 601e6d033d556e98e8c929905bef335f20d7389762812df4d0f709d9b4d2631610dda975fb272e23b5b68e24a163b3851b114c8080a0a19fb4c141a1eff6305b +"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.6.1": + version: 4.10.0 + resolution: "@eslint-community/regexpp@npm:4.10.0" + checksum: 2a6e345429ea8382aaaf3a61f865cae16ed44d31ca917910033c02dc00d505d939f10b81e079fa14d43b51499c640138e153b7e40743c4c094d9df97d4e56f7b languageName: node linkType: hard @@ -766,9 +766,9 @@ __metadata: languageName: node linkType: hard -"@mantine/core@npm:7.7.1": - version: 7.7.1 - resolution: "@mantine/core@npm:7.7.1" +"@mantine/core@npm:7.8.0": + version: 7.8.0 + resolution: "@mantine/core@npm:7.8.0" dependencies: "@floating-ui/react": ^0.26.9 clsx: 2.1.0 @@ -777,19 +777,19 @@ __metadata: react-textarea-autosize: 8.5.3 type-fest: ^4.12.0 peerDependencies: - "@mantine/hooks": 7.7.1 + "@mantine/hooks": 7.8.0 react: ^18.2.0 react-dom: ^18.2.0 - checksum: d3860a2ab7bbe02a32c0afb94f7fbe24b6c3cf24803a594b3aed492bb6730756758068cad049ac8a25a9641e48b986d4ef71e8bd517441985cf41f29960d4fce + checksum: 17dc89759e24881d9939d0a6225edc62cd6bfb8fb99a06499d4103c500246f79b87b1465765fd7edc9d38399c6880ee57e4a747faf9e42da1577ec94b7fe3f47 languageName: node linkType: hard -"@mantine/hooks@npm:7.7.1": - version: 7.7.1 - resolution: "@mantine/hooks@npm:7.7.1" +"@mantine/hooks@npm:7.8.0": + version: 7.8.0 + resolution: "@mantine/hooks@npm:7.8.0" peerDependencies: react: ^18.2.0 - checksum: 7f9e6f32783aa051ad9eef14353a1202c501fc23e7ec1f85dbaf7c497b996d9b00e8042dc25bc7929940e7111da3c9f2421ef0dc98a6e11c9babfe69b1aeb6ea + checksum: e8041d55e4f56731c210d386051b93a242350d8e79c8a61b642d761e432e393cc1067901335d9e9fdb70d21e326584ee291afde5c4e1a332a4dad937f8da07d5 languageName: node linkType: hard @@ -1261,9 +1261,9 @@ __metadata: languageName: node linkType: hard -"@puppeteer/browsers@npm:2.2.1": - version: 2.2.1 - resolution: "@puppeteer/browsers@npm:2.2.1" +"@puppeteer/browsers@npm:2.2.2": + version: 2.2.2 + resolution: "@puppeteer/browsers@npm:2.2.2" dependencies: debug: 4.3.4 extract-zip: 2.0.1 @@ -1275,7 +1275,7 @@ __metadata: yargs: 17.7.2 bin: browsers: lib/cjs/main-cli.js - checksum: c2ec8bac9978ae6279d67442a3a81c517db1e172bd4603d1983eb48de12b088d8b565b1817abe21ba6df76be9a68e3fc543d4c7111c964a93f3ac9b14f7c76e5 + checksum: 328a10ceb432784ec4cd524c461799936603b8436e50eed6a61127022f4c8a36ba31143b0d4d311190d619968f2e9db9fa7ac046757cff2c9f81d301110560be languageName: node linkType: hard @@ -1377,21 +1377,21 @@ __metadata: languageName: node linkType: hard -"@tabler/icons-react@npm:3.1.0": - version: 3.1.0 - resolution: "@tabler/icons-react@npm:3.1.0" +"@tabler/icons-react@npm:3.2.0": + version: 3.2.0 + resolution: "@tabler/icons-react@npm:3.2.0" dependencies: - "@tabler/icons": 3.1.0 + "@tabler/icons": 3.2.0 peerDependencies: react: ">= 16" - checksum: 8f82ea699f548450801d8b7bbe1252850304a425b79c62807118432af907b8239ce7a4524dc28f81278d947ea0b2b32f3f7a4601f4c1e5585ba5446326b354ec + checksum: 4412ecc1f3a3a2a4b85f122365ccfa7fabb1c84a7e40a6f2f31f155e12cf43b5772f84a079281b50d61f3dc916e8ff2c26f14b07c36b082f9937695220bb79cb languageName: node linkType: hard -"@tabler/icons@npm:3.1.0": - version: 3.1.0 - resolution: "@tabler/icons@npm:3.1.0" - checksum: 3dd9c54ec46dbf12571cc8a72e10f99f1b84e99eeb53170254e1ab0939b3d37b9c8031a5e6534315b7d3107e0724f7fae61749f9c159ac8fd34cf8d3b9f5e6cc +"@tabler/icons@npm:3.2.0": + version: 3.2.0 + resolution: "@tabler/icons@npm:3.2.0" + checksum: 61085d3c268269445ac5df7174aed6837e968b14d18255d057c86301226472cbd55912767cbb154ce6afd4482293d2237f5c1b80348b29e02c45535dd8b87352 languageName: node linkType: hard @@ -1690,10 +1690,10 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.11, @types/json-schema@npm:^7.0.12, @types/json-schema@npm:^7.0.5, @types/json-schema@npm:^7.0.6, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": - version: 7.0.12 - resolution: "@types/json-schema@npm:7.0.12" - checksum: 00239e97234eeb5ceefb0c1875d98ade6e922bfec39dd365ec6bd360b5c2f825e612ac4f6e5f1d13601b8b30f378f15e6faa805a3a732f4a1bbe61915163d293 +"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.11, @types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.5, @types/json-schema@npm:^7.0.6, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": + version: 7.0.15 + resolution: "@types/json-schema@npm:7.0.15" + checksum: 97ed0cb44d4070aecea772b7b2e2ed971e10c81ec87dd4ecc160322ffa55ff330dace1793489540e3e318d90942064bb697cc0f8989391797792d919737b3b98 languageName: node linkType: hard @@ -1755,12 +1755,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:20.12.5, @types/node@npm:>=10.0.0, @types/node@npm:^20.1.0, @types/node@npm:^20.1.1, @types/node@npm:^20.11.28": - version: 20.12.5 - resolution: "@types/node@npm:20.12.5" +"@types/node@npm:*, @types/node@npm:20.12.7, @types/node@npm:>=10.0.0, @types/node@npm:^20.1.0, @types/node@npm:^20.1.1, @types/node@npm:^20.11.28": + version: 20.12.7 + resolution: "@types/node@npm:20.12.7" dependencies: undici-types: ~5.26.4 - checksum: 38358c091392bb3def1136772ada4ccd39a9429d459160b5ab728b690d2f15f2212eafd9e65ce716e270f70a4e6927ebffccfefc08dabdf68f4016c1fc8a7938 + checksum: 7cc979f7e2ca9a339ec71318c3901b9978555257929ef3666987f3e447123bc6dc92afcc89f6347e09e07d602fde7d51bcddea626c23aa2bb74aeaacfd1e1686 languageName: node linkType: hard @@ -1806,29 +1806,29 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:18.2.24": - version: 18.2.24 - resolution: "@types/react-dom@npm:18.2.24" +"@types/react-dom@npm:18.2.25": + version: 18.2.25 + resolution: "@types/react-dom@npm:18.2.25" dependencies: "@types/react": "*" - checksum: 7fb0dd0c88c5219bcc27f1f9bf14d9e1b1593014ee7938dd46ee9782c77c39d1ccc79d2b8364a6113019d7f65c94e4dc3c37425c3972910bb2674cce98d3f3ca + checksum: 85f9278d6456c6cdc76da6806a33b472588cdd029b08dde32e8b5636b25a3eae529b4ac2e08c848a3d7ca44e4e97ee9a3df406c96fa0768de935c8eed6e07590 languageName: node linkType: hard -"@types/react@npm:*, @types/react@npm:18.2.74": - version: 18.2.74 - resolution: "@types/react@npm:18.2.74" +"@types/react@npm:*, @types/react@npm:18.2.79": + version: 18.2.79 + resolution: "@types/react@npm:18.2.79" dependencies: "@types/prop-types": "*" csstype: ^3.0.2 - checksum: 093c0e350552e61393e2ba30169aa620e2e64c1e2d0ff38efd2a7549ded689b6ab6bffb65fe0f7ef9e143174de54442d942bd70c014649f464c52465701208d8 + checksum: 85aa96e0e88725c84d8fc5f04f10a4da6a1f507dde33557ac9cc211414756867721264bfefd9e02bae1288ce2905351d949b652b931e734ea24519ee5c625138 languageName: node linkType: hard -"@types/semver@npm:^7.5.0": - version: 7.5.1 - resolution: "@types/semver@npm:7.5.1" - checksum: 2fffe938c7ac168711f245a16e1856a3578d77161ca17e29a05c3e02c7be3e9c5beefa29a3350f6c1bd982fb70aa28cc52e4845eb7d36246bcdc0377170d584d +"@types/semver@npm:^7.5.8": + version: 7.5.8 + resolution: "@types/semver@npm:7.5.8" + checksum: ea6f5276f5b84c55921785a3a27a3cd37afee0111dfe2bcb3e03c31819c197c782598f17f0b150a69d453c9584cd14c4c4d7b9a55d2c5e6cacd4d66fdb3b3663 languageName: node linkType: hard @@ -1907,126 +1907,126 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:7.5.0": - version: 7.5.0 - resolution: "@typescript-eslint/eslint-plugin@npm:7.5.0" +"@typescript-eslint/eslint-plugin@npm:7.7.0": + version: 7.7.0 + resolution: "@typescript-eslint/eslint-plugin@npm:7.7.0" dependencies: - "@eslint-community/regexpp": ^4.5.1 - "@typescript-eslint/scope-manager": 7.5.0 - "@typescript-eslint/type-utils": 7.5.0 - "@typescript-eslint/utils": 7.5.0 - "@typescript-eslint/visitor-keys": 7.5.0 + "@eslint-community/regexpp": ^4.10.0 + "@typescript-eslint/scope-manager": 7.7.0 + "@typescript-eslint/type-utils": 7.7.0 + "@typescript-eslint/utils": 7.7.0 + "@typescript-eslint/visitor-keys": 7.7.0 debug: ^4.3.4 graphemer: ^1.4.0 - ignore: ^5.2.4 + ignore: ^5.3.1 natural-compare: ^1.4.0 - semver: ^7.5.4 - ts-api-utils: ^1.0.1 + semver: ^7.6.0 + ts-api-utils: ^1.3.0 peerDependencies: "@typescript-eslint/parser": ^7.0.0 eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: 02ca180a4e46df840b84221219800cc5f6371254a37be7932a2586768925d8c8897b9eebe92d32093be254a98f10eae8b291638515edd79571826d6840044332 + checksum: f97348425d114282407f4f524cdc618199d0d6d86e4e556709063b07611192068872cbd7f612cbd670617d958ee4519b25eeca0bccbac1b08433ce41511d3825 languageName: node linkType: hard -"@typescript-eslint/parser@npm:7.5.0": - version: 7.5.0 - resolution: "@typescript-eslint/parser@npm:7.5.0" +"@typescript-eslint/parser@npm:7.7.0": + version: 7.7.0 + resolution: "@typescript-eslint/parser@npm:7.7.0" dependencies: - "@typescript-eslint/scope-manager": 7.5.0 - "@typescript-eslint/types": 7.5.0 - "@typescript-eslint/typescript-estree": 7.5.0 - "@typescript-eslint/visitor-keys": 7.5.0 + "@typescript-eslint/scope-manager": 7.7.0 + "@typescript-eslint/types": 7.7.0 + "@typescript-eslint/typescript-estree": 7.7.0 + "@typescript-eslint/visitor-keys": 7.7.0 debug: ^4.3.4 peerDependencies: eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: c9f85ae638e27fb249e565fc299cfee456b15f6a67156f373c10fa6e310a470b5298e174ca75309789c4cf1acd1a9fd3391d5fe128208b05bac0b701d5ddf512 + checksum: 44dc88eae2fafd3ae92338683d70d077d0c4342dcd4949dc05ffa7686de2753b8565c643eedb3bf1c6b6c1b4a2f0849000474b8e70572184ebe366b0b1dbb1ee languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:7.5.0": - version: 7.5.0 - resolution: "@typescript-eslint/scope-manager@npm:7.5.0" +"@typescript-eslint/scope-manager@npm:7.7.0": + version: 7.7.0 + resolution: "@typescript-eslint/scope-manager@npm:7.7.0" dependencies: - "@typescript-eslint/types": 7.5.0 - "@typescript-eslint/visitor-keys": 7.5.0 - checksum: 13d858ca9f77922578b154b8568ca172dc8bd3a557ec60b4d027174675e243e34f189127ae073b052c78a96aca2cbad098318388db8723bce25d0a63dd0ba8e3 + "@typescript-eslint/types": 7.7.0 + "@typescript-eslint/visitor-keys": 7.7.0 + checksum: cb280d4aa64cdefee362ef97b6fde3ae86a376fccff7f012e4e635ffe544dd90be37b340c7099784d0fbebb37b925aab6b53195825b41cee38e2382d0b552871 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:7.5.0": - version: 7.5.0 - resolution: "@typescript-eslint/type-utils@npm:7.5.0" +"@typescript-eslint/type-utils@npm:7.7.0": + version: 7.7.0 + resolution: "@typescript-eslint/type-utils@npm:7.7.0" dependencies: - "@typescript-eslint/typescript-estree": 7.5.0 - "@typescript-eslint/utils": 7.5.0 + "@typescript-eslint/typescript-estree": 7.7.0 + "@typescript-eslint/utils": 7.7.0 debug: ^4.3.4 - ts-api-utils: ^1.0.1 + ts-api-utils: ^1.3.0 peerDependencies: eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: 9dce5f7f9981febd5967c3c45266787ddf5805fcef40fd2cb644503a32aee4d06de77d64dc3903e61caff3a1a76817c0b3be35703401ddf1bec84c76757f0f69 + checksum: 74c07e4fcc8e6ee7870a161596d25ecfa22624947d94ca9af7147590caa13b6388f0e55101961ab02f77e7e6cffdaf19895575d7329dda50fa18fc71bf15f6b7 languageName: node linkType: hard -"@typescript-eslint/types@npm:7.5.0": - version: 7.5.0 - resolution: "@typescript-eslint/types@npm:7.5.0" - checksum: 038e5b10680fd32da8455d0590437888f57511ed8c2b984511890d6bfc0b230b269bc3de6970cc76660e8fd65657e201316bc4abd9758a2d6efb49d659dd4199 +"@typescript-eslint/types@npm:7.7.0": + version: 7.7.0 + resolution: "@typescript-eslint/types@npm:7.7.0" + checksum: c47aae2c1474b85fab012e0518c57685c595f11775b615b6a6749f943aa7a98554d9eb7054114850679f46699578049998408a492e0c1abd3bded2aee8e261a5 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:7.5.0": - version: 7.5.0 - resolution: "@typescript-eslint/typescript-estree@npm:7.5.0" +"@typescript-eslint/typescript-estree@npm:7.7.0": + version: 7.7.0 + resolution: "@typescript-eslint/typescript-estree@npm:7.7.0" dependencies: - "@typescript-eslint/types": 7.5.0 - "@typescript-eslint/visitor-keys": 7.5.0 + "@typescript-eslint/types": 7.7.0 + "@typescript-eslint/visitor-keys": 7.7.0 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 - minimatch: 9.0.3 - semver: ^7.5.4 - ts-api-utils: ^1.0.1 + minimatch: ^9.0.4 + semver: ^7.6.0 + ts-api-utils: ^1.3.0 peerDependenciesMeta: typescript: optional: true - checksum: ebc6838af9cf25be3c07ba4ea91bbbc2450d835eeb775139a0ed6344baf193824b0fd5319c0fe94d55c822a54670344655937894f6d0dc77bfbfcdc0493e567d + checksum: 54d16b2a083bff3c6d38fbee56465403bbcba411bf25e94f2d8bbbbd8b4b35c151c7845997e5141224f8dba5bc1f34964762713035d49113700efd7381246d02 languageName: node linkType: hard -"@typescript-eslint/utils@npm:7.5.0": - version: 7.5.0 - resolution: "@typescript-eslint/utils@npm:7.5.0" +"@typescript-eslint/utils@npm:7.7.0": + version: 7.7.0 + resolution: "@typescript-eslint/utils@npm:7.7.0" dependencies: "@eslint-community/eslint-utils": ^4.4.0 - "@types/json-schema": ^7.0.12 - "@types/semver": ^7.5.0 - "@typescript-eslint/scope-manager": 7.5.0 - "@typescript-eslint/types": 7.5.0 - "@typescript-eslint/typescript-estree": 7.5.0 - semver: ^7.5.4 + "@types/json-schema": ^7.0.15 + "@types/semver": ^7.5.8 + "@typescript-eslint/scope-manager": 7.7.0 + "@typescript-eslint/types": 7.7.0 + "@typescript-eslint/typescript-estree": 7.7.0 + semver: ^7.6.0 peerDependencies: eslint: ^8.56.0 - checksum: 03d5563f50da5f35c84c7ea9dd1c671afa668d614eacb3dff38a94b9f399bd4e371348f6a2feb1cbf0fcb3e3e6d0f662cef8d93a9f3a29ea9a4176aa3abc4902 + checksum: 830ff3af96538083d7513c211e39f07375b7e973c135a2b9bbae1ad7509bd4dce33a144a22d896a2ff4c18e9fcccd423535b6f9bb8adafe36e800f16bc53378c languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:7.5.0": - version: 7.5.0 - resolution: "@typescript-eslint/visitor-keys@npm:7.5.0" +"@typescript-eslint/visitor-keys@npm:7.7.0": + version: 7.7.0 + resolution: "@typescript-eslint/visitor-keys@npm:7.7.0" dependencies: - "@typescript-eslint/types": 7.5.0 - eslint-visitor-keys: ^3.4.1 - checksum: 8de0d3fb470f60b3aca7073ff62c7f9fe078d77c48d43033622ff059f201223fe35eaf834a6b63d95ee5d4c0e2e13669de3f20e4f4de597596dcf63537a60693 + "@typescript-eslint/types": 7.7.0 + eslint-visitor-keys: ^3.4.3 + checksum: 16d0b63b9d98ea1d3d20bd6f9dc3cbd2674055845ad493d98118669d54792b1c167f57ae25beaae2c1107ed07012ac3c3093cca978b2ab49833dc491bc302b33 languageName: node linkType: hard @@ -2048,16 +2048,16 @@ __metadata: languageName: node linkType: hard -"@wdio/browserstack-service@npm:8.35.1": - version: 8.35.1 - resolution: "@wdio/browserstack-service@npm:8.35.1" +"@wdio/browserstack-service@npm:8.36.0": + version: 8.36.0 + resolution: "@wdio/browserstack-service@npm:8.36.0" dependencies: "@percy/appium-app": ^2.0.1 "@percy/selenium-webdriver": ^2.0.3 "@types/gitconfiglocal": ^2.0.1 "@wdio/logger": 8.28.0 - "@wdio/reporter": 8.32.4 - "@wdio/types": 8.32.4 + "@wdio/reporter": 8.36.0 + "@wdio/types": 8.36.0 browserstack-local: ^1.5.1 chalk: ^5.3.0 csv-writer: ^1.6.0 @@ -2066,27 +2066,27 @@ __metadata: gitconfiglocal: ^2.1.0 got: ^12.6.1 uuid: ^9.0.0 - webdriverio: 8.35.1 + webdriverio: 8.36.0 winston-transport: ^4.5.0 yauzl: ^3.0.0 peerDependencies: "@wdio/cli": ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 76c3b6bf58d79be36e0f1b5f0db8465c9c5c355cdb9a3a275088c587730ad3fa410a60a9fcc5382dd4e0fa71dba8a2101783ea1dc7568f84b197bcad6aafbf41 + checksum: 38718cb71794f4e22a214b6f345d38c42e6a03b87155487e01860051158f91209eb9f2e286bbfd620f4716600910b2e8da3dafb5b55429451dd327458a7e59c0 languageName: node linkType: hard -"@wdio/cli@npm:8.35.1": - version: 8.35.1 - resolution: "@wdio/cli@npm:8.35.1" +"@wdio/cli@npm:8.36.0": + version: 8.36.0 + resolution: "@wdio/cli@npm:8.36.0" dependencies: "@types/node": ^20.1.1 "@vitest/snapshot": ^1.2.1 - "@wdio/config": 8.35.0 - "@wdio/globals": 8.35.1 + "@wdio/config": 8.36.0 + "@wdio/globals": 8.36.0 "@wdio/logger": 8.28.0 "@wdio/protocols": 8.32.0 - "@wdio/types": 8.32.4 - "@wdio/utils": 8.35.0 + "@wdio/types": 8.36.0 + "@wdio/utils": 8.36.0 async-exit-hook: ^2.0.1 chalk: ^5.2.0 chokidar: ^3.5.3 @@ -2101,84 +2101,84 @@ __metadata: lodash.union: ^4.6.0 read-pkg-up: 10.0.0 recursive-readdir: ^2.2.3 - webdriverio: 8.35.1 + webdriverio: 8.36.0 yargs: ^17.7.2 bin: wdio: bin/wdio.js - checksum: 4371cff010e53ce74919a955c7d6635b6ff109151ba24611698c8bd10d1301374d15c7849b827917c5e3855aab1f9b58b3075af826c4869636338684ecfaf7b0 + checksum: 4899ff1ddf0df7ecc2b521b1c74809a9dd6a6641d3e88dfa7429fe6143b95f77000753cc969e6a59b2f1070681123a56633af588c91fb76232a43bc45fafe0df languageName: node linkType: hard -"@wdio/config@npm:8.35.0": - version: 8.35.0 - resolution: "@wdio/config@npm:8.35.0" +"@wdio/config@npm:8.36.0": + version: 8.36.0 + resolution: "@wdio/config@npm:8.36.0" dependencies: "@wdio/logger": 8.28.0 - "@wdio/types": 8.32.4 - "@wdio/utils": 8.35.0 + "@wdio/types": 8.36.0 + "@wdio/utils": 8.36.0 decamelize: ^6.0.0 deepmerge-ts: ^5.0.0 glob: ^10.2.2 import-meta-resolve: ^4.0.0 - checksum: 90df0ba14a48164b150e5e096ef3d6759cadeb64c6ffdce12b25763156eea7bf0da55cc7e3f44ea4e69030c0189116ee7662ab97a2de67615143afd32e7bfb01 + checksum: a96a26c0bb309b42c915f9568493e478a351937d9eec985301b0353f0da4d94a16086ec97f0d4caa08dc6b61be0513024897d28fe90783ea36c9a130cfab3db1 languageName: node linkType: hard -"@wdio/globals@npm:8.35.1, @wdio/globals@npm:^8.29.3": - version: 8.35.1 - resolution: "@wdio/globals@npm:8.35.1" +"@wdio/globals@npm:8.36.0, @wdio/globals@npm:^8.29.3": + version: 8.36.0 + resolution: "@wdio/globals@npm:8.36.0" dependencies: expect-webdriverio: ^4.11.2 - webdriverio: 8.35.1 + webdriverio: 8.36.0 dependenciesMeta: expect-webdriverio: optional: true webdriverio: optional: true - checksum: 6f46ca6e34251baf6794a0cd9739c119185a0de351785e135a5567fe1eec21281d26f09079df71a963c55ff687182685e18336cb5549914fb554ec95b8b99175 + checksum: f57ded6f8b945abcac1fffe77d30f71e85ea9321899f660ce492aa27825cd6814fda2b3b30c993a0a1c2cfb94ba5158ac0a8dd89a50e768fcc5ceefc851bf282 languageName: node linkType: hard -"@wdio/jasmine-framework@npm:8.35.1": - version: 8.35.1 - resolution: "@wdio/jasmine-framework@npm:8.35.1" +"@wdio/jasmine-framework@npm:8.36.0": + version: 8.36.0 + resolution: "@wdio/jasmine-framework@npm:8.36.0" dependencies: "@types/node": ^20.1.0 - "@wdio/globals": 8.35.1 + "@wdio/globals": 8.36.0 "@wdio/logger": 8.28.0 - "@wdio/types": 8.32.4 - "@wdio/utils": 8.35.0 + "@wdio/types": 8.36.0 + "@wdio/utils": 8.36.0 expect-webdriverio: ^4.11.2 jasmine: ^5.0.0 - checksum: 0ed8f60c0c591c916051a470afc65fb3e6ee7d0cd19b30805f5eee42e7027a3646f593b48659e086fefd3be35fc7ec25eb1a02475ca5a7d934773ec5578b7db1 + checksum: 08f60071c8dedff04d631d70cdd65ece063843ae0979ce61ed747aa1b99672c43ab3bd93ebf8c5283d6065ed92743baae8016836fe817956a57dac104f0fc31c languageName: node linkType: hard -"@wdio/junit-reporter@npm:8.32.4": - version: 8.32.4 - resolution: "@wdio/junit-reporter@npm:8.32.4" +"@wdio/junit-reporter@npm:8.36.0": + version: 8.36.0 + resolution: "@wdio/junit-reporter@npm:8.36.0" dependencies: - "@wdio/reporter": 8.32.4 - "@wdio/types": 8.32.4 + "@wdio/reporter": 8.36.0 + "@wdio/types": 8.36.0 json-stringify-safe: ^5.0.1 junit-report-builder: ^3.0.0 - checksum: 8e13e0597025e7d2d461c9c5f983ec195d6b29e355b370bf3a0d69ab352eba3aa9080e35a50a47d34d8b34aa9ed0ce5a8e32dad19c03df846da69898a475e1e7 + checksum: 5e4c9fdb1c4d347bd0ff3056e58ef829ae61f34464fd93856d82247e0d07c523d14db008b03734fce547a157398c2dbe56676657bc526b811cd7955f3b28984e languageName: node linkType: hard -"@wdio/local-runner@npm:8.35.1": - version: 8.35.1 - resolution: "@wdio/local-runner@npm:8.35.1" +"@wdio/local-runner@npm:8.36.0": + version: 8.36.0 + resolution: "@wdio/local-runner@npm:8.36.0" dependencies: "@types/node": ^20.1.0 "@wdio/logger": 8.28.0 "@wdio/repl": 8.24.12 - "@wdio/runner": 8.35.1 - "@wdio/types": 8.32.4 + "@wdio/runner": 8.36.0 + "@wdio/types": 8.36.0 async-exit-hook: ^2.0.1 split2: ^4.1.0 stream-buffers: ^3.0.2 - checksum: 2cca8ce9b6cfee44479cc49c09e394f6efd855d44fa4f856ea1c1b472270c0a6a32380165442fb8c807c2d16e93f26b94f45a8b30a71769a999c996d60b86e93 + checksum: 7d5d669762c659cc1e36d3c1adcdaf446bdbdb2a12b350ef0060c22b563bc5f829354066a175f95f2496215b6a9d5d9d2c13fffebbfed251a532fcdce139977b languageName: node linkType: hard @@ -2210,67 +2210,67 @@ __metadata: languageName: node linkType: hard -"@wdio/reporter@npm:8.32.4": - version: 8.32.4 - resolution: "@wdio/reporter@npm:8.32.4" +"@wdio/reporter@npm:8.36.0": + version: 8.36.0 + resolution: "@wdio/reporter@npm:8.36.0" dependencies: "@types/node": ^20.1.0 "@wdio/logger": 8.28.0 - "@wdio/types": 8.32.4 + "@wdio/types": 8.36.0 diff: ^5.0.0 object-inspect: ^1.12.0 - checksum: 4fd3a615ec658e1059f3104bc9b2b6c38fbe513fab8f5bc47045602fe919b1c955cec00a9f9e1d828d3e297c565a1f44437d4f6705bd2644a91bffbe6e211465 + checksum: 27b520ee30b1c38a744d9df6c5e4837d05ccf147dec0d07bca4ff289bbdc61caf9fe739b7f278adaf4325c00957b7175c4b36e971ed7a64be360271740885f8d languageName: node linkType: hard -"@wdio/runner@npm:8.35.1": - version: 8.35.1 - resolution: "@wdio/runner@npm:8.35.1" +"@wdio/runner@npm:8.36.0": + version: 8.36.0 + resolution: "@wdio/runner@npm:8.36.0" dependencies: "@types/node": ^20.11.28 - "@wdio/config": 8.35.0 - "@wdio/globals": 8.35.1 + "@wdio/config": 8.36.0 + "@wdio/globals": 8.36.0 "@wdio/logger": 8.28.0 - "@wdio/types": 8.32.4 - "@wdio/utils": 8.35.0 + "@wdio/types": 8.36.0 + "@wdio/utils": 8.36.0 deepmerge-ts: ^5.1.0 expect-webdriverio: ^4.12.0 gaze: ^1.1.3 - webdriver: 8.35.0 - webdriverio: 8.35.1 - checksum: 5c860caa0b6b08b40a569591a53f8afc7776b8dec89f8759006667696de1aba8bb9c29b54ec1fdd237cd57d57d6918e3d707169ce9364c4162956d40007638b4 + webdriver: 8.36.0 + webdriverio: 8.36.0 + checksum: e0faa34e8e8df75b806cf3271722bc62c002816ba03cac289e998a7518f1fcbbc4446871420bc395c47ba6a39a01d8d68d97ed999031598b7644832b9413d2f4 languageName: node linkType: hard -"@wdio/spec-reporter@npm:8.32.4": - version: 8.32.4 - resolution: "@wdio/spec-reporter@npm:8.32.4" +"@wdio/spec-reporter@npm:8.36.0": + version: 8.36.0 + resolution: "@wdio/spec-reporter@npm:8.36.0" dependencies: - "@wdio/reporter": 8.32.4 - "@wdio/types": 8.32.4 + "@wdio/reporter": 8.36.0 + "@wdio/types": 8.36.0 chalk: ^5.1.2 easy-table: ^1.2.0 pretty-ms: ^7.0.0 - checksum: d4d28f86de9665306447d1fc2d08e1695200a80de2d8e60d98b4636010923b8e614fdf83a91df128fc1abadfad404a1f8e3c30ddbd2b5f232c78caac5008b300 + checksum: 006aae9d25c374487e557a78cc6fa99adaf4d6e372f71ae0817603a09a513b292247d28ca7b39126dfb1a2b5cf1bfcd7d1b597e2f990c8e44e33747a19730d76 languageName: node linkType: hard -"@wdio/types@npm:8.32.4": - version: 8.32.4 - resolution: "@wdio/types@npm:8.32.4" +"@wdio/types@npm:8.36.0": + version: 8.36.0 + resolution: "@wdio/types@npm:8.36.0" dependencies: "@types/node": ^20.1.0 - checksum: c7f051f3e48dffb3f3963d71dcf16fa600b53333cf5ef66a501211e6f747389a6763b311dba6f81d60e8819498604dd4fbc341f5745e9a5a20e727ec746bb8d9 + checksum: cfaf9f9e188bb2bda4140e95615f3ae33dd894b82a2a11966215a6b58ce8e9a575d4356e9efe77a9982c26401b8987f60eb2def3bc9c505fbf1b4c6c3cdf546b languageName: node linkType: hard -"@wdio/utils@npm:8.35.0": - version: 8.35.0 - resolution: "@wdio/utils@npm:8.35.0" +"@wdio/utils@npm:8.36.0": + version: 8.36.0 + resolution: "@wdio/utils@npm:8.36.0" dependencies: "@puppeteer/browsers": ^1.6.0 "@wdio/logger": 8.28.0 - "@wdio/types": 8.32.4 + "@wdio/types": 8.36.0 decamelize: ^6.0.0 deepmerge-ts: ^5.1.0 edgedriver: ^5.3.5 @@ -2281,7 +2281,7 @@ __metadata: safaridriver: ^0.1.0 split2: ^4.2.0 wait-port: ^1.0.4 - checksum: 14c379b8ef80fce0909404756d015a06d7e3887088023f0ca53288c5370692be13b643a45aa60b66c427c8a5b789afbc8f9333a67a6a81a9b612d0858928e9bd + checksum: 25b5bdf226e9ad4077da72ccb0d05b6cc75e02000a8575221d22486875ee7eeb27565ecf7cdd5dd59b63c5614c80fe6bd46a9dc29107bd14590d68109c8ac18e languageName: node linkType: hard @@ -3236,14 +3236,14 @@ __metadata: "@types/cors": 2.8.17 "@types/express": 4.17.21 "@types/jasmine": 3.10.18 - "@typescript-eslint/eslint-plugin": 7.5.0 - "@typescript-eslint/parser": 7.5.0 - "@wdio/browserstack-service": 8.35.1 - "@wdio/cli": 8.35.1 - "@wdio/jasmine-framework": 8.35.1 - "@wdio/junit-reporter": 8.32.4 - "@wdio/local-runner": 8.35.1 - "@wdio/spec-reporter": 8.32.4 + "@typescript-eslint/eslint-plugin": 7.7.0 + "@typescript-eslint/parser": 7.7.0 + "@wdio/browserstack-service": 8.36.0 + "@wdio/cli": 8.36.0 + "@wdio/jasmine-framework": 8.36.0 + "@wdio/junit-reporter": 8.36.0 + "@wdio/local-runner": 8.36.0 + "@wdio/spec-reporter": 8.36.0 ajv: 6.12.6 browserstack-local: 1.5.5 chrome-webstore-upload: 3.0.3 @@ -3280,8 +3280,8 @@ __metadata: ts-loader: 9.5.1 ts-node: 10.9.2 tsconfig-paths-webpack-plugin: 4.1.0 - typescript: 5.4.4 - webdriverio: 8.35.1 + typescript: 5.4.5 + webdriverio: 8.36.0 webpack: 5.91.0 webpack-cli: 5.1.4 webpack-dev-middleware: 7.2.1 @@ -3681,16 +3681,16 @@ __metadata: languageName: node linkType: hard -"chromium-bidi@npm:0.5.16": - version: 0.5.16 - resolution: "chromium-bidi@npm:0.5.16" +"chromium-bidi@npm:0.5.17": + version: 0.5.17 + resolution: "chromium-bidi@npm:0.5.17" dependencies: mitt: 3.0.1 urlpattern-polyfill: 10.0.0 zod: 3.22.4 peerDependencies: devtools-protocol: "*" - checksum: 5206876e240549daadfa390ae6475e8c891a0557006e0dea13590e2f5ed26b560fa9cbf5c654dde0a95a2c13d606e527de745b52c25273b6c200d9cbca4e6522 + checksum: 522da996ed5abfb47707583cc24785f9aa05d87bd968dbd520f245cf8972fa3ec102f8d1d72fa07558daa70495d8c6f2bf364d8599eb60b77504e528601d8a30 languageName: node linkType: hard @@ -4288,9 +4288,9 @@ __metadata: languageName: node linkType: hard -"css-loader@npm:7.1.0": - version: 7.1.0 - resolution: "css-loader@npm:7.1.0" +"css-loader@npm:6.11.0": + version: 6.11.0 + resolution: "css-loader@npm:6.11.0" dependencies: icss-utils: ^5.1.0 postcss: ^8.4.33 @@ -4302,13 +4302,13 @@ __metadata: semver: ^7.5.4 peerDependencies: "@rspack/core": 0.x || 1.x - webpack: ^5.27.0 + webpack: ^5.0.0 peerDependenciesMeta: "@rspack/core": optional: true webpack: optional: true - checksum: 247b07ca3f1369ec41e3c79cc59c6d97f0a473d767bf9ee563a2ee7ab404ff7f65ed4a2a3a108721250bc4055df82be6a88225f42adb360714533e37ea032da0 + checksum: 5c8d35975a7121334905394e88e28f05df72f037dbed2fb8fec4be5f0b313ae73a13894ba791867d4a4190c35896da84a7fd0c54fb426db55d85ba5e714edbe3 languageName: node linkType: hard @@ -4637,10 +4637,10 @@ __metadata: languageName: node linkType: hard -"devtools-protocol@npm:^0.0.1273771": - version: 0.0.1273771 - resolution: "devtools-protocol@npm:0.0.1273771" - checksum: 2a88694ec0f2f167f826cea8c3d6030ede911c2db79d2a62d76d1be450bcb395e8283ca03f225fa308710ab06182dced47eed8cece56b377d1946403a321b64f +"devtools-protocol@npm:^0.0.1282316": + version: 0.0.1282316 + resolution: "devtools-protocol@npm:0.0.1282316" + checksum: aa29d78a5b31e9e0b1bc8f7554fe2e6f1840bc4f7975fdc109442f8ef798c93e0cdddea73536b1d3b106c0a7a4771d03f324d0af0dec162649d5f46fda51c4ed languageName: node linkType: hard @@ -7025,10 +7025,10 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.0.4, ignore@npm:^5.2.0, ignore@npm:^5.2.4": - version: 5.2.4 - resolution: "ignore@npm:5.2.4" - checksum: 3d4c309c6006e2621659311783eaea7ebcd41fe4ca1d78c91c473157ad6666a57a2df790fe0d07a12300d9aac2888204d7be8d59f9aaf665b1c7fcdb432517ef +"ignore@npm:^5.0.4, ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:^5.3.1": + version: 5.3.1 + resolution: "ignore@npm:5.3.1" + checksum: 71d7bb4c1dbe020f915fd881108cbe85a0db3d636a0ea3ba911393c53946711d13a9b1143c7e70db06d571a5822c0a324a6bcde5c9904e7ca5047f01f1bf8cd3 languageName: node linkType: hard @@ -8929,7 +8929,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:9.0.4, minimatch@npm:^9.0.0, minimatch@npm:^9.0.1, minimatch@npm:^9.0.3": +"minimatch@npm:9.0.4, minimatch@npm:^9.0.0, minimatch@npm:^9.0.1, minimatch@npm:^9.0.3, minimatch@npm:^9.0.4": version: 9.0.4 resolution: "minimatch@npm:9.0.4" dependencies: @@ -10404,10 +10404,10 @@ __metadata: version: 0.0.0-use.local resolution: "performances@workspace:performances" dependencies: - "@types/node": 20.12.5 + "@types/node": 20.12.7 "@types/node-forge": 1.3.11 node-forge: 1.3.1 - puppeteer: 22.6.3 + puppeteer: 22.6.5 ts-node: 10.9.2 languageName: unknown linkType: soft @@ -10764,16 +10764,16 @@ __metadata: languageName: node linkType: hard -"puppeteer-core@npm:22.6.3": - version: 22.6.3 - resolution: "puppeteer-core@npm:22.6.3" +"puppeteer-core@npm:22.6.5": + version: 22.6.5 + resolution: "puppeteer-core@npm:22.6.5" dependencies: - "@puppeteer/browsers": 2.2.1 - chromium-bidi: 0.5.16 + "@puppeteer/browsers": 2.2.2 + chromium-bidi: 0.5.17 debug: 4.3.4 devtools-protocol: 0.0.1262051 ws: 8.16.0 - checksum: e81cc64cf5849d51ccecb9d450b88fb1046b360307655b8dac4ed062af3a52c8c64de778ddc6a6e0666374f6f3ee5862d958aa666979c8a193efae41e376e598 + checksum: 4dc58083179eae79397d2c55c8cf12b27228278c5ab2d4928dd44a954af17f0f55be0b91e0e442fd282fa96574a2403e6397b3ae10bedf6ff2b38bffed164ff2 languageName: node linkType: hard @@ -10796,17 +10796,17 @@ __metadata: languageName: node linkType: hard -"puppeteer@npm:22.6.3": - version: 22.6.3 - resolution: "puppeteer@npm:22.6.3" +"puppeteer@npm:22.6.5": + version: 22.6.5 + resolution: "puppeteer@npm:22.6.5" dependencies: - "@puppeteer/browsers": 2.2.1 + "@puppeteer/browsers": 2.2.2 cosmiconfig: 9.0.0 devtools-protocol: 0.0.1262051 - puppeteer-core: 22.6.3 + puppeteer-core: 22.6.5 bin: puppeteer: lib/esm/puppeteer/node/cli.js - checksum: 9f88ead64c1422031a01ab9416e41073fefa3e27e566a43ea84025e4cb1b0794970d661ca287a00c3582a26fac2ce2147124992b8f0fc852895e465f515f12b4 + checksum: d6361ae4e5dd7c55e244b98aca345745b147c434b3636896e1f01103de2994c48274a0ed2febf8ba917692f086d44e4d9a820007acc814e5dba7e8d18ad1aedd languageName: node linkType: hard @@ -12283,12 +12283,12 @@ __metadata: languageName: node linkType: hard -"style-loader@npm:3.3.4": - version: 3.3.4 - resolution: "style-loader@npm:3.3.4" +"style-loader@npm:4.0.0": + version: 4.0.0 + resolution: "style-loader@npm:4.0.0" peerDependencies: - webpack: ^5.0.0 - checksum: caac3f2fe2c3c89e49b7a2a9329e1cfa515ecf5f36b9c4885f9b218019fda207a9029939b2c35821dec177a264a007e7c391ccdd3ff7401881ce6287b9c8f38b + webpack: ^5.27.0 + checksum: 0b751b4cc8394a2fe1df6194bb2f6dd68e859e36f22030994bb7b5220f24f9efb5705e78b2442226e6fa4c90f74b397529c7eb0a1d7326fb016e1e140e90151c languageName: node linkType: hard @@ -12595,12 +12595,12 @@ __metadata: languageName: node linkType: hard -"ts-api-utils@npm:^1.0.1": - version: 1.0.3 - resolution: "ts-api-utils@npm:1.0.3" +"ts-api-utils@npm:^1.3.0": + version: 1.3.0 + resolution: "ts-api-utils@npm:1.3.0" peerDependencies: typescript: ">=4.2.0" - checksum: 441cc4489d65fd515ae6b0f4eb8690057add6f3b6a63a36073753547fb6ce0c9ea0e0530220a0b282b0eec535f52c4dfc315d35f8a4c9a91c0def0707a714ca6 + checksum: c746ddabfdffbf16cb0b0db32bb287236a19e583057f8649ee7c49995bb776e1d3ef384685181c11a1a480369e022ca97512cb08c517b2d2bd82c83754c97012 languageName: node linkType: hard @@ -12884,23 +12884,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:5.4.4, typescript@npm:>=3 < 6": - version: 5.4.4 - resolution: "typescript@npm:5.4.4" +"typescript@npm:5.4.5, typescript@npm:>=3 < 6": + version: 5.4.5 + resolution: "typescript@npm:5.4.5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: a7b54515d2adfc75c8d14188af0216b6b9ae3c192c9732797c24b1d14608eac1c83be7b1c65ce6f4ced0f2c40583f11b495fe1ba3d982afadbcc523517d183c8 + checksum: 53c879c6fa1e3bcb194b274d4501ba1985894b2c2692fa079db03c5a5a7140587a1e04e1ba03184605d35f439b40192d9e138eb3279ca8eee313c081c8bcd9b0 languageName: node linkType: hard -"typescript@patch:typescript@5.4.4#~builtin, typescript@patch:typescript@>=3 < 6#~builtin": - version: 5.4.4 - resolution: "typescript@patch:typescript@npm%3A5.4.4#~builtin::version=5.4.4&hash=5adc0c" +"typescript@patch:typescript@5.4.5#~builtin, typescript@patch:typescript@>=3 < 6#~builtin": + version: 5.4.5 + resolution: "typescript@patch:typescript@npm%3A5.4.5#~builtin::version=5.4.5&hash=5adc0c" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: f64e07e11f787a18b5b98cbe7506465a5a92f56bbd4691977cc9c690f4ec49ebce51dfac77728dc792b743f8d89e8d99004370f5c96f7f56947c25c3f90f9350 + checksum: d59e26e74f6b444517d0ba16e0ee16e75c652c2b49a59f2ebdbeb16647a855fd50c7fc786a58987e45f03bce0677092e2dd3333649fd53b11d0b0d271455837c languageName: node linkType: hard @@ -13273,41 +13273,41 @@ __metadata: languageName: node linkType: hard -"webdriver@npm:8.35.0": - version: 8.35.0 - resolution: "webdriver@npm:8.35.0" +"webdriver@npm:8.36.0": + version: 8.36.0 + resolution: "webdriver@npm:8.36.0" dependencies: "@types/node": ^20.1.0 "@types/ws": ^8.5.3 - "@wdio/config": 8.35.0 + "@wdio/config": 8.36.0 "@wdio/logger": 8.28.0 "@wdio/protocols": 8.32.0 - "@wdio/types": 8.32.4 - "@wdio/utils": 8.35.0 + "@wdio/types": 8.36.0 + "@wdio/utils": 8.36.0 deepmerge-ts: ^5.1.0 got: ^12.6.1 ky: ^0.33.0 ws: ^8.8.0 - checksum: 509940dabd6df49aae711e271185580de8daf9b9a3512141eb36e55af0484ca618d2dd45b64cec010cfb34548aaccaa48d59ce8426ab456e66906f60fe022332 + checksum: dc438eeefb6b2848a6c61c99505cbc8b98c6fa248a0684a5e69920f24e1a1a582a63f4a2afe22591b8894ca251d19aa6d9ab194b937cb9a7b4dd907035d6395f languageName: node linkType: hard -"webdriverio@npm:8.35.1, webdriverio@npm:^8.29.3": - version: 8.35.1 - resolution: "webdriverio@npm:8.35.1" +"webdriverio@npm:8.36.0, webdriverio@npm:^8.29.3": + version: 8.36.0 + resolution: "webdriverio@npm:8.36.0" dependencies: "@types/node": ^20.1.0 - "@wdio/config": 8.35.0 + "@wdio/config": 8.36.0 "@wdio/logger": 8.28.0 "@wdio/protocols": 8.32.0 "@wdio/repl": 8.24.12 - "@wdio/types": 8.32.4 - "@wdio/utils": 8.35.0 + "@wdio/types": 8.36.0 + "@wdio/utils": 8.36.0 archiver: ^7.0.0 aria-query: ^5.0.0 css-shorthand-properties: ^1.1.1 css-value: ^0.0.1 - devtools-protocol: ^0.0.1273771 + devtools-protocol: ^0.0.1282316 grapheme-splitter: ^1.0.2 import-meta-resolve: ^4.0.0 is-plain-obj: ^4.1.0 @@ -13319,13 +13319,13 @@ __metadata: resq: ^1.9.1 rgb2hex: 0.2.5 serialize-error: ^11.0.1 - webdriver: 8.35.0 + webdriver: 8.36.0 peerDependencies: devtools: ^8.14.0 peerDependenciesMeta: devtools: optional: true - checksum: 95688b779fe1c2a8136eb256cb126ebe7ae3743b5f8278b547086c59a5502cdc641a055e25b5603e917f21337058267bc133410a577ff41ef368aa37e9768232 + checksum: ba1de8f110a3a0208c966037aaab36ec3c1c66efdf1f1127d1c1c7448a200acc85f7bc522918bf70403bd167e072015db7871112019eb53eda3894de692fc9fd languageName: node linkType: hard