diff --git a/src/record/index.ts b/src/record/index.ts index c769a4d4..805782dc 100644 --- a/src/record/index.ts +++ b/src/record/index.ts @@ -34,6 +34,7 @@ function record(options: recordOptions = {}): listenerHandler | undefined { ignoreClass = 'rr-ignore', inlineStylesheet = true, maskAllInputs = false, + hooks, } = options; // runtime checks for user options if (!emit) { @@ -114,72 +115,75 @@ function record(options: recordOptions = {}): listenerHandler | undefined { takeFullSnapshot(); handlers.push( - initObservers({ - mutationCb: m => - wrappedEmit( - wrapEvent({ - type: EventType.IncrementalSnapshot, - data: { - source: IncrementalSource.Mutation, - ...m, - }, - }), - ), - mousemoveCb: (positions, source) => - wrappedEmit( - wrapEvent({ - type: EventType.IncrementalSnapshot, - data: { - source, - positions, - }, - }), - ), - mouseInteractionCb: d => - wrappedEmit( - wrapEvent({ - type: EventType.IncrementalSnapshot, - data: { - source: IncrementalSource.MouseInteraction, - ...d, - }, - }), - ), - scrollCb: p => - wrappedEmit( - wrapEvent({ - type: EventType.IncrementalSnapshot, - data: { - source: IncrementalSource.Scroll, - ...p, - }, - }), - ), - viewportResizeCb: d => - wrappedEmit( - wrapEvent({ - type: EventType.IncrementalSnapshot, - data: { - source: IncrementalSource.ViewportResize, - ...d, - }, - }), - ), - inputCb: v => - wrappedEmit( - wrapEvent({ - type: EventType.IncrementalSnapshot, - data: { - source: IncrementalSource.Input, - ...v, - }, - }), - ), - blockClass, - ignoreClass, - maskAllInputs, - inlineStylesheet, - }), + initObservers( + { + mutationCb: m => + wrappedEmit( + wrapEvent({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + ...m, + }, + }), + ), + mousemoveCb: (positions, source) => + wrappedEmit( + wrapEvent({ + type: EventType.IncrementalSnapshot, + data: { + source, + positions, + }, + }), + ), + mouseInteractionCb: d => + wrappedEmit( + wrapEvent({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.MouseInteraction, + ...d, + }, + }), + ), + scrollCb: p => + wrappedEmit( + wrapEvent({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Scroll, + ...p, + }, + }), + ), + viewportResizeCb: d => + wrappedEmit( + wrapEvent({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.ViewportResize, + ...d, + }, + }), + ), + inputCb: v => + wrappedEmit( + wrapEvent({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Input, + ...v, + }, + }), + ), + blockClass, + ignoreClass, + maskAllInputs, + inlineStylesheet, + }, + hooks, + ), ); }; if ( diff --git a/src/record/observer.ts b/src/record/observer.ts index 24cfa71a..b282d1a9 100644 --- a/src/record/observer.ts +++ b/src/record/observer.ts @@ -29,6 +29,8 @@ import { attributeCursor, blockClass, IncrementalSource, + hooksParam, + Arguments, } from '../types'; import { deepDelete, isParentRemoved, isAncestorInSet } from './collection'; @@ -506,7 +508,58 @@ function initInputObserver( }; } -export default function initObservers(o: observerParam): listenerHandler { +function mergeHooks(o: observerParam, hooks: hooksParam) { + const { + mutationCb, + mousemoveCb, + mouseInteractionCb, + scrollCb, + viewportResizeCb, + inputCb, + } = o; + o.mutationCb = (...p: Arguments) => { + if (hooks.mutation) { + hooks.mutation(...p); + } + mutationCb(...p); + }; + o.mousemoveCb = (...p: Arguments) => { + if (hooks.mousemove) { + hooks.mousemove(...p); + } + mousemoveCb(...p); + }; + o.mouseInteractionCb = (...p: Arguments) => { + if (hooks.mouseInteraction) { + hooks.mouseInteraction(...p); + } + mouseInteractionCb(...p); + }; + o.scrollCb = (...p: Arguments) => { + if (hooks.scroll) { + hooks.scroll(...p); + } + scrollCb(...p); + }; + o.viewportResizeCb = (...p: Arguments) => { + if (hooks.viewportResize) { + hooks.viewportResize(...p); + } + viewportResizeCb(...p); + }; + o.inputCb = (...p: Arguments) => { + if (hooks.input) { + hooks.input(...p); + } + inputCb(...p); + }; +} + +export default function initObservers( + o: observerParam, + hooks: hooksParam = {}, +): listenerHandler { + mergeHooks(o, hooks); const mutationObserver = initMutationObserver( o.mutationCb, o.blockClass, diff --git a/src/types.ts b/src/types.ts index de82467c..dd39cc62 100644 --- a/src/types.ts +++ b/src/types.ts @@ -119,6 +119,7 @@ export type recordOptions = { ignoreClass?: string; maskAllInputs?: boolean; inlineStylesheet?: boolean; + hooks?: hooksParam; }; export type observerParam = { @@ -134,6 +135,15 @@ export type observerParam = { inlineStylesheet: boolean; }; +export type hooksParam = { + mutation?: mutationCallBack; + mousemove?: mousemoveCallBack; + mouseInteraction?: mouseInteractionCallBack; + scroll?: scrollCallback; + viewportResize?: viewportResizeCallback; + input?: inputCallback; +}; + export type textCursor = { node: Node; value: string | null; @@ -285,6 +295,10 @@ export type Emitter = { emit(type: string, event?: unknown): void; }; +export type Arguments = T extends (...payload: infer U) => unknown + ? U + : unknown; + export enum ReplayerEvents { Start = 'start', Pause = 'pause', diff --git a/typings/record/observer.d.ts b/typings/record/observer.d.ts index 068a08f1..4c67f319 100644 --- a/typings/record/observer.d.ts +++ b/typings/record/observer.d.ts @@ -1,2 +1,2 @@ -import { observerParam, listenerHandler } from '../types'; -export default function initObservers(o: observerParam): listenerHandler; +import { observerParam, listenerHandler, hooksParam } from '../types'; +export default function initObservers(o: observerParam, hooks?: hooksParam): listenerHandler; diff --git a/typings/types.d.ts b/typings/types.d.ts index 13ab3d9c..fc98c1a4 100644 --- a/typings/types.d.ts +++ b/typings/types.d.ts @@ -88,6 +88,7 @@ export declare type recordOptions = { ignoreClass?: string; maskAllInputs?: boolean; inlineStylesheet?: boolean; + hooks?: hooksParam; }; export declare type observerParam = { mutationCb: mutationCallBack; @@ -101,6 +102,14 @@ export declare type observerParam = { maskAllInputs: boolean; inlineStylesheet: boolean; }; +export declare type hooksParam = { + mutation?: mutationCallBack; + mousemove?: mousemoveCallBack; + mouseInteraction?: mouseInteractionCallBack; + scroll?: scrollCallback; + viewportResize?: viewportResizeCallback; + input?: inputCallback; +}; export declare type textCursor = { node: Node; value: string | null; @@ -225,6 +234,7 @@ export declare type Emitter = { on(type: string, handler: Handler): void; emit(type: string, event?: unknown): void; }; +export declare type Arguments = T extends (...payload: infer U) => unknown ? U : unknown; export declare enum ReplayerEvents { Start = "start", Pause = "pause",