@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.
- package/DatadogSDKReactNativeSessionReplay.podspec +6 -2
- package/README.md +90 -1
- package/android/build.gradle +3 -2
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt +2 -2
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeInternalCallback.kt +121 -0
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupport.kt +5 -1
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactViewGroupMapper.kt +1 -0
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/SvgViewMapper.kt +145 -0
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/views/DdPrivacyView.kt +10 -0
- package/android/src/newarch/kotlin/com/datadog/reactnative/sessionreplay/views/DdPrivacyViewManager.kt +13 -0
- package/android/src/oldarch/kotlin/com/datadog/reactnative/sessionreplay/views/DdPrivacyViewManager.kt +13 -0
- package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementationTest.kt +8 -0
- package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupportTest.kt +21 -5
- package/assets/assets.bin +0 -0
- package/assets/assets.json +1 -0
- package/ios/Sources/DdPrivacyViewFabric.h +28 -0
- package/ios/Sources/DdPrivacyViewFabric.mm +22 -4
- package/ios/Sources/DdPrivacyViewPaper.m +24 -0
- package/ios/Sources/DdSessionReplayImplementation.swift +34 -6
- package/ios/Sources/SvgViewRecorder.swift +236 -0
- package/ios/Sources/Utils/Bundle+SessionReplay.swift +21 -0
- package/lib/commonjs/components/SessionReplayView/PrivacyView.js +2 -0
- package/lib/commonjs/components/SessionReplayView/PrivacyView.js.map +1 -1
- package/lib/commonjs/metro/index.js +75 -0
- package/lib/commonjs/metro/index.js.map +1 -0
- package/lib/commonjs/metro/processing.js +97 -0
- package/lib/commonjs/metro/processing.js.map +1 -0
- package/lib/commonjs/metro/utils.js +20 -0
- package/lib/commonjs/metro/utils.js.map +1 -0
- package/lib/commonjs/specs/DdPrivacyViewNativeComponent.js.map +1 -1
- package/lib/module/components/SessionReplayView/PrivacyView.js +2 -0
- package/lib/module/components/SessionReplayView/PrivacyView.js.map +1 -1
- package/lib/module/metro/index.js +68 -0
- package/lib/module/metro/index.js.map +1 -0
- package/lib/module/metro/processing.js +90 -0
- package/lib/module/metro/processing.js.map +1 -0
- package/lib/module/metro/utils.js +14 -0
- package/lib/module/metro/utils.js.map +1 -0
- package/lib/module/specs/DdPrivacyViewNativeComponent.js.map +1 -1
- package/lib/typescript/components/SessionReplayView/PrivacyView.d.ts +10 -1
- package/lib/typescript/components/SessionReplayView/PrivacyView.d.ts.map +1 -1
- package/lib/typescript/metro/index.d.ts +2 -0
- package/lib/typescript/metro/index.d.ts.map +1 -0
- package/lib/typescript/metro/processing.d.ts +9 -0
- package/lib/typescript/metro/processing.d.ts.map +1 -0
- package/lib/typescript/metro/utils.d.ts +2 -0
- package/lib/typescript/metro/utils.d.ts.map +1 -0
- package/lib/typescript/specs/DdPrivacyViewNativeComponent.d.ts +8 -0
- package/lib/typescript/specs/DdPrivacyViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/types/DdPrivacyView.d.ts +8 -0
- package/lib/typescript/types/DdPrivacyView.d.ts.map +1 -1
- package/metro.js +7 -0
- package/package.json +9 -3
- package/scripts/build-assets.js +31 -0
- package/src/components/SessionReplayView/PrivacyView.tsx +11 -0
- package/src/metro/index.ts +82 -0
- package/src/metro/processing.ts +119 -0
- package/src/metro/utils.ts +17 -0
- package/src/specs/DdPrivacyViewNativeComponent.ts +9 -0
- 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
|
-
|
|
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;;
|
|
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":";;;;;;AAMA,IAAAA,GAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,KAAA,GAAAF,sBAAA,CAAAC,OAAA;AAAwB,SAAAD,uBAAAG,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAPxB;AACA;AACA;AACA;AACA;;AAYA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASG,YAAYA,CAACC,OAAe,EAAEC,QAAgB,EAAE;EACrD,IAAI;IACAC,WAAE,CAACC,UAAU,CAACH,OAAO,CAAC;EAC1B,CAAC,CAAC,OAAOI,GAAG,EAAE;IACVC,OAAO,CAACC,IAAI,CAAC,gDAAgD,EAAEF,GAAG,CAAC;EACvE;EAEA,IAAI;IACAF,WAAE,CAACC,UAAU,CAACF,QAAQ,CAAC;EAC3B,CAAC,CAAC,OAAOG,GAAG,EAAE;IACVC,OAAO,CAACC,IAAI,CAAC,8CAA8C,EAAEF,GAAG,CAAC;EACrE;AACJ;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASG,cAAcA,CAACC,SAAiB,EAAE;EAC9C,IAAI;IACA,MAAMC,OAAO,GAAG,YAAY;IAC5B,MAAMC,QAAQ,GAAG,aAAa;IAE9B,MAAMV,OAAO,GAAGW,aAAI,CAACC,OAAO,CAACJ,SAAS,EAAEC,OAAO,CAAC;IAChD,MAAMR,QAAQ,GAAGU,aAAI,CAACC,OAAO,CAACJ,SAAS,EAAEE,QAAQ,CAAC;IAElD,IAAIG,KAAe,GAAG,CAAC,CAAC;IACxB,IAAIC,MAAM,GAAG,CAAC;IAEd,IAAI,CAACZ,WAAE,CAACa,UAAU,CAACP,SAAS,CAAC,EAAE;MAC3BN,WAAE,CAACc,SAAS,CAACR,SAAS,EAAE;QAAES,SAAS,EAAE;MAAK,CAAC,CAAC;IAChD;IAEA,IAAI;MACA,MAAMC,QAAQ,GAAGhB,WAAE,CAACiB,YAAY,CAAClB,QAAQ,EAAE,MAAM,CAAC;MAClD,MAAMmB,QAAQ,GAAGlB,WAAE,CAACmB,QAAQ,CAACrB,OAAO,CAAC;MAErCa,KAAK,GAAGS,IAAI,CAACC,KAAK,CAACL,QAAQ,CAAa;MACxCJ,MAAM,GAAGM,QAAQ,CAACI,IAAI;IAC1B,CAAC,CAAC,OAAOpB,GAAG,EAAE;MACVC,OAAO,CAACC,IAAI,CACR,8DACJ,CAAC;MACDO,KAAK,GAAG,CAAC,CAAC;MACVC,MAAM,GAAG,CAAC;MACVf,YAAY,CAACC,OAAO,EAAEC,QAAQ,CAAC;IACnC;IAEA,MAAMwB,KAAK,GAAGvB,WAAE,CACXwB,WAAW,CAAClB,SAAS,CAAC,CACtBmB,MAAM,CAACC,CAAC,IAAIA,CAAC,CAACC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAC/BC,IAAI,CAAC,CAAC;IAEX,IAAIC,KAAK,GAAG,CAAC;IAEb,KAAK,MAAMH,CAAC,IAAIH,KAAK,EAAE;MACnB,MAAMO,EAAE,GAAGrB,aAAI,CAACsB,QAAQ,CAACL,CAAC,EAAEjB,aAAI,CAACuB,OAAO,CAACN,CAAC,CAAC,CAAC;MAC5C,IAAIf,KAAK,CAACmB,EAAE,CAAC,EAAE;QACX;MACJ;MAEA,IAAI;QACA,MAAMG,GAAG,GAAGjC,WAAE,CAACiB,YAAY,CAACR,aAAI,CAACyB,IAAI,CAAC5B,SAAS,EAAEoB,CAAC,CAAC,EAAE,MAAM,CAAC;QAC5D,MAAMS,GAAG,GAAGC,MAAM,CAACC,IAAI,CAACJ,GAAG,EAAE,MAAM,CAAC;QACpC,MAAMK,MAAM,GAAGH,GAAG,CAACG,MAAM;QAEzBtC,WAAE,CAACuC,cAAc,CAACzC,OAAO,EAAEqC,GAAG,CAAC;QAC/BxB,KAAK,CAACmB,EAAE,CAAC,GAAG;UAAElB,MAAM;UAAE0B;QAAO,CAAC;QAC9B1B,MAAM,IAAI0B,MAAM;QAChBT,KAAK,EAAE;MACX,CAAC,CAAC,OAAO3B,GAAG,EAAE;QACVC,OAAO,CAACC,IAAI,CACR,iDAAiDsB,CAAC,GAAG,EACrDxB,GACJ,CAAC;MACL;IACJ;IAEAF,WAAE,CAACwC,aAAa,CAACzC,QAAQ,EAAEqB,IAAI,CAACqB,SAAS,CAAC9B,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC1D,IAAIkB,KAAK,GAAG,CAAC,EAAE;MACX1B,OAAO,CAACuC,IAAI,CACR,sCAAsCb,KAAK,2CACvCc,MAAM,CAACC,IAAI,CAACjC,KAAK,CAAC,CAAC2B,MAAM,EAEjC,CAAC;IACL;EACJ,CAAC,CAAC,OAAOpC,GAAG,EAAE;IACVC,OAAO,CAAC0C,KAAK,CACT,sDAAsD,EACtD3C,GACJ,CAAC;EACL;AACJ","ignoreList":[]}
|