tracker, ui, backend: checking support for network timings

This commit is contained in:
nick-delirium 2025-04-04 16:53:34 +02:00 committed by Delirium
parent b1b21937ed
commit e92ba42d82
23 changed files with 278 additions and 176 deletions

View file

@ -1466,7 +1466,7 @@ func (msg *SetNodeAttributeDict) TypeID() int {
return 52 return 52
} }
type ResourceTimingDeprecated struct { type ResourceTimingDeprecatedDeprecated struct {
message message
Timestamp uint64 Timestamp uint64
Duration uint64 Duration uint64
@ -1478,7 +1478,7 @@ type ResourceTimingDeprecated struct {
Initiator string Initiator string
} }
func (msg *ResourceTimingDeprecated) Encode() []byte { func (msg *ResourceTimingDeprecatedDeprecated) Encode() []byte {
buf := make([]byte, 81+len(msg.URL)+len(msg.Initiator)) buf := make([]byte, 81+len(msg.URL)+len(msg.Initiator))
buf[0] = 53 buf[0] = 53
p := 1 p := 1
@ -1493,11 +1493,11 @@ func (msg *ResourceTimingDeprecated) Encode() []byte {
return buf[:p] return buf[:p]
} }
func (msg *ResourceTimingDeprecated) Decode() Message { func (msg *ResourceTimingDeprecatedDeprecated) Decode() Message {
return msg return msg
} }
func (msg *ResourceTimingDeprecated) TypeID() int { func (msg *ResourceTimingDeprecatedDeprecated) TypeID() int {
return 53 return 53
} }
@ -2424,7 +2424,7 @@ func (msg *UnbindNodes) TypeID() int {
return 115 return 115
} }
type ResourceTiming struct { type ResourceTimingDeprecated struct {
message message
Timestamp uint64 Timestamp uint64
Duration uint64 Duration uint64
@ -2438,7 +2438,7 @@ type ResourceTiming struct {
Cached bool Cached bool
} }
func (msg *ResourceTiming) Encode() []byte { func (msg *ResourceTimingDeprecated) Encode() []byte {
buf := make([]byte, 101+len(msg.URL)+len(msg.Initiator)) buf := make([]byte, 101+len(msg.URL)+len(msg.Initiator))
buf[0] = 116 buf[0] = 116
p := 1 p := 1
@ -2455,11 +2455,11 @@ func (msg *ResourceTiming) Encode() []byte {
return buf[:p] return buf[:p]
} }
func (msg *ResourceTiming) Decode() Message { func (msg *ResourceTimingDeprecated) Decode() Message {
return msg return msg
} }
func (msg *ResourceTiming) TypeID() int { func (msg *ResourceTimingDeprecated) TypeID() int {
return 116 return 116
} }

View file

@ -873,9 +873,9 @@ func DecodeSetNodeAttributeDict(reader BytesReader) (Message, error) {
return msg, err return msg, err
} }
func DecodeResourceTimingDeprecated(reader BytesReader) (Message, error) { func DecodeResourceTimingDeprecatedDeprecated(reader BytesReader) (Message, error) {
var err error = nil var err error = nil
msg := &ResourceTimingDeprecated{} msg := &ResourceTimingDeprecatedDeprecated{}
if msg.Timestamp, err = reader.ReadUint(); err != nil { if msg.Timestamp, err = reader.ReadUint(); err != nil {
return nil, err return nil, err
} }
@ -1500,9 +1500,9 @@ func DecodeUnbindNodes(reader BytesReader) (Message, error) {
return msg, err return msg, err
} }
func DecodeResourceTiming(reader BytesReader) (Message, error) { func DecodeResourceTimingDeprecated(reader BytesReader) (Message, error) {
var err error = nil var err error = nil
msg := &ResourceTiming{} msg := &ResourceTimingDeprecated{}
if msg.Timestamp, err = reader.ReadUint(); err != nil { if msg.Timestamp, err = reader.ReadUint(); err != nil {
return nil, err return nil, err
} }
@ -2211,7 +2211,7 @@ func ReadMessage(t uint64, reader BytesReader) (Message, error) {
case 52: case 52:
return DecodeSetNodeAttributeDict(reader) return DecodeSetNodeAttributeDict(reader)
case 53: case 53:
return DecodeResourceTimingDeprecated(reader) return DecodeResourceTimingDeprecatedDeprecated(reader)
case 54: case 54:
return DecodeConnectionInformation(reader) return DecodeConnectionInformation(reader)
case 55: case 55:
@ -2283,7 +2283,7 @@ func ReadMessage(t uint64, reader BytesReader) (Message, error) {
case 115: case 115:
return DecodeUnbindNodes(reader) return DecodeUnbindNodes(reader)
case 116: case 116:
return DecodeResourceTiming(reader) return DecodeResourceTimingDeprecated(reader)
case 117: case 117:
return DecodeTabChange(reader) return DecodeTabChange(reader)
case 118: case 118:

View file

@ -71,7 +71,7 @@ class CreateDocument(Message):
__id__ = 7 __id__ = 7
def __init__(self, ): def __init__(self, ):
class CreateElementNode(Message): class CreateElementNode(Message):
@ -500,7 +500,7 @@ class SetNodeAttributeDict(Message):
self.value = value self.value = value
class ResourceTimingDeprecated(Message): class ResourceTimingDeprecatedDeprecated(Message):
__id__ = 53 __id__ = 53
def __init__(self, timestamp, duration, ttfb, header_size, encoded_body_size, decoded_body_size, url, initiator): def __init__(self, timestamp, duration, ttfb, header_size, encoded_body_size, decoded_body_size, url, initiator):
@ -853,7 +853,7 @@ class UnbindNodes(Message):
self.total_removed_percent = total_removed_percent self.total_removed_percent = total_removed_percent
class ResourceTiming(Message): class ResourceTimingDeprecated(Message):
__id__ = 116 __id__ = 116
def __init__(self, timestamp, duration, ttfb, header_size, encoded_body_size, decoded_body_size, url, initiator, transferred_size, cached): def __init__(self, timestamp, duration, ttfb, header_size, encoded_body_size, decoded_body_size, url, initiator, transferred_size, cached):
@ -1177,5 +1177,3 @@ class MobileIssueEvent(Message):
self.context_string = context_string self.context_string = context_string
self.context = context self.context = context
self.payload = payload self.payload = payload

View file

@ -104,11 +104,11 @@ cdef class SetViewportScroll(PyMessage):
cdef class CreateDocument(PyMessage): cdef class CreateDocument(PyMessage):
cdef public int __id__ cdef public int __id__
def __init__(self, ): def __init__(self, ):
self.__id__ = 7 self.__id__ = 7
cdef class CreateElementNode(PyMessage): cdef class CreateElementNode(PyMessage):
@ -743,7 +743,7 @@ cdef class SetNodeAttributeDict(PyMessage):
self.value = value self.value = value
cdef class ResourceTimingDeprecated(PyMessage): cdef class ResourceTimingDeprecatedDeprecated(PyMessage):
cdef public int __id__ cdef public int __id__
cdef public unsigned long timestamp cdef public unsigned long timestamp
cdef public unsigned long duration cdef public unsigned long duration
@ -1269,7 +1269,7 @@ cdef class UnbindNodes(PyMessage):
self.total_removed_percent = total_removed_percent self.total_removed_percent = total_removed_percent
cdef class ResourceTiming(PyMessage): cdef class ResourceTimingDeprecated(PyMessage):
cdef public int __id__ cdef public int __id__
cdef public unsigned long timestamp cdef public unsigned long timestamp
cdef public unsigned long duration cdef public unsigned long duration
@ -1764,5 +1764,3 @@ cdef class MobileIssueEvent(PyMessage):
self.context_string = context_string self.context_string = context_string
self.context = context self.context = context
self.payload = payload self.payload = payload

View file

@ -144,7 +144,7 @@ class MessageCodec(Codec):
if message_id == 7: if message_id == 7:
return CreateDocument( return CreateDocument(
) )
if message_id == 8: if message_id == 8:
@ -486,7 +486,7 @@ class MessageCodec(Codec):
) )
if message_id == 53: if message_id == 53:
return ResourceTimingDeprecated( return ResourceTimingDeprecatedDeprecated(
timestamp=self.read_uint(reader), timestamp=self.read_uint(reader),
duration=self.read_uint(reader), duration=self.read_uint(reader),
ttfb=self.read_uint(reader), ttfb=self.read_uint(reader),
@ -767,7 +767,7 @@ class MessageCodec(Codec):
) )
if message_id == 116: if message_id == 116:
return ResourceTiming( return ResourceTimingDeprecated(
timestamp=self.read_uint(reader), timestamp=self.read_uint(reader),
duration=self.read_uint(reader), duration=self.read_uint(reader),
ttfb=self.read_uint(reader), ttfb=self.read_uint(reader),
@ -1029,4 +1029,3 @@ class MessageCodec(Codec):
context=self.read_string(reader), context=self.read_string(reader),
payload=self.read_string(reader) payload=self.read_string(reader)
) )

View file

@ -242,7 +242,7 @@ cdef class MessageCodec:
if message_id == 7: if message_id == 7:
return CreateDocument( return CreateDocument(
) )
if message_id == 8: if message_id == 8:
@ -584,7 +584,7 @@ cdef class MessageCodec:
) )
if message_id == 53: if message_id == 53:
return ResourceTimingDeprecated( return ResourceTimingDeprecatedDeprecated(
timestamp=self.read_uint(reader), timestamp=self.read_uint(reader),
duration=self.read_uint(reader), duration=self.read_uint(reader),
ttfb=self.read_uint(reader), ttfb=self.read_uint(reader),
@ -865,7 +865,7 @@ cdef class MessageCodec:
) )
if message_id == 116: if message_id == 116:
return ResourceTiming( return ResourceTimingDeprecated(
timestamp=self.read_uint(reader), timestamp=self.read_uint(reader),
duration=self.read_uint(reader), duration=self.read_uint(reader),
ttfb=self.read_uint(reader), ttfb=self.read_uint(reader),
@ -1127,4 +1127,3 @@ cdef class MessageCodec:
context=self.read_string(reader), context=self.read_string(reader),
payload=self.read_string(reader) payload=self.read_string(reader)
) )

View file

@ -1,5 +1,5 @@
/* eslint-disable i18next/no-literal-string */ /* eslint-disable i18next/no-literal-string */
import { ResourceType, Timed } from 'Player'; import { IResourceRequest, ResourceType, Timed } from 'Player';
import { WsChannel } from 'Player/web/messages'; import { WsChannel } from 'Player/web/messages';
import MobilePlayer from 'Player/mobile/IOSPlayer'; import MobilePlayer from 'Player/mobile/IOSPlayer';
import WebPlayer from 'Player/web/WebPlayer'; import WebPlayer from 'Player/web/WebPlayer';
@ -400,8 +400,8 @@ export const NetworkPanelComp = observer(
transferredSize: 0, transferredSize: 0,
}); });
const originalListRef = useRef([]); const originalListRef = useRef<IResourceRequest[]>([]);
const socketListRef = useRef([]); const socketListRef = useRef<any[]>([]);
const { const {
sessionStore: { devTools }, sessionStore: { devTools },
@ -433,18 +433,38 @@ export const NetworkPanelComp = observer(
// Heaviest operation here, will create a final merged network list // Heaviest operation here, will create a final merged network list
const processData = async () => { const processData = async () => {
const fetchUrls = new Set( const fetchUrlMap: Record<string, number[]> = {}
fetchList.map((ft) => { const len = fetchList.length;
return `${ft.name}-${Math.floor(ft.time / 100)}-${Math.floor(ft.duration / 100)}`; for (let i = 0; i < len; i++) {
}), const ft = fetchList[i] as any;
); const key = `${ft.name}-${Math.round(ft.time / 10)}-${Math.round(ft.duration / 10)}`
if (fetchUrlMap[key]) {
fetchUrlMap[key].push(i);
}
fetchUrlMap[key] = [i];
}
// We want to get resources that aren't in fetch list // We want to get resources that aren't in fetch list
const filteredResources = await processInChunks(resourceList, (chunk) => const filteredResources = await processInChunks(resourceList, (chunk) => {
chunk.filter((res: any) => { const clearChunk = [];
const key = `${res.name}-${Math.floor(res.time / 100)}-${Math.floor(res.duration / 100)}`; for (const res of chunk) {
return !fetchUrls.has(key); const key = `${res.name}-${Math.floor(res.time / 10)}-${Math.floor(res.duration / 10)}`;
}), const possibleRequests = fetchUrlMap[key]
if (possibleRequests && possibleRequests.length) {
for (const i of possibleRequests) {
fetchList[i].timings = res.timings;
}
fetchUrlMap[key] = [];
} else {
clearChunk.push(res);
}
}
return clearChunk;
},
// chunk.filter((res: any) => {
// const key = `${res.name}-${Math.floor(res.time / 100)}-${Math.floor(res.duration / 100)}`;
// return !fetchUrls.has(key);
// }),
BATCH_SIZE, BATCH_SIZE,
25, 25,
); );
@ -612,6 +632,7 @@ export const NetworkPanelComp = observer(
return setSelectedWsChannel(socketMsgList); return setSelectedWsChannel(socketMsgList);
} }
setIsDetailsModalActive(true); setIsDetailsModalActive(true);
showModal( showModal(
<FetchDetailsModal <FetchDetailsModal

View file

@ -75,7 +75,7 @@ function FetchDetailsModal(props: Props) {
} }
/> />
{isXHR && <FetchTabs isSpot={isSpot} resource={resource} />} <FetchTabs isSpot={isSpot} resource={resource} isXHR={isXHR} />
{rows && rows.length > 0 && ( {rows && rows.length > 0 && (
<div className="flex justify-between absolute bottom-0 left-0 right-0 p-3 border-t bg-white"> <div className="flex justify-between absolute bottom-0 left-0 right-0 p-3 border-t bg-white">

View file

@ -9,10 +9,17 @@ import { TFunction } from 'i18next';
const HEADERS = 'HEADERS'; const HEADERS = 'HEADERS';
const REQUEST = 'REQUEST'; const REQUEST = 'REQUEST';
const RESPONSE = 'RESPONSE'; const RESPONSE = 'RESPONSE';
const TABS = [HEADERS, REQUEST, RESPONSE].map((tab) => ({ const TIMINGS = 'TIMINGS';
const TABS = [HEADERS, REQUEST, RESPONSE, TIMINGS].map((tab) => ({
text: tab, text: tab,
key: tab, key: tab,
})); }));
const RESOURCE_TABS = [
{
text: TIMINGS,
key: TIMINGS,
},
];
type RequestResponse = { type RequestResponse = {
headers?: Record<string, string>; headers?: Record<string, string>;
@ -76,10 +83,11 @@ function parseRequestResponse(
interface Props { interface Props {
resource: { request: string; response: string }; resource: { request: string; response: string };
isSpot?: boolean; isSpot?: boolean;
isXHR?: boolean;
} }
function FetchTabs({ resource, isSpot }: Props) { function FetchTabs({ resource, isSpot, isXHR }: Props) {
const { t } = useTranslation(); const { t } = useTranslation();
const [activeTab, setActiveTab] = useState(HEADERS); const [activeTab, setActiveTab] = useState(isXHR ? HEADERS : TIMINGS);
const onTabClick = (tab: string) => setActiveTab(tab); const onTabClick = (tab: string) => setActiveTab(tab);
const [jsonRequest, setJsonRequest] = useState<object | null>(null); const [jsonRequest, setJsonRequest] = useState<object | null>(null);
const [jsonResponse, setJsonResponse] = useState<object | null>(null); const [jsonResponse, setJsonResponse] = useState<object | null>(null);
@ -122,10 +130,13 @@ function FetchTabs({ resource, isSpot }: Props) {
<AnimatedSVG name={ICONS.NO_RESULTS} size={30} /> <AnimatedSVG name={ICONS.NO_RESULTS} size={30} />
<div className="mt-6 text-base font-normal"> <div className="mt-6 text-base font-normal">
{t('Body is empty or not captured.')}{' '} {t('Body is empty or not captured.')}{' '}
<a href="https://docs.openreplay.com/en/sdk/network-options" className="link" target="_blank"> <a
href="https://docs.openreplay.com/en/sdk/network-options"
className="link"
target="_blank"
>
{t('Configure')} {t('Configure')}
</a> </a>{' '}
{' '}
{t( {t(
'network capturing to get more out of Fetch/XHR requests.', 'network capturing to get more out of Fetch/XHR requests.',
)} )}
@ -160,10 +171,13 @@ function FetchTabs({ resource, isSpot }: Props) {
<AnimatedSVG name={ICONS.NO_RESULTS} size={30} /> <AnimatedSVG name={ICONS.NO_RESULTS} size={30} />
<div className="mt-6 text-base font-normal"> <div className="mt-6 text-base font-normal">
{t('Body is empty or not captured.')}{' '} {t('Body is empty or not captured.')}{' '}
<a href="https://docs.openreplay.com/en/sdk/network-options" className="link" target="_blank"> <a
href="https://docs.openreplay.com/en/sdk/network-options"
className="link"
target="_blank"
>
{t('Configure')} {t('Configure')}
</a> </a>{' '}
{' '}
{t( {t(
'network capturing to get more out of Fetch/XHR requests.', 'network capturing to get more out of Fetch/XHR requests.',
)} )}
@ -197,11 +211,16 @@ function FetchTabs({ resource, isSpot }: Props) {
responseHeaders={responseHeaders} responseHeaders={responseHeaders}
/> />
); );
case TIMINGS:
return <div>
{resource.timings ? JSON.stringify(resource.timings, null, 2) : 'notihng :('}
</div>;
} }
}; };
const usedTabs = isXHR ? TABS : RESOURCE_TABS;
return ( return (
<div> <div>
<Tabs tabs={TABS} active={activeTab} onClick={onTabClick} border /> <Tabs tabs={usedTabs} active={activeTab} onClick={onTabClick} border />
<div style={{ height: 'calc(100vh - 364px)', overflowY: 'auto' }}> <div style={{ height: 'calc(100vh - 364px)', overflowY: 'auto' }}>
{renderActiveTab()} {renderActiveTab()}
</div> </div>

View file

@ -304,6 +304,7 @@ export default class TabSessionManager {
Log(msg), Log(msg),
); );
break; break;
case MType.ResourceTimingDeprecatedDeprecated:
case MType.ResourceTimingDeprecated: case MType.ResourceTimingDeprecated:
case MType.ResourceTiming: case MType.ResourceTiming:
// TODO: merge `resource` and `fetch` lists into one here instead of UI // TODO: merge `resource` and `fetch` lists into one here instead of UI

View file

@ -449,7 +449,7 @@ export default class RawMessageReader extends PrimitiveReader {
const url = this.readString(); if (url === null) { return resetPointer() } const url = this.readString(); if (url === null) { return resetPointer() }
const initiator = this.readString(); if (initiator === null) { return resetPointer() } const initiator = this.readString(); if (initiator === null) { return resetPointer() }
return { return {
tp: MType.ResourceTimingDeprecated, tp: MType.ResourceTimingDeprecatedDeprecated,
timestamp, timestamp,
duration, duration,
ttfb, ttfb,
@ -783,7 +783,7 @@ export default class RawMessageReader extends PrimitiveReader {
const transferredSize = this.readUint(); if (transferredSize === null) { return resetPointer() } const transferredSize = this.readUint(); if (transferredSize === null) { return resetPointer() }
const cached = this.readBoolean(); if (cached === null) { return resetPointer() } const cached = this.readBoolean(); if (cached === null) { return resetPointer() }
return { return {
tp: MType.ResourceTiming, tp: MType.ResourceTimingDeprecated,
timestamp, timestamp,
duration, duration,
ttfb, ttfb,

View file

@ -7,4 +7,4 @@ const IOS_TYPES = [90,91,92,93,94,95,96,97,98,100,101,102,103,104,105,106,107,11
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 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) { export function isDOMType(t: MType) {
return DOM_TYPES.includes(t) return DOM_TYPES.includes(t)
} }

View file

@ -40,7 +40,7 @@ import type {
RawSetNodeAttributeDictDeprecated, RawSetNodeAttributeDictDeprecated,
RawStringDict, RawStringDict,
RawSetNodeAttributeDict, RawSetNodeAttributeDict,
RawResourceTimingDeprecated, RawResourceTimingDeprecatedDeprecated,
RawConnectionInformation, RawConnectionInformation,
RawSetPageVisibility, RawSetPageVisibility,
RawLoadFontFace, RawLoadFontFace,
@ -65,7 +65,7 @@ import type {
RawLongAnimationTask, RawLongAnimationTask,
RawSelectionChange, RawSelectionChange,
RawMouseThrashing, RawMouseThrashing,
RawResourceTiming, RawResourceTimingDeprecated,
RawTabChange, RawTabChange,
RawTabData, RawTabData,
RawCanvasNode, RawCanvasNode,
@ -245,4 +245,3 @@ export type MobileNetworkCall = RawMobileNetworkCall & Timed
export type MobileSwipeEvent = RawMobileSwipeEvent & Timed export type MobileSwipeEvent = RawMobileSwipeEvent & Timed
export type MobileIssueEvent = RawMobileIssueEvent & Timed export type MobileIssueEvent = RawMobileIssueEvent & Timed

View file

@ -38,7 +38,7 @@ export const enum MType {
SetNodeAttributeDictDeprecated = 51, SetNodeAttributeDictDeprecated = 51,
StringDict = 43, StringDict = 43,
SetNodeAttributeDict = 52, SetNodeAttributeDict = 52,
ResourceTimingDeprecated = 53, ResourceTimingDeprecatedDeprecated = 53,
ConnectionInformation = 54, ConnectionInformation = 54,
SetPageVisibility = 55, SetPageVisibility = 55,
LoadFontFace = 57, LoadFontFace = 57,
@ -63,7 +63,7 @@ export const enum MType {
LongAnimationTask = 89, LongAnimationTask = 89,
SelectionChange = 113, SelectionChange = 113,
MouseThrashing = 114, MouseThrashing = 114,
ResourceTiming = 116, ResourceTimingDeprecated = 116,
TabChange = 117, TabChange = 117,
TabData = 118, TabData = 118,
CanvasNode = 119, CanvasNode = 119,

View file

@ -39,7 +39,7 @@ export const TP_MAP = {
51: MType.SetNodeAttributeDictDeprecated, 51: MType.SetNodeAttributeDictDeprecated,
43: MType.StringDict, 43: MType.StringDict,
52: MType.SetNodeAttributeDict, 52: MType.SetNodeAttributeDict,
53: MType.ResourceTimingDeprecated, 53: MType.ResourceTimingDeprecatedDeprecated,
54: MType.ConnectionInformation, 54: MType.ConnectionInformation,
55: MType.SetPageVisibility, 55: MType.SetPageVisibility,
57: MType.LoadFontFace, 57: MType.LoadFontFace,
@ -64,7 +64,7 @@ export const TP_MAP = {
89: MType.LongAnimationTask, 89: MType.LongAnimationTask,
113: MType.SelectionChange, 113: MType.SelectionChange,
114: MType.MouseThrashing, 114: MType.MouseThrashing,
116: MType.ResourceTiming, 116: MType.ResourceTimingDeprecated,
117: MType.TabChange, 117: MType.TabChange,
118: MType.TabData, 118: MType.TabData,
119: MType.CanvasNode, 119: MType.CanvasNode,

View file

@ -31,7 +31,7 @@ type TrSetViewportScroll = [
type TrCreateDocument = [ type TrCreateDocument = [
type: 7, type: 7,
] ]
type TrCreateElementNode = [ type TrCreateElementNode = [
@ -298,7 +298,7 @@ type TrSetNodeAttributeDict = [
value: string, value: string,
] ]
type TrResourceTimingDeprecated = [ type TrResourceTimingDeprecatedDeprecated = [
type: 53, type: 53,
timestamp: number, timestamp: number,
duration: number, duration: number,
@ -526,7 +526,7 @@ type TrUnbindNodes = [
totalRemovedPercent: number, totalRemovedPercent: number,
] ]
type TrResourceTiming = [ type TrResourceTimingDeprecated = [
type: 116, type: 116,
timestamp: number, timestamp: number,
duration: number, duration: number,
@ -597,14 +597,14 @@ export type TrackerMessage = TrTimestamp | TrSetPageLocationDeprecated | TrSetVi
export default function translate(tMsg: TrackerMessage): RawMessage | null { export default function translate(tMsg: TrackerMessage): RawMessage | null {
switch(tMsg[0]) { switch(tMsg[0]) {
case 0: { case 0: {
return { return {
tp: MType.Timestamp, tp: MType.Timestamp,
timestamp: tMsg[1], timestamp: tMsg[1],
} }
} }
case 4: { case 4: {
return { return {
tp: MType.SetPageLocationDeprecated, tp: MType.SetPageLocationDeprecated,
@ -613,7 +613,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
navigationStart: tMsg[3], navigationStart: tMsg[3],
} }
} }
case 5: { case 5: {
return { return {
tp: MType.SetViewportSize, tp: MType.SetViewportSize,
@ -621,7 +621,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
height: tMsg[2], height: tMsg[2],
} }
} }
case 6: { case 6: {
return { return {
tp: MType.SetViewportScroll, tp: MType.SetViewportScroll,
@ -629,14 +629,14 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
y: tMsg[2], y: tMsg[2],
} }
} }
case 7: { case 7: {
return { return {
tp: MType.CreateDocument, tp: MType.CreateDocument,
} }
} }
case 8: { case 8: {
return { return {
tp: MType.CreateElementNode, tp: MType.CreateElementNode,
@ -647,7 +647,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
svg: tMsg[5], svg: tMsg[5],
} }
} }
case 9: { case 9: {
return { return {
tp: MType.CreateTextNode, tp: MType.CreateTextNode,
@ -656,7 +656,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
index: tMsg[3], index: tMsg[3],
} }
} }
case 10: { case 10: {
return { return {
tp: MType.MoveNode, tp: MType.MoveNode,
@ -665,14 +665,14 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
index: tMsg[3], index: tMsg[3],
} }
} }
case 11: { case 11: {
return { return {
tp: MType.RemoveNode, tp: MType.RemoveNode,
id: tMsg[1], id: tMsg[1],
} }
} }
case 12: { case 12: {
return { return {
tp: MType.SetNodeAttribute, tp: MType.SetNodeAttribute,
@ -681,7 +681,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
value: tMsg[3], value: tMsg[3],
} }
} }
case 13: { case 13: {
return { return {
tp: MType.RemoveNodeAttribute, tp: MType.RemoveNodeAttribute,
@ -689,7 +689,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
name: tMsg[2], name: tMsg[2],
} }
} }
case 14: { case 14: {
return { return {
tp: MType.SetNodeData, tp: MType.SetNodeData,
@ -697,7 +697,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
data: tMsg[2], data: tMsg[2],
} }
} }
case 16: { case 16: {
return { return {
tp: MType.SetNodeScroll, tp: MType.SetNodeScroll,
@ -706,7 +706,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
y: tMsg[3], y: tMsg[3],
} }
} }
case 18: { case 18: {
return { return {
tp: MType.SetInputValue, tp: MType.SetInputValue,
@ -715,7 +715,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
mask: tMsg[3], mask: tMsg[3],
} }
} }
case 19: { case 19: {
return { return {
tp: MType.SetInputChecked, tp: MType.SetInputChecked,
@ -723,7 +723,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
checked: tMsg[2], checked: tMsg[2],
} }
} }
case 20: { case 20: {
return { return {
tp: MType.MouseMove, tp: MType.MouseMove,
@ -731,7 +731,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
y: tMsg[2], y: tMsg[2],
} }
} }
case 21: { case 21: {
return { return {
tp: MType.NetworkRequestDeprecated, tp: MType.NetworkRequestDeprecated,
@ -745,7 +745,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
duration: tMsg[8], duration: tMsg[8],
} }
} }
case 22: { case 22: {
return { return {
tp: MType.ConsoleLog, tp: MType.ConsoleLog,
@ -753,7 +753,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
value: tMsg[2], value: tMsg[2],
} }
} }
case 34: { case 34: {
return { return {
tp: MType.StringDictGlobal, tp: MType.StringDictGlobal,
@ -761,7 +761,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
value: tMsg[2], value: tMsg[2],
} }
} }
case 35: { case 35: {
return { return {
tp: MType.SetNodeAttributeDictGlobal, tp: MType.SetNodeAttributeDictGlobal,
@ -770,7 +770,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
value: tMsg[3], value: tMsg[3],
} }
} }
case 37: { case 37: {
return { return {
tp: MType.CssInsertRule, tp: MType.CssInsertRule,
@ -779,7 +779,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
index: tMsg[3], index: tMsg[3],
} }
} }
case 38: { case 38: {
return { return {
tp: MType.CssDeleteRule, tp: MType.CssDeleteRule,
@ -787,7 +787,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
index: tMsg[2], index: tMsg[2],
} }
} }
case 39: { case 39: {
return { return {
tp: MType.Fetch, tp: MType.Fetch,
@ -800,7 +800,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
duration: tMsg[7], duration: tMsg[7],
} }
} }
case 40: { case 40: {
return { return {
tp: MType.Profiler, tp: MType.Profiler,
@ -810,7 +810,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
result: tMsg[4], result: tMsg[4],
} }
} }
case 41: { case 41: {
return { return {
tp: MType.OTable, tp: MType.OTable,
@ -818,7 +818,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
value: tMsg[2], value: tMsg[2],
} }
} }
case 44: { case 44: {
return { return {
tp: MType.ReduxDeprecated, tp: MType.ReduxDeprecated,
@ -827,7 +827,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
duration: tMsg[3], duration: tMsg[3],
} }
} }
case 45: { case 45: {
return { return {
tp: MType.Vuex, tp: MType.Vuex,
@ -835,7 +835,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
state: tMsg[2], state: tMsg[2],
} }
} }
case 46: { case 46: {
return { return {
tp: MType.MobX, tp: MType.MobX,
@ -843,7 +843,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
payload: tMsg[2], payload: tMsg[2],
} }
} }
case 47: { case 47: {
return { return {
tp: MType.NgRx, tp: MType.NgRx,
@ -852,7 +852,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
duration: tMsg[3], duration: tMsg[3],
} }
} }
case 48: { case 48: {
return { return {
tp: MType.GraphQlDeprecated, tp: MType.GraphQlDeprecated,
@ -863,7 +863,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
duration: tMsg[5], duration: tMsg[5],
} }
} }
case 49: { case 49: {
return { return {
tp: MType.PerformanceTrack, tp: MType.PerformanceTrack,
@ -873,7 +873,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
usedJSHeapSize: tMsg[4], usedJSHeapSize: tMsg[4],
} }
} }
case 50: { case 50: {
return { return {
tp: MType.StringDictDeprecated, tp: MType.StringDictDeprecated,
@ -881,7 +881,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
value: tMsg[2], value: tMsg[2],
} }
} }
case 51: { case 51: {
return { return {
tp: MType.SetNodeAttributeDictDeprecated, tp: MType.SetNodeAttributeDictDeprecated,
@ -890,7 +890,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
valueKey: tMsg[3], valueKey: tMsg[3],
} }
} }
case 43: { case 43: {
return { return {
tp: MType.StringDict, tp: MType.StringDict,
@ -898,7 +898,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
value: tMsg[2], value: tMsg[2],
} }
} }
case 52: { case 52: {
return { return {
tp: MType.SetNodeAttributeDict, tp: MType.SetNodeAttributeDict,
@ -907,10 +907,10 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
value: tMsg[3], value: tMsg[3],
} }
} }
case 53: { case 53: {
return { return {
tp: MType.ResourceTimingDeprecated, tp: MType.ResourceTimingDeprecatedDeprecated,
timestamp: tMsg[1], timestamp: tMsg[1],
duration: tMsg[2], duration: tMsg[2],
ttfb: tMsg[3], ttfb: tMsg[3],
@ -921,7 +921,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
initiator: tMsg[8], initiator: tMsg[8],
} }
} }
case 54: { case 54: {
return { return {
tp: MType.ConnectionInformation, tp: MType.ConnectionInformation,
@ -929,14 +929,14 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
type: tMsg[2], type: tMsg[2],
} }
} }
case 55: { case 55: {
return { return {
tp: MType.SetPageVisibility, tp: MType.SetPageVisibility,
hidden: tMsg[1], hidden: tMsg[1],
} }
} }
case 57: { case 57: {
return { return {
tp: MType.LoadFontFace, tp: MType.LoadFontFace,
@ -946,14 +946,14 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
descriptors: tMsg[4], descriptors: tMsg[4],
} }
} }
case 58: { case 58: {
return { return {
tp: MType.SetNodeFocus, tp: MType.SetNodeFocus,
id: tMsg[1], id: tMsg[1],
} }
} }
case 59: { case 59: {
return { return {
tp: MType.LongTask, tp: MType.LongTask,
@ -966,7 +966,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
containerName: tMsg[7], containerName: tMsg[7],
} }
} }
case 60: { case 60: {
return { return {
tp: MType.SetNodeAttributeURLBased, tp: MType.SetNodeAttributeURLBased,
@ -976,7 +976,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
baseURL: tMsg[4], baseURL: tMsg[4],
} }
} }
case 61: { case 61: {
return { return {
tp: MType.SetCssDataURLBased, tp: MType.SetCssDataURLBased,
@ -985,7 +985,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
baseURL: tMsg[3], baseURL: tMsg[3],
} }
} }
case 67: { case 67: {
return { return {
tp: MType.CssInsertRuleURLBased, tp: MType.CssInsertRuleURLBased,
@ -995,7 +995,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
baseURL: tMsg[4], baseURL: tMsg[4],
} }
} }
case 68: { case 68: {
return { return {
tp: MType.MouseClick, tp: MType.MouseClick,
@ -1007,7 +1007,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
normalizedY: tMsg[6], normalizedY: tMsg[6],
} }
} }
case 69: { case 69: {
return { return {
tp: MType.MouseClickDeprecated, tp: MType.MouseClickDeprecated,
@ -1017,7 +1017,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
selector: tMsg[4], selector: tMsg[4],
} }
} }
case 70: { case 70: {
return { return {
tp: MType.CreateIFrameDocument, tp: MType.CreateIFrameDocument,
@ -1025,7 +1025,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
id: tMsg[2], id: tMsg[2],
} }
} }
case 71: { case 71: {
return { return {
tp: MType.AdoptedSsReplaceURLBased, tp: MType.AdoptedSsReplaceURLBased,
@ -1034,7 +1034,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
baseURL: tMsg[3], baseURL: tMsg[3],
} }
} }
case 73: { case 73: {
return { return {
tp: MType.AdoptedSsInsertRuleURLBased, tp: MType.AdoptedSsInsertRuleURLBased,
@ -1044,7 +1044,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
baseURL: tMsg[4], baseURL: tMsg[4],
} }
} }
case 75: { case 75: {
return { return {
tp: MType.AdoptedSsDeleteRule, tp: MType.AdoptedSsDeleteRule,
@ -1052,7 +1052,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
index: tMsg[2], index: tMsg[2],
} }
} }
case 76: { case 76: {
return { return {
tp: MType.AdoptedSsAddOwner, tp: MType.AdoptedSsAddOwner,
@ -1060,7 +1060,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
id: tMsg[2], id: tMsg[2],
} }
} }
case 77: { case 77: {
return { return {
tp: MType.AdoptedSsRemoveOwner, tp: MType.AdoptedSsRemoveOwner,
@ -1068,7 +1068,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
id: tMsg[2], id: tMsg[2],
} }
} }
case 79: { case 79: {
return { return {
tp: MType.Zustand, tp: MType.Zustand,
@ -1076,7 +1076,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
state: tMsg[2], state: tMsg[2],
} }
} }
case 83: { case 83: {
return { return {
tp: MType.NetworkRequest, tp: MType.NetworkRequest,
@ -1091,7 +1091,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
transferredBodySize: tMsg[9], transferredBodySize: tMsg[9],
} }
} }
case 84: { case 84: {
return { return {
tp: MType.WsChannel, tp: MType.WsChannel,
@ -1103,7 +1103,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
messageType: tMsg[6], messageType: tMsg[6],
} }
} }
case 89: { case 89: {
return { return {
tp: MType.LongAnimationTask, tp: MType.LongAnimationTask,
@ -1115,7 +1115,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
scripts: tMsg[6], scripts: tMsg[6],
} }
} }
case 113: { case 113: {
return { return {
tp: MType.SelectionChange, tp: MType.SelectionChange,
@ -1124,17 +1124,17 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
selection: tMsg[3], selection: tMsg[3],
} }
} }
case 114: { case 114: {
return { return {
tp: MType.MouseThrashing, tp: MType.MouseThrashing,
timestamp: tMsg[1], timestamp: tMsg[1],
} }
} }
case 116: { case 116: {
return { return {
tp: MType.ResourceTiming, tp: MType.ResourceTimingDeprecated,
timestamp: tMsg[1], timestamp: tMsg[1],
duration: tMsg[2], duration: tMsg[2],
ttfb: tMsg[3], ttfb: tMsg[3],
@ -1147,21 +1147,21 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
cached: tMsg[10], cached: tMsg[10],
} }
} }
case 117: { case 117: {
return { return {
tp: MType.TabChange, tp: MType.TabChange,
tabId: tMsg[1], tabId: tMsg[1],
} }
} }
case 118: { case 118: {
return { return {
tp: MType.TabData, tp: MType.TabData,
tabId: tMsg[1], tabId: tMsg[1],
} }
} }
case 119: { case 119: {
return { return {
tp: MType.CanvasNode, tp: MType.CanvasNode,
@ -1169,14 +1169,14 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
timestamp: tMsg[2], timestamp: tMsg[2],
} }
} }
case 120: { case 120: {
return { return {
tp: MType.TagTrigger, tp: MType.TagTrigger,
tagId: tMsg[1], tagId: tMsg[1],
} }
} }
case 121: { case 121: {
return { return {
tp: MType.Redux, tp: MType.Redux,
@ -1186,7 +1186,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
actionTime: tMsg[4], actionTime: tMsg[4],
} }
} }
case 122: { case 122: {
return { return {
tp: MType.SetPageLocation, tp: MType.SetPageLocation,
@ -1196,7 +1196,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
documentTitle: tMsg[4], documentTitle: tMsg[4],
} }
} }
case 123: { case 123: {
return { return {
tp: MType.GraphQl, tp: MType.GraphQl,
@ -1207,7 +1207,7 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
duration: tMsg[5], duration: tMsg[5],
} }
} }
default: default:
return null return null
} }

View file

@ -65,7 +65,7 @@ export function getResourceName(url: string) {
return url return url
.split('/') .split('/')
.filter((s) => s !== '') .filter((s) => s !== '')
.pop(); .pop() as string;
} }
interface IResource { interface IResource {
@ -96,6 +96,15 @@ export interface IResourceTiming extends IResource {
success: boolean; success: boolean;
status: '2xx-3xx' | '4xx-5xx'; status: '2xx-3xx' | '4xx-5xx';
time: number; time: number;
timings: {
queueing: number;
dnsLookup: number;
initialConnection: number;
ssl: number;
ttfb: number;
contentDownload: number;
total: number;
};
} }
export interface IResourceRequest extends IResource { export interface IResourceRequest extends IResource {
@ -110,26 +119,31 @@ export interface IResourceRequest extends IResource {
decodedBodySize?: number; decodedBodySize?: number;
} }
const getGraphqlReqName = (resource: IResource) => { const getGraphqlReqName = (
resource: Partial<IResourceRequest | IResourceTiming>,
) => {
try { try {
if (!resource.request) return getResourceName(resource.url); if (!resource.request) return getResourceName(resource.url ?? '');
const req = JSON.parse(resource.request); const req = JSON.parse(resource.request);
const body = JSON.parse(req.body); const body = JSON.parse(req.body);
return /query (\w+)/.exec(body.query)?.[1]; return /query (\w+)/.exec(body.query)?.[1] as string;
} catch (e) { } catch (e) {
return getResourceName(resource.url); return getResourceName(resource.url ?? '');
} }
}; };
export const Resource = (resource: IResource) => { export const Resource = (
resource: Partial<IResourceRequest | IResourceTiming>,
) => {
const name = const name =
resource.type === 'graphql' resource.type === 'graphql'
? getGraphqlReqName(resource) ? getGraphqlReqName(resource)
: getResourceName(resource.url); : getResourceName(resource.url ?? '');
return { return {
...resource, ...resource,
name, name,
isRed: !resource.success || resource.error, // || resource.score >= RED_BOUND, isRed: Boolean(!resource.success || resource.error), // || resource.score >= RED_BOUND,
isYellow: false, // resource.score < RED_BOUND && resource.score >= YELLOW_BOUND, isYellow: false, // resource.score < RED_BOUND && resource.score >= YELLOW_BOUND,
}; };
}; };
@ -153,6 +167,15 @@ export function getResourceFromResourceTiming(
success: !failed, success: !failed,
status: !failed ? '2xx-3xx' : '4xx-5xx', status: !failed ? '2xx-3xx' : '4xx-5xx',
time: Math.max(0, msg.timestamp - sessStart), time: Math.max(0, msg.timestamp - sessStart),
timings: {
queueing: msg.queueing,
dnsLookup: msg.dnsLookup,
initialConnection: msg.initialConnection,
ssl: msg.ssl,
ttfb: msg.ttfb,
contentDownload: msg.contentDownload,
total: msg.total,
},
}); });
} }
@ -169,5 +192,6 @@ export function getResourceFromNetworkRequest(
time: Math.max(0, msg.timestamp - sessStart), time: Math.max(0, msg.timestamp - sessStart),
decodedBodySize: decodedBodySize:
'transferredBodySize' in msg ? msg.transferredBodySize : undefined, 'transferredBodySize' in msg ? msg.transferredBodySize : undefined,
timings: {},
}); });
} }

View file

@ -309,7 +309,9 @@ message 52, 'SetNodeAttributeDict' do
string 'Name' string 'Name'
string 'Value' string 'Value'
end end
message 53, 'ResourceTimingDeprecated', :replayer => :devtools do
# deprecated during 1.16.0 release
message 53, 'ResourceTimingDeprecatedDeprecated', :replayer => :devtools do
uint 'Timestamp' uint 'Timestamp'
uint 'Duration' uint 'Duration'
uint 'TTFB' uint 'TTFB'
@ -344,7 +346,7 @@ message 56, 'PerformanceTrackAggr', :tracker => false, :replayer => false do
end end
# Since 4.1.7 / 1.9.0 # Since 4.1.7 / 1.9.0
message 57, 'LoadFontFace' do message 57, 'LoadFontFace' do
uint 'ParentID' uint 'ParentID'
string 'Family' string 'Family'
string 'Source' string 'Source'
@ -520,6 +522,25 @@ message 84, 'WSChannel', :replayer => :devtools do
string 'MessageType' string 'MessageType'
end end
message 85, 'ResourceTiming', :replayer => :devtools do
uint 'Timestamp'
uint 'Duration'
uint 'TTFB'
uint 'HeaderSize'
uint 'EncodedBodySize'
uint 'DecodedBodySize'
string 'URL'
string 'Initiator'
uint 'TransferredSize'
boolean 'Cached'
uint 'Queueing'
uint 'DnsLookup'
uint 'InitialConnection'
uint 'SSL'
uint 'ContentDownload'
uint 'Total'
end
message 89, 'LongAnimationTask', :replayer => :devtools do message 89, 'LongAnimationTask', :replayer => :devtools do
string 'Name' string 'Name'
int 'Duration' int 'Duration'
@ -554,7 +575,8 @@ message 115, 'UnbindNodes', :replayer => false do
uint 'TotalRemovedPercent' uint 'TotalRemovedPercent'
end end
message 116, 'ResourceTiming', :replayer => :devtools do #deprecated during 1.23.0 release
message 116, 'ResourceTimingDeprecated', :replayer => :devtools do
uint 'Timestamp' uint 'Timestamp'
uint 'Duration' uint 'Duration'
uint 'TTFB' uint 'TTFB'
@ -630,4 +652,4 @@ message 127, 'SessionSearch', :tracker => false, :replayer => false do
uint 'Partition' uint 'Partition'
end end
# FREE 2, 35, 36, 65, 85, 86, 87, 88, 89 # FREE 2, 35, 36, 65, 85, 86, 87, 88, 89

View file

@ -45,7 +45,7 @@ export declare const enum Type {
SetNodeAttributeDictDeprecated = 51, SetNodeAttributeDictDeprecated = 51,
StringDict = 43, StringDict = 43,
SetNodeAttributeDict = 52, SetNodeAttributeDict = 52,
ResourceTimingDeprecated = 53, ResourceTimingDeprecatedDeprecated = 53,
ConnectionInformation = 54, ConnectionInformation = 54,
SetPageVisibility = 55, SetPageVisibility = 55,
LoadFontFace = 57, LoadFontFace = 57,
@ -75,7 +75,7 @@ export declare const enum Type {
SelectionChange = 113, SelectionChange = 113,
MouseThrashing = 114, MouseThrashing = 114,
UnbindNodes = 115, UnbindNodes = 115,
ResourceTiming = 116, ResourceTimingDeprecated = 116,
TabChange = 117, TabChange = 117,
TabData = 118, TabData = 118,
CanvasNode = 119, CanvasNode = 119,
@ -113,7 +113,7 @@ export type SetViewportScroll = [
export type CreateDocument = [ export type CreateDocument = [
/*type:*/ Type.CreateDocument, /*type:*/ Type.CreateDocument,
] ]
export type CreateElementNode = [ export type CreateElementNode = [
@ -380,8 +380,8 @@ export type SetNodeAttributeDict = [
/*value:*/ string, /*value:*/ string,
] ]
export type ResourceTimingDeprecated = [ export type ResourceTimingDeprecatedDeprecated = [
/*type:*/ Type.ResourceTimingDeprecated, /*type:*/ Type.ResourceTimingDeprecatedDeprecated,
/*timestamp:*/ number, /*timestamp:*/ number,
/*duration:*/ number, /*duration:*/ number,
/*ttfb:*/ number, /*ttfb:*/ number,
@ -608,8 +608,8 @@ export type UnbindNodes = [
/*totalRemovedPercent:*/ number, /*totalRemovedPercent:*/ number,
] ]
export type ResourceTiming = [ export type ResourceTimingDeprecated = [
/*type:*/ Type.ResourceTiming, /*type:*/ Type.ResourceTimingDeprecated,
/*timestamp:*/ number, /*timestamp:*/ number,
/*duration:*/ number, /*duration:*/ number,
/*ttfb:*/ number, /*ttfb:*/ number,

View file

@ -50,11 +50,11 @@ export function SetViewportScroll(
} }
export function CreateDocument( export function CreateDocument(
): Messages.CreateDocument { ): Messages.CreateDocument {
return [ return [
Messages.Type.CreateDocument, Messages.Type.CreateDocument,
] ]
} }
@ -548,7 +548,7 @@ export function SetNodeAttributeDict(
] ]
} }
export function ResourceTimingDeprecated( export function ResourceTimingDeprecatedDeprecated(
timestamp: number, timestamp: number,
duration: number, duration: number,
ttfb: number, ttfb: number,
@ -557,9 +557,9 @@ export function ResourceTimingDeprecated(
decodedBodySize: number, decodedBodySize: number,
url: string, url: string,
initiator: string, initiator: string,
): Messages.ResourceTimingDeprecated { ): Messages.ResourceTimingDeprecatedDeprecated {
return [ return [
Messages.Type.ResourceTimingDeprecated, Messages.Type.ResourceTimingDeprecatedDeprecated,
timestamp, timestamp,
duration, duration,
ttfb, ttfb,
@ -974,7 +974,7 @@ export function UnbindNodes(
] ]
} }
export function ResourceTiming( export function ResourceTimingDeprecated(
timestamp: number, timestamp: number,
duration: number, duration: number,
ttfb: number, ttfb: number,
@ -985,9 +985,9 @@ export function ResourceTiming(
initiator: string, initiator: string,
transferredSize: number, transferredSize: number,
cached: boolean, cached: boolean,
): Messages.ResourceTiming { ): Messages.ResourceTimingDeprecated {
return [ return [
Messages.Type.ResourceTiming, Messages.Type.ResourceTimingDeprecated,
timestamp, timestamp,
duration, duration,
ttfb, ttfb,
@ -1096,4 +1096,3 @@ export function WebVitals(
value, value,
] ]
} }

View file

@ -60,7 +60,7 @@ export default function (app: App): void {
const sendImgError = app.safe(function (img: HTMLImageElement): void { const sendImgError = app.safe(function (img: HTMLImageElement): void {
const resolvedSrc = resolveURL(img.src || '') // Src type is null sometimes. - is it true? const resolvedSrc = resolveURL(img.src || '') // Src type is null sometimes. - is it true?
if (isURL(resolvedSrc)) { if (isURL(resolvedSrc)) {
app.send(ResourceTiming(app.timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img', 0, false)) app.send(ResourceTiming(app.timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img', 0, false, 0, 0, 0, 0, 0, 0))
} }
}) })

View file

@ -123,19 +123,37 @@ export default function (app: App, opts: Partial<Options>): void {
} }
const failed = entry.responseEnd === 0 const failed = entry.responseEnd === 0
|| (entry.transferSize === 0 && entry.decodedBodySize === 0) || (entry.transferSize === 0 && entry.decodedBodySize === 0)
|| (entry.responseStatus && entry.responseStatus >= 400)
const timings = {
queueing: entry.domainLookupStart - entry.startTime,
dnsLookup: entry.domainLookupEnd - entry.domainLookupStart,
initialConnection: entry.connectEnd - entry.connectStart,
ssl: entry.secureConnectionStart > 0
? entry.connectEnd - entry.secureConnectionStart : 0,
ttfb: entry.responseStart - entry.requestStart,
contentDownload: entry.responseEnd - entry.responseStart,
total: entry.responseEnd - entry.startTime
};
if (failed) { if (failed) {
app.send( app.send(
ResourceTiming( ResourceTiming(
entry.startTime + getTimeOrigin(), entry.startTime + getTimeOrigin(),
0, 0,
0, timings.ttfb,
0, 0,
0, 0,
0, 0,
entry.name, entry.name,
entry.initiatorType, entry.initiatorType,
0, 0,
true, false,
timings.queueing,
timings.dnsLookup,
timings.initialConnection,
timings.ssl,
timings.contentDownload,
timings.total,
), ),
) )
} }
@ -143,15 +161,20 @@ export default function (app: App, opts: Partial<Options>): void {
ResourceTiming( ResourceTiming(
entry.startTime + getTimeOrigin(), entry.startTime + getTimeOrigin(),
entry.duration, entry.duration,
entry.responseStart && entry.startTime ? entry.responseStart - entry.startTime : 0, timings.ttfb,
entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0,
entry.encodedBodySize || 0, entry.encodedBodySize || 0,
entry.decodedBodySize || 0, entry.decodedBodySize || 0,
app.sanitizer.privateMode ? entry.name.replaceAll(/./g, '*') : entry.name, app.sanitizer.privateMode ? entry.name.replaceAll(/./g, '*') : entry.name,
entry.initiatorType, entry.initiatorType,
entry.transferSize, entry.transferSize,
// @ts-ignore
(entry.responseStatus && entry.responseStatus === 304) || entry.transferSize === 0, (entry.responseStatus && entry.responseStatus === 304) || entry.transferSize === 0,
timings.queueing,
timings.dnsLookup,
timings.initialConnection,
timings.ssl,
timings.contentDownload,
timings.total,
), ),
) )
} }

View file

@ -27,7 +27,7 @@ export default class MessageEncoder extends PrimitiveEncoder {
break break
case Messages.Type.CreateDocument: case Messages.Type.CreateDocument:
return true return true
break break
case Messages.Type.CreateElementNode: case Messages.Type.CreateElementNode:
@ -182,7 +182,7 @@ export default class MessageEncoder extends PrimitiveEncoder {
return this.uint(msg[1]) && this.string(msg[2]) && this.string(msg[3]) return this.uint(msg[1]) && this.string(msg[2]) && this.string(msg[3])
break break
case Messages.Type.ResourceTimingDeprecated: case Messages.Type.ResourceTimingDeprecatedDeprecated:
return this.uint(msg[1]) && this.uint(msg[2]) && this.uint(msg[3]) && this.uint(msg[4]) && this.uint(msg[5]) && this.uint(msg[6]) && this.string(msg[7]) && this.string(msg[8]) return this.uint(msg[1]) && this.uint(msg[2]) && this.uint(msg[3]) && this.uint(msg[4]) && this.uint(msg[5]) && this.uint(msg[6]) && this.string(msg[7]) && this.string(msg[8])
break break
@ -302,7 +302,7 @@ export default class MessageEncoder extends PrimitiveEncoder {
return this.uint(msg[1]) return this.uint(msg[1])
break break
case Messages.Type.ResourceTiming: case Messages.Type.ResourceTimingDeprecated:
return this.uint(msg[1]) && this.uint(msg[2]) && this.uint(msg[3]) && this.uint(msg[4]) && this.uint(msg[5]) && this.uint(msg[6]) && this.string(msg[7]) && this.string(msg[8]) && this.uint(msg[9]) && this.boolean(msg[10]) return this.uint(msg[1]) && this.uint(msg[2]) && this.uint(msg[3]) && this.uint(msg[4]) && this.uint(msg[5]) && this.uint(msg[6]) && this.string(msg[7]) && this.string(msg[8]) && this.uint(msg[9]) && this.boolean(msg[10])
break break