move maskInputValue to rrweb-snapshot (#83)

More info: https://github.com/rrweb-io/rrweb/pull/602#discussion_r660434998
This commit is contained in:
Justin Halsall
2026-04-01 12:00:00 +08:00
committed by GitHub
parent 30f82a891b
commit 9fdd8cd2dc
6 changed files with 62 additions and 11 deletions

View File

@@ -8,9 +8,10 @@ import {
MaskInputOptions,
SlimDOMOptions,
MaskTextFn,
MaskInputFn,
KeepIframeSrcFn,
} from './types';
import { isElement, isShadowRoot } from './utils';
import { isElement, isShadowRoot, maskInputValue } from './utils';
let _id = 1;
const tagNameRegex = RegExp('[^a-z0-9-_:]');
@@ -348,6 +349,7 @@ function serializeNode(
inlineStylesheet: boolean;
maskInputOptions: MaskInputOptions;
maskTextFn: MaskTextFn | undefined;
maskInputFn: MaskInputFn | undefined;
recordCanvas: boolean;
keepIframeSrcFn: KeepIframeSrcFn;
},
@@ -361,13 +363,14 @@ function serializeNode(
inlineStylesheet,
maskInputOptions = {},
maskTextFn,
maskInputFn,
recordCanvas,
keepIframeSrcFn,
} = options;
// Only record root id when document object is not the base document
let rootId: number | undefined;
if (((doc as unknown) as INode).__sn) {
const docId = ((doc as unknown) as INode).__sn.id;
if ((doc as unknown as INode).__sn) {
const docId = (doc as unknown as INode).__sn.id;
rootId = docId === 1 ? undefined : docId;
}
switch (n.nodeType) {
@@ -443,11 +446,13 @@ function serializeNode(
attributes.type !== 'button' &&
value
) {
attributes.value =
maskInputOptions[attributes.type as keyof MaskInputOptions] ||
maskInputOptions[tagName as keyof MaskInputOptions]
? '*'.repeat(value.length)
: value;
attributes.value = maskInputValue({
type: attributes.type,
tagName,
value,
maskInputOptions,
maskInputFn,
});
} else if ((n as HTMLInputElement).checked) {
attributes.checked = (n as HTMLInputElement).checked;
}
@@ -650,6 +655,7 @@ export function serializeNodeWithId(
inlineStylesheet: boolean;
maskInputOptions?: MaskInputOptions;
maskTextFn: MaskTextFn | undefined;
maskInputFn: MaskInputFn | undefined;
slimDOMOptions: SlimDOMOptions;
keepIframeSrcFn?: KeepIframeSrcFn;
recordCanvas?: boolean;
@@ -670,6 +676,7 @@ export function serializeNodeWithId(
inlineStylesheet = true,
maskInputOptions = {},
maskTextFn,
maskInputFn,
slimDOMOptions,
recordCanvas = false,
onSerialize,
@@ -687,6 +694,7 @@ export function serializeNodeWithId(
inlineStylesheet,
maskInputOptions,
maskTextFn,
maskInputFn,
recordCanvas,
keepIframeSrcFn,
});
@@ -750,6 +758,7 @@ export function serializeNodeWithId(
inlineStylesheet,
maskInputOptions,
maskTextFn,
maskInputFn,
slimDOMOptions,
recordCanvas,
preserveWhiteSpace,
@@ -801,6 +810,7 @@ export function serializeNodeWithId(
inlineStylesheet,
maskInputOptions,
maskTextFn,
maskInputFn,
slimDOMOptions,
recordCanvas,
preserveWhiteSpace,
@@ -832,6 +842,7 @@ function snapshot(
inlineStylesheet?: boolean;
maskAllInputs?: boolean | MaskInputOptions;
maskTextFn?: MaskTextFn;
maskInputFn?: MaskTextFn;
slimDOM?: boolean | SlimDOMOptions;
recordCanvas?: boolean;
preserveWhiteSpace?: boolean;
@@ -850,6 +861,7 @@ function snapshot(
recordCanvas = false,
maskAllInputs = false,
maskTextFn,
maskInputFn,
slimDOM = false,
preserveWhiteSpace,
onSerialize,
@@ -913,6 +925,7 @@ function snapshot(
inlineStylesheet,
maskInputOptions,
maskTextFn,
maskInputFn,
slimDOMOptions,
recordCanvas,
preserveWhiteSpace,

View File

@@ -108,5 +108,6 @@ export type SlimDOMOptions = Partial<{
}>;
export type MaskTextFn = (text: string) => string;
export type MaskInputFn = (text: string) => string;
export type KeepIframeSrcFn = (src: string) => boolean;

View File

@@ -1,4 +1,4 @@
import { INode } from './types';
import { INode, MaskInputFn, MaskInputOptions } from './types';
export function isElement(n: Node | INode): n is Element {
return n.nodeType === n.ELEMENT_NODE;
@@ -8,3 +8,30 @@ export function isShadowRoot(n: Node): n is ShadowRoot {
const host: Element | null = (n as ShadowRoot)?.host;
return Boolean(host && host.shadowRoot && host.shadowRoot === n);
}
export function maskInputValue({
maskInputOptions,
tagName,
type,
value,
maskInputFn,
}: {
maskInputOptions: MaskInputOptions;
tagName: string;
type: string | number | boolean | null;
value: string | null;
maskInputFn?: MaskInputFn;
}): string {
let text = value || '';
if (
maskInputOptions[tagName.toLowerCase() as keyof MaskInputOptions] ||
maskInputOptions[type as keyof MaskInputOptions]
) {
if (maskInputFn) {
text = maskInputFn(text);
} else {
text = '*'.repeat(text.length);
}
}
return text;
}

View File

@@ -1,4 +1,4 @@
import { serializedNodeWithId, INode, idNodeMap, MaskInputOptions, SlimDOMOptions, MaskTextFn, KeepIframeSrcFn } from './types';
import { serializedNodeWithId, INode, idNodeMap, MaskInputOptions, SlimDOMOptions, MaskTextFn, MaskInputFn, KeepIframeSrcFn } from './types';
export declare const IGNORED_NODE = -2;
export declare function absoluteToStylesheet(cssText: string | null, href: string): string;
export declare function absoluteToDoc(doc: Document, attributeValue: string): string;
@@ -16,6 +16,7 @@ export declare function serializeNodeWithId(n: Node | INode, options: {
inlineStylesheet: boolean;
maskInputOptions?: MaskInputOptions;
maskTextFn: MaskTextFn | undefined;
maskInputFn: MaskInputFn | undefined;
slimDOMOptions: SlimDOMOptions;
keepIframeSrcFn?: KeepIframeSrcFn;
recordCanvas?: boolean;
@@ -32,6 +33,7 @@ declare function snapshot(n: Document, options?: {
inlineStylesheet?: boolean;
maskAllInputs?: boolean | MaskInputOptions;
maskTextFn?: MaskTextFn;
maskInputFn?: MaskTextFn;
slimDOM?: boolean | SlimDOMOptions;
recordCanvas?: boolean;
preserveWhiteSpace?: boolean;

1
typings/types.d.ts vendored
View File

@@ -88,4 +88,5 @@ export declare type SlimDOMOptions = Partial<{
headMetaVerification: boolean;
}>;
export declare type MaskTextFn = (text: string) => string;
export declare type MaskInputFn = (text: string) => string;
export declare type KeepIframeSrcFn = (src: string) => boolean;

9
typings/utils.d.ts vendored
View File

@@ -1,3 +1,10 @@
import { INode } from './types';
import { INode, MaskInputFn, MaskInputOptions } from './types';
export declare function isElement(n: Node | INode): n is Element;
export declare function isShadowRoot(n: Node): n is ShadowRoot;
export declare function maskInputValue({ maskInputOptions, tagName, type, value, maskInputFn, }: {
maskInputOptions: MaskInputOptions;
tagName: string;
type: string | number | boolean | null;
value: string | null;
maskInputFn?: MaskInputFn;
}): string;