@datadog/mobile-react-native-session-replay 2.12.4 → 2.13.1

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 (60) hide show
  1. package/DatadogSDKReactNativeSessionReplay.podspec +6 -2
  2. package/README.md +90 -1
  3. package/android/build.gradle +3 -2
  4. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt +2 -2
  5. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeInternalCallback.kt +121 -0
  6. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupport.kt +5 -1
  7. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactViewGroupMapper.kt +1 -0
  8. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/SvgViewMapper.kt +145 -0
  9. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/views/DdPrivacyView.kt +10 -0
  10. package/android/src/newarch/kotlin/com/datadog/reactnative/sessionreplay/views/DdPrivacyViewManager.kt +13 -0
  11. package/android/src/oldarch/kotlin/com/datadog/reactnative/sessionreplay/views/DdPrivacyViewManager.kt +13 -0
  12. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementationTest.kt +8 -0
  13. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupportTest.kt +21 -5
  14. package/assets/assets.bin +0 -0
  15. package/assets/assets.json +1 -0
  16. package/ios/Sources/DdPrivacyViewFabric.h +28 -0
  17. package/ios/Sources/DdPrivacyViewFabric.mm +22 -4
  18. package/ios/Sources/DdPrivacyViewPaper.m +24 -0
  19. package/ios/Sources/DdSessionReplayImplementation.swift +34 -6
  20. package/ios/Sources/SvgViewRecorder.swift +236 -0
  21. package/ios/Sources/Utils/Bundle+SessionReplay.swift +21 -0
  22. package/lib/commonjs/components/SessionReplayView/PrivacyView.js +2 -0
  23. package/lib/commonjs/components/SessionReplayView/PrivacyView.js.map +1 -1
  24. package/lib/commonjs/metro/index.js +75 -0
  25. package/lib/commonjs/metro/index.js.map +1 -0
  26. package/lib/commonjs/metro/processing.js +97 -0
  27. package/lib/commonjs/metro/processing.js.map +1 -0
  28. package/lib/commonjs/metro/utils.js +20 -0
  29. package/lib/commonjs/metro/utils.js.map +1 -0
  30. package/lib/commonjs/specs/DdPrivacyViewNativeComponent.js.map +1 -1
  31. package/lib/module/components/SessionReplayView/PrivacyView.js +2 -0
  32. package/lib/module/components/SessionReplayView/PrivacyView.js.map +1 -1
  33. package/lib/module/metro/index.js +68 -0
  34. package/lib/module/metro/index.js.map +1 -0
  35. package/lib/module/metro/processing.js +90 -0
  36. package/lib/module/metro/processing.js.map +1 -0
  37. package/lib/module/metro/utils.js +14 -0
  38. package/lib/module/metro/utils.js.map +1 -0
  39. package/lib/module/specs/DdPrivacyViewNativeComponent.js.map +1 -1
  40. package/lib/typescript/components/SessionReplayView/PrivacyView.d.ts +10 -1
  41. package/lib/typescript/components/SessionReplayView/PrivacyView.d.ts.map +1 -1
  42. package/lib/typescript/metro/index.d.ts +2 -0
  43. package/lib/typescript/metro/index.d.ts.map +1 -0
  44. package/lib/typescript/metro/processing.d.ts +9 -0
  45. package/lib/typescript/metro/processing.d.ts.map +1 -0
  46. package/lib/typescript/metro/utils.d.ts +2 -0
  47. package/lib/typescript/metro/utils.d.ts.map +1 -0
  48. package/lib/typescript/specs/DdPrivacyViewNativeComponent.d.ts +8 -0
  49. package/lib/typescript/specs/DdPrivacyViewNativeComponent.d.ts.map +1 -1
  50. package/lib/typescript/types/DdPrivacyView.d.ts +8 -0
  51. package/lib/typescript/types/DdPrivacyView.d.ts.map +1 -1
  52. package/metro.js +7 -0
  53. package/package.json +9 -3
  54. package/scripts/build-assets.js +31 -0
  55. package/src/components/SessionReplayView/PrivacyView.tsx +11 -0
  56. package/src/metro/index.ts +82 -0
  57. package/src/metro/processing.ts +119 -0
  58. package/src/metro/utils.ts +17 -0
  59. package/src/specs/DdPrivacyViewNativeComponent.ts +9 -0
  60. package/src/types/DdPrivacyView.ts +9 -0
@@ -0,0 +1,28 @@
1
+ /*
2
+ * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3
+ * This product includes software developed at Datadog (https://www.datadoghq.com/).
4
+ * Copyright 2016-Present Datadog, Inc.
5
+ */
6
+
7
+ // DdPrivacyViewFabric.h
8
+
9
+ #if RCT_NEW_ARCH_ENABLED
10
+ #import <React/RCTViewComponentView.h>
11
+ #import <react/renderer/components/DdSDKReactNativeSessionReplay/ComponentDescriptors.h>
12
+ #import <react/renderer/components/DdSDKReactNativeSessionReplay/EventEmitters.h>
13
+ #import <react/renderer/components/DdSDKReactNativeSessionReplay/Props.h>
14
+ #import <react/renderer/components/DdSDKReactNativeSessionReplay/RCTComponentViewHelpers.h>
15
+ #import <React/RCTFabricComponentsPlugins.h>
16
+
17
+
18
+ #if __has_include("DatadogSDKReactNativeSessionReplay-Swift.h")
19
+ #import <DatadogSDKReactNativeSessionReplay-Swift.h>
20
+ #else
21
+ #import <DatadogSDKReactNativeSessionReplay/DatadogSDKReactNativeSessionReplay-Swift.h>
22
+ #endif
23
+
24
+ @interface DdPrivacyViewFabric : RCTViewComponentView
25
+ @property (nonatomic, copy) NSString *nativeID;
26
+ @property (nonatomic, copy) NSDictionary<NSString *, NSString *> *attributes;
27
+ @end
28
+ #endif
@@ -18,14 +18,14 @@
18
18
  #else
19
19
  #import <DatadogSDKReactNativeSessionReplay/DatadogSDKReactNativeSessionReplay-Swift.h>
20
20
  #endif
21
+ #import <objc/runtime.h>
22
+ #import "DdPrivacyViewFabric.h"
21
23
 
22
24
  using namespace facebook::react;
23
25
 
24
- @interface DdPrivacyViewFabric : RCTViewComponentView <RCTDdPrivacyViewViewProtocol>
25
- @end
26
-
27
26
  @implementation DdPrivacyViewFabric {
28
27
  UIView * _view;
28
+
29
29
  }
30
30
 
31
31
  + (ComponentDescriptorProvider)componentDescriptorProvider {
@@ -40,15 +40,33 @@ using namespace facebook::react;
40
40
  return self;
41
41
  }
42
42
 
43
+
44
+ - (void)setNativeID:(NSString *)nativeID {
45
+ objc_setAssociatedObject(self, @selector(nativeID), nativeID, OBJC_ASSOCIATION_COPY_NONATOMIC);
46
+ }
47
+
48
+ - (NSString *)nativeID {
49
+ return objc_getAssociatedObject(self, @selector(nativeID));
50
+ }
43
51
  - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps {
44
52
  const auto &newProps = *std::static_pointer_cast<DdPrivacyViewProps const>(props);
45
53
 
46
54
  NSString *text = [NSString stringWithUTF8String:newProps.textAndInputPrivacy.c_str()];
47
55
  NSString *image = [NSString stringWithUTF8String:newProps.imagePrivacy.c_str()];
48
56
  NSString *touch = [NSString stringWithUTF8String:newProps.touchPrivacy.c_str()];
49
-
57
+ NSString *nativeID = [NSString stringWithUTF8String:newProps.nativeID.c_str()];
58
+ NSMutableDictionary<NSString *, NSString *> *attributesDict = [NSMutableDictionary new];
59
+
60
+ attributesDict[@"type"] = [NSString stringWithUTF8String:newProps.attributes.type.c_str()];
61
+ attributesDict[@"width"] = [NSString stringWithUTF8String:newProps.attributes.width.c_str()];
62
+ attributesDict[@"height"] = [NSString stringWithUTF8String:newProps.attributes.height.c_str()];
63
+ attributesDict[@"hash"] = [NSString stringWithUTF8String:newProps.attributes.hash.c_str()];
64
+
50
65
  [DdPrivacyOverrider setOverridesFor:self textPrivacy:text imagePrivacy:image touchPrivacy:touch hide:newProps.hide];
66
+ self.nativeID = nativeID;
67
+ self.attributes = attributesDict;
51
68
 
69
+ self.accessibilityIdentifier = nativeID;
52
70
  [super updateProps:props oldProps:oldProps];
53
71
  }
54
72
 
@@ -17,6 +17,9 @@
17
17
  @property (nonatomic, strong) NSString *imagePrivacy;
18
18
  @property (nonatomic, strong) NSString *touchPrivacy;
19
19
  @property (nonatomic, assign) BOOL hide;
20
+ @property (nonatomic, copy) NSString *nativeID;
21
+
22
+ @property (nonatomic, copy) NSDictionary<NSString *, NSString *> *attributes;
20
23
 
21
24
  @end
22
25
 
@@ -55,6 +58,27 @@ RCT_CUSTOM_VIEW_PROPERTY(hide, BOOL, DdPrivacyView) {
55
58
  [self setPrivacyOverridesFor:view];
56
59
  }
57
60
 
61
+ RCT_CUSTOM_VIEW_PROPERTY(nativeID, NSString, DdPrivacyView) {
62
+ view.nativeID = [RCTConvert NSString:json];
63
+ }
64
+
65
+ RCT_CUSTOM_VIEW_PROPERTY(attributes, NSDictionary, DdPrivacyView) {
66
+ if (json && [json isKindOfClass:[NSDictionary class]]) {
67
+ NSMutableDictionary<NSString *, NSString *> *dict = [NSMutableDictionary new];
68
+ for (id key in json) {
69
+ id value = json[key];
70
+ if ([key isKindOfClass:[NSString class]] && [value isKindOfClass:[NSString class]]) {
71
+ dict[key] = value;
72
+ } else if ([key isKindOfClass:[NSString class]] && value != [NSNull null]) {
73
+ dict[key] = [value description];
74
+ }
75
+ }
76
+ view.attributes = dict;
77
+ } else {
78
+ view.attributes = nil;
79
+ }
80
+ }
81
+
58
82
  - (void) setPrivacyOverridesFor:(DdPrivacyView *) view {
59
83
  [DdPrivacyOverrider setOverridesFor:view textPrivacy:view.textPrivacy imagePrivacy:view.imagePrivacy touchPrivacy:view.touchPrivacy hide:view.hide];
60
84
  }
@@ -10,6 +10,11 @@ import DatadogInternal
10
10
  import DatadogSDKReactNative
11
11
  import React
12
12
 
13
+ internal struct SVGData: Codable {
14
+ let offset: Int
15
+ let length: Int
16
+ }
17
+
13
18
  @objc
14
19
  public class DdSessionReplayImplementation: NSObject {
15
20
  private lazy var sessionReplay: SessionReplayProtocol = sessionReplayProvider()
@@ -26,7 +31,7 @@ public class DdSessionReplayImplementation: NSObject {
26
31
  self.uiManager = uiManager
27
32
  self.fabricWrapper = fabricWrapper
28
33
  }
29
-
34
+
30
35
  @objc
31
36
  public convenience init(bridge: RCTBridge) {
32
37
  self.init(
@@ -35,7 +40,7 @@ public class DdSessionReplayImplementation: NSObject {
35
40
  fabricWrapper: RCTFabricWrapper()
36
41
  )
37
42
  }
38
-
43
+
39
44
  @objc
40
45
  public func enable(
41
46
  replaySampleRate: Double,
@@ -51,7 +56,7 @@ public class DdSessionReplayImplementation: NSObject {
51
56
  if (customEndpoint != "") {
52
57
  customEndpointURL = URL(string: "\(customEndpoint)/api/v2/replay" as String)
53
58
  }
54
-
59
+
55
60
  var sessionReplayConfiguration = SessionReplay.Configuration(
56
61
  replaySampleRate: Float(replaySampleRate),
57
62
  textAndInputPrivacyLevel: convertTextAndInputPrivacy(textAndInputPrivacyLevel),
@@ -60,11 +65,34 @@ public class DdSessionReplayImplementation: NSObject {
60
65
  startRecordingImmediately: startRecordingImmediately,
61
66
  customEndpoint: customEndpointURL
62
67
  )
63
-
68
+
69
+ // let bundle = Bundle(for: DdSessionReplayImplementation.self)
70
+
71
+ var svgMap: [String: SVGData] = [:]
72
+
73
+ if let bundle = Bundle.ddSessionReplayResources,
74
+ let url = bundle.url(forResource: "assets", withExtension: "json") {
75
+ do {
76
+ let data = try Data(contentsOf: url)
77
+ let decoder = JSONDecoder()
78
+ svgMap = try decoder.decode([String: SVGData].self, from: data)
79
+ } catch {
80
+ consolePrint("Failed to load or decode assets.json: \(error)", .debug)
81
+ }
82
+ }
83
+
64
84
  sessionReplayConfiguration.setAdditionalNodeRecorders([
65
- RCTTextViewRecorder(uiManager: uiManager, fabricWrapper: fabricWrapper)
85
+ SvgViewRecorder(
86
+ uiManager: uiManager,
87
+ fabricWrapper: fabricWrapper,
88
+ svgMap: svgMap
89
+ ),
90
+ RCTTextViewRecorder(
91
+ uiManager: uiManager,
92
+ fabricWrapper: fabricWrapper
93
+ )
66
94
  ])
67
-
95
+
68
96
  if let core = DatadogSDKWrapper.shared.getCoreInstance() {
69
97
  sessionReplay.enable(
70
98
  with: sessionReplayConfiguration,
@@ -0,0 +1,236 @@
1
+ /*
2
+ * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3
+ * This product includes software developed at Datadog (https://www.datadoghq.com/).
4
+ * Copyright 2016-Present Datadog, Inc.
5
+ */
6
+
7
+ import Foundation
8
+ @_spi(Internal)
9
+ import DatadogSessionReplay
10
+ import DatadogInternal
11
+ import DatadogSDKReactNative
12
+ import UIKit
13
+ import React
14
+
15
+ private enum SVGConstants {
16
+ static let attributes = "attributes"
17
+ static let width = "width"
18
+ static let height = "height"
19
+ static let hash = "hash"
20
+ static let type = "type"
21
+ static let svgTypeValue = "svg"
22
+ }
23
+
24
+ private enum PrivacyViewClasses {
25
+ static let paper = "DdPrivacyView"
26
+ static let fabric = "DdPrivacyViewFabric"
27
+ }
28
+
29
+ internal struct ReactNativeSVGResource: SessionReplayResource {
30
+ let identifier: String
31
+ let svgContent: String
32
+ let mimeType: String = "image/svg+xml"
33
+
34
+ init(id: String, svgContent: String) {
35
+ self.identifier = id
36
+ self.svgContent = svgContent
37
+ }
38
+
39
+ func calculateIdentifier() -> String {
40
+ return identifier
41
+ }
42
+
43
+ func calculateData() -> Data {
44
+ return svgContent.data(using: .utf8) ?? Data()
45
+ }
46
+ }
47
+
48
+ internal class SvgViewRecorder: SessionReplayNodeRecorder {
49
+ internal var identifier = UUID()
50
+
51
+ internal let uiManager: RCTUIManager
52
+ internal let fabricWrapper: RCTFabricWrapper
53
+ internal let svgMap: [String: SVGData]
54
+
55
+ internal init(uiManager: RCTUIManager, fabricWrapper: RCTFabricWrapper, svgMap: [String: SVGData]) {
56
+ self.uiManager = uiManager
57
+ self.fabricWrapper = fabricWrapper
58
+ self.svgMap = svgMap
59
+ }
60
+
61
+ func semantics(
62
+ of view: UIView,
63
+ with attributes: SessionReplayViewAttributes,
64
+ in context: SessionReplayViewTreeRecordingContext
65
+ ) -> SessionReplayNodeSemantics? {
66
+
67
+ if let cls1 = NSClassFromString(PrivacyViewClasses.paper),
68
+ let cls2 = NSClassFromString(PrivacyViewClasses.fabric) {
69
+ if !view.isKind(of: cls1) && !view.isKind(of: cls2) {
70
+ return nil
71
+ }
72
+ }
73
+
74
+ let sel = NSSelectorFromString(SVGConstants.attributes)
75
+ guard view.responds(to: sel) else { return nil }
76
+
77
+ guard let attrs = view.value(forKey: SVGConstants.attributes) as? [String: String] else {
78
+ return nil
79
+ }
80
+
81
+ guard let type = attrs[SVGConstants.type], type == "svg" else {
82
+ return nil
83
+ }
84
+
85
+ guard let hash = attrs[SVGConstants.hash] else {
86
+ return nil
87
+ }
88
+
89
+ let bundle = Bundle.ddSessionReplayResources
90
+ guard let url = bundle?.url(forResource: "assets", withExtension: "bin") else {
91
+ return nil
92
+ }
93
+
94
+ guard let subView = view.subviews.first else {
95
+ return nil
96
+ }
97
+
98
+ let viewId = context.ids.nodeID(view: view, nodeRecorder: self)
99
+ let svgId = context.ids.nodeID(view: subView, nodeRecorder: self)
100
+
101
+ guard let svgInfo = svgMap[hash] else {
102
+ return nil
103
+ }
104
+
105
+ guard let svgDataChunk = readDataChunk(from: url, offset: svgInfo.offset, length: svgInfo.length),
106
+ var svgData = String(data: svgDataChunk, encoding: .utf8) else {
107
+ return nil
108
+ }
109
+
110
+
111
+ if let updatedSvgData = handleDynamicSvgDimensions(view: subView, svgData: svgData, attrs: attrs) {
112
+ svgData = updatedSvgData
113
+ }
114
+
115
+ let svgResource = ReactNativeSVGResource(
116
+ id: hash,
117
+ svgContent: svgData
118
+ )
119
+
120
+ let contentFrame = CGRect(
121
+ origin: attributes.frame.origin,
122
+ size: CGSize(width: subView.bounds.width,
123
+ height: subView.bounds.height)
124
+ )
125
+
126
+ let builder = SvgViewWireframesBuilder(
127
+ wireframeID: viewId,
128
+ imageWireframeID: svgId,
129
+ attributes: attributes,
130
+ contentFrame: contentFrame,
131
+ svgResource: svgResource,
132
+ imagePrivacyLevel: context.recorder.imagePrivacy
133
+ )
134
+
135
+ // Children are ignored because they were already processed and converted by the babel plugin
136
+ let element = SessionReplaySpecificElement(subtreeStrategy: .ignore, nodes: [
137
+ SessionReplayNode(viewAttributes: attributes, wireframesBuilder: builder)
138
+ ])
139
+
140
+ return element
141
+ }
142
+ }
143
+
144
+ func readDataChunk(from url: URL, offset: Int, length: Int) -> Data? {
145
+ guard let fileHandle = FileHandle(forReadingAtPath: url.path) else {
146
+ return nil
147
+ }
148
+ defer {
149
+ fileHandle.closeFile()
150
+ }
151
+
152
+ fileHandle.seek(toFileOffset: UInt64(offset))
153
+
154
+ let data = fileHandle.readData(ofLength: length)
155
+ return data
156
+ }
157
+
158
+ func handleDynamicSvgDimensions(view: UIView, svgData: String, attrs: [String: String]) -> String? {
159
+ let width = attrs[SVGConstants.width] ?? ""
160
+ let height = attrs[SVGConstants.height] ?? ""
161
+
162
+ var svgAttributes: [String] = []
163
+
164
+ if width.isEmpty {
165
+ svgAttributes.append(#"width="\#(Int(view.bounds.width))""#)
166
+ }
167
+
168
+ if height.isEmpty {
169
+ svgAttributes.append(#"height="\#(Int(view.bounds.height))""#)
170
+ }
171
+
172
+ if !svgAttributes.isEmpty {
173
+ // Here we update the svg content but keep the original hash without these values
174
+ // The goal is to save some time, as it won't matter since the hash is used as an identifier
175
+ var svg = svgData
176
+ let pattern = #"<svg[^>]*>"#
177
+
178
+ if let match = svg.range(of: pattern, options: .regularExpression) {
179
+ let dimensions = " " + svgAttributes.joined(separator: " ")
180
+
181
+ if let closingBracketRange = svg.range(of: ">", range: match.lowerBound..<match.upperBound) {
182
+ svg.replaceSubrange(closingBracketRange, with: dimensions + ">")
183
+ return svg
184
+ }
185
+ }
186
+
187
+ }
188
+
189
+ return nil
190
+ }
191
+
192
+ internal struct SvgViewWireframesBuilder: SessionReplayNodeWireframesBuilder {
193
+ let wireframeID: WireframeID
194
+
195
+ var wireframeRect: CGRect {
196
+ attributes.frame
197
+ }
198
+
199
+ let imageWireframeID: WireframeID
200
+
201
+ let attributes: SessionReplayViewAttributes
202
+
203
+ let contentFrame: CGRect?
204
+
205
+ let svgResource: ReactNativeSVGResource?
206
+
207
+ let imagePrivacyLevel: ImagePrivacyLevel
208
+
209
+ func buildWireframes(with builder: SessionReplayWireframesBuilder) -> [SRWireframe] {
210
+ var wireframes = [
211
+ builder.createShapeWireframe(
212
+ id: wireframeID,
213
+ frame: attributes.frame,
214
+ clip: attributes.clip,
215
+ borderColor: attributes.layerBorderColor,
216
+ borderWidth: attributes.layerBorderWidth,
217
+ backgroundColor: attributes.backgroundColor,
218
+ cornerRadius: attributes.layerCornerRadius,
219
+ opacity: attributes.alpha
220
+ )
221
+ ]
222
+
223
+ if let svgResource {
224
+ wireframes.append(
225
+ builder.createImageWireframe(
226
+ id: imageWireframeID,
227
+ resource: svgResource,
228
+ frame: contentFrame ?? attributes.frame,
229
+ clip: attributes.clip
230
+ )
231
+ )
232
+ }
233
+
234
+ return wireframes
235
+ }
236
+ }
@@ -0,0 +1,21 @@
1
+ /*
2
+ * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3
+ * This product includes software developed at Datadog (https://www.datadoghq.com/).
4
+ * Copyright 2016-Present Datadog, Inc.
5
+ */
6
+
7
+ import Foundation
8
+
9
+ internal class BundleFinder {}
10
+
11
+ extension Bundle {
12
+ static var ddSessionReplayResources: Bundle? {
13
+ let bundle = Bundle(for: BundleFinder.self)
14
+ if let resourceURL = bundle.url(forResource: "DDSessionReplay", withExtension: "bundle"),
15
+ let resourceBundle = Bundle(url: resourceURL) {
16
+ return resourceBundle
17
+ }
18
+
19
+ return nil
20
+ }
21
+ }
@@ -31,6 +31,7 @@ function PrivacyView({
31
31
  textAndInputPrivacy,
32
32
  imagePrivacy,
33
33
  touchPrivacy,
34
+ nativeID,
34
35
  hide = false,
35
36
  ...props
36
37
  }) {
@@ -40,6 +41,7 @@ function PrivacyView({
40
41
  imagePrivacy: imagePrivacy,
41
42
  touchPrivacy: touchPrivacy,
42
43
  hide: hide || false,
44
+ nativeID: nativeID,
43
45
  children: children
44
46
  });
45
47
  }
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireDefault","require","_DdPrivacyView","_jsxRuntime","e","__esModule","default","PrivacyView","children","textAndInputPrivacy","imagePrivacy","touchPrivacy","hide","props","jsx"],"sourceRoot":"../../../../src","sources":["components/SessionReplayView/PrivacyView.tsx"],"mappings":";;;;;;AAOA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AAOA,IAAAC,cAAA,GAAAF,sBAAA,CAAAC,OAAA;AAA6C,IAAAE,WAAA,GAAAF,OAAA;AAAA,SAAAD,uBAAAI,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAd7C;AACA;AACA;AACA;AACA;;AA+BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASG,WAAWA,CAAC;EACxBC,QAAQ;EACRC,mBAAmB;EACnBC,YAAY;EACZC,YAAY;EACZC,IAAI,GAAG,KAAK;EACZ,GAAGC;AACA,CAAC,EAAE;EACN,oBACI,IAAAV,WAAA,CAAAW,GAAA,EAACZ,cAAA,CAAAI,OAAI;IAAA,GACGO,KAAK;IACTJ,mBAAmB,EAAEA,mBAA8B;IACnDC,YAAY,EAAEA,YAAuB;IACrCC,YAAY,EAAEA,YAAuB;IACrCC,IAAI,EAAEA,IAAI,IAAI,KAAM;IAAAJ,QAAA,EAEnBA;EAAQ,CACP,CAAC;AAEf","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireDefault","require","_DdPrivacyView","_jsxRuntime","e","__esModule","default","PrivacyView","children","textAndInputPrivacy","imagePrivacy","touchPrivacy","nativeID","hide","props","jsx"],"sourceRoot":"../../../../src","sources":["components/SessionReplayView/PrivacyView.tsx"],"mappings":";;;;;;AAOA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AAOA,IAAAC,cAAA,GAAAF,sBAAA,CAAAC,OAAA;AAA6C,IAAAE,WAAA,GAAAF,OAAA;AAAA,SAAAD,uBAAAI,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAd7C;AACA;AACA;AACA;AACA;;AAwCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASG,WAAWA,CAAC;EACxBC,QAAQ;EACRC,mBAAmB;EACnBC,YAAY;EACZC,YAAY;EACZC,QAAQ;EACRC,IAAI,GAAG,KAAK;EACZ,GAAGC;AACA,CAAC,EAAE;EACN,oBACI,IAAAX,WAAA,CAAAY,GAAA,EAACb,cAAA,CAAAI,OAAI;IAAA,GACGQ,KAAK;IACTL,mBAAmB,EAAEA,mBAA8B;IACnDC,YAAY,EAAEA,YAAuB;IACrCC,YAAY,EAAEA,YAAuB;IACrCE,IAAI,EAAEA,IAAI,IAAI,KAAM;IACpBD,QAAQ,EAAEA,QAAmB;IAAAJ,QAAA,EAE5BA;EAAQ,CACP,CAAC;AAEf","ignoreList":[]}
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.withSessionReplayAssetBundler = withSessionReplayAssetBundler;
7
+ var _chokidar = _interopRequireDefault(require("chokidar"));
8
+ var _fs = _interopRequireDefault(require("fs"));
9
+ var _path = _interopRequireDefault(require("path"));
10
+ var _processing = require("./processing");
11
+ var _utils = require("./utils");
12
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
+ /*
14
+ * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
15
+ * This product includes software developed at Datadog (https://www.datadoghq.com/).
16
+ * Copyright 2016-Present Datadog, Inc.
17
+ */
18
+
19
+ let watching = false;
20
+ const MERGE_DEBOUNCE_MS = 300;
21
+ const WATCH_STABILITY_THRESHOLD_MS = 200;
22
+ const WATCH_POLL_INTERVAL_MS = 50;
23
+ function withSessionReplayAssetBundler(metroConfig) {
24
+ const originalReporter = metroConfig.reporter;
25
+ const assetsDir = _path.default.resolve(__dirname, '../../../assets');
26
+ if (!_fs.default.existsSync(assetsDir)) {
27
+ _fs.default.mkdirSync(assetsDir, {
28
+ recursive: true
29
+ });
30
+ }
31
+ const debounceMerge = (0, _utils.debounce)(() => {
32
+ try {
33
+ (0, _processing.mergeSvgAssets)(assetsDir);
34
+ } catch (error) {
35
+ console.warn('[SessionReplayAssetBundler] SVGs merge failed:', error);
36
+ }
37
+ }, MERGE_DEBOUNCE_MS);
38
+
39
+ // Prevent potential multiple watchers if metro loads the config multiple times
40
+ if (!watching) {
41
+ watching = true;
42
+ _chokidar.default.watch(assetsDir, {
43
+ // Skip events for files that already exist
44
+ ignoreInitial: true,
45
+ depth: 0,
46
+ awaitWriteFinish: {
47
+ // Wait 200ms after last change
48
+ stabilityThreshold: WATCH_STABILITY_THRESHOLD_MS,
49
+ // Check every 50ms
50
+ pollInterval: WATCH_POLL_INTERVAL_MS
51
+ }
52
+ }).on('add', file => {
53
+ if (!file.endsWith('.svg')) {
54
+ return;
55
+ }
56
+ debounceMerge();
57
+ });
58
+ }
59
+ return {
60
+ ...metroConfig,
61
+ reporter: {
62
+ update(event) {
63
+ if (originalReporter?.update) {
64
+ originalReporter.update(event);
65
+ }
66
+
67
+ // https://github.com/facebook/metro/blob/main/packages/metro/src/lib/reporting.js
68
+ if (event.type === 'bundle_build_done' || event.type === 'transformer_load_done') {
69
+ (0, _processing.mergeSvgAssets)(assetsDir);
70
+ }
71
+ }
72
+ }
73
+ };
74
+ }
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_chokidar","_interopRequireDefault","require","_fs","_path","_processing","_utils","e","__esModule","default","watching","MERGE_DEBOUNCE_MS","WATCH_STABILITY_THRESHOLD_MS","WATCH_POLL_INTERVAL_MS","withSessionReplayAssetBundler","metroConfig","originalReporter","reporter","assetsDir","path","resolve","__dirname","fs","existsSync","mkdirSync","recursive","debounceMerge","debounce","mergeSvgAssets","error","console","warn","chokidar","watch","ignoreInitial","depth","awaitWriteFinish","stabilityThreshold","pollInterval","on","file","endsWith","update","event","type"],"sourceRoot":"../../../src","sources":["metro/index.ts"],"mappings":";;;;;;AAMA,IAAAA,SAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,GAAA,GAAAF,sBAAA,CAAAC,OAAA;AACA,IAAAE,KAAA,GAAAH,sBAAA,CAAAC,OAAA;AAEA,IAAAG,WAAA,GAAAH,OAAA;AACA,IAAAI,MAAA,GAAAJ,OAAA;AAAmC,SAAAD,uBAAAM,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAXnC;AACA;AACA;AACA;AACA;;AASA,IAAIG,QAAQ,GAAG,KAAK;AAEpB,MAAMC,iBAAiB,GAAG,GAAG;AAC7B,MAAMC,4BAA4B,GAAG,GAAG;AACxC,MAAMC,sBAAsB,GAAG,EAAE;AAE1B,SAASC,6BAA6BA,CAACC,WAAgB,EAAO;EACjE,MAAMC,gBAAgB,GAAGD,WAAW,CAACE,QAAQ;EAE7C,MAAMC,SAAS,GAAGC,aAAI,CAACC,OAAO,CAACC,SAAS,EAAE,iBAAiB,CAAC;EAE5D,IAAI,CAACC,WAAE,CAACC,UAAU,CAACL,SAAS,CAAC,EAAE;IAC3BI,WAAE,CAACE,SAAS,CAACN,SAAS,EAAE;MAAEO,SAAS,EAAE;IAAK,CAAC,CAAC;EAChD;EAEA,MAAMC,aAAa,GAAG,IAAAC,eAAQ,EAAC,MAAM;IACjC,IAAI;MACA,IAAAC,0BAAc,EAACV,SAAS,CAAC;IAC7B,CAAC,CAAC,OAAOW,KAAK,EAAE;MACZC,OAAO,CAACC,IAAI,CACR,gDAAgD,EAChDF,KACJ,CAAC;IACL;EACJ,CAAC,EAAElB,iBAAiB,CAAC;;EAErB;EACA,IAAI,CAACD,QAAQ,EAAE;IACXA,QAAQ,GAAG,IAAI;IACfsB,iBAAQ,CACHC,KAAK,CAACf,SAAS,EAAE;MACd;MACAgB,aAAa,EAAE,IAAI;MACnBC,KAAK,EAAE,CAAC;MACRC,gBAAgB,EAAE;QACd;QACAC,kBAAkB,EAAEzB,4BAA4B;QAChD;QACA0B,YAAY,EAAEzB;MAClB;IACJ,CAAC,CAAC,CACD0B,EAAE,CAAC,KAAK,EAAEC,IAAI,IAAI;MACf,IAAI,CAACA,IAAI,CAACC,QAAQ,CAAC,MAAM,CAAC,EAAE;QACxB;MACJ;MAEAf,aAAa,CAAC,CAAC;IACnB,CAAC,CAAC;EACV;EAEA,OAAO;IACH,GAAGX,WAAW;IACdE,QAAQ,EAAE;MACNyB,MAAMA,CAACC,KAAU,EAAE;QACf,IAAI3B,gBAAgB,EAAE0B,MAAM,EAAE;UAC1B1B,gBAAgB,CAAC0B,MAAM,CAACC,KAAK,CAAC;QAClC;;QAEA;QACA,IACIA,KAAK,CAACC,IAAI,KAAK,mBAAmB,IAClCD,KAAK,CAACC,IAAI,KAAK,uBAAuB,EACxC;UACE,IAAAhB,0BAAc,EAACV,SAAS,CAAC;QAC7B;MACJ;IACJ;EACJ,CAAC;AACL","ignoreList":[]}
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.mergeSvgAssets = mergeSvgAssets;
7
+ var _fs = _interopRequireDefault(require("fs"));
8
+ var _path = _interopRequireDefault(require("path"));
9
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
+ /*
11
+ * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
12
+ * This product includes software developed at Datadog (https://www.datadoghq.com/).
13
+ * Copyright 2016-Present Datadog, Inc.
14
+ */
15
+
16
+ /**
17
+ * Removes the binary and JSON asset files from the specified directory.
18
+ * This is used to clean up corrupted or partial asset files before regenerating them.
19
+ *
20
+ * @param binPath - Absolute path to the assets.bin file
21
+ * @param jsonPath - Absolute path to the assets.json file
22
+ */
23
+ function cleanupFiles(binPath, jsonPath) {
24
+ try {
25
+ _fs.default.unlinkSync(binPath);
26
+ } catch (err) {
27
+ console.warn('[cleanupFiles] Failed to cleanup binary assets', err);
28
+ }
29
+ try {
30
+ _fs.default.unlinkSync(jsonPath);
31
+ } catch (err) {
32
+ console.warn('[cleanupFiles] Failed to cleanup json assets', err);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Merges all individual SVG files into assets.bin and creates an index in assets.json.
38
+ * This function reads all .svg files from the assets directory and packs them into
39
+ * a single binary file with an accompanying JSON index for efficient lookup.
40
+ *
41
+ * @param assetsDir - Absolute path to the assets directory
42
+ */
43
+ function mergeSvgAssets(assetsDir) {
44
+ try {
45
+ const binName = 'assets.bin';
46
+ const jsonName = 'assets.json';
47
+ const binPath = _path.default.resolve(assetsDir, binName);
48
+ const jsonPath = _path.default.resolve(assetsDir, jsonName);
49
+ let index = {};
50
+ let offset = 0;
51
+ if (!_fs.default.existsSync(assetsDir)) {
52
+ _fs.default.mkdirSync(assetsDir, {
53
+ recursive: true
54
+ });
55
+ }
56
+ try {
57
+ const jsonData = _fs.default.readFileSync(jsonPath, 'utf8');
58
+ const binStats = _fs.default.statSync(binPath);
59
+ index = JSON.parse(jsonData);
60
+ offset = binStats.size;
61
+ } catch (err) {
62
+ console.warn('[mergeSvgAssets] Assets missing or corrupted, starting fresh');
63
+ index = {};
64
+ offset = 0;
65
+ cleanupFiles(binPath, jsonPath);
66
+ }
67
+ const files = _fs.default.readdirSync(assetsDir).filter(f => f.endsWith('.svg')).sort();
68
+ let added = 0;
69
+ for (const f of files) {
70
+ const id = _path.default.basename(f, _path.default.extname(f));
71
+ if (index[id]) {
72
+ continue;
73
+ }
74
+ try {
75
+ const svg = _fs.default.readFileSync(_path.default.join(assetsDir, f), 'utf8');
76
+ const buf = Buffer.from(svg, 'utf8');
77
+ const length = buf.length;
78
+ _fs.default.appendFileSync(binPath, buf);
79
+ index[id] = {
80
+ offset,
81
+ length
82
+ };
83
+ offset += length;
84
+ added++;
85
+ } catch (err) {
86
+ console.warn(`[SessionReplayAssetBundler] Failed to process ${f}:`, err);
87
+ }
88
+ }
89
+ _fs.default.writeFileSync(jsonPath, JSON.stringify(index, null, 2));
90
+ if (added > 0) {
91
+ console.info(`[SessionReplayAssetBundler] Packed ${added} new Session Replay SVG assets → total: ${Object.keys(index).length}`);
92
+ }
93
+ } catch (err) {
94
+ console.error('[mergeSvgAssets] Unexpected error during asset merge', err);
95
+ }
96
+ }
97
+ //# sourceMappingURL=processing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_fs","_interopRequireDefault","require","_path","e","__esModule","default","cleanupFiles","binPath","jsonPath","fs","unlinkSync","err","console","warn","mergeSvgAssets","assetsDir","binName","jsonName","path","resolve","index","offset","existsSync","mkdirSync","recursive","jsonData","readFileSync","binStats","statSync","JSON","parse","size","files","readdirSync","filter","f","endsWith","sort","added","id","basename","extname","svg","join","buf","Buffer","from","length","appendFileSync","writeFileSync","stringify","info","Object","keys","error"],"sourceRoot":"../../../src","sources":["metro/processing.ts"],"mappings":"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","ignoreList":[]}