Mask value attribute changes for elements in maskInputOptions (#602)

* mask value attribute changes for elements in maskInputOptions

* refactor initInputObserver to use maskInputValue

* add todo

* Fix typo

* upgrade rrweb-snapshot to 1.1.6

* move maskInputValue to rrweb-snapshot
This commit is contained in:
Justin Halsall
2026-04-01 12:00:00 +08:00
committed by GitHub
parent 38a9e36b8f
commit 4dcd674e34
15 changed files with 469 additions and 47 deletions

View File

@@ -66,6 +66,6 @@
"@xstate/fsm": "^1.4.0",
"fflate": "^0.4.4",
"mitt": "^1.1.3",
"rrweb-snapshot": "^1.1.5"
"rrweb-snapshot": "^1.1.6"
}
}

View File

@@ -194,6 +194,7 @@ function record<T = eventWithTime>(
inlineStylesheet,
maskInputOptions,
maskTextFn,
maskInputFn,
recordCanvas,
sampling,
slimDOMOptions,

View File

@@ -7,6 +7,9 @@ import {
IGNORED_NODE,
isShadowRoot,
needMaskingText,
maskInputValue,
MaskTextFn,
MaskInputFn,
} from 'rrweb-snapshot';
import {
mutationRecord,
@@ -17,7 +20,6 @@ import {
attributeCursor,
removedNodeMutation,
addedNodeMutation,
MaskTextFn,
Mirror,
} from '../types';
import {
@@ -167,6 +169,7 @@ export default class MutationBuffer {
private inlineStylesheet: boolean;
private maskInputOptions: MaskInputOptions;
private maskTextFn: MaskTextFn | undefined;
private maskInputFn: MaskInputFn | undefined;
private recordCanvas: boolean;
private slimDOMOptions: SlimDOMOptions;
private doc: Document;
@@ -184,6 +187,7 @@ export default class MutationBuffer {
inlineStylesheet: boolean,
maskInputOptions: MaskInputOptions,
maskTextFn: MaskTextFn | undefined,
maskInputFn: MaskInputFn | undefined,
recordCanvas: boolean,
slimDOMOptions: SlimDOMOptions,
doc: Document,
@@ -198,6 +202,7 @@ export default class MutationBuffer {
this.inlineStylesheet = inlineStylesheet;
this.maskInputOptions = maskInputOptions;
this.maskTextFn = maskTextFn;
this.maskInputFn = maskInputFn;
this.recordCanvas = recordCanvas;
this.slimDOMOptions = slimDOMOptions;
this.emissionCallback = cb;
@@ -287,6 +292,7 @@ export default class MutationBuffer {
inlineStylesheet: this.inlineStylesheet,
maskInputOptions: this.maskInputOptions,
maskTextFn: this.maskTextFn,
maskInputFn: this.maskInputFn,
slimDOMOptions: this.slimDOMOptions,
recordCanvas: this.recordCanvas,
onSerialize: (currentN) => {
@@ -443,7 +449,16 @@ export default class MutationBuffer {
break;
}
case 'attributes': {
const value = (m.target as HTMLElement).getAttribute(m.attributeName!);
let value = (m.target as HTMLElement).getAttribute(m.attributeName!);
if (m.attributeName === 'value') {
value = maskInputValue({
maskInputOptions: this.maskInputOptions,
tagName: (m.target as HTMLElement).tagName,
type: (m.target as HTMLElement).getAttribute('type'),
value,
maskInputFn: this.maskInputFn,
});
}
if (isBlocked(m.target, this.blockClass) || value === m.oldValue) {
return;
}

View File

@@ -1,4 +1,11 @@
import { INode, MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
import {
INode,
MaskInputOptions,
SlimDOMOptions,
maskInputValue,
MaskInputFn,
MaskTextFn,
} from 'rrweb-snapshot';
import { FontFaceDescriptors, FontFaceSet } from 'css-font-loading-module';
import {
throttle,
@@ -35,8 +42,6 @@ import {
canvasMutationCallback,
fontCallback,
fontParam,
MaskInputFn,
MaskTextFn,
Mirror,
} from '../types';
import MutationBuffer from './mutation';
@@ -83,6 +88,7 @@ export function initMutationObserver(
inlineStylesheet: boolean,
maskInputOptions: MaskInputOptions,
maskTextFn: MaskTextFn | undefined,
maskInputFn: MaskInputFn | undefined,
recordCanvas: boolean,
slimDOMOptions: SlimDOMOptions,
mirror: Mirror,
@@ -102,6 +108,7 @@ export function initMutationObserver(
inlineStylesheet,
maskInputOptions,
maskTextFn,
maskInputFn,
recordCanvas,
slimDOMOptions,
doc,
@@ -366,11 +373,13 @@ function initInputObserver(
] ||
maskInputOptions[type as keyof MaskInputOptions]
) {
if (maskInputFn) {
text = maskInputFn(text);
} else {
text = '*'.repeat(text.length);
}
text = maskInputValue({
maskInputOptions,
tagName: (target as HTMLElement).tagName,
type,
value: text,
maskInputFn,
});
}
cbWithDedup(target, { text, isChecked });
// if a radio was checked
@@ -476,7 +485,7 @@ function initMediaInteractionObserver(
blockClass: blockClass,
mirror: Mirror,
): listenerHandler {
const handler = (type: MediaInteractions ) => (event: Event) => {
const handler = (type: MediaInteractions) => (event: Event) => {
const target = getEventTarget(event);
if (!target || isBlocked(target as Node, blockClass)) {
return;
@@ -484,13 +493,13 @@ function initMediaInteractionObserver(
mediaInteractionCb({
type,
id: mirror.getId(target as INode),
currentTime: (target as HTMLMediaElement).currentTime
currentTime: (target as HTMLMediaElement).currentTime,
});
};
const handlers = [
on('play', handler(MediaInteractions.Play)),
on('pause', handler(MediaInteractions.Pause)),
on('seeked', handler(MediaInteractions.Seeked))
on('seeked', handler(MediaInteractions.Seeked)),
];
return () => {
handlers.forEach((h) => h());
@@ -716,6 +725,7 @@ export function initObservers(
o.inlineStylesheet,
o.maskInputOptions,
o.maskTextFn,
o.maskInputFn,
o.recordCanvas,
o.slimDOMOptions,
o.mirror,

View File

@@ -2,12 +2,16 @@ import {
mutationCallBack,
blockClass,
maskTextClass,
MaskTextFn,
Mirror,
scrollCallback,
SamplingStrategy,
} from '../types';
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
import {
MaskInputOptions,
SlimDOMOptions,
MaskTextFn,
MaskInputFn,
} from 'rrweb-snapshot';
import { IframeManager } from './iframe-manager';
import { initMutationObserver, initScrollObserver } from './observer';
@@ -19,6 +23,7 @@ type BypassOptions = {
inlineStylesheet: boolean;
maskInputOptions: MaskInputOptions;
maskTextFn: MaskTextFn | undefined;
maskInputFn: MaskInputFn | undefined;
recordCanvas: boolean;
sampling: SamplingStrategy;
slimDOMOptions: SlimDOMOptions;
@@ -54,6 +59,7 @@ export class ShadowDomManager {
this.bypassOptions.inlineStylesheet,
this.bypassOptions.maskInputOptions,
this.bypassOptions.maskTextFn,
this.bypassOptions.maskInputFn,
this.bypassOptions.recordCanvas,
this.bypassOptions.slimDOMOptions,
this.mirror,

View File

@@ -4,6 +4,8 @@ import {
INode,
MaskInputOptions,
SlimDOMOptions,
MaskInputFn,
MaskTextFn,
} from 'rrweb-snapshot';
import { PackFn, UnpackFn } from './packer/base';
import { FontFaceDescriptors } from 'css-font-loading-module';
@@ -544,10 +546,6 @@ export enum ReplayerEvents {
PlayBack = 'play-back',
}
export type MaskInputFn = (text: string) => string;
export type MaskTextFn = (text: string) => string;
// store the state that would be changed during the process(unmount from dom and mount again)
export type ElementState = {
// [scrollLeft,scrollTop]

View File

@@ -53,7 +53,7 @@ export function createMirror(): Mirror {
delete this.map[id];
if (n.childNodes) {
n.childNodes.forEach((child) =>
this.removeNodeFromMap(child as Node as INode),
this.removeNodeFromMap((child as Node) as INode),
);
}
},
@@ -275,7 +275,7 @@ export function isAncestorRemoved(target: INode, mirror: Mirror): boolean {
if (!target.parentNode) {
return true;
}
return isAncestorRemoved(target.parentNode as unknown as INode, mirror);
return isAncestorRemoved((target.parentNode as unknown) as INode, mirror);
}
export function isTouchEvent(
@@ -286,13 +286,13 @@ export function isTouchEvent(
export function polyfill(win = window) {
if ('NodeList' in win && !win.NodeList.prototype.forEach) {
win.NodeList.prototype.forEach = Array.prototype
.forEach as unknown as NodeList['forEach'];
win.NodeList.prototype.forEach = (Array.prototype
.forEach as unknown) as NodeList['forEach'];
}
if ('DOMTokenList' in win && !win.DOMTokenList.prototype.forEach) {
win.DOMTokenList.prototype.forEach = Array.prototype
.forEach as unknown as DOMTokenList['forEach'];
win.DOMTokenList.prototype.forEach = (Array.prototype
.forEach as unknown) as DOMTokenList['forEach'];
}
// https://github.com/Financial-Times/polyfill-service/pull/183
@@ -396,7 +396,7 @@ export class TreeIndex {
const node = mirror.getNode(id);
node?.childNodes.forEach((childNode) => {
if ('__sn' in childNode) {
deepRemoveFromMirror((childNode as unknown as INode).__sn.id);
deepRemoveFromMirror(((childNode as unknown) as INode).__sn.id);
}
});
};
@@ -460,8 +460,12 @@ export class TreeIndex {
scrollMap: TreeIndex['scrollMap'];
inputMap: TreeIndex['inputMap'];
} {
const { tree, removeNodeMutations, textMutations, attributeMutations } =
this;
const {
tree,
removeNodeMutations,
textMutations,
attributeMutations,
} = this;
const batchMutationData: mutationData = {
source: IncrementalSource.Mutation,
@@ -650,5 +654,5 @@ export function getBaseDimension(
export function hasShadowRoot<T extends Node>(
n: T,
): n is T & { shadowRoot: ShadowRoot } {
return Boolean((n as unknown as Element)?.shadowRoot);
return Boolean(((n as unknown) as Element)?.shadowRoot);
}

View File

@@ -5461,6 +5461,360 @@ exports[`maskInputOptions 1`] = `
]"
`;
exports[`maskPassword 1`] = `
"[
{
\\"type\\": 0,
\\"data\\": {}
},
{
\\"type\\": 1,
\\"data\\": {}
},
{
\\"type\\": 4,
\\"data\\": {
\\"href\\": \\"about:blank\\",
\\"width\\": 1920,
\\"height\\": 1080
}
},
{
\\"type\\": 2,
\\"data\\": {
\\"node\\": {
\\"type\\": 0,
\\"childNodes\\": [
{
\\"type\\": 2,
\\"tagName\\": \\"html\\",
\\"attributes\\": {
\\"lang\\": \\"en\\"
},
\\"childNodes\\": [
{
\\"type\\": 2,
\\"tagName\\": \\"head\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\",
\\"id\\": 4
},
{
\\"type\\": 2,
\\"tagName\\": \\"meta\\",
\\"attributes\\": {
\\"charset\\": \\"UTF-8\\"
},
\\"childNodes\\": [],
\\"id\\": 5
},
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\",
\\"id\\": 6
},
{
\\"type\\": 2,
\\"tagName\\": \\"meta\\",
\\"attributes\\": {
\\"http-equiv\\": \\"X-UA-Compatible\\",
\\"content\\": \\"IE=edge\\"
},
\\"childNodes\\": [],
\\"id\\": 7
},
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\",
\\"id\\": 8
},
{
\\"type\\": 2,
\\"tagName\\": \\"meta\\",
\\"attributes\\": {
\\"name\\": \\"viewport\\",
\\"content\\": \\"width=device-width, initial-scale=1.0\\"
},
\\"childNodes\\": [],
\\"id\\": 9
},
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\",
\\"id\\": 10
},
{
\\"type\\": 2,
\\"tagName\\": \\"title\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 3,
\\"textContent\\": \\"Document\\",
\\"id\\": 12
}
],
\\"id\\": 11
},
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\",
\\"id\\": 13
}
],
\\"id\\": 3
},
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\",
\\"id\\": 14
},
{
\\"type\\": 2,
\\"tagName\\": \\"body\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\",
\\"id\\": 16
},
{
\\"type\\": 2,
\\"tagName\\": \\"input\\",
\\"attributes\\": {
\\"type\\": \\"password\\",
\\"id\\": \\"password\\"
},
\\"childNodes\\": [],
\\"id\\": 17
},
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\",
\\"id\\": 18
},
{
\\"type\\": 2,
\\"tagName\\": \\"script\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 3,
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
\\"id\\": 20
}
],
\\"id\\": 19
},
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\\\n \\",
\\"id\\": 21
},
{
\\"type\\": 2,
\\"tagName\\": \\"script\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 3,
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
\\"id\\": 23
}
],
\\"id\\": 22
},
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\",
\\"id\\": 24
}
],
\\"id\\": 15
}
],
\\"id\\": 2
}
],
\\"id\\": 1
},
\\"initialOffset\\": {
\\"left\\": 0,
\\"top\\": 0
}
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 2,
\\"type\\": 5,
\\"id\\": 17
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 5,
\\"text\\": \\"*\\",
\\"isChecked\\": false,
\\"id\\": 17
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 0,
\\"texts\\": [],
\\"attributes\\": [
{
\\"id\\": 17,
\\"attributes\\": {
\\"value\\": \\"*\\"
}
}
],
\\"removes\\": [],
\\"adds\\": []
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 5,
\\"text\\": \\"**\\",
\\"isChecked\\": false,
\\"id\\": 17
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 0,
\\"texts\\": [],
\\"attributes\\": [
{
\\"id\\": 17,
\\"attributes\\": {
\\"value\\": \\"**\\"
}
}
],
\\"removes\\": [],
\\"adds\\": []
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 5,
\\"text\\": \\"***\\",
\\"isChecked\\": false,
\\"id\\": 17
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 0,
\\"texts\\": [],
\\"attributes\\": [
{
\\"id\\": 17,
\\"attributes\\": {
\\"value\\": \\"***\\"
}
}
],
\\"removes\\": [],
\\"adds\\": []
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 5,
\\"text\\": \\"****\\",
\\"isChecked\\": false,
\\"id\\": 17
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 0,
\\"texts\\": [],
\\"attributes\\": [
{
\\"id\\": 17,
\\"attributes\\": {
\\"value\\": \\"****\\"
}
}
],
\\"removes\\": [],
\\"adds\\": []
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 5,
\\"text\\": \\"*****\\",
\\"isChecked\\": false,
\\"id\\": 17
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 0,
\\"texts\\": [],
\\"attributes\\": [
{
\\"id\\": 17,
\\"attributes\\": {
\\"value\\": \\"*****\\"
}
}
],
\\"removes\\": [],
\\"adds\\": []
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 5,
\\"text\\": \\"******\\",
\\"isChecked\\": false,
\\"id\\": 17
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 0,
\\"texts\\": [],
\\"attributes\\": [
{
\\"id\\": 17,
\\"attributes\\": {
\\"value\\": \\"******\\"
}
}
],
\\"removes\\": [],
\\"adds\\": []
}
}
]"
`;
exports[`move-node-1 1`] = `
"[
{

17
test/html/password.html Normal file
View File

@@ -0,0 +1,17 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<input type="password" id="password" />
<script>
const password = document.getElementById('password');
password.addEventListener('keyup', (event) => {
password.setAttribute('value', password.value);
});
</script>
</body>
</html>

View File

@@ -267,6 +267,23 @@ describe('record integration tests', function (this: ISuite) {
assertSnapshot(snapshots, __filename, 'maskInputOptions');
});
it('should mask value attribute with maskInputOptions', async () => {
const page: puppeteer.Page = await this.browser.newPage();
await page.goto('about:blank');
await page.setContent(
getHtml.call(this, 'password.html', {
maskInputOptions: {
password: true,
},
}),
);
await page.type('input[type="password"]', 'secr3t');
const snapshots = await page.evaluate('window.snapshots');
assertSnapshot(snapshots, __filename, 'maskPassword');
});
it('should not record blocked elements and its child nodes', async () => {
const page: puppeteer.Page = await this.browser.newPage();
await page.goto('about:blank');

View File

@@ -1,5 +1,5 @@
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
import { mutationRecord, blockClass, maskTextClass, mutationCallBack, MaskTextFn, Mirror } from '../types';
import { MaskInputOptions, SlimDOMOptions, MaskTextFn, MaskInputFn } from 'rrweb-snapshot';
import { mutationRecord, blockClass, maskTextClass, mutationCallBack, Mirror } from '../types';
import { IframeManager } from './iframe-manager';
import { ShadowDomManager } from './shadow-dom-manager';
export default class MutationBuffer {
@@ -21,13 +21,14 @@ export default class MutationBuffer {
private inlineStylesheet;
private maskInputOptions;
private maskTextFn;
private maskInputFn;
private recordCanvas;
private slimDOMOptions;
private doc;
private mirror;
private iframeManager;
private shadowDomManager;
init(cb: mutationCallBack, blockClass: blockClass, blockSelector: string | null, maskTextClass: maskTextClass, maskTextSelector: string | null, inlineStylesheet: boolean, maskInputOptions: MaskInputOptions, maskTextFn: MaskTextFn | undefined, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, doc: Document, mirror: Mirror, iframeManager: IframeManager, shadowDomManager: ShadowDomManager): void;
init(cb: mutationCallBack, blockClass: blockClass, blockSelector: string | null, maskTextClass: maskTextClass, maskTextSelector: string | null, inlineStylesheet: boolean, maskInputOptions: MaskInputOptions, maskTextFn: MaskTextFn | undefined, maskInputFn: MaskInputFn | undefined, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, doc: Document, mirror: Mirror, iframeManager: IframeManager, shadowDomManager: ShadowDomManager): void;
freeze(): void;
unfreeze(): void;
isFrozen(): boolean;

View File

@@ -1,10 +1,10 @@
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
import { mutationCallBack, observerParam, listenerHandler, scrollCallback, blockClass, maskTextClass, hooksParam, SamplingStrategy, MaskTextFn, Mirror } from '../types';
import { MaskInputOptions, SlimDOMOptions, MaskInputFn, MaskTextFn } from 'rrweb-snapshot';
import { mutationCallBack, observerParam, listenerHandler, scrollCallback, blockClass, maskTextClass, hooksParam, SamplingStrategy, Mirror } from '../types';
import MutationBuffer from './mutation';
import { IframeManager } from './iframe-manager';
import { ShadowDomManager } from './shadow-dom-manager';
export declare const mutationBuffers: MutationBuffer[];
export declare function initMutationObserver(cb: mutationCallBack, doc: Document, blockClass: blockClass, blockSelector: string | null, maskTextClass: maskTextClass, maskTextSelector: string | null, inlineStylesheet: boolean, maskInputOptions: MaskInputOptions, maskTextFn: MaskTextFn | undefined, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, mirror: Mirror, iframeManager: IframeManager, shadowDomManager: ShadowDomManager, rootEl: Node): MutationObserver;
export declare function initMutationObserver(cb: mutationCallBack, doc: Document, blockClass: blockClass, blockSelector: string | null, maskTextClass: maskTextClass, maskTextSelector: string | null, inlineStylesheet: boolean, maskInputOptions: MaskInputOptions, maskTextFn: MaskTextFn | undefined, maskInputFn: MaskInputFn | undefined, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, mirror: Mirror, iframeManager: IframeManager, shadowDomManager: ShadowDomManager, rootEl: Node): MutationObserver;
export declare function initScrollObserver(cb: scrollCallback, doc: Document, mirror: Mirror, blockClass: blockClass, sampling: SamplingStrategy): listenerHandler;
export declare const INPUT_TAGS: string[];
export declare function initObservers(o: observerParam, hooks?: hooksParam): listenerHandler;

View File

@@ -1,5 +1,5 @@
import { mutationCallBack, blockClass, maskTextClass, MaskTextFn, Mirror, scrollCallback, SamplingStrategy } from '../types';
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
import { mutationCallBack, blockClass, maskTextClass, Mirror, scrollCallback, SamplingStrategy } from '../types';
import { MaskInputOptions, SlimDOMOptions, MaskTextFn, MaskInputFn } from 'rrweb-snapshot';
import { IframeManager } from './iframe-manager';
declare type BypassOptions = {
blockClass: blockClass;
@@ -9,6 +9,7 @@ declare type BypassOptions = {
inlineStylesheet: boolean;
maskInputOptions: MaskInputOptions;
maskTextFn: MaskTextFn | undefined;
maskInputFn: MaskInputFn | undefined;
recordCanvas: boolean;
sampling: SamplingStrategy;
slimDOMOptions: SlimDOMOptions;

4
typings/types.d.ts vendored
View File

@@ -1,4 +1,4 @@
import { serializedNodeWithId, idNodeMap, INode, MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
import { serializedNodeWithId, idNodeMap, INode, MaskInputOptions, SlimDOMOptions, MaskInputFn, MaskTextFn } from 'rrweb-snapshot';
import { PackFn, UnpackFn } from './packer/base';
import { FontFaceDescriptors } from 'css-font-loading-module';
import { IframeManager } from './record/iframe-manager';
@@ -413,8 +413,6 @@ export declare enum ReplayerEvents {
StateChange = "state-change",
PlayBack = "play-back"
}
export declare type MaskInputFn = (text: string) => string;
export declare type MaskTextFn = (text: string) => string;
export declare type ElementState = {
scroll?: [number, number];
};

View File

@@ -2845,10 +2845,10 @@ rollup@^2.3.3:
optionalDependencies:
fsevents "~2.1.2"
rrweb-snapshot@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/rrweb-snapshot/-/rrweb-snapshot-1.1.5.tgz#7784a01e7d2ccd067ec56cdec38a1fc2a77cbe86"
integrity sha512-U5mfiySoK85WkLCljFTh+ShgTDrOMXWrh1qpou8WwW3AEXba7CSv/sTtoXp3QfG5oxe1YOc+fC3tB4kHMdI00w==
rrweb-snapshot@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/rrweb-snapshot/-/rrweb-snapshot-1.1.6.tgz#e598510e387b9b2c62ce0cfc586a37c89549b228"
integrity sha512-twvqV1WRuFwodO5UV0tzS4Q2w+ZYi5kwOx/uwbRkplfazcnbphDLFvy8x9IwMOZFhyRg1jDJjbnlC8wy4xq5bQ==
run-async@^2.2.0:
version "2.4.1"