Add options to mask texts (#540)
* feat: add options to mask texts * feat: add the default mask function * refactor: rename options to identify the difference between mask text and mask input * test: add tests about masking * doc: add options about masking * chore: bump up rrweb-snapshot version
This commit is contained in:
@@ -43,11 +43,14 @@ function record<T = eventWithTime>(
|
||||
blockClass = 'rr-block',
|
||||
blockSelector = null,
|
||||
ignoreClass = 'rr-ignore',
|
||||
maskTextClass = 'rr-mask',
|
||||
maskTextSelector = null,
|
||||
inlineStylesheet = true,
|
||||
maskAllInputs,
|
||||
maskInputOptions: _maskInputOptions,
|
||||
slimDOMOptions: _slimDOMOptions,
|
||||
maskInputFn,
|
||||
maskTextFn,
|
||||
hooks,
|
||||
packFn,
|
||||
sampling = {},
|
||||
@@ -203,8 +206,11 @@ function record<T = eventWithTime>(
|
||||
bypassOptions: {
|
||||
blockClass,
|
||||
blockSelector,
|
||||
maskTextClass,
|
||||
maskTextSelector,
|
||||
inlineStylesheet,
|
||||
maskInputOptions,
|
||||
maskTextFn,
|
||||
recordCanvas,
|
||||
slimDOMOptions,
|
||||
iframeManager,
|
||||
@@ -228,8 +234,11 @@ function record<T = eventWithTime>(
|
||||
const [node, idNodeMap] = snapshot(document, {
|
||||
blockClass,
|
||||
blockSelector,
|
||||
maskTextClass,
|
||||
maskTextSelector,
|
||||
inlineStylesheet,
|
||||
maskAllInputs: maskInputOptions,
|
||||
maskTextFn,
|
||||
slimDOM: slimDOMOptions,
|
||||
recordCanvas,
|
||||
onSerialize: (n) => {
|
||||
@@ -396,6 +405,8 @@ function record<T = eventWithTime>(
|
||||
),
|
||||
blockClass,
|
||||
ignoreClass,
|
||||
maskTextClass,
|
||||
maskTextSelector,
|
||||
maskInputOptions,
|
||||
inlineStylesheet,
|
||||
sampling,
|
||||
@@ -403,6 +414,7 @@ function record<T = eventWithTime>(
|
||||
collectFonts,
|
||||
doc,
|
||||
maskInputFn,
|
||||
maskTextFn,
|
||||
logOptions,
|
||||
blockSelector,
|
||||
slimDOMOptions,
|
||||
|
||||
@@ -6,15 +6,18 @@ import {
|
||||
SlimDOMOptions,
|
||||
IGNORED_NODE,
|
||||
isShadowRoot,
|
||||
needMaskingText,
|
||||
} from 'rrweb-snapshot';
|
||||
import {
|
||||
mutationRecord,
|
||||
blockClass,
|
||||
maskTextClass,
|
||||
mutationCallBack,
|
||||
textCursor,
|
||||
attributeCursor,
|
||||
removedNodeMutation,
|
||||
addedNodeMutation,
|
||||
MaskTextFn,
|
||||
} from '../types';
|
||||
import {
|
||||
mirror,
|
||||
@@ -159,8 +162,11 @@ export default class MutationBuffer {
|
||||
private emissionCallback: mutationCallBack;
|
||||
private blockClass: blockClass;
|
||||
private blockSelector: string | null;
|
||||
private maskTextClass: maskTextClass;
|
||||
private maskTextSelector: string | null;
|
||||
private inlineStylesheet: boolean;
|
||||
private maskInputOptions: MaskInputOptions;
|
||||
private maskTextFn: MaskTextFn | undefined;
|
||||
private recordCanvas: boolean;
|
||||
private slimDOMOptions: SlimDOMOptions;
|
||||
private doc: Document;
|
||||
@@ -172,8 +178,11 @@ export default class MutationBuffer {
|
||||
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,
|
||||
@@ -182,8 +191,11 @@ export default class MutationBuffer {
|
||||
) {
|
||||
this.blockClass = blockClass;
|
||||
this.blockSelector = blockSelector;
|
||||
this.maskTextClass = maskTextClass;
|
||||
this.maskTextSelector = maskTextSelector;
|
||||
this.inlineStylesheet = inlineStylesheet;
|
||||
this.maskInputOptions = maskInputOptions;
|
||||
this.maskTextFn = maskTextFn;
|
||||
this.recordCanvas = recordCanvas;
|
||||
this.slimDOMOptions = slimDOMOptions;
|
||||
this.emissionCallback = cb;
|
||||
@@ -266,9 +278,12 @@ export default class MutationBuffer {
|
||||
map: mirror.map,
|
||||
blockClass: this.blockClass,
|
||||
blockSelector: this.blockSelector,
|
||||
maskTextClass: this.maskTextClass,
|
||||
maskTextSelector: this.maskTextSelector,
|
||||
skipChild: true,
|
||||
inlineStylesheet: this.inlineStylesheet,
|
||||
maskInputOptions: this.maskInputOptions,
|
||||
maskTextFn: this.maskTextFn,
|
||||
slimDOMOptions: this.slimDOMOptions,
|
||||
recordCanvas: this.recordCanvas,
|
||||
onSerialize: (currentN) => {
|
||||
@@ -409,7 +424,16 @@ export default class MutationBuffer {
|
||||
const value = m.target.textContent;
|
||||
if (!isBlocked(m.target, this.blockClass) && value !== m.oldValue) {
|
||||
this.texts.push({
|
||||
value,
|
||||
value:
|
||||
needMaskingText(
|
||||
m.target,
|
||||
this.maskTextClass,
|
||||
this.maskTextSelector,
|
||||
) && value
|
||||
? this.maskTextFn
|
||||
? this.maskTextFn(value)
|
||||
: value.replace(/[\S]/g, '*')
|
||||
: value,
|
||||
node: m.target,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
inputCallback,
|
||||
hookResetter,
|
||||
blockClass,
|
||||
maskTextClass,
|
||||
IncrementalSource,
|
||||
hooksParam,
|
||||
Arguments,
|
||||
@@ -36,6 +37,7 @@ import {
|
||||
fontCallback,
|
||||
fontParam,
|
||||
MaskInputFn,
|
||||
MaskTextFn,
|
||||
logCallback,
|
||||
LogRecordOptions,
|
||||
Logger,
|
||||
@@ -62,8 +64,11 @@ export function initMutationObserver(
|
||||
doc: Document,
|
||||
blockClass: blockClass,
|
||||
blockSelector: string | null,
|
||||
maskTextClass: maskTextClass,
|
||||
maskTextSelector: string | null,
|
||||
inlineStylesheet: boolean,
|
||||
maskInputOptions: MaskInputOptions,
|
||||
maskTextFn: MaskTextFn | undefined,
|
||||
recordCanvas: boolean,
|
||||
slimDOMOptions: SlimDOMOptions,
|
||||
iframeManager: IframeManager,
|
||||
@@ -77,8 +82,11 @@ export function initMutationObserver(
|
||||
cb,
|
||||
blockClass,
|
||||
blockSelector,
|
||||
maskTextClass,
|
||||
maskTextSelector,
|
||||
inlineStylesheet,
|
||||
maskInputOptions,
|
||||
maskTextFn,
|
||||
recordCanvas,
|
||||
slimDOMOptions,
|
||||
doc,
|
||||
@@ -777,8 +785,11 @@ export function initObservers(
|
||||
o.doc,
|
||||
o.blockClass,
|
||||
o.blockSelector,
|
||||
o.maskTextClass,
|
||||
o.maskTextSelector,
|
||||
o.inlineStylesheet,
|
||||
o.maskInputOptions,
|
||||
o.maskTextFn,
|
||||
o.recordCanvas,
|
||||
o.slimDOMOptions,
|
||||
o.iframeManager,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mutationCallBack, blockClass } from '../types';
|
||||
import { mutationCallBack, blockClass, maskTextClass, MaskTextFn } from '../types';
|
||||
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
||||
import { IframeManager } from './iframe-manager';
|
||||
import { initMutationObserver } from './observer';
|
||||
@@ -6,8 +6,11 @@ import { initMutationObserver } from './observer';
|
||||
type BypassOptions = {
|
||||
blockClass: blockClass;
|
||||
blockSelector: string | null;
|
||||
maskTextClass: maskTextClass;
|
||||
maskTextSelector: string | null;
|
||||
inlineStylesheet: boolean;
|
||||
maskInputOptions: MaskInputOptions;
|
||||
maskTextFn: MaskTextFn | undefined;
|
||||
recordCanvas: boolean;
|
||||
slimDOMOptions: SlimDOMOptions;
|
||||
iframeManager: IframeManager;
|
||||
@@ -31,8 +34,11 @@ export class ShadowDomManager {
|
||||
doc,
|
||||
this.bypassOptions.blockClass,
|
||||
this.bypassOptions.blockSelector,
|
||||
this.bypassOptions.maskTextClass,
|
||||
this.bypassOptions.maskTextSelector,
|
||||
this.bypassOptions.inlineStylesheet,
|
||||
this.bypassOptions.maskInputOptions,
|
||||
this.bypassOptions.maskTextFn,
|
||||
this.bypassOptions.recordCanvas,
|
||||
this.bypassOptions.slimDOMOptions,
|
||||
this.bypassOptions.iframeManager,
|
||||
|
||||
10
src/types.ts
10
src/types.ts
@@ -163,6 +163,8 @@ export type eventWithTime = event & {
|
||||
|
||||
export type blockClass = string | RegExp;
|
||||
|
||||
export type maskTextClass = string | RegExp;
|
||||
|
||||
export type SamplingStrategy = Partial<{
|
||||
/**
|
||||
* false means not to record mouse/touch move events
|
||||
@@ -196,9 +198,12 @@ export type recordOptions<T> = {
|
||||
blockClass?: blockClass;
|
||||
blockSelector?: string;
|
||||
ignoreClass?: string;
|
||||
maskTextClass?: maskTextClass;
|
||||
maskTextSelector?: string;
|
||||
maskAllInputs?: boolean;
|
||||
maskInputOptions?: MaskInputOptions;
|
||||
maskInputFn?: MaskInputFn;
|
||||
maskTextFn?: MaskTextFn;
|
||||
slimDOMOptions?: SlimDOMOptions | 'all' | true;
|
||||
inlineStylesheet?: boolean;
|
||||
hooks?: hooksParam;
|
||||
@@ -222,8 +227,11 @@ export type observerParam = {
|
||||
blockClass: blockClass;
|
||||
blockSelector: string | null;
|
||||
ignoreClass: string;
|
||||
maskTextClass: maskTextClass;
|
||||
maskTextSelector: string | null;
|
||||
maskInputOptions: MaskInputOptions;
|
||||
maskInputFn?: MaskInputFn;
|
||||
maskTextFn?: MaskTextFn;
|
||||
inlineStylesheet: boolean;
|
||||
styleSheetRuleCb: styleSheetRuleCallback;
|
||||
canvasMutationCb: canvasMutationCallback;
|
||||
@@ -583,6 +591,8 @@ export enum ReplayerEvents {
|
||||
|
||||
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]
|
||||
|
||||
Reference in New Issue
Block a user