diff --git a/src/snapshot.ts b/src/snapshot.ts index 7e5da9e3..27b882be 100644 --- a/src/snapshot.ts +++ b/src/snapshot.ts @@ -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, diff --git a/src/types.ts b/src/types.ts index baa52211..ffeef690 100644 --- a/src/types.ts +++ b/src/types.ts @@ -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; diff --git a/src/utils.ts b/src/utils.ts index df6cf20b..ed5c5f75 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -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; +} diff --git a/typings/snapshot.d.ts b/typings/snapshot.d.ts index 6db21b71..9ccb147a 100644 --- a/typings/snapshot.d.ts +++ b/typings/snapshot.d.ts @@ -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; diff --git a/typings/types.d.ts b/typings/types.d.ts index 93267fa2..8524801c 100644 --- a/typings/types.d.ts +++ b/typings/types.d.ts @@ -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; diff --git a/typings/utils.d.ts b/typings/utils.d.ts index a896748e..dfb1b70a 100644 --- a/typings/utils.d.ts +++ b/typings/utils.d.ts @@ -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;