diff --git a/packages/rum-core/src/domain/rumEventsCollection/view/trackTimings.spec.ts b/packages/rum-core/src/domain/rumEventsCollection/view/trackTimings.spec.ts index 365cf7e9b6..0939789f7e 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/view/trackTimings.spec.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/view/trackTimings.spec.ts @@ -11,7 +11,7 @@ import { resetFirstHidden } from './trackFirstHidden' import { Timings, trackFirstContentfulPaint, - trackFirstInputDelay, + trackFirstInputTimings, trackLargestContentfulPaint, trackNavigationTimings, trackTimings, @@ -71,6 +71,7 @@ describe('trackTimings', () => { domInteractive: 234, firstContentfulPaint: 123, firstInputDelay: 100, + firstInputTime: 1000, loadEvent: 567, }) }) @@ -188,14 +189,16 @@ describe('largestContentfulPaint', () => { }) }) -describe('firstInputDelay', () => { +describe('firstInputTimings', () => { let setupBuilder: TestSetupBuilder - let fidCallback: jasmine.Spy<(value: number) => void> + let fitCallback: jasmine.Spy< + ({ firstInputDelay, firstInputTime }: { firstInputDelay: number; firstInputTime: number }) => void + > beforeEach(() => { - fidCallback = jasmine.createSpy() + fitCallback = jasmine.createSpy() setupBuilder = setup().beforeBuild(({ lifeCycle }) => { - return trackFirstInputDelay(lifeCycle, fidCallback) + return trackFirstInputTimings(lifeCycle, fitCallback) }) resetFirstHidden() }) @@ -206,12 +209,12 @@ describe('firstInputDelay', () => { resetFirstHidden() }) - it('should provide the first input delay', () => { + it('should provide the first input timings', () => { const { lifeCycle } = setupBuilder.build() lifeCycle.notify(LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, FAKE_FIRST_INPUT_ENTRY) - expect(fidCallback).toHaveBeenCalledTimes(1) - expect(fidCallback).toHaveBeenCalledWith(100) + expect(fitCallback).toHaveBeenCalledTimes(1) + expect(fitCallback).toHaveBeenCalledWith({ firstInputDelay: 100, firstInputTime: 1000 }) }) it('should not be present if the page is hidden', () => { @@ -220,6 +223,6 @@ describe('firstInputDelay', () => { lifeCycle.notify(LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, FAKE_FIRST_INPUT_ENTRY) - expect(fidCallback).not.toHaveBeenCalled() + expect(fitCallback).not.toHaveBeenCalled() }) }) diff --git a/packages/rum-core/src/domain/rumEventsCollection/view/trackTimings.ts b/packages/rum-core/src/domain/rumEventsCollection/view/trackTimings.ts index eae2f2f8e3..6f3081f00c 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/view/trackTimings.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/view/trackTimings.ts @@ -10,6 +10,7 @@ export interface Timings { loadEvent?: number largestContentfulPaint?: number firstInputDelay?: number + firstInputTime?: number } export function trackTimings(lifeCycle: LifeCycle, callback: (timings: Timings) => void) { @@ -28,9 +29,10 @@ export function trackTimings(lifeCycle: LifeCycle, callback: (timings: Timings) largestContentfulPaint, }) }) - const { stop: stopFIDTracking } = trackFirstInputDelay(lifeCycle, (firstInputDelay) => { + const { stop: stopFIDTracking } = trackFirstInputTimings(lifeCycle, ({ firstInputDelay, firstInputTime }) => { setTimings({ firstInputDelay, + firstInputTime, }) }) @@ -74,7 +76,7 @@ export function trackFirstContentfulPaint(lifeCycle: LifeCycle, callback: (fcp: } /** - * Track the largest contentful paint (LCP) occuring during the initial View. This can yield + * Track the largest contentful paint (LCP) occurring during the initial View. This can yield * multiple values, only the most recent one should be used. * Documentation: https://web.dev/lcp/ * Reference implementation: https://github.com/GoogleChrome/web-vitals/blob/master/src/getLCP.ts @@ -121,17 +123,25 @@ export function trackLargestContentfulPaint( } /** - * Track the first input delay (FID) occuring during the initial View. This yields at most one - * value. + * Track the first input occurring during the initial View to return: + * - First Input Delay + * - First Input Time + * Callback is called at most one time. * Documentation: https://web.dev/fid/ * Reference implementation: https://github.com/GoogleChrome/web-vitals/blob/master/src/getFID.ts */ -export function trackFirstInputDelay(lifeCycle: LifeCycle, callback: (value: number) => void) { +export function trackFirstInputTimings( + lifeCycle: LifeCycle, + callback: ({ firstInputDelay, firstInputTime }: { firstInputDelay: number; firstInputTime: number }) => void +) { const firstHidden = trackFirstHidden() const { unsubscribe: stop } = lifeCycle.subscribe(LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, (entry) => { if (entry.entryType === 'first-input' && entry.startTime < firstHidden.timeStamp) { - callback(entry.processingStart - entry.startTime) + callback({ + firstInputDelay: entry.processingStart - entry.startTime, + firstInputTime: entry.startTime, + }) } }) diff --git a/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.spec.ts b/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.spec.ts index b653daaecc..fdddae30f2 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.spec.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.spec.ts @@ -46,6 +46,7 @@ describe('viewCollection', () => { domInteractive: 10, firstContentfulPaint: 10, firstInputDelay: 12, + firstInputTime: 10, largestContentfulPaint: 10, loadEvent: 10, }, @@ -72,6 +73,7 @@ describe('viewCollection', () => { }, firstContentfulPaint: 10 * 1e6, firstInputDelay: 12 * 1e6, + firstInputTime: 10 * 1e6, isActive: false, largestContentfulPaint: 10 * 1e6, loadEvent: 10 * 1e6, diff --git a/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.ts b/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.ts index 534f75994f..ed278c5151 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.ts @@ -31,6 +31,7 @@ function processViewUpdate(view: View) { }, firstContentfulPaint: msToNs(view.timings.firstContentfulPaint), firstInputDelay: msToNs(view.timings.firstInputDelay), + firstInputTime: msToNs(view.timings.firstInputTime), isActive: view.isActive, largestContentfulPaint: msToNs(view.timings.largestContentfulPaint), loadEvent: msToNs(view.timings.loadEvent), diff --git a/packages/rum-core/src/rawRumEvent.types.ts b/packages/rum-core/src/rawRumEvent.types.ts index 8f3013fb88..ab94073081 100644 --- a/packages/rum-core/src/rawRumEvent.types.ts +++ b/packages/rum-core/src/rawRumEvent.types.ts @@ -1,4 +1,4 @@ -import { Context, ContextValue, ErrorSource, ResourceType } from '@datadog/browser-core' +import { Context, ErrorSource, ResourceType } from '@datadog/browser-core' import { ActionType } from './domain/rumEventsCollection/action/trackActions' import { PerformanceResourceDetailsElement } from './domain/rumEventsCollection/resource/resourceUtils' import { ViewLoadingType } from './domain/rumEventsCollection/view/trackViews' @@ -58,6 +58,7 @@ export interface RawRumViewEvent { loadingType: ViewLoadingType firstContentfulPaint?: number firstInputDelay?: number + firstInputTime?: number cumulativeLayoutShift?: number largestContentfulPaint?: number domInteractive?: number diff --git a/packages/rum-core/src/rumEvent.types.ts b/packages/rum-core/src/rumEvent.types.ts index cd9bf117b2..1230c68075 100644 --- a/packages/rum-core/src/rumEvent.types.ts +++ b/packages/rum-core/src/rumEvent.types.ts @@ -435,6 +435,10 @@ export type RumViewEvent = CommonProperties & { * Duration in ns of the first input event delay */ readonly first_input_delay?: number + /** + * Duration in ns to the first input + */ + readonly first_input_time?: number /** * Total layout shift score that occured on the view */ diff --git a/rum-events-format b/rum-events-format index 5c673c12f2..e9f5cba5f6 160000 --- a/rum-events-format +++ b/rum-events-format @@ -1 +1 @@ -Subproject commit 5c673c12f2fc464ec87dcb5e3a79b0f739a311b7 +Subproject commit e9f5cba5f6fc90adc2e65641780ff23864eca4b3 diff --git a/scripts/generate-schema-types.js b/scripts/generate-schema-types.js index c4308eaef2..f530df3a42 100644 --- a/scripts/generate-schema-types.js +++ b/scripts/generate-schema-types.js @@ -5,7 +5,7 @@ const prettier = require('prettier') const workingDirectory = path.join(__dirname, '../rum-events-format') const schemaPath = path.join(workingDirectory, 'rum-events-format.json') -const compiledTypesPath = path.join(__dirname, '../packages/rum/src/rumEvent.types.ts') +const compiledTypesPath = path.join(__dirname, '../packages/rum-core/src/rumEvent.types.ts') const prettierConfigPath = path.join(__dirname, '../.prettierrc.yml') async function main() {