From 541c5b427c36a47ccb3fdb9e05ed37a47f27ad46 Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Fri, 8 Aug 2025 10:09:58 +0200 Subject: [PATCH 01/19] wip --- .../domain/telemetry/telemetryEvent.types.ts | 24 ---- packages/rum-core/src/domain/assembly.ts | 12 +- .../src/domain/event/eventCollection.ts | 3 + packages/rum-core/src/domain/stream/index.ts | 32 +++++ packages/rum-core/src/domain/stream/metric.ts | 19 +++ packages/rum-core/src/domain/stream/stream.ts | 72 ++++++++++ packages/rum-core/src/domain/stream/timer.ts | 22 +++ .../rum-core/src/domain/trackEventCounts.ts | 2 +- packages/rum-core/src/domainContext.types.ts | 6 +- packages/rum-core/src/index.ts | 1 + packages/rum-core/src/rawRumEvent.types.ts | 20 +++ packages/rum-core/src/rumEvent.types.ts | 127 ++++++------------ packages/rum/src/entries/main.ts | 2 + sandbox/react-app/main.tsx | 8 +- 14 files changed, 234 insertions(+), 116 deletions(-) create mode 100644 packages/rum-core/src/domain/stream/index.ts create mode 100644 packages/rum-core/src/domain/stream/metric.ts create mode 100644 packages/rum-core/src/domain/stream/stream.ts create mode 100644 packages/rum-core/src/domain/stream/timer.ts diff --git a/packages/core/src/domain/telemetry/telemetryEvent.types.ts b/packages/core/src/domain/telemetry/telemetryEvent.types.ts index 3aacc38c48..60fab02adb 100644 --- a/packages/core/src/domain/telemetry/telemetryEvent.types.ts +++ b/packages/core/src/domain/telemetry/telemetryEvent.types.ts @@ -209,14 +209,6 @@ export type TelemetryConfigurationEvent = CommonTelemetryProperties & { * Whether the allowed tracing urls list is used */ use_allowed_tracing_urls?: boolean - /** - * Whether the allowed GraphQL urls list is used - */ - use_allowed_graph_ql_urls?: boolean - /** - * Whether GraphQL payload tracking is used for at least one GraphQL endpoint - */ - use_track_graph_ql_payload?: boolean /** * A list of selected tracing propagators */ @@ -451,22 +443,6 @@ export type TelemetryConfigurationEvent = CommonTelemetryProperties & { * The variant of the SDK build (e.g., standard, lite, etc.). */ variant?: string - /** - * The id of the remote configuration - */ - remote_configuration_id?: string - /** - * Whether a proxy is used for remote configuration - */ - use_remote_configuration_proxy?: boolean - /** - * The percentage of sessions with Profiling enabled - */ - profiling_sample_rate?: number - /** - * Whether trace baggage is propagated to child spans - */ - propagate_trace_baggage?: boolean [k: string]: unknown } [k: string]: unknown diff --git a/packages/rum-core/src/domain/assembly.ts b/packages/rum-core/src/domain/assembly.ts index e5d5c5255d..624cdabcfc 100644 --- a/packages/rum-core/src/domain/assembly.ts +++ b/packages/rum-core/src/domain/assembly.ts @@ -86,6 +86,11 @@ export function startRumAssembly( ...VIEW_MODIFIABLE_FIELD_PATHS, ...ROOT_MODIFIABLE_FIELD_PATHS, }, + [RumEventType.STREAM]: { + ...USER_CUSTOMIZABLE_FIELD_PATHS, + ...VIEW_MODIFIABLE_FIELD_PATHS, + ...ROOT_MODIFIABLE_FIELD_PATHS, + }, } const eventRateLimiters = { [RumEventType.ERROR]: createEventRateLimiter( @@ -109,7 +114,7 @@ export function startRumAssembly( LifeCycleEventType.RAW_RUM_EVENT_COLLECTED, ({ startTime, duration, rawRumEvent, domainContext }) => { const defaultRumEventAttributes = hooks.triggerHook(HookNames.Assemble, { - eventType: rawRumEvent.type, + eventType: rawRumEvent.type === 'stream' ? 'view' : rawRumEvent.type, startTime, duration, })! @@ -126,6 +131,11 @@ export function startRumAssembly( if (isEmptyObject(serverRumEvent.context!)) { delete serverRumEvent.context } + + if (rawRumEvent.type === 'stream') { + serverRumEvent.type = 'view' + } + lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, serverRumEvent) } } diff --git a/packages/rum-core/src/domain/event/eventCollection.ts b/packages/rum-core/src/domain/event/eventCollection.ts index 2f10bf8593..16f0a123fa 100644 --- a/packages/rum-core/src/domain/event/eventCollection.ts +++ b/packages/rum-core/src/domain/event/eventCollection.ts @@ -10,6 +10,7 @@ import type { RawRumLongTaskEvent, RawRumResourceEvent, RawRumVitalEvent, + RawRumStreamEvent, } from '../../rawRumEvent.types' import { RumEventType } from '../../rawRumEvent.types' @@ -18,6 +19,7 @@ const allowedEventTypes = [ RumEventType.ERROR, RumEventType.LONG_TASK, RumEventType.RESOURCE, + RumEventType.STREAM, RumEventType.VITAL, ] as const @@ -28,6 +30,7 @@ export type AllowedRawRumEvent = ( | RawRumLongAnimationFrameEvent | RawRumActionEvent | RawRumVitalEvent + | RawRumStreamEvent ) & { context?: Context } export function startEventCollection(lifeCycle: LifeCycle) { diff --git a/packages/rum-core/src/domain/stream/index.ts b/packages/rum-core/src/domain/stream/index.ts new file mode 100644 index 0000000000..f5ca65c0a0 --- /dev/null +++ b/packages/rum-core/src/domain/stream/index.ts @@ -0,0 +1,32 @@ +import type { RumPlugin, OnRumStartOptions } from '../..' +import type { AddEvent, API } from './stream' +import { createStream } from './stream' + +export function createStreamPlugin(): { plugin: RumPlugin; createStream: () => ReturnType } { + let addEvent: OnRumStartOptions['addEvent'] + const callbacks = new Set<(addEvent: OnRumStartOptions['addEvent']) => void>() + const store: AddEvent = (...args) => { + callbacks.add(() => { + addEvent!(...args) + }) + } + + const api: API = { + get addEvent() { + return addEvent ?? store + }, + } + + return { + plugin: { + name: 'stream', + onRumStart: (options) => { + addEvent = options.addEvent + + callbacks.forEach((callback) => callback(addEvent)) + callbacks.clear() + }, + }, + createStream: () => createStream(api), + } +} diff --git a/packages/rum-core/src/domain/stream/metric.ts b/packages/rum-core/src/domain/stream/metric.ts new file mode 100644 index 0000000000..b8db3fc17d --- /dev/null +++ b/packages/rum-core/src/domain/stream/metric.ts @@ -0,0 +1,19 @@ +// eslint-disable-next-line no-restricted-syntax +abstract class Metric { + constructor(private name: string) {} + + update(value: number, weight?: number): void { + // This method is a placeholder for metric updates. + } +} + +// eslint-disable-next-line no-restricted-syntax +export class WeightAverageMetric extends Metric { + private average: number = 0 + private weight: number = 0 + + update(value: number, weight: number): void { + this.average = (this.average * this.weight + value * weight) / (this.weight + weight) + this.weight += weight + } +} diff --git a/packages/rum-core/src/domain/stream/stream.ts b/packages/rum-core/src/domain/stream/stream.ts new file mode 100644 index 0000000000..0d495ef8e4 --- /dev/null +++ b/packages/rum-core/src/domain/stream/stream.ts @@ -0,0 +1,72 @@ +import { clocksNow, generateUUID } from '@datadog/browser-core' +import type { StartRumResult } from '../..' +import type { WeightAverageMetric } from './metric' +import { createTimer } from './timer' + +export interface API { + addEvent: AddEvent +} + +export type AddEvent = StartRumResult['addEvent'] + +interface Meta { + duration?: number + format?: string + resolution?: string +} + +interface Metrics { + bitrate: WeightAverageMetric + fps: WeightAverageMetric + timestamp: number + watchTime: number +} + +type Transition = 'end' | 'error' | 'pause' | 'play' | 'preload' | 'quit' | 'rebuff' | 'seek' | 'start' + +export function createStream(api: API) { + const id = generateUUID() + const origin = clocksNow() + const timer = createTimer() + + return { + interaction(id: string): void {}, + transition(state: Transition, context: any): void { + if (state === 'play') { + timer.start() + } + + if (state === 'pause' || state === 'end') { + timer.stop() + } + + const now = clocksNow() + + api.addEvent( + now.relative, + { + date: now.timeStamp, + type: 'action', + action: { id: generateUUID(), type: 'custom', target: { name: state } }, + stream: { id }, + }, + context + ) + }, + update(key: string, value: any): void { + const now = clocksNow() + + api.addEvent( + now.relative, + { + date: now.timeStamp, + type: 'stream', + stream: { + id: generateUUID(), + }, + }, + {} + ) + }, + } +} diff --git a/packages/rum-core/src/domain/stream/timer.ts b/packages/rum-core/src/domain/stream/timer.ts new file mode 100644 index 0000000000..f339241d6c --- /dev/null +++ b/packages/rum-core/src/domain/stream/timer.ts @@ -0,0 +1,22 @@ +import type { RelativeTime } from '@datadog/browser-core' +import { relativeNow } from '@datadog/browser-core' + +export function createTimer() { + let start: RelativeTime + let count = 0 + + return { + start(): number { + start = relativeNow() + + return count + }, + stop(): number { + const duration = relativeNow() - start + + count += duration + + return count + }, + } +} diff --git a/packages/rum-core/src/domain/trackEventCounts.ts b/packages/rum-core/src/domain/trackEventCounts.ts index c7a0cc856b..ac2ec6debb 100644 --- a/packages/rum-core/src/domain/trackEventCounts.ts +++ b/packages/rum-core/src/domain/trackEventCounts.ts @@ -30,7 +30,7 @@ export function trackEventCounts({ } const subscription = lifeCycle.subscribe(LifeCycleEventType.RUM_EVENT_COLLECTED, (event): void => { - if (event.type === 'view' || event.type === 'vital' || !isChildEvent(event)) { + if (event.type === 'view' || event.type === 'vital' || !isChildEvent(event) || ['stream'].includes(event.type)) { return } switch (event.type) { diff --git a/packages/rum-core/src/domainContext.types.ts b/packages/rum-core/src/domainContext.types.ts index b95870b70e..9914b3d3e9 100644 --- a/packages/rum-core/src/domainContext.types.ts +++ b/packages/rum-core/src/domainContext.types.ts @@ -16,7 +16,9 @@ export type RumEventDomainContext = T extends type ? RumLongTaskEventDomainContext : T extends typeof RumEventType.VITAL ? RumVitalEventDomainContext - : never + : T extends typeof RumEventType.STREAM + ? { streamId: string } + : never export interface RumViewEventDomainContext { location: Readonly @@ -59,3 +61,5 @@ export interface RumLongTaskEventDomainContext { // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface RumVitalEventDomainContext {} + +export interface RumStreamEventDomainContext {} diff --git a/packages/rum-core/src/index.ts b/packages/rum-core/src/index.ts index c72f62a39c..237a7afbc7 100644 --- a/packages/rum-core/src/index.ts +++ b/packages/rum-core/src/index.ts @@ -71,3 +71,4 @@ export type { Hooks, DefaultRumEventAttributes, DefaultTelemetryEventAttributes export { createHooks } from './domain/hooks' export { isSampled } from './domain/sampler/sampler' export type { TracingOption, PropagatorType } from './domain/tracing/tracer.types' +export { createStreamPlugin } from './domain/stream' diff --git a/packages/rum-core/src/rawRumEvent.types.ts b/packages/rum-core/src/rawRumEvent.types.ts index 9229ae390b..c998506e59 100644 --- a/packages/rum-core/src/rawRumEvent.types.ts +++ b/packages/rum-core/src/rawRumEvent.types.ts @@ -28,6 +28,7 @@ export const RumEventType = { VIEW: 'view', RESOURCE: 'resource', VITAL: 'vital', + STREAM: 'stream', } as const export type RumEventType = (typeof RumEventType)[keyof typeof RumEventType] @@ -331,6 +332,9 @@ export interface RawRumActionEvent { pointer_up_delay?: Duration } } + stream?: { + id: string + } context?: Context } @@ -377,6 +381,21 @@ export const VitalType = { export type VitalType = (typeof VitalType)[keyof typeof VitalType] +export interface RawRumStreamEvent { + date: TimeStamp + type: typeof RumEventType.STREAM + stream: { + id: string + bitrate?: number + duration?: number + format?: string + fps?: number + resolution?: string + timestamp?: number + watch_time?: number + } +} + export type RawRumEvent = | RawRumErrorEvent | RawRumResourceEvent @@ -385,3 +404,4 @@ export type RawRumEvent = | RawRumLongAnimationFrameEvent | RawRumActionEvent | RawRumVitalEvent + | RawRumStreamEvent diff --git a/packages/rum-core/src/rumEvent.types.ts b/packages/rum-core/src/rumEvent.types.ts index 987305a25f..bc2468a92d 100644 --- a/packages/rum-core/src/rumEvent.types.ts +++ b/packages/rum-core/src/rumEvent.types.ts @@ -1288,7 +1288,44 @@ export type RumVitalEvent = CommonProperties & * RUM event type */ readonly type: 'vital' - readonly vital: DurationProperties | AppLaunchProperties | FeatureOperationProperties + /** + * Vital properties + */ + readonly vital: { + /** + * Type of the vital + */ + readonly type: 'duration' | 'step' + /** + * UUID of the vital + */ + readonly id: string + /** + * Optional key to distinguish between multiple operations of the same name running in parallel (e.g., 'photo_upload' with keys 'profile_pic' vs 'cover') + */ + readonly parent_id?: string + /** + * Name of the vital, as it is also used as facet path for its value, it must contain only letters, digits, or the characters - _ . @ $ + */ + readonly name?: string + /** + * Description of the vital. It can be used as a secondary identifier (URL, React component name...) + */ + readonly description?: string + /** + * Duration of the vital in nanoseconds + */ + readonly duration?: number + /** + * Type of the step that triggered the vital, if applicable + */ + readonly step_type?: 'start' | 'update' | 'retry' | 'end' + /** + * Reason for the failure of the step, if applicable + */ + readonly failure_reason?: 'error' | 'abandoned' | 'other' + [k: string]: unknown + } /** * Internal properties */ @@ -1307,90 +1344,6 @@ export type RumVitalEvent = CommonProperties & } [k: string]: unknown } -/** - * Duration properties of a Vital event - */ -export type DurationProperties = VitalCommonProperties & { - /** - * Type of the vital. - */ - readonly type: 'duration' - /** - * Duration of the vital in nanoseconds. - */ - readonly duration: number - [k: string]: unknown -} -/** - * Schema of common properties for a Vital event - */ -export type VitalCommonProperties = { - /** - * UUID of the vital - */ - readonly id: string - /** - * Name of the vital, as it is also used as facet path for its value, it must contain only letters, digits, or the characters - _ . @ $ - */ - readonly name?: string - /** - * Description of the vital. It can be used as a secondary identifier (URL, React component name...) - */ - readonly description?: string - [k: string]: unknown -} -/** - * Schema for app launch metrics. - */ -export type AppLaunchProperties = VitalCommonProperties & { - /** - * Type of the vital. - */ - readonly type: 'app_launch' - /** - * The metric of the app launch. - */ - readonly app_launch_metric: 'ttid' | 'ttfd' - /** - * Duration of the vital in nanoseconds. - */ - readonly duration: number - /** - * The type of the app launch. - */ - readonly startup_type?: 'cold_start' | 'warm_start' - /** - * Whether the app launch was prewarmed. - */ - readonly is_prewarmed?: boolean - /** - * If the app launch had a saved instance state bundle. - */ - readonly has_saved_instance_state_bundle?: boolean - [k: string]: unknown -} -/** - * Schema for a feature operation. - */ -export type FeatureOperationProperties = VitalCommonProperties & { - /** - * Type of the vital. - */ - readonly type: 'operation_step' - /** - * Optional key to distinguish between multiple operations of the same name running in parallel (e.g., 'photo_upload' with keys 'profile_pic' vs 'cover') - */ - readonly operation_key?: string - /** - * Type of the step that triggered the vital, if applicable - */ - readonly step_type?: 'start' | 'update' | 'retry' | 'end' - /** - * Reason for the failure of the step, if applicable - */ - readonly failure_reason?: 'error' | 'abandoned' | 'other' - [k: string]: unknown -} /** * Schema of common properties of RUM events @@ -1853,7 +1806,7 @@ export interface StreamSchema { */ bitrate?: number /** - * How long is the content (VOD only) (in ms) + * How long is the content (VOD only) */ readonly duration?: number /** @@ -1873,7 +1826,7 @@ export interface StreamSchema { */ timestamp?: number /** - * how much did the media progress since the last context update (in ms) + * how much did the media progress since the last context update */ watch_time?: number [k: string]: unknown diff --git a/packages/rum/src/entries/main.ts b/packages/rum/src/entries/main.ts index 72a7696ad3..94add6b8d5 100644 --- a/packages/rum/src/entries/main.ts +++ b/packages/rum/src/entries/main.ts @@ -15,6 +15,8 @@ import { createDeflateEncoder, startDeflateWorker } from '../domain/deflate' import { lazyLoadRecorder } from '../boot/lazyLoadRecorder' import { makeProfilerApi } from '../boot/profilerApi' +export { createStreamPlugin } from '@datadog/browser-rum-core' + export type { User, Account, diff --git a/sandbox/react-app/main.tsx b/sandbox/react-app/main.tsx index 16a36e7c6e..9e0ac890ab 100644 --- a/sandbox/react-app/main.tsx +++ b/sandbox/react-app/main.tsx @@ -1,15 +1,19 @@ import { Link, Outlet, RouterProvider, useParams } from 'react-router-dom' import React, { useEffect, useState } from 'react' import ReactDOM from 'react-dom/client' -import { datadogRum } from '@datadog/browser-rum' +import { datadogRum, createStreamPlugin } from '@datadog/browser-rum' import { createBrowserRouter } from '@datadog/browser-rum-react/react-router-v7' import { reactPlugin, ErrorBoundary, UNSTABLE_ReactComponentTracker } from '@datadog/browser-rum-react' import { datadogFlagging } from '@datadog/browser-flagging' +const stream = createStreamPlugin() +// @ts-ignore +window.stream = stream.createStream() + datadogRum.init({ applicationId: 'xxx', clientToken: 'xxx', - plugins: [reactPlugin({ router: true })], + plugins: [reactPlugin({ router: true }), stream.plugin], }) datadogFlagging.init({}) From 7391a219ee70e4ff16e75791d8ae548bc0c0c995 Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Fri, 22 Aug 2025 12:39:25 +0200 Subject: [PATCH 02/19] =?UTF-8?q?=F0=9F=90=9B=20temporary=20fix=20for=20ty?= =?UTF-8?q?pes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rum-core/src/domain/assembly.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/rum-core/src/domain/assembly.ts b/packages/rum-core/src/domain/assembly.ts index 624cdabcfc..b3ae4f6a1f 100644 --- a/packages/rum-core/src/domain/assembly.ts +++ b/packages/rum-core/src/domain/assembly.ts @@ -133,6 +133,7 @@ export function startRumAssembly( } if (rawRumEvent.type === 'stream') { + // @ts-expect-error TBF serverRumEvent.type = 'view' } From fbb7d464ba54383f31a2bfd1f8b9362cf7966fb2 Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Fri, 22 Aug 2025 14:06:12 +0200 Subject: [PATCH 03/19] wip --- packages/rum-core/src/domain/stream/index.ts | 1 + packages/rum/src/entries/main.ts | 5 ++++- sandbox/index.html | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/rum-core/src/domain/stream/index.ts b/packages/rum-core/src/domain/stream/index.ts index f5ca65c0a0..ee6af70ee2 100644 --- a/packages/rum-core/src/domain/stream/index.ts +++ b/packages/rum-core/src/domain/stream/index.ts @@ -13,6 +13,7 @@ export function createStreamPlugin(): { plugin: RumPlugin; createStream: () => R const api: API = { get addEvent() { + console.log('>>>', 'getter addEvent', addEvent) return addEvent ?? store }, } diff --git a/packages/rum/src/entries/main.ts b/packages/rum/src/entries/main.ts index 94add6b8d5..ac7acdeb7d 100644 --- a/packages/rum/src/entries/main.ts +++ b/packages/rum/src/entries/main.ts @@ -9,7 +9,8 @@ // Keep the following in sync with packages/rum-slim/src/entries/main.ts import { defineGlobal, getGlobalObject } from '@datadog/browser-core' import type { RumPublicApi } from '@datadog/browser-rum-core' -import { makeRumPublicApi, startRum } from '@datadog/browser-rum-core' +import { createStreamPlugin, makeRumPublicApi, startRum } from '@datadog/browser-rum-core' +import { createStream } from '@datadog/browser-rum-core/src/domain/stream/stream' import { makeRecorderApi } from '../boot/recorderApi' import { createDeflateEncoder, startDeflateWorker } from '../domain/deflate' import { lazyLoadRecorder } from '../boot/lazyLoadRecorder' @@ -83,6 +84,8 @@ export const datadogRum = makeRumPublicApi(startRum, recorderApi, profilerApi, { createDeflateEncoder, sdkName: 'rum', }) +// @ts-expect-error TBF +datadogRum.createStreamPlugin = createStreamPlugin interface BrowserWindow extends Window { DD_RUM?: RumPublicApi diff --git a/sandbox/index.html b/sandbox/index.html index 1e68157307..70896c3e95 100644 --- a/sandbox/index.html +++ b/sandbox/index.html @@ -7,6 +7,9 @@ From 47808905c577edc0a681ec2cb225fa4894f52ee7 Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Mon, 25 Aug 2025 17:17:31 +0200 Subject: [PATCH 04/19] =?UTF-8?q?=E2=9C=A8=20stream=20plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rum-core/src/domain/assembly.ts | 26 +++++-- packages/rum-core/src/domain/stream/index.ts | 1 - packages/rum-core/src/domain/stream/metric.ts | 46 ++++++++---- packages/rum-core/src/domain/stream/stream.ts | 70 +++++++++++++------ packages/rum-core/src/domain/stream/timer.ts | 30 +++++--- 5 files changed, 124 insertions(+), 49 deletions(-) diff --git a/packages/rum-core/src/domain/assembly.ts b/packages/rum-core/src/domain/assembly.ts index b3ae4f6a1f..207c036890 100644 --- a/packages/rum-core/src/domain/assembly.ts +++ b/packages/rum-core/src/domain/assembly.ts @@ -13,6 +13,7 @@ import { import type { RumEventDomainContext } from '../domainContext.types' import type { AssembledRumEvent } from '../rawRumEvent.types' import { RumEventType } from '../rawRumEvent.types' +import type { RumViewEvent } from '../rumEvent.types' import type { LifeCycle } from './lifeCycle' import { LifeCycleEventType } from './lifeCycle' import type { RumConfiguration } from './configuration' @@ -133,11 +134,28 @@ export function startRumAssembly( } if (rawRumEvent.type === 'stream') { - // @ts-expect-error TBF - serverRumEvent.type = 'view' - } + const streamEvent = { + ...(serverRumEvent as RumViewEvent), + view: { + ...serverRumEvent.view, + id: serverRumEvent.stream?.id, + action: { + count: 0, + }, + error: { + count: 0, + }, + resource: { + count: 0, + }, + }, + type: 'view', + } - lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, serverRumEvent) + lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, streamEvent as AssembledRumEvent) + } else { + lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, serverRumEvent) + } } } ) diff --git a/packages/rum-core/src/domain/stream/index.ts b/packages/rum-core/src/domain/stream/index.ts index ee6af70ee2..f5ca65c0a0 100644 --- a/packages/rum-core/src/domain/stream/index.ts +++ b/packages/rum-core/src/domain/stream/index.ts @@ -13,7 +13,6 @@ export function createStreamPlugin(): { plugin: RumPlugin; createStream: () => R const api: API = { get addEvent() { - console.log('>>>', 'getter addEvent', addEvent) return addEvent ?? store }, } diff --git a/packages/rum-core/src/domain/stream/metric.ts b/packages/rum-core/src/domain/stream/metric.ts index b8db3fc17d..4eef828583 100644 --- a/packages/rum-core/src/domain/stream/metric.ts +++ b/packages/rum-core/src/domain/stream/metric.ts @@ -1,19 +1,41 @@ -// eslint-disable-next-line no-restricted-syntax -abstract class Metric { - constructor(private name: string) {} +export function createWeightAverageMetric() { + let lastUpdate = 0 + let weight = 0 + let value: number | undefined - update(value: number, weight?: number): void { - // This method is a placeholder for metric updates. + return { + get value() { + console.log('>>>', value) + return value + }, + update(newLastUpdate: number, newValue: number) { + if (newLastUpdate <= lastUpdate) { + return + } + + const newWeight = newLastUpdate - lastUpdate + value = ((value ?? 0) * weight + newValue * newWeight) / (weight + newWeight) + weight += newWeight + lastUpdate = newLastUpdate + }, } } -// eslint-disable-next-line no-restricted-syntax -export class WeightAverageMetric extends Metric { - private average: number = 0 - private weight: number = 0 +export function createLastMetric() { + let lastUpdate = 0 + let value: number | undefined + + return { + get value() { + return value + }, + update(newLastUpdate: number, newValue: number) { + if (newLastUpdate <= lastUpdate) { + return + } - update(value: number, weight: number): void { - this.average = (this.average * this.weight + value * weight) / (this.weight + weight) - this.weight += weight + value = newValue + lastUpdate = newLastUpdate + }, } } diff --git a/packages/rum-core/src/domain/stream/stream.ts b/packages/rum-core/src/domain/stream/stream.ts index 0d495ef8e4..f8a01adb43 100644 --- a/packages/rum-core/src/domain/stream/stream.ts +++ b/packages/rum-core/src/domain/stream/stream.ts @@ -1,26 +1,19 @@ import { clocksNow, generateUUID } from '@datadog/browser-core' import type { StartRumResult } from '../..' -import type { WeightAverageMetric } from './metric' +import { createLastMetric, createWeightAverageMetric } from './metric' import { createTimer } from './timer' export interface API { addEvent: AddEvent } -export type AddEvent = StartRumResult['addEvent'] - interface Meta { duration?: number format?: string resolution?: string } -interface Metrics { - bitrate: WeightAverageMetric - fps: WeightAverageMetric - timestamp: number - watchTime: number -} +export type AddEvent = StartRumResult['addEvent'] type Transition = 'end' | 'error' | 'pause' | 'play' | 'preload' | 'quit' | 'rebuff' | 'seek' | 'start' @@ -28,9 +21,50 @@ export function createStream(api: API) { const id = generateUUID() const origin = clocksNow() const timer = createTimer() + const meta: Meta = {} + const metrics = { + bitrate: createWeightAverageMetric(), + fps: createWeightAverageMetric(), + timestamp: createLastMetric(), + watchTime: createLastMetric(), + } + + function sendStreamEvent() { + const now = clocksNow() + + api.addEvent( + now.relative, + { + date: now.timeStamp, + type: 'stream', + stream: { + id, + bitrate: metrics.bitrate.value, + duration: meta.duration, + format: meta.format, + fps: metrics.fps.value, + resolution: meta.resolution, + timestamp: metrics.timestamp.value, + watch_time: metrics.watchTime.value, + }, + }, + {} + ) + } return { - interaction(id: string): void {}, + interaction(id: string): void { + console.log('>>>', 'interaction', id) + }, + set(key: K, value: Meta[K]): void { + if (meta[key] !== undefined) { + return + } + + meta[key] = value + + sendStreamEvent() + }, transition(state: Transition, context: any): void { if (state === 'play') { timer.start() @@ -53,20 +87,10 @@ export function createStream(api: API) { context ) }, - update(key: string, value: any): void { - const now = clocksNow() + update(key: keyof typeof metrics, value: number): void { + metrics[key].update(timer.value, value) - api.addEvent( - now.relative, - { - date: now.timeStamp, - type: 'stream', - stream: { - id: generateUUID(), - }, - }, - {} - ) + sendStreamEvent() }, } } diff --git a/packages/rum-core/src/domain/stream/timer.ts b/packages/rum-core/src/domain/stream/timer.ts index f339241d6c..5fd1f0ec94 100644 --- a/packages/rum-core/src/domain/stream/timer.ts +++ b/packages/rum-core/src/domain/stream/timer.ts @@ -2,21 +2,33 @@ import type { RelativeTime } from '@datadog/browser-core' import { relativeNow } from '@datadog/browser-core' export function createTimer() { - let start: RelativeTime - let count = 0 + let counter = 0 + let start: RelativeTime = 0 as RelativeTime + let stopped = true return { - start(): number { - start = relativeNow() + get value() { + if (stopped) { + return counter + } - return count + return relativeNow() - start }, - stop(): number { - const duration = relativeNow() - start + start(): void { + if (!stopped) { + return + } - count += duration + start = relativeNow() + stopped = false + }, + stop(): void { + if (stopped) { + return + } - return count + counter = relativeNow() - start + stopped = true }, } } From 777e0b367fe3485d726381fe0d07c1008c656652 Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Tue, 26 Aug 2025 10:28:01 +0200 Subject: [PATCH 05/19] =?UTF-8?q?=F0=9F=90=9B=20remove=20transition=20cont?= =?UTF-8?q?ext?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rum-core/src/boot/preStartRum.ts | 12 ++++++++++++ packages/rum-core/src/domain/stream/stream.ts | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/rum-core/src/boot/preStartRum.ts b/packages/rum-core/src/boot/preStartRum.ts index db5832ecff..5a40e004fc 100644 --- a/packages/rum-core/src/boot/preStartRum.ts +++ b/packages/rum-core/src/boot/preStartRum.ts @@ -34,9 +34,17 @@ import type { } from '../domain/vital/vitalCollection' import { startDurationVital, stopDurationVital } from '../domain/vital/vitalCollection' import { callPluginsMethod } from '../domain/plugins' +import { createStreamPlugin } from '../domain/stream' import type { StartRumResult } from './startRum' import type { RumPublicApiOptions, Strategy } from './rumPublicApi' +declare global { + interface Window { + DD_STREAM_PLUGIN: ReturnType + DD_STREAM: ReturnType['createStream']> + } +} + export function createPreStartStrategy( { ignoreInitIfSyntheticsWillInjectRum = true, startDeflateWorker }: RumPublicApiOptions, trackingConsentState: TrackingConsentState, @@ -180,6 +188,10 @@ export function createPreStartStrategy( initFeatureFlags(initConfiguration.enableExperimentalFeatures) // Expose the initial configuration regardless of initialization success. + window.DD_STREAM_PLUGIN = createStreamPlugin() + window.DD_STREAM = window.DD_STREAM_PLUGIN.createStream() + + initConfiguration.plugins = (initConfiguration.plugins ?? []).concat([window.DD_STREAM_PLUGIN.plugin]) cachedInitConfiguration = initConfiguration // If we are in a Synthetics test configured to automatically inject a RUM instance, we want diff --git a/packages/rum-core/src/domain/stream/stream.ts b/packages/rum-core/src/domain/stream/stream.ts index f8a01adb43..af39ce7fab 100644 --- a/packages/rum-core/src/domain/stream/stream.ts +++ b/packages/rum-core/src/domain/stream/stream.ts @@ -65,7 +65,7 @@ export function createStream(api: API) { sendStreamEvent() }, - transition(state: Transition, context: any): void { + transition(state: Transition): void { if (state === 'play') { timer.start() } @@ -84,7 +84,7 @@ export function createStream(api: API) { action: { id: generateUUID(), type: 'custom', target: { name: state } }, stream: { id }, }, - context + {} ) }, update(key: keyof typeof metrics, value: number): void { From 94082e273073a47e846804ef3e6fbbe4f3fdad96 Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Wed, 27 Aug 2025 09:55:22 +0200 Subject: [PATCH 06/19] wip --- packages/rum-core/src/domain/assembly.ts | 9 +++++++++ packages/rum-core/src/domain/stream/metric.ts | 1 - packages/rum-core/src/domain/stream/stream.ts | 10 +++++++--- packages/rum-core/src/rawRumEvent.types.ts | 2 ++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/rum-core/src/domain/assembly.ts b/packages/rum-core/src/domain/assembly.ts index 207c036890..fc7a59b289 100644 --- a/packages/rum-core/src/domain/assembly.ts +++ b/packages/rum-core/src/domain/assembly.ts @@ -136,6 +136,14 @@ export function startRumAssembly( if (rawRumEvent.type === 'stream') { const streamEvent = { ...(serverRumEvent as RumViewEvent), + _dd: { + ...serverRumEvent._dd, + document_version: serverRumEvent.stream?.document_version, + }, + stream: { + ...serverRumEvent.stream, + time_spent: undefined, + }, view: { ...serverRumEvent.view, id: serverRumEvent.stream?.id, @@ -148,6 +156,7 @@ export function startRumAssembly( resource: { count: 0, }, + time_spent: serverRumEvent.stream?.time_spent, }, type: 'view', } diff --git a/packages/rum-core/src/domain/stream/metric.ts b/packages/rum-core/src/domain/stream/metric.ts index 4eef828583..a7b2f5e35f 100644 --- a/packages/rum-core/src/domain/stream/metric.ts +++ b/packages/rum-core/src/domain/stream/metric.ts @@ -5,7 +5,6 @@ export function createWeightAverageMetric() { return { get value() { - console.log('>>>', value) return value }, update(newLastUpdate: number, newValue: number) { diff --git a/packages/rum-core/src/domain/stream/stream.ts b/packages/rum-core/src/domain/stream/stream.ts index af39ce7fab..90dd27ba38 100644 --- a/packages/rum-core/src/domain/stream/stream.ts +++ b/packages/rum-core/src/domain/stream/stream.ts @@ -1,4 +1,4 @@ -import { clocksNow, generateUUID } from '@datadog/browser-core' +import { clocksNow, generateUUID, ONE_SECOND } from '@datadog/browser-core' import type { StartRumResult } from '../..' import { createLastMetric, createWeightAverageMetric } from './metric' import { createTimer } from './timer' @@ -26,8 +26,8 @@ export function createStream(api: API) { bitrate: createWeightAverageMetric(), fps: createWeightAverageMetric(), timestamp: createLastMetric(), - watchTime: createLastMetric(), } + let documentVersion = 0 function sendStreamEvent() { const now = clocksNow() @@ -41,15 +41,19 @@ export function createStream(api: API) { id, bitrate: metrics.bitrate.value, duration: meta.duration, + document_version: documentVersion, format: meta.format, fps: metrics.fps.value, resolution: meta.resolution, timestamp: metrics.timestamp.value, - watch_time: metrics.watchTime.value, + time_spent: (now.relative - origin.relative) * ONE_SECOND, + watch_time: timer.value * ONE_SECOND, }, }, {} ) + + documentVersion += 1 } return { diff --git a/packages/rum-core/src/rawRumEvent.types.ts b/packages/rum-core/src/rawRumEvent.types.ts index c998506e59..5bac4a9310 100644 --- a/packages/rum-core/src/rawRumEvent.types.ts +++ b/packages/rum-core/src/rawRumEvent.types.ts @@ -387,10 +387,12 @@ export interface RawRumStreamEvent { stream: { id: string bitrate?: number + document_version: number duration?: number format?: string fps?: number resolution?: string + time_spent: number timestamp?: number watch_time?: number } From f3c22e77be4cf3e5396c1060d259d46ebdf6593b Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Thu, 4 Sep 2025 17:32:07 +0200 Subject: [PATCH 07/19] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20revert=20sandbox=20e?= =?UTF-8?q?xamples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sandbox/index.html | 6 ------ sandbox/react-app/main.tsx | 8 ++------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/sandbox/index.html b/sandbox/index.html index 70896c3e95..1e68157307 100644 --- a/sandbox/index.html +++ b/sandbox/index.html @@ -7,9 +7,6 @@ diff --git a/sandbox/react-app/main.tsx b/sandbox/react-app/main.tsx index 9e0ac890ab..16a36e7c6e 100644 --- a/sandbox/react-app/main.tsx +++ b/sandbox/react-app/main.tsx @@ -1,19 +1,15 @@ import { Link, Outlet, RouterProvider, useParams } from 'react-router-dom' import React, { useEffect, useState } from 'react' import ReactDOM from 'react-dom/client' -import { datadogRum, createStreamPlugin } from '@datadog/browser-rum' +import { datadogRum } from '@datadog/browser-rum' import { createBrowserRouter } from '@datadog/browser-rum-react/react-router-v7' import { reactPlugin, ErrorBoundary, UNSTABLE_ReactComponentTracker } from '@datadog/browser-rum-react' import { datadogFlagging } from '@datadog/browser-flagging' -const stream = createStreamPlugin() -// @ts-ignore -window.stream = stream.createStream() - datadogRum.init({ applicationId: 'xxx', clientToken: 'xxx', - plugins: [reactPlugin({ router: true }), stream.plugin], + plugins: [reactPlugin({ router: true })], }) datadogFlagging.init({}) From 4f7da97f7b4b02d0bdaf73d5941d2f4f06b15e9e Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Fri, 5 Sep 2025 11:33:53 +0200 Subject: [PATCH 08/19] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20update=20schemas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/telemetry/telemetryEvent.types.ts | 24 ++++ packages/rum-core/src/rumEvent.types.ts | 127 ++++++++++++------ 2 files changed, 111 insertions(+), 40 deletions(-) diff --git a/packages/core/src/domain/telemetry/telemetryEvent.types.ts b/packages/core/src/domain/telemetry/telemetryEvent.types.ts index 60fab02adb..3aacc38c48 100644 --- a/packages/core/src/domain/telemetry/telemetryEvent.types.ts +++ b/packages/core/src/domain/telemetry/telemetryEvent.types.ts @@ -209,6 +209,14 @@ export type TelemetryConfigurationEvent = CommonTelemetryProperties & { * Whether the allowed tracing urls list is used */ use_allowed_tracing_urls?: boolean + /** + * Whether the allowed GraphQL urls list is used + */ + use_allowed_graph_ql_urls?: boolean + /** + * Whether GraphQL payload tracking is used for at least one GraphQL endpoint + */ + use_track_graph_ql_payload?: boolean /** * A list of selected tracing propagators */ @@ -443,6 +451,22 @@ export type TelemetryConfigurationEvent = CommonTelemetryProperties & { * The variant of the SDK build (e.g., standard, lite, etc.). */ variant?: string + /** + * The id of the remote configuration + */ + remote_configuration_id?: string + /** + * Whether a proxy is used for remote configuration + */ + use_remote_configuration_proxy?: boolean + /** + * The percentage of sessions with Profiling enabled + */ + profiling_sample_rate?: number + /** + * Whether trace baggage is propagated to child spans + */ + propagate_trace_baggage?: boolean [k: string]: unknown } [k: string]: unknown diff --git a/packages/rum-core/src/rumEvent.types.ts b/packages/rum-core/src/rumEvent.types.ts index bc2468a92d..987305a25f 100644 --- a/packages/rum-core/src/rumEvent.types.ts +++ b/packages/rum-core/src/rumEvent.types.ts @@ -1288,44 +1288,7 @@ export type RumVitalEvent = CommonProperties & * RUM event type */ readonly type: 'vital' - /** - * Vital properties - */ - readonly vital: { - /** - * Type of the vital - */ - readonly type: 'duration' | 'step' - /** - * UUID of the vital - */ - readonly id: string - /** - * Optional key to distinguish between multiple operations of the same name running in parallel (e.g., 'photo_upload' with keys 'profile_pic' vs 'cover') - */ - readonly parent_id?: string - /** - * Name of the vital, as it is also used as facet path for its value, it must contain only letters, digits, or the characters - _ . @ $ - */ - readonly name?: string - /** - * Description of the vital. It can be used as a secondary identifier (URL, React component name...) - */ - readonly description?: string - /** - * Duration of the vital in nanoseconds - */ - readonly duration?: number - /** - * Type of the step that triggered the vital, if applicable - */ - readonly step_type?: 'start' | 'update' | 'retry' | 'end' - /** - * Reason for the failure of the step, if applicable - */ - readonly failure_reason?: 'error' | 'abandoned' | 'other' - [k: string]: unknown - } + readonly vital: DurationProperties | AppLaunchProperties | FeatureOperationProperties /** * Internal properties */ @@ -1344,6 +1307,90 @@ export type RumVitalEvent = CommonProperties & } [k: string]: unknown } +/** + * Duration properties of a Vital event + */ +export type DurationProperties = VitalCommonProperties & { + /** + * Type of the vital. + */ + readonly type: 'duration' + /** + * Duration of the vital in nanoseconds. + */ + readonly duration: number + [k: string]: unknown +} +/** + * Schema of common properties for a Vital event + */ +export type VitalCommonProperties = { + /** + * UUID of the vital + */ + readonly id: string + /** + * Name of the vital, as it is also used as facet path for its value, it must contain only letters, digits, or the characters - _ . @ $ + */ + readonly name?: string + /** + * Description of the vital. It can be used as a secondary identifier (URL, React component name...) + */ + readonly description?: string + [k: string]: unknown +} +/** + * Schema for app launch metrics. + */ +export type AppLaunchProperties = VitalCommonProperties & { + /** + * Type of the vital. + */ + readonly type: 'app_launch' + /** + * The metric of the app launch. + */ + readonly app_launch_metric: 'ttid' | 'ttfd' + /** + * Duration of the vital in nanoseconds. + */ + readonly duration: number + /** + * The type of the app launch. + */ + readonly startup_type?: 'cold_start' | 'warm_start' + /** + * Whether the app launch was prewarmed. + */ + readonly is_prewarmed?: boolean + /** + * If the app launch had a saved instance state bundle. + */ + readonly has_saved_instance_state_bundle?: boolean + [k: string]: unknown +} +/** + * Schema for a feature operation. + */ +export type FeatureOperationProperties = VitalCommonProperties & { + /** + * Type of the vital. + */ + readonly type: 'operation_step' + /** + * Optional key to distinguish between multiple operations of the same name running in parallel (e.g., 'photo_upload' with keys 'profile_pic' vs 'cover') + */ + readonly operation_key?: string + /** + * Type of the step that triggered the vital, if applicable + */ + readonly step_type?: 'start' | 'update' | 'retry' | 'end' + /** + * Reason for the failure of the step, if applicable + */ + readonly failure_reason?: 'error' | 'abandoned' | 'other' + [k: string]: unknown +} /** * Schema of common properties of RUM events @@ -1806,7 +1853,7 @@ export interface StreamSchema { */ bitrate?: number /** - * How long is the content (VOD only) + * How long is the content (VOD only) (in ms) */ readonly duration?: number /** @@ -1826,7 +1873,7 @@ export interface StreamSchema { */ timestamp?: number /** - * how much did the media progress since the last context update + * how much did the media progress since the last context update (in ms) */ watch_time?: number [k: string]: unknown From f1a5c9614650b811a32dc4eb2e0bb0f165369a79 Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Fri, 5 Sep 2025 11:47:00 +0200 Subject: [PATCH 09/19] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20remove=20plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rum-core/src/boot/preStartRum.ts | 12 --- packages/rum-core/src/domain/stream/index.ts | 32 ------ packages/rum-core/src/domain/stream/metric.ts | 40 ------- packages/rum-core/src/domain/stream/stream.ts | 100 ------------------ packages/rum-core/src/domain/stream/timer.ts | 34 ------ packages/rum-core/src/domainContext.types.ts | 3 +- packages/rum-core/src/index.ts | 1 - packages/rum-core/test/fixtures.ts | 19 ++++ packages/rum/src/entries/main.ts | 5 +- 9 files changed, 22 insertions(+), 224 deletions(-) delete mode 100644 packages/rum-core/src/domain/stream/index.ts delete mode 100644 packages/rum-core/src/domain/stream/metric.ts delete mode 100644 packages/rum-core/src/domain/stream/stream.ts delete mode 100644 packages/rum-core/src/domain/stream/timer.ts diff --git a/packages/rum-core/src/boot/preStartRum.ts b/packages/rum-core/src/boot/preStartRum.ts index 5a40e004fc..db5832ecff 100644 --- a/packages/rum-core/src/boot/preStartRum.ts +++ b/packages/rum-core/src/boot/preStartRum.ts @@ -34,17 +34,9 @@ import type { } from '../domain/vital/vitalCollection' import { startDurationVital, stopDurationVital } from '../domain/vital/vitalCollection' import { callPluginsMethod } from '../domain/plugins' -import { createStreamPlugin } from '../domain/stream' import type { StartRumResult } from './startRum' import type { RumPublicApiOptions, Strategy } from './rumPublicApi' -declare global { - interface Window { - DD_STREAM_PLUGIN: ReturnType - DD_STREAM: ReturnType['createStream']> - } -} - export function createPreStartStrategy( { ignoreInitIfSyntheticsWillInjectRum = true, startDeflateWorker }: RumPublicApiOptions, trackingConsentState: TrackingConsentState, @@ -188,10 +180,6 @@ export function createPreStartStrategy( initFeatureFlags(initConfiguration.enableExperimentalFeatures) // Expose the initial configuration regardless of initialization success. - window.DD_STREAM_PLUGIN = createStreamPlugin() - window.DD_STREAM = window.DD_STREAM_PLUGIN.createStream() - - initConfiguration.plugins = (initConfiguration.plugins ?? []).concat([window.DD_STREAM_PLUGIN.plugin]) cachedInitConfiguration = initConfiguration // If we are in a Synthetics test configured to automatically inject a RUM instance, we want diff --git a/packages/rum-core/src/domain/stream/index.ts b/packages/rum-core/src/domain/stream/index.ts deleted file mode 100644 index f5ca65c0a0..0000000000 --- a/packages/rum-core/src/domain/stream/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { RumPlugin, OnRumStartOptions } from '../..' -import type { AddEvent, API } from './stream' -import { createStream } from './stream' - -export function createStreamPlugin(): { plugin: RumPlugin; createStream: () => ReturnType } { - let addEvent: OnRumStartOptions['addEvent'] - const callbacks = new Set<(addEvent: OnRumStartOptions['addEvent']) => void>() - const store: AddEvent = (...args) => { - callbacks.add(() => { - addEvent!(...args) - }) - } - - const api: API = { - get addEvent() { - return addEvent ?? store - }, - } - - return { - plugin: { - name: 'stream', - onRumStart: (options) => { - addEvent = options.addEvent - - callbacks.forEach((callback) => callback(addEvent)) - callbacks.clear() - }, - }, - createStream: () => createStream(api), - } -} diff --git a/packages/rum-core/src/domain/stream/metric.ts b/packages/rum-core/src/domain/stream/metric.ts deleted file mode 100644 index a7b2f5e35f..0000000000 --- a/packages/rum-core/src/domain/stream/metric.ts +++ /dev/null @@ -1,40 +0,0 @@ -export function createWeightAverageMetric() { - let lastUpdate = 0 - let weight = 0 - let value: number | undefined - - return { - get value() { - return value - }, - update(newLastUpdate: number, newValue: number) { - if (newLastUpdate <= lastUpdate) { - return - } - - const newWeight = newLastUpdate - lastUpdate - value = ((value ?? 0) * weight + newValue * newWeight) / (weight + newWeight) - weight += newWeight - lastUpdate = newLastUpdate - }, - } -} - -export function createLastMetric() { - let lastUpdate = 0 - let value: number | undefined - - return { - get value() { - return value - }, - update(newLastUpdate: number, newValue: number) { - if (newLastUpdate <= lastUpdate) { - return - } - - value = newValue - lastUpdate = newLastUpdate - }, - } -} diff --git a/packages/rum-core/src/domain/stream/stream.ts b/packages/rum-core/src/domain/stream/stream.ts deleted file mode 100644 index 90dd27ba38..0000000000 --- a/packages/rum-core/src/domain/stream/stream.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { clocksNow, generateUUID, ONE_SECOND } from '@datadog/browser-core' -import type { StartRumResult } from '../..' -import { createLastMetric, createWeightAverageMetric } from './metric' -import { createTimer } from './timer' - -export interface API { - addEvent: AddEvent -} - -interface Meta { - duration?: number - format?: string - resolution?: string -} - -export type AddEvent = StartRumResult['addEvent'] - -type Transition = 'end' | 'error' | 'pause' | 'play' | 'preload' | 'quit' | 'rebuff' | 'seek' | 'start' - -export function createStream(api: API) { - const id = generateUUID() - const origin = clocksNow() - const timer = createTimer() - const meta: Meta = {} - const metrics = { - bitrate: createWeightAverageMetric(), - fps: createWeightAverageMetric(), - timestamp: createLastMetric(), - } - let documentVersion = 0 - - function sendStreamEvent() { - const now = clocksNow() - - api.addEvent( - now.relative, - { - date: now.timeStamp, - type: 'stream', - stream: { - id, - bitrate: metrics.bitrate.value, - duration: meta.duration, - document_version: documentVersion, - format: meta.format, - fps: metrics.fps.value, - resolution: meta.resolution, - timestamp: metrics.timestamp.value, - time_spent: (now.relative - origin.relative) * ONE_SECOND, - watch_time: timer.value * ONE_SECOND, - }, - }, - {} - ) - - documentVersion += 1 - } - - return { - interaction(id: string): void { - console.log('>>>', 'interaction', id) - }, - set(key: K, value: Meta[K]): void { - if (meta[key] !== undefined) { - return - } - - meta[key] = value - - sendStreamEvent() - }, - transition(state: Transition): void { - if (state === 'play') { - timer.start() - } - - if (state === 'pause' || state === 'end') { - timer.stop() - } - - const now = clocksNow() - - api.addEvent( - now.relative, - { - date: now.timeStamp, - type: 'action', - action: { id: generateUUID(), type: 'custom', target: { name: state } }, - stream: { id }, - }, - {} - ) - }, - update(key: keyof typeof metrics, value: number): void { - metrics[key].update(timer.value, value) - - sendStreamEvent() - }, - } -} diff --git a/packages/rum-core/src/domain/stream/timer.ts b/packages/rum-core/src/domain/stream/timer.ts deleted file mode 100644 index 5fd1f0ec94..0000000000 --- a/packages/rum-core/src/domain/stream/timer.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { RelativeTime } from '@datadog/browser-core' -import { relativeNow } from '@datadog/browser-core' - -export function createTimer() { - let counter = 0 - let start: RelativeTime = 0 as RelativeTime - let stopped = true - - return { - get value() { - if (stopped) { - return counter - } - - return relativeNow() - start - }, - start(): void { - if (!stopped) { - return - } - - start = relativeNow() - stopped = false - }, - stop(): void { - if (stopped) { - return - } - - counter = relativeNow() - start - stopped = true - }, - } -} diff --git a/packages/rum-core/src/domainContext.types.ts b/packages/rum-core/src/domainContext.types.ts index 9914b3d3e9..872aa659cd 100644 --- a/packages/rum-core/src/domainContext.types.ts +++ b/packages/rum-core/src/domainContext.types.ts @@ -17,7 +17,7 @@ export type RumEventDomainContext = T extends type : T extends typeof RumEventType.VITAL ? RumVitalEventDomainContext : T extends typeof RumEventType.STREAM - ? { streamId: string } + ? RumStreamEventDomainContext : never export interface RumViewEventDomainContext { @@ -62,4 +62,5 @@ export interface RumLongTaskEventDomainContext { // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface RumVitalEventDomainContext {} +// eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface RumStreamEventDomainContext {} diff --git a/packages/rum-core/src/index.ts b/packages/rum-core/src/index.ts index 237a7afbc7..c72f62a39c 100644 --- a/packages/rum-core/src/index.ts +++ b/packages/rum-core/src/index.ts @@ -71,4 +71,3 @@ export type { Hooks, DefaultRumEventAttributes, DefaultTelemetryEventAttributes export { createHooks } from './domain/hooks' export { isSampled } from './domain/sampler/sampler' export type { TracingOption, PropagatorType } from './domain/tracing/tracer.types' -export { createStreamPlugin } from './domain/stream' diff --git a/packages/rum-core/test/fixtures.ts b/packages/rum-core/test/fixtures.ts index d5c15aceff..ae1c44c415 100644 --- a/packages/rum-core/test/fixtures.ts +++ b/packages/rum-core/test/fixtures.ts @@ -119,6 +119,25 @@ export function createRawRumEvent(type: RumEventType, overrides?: Context): RawR }, overrides ) + case RumEventType.STREAM: + return combine( + { + type, + _dd: { + document_version: 0, + }, + date: 0 as TimeStamp, + view: { + id: generateUUID(), + action: { count: 0 }, + error: { count: 0 }, + long_task: { count: 0 }, + resource: { count: 0 }, + time_spent: 0 as ServerDuration, + }, + }, + overrides + ) } } diff --git a/packages/rum/src/entries/main.ts b/packages/rum/src/entries/main.ts index ac7acdeb7d..931eea2766 100644 --- a/packages/rum/src/entries/main.ts +++ b/packages/rum/src/entries/main.ts @@ -9,15 +9,12 @@ // Keep the following in sync with packages/rum-slim/src/entries/main.ts import { defineGlobal, getGlobalObject } from '@datadog/browser-core' import type { RumPublicApi } from '@datadog/browser-rum-core' -import { createStreamPlugin, makeRumPublicApi, startRum } from '@datadog/browser-rum-core' -import { createStream } from '@datadog/browser-rum-core/src/domain/stream/stream' +import { makeRumPublicApi, startRum } from '@datadog/browser-rum-core' import { makeRecorderApi } from '../boot/recorderApi' import { createDeflateEncoder, startDeflateWorker } from '../domain/deflate' import { lazyLoadRecorder } from '../boot/lazyLoadRecorder' import { makeProfilerApi } from '../boot/profilerApi' -export { createStreamPlugin } from '@datadog/browser-rum-core' - export type { User, Account, From 3953082af960a55b25bbbddc1c2b51b3b80844dd Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Fri, 5 Sep 2025 11:48:56 +0200 Subject: [PATCH 10/19] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20remove=20plugin=20en?= =?UTF-8?q?try?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rum/src/entries/main.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/rum/src/entries/main.ts b/packages/rum/src/entries/main.ts index 931eea2766..72a7696ad3 100644 --- a/packages/rum/src/entries/main.ts +++ b/packages/rum/src/entries/main.ts @@ -81,8 +81,6 @@ export const datadogRum = makeRumPublicApi(startRum, recorderApi, profilerApi, { createDeflateEncoder, sdkName: 'rum', }) -// @ts-expect-error TBF -datadogRum.createStreamPlugin = createStreamPlugin interface BrowserWindow extends Window { DD_RUM?: RumPublicApi From 9dcaf191d45498f26f227fd47428ac4aa7962936 Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Fri, 5 Sep 2025 15:26:41 +0200 Subject: [PATCH 11/19] =?UTF-8?q?=E2=9C=A8=20add=20unit=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rum-core/src/domain/assembly.spec.ts | 60 +++++++++++++++++++ packages/rum-core/test/fixtures.ts | 5 ++ 2 files changed, 65 insertions(+) diff --git a/packages/rum-core/src/domain/assembly.spec.ts b/packages/rum-core/src/domain/assembly.spec.ts index 52a8c15053..e4ab580d96 100644 --- a/packages/rum-core/src/domain/assembly.spec.ts +++ b/packages/rum-core/src/domain/assembly.spec.ts @@ -575,6 +575,66 @@ describe('rum assembly', () => { }) }) }) + + describe('STREAM event processing', () => { + it('should convert STREAM events to VIEW events', () => { + const { lifeCycle, serverRumEvents } = setupAssemblyTestWithDefaults({}) + + const streamData = { + id: 'stream-id-123', + document_version: 42, + time_spent: 5000000000, // 5 seconds in nanoseconds + } + + notifyRawRumEvent(lifeCycle, { + rawRumEvent: createRawRumEvent(RumEventType.STREAM, { + stream: streamData, + view: { id: 'original-view-id', url: '/test' } + }), + }) + + expect(serverRumEvents.length).toBe(1) + const resultEvent = serverRumEvents[0] + + expect(resultEvent.type).toBe('view') + }) + + it('should map stream properties correctly in converted VIEW event', () => { + const { lifeCycle, serverRumEvents } = setupAssemblyTestWithDefaults({}) + + const streamData = { + id: 'stream-id-456', + document_version: 25, + time_spent: 3000000000, // 3 seconds in nanoseconds + } + + notifyRawRumEvent(lifeCycle, { + rawRumEvent: createRawRumEvent(RumEventType.STREAM, { + stream: streamData, + view: { id: 'original-view-id', url: '/test-page' } + }), + }) + + expect(serverRumEvents.length).toBe(1) + const resultEvent = serverRumEvents[0] as any + + // Check _dd.document_version is set from stream.document_version + expect(resultEvent._dd.document_version).toBe(25) + + // Check view.id is set from stream.id + expect(resultEvent.view.id).toBe('stream-id-456') + + // Check view.time_spent is set from stream.time_spent + expect(resultEvent.view.time_spent).toBe(3000000000) + + // Check stream.time_spent is undefined in the stream object + expect(resultEvent.stream.time_spent).toBeUndefined() + + // Check action/error/resource counts are set to 0 + expect(resultEvent.view.action.count).toBe(0) + expect(resultEvent.view.error.count).toBe(0) + expect(resultEvent.view.resource.count).toBe(0) + }) }) function notifyRawRumEvent( diff --git a/packages/rum-core/test/fixtures.ts b/packages/rum-core/test/fixtures.ts index ae1c44c415..6b20160686 100644 --- a/packages/rum-core/test/fixtures.ts +++ b/packages/rum-core/test/fixtures.ts @@ -135,6 +135,11 @@ export function createRawRumEvent(type: RumEventType, overrides?: Context): RawR resource: { count: 0 }, time_spent: 0 as ServerDuration, }, + stream: { + id: generateUUID(), + document_version: 0, + time_spent: 0 as ServerDuration, + }, }, overrides ) From c90908bc92fba9dc49d42ef5b26bbd50b5bd34e4 Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Fri, 5 Sep 2025 15:30:48 +0200 Subject: [PATCH 12/19] =?UTF-8?q?=F0=9F=90=9B=20typo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rum-core/src/domain/assembly.spec.ts | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/rum-core/src/domain/assembly.spec.ts b/packages/rum-core/src/domain/assembly.spec.ts index e4ab580d96..5f8e608fa9 100644 --- a/packages/rum-core/src/domain/assembly.spec.ts +++ b/packages/rum-core/src/domain/assembly.spec.ts @@ -579,62 +579,63 @@ describe('rum assembly', () => { describe('STREAM event processing', () => { it('should convert STREAM events to VIEW events', () => { const { lifeCycle, serverRumEvents } = setupAssemblyTestWithDefaults({}) - + const streamData = { id: 'stream-id-123', document_version: 42, time_spent: 5000000000, // 5 seconds in nanoseconds } - + notifyRawRumEvent(lifeCycle, { - rawRumEvent: createRawRumEvent(RumEventType.STREAM, { + rawRumEvent: createRawRumEvent(RumEventType.STREAM, { stream: streamData, - view: { id: 'original-view-id', url: '/test' } + view: { id: 'original-view-id', url: '/test' }, }), }) - + expect(serverRumEvents.length).toBe(1) const resultEvent = serverRumEvents[0] - + expect(resultEvent.type).toBe('view') }) it('should map stream properties correctly in converted VIEW event', () => { const { lifeCycle, serverRumEvents } = setupAssemblyTestWithDefaults({}) - + const streamData = { id: 'stream-id-456', document_version: 25, time_spent: 3000000000, // 3 seconds in nanoseconds } - + notifyRawRumEvent(lifeCycle, { - rawRumEvent: createRawRumEvent(RumEventType.STREAM, { + rawRumEvent: createRawRumEvent(RumEventType.STREAM, { stream: streamData, - view: { id: 'original-view-id', url: '/test-page' } + view: { id: 'original-view-id', url: '/test-page' }, }), }) - + expect(serverRumEvents.length).toBe(1) const resultEvent = serverRumEvents[0] as any - + // Check _dd.document_version is set from stream.document_version expect(resultEvent._dd.document_version).toBe(25) - + // Check view.id is set from stream.id expect(resultEvent.view.id).toBe('stream-id-456') - + // Check view.time_spent is set from stream.time_spent expect(resultEvent.view.time_spent).toBe(3000000000) - + // Check stream.time_spent is undefined in the stream object expect(resultEvent.stream.time_spent).toBeUndefined() - + // Check action/error/resource counts are set to 0 expect(resultEvent.view.action.count).toBe(0) expect(resultEvent.view.error.count).toBe(0) expect(resultEvent.view.resource.count).toBe(0) }) + }) }) function notifyRawRumEvent( From d6cf31ccf05adaf937ec3da9efeb3bfbafffa83b Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Fri, 5 Sep 2025 17:37:57 +0200 Subject: [PATCH 13/19] =?UTF-8?q?=F0=9F=90=9B=20pr-feedback:=20add=20check?= =?UTF-8?q?=20for=20stream=20field?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rum-core/src/domain/assembly.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/rum-core/src/domain/assembly.spec.ts b/packages/rum-core/src/domain/assembly.spec.ts index 5f8e608fa9..81c2460599 100644 --- a/packages/rum-core/src/domain/assembly.spec.ts +++ b/packages/rum-core/src/domain/assembly.spec.ts @@ -618,6 +618,8 @@ describe('rum assembly', () => { expect(serverRumEvents.length).toBe(1) const resultEvent = serverRumEvents[0] as any + expect(resultEvent.stream).toBeDefined() + // Check _dd.document_version is set from stream.document_version expect(resultEvent._dd.document_version).toBe(25) From e4c327705cc36566d4592bfac910a3d231b21eb8 Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Tue, 16 Sep 2025 10:23:56 +0200 Subject: [PATCH 14/19] =?UTF-8?q?=F0=9F=90=9B=20fix=20new=20event=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rum-core/src/domain/assembly.ts | 5 +++++ packages/rum-core/src/rawRumEvent.types.ts | 1 + 2 files changed, 6 insertions(+) diff --git a/packages/rum-core/src/domain/assembly.ts b/packages/rum-core/src/domain/assembly.ts index fc7a59b289..5c7420e290 100644 --- a/packages/rum-core/src/domain/assembly.ts +++ b/packages/rum-core/src/domain/assembly.ts @@ -92,6 +92,11 @@ export function startRumAssembly( ...VIEW_MODIFIABLE_FIELD_PATHS, ...ROOT_MODIFIABLE_FIELD_PATHS, }, + [RumEventType.TRANSITION]: { + ...USER_CUSTOMIZABLE_FIELD_PATHS, + ...VIEW_MODIFIABLE_FIELD_PATHS, + ...ROOT_MODIFIABLE_FIELD_PATHS, + }, } const eventRateLimiters = { [RumEventType.ERROR]: createEventRateLimiter( diff --git a/packages/rum-core/src/rawRumEvent.types.ts b/packages/rum-core/src/rawRumEvent.types.ts index 5bac4a9310..afc96f63f5 100644 --- a/packages/rum-core/src/rawRumEvent.types.ts +++ b/packages/rum-core/src/rawRumEvent.types.ts @@ -29,6 +29,7 @@ export const RumEventType = { RESOURCE: 'resource', VITAL: 'vital', STREAM: 'stream', + TRANSITION: 'transition', } as const export type RumEventType = (typeof RumEventType)[keyof typeof RumEventType] From fdd98c75054532e3c117432055f23c6f5ae87cdc Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Tue, 16 Sep 2025 12:22:17 +0200 Subject: [PATCH 15/19] =?UTF-8?q?=F0=9F=90=9B=20fixes=20due=20to=20new=20e?= =?UTF-8?q?vent=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/panel/components/tabs/eventsTab/eventRow.tsx | 1 + packages/rum-core/src/domain/assembly.ts | 5 ----- packages/rum-core/src/domain/trackEventCounts.ts | 8 +++++++- packages/rum-core/src/rawRumEvent.types.ts | 1 - 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx b/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx index 6e9740fdce..5be2f21fde 100644 --- a/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx +++ b/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx @@ -34,6 +34,7 @@ const RUM_EVENT_TYPE_COLOR = { view: 'blue', resource: 'cyan', telemetry: 'teal', + transition: 'black', vital: 'orange', transition: 'green', } diff --git a/packages/rum-core/src/domain/assembly.ts b/packages/rum-core/src/domain/assembly.ts index 5c7420e290..fc7a59b289 100644 --- a/packages/rum-core/src/domain/assembly.ts +++ b/packages/rum-core/src/domain/assembly.ts @@ -92,11 +92,6 @@ export function startRumAssembly( ...VIEW_MODIFIABLE_FIELD_PATHS, ...ROOT_MODIFIABLE_FIELD_PATHS, }, - [RumEventType.TRANSITION]: { - ...USER_CUSTOMIZABLE_FIELD_PATHS, - ...VIEW_MODIFIABLE_FIELD_PATHS, - ...ROOT_MODIFIABLE_FIELD_PATHS, - }, } const eventRateLimiters = { [RumEventType.ERROR]: createEventRateLimiter( diff --git a/packages/rum-core/src/domain/trackEventCounts.ts b/packages/rum-core/src/domain/trackEventCounts.ts index ac2ec6debb..d9d4b43d95 100644 --- a/packages/rum-core/src/domain/trackEventCounts.ts +++ b/packages/rum-core/src/domain/trackEventCounts.ts @@ -30,7 +30,13 @@ export function trackEventCounts({ } const subscription = lifeCycle.subscribe(LifeCycleEventType.RUM_EVENT_COLLECTED, (event): void => { - if (event.type === 'view' || event.type === 'vital' || !isChildEvent(event) || ['stream'].includes(event.type)) { + if ( + event.type === 'view' || + event.type === 'vital' || + event.type === 'transition' || + !isChildEvent(event) || + ['stream'].includes(event.type) + ) { return } switch (event.type) { diff --git a/packages/rum-core/src/rawRumEvent.types.ts b/packages/rum-core/src/rawRumEvent.types.ts index afc96f63f5..5bac4a9310 100644 --- a/packages/rum-core/src/rawRumEvent.types.ts +++ b/packages/rum-core/src/rawRumEvent.types.ts @@ -29,7 +29,6 @@ export const RumEventType = { RESOURCE: 'resource', VITAL: 'vital', STREAM: 'stream', - TRANSITION: 'transition', } as const export type RumEventType = (typeof RumEventType)[keyof typeof RumEventType] From f655cee66225659881d93cbfa14cb1714b07b039 Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Tue, 16 Sep 2025 14:43:58 +0200 Subject: [PATCH 16/19] =?UTF-8?q?=F0=9F=90=9B=20add=20transition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rum-core/src/domain/assembly.ts | 5 +++++ .../src/domain/event/eventCollection.ts | 2 ++ packages/rum-core/src/rawRumEvent.types.ts | 19 +++++++++++++++++++ packages/rum-core/test/fixtures.ts | 15 +++++++++++++++ 4 files changed, 41 insertions(+) diff --git a/packages/rum-core/src/domain/assembly.ts b/packages/rum-core/src/domain/assembly.ts index fc7a59b289..5c7420e290 100644 --- a/packages/rum-core/src/domain/assembly.ts +++ b/packages/rum-core/src/domain/assembly.ts @@ -92,6 +92,11 @@ export function startRumAssembly( ...VIEW_MODIFIABLE_FIELD_PATHS, ...ROOT_MODIFIABLE_FIELD_PATHS, }, + [RumEventType.TRANSITION]: { + ...USER_CUSTOMIZABLE_FIELD_PATHS, + ...VIEW_MODIFIABLE_FIELD_PATHS, + ...ROOT_MODIFIABLE_FIELD_PATHS, + }, } const eventRateLimiters = { [RumEventType.ERROR]: createEventRateLimiter( diff --git a/packages/rum-core/src/domain/event/eventCollection.ts b/packages/rum-core/src/domain/event/eventCollection.ts index 16f0a123fa..6b100dcfd0 100644 --- a/packages/rum-core/src/domain/event/eventCollection.ts +++ b/packages/rum-core/src/domain/event/eventCollection.ts @@ -20,6 +20,8 @@ const allowedEventTypes = [ RumEventType.LONG_TASK, RumEventType.RESOURCE, RumEventType.STREAM, + RumEventType.STREAM, + RumEventType.TRANSITION, RumEventType.VITAL, ] as const diff --git a/packages/rum-core/src/rawRumEvent.types.ts b/packages/rum-core/src/rawRumEvent.types.ts index 5bac4a9310..7b16c77262 100644 --- a/packages/rum-core/src/rawRumEvent.types.ts +++ b/packages/rum-core/src/rawRumEvent.types.ts @@ -29,6 +29,7 @@ export const RumEventType = { RESOURCE: 'resource', VITAL: 'vital', STREAM: 'stream', + TRANSITION: 'transition', } as const export type RumEventType = (typeof RumEventType)[keyof typeof RumEventType] @@ -398,6 +399,23 @@ export interface RawRumStreamEvent { } } +export interface RawRumTransitionEvent { + date: TimeStamp + type: typeof RumEventType.TRANSITION + stream: { + id: string + } + transition: { + type: string + id?: string + timestamp?: number + buffer_starvation_duration?: number + media_start_delay?: number + error_code?: number + duration?: number + } +} + export type RawRumEvent = | RawRumErrorEvent | RawRumResourceEvent @@ -407,3 +425,4 @@ export type RawRumEvent = | RawRumActionEvent | RawRumVitalEvent | RawRumStreamEvent + | RawRumTransitionEvent diff --git a/packages/rum-core/test/fixtures.ts b/packages/rum-core/test/fixtures.ts index 6b20160686..53922f5dff 100644 --- a/packages/rum-core/test/fixtures.ts +++ b/packages/rum-core/test/fixtures.ts @@ -143,6 +143,21 @@ export function createRawRumEvent(type: RumEventType, overrides?: Context): RawR }, overrides ) + case RumEventType.TRANSITION: + return combine( + { + type, + date: 0 as TimeStamp, + stream: { + id: generateUUID(), + }, + transition: { + id: generateUUID(), + type: 'MEDIA_PLAYER_PLAY', + } + }, + overrides + ) } } From eba566c630d86ba007eaeb84c029408923ccece1 Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Tue, 16 Sep 2025 15:06:37 +0200 Subject: [PATCH 17/19] =?UTF-8?q?=F0=9F=90=9B=20format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rum-core/test/fixtures.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rum-core/test/fixtures.ts b/packages/rum-core/test/fixtures.ts index 53922f5dff..b2d1f57165 100644 --- a/packages/rum-core/test/fixtures.ts +++ b/packages/rum-core/test/fixtures.ts @@ -154,7 +154,7 @@ export function createRawRumEvent(type: RumEventType, overrides?: Context): RawR transition: { id: generateUUID(), type: 'MEDIA_PLAYER_PLAY', - } + }, }, overrides ) From 4af6b654e31323390eb0c12ee203532f705d86cf Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Wed, 17 Sep 2025 16:30:22 +0200 Subject: [PATCH 18/19] =?UTF-8?q?=F0=9F=90=9B=20view=20is=5Factive=20for?= =?UTF-8?q?=20streams?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rum-core/src/domain/assembly.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/rum-core/src/domain/assembly.ts b/packages/rum-core/src/domain/assembly.ts index 5c7420e290..63506251ca 100644 --- a/packages/rum-core/src/domain/assembly.ts +++ b/packages/rum-core/src/domain/assembly.ts @@ -152,6 +152,7 @@ export function startRumAssembly( view: { ...serverRumEvent.view, id: serverRumEvent.stream?.id, + is_active: true, action: { count: 0, }, From 48f23b9cabf44cb72d834b137ac0e55f85f40dee Mon Sep 17 00:00:00 2001 From: Adrian de la Rosa Date: Thu, 18 Sep 2025 17:55:58 +0200 Subject: [PATCH 19/19] =?UTF-8?q?=F0=9F=90=9B=20conflicts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/panel/components/tabs/eventsTab/eventRow.tsx | 1 - packages/rum-core/src/rawRumEvent.types.ts | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx b/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx index 5be2f21fde..6e9740fdce 100644 --- a/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx +++ b/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx @@ -34,7 +34,6 @@ const RUM_EVENT_TYPE_COLOR = { view: 'blue', resource: 'cyan', telemetry: 'teal', - transition: 'black', vital: 'orange', transition: 'green', } diff --git a/packages/rum-core/src/rawRumEvent.types.ts b/packages/rum-core/src/rawRumEvent.types.ts index 7b16c77262..7369d08ad4 100644 --- a/packages/rum-core/src/rawRumEvent.types.ts +++ b/packages/rum-core/src/rawRumEvent.types.ts @@ -17,6 +17,7 @@ import type { RumErrorEvent, RumLongTaskEvent, RumResourceEvent, + RumTransitionEvent, RumViewEvent, RumVitalEvent, } from './rumEvent.types' @@ -41,6 +42,7 @@ export type AssembledRumEvent = ( | RumErrorEvent | RumVitalEvent | RumLongTaskEvent + | RumTransitionEvent ) & Context