@@ -110,6 +110,7 @@ class EventType(Enum):
110
110
UI_BLUR = 18
111
111
UI_FOCUS = 19
112
112
UNKNOWN = 20
113
+ CLS = 21
113
114
114
115
115
116
def which (event : dict [str , Any ]) -> EventType :
@@ -189,6 +190,8 @@ def which(event: dict[str, Any]) -> EventType:
189
190
return EventType .LCP
190
191
elif payload ["description" ] == "first-contentful-paint" :
191
192
return EventType .FCP
193
+ elif payload ["description" ] == "cumulative-layout-shift" :
194
+ return EventType .CLS
192
195
else :
193
196
return EventType .UNKNOWN
194
197
elif op == "memory" :
@@ -311,6 +314,10 @@ def as_trace_item_context(event_type: EventType, event: dict[str, Any]) -> Trace
311
314
case EventType .CLICK | EventType .DEAD_CLICK | EventType .RAGE_CLICK | EventType .SLOW_CLICK :
312
315
payload = event ["data" ]["payload" ]
313
316
317
+ # If the node wasn't provided we're forced to skip the event.
318
+ if "node" not in payload ["data" ]:
319
+ return None
320
+
314
321
node = payload ["data" ]["node" ]
315
322
node_attributes = node .get ("attributes" , {})
316
323
click_attributes = {
@@ -352,10 +359,7 @@ def as_trace_item_context(event_type: EventType, event: dict[str, Any]) -> Trace
352
359
payload = event ["data" ]["payload" ]
353
360
payload_data = payload ["data" ]
354
361
355
- navigation_attributes = {
356
- "category" : "navigation" ,
357
- "url" : as_string_strict (event ["data" ]["payload" ]["description" ]),
358
- }
362
+ navigation_attributes = {"category" : "navigation" }
359
363
if "from" in payload_data :
360
364
navigation_attributes ["from" ] = as_string_strict (payload_data ["from" ])
361
365
if "to" in payload_data :
@@ -373,25 +377,24 @@ def as_trace_item_context(event_type: EventType, event: dict[str, Any]) -> Trace
373
377
case EventType .UI_FOCUS :
374
378
return None
375
379
case EventType .RESOURCE_FETCH | EventType .RESOURCE_XHR :
380
+ payload = event ["data" ]["payload" ]
381
+
376
382
resource_attributes = {
377
383
"category" : (
378
384
"resource.xhr" if event_type == EventType .RESOURCE_XHR else "resource.fetch"
379
385
),
380
- "url" : as_string_strict (event ["data" ]["payload" ]["description" ]),
381
- "method" : str (event ["data" ]["payload" ]["data" ]["method" ]),
382
- "statusCode" : int (event ["data" ]["payload" ]["data" ]["statusCode" ]),
383
- "duration" : float (event ["data" ]["payload" ]["endTimestamp" ])
384
- - float (event ["data" ]["payload" ]["startTimestamp" ]),
386
+ "url" : as_string_strict (payload ["description" ]),
387
+ "method" : str (payload ["data" ]["method" ]),
388
+ "duration" : float (payload ["endTimestamp" ]) - float (payload ["startTimestamp" ]),
385
389
}
386
390
387
- for key , value in (
388
- event ["data" ]["payload" ]["data" ].get ("request" , {}).get ("headers" , {}).items ()
389
- ):
391
+ if "statusCode" in payload ["data" ]:
392
+ resource_attributes ["statusCode" ] = int (payload ["data" ]["statusCode" ])
393
+
394
+ for key , value in payload ["data" ].get ("request" , {}).get ("headers" , {}).items ():
390
395
resource_attributes [f"request.headers.{ key } " ] = str (value )
391
396
392
- for key , value in (
393
- event ["data" ]["payload" ]["data" ].get ("response" , {}).get ("headers" , {}).items ()
394
- ):
397
+ for key , value in payload ["data" ].get ("response" , {}).get ("headers" , {}).items ():
395
398
resource_attributes [f"response.headers.{ key } " ] = str (value )
396
399
397
400
request_size , response_size = parse_network_content_lengths (event )
@@ -403,7 +406,7 @@ def as_trace_item_context(event_type: EventType, event: dict[str, Any]) -> Trace
403
406
return {
404
407
"attributes" : resource_attributes ,
405
408
"event_hash" : uuid .uuid4 ().bytes ,
406
- "timestamp" : float (event [ "data" ][ " payload" ] ["startTimestamp" ]),
409
+ "timestamp" : float (payload ["startTimestamp" ]),
407
410
}
408
411
case EventType .RESOURCE_SCRIPT | EventType .RESOURCE_IMAGE :
409
412
return {
@@ -424,17 +427,27 @@ def as_trace_item_context(event_type: EventType, event: dict[str, Any]) -> Trace
424
427
"event_hash" : uuid .uuid4 ().bytes ,
425
428
"timestamp" : float (event ["data" ]["payload" ]["startTimestamp" ]),
426
429
}
427
- case EventType .LCP | EventType .FCP :
430
+ case EventType .LCP | EventType .FCP | EventType . CLS :
428
431
payload = event ["data" ]["payload" ]
432
+
433
+ if event_type == EventType .CLS :
434
+ category = "web-vital.cls"
435
+ elif event_type == EventType .FCP :
436
+ category = "web-vital.fcp"
437
+ else :
438
+ category = "web-vital.lcp"
439
+
429
440
return {
430
441
"attributes" : {
431
- "category" : "web-vital.fcp" if event_type == EventType .FCP else "web-vital.lcp" ,
442
+ "category" : category ,
443
+ "duration" : float (event ["data" ]["payload" ]["endTimestamp" ])
444
+ - float (event ["data" ]["payload" ]["startTimestamp" ]),
432
445
"rating" : as_string_strict (payload ["data" ]["rating" ]),
433
- "size" : int (payload ["data" ]["size" ]),
434
- "value" : int (payload ["data" ]["value" ]),
446
+ "size" : float (payload ["data" ]["size" ]),
447
+ "value" : float (payload ["data" ]["value" ]),
435
448
},
436
449
"event_hash" : uuid .uuid4 ().bytes ,
437
- "timestamp" : float (payload ["timestamp " ]),
450
+ "timestamp" : float (payload ["startTimestamp " ]),
438
451
}
439
452
case EventType .HYDRATION_ERROR :
440
453
payload = event ["data" ]["payload" ]
@@ -488,9 +501,9 @@ def as_trace_item_context(event_type: EventType, event: dict[str, Any]) -> Trace
488
501
return {
489
502
"attributes" : {
490
503
"category" : "memory" ,
491
- "jsHeapSizeLimit" : int (payload ["data" ]["jsHeapSizeLimit" ]),
492
- "totalJSHeapSize" : int (payload ["data" ]["totalJSHeapSize" ]),
493
- "usedJSHeapSize" : int (payload ["data" ]["usedJSHeapSize" ]),
504
+ "jsHeapSizeLimit" : int (payload ["data" ]["memory" ][ " jsHeapSizeLimit" ]),
505
+ "totalJSHeapSize" : int (payload ["data" ]["memory" ][ " totalJSHeapSize" ]),
506
+ "usedJSHeapSize" : int (payload ["data" ]["memory" ][ " usedJSHeapSize" ]),
494
507
"endTimestamp" : float (payload ["endTimestamp" ]),
495
508
"duration" : float (event ["data" ]["payload" ]["endTimestamp" ])
496
509
- float (event ["data" ]["payload" ]["startTimestamp" ]),
@@ -577,13 +590,13 @@ def as_highlighted_event(
577
590
return {"mutations" : [MutationEvent (event ["data" ]["payload" ])]}
578
591
elif event_type == EventType .CLICK or event_type == EventType .SLOW_CLICK :
579
592
click = parse_click_event (event ["data" ]["payload" ], is_dead = False , is_rage = False )
580
- return {"clicks" : [click ]}
593
+ return {"clicks" : [click ]} if click else {}
581
594
elif event_type == EventType .DEAD_CLICK :
582
595
click = parse_click_event (event ["data" ]["payload" ], is_dead = True , is_rage = False )
583
- return {"clicks" : [click ]}
596
+ return {"clicks" : [click ]} if click else {}
584
597
elif event_type == EventType .RAGE_CLICK :
585
598
click = parse_click_event (event ["data" ]["payload" ], is_dead = True , is_rage = True )
586
- return {"clicks" : [click ]}
599
+ return {"clicks" : [click ]} if click else {}
587
600
elif event_type == EventType .RESOURCE_FETCH or event_type == EventType .RESOURCE_XHR :
588
601
lengths = parse_network_content_lengths (event )
589
602
if lengths != (None , None ):
@@ -626,10 +639,11 @@ def _get_response_size(data: dict[str, Any]) -> int:
626
639
return request_size , response_size
627
640
628
641
629
- def parse_click_event (payload : dict [str , Any ], is_dead : bool , is_rage : bool ) -> ClickEvent :
630
- node = payload ["data" ]["node" ]
631
- assert node is not None
632
- assert node ["id" ] >= 0
642
+ def parse_click_event (payload : dict [str , Any ], is_dead : bool , is_rage : bool ) -> ClickEvent | None :
643
+ node = payload ["data" ].get ("node" )
644
+
645
+ if not isinstance (node , dict ) or node .get ("id" , - 1 ) < 0 :
646
+ return None
633
647
634
648
attributes = node .get ("attributes" , {})
635
649
0 commit comments