wip (frontent): assist call functionality

This commit is contained in:
ShiKhu 2021-06-22 01:33:22 +02:00
parent 13aaa642d3
commit 3190924c1d
28 changed files with 1362 additions and 1236 deletions

View file

@ -1,14 +1,49 @@
import React from 'react'
import React, { useEffect, useRef } from 'react';
import { connect } from 'react-redux';
import { findDOMNode } from 'react-dom';
import {
PlayerProvider,
attach as attachPlayer,
init as initPlayer,
clean as cleanPlayer,
callPeer,
// scale
} from 'App/player';
import ChatWindow from './ChatWindow/ChatWindow'
import ScreenSharing from './ScreenSharing/ScreenSharing'
//import ScreenSharing from './ScreenSharing/ScreenSharing'
function Assist() {
function Assist({ session, jwt }) {
const screeRef = useRef<HTMLDivElement>(null);
useEffect(() => {
initPlayer(session, jwt);
return () => cleanPlayer()
}, [ session.sessionId ]);
useEffect(() => {
if (screeRef.current) {
attachPlayer(findDOMNode(screeRef.current));
}
}, [ ])
return (
<div>
<ChatWindow />
<div className="h-screen w-screen">
<div ref={screeRef}
// Just for testing TODO: flexible layout.
// It should consider itself as empty but take maximum of the space available
// Screen will adapt automatically.
style={{margin: "100px", height: "300px", width:"600px" }}
className="relative overflow-hidden bg-gray-lightest"
/>
<ChatWindow call={ callPeer } />
{/* <ScreenSharing /> */}
</div>
)
}
export default Assist
export default connect(state => ({
session: { // Testing mock. Should be retrieved from redux
startedAt: 1624314191394,
live: true,
sessionId: "4870254843916045",
},
jwt: state.get('jwt'),
}))(Assist);

View file

@ -1,14 +1,35 @@
import React from 'react'
import VideoContainer from '../components/VideoContainer/VideoContainer'
import React, { useState } from 'react';
import { IconButton } from 'UI';
import VideoContainer from '../components/VideoContainer';
// import stl from './chatWindow.css';
function ChatWindow() {
interface Props {
call: (oStream: MediaStream, cb: (iStream: MediaStream)=>void)=>void
}
function ChatWindow({ call }: Props) {
const [ inputStream, setInputStream ] = useState<MediaStream | null>(null);
const [ outputStream, setOutputStream ] = useState<MediaStream | null>(null);
const onCallClick = () => {
navigator.mediaDevices.getUserMedia({video:true, audio:true})
.then(oStream => {
setOutputStream(oStream);
call(oStream, setInputStream); // Returns false when unable to connect.
// TODO: handle calling state
})
.catch(console.log) // TODO: handle error in ui
}
return (
<div className="fixed border radius bg-white z-50 shadow-xl mt-16">
<div className="p-2">
<VideoContainer />
<VideoContainer stream={ inputStream } />
<div className="py-1" />
<VideoContainer />
<VideoContainer stream={ outputStream } muted/>
<div className="cursor-pointer p-2 mr-2">
<IconButton icon="telephone" size="20" onClick={ onCallClick }/>
</div>
</div>
</div>
)

View file

@ -2,12 +2,11 @@ import React from 'react'
import { Button } from 'UI'
function ScreenSharing() {
const videoRef: React.RefObject<HTMLVideoElement> = React.createRef()
const videoRef = React.createRef<HTMLVideoElement>()
function handleSuccess(stream) {
// startButton.disabled = true;
// @ts-ignore
videoRef.current.srcObject = stream;
//videoRef.current?.srcObject = stream;
// @ts-ignore
window.stream = stream; // make variable available to browser console

View file

@ -1,13 +1,22 @@
import React, { useEffect } from 'react'
import React, { useEffect, useRef } from 'react'
import { Button, Icon } from 'UI'
function VideoContainer() {
interface Props {
stream: MediaProvider | null
muted?: boolean
}
function VideoContainer({ stream, muted = false }: Props) {
const ref = useRef<HTMLVideoElement>(null);
useEffect(() => {
// TODO check for video stream and display
}, [])
if (ref.current) {
ref.current.srcObject = stream;
}
}, [ ref.current, stream ])
return (
<div className="relative h-20 bg-gray-light-shade border p-1" style={{ height: '160px', width: '200px' }}>
<div className="absolute left-0 right-0 bottom-0 flex justify-center border border-gray-300 p-1 bg-white radius bg-opacity-25">
<video autoPlay ref={ ref } muted={ muted } />
<Button plain size="small">
<Icon name="mic" size="16" />
</Button>

View file

@ -124,7 +124,7 @@ export default class Player extends React.PureComponent {
// label="Esc"
// />
}
<div className={ stl.playerView }>
<div className="relative flex-1">
{ !inspectorMode &&
<div
className={ stl.overlay }

View file

@ -1,478 +0,0 @@
//@flow
import { Decoder } from "syncod";
import logger from 'App/logger';
import Resource, { TYPES } from 'Types/session/resource'; // MBTODO: player types?
import { TYPES as EVENT_TYPES } from 'Types/session/event';
import Log from 'Types/session/log';
import Profile from 'Types/session/profile';
import ReduxAction from 'Types/session/reduxAction';
import { update } from '../store';
import {
init as initListsDepr,
append as listAppend,
setStartTime as setListsStartTime
} from '../lists';
import StatedScreen from './StatedScreen';
import ListWalker from './managers/ListWalker';
import PagesManager from './managers/PagesManager';
import MouseManager from './managers/MouseManager';
import PerformanceTrackManager from './managers/PerformanceTrackManager';
import WindowNodeCounter from './managers/WindowNodeCounter';
import ActivityManager from './managers/ActivityManager';
import MessageGenerator from './MessageGenerator';
import { INITIAL_STATE as PARENT_INITIAL_STATE } from './StatedScreen';
const LIST_NAMES = [ "redux", "mobx", "vuex", "ngrx", "graphql", "exceptions", "profiles", "longtasks" ]
const LISTS_INITIAL_STATE = {};
LIST_NAMES.forEach(name => {
LISTS_INITIAL_STATE[`${name}ListNow`] = [];
LISTS_INITIAL_STATE[`${name}List`] = [];
})
export const INITIAL_STATE = {
...PARENT_INITIAL_STATE,
...LISTS_INITIAL_STATE,
performanceChartData: [],
skipIntervals: [],
}
function initLists() {
const lists = {};
for (var i = 0; i < LIST_NAMES.length; i++) {
lists[ LIST_NAMES[i] ] = new ListWalker();
}
return lists;
}
import type {
Message,
SetLocation,
SetTitle,
ConnectionInformation,
SetViewportSize,
SetViewportScroll,
} from './messages';
type ReduxDecoded = Timed & {
action: {},
state: {},
duration: number,
}
export default class MessageDistributor extends StatedScreen {
// TODO: consistent with the other data-lists
#locationEventManager: ListWalker<> = new ListWalker();
#locationManager: ListWalker<SetLocation> = new ListWalker();
#loadedLocationManager: ListWalker<SetLocation> = new ListWalker();
#titleManager: ListWalker<SetTitle> = new ListWalker();
#connectionInfoManger: ListWalker<ConnectionInformation> = new ListWalker();
#performanceTrackManager: PerformanceTrackManager = new PerformanceTrackManager();
#windowNodeCounter: WindowNodeCounter = new WindowNodeCounter();
#clickManager: ListWalker = new ListWalker();
#resizeManager: ListWalker<SetViewportSize> = new ListWalker();
#pagesManager: PagesManager;
#mouseManager: MouseManager;
#scrollManager: ListWalker<SetViewportScroll> = new ListWalker();
#decoder = new Decoder();
#lists = initLists();
#activirtManager: ActivityManager;
#sessionStart: number;
#navigationStartOffset: number = 0;
#lastMessageTime: number = 0;
constructor(sess: any /*Session*/, jwt: string) {
super();
this.#pagesManager = new PagesManager(this, sess.isMobile)
this.#mouseManager = new MouseManager(this);
this.#activirtManager = new ActivityManager(sess.duration.milliseconds);
this.#sessionStart = sess.startedAt;
/* == REFACTOR_ME == */
const eventList = sess.events.toJSON();
initListsDepr({
event: eventList,
stack: sess.stackEvents.toJSON(),
resource: sess.resources.toJSON(),
});
eventList.forEach(e => {
if (e.type === EVENT_TYPES.LOCATION) { //TODO type system
this.#locationEventManager.add(e);
}
if (e.type === EVENT_TYPES.CLICK) {
this.#clickManager.add(e);
}
});
sess.errors.forEach(e => {
this.#lists.exceptions.add(e);
});
/* === */
if (sess.live) {
// const sockUrl = `wss://live.openreplay.com/1/${ sess.siteId }/${ sess.sessionId }/${ jwt }`;
// this.#subscribeOnMessages(sockUrl);
} else {
this._loadMessages(sess.mobsUrl);
}
}
// #subscribeOnMessages(sockUrl) {
// this.setMessagesLoading(true);
// const socket = new WebSocket(sockUrl);
// socket.binaryType = 'arraybuffer';
// socket.onerror = (e) => {
// // TODO: reconnect
// update({ error: true });
// }
// socket.onmessage = (socketMessage) => {
// const data = new Uint8Array(socketMessage.data);
// const msgs = [];
// messageGenerator // parseBuffer(msgs, data);
// // TODO: count indexes. Now will not work due to wrong indexes
// //msgs.forEach(this.#distributeMessage);
// this.setMessagesLoading(false);
// this.setDisconnected(false);
// }
// this._socket = socket;
// }
_loadMessages(fileUrl): void {
this.setMessagesLoading(true);
window.fetch(fileUrl)
.then(r => r.arrayBuffer())
.then(b => {
const mGen = new MessageGenerator(new Uint8Array(b), this.#sessionStart);
let mCount = 0;
const msgs = [];
while (mGen.hasNext()) {
mCount++;
const next = mGen.next();
if (next != null) {
this.#lastMessageTime = next[0].time;
this.#distributeMessage(next[0], next[1]);
msgs.push(next[0]);
}
}
// Hack for upet (TODO: fix ordering in one mutation (removes first))
const headChildrenIds = msgs.filter(m => m.parentID === 1).map(m => m.id);
//const createNodeTypes = ["create_text_node", "create_element_node"];
this.#pagesManager.sort((m1, m2) =>{
if (m1.time === m2.time) {
if (m1.tp === "remove_node" && m2.tp !== "remove_node") {
if (headChildrenIds.includes(m1.id)) {
return -1;
}
} else if (m2.tp === "remove_node" && m1.tp !== "remove_node") {
if (headChildrenIds.includes(m2.id)) {
return 1;
}
} else if (m2.tp === "remove_node" && m1.tp === "remove_node") {
const m1FromHead = headChildrenIds.includes(m1.id);
const m2FromHead = headChildrenIds.includes(m2.id);
if (m1FromHead && !m2FromHead) {
return -1;
} else if (m2FromHead && !m1FromHead) {
return 1;
}
}
}
return 0;
})
//
logger.info("Messages count: ", mCount, msgs);
const stateToUpdate = {
performanceChartData: this.#performanceTrackManager.chartData,
performanceAvaliability: this.#performanceTrackManager.avaliability,
};
this.#activirtManager.end();
stateToUpdate.skipIntervals = this.#activirtManager.list;
LIST_NAMES.forEach(key => {
stateToUpdate[ `${ key }List` ] = this.#lists[ key ].list;
});
update(stateToUpdate);
this.#windowNodeCounter.reset();
this.setMessagesLoading(false);
})
.catch((e) => {
logger.error(e);
this.setMessagesLoading(false);
update({ error: true });
});
}
move(t: number, index: ?number):void {
const stateToUpdate = {};
/* == REFACTOR_ME == */
const lastLoadedLocationMsg = this.#loadedLocationManager.moveToLast(t, index);
if (!!lastLoadedLocationMsg) {
setListsStartTime(lastLoadedLocationMsg.time)
this.#navigationStartOffset = lastLoadedLocationMsg.navigationStart - this.#sessionStart;
}
const llEvent = this.#locationEventManager.moveToLast(t, index);
if (!!llEvent) {
if (llEvent.domContentLoadedTime != null) {
stateToUpdate.domContentLoadedTime = {
time: llEvent.domContentLoadedTime + this.#navigationStartOffset, //TODO: predefined list of load event for the network tab (merge events & setLocation: 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;
}
}
/* === */
const lastLocationMsg = this.#locationManager.moveToLast(t, index);
if (!!lastLocationMsg) {
stateToUpdate.location = lastLocationMsg.url;
}
const lastTitleMsg = this.#titleManager.moveToLast(t, index);
if (!!lastTitleMsg) {
stateToUpdate.title = lastTitleMsg.title;
}
const lastConnectionInfoMsg = this.#connectionInfoManger.moveToLast(t, index);
if (!!lastConnectionInfoMsg) {
stateToUpdate.connType = lastConnectionInfoMsg.type;
stateToUpdate.connBandwidth = lastConnectionInfoMsg.downlink;
}
const lastPerformanceTrackMessage = this.#performanceTrackManager.moveToLast(t, index);
if (!!lastPerformanceTrackMessage) {
stateToUpdate.performanceChartTime = lastPerformanceTrackMessage.time;
}
LIST_NAMES.forEach(key => {
const lastMsg = this.#lists[ key ].moveToLast(t, key === 'exceptions' ? null : index);
if (lastMsg != null) {
stateToUpdate[`${key}ListNow`] = this.#lists[ key ].listNow;
}
});
update(stateToUpdate);
/* Sequence of the managers is important here */
// Preparing the size of "screen"
const lastResize = this.#resizeManager.moveToLast(t, index);
if (!!lastResize) {
this.setSize(lastResize)
}
this.#pagesManager.moveReady(t).then(() => {
const lastScroll = this.#scrollManager.moveToLast(t, index);
if (!!lastScroll && this.window) {
this.window.scrollTo(lastScroll.x, lastScroll.y);
}
// Moving mouse and setting :hover classes on ready view
this.#mouseManager.move(t);
const lastClick = this.#clickManager.moveToLast(t);
// if (!!lastClick) {
// this.cursor.click();
// }
// After all changes - redraw the marker
//this.marker.redraw();
})
}
_decodeMessage(msg, keys: Array<string>) {
const decoded = {};
try {
keys.forEach(key => {
decoded[ key ] = this.#decoder.decode(msg[ key ]);
});
} catch (e) {
logger.error("Error on message decoding: ", e, msg);
return null;
}
return { ...msg, ...decoded };
}
/* Binded */
#distributeMessage = (msg: Message, index: number): void => {
if ([
"mouse_move",
"set_input_value",
"set_input_checked",
"set_viewport_size",
"set_viewport_scroll",
].includes(msg.tp)) {
this.#activirtManager.updateAcctivity(msg.time);
}
//const index = #i + index; //?
let decoded;
const time = msg.time;
switch (msg.tp) {
/* Lists: */
case "resource_timing":
logger.log(msg)
listAppend("resource", Resource({
time,
duration: msg.duration,
ttfb: msg.ttfb,
url: msg.url,
initiator: msg.initiator,
index,
}));
break;
case "console_log":
if (msg.level === 'debug') break;
listAppend("log", Log({
level: msg.level,
value: msg.value,
time,
index,
}));
break;
case "fetch":
listAppend("fetch", Resource({
method: msg.method,
url: msg.url,
payload: msg.request,
response: msg.response,
status: msg.status,
duration: msg.duration,
type: TYPES.FETCH,
time: msg.timestamp - this.#sessionStart, //~
index,
}));
break;
/* */
case "set_page_location":
this.#locationManager.add(msg);
if (msg.navigationStart > 0) {
this.#loadedLocationManager.add(msg);
}
break;
case "set_title":
this.#titleManager.add(msg);
break;
case "set_viewport_size":
this.#resizeManager.add(msg);
break;
case "mouse_move":
this.#mouseManager.add(msg);
break;
case "set_viewport_scroll":
this.#scrollManager.add(msg);
break;
case "performance_track":
this.#performanceTrackManager.add(msg);
break;
case "set_page_visibility":
this.#performanceTrackManager.handleVisibility(msg)
break;
case "connection_information":
this.#connectionInfoManger.add(msg);
break;
case "o_table":
this.#decoder.set(msg.key, msg.value);
break;
case "redux":
decoded = this._decodeMessage(msg, ["state", "action"]);
logger.log(decoded)
if (decoded != null) {
this.#lists.redux.add(decoded);
}
break;
case "ng_rx":
decoded = this._decodeMessage(msg, ["state", "action"]);
logger.log(decoded)
if (decoded != null) {
this.#lists.ngrx.add(decoded);
}
break;
case "vuex":
decoded = this._decodeMessage(msg, ["state", "mutation"]);
logger.log(decoded)
if (decoded != null) {
this.#lists.vuex.add(decoded);
}
break;
case "mob_x":
decoded = this._decodeMessage(msg, ["payload"]);
logger.log(decoded)
if (decoded != null) {
this.#lists.mobx.add(decoded);
}
break;
case "graph_ql":
msg.duration = 0;
this.#lists.graphql.add(msg);
break;
case "profiler":
this.#lists.profiles.add(msg);
break;
case "long_task":
this.#lists.longtasks.add({
...msg,
time: msg.timestamp - this.#sessionStart,
});
break;
default:
switch (msg.tp){
case "create_document":
this.#windowNodeCounter.reset();
this.#performanceTrackManager.setCurrentNodesCount(this.#windowNodeCounter.count);
break;
case "create_text_node":
case "create_element_node":
this.#windowNodeCounter.addNode(msg.id, msg.parentID);
this.#performanceTrackManager.setCurrentNodesCount(this.#windowNodeCounter.count);
break;
case "move_node":
this.#windowNodeCounter.moveNode(msg.id, msg.parentID);
this.#performanceTrackManager.setCurrentNodesCount(this.#windowNodeCounter.count);
break;
case "remove_node":
this.#windowNodeCounter.removeNode(msg.id);
this.#performanceTrackManager.setCurrentNodesCount(this.#windowNodeCounter.count);
break;
}
this.#pagesManager.add(msg);
break;
}
}
getLastMessageTime():number {
return this.#lastMessageTime;
}
getFirstMessageTime():number {
return 0; //this.#pagesManager.minTime;
}
// TODO: clean managers?
clean() {
super.clean();
if (this._socket) this._socket.close();
update(INITIAL_STATE);
}
}

View file

@ -0,0 +1,526 @@
import { Decoder } from "syncod";
import logger from 'App/logger';
import Resource, { TYPES } from 'Types/session/resource'; // MBTODO: player types?
import { TYPES as EVENT_TYPES } from 'Types/session/event';
import Log from 'Types/session/log';
import Profile from 'Types/session/profile';
import ReduxAction from 'Types/session/reduxAction';
import { update } from '../store';
import {
init as initListsDepr,
append as listAppend,
setStartTime as setListsStartTime
} from '../lists';
import StatedScreen from './StatedScreen';
import ListWalker from './managers/ListWalker';
import PagesManager from './managers/PagesManager';
import MouseManager from './managers/MouseManager';
import PerformanceTrackManager from './managers/PerformanceTrackManager';
import WindowNodeCounter from './managers/WindowNodeCounter';
import ActivityManager from './managers/ActivityManager';
import MessageReader from './MessageReader';
import { ID_TP_MAP } from './messages';
import { INITIAL_STATE as PARENT_INITIAL_STATE } from './StatedScreen';
import type Peer from 'peerjs';
import type { TimedMessage } from './Timed';
const LIST_NAMES = [ "redux", "mobx", "vuex", "ngrx", "graphql", "exceptions", "profiles", "longtasks" ] as const;
const LISTS_INITIAL_STATE = {};
LIST_NAMES.forEach(name => {
LISTS_INITIAL_STATE[`${name}ListNow`] = [];
LISTS_INITIAL_STATE[`${name}List`] = [];
})
export const INITIAL_STATE = {
...PARENT_INITIAL_STATE,
...LISTS_INITIAL_STATE,
performanceChartData: [],
skipIntervals: [],
} as const;
type ListsObject = {
[key in typeof LIST_NAMES[number]]: ListWalker<any> //
}
function initLists(): ListsObject {
const lists: Partial<ListsObject> = {} ;
for (var i = 0; i < LIST_NAMES.length; i++) {
lists[ LIST_NAMES[i] ] = new ListWalker();
}
return lists as ListsObject;
}
import type {
Message,
SetPageLocation,
ConnectionInformation,
SetViewportSize,
SetViewportScroll,
} from './messages';
interface Timed { //TODO: to common space
time: number;
}
type ReduxDecoded = Timed & {
action: {},
state: {},
duration: number,
}
export default class MessageDistributor extends StatedScreen {
// TODO: consistent with the other data-lists
private readonly locationEventManager: ListWalker<any>/*<LocationEvent>*/ = new ListWalker();
private readonly locationManager: ListWalker<SetPageLocation & Timed> = new ListWalker();
private readonly loadedLocationManager: ListWalker<SetPageLocation & Timed> = new ListWalker();
private readonly connectionInfoManger: ListWalker<ConnectionInformation & Timed> = new ListWalker();
private readonly performanceTrackManager: PerformanceTrackManager = new PerformanceTrackManager();
private readonly windowNodeCounter: WindowNodeCounter = new WindowNodeCounter();
private readonly clickManager: ListWalker<Timed> = new ListWalker();
private readonly resizeManager: ListWalker<SetViewportSize & Timed> = new ListWalker([]);
private readonly pagesManager: PagesManager;
private readonly mouseManager: MouseManager;
private readonly scrollManager: ListWalker<SetViewportScroll & Timed> = new ListWalker();
private readonly decoder = new Decoder();
private readonly lists = initLists();
private activirtManager: ActivityManager | null = null;
private readonly sessionStart: number;
private navigationStartOffset: number = 0;
private lastMessageTime: number = 0;
constructor(private readonly session: any /*Session*/, jwt: string) {
super();
this.pagesManager = new PagesManager(this, this.session.isMobile)
this.mouseManager = new MouseManager(this);
this.sessionStart = this.session.startedAt;
if (this.session.live) {
// const sockUrl = `wss://live.openreplay.com/1/${ this.session.siteId }/${ this.session.sessionId }/${ jwt }`;
// this.subscribeOnMessages(sockUrl);
initListsDepr({})
this.connectToPeer();
} else {
this.activirtManager = new ActivityManager(this.session.duration.milliseconds);
/* == REFACTOR_ME == */
const eventList = this.session.events.toJSON();
initListsDepr({
event: eventList,
stack: this.session.stackEvents.toJSON(),
resource: this.session.resources.toJSON(),
});
eventList.forEach(e => {
if (e.type === EVENT_TYPES.LOCATION) { //TODO type system
this.locationEventManager.add(e);
}
if (e.type === EVENT_TYPES.CLICK) {
this.clickManager.add(e);
}
});
this.session.errors.forEach(e => {
this.lists.exceptions.add(e);
});
/* === */
this._loadMessages();
}
}
private peer: Peer | null = null;
private connectToPeer() {
this.setMessagesLoading(true);
import('peerjs').then(({ default: Peer }) => {
const peer = new Peer();
this.peer = peer;
peer.on("open", me => {
console.log("peer opened", me);
const id = `Amva-sf98-234fd-OR-test-${this.session.sessionId}`;
console.log("trying to connect to", id)
const conn = peer.connect(id);
conn.on('open', () => {
this.setMessagesLoading(false);
let i = 0;
console.log("peer connected")
conn.on('data', (data) => {
if (!Array.isArray(data)) { return; }
let time = 0;
let ts0 = 0;
(data as Array<Message & { _id: number}>).forEach(msg => {
msg.tp = ID_TP_MAP[msg._id]; // _id goes from tracker
if (msg.tp === "timestamp") {
ts0 = ts0 || msg.timestamp
time = msg.timestamp - ts0;
return;
}
const tMsg: TimedMessage = Object.assign(msg, {
time,
_index: i,
});
this.distributeMessage(tMsg, i++);
});
});
});
});
});
}
callPeer(localStream: MediaStream, cb: (s: MediaStream)=>void): boolean {
if (!this.peer) { return false; }
const conn = this.peer.connections[`Amva-sf98-234fd-OR-test-${this.session.sessionId}`]?.[0];
if (!conn || !conn.open) { return false; } // Conn not established
const call = this.peer.call(conn.peer, localStream);
console.log('calling...')
// on refuse?
call.on('stream', function(stream) {
cb(stream);
});
//@ts-ignore
this.document?.
addEventListener("mousemove", ({ x, y }) => {
conn.send([ x, y ]); // debounce?
});
return true;
}
// subscribeOnMessages(sockUrl) {
// this.setMessagesLoading(true);
// const socket = new WebSocket(sockUrl);
// socket.binaryType = 'arraybuffer';
// socket.onerror = (e) => {
// // TODO: reconnect
// update({ error: true });
// }
// socket.onmessage = (socketMessage) => {
// const data = new Uint8Array(socketMessage.data);
// const msgs = [];
// messageGenerator // parseBuffer(msgs, data);
// // TODO: count indexes. Now will not work due to wrong indexes
// //msgs.forEach(this.distributeMessage);
// this.setMessagesLoading(false);
// this.setDisconnected(false);
// }
// this._socket = socket;
// }
_loadMessages(): void {
const fileUrl: string = this.session.mobsUrl;
this.setMessagesLoading(true);
window.fetch(fileUrl)
.then(r => r.arrayBuffer())
.then(b => {
const r = new MessageReader(new Uint8Array(b), this.sessionStart);
const msgs: Array<Message> = [];
while (r.hasNext()) {
const next = r.next();
if (next != null) {
this.lastMessageTime = next[0].time;
this.distributeMessage(next[0], next[1]);
msgs.push(next[0]);
}
}
// @ts-ignore Hack for upet (TODO: fix ordering in one mutation (removes first))
const headChildrenIds = msgs.filter(m => m.parentID === 1).map(m => m.id);
//const createNodeTypes = ["create_text_node", "create_element_node"];
this.pagesManager.sort((m1, m2) =>{
if (m1.time === m2.time) {
if (m1.tp === "remove_node" && m2.tp !== "remove_node") {
if (headChildrenIds.includes(m1.id)) {
return -1;
}
} else if (m2.tp === "remove_node" && m1.tp !== "remove_node") {
if (headChildrenIds.includes(m2.id)) {
return 1;
}
} else if (m2.tp === "remove_node" && m1.tp === "remove_node") {
const m1FromHead = headChildrenIds.includes(m1.id);
const m2FromHead = headChildrenIds.includes(m2.id);
if (m1FromHead && !m2FromHead) {
return -1;
} else if (m2FromHead && !m1FromHead) {
return 1;
}
}
}
return 0;
})
//
logger.info("Messages count: ", msgs.length, msgs);
const stateToUpdate: {[key:string]: any} = {
performanceChartData: this.performanceTrackManager.chartData,
performanceAvaliability: this.performanceTrackManager.avaliability,
};
this.activirtManager?.end();
stateToUpdate.skipIntervals = this.activirtManager?.list || [];
LIST_NAMES.forEach(key => {
stateToUpdate[ `${ key }List` ] = this.lists[ key ].list;
});
update(stateToUpdate);
this.windowNodeCounter.reset();
this.setMessagesLoading(false);
})
.catch((e) => {
logger.error(e);
this.setMessagesLoading(false);
update({ error: true });
});
}
move(t: number, index?: number):void {
const stateToUpdate: typeof INITIAL_STATE = {};
/* == REFACTOR_ME == */
const lastLoadedLocationMsg = this.loadedLocationManager.moveToLast(t, index);
if (!!lastLoadedLocationMsg) {
setListsStartTime(lastLoadedLocationMsg.time)
this.navigationStartOffset = lastLoadedLocationMsg.navigationStart - this.sessionStart;
}
const llEvent = this.locationEventManager.moveToLast(t, index);
if (!!llEvent) {
if (llEvent.domContentLoadedTime != null) {
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;
}
}
/* === */
const lastLocationMsg = this.locationManager.moveToLast(t, index);
if (!!lastLocationMsg) {
stateToUpdate.location = lastLocationMsg.url;
}
const lastConnectionInfoMsg = this.connectionInfoManger.moveToLast(t, index);
if (!!lastConnectionInfoMsg) {
stateToUpdate.connType = lastConnectionInfoMsg.type;
stateToUpdate.connBandwidth = lastConnectionInfoMsg.downlink;
}
const lastPerformanceTrackMessage = this.performanceTrackManager.moveToLast(t, index);
if (!!lastPerformanceTrackMessage) {
stateToUpdate.performanceChartTime = lastPerformanceTrackMessage.time;
}
LIST_NAMES.forEach(key => {
const lastMsg = this.lists[ key ].moveToLast(t, key === 'exceptions' ? undefined : index);
if (lastMsg != null) {
stateToUpdate[`${key}ListNow`] = this.lists[ key ].listNow;
}
});
update(stateToUpdate);
/* Sequence of the managers is important here */
// Preparing the size of "screen"
const lastResize = this.resizeManager.moveToLast(t, index);
if (!!lastResize) {
this.setSize(lastResize)
}
this.pagesManager.moveReady(t).then(() => {
const lastScroll = this.scrollManager.moveToLast(t, index);
// @ts-ignore ??can't see double inheritance
if (!!lastScroll && this.window) {
// @ts-ignore
this.window.scrollTo(lastScroll.x, lastScroll.y);
}
// Moving mouse and setting :hover classes on ready view
this.mouseManager.move(t);
const lastClick = this.clickManager.moveToLast(t);
// if (!!lastClick) {
// this.cursor.click();
// }
// After all changes - redraw the marker
//this.marker.redraw();
})
}
_decodeMessage(msg, keys: Array<string>) {
const decoded = {};
try {
keys.forEach(key => {
decoded[ key ] = this.decoder.decode(msg[ key ]);
});
} catch (e) {
logger.error("Error on message decoding: ", e, msg);
return null;
}
return { ...msg, ...decoded };
}
/* Binded */
distributeMessage = (msg: TimedMessage, index: number): void => {
if ([
"mouse_move",
"set_input_value",
"set_input_checked",
"set_viewport_size",
"set_viewport_scroll",
].includes(msg.tp)) {
this.activirtManager?.updateAcctivity(msg.time);
}
//const index = i + index; //?
let decoded;
const time = msg.time;
switch (msg.tp) {
/* Lists: */
case "console_log":
if (msg.level === 'debug') break;
listAppend("log", Log({
level: msg.level,
value: msg.value,
time,
index,
}));
break;
case "fetch":
listAppend("fetch", Resource({
method: msg.method,
url: msg.url,
payload: msg.request,
response: msg.response,
status: msg.status,
duration: msg.duration,
type: TYPES.FETCH,
time: msg.timestamp - this.sessionStart, //~
index,
}));
break;
/* */
case "set_page_location":
this.locationManager.add(msg);
if (msg.navigationStart > 0) {
this.loadedLocationManager.add(msg);
}
break;
case "set_viewport_size":
console.log("setvvs", msg)
this.resizeManager.add(msg);
break;
case "mouse_move":
this.mouseManager.add(msg);
break;
case "set_viewport_scroll":
this.scrollManager.add(msg);
break;
case "performance_track":
this.performanceTrackManager.add(msg);
break;
case "set_page_visibility":
this.performanceTrackManager.handleVisibility(msg)
break;
case "connection_information":
this.connectionInfoManger.add(msg);
break;
case "o_table":
this.decoder.set(msg.key, msg.value);
break;
case "redux":
decoded = this._decodeMessage(msg, ["state", "action"]);
logger.log(decoded)
if (decoded != null) {
this.lists.redux.add(decoded);
}
break;
case "ng_rx":
decoded = this._decodeMessage(msg, ["state", "action"]);
logger.log(decoded)
if (decoded != null) {
this.lists.ngrx.add(decoded);
}
break;
case "vuex":
decoded = this._decodeMessage(msg, ["state", "mutation"]);
logger.log(decoded)
if (decoded != null) {
this.lists.vuex.add(decoded);
}
break;
case "mob_x":
decoded = this._decodeMessage(msg, ["payload"]);
logger.log(decoded)
if (decoded != null) {
this.lists.mobx.add(decoded);
}
break;
case "graph_ql":
// @ts-ignore some hack? TODO: remove
msg.duration = 0;
this.lists.graphql.add(msg);
break;
case "profiler":
this.lists.profiles.add(msg);
break;
case "long_task":
this.lists.longtasks.add({
...msg,
time: msg.timestamp - this.sessionStart,
});
break;
default:
switch (msg.tp){
case "create_document":
this.windowNodeCounter.reset();
this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count);
break;
case "create_text_node":
case "create_element_node":
this.windowNodeCounter.addNode(msg.id, msg.parentID);
this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count);
break;
case "move_node":
this.windowNodeCounter.moveNode(msg.id, msg.parentID);
this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count);
break;
case "remove_node":
this.windowNodeCounter.removeNode(msg.id);
this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count);
break;
}
this.pagesManager.add(msg);
break;
}
}
getLastMessageTime():number {
return this.lastMessageTime;
}
getFirstMessageTime():number {
return 0; //this.pagesManager.minTime;
}
// TODO: clean managers?
clean() {
// @ts-ignore
super.clean();
//if (this._socket) this._socket.close();
update(INITIAL_STATE);
}
}

View file

@ -1,82 +0,0 @@
import type { TimedMessage } from './Timed';
import logger from 'App/logger';
import readMessage from './messages';
function needSkipMessage(data: Uint8Array, p: number, pLast: number): boolean {
for (let i = 7; i >= 0; i--) {
if (data[ p + i ] !== data[ pLast + i ]) {
return data[ p + i ] - data[ pLast + i ] < 0
}
}
return true
}
export default class MessageGenerator {
#data: Uint8Array;
#p: number = 0;
#pLastMessageID: number = 0;
#startTime: number;
#currentTime: ?number;
#error: boolean = false;
constructor(data: Uint8Array, startTime: number) {
this.#startTime = startTime;
this.#data = data;
}
_needSkipMessage():boolean {
if (this.#p === 0) return false;
for (let i = 7; i >= 0; i--) {
if (this.#data[ this.#p + i ] !== this.#data[ this.#pLastMessageID + i ]) {
return this.#data[ this.#p + i ] - this.#data[ this.#pLastMessageID + i ] < 0;
}
}
return true;
}
_readMessage(): ?Message {
this.#p += 8;
try {
let msg
[ msg, this.#p ] = readMessage(this.#data, this.#p);
return msg;
} catch (e) {
this.#error = true;
logger.error("Read message error:", e);
return null;
}
}
hasNext():boolean {
return !this.#error && this.#data.length > this.#p;
}
next(): ?[ TimedMessage, number] {
if (!this.hasNext()) {
return null;
}
while (this._needSkipMessage()) {
this._readMessage();
}
this.#pLastMessageID = this.#p;
const msg = this._readMessage();
if (!msg) {
return null;
}
if (msg.tp === "timestamp") {
// if (this.#startTime == null) {
// this.#startTime = msg.timestamp
// }
this.#currentTime = msg.timestamp - this.#startTime;
} else {
msg.time = this.#currentTime;
msg._index = this.#pLastMessageID;
return [msg, this.#pLastMessageID];
}
}
}

View file

@ -0,0 +1,80 @@
import type { TimedMessage, Indexed } from './Timed';
import logger from 'App/logger';
import readMessage, { Message } from './messages';
import PrimitiveReader from './PrimitiveReader';
// function needSkipMessage(data: Uint8Array, p: number, pLast: number): boolean {
// for (let i = 7; i >= 0; i--) {
// if (data[ p + i ] !== data[ pLast + i ]) {
// return data[ p + i ] - data[ pLast + i ] < 0
// }
// }
// return true
// }
export default class MessageReader extends PrimitiveReader {
private pLastMessageID: number = 0;
private currentTime: number = 0;
public error: boolean = false;
constructor(data: Uint8Array, private readonly startTime: number) {
super(data);
}
private needSkipMessage(): boolean {
if (this.p === 0) return false;
for (let i = 7; i >= 0; i--) {
if (this.buf[ this.p + i ] !== this.buf[ this.pLastMessageID + i ]) {
return this.buf[ this.p + i ] - this.buf[ this.pLastMessageID + i ] < 0;
}
}
return true;
}
private readMessage(): Message | null {
this.skip(8);
try {
let msg
msg = readMessage(this);
return msg;
} catch (e) {
this.error = true;
logger.error("Read message error:", e);
return null;
}
}
hasNext():boolean {
return !this.error && this.buf.length > this.p;
}
next(): [ TimedMessage, number] | null {
if (!this.hasNext()) {
return null;
}
while (this.needSkipMessage()) {
this.readMessage();
}
this.pLastMessageID = this.p;
const msg = this.readMessage();
if (!msg) {
return null;
}
if (msg.tp === "timestamp") {
// if (this.startTime == null) {
// this.startTime = msg.timestamp
// }
this.currentTime = msg.timestamp - this.startTime;
} else {
const tMsg = Object.assign(msg, {
time: this.currentTime,
_index: this.pLastMessageID,
})
return [tMsg, this.pLastMessageID];
}
return null;
}
}

View file

@ -0,0 +1,36 @@
export default class PrimitiveReader {
protected p = 0
constructor(protected readonly buf: Uint8Array) {}
readUint() {
var r = 0, s = 1, b;
do {
b = this.buf[this.p++];
r += (b & 0x7F) * s;
s *= 128;
} while (b >= 0x80)
return r;
}
readInt() {
let u = this.readUint();
if (u % 2) {
u = (u + 1) / -2;
} else {
u = u / 2;
}
return u;
}
readString() {
var l = this.readUint();
return new TextDecoder().decode(this.buf.subarray(this.p, this.p+=l));
}
readBoolean() {
return !!this.buf[this.p++];
}
skip(n: number) {
this.p += n;
}
}

View file

@ -9,6 +9,7 @@
.iframe {
position: absolute;
border: none;
background: whilte;
}
.overlay {
position: absolute;

View file

@ -12,28 +12,34 @@ export const INITIAL_STATE = {
export default class StatedScreen extends Screen {
setMessagesLoading(messagesLoading) {
// @ts-ignore
this.display(!messagesLoading);
update({ messagesLoading });
}
setCSSLoading(cssLoading) {
// @ts-ignore
this.displayFrame(!cssLoading);
update({ cssLoading });
}
setDisconnected(disconnected) {
if (!getState().live) return; //?
// @ts-ignore
this.display(!disconnected);
update({ disconnected });
}
setUserPageLoading(userPageLoading) {
// @ts-ignore
this.display(!userPageLoading);
update({ userPageLoading });
}
setSize({ height, width }) {
update({ width, height });
// @ts-ignore
this.scale();
}
}

View file

@ -1,5 +0,0 @@
// @flow
import type { Message } from './messages';
export type Timed = { +time: number };
export type TimedMessage = Timed & Message;

View file

@ -0,0 +1,5 @@
import type { Message } from './messages';
export interface Timed { readonly time: number };
export interface Indexed { readonly _index: number }; // TODO: remove dash (evwrywhere)
export type TimedMessage = Timed & Message;

View file

@ -1,8 +1,6 @@
//@flow
import type { Timed } from '../Timed';
export default class ListWalker<T: Timed> {
export default class ListWalker<T extends Timed> {
// Optimisation: #prop compiles to method that costs mor than strict property call.
_p = 0;
_list: Array<T>;
@ -15,7 +13,7 @@ export default class ListWalker<T: Timed> {
}
append(m: T): void {
if (this.length > 0 && m.time < this.last.time) {
if (this.length > 0 && this.last && m.time < this.last.time) {
console.error("Trying to append message with the less time then the list tail: ", m);
}
this._list.push(m);
@ -26,6 +24,7 @@ export default class ListWalker<T: Timed> {
}
sort(comparator): void {
// @ts-ignore
this._list.sort((m1,m2) => comparator(m1,m2) || (m1._index - m2._index) ); // indexes for sort stability (TODO: fix types???)
}
@ -100,10 +99,10 @@ export default class ListWalker<T: Timed> {
Assumed that the current message is already handled so
if pointer doesn't cahnge <undefined> is returned.
*/
moveToLast(t: number, index: ?number): ?T {
let key = "time"; //TODO
moveToLast(t: number, index?: number): T | null {
let key: string = "time"; //TODO
let val = t;
if (index != null) {
if (index) {
key = "_index";
val = index;
}
@ -117,7 +116,7 @@ export default class ListWalker<T: Timed> {
this._p--;
changed = true;
}
return changed ? this._list[ this._p - 1 ] : undefined;
return changed ? this._list[ this._p - 1 ] : null;
}
// moveToLastByIndex(i: number): ?T {
@ -133,7 +132,7 @@ export default class ListWalker<T: Timed> {
// return changed ? this._list[ this._p - 1 ] : undefined;
// }
moveApply(t: number, fn: T => void): void {
moveApply(t: number, fn: (T) => void): void {
// Applying only in increment order for now
if (t < this.timeNow) {
this.reset();

View file

@ -1,596 +0,0 @@
// Auto-generated, do not edit
import { readUint, readInt, readString, readBoolean } from './readPrimitives'
export type Timestamp = {
tp: "timestamp",
timestamp: number,
}
export type SessionDisconnect = {
tp: "session_disconnect",
timestamp: number,
}
export type SetPageLocation = {
tp: "set_page_location",
url: string,
referrer: string,
navigationStart: number,
}
export type SetViewportSize = {
tp: "set_viewport_size",
width: number,
height: number,
}
export type SetViewportScroll = {
tp: "set_viewport_scroll",
x: number,
y: number,
}
export type CreateDocument = {
tp: "create_document",
}
export type CreateElementNode = {
tp: "create_element_node",
id: number,
parentID: number,
index: number,
tag: string,
svg: boolean,
}
export type CreateTextNode = {
tp: "create_text_node",
id: number,
parentID: number,
index: number,
}
export type MoveNode = {
tp: "move_node",
id: number,
parentID: number,
index: number,
}
export type RemoveNode = {
tp: "remove_node",
id: number,
}
export type SetNodeAttribute = {
tp: "set_node_attribute",
id: number,
name: string,
value: string,
}
export type RemoveNodeAttribute = {
tp: "remove_node_attribute",
id: number,
name: string,
}
export type SetNodeData = {
tp: "set_node_data",
id: number,
data: string,
}
export type SetCssData = {
tp: "set_css_data",
id: number,
data: string,
}
export type SetNodeScroll = {
tp: "set_node_scroll",
id: number,
x: number,
y: number,
}
export type SetInputValue = {
tp: "set_input_value",
id: number,
value: string,
mask: number,
}
export type SetInputChecked = {
tp: "set_input_checked",
id: number,
checked: boolean,
}
export type MouseMove = {
tp: "mouse_move",
x: number,
y: number,
}
export type ConsoleLog = {
tp: "console_log",
level: string,
value: string,
}
export type PerformanceTrack = {
tp: "performance_track",
frames: number,
ticks: number,
totalJSHeapSize: number,
usedJSHeapSize: number,
}
export type ConnectionInformation = {
tp: "connection_information",
downlink: number,
type: string,
}
export type SetPageVisibility = {
tp: "set_page_visibility",
hidden: boolean,
}
export type CssInsertRule = {
tp: "css_insert_rule",
id: number,
rule: string,
index: number,
}
export type CssDeleteRule = {
tp: "css_delete_rule",
id: number,
index: number,
}
export type Fetch = {
tp: "fetch",
method: string,
url: string,
request: string,
response: string,
status: number,
timestamp: number,
duration: number,
}
export type Profiler = {
tp: "profiler",
name: string,
duration: number,
args: string,
result: string,
}
export type OTable = {
tp: "o_table",
key: string,
value: string,
}
export type Redux = {
tp: "redux",
action: string,
state: string,
duration: number,
}
export type Vuex = {
tp: "vuex",
mutation: string,
state: string,
}
export type MobX = {
tp: "mob_x",
type: string,
payload: string,
}
export type NgRx = {
tp: "ng_rx",
action: string,
state: string,
duration: number,
}
export type GraphQl = {
tp: "graph_ql",
operationKind: string,
operationName: string,
variables: string,
response: string,
}
export type LongTask = {
tp: "long_task",
timestamp: number,
duration: number,
context: number,
containerType: number,
containerSrc: string,
containerId: string,
containerName: string,
}
export type TechnicalInfo = {
tp: "technical_info",
type: string,
value: string,
}
export type IosSessionStart = {
tp: "ios_session_start",
timestamp: number,
projectID: number,
trackerVersion: string,
revID: string,
userUUID: string,
userOS: string,
userOSVersion: string,
userDevice: string,
userDeviceType: string,
userCountry: string,
}
export type IosCustomEvent = {
tp: "ios_custom_event",
timestamp: number,
length: number,
name: string,
payload: string,
}
export type IosClickEvent = {
tp: "ios_click_event",
timestamp: number,
length: number,
label: string,
x: number,
y: number,
}
export type IosPerformanceEvent = {
tp: "ios_performance_event",
timestamp: number,
length: number,
name: string,
value: number,
}
export type IosLog = {
tp: "ios_log",
timestamp: number,
length: number,
severity: string,
content: string,
}
export type IosNetworkCall = {
tp: "ios_network_call",
timestamp: number,
length: number,
duration: number,
headers: string,
body: string,
url: string,
success: boolean,
method: string,
status: number,
}
export type Message = Timestamp | SessionDisconnect | SetPageLocation | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetCssData | SetNodeScroll | SetInputValue | SetInputChecked | MouseMove | ConsoleLog | PerformanceTrack | ConnectionInformation | SetPageVisibility | CssInsertRule | CssDeleteRule | Fetch | Profiler | OTable | Redux | Vuex | MobX | NgRx | GraphQl | LongTask | TechnicalInfo | IosSessionStart | IosCustomEvent | IosClickEvent | IosPerformanceEvent | IosLog | IosNetworkCall;
export default function (buf: Uint8Array, p: number): [Message, number] {
const msg = {};
let r;
switch (buf[p++]) {
case 0:
(msg:Timestamp).tp = "timestamp";
r = readUint(buf, p); msg.timestamp = r[0]; p = r[1];
break;
case 2:
(msg:SessionDisconnect).tp = "session_disconnect";
r = readUint(buf, p); msg.timestamp = r[0]; p = r[1];
break;
case 4:
(msg:SetPageLocation).tp = "set_page_location";
r = readString(buf, p); msg.url = r[0]; p = r[1];
r = readString(buf, p); msg.referrer = r[0]; p = r[1];
r = readUint(buf, p); msg.navigationStart = r[0]; p = r[1];
break;
case 5:
(msg:SetViewportSize).tp = "set_viewport_size";
r = readUint(buf, p); msg.width = r[0]; p = r[1];
r = readUint(buf, p); msg.height = r[0]; p = r[1];
break;
case 6:
(msg:SetViewportScroll).tp = "set_viewport_scroll";
r = readInt(buf, p); msg.x = r[0]; p = r[1];
r = readInt(buf, p); msg.y = r[0]; p = r[1];
break;
case 7:
(msg:CreateDocument).tp = "create_document";
break;
case 8:
(msg:CreateElementNode).tp = "create_element_node";
r = readUint(buf, p); msg.id = r[0]; p = r[1];
r = readUint(buf, p); msg.parentID = r[0]; p = r[1];
r = readUint(buf, p); msg.index = r[0]; p = r[1];
r = readString(buf, p); msg.tag = r[0]; p = r[1];
r = readBoolean(buf, p); msg.svg = r[0]; p = r[1];
break;
case 9:
(msg:CreateTextNode).tp = "create_text_node";
r = readUint(buf, p); msg.id = r[0]; p = r[1];
r = readUint(buf, p); msg.parentID = r[0]; p = r[1];
r = readUint(buf, p); msg.index = r[0]; p = r[1];
break;
case 10:
(msg:MoveNode).tp = "move_node";
r = readUint(buf, p); msg.id = r[0]; p = r[1];
r = readUint(buf, p); msg.parentID = r[0]; p = r[1];
r = readUint(buf, p); msg.index = r[0]; p = r[1];
break;
case 11:
(msg:RemoveNode).tp = "remove_node";
r = readUint(buf, p); msg.id = r[0]; p = r[1];
break;
case 12:
(msg:SetNodeAttribute).tp = "set_node_attribute";
r = readUint(buf, p); msg.id = r[0]; p = r[1];
r = readString(buf, p); msg.name = r[0]; p = r[1];
r = readString(buf, p); msg.value = r[0]; p = r[1];
break;
case 13:
(msg:RemoveNodeAttribute).tp = "remove_node_attribute";
r = readUint(buf, p); msg.id = r[0]; p = r[1];
r = readString(buf, p); msg.name = r[0]; p = r[1];
break;
case 14:
(msg:SetNodeData).tp = "set_node_data";
r = readUint(buf, p); msg.id = r[0]; p = r[1];
r = readString(buf, p); msg.data = r[0]; p = r[1];
break;
case 15:
(msg:SetCssData).tp = "set_css_data";
r = readUint(buf, p); msg.id = r[0]; p = r[1];
r = readString(buf, p); msg.data = r[0]; p = r[1];
break;
case 16:
(msg:SetNodeScroll).tp = "set_node_scroll";
r = readUint(buf, p); msg.id = r[0]; p = r[1];
r = readInt(buf, p); msg.x = r[0]; p = r[1];
r = readInt(buf, p); msg.y = r[0]; p = r[1];
break;
case 18:
(msg:SetInputValue).tp = "set_input_value";
r = readUint(buf, p); msg.id = r[0]; p = r[1];
r = readString(buf, p); msg.value = r[0]; p = r[1];
r = readInt(buf, p); msg.mask = r[0]; p = r[1];
break;
case 19:
(msg:SetInputChecked).tp = "set_input_checked";
r = readUint(buf, p); msg.id = r[0]; p = r[1];
r = readBoolean(buf, p); msg.checked = r[0]; p = r[1];
break;
case 20:
(msg:MouseMove).tp = "mouse_move";
r = readUint(buf, p); msg.x = r[0]; p = r[1];
r = readUint(buf, p); msg.y = r[0]; p = r[1];
break;
case 22:
(msg:ConsoleLog).tp = "console_log";
r = readString(buf, p); msg.level = r[0]; p = r[1];
r = readString(buf, p); msg.value = r[0]; p = r[1];
break;
case 49:
(msg:PerformanceTrack).tp = "performance_track";
r = readInt(buf, p); msg.frames = r[0]; p = r[1];
r = readInt(buf, p); msg.ticks = r[0]; p = r[1];
r = readUint(buf, p); msg.totalJSHeapSize = r[0]; p = r[1];
r = readUint(buf, p); msg.usedJSHeapSize = r[0]; p = r[1];
break;
case 54:
(msg:ConnectionInformation).tp = "connection_information";
r = readUint(buf, p); msg.downlink = r[0]; p = r[1];
r = readString(buf, p); msg.type = r[0]; p = r[1];
break;
case 55:
(msg:SetPageVisibility).tp = "set_page_visibility";
r = readBoolean(buf, p); msg.hidden = r[0]; p = r[1];
break;
case 37:
(msg:CssInsertRule).tp = "css_insert_rule";
r = readUint(buf, p); msg.id = r[0]; p = r[1];
r = readString(buf, p); msg.rule = r[0]; p = r[1];
r = readUint(buf, p); msg.index = r[0]; p = r[1];
break;
case 38:
(msg:CssDeleteRule).tp = "css_delete_rule";
r = readUint(buf, p); msg.id = r[0]; p = r[1];
r = readUint(buf, p); msg.index = r[0]; p = r[1];
break;
case 39:
(msg:Fetch).tp = "fetch";
r = readString(buf, p); msg.method = r[0]; p = r[1];
r = readString(buf, p); msg.url = r[0]; p = r[1];
r = readString(buf, p); msg.request = r[0]; p = r[1];
r = readString(buf, p); msg.response = r[0]; p = r[1];
r = readUint(buf, p); msg.status = r[0]; p = r[1];
r = readUint(buf, p); msg.timestamp = r[0]; p = r[1];
r = readUint(buf, p); msg.duration = r[0]; p = r[1];
break;
case 40:
(msg:Profiler).tp = "profiler";
r = readString(buf, p); msg.name = r[0]; p = r[1];
r = readUint(buf, p); msg.duration = r[0]; p = r[1];
r = readString(buf, p); msg.args = r[0]; p = r[1];
r = readString(buf, p); msg.result = r[0]; p = r[1];
break;
case 41:
(msg:OTable).tp = "o_table";
r = readString(buf, p); msg.key = r[0]; p = r[1];
r = readString(buf, p); msg.value = r[0]; p = r[1];
break;
case 44:
(msg:Redux).tp = "redux";
r = readString(buf, p); msg.action = r[0]; p = r[1];
r = readString(buf, p); msg.state = r[0]; p = r[1];
r = readUint(buf, p); msg.duration = r[0]; p = r[1];
break;
case 45:
(msg:Vuex).tp = "vuex";
r = readString(buf, p); msg.mutation = r[0]; p = r[1];
r = readString(buf, p); msg.state = r[0]; p = r[1];
break;
case 46:
(msg:MobX).tp = "mob_x";
r = readString(buf, p); msg.type = r[0]; p = r[1];
r = readString(buf, p); msg.payload = r[0]; p = r[1];
break;
case 47:
(msg:NgRx).tp = "ng_rx";
r = readString(buf, p); msg.action = r[0]; p = r[1];
r = readString(buf, p); msg.state = r[0]; p = r[1];
r = readUint(buf, p); msg.duration = r[0]; p = r[1];
break;
case 48:
(msg:GraphQl).tp = "graph_ql";
r = readString(buf, p); msg.operationKind = r[0]; p = r[1];
r = readString(buf, p); msg.operationName = r[0]; p = r[1];
r = readString(buf, p); msg.variables = r[0]; p = r[1];
r = readString(buf, p); msg.response = r[0]; p = r[1];
break;
case 59:
(msg:LongTask).tp = "long_task";
r = readUint(buf, p); msg.timestamp = r[0]; p = r[1];
r = readUint(buf, p); msg.duration = r[0]; p = r[1];
r = readUint(buf, p); msg.context = r[0]; p = r[1];
r = readUint(buf, p); msg.containerType = r[0]; p = r[1];
r = readString(buf, p); msg.containerSrc = r[0]; p = r[1];
r = readString(buf, p); msg.containerId = r[0]; p = r[1];
r = readString(buf, p); msg.containerName = r[0]; p = r[1];
break;
case 63:
(msg:TechnicalInfo).tp = "technical_info";
r = readString(buf, p); msg.type = r[0]; p = r[1];
r = readString(buf, p); msg.value = r[0]; p = r[1];
break;
case 90:
(msg:IosSessionStart).tp = "ios_session_start";
r = readUint(buf, p); msg.timestamp = r[0]; p = r[1];
r = readUint(buf, p); msg.projectID = r[0]; p = r[1];
r = readString(buf, p); msg.trackerVersion = r[0]; p = r[1];
r = readString(buf, p); msg.revID = r[0]; p = r[1];
r = readString(buf, p); msg.userUUID = r[0]; p = r[1];
r = readString(buf, p); msg.userOS = r[0]; p = r[1];
r = readString(buf, p); msg.userOSVersion = r[0]; p = r[1];
r = readString(buf, p); msg.userDevice = r[0]; p = r[1];
r = readString(buf, p); msg.userDeviceType = r[0]; p = r[1];
r = readString(buf, p); msg.userCountry = r[0]; p = r[1];
break;
case 93:
(msg:IosCustomEvent).tp = "ios_custom_event";
r = readUint(buf, p); msg.timestamp = r[0]; p = r[1];
r = readUint(buf, p); msg.length = r[0]; p = r[1];
r = readString(buf, p); msg.name = r[0]; p = r[1];
r = readString(buf, p); msg.payload = r[0]; p = r[1];
break;
case 100:
(msg:IosClickEvent).tp = "ios_click_event";
r = readUint(buf, p); msg.timestamp = r[0]; p = r[1];
r = readUint(buf, p); msg.length = r[0]; p = r[1];
r = readString(buf, p); msg.label = r[0]; p = r[1];
r = readUint(buf, p); msg.x = r[0]; p = r[1];
r = readUint(buf, p); msg.y = r[0]; p = r[1];
break;
case 102:
(msg:IosPerformanceEvent).tp = "ios_performance_event";
r = readUint(buf, p); msg.timestamp = r[0]; p = r[1];
r = readUint(buf, p); msg.length = r[0]; p = r[1];
r = readString(buf, p); msg.name = r[0]; p = r[1];
r = readUint(buf, p); msg.value = r[0]; p = r[1];
break;
case 103:
(msg:IosLog).tp = "ios_log";
r = readUint(buf, p); msg.timestamp = r[0]; p = r[1];
r = readUint(buf, p); msg.length = r[0]; p = r[1];
r = readString(buf, p); msg.severity = r[0]; p = r[1];
r = readString(buf, p); msg.content = r[0]; p = r[1];
break;
case 105:
(msg:IosNetworkCall).tp = "ios_network_call";
r = readUint(buf, p); msg.timestamp = r[0]; p = r[1];
r = readUint(buf, p); msg.length = r[0]; p = r[1];
r = readUint(buf, p); msg.duration = r[0]; p = r[1];
r = readString(buf, p); msg.headers = r[0]; p = r[1];
r = readString(buf, p); msg.body = r[0]; p = r[1];
r = readString(buf, p); msg.url = r[0]; p = r[1];
r = readBoolean(buf, p); msg.success = r[0]; p = r[1];
r = readString(buf, p); msg.method = r[0]; p = r[1];
r = readUint(buf, p); msg.status = r[0]; p = r[1];
break;
default:
let len;
[ _, p ] = readUint(buf, p);
[ len, p ] = readUint(buf, p);
return [null, p + len] // skip
//throw `Unknown type (${buf[p-1]})`;
}
return [msg, p];
}

View file

@ -0,0 +1,543 @@
// Auto-generated, do not edit
import PrimitiveReader from './PrimitiveReader';
export const ID_TP_MAP = {
0: "timestamp",
4: "set_page_location",
5: "set_viewport_size",
6: "set_viewport_scroll",
7: "create_document",
8: "create_element_node",
9: "create_text_node",
10: "move_node",
11: "remove_node",
12: "set_node_attribute",
13: "remove_node_attribute",
14: "set_node_data",
15: "set_css_data",
16: "set_node_scroll",
18: "set_input_value",
19: "set_input_checked",
20: "mouse_move",
22: "console_log",
37: "css_insert_rule",
38: "css_delete_rule",
39: "fetch_depricated",
40: "profiler",
41: "o_table",
44: "redux",
45: "vuex",
46: "mob_x",
47: "ng_rx",
48: "graph_ql",
49: "performance_track",
54: "connection_information",
55: "set_page_visibility",
59: "long_task",
68: "fetch",
} as const;
export interface Timestamp {
tp: "timestamp",
timestamp: number,
}
export interface SetPageLocation {
tp: "set_page_location",
url: string,
referrer: string,
navigationStart: number,
}
export interface SetViewportSize {
tp: "set_viewport_size",
width: number,
height: number,
}
export interface SetViewportScroll {
tp: "set_viewport_scroll",
x: number,
y: number,
}
export interface CreateDocument {
tp: "create_document",
}
export interface CreateElementNode {
tp: "create_element_node",
id: number,
parentID: number,
index: number,
tag: string,
svg: boolean,
}
export interface CreateTextNode {
tp: "create_text_node",
id: number,
parentID: number,
index: number,
}
export interface MoveNode {
tp: "move_node",
id: number,
parentID: number,
index: number,
}
export interface RemoveNode {
tp: "remove_node",
id: number,
}
export interface SetNodeAttribute {
tp: "set_node_attribute",
id: number,
name: string,
value: string,
}
export interface RemoveNodeAttribute {
tp: "remove_node_attribute",
id: number,
name: string,
}
export interface SetNodeData {
tp: "set_node_data",
id: number,
data: string,
}
export interface SetCssData {
tp: "set_css_data",
id: number,
data: string,
}
export interface SetNodeScroll {
tp: "set_node_scroll",
id: number,
x: number,
y: number,
}
export interface SetInputValue {
tp: "set_input_value",
id: number,
value: string,
mask: number,
}
export interface SetInputChecked {
tp: "set_input_checked",
id: number,
checked: boolean,
}
export interface MouseMove {
tp: "mouse_move",
x: number,
y: number,
}
export interface ConsoleLog {
tp: "console_log",
level: string,
value: string,
}
export interface CssInsertRule {
tp: "css_insert_rule",
id: number,
rule: string,
index: number,
}
export interface CssDeleteRule {
tp: "css_delete_rule",
id: number,
index: number,
}
export interface FetchDepricated {
tp: "fetch_depricated",
method: string,
url: string,
request: string,
response: string,
status: number,
timestamp: number,
duration: number,
}
export interface Profiler {
tp: "profiler",
name: string,
duration: number,
args: string,
result: string,
}
export interface OTable {
tp: "o_table",
key: string,
value: string,
}
export interface Redux {
tp: "redux",
action: string,
state: string,
duration: number,
}
export interface Vuex {
tp: "vuex",
mutation: string,
state: string,
}
export interface MobX {
tp: "mob_x",
type: string,
payload: string,
}
export interface NgRx {
tp: "ng_rx",
action: string,
state: string,
duration: number,
}
export interface GraphQl {
tp: "graph_ql",
operationKind: string,
operationName: string,
variables: string,
response: string,
}
export interface PerformanceTrack {
tp: "performance_track",
frames: number,
ticks: number,
totalJSHeapSize: number,
usedJSHeapSize: number,
}
export interface ConnectionInformation {
tp: "connection_information",
downlink: number,
type: string,
}
export interface SetPageVisibility {
tp: "set_page_visibility",
hidden: boolean,
}
export interface LongTask {
tp: "long_task",
timestamp: number,
duration: number,
context: number,
containerType: number,
containerSrc: string,
containerId: string,
containerName: string,
}
export interface Fetch {
tp: "fetch",
method: string,
url: string,
request: string,
response: string,
status: number,
timestamp: number,
duration: number,
headers: string,
}
export type Message = Timestamp | SetPageLocation | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetCssData | SetNodeScroll | SetInputValue | SetInputChecked | MouseMove | ConsoleLog | CssInsertRule | CssDeleteRule | FetchDepricated | Profiler | OTable | Redux | Vuex | MobX | NgRx | GraphQl | PerformanceTrack | ConnectionInformation | SetPageVisibility | LongTask | Fetch;
export default function (r: PrimitiveReader): Message | null {
switch (r.readUint()) {
case 0:
return {
tp: ID_TP_MAP[0],
timestamp: r.readUint(),
};
case 4:
return {
tp: ID_TP_MAP[4],
url: r.readString(),
referrer: r.readString(),
navigationStart: r.readUint(),
};
case 5:
return {
tp: ID_TP_MAP[5],
width: r.readUint(),
height: r.readUint(),
};
case 6:
return {
tp: ID_TP_MAP[6],
x: r.readInt(),
y: r.readInt(),
};
case 7:
return {
tp: ID_TP_MAP[7],
};
case 8:
return {
tp: ID_TP_MAP[8],
id: r.readUint(),
parentID: r.readUint(),
index: r.readUint(),
tag: r.readString(),
svg: r.readBoolean(),
};
case 9:
return {
tp: ID_TP_MAP[9],
id: r.readUint(),
parentID: r.readUint(),
index: r.readUint(),
};
case 10:
return {
tp: ID_TP_MAP[10],
id: r.readUint(),
parentID: r.readUint(),
index: r.readUint(),
};
case 11:
return {
tp: ID_TP_MAP[11],
id: r.readUint(),
};
case 12:
return {
tp: ID_TP_MAP[12],
id: r.readUint(),
name: r.readString(),
value: r.readString(),
};
case 13:
return {
tp: ID_TP_MAP[13],
id: r.readUint(),
name: r.readString(),
};
case 14:
return {
tp: ID_TP_MAP[14],
id: r.readUint(),
data: r.readString(),
};
case 15:
return {
tp: ID_TP_MAP[15],
id: r.readUint(),
data: r.readString(),
};
case 16:
return {
tp: ID_TP_MAP[16],
id: r.readUint(),
x: r.readInt(),
y: r.readInt(),
};
case 18:
return {
tp: ID_TP_MAP[18],
id: r.readUint(),
value: r.readString(),
mask: r.readInt(),
};
case 19:
return {
tp: ID_TP_MAP[19],
id: r.readUint(),
checked: r.readBoolean(),
};
case 20:
return {
tp: ID_TP_MAP[20],
x: r.readUint(),
y: r.readUint(),
};
case 22:
return {
tp: ID_TP_MAP[22],
level: r.readString(),
value: r.readString(),
};
case 37:
return {
tp: ID_TP_MAP[37],
id: r.readUint(),
rule: r.readString(),
index: r.readUint(),
};
case 38:
return {
tp: ID_TP_MAP[38],
id: r.readUint(),
index: r.readUint(),
};
case 39:
return {
tp: ID_TP_MAP[39],
method: r.readString(),
url: r.readString(),
request: r.readString(),
response: r.readString(),
status: r.readUint(),
timestamp: r.readUint(),
duration: r.readUint(),
};
case 40:
return {
tp: ID_TP_MAP[40],
name: r.readString(),
duration: r.readUint(),
args: r.readString(),
result: r.readString(),
};
case 41:
return {
tp: ID_TP_MAP[41],
key: r.readString(),
value: r.readString(),
};
case 44:
return {
tp: ID_TP_MAP[44],
action: r.readString(),
state: r.readString(),
duration: r.readUint(),
};
case 45:
return {
tp: ID_TP_MAP[45],
mutation: r.readString(),
state: r.readString(),
};
case 46:
return {
tp: ID_TP_MAP[46],
type: r.readString(),
payload: r.readString(),
};
case 47:
return {
tp: ID_TP_MAP[47],
action: r.readString(),
state: r.readString(),
duration: r.readUint(),
};
case 48:
return {
tp: ID_TP_MAP[48],
operationKind: r.readString(),
operationName: r.readString(),
variables: r.readString(),
response: r.readString(),
};
case 49:
return {
tp: ID_TP_MAP[49],
frames: r.readInt(),
ticks: r.readInt(),
totalJSHeapSize: r.readUint(),
usedJSHeapSize: r.readUint(),
};
case 54:
return {
tp: ID_TP_MAP[54],
downlink: r.readUint(),
type: r.readString(),
};
case 55:
return {
tp: ID_TP_MAP[55],
hidden: r.readBoolean(),
};
case 59:
return {
tp: ID_TP_MAP[59],
timestamp: r.readUint(),
duration: r.readUint(),
context: r.readUint(),
containerType: r.readUint(),
containerSrc: r.readString(),
containerId: r.readString(),
containerName: r.readString(),
};
case 68:
return {
tp: ID_TP_MAP[68],
method: r.readString(),
url: r.readString(),
request: r.readString(),
response: r.readString(),
status: r.readUint(),
timestamp: r.readUint(),
duration: r.readUint(),
headers: r.readString(),
};
default:
r.readUint(); // IOS skip timestamp
r.skip(r.readUint());
return null;
}
}

View file

@ -1,31 +0,0 @@
export function readUint(buf, p) {
var r = 0, s = 1, b;
do {
b = buf[p++];
r += (b & 0x7F) * s;
s *= 128;
} while (b >= 0x80)
return [r, p];
}
export function readInt(buf, p) {
var r = readUint(buf, p);
if (r[0] % 2) {
r[0] = (r[0] + 1) / -2;
} else {
r[0] = r[0] / 2;
}
return r;
}
export function readString(buf, p) {
var r = readUint(buf, p);
var f = r[1];
r[1] += r[0];
r[0] = new TextDecoder().decode(buf.subarray(f, r[1]));
return r;
}
export function readBoolean(buf, p) {
return [!!buf[p], p+1];
}

View file

@ -7,12 +7,16 @@ const performance = window.performance || { now: Date.now.bind(Date) };
const requestAnimationFrame =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
// @ts-ignore
window.mozRequestAnimationFrame ||
// @ts-ignore
window.oRequestAnimationFrame ||
// @ts-ignore
window.msRequestAnimationFrame ||
(callback => window.setTimeout(() => { callback(performance.now()); }, 1000 / fps));
const cancelAnimationFrame =
window.cancelAnimationFrame ||
// @ts-ignore
window.mozCancelAnimationFrame ||
window.clearTimeout;
@ -23,7 +27,7 @@ const SPEED_STORAGE_KEY = "__$player-speed$__";
const SKIP_STORAGE_KEY = "__$player-skip$__";
const SKIP_TO_ISSUE_STORAGE_KEY = "__$player-skip-to-issue$__";
const AUTOPLAY_STORAGE_KEY = "__$player-autoplay$__";
const storedSpeed = +localStorage.getItem(SPEED_STORAGE_KEY);
const storedSpeed: number = parseInt(localStorage.getItem(SPEED_STORAGE_KEY) || "") ;
const initialSpeed = [1,2,3].includes(storedSpeed) ? storedSpeed : 1;
const initialSkip = !!localStorage.getItem(SKIP_STORAGE_KEY);
const initialSkipToIssue = !!localStorage.getItem(SKIP_TO_ISSUE_STORAGE_KEY);
@ -48,18 +52,18 @@ export const INITIAL_NON_RESETABLE_STATE = {
}
export default class Player extends MessageDistributor {
_animationFrameRequestId = null;
private _animationFrameRequestId: number = 0;
_setTime(time, index) {
private _setTime(time: number, index?: number) {
update({
time,
completed: false,
});
this.move(time, index);
super.move(time, index);
listsGoTo(time, index);
}
_startAnimation() {
private _startAnimation() {
let prevTime = getState().time;
let animationPrevTime = performance.now();
@ -86,10 +90,10 @@ export default class Player extends MessageDistributor {
const skipInterval = skip && skipIntervals.find(si => si.contains(time)); // TODO: good skip by messages
if (skipInterval) time = skipInterval.end;
const fmt = this.getFirstMessageTime();
const fmt = super.getFirstMessageTime();
if (time < fmt) time = fmt; // ?
const lmt = this.getLastMessageTime();
const lmt = super.getLastMessageTime();
if (livePlay && time < lmt) time = lmt;
if (endTime < lmt) {
update({
@ -161,7 +165,7 @@ export default class Player extends MessageDistributor {
toggleSkip() {
const skip = !getState().skip;
localStorage.setItem(SKIP_STORAGE_KEY, skip);
localStorage.setItem(SKIP_STORAGE_KEY, `${skip}`);
update({ skip });
}
@ -174,27 +178,27 @@ export default class Player extends MessageDistributor {
if (flag) {
this.pause();
update({ inspectorMode: true });
return this.enableInspector(clickCallback);
return super.enableInspector(clickCallback);
} else {
this.disableInspector();
super.disableInspector();
update({ inspectorMode: false });
}
}
toggleSkipToIssue() {
const skipToIssue = !getState().skipToIssue;
localStorage.setItem(SKIP_TO_ISSUE_STORAGE_KEY, skipToIssue);
localStorage.setItem(SKIP_TO_ISSUE_STORAGE_KEY, `${skipToIssue}`);
update({ skipToIssue });
}
toggleAutoplay() {
const autoplay = !getState().autoplay;
localStorage.setItem(AUTOPLAY_STORAGE_KEY, autoplay);
localStorage.setItem(AUTOPLAY_STORAGE_KEY, `${autoplay}`);
update({ autoplay });
}
_updateSpeed(speed) {
localStorage.setItem(SPEED_STORAGE_KEY, speed);
_updateSpeed(speed: number) {
localStorage.setItem(SPEED_STORAGE_KEY, `${speed}`);
update({ speed });
}

View file

@ -67,6 +67,7 @@ export const attach = initCheck((...args) => instance.attach(...args));
export const markElement = initCheck((...args) => instance.marker && instance.marker.mark(...args));
export const scale = initCheck(() => instance.scale());
export const toggleInspectorMode = initCheck((...args) => instance.toggleInspectorMode(...args));
export const callPeer = initCheck((...args) => instance.callPeer(...args))
export const Controls = {
jump,

View file

@ -16089,6 +16089,34 @@
"sha.js": "^2.4.8"
}
},
"peerjs": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/peerjs/-/peerjs-1.3.2.tgz",
"integrity": "sha512-+PHfmsC7QGUU8Ye3OLi6tKQZGPCNy7QatUVNw4JtE8alkguF3+DdO5W0bzepqP2OtE9FqH/ltXt37qyvHw2CqA==",
"requires": {
"@types/node": "^10.14.33",
"eventemitter3": "^3.1.2",
"peerjs-js-binarypack": "1.0.1",
"webrtc-adapter": "^7.7.1"
},
"dependencies": {
"@types/node": {
"version": "10.17.60",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
"integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
},
"eventemitter3": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
"integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q=="
}
}
},
"peerjs-js-binarypack": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/peerjs-js-binarypack/-/peerjs-js-binarypack-1.0.1.tgz",
"integrity": "sha512-N6aeia3NhdpV7kiGxJV5xQiZZCVEEVjRz2T2C6UZQiBkHWHzUv/oWA4myQLcwBwO8LUoR1KWW5oStvwVesmfCg=="
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@ -21695,6 +21723,14 @@
"integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==",
"dev": true
},
"rtcpeerconnection-shim": {
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz",
"integrity": "sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw==",
"requires": {
"sdp": "^2.6.0"
}
},
"run-async": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
@ -21876,6 +21912,11 @@
"ajv-keywords": "^3.5.2"
}
},
"sdp": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/sdp/-/sdp-2.12.0.tgz",
"integrity": "sha512-jhXqQAQVM+8Xj5EjJGVweuEzgtGWb3tmEEpl3CLP3cStInSbVHSg0QWOGQzNq8pSID4JkpeV2mPqlMDLrm0/Vw=="
},
"select": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
@ -25876,6 +25917,15 @@
}
}
},
"webrtc-adapter": {
"version": "7.7.1",
"resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.7.1.tgz",
"integrity": "sha512-TbrbBmiQBL9n0/5bvDdORc6ZfRY/Z7JnEj+EYOD1ghseZdpJ+nF2yx14k3LgQKc7JZnG7HAcL+zHnY25So9d7A==",
"requires": {
"rtcpeerconnection-shim": "^1.2.15",
"sdp": "^2.12.0"
}
},
"websocket-driver": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",

View file

@ -30,6 +30,7 @@
"moment": "^2.27.0",
"moment-range": "^4.0.2",
"optimal-select": "^4.0.1",
"peerjs": "^1.3.2",
"rc-time-picker": "^3.7.3",
"react": "^16.13.1",
"react-circular-progressbar": "^2.0.3",

View file

@ -1,7 +1,7 @@
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"module": "es2020",
"moduleResolution": "node", //?
//"allowJs": true,
"allowSyntheticDefaultImports": true,
@ -13,7 +13,9 @@
"incremental": true,
"baseUrl": ".",
"paths": { // TODO: one-source truth
"App": ["./app"],
"Appo": ["./app"],
"Types": ["./app/types" ],
"Types/*": ["./app/types/*"], // Sublime hack
"UI": ["./app/components/ui"]
}
},