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
This commit is contained in:
parent
7a5e2be138
commit
9e1add4ad9
27 changed files with 809 additions and 386 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -1254,6 +1254,30 @@ func DecodeNetworkRequest(reader BytesReader) (Message, error) {
|
||||||
return msg, err
|
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) {
|
func DecodeInputChange(reader BytesReader) (Message, error) {
|
||||||
var err error = nil
|
var err error = nil
|
||||||
msg := &InputChange{}
|
msg := &InputChange{}
|
||||||
|
|
@ -1991,6 +2015,8 @@ func ReadMessage(t uint64, reader BytesReader) (Message, error) {
|
||||||
return DecodePartitionedMessage(reader)
|
return DecodePartitionedMessage(reader)
|
||||||
case 83:
|
case 83:
|
||||||
return DecodeNetworkRequest(reader)
|
return DecodeNetworkRequest(reader)
|
||||||
|
case 84:
|
||||||
|
return DecodeWSChannel(reader)
|
||||||
case 112:
|
case 112:
|
||||||
return DecodeInputChange(reader)
|
return DecodeInputChange(reader)
|
||||||
case 113:
|
case 113:
|
||||||
|
|
|
||||||
|
|
@ -723,6 +723,18 @@ class NetworkRequest(Message):
|
||||||
self.transferred_body_size = transferred_body_size
|
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):
|
class InputChange(Message):
|
||||||
__id__ = 112
|
__id__ = 112
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1069,6 +1069,25 @@ cdef class NetworkRequest(PyMessage):
|
||||||
self.transferred_body_size = transferred_body_size
|
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 class InputChange(PyMessage):
|
||||||
cdef public int __id__
|
cdef public int __id__
|
||||||
cdef public unsigned long id
|
cdef public unsigned long id
|
||||||
|
|
|
||||||
|
|
@ -660,6 +660,16 @@ class MessageCodec(Codec):
|
||||||
transferred_body_size=self.read_uint(reader)
|
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:
|
if message_id == 112:
|
||||||
return InputChange(
|
return InputChange(
|
||||||
id=self.read_uint(reader),
|
id=self.read_uint(reader),
|
||||||
|
|
|
||||||
|
|
@ -758,6 +758,16 @@ cdef class MessageCodec:
|
||||||
transferred_body_size=self.read_uint(reader)
|
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:
|
if message_id == 112:
|
||||||
return InputChange(
|
return InputChange(
|
||||||
id=self.read_uint(reader),
|
id=self.read_uint(reader),
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,15 @@ function EventsList({ scale }: { scale: number }) {
|
||||||
|
|
||||||
const { tabStates, eventCount } = store.get();
|
const { tabStates, eventCount } = store.get();
|
||||||
const events = React.useMemo(() => {
|
const events = React.useMemo(() => {
|
||||||
return Object.values(tabStates)[0]?.eventList || [];
|
return Object.values(tabStates)[0]?.eventList.filter(e => e.time) || [];
|
||||||
}, [eventCount]);
|
}, [eventCount]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{events.map((e) => (
|
{events.map((e) => (
|
||||||
<div
|
<div
|
||||||
/*@ts-ignore TODO */
|
/*@ts-ignore TODO */
|
||||||
key={e.key}
|
key={`${e.key}_${e.time}`}
|
||||||
className={stl.event}
|
className={stl.event}
|
||||||
style={{ left: `${getTimelinePosition(e.time, scale)}%` }}
|
style={{ left: `${getTimelinePosition(e.time, scale)}%` }}
|
||||||
/>
|
/>
|
||||||
|
|
@ -35,7 +36,7 @@ function MobileEventsList({ scale }: { scale: number }) {
|
||||||
{events.map((e) => (
|
{events.map((e) => (
|
||||||
<div
|
<div
|
||||||
/*@ts-ignore TODO */
|
/*@ts-ignore TODO */
|
||||||
key={e.key}
|
key={`${e.key}_${e.time}`}
|
||||||
className={stl.event}
|
className={stl.event}
|
||||||
style={{ left: `${getTimelinePosition(e.time, scale)}%` }}
|
style={{ left: `${getTimelinePosition(e.time, scale)}%` }}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import BottomBlock from '../BottomBlock';
|
||||||
import InfoLine from '../BottomBlock/InfoLine';
|
import InfoLine from '../BottomBlock/InfoLine';
|
||||||
import useAutoscroll, { getLastItemTime } from '../useAutoscroll';
|
import useAutoscroll, { getLastItemTime } from '../useAutoscroll';
|
||||||
import { useRegExListFilterMemo, useTabListFilterMemo } from '../useListFilter';
|
import { useRegExListFilterMemo, useTabListFilterMemo } from '../useListFilter';
|
||||||
|
import WSModal from './WSModal'
|
||||||
|
|
||||||
const INDEX_KEY = 'network';
|
const INDEX_KEY = 'network';
|
||||||
|
|
||||||
|
|
@ -28,6 +29,7 @@ const CSS = 'css';
|
||||||
const IMG = 'img';
|
const IMG = 'img';
|
||||||
const MEDIA = 'media';
|
const MEDIA = 'media';
|
||||||
const OTHER = 'other';
|
const OTHER = 'other';
|
||||||
|
const WS = 'websocket';
|
||||||
|
|
||||||
const TYPE_TO_TAB = {
|
const TYPE_TO_TAB = {
|
||||||
[ResourceType.XHR]: XHR,
|
[ResourceType.XHR]: XHR,
|
||||||
|
|
@ -36,10 +38,11 @@ const TYPE_TO_TAB = {
|
||||||
[ResourceType.CSS]: CSS,
|
[ResourceType.CSS]: CSS,
|
||||||
[ResourceType.IMG]: IMG,
|
[ResourceType.IMG]: IMG,
|
||||||
[ResourceType.MEDIA]: MEDIA,
|
[ResourceType.MEDIA]: MEDIA,
|
||||||
|
[ResourceType.WS]: WS,
|
||||||
[ResourceType.OTHER]: OTHER,
|
[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) => ({
|
const TABS = TAP_KEYS.map((tab) => ({
|
||||||
text: tab === 'xhr' ? 'Fetch/XHR' : tab,
|
text: tab === 'xhr' ? 'Fetch/XHR' : tab,
|
||||||
key: tab,
|
key: tab,
|
||||||
|
|
@ -156,6 +159,8 @@ function NetworkPanelCont({ startedAt, panelHeight }: { startedAt: number; panel
|
||||||
resourceList = [],
|
resourceList = [],
|
||||||
fetchListNow = [],
|
fetchListNow = [],
|
||||||
resourceListNow = [],
|
resourceListNow = [],
|
||||||
|
websocketList = [],
|
||||||
|
websocketListNow = [],
|
||||||
} = tabStates[currentTab];
|
} = tabStates[currentTab];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -170,11 +175,19 @@ function NetworkPanelCont({ startedAt, panelHeight }: { startedAt: number; panel
|
||||||
resourceListNow={resourceListNow}
|
resourceListNow={resourceListNow}
|
||||||
player={player}
|
player={player}
|
||||||
startedAt={startedAt}
|
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 { player, store } = React.useContext(MobilePlayerContext);
|
||||||
|
|
||||||
const domContentLoadedTime = undefined;
|
const domContentLoadedTime = undefined;
|
||||||
|
|
@ -185,6 +198,8 @@ function MobileNetworkPanelCont({ startedAt, panelHeight }: { startedAt: number,
|
||||||
resourceList = [],
|
resourceList = [],
|
||||||
fetchListNow = [],
|
fetchListNow = [],
|
||||||
resourceListNow = [],
|
resourceListNow = [],
|
||||||
|
websocketList = [],
|
||||||
|
websocketListNow = [],
|
||||||
} = store.get();
|
} = store.get();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -200,10 +215,22 @@ function MobileNetworkPanelCont({ startedAt, panelHeight }: { startedAt: number,
|
||||||
resourceListNow={resourceListNow}
|
resourceListNow={resourceListNow}
|
||||||
player={player}
|
player={player}
|
||||||
startedAt={startedAt}
|
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 {
|
interface Props {
|
||||||
domContentLoadedTime?: {
|
domContentLoadedTime?: {
|
||||||
time: number;
|
time: number;
|
||||||
|
|
@ -218,6 +245,8 @@ interface Props {
|
||||||
resourceList: Timed[];
|
resourceList: Timed[];
|
||||||
fetchListNow: Timed[];
|
fetchListNow: Timed[];
|
||||||
resourceListNow: Timed[];
|
resourceListNow: Timed[];
|
||||||
|
websocketList: Array<WSMessage>;
|
||||||
|
websocketListNow: Array<WSMessage>;
|
||||||
player: WebPlayer | MobilePlayer;
|
player: WebPlayer | MobilePlayer;
|
||||||
startedAt: number;
|
startedAt: number;
|
||||||
isMobile?: boolean;
|
isMobile?: boolean;
|
||||||
|
|
@ -237,6 +266,7 @@ const NetworkPanelComp = observer(
|
||||||
startedAt,
|
startedAt,
|
||||||
isMobile,
|
isMobile,
|
||||||
panelHeight,
|
panelHeight,
|
||||||
|
websocketList,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { showModal } = useModal();
|
const { showModal } = useModal();
|
||||||
const [sortBy, setSortBy] = useState('time');
|
const [sortBy, setSortBy] = useState('time');
|
||||||
|
|
@ -251,6 +281,14 @@ const NetworkPanelComp = observer(
|
||||||
const activeTab = devTools[INDEX_KEY].activeTab;
|
const activeTab = devTools[INDEX_KEY].activeTab;
|
||||||
const activeIndex = devTools[INDEX_KEY].index;
|
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(
|
const list = useMemo(
|
||||||
() =>
|
() =>
|
||||||
// TODO: better merge (with body size info) - do it in player
|
// TODO: better merge (with body size info) - do it in player
|
||||||
|
|
@ -283,8 +321,20 @@ const NetworkPanelComp = observer(
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.concat(fetchList)
|
.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),
|
.sort((a, b) => a.time - b.time),
|
||||||
[resourceList.length, fetchList.length]
|
[resourceList.length, fetchList.length, socketList]
|
||||||
);
|
);
|
||||||
|
|
||||||
let filteredList = useMemo(() => {
|
let filteredList = useMemo(() => {
|
||||||
|
|
@ -354,6 +404,18 @@ const NetworkPanelComp = observer(
|
||||||
}, [domContentLoadedTime, loadTime]);
|
}, [domContentLoadedTime, loadTime]);
|
||||||
|
|
||||||
const showDetailsModal = (item: any) => {
|
const showDetailsModal = (item: any) => {
|
||||||
|
if (item.type === 'websocket') {
|
||||||
|
const socketMsgList = websocketList.filter((ws) => ws.channelName === item.channelName);
|
||||||
|
console.log(socketMsgList)
|
||||||
|
|
||||||
|
return showModal(
|
||||||
|
<WSModal
|
||||||
|
socketMsgList={socketMsgList}
|
||||||
|
/>, {
|
||||||
|
right: true, width: 700,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
setIsDetailsModalActive(true);
|
setIsDetailsModalActive(true);
|
||||||
showModal(
|
showModal(
|
||||||
<FetchDetailsModal
|
<FetchDetailsModal
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
import { durationFromMs } from 'App/date';
|
||||||
|
import { Timed } from 'Player';
|
||||||
|
import React from 'react';
|
||||||
|
import { Icon } from 'UI';
|
||||||
|
|
||||||
|
type SocketMsg = Timed & {
|
||||||
|
channelName: string;
|
||||||
|
data: string;
|
||||||
|
timestamp: number;
|
||||||
|
dir: 'up' | 'down';
|
||||||
|
messageType: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
socketMsgList: Array<SocketMsg>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function WSModal({ socketMsgList }: Props) {
|
||||||
|
return (
|
||||||
|
<div className={'h-screen w-full bg-white shadow'}>
|
||||||
|
<div className={'grid grid-cols-12 font-semibold border-b px-4 py-2'}>
|
||||||
|
<div className={'col-span-9 flex items-center gap-2'}>Data</div>
|
||||||
|
<div className={'col-span-1'}>Length</div>
|
||||||
|
<div className={'col-span-2 text-right'}>Time</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ height: '100%', maxWidth: 700, overflowY: 'auto' }}>
|
||||||
|
{socketMsgList.map((msg) => (
|
||||||
|
<Row msg={msg} key={msg.timestamp} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MsgDirection({ dir }: { dir: 'up' | 'down' }) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
name={dir === 'up' ? 'arrow-up' : 'arrow-down'}
|
||||||
|
size="12"
|
||||||
|
color={dir === 'up' ? 'red' : 'main'}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Row({ msg }) {
|
||||||
|
const [isOpen, setIsOpen] = React.useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className={`border-b grid grid-cols-12 ${
|
||||||
|
msg.data.length > 100 ? 'hover:bg-active-blue cursor-pointer' : ''
|
||||||
|
}`}
|
||||||
|
onClick={() => (msg.data.length > 100 ? setIsOpen(!isOpen) : null)}
|
||||||
|
style={{ width: 700 }}
|
||||||
|
>
|
||||||
|
<div className={'col-span-9 flex items-center gap-2 p-2'}>
|
||||||
|
<MsgDirection dir={msg.dir} />
|
||||||
|
<span className={'bg-active-blue px-2 py-1'}>{msg.messageType}</span>
|
||||||
|
<span
|
||||||
|
className={'overflow-hidden text-ellipsis whitespace-nowrap'}
|
||||||
|
style={{ maxHeight: 44 }}
|
||||||
|
>
|
||||||
|
{msg.data}
|
||||||
|
</span>
|
||||||
|
{msg.data.length > 100 ? (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
'rounded-full font-bold text-xl p-2 bg-white w-6 h-6 flex items-center justify-center'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span>{isOpen ? '-' : '+'}</span>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
<div className={'col-span-1 p-2'}>{msg.data.length}</div>
|
||||||
|
<div className={'col-span-2 p-2 text-right'}>{durationFromMs(msg.time, true)}</div>
|
||||||
|
</div>
|
||||||
|
{isOpen ? (
|
||||||
|
<div className={'w-full h-fit bg-active-blue pl-6 pb-4 pr-2'}>
|
||||||
|
<div
|
||||||
|
style={{ maxHeight: "100%", overflow: "auto" }}
|
||||||
|
className={'whitespace-pre-wrap'}
|
||||||
|
>
|
||||||
|
{msg.data}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WSModal;
|
||||||
|
|
@ -44,10 +44,10 @@ export function durationFromMsFormatted(ms: number): string {
|
||||||
return durationFormatted(Duration.fromMillis(ms || 0));
|
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)
|
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 => {
|
export const durationFormattedFull = (duration: Duration): string => {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ const SIMPLE_LIST_NAMES = [
|
||||||
"frustrations",
|
"frustrations",
|
||||||
"performance"
|
"performance"
|
||||||
] as const
|
] 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
|
const LIST_NAMES = [...SIMPLE_LIST_NAMES, ...MARKED_LIST_NAMES ] as const
|
||||||
|
|
||||||
|
|
@ -226,6 +226,9 @@ export default class IOSMessageManager implements IMessageManager {
|
||||||
case MType.IosNetworkCall:
|
case MType.IosNetworkCall:
|
||||||
this.lists.lists.fetch.insert(getResourceFromNetworkRequest(msg, this.sessionStart))
|
this.lists.lists.fetch.insert(getResourceFromNetworkRequest(msg, this.sessionStart))
|
||||||
break;
|
break;
|
||||||
|
case MType.WsChannel:
|
||||||
|
this.lists.lists.websocket.insert(msg)
|
||||||
|
break;
|
||||||
case MType.IosEvent:
|
case MType.IosEvent:
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.lists.lists.event.insert({...msg, source: 'openreplay'});
|
this.lists.lists.event.insert({...msg, source: 'openreplay'});
|
||||||
|
|
|
||||||
|
|
@ -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 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 entityNamesSimple = [ "event", "profile" ];
|
||||||
|
|
||||||
const LIST_NAMES = [...SIMPLE_LIST_NAMES, ...MARKED_LIST_NAMES ] as const
|
const LIST_NAMES = [...SIMPLE_LIST_NAMES, ...MARKED_LIST_NAMES ] as const
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,21 @@
|
||||||
import type { Store } from "Player";
|
import type { Store } from 'Player';
|
||||||
import { getResourceFromNetworkRequest, getResourceFromResourceTiming, Log, ResourceType } from "Player";
|
import {
|
||||||
import ListWalker from "Player/common/ListWalker";
|
getResourceFromNetworkRequest,
|
||||||
import Lists, { INITIAL_STATE as LISTS_INITIAL_STATE, InitialLists, State as ListsState } from "Player/web/Lists";
|
getResourceFromResourceTiming,
|
||||||
import CanvasManager from "Player/web/managers/CanvasManager";
|
Log,
|
||||||
import { VElement } from "Player/web/managers/DOM/VirtualDOM";
|
ResourceType,
|
||||||
import PagesManager from "Player/web/managers/PagesManager";
|
} from 'Player';
|
||||||
import PerformanceTrackManager from "Player/web/managers/PerformanceTrackManager";
|
import ListWalker from 'Player/common/ListWalker';
|
||||||
import WindowNodeCounter from "Player/web/managers/WindowNodeCounter";
|
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 {
|
import {
|
||||||
CanvasNode,
|
CanvasNode,
|
||||||
ConnectionInformation,
|
ConnectionInformation,
|
||||||
|
|
@ -15,22 +24,22 @@ import {
|
||||||
ResourceTiming,
|
ResourceTiming,
|
||||||
SetPageLocation,
|
SetPageLocation,
|
||||||
SetViewportScroll,
|
SetViewportScroll,
|
||||||
SetViewportSize
|
SetViewportSize,
|
||||||
} from "Player/web/messages";
|
} from 'Player/web/messages';
|
||||||
import { isDOMType } from "Player/web/messages/filters.gen";
|
import { isDOMType } from 'Player/web/messages/filters.gen';
|
||||||
import Screen from "Player/web/Screen/Screen";
|
import Screen from 'Player/web/Screen/Screen';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Decoder } from "syncod";
|
import { Decoder } from 'syncod';
|
||||||
import { TYPES as EVENT_TYPES } from "Types/session/event";
|
import { TYPES as EVENT_TYPES } from 'Types/session/event';
|
||||||
import type { PerformanceChartPoint } from "./managers/PerformanceTrackManager";
|
import type { PerformanceChartPoint } from './managers/PerformanceTrackManager';
|
||||||
|
|
||||||
export interface TabState extends ListsState {
|
export interface TabState extends ListsState {
|
||||||
performanceAvailability?: PerformanceTrackManager['availability']
|
performanceAvailability?: PerformanceTrackManager['availability'];
|
||||||
performanceChartData: PerformanceChartPoint[],
|
performanceChartData: PerformanceChartPoint[];
|
||||||
performanceChartTime: PerformanceChartPoint[]
|
performanceChartTime: PerformanceChartPoint[];
|
||||||
cssLoading: boolean
|
cssLoading: boolean;
|
||||||
location: string
|
location: string;
|
||||||
urlsList: SetPageLocation[]
|
urlsList: SetPageLocation[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -46,10 +55,10 @@ export default class TabSessionManager {
|
||||||
cssLoading: false,
|
cssLoading: false,
|
||||||
location: '',
|
location: '',
|
||||||
urlsList: [],
|
urlsList: [],
|
||||||
}
|
};
|
||||||
|
|
||||||
public locationManager: ListWalker<SetPageLocation> = new ListWalker();
|
public locationManager: ListWalker<SetPageLocation> = new ListWalker();
|
||||||
private locationEventManager: ListWalker<any>/*<LocationEvent>*/ = new ListWalker();
|
private locationEventManager: ListWalker<any> /*<LocationEvent>*/ = new ListWalker();
|
||||||
private loadedLocationManager: ListWalker<SetPageLocation> = new ListWalker();
|
private loadedLocationManager: ListWalker<SetPageLocation> = new ListWalker();
|
||||||
private connectionInfoManger: ListWalker<ConnectionInformation> = new ListWalker();
|
private connectionInfoManger: ListWalker<ConnectionInformation> = new ListWalker();
|
||||||
private performanceTrackManager: PerformanceTrackManager = new PerformanceTrackManager();
|
private performanceTrackManager: PerformanceTrackManager = new PerformanceTrackManager();
|
||||||
|
|
@ -61,8 +70,10 @@ export default class TabSessionManager {
|
||||||
|
|
||||||
public readonly decoder = new Decoder();
|
public readonly decoder = new Decoder();
|
||||||
private lists: Lists;
|
private lists: Lists;
|
||||||
private navigationStartOffset = 0
|
private navigationStartOffset = 0;
|
||||||
private canvasManagers: { [key: string]: { manager: CanvasManager, start: number, running: boolean } } = {}
|
private canvasManagers: {
|
||||||
|
[key: string]: { manager: CanvasManager; start: number; running: boolean };
|
||||||
|
} = {};
|
||||||
private canvasReplayWalker: ListWalker<CanvasNode> = new ListWalker();
|
private canvasReplayWalker: ListWalker<CanvasNode> = new ListWalker();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|
@ -70,36 +81,37 @@ export default class TabSessionManager {
|
||||||
private readonly state: Store<{ tabStates: { [tabId: string]: TabState } }>,
|
private readonly state: Store<{ tabStates: { [tabId: string]: TabState } }>,
|
||||||
private readonly screen: Screen,
|
private readonly screen: Screen,
|
||||||
private readonly id: string,
|
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,
|
private readonly sessionStart: number,
|
||||||
initialLists?: Partial<InitialLists>,
|
initialLists?: Partial<InitialLists>
|
||||||
) {
|
) {
|
||||||
this.pagesManager = new PagesManager(screen, this.session.isMobile, this.setCSSLoading)
|
this.pagesManager = new PagesManager(screen, this.session.isMobile, this.setCSSLoading);
|
||||||
this.lists = new Lists(initialLists)
|
this.lists = new Lists(initialLists);
|
||||||
initialLists?.event?.forEach((e: Record<string, string>) => { // TODO: to one of "Movable" module
|
initialLists?.event?.forEach((e: Record<string, string>) => {
|
||||||
|
// TODO: to one of "Movable" module
|
||||||
if (e.type === EVENT_TYPES.LOCATION) {
|
if (e.type === EVENT_TYPES.LOCATION) {
|
||||||
this.locationEventManager.append(e);
|
this.locationEventManager.append(e);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getNode = (id: number) => {
|
public getNode = (id: number) => {
|
||||||
return this.pagesManager.getNode(id)
|
return this.pagesManager.getNode(id);
|
||||||
}
|
};
|
||||||
|
|
||||||
public updateLists(lists: Partial<InitialLists>) {
|
public updateLists(lists: Partial<InitialLists>) {
|
||||||
Object.keys(lists).forEach((key: 'event' | 'stack' | 'exceptions') => {
|
Object.keys(lists).forEach((key: 'event' | 'stack' | 'exceptions') => {
|
||||||
const currentList = this.lists.lists[key]
|
const currentList = this.lists.lists[key];
|
||||||
lists[key]!.forEach(item => currentList.insert(item))
|
lists[key]!.forEach((item) => currentList.insert(item));
|
||||||
})
|
});
|
||||||
lists?.event?.forEach((e: Record<string, string>) => {
|
lists?.event?.forEach((e: Record<string, string>) => {
|
||||||
if (e.type === EVENT_TYPES.LOCATION) {
|
if (e.type === EVENT_TYPES.LOCATION) {
|
||||||
this.locationEventManager.append(e);
|
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({
|
this.state.update({
|
||||||
// @ts-ignore comes from parent state
|
// @ts-ignore comes from parent state
|
||||||
eventCount: currentState.eventCount + eventCount,
|
eventCount: currentState.eventCount + eventCount,
|
||||||
|
|
@ -108,9 +120,9 @@ export default class TabSessionManager {
|
||||||
[this.id]: {
|
[this.id]: {
|
||||||
...currentState.tabStates[this.id],
|
...currentState.tabStates[this.id],
|
||||||
...this.lists.getFullListsState(),
|
...this.lists.getFullListsState(),
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLocalState(state: Partial<TabState>) {
|
updateLocalState(state: Partial<TabState>) {
|
||||||
|
|
@ -119,22 +131,22 @@ export default class TabSessionManager {
|
||||||
...this.state.get().tabStates,
|
...this.state.get().tabStates,
|
||||||
[this.id]: {
|
[this.id]: {
|
||||||
...this.state.get().tabStates[this.id],
|
...this.state.get().tabStates[this.id],
|
||||||
...state
|
...state,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private setCSSLoading = (cssLoading: boolean) => {
|
private setCSSLoading = (cssLoading: boolean) => {
|
||||||
this.screen.displayFrame(!cssLoading)
|
this.screen.displayFrame(!cssLoading);
|
||||||
this.updateLocalState({
|
this.updateLocalState({
|
||||||
cssLoading
|
cssLoading,
|
||||||
})
|
});
|
||||||
this.state.update({
|
this.state.update({
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
ready: !this.state.get().messagesLoading && !cssLoading
|
ready: !this.state.get().messagesLoading && !cssLoading,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
public resetMessageManagers() {
|
public resetMessageManagers() {
|
||||||
this.locationEventManager = new ListWalker();
|
this.locationEventManager = new ListWalker();
|
||||||
|
|
@ -144,12 +156,11 @@ export default class TabSessionManager {
|
||||||
this.scrollManager = new ListWalker();
|
this.scrollManager = new ListWalker();
|
||||||
this.resizeManager = new ListWalker();
|
this.resizeManager = new ListWalker();
|
||||||
|
|
||||||
this.performanceTrackManager = new PerformanceTrackManager()
|
this.performanceTrackManager = new PerformanceTrackManager();
|
||||||
this.windowNodeCounter = new WindowNodeCounter();
|
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 {
|
distributeMessage(msg: Message): void {
|
||||||
switch (msg.tp) {
|
switch (msg.tp) {
|
||||||
case MType.CanvasNode:
|
case MType.CanvasNode:
|
||||||
|
|
@ -166,7 +177,6 @@ export default class TabSessionManager {
|
||||||
);
|
);
|
||||||
this.canvasManagers[managerId] = { manager, start: msg.timestamp, running: false };
|
this.canvasManagers[managerId] = { manager, start: msg.timestamp, running: false };
|
||||||
this.canvasReplayWalker.append(msg);
|
this.canvasReplayWalker.append(msg);
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MType.SetPageLocation:
|
case MType.SetPageLocation:
|
||||||
|
|
@ -185,7 +195,7 @@ export default class TabSessionManager {
|
||||||
this.performanceTrackManager.append(msg);
|
this.performanceTrackManager.append(msg);
|
||||||
break;
|
break;
|
||||||
case MType.SetPageVisibility:
|
case MType.SetPageVisibility:
|
||||||
this.performanceTrackManager.handleVisibility(msg)
|
this.performanceTrackManager.handleVisibility(msg);
|
||||||
break;
|
break;
|
||||||
case MType.ConnectionInformation:
|
case MType.ConnectionInformation:
|
||||||
this.connectionInfoManger.append(msg);
|
this.connectionInfoManger.append(msg);
|
||||||
|
|
@ -199,18 +209,23 @@ export default class TabSessionManager {
|
||||||
this.lists.lists.log.append(
|
this.lists.lists.log.append(
|
||||||
// @ts-ignore : TODO: enums in the message schema
|
// @ts-ignore : TODO: enums in the message schema
|
||||||
Log(msg)
|
Log(msg)
|
||||||
)
|
);
|
||||||
break;
|
break;
|
||||||
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
|
||||||
if (msg.initiator !== ResourceType.FETCH && msg.initiator !== ResourceType.XHR) {
|
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;
|
break;
|
||||||
case MType.Fetch:
|
case MType.Fetch:
|
||||||
case MType.NetworkRequest:
|
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;
|
break;
|
||||||
case MType.Redux:
|
case MType.Redux:
|
||||||
this.lists.lists.redux.append(msg);
|
this.lists.lists.redux.append(msg);
|
||||||
|
|
@ -222,8 +237,8 @@ export default class TabSessionManager {
|
||||||
this.lists.lists.vuex.append(msg);
|
this.lists.lists.vuex.append(msg);
|
||||||
break;
|
break;
|
||||||
case MType.Zustand:
|
case MType.Zustand:
|
||||||
this.lists.lists.zustand.append(msg)
|
this.lists.lists.zustand.append(msg);
|
||||||
break
|
break;
|
||||||
case MType.MobX:
|
case MType.MobX:
|
||||||
this.lists.lists.mobx.append(msg);
|
this.lists.lists.mobx.append(msg);
|
||||||
break;
|
break;
|
||||||
|
|
@ -254,8 +269,8 @@ export default class TabSessionManager {
|
||||||
this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count);
|
this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.performanceTrackManager.addNodeCountPointIfNeed(msg.time)
|
this.performanceTrackManager.addNodeCountPointIfNeed(msg.time);
|
||||||
isDOMType(msg.tp) && this.pagesManager.appendMessage(msg)
|
isDOMType(msg.tp) && this.pagesManager.appendMessage(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -274,13 +289,13 @@ export default class TabSessionManager {
|
||||||
stateToUpdate.domContentLoadedTime = {
|
stateToUpdate.domContentLoadedTime = {
|
||||||
time: llEvent.domContentLoadedTime + this.navigationStartOffset, //TODO: predefined list of load event for the network tab (merge events & SetPageLocation: add navigationStart to db)
|
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,
|
value: llEvent.domContentLoadedTime,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
if (llEvent.loadTime != null) {
|
if (llEvent.loadTime != null) {
|
||||||
stateToUpdate.loadTime = {
|
stateToUpdate.loadTime = {
|
||||||
time: llEvent.loadTime + this.navigationStartOffset,
|
time: llEvent.loadTime + this.navigationStartOffset,
|
||||||
value: llEvent.loadTime,
|
value: llEvent.loadTime,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
if (llEvent.domBuildingTime != null) {
|
if (llEvent.domBuildingTime != null) {
|
||||||
stateToUpdate.domBuildingTime = llEvent.domBuildingTime;
|
stateToUpdate.domBuildingTime = llEvent.domBuildingTime;
|
||||||
|
|
@ -290,7 +305,7 @@ export default class TabSessionManager {
|
||||||
const lastLocationMsg = this.locationManager.moveGetLast(t, index);
|
const lastLocationMsg = this.locationManager.moveGetLast(t, index);
|
||||||
if (!!lastLocationMsg) {
|
if (!!lastLocationMsg) {
|
||||||
// @ts-ignore comes from parent state
|
// @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
|
// ConnectionInformation message is not used at this moment
|
||||||
// const lastConnectionInfoMsg = this.connectionInfoManger.moveGetLast(t, index);
|
// const lastConnectionInfoMsg = this.connectionInfoManger.moveGetLast(t, index);
|
||||||
|
|
@ -303,40 +318,42 @@ export default class TabSessionManager {
|
||||||
stateToUpdate.performanceChartTime = lastPerformanceTrackMessage.time;
|
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);
|
Object.keys(stateToUpdate).length > 0 && this.updateLocalState(stateToUpdate);
|
||||||
|
|
||||||
/* Sequence of the managers is important here */
|
/* Sequence of the managers is important here */
|
||||||
// Preparing the size of "screen"
|
// Preparing the size of "screen"
|
||||||
const lastResize = this.resizeManager.moveGetLast(t, index);
|
const lastResize = this.resizeManager.moveGetLast(t, index);
|
||||||
if (!!lastResize) {
|
if (!!lastResize) {
|
||||||
this.setSize(lastResize)
|
this.setSize(lastResize);
|
||||||
}
|
}
|
||||||
this.pagesManager.moveReady(t).then(() => {
|
this.pagesManager.moveReady(t).then(() => {
|
||||||
const lastScroll = this.scrollManager.moveGetLast(t, index);
|
const lastScroll = this.scrollManager.moveGetLast(t, index);
|
||||||
if (!!lastScroll && this.screen.window) {
|
if (!!lastScroll && this.screen.window) {
|
||||||
this.screen.window.scrollTo(lastScroll.x, lastScroll.y);
|
this.screen.window.scrollTo(lastScroll.x, lastScroll.y);
|
||||||
}
|
}
|
||||||
const canvasMsg = this.canvasReplayWalker.moveGetLast(t)
|
const canvasMsg = this.canvasReplayWalker.moveGetLast(t);
|
||||||
if (canvasMsg) {
|
if (canvasMsg) {
|
||||||
this.canvasManagers[`${canvasMsg.timestamp}_${canvasMsg.nodeId}`].manager.startVideo();
|
this.canvasManagers[`${canvasMsg.timestamp}_${canvasMsg.nodeId}`].manager.startVideo();
|
||||||
this.canvasManagers[`${canvasMsg.timestamp}_${canvasMsg.nodeId}`].running = true;
|
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) => {
|
runningManagers.forEach((key) => {
|
||||||
const manager = this.canvasManagers[key].manager;
|
const manager = this.canvasManagers[key].manager;
|
||||||
manager.move(t);
|
manager.move(t);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public decodeMessage(msg: Message) {
|
public decodeMessage(msg: Message) {
|
||||||
return this.decoder.decode(msg)
|
return this.decoder.decode(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public _sortMessagesHack = (msgs: Message[]) => {
|
public _sortMessagesHack = (msgs: Message[]) => {
|
||||||
// @ts-ignore Hack for upet (TODO: fix ordering in one mutation in tracker(removes first))
|
// @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) => {
|
this.pagesManager.sortPages((m1, m2) => {
|
||||||
if (m1.time === m2.time) {
|
if (m1.time === m2.time) {
|
||||||
if (m1.tp === MType.RemoveNode && m2.tp !== MType.RemoveNode) {
|
if (m1.tp === MType.RemoveNode && m2.tp !== MType.RemoveNode) {
|
||||||
|
|
@ -347,7 +364,7 @@ export default class TabSessionManager {
|
||||||
if (headChildrenIds.includes(m2.id)) {
|
if (headChildrenIds.includes(m2.id)) {
|
||||||
return 1;
|
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 m1FromHead = headChildrenIds.includes(m1.id);
|
||||||
const m2FromHead = headChildrenIds.includes(m2.id);
|
const m2FromHead = headChildrenIds.includes(m2.id);
|
||||||
if (m1FromHead && !m2FromHead) {
|
if (m1FromHead && !m2FromHead) {
|
||||||
|
|
@ -358,25 +375,25 @@ export default class TabSessionManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
public onFileReadSuccess = () => {
|
public onFileReadSuccess = () => {
|
||||||
const stateToUpdate : Partial<Record<string,any>> = {
|
const stateToUpdate: Partial<Record<string, any>> = {
|
||||||
performanceChartData: this.performanceTrackManager.chartData,
|
performanceChartData: this.performanceTrackManager.chartData,
|
||||||
performanceAvailability: this.performanceTrackManager.availability,
|
performanceAvailability: this.performanceTrackManager.availability,
|
||||||
urlsList: this.locationManager.list,
|
urlsList: this.locationManager.list,
|
||||||
...this.lists.getFullListsState(),
|
...this.lists.getFullListsState(),
|
||||||
}
|
};
|
||||||
|
|
||||||
this.updateLocalState(stateToUpdate)
|
this.updateLocalState(stateToUpdate);
|
||||||
}
|
};
|
||||||
|
|
||||||
public getListsFullState = () => {
|
public getListsFullState = () => {
|
||||||
return this.lists.getFullListsState()
|
return this.lists.getFullListsState();
|
||||||
}
|
};
|
||||||
|
|
||||||
clean() {
|
clean() {
|
||||||
this.pagesManager.reset()
|
this.pagesManager.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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: {
|
case 113: {
|
||||||
const selectionStart = this.readUint(); if (selectionStart === null) { return resetPointer() }
|
const selectionStart = this.readUint(); if (selectionStart === null) { return resetPointer() }
|
||||||
const selectionEnd = this.readUint(); if (selectionEnd === null) { return resetPointer() }
|
const selectionEnd = this.readUint(); if (selectionEnd === null) { return resetPointer() }
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ import type {
|
||||||
RawAdoptedSsRemoveOwner,
|
RawAdoptedSsRemoveOwner,
|
||||||
RawZustand,
|
RawZustand,
|
||||||
RawNetworkRequest,
|
RawNetworkRequest,
|
||||||
|
RawWsChannel,
|
||||||
RawSelectionChange,
|
RawSelectionChange,
|
||||||
RawMouseThrashing,
|
RawMouseThrashing,
|
||||||
RawResourceTiming,
|
RawResourceTiming,
|
||||||
|
|
@ -181,6 +182,8 @@ export type Zustand = RawZustand & Timed
|
||||||
|
|
||||||
export type NetworkRequest = RawNetworkRequest & Timed
|
export type NetworkRequest = RawNetworkRequest & Timed
|
||||||
|
|
||||||
|
export type WsChannel = RawWsChannel & Timed
|
||||||
|
|
||||||
export type SelectionChange = RawSelectionChange & Timed
|
export type SelectionChange = RawSelectionChange & Timed
|
||||||
|
|
||||||
export type MouseThrashing = RawMouseThrashing & Timed
|
export type MouseThrashing = RawMouseThrashing & Timed
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ export const enum MType {
|
||||||
AdoptedSsRemoveOwner = 77,
|
AdoptedSsRemoveOwner = 77,
|
||||||
Zustand = 79,
|
Zustand = 79,
|
||||||
NetworkRequest = 83,
|
NetworkRequest = 83,
|
||||||
|
WsChannel = 84,
|
||||||
SelectionChange = 113,
|
SelectionChange = 113,
|
||||||
MouseThrashing = 114,
|
MouseThrashing = 114,
|
||||||
ResourceTiming = 116,
|
ResourceTiming = 116,
|
||||||
|
|
@ -441,6 +442,16 @@ export interface RawNetworkRequest {
|
||||||
transferredBodySize: number,
|
transferredBodySize: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RawWsChannel {
|
||||||
|
tp: MType.WsChannel,
|
||||||
|
chType: string,
|
||||||
|
channelName: string,
|
||||||
|
data: string,
|
||||||
|
timestamp: number,
|
||||||
|
dir: string,
|
||||||
|
messageType: string,
|
||||||
|
}
|
||||||
|
|
||||||
export interface RawSelectionChange {
|
export interface RawSelectionChange {
|
||||||
tp: MType.SelectionChange,
|
tp: MType.SelectionChange,
|
||||||
selectionStart: number,
|
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;
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ export const TP_MAP = {
|
||||||
77: MType.AdoptedSsRemoveOwner,
|
77: MType.AdoptedSsRemoveOwner,
|
||||||
79: MType.Zustand,
|
79: MType.Zustand,
|
||||||
83: MType.NetworkRequest,
|
83: MType.NetworkRequest,
|
||||||
|
84: MType.WsChannel,
|
||||||
113: MType.SelectionChange,
|
113: MType.SelectionChange,
|
||||||
114: MType.MouseThrashing,
|
114: MType.MouseThrashing,
|
||||||
116: MType.ResourceTiming,
|
116: MType.ResourceTiming,
|
||||||
|
|
|
||||||
|
|
@ -442,6 +442,16 @@ type TrNetworkRequest = [
|
||||||
transferredBodySize: number,
|
transferredBodySize: number,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
type TrWSChannel = [
|
||||||
|
type: 84,
|
||||||
|
chType: string,
|
||||||
|
channelName: string,
|
||||||
|
data: string,
|
||||||
|
timestamp: number,
|
||||||
|
dir: string,
|
||||||
|
messageType: string,
|
||||||
|
]
|
||||||
|
|
||||||
type TrInputChange = [
|
type TrInputChange = [
|
||||||
type: 112,
|
type: 112,
|
||||||
id: number,
|
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 {
|
export default function translate(tMsg: TrackerMessage): RawMessage | null {
|
||||||
switch(tMsg[0]) {
|
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: {
|
case 113: {
|
||||||
return {
|
return {
|
||||||
tp: MType.SelectionChange,
|
tp: MType.SelectionChange,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ export const enum ResourceType {
|
||||||
CSS = 'css',
|
CSS = 'css',
|
||||||
IMG = 'img',
|
IMG = 'img',
|
||||||
MEDIA = 'media',
|
MEDIA = 'media',
|
||||||
|
WS = 'websocket',
|
||||||
OTHER = 'other',
|
OTHER = 'other',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -458,6 +458,15 @@ message 83, 'NetworkRequest', :replayer => :devtools do
|
||||||
uint 'TransferredBodySize'
|
uint 'TransferredBodySize'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
message 84, 'WSChannel', :replayer => :devtools do
|
||||||
|
string 'ChType'
|
||||||
|
string 'ChannelName'
|
||||||
|
string 'Data'
|
||||||
|
uint 'Timestamp'
|
||||||
|
string 'Dir'
|
||||||
|
string 'MessageType'
|
||||||
|
end
|
||||||
|
|
||||||
# 90-111 reserved iOS
|
# 90-111 reserved iOS
|
||||||
|
|
||||||
message 112, 'InputChange', :replayer => false do
|
message 112, 'InputChange', :replayer => false do
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@openreplay/tracker",
|
"name": "@openreplay/tracker",
|
||||||
"description": "The OpenReplay tracker main package",
|
"description": "The OpenReplay tracker main package",
|
||||||
"version": "11.0.2",
|
"version": "11.0.0-beta.7",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"logging",
|
"logging",
|
||||||
"replay"
|
"replay"
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ export declare const enum Type {
|
||||||
BatchMetadata = 81,
|
BatchMetadata = 81,
|
||||||
PartitionedMessage = 82,
|
PartitionedMessage = 82,
|
||||||
NetworkRequest = 83,
|
NetworkRequest = 83,
|
||||||
|
WSChannel = 84,
|
||||||
InputChange = 112,
|
InputChange = 112,
|
||||||
SelectionChange = 113,
|
SelectionChange = 113,
|
||||||
MouseThrashing = 114,
|
MouseThrashing = 114,
|
||||||
|
|
@ -512,6 +513,16 @@ export type NetworkRequest = [
|
||||||
/*transferredBodySize:*/ number,
|
/*transferredBodySize:*/ number,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export type WSChannel = [
|
||||||
|
/*type:*/ Type.WSChannel,
|
||||||
|
/*chType:*/ string,
|
||||||
|
/*channelName:*/ string,
|
||||||
|
/*data:*/ string,
|
||||||
|
/*timestamp:*/ number,
|
||||||
|
/*dir:*/ string,
|
||||||
|
/*messageType:*/ string,
|
||||||
|
]
|
||||||
|
|
||||||
export type InputChange = [
|
export type InputChange = [
|
||||||
/*type:*/ Type.InputChange,
|
/*type:*/ Type.InputChange,
|
||||||
/*id:*/ number,
|
/*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
|
export default Message
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,13 @@
|
||||||
import type Message from './messages.gen.js'
|
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 {
|
import {
|
||||||
now,
|
now,
|
||||||
adjustTimeOrigin,
|
adjustTimeOrigin,
|
||||||
|
|
@ -812,6 +820,26 @@ export default class App {
|
||||||
return this.session.getTabId()
|
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 {
|
stop(stopWorker = true): void {
|
||||||
if (this.activityState !== ActivityState.NotActive) {
|
if (this.activityState !== ActivityState.NotActive) {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -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(
|
export function InputChange(
|
||||||
id: number,
|
id: number,
|
||||||
value: string,
|
value: string,
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,20 @@ export default class API {
|
||||||
return this.app.active()
|
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<StartOptions>): Promise<StartPromiseReturn> {
|
start(startOpts?: Partial<StartOptions>): Promise<StartPromiseReturn> {
|
||||||
if (!IN_BROWSER) {
|
if (!IN_BROWSER) {
|
||||||
console.error(
|
console.error(
|
||||||
|
|
|
||||||
|
|
@ -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])
|
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
|
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:
|
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])
|
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
|
break
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue