frontend(feat): message reading refactored; Assist fixes
This commit is contained in:
parent
f056f69067
commit
bb2875b9ee
24 changed files with 2274 additions and 1026 deletions
|
|
@ -25,12 +25,11 @@ import WindowNodeCounter from './managers/WindowNodeCounter';
|
|||
import ActivityManager from './managers/ActivityManager';
|
||||
import AssistManager from './managers/AssistManager';
|
||||
|
||||
import MessageReader from './MessageReader';
|
||||
import MFileReader from './messages/MFileReader';
|
||||
|
||||
import { INITIAL_STATE as SUPER_INITIAL_STATE, State as SuperState } from './StatedScreen/StatedScreen';
|
||||
import { INITIAL_STATE as ASSIST_INITIAL_STATE, State as AssistState } from './managers/AssistManager';
|
||||
|
||||
import type { TimedMessage } from './Timed';
|
||||
import type { PerformanceChartPoint } from './managers/PerformanceTrackManager';
|
||||
import type { SkipInterval } from './managers/ActivityManager';
|
||||
|
||||
|
|
@ -82,32 +81,24 @@ import type {
|
|||
SetViewportScroll,
|
||||
} from './messages';
|
||||
|
||||
interface Timed { //TODO: to common space
|
||||
time: number;
|
||||
}
|
||||
|
||||
type ReduxDecoded = Timed & {
|
||||
action: {},
|
||||
state: {},
|
||||
duration: number,
|
||||
}
|
||||
import type { Timed } from './messages/timed';
|
||||
|
||||
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 locationManager: ListWalker<SetPageLocation> = new ListWalker();
|
||||
private readonly loadedLocationManager: ListWalker<SetPageLocation> = new ListWalker();
|
||||
private readonly connectionInfoManger: ListWalker<ConnectionInformation> = 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 resizeManager: ListWalker<SetViewportSize> = new ListWalker([]);
|
||||
private readonly pagesManager: PagesManager;
|
||||
private readonly mouseManager: MouseManager;
|
||||
private readonly assistManager: AssistManager;
|
||||
|
||||
private readonly scrollManager: ListWalker<SetViewportScroll & Timed> = new ListWalker();
|
||||
private readonly scrollManager: ListWalker<SetViewportScroll> = new ListWalker();
|
||||
|
||||
private readonly decoder = new Decoder();
|
||||
private readonly lists = initLists();
|
||||
|
|
@ -184,7 +175,7 @@ export default class MessageDistributor extends StatedScreen {
|
|||
window.fetch(fileUrl)
|
||||
.then(r => r.arrayBuffer())
|
||||
.then(b => {
|
||||
const r = new MessageReader(new Uint8Array(b), this.sessionStart);
|
||||
const r = new MFileReader(new Uint8Array(b), this.sessionStart);
|
||||
const msgs: Array<Message> = [];
|
||||
|
||||
while (r.hasNext()) {
|
||||
|
|
@ -334,7 +325,7 @@ export default class MessageDistributor extends StatedScreen {
|
|||
}
|
||||
|
||||
/* Binded */
|
||||
distributeMessage = (msg: TimedMessage, index: number): void => {
|
||||
distributeMessage = (msg: Message, index: number): void => {
|
||||
if ([
|
||||
"mouse_move",
|
||||
"mouse_click",
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
export default class PrimitiveReader {
|
||||
protected p = 0
|
||||
constructor(protected readonly buf: Uint8Array) {}
|
||||
|
||||
hasNext() {
|
||||
return this.p < this.buf.length
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
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;
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
import type Peer from 'peerjs';
|
||||
import type { DataConnection, MediaConnection } from 'peerjs';
|
||||
import type MessageDistributor from '../MessageDistributor';
|
||||
import type { TimedMessage } from '../Timed';
|
||||
import type { Message } from '../messages'
|
||||
import { ID_TP_MAP } from '../messages';
|
||||
import store from 'App/store';
|
||||
import type { LocalStream } from './LocalStream';
|
||||
import { update, getState } from '../../store';
|
||||
import { iceServerConfigFromString } from 'App/utils'
|
||||
|
||||
import MStreamReader from '../messages/MStreamReader';;
|
||||
import JSONRawMessageReader from '../messages/JSONRawMessageReader'
|
||||
|
||||
export enum CallingState {
|
||||
Reconnecting,
|
||||
|
|
@ -59,68 +59,15 @@ export const INITIAL_STATE: State = {
|
|||
const MAX_RECONNECTION_COUNT = 4;
|
||||
|
||||
|
||||
function resolveURL(baseURL: string, relURL: string): string {
|
||||
if (relURL.startsWith('#') || relURL === "") {
|
||||
return relURL;
|
||||
}
|
||||
return new URL(relURL, baseURL).toString();
|
||||
}
|
||||
|
||||
|
||||
var match = /bar/.exec("foobar");
|
||||
const re1 = /url\(("[^"]*"|'[^']*'|[^)]*)\)/g
|
||||
const re2 = /@import "(.*?)"/g
|
||||
function cssUrlsIndex(css: string): Array<[number, number]> {
|
||||
const idxs: Array<[number, number]> = [];
|
||||
const i1 = css.matchAll(re1);
|
||||
// @ts-ignore
|
||||
for (let m of i1) {
|
||||
// @ts-ignore
|
||||
const s: number = m.index + m[0].indexOf(m[1]);
|
||||
const e: number = s + m[1].length;
|
||||
idxs.push([s, e]);
|
||||
}
|
||||
const i2 = css.matchAll(re2);
|
||||
// @ts-ignore
|
||||
for (let m of i2) {
|
||||
// @ts-ignore
|
||||
const s = m.index + m[0].indexOf(m[1]);
|
||||
const e = s + m[1].length;
|
||||
idxs.push([s, e])
|
||||
}
|
||||
return idxs;
|
||||
}
|
||||
function unquote(str: string): [string, string] {
|
||||
str = str.trim();
|
||||
if (str.length <= 2) {
|
||||
return [str, ""]
|
||||
}
|
||||
if (str[0] == '"' && str[str.length-1] == '"') {
|
||||
return [ str.substring(1, str.length-1), "\""];
|
||||
}
|
||||
if (str[0] == '\'' && str[str.length-1] == '\'') {
|
||||
return [ str.substring(1, str.length-1), "'" ];
|
||||
}
|
||||
return [str, ""]
|
||||
}
|
||||
function rewriteCSSLinks(css: string, rewriter: (rawurl: string) => string): string {
|
||||
for (let idx of cssUrlsIndex(css)) {
|
||||
const f = idx[0]
|
||||
const t = idx[1]
|
||||
const [ rawurl, q ] = unquote(css.substring(f, t));
|
||||
css = css.substring(0,f) + q + rewriter(rawurl) + q + css.substring(t);
|
||||
}
|
||||
return css
|
||||
}
|
||||
|
||||
function resolveCSS(baseURL: string, css: string): string {
|
||||
return rewriteCSSLinks(css, rawurl => resolveURL(baseURL, rawurl));
|
||||
}
|
||||
|
||||
export default class AssistManager {
|
||||
constructor(private session, private md: MessageDistributor, private config) {}
|
||||
|
||||
private setStatus(status: ConnectionStatus) {
|
||||
if (getState().peerConnectionStatus === ConnectionStatus.Disconnected &&
|
||||
status !== ConnectionStatus.Connected) {
|
||||
return
|
||||
}
|
||||
|
||||
if (status === ConnectionStatus.Connecting) {
|
||||
this.md.setMessagesLoading(true);
|
||||
} else {
|
||||
|
|
@ -148,6 +95,7 @@ export default class AssistManager {
|
|||
}
|
||||
this.setStatus(ConnectionStatus.Connecting)
|
||||
import('peerjs').then(({ default: Peer }) => {
|
||||
if (this.closed) {return}
|
||||
const _config = {
|
||||
// @ts-ignore
|
||||
host: new URL(window.ENV.API_EDP).host,
|
||||
|
|
@ -170,12 +118,11 @@ export default class AssistManager {
|
|||
console.warn("AssistManager PeerJS peer error: ", e.type, e)
|
||||
}
|
||||
if (['peer-unavailable', 'network', 'webrtc'].includes(e.type)) {
|
||||
if (this.peer && this.connectionAttempts++ < MAX_RECONNECTION_COUNT) {
|
||||
this.setStatus(ConnectionStatus.Connecting);
|
||||
if (this.peer) {
|
||||
this.setStatus(this.connectionAttempts++ < MAX_RECONNECTION_COUNT
|
||||
? ConnectionStatus.Connecting
|
||||
: ConnectionStatus.Disconnected);
|
||||
this.connectToPeer();
|
||||
} else {
|
||||
this.setStatus(ConnectionStatus.Disconnected);
|
||||
this.dataCheckIntervalID && clearInterval(this.dataCheckIntervalID);
|
||||
}
|
||||
} else {
|
||||
console.error(`PeerJS error (on peer). Type ${e.type}`, e);
|
||||
|
|
@ -190,12 +137,11 @@ export default class AssistManager {
|
|||
});
|
||||
}
|
||||
|
||||
private dataCheckIntervalID: ReturnType<typeof setInterval> | undefined;
|
||||
private connectToPeer() {
|
||||
if (!this.peer) { return; }
|
||||
this.setStatus(ConnectionStatus.Connecting);
|
||||
const id = this.peerID;
|
||||
const conn = this.peer.connect(id, { serialization: 'json', reliable: true});
|
||||
const conn = this.peer.connect(id, { serialization: "json", reliable: true});
|
||||
conn.on('open', () => {
|
||||
window.addEventListener("beforeunload", ()=>conn.open &&conn.send("unload"));
|
||||
|
||||
|
|
@ -206,75 +152,42 @@ export default class AssistManager {
|
|||
this._call()
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
let firstMessage = true;
|
||||
|
||||
this.setStatus(ConnectionStatus.WaitingMessages)
|
||||
|
||||
const jmr = new JSONRawMessageReader()
|
||||
const reader = new MStreamReader(jmr)
|
||||
|
||||
conn.on('data', (data) => {
|
||||
if (!Array.isArray(data)) { return this.handleCommand(data); }
|
||||
this.disconnectTimeout && clearTimeout(this.disconnectTimeout);
|
||||
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
jmr.append(data) // as RawMessage[]
|
||||
} else if (data instanceof ArrayBuffer) {
|
||||
//rawMessageReader.append(new Uint8Array(data))
|
||||
} else { return this.handleCommand(data); }
|
||||
|
||||
if (firstMessage) {
|
||||
firstMessage = false;
|
||||
this.setStatus(ConnectionStatus.Connected)
|
||||
}
|
||||
|
||||
let time = 0;
|
||||
let ts0 = 0;
|
||||
(data as Array<Message & { _id: number}>).forEach(msg => {
|
||||
|
||||
// TODO: more appropriate way to do it.
|
||||
if (msg._id === 60) {
|
||||
// @ts-ignore
|
||||
if (msg.name === 'src' || msg.name === 'href') {
|
||||
// @ts-ignore
|
||||
msg.value = resolveURL(msg.baseURL, msg.value);
|
||||
// @ts-ignore
|
||||
} else if (msg.name === 'style') {
|
||||
// @ts-ignore
|
||||
msg.value = resolveCSS(msg.baseURL, msg.value);
|
||||
}
|
||||
msg._id = 12;
|
||||
} else if (msg._id === 61) { // "SetCSSDataURLBased"
|
||||
// @ts-ignore
|
||||
msg.data = resolveCSS(msg.baseURL, msg.data);
|
||||
msg._id = 15;
|
||||
} else if (msg._id === 67) { // "insert_rule"
|
||||
// @ts-ignore
|
||||
msg.rule = resolveCSS(msg.baseURL, msg.rule);
|
||||
msg._id = 37;
|
||||
}
|
||||
|
||||
|
||||
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.md.distributeMessage(tMsg, i++);
|
||||
});
|
||||
for (let msg = reader.readNext();msg !== null;msg = reader.readNext()) {
|
||||
//@ts-ignore
|
||||
this.md.distributeMessage(msg, msg._index);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
const onDataClose = () => {
|
||||
this.onCallDisconnect()
|
||||
//console.log('closed peer conn. Reconnecting...')
|
||||
this.connectToPeer();
|
||||
}
|
||||
|
||||
// this.dataCheckIntervalID = setInterval(() => {
|
||||
// if (!this.dataConnection && getState().peerConnectionStatus === ConnectionStatus.Connected) {
|
||||
// onDataClose();
|
||||
// }
|
||||
// }, 3000);
|
||||
conn.on('close', onDataClose);// Does it work ?
|
||||
conn.on('close', onDataClose);// What case does it work ?
|
||||
conn.on("error", (e) => {
|
||||
this.setStatus(ConnectionStatus.Error);
|
||||
})
|
||||
|
|
@ -284,11 +197,9 @@ export default class AssistManager {
|
|||
private get dataConnection(): DataConnection | undefined {
|
||||
return this.peer?.connections[this.peerID]?.find(c => c.type === 'data' && c.open);
|
||||
}
|
||||
|
||||
private get callConnection(): MediaConnection | undefined {
|
||||
return this.peer?.connections[this.peerID]?.find(c => c.type === 'media' && c.open);
|
||||
}
|
||||
|
||||
private send(data: any) {
|
||||
this.dataConnection?.send(data);
|
||||
}
|
||||
|
|
@ -326,18 +237,20 @@ export default class AssistManager {
|
|||
|
||||
|
||||
private disconnectTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||
private closeDataConnectionTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||
private handleCommand(command: string) {
|
||||
console.log("Data command", command)
|
||||
switch (command) {
|
||||
case "unload":
|
||||
//this.onTrackerCallEnd();
|
||||
this.onCallDisconnect()
|
||||
this.dataConnection?.close();
|
||||
this.closeDataConnectionTimeout = setTimeout(() => {
|
||||
this.onCallDisconnect()
|
||||
this.dataConnection?.close();
|
||||
}, 1500);
|
||||
this.disconnectTimeout = setTimeout(() => {
|
||||
this.onTrackerCallEnd();
|
||||
this.setStatus(ConnectionStatus.Disconnected);
|
||||
}, 15000); // TODO: more convenient way
|
||||
//this.dataConnection?.close();
|
||||
return;
|
||||
case "call_end":
|
||||
this.onTrackerCallEnd();
|
||||
|
|
@ -349,29 +262,17 @@ export default class AssistManager {
|
|||
}
|
||||
}
|
||||
|
||||
// private mmtid?:ReturnType<typeof setTimeout>
|
||||
private onMouseMove = (e: MouseEvent): void => {
|
||||
// this.mmtid && clearTimeout(this.mmtid)
|
||||
// this.mmtid = setTimeout(() => {
|
||||
const data = this.md.getInternalCoordinates(e);
|
||||
this.send({ x: Math.round(data.x), y: Math.round(data.y) });
|
||||
// }, 5)
|
||||
}
|
||||
|
||||
|
||||
// private wtid?: ReturnType<typeof setTimeout>
|
||||
// private scrollDelta: [number, number] = [0,0]
|
||||
private onWheel = (e: WheelEvent): void => {
|
||||
e.preventDefault()
|
||||
//throttling makes movements less smooth
|
||||
// this.wtid && clearTimeout(this.wtid)
|
||||
// this.scrollDelta[0] += e.deltaX
|
||||
// this.scrollDelta[1] += e.deltaY
|
||||
// this.wtid = setTimeout(() => {
|
||||
this.send({ type: "scroll", delta: [ e.deltaX, e.deltaY ]})//this.scrollDelta });
|
||||
this.onMouseMove(e)
|
||||
// this.scrollDelta = [0,0]
|
||||
// }, 20)
|
||||
//throttling makes movements less smooth, so it is omitted
|
||||
//this.onMouseMove(e)
|
||||
this.send({ type: "scroll", delta: [ e.deltaX, e.deltaY ]})
|
||||
}
|
||||
|
||||
private onMouseClick = (e: MouseEvent): void => {
|
||||
|
|
@ -459,7 +360,7 @@ export default class AssistManager {
|
|||
});
|
||||
|
||||
this.md.overlay.addEventListener("mousemove", this.onMouseMove)
|
||||
// this.md.overlay.addEventListener("click", this.onMouseClick)
|
||||
this.md.overlay.addEventListener("click", this.onMouseClick)
|
||||
});
|
||||
//call.peerConnection.addEventListener("track", e => console.log('newtrack',e.track))
|
||||
|
||||
|
|
@ -473,13 +374,15 @@ export default class AssistManager {
|
|||
window.addEventListener("beforeunload", this.initiateCallEnd)
|
||||
}
|
||||
|
||||
closed = false
|
||||
clear() {
|
||||
this.closed =true
|
||||
this.initiateCallEnd();
|
||||
this.dataCheckIntervalID && clearInterval(this.dataCheckIntervalID);
|
||||
if (this.peer) {
|
||||
//console.log("destroying peer...")
|
||||
console.log("destroying peer...")
|
||||
const peer = this.peer; // otherwise it calls reconnection on data chan close
|
||||
this.peer = null;
|
||||
peer.disconnect();
|
||||
peer.destroy();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,486 @@
|
|||
// import type Peer from 'peerjs';
|
||||
// import type { DataConnection, MediaConnection } from 'peerjs';
|
||||
// import type MessageDistributor from '../MessageDistributor';
|
||||
// import type { Message } from '../messages'
|
||||
// import store from 'App/store';
|
||||
// import type { LocalStream } from './LocalStream';
|
||||
// import { update, getState } from '../../store';
|
||||
// import { iceServerConfigFromString } from 'App/utils'
|
||||
|
||||
|
||||
// export enum CallingState {
|
||||
// Reconnecting,
|
||||
// Requesting,
|
||||
// True,
|
||||
// False,
|
||||
// };
|
||||
|
||||
// export enum ConnectionStatus {
|
||||
// Connecting,
|
||||
// WaitingMessages,
|
||||
// Connected,
|
||||
// Inactive,
|
||||
// Disconnected,
|
||||
// Error,
|
||||
// };
|
||||
|
||||
|
||||
// export function getStatusText(status: ConnectionStatus): string {
|
||||
// switch(status) {
|
||||
// case ConnectionStatus.Connecting:
|
||||
// return "Connecting...";
|
||||
// case ConnectionStatus.Connected:
|
||||
// return "";
|
||||
// case ConnectionStatus.Inactive:
|
||||
// return "Client tab is inactive";
|
||||
// case ConnectionStatus.Disconnected:
|
||||
// return "Disconnected";
|
||||
// case ConnectionStatus.Error:
|
||||
// return "Something went wrong. Try to reload the page.";
|
||||
// case ConnectionStatus.WaitingMessages:
|
||||
// return "Connected. Waiting for the data... (The tab might be inactive)"
|
||||
// }
|
||||
// }
|
||||
|
||||
// export interface State {
|
||||
// calling: CallingState,
|
||||
// peerConnectionStatus: ConnectionStatus,
|
||||
// remoteControl: boolean,
|
||||
// }
|
||||
|
||||
// export const INITIAL_STATE: State = {
|
||||
// calling: CallingState.False,
|
||||
// peerConnectionStatus: ConnectionStatus.Connecting,
|
||||
// remoteControl: false,
|
||||
// }
|
||||
|
||||
// const MAX_RECONNECTION_COUNT = 4;
|
||||
|
||||
|
||||
// function resolveURL(baseURL: string, relURL: string): string {
|
||||
// if (relURL.startsWith('#') || relURL === "") {
|
||||
// return relURL;
|
||||
// }
|
||||
// return new URL(relURL, baseURL).toString();
|
||||
// }
|
||||
|
||||
|
||||
// var match = /bar/.exec("foobar");
|
||||
// const re1 = /url\(("[^"]*"|'[^']*'|[^)]*)\)/g
|
||||
// const re2 = /@import "(.*?)"/g
|
||||
// function cssUrlsIndex(css: string): Array<[number, number]> {
|
||||
// const idxs: Array<[number, number]> = [];
|
||||
// const i1 = css.matchAll(re1);
|
||||
// // @ts-ignore
|
||||
// for (let m of i1) {
|
||||
// // @ts-ignore
|
||||
// const s: number = m.index + m[0].indexOf(m[1]);
|
||||
// const e: number = s + m[1].length;
|
||||
// idxs.push([s, e]);
|
||||
// }
|
||||
// const i2 = css.matchAll(re2);
|
||||
// // @ts-ignore
|
||||
// for (let m of i2) {
|
||||
// // @ts-ignore
|
||||
// const s = m.index + m[0].indexOf(m[1]);
|
||||
// const e = s + m[1].length;
|
||||
// idxs.push([s, e])
|
||||
// }
|
||||
// return idxs;
|
||||
// }
|
||||
// function unquote(str: string): [string, string] {
|
||||
// str = str.trim();
|
||||
// if (str.length <= 2) {
|
||||
// return [str, ""]
|
||||
// }
|
||||
// if (str[0] == '"' && str[str.length-1] == '"') {
|
||||
// return [ str.substring(1, str.length-1), "\""];
|
||||
// }
|
||||
// if (str[0] == '\'' && str[str.length-1] == '\'') {
|
||||
// return [ str.substring(1, str.length-1), "'" ];
|
||||
// }
|
||||
// return [str, ""]
|
||||
// }
|
||||
// function rewriteCSSLinks(css: string, rewriter: (rawurl: string) => string): string {
|
||||
// for (let idx of cssUrlsIndex(css)) {
|
||||
// const f = idx[0]
|
||||
// const t = idx[1]
|
||||
// const [ rawurl, q ] = unquote(css.substring(f, t));
|
||||
// css = css.substring(0,f) + q + rewriter(rawurl) + q + css.substring(t);
|
||||
// }
|
||||
// return css
|
||||
// }
|
||||
|
||||
// function resolveCSS(baseURL: string, css: string): string {
|
||||
// return rewriteCSSLinks(css, rawurl => resolveURL(baseURL, rawurl));
|
||||
// }
|
||||
|
||||
// export default class AssistManager {
|
||||
// constructor(private session, private md: MessageDistributor, private config) {}
|
||||
|
||||
// private setStatus(status: ConnectionStatus) {
|
||||
// if (status === ConnectionStatus.Connecting) {
|
||||
// this.md.setMessagesLoading(true);
|
||||
// } else {
|
||||
// this.md.setMessagesLoading(false);
|
||||
// }
|
||||
// if (status === ConnectionStatus.Connected) {
|
||||
// this.md.display(true);
|
||||
// } else {
|
||||
// this.md.display(false);
|
||||
// }
|
||||
// update({ peerConnectionStatus: status });
|
||||
// }
|
||||
|
||||
// private get peerID(): string {
|
||||
// return `${this.session.projectKey}-${this.session.sessionId}`
|
||||
// }
|
||||
|
||||
// private peer: Peer | null = null;
|
||||
// connectionAttempts: number = 0;
|
||||
// private peeropened: boolean = false;
|
||||
// connect() {
|
||||
// if (this.peer != null) {
|
||||
// console.error("AssistManager: trying to connect more than once");
|
||||
// return;
|
||||
// }
|
||||
// this.setStatus(ConnectionStatus.Connecting)
|
||||
// import('peerjs').then(({ default: Peer }) => {
|
||||
// const _config = {
|
||||
// // @ts-ignore
|
||||
// host: new URL(window.ENV.API_EDP).host,
|
||||
// path: '/assist',
|
||||
// port: location.protocol === 'https:' ? 443 : 80,
|
||||
// }
|
||||
|
||||
// if (this.config) {
|
||||
// _config['config'] = {
|
||||
// iceServers: this.config,
|
||||
// sdpSemantics: 'unified-plan',
|
||||
// iceTransportPolicy: 'relay',
|
||||
// };
|
||||
// }
|
||||
|
||||
// const peer = new Peer(_config);
|
||||
// this.peer = peer;
|
||||
// peer.on('error', e => {
|
||||
// if (e.type !== 'peer-unavailable') {
|
||||
// console.warn("AssistManager PeerJS peer error: ", e.type, e)
|
||||
// }
|
||||
// if (['peer-unavailable', 'network', 'webrtc'].includes(e.type)) {
|
||||
// if (this.peer && this.connectionAttempts++ < MAX_RECONNECTION_COUNT) {
|
||||
// this.setStatus(ConnectionStatus.Connecting);
|
||||
// this.connectToPeer();
|
||||
// } else {
|
||||
// this.setStatus(ConnectionStatus.Disconnected);
|
||||
// this.dataCheckIntervalID && clearInterval(this.dataCheckIntervalID);
|
||||
// }
|
||||
// } else {
|
||||
// console.error(`PeerJS error (on peer). Type ${e.type}`, e);
|
||||
// this.setStatus(ConnectionStatus.Error)
|
||||
// }
|
||||
// })
|
||||
// peer.on("open", () => {
|
||||
// if (this.peeropened) { return; }
|
||||
// this.peeropened = true;
|
||||
// this.connectToPeer();
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
// private dataCheckIntervalID: ReturnType<typeof setInterval> | undefined;
|
||||
// private connectToPeer() {
|
||||
// if (!this.peer) { return; }
|
||||
// this.setStatus(ConnectionStatus.Connecting);
|
||||
// const id = this.peerID;
|
||||
// const conn = this.peer.connect(id, { serialization: 'json', reliable: true});
|
||||
// conn.on('open', () => {
|
||||
// window.addEventListener("beforeunload", ()=>conn.open &&conn.send("unload"));
|
||||
|
||||
// //console.log("peer connected")
|
||||
|
||||
|
||||
// if (getState().calling === CallingState.Reconnecting) {
|
||||
// this._call()
|
||||
// }
|
||||
|
||||
// let i = 0;
|
||||
// let firstMessage = true;
|
||||
|
||||
// this.setStatus(ConnectionStatus.WaitingMessages)
|
||||
|
||||
// conn.on('data', (data) => {
|
||||
// if (!Array.isArray(data)) { return this.handleCommand(data); }
|
||||
// this.disconnectTimeout && clearTimeout(this.disconnectTimeout);
|
||||
// if (firstMessage) {
|
||||
// firstMessage = false;
|
||||
// this.setStatus(ConnectionStatus.Connected)
|
||||
// }
|
||||
|
||||
// let time = 0;
|
||||
// let ts0 = 0;
|
||||
// (data as Array<Message & { _id: number}>).forEach(msg => {
|
||||
|
||||
// // TODO: more appropriate way to do it.
|
||||
// if (msg._id === 60) {
|
||||
// // @ts-ignore
|
||||
// if (msg.name === 'src' || msg.name === 'href') {
|
||||
// // @ts-ignore
|
||||
// msg.value = resolveURL(msg.baseURL, msg.value);
|
||||
// // @ts-ignore
|
||||
// } else if (msg.name === 'style') {
|
||||
// // @ts-ignore
|
||||
// msg.value = resolveCSS(msg.baseURL, msg.value);
|
||||
// }
|
||||
// msg._id = 12;
|
||||
// } else if (msg._id === 61) { // "SetCSSDataURLBased"
|
||||
// // @ts-ignore
|
||||
// msg.data = resolveCSS(msg.baseURL, msg.data);
|
||||
// msg._id = 15;
|
||||
// } else if (msg._id === 67) { // "insert_rule"
|
||||
// // @ts-ignore
|
||||
// msg.rule = resolveCSS(msg.baseURL, msg.rule);
|
||||
// msg._id = 37;
|
||||
// }
|
||||
|
||||
|
||||
// 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.md.distributeMessage(tMsg, i++);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// const onDataClose = () => {
|
||||
// this.onCallDisconnect()
|
||||
// //console.log('closed peer conn. Reconnecting...')
|
||||
// this.connectToPeer();
|
||||
// }
|
||||
|
||||
// // this.dataCheckIntervalID = setInterval(() => {
|
||||
// // if (!this.dataConnection && getState().peerConnectionStatus === ConnectionStatus.Connected) {
|
||||
// // onDataClose();
|
||||
// // }
|
||||
// // }, 3000);
|
||||
// conn.on('close', onDataClose);// Does it work ?
|
||||
// conn.on("error", (e) => {
|
||||
// this.setStatus(ConnectionStatus.Error);
|
||||
// })
|
||||
// }
|
||||
|
||||
|
||||
// private get dataConnection(): DataConnection | undefined {
|
||||
// return this.peer?.connections[this.peerID]?.find(c => c.type === 'data' && c.open);
|
||||
// }
|
||||
|
||||
// private get callConnection(): MediaConnection | undefined {
|
||||
// return this.peer?.connections[this.peerID]?.find(c => c.type === 'media' && c.open);
|
||||
// }
|
||||
|
||||
// private send(data: any) {
|
||||
// this.dataConnection?.send(data);
|
||||
// }
|
||||
|
||||
|
||||
// private forceCallEnd() {
|
||||
// this.callConnection?.close();
|
||||
// }
|
||||
// private notifyCallEnd() {
|
||||
// const dataConn = this.dataConnection;
|
||||
// if (dataConn) {
|
||||
// dataConn.send("call_end");
|
||||
// }
|
||||
// }
|
||||
// private initiateCallEnd = () => {
|
||||
// this.forceCallEnd();
|
||||
// this.notifyCallEnd();
|
||||
// this.localCallData && this.localCallData.onCallEnd();
|
||||
// }
|
||||
|
||||
// private onTrackerCallEnd = () => {
|
||||
// console.log('onTrackerCallEnd')
|
||||
// this.forceCallEnd();
|
||||
// if (getState().calling === CallingState.Requesting) {
|
||||
// this.localCallData && this.localCallData.onReject();
|
||||
// }
|
||||
// this.localCallData && this.localCallData.onCallEnd();
|
||||
// }
|
||||
|
||||
// private onCallDisconnect = () => {
|
||||
// if (getState().calling === CallingState.True) {
|
||||
// update({ calling: CallingState.Reconnecting });
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// private disconnectTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||
// private handleCommand(command: string) {
|
||||
// console.log("Data command", command)
|
||||
// switch (command) {
|
||||
// case "unload":
|
||||
// //this.onTrackerCallEnd();
|
||||
// this.onCallDisconnect()
|
||||
// this.dataConnection?.close();
|
||||
// this.disconnectTimeout = setTimeout(() => {
|
||||
// this.onTrackerCallEnd();
|
||||
// this.setStatus(ConnectionStatus.Disconnected);
|
||||
// }, 15000); // TODO: more convenient way
|
||||
// //this.dataConnection?.close();
|
||||
// return;
|
||||
// case "call_end":
|
||||
// this.onTrackerCallEnd();
|
||||
// return;
|
||||
// case "call_error":
|
||||
// this.onTrackerCallEnd();
|
||||
// this.setStatus(ConnectionStatus.Error);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // private mmtid?:ReturnType<typeof setTimeout>
|
||||
// private onMouseMove = (e: MouseEvent): void => {
|
||||
// // this.mmtid && clearTimeout(this.mmtid)
|
||||
// // this.mmtid = setTimeout(() => {
|
||||
// const data = this.md.getInternalCoordinates(e);
|
||||
// this.send({ x: Math.round(data.x), y: Math.round(data.y) });
|
||||
// // }, 5)
|
||||
// }
|
||||
|
||||
|
||||
// // private wtid?: ReturnType<typeof setTimeout>
|
||||
// // private scrollDelta: [number, number] = [0,0]
|
||||
// private onWheel = (e: WheelEvent): void => {
|
||||
// e.preventDefault()
|
||||
// //throttling makes movements less smooth
|
||||
// // this.wtid && clearTimeout(this.wtid)
|
||||
// // this.scrollDelta[0] += e.deltaX
|
||||
// // this.scrollDelta[1] += e.deltaY
|
||||
// // this.wtid = setTimeout(() => {
|
||||
// this.send({ type: "scroll", delta: [ e.deltaX, e.deltaY ]})//this.scrollDelta });
|
||||
// this.onMouseMove(e)
|
||||
// // this.scrollDelta = [0,0]
|
||||
// // }, 20)
|
||||
// }
|
||||
|
||||
// private onMouseClick = (e: MouseEvent): void => {
|
||||
// const conn = this.dataConnection;
|
||||
// if (!conn) { return; }
|
||||
// const data = this.md.getInternalCoordinates(e);
|
||||
// // const el = this.md.getElementFromPoint(e); // requires requestiong node_id from domManager
|
||||
// const el = this.md.getElementFromInternalPoint(data)
|
||||
// if (el instanceof HTMLElement) {
|
||||
// el.focus()
|
||||
// el.oninput = e => e.preventDefault();
|
||||
// el.onkeydown = e => e.preventDefault();
|
||||
// }
|
||||
// conn.send({ type: "click", x: Math.round(data.x), y: Math.round(data.y) });
|
||||
// }
|
||||
|
||||
// private toggleRemoteControl = (flag?: boolean) => {
|
||||
// const state = getState().remoteControl;
|
||||
// const newState = typeof flag === 'boolean' ? flag : !state;
|
||||
// if (state === newState) { return }
|
||||
// if (newState) {
|
||||
// this.md.overlay.addEventListener("click", this.onMouseClick);
|
||||
// this.md.overlay.addEventListener("wheel", this.onWheel)
|
||||
// update({ remoteControl: true })
|
||||
// } else {
|
||||
// this.md.overlay.removeEventListener("click", this.onMouseClick);
|
||||
// this.md.overlay.removeEventListener("wheel", this.onWheel);
|
||||
// update({ remoteControl: false })
|
||||
// }
|
||||
// }
|
||||
|
||||
// private localCallData: {
|
||||
// localStream: LocalStream,
|
||||
// onStream: (s: MediaStream)=>void,
|
||||
// onCallEnd: () => void,
|
||||
// onReject: () => void,
|
||||
// onError?: ()=> void
|
||||
// } | null = null
|
||||
|
||||
// call(localStream: LocalStream, onStream: (s: MediaStream)=>void, onCallEnd: () => void, onReject: () => void, onError?: ()=> void): { end: Function, toggleRemoteControl: Function } {
|
||||
// this.localCallData = {
|
||||
// localStream,
|
||||
// onStream,
|
||||
// onCallEnd: () => {
|
||||
// onCallEnd();
|
||||
// this.toggleRemoteControl(false);
|
||||
// this.md.overlay.removeEventListener("mousemove", this.onMouseMove);
|
||||
// this.md.overlay.removeEventListener("click", this.onMouseClick);
|
||||
// update({ calling: CallingState.False });
|
||||
// this.localCallData = null;
|
||||
// },
|
||||
// onReject,
|
||||
// onError,
|
||||
// }
|
||||
// this._call()
|
||||
// return {
|
||||
// end: this.initiateCallEnd,
|
||||
// toggleRemoteControl: this.toggleRemoteControl,
|
||||
// }
|
||||
// }
|
||||
|
||||
// private _call() {
|
||||
// if (!this.peer || !this.localCallData || ![CallingState.False, CallingState.Reconnecting].includes(getState().calling)) { return null; }
|
||||
|
||||
// update({ calling: CallingState.Requesting });
|
||||
|
||||
// //console.log('calling...', this.localCallData.localStream)
|
||||
|
||||
// const call = this.peer.call(this.peerID, this.localCallData.localStream.stream);
|
||||
// this.localCallData.localStream.onVideoTrack(vTrack => {
|
||||
// const sender = call.peerConnection.getSenders().find(s => s.track?.kind === "video")
|
||||
// if (!sender) {
|
||||
// //logger.warn("No video sender found")
|
||||
// return
|
||||
// }
|
||||
// //logger.log("sender found:", sender)
|
||||
// sender.replaceTrack(vTrack)
|
||||
// })
|
||||
|
||||
// call.on('stream', stream => {
|
||||
// update({ calling: CallingState.True });
|
||||
// this.localCallData && this.localCallData.onStream(stream);
|
||||
// this.send({
|
||||
// name: store.getState().getIn([ 'user', 'account', 'name']),
|
||||
// });
|
||||
|
||||
// this.md.overlay.addEventListener("mousemove", this.onMouseMove)
|
||||
// // this.md.overlay.addEventListener("click", this.onMouseClick)
|
||||
// });
|
||||
// //call.peerConnection.addEventListener("track", e => console.log('newtrack',e.track))
|
||||
|
||||
// call.on("close", this.localCallData.onCallEnd);
|
||||
// call.on("error", (e) => {
|
||||
// console.error("PeerJS error (on call):", e)
|
||||
// this.initiateCallEnd();
|
||||
// this.localCallData && this.localCallData.onError && this.localCallData.onError();
|
||||
// });
|
||||
|
||||
// window.addEventListener("beforeunload", this.initiateCallEnd)
|
||||
// }
|
||||
|
||||
// clear() {
|
||||
// this.initiateCallEnd();
|
||||
// this.dataCheckIntervalID && clearInterval(this.dataCheckIntervalID);
|
||||
// if (this.peer) {
|
||||
// //console.log("destroying peer...")
|
||||
// const peer = this.peer; // otherwise it calls reconnection on data chan close
|
||||
// this.peer = null;
|
||||
// peer.destroy();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
|
@ -1,24 +1,22 @@
|
|||
import type StatedScreen from '../StatedScreen';
|
||||
import type { Message, SetNodeScroll, CreateElementNode } from '../messages';
|
||||
import type { TimedMessage } from '../Timed';
|
||||
|
||||
import logger from 'App/logger';
|
||||
import StylesManager, { rewriteNodeStyleSheet } from './StylesManager';
|
||||
import ListWalker from './ListWalker';
|
||||
import type { Timed }from '../Timed';
|
||||
|
||||
const IGNORED_ATTRS = [ "autocomplete", "name" ];
|
||||
|
||||
const ATTR_NAME_REGEXP = /([^\t\n\f \/>"'=]+)/; // regexp costs ~
|
||||
|
||||
export default class DOMManager extends ListWalker<TimedMessage> {
|
||||
export default class DOMManager extends ListWalker<Message> {
|
||||
private isMobile: boolean;
|
||||
private screen: StatedScreen;
|
||||
private nl: Array<Node> = [];
|
||||
private isLink: Array<boolean> = []; // Optimisations
|
||||
private bodyId: number = -1;
|
||||
private postponedBodyMessage: CreateElementNode | null = null;
|
||||
private nodeScrollManagers: Array<ListWalker<Timed & SetNodeScroll>> = [];
|
||||
private nodeScrollManagers: Array<ListWalker<SetNodeScroll>> = [];
|
||||
|
||||
private stylesManager: StylesManager;
|
||||
|
||||
|
|
@ -36,7 +34,7 @@ export default class DOMManager extends ListWalker<TimedMessage> {
|
|||
return this.startTime;
|
||||
}
|
||||
|
||||
add(m: TimedMessage): void {
|
||||
add(m: Message): void {
|
||||
switch (m.tp) {
|
||||
case "set_node_scroll":
|
||||
if (!this.nodeScrollManagers[ m.id ]) {
|
||||
|
|
@ -104,8 +102,9 @@ export default class DOMManager extends ListWalker<TimedMessage> {
|
|||
if ((el instanceof HTMLStyleElement) && // TODO: correct ordering OR filter in tracker
|
||||
el.sheet &&
|
||||
el.sheet.cssRules &&
|
||||
el.sheet.cssRules.length > 0) {
|
||||
logger.log("Trying to insert child to style tag with virtual rules: ", this.nl[ parentID ], this.nl[ id ]);
|
||||
el.sheet.cssRules.length > 0 &&
|
||||
el.innerText.trim().length === 0) {
|
||||
logger.log("Trying to insert child to a style tag with virtual rules: ", this.nl[ parentID ], this.nl[ id ]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -183,6 +182,9 @@ export default class DOMManager extends ListWalker<TimedMessage> {
|
|||
}
|
||||
this.stylesManager.setStyleHandlers(node, value);
|
||||
}
|
||||
if (node.namespaceURI === 'http://www.w3.org/2000/svg' && value.startsWith("url(")) {
|
||||
value = "url(#" + (value.split("#")[1] ||")")
|
||||
}
|
||||
try {
|
||||
node.setAttribute(name, value);
|
||||
} catch(e) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { Timed } from '../Timed';
|
||||
import type { Timed } from '../messages/timed';
|
||||
|
||||
export default class ListWalker<T extends Timed> {
|
||||
// Optimisation: #prop compiles to method that costs mor than strict property call.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
// import type { MobX } from '../messages';
|
||||
// import type { Timed } from '../Timed';
|
||||
|
||||
// import ListWalker from './ListWalker';
|
||||
|
||||
// type MobXTimed = MobX & Timed;
|
||||
|
||||
// export default class MobXStateManager extends ListWalker<MobXTimed> {
|
||||
// moveToLast(t: number) {
|
||||
// super.moveApply(t, )
|
||||
// }
|
||||
|
||||
|
||||
// }
|
||||
|
|
@ -1,15 +1,12 @@
|
|||
import type StatedScreen from '../StatedScreen';
|
||||
import type { MouseMove } from '../messages';
|
||||
import type { Timed } from '../Timed';
|
||||
|
||||
import ListWalker from './ListWalker';
|
||||
|
||||
type MouseMoveTimed = MouseMove & Timed;
|
||||
|
||||
const HOVER_CLASS = "-openreplay-hover";
|
||||
const HOVER_CLASS_DEPR = "-asayer-hover";
|
||||
|
||||
export default class MouseManager extends ListWalker<MouseMoveTimed> {
|
||||
export default class MouseManager extends ListWalker<MouseMove> {
|
||||
private hoverElements: Array<Element> = [];
|
||||
|
||||
constructor(private screen: StatedScreen) {super();}
|
||||
|
|
@ -39,6 +36,7 @@ export default class MouseManager extends ListWalker<MouseMoveTimed> {
|
|||
if (!!lastMouseMove){
|
||||
// @ts-ignore TODO
|
||||
this.screen.cursor.move(lastMouseMove);
|
||||
//window.getComputedStyle(this.screen.getCursorTarget()).cursor === 'pointer' // might nfluence performance though
|
||||
this.updateHover();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
import type { PerformanceTrack, SetPageVisibility } from '../messages';
|
||||
import type { Timed } from '../Timed';
|
||||
|
||||
import ListWalker from './ListWalker';
|
||||
|
||||
type TimedPerformanceTrack = Timed & PerformanceTrack;
|
||||
type TimedSetPageVisibility = Timed & SetPageVisibility;
|
||||
|
||||
export type PerformanceChartPoint = {
|
||||
time: number,
|
||||
usedHeap: number,
|
||||
|
|
@ -15,7 +11,7 @@ export type PerformanceChartPoint = {
|
|||
nodesCount: number,
|
||||
}
|
||||
|
||||
export default class PerformanceTrackManager extends ListWalker<TimedPerformanceTrack> {
|
||||
export default class PerformanceTrackManager extends ListWalker<PerformanceTrack> {
|
||||
private chart: Array<PerformanceChartPoint> = [];
|
||||
private isHidden: boolean = false;
|
||||
private timeCorrection: number = 0;
|
||||
|
|
@ -26,7 +22,7 @@ export default class PerformanceTrackManager extends ListWalker<TimedPerformance
|
|||
private prevNodesCount: number = 0;
|
||||
|
||||
|
||||
add(msg: TimedPerformanceTrack):void {
|
||||
add(msg: PerformanceTrack):void {
|
||||
let fps: number | undefined;
|
||||
let cpu: number | undefined;
|
||||
if (!this.isHidden && this.prevTime != null) {
|
||||
|
|
@ -76,7 +72,7 @@ export default class PerformanceTrackManager extends ListWalker<TimedPerformance
|
|||
}
|
||||
}
|
||||
|
||||
handleVisibility(msg: TimedSetPageVisibility):void {
|
||||
handleVisibility(msg: SetPageVisibility):void {
|
||||
if (!this.isHidden && msg.hidden && this.prevTime != null) {
|
||||
this.timeCorrection = msg.time - this.prevTime;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
|
||||
import type StatedScreen from '../StatedScreen';
|
||||
import type { CssInsertRule, CssDeleteRule } from '../messages';
|
||||
import type { Timed } from '../Timed';
|
||||
|
||||
type CSSRuleMessage = CssInsertRule | CssDeleteRule;
|
||||
type TimedCSSRuleMessage = Timed & CSSRuleMessage;
|
||||
|
||||
import logger from 'App/logger';
|
||||
import ListWalker from './ListWalker';
|
||||
|
|
@ -25,7 +22,7 @@ export function rewriteNodeStyleSheet(doc: Document, node: HTMLLinkElement | HTM
|
|||
}
|
||||
}
|
||||
|
||||
export default class StylesManager extends ListWalker<TimedCSSRuleMessage> {
|
||||
export default class StylesManager extends ListWalker<CSSRuleMessage> {
|
||||
private linkLoadingCount: number = 0;
|
||||
private linkLoadPromises: Array<Promise<void>> = [];
|
||||
private skipCSSLinks: Array<string> = []; // should be common for all pages
|
||||
|
|
|
|||
|
|
@ -1,715 +0,0 @@
|
|||
// Auto-generated, do not edit
|
||||
|
||||
import PrimitiveReader from './PrimitiveReader';
|
||||
|
||||
export const ID_TP_MAP = {
|
||||
|
||||
0: "timestamp",
|
||||
2: "session_disconnect",
|
||||
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",
|
||||
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",
|
||||
69: "mouse_click",
|
||||
70: "create_i_frame_document",
|
||||
90: "ios_session_start",
|
||||
93: "ios_custom_event",
|
||||
96: "ios_screen_changes",
|
||||
100: "ios_click_event",
|
||||
102: "ios_performance_event",
|
||||
103: "ios_log",
|
||||
105: "ios_network_call",
|
||||
} as const;
|
||||
|
||||
|
||||
export interface Timestamp {
|
||||
tp: "timestamp",
|
||||
timestamp: number,
|
||||
}
|
||||
|
||||
export interface SessionDisconnect {
|
||||
tp: "session_disconnect",
|
||||
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 Fetch {
|
||||
tp: "fetch",
|
||||
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 MouseClick {
|
||||
tp: "mouse_click",
|
||||
id: number,
|
||||
hesitationTime: number,
|
||||
label: string,
|
||||
selector: string,
|
||||
}
|
||||
|
||||
export interface CreateIFrameDocument {
|
||||
tp: "create_i_frame_document",
|
||||
frameID: number,
|
||||
id: number,
|
||||
}
|
||||
|
||||
export interface 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 interface IosCustomEvent {
|
||||
tp: "ios_custom_event",
|
||||
timestamp: number,
|
||||
length: number,
|
||||
name: string,
|
||||
payload: string,
|
||||
}
|
||||
|
||||
export interface IosScreenChanges {
|
||||
tp: "ios_screen_changes",
|
||||
timestamp: number,
|
||||
length: number,
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
}
|
||||
|
||||
export interface IosClickEvent {
|
||||
tp: "ios_click_event",
|
||||
timestamp: number,
|
||||
length: number,
|
||||
label: string,
|
||||
x: number,
|
||||
y: number,
|
||||
}
|
||||
|
||||
export interface IosPerformanceEvent {
|
||||
tp: "ios_performance_event",
|
||||
timestamp: number,
|
||||
length: number,
|
||||
name: string,
|
||||
value: number,
|
||||
}
|
||||
|
||||
export interface IosLog {
|
||||
tp: "ios_log",
|
||||
timestamp: number,
|
||||
length: number,
|
||||
severity: string,
|
||||
content: string,
|
||||
}
|
||||
|
||||
export interface 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 | CssInsertRule | CssDeleteRule | Fetch | Profiler | OTable | Redux | Vuex | MobX | NgRx | GraphQl | PerformanceTrack | ConnectionInformation | SetPageVisibility | LongTask | MouseClick | CreateIFrameDocument | IosSessionStart | IosCustomEvent | IosScreenChanges | IosClickEvent | IosPerformanceEvent | IosLog | IosNetworkCall;
|
||||
|
||||
export default function (r: PrimitiveReader): Message | null {
|
||||
const tp = r.readUint()
|
||||
switch (tp) {
|
||||
|
||||
case 0:
|
||||
return {
|
||||
tp: ID_TP_MAP[0],
|
||||
timestamp: r.readUint(),
|
||||
};
|
||||
|
||||
case 2:
|
||||
return {
|
||||
tp: ID_TP_MAP[2],
|
||||
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 69:
|
||||
return {
|
||||
tp: ID_TP_MAP[69],
|
||||
id: r.readUint(),
|
||||
hesitationTime: r.readUint(),
|
||||
label: r.readString(),
|
||||
selector: r.readString(),
|
||||
};
|
||||
|
||||
case 70:
|
||||
return {
|
||||
tp: ID_TP_MAP[70],
|
||||
frameID: r.readUint(),
|
||||
id: r.readUint(),
|
||||
};
|
||||
|
||||
case 90:
|
||||
return {
|
||||
tp: ID_TP_MAP[90],
|
||||
timestamp: r.readUint(),
|
||||
projectID: r.readUint(),
|
||||
trackerVersion: r.readString(),
|
||||
revID: r.readString(),
|
||||
userUUID: r.readString(),
|
||||
userOS: r.readString(),
|
||||
userOSVersion: r.readString(),
|
||||
userDevice: r.readString(),
|
||||
userDeviceType: r.readString(),
|
||||
userCountry: r.readString(),
|
||||
};
|
||||
|
||||
case 93:
|
||||
return {
|
||||
tp: ID_TP_MAP[93],
|
||||
timestamp: r.readUint(),
|
||||
length: r.readUint(),
|
||||
name: r.readString(),
|
||||
payload: r.readString(),
|
||||
};
|
||||
|
||||
case 96:
|
||||
return {
|
||||
tp: ID_TP_MAP[96],
|
||||
timestamp: r.readUint(),
|
||||
length: r.readUint(),
|
||||
x: r.readUint(),
|
||||
y: r.readUint(),
|
||||
width: r.readUint(),
|
||||
height: r.readUint(),
|
||||
};
|
||||
|
||||
case 100:
|
||||
return {
|
||||
tp: ID_TP_MAP[100],
|
||||
timestamp: r.readUint(),
|
||||
length: r.readUint(),
|
||||
label: r.readString(),
|
||||
x: r.readUint(),
|
||||
y: r.readUint(),
|
||||
};
|
||||
|
||||
case 102:
|
||||
return {
|
||||
tp: ID_TP_MAP[102],
|
||||
timestamp: r.readUint(),
|
||||
length: r.readUint(),
|
||||
name: r.readString(),
|
||||
value: r.readUint(),
|
||||
};
|
||||
|
||||
case 103:
|
||||
return {
|
||||
tp: ID_TP_MAP[103],
|
||||
timestamp: r.readUint(),
|
||||
length: r.readUint(),
|
||||
severity: r.readString(),
|
||||
content: r.readString(),
|
||||
};
|
||||
|
||||
case 105:
|
||||
return {
|
||||
tp: ID_TP_MAP[105],
|
||||
timestamp: r.readUint(),
|
||||
length: r.readUint(),
|
||||
duration: r.readUint(),
|
||||
headers: r.readString(),
|
||||
body: r.readString(),
|
||||
url: r.readString(),
|
||||
success: r.readBoolean(),
|
||||
method: r.readString(),
|
||||
status: r.readUint(),
|
||||
};
|
||||
|
||||
default:
|
||||
throw new Error(`Unrecognizable message type: ${ tp }`)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import type { RawMessage } from './raw'
|
||||
|
||||
import { TP_MAP } from './raw'
|
||||
|
||||
export default class JSONRawMessageReader {
|
||||
constructor(private messages: any[] = []){}
|
||||
append(messages: any[]) {
|
||||
this.messages = this.messages.concat(messages)
|
||||
}
|
||||
readMessage(): RawMessage | null {
|
||||
const msg = this.messages.shift()
|
||||
if (!msg) { return null }
|
||||
msg.tp = TP_MAP[msg._id]
|
||||
delete msg._id
|
||||
return msg as RawMessage
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
import type { Message } from './message';
|
||||
import type { RawMessage } from './raw';
|
||||
import logger from 'App/logger';
|
||||
import RawMessageReader from './RawMessageReader';
|
||||
|
||||
// TODO: composition instead of inheritance
|
||||
// needSkipMessage() and next() methods here use buf and p protected properties,
|
||||
// which should be probably somehow incapsulated
|
||||
export default class MFileReader extends RawMessageReader {
|
||||
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 readRawMessage(): RawMessage | null {
|
||||
this.skip(8);
|
||||
try {
|
||||
return super.readMessage();
|
||||
} catch (e) {
|
||||
this.error = true;
|
||||
logger.error("Read message error:", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
hasNext():boolean {
|
||||
return !this.error && this.hasNextByte();
|
||||
}
|
||||
|
||||
next(): [ Message, number] | null {
|
||||
if (!this.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while (this.needSkipMessage()) {
|
||||
this.readRawMessage();
|
||||
}
|
||||
this.pLastMessageID = this.p;
|
||||
|
||||
const rMsg = this.readRawMessage();
|
||||
if (!rMsg) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (rMsg.tp === "timestamp") {
|
||||
this.currentTime = rMsg.timestamp - this.startTime;
|
||||
} else {
|
||||
const msg = Object.assign(rMsg, {
|
||||
time: this.currentTime,
|
||||
_index: this.pLastMessageID,
|
||||
})
|
||||
return [msg, this.pLastMessageID];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
import type { Message } from './message'
|
||||
import type {
|
||||
RawMessage,
|
||||
RawSetNodeAttributeURLBased,
|
||||
RawSetNodeAttribute,
|
||||
RawSetCssDataURLBased,
|
||||
RawSetCssData,
|
||||
RawCssInsertRuleURLBased,
|
||||
RawCssInsertRule,
|
||||
} from './raw'
|
||||
import RawMessageReader from './RawMessageReader'
|
||||
import type { RawMessageReaderI } from './RawMessageReader'
|
||||
import { resolveURL, resolveCSS } from './urlResolve'
|
||||
|
||||
|
||||
const resolveMsg = {
|
||||
"set_node_attribute_url_based": (msg: RawSetNodeAttributeURLBased): RawSetNodeAttribute =>
|
||||
({
|
||||
...msg,
|
||||
value: msg.name === 'src' || msg.name === 'href'
|
||||
? resolveURL(msg.baseURL, msg.value)
|
||||
: (msg.name === 'style'
|
||||
? resolveCSS(msg.baseURL, msg.value)
|
||||
: msg.value
|
||||
),
|
||||
tp: "set_node_attribute",
|
||||
}),
|
||||
"set_css_data_url_based": (msg: RawSetCssDataURLBased): RawSetCssData =>
|
||||
({
|
||||
...msg,
|
||||
data: resolveCSS(msg.baseURL, msg.data),
|
||||
tp: "set_css_data",
|
||||
}),
|
||||
"css_insert_rule_url_based": (msg: RawCssInsertRuleURLBased): RawCssInsertRule =>
|
||||
({
|
||||
...msg,
|
||||
rule: resolveCSS(msg.baseURL, msg.rule),
|
||||
tp: "css_insert_rule",
|
||||
})
|
||||
}
|
||||
|
||||
export default class MStreamReader {
|
||||
constructor(private readonly r: RawMessageReaderI = new RawMessageReader()){}
|
||||
|
||||
// append(buf: Uint8Array) {
|
||||
// this.r.append(buf)
|
||||
// }
|
||||
|
||||
private t0: number = 0
|
||||
private t: number = 0
|
||||
private idx: number = 0
|
||||
readNext(): Message | null {
|
||||
let msg = this.r.readMessage()
|
||||
if (msg === null) { return null }
|
||||
if (msg.tp === "timestamp" || msg.tp === "batch_meta") {
|
||||
this.t0 = this.t0 || msg.timestamp
|
||||
this.t = msg.timestamp - this.t0
|
||||
return this.readNext()
|
||||
}
|
||||
|
||||
// why typescript doesn't work here?
|
||||
msg = (resolveMsg[msg.tp] || ((m:RawMessage)=>m))(msg)
|
||||
|
||||
return Object.assign(msg, {
|
||||
time: this.t,
|
||||
_index: this.idx++,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
export default class PrimitiveReader {
|
||||
protected p: number = 0
|
||||
constructor(protected buf: Uint8Array = new Uint8Array(0)) {}
|
||||
|
||||
append(buf: Uint8Array) {
|
||||
const newBuf = new Uint8Array(this.buf.length + buf.length)
|
||||
newBuf.set(this.buf)
|
||||
newBuf.set(buf, this.buf.length)
|
||||
this.buf = newBuf
|
||||
}
|
||||
|
||||
hasNextByte(): boolean {
|
||||
return this.p < this.buf.length
|
||||
}
|
||||
|
||||
readUint(): number | null {
|
||||
let p = this.p, r = 0, s = 1, b
|
||||
do {
|
||||
if (p >= this.buf.length) {
|
||||
return null
|
||||
}
|
||||
b = this.buf[ p++ ]
|
||||
r += (b & 0x7F) * s
|
||||
s *= 128;
|
||||
} while (b >= 0x80)
|
||||
this.p = p
|
||||
return r;
|
||||
}
|
||||
|
||||
readInt(): number | null {
|
||||
let u = this.readUint();
|
||||
if (u === null) { return u }
|
||||
if (u % 2) {
|
||||
u = (u + 1) / -2;
|
||||
} else {
|
||||
u = u / 2;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
readString(): string | null {
|
||||
var l = this.readUint();
|
||||
if (l === null || this.p + l > this.buf.length) { return null }
|
||||
return new TextDecoder().decode(this.buf.subarray(this.p, this.p+=l));
|
||||
}
|
||||
|
||||
readBoolean(): boolean | null {
|
||||
if (this.p >= this.buf.length) { return null }
|
||||
return !!this.buf[this.p++];
|
||||
}
|
||||
|
||||
skip(n: number) {
|
||||
this.p += n;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,758 @@
|
|||
// Auto-generated, do not edit
|
||||
|
||||
import PrimitiveReader from './PrimitiveReader'
|
||||
import type { RawMessage } from './raw'
|
||||
|
||||
export interface RawMessageReaderI {
|
||||
readMessage(): RawMessage | null
|
||||
}
|
||||
|
||||
export default class RawMessageReader extends PrimitiveReader {
|
||||
readMessage(): RawMessage | null {
|
||||
const p = this.p
|
||||
const resetPointer = () => {
|
||||
this.p = p
|
||||
return null
|
||||
}
|
||||
|
||||
const tp = this.readUint()
|
||||
if (tp === null) { return resetPointer() }
|
||||
|
||||
switch (tp) {
|
||||
|
||||
case 80: {
|
||||
const pageNo = this.readUint(); if (pageNo === null) { return resetPointer() }
|
||||
const firstIndex = this.readUint(); if (firstIndex === null) { return resetPointer() }
|
||||
const timestamp = this.readInt(); if (timestamp === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "batch_meta",
|
||||
pageNo,
|
||||
firstIndex,
|
||||
timestamp,
|
||||
};
|
||||
}
|
||||
|
||||
case 0: {
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "timestamp",
|
||||
timestamp,
|
||||
};
|
||||
}
|
||||
|
||||
case 2: {
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "session_disconnect",
|
||||
timestamp,
|
||||
};
|
||||
}
|
||||
|
||||
case 4: {
|
||||
const url = this.readString(); if (url === null) { return resetPointer() }
|
||||
const referrer = this.readString(); if (referrer === null) { return resetPointer() }
|
||||
const navigationStart = this.readUint(); if (navigationStart === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "set_page_location",
|
||||
url,
|
||||
referrer,
|
||||
navigationStart,
|
||||
};
|
||||
}
|
||||
|
||||
case 5: {
|
||||
const width = this.readUint(); if (width === null) { return resetPointer() }
|
||||
const height = this.readUint(); if (height === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "set_viewport_size",
|
||||
width,
|
||||
height,
|
||||
};
|
||||
}
|
||||
|
||||
case 6: {
|
||||
const x = this.readInt(); if (x === null) { return resetPointer() }
|
||||
const y = this.readInt(); if (y === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "set_viewport_scroll",
|
||||
x,
|
||||
y,
|
||||
};
|
||||
}
|
||||
|
||||
case 7: {
|
||||
|
||||
return {
|
||||
tp: "create_document",
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
case 8: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const parentID = this.readUint(); if (parentID === null) { return resetPointer() }
|
||||
const index = this.readUint(); if (index === null) { return resetPointer() }
|
||||
const tag = this.readString(); if (tag === null) { return resetPointer() }
|
||||
const svg = this.readBoolean(); if (svg === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "create_element_node",
|
||||
id,
|
||||
parentID,
|
||||
index,
|
||||
tag,
|
||||
svg,
|
||||
};
|
||||
}
|
||||
|
||||
case 9: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const parentID = this.readUint(); if (parentID === null) { return resetPointer() }
|
||||
const index = this.readUint(); if (index === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "create_text_node",
|
||||
id,
|
||||
parentID,
|
||||
index,
|
||||
};
|
||||
}
|
||||
|
||||
case 10: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const parentID = this.readUint(); if (parentID === null) { return resetPointer() }
|
||||
const index = this.readUint(); if (index === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "move_node",
|
||||
id,
|
||||
parentID,
|
||||
index,
|
||||
};
|
||||
}
|
||||
|
||||
case 11: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "remove_node",
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
case 12: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const name = this.readString(); if (name === null) { return resetPointer() }
|
||||
const value = this.readString(); if (value === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "set_node_attribute",
|
||||
id,
|
||||
name,
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
case 13: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const name = this.readString(); if (name === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "remove_node_attribute",
|
||||
id,
|
||||
name,
|
||||
};
|
||||
}
|
||||
|
||||
case 14: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const data = this.readString(); if (data === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "set_node_data",
|
||||
id,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
case 15: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const data = this.readString(); if (data === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "set_css_data",
|
||||
id,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
case 16: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const x = this.readInt(); if (x === null) { return resetPointer() }
|
||||
const y = this.readInt(); if (y === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "set_node_scroll",
|
||||
id,
|
||||
x,
|
||||
y,
|
||||
};
|
||||
}
|
||||
|
||||
case 17: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const label = this.readString(); if (label === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "set_input_target",
|
||||
id,
|
||||
label,
|
||||
};
|
||||
}
|
||||
|
||||
case 18: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const value = this.readString(); if (value === null) { return resetPointer() }
|
||||
const mask = this.readInt(); if (mask === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "set_input_value",
|
||||
id,
|
||||
value,
|
||||
mask,
|
||||
};
|
||||
}
|
||||
|
||||
case 19: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const checked = this.readBoolean(); if (checked === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "set_input_checked",
|
||||
id,
|
||||
checked,
|
||||
};
|
||||
}
|
||||
|
||||
case 20: {
|
||||
const x = this.readUint(); if (x === null) { return resetPointer() }
|
||||
const y = this.readUint(); if (y === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "mouse_move",
|
||||
x,
|
||||
y,
|
||||
};
|
||||
}
|
||||
|
||||
case 22: {
|
||||
const level = this.readString(); if (level === null) { return resetPointer() }
|
||||
const value = this.readString(); if (value === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "console_log",
|
||||
level,
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
case 23: {
|
||||
const requestStart = this.readUint(); if (requestStart === null) { return resetPointer() }
|
||||
const responseStart = this.readUint(); if (responseStart === null) { return resetPointer() }
|
||||
const responseEnd = this.readUint(); if (responseEnd === null) { return resetPointer() }
|
||||
const domContentLoadedEventStart = this.readUint(); if (domContentLoadedEventStart === null) { return resetPointer() }
|
||||
const domContentLoadedEventEnd = this.readUint(); if (domContentLoadedEventEnd === null) { return resetPointer() }
|
||||
const loadEventStart = this.readUint(); if (loadEventStart === null) { return resetPointer() }
|
||||
const loadEventEnd = this.readUint(); if (loadEventEnd === null) { return resetPointer() }
|
||||
const firstPaint = this.readUint(); if (firstPaint === null) { return resetPointer() }
|
||||
const firstContentfulPaint = this.readUint(); if (firstContentfulPaint === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "page_load_timing",
|
||||
requestStart,
|
||||
responseStart,
|
||||
responseEnd,
|
||||
domContentLoadedEventStart,
|
||||
domContentLoadedEventEnd,
|
||||
loadEventStart,
|
||||
loadEventEnd,
|
||||
firstPaint,
|
||||
firstContentfulPaint,
|
||||
};
|
||||
}
|
||||
|
||||
case 24: {
|
||||
const speedIndex = this.readUint(); if (speedIndex === null) { return resetPointer() }
|
||||
const visuallyComplete = this.readUint(); if (visuallyComplete === null) { return resetPointer() }
|
||||
const timeToInteractive = this.readUint(); if (timeToInteractive === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "page_render_timing",
|
||||
speedIndex,
|
||||
visuallyComplete,
|
||||
timeToInteractive,
|
||||
};
|
||||
}
|
||||
|
||||
case 25: {
|
||||
const name = this.readString(); if (name === null) { return resetPointer() }
|
||||
const message = this.readString(); if (message === null) { return resetPointer() }
|
||||
const payload = this.readString(); if (payload === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "js_exception",
|
||||
name,
|
||||
message,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
case 27: {
|
||||
const name = this.readString(); if (name === null) { return resetPointer() }
|
||||
const payload = this.readString(); if (payload === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "raw_custom_event",
|
||||
name,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
case 28: {
|
||||
const id = this.readString(); if (id === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "user_id",
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
case 29: {
|
||||
const id = this.readString(); if (id === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "user_anonymous_id",
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
case 30: {
|
||||
const key = this.readString(); if (key === null) { return resetPointer() }
|
||||
const value = this.readString(); if (value === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "metadata",
|
||||
key,
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
case 37: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const rule = this.readString(); if (rule === null) { return resetPointer() }
|
||||
const index = this.readUint(); if (index === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "css_insert_rule",
|
||||
id,
|
||||
rule,
|
||||
index,
|
||||
};
|
||||
}
|
||||
|
||||
case 38: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const index = this.readUint(); if (index === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "css_delete_rule",
|
||||
id,
|
||||
index,
|
||||
};
|
||||
}
|
||||
|
||||
case 39: {
|
||||
const method = this.readString(); if (method === null) { return resetPointer() }
|
||||
const url = this.readString(); if (url === null) { return resetPointer() }
|
||||
const request = this.readString(); if (request === null) { return resetPointer() }
|
||||
const response = this.readString(); if (response === null) { return resetPointer() }
|
||||
const status = this.readUint(); if (status === null) { return resetPointer() }
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
const duration = this.readUint(); if (duration === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "fetch",
|
||||
method,
|
||||
url,
|
||||
request,
|
||||
response,
|
||||
status,
|
||||
timestamp,
|
||||
duration,
|
||||
};
|
||||
}
|
||||
|
||||
case 40: {
|
||||
const name = this.readString(); if (name === null) { return resetPointer() }
|
||||
const duration = this.readUint(); if (duration === null) { return resetPointer() }
|
||||
const args = this.readString(); if (args === null) { return resetPointer() }
|
||||
const result = this.readString(); if (result === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "profiler",
|
||||
name,
|
||||
duration,
|
||||
args,
|
||||
result,
|
||||
};
|
||||
}
|
||||
|
||||
case 41: {
|
||||
const key = this.readString(); if (key === null) { return resetPointer() }
|
||||
const value = this.readString(); if (value === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "o_table",
|
||||
key,
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
case 42: {
|
||||
const type = this.readString(); if (type === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "state_action",
|
||||
type,
|
||||
};
|
||||
}
|
||||
|
||||
case 44: {
|
||||
const action = this.readString(); if (action === null) { return resetPointer() }
|
||||
const state = this.readString(); if (state === null) { return resetPointer() }
|
||||
const duration = this.readUint(); if (duration === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "redux",
|
||||
action,
|
||||
state,
|
||||
duration,
|
||||
};
|
||||
}
|
||||
|
||||
case 45: {
|
||||
const mutation = this.readString(); if (mutation === null) { return resetPointer() }
|
||||
const state = this.readString(); if (state === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "vuex",
|
||||
mutation,
|
||||
state,
|
||||
};
|
||||
}
|
||||
|
||||
case 46: {
|
||||
const type = this.readString(); if (type === null) { return resetPointer() }
|
||||
const payload = this.readString(); if (payload === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "mob_x",
|
||||
type,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
case 47: {
|
||||
const action = this.readString(); if (action === null) { return resetPointer() }
|
||||
const state = this.readString(); if (state === null) { return resetPointer() }
|
||||
const duration = this.readUint(); if (duration === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "ng_rx",
|
||||
action,
|
||||
state,
|
||||
duration,
|
||||
};
|
||||
}
|
||||
|
||||
case 48: {
|
||||
const operationKind = this.readString(); if (operationKind === null) { return resetPointer() }
|
||||
const operationName = this.readString(); if (operationName === null) { return resetPointer() }
|
||||
const variables = this.readString(); if (variables === null) { return resetPointer() }
|
||||
const response = this.readString(); if (response === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "graph_ql",
|
||||
operationKind,
|
||||
operationName,
|
||||
variables,
|
||||
response,
|
||||
};
|
||||
}
|
||||
|
||||
case 49: {
|
||||
const frames = this.readInt(); if (frames === null) { return resetPointer() }
|
||||
const ticks = this.readInt(); if (ticks === null) { return resetPointer() }
|
||||
const totalJSHeapSize = this.readUint(); if (totalJSHeapSize === null) { return resetPointer() }
|
||||
const usedJSHeapSize = this.readUint(); if (usedJSHeapSize === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "performance_track",
|
||||
frames,
|
||||
ticks,
|
||||
totalJSHeapSize,
|
||||
usedJSHeapSize,
|
||||
};
|
||||
}
|
||||
|
||||
case 53: {
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
const duration = this.readUint(); if (duration === null) { return resetPointer() }
|
||||
const ttfb = this.readUint(); if (ttfb === null) { return resetPointer() }
|
||||
const headerSize = this.readUint(); if (headerSize === null) { return resetPointer() }
|
||||
const encodedBodySize = this.readUint(); if (encodedBodySize === null) { return resetPointer() }
|
||||
const decodedBodySize = this.readUint(); if (decodedBodySize === null) { return resetPointer() }
|
||||
const url = this.readString(); if (url === null) { return resetPointer() }
|
||||
const initiator = this.readString(); if (initiator === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "resource_timing",
|
||||
timestamp,
|
||||
duration,
|
||||
ttfb,
|
||||
headerSize,
|
||||
encodedBodySize,
|
||||
decodedBodySize,
|
||||
url,
|
||||
initiator,
|
||||
};
|
||||
}
|
||||
|
||||
case 54: {
|
||||
const downlink = this.readUint(); if (downlink === null) { return resetPointer() }
|
||||
const type = this.readString(); if (type === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "connection_information",
|
||||
downlink,
|
||||
type,
|
||||
};
|
||||
}
|
||||
|
||||
case 55: {
|
||||
const hidden = this.readBoolean(); if (hidden === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "set_page_visibility",
|
||||
hidden,
|
||||
};
|
||||
}
|
||||
|
||||
case 59: {
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
const duration = this.readUint(); if (duration === null) { return resetPointer() }
|
||||
const context = this.readUint(); if (context === null) { return resetPointer() }
|
||||
const containerType = this.readUint(); if (containerType === null) { return resetPointer() }
|
||||
const containerSrc = this.readString(); if (containerSrc === null) { return resetPointer() }
|
||||
const containerId = this.readString(); if (containerId === null) { return resetPointer() }
|
||||
const containerName = this.readString(); if (containerName === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "long_task",
|
||||
timestamp,
|
||||
duration,
|
||||
context,
|
||||
containerType,
|
||||
containerSrc,
|
||||
containerId,
|
||||
containerName,
|
||||
};
|
||||
}
|
||||
|
||||
case 60: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const name = this.readString(); if (name === null) { return resetPointer() }
|
||||
const value = this.readString(); if (value === null) { return resetPointer() }
|
||||
const baseURL = this.readString(); if (baseURL === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "set_node_attribute_url_based",
|
||||
id,
|
||||
name,
|
||||
value,
|
||||
baseURL,
|
||||
};
|
||||
}
|
||||
|
||||
case 61: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const data = this.readString(); if (data === null) { return resetPointer() }
|
||||
const baseURL = this.readString(); if (baseURL === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "set_css_data_url_based",
|
||||
id,
|
||||
data,
|
||||
baseURL,
|
||||
};
|
||||
}
|
||||
|
||||
case 63: {
|
||||
const type = this.readString(); if (type === null) { return resetPointer() }
|
||||
const value = this.readString(); if (value === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "technical_info",
|
||||
type,
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
case 64: {
|
||||
const name = this.readString(); if (name === null) { return resetPointer() }
|
||||
const payload = this.readString(); if (payload === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "custom_issue",
|
||||
name,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
case 65: {
|
||||
|
||||
return {
|
||||
tp: "page_close",
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
case 67: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const rule = this.readString(); if (rule === null) { return resetPointer() }
|
||||
const index = this.readUint(); if (index === null) { return resetPointer() }
|
||||
const baseURL = this.readString(); if (baseURL === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "css_insert_rule_url_based",
|
||||
id,
|
||||
rule,
|
||||
index,
|
||||
baseURL,
|
||||
};
|
||||
}
|
||||
|
||||
case 69: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const hesitationTime = this.readUint(); if (hesitationTime === null) { return resetPointer() }
|
||||
const label = this.readString(); if (label === null) { return resetPointer() }
|
||||
const selector = this.readString(); if (selector === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "mouse_click",
|
||||
id,
|
||||
hesitationTime,
|
||||
label,
|
||||
selector,
|
||||
};
|
||||
}
|
||||
|
||||
case 70: {
|
||||
const frameID = this.readUint(); if (frameID === null) { return resetPointer() }
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "create_i_frame_document",
|
||||
frameID,
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
case 90: {
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
const projectID = this.readUint(); if (projectID === null) { return resetPointer() }
|
||||
const trackerVersion = this.readString(); if (trackerVersion === null) { return resetPointer() }
|
||||
const revID = this.readString(); if (revID === null) { return resetPointer() }
|
||||
const userUUID = this.readString(); if (userUUID === null) { return resetPointer() }
|
||||
const userOS = this.readString(); if (userOS === null) { return resetPointer() }
|
||||
const userOSVersion = this.readString(); if (userOSVersion === null) { return resetPointer() }
|
||||
const userDevice = this.readString(); if (userDevice === null) { return resetPointer() }
|
||||
const userDeviceType = this.readString(); if (userDeviceType === null) { return resetPointer() }
|
||||
const userCountry = this.readString(); if (userCountry === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "ios_session_start",
|
||||
timestamp,
|
||||
projectID,
|
||||
trackerVersion,
|
||||
revID,
|
||||
userUUID,
|
||||
userOS,
|
||||
userOSVersion,
|
||||
userDevice,
|
||||
userDeviceType,
|
||||
userCountry,
|
||||
};
|
||||
}
|
||||
|
||||
case 93: {
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
const length = this.readUint(); if (length === null) { return resetPointer() }
|
||||
const name = this.readString(); if (name === null) { return resetPointer() }
|
||||
const payload = this.readString(); if (payload === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "ios_custom_event",
|
||||
timestamp,
|
||||
length,
|
||||
name,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
case 96: {
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
const length = this.readUint(); if (length === null) { return resetPointer() }
|
||||
const x = this.readUint(); if (x === null) { return resetPointer() }
|
||||
const y = this.readUint(); if (y === null) { return resetPointer() }
|
||||
const width = this.readUint(); if (width === null) { return resetPointer() }
|
||||
const height = this.readUint(); if (height === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "ios_screen_changes",
|
||||
timestamp,
|
||||
length,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
}
|
||||
|
||||
case 100: {
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
const length = this.readUint(); if (length === null) { return resetPointer() }
|
||||
const label = this.readString(); if (label === null) { return resetPointer() }
|
||||
const x = this.readUint(); if (x === null) { return resetPointer() }
|
||||
const y = this.readUint(); if (y === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "ios_click_event",
|
||||
timestamp,
|
||||
length,
|
||||
label,
|
||||
x,
|
||||
y,
|
||||
};
|
||||
}
|
||||
|
||||
case 102: {
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
const length = this.readUint(); if (length === null) { return resetPointer() }
|
||||
const name = this.readString(); if (name === null) { return resetPointer() }
|
||||
const value = this.readUint(); if (value === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "ios_performance_event",
|
||||
timestamp,
|
||||
length,
|
||||
name,
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
case 103: {
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
const length = this.readUint(); if (length === null) { return resetPointer() }
|
||||
const severity = this.readString(); if (severity === null) { return resetPointer() }
|
||||
const content = this.readString(); if (content === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "ios_log",
|
||||
timestamp,
|
||||
length,
|
||||
severity,
|
||||
content,
|
||||
};
|
||||
}
|
||||
|
||||
case 105: {
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
const length = this.readUint(); if (length === null) { return resetPointer() }
|
||||
const duration = this.readUint(); if (duration === null) { return resetPointer() }
|
||||
const headers = this.readString(); if (headers === null) { return resetPointer() }
|
||||
const body = this.readString(); if (body === null) { return resetPointer() }
|
||||
const url = this.readString(); if (url === null) { return resetPointer() }
|
||||
const success = this.readBoolean(); if (success === null) { return resetPointer() }
|
||||
const method = this.readString(); if (method === null) { return resetPointer() }
|
||||
const status = this.readUint(); if (status === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "ios_network_call",
|
||||
timestamp,
|
||||
length,
|
||||
duration,
|
||||
headers,
|
||||
body,
|
||||
url,
|
||||
success,
|
||||
method,
|
||||
status,
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unrecognizable message type: ${ tp }`)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
1
frontend/app/player/MessageDistributor/messages/index.ts
Normal file
1
frontend/app/player/MessageDistributor/messages/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from './message'
|
||||
184
frontend/app/player/MessageDistributor/messages/message.ts
Normal file
184
frontend/app/player/MessageDistributor/messages/message.ts
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
// Auto-generated, do not edit
|
||||
|
||||
import type { Timed } from './timed'
|
||||
import type { RawMessage } from './raw'
|
||||
import type { RawBatchMeta,
|
||||
RawTimestamp,
|
||||
RawSessionDisconnect,
|
||||
RawSetPageLocation,
|
||||
RawSetViewportSize,
|
||||
RawSetViewportScroll,
|
||||
RawCreateDocument,
|
||||
RawCreateElementNode,
|
||||
RawCreateTextNode,
|
||||
RawMoveNode,
|
||||
RawRemoveNode,
|
||||
RawSetNodeAttribute,
|
||||
RawRemoveNodeAttribute,
|
||||
RawSetNodeData,
|
||||
RawSetCssData,
|
||||
RawSetNodeScroll,
|
||||
RawSetInputTarget,
|
||||
RawSetInputValue,
|
||||
RawSetInputChecked,
|
||||
RawMouseMove,
|
||||
RawConsoleLog,
|
||||
RawPageLoadTiming,
|
||||
RawPageRenderTiming,
|
||||
RawJsException,
|
||||
RawRawCustomEvent,
|
||||
RawUserID,
|
||||
RawUserAnonymousID,
|
||||
RawMetadata,
|
||||
RawCssInsertRule,
|
||||
RawCssDeleteRule,
|
||||
RawFetch,
|
||||
RawProfiler,
|
||||
RawOTable,
|
||||
RawStateAction,
|
||||
RawRedux,
|
||||
RawVuex,
|
||||
RawMobX,
|
||||
RawNgRx,
|
||||
RawGraphQl,
|
||||
RawPerformanceTrack,
|
||||
RawResourceTiming,
|
||||
RawConnectionInformation,
|
||||
RawSetPageVisibility,
|
||||
RawLongTask,
|
||||
RawSetNodeAttributeURLBased,
|
||||
RawSetCssDataURLBased,
|
||||
RawTechnicalInfo,
|
||||
RawCustomIssue,
|
||||
RawPageClose,
|
||||
RawCssInsertRuleURLBased,
|
||||
RawMouseClick,
|
||||
RawCreateIFrameDocument,
|
||||
RawIosSessionStart,
|
||||
RawIosCustomEvent,
|
||||
RawIosScreenChanges,
|
||||
RawIosClickEvent,
|
||||
RawIosPerformanceEvent,
|
||||
RawIosLog,
|
||||
RawIosNetworkCall, } from './raw'
|
||||
|
||||
export type Message = RawMessage & Timed
|
||||
|
||||
|
||||
export type BatchMeta = RawBatchMeta & Timed
|
||||
|
||||
export type Timestamp = RawTimestamp & Timed
|
||||
|
||||
export type SessionDisconnect = RawSessionDisconnect & Timed
|
||||
|
||||
export type SetPageLocation = RawSetPageLocation & Timed
|
||||
|
||||
export type SetViewportSize = RawSetViewportSize & Timed
|
||||
|
||||
export type SetViewportScroll = RawSetViewportScroll & Timed
|
||||
|
||||
export type CreateDocument = RawCreateDocument & Timed
|
||||
|
||||
export type CreateElementNode = RawCreateElementNode & Timed
|
||||
|
||||
export type CreateTextNode = RawCreateTextNode & Timed
|
||||
|
||||
export type MoveNode = RawMoveNode & Timed
|
||||
|
||||
export type RemoveNode = RawRemoveNode & Timed
|
||||
|
||||
export type SetNodeAttribute = RawSetNodeAttribute & Timed
|
||||
|
||||
export type RemoveNodeAttribute = RawRemoveNodeAttribute & Timed
|
||||
|
||||
export type SetNodeData = RawSetNodeData & Timed
|
||||
|
||||
export type SetCssData = RawSetCssData & Timed
|
||||
|
||||
export type SetNodeScroll = RawSetNodeScroll & Timed
|
||||
|
||||
export type SetInputTarget = RawSetInputTarget & Timed
|
||||
|
||||
export type SetInputValue = RawSetInputValue & Timed
|
||||
|
||||
export type SetInputChecked = RawSetInputChecked & Timed
|
||||
|
||||
export type MouseMove = RawMouseMove & Timed
|
||||
|
||||
export type ConsoleLog = RawConsoleLog & Timed
|
||||
|
||||
export type PageLoadTiming = RawPageLoadTiming & Timed
|
||||
|
||||
export type PageRenderTiming = RawPageRenderTiming & Timed
|
||||
|
||||
export type JsException = RawJsException & Timed
|
||||
|
||||
export type RawCustomEvent = RawRawCustomEvent & Timed
|
||||
|
||||
export type UserID = RawUserID & Timed
|
||||
|
||||
export type UserAnonymousID = RawUserAnonymousID & Timed
|
||||
|
||||
export type Metadata = RawMetadata & Timed
|
||||
|
||||
export type CssInsertRule = RawCssInsertRule & Timed
|
||||
|
||||
export type CssDeleteRule = RawCssDeleteRule & Timed
|
||||
|
||||
export type Fetch = RawFetch & Timed
|
||||
|
||||
export type Profiler = RawProfiler & Timed
|
||||
|
||||
export type OTable = RawOTable & Timed
|
||||
|
||||
export type StateAction = RawStateAction & Timed
|
||||
|
||||
export type Redux = RawRedux & Timed
|
||||
|
||||
export type Vuex = RawVuex & Timed
|
||||
|
||||
export type MobX = RawMobX & Timed
|
||||
|
||||
export type NgRx = RawNgRx & Timed
|
||||
|
||||
export type GraphQl = RawGraphQl & Timed
|
||||
|
||||
export type PerformanceTrack = RawPerformanceTrack & Timed
|
||||
|
||||
export type ResourceTiming = RawResourceTiming & Timed
|
||||
|
||||
export type ConnectionInformation = RawConnectionInformation & Timed
|
||||
|
||||
export type SetPageVisibility = RawSetPageVisibility & Timed
|
||||
|
||||
export type LongTask = RawLongTask & Timed
|
||||
|
||||
export type SetNodeAttributeURLBased = RawSetNodeAttributeURLBased & Timed
|
||||
|
||||
export type SetCssDataURLBased = RawSetCssDataURLBased & Timed
|
||||
|
||||
export type TechnicalInfo = RawTechnicalInfo & Timed
|
||||
|
||||
export type CustomIssue = RawCustomIssue & Timed
|
||||
|
||||
export type PageClose = RawPageClose & Timed
|
||||
|
||||
export type CssInsertRuleURLBased = RawCssInsertRuleURLBased & Timed
|
||||
|
||||
export type MouseClick = RawMouseClick & Timed
|
||||
|
||||
export type CreateIFrameDocument = RawCreateIFrameDocument & Timed
|
||||
|
||||
export type IosSessionStart = RawIosSessionStart & Timed
|
||||
|
||||
export type IosCustomEvent = RawIosCustomEvent & Timed
|
||||
|
||||
export type IosScreenChanges = RawIosScreenChanges & Timed
|
||||
|
||||
export type IosClickEvent = RawIosClickEvent & Timed
|
||||
|
||||
export type IosPerformanceEvent = RawIosPerformanceEvent & Timed
|
||||
|
||||
export type IosLog = RawIosLog & Timed
|
||||
|
||||
export type IosNetworkCall = RawIosNetworkCall & Timed
|
||||
491
frontend/app/player/MessageDistributor/messages/raw.ts
Normal file
491
frontend/app/player/MessageDistributor/messages/raw.ts
Normal file
|
|
@ -0,0 +1,491 @@
|
|||
// Auto-generated, do not edit
|
||||
|
||||
export const TP_MAP = {
|
||||
80: "batch_meta",
|
||||
0: "timestamp",
|
||||
2: "session_disconnect",
|
||||
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",
|
||||
17: "set_input_target",
|
||||
18: "set_input_value",
|
||||
19: "set_input_checked",
|
||||
20: "mouse_move",
|
||||
22: "console_log",
|
||||
23: "page_load_timing",
|
||||
24: "page_render_timing",
|
||||
25: "js_exception",
|
||||
27: "raw_custom_event",
|
||||
28: "user_id",
|
||||
29: "user_anonymous_id",
|
||||
30: "metadata",
|
||||
37: "css_insert_rule",
|
||||
38: "css_delete_rule",
|
||||
39: "fetch",
|
||||
40: "profiler",
|
||||
41: "o_table",
|
||||
42: "state_action",
|
||||
44: "redux",
|
||||
45: "vuex",
|
||||
46: "mob_x",
|
||||
47: "ng_rx",
|
||||
48: "graph_ql",
|
||||
49: "performance_track",
|
||||
53: "resource_timing",
|
||||
54: "connection_information",
|
||||
55: "set_page_visibility",
|
||||
59: "long_task",
|
||||
60: "set_node_attribute_url_based",
|
||||
61: "set_css_data_url_based",
|
||||
63: "technical_info",
|
||||
64: "custom_issue",
|
||||
65: "page_close",
|
||||
67: "css_insert_rule_url_based",
|
||||
69: "mouse_click",
|
||||
70: "create_i_frame_document",
|
||||
90: "ios_session_start",
|
||||
93: "ios_custom_event",
|
||||
96: "ios_screen_changes",
|
||||
100: "ios_click_event",
|
||||
102: "ios_performance_event",
|
||||
103: "ios_log",
|
||||
105: "ios_network_call",
|
||||
}
|
||||
|
||||
|
||||
export interface RawBatchMeta {
|
||||
tp: "batch_meta",
|
||||
pageNo: number,
|
||||
firstIndex: number,
|
||||
timestamp: number,
|
||||
}
|
||||
|
||||
export interface RawTimestamp {
|
||||
tp: "timestamp",
|
||||
timestamp: number,
|
||||
}
|
||||
|
||||
export interface RawSessionDisconnect {
|
||||
tp: "session_disconnect",
|
||||
timestamp: number,
|
||||
}
|
||||
|
||||
export interface RawSetPageLocation {
|
||||
tp: "set_page_location",
|
||||
url: string,
|
||||
referrer: string,
|
||||
navigationStart: number,
|
||||
}
|
||||
|
||||
export interface RawSetViewportSize {
|
||||
tp: "set_viewport_size",
|
||||
width: number,
|
||||
height: number,
|
||||
}
|
||||
|
||||
export interface RawSetViewportScroll {
|
||||
tp: "set_viewport_scroll",
|
||||
x: number,
|
||||
y: number,
|
||||
}
|
||||
|
||||
export interface RawCreateDocument {
|
||||
tp: "create_document",
|
||||
|
||||
}
|
||||
|
||||
export interface RawCreateElementNode {
|
||||
tp: "create_element_node",
|
||||
id: number,
|
||||
parentID: number,
|
||||
index: number,
|
||||
tag: string,
|
||||
svg: boolean,
|
||||
}
|
||||
|
||||
export interface RawCreateTextNode {
|
||||
tp: "create_text_node",
|
||||
id: number,
|
||||
parentID: number,
|
||||
index: number,
|
||||
}
|
||||
|
||||
export interface RawMoveNode {
|
||||
tp: "move_node",
|
||||
id: number,
|
||||
parentID: number,
|
||||
index: number,
|
||||
}
|
||||
|
||||
export interface RawRemoveNode {
|
||||
tp: "remove_node",
|
||||
id: number,
|
||||
}
|
||||
|
||||
export interface RawSetNodeAttribute {
|
||||
tp: "set_node_attribute",
|
||||
id: number,
|
||||
name: string,
|
||||
value: string,
|
||||
}
|
||||
|
||||
export interface RawRemoveNodeAttribute {
|
||||
tp: "remove_node_attribute",
|
||||
id: number,
|
||||
name: string,
|
||||
}
|
||||
|
||||
export interface RawSetNodeData {
|
||||
tp: "set_node_data",
|
||||
id: number,
|
||||
data: string,
|
||||
}
|
||||
|
||||
export interface RawSetCssData {
|
||||
tp: "set_css_data",
|
||||
id: number,
|
||||
data: string,
|
||||
}
|
||||
|
||||
export interface RawSetNodeScroll {
|
||||
tp: "set_node_scroll",
|
||||
id: number,
|
||||
x: number,
|
||||
y: number,
|
||||
}
|
||||
|
||||
export interface RawSetInputTarget {
|
||||
tp: "set_input_target",
|
||||
id: number,
|
||||
label: string,
|
||||
}
|
||||
|
||||
export interface RawSetInputValue {
|
||||
tp: "set_input_value",
|
||||
id: number,
|
||||
value: string,
|
||||
mask: number,
|
||||
}
|
||||
|
||||
export interface RawSetInputChecked {
|
||||
tp: "set_input_checked",
|
||||
id: number,
|
||||
checked: boolean,
|
||||
}
|
||||
|
||||
export interface RawMouseMove {
|
||||
tp: "mouse_move",
|
||||
x: number,
|
||||
y: number,
|
||||
}
|
||||
|
||||
export interface RawConsoleLog {
|
||||
tp: "console_log",
|
||||
level: string,
|
||||
value: string,
|
||||
}
|
||||
|
||||
export interface RawPageLoadTiming {
|
||||
tp: "page_load_timing",
|
||||
requestStart: number,
|
||||
responseStart: number,
|
||||
responseEnd: number,
|
||||
domContentLoadedEventStart: number,
|
||||
domContentLoadedEventEnd: number,
|
||||
loadEventStart: number,
|
||||
loadEventEnd: number,
|
||||
firstPaint: number,
|
||||
firstContentfulPaint: number,
|
||||
}
|
||||
|
||||
export interface RawPageRenderTiming {
|
||||
tp: "page_render_timing",
|
||||
speedIndex: number,
|
||||
visuallyComplete: number,
|
||||
timeToInteractive: number,
|
||||
}
|
||||
|
||||
export interface RawJsException {
|
||||
tp: "js_exception",
|
||||
name: string,
|
||||
message: string,
|
||||
payload: string,
|
||||
}
|
||||
|
||||
export interface RawRawCustomEvent {
|
||||
tp: "raw_custom_event",
|
||||
name: string,
|
||||
payload: string,
|
||||
}
|
||||
|
||||
export interface RawUserID {
|
||||
tp: "user_id",
|
||||
id: string,
|
||||
}
|
||||
|
||||
export interface RawUserAnonymousID {
|
||||
tp: "user_anonymous_id",
|
||||
id: string,
|
||||
}
|
||||
|
||||
export interface RawMetadata {
|
||||
tp: "metadata",
|
||||
key: string,
|
||||
value: string,
|
||||
}
|
||||
|
||||
export interface RawCssInsertRule {
|
||||
tp: "css_insert_rule",
|
||||
id: number,
|
||||
rule: string,
|
||||
index: number,
|
||||
}
|
||||
|
||||
export interface RawCssDeleteRule {
|
||||
tp: "css_delete_rule",
|
||||
id: number,
|
||||
index: number,
|
||||
}
|
||||
|
||||
export interface RawFetch {
|
||||
tp: "fetch",
|
||||
method: string,
|
||||
url: string,
|
||||
request: string,
|
||||
response: string,
|
||||
status: number,
|
||||
timestamp: number,
|
||||
duration: number,
|
||||
}
|
||||
|
||||
export interface RawProfiler {
|
||||
tp: "profiler",
|
||||
name: string,
|
||||
duration: number,
|
||||
args: string,
|
||||
result: string,
|
||||
}
|
||||
|
||||
export interface RawOTable {
|
||||
tp: "o_table",
|
||||
key: string,
|
||||
value: string,
|
||||
}
|
||||
|
||||
export interface RawStateAction {
|
||||
tp: "state_action",
|
||||
type: string,
|
||||
}
|
||||
|
||||
export interface RawRedux {
|
||||
tp: "redux",
|
||||
action: string,
|
||||
state: string,
|
||||
duration: number,
|
||||
}
|
||||
|
||||
export interface RawVuex {
|
||||
tp: "vuex",
|
||||
mutation: string,
|
||||
state: string,
|
||||
}
|
||||
|
||||
export interface RawMobX {
|
||||
tp: "mob_x",
|
||||
type: string,
|
||||
payload: string,
|
||||
}
|
||||
|
||||
export interface RawNgRx {
|
||||
tp: "ng_rx",
|
||||
action: string,
|
||||
state: string,
|
||||
duration: number,
|
||||
}
|
||||
|
||||
export interface RawGraphQl {
|
||||
tp: "graph_ql",
|
||||
operationKind: string,
|
||||
operationName: string,
|
||||
variables: string,
|
||||
response: string,
|
||||
}
|
||||
|
||||
export interface RawPerformanceTrack {
|
||||
tp: "performance_track",
|
||||
frames: number,
|
||||
ticks: number,
|
||||
totalJSHeapSize: number,
|
||||
usedJSHeapSize: number,
|
||||
}
|
||||
|
||||
export interface RawResourceTiming {
|
||||
tp: "resource_timing",
|
||||
timestamp: number,
|
||||
duration: number,
|
||||
ttfb: number,
|
||||
headerSize: number,
|
||||
encodedBodySize: number,
|
||||
decodedBodySize: number,
|
||||
url: string,
|
||||
initiator: string,
|
||||
}
|
||||
|
||||
export interface RawConnectionInformation {
|
||||
tp: "connection_information",
|
||||
downlink: number,
|
||||
type: string,
|
||||
}
|
||||
|
||||
export interface RawSetPageVisibility {
|
||||
tp: "set_page_visibility",
|
||||
hidden: boolean,
|
||||
}
|
||||
|
||||
export interface RawLongTask {
|
||||
tp: "long_task",
|
||||
timestamp: number,
|
||||
duration: number,
|
||||
context: number,
|
||||
containerType: number,
|
||||
containerSrc: string,
|
||||
containerId: string,
|
||||
containerName: string,
|
||||
}
|
||||
|
||||
export interface RawSetNodeAttributeURLBased {
|
||||
tp: "set_node_attribute_url_based",
|
||||
id: number,
|
||||
name: string,
|
||||
value: string,
|
||||
baseURL: string,
|
||||
}
|
||||
|
||||
export interface RawSetCssDataURLBased {
|
||||
tp: "set_css_data_url_based",
|
||||
id: number,
|
||||
data: string,
|
||||
baseURL: string,
|
||||
}
|
||||
|
||||
export interface RawTechnicalInfo {
|
||||
tp: "technical_info",
|
||||
type: string,
|
||||
value: string,
|
||||
}
|
||||
|
||||
export interface RawCustomIssue {
|
||||
tp: "custom_issue",
|
||||
name: string,
|
||||
payload: string,
|
||||
}
|
||||
|
||||
export interface RawPageClose {
|
||||
tp: "page_close",
|
||||
|
||||
}
|
||||
|
||||
export interface RawCssInsertRuleURLBased {
|
||||
tp: "css_insert_rule_url_based",
|
||||
id: number,
|
||||
rule: string,
|
||||
index: number,
|
||||
baseURL: string,
|
||||
}
|
||||
|
||||
export interface RawMouseClick {
|
||||
tp: "mouse_click",
|
||||
id: number,
|
||||
hesitationTime: number,
|
||||
label: string,
|
||||
selector: string,
|
||||
}
|
||||
|
||||
export interface RawCreateIFrameDocument {
|
||||
tp: "create_i_frame_document",
|
||||
frameID: number,
|
||||
id: number,
|
||||
}
|
||||
|
||||
export interface RawIosSessionStart {
|
||||
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 interface RawIosCustomEvent {
|
||||
tp: "ios_custom_event",
|
||||
timestamp: number,
|
||||
length: number,
|
||||
name: string,
|
||||
payload: string,
|
||||
}
|
||||
|
||||
export interface RawIosScreenChanges {
|
||||
tp: "ios_screen_changes",
|
||||
timestamp: number,
|
||||
length: number,
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
}
|
||||
|
||||
export interface RawIosClickEvent {
|
||||
tp: "ios_click_event",
|
||||
timestamp: number,
|
||||
length: number,
|
||||
label: string,
|
||||
x: number,
|
||||
y: number,
|
||||
}
|
||||
|
||||
export interface RawIosPerformanceEvent {
|
||||
tp: "ios_performance_event",
|
||||
timestamp: number,
|
||||
length: number,
|
||||
name: string,
|
||||
value: number,
|
||||
}
|
||||
|
||||
export interface RawIosLog {
|
||||
tp: "ios_log",
|
||||
timestamp: number,
|
||||
length: number,
|
||||
severity: string,
|
||||
content: string,
|
||||
}
|
||||
|
||||
export interface RawIosNetworkCall {
|
||||
tp: "ios_network_call",
|
||||
timestamp: number,
|
||||
length: number,
|
||||
duration: number,
|
||||
headers: string,
|
||||
body: string,
|
||||
url: string,
|
||||
success: boolean,
|
||||
method: string,
|
||||
status: number,
|
||||
}
|
||||
|
||||
|
||||
export type RawMessage = RawBatchMeta | RawTimestamp | RawSessionDisconnect | RawSetPageLocation | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputTarget | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawConsoleLog | RawPageLoadTiming | RawPageRenderTiming | RawJsException | RawRawCustomEvent | RawUserID | RawUserAnonymousID | RawMetadata | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawStateAction | RawRedux | RawVuex | RawMobX | RawNgRx | RawGraphQl | RawPerformanceTrack | RawResourceTiming | RawConnectionInformation | RawSetPageVisibility | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawTechnicalInfo | RawCustomIssue | RawPageClose | RawCssInsertRuleURLBased | RawMouseClick | RawCreateIFrameDocument | RawIosSessionStart | RawIosCustomEvent | RawIosScreenChanges | RawIosClickEvent | RawIosPerformanceEvent | RawIosLog | RawIosNetworkCall;
|
||||
1
frontend/app/player/MessageDistributor/messages/timed.ts
Normal file
1
frontend/app/player/MessageDistributor/messages/timed.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export interface Timed { readonly time: number };
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
export function resolveURL(baseURL: string, relURL: string): string {
|
||||
if (relURL.startsWith('#') || relURL === "") {
|
||||
return relURL;
|
||||
}
|
||||
return new URL(relURL, baseURL).toString();
|
||||
}
|
||||
|
||||
|
||||
var match = /bar/.exec("foobar");
|
||||
const re1 = /url\(("[^"]*"|'[^']*'|[^)]*)\)/g
|
||||
const re2 = /@import "(.*?)"/g
|
||||
function cssUrlsIndex(css: string): Array<[number, number]> {
|
||||
const idxs: Array<[number, number]> = [];
|
||||
const i1 = css.matchAll(re1);
|
||||
// @ts-ignore
|
||||
for (let m of i1) {
|
||||
// @ts-ignore
|
||||
const s: number = m.index + m[0].indexOf(m[1]);
|
||||
const e: number = s + m[1].length;
|
||||
idxs.push([s, e]);
|
||||
}
|
||||
const i2 = css.matchAll(re2);
|
||||
// @ts-ignore
|
||||
for (let m of i2) {
|
||||
// @ts-ignore
|
||||
const s = m.index + m[0].indexOf(m[1]);
|
||||
const e = s + m[1].length;
|
||||
idxs.push([s, e])
|
||||
}
|
||||
return idxs;
|
||||
}
|
||||
function unquote(str: string): [string, string] {
|
||||
str = str.trim();
|
||||
if (str.length <= 2) {
|
||||
return [str, ""]
|
||||
}
|
||||
if (str[0] == '"' && str[str.length-1] == '"') {
|
||||
return [ str.substring(1, str.length-1), "\""];
|
||||
}
|
||||
if (str[0] == '\'' && str[str.length-1] == '\'') {
|
||||
return [ str.substring(1, str.length-1), "'" ];
|
||||
}
|
||||
return [str, ""]
|
||||
}
|
||||
function rewriteCSSLinks(css: string, rewriter: (rawurl: string) => string): string {
|
||||
for (let idx of cssUrlsIndex(css)) {
|
||||
const f = idx[0]
|
||||
const t = idx[1]
|
||||
const [ rawurl, q ] = unquote(css.substring(f, t));
|
||||
css = css.substring(0,f) + q + rewriter(rawurl) + q + css.substring(t);
|
||||
}
|
||||
return css
|
||||
}
|
||||
|
||||
export function resolveCSS(baseURL: string, css: string): string {
|
||||
return rewriteCSSLinks(css, rawurl => resolveURL(baseURL, rawurl));
|
||||
}
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
import readMessage from '../MessageDistributor/messages';
|
||||
import PrimitiveReader from '../MessageDistributor/PrimitiveReader';
|
||||
import RawMessageReader from '../MessageDistributor/messages/RawMessageReader';
|
||||
|
||||
|
||||
export default class Parser {
|
||||
private reader: PrimitiveReader
|
||||
private reader: RawMessageReader
|
||||
private error: boolean = false
|
||||
constructor(byteArray) {
|
||||
this.reader = new PrimitiveReader(byteArray)
|
||||
this.reader = new RawMessageReader(byteArray)
|
||||
}
|
||||
|
||||
parseEach(cb) {
|
||||
|
|
@ -19,12 +18,12 @@ export default class Parser {
|
|||
}
|
||||
|
||||
hasNext() {
|
||||
return !this.error && this.reader.hasNext();
|
||||
return !this.error && this.reader.hasNextByte();
|
||||
}
|
||||
|
||||
next() {
|
||||
try {
|
||||
return readMessage(this.reader)
|
||||
return this.reader.readMessage()
|
||||
} catch(e) {
|
||||
console.warn(e)
|
||||
this.error = true
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue