feat (tracker):v3.4.5: capture iframe console; simple selector detector; log fixes; code fixes

This commit is contained in:
ShiKhu 2021-10-26 23:15:10 +02:00
parent 83389bfda7
commit d1386460cd
8 changed files with 76 additions and 55 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@openreplay/tracker",
"version": "3.4.1",
"version": "3.4.4",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -293,11 +293,6 @@
"to-fast-properties": "^2.0.0"
}
},
"@medv/finder": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@medv/finder/-/finder-2.0.0.tgz",
"integrity": "sha512-gV4jOsGpiWNDGd8Dw7tod1Fc9Gc7StaOT4oZ/6srHRWtsHU+HYWzmkYsa3Qy/z0e9tY1WpJ9wWdBFGskfbzoug=="
},
"@nodelib/fs.scandir": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",

View file

@ -1,7 +1,7 @@
{
"name": "@openreplay/tracker",
"description": "The OpenReplay tracker main package",
"version": "3.4.4",
"version": "3.4.5",
"keywords": [
"logging",
"replay"
@ -38,7 +38,6 @@
"typescript": "^4.3.4"
},
"dependencies": {
"@medv/finder": "^2.0.0",
"error-stack-parser": "^2.0.6"
},
"engines": {

View file

@ -131,7 +131,7 @@ export default class App {
});
}
if(this.options.__debug_log) {
warn("OpenReplay errror: ", context, e)
warn("OpenReplay error: ", context, e)
}
}
@ -153,9 +153,13 @@ export default class App {
}
}
addCommitCallback(cb: CommitCallback): void {
attachCommitCallback(cb: CommitCallback): void {
this.commitCallbacks.push(cb)
}
// @Depricated (TODO: remove in 3.5.*)
addCommitCallback(cb: CommitCallback): void {
this.attachCommitCallback(cb)
}
safe<T extends (...args: any[]) => void>(fn: T): T {

View file

@ -411,11 +411,13 @@ export default class Observer {
private iframeObservers: Observer[] = [];
private handleIframe(iframe: HTMLIFrameElement): void {
let context: Window | null = null
const handle = () => {
const context = iframe.contentWindow as Window | null
const id = this.app.nodes.getID(iframe)
if (!context || id === undefined) { return }
if (id === undefined) { return }
if (iframe.contentWindow === context) { return }
context = iframe.contentWindow as Window | null;
if (!context) { return }
const observer = new Observer(this.app, this.options, context)
this.iframeObservers.push(observer)
observer.observeIframe(id, context)

View file

@ -23,11 +23,11 @@ import { Options as AppOptions } from './app';
import { Options as ConsoleOptions } from './modules/console';
import { Options as ExceptionOptions } from './modules/exception';
import { Options as InputOptions } from './modules/input';
import { Options as MouseOptions } from './modules/mouse';
import { Options as PerformanceOptions } from './modules/performance';
import { Options as TimingOptions } from './modules/timing';
export type Options = Partial<
AppOptions & ConsoleOptions & ExceptionOptions & InputOptions & MouseOptions & PerformanceOptions & TimingOptions
AppOptions & ConsoleOptions & ExceptionOptions & InputOptions & PerformanceOptions & TimingOptions
> & {
projectID?: number; // For the back compatibility only (deprecated)
projectKey: string;
@ -98,7 +98,7 @@ export default class API {
Exception(this.app, options);
Img(this.app);
Input(this.app, options);
Mouse(this.app, options);
Mouse(this.app);
Timing(this.app, options);
Performance(this.app, options);
Scroll(this.app);

View file

@ -110,7 +110,7 @@ export default function (app: App, opts: Partial<Options>): void {
return;
}
const sendConsoleLog = app.safe((level: string, args: any[]): void =>
const sendConsoleLog = app.safe((level: string, args: unknown[]): void =>
app.send(new ConsoleLog(level, printf(args))),
);
@ -121,18 +121,36 @@ export default function (app: App, opts: Partial<Options>): void {
app.attachStartCallback(reset);
app.ticker.attach(reset, 33, false);
options.consoleMethods.forEach((method) => {
if (consoleMethods.indexOf(method) === -1) {
console.error(`OpenReplay: unsupported console method "${method}"`);
return;
}
const fn = (console as any)[method];
(console as any)[method] = function (...args: any[]): void {
fn.apply(this, args);
if (n++ > options.consoleThrottling) {
const patchConsole = (console: Console) =>
options.consoleMethods!.forEach((method) => {
if (consoleMethods.indexOf(method) === -1) {
console.error(`OpenReplay: unsupported console method "${method}"`);
return;
}
sendConsoleLog(method, args);
};
});
const fn = (console as any)[method];
(console as any)[method] = function (...args: unknown[]): void {
fn.apply(this, args);
if (n++ > options.consoleThrottling) {
return;
}
sendConsoleLog(method, args);
};
});
patchConsole(window.console);
app.nodes.attachNodeCallback(node => {
if (node instanceof HTMLIFrameElement) {
let context = node.contentWindow
if (context) {
patchConsole((context as (Window & typeof globalThis)).console)
}
app.attachEventListener(node, "load", () => {
if (node.contentWindow !== context) {
context = node.contentWindow
patchConsole((context as (Window & typeof globalThis)).console)
}
})
}
})
}

View file

@ -47,5 +47,5 @@ export default function (app: App): void {
const observer: PerformanceObserver = new PerformanceObserver((list) =>
list.getEntries().forEach(longTask),
);
observer.observe({ entryTypes: ['longtask'], buffered: true });
observer.observe({ entryTypes: ['longtask'] });
}

View file

@ -1,10 +1,30 @@
import type { Options as FinderOptions } from '../vendors/finder/finder';
import { finder } from '../vendors/finder/finder';
import { normSpaces, hasOpenreplayAttribute, getLabelAttribute } from '../utils';
import App from '../app';
import { MouseMove, MouseClick } from '../../messages';
import { getInputLabel } from './input';
function _getSelector(target: Element): string {
let el: Element | null = target
let selector: string | null = null
do {
if (el.id) {
return `#${el.id}` + (selector ? ` > ${selector}` : '')
}
selector =
el.className.split(' ')
.map(cn => cn.trim())
.filter(cn => cn !== '')
.reduce((sel, cn) => `${sel}.${cn}`, el.tagName.toLowerCase()) +
(selector ? ` > ${selector}` : '');
if (el === document.body) {
return selector
}
el = el.parentElement
} while (el !== document.body && el !== null)
return selector
}
//TODO: fix (typescript doesn't allow work when the guard is inside the function)
function getTarget(target: EventTarget | null): Element | null {
if (target instanceof Element) {
return _getTarget(target);
@ -72,26 +92,11 @@ function getTargetLabel(target: Element): string {
return '';
}
interface HeatmapsOptions {
finder: FinderOptions,
}
export interface Options {
heatmaps: boolean | HeatmapsOptions;
}
export default function (app: App, opts: Partial<Options>): void {
const options: Options = Object.assign(
{
heatmaps: false // {
// finder: {
// threshold: 5,
// maxNumberOfTries: 600,
// },
// },
},
opts,
);
export default function (app: App): void {
// const options: Options = Object.assign(
// {},
// opts,
// );
let mousePositionX = -1;
let mousePositionY = -1;
@ -115,9 +120,7 @@ export default function (app: App, opts: Partial<Options>): void {
const selectorMap: {[id:number]: string} = {};
function getSelector(id: number, target: Element): string {
if (options.heatmaps === false) { return '' }
return selectorMap[id] = selectorMap[id] ||
finder(target, options.heatmaps === true ? undefined : options.heatmaps.finder);
return selectorMap[id] = selectorMap[id] || _getSelector(target);
}
app.attachEventListener(