From f69ad3c65cf29031ec8d30a30f75e0f5cf9583b5 Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Wed, 14 Aug 2024 13:02:00 -0400 Subject: [PATCH 01/14] update cls to include all entries --- .../utils/replayEventTemplates.ts | 8 ++-- .../tests/fixtures/ReplayRecordingData.ts | 6 +-- .../replay-internal/src/types/performance.ts | 8 +++- .../src/util/createPerformanceEntries.ts | 43 ++++++++++++------- .../unit/util/createPerformanceEntry.test.ts | 8 ++-- 5 files changed, 45 insertions(+), 28 deletions(-) diff --git a/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts b/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts index f4defc27182c..47f9e4ef6195 100644 --- a/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts +++ b/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts @@ -127,7 +127,7 @@ export const expectedLCPPerformanceSpan = { endTimestamp: expect.any(Number), data: { value: expect.any(Number), - nodeIds: expect.any(Array), + nodeId: expect.any(Number), rating: expect.any(String), size: expect.any(Number), }, @@ -140,7 +140,7 @@ export const expectedCLSPerformanceSpan = { endTimestamp: expect.any(Number), data: { value: expect.any(Number), - nodeIds: expect.any(Array), + nodeId: expect.any(Number), rating: expect.any(String), size: expect.any(Number), }, @@ -155,7 +155,7 @@ export const expectedFIDPerformanceSpan = { value: expect.any(Number), rating: expect.any(String), size: expect.any(Number), - nodeIds: expect.any(Array), + nodeId: expect.any(Number), }, }; @@ -168,7 +168,7 @@ export const expectedINPPerformanceSpan = { value: expect.any(Number), rating: expect.any(String), size: expect.any(Number), - nodeIds: expect.any(Array), + nodeId: expect.any(Number), }, }; diff --git a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts index 1b054c099b3d..5fbef10b8d5c 100644 --- a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts +++ b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts @@ -220,7 +220,7 @@ export const ReplayRecordingData = [ value: expect.any(Number), size: expect.any(Number), rating: expect.any(String), - nodeIds: [16], + nodeIds: 16, }, }, }, @@ -239,7 +239,7 @@ export const ReplayRecordingData = [ value: expect.any(Number), size: expect.any(Number), rating: expect.any(String), - nodeIds: expect.any(Array), + nodeId: expect.any(Number), }, }, }, @@ -258,7 +258,7 @@ export const ReplayRecordingData = [ value: expect.any(Number), size: expect.any(Number), rating: expect.any(String), - nodeIds: [10], + nodeId: 10, }, }, }, diff --git a/packages/replay-internal/src/types/performance.ts b/packages/replay-internal/src/types/performance.ts index 6b264a44ee9c..162676c92a06 100644 --- a/packages/replay-internal/src/types/performance.ts +++ b/packages/replay-internal/src/types/performance.ts @@ -108,9 +108,13 @@ export interface WebVitalData { */ rating: 'good' | 'needs-improvement' | 'poor'; /** - * The recording id of the web vital nodes. -1 if not found + * The recording id of the web vital node. -1 if not found */ - nodeIds?: number[]; + nodeId: number | undefined; + /** + * The layout shifts of a CLS metric + */ + layoutShift?: { value: number; sources?: number[]}[]; } /** diff --git a/packages/replay-internal/src/util/createPerformanceEntries.ts b/packages/replay-internal/src/util/createPerformanceEntries.ts index d55c2269d0f4..76cf0ec4d52e 100644 --- a/packages/replay-internal/src/util/createPerformanceEntries.ts +++ b/packages/replay-internal/src/util/createPerformanceEntries.ts @@ -43,7 +43,13 @@ export interface Metric { * The array may also be empty if the metric value was not based on any * entries (e.g. a CLS value of 0 given no layout shifts). */ - entries: PerformanceEntry[] | PerformanceEventTiming[]; + entries: PerformanceEntry[] | PerformanceEventTiming[] | LayoutShift[]; +} + +interface LayoutShift extends PerformanceEntry { + value: number; + sources: LayoutShiftAttribution[]; + hadRecentInput: boolean; } interface LayoutShiftAttribution { @@ -183,7 +189,7 @@ function createResourceEntry( */ export function getLargestContentfulPaint(metric: Metric): ReplayPerformanceEntry { const lastEntry = metric.entries[metric.entries.length - 1] as (PerformanceEntry & { element?: Node }) | undefined; - const node = lastEntry && lastEntry.element ? [lastEntry.element] : undefined; + const node = lastEntry && lastEntry.element ? lastEntry.element : undefined; return getWebVital(metric, 'largest-contentful-paint', node); } @@ -191,18 +197,24 @@ export function getLargestContentfulPaint(metric: Metric): ReplayPerformanceEntr * Add a CLS event to the replay based on a CLS metric. */ export function getCumulativeLayoutShift(metric: Metric): ReplayPerformanceEntry { - const lastEntry = metric.entries[metric.entries.length - 1] as - | (PerformanceEntry & { sources?: LayoutShiftAttribution[] }) - | undefined; - const nodes: Node[] = []; - if (lastEntry && lastEntry.sources) { - for (const source of lastEntry.sources) { - if (source.node) { - nodes.push(source.node); + const layoutShifts = []; + for (const entry of metric.entries) { + const layoutShift = entry as + | LayoutShift + | undefined; + if (layoutShift) { + const sources = []; + for (const source of layoutShift.sources) { + const nodeId = record.mirror.getId(source.node); + if (nodeId) { + sources.push(nodeId); + } } + layoutShifts.push({value: layoutShift.value, sources: sources.length ? sources : undefined}) } + } - return getWebVital(metric, 'cumulative-layout-shift', nodes); + return getWebVital(metric, 'cumulative-layout-shift', undefined, layoutShifts); } /** @@ -210,7 +222,7 @@ export function getCumulativeLayoutShift(metric: Metric): ReplayPerformanceEntry */ export function getFirstInputDelay(metric: Metric): ReplayPerformanceEntry { const lastEntry = metric.entries[metric.entries.length - 1] as (PerformanceEntry & { target?: Node }) | undefined; - const node = lastEntry && lastEntry.target ? [lastEntry.target] : undefined; + const node = lastEntry && lastEntry.target ? lastEntry.target : undefined; return getWebVital(metric, 'first-input-delay', node); } @@ -219,14 +231,14 @@ export function getFirstInputDelay(metric: Metric): ReplayPerformanceEntry { const lastEntry = metric.entries[metric.entries.length - 1] as (PerformanceEntry & { target?: Node }) | undefined; - const node = lastEntry && lastEntry.target ? [lastEntry.target] : undefined; + const node = lastEntry && lastEntry.target ? lastEntry.target : undefined; return getWebVital(metric, 'interaction-to-next-paint', node); } /** * Add an web vital event to the replay based on the web vital metric. */ -function getWebVital(metric: Metric, name: string, nodes: Node[] | undefined): ReplayPerformanceEntry { +function getWebVital(metric: Metric, name: string, node: Node | undefined, layoutShift?: {value: number, sources: number[] | undefined}[]): ReplayPerformanceEntry { const value = metric.value; const rating = metric.rating; @@ -241,7 +253,8 @@ function getWebVital(metric: Metric, name: string, nodes: Node[] | undefined): R value, size: value, rating, - nodeIds: nodes ? nodes.map(node => record.mirror.getId(node)) : undefined, + nodeId: record.mirror.getId(node), + layoutShift, }, }; diff --git a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts index d85698d1be1d..240de087b357 100644 --- a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts +++ b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts @@ -83,7 +83,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'largest-contentful-paint', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, rating: 'good', size: 5108.299, nodeIds: undefined }, + data: { value: 5108.299, rating: 'good', size: 5108.299, nodeId: undefined }, }); }); }); @@ -103,7 +103,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'cumulative-layout-shift', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [] }, + data: { value: 5108.299, size: 5108.299, rating: 'good' }, }); }); }); @@ -123,7 +123,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'first-input-delay', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: undefined }, + data: { value: 5108.299, size: 5108.299, rating: 'good' }, }); }); }); @@ -143,7 +143,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'interaction-to-next-paint', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: undefined }, + data: { value: 5108.299, size: 5108.299, rating: 'good' }, }); }); }); From 47b9d8c6c20ada81dd2b17d9b8bcc7d1032fe2fb Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Wed, 14 Aug 2024 13:02:48 -0400 Subject: [PATCH 02/14] update test --- .../test/unit/util/createPerformanceEntry.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts index 240de087b357..31b32bcfee7d 100644 --- a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts +++ b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts @@ -83,7 +83,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'largest-contentful-paint', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, rating: 'good', size: 5108.299, nodeId: undefined }, + data: { value: 5108.299, rating: 'good', size: 5108.299}, }); }); }); From 46d2fc883e8a96b24baba162bd0f9b5c7632a75d Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Wed, 14 Aug 2024 15:13:22 -0400 Subject: [PATCH 03/14] fix lint and tests --- .../tests/fixtures/ReplayRecordingData.ts | 3 ++- packages/replay-internal/src/types/performance.ts | 4 ++-- .../src/util/createPerformanceEntries.ts | 15 +++++++++------ .../test/unit/util/createPerformanceEntry.test.ts | 8 ++++---- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts index 5fbef10b8d5c..ad4792c7e14a 100644 --- a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts +++ b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts @@ -220,7 +220,7 @@ export const ReplayRecordingData = [ value: expect.any(Number), size: expect.any(Number), rating: expect.any(String), - nodeIds: 16, + nodeId: 16, }, }, }, @@ -240,6 +240,7 @@ export const ReplayRecordingData = [ size: expect.any(Number), rating: expect.any(String), nodeId: expect.any(Number), + layoutShift: expect.any(Array), }, }, }, diff --git a/packages/replay-internal/src/types/performance.ts b/packages/replay-internal/src/types/performance.ts index 162676c92a06..1e802241814d 100644 --- a/packages/replay-internal/src/types/performance.ts +++ b/packages/replay-internal/src/types/performance.ts @@ -111,10 +111,10 @@ export interface WebVitalData { * The recording id of the web vital node. -1 if not found */ nodeId: number | undefined; - /** + /** * The layout shifts of a CLS metric */ - layoutShift?: { value: number; sources?: number[]}[]; + layoutShift?: { value: number; sources?: number[] }[]; } /** diff --git a/packages/replay-internal/src/util/createPerformanceEntries.ts b/packages/replay-internal/src/util/createPerformanceEntries.ts index 76cf0ec4d52e..3d6b404ba8ae 100644 --- a/packages/replay-internal/src/util/createPerformanceEntries.ts +++ b/packages/replay-internal/src/util/createPerformanceEntries.ts @@ -199,9 +199,7 @@ export function getLargestContentfulPaint(metric: Metric): ReplayPerformanceEntr export function getCumulativeLayoutShift(metric: Metric): ReplayPerformanceEntry { const layoutShifts = []; for (const entry of metric.entries) { - const layoutShift = entry as - | LayoutShift - | undefined; + const layoutShift = entry as LayoutShift | undefined; if (layoutShift) { const sources = []; for (const source of layoutShift.sources) { @@ -210,7 +208,7 @@ export function getCumulativeLayoutShift(metric: Metric): ReplayPerformanceEntry sources.push(nodeId); } } - layoutShifts.push({value: layoutShift.value, sources: sources.length ? sources : undefined}) + layoutShifts.push({ value: layoutShift.value, sources: sources.length ? sources : undefined }) } } @@ -238,7 +236,12 @@ export function getInteractionToNextPaint(metric: Metric): ReplayPerformanceEntr /** * Add an web vital event to the replay based on the web vital metric. */ -function getWebVital(metric: Metric, name: string, node: Node | undefined, layoutShift?: {value: number, sources: number[] | undefined}[]): ReplayPerformanceEntry { +function getWebVital( + metric: Metric, + name: string, + node: Node | undefined, + layoutShift?: { value: number, sources: number[] | undefined }[], +): ReplayPerformanceEntry { const value = metric.value; const rating = metric.rating; @@ -253,7 +256,7 @@ function getWebVital(metric: Metric, name: string, node: Node | undefined, layou value, size: value, rating, - nodeId: record.mirror.getId(node), + nodeId: node ? record.mirror.getId(node) : undefined, layoutShift, }, }; diff --git a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts index 31b32bcfee7d..c10b3dee3f4a 100644 --- a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts +++ b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts @@ -83,7 +83,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'largest-contentful-paint', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, rating: 'good', size: 5108.299}, + data: { value: 5108.299, rating: 'good', size: 5108.299, nodeId: undefined, layoutShift: undefined }, }); }); }); @@ -103,7 +103,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'cumulative-layout-shift', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good' }, + data: { value: 5108.299, size: 5108.299, rating: 'good', nodeId: undefined, layoutShift: undefined }, }); }); }); @@ -123,7 +123,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'first-input-delay', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good' }, + data: { value: 5108.299, size: 5108.299, rating: 'good', nodeId: undefined, layoutShift: undefined }, }); }); }); @@ -143,7 +143,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'interaction-to-next-paint', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good' }, + data: { value: 5108.299, size: 5108.299, rating: 'good', nodeId: undefined, layoutShift: undefined }, }); }); }); From ccc3f9e19bde6023a090c114b7b11ec8a0a0e58f Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Wed, 14 Aug 2024 15:57:07 -0400 Subject: [PATCH 04/14] change back to nodeIds --- .../utils/replayEventTemplates.ts | 8 ++++---- .../tests/fixtures/ReplayRecordingData.ts | 2 +- .../tests/fixtures/ReplayRecordingData.ts | 6 +++--- packages/replay-internal/src/types/performance.ts | 4 ++-- .../src/util/createPerformanceEntries.ts | 10 +++++----- .../test/unit/util/createPerformanceEntry.test.ts | 8 ++++---- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts b/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts index 47f9e4ef6195..f4defc27182c 100644 --- a/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts +++ b/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts @@ -127,7 +127,7 @@ export const expectedLCPPerformanceSpan = { endTimestamp: expect.any(Number), data: { value: expect.any(Number), - nodeId: expect.any(Number), + nodeIds: expect.any(Array), rating: expect.any(String), size: expect.any(Number), }, @@ -140,7 +140,7 @@ export const expectedCLSPerformanceSpan = { endTimestamp: expect.any(Number), data: { value: expect.any(Number), - nodeId: expect.any(Number), + nodeIds: expect.any(Array), rating: expect.any(String), size: expect.any(Number), }, @@ -155,7 +155,7 @@ export const expectedFIDPerformanceSpan = { value: expect.any(Number), rating: expect.any(String), size: expect.any(Number), - nodeId: expect.any(Number), + nodeIds: expect.any(Array), }, }; @@ -168,7 +168,7 @@ export const expectedINPPerformanceSpan = { value: expect.any(Number), rating: expect.any(String), size: expect.any(Number), - nodeId: expect.any(Number), + nodeIds: expect.any(Array), }, }; diff --git a/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/tests/fixtures/ReplayRecordingData.ts b/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/tests/fixtures/ReplayRecordingData.ts index e7fd943c0f08..f65efa7cca2d 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/tests/fixtures/ReplayRecordingData.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/tests/fixtures/ReplayRecordingData.ts @@ -219,7 +219,7 @@ export const ReplayRecordingData = [ data: { value: expect.any(Number), size: expect.any(Number), - nodeId: 16, + nodeIds: [16], }, }, }, diff --git a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts index ad4792c7e14a..c2c98f97c264 100644 --- a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts +++ b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts @@ -220,7 +220,7 @@ export const ReplayRecordingData = [ value: expect.any(Number), size: expect.any(Number), rating: expect.any(String), - nodeId: 16, + nodeIds: [16], }, }, }, @@ -239,7 +239,7 @@ export const ReplayRecordingData = [ value: expect.any(Number), size: expect.any(Number), rating: expect.any(String), - nodeId: expect.any(Number), + nodeIds: expect.any(Array), layoutShift: expect.any(Array), }, }, @@ -259,7 +259,7 @@ export const ReplayRecordingData = [ value: expect.any(Number), size: expect.any(Number), rating: expect.any(String), - nodeId: 10, + nodeIds: [10], }, }, }, diff --git a/packages/replay-internal/src/types/performance.ts b/packages/replay-internal/src/types/performance.ts index 1e802241814d..f983759d425c 100644 --- a/packages/replay-internal/src/types/performance.ts +++ b/packages/replay-internal/src/types/performance.ts @@ -108,9 +108,9 @@ export interface WebVitalData { */ rating: 'good' | 'needs-improvement' | 'poor'; /** - * The recording id of the web vital node. -1 if not found + * The recording id of the web vital nodes. -1 if not found */ - nodeId: number | undefined; + nodeIds?: number[]; /** * The layout shifts of a CLS metric */ diff --git a/packages/replay-internal/src/util/createPerformanceEntries.ts b/packages/replay-internal/src/util/createPerformanceEntries.ts index 3d6b404ba8ae..25e215b435ee 100644 --- a/packages/replay-internal/src/util/createPerformanceEntries.ts +++ b/packages/replay-internal/src/util/createPerformanceEntries.ts @@ -189,7 +189,7 @@ function createResourceEntry( */ export function getLargestContentfulPaint(metric: Metric): ReplayPerformanceEntry { const lastEntry = metric.entries[metric.entries.length - 1] as (PerformanceEntry & { element?: Node }) | undefined; - const node = lastEntry && lastEntry.element ? lastEntry.element : undefined; + const node = lastEntry && lastEntry.element ? [lastEntry.element] : undefined; return getWebVital(metric, 'largest-contentful-paint', node); } @@ -220,7 +220,7 @@ export function getCumulativeLayoutShift(metric: Metric): ReplayPerformanceEntry */ export function getFirstInputDelay(metric: Metric): ReplayPerformanceEntry { const lastEntry = metric.entries[metric.entries.length - 1] as (PerformanceEntry & { target?: Node }) | undefined; - const node = lastEntry && lastEntry.target ? lastEntry.target : undefined; + const node = lastEntry && lastEntry.target ? [lastEntry.target] : undefined; return getWebVital(metric, 'first-input-delay', node); } @@ -229,7 +229,7 @@ export function getFirstInputDelay(metric: Metric): ReplayPerformanceEntry { const lastEntry = metric.entries[metric.entries.length - 1] as (PerformanceEntry & { target?: Node }) | undefined; - const node = lastEntry && lastEntry.target ? lastEntry.target : undefined; + const node = lastEntry && lastEntry.target ? [lastEntry.target] : undefined; return getWebVital(metric, 'interaction-to-next-paint', node); } @@ -239,7 +239,7 @@ export function getInteractionToNextPaint(metric: Metric): ReplayPerformanceEntr function getWebVital( metric: Metric, name: string, - node: Node | undefined, + nodes: Node[] | undefined, layoutShift?: { value: number, sources: number[] | undefined }[], ): ReplayPerformanceEntry { const value = metric.value; @@ -256,7 +256,7 @@ function getWebVital( value, size: value, rating, - nodeId: node ? record.mirror.getId(node) : undefined, + nodeIds: nodes ? nodes.map(node => record.mirror.getId(node)) : undefined, layoutShift, }, }; diff --git a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts index c10b3dee3f4a..40641e9af2a6 100644 --- a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts +++ b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts @@ -83,7 +83,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'largest-contentful-paint', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, rating: 'good', size: 5108.299, nodeId: undefined, layoutShift: undefined }, + data: { value: 5108.299, rating: 'good', size: 5108.299, nodeIds: [], layoutShift: undefined }, }); }); }); @@ -103,7 +103,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'cumulative-layout-shift', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good', nodeId: undefined, layoutShift: undefined }, + data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [], layoutShift: undefined }, }); }); }); @@ -123,7 +123,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'first-input-delay', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good', nodeId: undefined, layoutShift: undefined }, + data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [], layoutShift: undefined }, }); }); }); @@ -143,7 +143,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'interaction-to-next-paint', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good', nodeId: undefined, layoutShift: undefined }, + data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [], layoutShift: undefined }, }); }); }); From 3c4ac43b6de2862e359e21a66ffbf25c2c777641 Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:20:51 -0400 Subject: [PATCH 05/14] CI --- .size-limit.js | 2 +- .../tests/fixtures/ReplayRecordingData.ts | 1 - packages/replay-internal/src/util/createPerformanceEntries.ts | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.size-limit.js b/.size-limit.js index 859ce741cc3d..955bd22b65bc 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -29,7 +29,7 @@ module.exports = [ path: 'packages/browser/build/npm/esm/index.js', import: createImport('init', 'browserTracingIntegration', 'replayIntegration'), gzip: true, - limit: '66 KB', + limit: '67 KB', modifyWebpackConfig: function (config) { const webpack = require('webpack'); config.plugins.push( diff --git a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts index c2c98f97c264..164e7ae23482 100644 --- a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts +++ b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts @@ -239,7 +239,6 @@ export const ReplayRecordingData = [ value: expect.any(Number), size: expect.any(Number), rating: expect.any(String), - nodeIds: expect.any(Array), layoutShift: expect.any(Array), }, }, diff --git a/packages/replay-internal/src/util/createPerformanceEntries.ts b/packages/replay-internal/src/util/createPerformanceEntries.ts index 25e215b435ee..092356804dc2 100644 --- a/packages/replay-internal/src/util/createPerformanceEntries.ts +++ b/packages/replay-internal/src/util/createPerformanceEntries.ts @@ -208,7 +208,7 @@ export function getCumulativeLayoutShift(metric: Metric): ReplayPerformanceEntry sources.push(nodeId); } } - layoutShifts.push({ value: layoutShift.value, sources: sources.length ? sources : undefined }) + layoutShifts.push({ value: layoutShift.value, sources: sources.length ? sources : undefined }); } } @@ -240,7 +240,7 @@ function getWebVital( metric: Metric, name: string, nodes: Node[] | undefined, - layoutShift?: { value: number, sources: number[] | undefined }[], + layoutShift?: { value: number; sources: number[] | undefined }[], ): ReplayPerformanceEntry { const value = metric.value; const rating = metric.rating; From d228107001f08582e0dedfaf59952809e7e73650 Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:23:05 -0400 Subject: [PATCH 06/14] lint --- packages/replay-internal/src/util/createPerformanceEntries.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/replay-internal/src/util/createPerformanceEntries.ts b/packages/replay-internal/src/util/createPerformanceEntries.ts index 092356804dc2..1d1ae0ddca80 100644 --- a/packages/replay-internal/src/util/createPerformanceEntries.ts +++ b/packages/replay-internal/src/util/createPerformanceEntries.ts @@ -210,7 +210,6 @@ export function getCumulativeLayoutShift(metric: Metric): ReplayPerformanceEntry } layoutShifts.push({ value: layoutShift.value, sources: sources.length ? sources : undefined }); } - } return getWebVital(metric, 'cumulative-layout-shift', undefined, layoutShifts); } From 3bc50082abe78e90123b96fb69ca59ae0daa1ab6 Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:53:54 -0400 Subject: [PATCH 07/14] Update packages/replay-internal/src/util/createPerformanceEntries.ts Co-authored-by: Billy Vong --- packages/replay-internal/src/util/createPerformanceEntries.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/replay-internal/src/util/createPerformanceEntries.ts b/packages/replay-internal/src/util/createPerformanceEntries.ts index 1d1ae0ddca80..79b05b1f0337 100644 --- a/packages/replay-internal/src/util/createPerformanceEntries.ts +++ b/packages/replay-internal/src/util/createPerformanceEntries.ts @@ -208,7 +208,7 @@ export function getCumulativeLayoutShift(metric: Metric): ReplayPerformanceEntry sources.push(nodeId); } } - layoutShifts.push({ value: layoutShift.value, sources: sources.length ? sources : undefined }); + layoutShifts.push({ value: layoutShift.value, sources }); } } return getWebVital(metric, 'cumulative-layout-shift', undefined, layoutShifts); From 9a01f96c68cc950a8e9742455a32fc7824c855c2 Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Wed, 14 Aug 2024 20:34:39 -0400 Subject: [PATCH 08/14] PR comments --- .../tests/fixtures/ReplayRecordingData.ts | 2 +- .../replay-internal/src/types/performance.ts | 2 +- .../src/util/createPerformanceEntries.ts | 17 ++++++++++------- .../unit/util/createPerformanceEntry.test.ts | 8 ++++---- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts index 164e7ae23482..edf6e50f6b88 100644 --- a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts +++ b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts @@ -239,7 +239,7 @@ export const ReplayRecordingData = [ value: expect.any(Number), size: expect.any(Number), rating: expect.any(String), - layoutShift: expect.any(Array), + attributions: expect.any(Array), }, }, }, diff --git a/packages/replay-internal/src/types/performance.ts b/packages/replay-internal/src/types/performance.ts index f983759d425c..7a60e51684f3 100644 --- a/packages/replay-internal/src/types/performance.ts +++ b/packages/replay-internal/src/types/performance.ts @@ -114,7 +114,7 @@ export interface WebVitalData { /** * The layout shifts of a CLS metric */ - layoutShift?: { value: number; sources?: number[] }[]; + attributions?: { value: number; sources?: number[] }[]; } /** diff --git a/packages/replay-internal/src/util/createPerformanceEntries.ts b/packages/replay-internal/src/util/createPerformanceEntries.ts index 79b05b1f0337..af8099d5979d 100644 --- a/packages/replay-internal/src/util/createPerformanceEntries.ts +++ b/packages/replay-internal/src/util/createPerformanceEntries.ts @@ -43,7 +43,7 @@ export interface Metric { * The array may also be empty if the metric value was not based on any * entries (e.g. a CLS value of 0 given no layout shifts). */ - entries: PerformanceEntry[] | PerformanceEventTiming[] | LayoutShift[]; + entries: PerformanceEntry[] | LayoutShift[]; } interface LayoutShift extends PerformanceEntry { @@ -193,22 +193,25 @@ export function getLargestContentfulPaint(metric: Metric): ReplayPerformanceEntr return getWebVital(metric, 'largest-contentful-paint', node); } +function isLayoutShift(entry: PerformanceEntry | LayoutShift): entry is LayoutShift { + return (entry as LayoutShift).value !== undefined; +} + /** * Add a CLS event to the replay based on a CLS metric. */ export function getCumulativeLayoutShift(metric: Metric): ReplayPerformanceEntry { const layoutShifts = []; for (const entry of metric.entries) { - const layoutShift = entry as LayoutShift | undefined; - if (layoutShift) { + if (isLayoutShift(entry)) { const sources = []; - for (const source of layoutShift.sources) { + for (const source of entry.sources) { const nodeId = record.mirror.getId(source.node); if (nodeId) { sources.push(nodeId); } } - layoutShifts.push({ value: layoutShift.value, sources }); + layoutShifts.push({ value: entry.value, sources }); } } return getWebVital(metric, 'cumulative-layout-shift', undefined, layoutShifts); @@ -239,7 +242,7 @@ function getWebVital( metric: Metric, name: string, nodes: Node[] | undefined, - layoutShift?: { value: number; sources: number[] | undefined }[], + attributions?: { value: number; sources: number[] }[], ): ReplayPerformanceEntry { const value = metric.value; const rating = metric.rating; @@ -256,7 +259,7 @@ function getWebVital( size: value, rating, nodeIds: nodes ? nodes.map(node => record.mirror.getId(node)) : undefined, - layoutShift, + attributions, }, }; diff --git a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts index 40641e9af2a6..0494b22f6339 100644 --- a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts +++ b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts @@ -83,7 +83,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'largest-contentful-paint', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, rating: 'good', size: 5108.299, nodeIds: [], layoutShift: undefined }, + data: { value: 5108.299, rating: 'good', size: 5108.299, nodeIds: [], attributions: undefined }, }); }); }); @@ -103,7 +103,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'cumulative-layout-shift', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [], layoutShift: undefined }, + data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [], attributions: undefined }, }); }); }); @@ -123,7 +123,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'first-input-delay', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [], layoutShift: undefined }, + data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [], attributions: undefined }, }); }); }); @@ -143,7 +143,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'interaction-to-next-paint', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [], layoutShift: undefined }, + data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [], attributions: undefined }, }); }); }); From 505afeb3dce6da1e7d05f866ccce528f0dedd24b Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Thu, 15 Aug 2024 14:03:00 -0400 Subject: [PATCH 09/14] rename --- .../src/util/createPerformanceEntries.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/replay-internal/src/util/createPerformanceEntries.ts b/packages/replay-internal/src/util/createPerformanceEntries.ts index af8099d5979d..34fd4b97c03c 100644 --- a/packages/replay-internal/src/util/createPerformanceEntries.ts +++ b/packages/replay-internal/src/util/createPerformanceEntries.ts @@ -58,6 +58,11 @@ interface LayoutShiftAttribution { currentRect: DOMRectReadOnly; } +interface Attribution { + value: number; + nodeIds?: number[]; +} + /** * Handler creater for web vitals */ @@ -201,17 +206,17 @@ function isLayoutShift(entry: PerformanceEntry | LayoutShift): entry is LayoutSh * Add a CLS event to the replay based on a CLS metric. */ export function getCumulativeLayoutShift(metric: Metric): ReplayPerformanceEntry { - const layoutShifts = []; + const layoutShifts: Attribution[] = []; for (const entry of metric.entries) { if (isLayoutShift(entry)) { - const sources = []; + const nodeIds = []; for (const source of entry.sources) { const nodeId = record.mirror.getId(source.node); if (nodeId) { - sources.push(nodeId); + nodeIds.push(nodeId); } } - layoutShifts.push({ value: entry.value, sources }); + layoutShifts.push({ value: entry.value, nodeIds }); } } return getWebVital(metric, 'cumulative-layout-shift', undefined, layoutShifts); @@ -242,7 +247,7 @@ function getWebVital( metric: Metric, name: string, nodes: Node[] | undefined, - attributions?: { value: number; sources: number[] }[], + attributions?: Attribution[], ): ReplayPerformanceEntry { const value = metric.value; const rating = metric.rating; From b5cc4f02404f95f8edcc664a7c1b1cca4791f9c9 Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Mon, 26 Aug 2024 23:03:57 -0400 Subject: [PATCH 10/14] return all nodeIds --- .../replay-internal/src/util/createPerformanceEntries.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/replay-internal/src/util/createPerformanceEntries.ts b/packages/replay-internal/src/util/createPerformanceEntries.ts index 34fd4b97c03c..ea1a91e1e285 100644 --- a/packages/replay-internal/src/util/createPerformanceEntries.ts +++ b/packages/replay-internal/src/util/createPerformanceEntries.ts @@ -207,19 +207,21 @@ function isLayoutShift(entry: PerformanceEntry | LayoutShift): entry is LayoutSh */ export function getCumulativeLayoutShift(metric: Metric): ReplayPerformanceEntry { const layoutShifts: Attribution[] = []; + const allNodes: Node[] = []; for (const entry of metric.entries) { if (isLayoutShift(entry)) { const nodeIds = []; for (const source of entry.sources) { const nodeId = record.mirror.getId(source.node); - if (nodeId) { + if (source.node && nodeId) { nodeIds.push(nodeId); + allNodes.push(source.node); } } layoutShifts.push({ value: entry.value, nodeIds }); } } - return getWebVital(metric, 'cumulative-layout-shift', undefined, layoutShifts); + return getWebVital(metric, 'cumulative-layout-shift', allNodes, layoutShifts); } /** From bde59a9bd39f18b30112ccdfc4b8e4144e98cf11 Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Tue, 27 Aug 2024 10:12:24 -0400 Subject: [PATCH 11/14] update tests --- .../browser-integration-tests/utils/replayEventTemplates.ts | 1 + .../react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts b/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts index f4defc27182c..e711ea3bb0bb 100644 --- a/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts +++ b/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts @@ -141,6 +141,7 @@ export const expectedCLSPerformanceSpan = { data: { value: expect.any(Number), nodeIds: expect.any(Array), + attributions: expect.any(Array), rating: expect.any(String), size: expect.any(Number), }, diff --git a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts index edf6e50f6b88..76abdb1fa6b5 100644 --- a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts +++ b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts @@ -239,6 +239,7 @@ export const ReplayRecordingData = [ value: expect.any(Number), size: expect.any(Number), rating: expect.any(String), + nodeIds: expect.any(Array), attributions: expect.any(Array), }, }, From ee1c9ef9a820ea4e54291a0a9b5ece24c2d171ce Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Tue, 27 Aug 2024 10:36:40 -0400 Subject: [PATCH 12/14] rename --- .../src/util/createPerformanceEntries.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/replay-internal/src/util/createPerformanceEntries.ts b/packages/replay-internal/src/util/createPerformanceEntries.ts index ea1a91e1e285..4b29c36e8c90 100644 --- a/packages/replay-internal/src/util/createPerformanceEntries.ts +++ b/packages/replay-internal/src/util/createPerformanceEntries.ts @@ -207,21 +207,23 @@ function isLayoutShift(entry: PerformanceEntry | LayoutShift): entry is LayoutSh */ export function getCumulativeLayoutShift(metric: Metric): ReplayPerformanceEntry { const layoutShifts: Attribution[] = []; - const allNodes: Node[] = []; + const nodes: Node[] = []; for (const entry of metric.entries) { if (isLayoutShift(entry)) { const nodeIds = []; for (const source of entry.sources) { - const nodeId = record.mirror.getId(source.node); - if (source.node && nodeId) { - nodeIds.push(nodeId); - allNodes.push(source.node); + if (source.node) { + nodes.push(source.node); + const nodeId = record.mirror.getId(source.node); + if (nodeId) { + nodeIds.push(nodeId); + } } } layoutShifts.push({ value: entry.value, nodeIds }); } } - return getWebVital(metric, 'cumulative-layout-shift', allNodes, layoutShifts); + return getWebVital(metric, 'cumulative-layout-shift', nodes, layoutShifts); } /** From 2afdbb17a261c3ee35a72effc395c6b4633037fe Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:04:03 -0400 Subject: [PATCH 13/14] update browser tests --- .../test/unit/util/createPerformanceEntry.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts index 0494b22f6339..b82a8941269d 100644 --- a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts +++ b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts @@ -83,7 +83,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'largest-contentful-paint', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, rating: 'good', size: 5108.299, nodeIds: [], attributions: undefined }, + data: { value: 5108.299, rating: 'good', size: 5108.299, nodeIds: undefined, attributions: undefined }, }); }); }); @@ -103,7 +103,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'cumulative-layout-shift', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [], attributions: undefined }, + data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [], attributions: [] }, }); }); }); @@ -123,7 +123,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'first-input-delay', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [], attributions: undefined }, + data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: undefined, attributions: undefined }, }); }); }); @@ -143,7 +143,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'interaction-to-next-paint', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [], attributions: undefined }, + data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: undefined, attributions: undefined }, }); }); }); From 6fd3b0c62d6f9ea2d37c2fb828f9c185871db68c Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Wed, 28 Aug 2024 14:13:13 -0400 Subject: [PATCH 14/14] comments --- packages/replay-internal/src/util/createPerformanceEntries.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/replay-internal/src/util/createPerformanceEntries.ts b/packages/replay-internal/src/util/createPerformanceEntries.ts index 4b29c36e8c90..830f878dc8ea 100644 --- a/packages/replay-internal/src/util/createPerformanceEntries.ts +++ b/packages/replay-internal/src/util/createPerformanceEntries.ts @@ -199,7 +199,7 @@ export function getLargestContentfulPaint(metric: Metric): ReplayPerformanceEntr } function isLayoutShift(entry: PerformanceEntry | LayoutShift): entry is LayoutShift { - return (entry as LayoutShift).value !== undefined; + return (entry as LayoutShift).sources !== undefined; } /**