ui, tracker, backend: support long animation metrics (#3262)

* ui, tracker, backend: support long animation metrics

* ui: fix LAT mapping

* ui: change jump button display, longTask time definition

* ui: border for rows

* ui: refine LAT design

* tracker: regenerate messages
This commit is contained in:
Delirium 2025-05-13 12:04:14 +02:00 committed by GitHub
parent 55d435be87
commit b1b21937ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 1953 additions and 643 deletions

View file

@ -84,6 +84,7 @@ const (
MsgPartitionedMessage = 82
MsgNetworkRequest = 83
MsgWSChannel = 84
MsgLongAnimationTask = 89
MsgInputChange = 112
MsgSelectionChange = 113
MsgMouseThrashing = 114
@ -2294,6 +2295,37 @@ func (msg *WSChannel) TypeID() int {
return 84
}
type LongAnimationTask struct {
message
Name string
Duration int64
BlockingDuration int64
FirstUIEventTimestamp int64
StartTime int64
Scripts string
}
func (msg *LongAnimationTask) Encode() []byte {
buf := make([]byte, 61+len(msg.Name)+len(msg.Scripts))
buf[0] = 89
p := 1
p = WriteString(msg.Name, buf, p)
p = WriteInt(msg.Duration, buf, p)
p = WriteInt(msg.BlockingDuration, buf, p)
p = WriteInt(msg.FirstUIEventTimestamp, buf, p)
p = WriteInt(msg.StartTime, buf, p)
p = WriteString(msg.Scripts, buf, p)
return buf[:p]
}
func (msg *LongAnimationTask) Decode() Message {
return msg
}
func (msg *LongAnimationTask) TypeID() int {
return 89
}
type InputChange struct {
message
ID uint64

View file

@ -1419,6 +1419,30 @@ func DecodeWSChannel(reader BytesReader) (Message, error) {
return msg, err
}
func DecodeLongAnimationTask(reader BytesReader) (Message, error) {
var err error = nil
msg := &LongAnimationTask{}
if msg.Name, err = reader.ReadString(); err != nil {
return nil, err
}
if msg.Duration, err = reader.ReadInt(); err != nil {
return nil, err
}
if msg.BlockingDuration, err = reader.ReadInt(); err != nil {
return nil, err
}
if msg.FirstUIEventTimestamp, err = reader.ReadInt(); err != nil {
return nil, err
}
if msg.StartTime, err = reader.ReadInt(); err != nil {
return nil, err
}
if msg.Scripts, err = reader.ReadString(); err != nil {
return nil, err
}
return msg, err
}
func DecodeInputChange(reader BytesReader) (Message, error) {
var err error = nil
msg := &InputChange{}
@ -2248,6 +2272,8 @@ func ReadMessage(t uint64, reader BytesReader) (Message, error) {
return DecodeNetworkRequest(reader)
case 84:
return DecodeWSChannel(reader)
case 89:
return DecodeLongAnimationTask(reader)
case 112:
return DecodeInputChange(reader)
case 113:

View file

@ -339,6 +339,23 @@ class PageEvent(Message):
self.web_vitals = web_vitals
class StringDictGlobal(Message):
__id__ = 34
def __init__(self, key, value):
self.key = key
self.value = value
class SetNodeAttributeDictGlobal(Message):
__id__ = 35
def __init__(self, id, name, value):
self.id = id
self.name = name
self.value = value
class CSSInsertRule(Message):
__id__ = 37
@ -789,6 +806,18 @@ class WSChannel(Message):
self.message_type = message_type
class LongAnimationTask(Message):
__id__ = 89
def __init__(self, name, duration, blocking_duration, first_ui_event_timestamp, start_time, scripts):
self.name = name
self.duration = duration
self.blocking_duration = blocking_duration
self.first_ui_event_timestamp = first_ui_event_timestamp
self.start_time = start_time
self.scripts = scripts
class InputChange(Message):
__id__ = 112

View file

@ -511,6 +511,30 @@ cdef class PageEvent(PyMessage):
self.web_vitals = web_vitals
cdef class StringDictGlobal(PyMessage):
cdef public int __id__
cdef public unsigned long key
cdef public str value
def __init__(self, unsigned long key, str value):
self.__id__ = 34
self.key = key
self.value = value
cdef class SetNodeAttributeDictGlobal(PyMessage):
cdef public int __id__
cdef public unsigned long id
cdef public unsigned long name
cdef public unsigned long value
def __init__(self, unsigned long id, unsigned long name, unsigned long value):
self.__id__ = 35
self.id = id
self.name = name
self.value = value
cdef class CSSInsertRule(PyMessage):
cdef public int __id__
cdef public unsigned long id
@ -1176,6 +1200,25 @@ cdef class WSChannel(PyMessage):
self.message_type = message_type
cdef class LongAnimationTask(PyMessage):
cdef public int __id__
cdef public str name
cdef public long duration
cdef public long blocking_duration
cdef public long first_ui_event_timestamp
cdef public long start_time
cdef public str scripts
def __init__(self, str name, long duration, long blocking_duration, long first_ui_event_timestamp, long start_time, str scripts):
self.__id__ = 89
self.name = name
self.duration = duration
self.blocking_duration = blocking_duration
self.first_ui_event_timestamp = first_ui_event_timestamp
self.start_time = start_time
self.scripts = scripts
cdef class InputChange(PyMessage):
cdef public int __id__
cdef public unsigned long id

View file

@ -360,6 +360,19 @@ class MessageCodec(Codec):
web_vitals=self.read_string(reader)
)
if message_id == 34:
return StringDictGlobal(
key=self.read_uint(reader),
value=self.read_string(reader)
)
if message_id == 35:
return SetNodeAttributeDictGlobal(
id=self.read_uint(reader),
name=self.read_uint(reader),
value=self.read_uint(reader)
)
if message_id == 37:
return CSSInsertRule(
id=self.read_uint(reader),
@ -716,6 +729,16 @@ class MessageCodec(Codec):
message_type=self.read_string(reader)
)
if message_id == 89:
return LongAnimationTask(
name=self.read_string(reader),
duration=self.read_int(reader),
blocking_duration=self.read_int(reader),
first_ui_event_timestamp=self.read_int(reader),
start_time=self.read_int(reader),
scripts=self.read_string(reader)
)
if message_id == 112:
return InputChange(
id=self.read_uint(reader),

View file

@ -458,6 +458,19 @@ cdef class MessageCodec:
web_vitals=self.read_string(reader)
)
if message_id == 34:
return StringDictGlobal(
key=self.read_uint(reader),
value=self.read_string(reader)
)
if message_id == 35:
return SetNodeAttributeDictGlobal(
id=self.read_uint(reader),
name=self.read_uint(reader),
value=self.read_uint(reader)
)
if message_id == 37:
return CSSInsertRule(
id=self.read_uint(reader),
@ -814,6 +827,16 @@ cdef class MessageCodec:
message_type=self.read_string(reader)
)
if message_id == 89:
return LongAnimationTask(
name=self.read_string(reader),
duration=self.read_int(reader),
blocking_duration=self.read_int(reader),
first_ui_event_timestamp=self.read_int(reader),
start_time=self.read_int(reader),
scripts=self.read_string(reader)
)
if message_id == 112:
return InputChange(
id=self.read_uint(reader),

View file

@ -14,8 +14,8 @@ import {
EXCEPTIONS,
INSPECTOR,
OVERVIEW,
BACKENDLOGS,
} from 'App/mstore/uiPlayerStore';
BACKENDLOGS, LONG_TASK
} from "App/mstore/uiPlayerStore";
import { WebNetworkPanel } from 'Shared/DevTools/NetworkPanel';
import Storage from 'Components/Session_/Storage';
import { ConnectedPerformance } from 'Components/Session_/Performance';
@ -31,6 +31,7 @@ import { PlayerContext } from 'App/components/Session/playerContext';
import { debounce } from 'App/utils';
import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore';
import LongTaskPanel from "../../../shared/DevTools/LongTaskPanel/LongTaskPanel";
import BackendLogsPanel from '../SharedComponents/BackendLogs/BackendLogsPanel';
interface IProps {
@ -158,20 +159,7 @@ function Player(props: IProps) {
onMouseDown={handleResize}
className="w-full h-2 cursor-ns-resize absolute top-0 left-0 z-20"
/>
{bottomBlock === OVERVIEW && <OverviewPanel />}
{bottomBlock === CONSOLE && <ConsolePanel />}
{bottomBlock === NETWORK && (
<WebNetworkPanel panelHeight={panelHeight} />
)}
{bottomBlock === STACKEVENTS && <WebStackEventPanel />}
{bottomBlock === STORAGE && <Storage />}
{bottomBlock === PROFILER && (
<ProfilerPanel panelHeight={panelHeight} />
)}
{bottomBlock === PERFORMANCE && <ConnectedPerformance />}
{bottomBlock === GRAPHQL && <GraphQL panelHeight={panelHeight} />}
{bottomBlock === EXCEPTIONS && <Exceptions />}
{bottomBlock === BACKENDLOGS && <BackendLogsPanel />}
<BottomBlock block={bottomBlock} panelHeight={panelHeight} />
</div>
)}
{!fullView ? (
@ -189,4 +177,31 @@ function Player(props: IProps) {
);
}
function BottomBlock({ panelHeight, block }: { panelHeight: number; block: number }) {
switch (block) {
case CONSOLE:
return <ConsolePanel />;
case NETWORK:
return <WebNetworkPanel panelHeight={panelHeight} />;
case STACKEVENTS:
return <WebStackEventPanel />;
case STORAGE:
return <Storage />;
case PROFILER:
return <ProfilerPanel panelHeight={panelHeight} />;
case PERFORMANCE:
return <ConnectedPerformance />;
case GRAPHQL:
return <GraphQL panelHeight={panelHeight} />;
case EXCEPTIONS:
return <Exceptions />;
case BACKENDLOGS:
return <BackendLogsPanel />;
case LONG_TASK:
return <LongTaskPanel />;
default:
return null;
}
}
export default observer(Player);

View file

@ -29,11 +29,12 @@ import {
STACKEVENTS,
STORAGE,
BACKENDLOGS,
} from 'App/mstore/uiPlayerStore';
LONG_TASK
} from "App/mstore/uiPlayerStore";
import { Icon } from 'UI';
import LogsButton from 'App/components/Session/Player/SharedComponents/BackendLogs/LogsButton';
import { CodeOutlined, DashboardOutlined, ClusterOutlined } from '@ant-design/icons';
import { ArrowDownUp, ListCollapse, Merge, Waypoints } from 'lucide-react'
import { ArrowDownUp, ListCollapse, Merge, Waypoints, Timer } from 'lucide-react'
import ControlButton from './ControlButton';
import Timeline from './Timeline';
@ -293,7 +294,11 @@ const DevtoolsButtons = observer(
graphql: {
icon: <Merge size={14} strokeWidth={2} />,
label: 'Graphql',
}
},
longTask: {
icon: <Timer size={14} strokeWidth={2} />,
label: t('Long Tasks'),
},
}
// @ts-ignore
const getLabel = (block: string) => labels[block][showIcons ? 'icon' : 'label']
@ -359,6 +364,14 @@ const DevtoolsButtons = observer(
label={getLabel('performance')}
/>
<ControlButton
customKey="longTask"
disabled={disableButtons}
onClick={() => toggleBottomTools(LONG_TASK)}
active={bottomBlock === LONG_TASK && !inspectorMode}
label={getLabel('longTask')}
/>
{showGraphql && (
<ControlButton
disabled={disableButtons}

View file

@ -2,7 +2,6 @@ import React, { useState } from 'react';
import cn from 'classnames';
import { Icon } from 'UI';
import JumpButton from 'Shared/DevTools/JumpButton';
import { Tag } from 'antd';
import TabTag from '../TabTag';
interface Props {

View file

@ -13,6 +13,11 @@ function JumpButton(props: Props) {
const { tooltip } = props;
return (
<div className="absolute right-2 top-0 bottom-0 my-auto flex items-center">
{props.time ? (
<div className="block mr-2 text-sm">
{shortDurationFromMs(props.time)}
</div>
) : null}
<Tooltip title={tooltip} disabled={!tooltip}>
<Button
type="default"
@ -27,11 +32,6 @@ function JumpButton(props: Props) {
>
JUMP
</Button>
{props.time ? (
<div className="block group-hover:hidden mr-2 text-sm">
{shortDurationFromMs(props.time)}
</div>
) : null}
</Tooltip>
</div>
);

View file

@ -0,0 +1,265 @@
import React from 'react';
import { observer } from 'mobx-react-lite';
import { useTranslation } from 'react-i18next';
import { Input } from 'antd';
import { VList, VListHandle } from 'virtua';
import { PlayerContext } from 'App/components/Session/playerContext';
import JumpButton from '../JumpButton';
import { useRegExListFilterMemo } from '../useListFilter';
import BottomBlock from '../BottomBlock';
import { NoContent, Icon } from 'UI';
import { InfoCircleOutlined } from '@ant-design/icons';
import { Segmented, Select, Tag } from 'antd';
import { LongAnimationTask } from './type';
import Script from './Script';
import TaskTimeline from './TaskTimeline';
import { Hourglass } from 'lucide-react';
interface Row extends LongAnimationTask {
time: number;
}
const TABS = {
all: 'all',
blocking: 'blocking',
};
const SORT_BY = {
timeAsc: 'timeAsc',
blocking: 'blockingDesc',
duration: 'durationDesc',
};
function LongTaskPanel() {
const { t } = useTranslation();
const [tab, setTab] = React.useState(TABS.all);
const [sortBy, setSortBy] = React.useState(SORT_BY.timeAsc);
const _list = React.useRef<VListHandle>(null);
const { player, store } = React.useContext(PlayerContext);
const [searchValue, setSearchValue] = React.useState('');
const { currentTab, tabStates } = store.get();
const longTasks = tabStates[currentTab]?.longTaskList || [];
const filteredList = useRegExListFilterMemo(
longTasks,
(task: LongAnimationTask) => [
task.name,
task.scripts.map((script) => script.name).join(','),
task.scripts.map((script) => script.sourceURL).join(','),
],
searchValue,
);
const onFilterChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setSearchValue(value);
};
const onRowClick = (time: number) => {
player.jump(time);
};
const rows: Row[] = React.useMemo(() => {
let rowMap = filteredList.map((task) => ({
...task,
time: task.time ?? task.startTime,
}));
if (tab === 'blocking') {
rowMap = rowMap.filter((task) => task.blockingDuration > 0);
}
switch (sortBy) {
case SORT_BY.blocking:
rowMap = rowMap.sort((a, b) => b.blockingDuration - a.blockingDuration);
break;
case SORT_BY.duration:
rowMap = rowMap.sort((a, b) => b.duration - a.duration);
break;
default:
rowMap = rowMap.sort((a, b) => a.time - b.time);
}
return rowMap;
}, [filteredList.length, tab, sortBy]);
const blockingTasks = React.useMemo(() => {
let blockingAmount = 0;
for (const task of longTasks) {
if (task.blockingDuration > 0) {
blockingAmount++;
}
}
return blockingAmount;
}, [longTasks.length]);
return (
<BottomBlock style={{ height: '100%' }}>
<BottomBlock.Header>
<div className="flex items-center gap-2">
<span className="font-semibold color-gray-medium mr-4">
{t('Long Tasks')}
</span>
</div>
<div className="flex items-center gap-4">
<Segmented
size={'small'}
value={tab}
onChange={setTab}
options={[
{ label: t('All'), value: 'all' },
{
label: (
<div>
{t('Blocking')} ({blockingTasks})
</div>
),
value: 'blocking',
},
]}
/>
<Select
size="small"
className="rounded-lg"
value={sortBy}
onChange={setSortBy}
popupMatchSelectWidth={150}
dropdownStyle={{ minWidth: '150px' }}
options={[
{ label: t('Default Order'), value: 'timeAsc' },
{ label: t('Blocking Duration'), value: 'blockingDesc' },
{ label: t('Task Duration'), value: 'durationDesc' },
]}
/>
<Input.Search
className="rounded-lg"
placeholder={t('Filter by name or source URL')}
name="filter"
onChange={onFilterChange}
value={searchValue}
size="small"
/>
</div>
</BottomBlock.Header>
<BottomBlock.Content>
<NoContent
title={
<div className="capitalize flex items-center gap-2">
<InfoCircleOutlined size={18} />
{t('No Data')}
</div>
}
size="small"
show={filteredList.length === 0}
>
<VList ref={_list} itemSize={25}>
{rows.map((task) => (
<LongTaskRow key={task.time} task={task} onJump={onRowClick} />
))}
</VList>
</NoContent>
</BottomBlock.Content>
</BottomBlock>
);
}
function LongTaskRow({
task,
onJump,
}: {
task: Row;
onJump: (time: number) => void;
}) {
const [expanded, setExpanded] = React.useState(false);
return (
<div
className={
'relative border-b border-neutral-950/5 group hover:bg-active-blue py-1 px-4 pe-8'
}
>
<div className="flex flex-col w-full">
<TaskTitle expanded={expanded} entry={task} toggleExpand={() => setExpanded(!expanded)} />
{expanded ? (
<>
<TaskTimeline task={task} />
<div className={'flex items-center gap-1 mb-2'}>
<div className={'text-neutral-900 font-medium'}>
First UI event timestamp:
</div>
<div className="text-neutral-600 font-mono block">
{Math.round(task.firstUIEventTimestamp)} ms
</div>
</div>
<div className={'text-neutral-900 font-medium'}>Scripts:</div>
<div className="flex flex-col gap-1">
{task.scripts.map((script, index) => (
<Script script={script} key={index} />
))}
</div>
</>
) : null}
</div>
<JumpButton time={task.time} onClick={() => onJump(task.time)} />
</div>
);
}
function TaskTitle({
entry,
toggleExpand,
expanded,
}: {
entry: {
name: string;
duration: number;
blockingDuration?: number;
scripts: LongAnimationTask['scripts'];
};
expanded: boolean;
toggleExpand: () => void;
}) {
const isBlocking =
entry.blockingDuration !== undefined && entry.blockingDuration > 0;
const scriptTitles = entry.scripts.map((script) =>
script.invokerType ? script.invokerType : script.name,
);
const { title, plusMore } = getFirstTwoScripts(scriptTitles);
return (
<div className={'flex items-center gap-1 text-sm cursor-pointer'} onClick={toggleExpand}>
<Icon
name={expanded ? 'caret-down-fill' : 'caret-right-fill'}
/>
<span className="font-mono font-bold">{title}</span>
<Tag color="default" bordered={false}>
{plusMore}
</Tag>
<span className={'text-neutral-600 font-mono'}>
{Math.round(entry.duration)} ms
</span>
{isBlocking ? (
<Tag
bordered={false}
color="red"
className="font-mono rounded-lg text-xs flex gap-1 items-center text-red-600"
>
<Hourglass size={11} /> {Math.round(entry.blockingDuration!)} ms
blocking
</Tag>
) : null}
</div>
);
}
function getFirstTwoScripts(titles: string[]) {
if (titles.length === 0) {
return { title: 'Long Animation Task', plusMore: null };
}
const additional = titles.length - 2;
const additionalStr = additional > 0 ? `+ ${additional} more` : null;
return {
title: `${titles[0]}${titles[1] ? `, ${titles[1]}` : ''}`,
plusMore: additionalStr,
};
}
export default observer(LongTaskPanel);

View file

@ -0,0 +1,72 @@
import React from 'react';
import { LongAnimationTask } from './type';
import { Tag } from 'antd';
import { Code } from 'lucide-react';
function getAddress(script: LongAnimationTask['scripts'][number]) {
return `${script.sourceURL}${script.sourceFunctionName ? ':' + script.sourceFunctionName : ''}${script.sourceCharPosition && script.sourceCharPosition >= 0 ? ':' + script.sourceCharPosition : ''}`;
}
function ScriptTitle({
script,
}: {
script: LongAnimationTask['scripts'][number];
}) {
return script.invokerType ? (
<span>{script.invokerType}</span>
) : (
<span>{script.name}</span>
);
}
function ScriptInfo({
script,
}: {
script: LongAnimationTask['scripts'][number];
}) {
const hasInvoker = script.invoker !== script.sourceURL;
return (
<div className={'border-l border-l-gray-light pl-1'}>
{hasInvoker ? (
<InfoEntry title={'invoker:'} value={script.invoker} />
) : null}
<InfoEntry title={'address:'} value={getAddress(script)} />
<InfoEntry
title={'script execution:'}
value={`${Math.round(script.duration)} ms`}
/>
<InfoEntry
title={'pause duration:'}
value={`${Math.round(script.pauseDuration)} ms`}
/>
</div>
);
}
function InfoEntry({
title,
value,
}: {
title: string;
value: string | number;
}) {
return (
<div className={'flex items-center gap-1 text-sm'}>
<div className={'text-disabled-text'}>{title}</div>
<div className='font-mono text-neutral-600'>{value}</div>
</div>
);
}
function Script({ script }: { script: LongAnimationTask['scripts'][number] }) {
return (
<div className="flex flex-col mb-4">
<Tag className='w-fit font-mono text-sm font-bold flex gap-1 items-center rounded-lg'>
<Code size={12} />
<ScriptTitle script={script} />
</Tag>
<ScriptInfo script={script} />
</div>
);
}
export default Script;

View file

@ -0,0 +1,79 @@
import React from 'react'
import { Tooltip } from 'antd'
import { LongAnimationTask } from "./type";
import cn from "classnames";
const getSeverityClass = (duration: number) => {
if (duration > 200) return 'bg-[#CC0000]';
if (duration > 100) return 'bg-[#EFB100]';
return 'bg-[#66a299]';
};
function TaskTimeline({ task }: { task: LongAnimationTask }) {
const totalDuration = task.duration;
const scriptDuration = task.scripts.reduce((sum, script) => sum + script.duration, 0);
const layoutDuration = task.scripts.reduce(
(sum, script) => sum + (script.forcedStyleAndLayoutDuration || 0),
0
);
const idleDuration = totalDuration - scriptDuration - layoutDuration;
const scriptWidth = (scriptDuration / totalDuration) * 100;
const layoutWidth = (layoutDuration / totalDuration) * 100;
const idleWidth = (idleDuration / totalDuration) * 100;
return (
<div className="w-full mb-2 mt-1">
<div className="text-gray-dark mb-1">Timeline:</div>
<div className="flex h-2 w-full rounded overflow-hidden">
{scriptDuration > 0 && (
<TimelineSegment
classes={`${getSeverityClass(scriptDuration)} h-full`}
name={`Script: ${Math.round(scriptDuration)}ms`}
width={scriptWidth}
/>
)}
{idleDuration > 0 && (
<TimelineSegment
classes="bg-gray-light h-full bg-[repeating-linear-gradient(45deg,#ccc_0px,#ccc_5px,#f2f2f2_5px,#f2f2f2_10px)]"
width={idleWidth}
name={`Idle: ${Math.round(idleDuration)}ms`}
/>
)}
{layoutDuration > 0 && (
<TimelineSegment
classes="bg-[#8200db] h-full"
width={layoutWidth}
name={`Layout & Style: ${Math.round(layoutDuration)}ms`}
/>
)}
</div>
<div className="flex justify-between text-xs text-gray-500 mt-1">
<span>start: {Math.round(task.startTime)}ms</span>
<span>finish: {Math.round(task.startTime + task.duration)}ms</span>
</div>
</div>
);
}
function TimelineSegment({
name,
classes,
width,
}: {
name: string;
width: number;
classes: string;
}) {
return (
<Tooltip title={name}>
<div
style={{ width: `${width}%` }}
className={cn(classes)}
/>
</Tooltip>
);
}
export default TaskTimeline;

View file

@ -0,0 +1,268 @@
export const mockData = [
{
name: 'long-animation-frame',
entryType: 'long-animation-frame',
startTime: 3.5,
duration: 74.5,
renderStart: 77.79999923706055,
styleAndLayoutStart: 77.89999961853027,
firstUIEventTimestamp: 0,
blockingDuration: 0,
scripts: [
{
name: 'script',
entryType: 'script',
startTime: 31,
duration: 5,
invoker: 'http://localhost:3333/2325/session/3249172234241386834',
invokerType: 'classic-script',
windowAttribution: 'self',
executionStart: 35,
forcedStyleAndLayoutDuration: 0,
pauseDuration: 0,
sourceURL: 'http://localhost:3333/2325/session/3249172234241386834',
sourceFunctionName: '',
sourceCharPosition: 0,
},
],
},
{
name: 'long-animation-frame',
entryType: 'long-animation-frame',
startTime: 615.8999996185303,
duration: 464.8999996185303,
renderStart: 1080.6999998092651,
styleAndLayoutStart: 1080.7999992370605,
firstUIEventTimestamp: 0,
blockingDuration: 414.361,
scripts: [
{
name: 'script',
entryType: 'script',
startTime: 616.0999994277954,
duration: 234,
invoker: 'http://localhost:3333/app-3a809cc.js',
invokerType: 'classic-script',
windowAttribution: 'self',
executionStart: 849.8999996185303,
forcedStyleAndLayoutDuration: 0,
pauseDuration: 0,
sourceURL: 'http://localhost:3333/app-3a809cc.js',
sourceFunctionName: '',
sourceCharPosition: 0,
},
{
name: 'script',
entryType: 'script',
startTime: 850.8999996185303,
duration: 219,
invoker: 'http://localhost:3333/app-22de34a.js',
invokerType: 'classic-script',
windowAttribution: 'self',
executionStart: 930.5999994277954,
forcedStyleAndLayoutDuration: 0,
pauseDuration: 0,
sourceURL: 'http://localhost:3333/app-22de34a.js',
sourceFunctionName: '',
sourceCharPosition: 0,
},
{
name: 'script',
entryType: 'script',
startTime: 1070.5999994277954,
duration: 9,
invoker: '#document.onDOMContentLoaded',
invokerType: 'event-listener',
windowAttribution: 'self',
executionStart: 1070.5999994277954,
forcedStyleAndLayoutDuration: 0,
pauseDuration: 0,
sourceURL: 'http://localhost:3333/app-22de34a.js',
sourceFunctionName: '',
sourceCharPosition: 2298086,
},
],
},
{
name: 'long-animation-frame',
entryType: 'long-animation-frame',
startTime: 1081.3999996185303,
duration: 55.69999980926514,
renderStart: 1136.5999994277954,
styleAndLayoutStart: 1136.6999998092651,
firstUIEventTimestamp: 0,
blockingDuration: 0,
scripts: [
{
name: 'script',
entryType: 'script',
startTime: 1081.3999996185303,
duration: 45,
invoker: 'MessagePort.onmessage',
invokerType: 'event-listener',
windowAttribution: 'self',
executionStart: 1081.3999996185303,
forcedStyleAndLayoutDuration: 0,
pauseDuration: 0,
sourceURL: 'http://localhost:3333/app-3a809cc.js',
sourceFunctionName: 'performWorkUntilDeadline',
sourceCharPosition: 8985606,
},
],
},
{
name: 'long-animation-frame',
entryType: 'long-animation-frame',
startTime: 1495.7999992370605,
duration: 56.40000057220459,
renderStart: 1552.0999994277954,
styleAndLayoutStart: 1552.0999994277954,
firstUIEventTimestamp: 0,
blockingDuration: 0,
scripts: [
{
name: 'script',
entryType: 'script',
startTime: 1495.8999996185303,
duration: 18,
invoker:
'http://localhost:3333/vendors-node_modules_store_ant-design-icons-virtual-42686020c5_package_es_icons_SyncOutlined_-1757ec.app-446e3c1.js',
invokerType: 'classic-script',
windowAttribution: 'self',
executionStart: 1514.5999994277954,
forcedStyleAndLayoutDuration: 0,
pauseDuration: 0,
sourceURL:
'http://localhost:3333/vendors-node_modules_store_ant-design-icons-virtual-42686020c5_package_es_icons_SyncOutlined_-1757ec.app-446e3c1.js',
sourceFunctionName: '',
sourceCharPosition: 0,
},
{
name: 'script',
entryType: 'script',
startTime: 1517.1999998092651,
duration: 12,
invoker:
'http://localhost:3333/app_components_Session_Player_ReplayPlayer_PlayerInst_tsx-app_components_Session__Player_Over-d1e8de.app-1eb48ad.js',
invokerType: 'classic-script',
windowAttribution: 'self',
executionStart: 1529.6999998092651,
forcedStyleAndLayoutDuration: 0,
pauseDuration: 0,
sourceURL:
'http://localhost:3333/app_components_Session_Player_ReplayPlayer_PlayerInst_tsx-app_components_Session__Player_Over-d1e8de.app-1eb48ad.js',
sourceFunctionName: '',
sourceCharPosition: 0,
},
{
name: 'script',
entryType: 'script',
startTime: 1531.5999994277954,
duration: 19,
invoker:
'http://localhost:3333/app_components_Session_Session_tsx-app_components_Session_Tabs_tabs_module_css-app_components-e2a7c2.app-fd8d38a.js',
invokerType: 'classic-script',
windowAttribution: 'self',
executionStart: 1539.1999998092651,
forcedStyleAndLayoutDuration: 0,
pauseDuration: 0,
sourceURL:
'http://localhost:3333/app_components_Session_Session_tsx-app_components_Session_Tabs_tabs_module_css-app_components-e2a7c2.app-fd8d38a.js',
sourceFunctionName: '',
sourceCharPosition: 0,
},
],
},
{
name: 'long-animation-frame',
entryType: 'long-animation-frame',
startTime: 2392.699999809265,
duration: 139.5,
renderStart: 2529.8999996185303,
styleAndLayoutStart: 2531,
firstUIEventTimestamp: 2529.8999996185303,
blockingDuration: 85.95,
scripts: [
{
name: 'script',
entryType: 'script',
startTime: 2392.699999809265,
duration: 133,
invoker: 'Response.json.then',
invokerType: 'resolve-promise',
windowAttribution: 'self',
executionStart: 2392.699999809265,
forcedStyleAndLayoutDuration: 6,
pauseDuration: 0,
sourceURL: 'http://localhost:3333/app-22de34a.js',
sourceFunctionName: '',
sourceCharPosition: -1,
},
],
},
{
name: 'long-animation-frame',
entryType: 'long-animation-frame',
startTime: 2536.2999992370605,
duration: 117,
renderStart: 2650.3999996185303,
styleAndLayoutStart: 2653.2999992370605,
firstUIEventTimestamp: 2650.3999996185303,
blockingDuration: 60.8,
scripts: [
{
name: 'script',
entryType: 'script',
startTime: 2541.0999994277954,
duration: 107,
invoker: 'Response.arrayBuffer.then',
invokerType: 'resolve-promise',
windowAttribution: 'self',
executionStart: 2541.0999994277954,
forcedStyleAndLayoutDuration: 3,
pauseDuration: 0,
sourceURL: 'http://localhost:3333/app-22de34a.js',
sourceFunctionName: '',
sourceCharPosition: -1,
},
],
},
{
name: 'long-animation-frame',
entryType: 'long-animation-frame',
startTime: 5621.099999427795,
duration: 62.90000057220459,
renderStart: 5680.5,
styleAndLayoutStart: 5682.099999427795,
firstUIEventTimestamp: 5633.199999809265,
blockingDuration: 0,
scripts: [
{
name: 'script',
entryType: 'script',
startTime: 5635.39999961853,
duration: 43,
invoker: 'DIV#app.onclick',
invokerType: 'event-listener',
windowAttribution: 'self',
executionStart: 5635.39999961853,
forcedStyleAndLayoutDuration: 2,
pauseDuration: 0,
sourceURL: 'http://localhost:3333/app-3a809cc.js',
sourceFunctionName: 'dispatchDiscreteEvent',
sourceCharPosition: 7365614,
},
],
},
{
name: 'long-animation-frame',
entryType: 'long-animation-frame',
startTime: 31203.599999427795,
duration: 118.5,
renderStart: 31322,
styleAndLayoutStart: 31322.099999427795,
firstUIEventTimestamp: 0,
blockingDuration: 0,
scripts: [],
},
];

View file

@ -0,0 +1,21 @@
export interface LongAnimationTask {
name: string;
duration: number;
blockingDuration: number;
firstUIEventTimestamp: number;
startTime: number;
time?: number;
scripts: [
{
name: string;
duration: number;
invoker: string;
invokerType: string;
pauseDuration: number;
sourceURL: string;
sourceFunctionName: string;
sourceCharPosition: number;
forcedStyleAndLayoutDuration: number;
},
];
}

View file

@ -18,6 +18,7 @@ export const EXCEPTIONS = 9;
export const INSPECTOR = 11;
export const OVERVIEW = 12;
export const BACKENDLOGS = 13;
export const LONG_TASK = 14;
export const blocks = {
none: NONE,
@ -33,6 +34,7 @@ export const blocks = {
inspector: INSPECTOR,
overview: OVERVIEW,
backendLogs: BACKENDLOGS,
longTask: LONG_TASK,
} as const;
export const blockValues = [
@ -49,6 +51,7 @@ export const blockValues = [
INSPECTOR,
OVERVIEW,
BACKENDLOGS,
LONG_TASK
] as const;
export default class UiPlayerStore {

View file

@ -11,9 +11,11 @@ import {
ConsoleLog as logMsg,
WsChannel as websocketMsg,
Profiler as profilerMsg,
LongAnimationTask as longTaskMsg,
} from 'Player/web/messages';
import ListWalker from '../common/ListWalker';
import ListWalkerWithMarks from '../common/ListWalkerWithMarks';
import { ILongAnimationTask } from './types/longTask';
type stackMsg = {
name: string;
@ -43,6 +45,7 @@ type MsgTypeMap = {
profilerList: profilerMsg;
exceptionsList: exceptionsMsg;
frustrationsList: Issue | InjectedEvent;
longTaskList: ILongAnimationTask;
};
type ListMessageType<K> = K extends keyof MsgTypeMap
? Array<MsgTypeMap[K]>
@ -66,6 +69,7 @@ const MARKED_LIST_NAMES = [
'fetch',
'stack',
'websocket',
'longTask',
] as const;
const LIST_NAMES = [...SIMPLE_LIST_NAMES, ...MARKED_LIST_NAMES] as const;

View file

@ -33,6 +33,7 @@ import { TYPES as EVENT_TYPES } from 'Types/session/event';
import { Decoder } from 'syncod';
import type { PerformanceChartPoint } from './managers/PerformanceTrackManager';
import { getLongTask } from './types/longTask';
export interface TabState extends ListsState {
performanceAvailability?: PerformanceTrackManager['availability'];
@ -349,6 +350,9 @@ export default class TabSessionManager {
case MType.Profiler:
this.lists.lists.profiles.append(msg);
break;
case MType.LongAnimationTask:
this.lists.lists.longTask.append(getLongTask(msg));
break;
/* ===|=== */
default:
switch (msg.tp) {

View file

@ -1,22 +1,22 @@
// Auto-generated, do not edit
/* eslint-disable */
import PrimitiveReader from './PrimitiveReader'
import { MType } from './raw.gen'
import type { RawMessage } from './raw.gen'
import PrimitiveReader from './PrimitiveReader';
import { MType } from './raw.gen';
import type { RawMessage } from './raw.gen';
export default class RawMessageReader extends PrimitiveReader {
readMessage(): RawMessage | null {
const p = this.p;
const p = this.p
const resetPointer = () => {
this.p = p;
return null;
};
const tp = this.readUint();
if (tp === null) {
return resetPointer();
this.p = p
return null
}
const tp = this.readUint()
if (tp === null) { return resetPointer() }
switch (tp) {
case 0: {
@ -733,6 +733,24 @@ export default class RawMessageReader extends PrimitiveReader {
};
}
case 89: {
const name = this.readString(); if (name === null) { return resetPointer() }
const duration = this.readInt(); if (duration === null) { return resetPointer() }
const blockingDuration = this.readInt(); if (blockingDuration === null) { return resetPointer() }
const firstUIEventTimestamp = this.readInt(); if (firstUIEventTimestamp === null) { return resetPointer() }
const startTime = this.readInt(); if (startTime === null) { return resetPointer() }
const scripts = this.readString(); if (scripts === null) { return resetPointer() }
return {
tp: MType.LongAnimationTask,
name,
duration,
blockingDuration,
firstUIEventTimestamp,
startTime,
scripts,
};
}
case 113: {
const selectionStart = this.readUint(); if (selectionStart === null) { return resetPointer() }
const selectionEnd = this.readUint(); if (selectionEnd === null) { return resetPointer() }

View file

@ -1,16 +1,10 @@
// Auto-generated, do not edit
/* eslint-disable */
import { MType } from './raw.gen';
import { MType } from './raw.gen'
const IOS_TYPES = [
90, 91, 92, 93, 94, 95, 96, 97, 98, 100, 101, 102, 103, 104, 105, 106, 107,
110, 111,
];
const DOM_TYPES = [
0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 34, 35, 37,38,49, 50,
51, 43, 52, 54, 55, 57, 58, 59,60, 61, 67,68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
113, 114, 117, 118, 119, 122,
];
const IOS_TYPES = [90,91,92,93,94,95,96,97,98,100,101,102,103,104,105,106,107,110,111]
const DOM_TYPES = [0,4,5,6,7,8,9,10,11,12,13,14,15,16,18,19,20,34,35,37,38,49,50,51,43,52,54,55,57,58,59,60,61,67,68,69,70,71,72,73,74,75,76,77,113,114,117,118,119,122]
export function isDOMType(t: MType) {
return DOM_TYPES.includes(t);
}
return DOM_TYPES.includes(t)
}

View file

@ -1,8 +1,9 @@
// Auto-generated, do not edit
/* eslint-disable */
import type { Timed } from './timed';
import type { Timed } from './timed'
import type { RawMessage } from './raw.gen'
import type {
RawMessage,
RawTimestamp,
RawSetPageLocationDeprecated,
RawSetViewportSize,
@ -61,6 +62,7 @@ import type {
RawZustand,
RawNetworkRequest,
RawWsChannel,
RawLongAnimationTask,
RawSelectionChange,
RawMouseThrashing,
RawResourceTiming,
@ -81,53 +83,52 @@ import type {
RawMobileNetworkCall,
RawMobileSwipeEvent,
RawMobileIssueEvent,
} from './raw.gen';
} from './raw.gen'
export type Message = RawMessage & Timed;
export type Message = RawMessage & Timed
export type Timestamp = RawTimestamp & Timed;
export type SetPageLocationDeprecated = RawSetPageLocationDeprecated & Timed;
export type Timestamp = RawTimestamp & Timed
export type SetViewportSize = RawSetViewportSize & Timed;
export type SetPageLocationDeprecated = RawSetPageLocationDeprecated & Timed
export type SetViewportScroll = RawSetViewportScroll & Timed;
export type SetViewportSize = RawSetViewportSize & Timed
export type CreateDocument = RawCreateDocument & Timed;
export type SetViewportScroll = RawSetViewportScroll & Timed
export type CreateElementNode = RawCreateElementNode & Timed;
export type CreateDocument = RawCreateDocument & Timed
export type CreateTextNode = RawCreateTextNode & Timed;
export type CreateElementNode = RawCreateElementNode & Timed
export type MoveNode = RawMoveNode & Timed;
export type CreateTextNode = RawCreateTextNode & Timed
export type RemoveNode = RawRemoveNode & Timed;
export type MoveNode = RawMoveNode & Timed
export type SetNodeAttribute = RawSetNodeAttribute & Timed;
export type RemoveNode = RawRemoveNode & Timed
export type RemoveNodeAttribute = RawRemoveNodeAttribute & Timed;
export type SetNodeAttribute = RawSetNodeAttribute & Timed
export type SetNodeData = RawSetNodeData & Timed;
export type RemoveNodeAttribute = RawRemoveNodeAttribute & Timed
export type SetCssData = RawSetCssData & Timed;
export type SetNodeData = RawSetNodeData & Timed
export type SetNodeScroll = RawSetNodeScroll & Timed;
export type SetCssData = RawSetCssData & Timed
export type SetInputValue = RawSetInputValue & Timed;
export type SetNodeScroll = RawSetNodeScroll & Timed
export type SetInputChecked = RawSetInputChecked & Timed;
export type SetInputValue = RawSetInputValue & Timed
export type MouseMove = RawMouseMove & Timed;
export type SetInputChecked = RawSetInputChecked & Timed
export type NetworkRequestDeprecated = RawNetworkRequestDeprecated & Timed;
export type MouseMove = RawMouseMove & Timed
export type ConsoleLog = RawConsoleLog & Timed;
export type NetworkRequestDeprecated = RawNetworkRequestDeprecated & Timed
export type StringDictGlobal = RawStringDictGlobal & Timed;
export type ConsoleLog = RawConsoleLog & Timed
export type SetNodeAttributeDictGlobal = RawSetNodeAttributeDictGlobal & Timed;
export type StringDictGlobal = RawStringDictGlobal & Timed
export type CssInsertRule = RawCssInsertRule & Timed;
export type SetNodeAttributeDictGlobal = RawSetNodeAttributeDictGlobal & Timed
export type CssInsertRule = RawCssInsertRule & Timed
@ -137,108 +138,111 @@ export type Fetch = RawFetch & Timed
export type Profiler = RawProfiler & Timed
export type ReduxDeprecated = RawReduxDeprecated & Timed;
export type OTable = RawOTable & Timed
export type Vuex = RawVuex & Timed;
export type ReduxDeprecated = RawReduxDeprecated & Timed
export type MobX = RawMobX & Timed;
export type Vuex = RawVuex & Timed
export type NgRx = RawNgRx & Timed;
export type MobX = RawMobX & Timed
export type GraphQlDeprecated = RawGraphQlDeprecated & Timed;
export type NgRx = RawNgRx & Timed
export type PerformanceTrack = RawPerformanceTrack & Timed;
export type GraphQlDeprecated = RawGraphQlDeprecated & Timed
export type StringDictDeprecated = RawStringDictDeprecated & Timed;
export type PerformanceTrack = RawPerformanceTrack & Timed
export type SetNodeAttributeDictDeprecated = RawSetNodeAttributeDictDeprecated &
Timed;
export type StringDictDeprecated = RawStringDictDeprecated & Timed
export type StringDict = RawStringDict & Timed;
export type SetNodeAttributeDictDeprecated = RawSetNodeAttributeDictDeprecated & Timed
export type SetNodeAttributeDict = RawSetNodeAttributeDict & Timed;
export type StringDict = RawStringDict & Timed
export type ResourceTimingDeprecated = RawResourceTimingDeprecated & Timed;
export type SetNodeAttributeDict = RawSetNodeAttributeDict & Timed
export type ConnectionInformation = RawConnectionInformation & Timed;
export type ResourceTimingDeprecated = RawResourceTimingDeprecated & Timed
export type SetPageVisibility = RawSetPageVisibility & Timed;
export type ConnectionInformation = RawConnectionInformation & Timed
export type LoadFontFace = RawLoadFontFace & Timed;
export type SetPageVisibility = RawSetPageVisibility & Timed
export type SetNodeFocus = RawSetNodeFocus & Timed;
export type LoadFontFace = RawLoadFontFace & Timed
export type LongTask = RawLongTask & Timed;
export type SetNodeFocus = RawSetNodeFocus & Timed
export type LongTask = RawLongTask & Timed
export type SetNodeAttributeURLBased = RawSetNodeAttributeURLBased & Timed
export type CssInsertRuleURLBased = RawCssInsertRuleURLBased & Timed;
export type SetCssDataURLBased = RawSetCssDataURLBased & Timed
export type CssInsertRuleURLBased = RawCssInsertRuleURLBased & Timed
export type MouseClick = RawMouseClick & Timed
export type CreateIFrameDocument = RawCreateIFrameDocument & Timed;
export type MouseClickDeprecated = RawMouseClickDeprecated & Timed
export type AdoptedSsReplaceURLBased = RawAdoptedSsReplaceURLBased & Timed;
export type CreateIFrameDocument = RawCreateIFrameDocument & Timed
export type AdoptedSsReplace = RawAdoptedSsReplace & Timed;
export type AdoptedSsReplaceURLBased = RawAdoptedSsReplaceURLBased & Timed
export type AdoptedSsInsertRuleURLBased = RawAdoptedSsInsertRuleURLBased &
Timed;
export type AdoptedSsReplace = RawAdoptedSsReplace & Timed
export type AdoptedSsInsertRule = RawAdoptedSsInsertRule & Timed;
export type AdoptedSsInsertRuleURLBased = RawAdoptedSsInsertRuleURLBased & Timed
export type AdoptedSsDeleteRule = RawAdoptedSsDeleteRule & Timed;
export type AdoptedSsInsertRule = RawAdoptedSsInsertRule & Timed
export type AdoptedSsAddOwner = RawAdoptedSsAddOwner & Timed;
export type AdoptedSsDeleteRule = RawAdoptedSsDeleteRule & Timed
export type AdoptedSsRemoveOwner = RawAdoptedSsRemoveOwner & Timed;
export type AdoptedSsAddOwner = RawAdoptedSsAddOwner & Timed
export type Zustand = RawZustand & Timed;
export type AdoptedSsRemoveOwner = RawAdoptedSsRemoveOwner & Timed
export type NetworkRequest = RawNetworkRequest & Timed;
export type Zustand = RawZustand & Timed
export type WsChannel = RawWsChannel & Timed;
export type NetworkRequest = RawNetworkRequest & Timed
export type SelectionChange = RawSelectionChange & Timed;
export type WsChannel = RawWsChannel & Timed
export type MouseThrashing = RawMouseThrashing & Timed;
export type LongAnimationTask = RawLongAnimationTask & Timed
export type ResourceTiming = RawResourceTiming & Timed;
export type SelectionChange = RawSelectionChange & Timed
export type TabChange = RawTabChange & Timed;
export type MouseThrashing = RawMouseThrashing & Timed
export type TabData = RawTabData & Timed;
export type ResourceTiming = RawResourceTiming & Timed
export type CanvasNode = RawCanvasNode & Timed;
export type TabChange = RawTabChange & Timed
export type TagTrigger = RawTagTrigger & Timed;
export type TabData = RawTabData & Timed
export type Redux = RawRedux & Timed;
export type CanvasNode = RawCanvasNode & Timed
export type SetPageLocation = RawSetPageLocation & Timed;
export type TagTrigger = RawTagTrigger & Timed
export type GraphQl = RawGraphQl & Timed;
export type Redux = RawRedux & Timed
export type MobileEvent = RawMobileEvent & Timed;
export type SetPageLocation = RawSetPageLocation & Timed
export type MobileScreenChanges = RawMobileScreenChanges & Timed;
export type GraphQl = RawGraphQl & Timed
export type MobileClickEvent = RawMobileClickEvent & Timed;
export type MobileEvent = RawMobileEvent & Timed
export type MobileInputEvent = RawMobileInputEvent & Timed;
export type MobileScreenChanges = RawMobileScreenChanges & Timed
export type MobilePerformanceEvent = RawMobilePerformanceEvent & Timed;
export type MobileClickEvent = RawMobileClickEvent & Timed
export type MobileLog = RawMobileLog & Timed;
export type MobileInputEvent = RawMobileInputEvent & Timed
export type MobileInternalError = RawMobileInternalError & Timed;
export type MobilePerformanceEvent = RawMobilePerformanceEvent & Timed
export type MobileNetworkCall = RawMobileNetworkCall & Timed;
export type MobileLog = RawMobileLog & Timed
export type MobileSwipeEvent = RawMobileSwipeEvent & Timed;
export type MobileInternalError = RawMobileInternalError & Timed
export type MobileNetworkCall = RawMobileNetworkCall & Timed
export type MobileSwipeEvent = RawMobileSwipeEvent & Timed
export type MobileIssueEvent = RawMobileIssueEvent & Timed
export type MobileIssueEvent = RawMobileIssueEvent & Timed;

View file

@ -1,4 +1,5 @@
// Auto-generated, do not edit
/* eslint-disable */
export const enum MType {
Timestamp = 0,
@ -59,6 +60,7 @@ export const enum MType {
Zustand = 79,
NetworkRequest = 83,
WsChannel = 84,
LongAnimationTask = 89,
SelectionChange = 113,
MouseThrashing = 114,
ResourceTiming = 116,
@ -81,142 +83,144 @@ export const enum MType {
MobileIssueEvent = 111,
}
export interface RawTimestamp {
tp: MType.Timestamp;
timestamp: number;
tp: MType.Timestamp,
timestamp: number,
}
export interface RawSetPageLocationDeprecated {
tp: MType.SetPageLocationDeprecated;
url: string;
referrer: string;
navigationStart: number;
tp: MType.SetPageLocationDeprecated,
url: string,
referrer: string,
navigationStart: number,
}
export interface RawSetViewportSize {
tp: MType.SetViewportSize;
width: number;
height: number;
tp: MType.SetViewportSize,
width: number,
height: number,
}
export interface RawSetViewportScroll {
tp: MType.SetViewportScroll;
x: number;
y: number;
tp: MType.SetViewportScroll,
x: number,
y: number,
}
export interface RawCreateDocument {
tp: MType.CreateDocument;
tp: MType.CreateDocument,
}
export interface RawCreateElementNode {
tp: MType.CreateElementNode;
id: number;
parentID: number;
index: number;
tag: string;
svg: boolean;
tp: MType.CreateElementNode,
id: number,
parentID: number,
index: number,
tag: string,
svg: boolean,
}
export interface RawCreateTextNode {
tp: MType.CreateTextNode;
id: number;
parentID: number;
index: number;
tp: MType.CreateTextNode,
id: number,
parentID: number,
index: number,
}
export interface RawMoveNode {
tp: MType.MoveNode;
id: number;
parentID: number;
index: number;
tp: MType.MoveNode,
id: number,
parentID: number,
index: number,
}
export interface RawRemoveNode {
tp: MType.RemoveNode;
id: number;
tp: MType.RemoveNode,
id: number,
}
export interface RawSetNodeAttribute {
tp: MType.SetNodeAttribute;
id: number;
name: string;
value: string;
tp: MType.SetNodeAttribute,
id: number,
name: string,
value: string,
}
export interface RawRemoveNodeAttribute {
tp: MType.RemoveNodeAttribute;
id: number;
name: string;
tp: MType.RemoveNodeAttribute,
id: number,
name: string,
}
export interface RawSetNodeData {
tp: MType.SetNodeData;
id: number;
data: string;
tp: MType.SetNodeData,
id: number,
data: string,
}
export interface RawSetCssData {
tp: MType.SetCssData;
id: number;
data: string;
tp: MType.SetCssData,
id: number,
data: string,
}
export interface RawSetNodeScroll {
tp: MType.SetNodeScroll;
id: number;
x: number;
y: number;
tp: MType.SetNodeScroll,
id: number,
x: number,
y: number,
}
export interface RawSetInputValue {
tp: MType.SetInputValue;
id: number;
value: string;
mask: number;
tp: MType.SetInputValue,
id: number,
value: string,
mask: number,
}
export interface RawSetInputChecked {
tp: MType.SetInputChecked;
id: number;
checked: boolean;
tp: MType.SetInputChecked,
id: number,
checked: boolean,
}
export interface RawMouseMove {
tp: MType.MouseMove;
x: number;
y: number;
tp: MType.MouseMove,
x: number,
y: number,
}
export interface RawNetworkRequestDeprecated {
tp: MType.NetworkRequestDeprecated;
type: string;
method: string;
url: string;
request: string;
response: string;
status: number;
timestamp: number;
duration: number;
tp: MType.NetworkRequestDeprecated,
type: string,
method: string,
url: string,
request: string,
response: string,
status: number,
timestamp: number,
duration: number,
}
export interface RawConsoleLog {
tp: MType.ConsoleLog;
level: string;
value: string;
tp: MType.ConsoleLog,
level: string,
value: string,
}
export interface RawStringDictGlobal {
tp: MType.StringDictGlobal;
key: number;
value: string;
tp: MType.StringDictGlobal,
key: number,
value: string,
}
export interface RawSetNodeAttributeDictGlobal {
tp: MType.SetNodeAttributeDictGlobal;
id: number;
name: number;
value: number;
tp: MType.SetNodeAttributeDictGlobal,
id: number,
name: number,
value: number,
}
export interface RawCssInsertRule {
@ -244,122 +248,122 @@ export interface RawFetch {
}
export interface RawProfiler {
tp: MType.Profiler;
name: string;
duration: number;
args: string;
result: string;
tp: MType.Profiler,
name: string,
duration: number,
args: string,
result: string,
}
export interface RawOTable {
tp: MType.OTable;
key: string;
value: string;
tp: MType.OTable,
key: string,
value: string,
}
export interface RawReduxDeprecated {
tp: MType.ReduxDeprecated;
action: string;
state: string;
duration: number;
tp: MType.ReduxDeprecated,
action: string,
state: string,
duration: number,
}
export interface RawVuex {
tp: MType.Vuex;
mutation: string;
state: string;
tp: MType.Vuex,
mutation: string,
state: string,
}
export interface RawMobX {
tp: MType.MobX;
type: string;
payload: string;
tp: MType.MobX,
type: string,
payload: string,
}
export interface RawNgRx {
tp: MType.NgRx;
action: string;
state: string;
duration: number;
tp: MType.NgRx,
action: string,
state: string,
duration: number,
}
export interface RawGraphQlDeprecated {
tp: MType.GraphQlDeprecated;
operationKind: string;
operationName: string;
variables: string;
response: string;
duration: number;
tp: MType.GraphQlDeprecated,
operationKind: string,
operationName: string,
variables: string,
response: string,
duration: number,
}
export interface RawPerformanceTrack {
tp: MType.PerformanceTrack;
frames: number;
ticks: number;
totalJSHeapSize: number;
usedJSHeapSize: number;
tp: MType.PerformanceTrack,
frames: number,
ticks: number,
totalJSHeapSize: number,
usedJSHeapSize: number,
}
export interface RawStringDictDeprecated {
tp: MType.StringDictDeprecated;
key: number;
value: string;
tp: MType.StringDictDeprecated,
key: number,
value: string,
}
export interface RawSetNodeAttributeDictDeprecated {
tp: MType.SetNodeAttributeDictDeprecated;
id: number;
nameKey: number;
valueKey: number;
tp: MType.SetNodeAttributeDictDeprecated,
id: number,
nameKey: number,
valueKey: number,
}
export interface RawStringDict {
tp: MType.StringDict;
key: string;
value: string;
tp: MType.StringDict,
key: string,
value: string,
}
export interface RawSetNodeAttributeDict {
tp: MType.SetNodeAttributeDict;
id: number;
name: string;
value: string;
tp: MType.SetNodeAttributeDict,
id: number,
name: string,
value: string,
}
export interface RawResourceTimingDeprecated {
tp: MType.ResourceTimingDeprecated;
timestamp: number;
duration: number;
ttfb: number;
headerSize: number;
encodedBodySize: number;
decodedBodySize: number;
url: string;
initiator: string;
tp: MType.ResourceTimingDeprecated,
timestamp: number,
duration: number,
ttfb: number,
headerSize: number,
encodedBodySize: number,
decodedBodySize: number,
url: string,
initiator: string,
}
export interface RawConnectionInformation {
tp: MType.ConnectionInformation;
downlink: number;
type: string;
tp: MType.ConnectionInformation,
downlink: number,
type: string,
}
export interface RawSetPageVisibility {
tp: MType.SetPageVisibility;
hidden: boolean;
tp: MType.SetPageVisibility,
hidden: boolean,
}
export interface RawLoadFontFace {
tp: MType.LoadFontFace;
parentID: number;
family: string;
source: string;
descriptors: string;
tp: MType.LoadFontFace,
parentID: number,
family: string,
source: string,
descriptors: string,
}
export interface RawSetNodeFocus {
tp: MType.SetNodeFocus;
id: number;
tp: MType.SetNodeFocus,
id: number,
}
export interface RawLongTask {
@ -374,18 +378,18 @@ export interface RawLongTask {
}
export interface RawSetNodeAttributeURLBased {
tp: MType.SetNodeAttributeURLBased;
id: number;
name: string;
value: string;
baseURL: string;
tp: MType.SetNodeAttributeURLBased,
id: number,
name: string,
value: string,
baseURL: string,
}
export interface RawSetCssDataURLBased {
tp: MType.SetCssDataURLBased;
id: number;
data: string;
baseURL: string;
tp: MType.SetCssDataURLBased,
id: number,
data: string,
baseURL: string,
}
export interface RawCssInsertRuleURLBased {
@ -397,266 +401,276 @@ export interface RawCssInsertRuleURLBased {
}
export interface RawMouseClick {
tp: MType.MouseClick;
id: number;
hesitationTime: number;
label: string;
selector: string;
normalizedX: number;
normalizedY: number;
tp: MType.MouseClick,
id: number,
hesitationTime: number,
label: string,
selector: string,
normalizedX: number,
normalizedY: number,
}
export interface RawMouseClickDeprecated {
tp: MType.MouseClickDeprecated;
id: number;
hesitationTime: number;
label: string;
selector: string;
tp: MType.MouseClickDeprecated,
id: number,
hesitationTime: number,
label: string,
selector: string,
}
export interface RawCreateIFrameDocument {
tp: MType.CreateIFrameDocument;
frameID: number;
id: number;
tp: MType.CreateIFrameDocument,
frameID: number,
id: number,
}
export interface RawAdoptedSsReplaceURLBased {
tp: MType.AdoptedSsReplaceURLBased;
sheetID: number;
text: string;
baseURL: string;
tp: MType.AdoptedSsReplaceURLBased,
sheetID: number,
text: string,
baseURL: string,
}
export interface RawAdoptedSsReplace {
tp: MType.AdoptedSsReplace;
sheetID: number;
text: string;
tp: MType.AdoptedSsReplace,
sheetID: number,
text: string,
}
export interface RawAdoptedSsInsertRuleURLBased {
tp: MType.AdoptedSsInsertRuleURLBased;
sheetID: number;
rule: string;
index: number;
baseURL: string;
tp: MType.AdoptedSsInsertRuleURLBased,
sheetID: number,
rule: string,
index: number,
baseURL: string,
}
export interface RawAdoptedSsInsertRule {
tp: MType.AdoptedSsInsertRule;
sheetID: number;
rule: string;
index: number;
tp: MType.AdoptedSsInsertRule,
sheetID: number,
rule: string,
index: number,
}
export interface RawAdoptedSsDeleteRule {
tp: MType.AdoptedSsDeleteRule;
sheetID: number;
index: number;
tp: MType.AdoptedSsDeleteRule,
sheetID: number,
index: number,
}
export interface RawAdoptedSsAddOwner {
tp: MType.AdoptedSsAddOwner;
sheetID: number;
id: number;
tp: MType.AdoptedSsAddOwner,
sheetID: number,
id: number,
}
export interface RawAdoptedSsRemoveOwner {
tp: MType.AdoptedSsRemoveOwner;
sheetID: number;
id: number;
tp: MType.AdoptedSsRemoveOwner,
sheetID: number,
id: number,
}
export interface RawZustand {
tp: MType.Zustand;
mutation: string;
state: string;
tp: MType.Zustand,
mutation: string,
state: string,
}
export interface RawNetworkRequest {
tp: MType.NetworkRequest;
type: string;
method: string;
url: string;
request: string;
response: string;
status: number;
timestamp: number;
duration: number;
transferredBodySize: number;
tp: MType.NetworkRequest,
type: string,
method: string,
url: string,
request: string,
response: string,
status: number,
timestamp: number,
duration: number,
transferredBodySize: number,
}
export interface RawWsChannel {
tp: MType.WsChannel;
chType: string;
channelName: string;
data: string;
timestamp: number;
dir: string;
messageType: string;
tp: MType.WsChannel,
chType: string,
channelName: string,
data: string,
timestamp: number,
dir: string,
messageType: string,
}
export interface RawLongAnimationTask {
tp: MType.LongAnimationTask,
name: string,
duration: number,
blockingDuration: number,
firstUIEventTimestamp: number,
startTime: number,
scripts: string,
}
export interface RawSelectionChange {
tp: MType.SelectionChange;
selectionStart: number;
selectionEnd: number;
selection: string;
tp: MType.SelectionChange,
selectionStart: number,
selectionEnd: number,
selection: string,
}
export interface RawMouseThrashing {
tp: MType.MouseThrashing;
timestamp: number;
tp: MType.MouseThrashing,
timestamp: number,
}
export interface RawResourceTiming {
tp: MType.ResourceTiming;
timestamp: number;
duration: number;
ttfb: number;
headerSize: number;
encodedBodySize: number;
decodedBodySize: number;
url: string;
initiator: string;
transferredSize: number;
cached: boolean;
tp: MType.ResourceTiming,
timestamp: number,
duration: number,
ttfb: number,
headerSize: number,
encodedBodySize: number,
decodedBodySize: number,
url: string,
initiator: string,
transferredSize: number,
cached: boolean,
}
export interface RawTabChange {
tp: MType.TabChange;
tabId: string;
tp: MType.TabChange,
tabId: string,
}
export interface RawTabData {
tp: MType.TabData;
tabId: string;
tp: MType.TabData,
tabId: string,
}
export interface RawCanvasNode {
tp: MType.CanvasNode;
nodeId: string;
timestamp: number;
tp: MType.CanvasNode,
nodeId: string,
timestamp: number,
}
export interface RawTagTrigger {
tp: MType.TagTrigger;
tagId: number;
tp: MType.TagTrigger,
tagId: number,
}
export interface RawRedux {
tp: MType.Redux;
action: string;
state: string;
duration: number;
actionTime: number;
tp: MType.Redux,
action: string,
state: string,
duration: number,
actionTime: number,
}
export interface RawSetPageLocation {
tp: MType.SetPageLocation;
url: string;
referrer: string;
navigationStart: number;
documentTitle: string;
tp: MType.SetPageLocation,
url: string,
referrer: string,
navigationStart: number,
documentTitle: string,
}
export interface RawGraphQl {
tp: MType.GraphQl;
operationKind: string;
operationName: string;
variables: string;
response: string;
duration: number;
tp: MType.GraphQl,
operationKind: string,
operationName: string,
variables: string,
response: string,
duration: number,
}
export interface RawMobileEvent {
tp: MType.MobileEvent;
timestamp: number;
length: number;
name: string;
payload: string;
tp: MType.MobileEvent,
timestamp: number,
length: number,
name: string,
payload: string,
}
export interface RawMobileScreenChanges {
tp: MType.MobileScreenChanges;
timestamp: number;
length: number;
x: number;
y: number;
width: number;
height: number;
tp: MType.MobileScreenChanges,
timestamp: number,
length: number,
x: number,
y: number,
width: number,
height: number,
}
export interface RawMobileClickEvent {
tp: MType.MobileClickEvent;
timestamp: number;
length: number;
label: string;
x: number;
y: number;
tp: MType.MobileClickEvent,
timestamp: number,
length: number,
label: string,
x: number,
y: number,
}
export interface RawMobileInputEvent {
tp: MType.MobileInputEvent;
timestamp: number;
length: number;
value: string;
valueMasked: boolean;
label: string;
tp: MType.MobileInputEvent,
timestamp: number,
length: number,
value: string,
valueMasked: boolean,
label: string,
}
export interface RawMobilePerformanceEvent {
tp: MType.MobilePerformanceEvent;
timestamp: number;
length: number;
name: string;
value: number;
tp: MType.MobilePerformanceEvent,
timestamp: number,
length: number,
name: string,
value: number,
}
export interface RawMobileLog {
tp: MType.MobileLog;
timestamp: number;
length: number;
severity: string;
content: string;
tp: MType.MobileLog,
timestamp: number,
length: number,
severity: string,
content: string,
}
export interface RawMobileInternalError {
tp: MType.MobileInternalError;
timestamp: number;
length: number;
content: string;
tp: MType.MobileInternalError,
timestamp: number,
length: number,
content: string,
}
export interface RawMobileNetworkCall {
tp: MType.MobileNetworkCall;
timestamp: number;
length: number;
type: string;
method: string;
url: string;
request: string;
response: string;
status: number;
duration: number;
tp: MType.MobileNetworkCall,
timestamp: number,
length: number,
type: string,
method: string,
url: string,
request: string,
response: string,
status: number,
duration: number,
}
export interface RawMobileSwipeEvent {
tp: MType.MobileSwipeEvent;
timestamp: number;
length: number;
label: string;
x: number;
y: number;
direction: string;
tp: MType.MobileSwipeEvent,
timestamp: number,
length: number,
label: string,
x: number,
y: number,
direction: string,
}
export interface RawMobileIssueEvent {
tp: MType.MobileIssueEvent;
timestamp: number;
type: string;
contextString: string;
context: string;
payload: string;
tp: MType.MobileIssueEvent,
timestamp: number,
type: string,
contextString: string,
context: string,
payload: string,
}
export type RawMessage = RawTimestamp | RawSetPageLocationDeprecated | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawNetworkRequestDeprecated | RawConsoleLog | RawStringDictGlobal | RawSetNodeAttributeDictGlobal | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawReduxDeprecated | RawVuex | RawMobX | RawNgRx | RawGraphQlDeprecated | RawPerformanceTrack | RawStringDictDeprecated | RawSetNodeAttributeDictDeprecated | RawStringDict | RawSetNodeAttributeDict | RawResourceTimingDeprecated | RawConnectionInformation | RawSetPageVisibility | RawLoadFontFace | RawSetNodeFocus | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawCssInsertRuleURLBased | RawMouseClick | RawMouseClickDeprecated | RawCreateIFrameDocument | RawAdoptedSsReplaceURLBased | RawAdoptedSsReplace | RawAdoptedSsInsertRuleURLBased | RawAdoptedSsInsertRule | RawAdoptedSsDeleteRule | RawAdoptedSsAddOwner | RawAdoptedSsRemoveOwner | RawZustand | RawNetworkRequest | RawWsChannel | RawSelectionChange | RawMouseThrashing | RawResourceTiming | RawTabChange | RawTabData | RawCanvasNode | RawTagTrigger | RawRedux | RawSetPageLocation | RawGraphQl | RawMobileEvent | RawMobileScreenChanges | RawMobileClickEvent | RawMobileInputEvent | RawMobilePerformanceEvent | RawMobileLog | RawMobileInternalError | RawMobileNetworkCall | RawMobileSwipeEvent | RawMobileIssueEvent;
export type RawMessage = RawTimestamp | RawSetPageLocationDeprecated | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawNetworkRequestDeprecated | RawConsoleLog | RawStringDictGlobal | RawSetNodeAttributeDictGlobal | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawReduxDeprecated | RawVuex | RawMobX | RawNgRx | RawGraphQlDeprecated | RawPerformanceTrack | RawStringDictDeprecated | RawSetNodeAttributeDictDeprecated | RawStringDict | RawSetNodeAttributeDict | RawResourceTimingDeprecated | RawConnectionInformation | RawSetPageVisibility | RawLoadFontFace | RawSetNodeFocus | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawCssInsertRuleURLBased | RawMouseClick | RawMouseClickDeprecated | RawCreateIFrameDocument | RawAdoptedSsReplaceURLBased | RawAdoptedSsReplace | RawAdoptedSsInsertRuleURLBased | RawAdoptedSsInsertRule | RawAdoptedSsDeleteRule | RawAdoptedSsAddOwner | RawAdoptedSsRemoveOwner | RawZustand | RawNetworkRequest | RawWsChannel | RawLongAnimationTask | RawSelectionChange | RawMouseThrashing | RawResourceTiming | RawTabChange | RawTabData | RawCanvasNode | RawTagTrigger | RawRedux | RawSetPageLocation | RawGraphQl | RawMobileEvent | RawMobileScreenChanges | RawMobileClickEvent | RawMobileInputEvent | RawMobilePerformanceEvent | RawMobileLog | RawMobileInternalError | RawMobileNetworkCall | RawMobileSwipeEvent | RawMobileIssueEvent;

View file

@ -1,6 +1,6 @@
// Auto-generated, do not edit
import { MType } from './raw.gen';
import { MType } from './raw.gen'
export const TP_MAP = {
0: MType.Timestamp,
@ -61,6 +61,7 @@ export const TP_MAP = {
79: MType.Zustand,
83: MType.NetworkRequest,
84: MType.WsChannel,
89: MType.LongAnimationTask,
113: MType.SelectionChange,
114: MType.MouseThrashing,
116: MType.ResourceTiming,
@ -81,4 +82,4 @@ export const TP_MAP = {
105: MType.MobileNetworkCall,
106: MType.MobileSwipeEvent,
111: MType.MobileIssueEvent,
} as const;
} as const

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,43 @@
import { LongAnimationTask } from "../messages";
export interface ILongAnimationTask {
name: string;
duration: number;
blockingDuration: number;
firstUIEventTimestamp: number;
startTime: number;
time: number;
scripts: [
{
name: string;
duration: number;
invoker: string;
invokerType: string;
pauseDuration: number;
sourceURL: string;
sourceFunctionName: string;
sourceCharPosition: number;
forcedStyleAndLayoutDuration: number;
},
];
isRed: boolean;
}
export const getLongTask = (msg: LongAnimationTask): ILongAnimationTask => {
let scripts = []
try {
scripts = JSON.parse(msg.scripts)
} catch (e) {
console.error('Error parsing scripts for LAT:', e, msg)
}
return ({
name: msg.name,
duration: msg.duration,
blockingDuration: msg.blockingDuration,
firstUIEventTimestamp: msg.firstUIEventTimestamp,
startTime: msg.startTime,
scripts,
isRed: msg.blockingDuration > 50,
time: msg.startTime,
});
}

View file

@ -520,6 +520,15 @@ message 84, 'WSChannel', :replayer => :devtools do
string 'MessageType'
end
message 89, 'LongAnimationTask', :replayer => :devtools do
string 'Name'
int 'Duration'
int 'BlockingDuration'
int 'FirstUIEventTimestamp'
int 'StartTime'
string 'Scripts'
end
# 90-111 reserved iOS
message 112, 'InputChange', :replayer => false do
@ -621,4 +630,4 @@ message 127, 'SessionSearch', :tracker => false, :replayer => false do
uint 'Partition'
end
# FREE 2, 34, 35, 36, 65, 85, 86, 87, 88, 89
# FREE 2, 35, 36, 65, 85, 86, 87, 88, 89

View file

@ -70,6 +70,7 @@ export declare const enum Type {
PartitionedMessage = 82,
NetworkRequest = 83,
WSChannel = 84,
LongAnimationTask = 89,
InputChange = 112,
SelectionChange = 113,
MouseThrashing = 114,
@ -570,6 +571,16 @@ export type WSChannel = [
/*messageType:*/ string,
]
export type LongAnimationTask = [
/*type:*/ Type.LongAnimationTask,
/*name:*/ string,
/*duration:*/ number,
/*blockingDuration:*/ number,
/*firstUIEventTimestamp:*/ number,
/*startTime:*/ number,
/*scripts:*/ string,
]
export type InputChange = [
/*type:*/ Type.InputChange,
/*id:*/ number,
@ -664,5 +675,5 @@ export type WebVitals = [
]
type Message = Timestamp | SetPageLocationDeprecated | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetNodeScroll | SetInputTarget | SetInputValue | SetInputChecked | MouseMove | NetworkRequestDeprecated | ConsoleLog | PageLoadTiming | PageRenderTiming | CustomEvent | UserID | UserAnonymousID | Metadata | StringDictGlobal | SetNodeAttributeDictGlobal | CSSInsertRule | CSSDeleteRule | Fetch | Profiler | OTable | StateAction | ReduxDeprecated | Vuex | MobX | NgRx | GraphQLDeprecated | PerformanceTrack | StringDictDeprecated | SetNodeAttributeDictDeprecated | StringDict | SetNodeAttributeDict | ResourceTimingDeprecated | ConnectionInformation | SetPageVisibility | LoadFontFace | SetNodeFocus | LongTask | SetNodeAttributeURLBased | SetCSSDataURLBased | TechnicalInfo | CustomIssue | CSSInsertRuleURLBased | MouseClick | MouseClickDeprecated | CreateIFrameDocument | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner | JSException | Zustand | BatchMetadata | PartitionedMessage | NetworkRequest | WSChannel | InputChange | SelectionChange | MouseThrashing | UnbindNodes | ResourceTiming | TabChange | TabData | CanvasNode | TagTrigger | Redux | SetPageLocation | GraphQL | WebVitals
type Message = Timestamp | SetPageLocationDeprecated | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetNodeScroll | SetInputTarget | SetInputValue | SetInputChecked | MouseMove | NetworkRequestDeprecated | ConsoleLog | PageLoadTiming | PageRenderTiming | CustomEvent | UserID | UserAnonymousID | Metadata | StringDictGlobal | SetNodeAttributeDictGlobal | CSSInsertRule | CSSDeleteRule | Fetch | Profiler | OTable | StateAction | ReduxDeprecated | Vuex | MobX | NgRx | GraphQLDeprecated | PerformanceTrack | StringDictDeprecated | SetNodeAttributeDictDeprecated | StringDict | SetNodeAttributeDict | ResourceTimingDeprecated | ConnectionInformation | SetPageVisibility | LoadFontFace | SetNodeFocus | LongTask | SetNodeAttributeURLBased | SetCSSDataURLBased | TechnicalInfo | CustomIssue | CSSInsertRuleURLBased | MouseClick | MouseClickDeprecated | CreateIFrameDocument | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner | JSException | Zustand | BatchMetadata | PartitionedMessage | NetworkRequest | WSChannel | LongAnimationTask | InputChange | SelectionChange | MouseThrashing | UnbindNodes | ResourceTiming | TabChange | TabData | CanvasNode | TagTrigger | Redux | SetPageLocation | GraphQL | WebVitals
export default Message

View file

@ -905,6 +905,25 @@ export function WSChannel(
]
}
export function LongAnimationTask(
name: string,
duration: number,
blockingDuration: number,
firstUIEventTimestamp: number,
startTime: number,
scripts: string,
): Messages.LongAnimationTask {
return [
Messages.Type.LongAnimationTask,
name,
duration,
blockingDuration,
firstUIEventTimestamp,
startTime,
scripts,
]
}
export function InputChange(
id: number,
value: string,

View file

@ -28,6 +28,7 @@ import Network from './modules/network.js'
import ConstructedStyleSheets from './modules/constructedStyleSheets.js'
import Selection from './modules/selection.js'
import Tabs from './modules/tabs.js'
import LongAnimationTask from "./modules/longAnimationTask.js";
import { IN_BROWSER, deprecationWarn, DOCS_HOST, inIframe } from './utils.js'
import FeatureFlags, { IFeatureFlag } from './modules/featureFlags.js'
@ -41,13 +42,20 @@ import type { Options as NetworkOptions } from './modules/network.js'
import type { MouseHandlerOptions } from './modules/mouse.js'
import type { SessionInfo } from './app/session.js'
import type { CssRulesOptions } from './modules/cssrules.js'
import type { LATOptions } from './modules/longAnimationTask.js'
import type { StartOptions } from './app/index.js'
//TODO: unique options init
import type { StartPromiseReturn } from './app/index.js'
export type Options = Partial<
AppOptions & ConsoleOptions & ExceptionOptions & InputOptions & PerformanceOptions & TimingOptions
AppOptions &
ConsoleOptions &
ExceptionOptions &
InputOptions &
PerformanceOptions &
TimingOptions &
LATOptions
> & {
projectID?: number // For the back compatibility only (deprecated)
projectKey: string
@ -201,6 +209,7 @@ export default class API {
Img(app)
Input(app, options)
Timing(app, options)
LongAnimationTask(app, options)
Focus(app)
Fonts(app)
const skipNetwork = options.network?.disabled

View file

@ -0,0 +1,61 @@
import type App from '../app/index.js'
import { LongAnimationTask } from "../app/messages.gen";
export interface LongAnimationTask extends PerformanceEntry {
name: string;
duration: number;
blockingDuration: number;
firstUIEventTimestamp: number;
startTime: number;
scripts: [
{
name: string;
duration: number;
invoker: string;
invokerType: string;
pauseDuration: number;
sourceURL: string;
sourceFunctionName: string;
sourceCharPosition: number;
forcedStyleAndLayoutDuration: number;
},
];
}
export interface LATOptions {
longTasks: boolean;
}
export default function (app: App, opts: Partial<LATOptions>): void {
if (!opts.longTasks || !('PerformanceObserver' in window)) {
return;
}
const onEntry = (entry: LongAnimationTask) => {
app.send(LongAnimationTask(
entry.name,
entry.duration,
entry.blockingDuration,
entry.firstUIEventTimestamp,
entry.startTime,
JSON.stringify(entry.scripts ?? []),
))
}
const observer = new PerformanceObserver((entryList) => {
entryList.getEntries().forEach((entry) => {
if (entry.entryType === 'long-animation-frame') {
onEntry(entry as LongAnimationTask)
}
})
})
app.attachStartCallback(() => {
performance.getEntriesByType('long-animation-frame').forEach((lat: LongAnimationTask) => {
onEntry(lat)
})
observer.observe({
entryTypes: ['long-animation-frame'],
})
})
app.attachStopCallback(() => {
observer.disconnect()
})
}

View file

@ -282,6 +282,10 @@ export default class MessageEncoder extends PrimitiveEncoder {
return this.string(msg[1]) && this.string(msg[2]) && this.string(msg[3]) && this.uint(msg[4]) && this.string(msg[5]) && this.string(msg[6])
break
case Messages.Type.LongAnimationTask:
return this.string(msg[1]) && this.int(msg[2]) && this.int(msg[3]) && this.int(msg[4]) && this.int(msg[5]) && this.string(msg[6])
break
case Messages.Type.InputChange:
return this.uint(msg[1]) && this.string(msg[2]) && this.boolean(msg[3]) && this.string(msg[4]) && this.int(msg[5]) && this.int(msg[6])
break