impl sequential id plugins (#819)

Also introduce a new kind of plugin: event processor
This commit is contained in:
yz-yu
2022-02-03 20:00:47 +08:00
committed by GitHub
parent ab9fed0c7e
commit 14ea6362ba
8 changed files with 125 additions and 17 deletions

View File

@@ -93,6 +93,16 @@ const baseConfigs = [
name: 'rrwebConsoleReplay', name: 'rrwebConsoleReplay',
pathFn: toPluginPath('console', 'replay'), pathFn: toPluginPath('console', 'replay'),
}, },
{
input: './src/plugins/sequential-id/record/index.ts',
name: 'rrwebSequentialIdRecord',
pathFn: toPluginPath('sequential-id', 'record'),
},
{
input: './src/plugins/sequential-id/replay/index.ts',
name: 'rrwebSequentialIdReplay',
pathFn: toPluginPath('sequential-id', 'replay'),
},
]; ];
let configs = []; let configs = [];

View File

@@ -0,0 +1,31 @@
import { RecordPlugin } from '../../../types';
export type SequentialIdOptions = {
key: string;
};
const defaultOptions: SequentialIdOptions = {
key: '_sid',
};
export const PLUGIN_NAME = 'rrweb/sequential-id@1';
export const getRecordSequentialIdPlugin: (
options?: Partial<SequentialIdOptions>,
) => RecordPlugin = (options) => {
const _options = options
? Object.assign({}, defaultOptions, options)
: defaultOptions;
let id = 0;
return {
name: PLUGIN_NAME,
eventProcessor(event) {
Object.assign(event, {
[_options.key]: ++id,
});
return event;
},
options: _options,
};
};

View File

@@ -0,0 +1,39 @@
import type { SequentialIdOptions } from '../record';
import { ReplayPlugin, eventWithTime } from '../../../types';
type Options = SequentialIdOptions & {
warnOnMissingId: boolean;
};
const defaultOptions: Options = {
key: '_sid',
warnOnMissingId: true,
};
export const getReplaySequentialIdPlugin: (
options?: Partial<Options>,
) => ReplayPlugin = (options) => {
const { key, warnOnMissingId } = options
? Object.assign({}, defaultOptions, options)
: defaultOptions;
let currentId = 1;
return {
handler(event: eventWithTime) {
if (key in event) {
const id = ((event as unknown) as Record<string, number>)[key];
if (id !== currentId) {
console.error(
`[sequential-id-plugin]: expect to get an id with value "${currentId}", but got "${id}"`,
);
} else {
currentId++;
}
} else if (warnOnMissingId) {
console.warn(
`[sequential-id-plugin]: failed to get id in key: "${key}"`,
);
}
},
};
};

View File

@@ -122,6 +122,17 @@ function record<T = eventWithTime>(
let lastFullSnapshotEvent: eventWithTime; let lastFullSnapshotEvent: eventWithTime;
let incrementalSnapshotCount = 0; let incrementalSnapshotCount = 0;
const eventProcessor = (e: eventWithTime): T => {
for (const plugin of plugins || []) {
if (plugin.eventProcessor) {
e = plugin.eventProcessor(e);
}
}
if (packFn) {
e = (packFn(e) as unknown) as eventWithTime;
}
return (e as unknown) as T;
};
wrappedEmit = (e: eventWithTime, isCheckout?: boolean) => { wrappedEmit = (e: eventWithTime, isCheckout?: boolean) => {
if ( if (
mutationBuffers[0]?.isFrozen() && mutationBuffers[0]?.isFrozen() &&
@@ -136,7 +147,7 @@ function record<T = eventWithTime>(
mutationBuffers.forEach((buf) => buf.unfreeze()); mutationBuffers.forEach((buf) => buf.unfreeze());
} }
emit(((packFn ? packFn(e) : e) as unknown) as T, isCheckout); emit(eventProcessor(e), isCheckout);
if (e.type === EventType.FullSnapshot) { if (e.type === EventType.FullSnapshot) {
lastFullSnapshotEvent = e; lastFullSnapshotEvent = e;
incrementalSnapshotCount = 0; incrementalSnapshotCount = 0;
@@ -417,8 +428,10 @@ function record<T = eventWithTime>(
shadowDomManager, shadowDomManager,
canvasManager, canvasManager,
plugins: plugins:
plugins?.map((p) => ({ plugins
observer: p.observer, ?.filter((p) => p.observer)
?.map((p) => ({
observer: p.observer!,
options: p.options, options: p.options,
callback: (payload: object) => callback: (payload: object) =>
wrappedEmit( wrappedEmit(

View File

@@ -205,7 +205,8 @@ export type SamplingStrategy = Partial<{
export type RecordPlugin<TOptions = unknown> = { export type RecordPlugin<TOptions = unknown> = {
name: string; name: string;
observer: (cb: Function, win: IWindow, options: TOptions) => listenerHandler; observer?: (cb: Function, win: IWindow, options: TOptions) => listenerHandler;
eventProcessor?: <TExtend>(event: eventWithTime) => eventWithTime & TExtend;
options: TOptions; options: TOptions;
}; };

View File

@@ -0,0 +1,6 @@
import { RecordPlugin } from '../../../types';
export declare type SequentialIdOptions = {
key: string;
};
export declare const PLUGIN_NAME = "rrweb/sequential-id@1";
export declare const getRecordSequentialIdPlugin: (options?: Partial<SequentialIdOptions>) => RecordPlugin;

View File

@@ -0,0 +1,7 @@
import type { SequentialIdOptions } from '../record';
import { ReplayPlugin } from '../../../types';
declare type Options = SequentialIdOptions & {
warnOnMissingId: boolean;
};
export declare const getReplaySequentialIdPlugin: (options?: Partial<Options>) => ReplayPlugin;
export {};

View File

@@ -128,7 +128,8 @@ export declare type SamplingStrategy = Partial<{
}>; }>;
export declare type RecordPlugin<TOptions = unknown> = { export declare type RecordPlugin<TOptions = unknown> = {
name: string; name: string;
observer: (cb: Function, win: IWindow, options: TOptions) => listenerHandler; observer?: (cb: Function, win: IWindow, options: TOptions) => listenerHandler;
eventProcessor?: <TExtend>(event: eventWithTime) => eventWithTime & TExtend;
options: TOptions; options: TOptions;
}; };
export declare type recordOptions<T> = { export declare type recordOptions<T> = {