feat (tracker):v3.4.5: capture iframe console; simple selector detector; log fixes; code fixes
This commit is contained in:
parent
83389bfda7
commit
d1386460cd
8 changed files with 76 additions and 55 deletions
7
tracker/tracker/package-lock.json
generated
7
tracker/tracker/package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'] });
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue