@datadog/mobile-react-native-session-replay 2.4.4-alpha.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/DatadogSDKReactNativeSessionReplay.podspec +1 -1
  2. package/android/build.gradle +11 -1
  3. package/android/consumer-proguard-rules.pro +3 -0
  4. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt +32 -27
  5. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupport.kt +15 -4
  6. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ReactTextPropertiesResolver.kt +8 -10
  7. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/SessionReplayPrivacySettings.kt +90 -0
  8. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/SessionReplaySDKWrapper.kt +14 -0
  9. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/SessionReplayWrapper.kt +10 -0
  10. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/extensions/ReactDrawablesExt.kt +190 -0
  11. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactEditTextMapper.kt +84 -10
  12. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactNativeImageViewMapper.kt +106 -0
  13. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactViewGroupMapper.kt +4 -5
  14. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/resources/ReactDrawableCopier.kt +34 -0
  15. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/DrawableUtils.kt +10 -23
  16. package/android/src/newarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt +28 -4
  17. package/android/src/oldarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt +35 -3
  18. package/android/src/{main/kotlin/com/datadog/reactnative/sessionreplay/extensions/LongExt.kt → rn75/kotlin/com/datadog/reactnative/sessionreplay/extensions/LengthPercentageExt.kt} +6 -8
  19. package/android/src/rn75/kotlin/com/datadog/reactnative/sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +101 -0
  20. package/android/src/rn76/kotlin/com/datadog/reactnative/sessionreplay/extensions/LengthPercentageExt.kt +13 -0
  21. package/android/src/rn76/kotlin/com/datadog/reactnative/sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +101 -0
  22. package/android/src/rnlegacy/kotlin/com.datadog.reactnative.sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +88 -0
  23. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementationTest.kt +59 -50
  24. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupportTest.kt +7 -3
  25. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/ReactTextPropertiesResolverTest.kt +2 -7
  26. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactViewGroupMapperTest.kt +2 -7
  27. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/utils/DrawableUtilsTest.kt +2 -1
  28. package/ios/Sources/DdSessionReplay.mm +46 -4
  29. package/ios/Sources/DdSessionReplayImplementation.swift +83 -11
  30. package/ios/Sources/RCTTextViewRecorder.swift +1 -1
  31. package/lib/commonjs/SessionReplay.js +78 -10
  32. package/lib/commonjs/SessionReplay.js.map +1 -1
  33. package/lib/commonjs/index.js +18 -0
  34. package/lib/commonjs/index.js.map +1 -1
  35. package/lib/commonjs/specs/NativeDdSessionReplay.js.map +1 -1
  36. package/lib/module/SessionReplay.js +77 -9
  37. package/lib/module/SessionReplay.js.map +1 -1
  38. package/lib/module/index.js +2 -2
  39. package/lib/module/index.js.map +1 -1
  40. package/lib/module/specs/NativeDdSessionReplay.js.map +1 -1
  41. package/lib/typescript/SessionReplay.d.ts +73 -4
  42. package/lib/typescript/SessionReplay.d.ts.map +1 -1
  43. package/lib/typescript/index.d.ts +2 -2
  44. package/lib/typescript/index.d.ts.map +1 -1
  45. package/lib/typescript/nativeModulesTypes.d.ts +18 -3
  46. package/lib/typescript/nativeModulesTypes.d.ts.map +1 -1
  47. package/lib/typescript/specs/NativeDdSessionReplay.d.ts +15 -2
  48. package/lib/typescript/specs/NativeDdSessionReplay.d.ts.map +1 -1
  49. package/package.json +85 -84
  50. package/src/SessionReplay.ts +170 -23
  51. package/src/__tests__/SessionReplay.test.ts +94 -8
  52. package/src/index.ts +14 -2
  53. package/src/nativeModulesTypes.ts +27 -4
  54. package/src/specs/NativeDdSessionReplay.ts +21 -3
  55. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +0 -66
@@ -7,10 +7,23 @@ export interface Spec extends TurboModule {
7
7
  /**
8
8
  * Enable session replay and start recording session.
9
9
  * @param replaySampleRate: The sample rate applied for session replay.
10
- * @param defaultPrivacyLevel: The privacy level used for replay.
11
10
  * @param customEndpoint: Custom server url for sending replay data.
11
+ * @param imagePrivacyLevel: Defines the way images should be masked.
12
+ * @param touchPrivacyLevel: Defines the way user touches should be masked.
13
+ * @param textAndInputPrivacyLevel: Defines the way text and input should be masked.
14
+ * @param startRecordingImmediately: Whether the recording should start automatically when the feature is enabled.
15
+ * When `true`, the recording starts automatically; when `false` it doesn't,
16
+ * and the recording will need to be started manually. Default: `true`.
12
17
  */
13
- enable(replaySampleRate: number, defaultPrivacyLevel: string, customEndpoint: string): Promise<void>;
18
+ enable(replaySampleRate: number, customEndpoint: string, imagePrivacyLevel: string, touchPrivacyLevel: string, textAndInputPrivacyLevel: string, startRecordingImmediately: boolean): Promise<void>;
19
+ /**
20
+ * Manually start the recording of the current session.
21
+ */
22
+ startRecording(): Promise<void>;
23
+ /**
24
+ * Manually stop the recording of the current session.
25
+ */
26
+ stopRecording(): Promise<void>;
14
27
  }
15
28
  declare const _default: Spec | null;
16
29
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"NativeDdSessionReplay.d.ts","sourceRoot":"","sources":["../../../src/specs/NativeDdSessionReplay.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD;;GAEG;AACH,MAAM,WAAW,IAAK,SAAQ,WAAW;IACrC,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;IAEhC;;;;;OAKG;IACH,MAAM,CACF,gBAAgB,EAAE,MAAM,EACxB,mBAAmB,EAAE,MAAM,EAC3B,cAAc,EAAE,MAAM,GACvB,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;;AAGD,wBAAgE"}
1
+ {"version":3,"file":"NativeDdSessionReplay.d.ts","sourceRoot":"","sources":["../../../src/specs/NativeDdSessionReplay.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD;;GAEG;AACH,MAAM,WAAW,IAAK,SAAQ,WAAW;IACrC,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;IAEhC;;;;;;;;;;OAUG;IACH,MAAM,CACF,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,EACtB,iBAAiB,EAAE,MAAM,EACzB,iBAAiB,EAAE,MAAM,EACzB,wBAAwB,EAAE,MAAM,EAChC,yBAAyB,EAAE,OAAO,GACnC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;OAEG;IACH,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhC;;OAEG;IACH,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;;AAGD,wBAAgE"}
package/package.json CHANGED
@@ -1,90 +1,91 @@
1
1
  {
2
- "name": "@datadog/mobile-react-native-session-replay",
3
- "version": "2.4.4-alpha.0",
4
- "description": "A client-side React Native module to enable session replay with Datadog",
5
- "keywords": [
6
- "datadog",
7
- "react-native",
8
- "ios",
9
- "android"
10
- ],
11
- "author": "Datadog (https://github.com/DataDog)",
12
- "homepage": "https://github.com/DataDog/dd-sdk-reactnative#readme",
13
- "repository": "https://github.com/DataDog/dd-sdk-reactnative",
14
- "bugs": {
15
- "url": "https://github.com/DataDog/dd-sdk-reactnative/issues"
16
- },
17
- "license": "Apache-2.0",
18
- "main": "lib/commonjs/index",
19
- "files": [
20
- "src/**",
21
- "lib/**",
22
- "android/build.gradle",
23
- "android/detekt.yml",
24
- "android/gradle.properties",
25
- "android/src/**",
26
- "ios/Sources/**",
27
- "ios/DatadogSDKReactNativeSessionReplay.xcodeproj/project.xcworkspace/xcsharedata",
28
- "ios/DatadogSDKReactNativeSessionReplay.xcodeproj/project.xcworkspace/*.xcworkspacedata",
29
- "ios/DatadogSDKReactNativeSessionReplay.xcodeproj/*.pbxproj",
30
- "DatadogSDKReactNativeSessionReplay.podspec"
31
- ],
32
- "types": "lib/typescript/index.d.ts",
33
- "react-native": "src/index",
34
- "source": "src",
35
- "module": "lib/module/index",
36
- "publishConfig": {
37
- "access": "restricted"
38
- },
39
- "scripts": {
40
- "test": "jest",
41
- "lint": "eslint .",
42
- "prepare": "rm -rf lib && yarn bob build"
43
- },
44
- "peerDependencies": {
45
- "react": ">=16.13.1",
46
- "react-native": ">=0.63.4 <1.0"
47
- },
48
- "devDependencies": {
49
- "@testing-library/react-native": "7.0.2",
50
- "react-native-builder-bob": "0.26.0"
51
- },
52
- "jest": {
53
- "preset": "react-native",
54
- "moduleNameMapper": {
55
- "@datadog/mobile-react-native": "<rootDir>../core/src"
56
- },
57
- "modulePathIgnorePatterns": [
58
- "<rootDir>/lib/"
2
+ "name": "@datadog/mobile-react-native-session-replay",
3
+ "version": "2.6.0",
4
+ "description": "A client-side React Native module to enable session replay with Datadog",
5
+ "keywords": [
6
+ "datadog",
7
+ "react-native",
8
+ "ios",
9
+ "android"
59
10
  ],
60
- "setupFiles": [
61
- "./../../node_modules/react-native-gesture-handler/jestSetup.js",
62
- "./../../jest.setup.js"
11
+ "author": "Datadog (https://github.com/DataDog)",
12
+ "homepage": "https://github.com/DataDog/dd-sdk-reactnative#readme",
13
+ "repository": "https://github.com/DataDog/dd-sdk-reactnative",
14
+ "bugs": {
15
+ "url": "https://github.com/DataDog/dd-sdk-reactnative/issues"
16
+ },
17
+ "license": "Apache-2.0",
18
+ "main": "lib/commonjs/index",
19
+ "files": [
20
+ "src/**",
21
+ "lib/**",
22
+ "android/build.gradle",
23
+ "android/detekt.yml",
24
+ "android/gradle.properties",
25
+ "android/consumer-proguard-rules.pro",
26
+ "android/src/**",
27
+ "ios/Sources/**",
28
+ "ios/DatadogSDKReactNativeSessionReplay.xcodeproj/project.xcworkspace/xcsharedata",
29
+ "ios/DatadogSDKReactNativeSessionReplay.xcodeproj/project.xcworkspace/*.xcworkspacedata",
30
+ "ios/DatadogSDKReactNativeSessionReplay.xcodeproj/*.pbxproj",
31
+ "DatadogSDKReactNativeSessionReplay.podspec"
63
32
  ],
64
- "transformIgnorePatterns": [
65
- "jest-runner"
66
- ]
67
- },
68
- "react-native-builder-bob": {
33
+ "types": "lib/typescript/index.d.ts",
34
+ "react-native": "src/index",
69
35
  "source": "src",
70
- "output": "lib",
71
- "targets": [
72
- "commonjs",
73
- "module",
74
- [
75
- "typescript",
76
- {
77
- "tsc": "./../../node_modules/.bin/tsc"
36
+ "module": "lib/module/index",
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "scripts": {
41
+ "test": "jest",
42
+ "lint": "eslint .",
43
+ "prepare": "rm -rf lib && yarn bob build"
44
+ },
45
+ "peerDependencies": {
46
+ "react": ">=16.13.1",
47
+ "react-native": ">=0.63.4 <1.0"
48
+ },
49
+ "devDependencies": {
50
+ "@testing-library/react-native": "7.0.2",
51
+ "react-native-builder-bob": "0.26.0"
52
+ },
53
+ "jest": {
54
+ "preset": "react-native",
55
+ "moduleNameMapper": {
56
+ "@datadog/mobile-react-native": "<rootDir>../core/src"
57
+ },
58
+ "modulePathIgnorePatterns": [
59
+ "<rootDir>/lib/"
60
+ ],
61
+ "setupFiles": [
62
+ "./../../node_modules/react-native-gesture-handler/jestSetup.js",
63
+ "./../../jest.setup.js"
64
+ ],
65
+ "transformIgnorePatterns": [
66
+ "jest-runner"
67
+ ]
68
+ },
69
+ "react-native-builder-bob": {
70
+ "source": "src",
71
+ "output": "lib",
72
+ "targets": [
73
+ "commonjs",
74
+ "module",
75
+ [
76
+ "typescript",
77
+ {
78
+ "tsc": "./../../node_modules/.bin/tsc"
79
+ }
80
+ ]
81
+ ]
82
+ },
83
+ "codegenConfig": {
84
+ "name": "DdSDKReactNativeSessionReplay",
85
+ "type": "modules",
86
+ "jsSrcsDir": "./src/specs",
87
+ "android": {
88
+ "javaPackageName": "com.datadog.reactnative.sessionreplay"
78
89
  }
79
- ]
80
- ]
81
- },
82
- "codegenConfig": {
83
- "name": "DdSDKReactNativeSessionReplay",
84
- "type": "modules",
85
- "jsSrcsDir": "./src/specs",
86
- "android": {
87
- "javaPackageName": "com.datadog.reactnative.sessionreplay"
88
90
  }
89
- }
90
- }
91
+ }
@@ -12,6 +12,49 @@ export enum SessionReplayPrivacy {
12
12
  MASK_USER_INPUT = 'MASK_USER_INPUT'
13
13
  }
14
14
 
15
+ export enum ImagePrivacyLevel {
16
+ /**
17
+ * Only images that are bundled within the application will be recorded.
18
+ *
19
+ * On Android, all images larger than 100x100 dp will be masked, as we consider them non-bundled images.
20
+ */
21
+ MASK_NON_BUNDLED_ONLY = 'MASK_NON_BUNDLED_ONLY',
22
+ /**
23
+ * No images will be recorded.
24
+ */
25
+ MASK_ALL = 'MASK_ALL',
26
+ /**
27
+ * All images will be recorded, including the ones downloaded from the Internet or generated during the app runtime.
28
+ */
29
+ MASK_NONE = 'MASK_NONE'
30
+ }
31
+
32
+ export enum TouchPrivacyLevel {
33
+ /**
34
+ * Show all user touches.
35
+ */
36
+ SHOW = 'SHOW',
37
+ /**
38
+ * Hide all user touches.
39
+ */
40
+ HIDE = 'HIDE'
41
+ }
42
+
43
+ export enum TextAndInputPrivacyLevel {
44
+ /**
45
+ * Show all texts except sensitive inputs (e.g password fields).
46
+ */
47
+ MASK_SENSITIVE_INPUTS = 'MASK_SENSITIVE_INPUTS',
48
+ /**
49
+ * Mask all input fields (e.g text fields, switches, checkboxes).
50
+ */
51
+ MASK_ALL_INPUTS = 'MASK_ALL_INPUTS',
52
+ /**
53
+ * Mask all texts and inputs (e.g labels).
54
+ */
55
+ MASK_ALL = 'MASK_ALL'
56
+ }
57
+
15
58
  /**
16
59
  * The Session Replay configuration object.
17
60
  */
@@ -24,22 +67,70 @@ export interface SessionReplayConfiguration {
24
67
  * Default value is `20`.
25
68
  */
26
69
  replaySampleRate?: number;
70
+
27
71
  /**
28
- * Defines the way sensitive content (e.g. text) should be masked.
29
- *
30
- * Default `SessionReplayPrivacy.MASK`.
72
+ * Defines the way images should be masked (Default: `MASK_ALL`)
31
73
  */
32
- defaultPrivacyLevel?: SessionReplayPrivacy;
74
+ imagePrivacyLevel?: ImagePrivacyLevel;
75
+
76
+ /**
77
+ * Defines the way user touches (e.g tap) should be masked (Default: `HIDE`)
78
+ */
79
+ touchPrivacyLevel?: TouchPrivacyLevel;
80
+
81
+ /**
82
+ * Defines the way text and input (e.g text fields, checkboxes) should be masked (Default: `MASK_ALL`)
83
+ */
84
+ textAndInputPrivacyLevel?: TextAndInputPrivacyLevel;
85
+
33
86
  /**
34
87
  * Custom server url for sending replay data.
35
88
  */
36
89
  customEndpoint?: string;
90
+
91
+ /**
92
+ * Whether the recording should start automatically when the feature is enabled.
93
+ * When `true`, the recording starts automatically.
94
+ * when `false` it doesn't, and the recording will need to be started manually.
95
+ * Default: `true`.
96
+ */
97
+ startRecordingImmediately?: boolean;
98
+
99
+ /**
100
+ * Defines the way sensitive content (e.g. text) should be masked.
101
+ *
102
+ * Default `SessionReplayPrivacy.MASK`.
103
+ * @deprecated Use {@link imagePrivacyLevel}, {@link touchPrivacyLevel} and {@link textAndInputPrivacyLevel} instead.
104
+ * Note: setting this property (`defaultPrivacyLevel`) will override the individual privacy levels.
105
+ */
106
+ defaultPrivacyLevel?: SessionReplayPrivacy;
37
107
  }
38
108
 
39
- const DEFAULTS = {
109
+ type InternalBaseSessionReplayConfiguration = {
110
+ replaySampleRate: number;
111
+ customEndpoint: string;
112
+ startRecordingImmediately: boolean;
113
+ };
114
+
115
+ type InternalPrivacySessionReplayConfiguration = {
116
+ imagePrivacyLevel: ImagePrivacyLevel;
117
+ touchPrivacyLevel: TouchPrivacyLevel;
118
+ textAndInputPrivacyLevel: TextAndInputPrivacyLevel;
119
+ };
120
+
121
+ type InternalSessionReplayConfiguration = InternalBaseSessionReplayConfiguration &
122
+ InternalPrivacySessionReplayConfiguration;
123
+
124
+ const DEFAULTS: InternalSessionReplayConfiguration & {
125
+ defaultPrivacyLevel: SessionReplayPrivacy;
126
+ } = {
40
127
  replaySampleRate: 0,
41
128
  defaultPrivacyLevel: SessionReplayPrivacy.MASK,
42
- customEndpoint: ''
129
+ customEndpoint: '',
130
+ imagePrivacyLevel: ImagePrivacyLevel.MASK_ALL,
131
+ touchPrivacyLevel: TouchPrivacyLevel.HIDE,
132
+ textAndInputPrivacyLevel: TextAndInputPrivacyLevel.MASK_ALL,
133
+ startRecordingImmediately: true
43
134
  };
44
135
 
45
136
  export class SessionReplayWrapper {
@@ -49,33 +140,69 @@ export class SessionReplayWrapper {
49
140
 
50
141
  private buildConfiguration = (
51
142
  configuration?: SessionReplayConfiguration
52
- ): {
53
- replaySampleRate: number;
54
- defaultPrivacyLevel: SessionReplayPrivacy;
55
- customEndpoint: string;
56
- } => {
143
+ ): InternalSessionReplayConfiguration => {
57
144
  if (!configuration) {
58
145
  return DEFAULTS;
59
146
  }
60
147
  const {
61
148
  replaySampleRate,
62
- defaultPrivacyLevel,
63
- customEndpoint
149
+ customEndpoint,
150
+ startRecordingImmediately
64
151
  } = configuration;
65
- return {
152
+
153
+ const baseConfig: InternalBaseSessionReplayConfiguration = {
66
154
  replaySampleRate:
67
155
  replaySampleRate !== undefined
68
156
  ? replaySampleRate
69
157
  : DEFAULTS.replaySampleRate,
70
- defaultPrivacyLevel:
71
- defaultPrivacyLevel !== undefined
72
- ? defaultPrivacyLevel
73
- : DEFAULTS.defaultPrivacyLevel,
74
158
  customEndpoint:
75
159
  customEndpoint !== undefined
76
160
  ? customEndpoint
77
- : DEFAULTS.customEndpoint
161
+ : DEFAULTS.customEndpoint,
162
+ startRecordingImmediately:
163
+ startRecordingImmediately !== undefined
164
+ ? startRecordingImmediately
165
+ : DEFAULTS.startRecordingImmediately
166
+ };
167
+
168
+ const privacyConfig: InternalPrivacySessionReplayConfiguration = {
169
+ imagePrivacyLevel:
170
+ configuration.imagePrivacyLevel ?? DEFAULTS.imagePrivacyLevel,
171
+ touchPrivacyLevel:
172
+ configuration.touchPrivacyLevel ?? DEFAULTS.touchPrivacyLevel,
173
+ textAndInputPrivacyLevel:
174
+ configuration.textAndInputPrivacyLevel ??
175
+ DEFAULTS.textAndInputPrivacyLevel
78
176
  };
177
+
178
+ // Legacy Default Privacy Level property handling
179
+ if (configuration.defaultPrivacyLevel) {
180
+ switch (configuration.defaultPrivacyLevel) {
181
+ case SessionReplayPrivacy.MASK:
182
+ privacyConfig.imagePrivacyLevel =
183
+ ImagePrivacyLevel.MASK_ALL;
184
+ privacyConfig.touchPrivacyLevel = TouchPrivacyLevel.HIDE;
185
+ privacyConfig.textAndInputPrivacyLevel =
186
+ TextAndInputPrivacyLevel.MASK_ALL;
187
+ break;
188
+ case SessionReplayPrivacy.MASK_USER_INPUT:
189
+ privacyConfig.imagePrivacyLevel =
190
+ ImagePrivacyLevel.MASK_NONE;
191
+ privacyConfig.touchPrivacyLevel = TouchPrivacyLevel.HIDE;
192
+ privacyConfig.textAndInputPrivacyLevel =
193
+ TextAndInputPrivacyLevel.MASK_ALL_INPUTS;
194
+ break;
195
+ case SessionReplayPrivacy.ALLOW:
196
+ privacyConfig.imagePrivacyLevel =
197
+ ImagePrivacyLevel.MASK_NONE;
198
+ privacyConfig.touchPrivacyLevel = TouchPrivacyLevel.SHOW;
199
+ privacyConfig.textAndInputPrivacyLevel =
200
+ TextAndInputPrivacyLevel.MASK_SENSITIVE_INPUTS;
201
+ break;
202
+ }
203
+ }
204
+
205
+ return { ...baseConfig, ...privacyConfig };
79
206
  };
80
207
 
81
208
  /**
@@ -85,16 +212,36 @@ export class SessionReplayWrapper {
85
212
  enable = (configuration?: SessionReplayConfiguration): Promise<void> => {
86
213
  const {
87
214
  replaySampleRate,
88
- defaultPrivacyLevel,
89
- customEndpoint
215
+ customEndpoint,
216
+ imagePrivacyLevel,
217
+ touchPrivacyLevel,
218
+ textAndInputPrivacyLevel,
219
+ startRecordingImmediately
90
220
  } = this.buildConfiguration(configuration);
91
221
 
92
222
  return this.nativeSessionReplay.enable(
93
223
  replaySampleRate,
94
- defaultPrivacyLevel,
95
- customEndpoint
224
+ customEndpoint,
225
+ imagePrivacyLevel,
226
+ touchPrivacyLevel,
227
+ textAndInputPrivacyLevel,
228
+ startRecordingImmediately
96
229
  );
97
230
  };
231
+
232
+ /**
233
+ * Manually start the recording of the current session.
234
+ */
235
+ startRecording = (): Promise<void> => {
236
+ return this.nativeSessionReplay.startRecording();
237
+ };
238
+
239
+ /**
240
+ * Manually stop the recording of the current session.
241
+ */
242
+ stopRecording = (): Promise<void> => {
243
+ return this.nativeSessionReplay.stopRecording();
244
+ };
98
245
  }
99
246
 
100
247
  export const SessionReplay = new SessionReplayWrapper();
@@ -6,7 +6,21 @@
6
6
 
7
7
  import { NativeModules } from 'react-native';
8
8
 
9
- import { SessionReplay, SessionReplayPrivacy } from '../SessionReplay';
9
+ import {
10
+ ImagePrivacyLevel,
11
+ SessionReplay,
12
+ SessionReplayPrivacy,
13
+ TextAndInputPrivacyLevel,
14
+ TouchPrivacyLevel
15
+ } from '../SessionReplay';
16
+
17
+ function getRandomEnumValue<
18
+ T extends { [s: string]: T[keyof T] } | ArrayLike<T[keyof T]>
19
+ >(enumObj: T): T[keyof T] {
20
+ const values = Object.values(enumObj) as T[keyof T][]; // Get all enum values
21
+ const randomIndex = Math.floor(Math.random() * values.length); // Generate a random index
22
+ return values[randomIndex]; // Return the random value
23
+ }
10
24
 
11
25
  beforeEach(() => {
12
26
  NativeModules.DdSessionReplay.enable.mockClear();
@@ -19,12 +33,15 @@ describe('SessionReplay', () => {
19
33
 
20
34
  expect(NativeModules.DdSessionReplay.enable).toHaveBeenCalledWith(
21
35
  0,
22
- 'MASK',
23
- ''
36
+ '',
37
+ 'MASK_ALL',
38
+ 'HIDE',
39
+ 'MASK_ALL',
40
+ true
24
41
  );
25
42
  });
26
43
 
27
- it('calls native session replay with provided configuration', () => {
44
+ it('calls native session replay with provided configuration { w defaultPrivacyLevel = ALLOW }', () => {
28
45
  SessionReplay.enable({
29
46
  replaySampleRate: 100,
30
47
  defaultPrivacyLevel: SessionReplayPrivacy.ALLOW,
@@ -33,11 +50,77 @@ describe('SessionReplay', () => {
33
50
 
34
51
  expect(NativeModules.DdSessionReplay.enable).toHaveBeenCalledWith(
35
52
  100,
36
- 'ALLOW',
37
- 'https://session-replay.example.com'
53
+ 'https://session-replay.example.com',
54
+ 'MASK_NONE',
55
+ 'SHOW',
56
+ 'MASK_SENSITIVE_INPUTS',
57
+ true
58
+ );
59
+ });
60
+
61
+ it('calls native session replay with provided configuration { w defaultPrivacyLevel = MASK }', () => {
62
+ SessionReplay.enable({
63
+ replaySampleRate: 100,
64
+ defaultPrivacyLevel: SessionReplayPrivacy.MASK,
65
+ customEndpoint: 'https://session-replay.example.com'
66
+ });
67
+
68
+ expect(NativeModules.DdSessionReplay.enable).toHaveBeenCalledWith(
69
+ 100,
70
+ 'https://session-replay.example.com',
71
+ 'MASK_ALL',
72
+ 'HIDE',
73
+ 'MASK_ALL',
74
+ true
75
+ );
76
+ });
77
+
78
+ it('calls native session replay with provided configuration { w defaultPrivacyLevel = MASK_USER_INPUT }', () => {
79
+ SessionReplay.enable({
80
+ replaySampleRate: 100,
81
+ defaultPrivacyLevel: SessionReplayPrivacy.MASK_USER_INPUT,
82
+ customEndpoint: 'https://session-replay.example.com'
83
+ });
84
+
85
+ expect(NativeModules.DdSessionReplay.enable).toHaveBeenCalledWith(
86
+ 100,
87
+ 'https://session-replay.example.com',
88
+ 'MASK_NONE',
89
+ 'HIDE',
90
+ 'MASK_ALL_INPUTS',
91
+ true
38
92
  );
39
93
  });
40
94
 
95
+ it('calls native session replay with provided configuration { w random privacy levels }', () => {
96
+ const TIMES = 20;
97
+
98
+ const image = getRandomEnumValue(ImagePrivacyLevel);
99
+ const touch = getRandomEnumValue(TouchPrivacyLevel);
100
+ const textAndInput = getRandomEnumValue(TextAndInputPrivacyLevel);
101
+
102
+ for (let i = 0; i < TIMES; ++i) {
103
+ SessionReplay.enable({
104
+ replaySampleRate: 100,
105
+ customEndpoint: 'https://session-replay.example.com',
106
+ imagePrivacyLevel: image,
107
+ touchPrivacyLevel: touch,
108
+ textAndInputPrivacyLevel: textAndInput
109
+ });
110
+
111
+ expect(
112
+ NativeModules.DdSessionReplay.enable
113
+ ).toHaveBeenCalledWith(
114
+ 100,
115
+ 'https://session-replay.example.com',
116
+ image,
117
+ touch,
118
+ textAndInput,
119
+ true
120
+ );
121
+ }
122
+ });
123
+
41
124
  it('calls native session replay with edge cases in configuration', () => {
42
125
  SessionReplay.enable({
43
126
  replaySampleRate: 0,
@@ -46,8 +129,11 @@ describe('SessionReplay', () => {
46
129
 
47
130
  expect(NativeModules.DdSessionReplay.enable).toHaveBeenCalledWith(
48
131
  0,
49
- 'MASK',
50
- ''
132
+ '',
133
+ 'MASK_ALL',
134
+ 'HIDE',
135
+ 'MASK_ALL',
136
+ true
51
137
  );
52
138
  });
53
139
  });
package/src/index.ts CHANGED
@@ -5,8 +5,20 @@
5
5
  */
6
6
 
7
7
  import type { SessionReplayConfiguration } from './SessionReplay';
8
- import { SessionReplay, SessionReplayPrivacy } from './SessionReplay';
8
+ import {
9
+ SessionReplay,
10
+ SessionReplayPrivacy,
11
+ ImagePrivacyLevel,
12
+ TouchPrivacyLevel,
13
+ TextAndInputPrivacyLevel
14
+ } from './SessionReplay';
9
15
 
10
- export { SessionReplay, SessionReplayPrivacy };
16
+ export {
17
+ SessionReplay,
18
+ SessionReplayPrivacy,
19
+ ImagePrivacyLevel,
20
+ TouchPrivacyLevel,
21
+ TextAndInputPrivacyLevel
22
+ };
11
23
 
12
24
  export type { SessionReplayConfiguration };
@@ -11,7 +11,12 @@ import type { Spec as NativeDdSessionReplay } from './specs/NativeDdSessionRepla
11
11
  * As we cannot use enums or classes in the specs, we override methods using them here.
12
12
  */
13
13
 
14
- type PrivacyLevel = 'MASK' | 'MASK_USER_INPUT' | 'ALLOW';
14
+ type ImagePrivacyLevel = 'MASK_NON_BUNDLED_ONLY' | 'MASK_ALL' | 'MASK_NONE';
15
+ type TouchPrivacyLevel = 'SHOW' | 'HIDE';
16
+ type TextAndInputPrivacyLevel =
17
+ | 'MASK_SENSITIVE_INPUTS'
18
+ | 'MASK_ALL_INPUTS'
19
+ | 'MASK_ALL';
15
20
 
16
21
  /**
17
22
  * The entry point to use Datadog's Session Replay feature.
@@ -20,12 +25,30 @@ export interface NativeSessionReplayType extends NativeDdSessionReplay {
20
25
  /**
21
26
  * Enable session replay and start recording session.
22
27
  * @param replaySampleRate: The sample rate applied for session replay.
23
- * @param defaultPrivacyLevel: The privacy level used for replay.
24
28
  * @param customEndpoint: Custom server url for sending replay data.
29
+ * @param imagePrivacyLevel: Defines the way images should be masked.
30
+ * @param touchPrivacyLevel: Defines the way user touches should be masked.
31
+ * @param textAndInputPrivacyLevel: Defines the way text and input should be masked.
32
+ * @param startRecordingImmediately: Whether the recording should start automatically when the feature is enabled.
33
+ * When `true`, the recording starts automatically; when `false` it doesn't, and the recording will need
34
+ * to be started manually. Default: `true`.
25
35
  */
26
36
  enable(
27
37
  replaySampleRate: number,
28
- defaultPrivacyLevel: PrivacyLevel,
29
- customEndpoint: string
38
+ customEndpoint: string,
39
+ imagePrivacyLevel: ImagePrivacyLevel,
40
+ touchPrivacyLevel: TouchPrivacyLevel,
41
+ textAndInputPrivacyLevel: TextAndInputPrivacyLevel,
42
+ startRecordingImmediately: boolean
30
43
  ): Promise<void>;
44
+
45
+ /**
46
+ * Manually start the recording of the current session.
47
+ */
48
+ startRecording(): Promise<void>;
49
+
50
+ /**
51
+ * Manually stop the recording of the current session.
52
+ */
53
+ stopRecording(): Promise<void>;
31
54
  }