refactor: eliminate eslint errors (#920)
* refactor: eliminate eslint errors as many as I can * refactor: fix more eslint errors in the record module * LINT: fix @typescript-eslint/unbound-method * LINT: fix all eslint errors in source code * LINT: fix as many eslint warnings as possible Co-authored-by: Justin Halsall <Juice10@users.noreply.github.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
import { NodeType as RRNodeType } from 'rrweb-snapshot';
|
import { NodeType as RRNodeType } from 'rrweb-snapshot';
|
||||||
import type { NWSAPI } from 'nwsapi';
|
import type { NWSAPI } from 'nwsapi';
|
||||||
import type { CSSStyleDeclaration as CSSStyleDeclarationType } from 'cssstyle';
|
import type { CSSStyleDeclaration as CSSStyleDeclarationType } from 'cssstyle';
|
||||||
@@ -14,8 +15,11 @@ import {
|
|||||||
IRRDocument,
|
IRRDocument,
|
||||||
CSSStyleDeclaration,
|
CSSStyleDeclaration,
|
||||||
} from 'rrdom';
|
} from 'rrdom';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
|
||||||
const nwsapi = require('nwsapi');
|
const nwsapi = require('nwsapi');
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
|
||||||
const cssom = require('cssom');
|
const cssom = require('cssom');
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
|
||||||
const cssstyle = require('cssstyle');
|
const cssstyle = require('cssstyle');
|
||||||
|
|
||||||
export class RRNode extends BaseRRNode {}
|
export class RRNode extends BaseRRNode {}
|
||||||
@@ -37,6 +41,7 @@ export class RRDocument
|
|||||||
private _nwsapi: NWSAPI;
|
private _nwsapi: NWSAPI;
|
||||||
get nwsapi() {
|
get nwsapi() {
|
||||||
if (!this._nwsapi) {
|
if (!this._nwsapi) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
|
||||||
this._nwsapi = nwsapi({
|
this._nwsapi = nwsapi({
|
||||||
document: (this as unknown) as Document,
|
document: (this as unknown) as Document,
|
||||||
DOMException: (null as unknown) as new (
|
DOMException: (null as unknown) as new (
|
||||||
@@ -53,26 +58,31 @@ export class RRDocument
|
|||||||
return this._nwsapi;
|
return this._nwsapi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
get documentElement(): RRElement | null {
|
get documentElement(): RRElement | null {
|
||||||
return super.documentElement as RRElement | null;
|
return super.documentElement as RRElement | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
get body(): RRElement | null {
|
get body(): RRElement | null {
|
||||||
return super.body as RRElement | null;
|
return super.body as RRElement | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
get head() {
|
get head() {
|
||||||
return super.head as RRElement | null;
|
return super.head as RRElement | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
get implementation(): RRDocument {
|
get implementation(): RRDocument {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
get firstElementChild(): RRElement | null {
|
get firstElementChild(): RRElement | null {
|
||||||
return this.documentElement;
|
return this.documentElement;
|
||||||
@@ -109,8 +119,11 @@ export class RRDocument
|
|||||||
}
|
}
|
||||||
|
|
||||||
createDocument(
|
createDocument(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
_namespace: string | null,
|
_namespace: string | null,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
_qualifiedName: string | null,
|
_qualifiedName: string | null,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
_doctype?: DocumentType | null,
|
_doctype?: DocumentType | null,
|
||||||
) {
|
) {
|
||||||
return new RRDocument();
|
return new RRDocument();
|
||||||
@@ -191,6 +204,7 @@ export class RRElement extends BaseRRElementImpl(RRNode) {
|
|||||||
private _style: CSSStyleDeclarationType;
|
private _style: CSSStyleDeclarationType;
|
||||||
constructor(tagName: string) {
|
constructor(tagName: string) {
|
||||||
super(tagName);
|
super(tagName);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
||||||
this._style = new cssstyle.CSSStyleDeclaration();
|
this._style = new cssstyle.CSSStyleDeclaration();
|
||||||
const style = this._style;
|
const style = this._style;
|
||||||
Object.defineProperty(this.attributes, 'style', {
|
Object.defineProperty(this.attributes, 'style', {
|
||||||
@@ -203,6 +217,7 @@ export class RRElement extends BaseRRElementImpl(RRNode) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
get style() {
|
get style() {
|
||||||
return (this._style as unknown) as CSSStyleDeclaration;
|
return (this._style as unknown) as CSSStyleDeclaration;
|
||||||
@@ -311,7 +326,7 @@ export class RRImageElement extends RRElement {
|
|||||||
src: string;
|
src: string;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
onload: ((this: GlobalEventHandlers, ev: Event) => any) | null;
|
onload: ((this: GlobalEventHandlers, ev: Event) => unknown) | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RRMediaElement extends BaseRRMediaElementImpl(RRElement) {}
|
export class RRMediaElement extends BaseRRMediaElementImpl(RRElement) {}
|
||||||
@@ -334,6 +349,7 @@ export class RRStyleElement extends RRElement {
|
|||||||
for (const child of this.childNodes)
|
for (const child of this.childNodes)
|
||||||
if (child.RRNodeType === RRNodeType.Text)
|
if (child.RRNodeType === RRNodeType.Text)
|
||||||
result += (child as RRText).textContent;
|
result += (child as RRText).textContent;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
||||||
this._sheet = cssom.parse(result);
|
this._sheet = cssom.parse(result);
|
||||||
}
|
}
|
||||||
return this._sheet;
|
return this._sheet;
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ import { RRDocument, RRNode } from './document-nodejs';
|
|||||||
*/
|
*/
|
||||||
export function polyfillPerformance() {
|
export function polyfillPerformance() {
|
||||||
if (typeof window !== 'undefined' || 'performance' in global) return;
|
if (typeof window !== 'undefined' || 'performance' in global) return;
|
||||||
((global as Window & typeof globalThis)
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-var-requires
|
||||||
.performance as unknown) = require('perf_hooks').performance;
|
const performance = require('perf_hooks').performance;
|
||||||
|
((global as Window & typeof globalThis).performance as unknown) = performance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,11 +23,11 @@ export function polyfillRAF() {
|
|||||||
INTERVAL = 1_000 / FPS;
|
INTERVAL = 1_000 / FPS;
|
||||||
let timeoutHandle: NodeJS.Timeout | null = null,
|
let timeoutHandle: NodeJS.Timeout | null = null,
|
||||||
rafCount = 0,
|
rafCount = 0,
|
||||||
requests = Object.create(null);
|
requests = Object.create(null) as Record<string, (time: number) => void>;
|
||||||
|
|
||||||
function onFrameTimer() {
|
function onFrameTimer() {
|
||||||
const currentRequests = requests;
|
const currentRequests = requests;
|
||||||
requests = Object.create(null);
|
requests = Object.create(null) as Record<string, (time: number) => void>;
|
||||||
timeoutHandle = null;
|
timeoutHandle = null;
|
||||||
Object.keys(currentRequests).forEach(function (id) {
|
Object.keys(currentRequests).forEach(function (id) {
|
||||||
const request = currentRequests[id];
|
const request = currentRequests[id];
|
||||||
@@ -63,7 +64,9 @@ export function polyfillRAF() {
|
|||||||
*/
|
*/
|
||||||
export function polyfillEvent() {
|
export function polyfillEvent() {
|
||||||
if (typeof Event !== 'undefined') return;
|
if (typeof Event !== 'undefined') return;
|
||||||
(global.Event as unknown) = function () {};
|
(global.Event as unknown) = function () {
|
||||||
|
//
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -125,8 +125,8 @@ export function diff(
|
|||||||
const newMediaRRElement = newRRElement as RRMediaElement;
|
const newMediaRRElement = newRRElement as RRMediaElement;
|
||||||
if (newMediaRRElement.paused !== undefined)
|
if (newMediaRRElement.paused !== undefined)
|
||||||
newMediaRRElement.paused
|
newMediaRRElement.paused
|
||||||
? oldMediaElement.pause()
|
? void oldMediaElement.pause()
|
||||||
: oldMediaElement.play();
|
: void oldMediaElement.play();
|
||||||
if (newMediaRRElement.muted !== undefined)
|
if (newMediaRRElement.muted !== undefined)
|
||||||
oldMediaElement.muted = newMediaRRElement.muted;
|
oldMediaElement.muted = newMediaRRElement.muted;
|
||||||
if (newMediaRRElement.volume !== undefined)
|
if (newMediaRRElement.volume !== undefined)
|
||||||
@@ -383,10 +383,7 @@ export function createOrGetNode(
|
|||||||
let tagName = (rrNode as IRRElement).tagName.toLowerCase();
|
let tagName = (rrNode as IRRElement).tagName.toLowerCase();
|
||||||
tagName = SVGTagMap[tagName] || tagName;
|
tagName = SVGTagMap[tagName] || tagName;
|
||||||
if (sn && 'isSVG' in sn && sn?.isSVG) {
|
if (sn && 'isSVG' in sn && sn?.isSVG) {
|
||||||
node = document.createElementNS(
|
node = document.createElementNS(NAMESPACES['svg'], tagName);
|
||||||
NAMESPACES['svg'],
|
|
||||||
(rrNode as IRRElement).tagName.toLowerCase(),
|
|
||||||
);
|
|
||||||
} else node = document.createElement((rrNode as IRRElement).tagName);
|
} else node = document.createElement((rrNode as IRRElement).tagName);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ export interface IRRCDATASection extends IRRNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ConstrainedConstructor<T = Record<string, unknown>> = new (
|
type ConstrainedConstructor<T = Record<string, unknown>> = new (
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
...args: any[]
|
...args: any[]
|
||||||
) => T;
|
) => T;
|
||||||
|
|
||||||
@@ -138,7 +139,8 @@ export class BaseRRNode implements IRRNode {
|
|||||||
public readonly nodeName: string;
|
public readonly nodeName: string;
|
||||||
public readonly RRNodeType: RRNodeType;
|
public readonly RRNodeType: RRNodeType;
|
||||||
|
|
||||||
constructor(...args: any[]) {
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
||||||
|
constructor(..._args: any[]) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,19 +168,22 @@ export class BaseRRNode implements IRRNode {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
public appendChild(_newChild: IRRNode): IRRNode {
|
public appendChild(_newChild: IRRNode): IRRNode {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`RRDomException: Failed to execute 'appendChild' on 'RRNode': This RRNode type does not support this method.`,
|
`RRDomException: Failed to execute 'appendChild' on 'RRNode': This RRNode type does not support this method.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
public insertBefore(_newChild: IRRNode, _refChild: IRRNode | null): IRRNode {
|
public insertBefore(_newChild: IRRNode, _refChild: IRRNode | null): IRRNode {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`RRDomException: Failed to execute 'insertBefore' on 'RRNode': This RRNode type does not support this method.`,
|
`RRDomException: Failed to execute 'insertBefore' on 'RRNode': This RRNode type does not support this method.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeChild(node: IRRNode): IRRNode {
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
public removeChild(_node: IRRNode): IRRNode {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`RRDomException: Failed to execute 'removeChild' on 'RRNode': This RRNode type does not support this method.`,
|
`RRDomException: Failed to execute 'removeChild' on 'RRNode': This RRNode type does not support this method.`,
|
||||||
);
|
);
|
||||||
@@ -306,8 +311,8 @@ export function BaseRRDocumentImpl<
|
|||||||
/**
|
/**
|
||||||
* Adhoc implementation for setting xhtml namespace in rebuilt.ts (rrweb-snapshot).
|
* Adhoc implementation for setting xhtml namespace in rebuilt.ts (rrweb-snapshot).
|
||||||
* There are two lines used this function:
|
* There are two lines used this function:
|
||||||
* 1. doc.write('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "">')
|
* 1. doc.write('\<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ""\>')
|
||||||
* 2. doc.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "">')
|
* 2. doc.write('\<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" ""\>')
|
||||||
*/
|
*/
|
||||||
public write(content: string) {
|
public write(content: string) {
|
||||||
let publicId;
|
let publicId;
|
||||||
@@ -329,8 +334,11 @@ export function BaseRRDocumentImpl<
|
|||||||
}
|
}
|
||||||
|
|
||||||
createDocument(
|
createDocument(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
_namespace: string | null,
|
_namespace: string | null,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
_qualifiedName: string | null,
|
_qualifiedName: string | null,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
_doctype?: DocumentType | null,
|
_doctype?: DocumentType | null,
|
||||||
): IRRDocument {
|
): IRRDocument {
|
||||||
return new BaseRRDocument();
|
return new BaseRRDocument();
|
||||||
@@ -542,12 +550,14 @@ export function BaseRRElementImpl<
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
public attachShadow(_init: ShadowRootInit): IRRElement {
|
public attachShadow(_init: ShadowRootInit): IRRElement {
|
||||||
const shadowRoot = this.ownerDocument.createElement('SHADOWROOT');
|
const shadowRoot = this.ownerDocument.createElement('SHADOWROOT');
|
||||||
this.shadowRoot = shadowRoot;
|
this.shadowRoot = shadowRoot;
|
||||||
return shadowRoot;
|
return shadowRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
public dispatchEvent(_event: Event) {
|
public dispatchEvent(_event: Event) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -570,6 +580,7 @@ export function BaseRRMediaElementImpl<
|
|||||||
public volume?: number;
|
public volume?: number;
|
||||||
public paused?: boolean;
|
public paused?: boolean;
|
||||||
public muted?: boolean;
|
public muted?: boolean;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
attachShadow(_init: ShadowRootInit): IRRElement {
|
attachShadow(_init: ShadowRootInit): IRRElement {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`RRDomException: Failed to execute 'attachShadow' on 'RRElement': This RRElement does not support attachShadow`,
|
`RRDomException: Failed to execute 'attachShadow' on 'RRElement': This RRElement does not support attachShadow`,
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import {
|
|||||||
export class RRDocument extends BaseRRDocumentImpl(RRNode) {
|
export class RRDocument extends BaseRRDocumentImpl(RRNode) {
|
||||||
// In the rrweb replayer, there are some unserialized nodes like the element that stores the injected style rules.
|
// In the rrweb replayer, there are some unserialized nodes like the element that stores the injected style rules.
|
||||||
// These unserialized nodes may interfere the execution of the diff algorithm.
|
// These unserialized nodes may interfere the execution of the diff algorithm.
|
||||||
// The id of serialized node is larger than 0. So this value less than 0 is used as id for these unserialized nodes.
|
// The id of serialized node is larger than 0. So this value less than 0 is used as id for these unserialized nodes.
|
||||||
private _unserializedId = -1;
|
private _unserializedId = -1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,8 +57,11 @@ export class RRDocument extends BaseRRDocumentImpl(RRNode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createDocument(
|
createDocument(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
_namespace: string | null,
|
_namespace: string | null,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
_qualifiedName: string | null,
|
_qualifiedName: string | null,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
_doctype?: DocumentType | null,
|
_doctype?: DocumentType | null,
|
||||||
) {
|
) {
|
||||||
return new RRDocument();
|
return new RRDocument();
|
||||||
@@ -201,9 +204,9 @@ function getValidTagName(element: HTMLElement): string {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a RRNode from a real Node.
|
* Build a RRNode from a real Node.
|
||||||
* @param node the real Node
|
* @param node - the real Node
|
||||||
* @param rrdom the RRDocument
|
* @param rrdom - the RRDocument
|
||||||
* @param domMirror the NodeMirror that records the real document tree
|
* @param domMirror - the NodeMirror that records the real document tree
|
||||||
* @returns the built RRNode
|
* @returns the built RRNode
|
||||||
*/
|
*/
|
||||||
export function buildFromNode(
|
export function buildFromNode(
|
||||||
@@ -225,7 +228,7 @@ export function buildFromNode(
|
|||||||
| 'CSS1Compat';
|
| 'CSS1Compat';
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NodeType.DOCUMENT_TYPE_NODE:
|
case NodeType.DOCUMENT_TYPE_NODE: {
|
||||||
const documentType = node as DocumentType;
|
const documentType = node as DocumentType;
|
||||||
rrNode = rrdom.createDocumentType(
|
rrNode = rrdom.createDocumentType(
|
||||||
documentType.name,
|
documentType.name,
|
||||||
@@ -233,7 +236,8 @@ export function buildFromNode(
|
|||||||
documentType.systemId,
|
documentType.systemId,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case NodeType.ELEMENT_NODE:
|
}
|
||||||
|
case NodeType.ELEMENT_NODE: {
|
||||||
const elementNode = node as HTMLElement;
|
const elementNode = node as HTMLElement;
|
||||||
const tagName = getValidTagName(elementNode);
|
const tagName = getValidTagName(elementNode);
|
||||||
rrNode = rrdom.createElement(tagName);
|
rrNode = rrdom.createElement(tagName);
|
||||||
@@ -248,6 +252,7 @@ export function buildFromNode(
|
|||||||
* Because if these values are changed later, the mutation will be applied through the batched input events on its RRElement after the diff algorithm is executed.
|
* Because if these values are changed later, the mutation will be applied through the batched input events on its RRElement after the diff algorithm is executed.
|
||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case NodeType.TEXT_NODE:
|
case NodeType.TEXT_NODE:
|
||||||
rrNode = rrdom.createTextNode((node as Text).textContent || '');
|
rrNode = rrdom.createTextNode((node as Text).textContent || '');
|
||||||
break;
|
break;
|
||||||
@@ -280,9 +285,9 @@ export function buildFromNode(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a RRDocument from a real document tree.
|
* Build a RRDocument from a real document tree.
|
||||||
* @param dom the real document tree
|
* @param dom - the real document tree
|
||||||
* @param domMirror the NodeMirror that records the real document tree
|
* @param domMirror - the NodeMirror that records the real document tree
|
||||||
* @param rrdom the rrdom object to be constructed
|
* @param rrdom - the rrdom object to be constructed
|
||||||
* @returns the build rrdom
|
* @returns the build rrdom
|
||||||
*/
|
*/
|
||||||
export function buildFromDom(
|
export function buildFromDom(
|
||||||
@@ -390,7 +395,7 @@ export class Mirror implements IMirror<RRNode> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a default serializedNodeWithId value for a RRNode.
|
* Get a default serializedNodeWithId value for a RRNode.
|
||||||
* @param id the serialized id to assign
|
* @param id - the serialized id to assign
|
||||||
*/
|
*/
|
||||||
export function getDefaultSN(node: IRRNode, id: number): serializedNodeWithId {
|
export function getDefaultSN(node: IRRNode, id: number): serializedNodeWithId {
|
||||||
switch (node.RRNodeType) {
|
switch (node.RRNodeType) {
|
||||||
@@ -400,7 +405,7 @@ export function getDefaultSN(node: IRRNode, id: number): serializedNodeWithId {
|
|||||||
type: node.RRNodeType,
|
type: node.RRNodeType,
|
||||||
childNodes: [],
|
childNodes: [],
|
||||||
};
|
};
|
||||||
case RRNodeType.DocumentType:
|
case RRNodeType.DocumentType: {
|
||||||
const doctype = node as IRRDocumentType;
|
const doctype = node as IRRDocumentType;
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
@@ -409,6 +414,7 @@ export function getDefaultSN(node: IRRNode, id: number): serializedNodeWithId {
|
|||||||
publicId: doctype.publicId,
|
publicId: doctype.publicId,
|
||||||
systemId: doctype.systemId,
|
systemId: doctype.systemId,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
case RRNodeType.Element:
|
case RRNodeType.Element:
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
@@ -438,6 +444,33 @@ export function getDefaultSN(node: IRRNode, id: number): serializedNodeWithId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print the RRDom as a string.
|
||||||
|
* @param rootNode - the root node of the RRDom tree
|
||||||
|
* @param mirror - a rrweb or rrdom Mirror
|
||||||
|
* @returns printed string
|
||||||
|
*/
|
||||||
|
export function printRRDom(rootNode: IRRNode, mirror: IMirror<IRRNode>) {
|
||||||
|
return walk(rootNode, mirror, '');
|
||||||
|
}
|
||||||
|
function walk(node: IRRNode, mirror: IMirror<IRRNode>, blankSpace: string) {
|
||||||
|
let printText = `${blankSpace}${mirror.getId(node)} ${node.toString()}\n`;
|
||||||
|
if (node.RRNodeType === RRNodeType.Element) {
|
||||||
|
const element = node as IRRElement;
|
||||||
|
if (element.shadowRoot)
|
||||||
|
printText += walk(element.shadowRoot, mirror, blankSpace + ' ');
|
||||||
|
}
|
||||||
|
for (const child of node.childNodes)
|
||||||
|
printText += walk(child, mirror, blankSpace + ' ');
|
||||||
|
if (node.nodeName === 'IFRAME')
|
||||||
|
printText += walk(
|
||||||
|
(node as RRIFrameElement).contentDocument,
|
||||||
|
mirror,
|
||||||
|
blankSpace + ' ',
|
||||||
|
);
|
||||||
|
return printText;
|
||||||
|
}
|
||||||
|
|
||||||
export { RRNode };
|
export { RRNode };
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const camelizeRE = /-([a-z])/g;
|
|||||||
const CUSTOM_PROPERTY_REGEX = /^--[a-zA-Z0-9-]+$/;
|
const CUSTOM_PROPERTY_REGEX = /^--[a-zA-Z0-9-]+$/;
|
||||||
export const camelize = (str: string): string => {
|
export const camelize = (str: string): string => {
|
||||||
if (CUSTOM_PROPERTY_REGEX.test(str)) return str;
|
if (CUSTOM_PROPERTY_REGEX.test(str)) return str;
|
||||||
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));
|
return str.replace(camelizeRE, (_, c: string) => (c ? c.toUpperCase() : ''));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -905,8 +905,8 @@ describe('diff algorithm for rrdom', () => {
|
|||||||
/* Number of elements remains the same and no element will be added or removed. */
|
/* Number of elements remains the same and no element will be added or removed. */
|
||||||
let oldElementsNum = 15,
|
let oldElementsNum = 15,
|
||||||
newElementsNum = 15;
|
newElementsNum = 15;
|
||||||
let oldElementsIds = [],
|
let oldElementsIds: number[] = [],
|
||||||
newElementsIds = [];
|
newElementsIds: number[] = [];
|
||||||
for (let i = 1; i <= oldElementsNum; i++) {
|
for (let i = 1; i <= oldElementsNum; i++) {
|
||||||
oldElementsIds.push(i);
|
oldElementsIds.push(i);
|
||||||
newElementsIds.push(i);
|
newElementsIds.push(i);
|
||||||
@@ -950,8 +950,8 @@ describe('diff algorithm for rrdom', () => {
|
|||||||
/* May need to add or remove some elements. */
|
/* May need to add or remove some elements. */
|
||||||
let oldElementsNum = 20,
|
let oldElementsNum = 20,
|
||||||
newElementsNum = 30;
|
newElementsNum = 30;
|
||||||
let oldElementsIds = [],
|
let oldElementsIds: number[] = [],
|
||||||
newElementsIds = [];
|
newElementsIds: number[] = [];
|
||||||
for (let i = 1; i <= oldElementsNum + 10; i++) oldElementsIds.push(i);
|
for (let i = 1; i <= oldElementsNum + 10; i++) oldElementsIds.push(i);
|
||||||
for (let i = 1; i <= newElementsNum + 10; i++) newElementsIds.push(i);
|
for (let i = 1; i <= newElementsNum + 10; i++) newElementsIds.push(i);
|
||||||
shuffle(oldElementsIds);
|
shuffle(oldElementsIds);
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ export function typeOf(
|
|||||||
| 'undefined'
|
| 'undefined'
|
||||||
| 'null'
|
| 'null'
|
||||||
| 'object' {
|
| 'object' {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
const toString = Object.prototype.toString;
|
const toString = Object.prototype.toString;
|
||||||
const map = {
|
const map = {
|
||||||
'[object Boolean]': 'boolean',
|
'[object Boolean]': 'boolean',
|
||||||
@@ -131,5 +132,6 @@ export function typeOf(
|
|||||||
'[object Null]': 'null',
|
'[object Null]': 'null',
|
||||||
'[object Object]': 'object',
|
'[object Object]': 'object',
|
||||||
};
|
};
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
|
||||||
return map[toString.call(obj)];
|
return map[toString.call(obj)];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
* 1. The css library was built for node.js which does not have tree-shaking supports.
|
* 1. The css library was built for node.js which does not have tree-shaking supports.
|
||||||
* 2. Rewrites into typescript give us a better type interface.
|
* 2. Rewrites into typescript give us a better type interface.
|
||||||
*/
|
*/
|
||||||
|
/* eslint-disable tsdoc/syntax */
|
||||||
/* tslint:disable no-conditional-assignment interface-name no-shadowed-variable */
|
|
||||||
|
|
||||||
export interface ParserOptions {
|
export interface ParserOptions {
|
||||||
/** Silently fail on parse errors */
|
/** Silently fail on parse errors */
|
||||||
@@ -288,7 +287,7 @@ export function parse(css: string, options: ParserOptions = {}) {
|
|||||||
|
|
||||||
function error(msg: string) {
|
function error(msg: string) {
|
||||||
const err = new Error(
|
const err = new Error(
|
||||||
options.source + ':' + lineno + ':' + column + ': ' + msg,
|
`${options.source || ''}:${lineno}:${column}: ${msg}`,
|
||||||
) as ParserError;
|
) as ParserError;
|
||||||
err.reason = msg;
|
err.reason = msg;
|
||||||
err.filename = options.source;
|
err.filename = options.source;
|
||||||
@@ -457,6 +456,7 @@ export function parse(css: string, options: ParserOptions = {}) {
|
|||||||
const pos = position();
|
const pos = position();
|
||||||
|
|
||||||
// prop
|
// prop
|
||||||
|
// eslint-disable-next-line no-useless-escape
|
||||||
const propMatch = match(/^(\*?[-#\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/);
|
const propMatch = match(/^(\*?[-#\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/);
|
||||||
if (!propMatch) {
|
if (!propMatch) {
|
||||||
return;
|
return;
|
||||||
@@ -469,6 +469,7 @@ export function parse(css: string, options: ParserOptions = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// val
|
// val
|
||||||
|
// eslint-disable-next-line no-useless-escape
|
||||||
const val = match(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)/);
|
const val = match(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)/);
|
||||||
|
|
||||||
const ret = pos({
|
const ret = pos({
|
||||||
@@ -889,6 +890,7 @@ function addParent(obj: Stylesheet, parent?: Stylesheet) {
|
|||||||
const value = obj[k as keyof Stylesheet];
|
const value = obj[k as keyof Stylesheet];
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
value.forEach((v) => {
|
value.forEach((v) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
addParent(v, childParent);
|
addParent(v, childParent);
|
||||||
});
|
});
|
||||||
} else if (value && typeof value === 'object') {
|
} else if (value && typeof value === 'object') {
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ function buildNode(
|
|||||||
n.publicId,
|
n.publicId,
|
||||||
n.systemId,
|
n.systemId,
|
||||||
);
|
);
|
||||||
case NodeType.Element:
|
case NodeType.Element: {
|
||||||
const tagName = getTagName(n);
|
const tagName = getTagName(n);
|
||||||
let node: Element;
|
let node: Element;
|
||||||
if (n.isSVG) {
|
if (n.isSVG) {
|
||||||
@@ -143,7 +143,7 @@ function buildNode(
|
|||||||
node = doc.createElement(tagName);
|
node = doc.createElement(tagName);
|
||||||
}
|
}
|
||||||
for (const name in n.attributes) {
|
for (const name in n.attributes) {
|
||||||
if (!n.attributes.hasOwnProperty(name)) {
|
if (!Object.prototype.hasOwnProperty.call(n.attributes, name)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let value = n.attributes[name];
|
let value = n.attributes[name];
|
||||||
@@ -290,6 +290,7 @@ function buildNode(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
|
}
|
||||||
case NodeType.Text:
|
case NodeType.Text:
|
||||||
return doc.createTextNode(
|
return doc.createTextNode(
|
||||||
n.isStyle && hackCss
|
n.isStyle && hackCss
|
||||||
@@ -417,7 +418,12 @@ function handleScroll(node: Node, mirror: Mirror) {
|
|||||||
}
|
}
|
||||||
const el = node as HTMLElement;
|
const el = node as HTMLElement;
|
||||||
for (const name in n.attributes) {
|
for (const name in n.attributes) {
|
||||||
if (!(n.attributes.hasOwnProperty(name) && name.startsWith('rr_'))) {
|
if (
|
||||||
|
!(
|
||||||
|
Object.prototype.hasOwnProperty.call(n.attributes, name) &&
|
||||||
|
name.startsWith('rr_')
|
||||||
|
)
|
||||||
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const value = n.attributes[name];
|
const value = n.attributes[name];
|
||||||
|
|||||||
@@ -102,7 +102,14 @@ export function absoluteToStylesheet(
|
|||||||
): string {
|
): string {
|
||||||
return (cssText || '').replace(
|
return (cssText || '').replace(
|
||||||
URL_IN_CSS_REF,
|
URL_IN_CSS_REF,
|
||||||
(origin, quote1, path1, quote2, path2, path3) => {
|
(
|
||||||
|
origin: string,
|
||||||
|
quote1: string,
|
||||||
|
path1: string,
|
||||||
|
quote2: string,
|
||||||
|
path2: string,
|
||||||
|
path3: string,
|
||||||
|
) => {
|
||||||
const filePath = path1 || path2 || path3;
|
const filePath = path1 || path2 || path3;
|
||||||
const maybeQuote = quote1 || quote2 || '';
|
const maybeQuote = quote1 || quote2 || '';
|
||||||
if (!filePath) {
|
if (!filePath) {
|
||||||
@@ -136,7 +143,9 @@ export function absoluteToStylesheet(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
const SRCSET_NOT_SPACES = /^[^ \t\n\r\u000c]+/; // Don't use \s, to avoid matching non-breaking space
|
const SRCSET_NOT_SPACES = /^[^ \t\n\r\u000c]+/; // Don't use \s, to avoid matching non-breaking space
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
const SRCSET_COMMAS_OR_SPACES = /^[, \t\n\r\u000c]+/;
|
const SRCSET_COMMAS_OR_SPACES = /^[, \t\n\r\u000c]+/;
|
||||||
function getAbsoluteSrcsetString(doc: Document, attributeValue: string) {
|
function getAbsoluteSrcsetString(doc: Document, attributeValue: string) {
|
||||||
/*
|
/*
|
||||||
@@ -165,6 +174,7 @@ function getAbsoluteSrcsetString(doc: Document, attributeValue: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const output = [];
|
const output = [];
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
while (true) {
|
while (true) {
|
||||||
collectCharacters(SRCSET_COMMAS_OR_SPACES);
|
collectCharacters(SRCSET_COMMAS_OR_SPACES);
|
||||||
if (pos >= attributeValue.length) {
|
if (pos >= attributeValue.length) {
|
||||||
@@ -182,6 +192,7 @@ function getAbsoluteSrcsetString(doc: Document, attributeValue: string) {
|
|||||||
let descriptorsStr = '';
|
let descriptorsStr = '';
|
||||||
url = absoluteToDoc(doc, url);
|
url = absoluteToDoc(doc, url);
|
||||||
let inParens = false;
|
let inParens = false;
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
while (true) {
|
while (true) {
|
||||||
const c = attributeValue.charAt(pos);
|
const c = attributeValue.charAt(pos);
|
||||||
if (c === '') {
|
if (c === '') {
|
||||||
@@ -554,7 +565,7 @@ function serializeTextNode(
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Cannot get CSS styles from text's parentNode. Error: ${err}`,
|
`Cannot get CSS styles from text's parentNode. Error: ${err as string}`,
|
||||||
n,
|
n,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -740,7 +751,7 @@ function serializeElementNode(
|
|||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Cannot inline img src=${image.currentSrc}! Error: ${err}`,
|
`Cannot inline img src=${image.currentSrc}! Error: ${err as string}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
oldValue
|
oldValue
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ export function is2DCanvasBlank(canvas: HTMLCanvasElement): boolean {
|
|||||||
// get chunks of the canvas and check if it is blank
|
// get chunks of the canvas and check if it is blank
|
||||||
for (let x = 0; x < canvas.width; x += chunkSize) {
|
for (let x = 0; x < canvas.width; x += chunkSize) {
|
||||||
for (let y = 0; y < canvas.height; y += chunkSize) {
|
for (let y = 0; y < canvas.height; y += chunkSize) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
const getImageData = ctx.getImageData as PatchedGetImageData;
|
const getImageData = ctx.getImageData as PatchedGetImageData;
|
||||||
const originalGetImageData =
|
const originalGetImageData =
|
||||||
ORIGINAL_ATTRIBUTE_NAME in getImageData
|
ORIGINAL_ATTRIBUTE_NAME in getImageData
|
||||||
@@ -132,6 +133,7 @@ export function is2DCanvasBlank(canvas: HTMLCanvasElement): boolean {
|
|||||||
// even if we can already tell from the first chunk(s) that
|
// even if we can already tell from the first chunk(s) that
|
||||||
// the canvas isn't blank
|
// the canvas isn't blank
|
||||||
const pixelBuffer = new Uint32Array(
|
const pixelBuffer = new Uint32Array(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
||||||
originalGetImageData.call(
|
originalGetImageData.call(
|
||||||
ctx,
|
ctx,
|
||||||
x,
|
x,
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
"@xstate/fsm": "^1.4.0",
|
"@xstate/fsm": "^1.4.0",
|
||||||
"base64-arraybuffer": "^1.0.1",
|
"base64-arraybuffer": "^1.0.1",
|
||||||
"fflate": "^0.4.4",
|
"fflate": "^0.4.4",
|
||||||
"mitt": "^1.1.3",
|
"mitt": "^3.0.0",
|
||||||
"rrdom": "^0.1.2",
|
"rrdom": "^0.1.2",
|
||||||
"rrweb-snapshot": "^1.1.14"
|
"rrweb-snapshot": "^1.1.14"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export const unpack: UnpackFn = (raw: string) => {
|
|||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const e: eventWithTime = JSON.parse(raw);
|
const e: eventWithTime = JSON.parse(raw) as eventWithTime;
|
||||||
if (e.timestamp) {
|
if (e.timestamp) {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@ export const unpack: UnpackFn = (raw: string) => {
|
|||||||
try {
|
try {
|
||||||
const e: eventWithTimeAndPacker = JSON.parse(
|
const e: eventWithTimeAndPacker = JSON.parse(
|
||||||
strFromU8(unzlibSync(strToU8(raw, true))),
|
strFromU8(unzlibSync(strToU8(raw, true))),
|
||||||
);
|
) as eventWithTimeAndPacker;
|
||||||
if (e.v === MARK) {
|
if (e.v === MARK) {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
// tslint:disable
|
// tslint:disable
|
||||||
/**
|
/**
|
||||||
* Class StackFrame is a fork of https://github.com/stacktracejs/stackframe/blob/master/stackframe.js
|
* Class StackFrame is a fork of https://github.com/stacktracejs/stackframe/blob/master/stackframe.js
|
||||||
@@ -27,19 +32,9 @@ export class StackFrame {
|
|||||||
toString() {
|
toString() {
|
||||||
const lineNumber = this.lineNumber || '';
|
const lineNumber = this.lineNumber || '';
|
||||||
const columnNumber = this.columnNumber || '';
|
const columnNumber = this.columnNumber || '';
|
||||||
if (this.functionName) {
|
if (this.functionName)
|
||||||
return (
|
return `${this.functionName} (${this.fileName}:${lineNumber}:${columnNumber})`;
|
||||||
this.functionName +
|
return `${this.fileName}:${lineNumber}:${columnNumber}`;
|
||||||
' (' +
|
|
||||||
this.fileName +
|
|
||||||
':' +
|
|
||||||
lineNumber +
|
|
||||||
':' +
|
|
||||||
columnNumber +
|
|
||||||
')'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return this.fileName + ':' + lineNumber + ':' + columnNumber;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,9 +50,6 @@ const SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code])?$/;
|
|||||||
export const ErrorStackParser = {
|
export const ErrorStackParser = {
|
||||||
/**
|
/**
|
||||||
* Given an Error object, extract the most information from it.
|
* Given an Error object, extract the most information from it.
|
||||||
*
|
|
||||||
* @param {Error} error object
|
|
||||||
* @return {Array} of StackFrames
|
|
||||||
*/
|
*/
|
||||||
parse: function (error: Error): StackFrame[] {
|
parse: function (error: Error): StackFrame[] {
|
||||||
// https://github.com/rrweb-io/rrweb/issues/782
|
// https://github.com/rrweb-io/rrweb/issues/782
|
||||||
@@ -65,8 +57,10 @@ export const ErrorStackParser = {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
typeof error.stacktrace !== 'undefined' ||
|
typeof error.stacktrace !== 'undefined' ||
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
typeof error['opera#sourceloc'] !== 'undefined'
|
typeof error['opera#sourceloc'] !== 'undefined'
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -59,27 +59,6 @@ export type LogData = {
|
|||||||
|
|
||||||
type logCallback = (p: LogData) => void;
|
type logCallback = (p: LogData) => void;
|
||||||
|
|
||||||
export type LogLevel =
|
|
||||||
| 'assert'
|
|
||||||
| 'clear'
|
|
||||||
| 'count'
|
|
||||||
| 'countReset'
|
|
||||||
| 'debug'
|
|
||||||
| 'dir'
|
|
||||||
| 'dirxml'
|
|
||||||
| 'error'
|
|
||||||
| 'group'
|
|
||||||
| 'groupCollapsed'
|
|
||||||
| 'groupEnd'
|
|
||||||
| 'info'
|
|
||||||
| 'log'
|
|
||||||
| 'table'
|
|
||||||
| 'time'
|
|
||||||
| 'timeEnd'
|
|
||||||
| 'timeLog'
|
|
||||||
| 'trace'
|
|
||||||
| 'warn';
|
|
||||||
|
|
||||||
/* fork from interface Console */
|
/* fork from interface Console */
|
||||||
// all kinds of console functions
|
// all kinds of console functions
|
||||||
export type Logger = {
|
export type Logger = {
|
||||||
@@ -104,14 +83,26 @@ export type Logger = {
|
|||||||
warn?: typeof console.warn;
|
warn?: typeof console.warn;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type LogLevel = keyof Logger;
|
||||||
|
|
||||||
function initLogObserver(
|
function initLogObserver(
|
||||||
cb: logCallback,
|
cb: logCallback,
|
||||||
win: IWindow, // top window or in an iframe
|
win: IWindow, // top window or in an iframe
|
||||||
logOptions: LogRecordOptions,
|
options: LogRecordOptions,
|
||||||
): listenerHandler {
|
): listenerHandler {
|
||||||
|
const logOptions = (options
|
||||||
|
? Object.assign({}, defaultLogOptions, options)
|
||||||
|
: defaultLogOptions) as {
|
||||||
|
level: LogLevel[];
|
||||||
|
lengthThreshold: number;
|
||||||
|
stringifyOptions?: StringifyOptions;
|
||||||
|
logger: Logger | 'console';
|
||||||
|
};
|
||||||
const loggerType = logOptions.logger;
|
const loggerType = logOptions.logger;
|
||||||
if (!loggerType) {
|
if (!loggerType) {
|
||||||
return () => {};
|
return () => {
|
||||||
|
//
|
||||||
|
};
|
||||||
}
|
}
|
||||||
let logger: Logger;
|
let logger: Logger;
|
||||||
if (typeof loggerType === 'string') {
|
if (typeof loggerType === 'string') {
|
||||||
@@ -122,10 +113,11 @@ function initLogObserver(
|
|||||||
let logCount = 0;
|
let logCount = 0;
|
||||||
const cancelHandlers: listenerHandler[] = [];
|
const cancelHandlers: listenerHandler[] = [];
|
||||||
// add listener to thrown errors
|
// add listener to thrown errors
|
||||||
if (logOptions.level!.includes('error')) {
|
if (logOptions.level.includes('error')) {
|
||||||
if (window) {
|
if (window) {
|
||||||
const errorHandler = (event: ErrorEvent) => {
|
const errorHandler = (event: ErrorEvent) => {
|
||||||
const { message, error } = event;
|
const message = event.message,
|
||||||
|
error = event.error as Error;
|
||||||
const trace: string[] = ErrorStackParser.parse(
|
const trace: string[] = ErrorStackParser.parse(
|
||||||
error,
|
error,
|
||||||
).map((stackFrame: StackFrame) => stackFrame.toString());
|
).map((stackFrame: StackFrame) => stackFrame.toString());
|
||||||
@@ -142,7 +134,7 @@ function initLogObserver(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const levelType of logOptions.level!) {
|
for (const levelType of logOptions.level) {
|
||||||
cancelHandlers.push(replace(logger, levelType));
|
cancelHandlers.push(replace(logger, levelType));
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
@@ -151,46 +143,52 @@ function initLogObserver(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* replace the original console function and record logs
|
* replace the original console function and record logs
|
||||||
* @param logger the logger object such as Console
|
* @param logger - the logger object such as Console
|
||||||
* @param level the name of log function to be replaced
|
* @param level - the name of log function to be replaced
|
||||||
*/
|
*/
|
||||||
function replace(_logger: Logger, level: LogLevel) {
|
function replace(_logger: Logger, level: LogLevel) {
|
||||||
if (!_logger[level]) {
|
if (!_logger[level]) {
|
||||||
return () => {};
|
return () => {
|
||||||
|
//
|
||||||
|
};
|
||||||
}
|
}
|
||||||
// replace the logger.{level}. return a restore function
|
// replace the logger.{level}. return a restore function
|
||||||
return patch(_logger, level, (original) => {
|
return patch(
|
||||||
return (...args: Array<unknown>) => {
|
_logger,
|
||||||
original.apply(this, args);
|
level,
|
||||||
try {
|
(original: (...args: Array<unknown>) => void) => {
|
||||||
const trace = ErrorStackParser.parse(new Error())
|
return (...args: Array<unknown>) => {
|
||||||
.map((stackFrame: StackFrame) => stackFrame.toString())
|
original.apply(this, args);
|
||||||
.splice(1); // splice(1) to omit the hijacked log function
|
try {
|
||||||
const payload = args.map((s) =>
|
const trace = ErrorStackParser.parse(new Error())
|
||||||
stringify(s, logOptions.stringifyOptions),
|
.map((stackFrame: StackFrame) => stackFrame.toString())
|
||||||
);
|
.splice(1); // splice(1) to omit the hijacked log function
|
||||||
logCount++;
|
const payload = args.map((s) =>
|
||||||
if (logCount < logOptions.lengthThreshold!) {
|
stringify(s, logOptions.stringifyOptions),
|
||||||
cb({
|
);
|
||||||
level,
|
logCount++;
|
||||||
trace,
|
if (logCount < logOptions.lengthThreshold) {
|
||||||
payload,
|
cb({
|
||||||
});
|
level,
|
||||||
} else if (logCount === logOptions.lengthThreshold) {
|
trace,
|
||||||
// notify the user
|
payload,
|
||||||
cb({
|
});
|
||||||
level: 'warn',
|
} else if (logCount === logOptions.lengthThreshold) {
|
||||||
trace: [],
|
// notify the user
|
||||||
payload: [
|
cb({
|
||||||
stringify('The number of log records reached the threshold.'),
|
level: 'warn',
|
||||||
],
|
trace: [],
|
||||||
});
|
payload: [
|
||||||
|
stringify('The number of log records reached the threshold.'),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
original('rrweb logger error:', error, ...args);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
};
|
||||||
original('rrweb logger error:', error, ...args);
|
},
|
||||||
}
|
);
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +199,5 @@ export const getRecordConsolePlugin: (
|
|||||||
) => RecordPlugin = (options) => ({
|
) => RecordPlugin = (options) => ({
|
||||||
name: PLUGIN_NAME,
|
name: PLUGIN_NAME,
|
||||||
observer: initLogObserver,
|
observer: initLogObserver,
|
||||||
options: options
|
options: options,
|
||||||
? Object.assign({}, defaultLogOptions, options)
|
|
||||||
: defaultLogOptions,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type { StringifyOptions } from './index';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* transfer the node path in Event to string
|
* transfer the node path in Event to string
|
||||||
* @param node the first node in a node path array
|
* @param node - the first node in a node path array
|
||||||
*/
|
*/
|
||||||
function pathToSelector(node: HTMLElement): string | '' {
|
function pathToSelector(node: HTMLElement): string | '' {
|
||||||
if (!node || !node.outerHTML) {
|
if (!node || !node.outerHTML) {
|
||||||
@@ -39,7 +39,7 @@ function pathToSelector(node: HTMLElement): string | '' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (domSiblings.length > 1) {
|
if (domSiblings.length > 1) {
|
||||||
name += ':eq(' + domSiblings.indexOf(node) + ')';
|
name += `:eq(${domSiblings.indexOf(node)})`;
|
||||||
}
|
}
|
||||||
path = name + (path ? '>' + path : '');
|
path = name + (path ? '>' + path : '');
|
||||||
node = parent;
|
node = parent;
|
||||||
@@ -51,21 +51,24 @@ function pathToSelector(node: HTMLElement): string | '' {
|
|||||||
/**
|
/**
|
||||||
* judge is object
|
* judge is object
|
||||||
*/
|
*/
|
||||||
function isObject(obj: any): boolean {
|
function isObject(obj: unknown): boolean {
|
||||||
return Object.prototype.toString.call(obj) === '[object Object]';
|
return Object.prototype.toString.call(obj) === '[object Object]';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* judge the object's depth
|
* judge the object's depth
|
||||||
*/
|
*/
|
||||||
function isObjTooDeep(obj: any, limit: number): boolean {
|
function isObjTooDeep(obj: Record<string, unknown>, limit: number): boolean {
|
||||||
if (limit === 0) {
|
if (limit === 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const keys = Object.keys(obj);
|
const keys = Object.keys(obj);
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
if (isObject(obj[key]) && isObjTooDeep(obj[key], limit - 1)) {
|
if (
|
||||||
|
isObject(obj[key]) &&
|
||||||
|
isObjTooDeep(obj[key] as Record<string, unknown>, limit - 1)
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,10 +78,10 @@ function isObjTooDeep(obj: any, limit: number): boolean {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* stringify any js object
|
* stringify any js object
|
||||||
* @param obj the object to stringify
|
* @param obj - the object to stringify
|
||||||
*/
|
*/
|
||||||
export function stringify(
|
export function stringify(
|
||||||
obj: any,
|
obj: unknown,
|
||||||
stringifyOptions?: StringifyOptions,
|
stringifyOptions?: StringifyOptions,
|
||||||
): string {
|
): string {
|
||||||
const options: StringifyOptions = {
|
const options: StringifyOptions = {
|
||||||
@@ -86,63 +89,68 @@ export function stringify(
|
|||||||
depthOfLimit: 4,
|
depthOfLimit: 4,
|
||||||
};
|
};
|
||||||
Object.assign(options, stringifyOptions);
|
Object.assign(options, stringifyOptions);
|
||||||
const stack: any[] = [];
|
const stack: unknown[] = [];
|
||||||
const keys: any[] = [];
|
const keys: unknown[] = [];
|
||||||
return JSON.stringify(obj, function (key, value) {
|
return JSON.stringify(
|
||||||
/**
|
obj,
|
||||||
* forked from https://github.com/moll/json-stringify-safe/blob/master/stringify.js
|
function (key, value: string | object | null | undefined) {
|
||||||
* to deCycle the object
|
/**
|
||||||
*/
|
* forked from https://github.com/moll/json-stringify-safe/blob/master/stringify.js
|
||||||
if (stack.length > 0) {
|
* to deCycle the object
|
||||||
const thisPos = stack.indexOf(this);
|
*/
|
||||||
~thisPos ? stack.splice(thisPos + 1) : stack.push(this);
|
if (stack.length > 0) {
|
||||||
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key);
|
const thisPos = stack.indexOf(this);
|
||||||
if (~stack.indexOf(value)) {
|
~thisPos ? stack.splice(thisPos + 1) : stack.push(this);
|
||||||
if (stack[0] === value) {
|
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key);
|
||||||
value = '[Circular ~]';
|
if (~stack.indexOf(value)) {
|
||||||
} else {
|
if (stack[0] === value) {
|
||||||
value =
|
value = '[Circular ~]';
|
||||||
'[Circular ~.' +
|
} else {
|
||||||
keys.slice(0, stack.indexOf(value)).join('.') +
|
value =
|
||||||
']';
|
'[Circular ~.' +
|
||||||
|
keys.slice(0, stack.indexOf(value)).join('.') +
|
||||||
|
']';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
stack.push(value);
|
||||||
}
|
}
|
||||||
} else {
|
/* END of the FORK */
|
||||||
stack.push(value);
|
|
||||||
}
|
|
||||||
/* END of the FORK */
|
|
||||||
|
|
||||||
if (value === null || value === undefined) {
|
if (value === null || value === undefined) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
if (shouldIgnore(value)) {
|
if (shouldIgnore(value as object)) {
|
||||||
return toString(value);
|
return toString(value as object);
|
||||||
}
|
}
|
||||||
if (value instanceof Event) {
|
if (value instanceof Event) {
|
||||||
const eventResult: any = {};
|
const eventResult: Record<string, unknown> = {};
|
||||||
for (const eventKey in value) {
|
for (const eventKey in value) {
|
||||||
const eventValue = (value as any)[eventKey];
|
const eventValue = ((value as unknown) as Record<string, unknown>)[
|
||||||
if (Array.isArray(eventValue)) {
|
eventKey
|
||||||
eventResult[eventKey] = pathToSelector(
|
];
|
||||||
eventValue.length ? eventValue[0] : null,
|
if (Array.isArray(eventValue)) {
|
||||||
);
|
eventResult[eventKey] = pathToSelector(
|
||||||
} else {
|
(eventValue.length ? eventValue[0] : null) as HTMLElement,
|
||||||
eventResult[eventKey] = eventValue;
|
);
|
||||||
|
} else {
|
||||||
|
eventResult[eventKey] = eventValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return eventResult;
|
||||||
|
} else if (value instanceof Node) {
|
||||||
|
if (value instanceof HTMLElement) {
|
||||||
|
return value ? value.outerHTML : '';
|
||||||
|
}
|
||||||
|
return value.nodeName;
|
||||||
|
} else if (value instanceof Error) {
|
||||||
|
return value.stack
|
||||||
|
? value.stack + '\nEnd of stack for Error object'
|
||||||
|
: value.name + ': ' + value.message;
|
||||||
}
|
}
|
||||||
return eventResult;
|
return value;
|
||||||
} else if (value instanceof Node) {
|
},
|
||||||
if (value instanceof HTMLElement) {
|
);
|
||||||
return value ? value.outerHTML : '';
|
|
||||||
}
|
|
||||||
return value.nodeName;
|
|
||||||
} else if (value instanceof Error) {
|
|
||||||
return value.stack
|
|
||||||
? value.stack + '\nEnd of stack for Error object'
|
|
||||||
: value.name + ': ' + value.message;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* whether we should ignore obj's info and call toString() function instead
|
* whether we should ignore obj's info and call toString() function instead
|
||||||
@@ -163,7 +171,10 @@ export function stringify(
|
|||||||
*
|
*
|
||||||
* issues: https://github.com/rrweb-io/rrweb/issues/653
|
* issues: https://github.com/rrweb-io/rrweb/issues/653
|
||||||
*/
|
*/
|
||||||
if (isObject(_obj) && isObjTooDeep(_obj, options.depthOfLimit)) {
|
if (
|
||||||
|
isObject(_obj) &&
|
||||||
|
isObjTooDeep(_obj as Record<string, unknown>, options.depthOfLimit)
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class LogReplayPlugin {
|
|||||||
]
|
]
|
||||||
: console.log;
|
: console.log;
|
||||||
logger(
|
logger(
|
||||||
...data.payload.map((s) => JSON.parse(s)),
|
...data.payload.map((s) => JSON.parse(s) as object),
|
||||||
this.formatMessage(data),
|
this.formatMessage(data),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -84,7 +84,7 @@ class LogReplayPlugin {
|
|||||||
]
|
]
|
||||||
: console[level];
|
: console[level];
|
||||||
logger(
|
logger(
|
||||||
...data.payload.map((s) => JSON.parse(s)),
|
...data.payload.map((s) => JSON.parse(s) as object),
|
||||||
this.formatMessage(data),
|
this.formatMessage(data),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -95,7 +95,7 @@ class LogReplayPlugin {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* format the trace data to a string
|
* format the trace data to a string
|
||||||
* @param data the log data
|
* @param data - the log data
|
||||||
*/
|
*/
|
||||||
private formatMessage(data: LogData): string {
|
private formatMessage(data: LogData): string {
|
||||||
if (data.trace.length === 0) {
|
if (data.trace.length === 0) {
|
||||||
|
|||||||
@@ -129,7 +129,9 @@ function initMoveObserver({
|
|||||||
mirror,
|
mirror,
|
||||||
}: observerParam): listenerHandler {
|
}: observerParam): listenerHandler {
|
||||||
if (sampling.mousemove === false) {
|
if (sampling.mousemove === false) {
|
||||||
return () => {};
|
return () => {
|
||||||
|
//
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const threshold =
|
const threshold =
|
||||||
@@ -209,7 +211,9 @@ function initMouseInteractionObserver({
|
|||||||
sampling,
|
sampling,
|
||||||
}: observerParam): listenerHandler {
|
}: observerParam): listenerHandler {
|
||||||
if (sampling.mouseInteraction === false) {
|
if (sampling.mouseInteraction === false) {
|
||||||
return () => {};
|
return () => {
|
||||||
|
//
|
||||||
|
};
|
||||||
}
|
}
|
||||||
const disableMap: Record<string, boolean | undefined> =
|
const disableMap: Record<string, boolean | undefined> =
|
||||||
sampling.mouseInteraction === true ||
|
sampling.mouseInteraction === true ||
|
||||||
@@ -438,7 +442,7 @@ function initInputObserver({
|
|||||||
hookSetter<HTMLElement>(p[0], p[1], {
|
hookSetter<HTMLElement>(p[0], p[1], {
|
||||||
set() {
|
set() {
|
||||||
// mock to a normal event
|
// mock to a normal event
|
||||||
eventHandler({ target: this } as Event);
|
eventHandler({ target: this as EventTarget } as Event);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -492,31 +496,37 @@ function initStyleSheetObserver(
|
|||||||
{ styleSheetRuleCb, mirror }: observerParam,
|
{ styleSheetRuleCb, mirror }: observerParam,
|
||||||
{ win }: { win: IWindow },
|
{ win }: { win: IWindow },
|
||||||
): listenerHandler {
|
): listenerHandler {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
const insertRule = win.CSSStyleSheet.prototype.insertRule;
|
const insertRule = win.CSSStyleSheet.prototype.insertRule;
|
||||||
win.CSSStyleSheet.prototype.insertRule = function (
|
win.CSSStyleSheet.prototype.insertRule = function (
|
||||||
|
this: CSSStyleSheet,
|
||||||
rule: string,
|
rule: string,
|
||||||
index?: number,
|
index?: number,
|
||||||
) {
|
) {
|
||||||
const id = mirror.getId(this.ownerNode);
|
const id = mirror.getId(this.ownerNode as Node);
|
||||||
if (id !== -1) {
|
if (id !== -1) {
|
||||||
styleSheetRuleCb({
|
styleSheetRuleCb({
|
||||||
id,
|
id,
|
||||||
adds: [{ rule, index }],
|
adds: [{ rule, index }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return insertRule.apply(this, arguments);
|
return insertRule.apply(this, [rule, index]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
const deleteRule = win.CSSStyleSheet.prototype.deleteRule;
|
const deleteRule = win.CSSStyleSheet.prototype.deleteRule;
|
||||||
win.CSSStyleSheet.prototype.deleteRule = function (index: number) {
|
win.CSSStyleSheet.prototype.deleteRule = function (
|
||||||
const id = mirror.getId(this.ownerNode);
|
this: CSSStyleSheet,
|
||||||
|
index: number,
|
||||||
|
) {
|
||||||
|
const id = mirror.getId(this.ownerNode as Node);
|
||||||
if (id !== -1) {
|
if (id !== -1) {
|
||||||
styleSheetRuleCb({
|
styleSheetRuleCb({
|
||||||
id,
|
id,
|
||||||
removes: [{ index }],
|
removes: [{ index }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return deleteRule.apply(this, arguments);
|
return deleteRule.apply(this, [index]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const supportedNestedCSSRuleTypes: {
|
const supportedNestedCSSRuleTypes: {
|
||||||
@@ -549,12 +559,15 @@ function initStyleSheetObserver(
|
|||||||
|
|
||||||
Object.entries(supportedNestedCSSRuleTypes).forEach(([typeKey, type]) => {
|
Object.entries(supportedNestedCSSRuleTypes).forEach(([typeKey, type]) => {
|
||||||
unmodifiedFunctions[typeKey] = {
|
unmodifiedFunctions[typeKey] = {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
insertRule: type.prototype.insertRule,
|
insertRule: type.prototype.insertRule,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
deleteRule: type.prototype.deleteRule,
|
deleteRule: type.prototype.deleteRule,
|
||||||
};
|
};
|
||||||
|
|
||||||
type.prototype.insertRule = function (rule: string, index?: number) {
|
type.prototype.insertRule = function (rule: string, index?: number) {
|
||||||
const id = mirror.getId(this.parentStyleSheet.ownerNode);
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||||
|
const id = mirror.getId(this.parentStyleSheet.ownerNode as Node);
|
||||||
if (id !== -1) {
|
if (id !== -1) {
|
||||||
styleSheetRuleCb({
|
styleSheetRuleCb({
|
||||||
id,
|
id,
|
||||||
@@ -562,25 +575,28 @@ function initStyleSheetObserver(
|
|||||||
{
|
{
|
||||||
rule,
|
rule,
|
||||||
index: [
|
index: [
|
||||||
...getNestedCSSRulePositions(this),
|
...getNestedCSSRulePositions(this as CSSRule),
|
||||||
index || 0, // defaults to 0
|
index || 0, // defaults to 0
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return unmodifiedFunctions[typeKey].insertRule.apply(this, arguments);
|
return unmodifiedFunctions[typeKey].insertRule.apply(this, [rule, index]);
|
||||||
};
|
};
|
||||||
|
|
||||||
type.prototype.deleteRule = function (index: number) {
|
type.prototype.deleteRule = function (index: number) {
|
||||||
const id = mirror.getId(this.parentStyleSheet.ownerNode);
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||||
|
const id = mirror.getId(this.parentStyleSheet.ownerNode as Node);
|
||||||
if (id !== -1) {
|
if (id !== -1) {
|
||||||
styleSheetRuleCb({
|
styleSheetRuleCb({
|
||||||
id,
|
id,
|
||||||
removes: [{ index: [...getNestedCSSRulePositions(this), index] }],
|
removes: [
|
||||||
|
{ index: [...getNestedCSSRulePositions(this as CSSRule), index] },
|
||||||
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return unmodifiedFunctions[typeKey].deleteRule.apply(this, arguments);
|
return unmodifiedFunctions[typeKey].deleteRule.apply(this, [index]);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -598,6 +614,7 @@ function initStyleDeclarationObserver(
|
|||||||
{ styleDeclarationCb, mirror }: observerParam,
|
{ styleDeclarationCb, mirror }: observerParam,
|
||||||
{ win }: { win: IWindow },
|
{ win }: { win: IWindow },
|
||||||
): listenerHandler {
|
): listenerHandler {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
const setProperty = win.CSSStyleDeclaration.prototype.setProperty;
|
const setProperty = win.CSSStyleDeclaration.prototype.setProperty;
|
||||||
win.CSSStyleDeclaration.prototype.setProperty = function (
|
win.CSSStyleDeclaration.prototype.setProperty = function (
|
||||||
this: CSSStyleDeclaration,
|
this: CSSStyleDeclaration,
|
||||||
@@ -617,9 +634,10 @@ function initStyleDeclarationObserver(
|
|||||||
index: getNestedCSSRulePositions(this.parentRule!),
|
index: getNestedCSSRulePositions(this.parentRule!),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return setProperty.apply(this, arguments);
|
return setProperty.apply(this, [property, value, priority]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
const removeProperty = win.CSSStyleDeclaration.prototype.removeProperty;
|
const removeProperty = win.CSSStyleDeclaration.prototype.removeProperty;
|
||||||
win.CSSStyleDeclaration.prototype.removeProperty = function (
|
win.CSSStyleDeclaration.prototype.removeProperty = function (
|
||||||
this: CSSStyleDeclaration,
|
this: CSSStyleDeclaration,
|
||||||
@@ -635,7 +653,7 @@ function initStyleDeclarationObserver(
|
|||||||
index: getNestedCSSRulePositions(this.parentRule!),
|
index: getNestedCSSRulePositions(this.parentRule!),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return removeProperty.apply(this, arguments);
|
return removeProperty.apply(this, [property]);
|
||||||
};
|
};
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@@ -679,7 +697,9 @@ function initMediaInteractionObserver({
|
|||||||
function initFontObserver({ fontCb, doc }: observerParam): listenerHandler {
|
function initFontObserver({ fontCb, doc }: observerParam): listenerHandler {
|
||||||
const win = doc.defaultView as IWindow;
|
const win = doc.defaultView as IWindow;
|
||||||
if (!win) {
|
if (!win) {
|
||||||
return () => {};
|
return () => {
|
||||||
|
//
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlers: listenerHandler[] = [];
|
const handlers: listenerHandler[] = [];
|
||||||
@@ -689,7 +709,7 @@ function initFontObserver({ fontCb, doc }: observerParam): listenerHandler {
|
|||||||
const originalFontFace = win.FontFace;
|
const originalFontFace = win.FontFace;
|
||||||
win.FontFace = (function FontFace(
|
win.FontFace = (function FontFace(
|
||||||
family: string,
|
family: string,
|
||||||
source: string | ArrayBufferView,
|
source: string | ArrayBufferLike,
|
||||||
descriptors?: FontFaceDescriptors,
|
descriptors?: FontFaceDescriptors,
|
||||||
) {
|
) {
|
||||||
const fontFace = new originalFontFace(family, source, descriptors);
|
const fontFace = new originalFontFace(family, source, descriptors);
|
||||||
@@ -701,23 +721,27 @@ function initFontObserver({ fontCb, doc }: observerParam): listenerHandler {
|
|||||||
typeof source === 'string'
|
typeof source === 'string'
|
||||||
? source
|
? source
|
||||||
: // tslint:disable-next-line: no-any
|
: // tslint:disable-next-line: no-any
|
||||||
JSON.stringify(Array.from(new Uint8Array(source as any))),
|
JSON.stringify(Array.from(new Uint8Array(source))),
|
||||||
});
|
});
|
||||||
return fontFace;
|
return fontFace;
|
||||||
} as unknown) as typeof FontFace;
|
} as unknown) as typeof FontFace;
|
||||||
|
|
||||||
const restoreHandler = patch(doc.fonts, 'add', function (original) {
|
const restoreHandler = patch(
|
||||||
return function (this: FontFaceSet, fontFace: FontFace) {
|
doc.fonts,
|
||||||
setTimeout(() => {
|
'add',
|
||||||
const p = fontMap.get(fontFace);
|
function (original: (font: FontFace) => void) {
|
||||||
if (p) {
|
return function (this: FontFaceSet, fontFace: FontFace) {
|
||||||
fontCb(p);
|
setTimeout(() => {
|
||||||
fontMap.delete(fontFace);
|
const p = fontMap.get(fontFace);
|
||||||
}
|
if (p) {
|
||||||
}, 0);
|
fontCb(p);
|
||||||
return original.apply(this, [fontFace]);
|
fontMap.delete(fontFace);
|
||||||
};
|
}
|
||||||
});
|
}, 0);
|
||||||
|
return original.apply(this, [fontFace]);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
handlers.push(() => {
|
handlers.push(() => {
|
||||||
win.FontFace = originalFontFace;
|
win.FontFace = originalFontFace;
|
||||||
@@ -817,7 +841,9 @@ export function initObservers(
|
|||||||
): listenerHandler {
|
): listenerHandler {
|
||||||
const currentWindow = o.doc.defaultView; // basically document.window
|
const currentWindow = o.doc.defaultView; // basically document.window
|
||||||
if (!currentWindow) {
|
if (!currentWindow) {
|
||||||
return () => {};
|
return () => {
|
||||||
|
//
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
mergeHooks(o, hooks);
|
mergeHooks(o, hooks);
|
||||||
@@ -833,7 +859,11 @@ export function initObservers(
|
|||||||
const styleDeclarationObserver = initStyleDeclarationObserver(o, {
|
const styleDeclarationObserver = initStyleDeclarationObserver(o, {
|
||||||
win: currentWindow,
|
win: currentWindow,
|
||||||
});
|
});
|
||||||
const fontObserver = o.collectFonts ? initFontObserver(o) : () => {};
|
const fontObserver = o.collectFonts
|
||||||
|
? initFontObserver(o)
|
||||||
|
: () => {
|
||||||
|
//
|
||||||
|
};
|
||||||
// plugins
|
// plugins
|
||||||
const pluginHandlers: listenerHandler[] = [];
|
const pluginHandlers: listenerHandler[] = [];
|
||||||
for (const plugin of o.plugins) {
|
for (const plugin of o.plugins) {
|
||||||
|
|||||||
@@ -31,7 +31,12 @@ export default function initCanvas2DMutationObserver(
|
|||||||
const restoreHandler = patch(
|
const restoreHandler = patch(
|
||||||
win.CanvasRenderingContext2D.prototype,
|
win.CanvasRenderingContext2D.prototype,
|
||||||
prop,
|
prop,
|
||||||
function (original) {
|
function (
|
||||||
|
original: (
|
||||||
|
this: CanvasRenderingContext2D,
|
||||||
|
...args: unknown[]
|
||||||
|
) => void,
|
||||||
|
) {
|
||||||
return function (
|
return function (
|
||||||
this: CanvasRenderingContext2D,
|
this: CanvasRenderingContext2D,
|
||||||
...args: Array<unknown>
|
...args: Array<unknown>
|
||||||
@@ -59,6 +64,7 @@ export default function initCanvas2DMutationObserver(
|
|||||||
prop,
|
prop,
|
||||||
{
|
{
|
||||||
set(v) {
|
set(v) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
||||||
cb(this.canvas, {
|
cb(this.canvas, {
|
||||||
type: CanvasContext['2D'],
|
type: CanvasContext['2D'],
|
||||||
property: prop,
|
property: prop,
|
||||||
|
|||||||
@@ -72,10 +72,10 @@ export class CanvasManager {
|
|||||||
this.initCanvasFPSObserver(sampling, win, blockClass);
|
this.initCanvasFPSObserver(sampling, win, blockClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
private processMutation: canvasManagerMutationCallback = function (
|
private processMutation: canvasManagerMutationCallback = (
|
||||||
target,
|
target,
|
||||||
mutation,
|
mutation,
|
||||||
) {
|
) => {
|
||||||
const newFrame =
|
const newFrame =
|
||||||
this.rafStamps.invokeId &&
|
this.rafStamps.invokeId &&
|
||||||
this.rafStamps.latestId !== this.rafStamps.invokeId;
|
this.rafStamps.latestId !== this.rafStamps.invokeId;
|
||||||
@@ -148,7 +148,8 @@ export class CanvasManager {
|
|||||||
lastSnapshotTime = timestamp;
|
lastSnapshotTime = timestamp;
|
||||||
|
|
||||||
win.document
|
win.document
|
||||||
.querySelectorAll(`canvas:not(.${blockClass} *)`)
|
.querySelectorAll(`canvas:not(.${blockClass as string} *)`)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
.forEach(async (canvas: HTMLCanvasElement) => {
|
.forEach(async (canvas: HTMLCanvasElement) => {
|
||||||
const id = this.mirror.getId(canvas);
|
const id = this.mirror.getId(canvas);
|
||||||
if (snapshotInProgressMap.get(id)) return;
|
if (snapshotInProgressMap.get(id)) return;
|
||||||
|
|||||||
@@ -11,7 +11,13 @@ export default function initCanvasContextObserver(
|
|||||||
const restoreHandler = patch(
|
const restoreHandler = patch(
|
||||||
win.HTMLCanvasElement.prototype,
|
win.HTMLCanvasElement.prototype,
|
||||||
'getContext',
|
'getContext',
|
||||||
function (original) {
|
function (
|
||||||
|
original: (
|
||||||
|
this: ICanvas,
|
||||||
|
contextType: string,
|
||||||
|
...args: Array<unknown>
|
||||||
|
) => void,
|
||||||
|
) {
|
||||||
return function (
|
return function (
|
||||||
this: ICanvas,
|
this: ICanvas,
|
||||||
contextType: string,
|
contextType: string,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { encode } from 'base64-arraybuffer';
|
|||||||
import type { IWindow, CanvasArg } from '../../../types';
|
import type { IWindow, CanvasArg } from '../../../types';
|
||||||
|
|
||||||
// TODO: unify with `replay/webgl.ts`
|
// TODO: unify with `replay/webgl.ts`
|
||||||
type CanvasVarMap = Map<string, any[]>;
|
type CanvasVarMap = Map<string, unknown[]>;
|
||||||
const canvasVarMap: Map<RenderingContext, CanvasVarMap> = new Map();
|
const canvasVarMap: Map<RenderingContext, CanvasVarMap> = new Map();
|
||||||
export function variableListFor(ctx: RenderingContext, ctor: string) {
|
export function variableListFor(ctx: RenderingContext, ctor: string) {
|
||||||
let contextMap = canvasVarMap.get(ctx);
|
let contextMap = canvasVarMap.get(ctx);
|
||||||
@@ -13,11 +13,11 @@ export function variableListFor(ctx: RenderingContext, ctor: string) {
|
|||||||
if (!contextMap.has(ctor)) {
|
if (!contextMap.has(ctor)) {
|
||||||
contextMap.set(ctor, []);
|
contextMap.set(ctor, []);
|
||||||
}
|
}
|
||||||
return contextMap.get(ctor) as any[];
|
return contextMap.get(ctor) as unknown[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const saveWebGLVar = (
|
export const saveWebGLVar = (
|
||||||
value: any,
|
value: unknown,
|
||||||
win: IWindow,
|
win: IWindow,
|
||||||
ctx: RenderingContext,
|
ctx: RenderingContext,
|
||||||
): number | void => {
|
): number | void => {
|
||||||
@@ -40,7 +40,7 @@ export const saveWebGLVar = (
|
|||||||
|
|
||||||
// from webgl-recorder: https://github.com/evanw/webgl-recorder/blob/bef0e65596e981ee382126587e2dcbe0fc7748e2/webgl-recorder.js#L50-L77
|
// from webgl-recorder: https://github.com/evanw/webgl-recorder/blob/bef0e65596e981ee382126587e2dcbe0fc7748e2/webgl-recorder.js#L50-L77
|
||||||
export function serializeArg(
|
export function serializeArg(
|
||||||
value: any,
|
value: unknown,
|
||||||
win: IWindow,
|
win: IWindow,
|
||||||
ctx: RenderingContext,
|
ctx: RenderingContext,
|
||||||
): CanvasArg {
|
): CanvasArg {
|
||||||
@@ -125,11 +125,11 @@ export function serializeArg(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value as CanvasArg;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const serializeArgs = (
|
export const serializeArgs = (
|
||||||
args: Array<any>,
|
args: Array<unknown>,
|
||||||
win: IWindow,
|
win: IWindow,
|
||||||
ctx: RenderingContext,
|
ctx: RenderingContext,
|
||||||
) => {
|
) => {
|
||||||
@@ -137,7 +137,7 @@ export const serializeArgs = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const isInstanceOfWebGLObject = (
|
export const isInstanceOfWebGLObject = (
|
||||||
value: any,
|
value: unknown,
|
||||||
win: IWindow,
|
win: IWindow,
|
||||||
): value is
|
): value is
|
||||||
| WebGLActiveInfo
|
| WebGLActiveInfo
|
||||||
|
|||||||
@@ -27,30 +27,36 @@ function patchGLPrototype(
|
|||||||
if (typeof prototype[prop as keyof typeof prototype] !== 'function') {
|
if (typeof prototype[prop as keyof typeof prototype] !== 'function') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const restoreHandler = patch(prototype, prop, function (original) {
|
const restoreHandler = patch(
|
||||||
return function (this: typeof prototype, ...args: Array<unknown>) {
|
prototype,
|
||||||
const result = original.apply(this, args);
|
prop,
|
||||||
saveWebGLVar(result, win, prototype);
|
function (
|
||||||
if (!isBlocked(this.canvas, blockClass, true)) {
|
original: (this: typeof prototype, ...args: Array<unknown>) => void,
|
||||||
|
) {
|
||||||
|
return function (this: typeof prototype, ...args: Array<unknown>) {
|
||||||
|
const result = original.apply(this, args);
|
||||||
|
saveWebGLVar(result, win, prototype);
|
||||||
|
if (!isBlocked(this.canvas, blockClass, true)) {
|
||||||
|
const recordArgs = serializeArgs([...args], win, prototype);
|
||||||
|
const mutation: canvasMutationWithType = {
|
||||||
|
type,
|
||||||
|
property: prop,
|
||||||
|
args: recordArgs,
|
||||||
|
};
|
||||||
|
// TODO: this could potentially also be an OffscreenCanvas as well as HTMLCanvasElement
|
||||||
|
cb(this.canvas, mutation);
|
||||||
|
}
|
||||||
|
|
||||||
const recordArgs = serializeArgs([...args], win, prototype);
|
return result;
|
||||||
const mutation: canvasMutationWithType = {
|
};
|
||||||
type,
|
},
|
||||||
property: prop,
|
);
|
||||||
args: recordArgs,
|
|
||||||
};
|
|
||||||
// TODO: this could potentially also be an OffscreenCanvas as well as HTMLCanvasElement
|
|
||||||
cb(this.canvas, mutation);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
handlers.push(restoreHandler);
|
handlers.push(restoreHandler);
|
||||||
} catch {
|
} catch {
|
||||||
const hookHandler = hookSetter<typeof prototype>(prototype, prop, {
|
const hookHandler = hookSetter<typeof prototype>(prototype, prop, {
|
||||||
set(v) {
|
set(v) {
|
||||||
// TODO: this could potentially also be an OffscreenCanvas as well as HTMLCanvasElement
|
// TODO: this could potentially also be an OffscreenCanvas as well as HTMLCanvasElement
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||||
cb(this.canvas as HTMLCanvasElement, {
|
cb(this.canvas as HTMLCanvasElement, {
|
||||||
type,
|
type,
|
||||||
property: prop,
|
property: prop,
|
||||||
|
|||||||
@@ -34,16 +34,21 @@ export class ShadowDomManager {
|
|||||||
this.mirror = options.mirror;
|
this.mirror = options.mirror;
|
||||||
|
|
||||||
// Patch 'attachShadow' to observe newly added shadow doms.
|
// Patch 'attachShadow' to observe newly added shadow doms.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
const manager = this;
|
const manager = this;
|
||||||
this.restorePatches.push(
|
this.restorePatches.push(
|
||||||
patch(HTMLElement.prototype, 'attachShadow', function (original) {
|
patch(
|
||||||
return function () {
|
HTMLElement.prototype,
|
||||||
const shadowRoot = original.apply(this, arguments);
|
'attachShadow',
|
||||||
if (this.shadowRoot)
|
function (original: (init: ShadowRootInit) => ShadowRoot) {
|
||||||
manager.addShadowRoot(this.shadowRoot, this.ownerDocument);
|
return function (this: HTMLElement, option: ShadowRootInit) {
|
||||||
return shadowRoot;
|
const shadowRoot = original.call(this, option);
|
||||||
};
|
if (this.shadowRoot)
|
||||||
}),
|
manager.addShadowRoot(this.shadowRoot, this.ownerDocument);
|
||||||
|
return shadowRoot;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +78,7 @@ export class ShadowDomManager {
|
|||||||
*/
|
*/
|
||||||
public observeAttachShadow(iframeElement: HTMLIFrameElement) {
|
public observeAttachShadow(iframeElement: HTMLIFrameElement) {
|
||||||
if (iframeElement.contentWindow) {
|
if (iframeElement.contentWindow) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
const manager = this;
|
const manager = this;
|
||||||
this.restorePatches.push(
|
this.restorePatches.push(
|
||||||
patch(
|
patch(
|
||||||
@@ -80,9 +86,9 @@ export class ShadowDomManager {
|
|||||||
HTMLElement: { prototype: HTMLElement };
|
HTMLElement: { prototype: HTMLElement };
|
||||||
}).HTMLElement.prototype,
|
}).HTMLElement.prototype,
|
||||||
'attachShadow',
|
'attachShadow',
|
||||||
function (original) {
|
function (original: (init: ShadowRootInit) => ShadowRoot) {
|
||||||
return function () {
|
return function (this: HTMLElement, option: ShadowRootInit) {
|
||||||
const shadowRoot = original.apply(this, arguments);
|
const shadowRoot = original.call(this, option);
|
||||||
if (this.shadowRoot)
|
if (this.shadowRoot)
|
||||||
manager.addShadowRoot(
|
manager.addShadowRoot(
|
||||||
this.shadowRoot,
|
this.shadowRoot,
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ async function getTransparentBlobFor(
|
|||||||
// `as any` because: https://github.com/Microsoft/TypeScript/issues/20595
|
// `as any` because: https://github.com/Microsoft/TypeScript/issues/20595
|
||||||
const worker: ImageBitmapDataURLResponseWorker = self;
|
const worker: ImageBitmapDataURLResponseWorker = self;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
worker.onmessage = async function (e) {
|
worker.onmessage = async function (e) {
|
||||||
if (!('OffscreenCanvas' in globalThis))
|
if (!('OffscreenCanvas' in globalThis))
|
||||||
return worker.postMessage({ id: e.data.id });
|
return worker.postMessage({ id: e.data.id });
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ export default async function canvasMutation({
|
|||||||
|
|
||||||
if (mutation.setter) {
|
if (mutation.setter) {
|
||||||
// skip some read-only type checks
|
// skip some read-only type checks
|
||||||
// tslint:disable-next-line:no-any
|
((ctx as unknown) as Record<string, unknown>)[mutation.property] =
|
||||||
(ctx as any)[mutation.property] = mutation.args[0];
|
mutation.args[0];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const original = ctx[
|
const original = ctx[
|
||||||
mutation.property as Exclude<keyof typeof ctx, 'canvas'>
|
mutation.property as Exclude<keyof typeof ctx, 'canvas'>
|
||||||
] as Function;
|
] as (ctx: CanvasRenderingContext2D, args: unknown[]) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We have serialized the image source into base64 string during recording,
|
* We have serialized the image source into base64 string during recording,
|
||||||
@@ -37,7 +37,7 @@ export default async function canvasMutation({
|
|||||||
mutation.property === 'drawImage' &&
|
mutation.property === 'drawImage' &&
|
||||||
typeof mutation.args[0] === 'string'
|
typeof mutation.args[0] === 'string'
|
||||||
) {
|
) {
|
||||||
const image = imageMap.get(event);
|
imageMap.get(event);
|
||||||
original.apply(ctx, mutation.args);
|
original.apply(ctx, mutation.args);
|
||||||
} else {
|
} else {
|
||||||
const args = await Promise.all(
|
const args = await Promise.all(
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export function variableListFor(
|
|||||||
if (!contextMap.has(ctor)) {
|
if (!contextMap.has(ctor)) {
|
||||||
contextMap.set(ctor, []);
|
contextMap.set(ctor, []);
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
return contextMap.get(ctor) as any[];
|
return contextMap.get(ctor) as any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,16 +46,21 @@ export function deserializeArg(
|
|||||||
if (arg && typeof arg === 'object' && 'rr_type' in arg) {
|
if (arg && typeof arg === 'object' && 'rr_type' in arg) {
|
||||||
if (preload) preload.isUnchanged = false;
|
if (preload) preload.isUnchanged = false;
|
||||||
if (arg.rr_type === 'ImageBitmap' && 'args' in arg) {
|
if (arg.rr_type === 'ImageBitmap' && 'args' in arg) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
const args = await deserializeArg(imageMap, ctx, preload)(arg.args);
|
const args = await deserializeArg(imageMap, ctx, preload)(arg.args);
|
||||||
|
// eslint-disable-next-line prefer-spread
|
||||||
return await createImageBitmap.apply(null, args);
|
return await createImageBitmap.apply(null, args);
|
||||||
} else if ('index' in arg) {
|
} else if ('index' in arg) {
|
||||||
if (preload || ctx === null) return arg; // we are preloading, ctx is unknown
|
if (preload || ctx === null) return arg; // we are preloading, ctx is unknown
|
||||||
const { rr_type: name, index } = arg;
|
const { rr_type: name, index } = arg;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
return variableListFor(ctx, name)[index];
|
return variableListFor(ctx, name)[index];
|
||||||
} else if ('args' in arg) {
|
} else if ('args' in arg) {
|
||||||
const { rr_type: name, args } = arg;
|
const { rr_type: name, args } = arg;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
const ctor = window[name as keyof Window];
|
const ctor = window[name as keyof Window];
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
|
||||||
return new ctor(
|
return new ctor(
|
||||||
...(await Promise.all(
|
...(await Promise.all(
|
||||||
args.map(deserializeArg(imageMap, ctx, preload)),
|
args.map(deserializeArg(imageMap, ctx, preload)),
|
||||||
@@ -85,6 +91,7 @@ export function deserializeArg(
|
|||||||
const result = await Promise.all(
|
const result = await Promise.all(
|
||||||
arg.map(deserializeArg(imageMap, ctx, preload)),
|
arg.map(deserializeArg(imageMap, ctx, preload)),
|
||||||
);
|
);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return arg;
|
return arg;
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ function getContext(
|
|||||||
// you might have to do `ctx.flush()` before every webgl canvas event
|
// you might have to do `ctx.flush()` before every webgl canvas event
|
||||||
try {
|
try {
|
||||||
if (type === CanvasContext.WebGL) {
|
if (type === CanvasContext.WebGL) {
|
||||||
return (target.getContext('webgl')! ||
|
return (
|
||||||
target.getContext('experimental-webgl'));
|
target.getContext('webgl')! || target.getContext('experimental-webgl')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return target.getContext('webgl2')!;
|
return target.getContext('webgl2')!;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -37,11 +38,15 @@ function saveToWebGLVarMap(
|
|||||||
ctx: WebGLRenderingContext | WebGL2RenderingContext,
|
ctx: WebGLRenderingContext | WebGL2RenderingContext,
|
||||||
result: any,
|
result: any,
|
||||||
) {
|
) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||||
if (!result?.constructor) return; // probably null or undefined
|
if (!result?.constructor) return; // probably null or undefined
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
||||||
const { name } = result.constructor;
|
const { name } = result.constructor;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
if (!WebGLVariableConstructorsNames.includes(name)) return; // not a WebGL variable
|
if (!WebGLVariableConstructorsNames.includes(name)) return; // not a WebGL variable
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
const variables = variableListFor(ctx, name);
|
const variables = variableListFor(ctx, name);
|
||||||
if (!variables.includes(result)) variables.push(result);
|
if (!variables.includes(result)) variables.push(result);
|
||||||
}
|
}
|
||||||
@@ -69,13 +74,16 @@ export default async function webglMutation({
|
|||||||
|
|
||||||
if (mutation.setter) {
|
if (mutation.setter) {
|
||||||
// skip some read-only type checks
|
// skip some read-only type checks
|
||||||
// tslint:disable-next-line:no-any
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||||
(ctx as any)[mutation.property] = mutation.args[0];
|
(ctx as any)[mutation.property] = mutation.args[0];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const original = ctx[
|
const original = ctx[
|
||||||
mutation.property as Exclude<keyof typeof ctx, 'canvas'>
|
mutation.property as Exclude<keyof typeof ctx, 'canvas'>
|
||||||
] as Function;
|
] as (
|
||||||
|
ctx: WebGLRenderingContext | WebGL2RenderingContext,
|
||||||
|
args: unknown[],
|
||||||
|
) => void;
|
||||||
|
|
||||||
const args = await Promise.all(
|
const args = await Promise.all(
|
||||||
mutation.args.map(deserializeArg(imageMap, ctx)),
|
mutation.args.map(deserializeArg(imageMap, ctx)),
|
||||||
@@ -87,16 +95,21 @@ export default async function webglMutation({
|
|||||||
const debugMode = false;
|
const debugMode = false;
|
||||||
if (debugMode) {
|
if (debugMode) {
|
||||||
if (mutation.property === 'compileShader') {
|
if (mutation.property === 'compileShader') {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
if (!ctx.getShaderParameter(args[0], ctx.COMPILE_STATUS))
|
if (!ctx.getShaderParameter(args[0], ctx.COMPILE_STATUS))
|
||||||
console.warn(
|
console.warn(
|
||||||
'something went wrong in replay',
|
'something went wrong in replay',
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
ctx.getShaderInfoLog(args[0]),
|
ctx.getShaderInfoLog(args[0]),
|
||||||
);
|
);
|
||||||
} else if (mutation.property === 'linkProgram') {
|
} else if (mutation.property === 'linkProgram') {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
ctx.validateProgram(args[0]);
|
ctx.validateProgram(args[0]);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
if (!ctx.getProgramParameter(args[0], ctx.LINK_STATUS))
|
if (!ctx.getProgramParameter(args[0], ctx.LINK_STATUS))
|
||||||
console.warn(
|
console.warn(
|
||||||
'something went wrong in replay',
|
'something went wrong in replay',
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
ctx.getProgramInfoLog(args[0]),
|
ctx.getProgramInfoLog(args[0]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -107,6 +120,7 @@ export default async function webglMutation({
|
|||||||
webglError,
|
webglError,
|
||||||
'on command:',
|
'on command:',
|
||||||
mutation.property,
|
mutation.property,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
...args,
|
...args,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ import {
|
|||||||
viewportResizeDimension,
|
viewportResizeDimension,
|
||||||
missingNodeMap,
|
missingNodeMap,
|
||||||
addedNodeMutation,
|
addedNodeMutation,
|
||||||
missingNode,
|
|
||||||
incrementalSnapshotEvent,
|
incrementalSnapshotEvent,
|
||||||
incrementalData,
|
incrementalData,
|
||||||
ReplayerEvents,
|
ReplayerEvents,
|
||||||
@@ -54,7 +53,6 @@ import {
|
|||||||
scrollData,
|
scrollData,
|
||||||
inputData,
|
inputData,
|
||||||
canvasMutationData,
|
canvasMutationData,
|
||||||
styleAttributeValue,
|
|
||||||
styleValueWithPriority,
|
styleValueWithPriority,
|
||||||
mouseMovePos,
|
mouseMovePos,
|
||||||
IWindow,
|
IWindow,
|
||||||
@@ -83,8 +81,7 @@ const SKIP_TIME_THRESHOLD = 10 * 1000;
|
|||||||
const SKIP_TIME_INTERVAL = 5 * 1000;
|
const SKIP_TIME_INTERVAL = 5 * 1000;
|
||||||
|
|
||||||
// https://github.com/rollup/rollup/issues/1267#issuecomment-296395734
|
// https://github.com/rollup/rollup/issues/1267#issuecomment-296395734
|
||||||
// tslint:disable-next-line
|
const mitt = mittProxy.default || mittProxy;
|
||||||
const mitt = (mittProxy as any).default || mittProxy;
|
|
||||||
|
|
||||||
const REPLAY_CONSOLE_PREFIX = '[replayer]';
|
const REPLAY_CONSOLE_PREFIX = '[replayer]';
|
||||||
|
|
||||||
@@ -188,7 +185,7 @@ export class Replayer {
|
|||||||
canvasMutationData: canvasMutationData,
|
canvasMutationData: canvasMutationData,
|
||||||
target: HTMLCanvasElement,
|
target: HTMLCanvasElement,
|
||||||
) => {
|
) => {
|
||||||
canvasMutation({
|
void canvasMutation({
|
||||||
event: canvasEvent,
|
event: canvasEvent,
|
||||||
mutation: canvasMutationData,
|
mutation: canvasMutationData,
|
||||||
target,
|
target,
|
||||||
@@ -340,8 +337,10 @@ export class Replayer {
|
|||||||
|
|
||||||
public setConfig(config: Partial<playerConfig>) {
|
public setConfig(config: Partial<playerConfig>) {
|
||||||
Object.keys(config).forEach((key) => {
|
Object.keys(config).forEach((key) => {
|
||||||
// @ts-ignore
|
const newConfigValue = config[key as keyof playerConfig];
|
||||||
this.config[key] = config[key];
|
(this.config as Record<keyof playerConfig, typeof newConfigValue>)[
|
||||||
|
key as keyof playerConfig
|
||||||
|
] = config[key as keyof playerConfig];
|
||||||
});
|
});
|
||||||
if (!this.config.skipInactive) {
|
if (!this.config.skipInactive) {
|
||||||
this.backToNormal();
|
this.backToNormal();
|
||||||
@@ -404,7 +403,7 @@ export class Replayer {
|
|||||||
* So the implementation of play at any time offset will always iterate
|
* So the implementation of play at any time offset will always iterate
|
||||||
* all of the events, cast event before the offset synchronously
|
* all of the events, cast event before the offset synchronously
|
||||||
* and cast event after the offset asynchronously with timer.
|
* and cast event after the offset asynchronously with timer.
|
||||||
* @param timeOffset number
|
* @param timeOffset - number
|
||||||
*/
|
*/
|
||||||
public play(timeOffset = 0) {
|
public play(timeOffset = 0) {
|
||||||
if (this.service.state.matches('paused')) {
|
if (this.service.state.matches('paused')) {
|
||||||
@@ -452,7 +451,7 @@ export class Replayer {
|
|||||||
if (indicatesTouchDevice(event)) {
|
if (indicatesTouchDevice(event)) {
|
||||||
this.mouse.classList.add('touch-device');
|
this.mouse.classList.add('touch-device');
|
||||||
}
|
}
|
||||||
Promise.resolve().then(() =>
|
void Promise.resolve().then(() =>
|
||||||
this.service.send({ type: 'ADD_EVENT', payload: { event } }),
|
this.service.send({ type: 'ADD_EVENT', payload: { event } }),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -511,7 +510,7 @@ export class Replayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleResize(dimension: viewportResizeDimension) {
|
private handleResize = (dimension: viewportResizeDimension) => {
|
||||||
this.iframe.style.display = 'inherit';
|
this.iframe.style.display = 'inherit';
|
||||||
for (const el of [this.mouseTail, this.iframe]) {
|
for (const el of [this.mouseTail, this.iframe]) {
|
||||||
if (!el) {
|
if (!el) {
|
||||||
@@ -520,9 +519,9 @@ export class Replayer {
|
|||||||
el.setAttribute('width', String(dimension.width));
|
el.setAttribute('width', String(dimension.width));
|
||||||
el.setAttribute('height', String(dimension.height));
|
el.setAttribute('height', String(dimension.height));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
private applyEventsSynchronously(events: Array<eventWithTime>) {
|
private applyEventsSynchronously = (events: Array<eventWithTime>) => {
|
||||||
for (const event of events) {
|
for (const event of events) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case EventType.DomContentLoaded:
|
case EventType.DomContentLoaded:
|
||||||
@@ -546,9 +545,9 @@ export class Replayer {
|
|||||||
this.mouse.classList.remove('touch-active');
|
this.mouse.classList.remove('touch-active');
|
||||||
}
|
}
|
||||||
this.touchActive = null;
|
this.touchActive = null;
|
||||||
}
|
};
|
||||||
|
|
||||||
private getCastFn(event: eventWithTime, isSync = false) {
|
private getCastFn = (event: eventWithTime, isSync = false) => {
|
||||||
let castFn: undefined | (() => void);
|
let castFn: undefined | (() => void);
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case EventType.DomContentLoaded:
|
case EventType.DomContentLoaded:
|
||||||
@@ -671,7 +670,7 @@ export class Replayer {
|
|||||||
this.emitter.emit(ReplayerEvents.EventCast, event);
|
this.emitter.emit(ReplayerEvents.EventCast, event);
|
||||||
};
|
};
|
||||||
return wrappedCastFn;
|
return wrappedCastFn;
|
||||||
}
|
};
|
||||||
|
|
||||||
private rebuildFullSnapshot(
|
private rebuildFullSnapshot(
|
||||||
event: fullSnapshotEvent & { timestamp: number },
|
event: fullSnapshotEvent & { timestamp: number },
|
||||||
@@ -714,7 +713,7 @@ export class Replayer {
|
|||||||
this.waitForStylesheetLoad();
|
this.waitForStylesheetLoad();
|
||||||
}
|
}
|
||||||
if (this.config.UNSAFE_replayCanvas) {
|
if (this.config.UNSAFE_replayCanvas) {
|
||||||
this.preloadAllImages();
|
void this.preloadAllImages();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -870,37 +869,6 @@ export class Replayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private hasImageArg(args: any[]): boolean {
|
|
||||||
for (const arg of args) {
|
|
||||||
if (!arg || typeof arg !== 'object') {
|
|
||||||
// do nothing
|
|
||||||
} else if ('rr_type' in arg && 'args' in arg) {
|
|
||||||
if (this.hasImageArg(arg.args)) return true;
|
|
||||||
} else if ('rr_type' in arg && arg.rr_type === 'HTMLImageElement') {
|
|
||||||
return true; // has image!
|
|
||||||
} else if (arg instanceof Array) {
|
|
||||||
if (this.hasImageArg(arg)) return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getImageArgs(args: any[]): string[] {
|
|
||||||
const images: string[] = [];
|
|
||||||
for (const arg of args) {
|
|
||||||
if (!arg || typeof arg !== 'object') {
|
|
||||||
// do nothing
|
|
||||||
} else if ('rr_type' in arg && 'args' in arg) {
|
|
||||||
images.push(...this.getImageArgs(arg.args));
|
|
||||||
} else if ('rr_type' in arg && arg.rr_type === 'HTMLImageElement') {
|
|
||||||
images.push(arg.src);
|
|
||||||
} else if (arg instanceof Array) {
|
|
||||||
images.push(...this.getImageArgs(arg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return images;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pause when there are some canvas drawImage args need to be loaded
|
* pause when there are some canvas drawImage args need to be loaded
|
||||||
*/
|
*/
|
||||||
@@ -940,7 +908,7 @@ export class Replayer {
|
|||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
const imgd = ctx?.createImageData(canvas.width, canvas.height);
|
const imgd = ctx?.createImageData(canvas.width, canvas.height);
|
||||||
let d = imgd?.data;
|
let d = imgd?.data;
|
||||||
d = JSON.parse(data.args[0]);
|
d = JSON.parse(data.args[0]) as Uint8ClampedArray;
|
||||||
ctx?.putImageData(imgd!, 0, 0);
|
ctx?.putImageData(imgd!, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -983,6 +951,7 @@ export class Replayer {
|
|||||||
try {
|
try {
|
||||||
this.applyMutation(d, isSync);
|
this.applyMutation(d, isSync);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/restrict-template-expressions
|
||||||
this.warn(`Exception in mutation ${error.message || error}`, d);
|
this.warn(`Exception in mutation ${error.message || error}`, d);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1013,7 +982,9 @@ export class Replayer {
|
|||||||
});
|
});
|
||||||
// add a dummy action to keep timer alive
|
// add a dummy action to keep timer alive
|
||||||
this.timer.addAction({
|
this.timer.addAction({
|
||||||
doAction() {},
|
doAction() {
|
||||||
|
//
|
||||||
|
},
|
||||||
delay: e.delay! - d.positions[0]?.timeOffset,
|
delay: e.delay! - d.positions[0]?.timeOffset,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1174,11 +1145,12 @@ export class Replayer {
|
|||||||
// i.e. media will evntualy start to play when data is loaded
|
// i.e. media will evntualy start to play when data is loaded
|
||||||
// 'canplay' event fires even when currentTime attribute changes which may lead to
|
// 'canplay' event fires even when currentTime attribute changes which may lead to
|
||||||
// unexpeted behavior
|
// unexpeted behavior
|
||||||
mediaEl.play();
|
void mediaEl.play();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (this.config.showWarning) {
|
if (this.config.showWarning) {
|
||||||
console.warn(
|
console.warn(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/restrict-template-expressions
|
||||||
`Failed to replay media interactions: ${error.message || error}`,
|
`Failed to replay media interactions: ${error.message || error}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1322,7 +1294,7 @@ export class Replayer {
|
|||||||
if (!target) {
|
if (!target) {
|
||||||
return this.debugNodeNotFound(d, d.id);
|
return this.debugNodeNotFound(d, d.id);
|
||||||
}
|
}
|
||||||
canvasMutation({
|
void canvasMutation({
|
||||||
event: e,
|
event: e,
|
||||||
mutation: d,
|
mutation: d,
|
||||||
target: target as HTMLCanvasElement,
|
target: target as HTMLCanvasElement,
|
||||||
@@ -1337,7 +1309,9 @@ export class Replayer {
|
|||||||
try {
|
try {
|
||||||
const fontFace = new FontFace(
|
const fontFace = new FontFace(
|
||||||
d.family,
|
d.family,
|
||||||
d.buffer ? new Uint8Array(JSON.parse(d.fontSource)) : d.fontSource,
|
d.buffer
|
||||||
|
? new Uint8Array(JSON.parse(d.fontSource) as Iterable<number>)
|
||||||
|
: d.fontSource,
|
||||||
d.descriptors,
|
d.descriptors,
|
||||||
);
|
);
|
||||||
this.iframe.contentDocument?.fonts.add(fontFace);
|
this.iframe.contentDocument?.fonts.add(fontFace);
|
||||||
@@ -1711,8 +1685,8 @@ export class Replayer {
|
|||||||
/**
|
/**
|
||||||
* Apply the scroll data on real elements.
|
* Apply the scroll data on real elements.
|
||||||
* If the replayer is in sync mode, smooth scroll behavior should be disabled.
|
* If the replayer is in sync mode, smooth scroll behavior should be disabled.
|
||||||
* @param d the scroll data
|
* @param d - the scroll data
|
||||||
* @param isSync whether the replayer is in sync mode(fast-forward)
|
* @param isSync - whether the replayer is in sync mode(fast-forward)
|
||||||
*/
|
*/
|
||||||
private applyScroll(d: scrollData, isSync: boolean) {
|
private applyScroll(d: scrollData, isSync: boolean) {
|
||||||
const target = this.mirror.getNode(d.id);
|
const target = this.mirror.getNode(d.id);
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
* Add support of customize target window and document
|
* Add support of customize target window and document
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
// tslint:disable
|
|
||||||
export function polyfill(w: Window = window, d = document) {
|
export function polyfill(w: Window = window, d = document) {
|
||||||
// return if scroll behavior is supported and polyfill is not forced
|
// return if scroll behavior is supported and polyfill is not forced
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ export class Timer {
|
|||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Add an action after the timer starts.
|
* Add an action after the timer starts.
|
||||||
* @param action
|
|
||||||
*/
|
*/
|
||||||
public addAction(action: actionWithDelay) {
|
public addAction(action: actionWithDelay) {
|
||||||
const index = this.findActionIndex(action);
|
const index = this.findActionIndex(action);
|
||||||
@@ -27,7 +26,6 @@ export class Timer {
|
|||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Add all actions before the timer starts
|
* Add all actions before the timer starts
|
||||||
* @param actions
|
|
||||||
*/
|
*/
|
||||||
public addActions(actions: actionWithDelay[]) {
|
public addActions(actions: actionWithDelay[]) {
|
||||||
this.actions = this.actions.concat(actions);
|
this.actions = this.actions.concat(actions);
|
||||||
@@ -37,25 +35,24 @@ export class Timer {
|
|||||||
this.timeOffset = 0;
|
this.timeOffset = 0;
|
||||||
let lastTimestamp = performance.now();
|
let lastTimestamp = performance.now();
|
||||||
const { actions } = this;
|
const { actions } = this;
|
||||||
const self = this;
|
const check = () => {
|
||||||
function check() {
|
|
||||||
const time = performance.now();
|
const time = performance.now();
|
||||||
self.timeOffset += (time - lastTimestamp) * self.speed;
|
this.timeOffset += (time - lastTimestamp) * this.speed;
|
||||||
lastTimestamp = time;
|
lastTimestamp = time;
|
||||||
while (actions.length) {
|
while (actions.length) {
|
||||||
const action = actions[0];
|
const action = actions[0];
|
||||||
|
|
||||||
if (self.timeOffset >= action.delay) {
|
if (this.timeOffset >= action.delay) {
|
||||||
actions.shift();
|
actions.shift();
|
||||||
action.doAction();
|
action.doAction();
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (actions.length > 0 || self.liveMode) {
|
if (actions.length > 0 || this.liveMode) {
|
||||||
self.raf = requestAnimationFrame(check);
|
this.raf = requestAnimationFrame(check);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
this.raf = requestAnimationFrame(check);
|
this.raf = requestAnimationFrame(check);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export class RRdomTreeNode implements AnyObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setCachedIndex(parentNode: AnyObject, index: number) {
|
public setCachedIndex(parentNode: AnyObject, index: number) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
this.cachedIndexVersion = parentNode.childrenVersion;
|
this.cachedIndexVersion = parentNode.childrenVersion;
|
||||||
this.cachedIndex = index;
|
this.cachedIndex = index;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ export enum EventType {
|
|||||||
|
|
||||||
export type domContentLoadedEvent = {
|
export type domContentLoadedEvent = {
|
||||||
type: EventType.DomContentLoaded;
|
type: EventType.DomContentLoaded;
|
||||||
data: {};
|
data: unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type loadedEvent = {
|
export type loadedEvent = {
|
||||||
type: EventType.Load;
|
type: EventType.Load;
|
||||||
data: {};
|
data: unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type fullSnapshotEvent = {
|
export type fullSnapshotEvent = {
|
||||||
@@ -76,8 +76,6 @@ export type pluginEvent<T = unknown> = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type styleSheetEvent = {};
|
|
||||||
|
|
||||||
export enum IncrementalSource {
|
export enum IncrementalSource {
|
||||||
Mutation,
|
Mutation,
|
||||||
MouseMove,
|
MouseMove,
|
||||||
@@ -218,7 +216,11 @@ export type SamplingStrategy = Partial<{
|
|||||||
|
|
||||||
export type RecordPlugin<TOptions = unknown> = {
|
export type RecordPlugin<TOptions = unknown> = {
|
||||||
name: string;
|
name: string;
|
||||||
observer?: (cb: Function, win: IWindow, options: TOptions) => listenerHandler;
|
observer?: (
|
||||||
|
cb: (...args: Array<unknown>) => void,
|
||||||
|
win: IWindow,
|
||||||
|
options: TOptions,
|
||||||
|
) => listenerHandler;
|
||||||
eventProcessor?: <TExtend>(event: eventWithTime) => eventWithTime & TExtend;
|
eventProcessor?: <TExtend>(event: eventWithTime) => eventWithTime & TExtend;
|
||||||
options: TOptions;
|
options: TOptions;
|
||||||
};
|
};
|
||||||
@@ -285,8 +287,12 @@ export type observerParam = {
|
|||||||
shadowDomManager: ShadowDomManager;
|
shadowDomManager: ShadowDomManager;
|
||||||
canvasManager: CanvasManager;
|
canvasManager: CanvasManager;
|
||||||
plugins: Array<{
|
plugins: Array<{
|
||||||
observer: Function;
|
observer: (
|
||||||
callback: Function;
|
cb: (...arg: Array<unknown>) => void,
|
||||||
|
win: IWindow,
|
||||||
|
options: unknown,
|
||||||
|
) => listenerHandler;
|
||||||
|
callback: (...arg: Array<unknown>) => void;
|
||||||
options: unknown;
|
options: unknown;
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ if (typeof window !== 'undefined' && window.Proxy && window.Reflect) {
|
|||||||
if (prop === 'map') {
|
if (prop === 'map') {
|
||||||
console.error(DEPARTED_MIRROR_ACCESS_WARNING);
|
console.error(DEPARTED_MIRROR_ACCESS_WARNING);
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
return Reflect.get(target, prop, receiver);
|
return Reflect.get(target, prop, receiver);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -70,14 +71,14 @@ export function throttle<T>(
|
|||||||
) {
|
) {
|
||||||
let timeout: ReturnType<typeof setTimeout> | null = null;
|
let timeout: ReturnType<typeof setTimeout> | null = null;
|
||||||
let previous = 0;
|
let previous = 0;
|
||||||
return function (arg: T) {
|
return function (...args: T[]) {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (!previous && options.leading === false) {
|
if (!previous && options.leading === false) {
|
||||||
previous = now;
|
previous = now;
|
||||||
}
|
}
|
||||||
const remaining = wait - (now - previous);
|
const remaining = wait - (now - previous);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-this-alias
|
||||||
const context = this;
|
const context = this;
|
||||||
const args = arguments;
|
|
||||||
if (remaining <= 0 || remaining > wait) {
|
if (remaining <= 0 || remaining > wait) {
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
@@ -129,11 +130,13 @@ export function patch(
|
|||||||
source: { [key: string]: any },
|
source: { [key: string]: any },
|
||||||
name: string,
|
name: string,
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
replacement: (...args: any[]) => any,
|
replacement: (...args: unknown[]) => unknown,
|
||||||
): () => void {
|
): () => void {
|
||||||
try {
|
try {
|
||||||
if (!(name in source)) {
|
if (!(name in source)) {
|
||||||
return () => {};
|
return () => {
|
||||||
|
//
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const original = source[name] as () => unknown;
|
const original = source[name] as () => unknown;
|
||||||
@@ -143,6 +146,7 @@ export function patch(
|
|||||||
// otherwise it'll throw "TypeError: Object.defineProperties called on non-object"
|
// otherwise it'll throw "TypeError: Object.defineProperties called on non-object"
|
||||||
// tslint:disable-next-line:strict-type-predicates
|
// tslint:disable-next-line:strict-type-predicates
|
||||||
if (typeof wrapped === 'function') {
|
if (typeof wrapped === 'function') {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
wrapped.prototype = wrapped.prototype || {};
|
wrapped.prototype = wrapped.prototype || {};
|
||||||
Object.defineProperties(wrapped, {
|
Object.defineProperties(wrapped, {
|
||||||
__rrweb_original__: {
|
__rrweb_original__: {
|
||||||
@@ -158,7 +162,9 @@ export function patch(
|
|||||||
source[name] = original;
|
source[name] = original;
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return () => {};
|
return () => {
|
||||||
|
//
|
||||||
|
};
|
||||||
// This can throw if multiple fill happens on a global object like XMLHttpRequest
|
// This can throw if multiple fill happens on a global object like XMLHttpRequest
|
||||||
// Fixes https://github.com/getsentry/sentry-javascript/issues/2043
|
// Fixes https://github.com/getsentry/sentry-javascript/issues/2043
|
||||||
}
|
}
|
||||||
@@ -249,19 +255,22 @@ export function isTouchEvent(
|
|||||||
|
|
||||||
export function polyfill(win = window) {
|
export function polyfill(win = window) {
|
||||||
if ('NodeList' in win && !win.NodeList.prototype.forEach) {
|
if ('NodeList' in win && !win.NodeList.prototype.forEach) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
win.NodeList.prototype.forEach = (Array.prototype
|
win.NodeList.prototype.forEach = (Array.prototype
|
||||||
.forEach as unknown) as NodeList['forEach'];
|
.forEach as unknown) as NodeList['forEach'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('DOMTokenList' in win && !win.DOMTokenList.prototype.forEach) {
|
if ('DOMTokenList' in win && !win.DOMTokenList.prototype.forEach) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
win.DOMTokenList.prototype.forEach = (Array.prototype
|
win.DOMTokenList.prototype.forEach = (Array.prototype
|
||||||
.forEach as unknown) as DOMTokenList['forEach'];
|
.forEach as unknown) as DOMTokenList['forEach'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/Financial-Times/polyfill-service/pull/183
|
// https://github.com/Financial-Times/polyfill-service/pull/183
|
||||||
if (!Node.prototype.contains) {
|
if (!Node.prototype.contains) {
|
||||||
Node.prototype.contains = function contains(node) {
|
Node.prototype.contains = (...args: unknown[]) => {
|
||||||
if (!(0 in arguments)) {
|
let node = args[0] as Node | null;
|
||||||
|
if (!(0 in args)) {
|
||||||
throw new TypeError('1 argument is required');
|
throw new TypeError('1 argument is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +278,6 @@ export function polyfill(win = window) {
|
|||||||
if (this === node) {
|
if (this === node) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// tslint:disable-next-line: no-conditional-assignment
|
|
||||||
} while ((node = node && node.parentNode));
|
} while ((node = node && node.parentNode));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -426,8 +434,8 @@ export function getPositionsAndIndex(nestedIndex: number[]) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the latest mutation in the queue for each node.
|
* Returns the latest mutation in the queue for each node.
|
||||||
* @param {textMutation[]} mutations The text mutations to filter.
|
* @param mutations - mutations The text mutations to filter.
|
||||||
* @returns {textMutation[]} The filtered text mutations.
|
* @returns The filtered text mutations.
|
||||||
*/
|
*/
|
||||||
export function uniqueTextMutations(mutations: textMutation[]): textMutation[] {
|
export function uniqueTextMutations(mutations: textMutation[]): textMutation[] {
|
||||||
const idSet = new Set<number>();
|
const idSet = new Set<number>();
|
||||||
|
|||||||
@@ -12,7 +12,12 @@ import {
|
|||||||
generateRecordSnippet,
|
generateRecordSnippet,
|
||||||
ISuite,
|
ISuite,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import { recordOptions, eventWithTime, EventType } from '../src/types';
|
import {
|
||||||
|
recordOptions,
|
||||||
|
eventWithTime,
|
||||||
|
EventType,
|
||||||
|
RecordPlugin,
|
||||||
|
} from '../src/types';
|
||||||
import { visitSnapshot, NodeType } from 'rrweb-snapshot';
|
import { visitSnapshot, NodeType } from 'rrweb-snapshot';
|
||||||
|
|
||||||
describe('record integration tests', function (this: ISuite) {
|
describe('record integration tests', function (this: ISuite) {
|
||||||
@@ -442,8 +447,8 @@ describe('record integration tests', function (this: ISuite) {
|
|||||||
const page: puppeteer.Page = await browser.newPage();
|
const page: puppeteer.Page = await browser.newPage();
|
||||||
await page.goto('about:blank');
|
await page.goto('about:blank');
|
||||||
await page.setContent(
|
await page.setContent(
|
||||||
getHtml.call(this, 'log.html', {
|
getHtml('log.html', {
|
||||||
plugins: '[rrwebConsoleRecord.getRecordConsolePlugin()]',
|
plugins: ('[rrwebConsoleRecord.getRecordConsolePlugin()]' as unknown) as RecordPlugin<unknown>[],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
"outDir": "build",
|
"outDir": "build",
|
||||||
"lib": ["es6", "dom"],
|
"lib": ["es6", "dom"],
|
||||||
"downlevelIteration": true,
|
"downlevelIteration": true,
|
||||||
"importsNotUsedAsValues": "error"
|
"importsNotUsedAsValues": "error",
|
||||||
|
"strictBindCallApply": true
|
||||||
},
|
},
|
||||||
"exclude": ["test"],
|
"exclude": ["test"],
|
||||||
"include": [
|
"include": [
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ export declare type LogData = {
|
|||||||
trace: string[];
|
trace: string[];
|
||||||
payload: string[];
|
payload: string[];
|
||||||
};
|
};
|
||||||
export declare type LogLevel = 'assert' | 'clear' | 'count' | 'countReset' | 'debug' | 'dir' | 'dirxml' | 'error' | 'group' | 'groupCollapsed' | 'groupEnd' | 'info' | 'log' | 'table' | 'time' | 'timeEnd' | 'timeLog' | 'trace' | 'warn';
|
|
||||||
export declare type Logger = {
|
export declare type Logger = {
|
||||||
assert?: typeof console.assert;
|
assert?: typeof console.assert;
|
||||||
clear?: typeof console.clear;
|
clear?: typeof console.clear;
|
||||||
@@ -37,6 +36,7 @@ export declare type Logger = {
|
|||||||
trace?: typeof console.trace;
|
trace?: typeof console.trace;
|
||||||
warn?: typeof console.warn;
|
warn?: typeof console.warn;
|
||||||
};
|
};
|
||||||
|
export declare type LogLevel = keyof Logger;
|
||||||
export declare const PLUGIN_NAME = "rrweb/console@1";
|
export declare const PLUGIN_NAME = "rrweb/console@1";
|
||||||
export declare const getRecordConsolePlugin: (options?: LogRecordOptions) => RecordPlugin;
|
export declare const getRecordConsolePlugin: (options?: LogRecordOptions) => RecordPlugin;
|
||||||
export {};
|
export {};
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
import type { StringifyOptions } from './index';
|
import type { StringifyOptions } from './index';
|
||||||
export declare function stringify(obj: any, stringifyOptions?: StringifyOptions): string;
|
export declare function stringify(obj: unknown, stringifyOptions?: StringifyOptions): string;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { IWindow, CanvasArg } from '../../../types';
|
import type { IWindow, CanvasArg } from '../../../types';
|
||||||
export declare function variableListFor(ctx: RenderingContext, ctor: string): any[];
|
export declare function variableListFor(ctx: RenderingContext, ctor: string): unknown[];
|
||||||
export declare const saveWebGLVar: (value: any, win: IWindow, ctx: RenderingContext) => number | void;
|
export declare const saveWebGLVar: (value: unknown, win: IWindow, ctx: RenderingContext) => number | void;
|
||||||
export declare function serializeArg(value: any, win: IWindow, ctx: RenderingContext): CanvasArg;
|
export declare function serializeArg(value: unknown, win: IWindow, ctx: RenderingContext): CanvasArg;
|
||||||
export declare const serializeArgs: (args: Array<any>, win: IWindow, ctx: RenderingContext) => CanvasArg[];
|
export declare const serializeArgs: (args: Array<unknown>, win: IWindow, ctx: RenderingContext) => CanvasArg[];
|
||||||
export declare const isInstanceOfWebGLObject: (value: any, win: IWindow) => value is WebGLTexture | WebGLShader | WebGLBuffer | WebGLVertexArrayObject | WebGLProgram | WebGLActiveInfo | WebGLUniformLocation | WebGLFramebuffer | WebGLRenderbuffer | WebGLShaderPrecisionFormat;
|
export declare const isInstanceOfWebGLObject: (value: unknown, win: IWindow) => value is WebGLTexture | WebGLShader | WebGLBuffer | WebGLVertexArrayObject | WebGLProgram | WebGLActiveInfo | WebGLUniformLocation | WebGLFramebuffer | WebGLRenderbuffer | WebGLShaderPrecisionFormat;
|
||||||
|
|||||||
2
packages/rrweb/typings/replay/index.d.ts
vendored
2
packages/rrweb/typings/replay/index.d.ts
vendored
@@ -52,8 +52,6 @@ export declare class Replayer {
|
|||||||
private attachDocumentToIframe;
|
private attachDocumentToIframe;
|
||||||
private collectIframeAndAttachDocument;
|
private collectIframeAndAttachDocument;
|
||||||
private waitForStylesheetLoad;
|
private waitForStylesheetLoad;
|
||||||
private hasImageArg;
|
|
||||||
private getImageArgs;
|
|
||||||
private preloadAllImages;
|
private preloadAllImages;
|
||||||
private preloadImages;
|
private preloadImages;
|
||||||
private deserializeAndPreloadCanvasEvents;
|
private deserializeAndPreloadCanvasEvents;
|
||||||
|
|||||||
11
packages/rrweb/typings/types.d.ts
vendored
11
packages/rrweb/typings/types.d.ts
vendored
@@ -17,11 +17,11 @@ export declare enum EventType {
|
|||||||
}
|
}
|
||||||
export declare type domContentLoadedEvent = {
|
export declare type domContentLoadedEvent = {
|
||||||
type: EventType.DomContentLoaded;
|
type: EventType.DomContentLoaded;
|
||||||
data: {};
|
data: unknown;
|
||||||
};
|
};
|
||||||
export declare type loadedEvent = {
|
export declare type loadedEvent = {
|
||||||
type: EventType.Load;
|
type: EventType.Load;
|
||||||
data: {};
|
data: unknown;
|
||||||
};
|
};
|
||||||
export declare type fullSnapshotEvent = {
|
export declare type fullSnapshotEvent = {
|
||||||
type: EventType.FullSnapshot;
|
type: EventType.FullSnapshot;
|
||||||
@@ -59,7 +59,6 @@ export declare type pluginEvent<T = unknown> = {
|
|||||||
payload: T;
|
payload: T;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export declare type styleSheetEvent = {};
|
|
||||||
export declare enum IncrementalSource {
|
export declare enum IncrementalSource {
|
||||||
Mutation = 0,
|
Mutation = 0,
|
||||||
MouseMove = 1,
|
MouseMove = 1,
|
||||||
@@ -134,7 +133,7 @@ export declare type SamplingStrategy = Partial<{
|
|||||||
}>;
|
}>;
|
||||||
export declare type RecordPlugin<TOptions = unknown> = {
|
export declare type RecordPlugin<TOptions = unknown> = {
|
||||||
name: string;
|
name: string;
|
||||||
observer?: (cb: Function, win: IWindow, options: TOptions) => listenerHandler;
|
observer?: (cb: (...args: Array<unknown>) => void, win: IWindow, options: TOptions) => listenerHandler;
|
||||||
eventProcessor?: <TExtend>(event: eventWithTime) => eventWithTime & TExtend;
|
eventProcessor?: <TExtend>(event: eventWithTime) => eventWithTime & TExtend;
|
||||||
options: TOptions;
|
options: TOptions;
|
||||||
};
|
};
|
||||||
@@ -198,8 +197,8 @@ export declare type observerParam = {
|
|||||||
shadowDomManager: ShadowDomManager;
|
shadowDomManager: ShadowDomManager;
|
||||||
canvasManager: CanvasManager;
|
canvasManager: CanvasManager;
|
||||||
plugins: Array<{
|
plugins: Array<{
|
||||||
observer: Function;
|
observer: (cb: (...arg: Array<unknown>) => void, win: IWindow, options: unknown) => listenerHandler;
|
||||||
callback: Function;
|
callback: (...arg: Array<unknown>) => void;
|
||||||
options: unknown;
|
options: unknown;
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|||||||
4
packages/rrweb/typings/utils.d.ts
vendored
4
packages/rrweb/typings/utils.d.ts
vendored
@@ -3,11 +3,11 @@ import type { IMirror, Mirror } from 'rrweb-snapshot';
|
|||||||
import type { RRNode, RRIFrameElement } from 'rrdom';
|
import type { RRNode, RRIFrameElement } from 'rrdom';
|
||||||
export declare function on(type: string, fn: EventListenerOrEventListenerObject, target?: Document | IWindow): listenerHandler;
|
export declare function on(type: string, fn: EventListenerOrEventListenerObject, target?: Document | IWindow): listenerHandler;
|
||||||
export declare let _mirror: DeprecatedMirror;
|
export declare let _mirror: DeprecatedMirror;
|
||||||
export declare function throttle<T>(func: (arg: T) => void, wait: number, options?: throttleOptions): (arg: T) => void;
|
export declare function throttle<T>(func: (arg: T) => void, wait: number, options?: throttleOptions): (...args: T[]) => void;
|
||||||
export declare function hookSetter<T>(target: T, key: string | number | symbol, d: PropertyDescriptor, isRevoked?: boolean, win?: Window & typeof globalThis): hookResetter;
|
export declare function hookSetter<T>(target: T, key: string | number | symbol, d: PropertyDescriptor, isRevoked?: boolean, win?: Window & typeof globalThis): hookResetter;
|
||||||
export declare function patch(source: {
|
export declare function patch(source: {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}, name: string, replacement: (...args: any[]) => any): () => void;
|
}, name: string, replacement: (...args: unknown[]) => unknown): () => void;
|
||||||
export declare function getWindowHeight(): number;
|
export declare function getWindowHeight(): number;
|
||||||
export declare function getWindowWidth(): number;
|
export declare function getWindowWidth(): number;
|
||||||
export declare function isBlocked(node: Node | null, blockClass: blockClass, checkAncestors: boolean): boolean;
|
export declare function isBlocked(node: Node | null, blockClass: blockClass, checkAncestors: boolean): boolean;
|
||||||
|
|||||||
@@ -7945,10 +7945,10 @@ minizlib@^2.0.0, minizlib@^2.1.1:
|
|||||||
minipass "^3.0.0"
|
minipass "^3.0.0"
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
mitt@^1.1.3:
|
mitt@^3.0.0:
|
||||||
version "1.2.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz"
|
resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd"
|
||||||
integrity sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw==
|
integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==
|
||||||
|
|
||||||
mkdirp-classic@^0.5.2:
|
mkdirp-classic@^0.5.2:
|
||||||
version "0.5.3"
|
version "0.5.3"
|
||||||
|
|||||||
Reference in New Issue
Block a user