From 9e1add4ad9f7dd477cdc77ea73545a9d47f318ff Mon Sep 17 00:00:00 2001 From: Delirium Date: Wed, 3 Jan 2024 15:54:18 +0100 Subject: [PATCH] feat(tracker/ui): add websocket support (#1733) * feat(tracker/ui): add websocket support * feat(tracker): expose ws tracker method * fix(ui): add docs, fix types for ws methods * fix(ui): some style fixes, rename field in mob * fix(ui): change ws modal * fix(ui): change ws modal * fix(ui): rm mock data --- backend/pkg/messages/messages.go | 595 +++++++++--------- backend/pkg/messages/read-message.go | 26 + ee/connectors/msgcodec/messages.py | 12 + ee/connectors/msgcodec/messages.pyx | 19 + ee/connectors/msgcodec/msgcodec.py | 10 + ee/connectors/msgcodec/msgcodec.pyx | 10 + .../Session_/Player/Controls/EventsList.tsx | 7 +- .../DevTools/NetworkPanel/NetworkPanel.tsx | 68 +- .../shared/DevTools/NetworkPanel/WSModal.tsx | 93 +++ frontend/app/date.ts | 4 +- .../mobile/{IOSLists.tsx => IOSLists.ts} | 2 +- .../app/player/mobile/IOSMessageManager.ts | 3 + frontend/app/player/web/Lists.ts | 2 +- frontend/app/player/web/TabManager.ts | 193 +++--- .../web/messages/RawMessageReader.gen.ts | 18 + .../app/player/web/messages/message.gen.ts | 3 + frontend/app/player/web/messages/raw.gen.ts | 13 +- .../player/web/messages/tracker-legacy.gen.ts | 1 + .../app/player/web/messages/tracker.gen.ts | 24 +- frontend/app/player/web/types/resource.ts | 1 + mobs/messages.rb | 9 + tracker/tracker/package.json | 2 +- tracker/tracker/src/common/messages.gen.ts | 13 +- tracker/tracker/src/main/app/index.ts | 30 +- tracker/tracker/src/main/app/messages.gen.ts | 19 + tracker/tracker/src/main/index.ts | 14 + .../src/webworker/MessageEncoder.gen.ts | 4 + 27 files changed, 809 insertions(+), 386 deletions(-) create mode 100644 frontend/app/components/shared/DevTools/NetworkPanel/WSModal.tsx rename frontend/app/player/mobile/{IOSLists.tsx => IOSLists.ts} (96%) diff --git a/backend/pkg/messages/messages.go b/backend/pkg/messages/messages.go index a5307ee44..8dab53ff0 100644 --- a/backend/pkg/messages/messages.go +++ b/backend/pkg/messages/messages.go @@ -77,6 +77,7 @@ const ( MsgBatchMetadata = 81 MsgPartitionedMessage = 82 MsgNetworkRequest = 83 + MsgWSChannel = 84 MsgInputChange = 112 MsgSelectionChange = 113 MsgMouseThrashing = 114 @@ -109,7 +110,6 @@ const ( MsgIOSIssueEvent = 111 ) - type Timestamp struct { message Timestamp uint64 @@ -133,22 +133,22 @@ func (msg *Timestamp) TypeID() int { type SessionStart struct { message - Timestamp uint64 - ProjectID uint64 - TrackerVersion string - RevID string - UserUUID string - UserAgent string - UserOS string - UserOSVersion string - UserBrowser string - UserBrowserVersion string - UserDevice string - UserDeviceType string + Timestamp uint64 + ProjectID uint64 + TrackerVersion string + RevID string + UserUUID string + UserAgent string + UserOS string + UserOSVersion string + UserBrowser string + UserBrowserVersion string + UserDevice string + UserDeviceType string UserDeviceMemorySize uint64 - UserDeviceHeapSize uint64 - UserCountry string - UserID string + UserDeviceHeapSize uint64 + UserCountry string + UserID string } func (msg *SessionStart) Encode() []byte { @@ -205,8 +205,8 @@ func (msg *SessionEndDeprecated) TypeID() int { type SetPageLocation struct { message - URL string - Referrer string + URL string + Referrer string NavigationStart uint64 } @@ -230,7 +230,7 @@ func (msg *SetPageLocation) TypeID() int { type SetViewportSize struct { message - Width uint64 + Width uint64 Height uint64 } @@ -276,7 +276,6 @@ func (msg *SetViewportScroll) TypeID() int { type CreateDocument struct { message - } func (msg *CreateDocument) Encode() []byte { @@ -297,11 +296,11 @@ func (msg *CreateDocument) TypeID() int { type CreateElementNode struct { message - ID uint64 + ID uint64 ParentID uint64 - index uint64 - Tag string - SVG bool + index uint64 + Tag string + SVG bool } func (msg *CreateElementNode) Encode() []byte { @@ -326,9 +325,9 @@ func (msg *CreateElementNode) TypeID() int { type CreateTextNode struct { message - ID uint64 + ID uint64 ParentID uint64 - Index uint64 + Index uint64 } func (msg *CreateTextNode) Encode() []byte { @@ -351,9 +350,9 @@ func (msg *CreateTextNode) TypeID() int { type MoveNode struct { message - ID uint64 + ID uint64 ParentID uint64 - Index uint64 + Index uint64 } func (msg *MoveNode) Encode() []byte { @@ -397,8 +396,8 @@ func (msg *RemoveNode) TypeID() int { type SetNodeAttribute struct { message - ID uint64 - Name string + ID uint64 + Name string Value string } @@ -422,7 +421,7 @@ func (msg *SetNodeAttribute) TypeID() int { type RemoveNodeAttribute struct { message - ID uint64 + ID uint64 Name string } @@ -445,7 +444,7 @@ func (msg *RemoveNodeAttribute) TypeID() int { type SetNodeData struct { message - ID uint64 + ID uint64 Data string } @@ -468,7 +467,7 @@ func (msg *SetNodeData) TypeID() int { type SetCSSData struct { message - ID uint64 + ID uint64 Data string } @@ -492,8 +491,8 @@ func (msg *SetCSSData) TypeID() int { type SetNodeScroll struct { message ID uint64 - X int64 - Y int64 + X int64 + Y int64 } func (msg *SetNodeScroll) Encode() []byte { @@ -516,7 +515,7 @@ func (msg *SetNodeScroll) TypeID() int { type SetInputTarget struct { message - ID uint64 + ID uint64 Label string } @@ -539,9 +538,9 @@ func (msg *SetInputTarget) TypeID() int { type SetInputValue struct { message - ID uint64 + ID uint64 Value string - Mask int64 + Mask int64 } func (msg *SetInputValue) Encode() []byte { @@ -564,7 +563,7 @@ func (msg *SetInputValue) TypeID() int { type SetInputChecked struct { message - ID uint64 + ID uint64 Checked bool } @@ -610,14 +609,14 @@ func (msg *MouseMove) TypeID() int { type NetworkRequestDeprecated struct { message - Type string - Method string - URL string - Request string - Response string - Status uint64 + Type string + Method string + URL string + Request string + Response string + Status uint64 Timestamp uint64 - Duration uint64 + Duration uint64 } func (msg *NetworkRequestDeprecated) Encode() []byte { @@ -668,15 +667,15 @@ func (msg *ConsoleLog) TypeID() int { type PageLoadTiming struct { message - RequestStart uint64 - ResponseStart uint64 - ResponseEnd uint64 + RequestStart uint64 + ResponseStart uint64 + ResponseEnd uint64 DomContentLoadedEventStart uint64 - DomContentLoadedEventEnd uint64 - LoadEventStart uint64 - LoadEventEnd uint64 - FirstPaint uint64 - FirstContentfulPaint uint64 + DomContentLoadedEventEnd uint64 + LoadEventStart uint64 + LoadEventEnd uint64 + FirstPaint uint64 + FirstContentfulPaint uint64 } func (msg *PageLoadTiming) Encode() []byte { @@ -705,8 +704,8 @@ func (msg *PageLoadTiming) TypeID() int { type PageRenderTiming struct { message - SpeedIndex uint64 - VisuallyComplete uint64 + SpeedIndex uint64 + VisuallyComplete uint64 TimeToInteractive uint64 } @@ -730,7 +729,7 @@ func (msg *PageRenderTiming) TypeID() int { type JSExceptionDeprecated struct { message - Name string + Name string Message string Payload string } @@ -756,10 +755,10 @@ func (msg *JSExceptionDeprecated) TypeID() int { type IntegrationEvent struct { message Timestamp uint64 - Source string - Name string - Message string - Payload string + Source string + Name string + Message string + Payload string } func (msg *IntegrationEvent) Encode() []byte { @@ -784,7 +783,7 @@ func (msg *IntegrationEvent) TypeID() int { type CustomEvent struct { message - Name string + Name string Payload string } @@ -849,7 +848,7 @@ func (msg *UserAnonymousID) TypeID() int { type Metadata struct { message - Key string + Key string Value string } @@ -872,23 +871,23 @@ func (msg *Metadata) TypeID() int { type PageEvent struct { message - MessageID uint64 - Timestamp uint64 - URL string - Referrer string - Loaded bool - RequestStart uint64 - ResponseStart uint64 - ResponseEnd uint64 + MessageID uint64 + Timestamp uint64 + URL string + Referrer string + Loaded bool + RequestStart uint64 + ResponseStart uint64 + ResponseEnd uint64 DomContentLoadedEventStart uint64 - DomContentLoadedEventEnd uint64 - LoadEventStart uint64 - LoadEventEnd uint64 - FirstPaint uint64 - FirstContentfulPaint uint64 - SpeedIndex uint64 - VisuallyComplete uint64 - TimeToInteractive uint64 + DomContentLoadedEventEnd uint64 + LoadEventStart uint64 + LoadEventEnd uint64 + FirstPaint uint64 + FirstContentfulPaint uint64 + SpeedIndex uint64 + VisuallyComplete uint64 + TimeToInteractive uint64 } func (msg *PageEvent) Encode() []byte { @@ -925,11 +924,11 @@ func (msg *PageEvent) TypeID() int { type InputEvent struct { message - MessageID uint64 - Timestamp uint64 - Value string + MessageID uint64 + Timestamp uint64 + Value string ValueMasked bool - Label string + Label string } func (msg *InputEvent) Encode() []byte { @@ -954,8 +953,8 @@ func (msg *InputEvent) TypeID() int { type CSSInsertRule struct { message - ID uint64 - Rule string + ID uint64 + Rule string Index uint64 } @@ -979,7 +978,7 @@ func (msg *CSSInsertRule) TypeID() int { type CSSDeleteRule struct { message - ID uint64 + ID uint64 Index uint64 } @@ -1002,13 +1001,13 @@ func (msg *CSSDeleteRule) TypeID() int { type Fetch struct { message - Method string - URL string - Request string - Response string - Status uint64 + Method string + URL string + Request string + Response string + Status uint64 Timestamp uint64 - Duration uint64 + Duration uint64 } func (msg *Fetch) Encode() []byte { @@ -1035,10 +1034,10 @@ func (msg *Fetch) TypeID() int { type Profiler struct { message - Name string + Name string Duration uint64 - Args string - Result string + Args string + Result string } func (msg *Profiler) Encode() []byte { @@ -1062,7 +1061,7 @@ func (msg *Profiler) TypeID() int { type OTable struct { message - Key string + Key string Value string } @@ -1106,8 +1105,8 @@ func (msg *StateAction) TypeID() int { type Redux struct { message - Action string - State string + Action string + State string Duration uint64 } @@ -1132,7 +1131,7 @@ func (msg *Redux) TypeID() int { type Vuex struct { message Mutation string - State string + State string } func (msg *Vuex) Encode() []byte { @@ -1154,7 +1153,7 @@ func (msg *Vuex) TypeID() int { type MobX struct { message - Type string + Type string Payload string } @@ -1177,8 +1176,8 @@ func (msg *MobX) TypeID() int { type NgRx struct { message - Action string - State string + Action string + State string Duration uint64 } @@ -1204,8 +1203,8 @@ type GraphQL struct { message OperationKind string OperationName string - Variables string - Response string + Variables string + Response string } func (msg *GraphQL) Encode() []byte { @@ -1229,10 +1228,10 @@ func (msg *GraphQL) TypeID() int { type PerformanceTrack struct { message - Frames int64 - Ticks int64 + Frames int64 + Ticks int64 TotalJSHeapSize uint64 - UsedJSHeapSize uint64 + UsedJSHeapSize uint64 } func (msg *PerformanceTrack) Encode() []byte { @@ -1256,7 +1255,7 @@ func (msg *PerformanceTrack) TypeID() int { type StringDict struct { message - Key uint64 + Key uint64 Value string } @@ -1279,8 +1278,8 @@ func (msg *StringDict) TypeID() int { type SetNodeAttributeDict struct { message - ID uint64 - NameKey uint64 + ID uint64 + NameKey uint64 ValueKey uint64 } @@ -1304,14 +1303,14 @@ func (msg *SetNodeAttributeDict) TypeID() int { type ResourceTimingDeprecated struct { message - Timestamp uint64 - Duration uint64 - TTFB uint64 - HeaderSize uint64 + Timestamp uint64 + Duration uint64 + TTFB uint64 + HeaderSize uint64 EncodedBodySize uint64 DecodedBodySize uint64 - URL string - Initiator string + URL string + Initiator string } func (msg *ResourceTimingDeprecated) Encode() []byte { @@ -1340,7 +1339,7 @@ func (msg *ResourceTimingDeprecated) TypeID() int { type ConnectionInformation struct { message Downlink uint64 - Type string + Type string } func (msg *ConnectionInformation) Encode() []byte { @@ -1383,20 +1382,20 @@ func (msg *SetPageVisibility) TypeID() int { type PerformanceTrackAggr struct { message - TimestampStart uint64 - TimestampEnd uint64 - MinFPS uint64 - AvgFPS uint64 - MaxFPS uint64 - MinCPU uint64 - AvgCPU uint64 - MaxCPU uint64 + TimestampStart uint64 + TimestampEnd uint64 + MinFPS uint64 + AvgFPS uint64 + MaxFPS uint64 + MinCPU uint64 + AvgCPU uint64 + MaxCPU uint64 MinTotalJSHeapSize uint64 AvgTotalJSHeapSize uint64 MaxTotalJSHeapSize uint64 - MinUsedJSHeapSize uint64 - AvgUsedJSHeapSize uint64 - MaxUsedJSHeapSize uint64 + MinUsedJSHeapSize uint64 + AvgUsedJSHeapSize uint64 + MaxUsedJSHeapSize uint64 } func (msg *PerformanceTrackAggr) Encode() []byte { @@ -1430,9 +1429,9 @@ func (msg *PerformanceTrackAggr) TypeID() int { type LoadFontFace struct { message - ParentID uint64 - Family string - Source string + ParentID uint64 + Family string + Source string Descriptors string } @@ -1478,12 +1477,12 @@ func (msg *SetNodeFocus) TypeID() int { type LongTask struct { message - Timestamp uint64 - Duration uint64 - Context uint64 + Timestamp uint64 + Duration uint64 + Context uint64 ContainerType uint64 - ContainerSrc string - ContainerId string + ContainerSrc string + ContainerId string ContainerName string } @@ -1511,9 +1510,9 @@ func (msg *LongTask) TypeID() int { type SetNodeAttributeURLBased struct { message - ID uint64 - Name string - Value string + ID uint64 + Name string + Value string BaseURL string } @@ -1538,8 +1537,8 @@ func (msg *SetNodeAttributeURLBased) TypeID() int { type SetCSSDataURLBased struct { message - ID uint64 - Data string + ID uint64 + Data string BaseURL string } @@ -1563,12 +1562,12 @@ func (msg *SetCSSDataURLBased) TypeID() int { type IssueEventDeprecated struct { message - MessageID uint64 - Timestamp uint64 - Type string + MessageID uint64 + Timestamp uint64 + Type string ContextString string - Context string - Payload string + Context string + Payload string } func (msg *IssueEventDeprecated) Encode() []byte { @@ -1594,7 +1593,7 @@ func (msg *IssueEventDeprecated) TypeID() int { type TechnicalInfo struct { message - Type string + Type string Value string } @@ -1617,7 +1616,7 @@ func (msg *TechnicalInfo) TypeID() int { type CustomIssue struct { message - Name string + Name string Payload string } @@ -1661,9 +1660,9 @@ func (msg *AssetCache) TypeID() int { type CSSInsertRuleURLBased struct { message - ID uint64 - Rule string - Index uint64 + ID uint64 + Rule string + Index uint64 BaseURL string } @@ -1688,10 +1687,10 @@ func (msg *CSSInsertRuleURLBased) TypeID() int { type MouseClick struct { message - ID uint64 + ID uint64 HesitationTime uint64 - Label string - Selector string + Label string + Selector string } func (msg *MouseClick) Encode() []byte { @@ -1716,7 +1715,7 @@ func (msg *MouseClick) TypeID() int { type CreateIFrameDocument struct { message FrameID uint64 - ID uint64 + ID uint64 } func (msg *CreateIFrameDocument) Encode() []byte { @@ -1739,7 +1738,7 @@ func (msg *CreateIFrameDocument) TypeID() int { type AdoptedSSReplaceURLBased struct { message SheetID uint64 - Text string + Text string BaseURL string } @@ -1764,7 +1763,7 @@ func (msg *AdoptedSSReplaceURLBased) TypeID() int { type AdoptedSSReplace struct { message SheetID uint64 - Text string + Text string } func (msg *AdoptedSSReplace) Encode() []byte { @@ -1787,8 +1786,8 @@ func (msg *AdoptedSSReplace) TypeID() int { type AdoptedSSInsertRuleURLBased struct { message SheetID uint64 - Rule string - Index uint64 + Rule string + Index uint64 BaseURL string } @@ -1814,8 +1813,8 @@ func (msg *AdoptedSSInsertRuleURLBased) TypeID() int { type AdoptedSSInsertRule struct { message SheetID uint64 - Rule string - Index uint64 + Rule string + Index uint64 } func (msg *AdoptedSSInsertRule) Encode() []byte { @@ -1839,7 +1838,7 @@ func (msg *AdoptedSSInsertRule) TypeID() int { type AdoptedSSDeleteRule struct { message SheetID uint64 - Index uint64 + Index uint64 } func (msg *AdoptedSSDeleteRule) Encode() []byte { @@ -1862,7 +1861,7 @@ func (msg *AdoptedSSDeleteRule) TypeID() int { type AdoptedSSAddOwner struct { message SheetID uint64 - ID uint64 + ID uint64 } func (msg *AdoptedSSAddOwner) Encode() []byte { @@ -1885,7 +1884,7 @@ func (msg *AdoptedSSAddOwner) TypeID() int { type AdoptedSSRemoveOwner struct { message SheetID uint64 - ID uint64 + ID uint64 } func (msg *AdoptedSSRemoveOwner) Encode() []byte { @@ -1907,9 +1906,9 @@ func (msg *AdoptedSSRemoveOwner) TypeID() int { type JSException struct { message - Name string - Message string - Payload string + Name string + Message string + Payload string Metadata string } @@ -1935,7 +1934,7 @@ func (msg *JSException) TypeID() int { type Zustand struct { message Mutation string - State string + State string } func (msg *Zustand) Encode() []byte { @@ -1957,9 +1956,9 @@ func (msg *Zustand) TypeID() int { type BatchMeta struct { message - PageNo uint64 + PageNo uint64 FirstIndex uint64 - Timestamp int64 + Timestamp int64 } func (msg *BatchMeta) Encode() []byte { @@ -1982,11 +1981,11 @@ func (msg *BatchMeta) TypeID() int { type BatchMetadata struct { message - Version uint64 - PageNo uint64 + Version uint64 + PageNo uint64 FirstIndex uint64 - Timestamp int64 - Location string + Timestamp int64 + Location string } func (msg *BatchMetadata) Encode() []byte { @@ -2011,7 +2010,7 @@ func (msg *BatchMetadata) TypeID() int { type PartitionedMessage struct { message - PartNo uint64 + PartNo uint64 PartTotal uint64 } @@ -2034,14 +2033,14 @@ func (msg *PartitionedMessage) TypeID() int { type NetworkRequest struct { message - Type string - Method string - URL string - Request string - Response string - Status uint64 - Timestamp uint64 - Duration uint64 + Type string + Method string + URL string + Request string + Response string + Status uint64 + Timestamp uint64 + Duration uint64 TransferredBodySize uint64 } @@ -2069,14 +2068,45 @@ func (msg *NetworkRequest) TypeID() int { return 83 } +type WSChannel struct { + message + ChType string + ChannelName string + Data string + Timestamp uint64 + Dir string + MessageType string +} + +func (msg *WSChannel) Encode() []byte { + buf := make([]byte, 61+len(msg.ChType)+len(msg.ChannelName)+len(msg.Data)+len(msg.Dir)+len(msg.MessageType)) + buf[0] = 84 + p := 1 + p = WriteString(msg.ChType, buf, p) + p = WriteString(msg.ChannelName, buf, p) + p = WriteString(msg.Data, buf, p) + p = WriteUint(msg.Timestamp, buf, p) + p = WriteString(msg.Dir, buf, p) + p = WriteString(msg.MessageType, buf, p) + return buf[:p] +} + +func (msg *WSChannel) Decode() Message { + return msg +} + +func (msg *WSChannel) TypeID() int { + return 84 +} + type InputChange struct { message - ID uint64 - Value string - ValueMasked bool - Label string + ID uint64 + Value string + ValueMasked bool + Label string HesitationTime int64 - InputDuration int64 + InputDuration int64 } func (msg *InputChange) Encode() []byte { @@ -2103,8 +2133,8 @@ func (msg *InputChange) TypeID() int { type SelectionChange struct { message SelectionStart uint64 - SelectionEnd uint64 - Selection string + SelectionEnd uint64 + Selection string } func (msg *SelectionChange) Encode() []byte { @@ -2169,16 +2199,16 @@ func (msg *UnbindNodes) TypeID() int { type ResourceTiming struct { message - Timestamp uint64 - Duration uint64 - TTFB uint64 - HeaderSize uint64 + Timestamp uint64 + Duration uint64 + TTFB uint64 + HeaderSize uint64 EncodedBodySize uint64 DecodedBodySize uint64 - URL string - Initiator string + URL string + Initiator string TransferredSize uint64 - Cached bool + Cached bool } func (msg *ResourceTiming) Encode() []byte { @@ -2273,13 +2303,13 @@ func (msg *CanvasNode) TypeID() int { type IssueEvent struct { message - MessageID uint64 - Timestamp uint64 - Type string + MessageID uint64 + Timestamp uint64 + Type string ContextString string - Context string - Payload string - URL string + Context string + Payload string + URL string } func (msg *IssueEvent) Encode() []byte { @@ -2306,7 +2336,7 @@ func (msg *IssueEvent) TypeID() int { type SessionEnd struct { message - Timestamp uint64 + Timestamp uint64 EncryptionKey string } @@ -2352,16 +2382,16 @@ func (msg *SessionSearch) TypeID() int { type IOSSessionStart struct { message - Timestamp uint64 - ProjectID uint64 + Timestamp uint64 + ProjectID uint64 TrackerVersion string - RevID string - UserUUID string - UserOS string - UserOSVersion string - UserDevice string + RevID string + UserUUID string + UserOS string + UserOSVersion string + UserDevice string UserDeviceType string - UserCountry string + UserCountry string } func (msg *IOSSessionStart) Encode() []byte { @@ -2413,9 +2443,9 @@ func (msg *IOSSessionEnd) TypeID() int { type IOSMetadata struct { message Timestamp uint64 - Length uint64 - Key string - Value string + Length uint64 + Key string + Value string } func (msg *IOSMetadata) Encode() []byte { @@ -2440,9 +2470,9 @@ func (msg *IOSMetadata) TypeID() int { type IOSEvent struct { message Timestamp uint64 - Length uint64 - Name string - Payload string + Length uint64 + Name string + Payload string } func (msg *IOSEvent) Encode() []byte { @@ -2467,8 +2497,8 @@ func (msg *IOSEvent) TypeID() int { type IOSUserID struct { message Timestamp uint64 - Length uint64 - ID string + Length uint64 + ID string } func (msg *IOSUserID) Encode() []byte { @@ -2492,8 +2522,8 @@ func (msg *IOSUserID) TypeID() int { type IOSUserAnonymousID struct { message Timestamp uint64 - Length uint64 - ID string + Length uint64 + ID string } func (msg *IOSUserAnonymousID) Encode() []byte { @@ -2517,11 +2547,11 @@ func (msg *IOSUserAnonymousID) TypeID() int { type IOSScreenChanges struct { message Timestamp uint64 - Length uint64 - X uint64 - Y uint64 - Width uint64 - Height uint64 + Length uint64 + X uint64 + Y uint64 + Width uint64 + Height uint64 } func (msg *IOSScreenChanges) Encode() []byte { @@ -2547,10 +2577,10 @@ func (msg *IOSScreenChanges) TypeID() int { type IOSCrash struct { message - Timestamp uint64 - Length uint64 - Name string - Reason string + Timestamp uint64 + Length uint64 + Name string + Reason string Stacktrace string } @@ -2576,11 +2606,11 @@ func (msg *IOSCrash) TypeID() int { type IOSViewComponentEvent struct { message - Timestamp uint64 - Length uint64 + Timestamp uint64 + Length uint64 ScreenName string - ViewName string - Visible bool + ViewName string + Visible bool } func (msg *IOSViewComponentEvent) Encode() []byte { @@ -2606,10 +2636,10 @@ func (msg *IOSViewComponentEvent) TypeID() int { type IOSClickEvent struct { message Timestamp uint64 - Length uint64 - Label string - X uint64 - Y uint64 + Length uint64 + Label string + X uint64 + Y uint64 } func (msg *IOSClickEvent) Encode() []byte { @@ -2634,11 +2664,11 @@ func (msg *IOSClickEvent) TypeID() int { type IOSInputEvent struct { message - Timestamp uint64 - Length uint64 - Value string + Timestamp uint64 + Length uint64 + Value string ValueMasked bool - Label string + Label string } func (msg *IOSInputEvent) Encode() []byte { @@ -2664,9 +2694,9 @@ func (msg *IOSInputEvent) TypeID() int { type IOSPerformanceEvent struct { message Timestamp uint64 - Length uint64 - Name string - Value uint64 + Length uint64 + Name string + Value uint64 } func (msg *IOSPerformanceEvent) Encode() []byte { @@ -2691,9 +2721,9 @@ func (msg *IOSPerformanceEvent) TypeID() int { type IOSLog struct { message Timestamp uint64 - Length uint64 - Severity string - Content string + Length uint64 + Severity string + Content string } func (msg *IOSLog) Encode() []byte { @@ -2718,8 +2748,8 @@ func (msg *IOSLog) TypeID() int { type IOSInternalError struct { message Timestamp uint64 - Length uint64 - Content string + Length uint64 + Content string } func (msg *IOSInternalError) Encode() []byte { @@ -2743,14 +2773,14 @@ func (msg *IOSInternalError) TypeID() int { type IOSNetworkCall struct { message Timestamp uint64 - Length uint64 - Type string - Method string - URL string - Request string - Response string - Status uint64 - Duration uint64 + Length uint64 + Type string + Method string + URL string + Request string + Response string + Status uint64 + Duration uint64 } func (msg *IOSNetworkCall) Encode() []byte { @@ -2780,10 +2810,10 @@ func (msg *IOSNetworkCall) TypeID() int { type IOSSwipeEvent struct { message Timestamp uint64 - Length uint64 - Label string - X uint64 - Y uint64 + Length uint64 + Label string + X uint64 + Y uint64 Direction string } @@ -2810,8 +2840,8 @@ func (msg *IOSSwipeEvent) TypeID() int { type IOSBatchMeta struct { message - Timestamp uint64 - Length uint64 + Timestamp uint64 + Length uint64 FirstIndex uint64 } @@ -2836,19 +2866,19 @@ func (msg *IOSBatchMeta) TypeID() int { type IOSPerformanceAggregated struct { message TimestampStart uint64 - TimestampEnd uint64 - MinFPS uint64 - AvgFPS uint64 - MaxFPS uint64 - MinCPU uint64 - AvgCPU uint64 - MaxCPU uint64 - MinMemory uint64 - AvgMemory uint64 - MaxMemory uint64 - MinBattery uint64 - AvgBattery uint64 - MaxBattery uint64 + TimestampEnd uint64 + MinFPS uint64 + AvgFPS uint64 + MaxFPS uint64 + MinCPU uint64 + AvgCPU uint64 + MaxCPU uint64 + MinMemory uint64 + AvgMemory uint64 + MaxMemory uint64 + MinBattery uint64 + AvgBattery uint64 + MaxBattery uint64 } func (msg *IOSPerformanceAggregated) Encode() []byte { @@ -2882,11 +2912,11 @@ func (msg *IOSPerformanceAggregated) TypeID() int { type IOSIssueEvent struct { message - Timestamp uint64 - Type string + Timestamp uint64 + Type string ContextString string - Context string - Payload string + Context string + Payload string } func (msg *IOSIssueEvent) Encode() []byte { @@ -2908,4 +2938,3 @@ func (msg *IOSIssueEvent) Decode() Message { func (msg *IOSIssueEvent) TypeID() int { return 111 } - diff --git a/backend/pkg/messages/read-message.go b/backend/pkg/messages/read-message.go index 910922b8a..f71869977 100644 --- a/backend/pkg/messages/read-message.go +++ b/backend/pkg/messages/read-message.go @@ -1254,6 +1254,30 @@ func DecodeNetworkRequest(reader BytesReader) (Message, error) { return msg, err } +func DecodeWSChannel(reader BytesReader) (Message, error) { + var err error = nil + msg := &WSChannel{} + if msg.ChType, err = reader.ReadString(); err != nil { + return nil, err + } + if msg.ChannelName, err = reader.ReadString(); err != nil { + return nil, err + } + if msg.Data, err = reader.ReadString(); err != nil { + return nil, err + } + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } + if msg.Dir, err = reader.ReadString(); err != nil { + return nil, err + } + if msg.MessageType, err = reader.ReadString(); err != nil { + return nil, err + } + return msg, err +} + func DecodeInputChange(reader BytesReader) (Message, error) { var err error = nil msg := &InputChange{} @@ -1991,6 +2015,8 @@ func ReadMessage(t uint64, reader BytesReader) (Message, error) { return DecodePartitionedMessage(reader) case 83: return DecodeNetworkRequest(reader) + case 84: + return DecodeWSChannel(reader) case 112: return DecodeInputChange(reader) case 113: diff --git a/ee/connectors/msgcodec/messages.py b/ee/connectors/msgcodec/messages.py index d5348a9e5..494af3793 100644 --- a/ee/connectors/msgcodec/messages.py +++ b/ee/connectors/msgcodec/messages.py @@ -723,6 +723,18 @@ class NetworkRequest(Message): self.transferred_body_size = transferred_body_size +class WSChannel(Message): + __id__ = 84 + + def __init__(self, ch_type, channel_name, data, timestamp, dir, message_type): + self.ch_type = ch_type + self.channel_name = channel_name + self.data = data + self.timestamp = timestamp + self.dir = dir + self.message_type = message_type + + class InputChange(Message): __id__ = 112 diff --git a/ee/connectors/msgcodec/messages.pyx b/ee/connectors/msgcodec/messages.pyx index af0816b95..7e8df67fc 100644 --- a/ee/connectors/msgcodec/messages.pyx +++ b/ee/connectors/msgcodec/messages.pyx @@ -1069,6 +1069,25 @@ cdef class NetworkRequest(PyMessage): self.transferred_body_size = transferred_body_size +cdef class WSChannel(PyMessage): + cdef public int __id__ + cdef public str ch_type + cdef public str channel_name + cdef public str data + cdef public unsigned long timestamp + cdef public str dir + cdef public str message_type + + def __init__(self, str ch_type, str channel_name, str data, unsigned long timestamp, str dir, str message_type): + self.__id__ = 84 + self.ch_type = ch_type + self.channel_name = channel_name + self.data = data + self.timestamp = timestamp + self.dir = dir + self.message_type = message_type + + cdef class InputChange(PyMessage): cdef public int __id__ cdef public unsigned long id diff --git a/ee/connectors/msgcodec/msgcodec.py b/ee/connectors/msgcodec/msgcodec.py index 4aba0f775..fe3a34324 100644 --- a/ee/connectors/msgcodec/msgcodec.py +++ b/ee/connectors/msgcodec/msgcodec.py @@ -660,6 +660,16 @@ class MessageCodec(Codec): transferred_body_size=self.read_uint(reader) ) + if message_id == 84: + return WSChannel( + ch_type=self.read_string(reader), + channel_name=self.read_string(reader), + data=self.read_string(reader), + timestamp=self.read_uint(reader), + dir=self.read_string(reader), + message_type=self.read_string(reader) + ) + if message_id == 112: return InputChange( id=self.read_uint(reader), diff --git a/ee/connectors/msgcodec/msgcodec.pyx b/ee/connectors/msgcodec/msgcodec.pyx index c918be6ea..ff097f7d8 100644 --- a/ee/connectors/msgcodec/msgcodec.pyx +++ b/ee/connectors/msgcodec/msgcodec.pyx @@ -758,6 +758,16 @@ cdef class MessageCodec: transferred_body_size=self.read_uint(reader) ) + if message_id == 84: + return WSChannel( + ch_type=self.read_string(reader), + channel_name=self.read_string(reader), + data=self.read_string(reader), + timestamp=self.read_uint(reader), + dir=self.read_string(reader), + message_type=self.read_string(reader) + ) + if message_id == 112: return InputChange( id=self.read_uint(reader), diff --git a/frontend/app/components/Session_/Player/Controls/EventsList.tsx b/frontend/app/components/Session_/Player/Controls/EventsList.tsx index 860b6d0c5..7b8de8d6c 100644 --- a/frontend/app/components/Session_/Player/Controls/EventsList.tsx +++ b/frontend/app/components/Session_/Player/Controls/EventsList.tsx @@ -9,14 +9,15 @@ function EventsList({ scale }: { scale: number }) { const { tabStates, eventCount } = store.get(); const events = React.useMemo(() => { - return Object.values(tabStates)[0]?.eventList || []; + return Object.values(tabStates)[0]?.eventList.filter(e => e.time) || []; }, [eventCount]); + return ( <> {events.map((e) => (
@@ -35,7 +36,7 @@ function MobileEventsList({ scale }: { scale: number }) { {events.map((e) => (
diff --git a/frontend/app/components/shared/DevTools/NetworkPanel/NetworkPanel.tsx b/frontend/app/components/shared/DevTools/NetworkPanel/NetworkPanel.tsx index c8f3fc060..29e8f9fb8 100644 --- a/frontend/app/components/shared/DevTools/NetworkPanel/NetworkPanel.tsx +++ b/frontend/app/components/shared/DevTools/NetworkPanel/NetworkPanel.tsx @@ -18,6 +18,7 @@ import BottomBlock from '../BottomBlock'; import InfoLine from '../BottomBlock/InfoLine'; import useAutoscroll, { getLastItemTime } from '../useAutoscroll'; import { useRegExListFilterMemo, useTabListFilterMemo } from '../useListFilter'; +import WSModal from './WSModal' const INDEX_KEY = 'network'; @@ -28,6 +29,7 @@ const CSS = 'css'; const IMG = 'img'; const MEDIA = 'media'; const OTHER = 'other'; +const WS = 'websocket'; const TYPE_TO_TAB = { [ResourceType.XHR]: XHR, @@ -36,10 +38,11 @@ const TYPE_TO_TAB = { [ResourceType.CSS]: CSS, [ResourceType.IMG]: IMG, [ResourceType.MEDIA]: MEDIA, + [ResourceType.WS]: WS, [ResourceType.OTHER]: OTHER, }; -const TAP_KEYS = [ALL, XHR, JS, CSS, IMG, MEDIA, OTHER] as const; +const TAP_KEYS = [ALL, XHR, JS, CSS, IMG, MEDIA, OTHER, WS] as const; const TABS = TAP_KEYS.map((tab) => ({ text: tab === 'xhr' ? 'Fetch/XHR' : tab, key: tab, @@ -156,6 +159,8 @@ function NetworkPanelCont({ startedAt, panelHeight }: { startedAt: number; panel resourceList = [], fetchListNow = [], resourceListNow = [], + websocketList = [], + websocketListNow = [], } = tabStates[currentTab]; return ( @@ -170,11 +175,19 @@ function NetworkPanelCont({ startedAt, panelHeight }: { startedAt: number; panel resourceListNow={resourceListNow} player={player} startedAt={startedAt} + websocketList={websocketList as WSMessage[]} + websocketListNow={websocketListNow as WSMessage[]} /> ); } -function MobileNetworkPanelCont({ startedAt, panelHeight }: { startedAt: number, panelHeight: number }) { +function MobileNetworkPanelCont({ + startedAt, + panelHeight, +}: { + startedAt: number; + panelHeight: number; +}) { const { player, store } = React.useContext(MobilePlayerContext); const domContentLoadedTime = undefined; @@ -185,6 +198,8 @@ function MobileNetworkPanelCont({ startedAt, panelHeight }: { startedAt: number, resourceList = [], fetchListNow = [], resourceListNow = [], + websocketList = [], + websocketListNow = [], } = store.get(); return ( @@ -200,10 +215,22 @@ function MobileNetworkPanelCont({ startedAt, panelHeight }: { startedAt: number, resourceListNow={resourceListNow} player={player} startedAt={startedAt} + // @ts-ignore + websocketList={websocketList} + // @ts-ignore + websocketListNow={websocketListNow} /> ); } +type WSMessage = Timed & { + channelName: string; + data: string; + timestamp: number; + dir: 'up' | 'down'; + messageType: string; +} + interface Props { domContentLoadedTime?: { time: number; @@ -218,6 +245,8 @@ interface Props { resourceList: Timed[]; fetchListNow: Timed[]; resourceListNow: Timed[]; + websocketList: Array; + websocketListNow: Array; player: WebPlayer | MobilePlayer; startedAt: number; isMobile?: boolean; @@ -237,6 +266,7 @@ const NetworkPanelComp = observer( startedAt, isMobile, panelHeight, + websocketList, }: Props) => { const { showModal } = useModal(); const [sortBy, setSortBy] = useState('time'); @@ -251,6 +281,14 @@ const NetworkPanelComp = observer( const activeTab = devTools[INDEX_KEY].activeTab; const activeIndex = devTools[INDEX_KEY].index; + const socketList = useMemo( + () => + websocketList.filter( + (ws, i, arr) => arr.findIndex((it) => it.channelName === ws.channelName) === i + ), + [websocketList] + ); + const list = useMemo( () => // TODO: better merge (with body size info) - do it in player @@ -283,8 +321,20 @@ const NetworkPanelComp = observer( }) ) .concat(fetchList) + .concat( + socketList.map((ws) => ({ + ...ws, + type: 'websocket', + method: 'ws', + url: ws.channelName, + name: ws.channelName, + status: '101', + duration: 0, + transferredBodySize: 0, + })) + ) .sort((a, b) => a.time - b.time), - [resourceList.length, fetchList.length] + [resourceList.length, fetchList.length, socketList] ); let filteredList = useMemo(() => { @@ -354,6 +404,18 @@ const NetworkPanelComp = observer( }, [domContentLoadedTime, loadTime]); const showDetailsModal = (item: any) => { + if (item.type === 'websocket') { + const socketMsgList = websocketList.filter((ws) => ws.channelName === item.channelName); + console.log(socketMsgList) + + return showModal( + , { + right: true, width: 700, + } + ) + } setIsDetailsModalActive(true); showModal( ; +} + +function WSModal({ socketMsgList }: Props) { + return ( +
+
+
Data
+
Length
+
Time
+
+
+ {socketMsgList.map((msg) => ( + + ))} +
+
+ ); +} + +function MsgDirection({ dir }: { dir: 'up' | 'down' }) { + return ( + + ); +} + +function Row({ msg }) { + const [isOpen, setIsOpen] = React.useState(false); + + return ( + <> +
100 ? 'hover:bg-active-blue cursor-pointer' : '' + }`} + onClick={() => (msg.data.length > 100 ? setIsOpen(!isOpen) : null)} + style={{ width: 700 }} + > +
+ + {msg.messageType} + + {msg.data} + + {msg.data.length > 100 ? ( +
+ {isOpen ? '-' : '+'} +
+ ) : null} +
+
{msg.data.length}
+
{durationFromMs(msg.time, true)}
+
+ {isOpen ? ( +
+
+ {msg.data} +
+
+ ) : null} + + ); +} + +export default WSModal; diff --git a/frontend/app/date.ts b/frontend/app/date.ts index 38a240cdf..a87d5785e 100644 --- a/frontend/app/date.ts +++ b/frontend/app/date.ts @@ -44,10 +44,10 @@ export function durationFromMsFormatted(ms: number): string { return durationFormatted(Duration.fromMillis(ms || 0)); } -export function durationFromMs(ms: number): string { +export function durationFromMs(ms: number, isFull?: boolean): string { const dur = Duration.fromMillis(ms) - return dur.toFormat('hh:mm:ss') + return dur.toFormat(`hh:mm:ss${ isFull ? '.SSS' : '' }`) } export const durationFormattedFull = (duration: Duration): string => { diff --git a/frontend/app/player/mobile/IOSLists.tsx b/frontend/app/player/mobile/IOSLists.ts similarity index 96% rename from frontend/app/player/mobile/IOSLists.tsx rename to frontend/app/player/mobile/IOSLists.ts index 04c599633..fe983f3e2 100644 --- a/frontend/app/player/mobile/IOSLists.tsx +++ b/frontend/app/player/mobile/IOSLists.ts @@ -10,7 +10,7 @@ const SIMPLE_LIST_NAMES = [ "frustrations", "performance" ] as const -const MARKED_LIST_NAMES = [ "log", "resource", "fetch", "stack" ] as const +const MARKED_LIST_NAMES = [ "log", "resource", "fetch", "stack", "websocket" ] as const const LIST_NAMES = [...SIMPLE_LIST_NAMES, ...MARKED_LIST_NAMES ] as const diff --git a/frontend/app/player/mobile/IOSMessageManager.ts b/frontend/app/player/mobile/IOSMessageManager.ts index fe41a6f06..def6025fd 100644 --- a/frontend/app/player/mobile/IOSMessageManager.ts +++ b/frontend/app/player/mobile/IOSMessageManager.ts @@ -226,6 +226,9 @@ export default class IOSMessageManager implements IMessageManager { case MType.IosNetworkCall: this.lists.lists.fetch.insert(getResourceFromNetworkRequest(msg, this.sessionStart)) break; + case MType.WsChannel: + this.lists.lists.websocket.insert(msg) + break; case MType.IosEvent: // @ts-ignore this.lists.lists.event.insert({...msg, source: 'openreplay'}); diff --git a/frontend/app/player/web/Lists.ts b/frontend/app/player/web/Lists.ts index 113e58036..73021c4ac 100644 --- a/frontend/app/player/web/Lists.ts +++ b/frontend/app/player/web/Lists.ts @@ -4,7 +4,7 @@ import type { Timed } from '../common/types'; const SIMPLE_LIST_NAMES = [ "event", "redux", "mobx", "vuex", "zustand", "ngrx", "graphql", "exceptions", "profiles", "frustrations"] as const -const MARKED_LIST_NAMES = [ "log", "resource", "fetch", "stack" ] as const +const MARKED_LIST_NAMES = [ "log", "resource", "fetch", "stack", "websocket" ] as const //const entityNamesSimple = [ "event", "profile" ]; const LIST_NAMES = [...SIMPLE_LIST_NAMES, ...MARKED_LIST_NAMES ] as const diff --git a/frontend/app/player/web/TabManager.ts b/frontend/app/player/web/TabManager.ts index 2dc7c3554..2534dad01 100644 --- a/frontend/app/player/web/TabManager.ts +++ b/frontend/app/player/web/TabManager.ts @@ -1,12 +1,21 @@ -import type { Store } from "Player"; -import { getResourceFromNetworkRequest, getResourceFromResourceTiming, Log, ResourceType } from "Player"; -import ListWalker from "Player/common/ListWalker"; -import Lists, { INITIAL_STATE as LISTS_INITIAL_STATE, InitialLists, State as ListsState } from "Player/web/Lists"; -import CanvasManager from "Player/web/managers/CanvasManager"; -import { VElement } from "Player/web/managers/DOM/VirtualDOM"; -import PagesManager from "Player/web/managers/PagesManager"; -import PerformanceTrackManager from "Player/web/managers/PerformanceTrackManager"; -import WindowNodeCounter from "Player/web/managers/WindowNodeCounter"; +import type { Store } from 'Player'; +import { + getResourceFromNetworkRequest, + getResourceFromResourceTiming, + Log, + ResourceType, +} from 'Player'; +import ListWalker from 'Player/common/ListWalker'; +import Lists, { + INITIAL_STATE as LISTS_INITIAL_STATE, + InitialLists, + State as ListsState, +} from 'Player/web/Lists'; +import CanvasManager from 'Player/web/managers/CanvasManager'; +import { VElement } from 'Player/web/managers/DOM/VirtualDOM'; +import PagesManager from 'Player/web/managers/PagesManager'; +import PerformanceTrackManager from 'Player/web/managers/PerformanceTrackManager'; +import WindowNodeCounter from 'Player/web/managers/WindowNodeCounter'; import { CanvasNode, ConnectionInformation, @@ -15,22 +24,22 @@ import { ResourceTiming, SetPageLocation, SetViewportScroll, - SetViewportSize -} from "Player/web/messages"; -import { isDOMType } from "Player/web/messages/filters.gen"; -import Screen from "Player/web/Screen/Screen"; + SetViewportSize, +} from 'Player/web/messages'; +import { isDOMType } from 'Player/web/messages/filters.gen'; +import Screen from 'Player/web/Screen/Screen'; // @ts-ignore -import { Decoder } from "syncod"; -import { TYPES as EVENT_TYPES } from "Types/session/event"; -import type { PerformanceChartPoint } from "./managers/PerformanceTrackManager"; +import { Decoder } from 'syncod'; +import { TYPES as EVENT_TYPES } from 'Types/session/event'; +import type { PerformanceChartPoint } from './managers/PerformanceTrackManager'; export interface TabState extends ListsState { - performanceAvailability?: PerformanceTrackManager['availability'] - performanceChartData: PerformanceChartPoint[], - performanceChartTime: PerformanceChartPoint[] - cssLoading: boolean - location: string - urlsList: SetPageLocation[] + performanceAvailability?: PerformanceTrackManager['availability']; + performanceChartData: PerformanceChartPoint[]; + performanceChartTime: PerformanceChartPoint[]; + cssLoading: boolean; + location: string; + urlsList: SetPageLocation[]; } /** @@ -46,10 +55,10 @@ export default class TabSessionManager { cssLoading: false, location: '', urlsList: [], - } + }; public locationManager: ListWalker = new ListWalker(); - private locationEventManager: ListWalker/**/ = new ListWalker(); + private locationEventManager: ListWalker /**/ = new ListWalker(); private loadedLocationManager: ListWalker = new ListWalker(); private connectionInfoManger: ListWalker = new ListWalker(); private performanceTrackManager: PerformanceTrackManager = new PerformanceTrackManager(); @@ -61,8 +70,10 @@ export default class TabSessionManager { public readonly decoder = new Decoder(); private lists: Lists; - private navigationStartOffset = 0 - private canvasManagers: { [key: string]: { manager: CanvasManager, start: number, running: boolean } } = {} + private navigationStartOffset = 0; + private canvasManagers: { + [key: string]: { manager: CanvasManager; start: number; running: boolean }; + } = {}; private canvasReplayWalker: ListWalker = new ListWalker(); constructor( @@ -70,36 +81,37 @@ export default class TabSessionManager { private readonly state: Store<{ tabStates: { [tabId: string]: TabState } }>, private readonly screen: Screen, private readonly id: string, - private readonly setSize: ({ height, width }: { height: number, width: number }) => void, + private readonly setSize: ({ height, width }: { height: number; width: number }) => void, private readonly sessionStart: number, - initialLists?: Partial, + initialLists?: Partial ) { - this.pagesManager = new PagesManager(screen, this.session.isMobile, this.setCSSLoading) - this.lists = new Lists(initialLists) - initialLists?.event?.forEach((e: Record) => { // TODO: to one of "Movable" module + this.pagesManager = new PagesManager(screen, this.session.isMobile, this.setCSSLoading); + this.lists = new Lists(initialLists); + initialLists?.event?.forEach((e: Record) => { + // TODO: to one of "Movable" module if (e.type === EVENT_TYPES.LOCATION) { this.locationEventManager.append(e); } - }) + }); } public getNode = (id: number) => { - return this.pagesManager.getNode(id) - } + return this.pagesManager.getNode(id); + }; public updateLists(lists: Partial) { Object.keys(lists).forEach((key: 'event' | 'stack' | 'exceptions') => { - const currentList = this.lists.lists[key] - lists[key]!.forEach(item => currentList.insert(item)) - }) + const currentList = this.lists.lists[key]; + lists[key]!.forEach((item) => currentList.insert(item)); + }); lists?.event?.forEach((e: Record) => { if (e.type === EVENT_TYPES.LOCATION) { this.locationEventManager.append(e); } - }) - const eventCount = lists?.event?.length || 0 + }); + const eventCount = lists?.event?.length || 0; - const currentState = this.state.get() + const currentState = this.state.get(); this.state.update({ // @ts-ignore comes from parent state eventCount: currentState.eventCount + eventCount, @@ -108,9 +120,9 @@ export default class TabSessionManager { [this.id]: { ...currentState.tabStates[this.id], ...this.lists.getFullListsState(), - } - } - }) + }, + }, + }); } updateLocalState(state: Partial) { @@ -119,22 +131,22 @@ export default class TabSessionManager { ...this.state.get().tabStates, [this.id]: { ...this.state.get().tabStates[this.id], - ...state - } - } - }) + ...state, + }, + }, + }); } private setCSSLoading = (cssLoading: boolean) => { - this.screen.displayFrame(!cssLoading) + this.screen.displayFrame(!cssLoading); this.updateLocalState({ - cssLoading - }) + cssLoading, + }); this.state.update({ - // @ts-ignore - ready: !this.state.get().messagesLoading && !cssLoading - }) - } + // @ts-ignore + ready: !this.state.get().messagesLoading && !cssLoading, + }); + }; public resetMessageManagers() { this.locationEventManager = new ListWalker(); @@ -144,12 +156,11 @@ export default class TabSessionManager { this.scrollManager = new ListWalker(); this.resizeManager = new ListWalker(); - this.performanceTrackManager = new PerformanceTrackManager() + this.performanceTrackManager = new PerformanceTrackManager(); this.windowNodeCounter = new WindowNodeCounter(); - this.pagesManager = new PagesManager(this.screen, this.session.isMobile, this.setCSSLoading) + this.pagesManager = new PagesManager(this.screen, this.session.isMobile, this.setCSSLoading); } - distributeMessage(msg: Message): void { switch (msg.tp) { case MType.CanvasNode: @@ -166,7 +177,6 @@ export default class TabSessionManager { ); this.canvasManagers[managerId] = { manager, start: msg.timestamp, running: false }; this.canvasReplayWalker.append(msg); - } break; case MType.SetPageLocation: @@ -185,7 +195,7 @@ export default class TabSessionManager { this.performanceTrackManager.append(msg); break; case MType.SetPageVisibility: - this.performanceTrackManager.handleVisibility(msg) + this.performanceTrackManager.handleVisibility(msg); break; case MType.ConnectionInformation: this.connectionInfoManger.append(msg); @@ -199,18 +209,23 @@ export default class TabSessionManager { this.lists.lists.log.append( // @ts-ignore : TODO: enums in the message schema Log(msg) - ) + ); break; case MType.ResourceTimingDeprecated: case MType.ResourceTiming: // TODO: merge `resource` and `fetch` lists into one here instead of UI if (msg.initiator !== ResourceType.FETCH && msg.initiator !== ResourceType.XHR) { - this.lists.lists.resource.insert(getResourceFromResourceTiming(msg as ResourceTiming, this.sessionStart)) + this.lists.lists.resource.insert( + getResourceFromResourceTiming(msg as ResourceTiming, this.sessionStart) + ); } break; case MType.Fetch: case MType.NetworkRequest: - this.lists.lists.fetch.insert(getResourceFromNetworkRequest(msg, this.sessionStart)) + this.lists.lists.fetch.insert(getResourceFromNetworkRequest(msg, this.sessionStart)); + break; + case MType.WsChannel: + this.lists.lists.websocket.insert(msg); break; case MType.Redux: this.lists.lists.redux.append(msg); @@ -222,8 +237,8 @@ export default class TabSessionManager { this.lists.lists.vuex.append(msg); break; case MType.Zustand: - this.lists.lists.zustand.append(msg) - break + this.lists.lists.zustand.append(msg); + break; case MType.MobX: this.lists.lists.mobx.append(msg); break; @@ -254,8 +269,8 @@ export default class TabSessionManager { this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count); break; } - this.performanceTrackManager.addNodeCountPointIfNeed(msg.time) - isDOMType(msg.tp) && this.pagesManager.appendMessage(msg) + this.performanceTrackManager.addNodeCountPointIfNeed(msg.time); + isDOMType(msg.tp) && this.pagesManager.appendMessage(msg); break; } } @@ -274,13 +289,13 @@ export default class TabSessionManager { stateToUpdate.domContentLoadedTime = { time: llEvent.domContentLoadedTime + this.navigationStartOffset, //TODO: predefined list of load event for the network tab (merge events & SetPageLocation: add navigationStart to db) value: llEvent.domContentLoadedTime, - } + }; } if (llEvent.loadTime != null) { stateToUpdate.loadTime = { time: llEvent.loadTime + this.navigationStartOffset, value: llEvent.loadTime, - } + }; } if (llEvent.domBuildingTime != null) { stateToUpdate.domBuildingTime = llEvent.domBuildingTime; @@ -290,7 +305,7 @@ export default class TabSessionManager { const lastLocationMsg = this.locationManager.moveGetLast(t, index); if (!!lastLocationMsg) { // @ts-ignore comes from parent state - this.state.update({ location: lastLocationMsg.url }) + this.state.update({ location: lastLocationMsg.url }); } // ConnectionInformation message is not used at this moment // const lastConnectionInfoMsg = this.connectionInfoManger.moveGetLast(t, index); @@ -303,40 +318,42 @@ export default class TabSessionManager { stateToUpdate.performanceChartTime = lastPerformanceTrackMessage.time; } - Object.assign(stateToUpdate, this.lists.moveGetState(t)) + Object.assign(stateToUpdate, this.lists.moveGetState(t)); Object.keys(stateToUpdate).length > 0 && this.updateLocalState(stateToUpdate); /* Sequence of the managers is important here */ // Preparing the size of "screen" const lastResize = this.resizeManager.moveGetLast(t, index); if (!!lastResize) { - this.setSize(lastResize) + this.setSize(lastResize); } this.pagesManager.moveReady(t).then(() => { const lastScroll = this.scrollManager.moveGetLast(t, index); if (!!lastScroll && this.screen.window) { this.screen.window.scrollTo(lastScroll.x, lastScroll.y); } - const canvasMsg = this.canvasReplayWalker.moveGetLast(t) + const canvasMsg = this.canvasReplayWalker.moveGetLast(t); if (canvasMsg) { this.canvasManagers[`${canvasMsg.timestamp}_${canvasMsg.nodeId}`].manager.startVideo(); this.canvasManagers[`${canvasMsg.timestamp}_${canvasMsg.nodeId}`].running = true; } - const runningManagers = Object.keys(this.canvasManagers).filter((key) => this.canvasManagers[key].running); + const runningManagers = Object.keys(this.canvasManagers).filter( + (key) => this.canvasManagers[key].running + ); runningManagers.forEach((key) => { const manager = this.canvasManagers[key].manager; manager.move(t); - }) - }) + }); + }); } public decodeMessage(msg: Message) { - return this.decoder.decode(msg) + return this.decoder.decode(msg); } public _sortMessagesHack = (msgs: Message[]) => { // @ts-ignore Hack for upet (TODO: fix ordering in one mutation in tracker(removes first)) - const headChildrenIds = msgs.filter(m => m.parentID === 1).map(m => m.id); + const headChildrenIds = msgs.filter((m) => m.parentID === 1).map((m) => m.id); this.pagesManager.sortPages((m1, m2) => { if (m1.time === m2.time) { if (m1.tp === MType.RemoveNode && m2.tp !== MType.RemoveNode) { @@ -347,7 +364,7 @@ export default class TabSessionManager { if (headChildrenIds.includes(m2.id)) { return 1; } - } else if (m2.tp === MType.RemoveNode && m1.tp === MType.RemoveNode) { + } else if (m2.tp === MType.RemoveNode && m1.tp === MType.RemoveNode) { const m1FromHead = headChildrenIds.includes(m1.id); const m2FromHead = headChildrenIds.includes(m2.id); if (m1FromHead && !m2FromHead) { @@ -358,25 +375,25 @@ export default class TabSessionManager { } } return 0; - }) - } + }); + }; public onFileReadSuccess = () => { - const stateToUpdate : Partial> = { + const stateToUpdate: Partial> = { performanceChartData: this.performanceTrackManager.chartData, performanceAvailability: this.performanceTrackManager.availability, urlsList: this.locationManager.list, ...this.lists.getFullListsState(), - } + }; - this.updateLocalState(stateToUpdate) - } + this.updateLocalState(stateToUpdate); + }; public getListsFullState = () => { - return this.lists.getFullListsState() - } + return this.lists.getFullListsState(); + }; clean() { - this.pagesManager.reset() + this.pagesManager.reset(); } -} \ No newline at end of file +} diff --git a/frontend/app/player/web/messages/RawMessageReader.gen.ts b/frontend/app/player/web/messages/RawMessageReader.gen.ts index 793d3967e..a392655ba 100644 --- a/frontend/app/player/web/messages/RawMessageReader.gen.ts +++ b/frontend/app/player/web/messages/RawMessageReader.gen.ts @@ -651,6 +651,24 @@ export default class RawMessageReader extends PrimitiveReader { }; } + case 84: { + const chType = this.readString(); if (chType === null) { return resetPointer() } + const channelName = this.readString(); if (channelName === null) { return resetPointer() } + const data = this.readString(); if (data === null) { return resetPointer() } + const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() } + const dir = this.readString(); if (dir === null) { return resetPointer() } + const messageType = this.readString(); if (messageType === null) { return resetPointer() } + return { + tp: MType.WsChannel, + chType, + channelName, + data, + timestamp, + dir, + messageType, + }; + } + case 113: { const selectionStart = this.readUint(); if (selectionStart === null) { return resetPointer() } const selectionEnd = this.readUint(); if (selectionEnd === null) { return resetPointer() } diff --git a/frontend/app/player/web/messages/message.gen.ts b/frontend/app/player/web/messages/message.gen.ts index efc5d6cd6..b121ae69f 100644 --- a/frontend/app/player/web/messages/message.gen.ts +++ b/frontend/app/player/web/messages/message.gen.ts @@ -56,6 +56,7 @@ import type { RawAdoptedSsRemoveOwner, RawZustand, RawNetworkRequest, + RawWsChannel, RawSelectionChange, RawMouseThrashing, RawResourceTiming, @@ -181,6 +182,8 @@ export type Zustand = RawZustand & Timed export type NetworkRequest = RawNetworkRequest & Timed +export type WsChannel = RawWsChannel & Timed + export type SelectionChange = RawSelectionChange & Timed export type MouseThrashing = RawMouseThrashing & Timed diff --git a/frontend/app/player/web/messages/raw.gen.ts b/frontend/app/player/web/messages/raw.gen.ts index f808a7713..ca3af1886 100644 --- a/frontend/app/player/web/messages/raw.gen.ts +++ b/frontend/app/player/web/messages/raw.gen.ts @@ -54,6 +54,7 @@ export const enum MType { AdoptedSsRemoveOwner = 77, Zustand = 79, NetworkRequest = 83, + WsChannel = 84, SelectionChange = 113, MouseThrashing = 114, ResourceTiming = 116, @@ -441,6 +442,16 @@ export interface RawNetworkRequest { transferredBodySize: number, } +export interface RawWsChannel { + tp: MType.WsChannel, + chType: string, + channelName: string, + data: string, + timestamp: number, + dir: string, + messageType: string, +} + export interface RawSelectionChange { tp: MType.SelectionChange, selectionStart: number, @@ -575,4 +586,4 @@ export interface RawIosIssueEvent { } -export type RawMessage = RawTimestamp | RawSetPageLocation | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawNetworkRequestDeprecated | RawConsoleLog | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawRedux | RawVuex | RawMobX | RawNgRx | RawGraphQl | RawPerformanceTrack | RawStringDict | RawSetNodeAttributeDict | RawResourceTimingDeprecated | RawConnectionInformation | RawSetPageVisibility | RawLoadFontFace | RawSetNodeFocus | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawCssInsertRuleURLBased | RawMouseClick | RawCreateIFrameDocument | RawAdoptedSsReplaceURLBased | RawAdoptedSsReplace | RawAdoptedSsInsertRuleURLBased | RawAdoptedSsInsertRule | RawAdoptedSsDeleteRule | RawAdoptedSsAddOwner | RawAdoptedSsRemoveOwner | RawZustand | RawNetworkRequest | RawSelectionChange | RawMouseThrashing | RawResourceTiming | RawTabChange | RawTabData | RawCanvasNode | RawIosEvent | RawIosScreenChanges | RawIosClickEvent | RawIosInputEvent | RawIosPerformanceEvent | RawIosLog | RawIosInternalError | RawIosNetworkCall | RawIosSwipeEvent | RawIosIssueEvent; +export type RawMessage = RawTimestamp | RawSetPageLocation | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawNetworkRequestDeprecated | RawConsoleLog | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawRedux | RawVuex | RawMobX | RawNgRx | RawGraphQl | RawPerformanceTrack | RawStringDict | RawSetNodeAttributeDict | RawResourceTimingDeprecated | RawConnectionInformation | RawSetPageVisibility | RawLoadFontFace | RawSetNodeFocus | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawCssInsertRuleURLBased | RawMouseClick | RawCreateIFrameDocument | RawAdoptedSsReplaceURLBased | RawAdoptedSsReplace | RawAdoptedSsInsertRuleURLBased | RawAdoptedSsInsertRule | RawAdoptedSsDeleteRule | RawAdoptedSsAddOwner | RawAdoptedSsRemoveOwner | RawZustand | RawNetworkRequest | RawWsChannel | RawSelectionChange | RawMouseThrashing | RawResourceTiming | RawTabChange | RawTabData | RawCanvasNode | RawIosEvent | RawIosScreenChanges | RawIosClickEvent | RawIosInputEvent | RawIosPerformanceEvent | RawIosLog | RawIosInternalError | RawIosNetworkCall | RawIosSwipeEvent | RawIosIssueEvent; diff --git a/frontend/app/player/web/messages/tracker-legacy.gen.ts b/frontend/app/player/web/messages/tracker-legacy.gen.ts index 5ee4cf6a0..09d7576c6 100644 --- a/frontend/app/player/web/messages/tracker-legacy.gen.ts +++ b/frontend/app/player/web/messages/tracker-legacy.gen.ts @@ -55,6 +55,7 @@ export const TP_MAP = { 77: MType.AdoptedSsRemoveOwner, 79: MType.Zustand, 83: MType.NetworkRequest, + 84: MType.WsChannel, 113: MType.SelectionChange, 114: MType.MouseThrashing, 116: MType.ResourceTiming, diff --git a/frontend/app/player/web/messages/tracker.gen.ts b/frontend/app/player/web/messages/tracker.gen.ts index d7042a355..6ad41e111 100644 --- a/frontend/app/player/web/messages/tracker.gen.ts +++ b/frontend/app/player/web/messages/tracker.gen.ts @@ -442,6 +442,16 @@ type TrNetworkRequest = [ transferredBodySize: number, ] +type TrWSChannel = [ + type: 84, + chType: string, + channelName: string, + data: string, + timestamp: number, + dir: string, + messageType: string, +] + type TrInputChange = [ type: 112, id: number, @@ -500,7 +510,7 @@ type TrCanvasNode = [ ] -export type TrackerMessage = TrTimestamp | TrSetPageLocation | TrSetViewportSize | TrSetViewportScroll | TrCreateDocument | TrCreateElementNode | TrCreateTextNode | TrMoveNode | TrRemoveNode | TrSetNodeAttribute | TrRemoveNodeAttribute | TrSetNodeData | TrSetNodeScroll | TrSetInputTarget | TrSetInputValue | TrSetInputChecked | TrMouseMove | TrNetworkRequestDeprecated | TrConsoleLog | TrPageLoadTiming | TrPageRenderTiming | TrCustomEvent | TrUserID | TrUserAnonymousID | TrMetadata | TrCSSInsertRule | TrCSSDeleteRule | TrFetch | TrProfiler | TrOTable | TrStateAction | TrRedux | TrVuex | TrMobX | TrNgRx | TrGraphQL | TrPerformanceTrack | TrStringDict | TrSetNodeAttributeDict | TrResourceTimingDeprecated | TrConnectionInformation | TrSetPageVisibility | TrLoadFontFace | TrSetNodeFocus | TrLongTask | TrSetNodeAttributeURLBased | TrSetCSSDataURLBased | TrTechnicalInfo | TrCustomIssue | TrCSSInsertRuleURLBased | TrMouseClick | TrCreateIFrameDocument | TrAdoptedSSReplaceURLBased | TrAdoptedSSInsertRuleURLBased | TrAdoptedSSDeleteRule | TrAdoptedSSAddOwner | TrAdoptedSSRemoveOwner | TrJSException | TrZustand | TrBatchMetadata | TrPartitionedMessage | TrNetworkRequest | TrInputChange | TrSelectionChange | TrMouseThrashing | TrUnbindNodes | TrResourceTiming | TrTabChange | TrTabData | TrCanvasNode +export type TrackerMessage = TrTimestamp | TrSetPageLocation | TrSetViewportSize | TrSetViewportScroll | TrCreateDocument | TrCreateElementNode | TrCreateTextNode | TrMoveNode | TrRemoveNode | TrSetNodeAttribute | TrRemoveNodeAttribute | TrSetNodeData | TrSetNodeScroll | TrSetInputTarget | TrSetInputValue | TrSetInputChecked | TrMouseMove | TrNetworkRequestDeprecated | TrConsoleLog | TrPageLoadTiming | TrPageRenderTiming | TrCustomEvent | TrUserID | TrUserAnonymousID | TrMetadata | TrCSSInsertRule | TrCSSDeleteRule | TrFetch | TrProfiler | TrOTable | TrStateAction | TrRedux | TrVuex | TrMobX | TrNgRx | TrGraphQL | TrPerformanceTrack | TrStringDict | TrSetNodeAttributeDict | TrResourceTimingDeprecated | TrConnectionInformation | TrSetPageVisibility | TrLoadFontFace | TrSetNodeFocus | TrLongTask | TrSetNodeAttributeURLBased | TrSetCSSDataURLBased | TrTechnicalInfo | TrCustomIssue | TrCSSInsertRuleURLBased | TrMouseClick | TrCreateIFrameDocument | TrAdoptedSSReplaceURLBased | TrAdoptedSSInsertRuleURLBased | TrAdoptedSSDeleteRule | TrAdoptedSSAddOwner | TrAdoptedSSRemoveOwner | TrJSException | TrZustand | TrBatchMetadata | TrPartitionedMessage | TrNetworkRequest | TrWSChannel | TrInputChange | TrSelectionChange | TrMouseThrashing | TrUnbindNodes | TrResourceTiming | TrTabChange | TrTabData | TrCanvasNode export default function translate(tMsg: TrackerMessage): RawMessage | null { switch(tMsg[0]) { @@ -952,6 +962,18 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null { } } + case 84: { + return { + tp: MType.WsChannel, + chType: tMsg[1], + channelName: tMsg[2], + data: tMsg[3], + timestamp: tMsg[4], + dir: tMsg[5], + messageType: tMsg[6], + } + } + case 113: { return { tp: MType.SelectionChange, diff --git a/frontend/app/player/web/types/resource.ts b/frontend/app/player/web/types/resource.ts index ce2f3eb45..d76557e78 100644 --- a/frontend/app/player/web/types/resource.ts +++ b/frontend/app/player/web/types/resource.ts @@ -9,6 +9,7 @@ export const enum ResourceType { CSS = 'css', IMG = 'img', MEDIA = 'media', + WS = 'websocket', OTHER = 'other', } diff --git a/mobs/messages.rb b/mobs/messages.rb index 538f1eeb4..9c6b7e0ba 100644 --- a/mobs/messages.rb +++ b/mobs/messages.rb @@ -458,6 +458,15 @@ message 83, 'NetworkRequest', :replayer => :devtools do uint 'TransferredBodySize' end +message 84, 'WSChannel', :replayer => :devtools do + string 'ChType' + string 'ChannelName' + string 'Data' + uint 'Timestamp' + string 'Dir' + string 'MessageType' +end + # 90-111 reserved iOS message 112, 'InputChange', :replayer => false do diff --git a/tracker/tracker/package.json b/tracker/tracker/package.json index 9a293089a..fce53ae94 100644 --- a/tracker/tracker/package.json +++ b/tracker/tracker/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker", "description": "The OpenReplay tracker main package", - "version": "11.0.2", + "version": "11.0.0-beta.7", "keywords": [ "logging", "replay" diff --git a/tracker/tracker/src/common/messages.gen.ts b/tracker/tracker/src/common/messages.gen.ts index 46eda477e..188ba2caa 100644 --- a/tracker/tracker/src/common/messages.gen.ts +++ b/tracker/tracker/src/common/messages.gen.ts @@ -64,6 +64,7 @@ export declare const enum Type { BatchMetadata = 81, PartitionedMessage = 82, NetworkRequest = 83, + WSChannel = 84, InputChange = 112, SelectionChange = 113, MouseThrashing = 114, @@ -512,6 +513,16 @@ export type NetworkRequest = [ /*transferredBodySize:*/ number, ] +export type WSChannel = [ + /*type:*/ Type.WSChannel, + /*chType:*/ string, + /*channelName:*/ string, + /*data:*/ string, + /*timestamp:*/ number, + /*dir:*/ string, + /*messageType:*/ string, +] + export type InputChange = [ /*type:*/ Type.InputChange, /*id:*/ number, @@ -570,5 +581,5 @@ export type CanvasNode = [ ] -type Message = Timestamp | SetPageLocation | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetNodeScroll | SetInputTarget | SetInputValue | SetInputChecked | MouseMove | NetworkRequestDeprecated | ConsoleLog | PageLoadTiming | PageRenderTiming | CustomEvent | UserID | UserAnonymousID | Metadata | CSSInsertRule | CSSDeleteRule | Fetch | Profiler | OTable | StateAction | Redux | Vuex | MobX | NgRx | GraphQL | PerformanceTrack | StringDict | SetNodeAttributeDict | ResourceTimingDeprecated | ConnectionInformation | SetPageVisibility | LoadFontFace | SetNodeFocus | LongTask | SetNodeAttributeURLBased | SetCSSDataURLBased | TechnicalInfo | CustomIssue | CSSInsertRuleURLBased | MouseClick | CreateIFrameDocument | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner | JSException | Zustand | BatchMetadata | PartitionedMessage | NetworkRequest | InputChange | SelectionChange | MouseThrashing | UnbindNodes | ResourceTiming | TabChange | TabData | CanvasNode +type Message = Timestamp | SetPageLocation | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetNodeScroll | SetInputTarget | SetInputValue | SetInputChecked | MouseMove | NetworkRequestDeprecated | ConsoleLog | PageLoadTiming | PageRenderTiming | CustomEvent | UserID | UserAnonymousID | Metadata | CSSInsertRule | CSSDeleteRule | Fetch | Profiler | OTable | StateAction | Redux | Vuex | MobX | NgRx | GraphQL | PerformanceTrack | StringDict | SetNodeAttributeDict | ResourceTimingDeprecated | ConnectionInformation | SetPageVisibility | LoadFontFace | SetNodeFocus | LongTask | SetNodeAttributeURLBased | SetCSSDataURLBased | TechnicalInfo | CustomIssue | CSSInsertRuleURLBased | MouseClick | CreateIFrameDocument | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner | JSException | Zustand | BatchMetadata | PartitionedMessage | NetworkRequest | WSChannel | InputChange | SelectionChange | MouseThrashing | UnbindNodes | ResourceTiming | TabChange | TabData | CanvasNode export default Message diff --git a/tracker/tracker/src/main/app/index.ts b/tracker/tracker/src/main/app/index.ts index 8565b2696..d11c57134 100644 --- a/tracker/tracker/src/main/app/index.ts +++ b/tracker/tracker/src/main/app/index.ts @@ -1,5 +1,13 @@ import type Message from './messages.gen.js' -import { Timestamp, Metadata, UserID, Type as MType, TabChange, TabData } from './messages.gen.js' +import { + Timestamp, + Metadata, + UserID, + Type as MType, + TabChange, + TabData, + WSChannel, +} from './messages.gen.js' import { now, adjustTimeOrigin, @@ -812,6 +820,26 @@ export default class App { return this.session.getTabId() } + /** + * Creates a named hook that expects event name, data string and msg direction (up/down), + * it will skip any message bigger than 5 mb or event name bigger than 255 symbols + * @returns {(msgType: string, data: string, dir: 'up' | 'down') => void} + * */ + trackWs(channelName: string): (msgType: string, data: string, dir: 'up' | 'down') => void { + const channel = channelName + return (msgType: string, data: string, dir: 'up' | 'down' = 'down') => { + if ( + typeof msgType !== 'string' || + typeof data !== 'string' || + data.length > 5 * 1024 * 1024 || + msgType.length > 255 + ) { + return + } + this.send(WSChannel('websocket', channel, data, this.timestamp(), dir, msgType)) + } + } + stop(stopWorker = true): void { if (this.activityState !== ActivityState.NotActive) { try { diff --git a/tracker/tracker/src/main/app/messages.gen.ts b/tracker/tracker/src/main/app/messages.gen.ts index f186a5f27..57b4722f9 100644 --- a/tracker/tracker/src/main/app/messages.gen.ts +++ b/tracker/tracker/src/main/app/messages.gen.ts @@ -817,6 +817,25 @@ export function NetworkRequest( ] } +export function WSChannel( + chType: string, + channelName: string, + data: string, + timestamp: number, + dir: string, + messageType: string, +): Messages.WSChannel { + return [ + Messages.Type.WSChannel, + chType, + channelName, + data, + timestamp, + dir, + messageType, + ] +} + export function InputChange( id: number, value: string, diff --git a/tracker/tracker/src/main/index.ts b/tracker/tracker/src/main/index.ts index 3c70078a3..cf90f27a4 100644 --- a/tracker/tracker/src/main/index.ts +++ b/tracker/tracker/src/main/index.ts @@ -234,6 +234,20 @@ export default class API { return this.app.active() } + /** + * Creates a named hook that expects event name, data string and msg direction (up/down), + * it will skip any message bigger than 5 mb or event name bigger than 255 symbols + * msg direction is "down" (incoming) by default + * + * @returns {(msgType: string, data: string, dir: 'up' | 'down') => void} + * */ + trackWs(channelName: string) { + if (this.app === null) { + return + } + return this.app.trackWs(channelName) + } + start(startOpts?: Partial): Promise { if (!IN_BROWSER) { console.error( diff --git a/tracker/tracker/src/webworker/MessageEncoder.gen.ts b/tracker/tracker/src/webworker/MessageEncoder.gen.ts index 4ae5cfbd3..cf9f4855e 100644 --- a/tracker/tracker/src/webworker/MessageEncoder.gen.ts +++ b/tracker/tracker/src/webworker/MessageEncoder.gen.ts @@ -258,6 +258,10 @@ export default class MessageEncoder extends PrimitiveEncoder { return this.string(msg[1]) && this.string(msg[2]) && this.string(msg[3]) && this.string(msg[4]) && this.string(msg[5]) && this.uint(msg[6]) && this.uint(msg[7]) && this.uint(msg[8]) && this.uint(msg[9]) break + case Messages.Type.WSChannel: + 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.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