/
record.ts
126 lines (115 loc) · 4.23 KB
/
record.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import type { Context } from '@datadog/browser-core'
import { addTelemetryError, sendToExtension, timeStampNow } from '@datadog/browser-core'
import type { LifeCycle, RumConfiguration, ViewContexts } from '@datadog/browser-rum-core'
import type {
BrowserMutationData,
BrowserMutationPayload,
BrowserRecord,
InputData,
MediaInteractionData,
MousemoveData,
ScrollData,
StyleSheetRuleData,
ViewportResizeData,
} from '../../types'
import { RecordType, IncrementalSource } from '../../types'
import * as replayStats from '../replayStats'
import { assembleIncrementalSnapshot } from './assembly'
import { initObservers } from './observers'
import { createElementsScrollPositions } from './elementsScrollPositions'
import type { ShadowRootsController } from './shadowRootsController'
import { initShadowRootsController } from './shadowRootsController'
import type { InputCallback } from './observers'
import { startFullSnapshots } from './startFullSnapshots'
export interface RecordOptions {
emit?: (record: BrowserRecord) => void
configuration: RumConfiguration
lifeCycle: LifeCycle
viewContexts: ViewContexts
}
export interface RecordAPI {
stop: () => void
flushMutations: () => void
shadowRootsController: ShadowRootsController
}
export function record(options: RecordOptions): RecordAPI {
const { emit, configuration, lifeCycle } = options
// runtime checks for user options
if (!emit) {
throw new Error('emit function is required')
}
const emitAndComputeStats = (record: BrowserRecord) => {
emit(record)
sendToExtension('record', { record })
const view = options.viewContexts.findView()!
if (view) {
replayStats.addRecord(view.id)
} else {
addTelemetryError('[Record] no active view', { record } as unknown as Context)
}
}
const elementsScrollPositions = createElementsScrollPositions()
const mutationCb = (mutation: BrowserMutationPayload) => {
emitAndComputeStats(assembleIncrementalSnapshot<BrowserMutationData>(IncrementalSource.Mutation, mutation))
}
const inputCb: InputCallback = (s) =>
emitAndComputeStats(assembleIncrementalSnapshot<InputData>(IncrementalSource.Input, s))
const shadowRootsController = initShadowRootsController(configuration, { mutationCb, inputCb })
const { stop: stopFullSnapshots } = startFullSnapshots(
elementsScrollPositions,
shadowRootsController,
lifeCycle,
configuration,
flushMutations,
(records) => records.forEach((record) => emitAndComputeStats(record))
)
function flushMutations() {
shadowRootsController.flush()
flushMutationsFromObservers()
}
const { stop: stopObservers, flush: flushMutationsFromObservers } = initObservers(configuration, {
lifeCycle: options.lifeCycle,
configuration,
elementsScrollPositions,
inputCb,
mediaInteractionCb: (p) =>
emitAndComputeStats(assembleIncrementalSnapshot<MediaInteractionData>(IncrementalSource.MediaInteraction, p)),
mouseInteractionCb: (mouseInteractionRecord) => emitAndComputeStats(mouseInteractionRecord),
mousemoveCb: (positions, source) =>
emitAndComputeStats(assembleIncrementalSnapshot<MousemoveData>(source, { positions })),
mutationCb,
scrollCb: (p) => emitAndComputeStats(assembleIncrementalSnapshot<ScrollData>(IncrementalSource.Scroll, p)),
styleSheetCb: (r) =>
emitAndComputeStats(assembleIncrementalSnapshot<StyleSheetRuleData>(IncrementalSource.StyleSheetRule, r)),
viewportResizeCb: (d) =>
emitAndComputeStats(assembleIncrementalSnapshot<ViewportResizeData>(IncrementalSource.ViewportResize, d)),
frustrationCb: (frustrationRecord) => emitAndComputeStats(frustrationRecord),
focusCb: (data) =>
emitAndComputeStats({
data,
type: RecordType.Focus,
timestamp: timeStampNow(),
}),
visualViewportResizeCb: (data) => {
emitAndComputeStats({
data,
type: RecordType.VisualViewport,
timestamp: timeStampNow(),
})
},
viewEndCb: (viewEndRecord) => {
flushMutations()
emitAndComputeStats(viewEndRecord)
},
shadowRootsController,
})
return {
stop: () => {
shadowRootsController.stop()
stopObservers()
stopFullSnapshots()
},
flushMutations,
shadowRootsController,
}
}