Skip to content

Commit 9f3fe95

Browse files
author
John Pham
authored
John/hig-1984-pull-in-rrweb-sequential-id-pr (#65)
* Add sequential IDs * rrweb-io/rrweb#840 * bump version
1 parent d6143d2 commit 9f3fe95

File tree

6 files changed

+106
-18
lines changed

6 files changed

+106
-18
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@highlight-run/rrweb",
3-
"version": "1.1.11",
3+
"version": "1.1.12",
44
"description": "record and replay the web",
55
"scripts": {
66
"test": "npm run bundle:browser && cross-env TS_NODE_CACHE=false TS_NODE_FILES=true mocha -r ts-node/register -r ignore-styles -r jsdom-global/register test/**.test.ts",
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { RecordPlugin } from '../../../types';
2+
3+
export type SequentialIdOptions = {
4+
key: string;
5+
};
6+
7+
const defaultOptions: SequentialIdOptions = {
8+
key: '_sid',
9+
};
10+
11+
export const PLUGIN_NAME = 'rrweb/sequential-id@1';
12+
13+
export const getRecordSequentialIdPlugin: (
14+
options?: Partial<SequentialIdOptions>,
15+
) => RecordPlugin = (options) => {
16+
const _options = options
17+
? Object.assign({}, defaultOptions, options)
18+
: defaultOptions;
19+
let id = 0;
20+
21+
return {
22+
name: PLUGIN_NAME,
23+
eventProcessor(event) {
24+
Object.assign(event, {
25+
[_options.key]: ++id,
26+
});
27+
return event;
28+
},
29+
options: _options,
30+
};
31+
};
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type { SequentialIdOptions } from '../record';
2+
import { ReplayPlugin, eventWithTime } from '../../../types';
3+
4+
type Options = SequentialIdOptions & {
5+
warnOnMissingId: boolean;
6+
};
7+
8+
const defaultOptions: Options = {
9+
key: '_sid',
10+
warnOnMissingId: true,
11+
};
12+
13+
export const getReplaySequentialIdPlugin: (
14+
options?: Partial<Options>,
15+
) => ReplayPlugin = (options) => {
16+
const { key, warnOnMissingId } = options
17+
? Object.assign({}, defaultOptions, options)
18+
: defaultOptions;
19+
let currentId = 1;
20+
21+
return {
22+
handler(event: eventWithTime) {
23+
if (key in event) {
24+
const id = ((event as unknown) as Record<string, number>)[key];
25+
if (id !== currentId) {
26+
console.error(
27+
`[sequential-id-plugin]: expect to get an id with value "${currentId}", but got "${id}"`,
28+
);
29+
} else {
30+
currentId++;
31+
}
32+
} else if (warnOnMissingId) {
33+
console.warn(
34+
`[sequential-id-plugin]: failed to get id in key: "${key}"`,
35+
);
36+
}
37+
},
38+
};
39+
};

src/record/index.ts

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,17 @@ function record<T = eventWithTime>(
122122

123123
let lastFullSnapshotEvent: eventWithTime;
124124
let incrementalSnapshotCount = 0;
125+
const eventProcessor = (e: eventWithTime): T => {
126+
for (const plugin of plugins || []) {
127+
if (plugin.eventProcessor) {
128+
e = plugin.eventProcessor(e);
129+
}
130+
}
131+
if (packFn) {
132+
e = (packFn(e) as unknown) as eventWithTime;
133+
}
134+
return (e as unknown) as T;
135+
};
125136
wrappedEmit = (e: eventWithTime, isCheckout?: boolean) => {
126137
if (
127138
mutationBuffers[0]?.isFrozen() &&
@@ -136,7 +147,7 @@ function record<T = eventWithTime>(
136147
mutationBuffers.forEach((buf) => buf.unfreeze());
137148
}
138149

139-
emit(((packFn ? packFn(e) : e) as unknown) as T, isCheckout);
150+
emit(eventProcessor(e), isCheckout);
140151
if (e.type === EventType.FullSnapshot) {
141152
lastFullSnapshotEvent = e;
142153
incrementalSnapshotCount = 0;
@@ -417,20 +428,22 @@ function record<T = eventWithTime>(
417428
shadowDomManager,
418429
canvasManager,
419430
plugins:
420-
plugins?.map((p) => ({
421-
observer: p.observer,
422-
options: p.options,
423-
callback: (payload: object) =>
424-
wrappedEmit(
425-
wrapEvent({
426-
type: EventType.Plugin,
427-
data: {
428-
plugin: p.name,
429-
payload,
430-
},
431-
}),
432-
),
433-
})) || [],
431+
plugins
432+
?.filter((p) => p.observer)
433+
?.map((p) => ({
434+
observer: p.observer!,
435+
options: p.options,
436+
callback: (payload: object) =>
437+
wrappedEmit(
438+
wrapEvent({
439+
type: EventType.Plugin,
440+
data: {
441+
plugin: p.name,
442+
payload,
443+
},
444+
}),
445+
),
446+
})) || [],
434447
enableStrictPrivacy,
435448
},
436449
hooks,

src/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,8 @@ export type SamplingStrategy = Partial<{
213213

214214
export type RecordPlugin<TOptions = unknown> = {
215215
name: string;
216-
observer: (cb: Function, win: IWindow, options: TOptions) => listenerHandler;
216+
observer?: (cb: Function, win: IWindow, options: TOptions) => listenerHandler;
217+
eventProcessor?: <TExtend>(event: eventWithTime) => eventWithTime & TExtend;
217218
options: TOptions;
218219
};
219220

src/utils.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,11 @@ export function isBlocked(node: Node | null, blockClass: blockClass): boolean {
232232
if (node.nodeType === node.ELEMENT_NODE) {
233233
let needBlock = false;
234234
if (typeof blockClass === 'string') {
235-
needBlock = (node as HTMLElement).classList.contains(blockClass);
235+
if ((node as HTMLElement).closest !== undefined) {
236+
return (node as HTMLElement).closest('.' + blockClass) !== null;
237+
} else {
238+
needBlock = (node as HTMLElement).classList.contains(blockClass);
239+
}
236240
} else {
237241
(node as HTMLElement).classList.forEach((className) => {
238242
if (blockClass.test(className)) {

0 commit comments

Comments
 (0)