1
1
import { getCurrentHub } from '@sentry/core' ;
2
2
import type { BrowserClientReplayOptions , Integration } from '@sentry/types' ;
3
+ import { dropUndefinedKeys } from '@sentry/utils' ;
3
4
4
5
import { DEFAULT_FLUSH_MAX_DELAY , DEFAULT_FLUSH_MIN_DELAY , MASK_ALL_TEXT_SELECTOR } from './constants' ;
5
6
import { ReplayContainer } from './replay' ;
@@ -11,6 +12,9 @@ const MEDIA_SELECTORS = 'img,image,svg,path,rect,area,video,object,picture,embed
11
12
12
13
let _initialized = false ;
13
14
15
+ type InitialReplayPluginOptions = Omit < ReplayPluginOptions , 'sessionSampleRate' | 'errorSampleRate' > &
16
+ Partial < Pick < ReplayPluginOptions , 'sessionSampleRate' | 'errorSampleRate' > > ;
17
+
14
18
/**
15
19
* The main replay integration class, to be passed to `init({ integrations: [] })`.
16
20
*/
@@ -30,7 +34,14 @@ export class Replay implements Integration {
30
34
*/
31
35
private readonly _recordingOptions : RecordingOptions ;
32
36
33
- private readonly _options : ReplayPluginOptions ;
37
+ /**
38
+ * Initial options passed to the replay integration, merged with default values.
39
+ * Note: `sessionSampleRate` and `errorSampleRate` are not required here, as they
40
+ * can only be finally set when setupOnce() is called.
41
+ *
42
+ * @private
43
+ */
44
+ private readonly _initialOptions : InitialReplayPluginOptions ;
34
45
35
46
private _replay ?: ReplayContainer ;
36
47
@@ -92,12 +103,12 @@ export class Replay implements Integration {
92
103
collectFonts : true ,
93
104
} ;
94
105
95
- this . _options = {
106
+ this . _initialOptions = {
96
107
flushMinDelay,
97
108
flushMaxDelay,
98
109
stickySession,
99
- sessionSampleRate : 0 ,
100
- errorSampleRate : 0 ,
110
+ sessionSampleRate,
111
+ errorSampleRate,
101
112
useCompression,
102
113
maskAllText : typeof maskAllText === 'boolean' ? maskAllText : ! maskTextSelector ,
103
114
blockAllMedia,
@@ -113,7 +124,7 @@ Instead, configure \`replaysSessionSampleRate\` directly in the SDK init options
113
124
Sentry.init({ replaysSessionSampleRate: ${ sessionSampleRate } })` ,
114
125
) ;
115
126
116
- this . _options . sessionSampleRate = sessionSampleRate ;
127
+ this . _initialOptions . sessionSampleRate = sessionSampleRate ;
117
128
}
118
129
119
130
if ( typeof errorSampleRate === 'number' ) {
@@ -125,17 +136,17 @@ Instead, configure \`replaysOnErrorSampleRate\` directly in the SDK init options
125
136
Sentry.init({ replaysOnErrorSampleRate: ${ errorSampleRate } })` ,
126
137
) ;
127
138
128
- this . _options . errorSampleRate = errorSampleRate ;
139
+ this . _initialOptions . errorSampleRate = errorSampleRate ;
129
140
}
130
141
131
- if ( this . _options . maskAllText ) {
142
+ if ( this . _initialOptions . maskAllText ) {
132
143
// `maskAllText` is a more user friendly option to configure
133
144
// `maskTextSelector`. This means that all nodes will have their text
134
145
// content masked.
135
146
this . _recordingOptions . maskTextSelector = MASK_ALL_TEXT_SELECTOR ;
136
147
}
137
148
138
- if ( this . _options . blockAllMedia ) {
149
+ if ( this . _initialOptions . blockAllMedia ) {
139
150
// `blockAllMedia` is a more user friendly option to configure blocking
140
151
// embedded media elements
141
152
this . _recordingOptions . blockSelector = ! this . _recordingOptions . blockSelector
@@ -221,25 +232,47 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
221
232
/** Setup the integration. */
222
233
private _setup ( ) : void {
223
234
// Client is not available in constructor, so we need to wait until setupOnce
224
- this . _loadReplayOptionsFromClient ( ) ;
235
+ const finalOptions = loadReplayOptionsFromClient ( this . _initialOptions ) ;
225
236
226
237
this . _replay = new ReplayContainer ( {
227
- options : this . _options ,
238
+ options : finalOptions ,
228
239
recordingOptions : this . _recordingOptions ,
229
240
} ) ;
230
241
}
242
+ }
231
243
232
- /** Parse Replay-related options from SDK options */
233
- private _loadReplayOptionsFromClient ( ) : void {
234
- const client = getCurrentHub ( ) . getClient ( ) ;
235
- const opt = client && ( client . getOptions ( ) as BrowserClientReplayOptions | undefined ) ;
244
+ /** Parse Replay-related options from SDK options */
245
+ function loadReplayOptionsFromClient ( initialOptions : InitialReplayPluginOptions ) : ReplayPluginOptions {
246
+ const client = getCurrentHub ( ) . getClient ( ) ;
247
+ const opt = client && ( client . getOptions ( ) as BrowserClientReplayOptions ) ;
236
248
237
- if ( opt && typeof opt . replaysSessionSampleRate === 'number' ) {
238
- this . _options . sessionSampleRate = opt . replaysSessionSampleRate ;
239
- }
249
+ const finalOptions = { sessionSampleRate : 0 , errorSampleRate : 0 , ...dropUndefinedKeys ( initialOptions ) } ;
240
250
241
- if ( opt && typeof opt . replaysOnErrorSampleRate === 'number' ) {
242
- this . _options . errorSampleRate = opt . replaysOnErrorSampleRate ;
243
- }
251
+ if ( ! opt ) {
252
+ // eslint-disable-next-line no-console
253
+ console . warn ( 'SDK client is not available.' ) ;
254
+ return finalOptions ;
244
255
}
256
+
257
+ if (
258
+ initialOptions . sessionSampleRate == null && // TODO remove once deprecated rates are removed
259
+ initialOptions . errorSampleRate == null && // TODO remove once deprecated rates are removed
260
+ opt . replaysSessionSampleRate == null &&
261
+ opt . replaysOnErrorSampleRate == null
262
+ ) {
263
+ // eslint-disable-next-line no-console
264
+ console . warn (
265
+ 'Replay is disabled because neither `replaysSessionSampleRate` nor `replaysOnErrorSampleRate` are set.' ,
266
+ ) ;
267
+ }
268
+
269
+ if ( typeof opt . replaysSessionSampleRate === 'number' ) {
270
+ finalOptions . sessionSampleRate = opt . replaysSessionSampleRate ;
271
+ }
272
+
273
+ if ( typeof opt . replaysOnErrorSampleRate === 'number' ) {
274
+ finalOptions . errorSampleRate = opt . replaysOnErrorSampleRate ;
275
+ }
276
+
277
+ return finalOptions ;
245
278
}
0 commit comments