@ansight/react-native 1.3.0-preview.1 → 1.3.0-preview.10
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/README.md +15 -3
- package/android/build.gradle +2 -2
- package/android/src/main/kotlin/ai/ansight/reactnative/AnsightReactNativeModule.kt +69 -8
- package/index.d.ts +34 -2
- package/index.js +172 -28
- package/ios/AnsightReactNative.swift +66 -6
- package/ios/AnsightReactNativeModule.m +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,8 +77,8 @@ bundle; its API requires the native Ansight module.
|
|
|
77
77
|
|
|
78
78
|
This package version expects matching native SDK packages:
|
|
79
79
|
|
|
80
|
-
- CocoaPods: `Ansight`, `AnsightObjC` version `1.3.0-preview.
|
|
81
|
-
- Maven: `ai.ansight:ansight-android:1.3.0-preview.
|
|
80
|
+
- CocoaPods: `Ansight`, `AnsightObjC` version `1.3.0-preview.10`
|
|
81
|
+
- Maven: `ai.ansight:ansight-android:1.3.0-preview.10`
|
|
82
82
|
|
|
83
83
|
## Quickstart
|
|
84
84
|
|
|
@@ -136,10 +136,12 @@ The TypeScript `AnsightOptions` surface mirrors Android `AnsightOptions`, iOS
|
|
|
136
136
|
| `retentionPeriodSeconds` | Local metric/event retention window. |
|
|
137
137
|
| `enableFramesPerSecond` | Enables native FPS sampling. |
|
|
138
138
|
| `enableBatteryLevel` | Enables battery sampling where available. |
|
|
139
|
+
| `enableOpenFileHandleTracking` | Enables native process open-file-handle sampling. Disabled by default. |
|
|
140
|
+
| `enableJniReferenceCountTracking` | Enables JNI reference-count tracking where the runtime can supply it. Disabled by default. |
|
|
139
141
|
| `defaultMemoryChannels` | Selects built-in memory channels. Prefer `managedHeap`, `nativeHeap`, `residentSetSize`, and `physicalFootprint`; `javaHeap` and `rss` are accepted as Android/RN compatibility aliases. |
|
|
140
142
|
| `reactNativeMemory` | Controls native React Native runtime memory channels. Enabled by default; set to `false` to disable, or use `{ jsHeapUsed, jsHeapTotal }`. |
|
|
141
143
|
| `additionalChannels` | Registers custom metric channels. |
|
|
142
|
-
| `sessionJpegCapture` | Object to enable/configure capture, or `false` to disable. Supports `captureGpuBackedSurfaces`; on iOS it defaults to `true` so Metal, SceneKit, and similar GPU-backed views are included. |
|
|
144
|
+
| `sessionJpegCapture` | Object to enable/configure capture, or `false` to disable. `mode` accepts `screenshotOnly`, `screenshotAndVisualTree`, or `screenshotWithVisualTreeOnTouch`. Supports `captureGpuBackedSurfaces`; on iOS it defaults to `true` so Metal, SceneKit, and similar GPU-backed views are included. |
|
|
143
145
|
| `touchCapture` | Object to enable/configure capture, or `false` to disable. |
|
|
144
146
|
| `lifecycleCapture` | Native lifecycle and screen-view capture options. |
|
|
145
147
|
| `toolGuard` | `"disabled"`, `"readOnly"`, `"readWrite"`, or `"fullAccess"`. |
|
|
@@ -150,6 +152,11 @@ The TypeScript `AnsightOptions` surface mirrors Android `AnsightOptions`, iOS
|
|
|
150
152
|
| `remoteTools` | Native visual tree, file, database, preferences, reflection, and secure-storage tool options. |
|
|
151
153
|
| `lifecycle` | JS AppState tracking toggle. Defaults to true. |
|
|
152
154
|
|
|
155
|
+
Use `withOpenFileHandleTracking()` and `withJniReferenceCountTracking()` to
|
|
156
|
+
opt in through the builder. Matching `without...` methods disable the channels
|
|
157
|
+
again. Open handles are sampled by the native Android/iOS runtime. JNI counts
|
|
158
|
+
are available on Android only when the host integration can supply them.
|
|
159
|
+
|
|
153
160
|
Cellular host connections are disabled by default. The restriction applies to
|
|
154
161
|
enrollment and reconnect requests. Opt in only for a trusted development host
|
|
155
162
|
or personal hotspot:
|
|
@@ -175,6 +182,7 @@ await Ansight.initializeAndActivate({
|
|
|
175
182
|
quality: 60,
|
|
176
183
|
maxWidth: 480,
|
|
177
184
|
captureGpuBackedSurfaces: true,
|
|
185
|
+
mode: "screenshotWithVisualTreeOnTouch",
|
|
178
186
|
},
|
|
179
187
|
touchCapture: {
|
|
180
188
|
captureMoveEvents: true,
|
|
@@ -192,6 +200,10 @@ await Ansight.initializeAndActivate({
|
|
|
192
200
|
});
|
|
193
201
|
```
|
|
194
202
|
|
|
203
|
+
The touch mode captures native visual trees only on touch down and touch up.
|
|
204
|
+
Move and cancel events do not trigger capture. Native touch capture and
|
|
205
|
+
visual-tree tools/providers must remain enabled.
|
|
206
|
+
|
|
195
207
|
On iOS, `captureGpuBackedSurfaces` defaults to `true` so Metal, SceneKit, and
|
|
196
208
|
similar GPU-backed views are included. Set it to `false` to use a lower-overhead
|
|
197
209
|
capture path when those surfaces are not needed.
|
package/android/build.gradle
CHANGED
|
@@ -67,6 +67,6 @@ repositories {
|
|
|
67
67
|
dependencies {
|
|
68
68
|
implementation("com.facebook.react:react-android")
|
|
69
69
|
def ansightAndroidVersion =
|
|
70
|
-
findProperty("ansightAndroidVersion") ?: "1.3.0-preview.
|
|
71
|
-
implementation("ai.ansight:ansight-android:1.3.0-preview.
|
|
70
|
+
findProperty("ansightAndroidVersion") ?: "1.3.0-preview.10"
|
|
71
|
+
implementation("ai.ansight:ansight-android:1.3.0-preview.10")
|
|
72
72
|
}
|
|
@@ -6,6 +6,7 @@ import ai.ansight.runtime.AndroidToolExecutionContext
|
|
|
6
6
|
import ai.ansight.runtime.AndroidToolResult
|
|
7
7
|
import ai.ansight.runtime.AnsightChannel
|
|
8
8
|
import ai.ansight.runtime.AnsightChannels
|
|
9
|
+
import ai.ansight.runtime.AnsightCrashCaptureOptions
|
|
9
10
|
import ai.ansight.runtime.AnsightHostAutoProbeOptions
|
|
10
11
|
import ai.ansight.runtime.AnsightHostConnectionOptions
|
|
11
12
|
import ai.ansight.runtime.AnsightLogCallback
|
|
@@ -225,6 +226,21 @@ class AnsightReactNativeModule(
|
|
|
225
226
|
}.resolve(promise)
|
|
226
227
|
}
|
|
227
228
|
|
|
229
|
+
@ReactMethod
|
|
230
|
+
fun recordCrashCandidate(input: ReadableMap, promise: Promise) {
|
|
231
|
+
runCatching {
|
|
232
|
+
val candidateId = AnsightRuntime.recordCrashCandidate(
|
|
233
|
+
runtime = input.stringValue("runtime") ?: "react-native-javascript",
|
|
234
|
+
kind = input.stringValue("kind") ?: "unhandled_javascript_error",
|
|
235
|
+
message = input.stringValue("message"),
|
|
236
|
+
stack = input.stringValue("stack"),
|
|
237
|
+
fatal = input.booleanValue("fatal", false),
|
|
238
|
+
metadataJson = input.stringValue("metadata"),
|
|
239
|
+
)
|
|
240
|
+
Arguments.createMap().apply { putString("candidateId", candidateId) }
|
|
241
|
+
}.resolve(promise)
|
|
242
|
+
}
|
|
243
|
+
|
|
228
244
|
@ReactMethod
|
|
229
245
|
fun screenViewed(name: String, details: ReadableMap?, promise: Promise) {
|
|
230
246
|
runCatching {
|
|
@@ -693,6 +709,22 @@ class AnsightReactNativeModule(
|
|
|
693
709
|
if (map.hasBoolean("enableBatteryLevel")) {
|
|
694
710
|
options = options.copy(enableBatteryLevel = map.booleanValue("enableBatteryLevel", options.enableBatteryLevel))
|
|
695
711
|
}
|
|
712
|
+
if (map.hasBoolean("enableOpenFileHandleTracking")) {
|
|
713
|
+
options = options.copy(
|
|
714
|
+
enableOpenFileHandleTracking = map.booleanValue(
|
|
715
|
+
"enableOpenFileHandleTracking",
|
|
716
|
+
options.enableOpenFileHandleTracking,
|
|
717
|
+
),
|
|
718
|
+
)
|
|
719
|
+
}
|
|
720
|
+
if (map.hasBoolean("enableJniReferenceCountTracking")) {
|
|
721
|
+
options = options.copy(
|
|
722
|
+
enableJniReferenceCountTracking = map.booleanValue(
|
|
723
|
+
"enableJniReferenceCountTracking",
|
|
724
|
+
options.enableJniReferenceCountTracking,
|
|
725
|
+
),
|
|
726
|
+
)
|
|
727
|
+
}
|
|
696
728
|
if (map.hasMap("defaultMemoryChannels")) {
|
|
697
729
|
val memory = map.getMapOrNull("defaultMemoryChannels")
|
|
698
730
|
val managedHeap = if (memory.hasBoolean("managedHeap")) {
|
|
@@ -751,6 +783,10 @@ class AnsightReactNativeModule(
|
|
|
751
783
|
AnsightSessionJpegCaptureOptions.DefaultCaptureGpuBackedSurfaces,
|
|
752
784
|
),
|
|
753
785
|
mode = sessionJpegCaptureMode(jpeg.stringValue("mode")),
|
|
786
|
+
captureKeyboardPresence = jpeg.booleanValue(
|
|
787
|
+
"captureKeyboardPresence",
|
|
788
|
+
AnsightSessionJpegCaptureOptions.DefaultCaptureKeyboardPresence,
|
|
789
|
+
),
|
|
754
790
|
)
|
|
755
791
|
},
|
|
756
792
|
)
|
|
@@ -768,6 +804,24 @@ class AnsightReactNativeModule(
|
|
|
768
804
|
},
|
|
769
805
|
)
|
|
770
806
|
}
|
|
807
|
+
if (map.hasKey("crashCapture")) {
|
|
808
|
+
options = options.copy(
|
|
809
|
+
crashCapture = if (map.isFalse("crashCapture")) {
|
|
810
|
+
options.crashCapture.copy(enabled = false)
|
|
811
|
+
} else {
|
|
812
|
+
val crash = map.getMapOrNull("crashCapture")
|
|
813
|
+
AnsightCrashCaptureOptions(
|
|
814
|
+
enabled = crash.booleanValue("enabled", true),
|
|
815
|
+
studioHandoffEnabled = crash.booleanValue("studioHandoffEnabled", true),
|
|
816
|
+
offlineCaptureAttachmentEnabled = crash.booleanValue("offlineCaptureAttachmentEnabled", true),
|
|
817
|
+
maximumPendingReports = crash.intValue("maximumPendingReports", 8),
|
|
818
|
+
retentionDays = crash.intValue("retentionDays", 7),
|
|
819
|
+
maximumBreadcrumbs = crash.intValue("maximumBreadcrumbs", 64),
|
|
820
|
+
maximumTraceBytes = crash.intValue("maximumTraceBytes", 1_048_576),
|
|
821
|
+
)
|
|
822
|
+
},
|
|
823
|
+
)
|
|
824
|
+
}
|
|
771
825
|
if (map.hasString("toolGuard")) {
|
|
772
826
|
options = options.copy(toolGuard = toolGuard(map.stringValue("toolGuard")))
|
|
773
827
|
}
|
|
@@ -931,14 +985,18 @@ class AnsightReactNativeModule(
|
|
|
931
985
|
AnsightSessionJpegCaptureOptions.DefaultCaptureGpuBackedSurfaces,
|
|
932
986
|
),
|
|
933
987
|
mode = sessionJpegCaptureMode(map.stringValue("mode")),
|
|
988
|
+
captureKeyboardPresence = map.booleanValue(
|
|
989
|
+
"captureKeyboardPresence",
|
|
990
|
+
AnsightSessionJpegCaptureOptions.DefaultCaptureKeyboardPresence,
|
|
991
|
+
),
|
|
934
992
|
)
|
|
935
993
|
}
|
|
936
994
|
|
|
937
995
|
private fun sessionJpegCaptureMode(value: String?): AnsightSessionJpegCaptureMode =
|
|
938
|
-
|
|
939
|
-
AnsightSessionJpegCaptureMode.ScreenshotAndVisualTree
|
|
940
|
-
|
|
941
|
-
AnsightSessionJpegCaptureMode.ScreenshotOnly
|
|
996
|
+
when (value) {
|
|
997
|
+
"screenshotAndVisualTree" -> AnsightSessionJpegCaptureMode.ScreenshotAndVisualTree
|
|
998
|
+
"screenshotWithVisualTreeOnTouch" -> AnsightSessionJpegCaptureMode.ScreenshotWithVisualTreeOnTouch
|
|
999
|
+
else -> AnsightSessionJpegCaptureMode.ScreenshotOnly
|
|
942
1000
|
}
|
|
943
1001
|
|
|
944
1002
|
private fun toolDefinition(map: ReadableMap): ToolDefinition =
|
|
@@ -1111,6 +1169,8 @@ class AnsightReactNativeModule(
|
|
|
1111
1169
|
putInt("retentionPeriodSeconds", options.retentionPeriodSeconds)
|
|
1112
1170
|
putBoolean("enableFramesPerSecond", options.enableFramesPerSecond)
|
|
1113
1171
|
putBoolean("enableBatteryLevel", options.enableBatteryLevel)
|
|
1172
|
+
putBoolean("enableOpenFileHandleTracking", options.enableOpenFileHandleTracking)
|
|
1173
|
+
putBoolean("enableJniReferenceCountTracking", options.enableJniReferenceCountTracking)
|
|
1114
1174
|
putMap("defaultMemoryChannels", mapOf(
|
|
1115
1175
|
"managedHeap" to options.defaultMemoryChannels.javaHeap,
|
|
1116
1176
|
"javaHeap" to options.defaultMemoryChannels.javaHeap,
|
|
@@ -1127,10 +1187,11 @@ class AnsightReactNativeModule(
|
|
|
1127
1187
|
"quality" to capture.quality,
|
|
1128
1188
|
"maxWidth" to capture.maxWidth,
|
|
1129
1189
|
"captureGpuBackedSurfaces" to capture.captureGpuBackedSurfaces,
|
|
1130
|
-
"
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
"
|
|
1190
|
+
"captureKeyboardPresence" to capture.captureKeyboardPresence,
|
|
1191
|
+
"mode" to when (capture.mode) {
|
|
1192
|
+
AnsightSessionJpegCaptureMode.ScreenshotAndVisualTree -> "screenshotAndVisualTree"
|
|
1193
|
+
AnsightSessionJpegCaptureMode.ScreenshotWithVisualTreeOnTouch -> "screenshotWithVisualTreeOnTouch"
|
|
1194
|
+
else -> "screenshotOnly"
|
|
1134
1195
|
},
|
|
1135
1196
|
).toWritableMap())
|
|
1136
1197
|
} ?: putNull("sessionJpegCapture")
|
package/index.d.ts
CHANGED
|
@@ -37,7 +37,8 @@ export interface AnsightSessionJpegCaptureOptions {
|
|
|
37
37
|
quality?: number;
|
|
38
38
|
maxWidth?: number;
|
|
39
39
|
captureGpuBackedSurfaces?: boolean;
|
|
40
|
-
|
|
40
|
+
captureKeyboardPresence?: boolean;
|
|
41
|
+
mode?: "screenshotOnly" | "screenshotAndVisualTree" | "screenshotWithVisualTreeOnTouch";
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
export interface AnsightTouchCaptureOptions {
|
|
@@ -110,6 +111,8 @@ export interface AnsightOptions {
|
|
|
110
111
|
retentionPeriodSeconds?: number;
|
|
111
112
|
enableFramesPerSecond?: boolean;
|
|
112
113
|
enableBatteryLevel?: boolean;
|
|
114
|
+
enableOpenFileHandleTracking?: boolean;
|
|
115
|
+
enableJniReferenceCountTracking?: boolean;
|
|
113
116
|
defaultMemoryChannels?: {
|
|
114
117
|
managedHeap?: boolean;
|
|
115
118
|
physicalFootprint?: boolean;
|
|
@@ -121,6 +124,7 @@ export interface AnsightOptions {
|
|
|
121
124
|
reactNativeMemory?: false | AnsightReactNativeMemoryOptions;
|
|
122
125
|
sessionJpegCapture?: false | AnsightSessionJpegCaptureOptions;
|
|
123
126
|
touchCapture?: false | AnsightTouchCaptureOptions;
|
|
127
|
+
crashCapture?: false | AnsightCrashCaptureOptions;
|
|
124
128
|
lifecycleCapture?: {
|
|
125
129
|
enabled?: boolean;
|
|
126
130
|
captureAppLifecycle?: boolean;
|
|
@@ -153,6 +157,25 @@ export interface AnsightOptions {
|
|
|
153
157
|
lifecycle?: boolean;
|
|
154
158
|
}
|
|
155
159
|
|
|
160
|
+
export interface AnsightCrashCaptureOptions {
|
|
161
|
+
enabled?: boolean;
|
|
162
|
+
studioHandoffEnabled?: boolean;
|
|
163
|
+
offlineCaptureAttachmentEnabled?: boolean;
|
|
164
|
+
maximumPendingReports?: number;
|
|
165
|
+
retentionDays?: number;
|
|
166
|
+
maximumBreadcrumbs?: number;
|
|
167
|
+
maximumTraceBytes?: number;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface AnsightCrashCandidate {
|
|
171
|
+
runtime?: string;
|
|
172
|
+
kind?: string;
|
|
173
|
+
message?: string;
|
|
174
|
+
stack?: string;
|
|
175
|
+
fatal?: boolean;
|
|
176
|
+
metadata?: string | Record<string, string>;
|
|
177
|
+
}
|
|
178
|
+
|
|
156
179
|
export interface AnsightOperationResult {
|
|
157
180
|
success: boolean;
|
|
158
181
|
message: string;
|
|
@@ -251,6 +274,10 @@ export class AnsightOptionsBuilder {
|
|
|
251
274
|
withoutFramesPerSecond(): this;
|
|
252
275
|
withBatteryLevel(): this;
|
|
253
276
|
withoutBatteryLevel(): this;
|
|
277
|
+
withOpenFileHandleTracking(): this;
|
|
278
|
+
withoutOpenFileHandleTracking(): this;
|
|
279
|
+
withJniReferenceCountTracking(): this;
|
|
280
|
+
withoutJniReferenceCountTracking(): this;
|
|
254
281
|
withRetentionPeriodSeconds(retentionPeriodSeconds: number): this;
|
|
255
282
|
withAdditionalChannels(additionalChannels: AnsightChannel[]): this;
|
|
256
283
|
addAdditionalChannel(additionalChannel: AnsightChannel): this;
|
|
@@ -264,11 +291,14 @@ export class AnsightOptionsBuilder {
|
|
|
264
291
|
quality?: number,
|
|
265
292
|
maxWidth?: number | null,
|
|
266
293
|
captureGpuBackedSurfaces?: boolean,
|
|
267
|
-
mode?: "screenshotOnly" | "screenshotAndVisualTree"
|
|
294
|
+
mode?: "screenshotOnly" | "screenshotAndVisualTree" | "screenshotWithVisualTreeOnTouch",
|
|
295
|
+
captureKeyboardPresence?: boolean
|
|
268
296
|
): this;
|
|
269
297
|
withoutSessionJpegCapture(): this;
|
|
270
298
|
withTouchCapture(touchCapture?: AnsightTouchCaptureOptions): this;
|
|
271
299
|
withoutTouchCapture(): this;
|
|
300
|
+
withCrashCapture(crashCapture?: AnsightCrashCaptureOptions): this;
|
|
301
|
+
withoutCrashCapture(): this;
|
|
272
302
|
withLifecycleCapture(lifecycleCapture?: NonNullable<AnsightOptions["lifecycleCapture"]>): this;
|
|
273
303
|
withToolGuard(toolGuard: NonNullable<AnsightOptions["toolGuard"]>): this;
|
|
274
304
|
withToolsDisabled(): this;
|
|
@@ -512,6 +542,7 @@ export function metric(value: number, channel?: number): Promise<AnsightDebugSna
|
|
|
512
542
|
export function recordMetric(value: number, channel?: number): Promise<AnsightDebugSnapshot>;
|
|
513
543
|
export function event(input: string | { label: string; type?: string; details?: string; channel?: number }): Promise<AnsightDebugSnapshot>;
|
|
514
544
|
export function recordEvent(input: string | { label: string; type?: string; details?: string; channel?: number }): Promise<AnsightDebugSnapshot>;
|
|
545
|
+
export function recordCrashCandidate(input?: AnsightCrashCandidate): Promise<{ candidateId?: string }>;
|
|
515
546
|
export function screenViewed(name: string, details?: Record<string, string>): Promise<AnsightDebugSnapshot>;
|
|
516
547
|
export function trackRoute(name: string, details?: Record<string, string>): Promise<AnsightDebugSnapshot>;
|
|
517
548
|
export function setAppLifecycleState(state: AnsightLifecycleState): Promise<AnsightDebugSnapshot>;
|
|
@@ -585,6 +616,7 @@ declare const Ansight: {
|
|
|
585
616
|
recordMetric: typeof recordMetric;
|
|
586
617
|
event: typeof event;
|
|
587
618
|
recordEvent: typeof recordEvent;
|
|
619
|
+
recordCrashCandidate: typeof recordCrashCandidate;
|
|
588
620
|
screenViewed: typeof screenViewed;
|
|
589
621
|
trackRoute: typeof trackRoute;
|
|
590
622
|
setAppLifecycleState: typeof setAppLifecycleState;
|
package/index.js
CHANGED
|
@@ -61,6 +61,9 @@ function cloneOptions(options = {}) {
|
|
|
61
61
|
if (options.touchCapture && typeof options.touchCapture === "object") {
|
|
62
62
|
clone.touchCapture = { ...options.touchCapture };
|
|
63
63
|
}
|
|
64
|
+
if (options.crashCapture && typeof options.crashCapture === "object") {
|
|
65
|
+
clone.crashCapture = { ...options.crashCapture };
|
|
66
|
+
}
|
|
64
67
|
if (options.lifecycleCapture) {
|
|
65
68
|
clone.lifecycleCapture = { ...options.lifecycleCapture };
|
|
66
69
|
}
|
|
@@ -157,6 +160,8 @@ class AnsightOptionsBuilder {
|
|
|
157
160
|
retentionPeriodSeconds: 120,
|
|
158
161
|
enableFramesPerSecond: true,
|
|
159
162
|
enableBatteryLevel: false,
|
|
163
|
+
enableOpenFileHandleTracking: false,
|
|
164
|
+
enableJniReferenceCountTracking: false,
|
|
160
165
|
sessionJpegCapture: {
|
|
161
166
|
intervalMilliseconds: 2000,
|
|
162
167
|
quality: 60,
|
|
@@ -209,6 +214,26 @@ class AnsightOptionsBuilder {
|
|
|
209
214
|
return this;
|
|
210
215
|
}
|
|
211
216
|
|
|
217
|
+
withOpenFileHandleTracking() {
|
|
218
|
+
this._options.enableOpenFileHandleTracking = true;
|
|
219
|
+
return this;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
withoutOpenFileHandleTracking() {
|
|
223
|
+
this._options.enableOpenFileHandleTracking = false;
|
|
224
|
+
return this;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
withJniReferenceCountTracking() {
|
|
228
|
+
this._options.enableJniReferenceCountTracking = true;
|
|
229
|
+
return this;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
withoutJniReferenceCountTracking() {
|
|
233
|
+
this._options.enableJniReferenceCountTracking = false;
|
|
234
|
+
return this;
|
|
235
|
+
}
|
|
236
|
+
|
|
212
237
|
withRetentionPeriodSeconds(retentionPeriodSeconds) {
|
|
213
238
|
this._options.retentionPeriodSeconds = retentionPeriodSeconds;
|
|
214
239
|
return this;
|
|
@@ -269,6 +294,7 @@ class AnsightOptionsBuilder {
|
|
|
269
294
|
maxWidth: arguments.length > 2 ? arguments[2] : 480,
|
|
270
295
|
captureGpuBackedSurfaces: arguments.length > 3 ? arguments[3] : true,
|
|
271
296
|
mode: arguments.length > 4 ? arguments[4] : "screenshotOnly",
|
|
297
|
+
captureKeyboardPresence: arguments.length > 5 ? arguments[5] : false,
|
|
272
298
|
};
|
|
273
299
|
return this;
|
|
274
300
|
}
|
|
@@ -276,6 +302,7 @@ class AnsightOptionsBuilder {
|
|
|
276
302
|
intervalMilliseconds: 2000,
|
|
277
303
|
quality: 60,
|
|
278
304
|
maxWidth: 480,
|
|
305
|
+
captureKeyboardPresence: false,
|
|
279
306
|
mode: "screenshotOnly",
|
|
280
307
|
...(optionsOrIntervalMilliseconds || {}),
|
|
281
308
|
};
|
|
@@ -297,6 +324,16 @@ class AnsightOptionsBuilder {
|
|
|
297
324
|
return this;
|
|
298
325
|
}
|
|
299
326
|
|
|
327
|
+
withCrashCapture(crashCapture = {}) {
|
|
328
|
+
this._options.crashCapture = { ...crashCapture, enabled: true };
|
|
329
|
+
return this;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
withoutCrashCapture() {
|
|
333
|
+
this._options.crashCapture = false;
|
|
334
|
+
return this;
|
|
335
|
+
}
|
|
336
|
+
|
|
300
337
|
withLifecycleCapture(lifecycleCapture = {}) {
|
|
301
338
|
this._options.lifecycleCapture = { ...lifecycleCapture };
|
|
302
339
|
return this;
|
|
@@ -1391,7 +1428,7 @@ function summarizeProps(props) {
|
|
|
1391
1428
|
}
|
|
1392
1429
|
const keys = Object.keys(props).filter((key) => key !== "children");
|
|
1393
1430
|
const summary = { keys };
|
|
1394
|
-
["testID", "nativeID", "accessibilityLabel", "role"].forEach((key) => {
|
|
1431
|
+
["testID", "nativeID", "accessibilityLabel", "accessibilityRole", "role"].forEach((key) => {
|
|
1395
1432
|
if (props[key] != null && !isSensitiveKey(key)) {
|
|
1396
1433
|
summary[key] = String(props[key]);
|
|
1397
1434
|
}
|
|
@@ -1403,6 +1440,42 @@ function summarizeProps(props) {
|
|
|
1403
1440
|
return summary;
|
|
1404
1441
|
}
|
|
1405
1442
|
|
|
1443
|
+
function reactSemanticRole(type, props, fiberTag) {
|
|
1444
|
+
const declared = props && (props.accessibilityRole || props.role);
|
|
1445
|
+
if (declared) return String(declared).toLowerCase();
|
|
1446
|
+
if (fiberTag === 6 || /text/i.test(type)) return "text";
|
|
1447
|
+
if (/button|pressable|touchable/i.test(type)) return "button";
|
|
1448
|
+
if (/textinput/i.test(type)) return "textbox";
|
|
1449
|
+
if (/switch/i.test(type)) return "switch";
|
|
1450
|
+
if (/scrollview|flatlist|sectionlist/i.test(type)) return "scrollview";
|
|
1451
|
+
return "view";
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
function reactSupportedActions(type, props) {
|
|
1455
|
+
const actions = [];
|
|
1456
|
+
if (props && typeof props.onPress === "function") actions.push("tap");
|
|
1457
|
+
if (props && (typeof props.onChangeText === "function" || /textinput/i.test(type))) {
|
|
1458
|
+
actions.push("typeText", "focus");
|
|
1459
|
+
}
|
|
1460
|
+
if (/scrollview|flatlist|sectionlist/i.test(type) || (props && typeof props.onScroll === "function")) {
|
|
1461
|
+
actions.push("scroll", "swipe");
|
|
1462
|
+
}
|
|
1463
|
+
return actions;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
function applyReactTargetability(node, fiber, props, type) {
|
|
1467
|
+
const style = StyleSheet && typeof StyleSheet.flatten === "function"
|
|
1468
|
+
? StyleSheet.flatten(props && props.style)
|
|
1469
|
+
: props && props.style;
|
|
1470
|
+
node.text = node.label || (node.visual && node.visual.text) || null;
|
|
1471
|
+
node.role = reactSemanticRole(type, props, fiber.tag);
|
|
1472
|
+
node.supportedActions = reactSupportedActions(type, props);
|
|
1473
|
+
node.visible = !(style && style.display === "none") && !(props && props.accessibilityElementsHidden);
|
|
1474
|
+
node.enabled = !(props && (props.disabled === true || props.accessibilityState && props.accessibilityState.disabled === true));
|
|
1475
|
+
node.focusable = !!(props && props.focusable) || node.supportedActions.includes("focus");
|
|
1476
|
+
node.interactable = node.visible && node.enabled && node.supportedActions.length > 0;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1406
1479
|
function reactColorToArgbHex(value) {
|
|
1407
1480
|
if (value == null) {
|
|
1408
1481
|
return undefined;
|
|
@@ -1453,6 +1526,28 @@ function createReactVisual(fiber, props, maxStringLength) {
|
|
|
1453
1526
|
return visual;
|
|
1454
1527
|
}
|
|
1455
1528
|
|
|
1529
|
+
function createReactZIndex(props) {
|
|
1530
|
+
const style = StyleSheet && typeof StyleSheet.flatten === "function"
|
|
1531
|
+
? StyleSheet.flatten(props && props.style)
|
|
1532
|
+
: props && props.style;
|
|
1533
|
+
const zIndex = style && style.zIndex != null ? Number(style.zIndex) : NaN;
|
|
1534
|
+
const elevation = style && style.elevation != null ? Number(style.elevation) : NaN;
|
|
1535
|
+
if (Number.isFinite(zIndex) && zIndex !== 0) return zIndex;
|
|
1536
|
+
if (Number.isFinite(elevation) && elevation !== 0) return elevation;
|
|
1537
|
+
return null;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
function registerReactType(context, typeName) {
|
|
1541
|
+
const normalizedTypeName = String(typeName || "UnknownComponent").trim() || "UnknownComponent";
|
|
1542
|
+
const existingTypeId = context.typeIdsByName.get(normalizedTypeName);
|
|
1543
|
+
if (existingTypeId != null) return existingTypeId;
|
|
1544
|
+
|
|
1545
|
+
const typeId = context.types.length;
|
|
1546
|
+
context.types.push(normalizedTypeName);
|
|
1547
|
+
context.typeIdsByName.set(normalizedTypeName, typeId);
|
|
1548
|
+
return typeId;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1456
1551
|
function nativeTagForFiber(fiber) {
|
|
1457
1552
|
const stateNode = fiber && fiber.stateNode;
|
|
1458
1553
|
if (!stateNode) {
|
|
@@ -1524,17 +1619,17 @@ function serializeFiber(fiber, context, depth) {
|
|
|
1524
1619
|
|
|
1525
1620
|
const props = fiber.memoizedProps || fiber.pendingProps || {};
|
|
1526
1621
|
const type = reactFiberTypeName(fiber);
|
|
1527
|
-
const kind = reactFiberKind(fiber.tag);
|
|
1528
1622
|
const node = {
|
|
1529
1623
|
id: reactFiberId(fiber),
|
|
1530
|
-
type,
|
|
1531
|
-
kind,
|
|
1624
|
+
typeId: registerReactType(context, type),
|
|
1532
1625
|
tag: fiber.tag,
|
|
1533
1626
|
key: fiber.key == null ? null : String(fiber.key),
|
|
1534
1627
|
depth,
|
|
1535
1628
|
visual: createReactVisual(fiber, props, context.maxStringLength),
|
|
1536
1629
|
children: [],
|
|
1537
1630
|
};
|
|
1631
|
+
const zIndex = createReactZIndex(props);
|
|
1632
|
+
if (zIndex != null) node.z = zIndex;
|
|
1538
1633
|
|
|
1539
1634
|
if (fiber._debugSource) {
|
|
1540
1635
|
node.source = {
|
|
@@ -1546,7 +1641,7 @@ function serializeFiber(fiber, context, depth) {
|
|
|
1546
1641
|
|
|
1547
1642
|
const owner = fiber._debugOwner && reactFiberTypeName(fiber._debugOwner);
|
|
1548
1643
|
if (owner) {
|
|
1549
|
-
node.
|
|
1644
|
+
node.ownerTypeId = registerReactType(context, owner);
|
|
1550
1645
|
}
|
|
1551
1646
|
|
|
1552
1647
|
const nativeTag = nativeTagForFiber(fiber);
|
|
@@ -1577,6 +1672,8 @@ function serializeFiber(fiber, context, depth) {
|
|
|
1577
1672
|
node.state = sanitizeValue(fiber.memoizedState, context);
|
|
1578
1673
|
}
|
|
1579
1674
|
|
|
1675
|
+
applyReactTargetability(node, fiber, props, type);
|
|
1676
|
+
|
|
1580
1677
|
if (depth < context.maxDepth) {
|
|
1581
1678
|
let child = fiber.child;
|
|
1582
1679
|
while (child) {
|
|
@@ -1600,16 +1697,18 @@ function isShadowTreeFiber(fiber) {
|
|
|
1600
1697
|
|
|
1601
1698
|
function createShadowTreeNode(fiber, context, depth) {
|
|
1602
1699
|
const props = fiber.memoizedProps || fiber.pendingProps || {};
|
|
1700
|
+
const type = reactFiberTypeName(fiber);
|
|
1603
1701
|
const node = {
|
|
1604
1702
|
id: reactFiberId(fiber),
|
|
1605
|
-
|
|
1606
|
-
kind: fiber.tag === 3 ? "root" : fiber.tag === 6 ? "text" : "host",
|
|
1703
|
+
typeId: registerReactType(context, type),
|
|
1607
1704
|
tag: fiber.tag,
|
|
1608
1705
|
key: fiber.key == null ? null : String(fiber.key),
|
|
1609
1706
|
depth,
|
|
1610
1707
|
visual: createReactVisual(fiber, props, context.maxStringLength),
|
|
1611
1708
|
children: [],
|
|
1612
1709
|
};
|
|
1710
|
+
const zIndex = createReactZIndex(props);
|
|
1711
|
+
if (zIndex != null) node.z = zIndex;
|
|
1613
1712
|
|
|
1614
1713
|
const nativeTag = nativeTagForFiber(fiber);
|
|
1615
1714
|
if (nativeTag != null) {
|
|
@@ -1635,6 +1734,8 @@ function createShadowTreeNode(fiber, context, depth) {
|
|
|
1635
1734
|
}
|
|
1636
1735
|
}
|
|
1637
1736
|
|
|
1737
|
+
applyReactTargetability(node, fiber, props, type);
|
|
1738
|
+
|
|
1638
1739
|
return node;
|
|
1639
1740
|
}
|
|
1640
1741
|
|
|
@@ -1691,19 +1792,21 @@ async function captureReactVisualTree(rawOptions = {}) {
|
|
|
1691
1792
|
maxStringLength: rawOptions.maxStringLength || 180,
|
|
1692
1793
|
maxValueDepth: rawOptions.maxValueDepth || 2,
|
|
1693
1794
|
nodesWithNativeTags: [],
|
|
1795
|
+
typeIdsByName: new Map(),
|
|
1796
|
+
types: [],
|
|
1694
1797
|
count: 0,
|
|
1695
1798
|
truncated: false,
|
|
1696
1799
|
visited: new Set(),
|
|
1697
1800
|
};
|
|
1698
1801
|
const roots = getReactRoots();
|
|
1699
|
-
const rootNodes = roots.roots.map((root
|
|
1802
|
+
const rootNodes = roots.roots.map((root) => {
|
|
1700
1803
|
const node = serializeFiber(root.fiber, context, 0);
|
|
1701
1804
|
if (node) {
|
|
1702
1805
|
node.rendererId = root.rendererId;
|
|
1703
|
-
node.rootIndex = index;
|
|
1704
1806
|
}
|
|
1705
1807
|
return node;
|
|
1706
1808
|
}).filter(Boolean);
|
|
1809
|
+
const rootTypeId = registerReactType(context, "ReactRoots");
|
|
1707
1810
|
|
|
1708
1811
|
if (includeBounds && context.nodesWithNativeTags.length > 0) {
|
|
1709
1812
|
await Promise.all(context.nodesWithNativeTags.slice(0, 300).map(async (node) => {
|
|
@@ -1715,6 +1818,7 @@ async function captureReactVisualTree(rawOptions = {}) {
|
|
|
1715
1818
|
}
|
|
1716
1819
|
|
|
1717
1820
|
return {
|
|
1821
|
+
format: "ansight.react.visual-tree.compact.v2",
|
|
1718
1822
|
platform: Platform.OS,
|
|
1719
1823
|
source: "react",
|
|
1720
1824
|
adapter: "react.fiber",
|
|
@@ -1724,11 +1828,11 @@ async function captureReactVisualTree(rawOptions = {}) {
|
|
|
1724
1828
|
renderers: roots.renderers,
|
|
1725
1829
|
root: {
|
|
1726
1830
|
id: "react:roots",
|
|
1727
|
-
|
|
1728
|
-
|
|
1831
|
+
typeId: rootTypeId,
|
|
1832
|
+
childCount: rootNodes.length,
|
|
1729
1833
|
children: rootNodes,
|
|
1730
1834
|
},
|
|
1731
|
-
|
|
1835
|
+
types: context.types,
|
|
1732
1836
|
nodeCount: context.count,
|
|
1733
1837
|
truncated: context.truncated,
|
|
1734
1838
|
unavailableReason: roots.hookAvailable ? undefined : "React DevTools global hook is not available in this runtime.",
|
|
@@ -1748,19 +1852,21 @@ async function captureReactShadowTree(rawOptions = {}) {
|
|
|
1748
1852
|
maxStringLength: rawOptions.maxStringLength || 180,
|
|
1749
1853
|
maxValueDepth: rawOptions.maxValueDepth || 2,
|
|
1750
1854
|
nodesWithNativeTags: [],
|
|
1855
|
+
typeIdsByName: new Map(),
|
|
1856
|
+
types: [],
|
|
1751
1857
|
count: 0,
|
|
1752
1858
|
truncated: false,
|
|
1753
1859
|
visited: new Set(),
|
|
1754
1860
|
};
|
|
1755
1861
|
const roots = getReactRoots();
|
|
1756
|
-
const rootNodes = roots.roots.flatMap((root
|
|
1862
|
+
const rootNodes = roots.roots.flatMap((root) => {
|
|
1757
1863
|
const nodes = serializeShadowFiber(root.fiber, context, 0);
|
|
1758
1864
|
nodes.forEach((node) => {
|
|
1759
1865
|
node.rendererId = root.rendererId;
|
|
1760
|
-
node.rootIndex = index;
|
|
1761
1866
|
});
|
|
1762
1867
|
return nodes;
|
|
1763
1868
|
});
|
|
1869
|
+
const rootTypeId = registerReactType(context, "ReactNativeShadowRoots");
|
|
1764
1870
|
|
|
1765
1871
|
if (includeBounds && context.nodesWithNativeTags.length > 0) {
|
|
1766
1872
|
await Promise.all(context.nodesWithNativeTags.slice(0, 300).map(async (node) => {
|
|
@@ -1772,6 +1878,7 @@ async function captureReactShadowTree(rawOptions = {}) {
|
|
|
1772
1878
|
}
|
|
1773
1879
|
|
|
1774
1880
|
return {
|
|
1881
|
+
format: "ansight.react.visual-tree.compact.v2",
|
|
1775
1882
|
platform: Platform.OS,
|
|
1776
1883
|
source: "react-native",
|
|
1777
1884
|
adapter: "react-native.host-fiber",
|
|
@@ -1781,11 +1888,11 @@ async function captureReactShadowTree(rawOptions = {}) {
|
|
|
1781
1888
|
renderers: roots.renderers,
|
|
1782
1889
|
root: {
|
|
1783
1890
|
id: "react:shadow-roots",
|
|
1784
|
-
|
|
1785
|
-
|
|
1891
|
+
typeId: rootTypeId,
|
|
1892
|
+
childCount: rootNodes.length,
|
|
1786
1893
|
children: rootNodes,
|
|
1787
1894
|
},
|
|
1788
|
-
|
|
1895
|
+
types: context.types,
|
|
1789
1896
|
nodeCount: context.count,
|
|
1790
1897
|
truncated: context.truncated,
|
|
1791
1898
|
unavailableReason: roots.hookAvailable ? undefined : "React DevTools global hook is not available in this runtime.",
|
|
@@ -1801,13 +1908,18 @@ function flattenReactTree(node, output = []) {
|
|
|
1801
1908
|
return output;
|
|
1802
1909
|
}
|
|
1803
1910
|
|
|
1804
|
-
function
|
|
1911
|
+
function createReactNodeSnapshot(node) {
|
|
1912
|
+
const snapshot = { ...node };
|
|
1913
|
+
delete snapshot.children;
|
|
1914
|
+
return snapshot;
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
function nodeSearchText(node, types) {
|
|
1805
1918
|
return [
|
|
1806
1919
|
node.id,
|
|
1807
|
-
node.
|
|
1808
|
-
node.kind,
|
|
1920
|
+
types[node.typeId],
|
|
1809
1921
|
node.label,
|
|
1810
|
-
node.
|
|
1922
|
+
types[node.ownerTypeId],
|
|
1811
1923
|
node.propsSummary && node.propsSummary.testID,
|
|
1812
1924
|
node.propsSummary && node.propsSummary.nativeID,
|
|
1813
1925
|
node.propsSummary && node.propsSummary.accessibilityLabel,
|
|
@@ -1815,14 +1927,14 @@ function nodeSearchText(node) {
|
|
|
1815
1927
|
].filter(Boolean).join(" ").toLowerCase();
|
|
1816
1928
|
}
|
|
1817
1929
|
|
|
1818
|
-
function matchesReactNode(node, args) {
|
|
1930
|
+
function matchesReactNode(node, args, types) {
|
|
1819
1931
|
const query = args.query ? String(args.query).toLowerCase() : null;
|
|
1820
1932
|
const type = args.type ? String(args.type).toLowerCase() : null;
|
|
1821
1933
|
const testID = args.testID ? String(args.testID).toLowerCase() : null;
|
|
1822
1934
|
const text = args.text ? String(args.text).toLowerCase() : null;
|
|
1823
|
-
const searchText = nodeSearchText(node);
|
|
1935
|
+
const searchText = nodeSearchText(node, types);
|
|
1824
1936
|
return (!query || searchText.includes(query)) &&
|
|
1825
|
-
(!type || String(node.
|
|
1937
|
+
(!type || String(types[node.typeId] || "").toLowerCase().includes(type)) &&
|
|
1826
1938
|
(!testID || String((node.propsSummary && node.propsSummary.testID) || "").toLowerCase() === testID) &&
|
|
1827
1939
|
(!text || searchText.includes(text));
|
|
1828
1940
|
}
|
|
@@ -2043,12 +2155,14 @@ function installReactTools(options = {}) {
|
|
|
2043
2155
|
});
|
|
2044
2156
|
const maxResults = parseInteger(args.maxResults, 50, 1, 500);
|
|
2045
2157
|
const matches = flattenReactTree(tree.root)
|
|
2046
|
-
.filter((node) => node.id !== "react:roots" && matchesReactNode(node, args))
|
|
2047
|
-
.slice(0, maxResults)
|
|
2158
|
+
.filter((node) => node.id !== "react:roots" && matchesReactNode(node, args, tree.types))
|
|
2159
|
+
.slice(0, maxResults)
|
|
2160
|
+
.map(createReactNodeSnapshot);
|
|
2048
2161
|
return {
|
|
2049
2162
|
success: tree.hookAvailable,
|
|
2050
2163
|
message: `Found ${matches.length} React component(s).`,
|
|
2051
2164
|
result: {
|
|
2165
|
+
types: tree.types,
|
|
2052
2166
|
matches,
|
|
2053
2167
|
count: matches.length,
|
|
2054
2168
|
truncated: matches.length === maxResults,
|
|
@@ -2060,7 +2174,11 @@ function installReactTools(options = {}) {
|
|
|
2060
2174
|
const tree = await captureReactVisualTree(reactToolOptions(options, args));
|
|
2061
2175
|
const node = flattenReactTree(tree.root).find((candidate) => candidate.id === args.nodeId);
|
|
2062
2176
|
return node
|
|
2063
|
-
? {
|
|
2177
|
+
? {
|
|
2178
|
+
success: true,
|
|
2179
|
+
message: "React component captured.",
|
|
2180
|
+
result: { types: tree.types, node: createReactNodeSnapshot(node) },
|
|
2181
|
+
}
|
|
2064
2182
|
: { success: false, message: `React component '${args.nodeId}' was not found.`, errorCode: "react_component_not_found" };
|
|
2065
2183
|
}
|
|
2066
2184
|
|
|
@@ -2101,7 +2219,7 @@ function installReactTools(options = {}) {
|
|
|
2101
2219
|
result: {
|
|
2102
2220
|
nodeId: args.nodeId,
|
|
2103
2221
|
prop,
|
|
2104
|
-
type: node.
|
|
2222
|
+
type: tree.types[node.typeId],
|
|
2105
2223
|
},
|
|
2106
2224
|
};
|
|
2107
2225
|
}
|
|
@@ -2137,6 +2255,14 @@ function installErrorHandlers(options = {}) {
|
|
|
2137
2255
|
|
|
2138
2256
|
if (global.ErrorUtils && global.ErrorUtils.setGlobalHandler) {
|
|
2139
2257
|
global.ErrorUtils.setGlobalHandler((error, isFatal) => {
|
|
2258
|
+
nativeModule.recordCrashCandidate({
|
|
2259
|
+
runtime: "react-native-javascript",
|
|
2260
|
+
kind: "unhandled_javascript_error",
|
|
2261
|
+
message: error && error.message,
|
|
2262
|
+
stack: error && error.stack,
|
|
2263
|
+
fatal: !!isFatal,
|
|
2264
|
+
metadata: JSON.stringify({ name: error && error.name }),
|
|
2265
|
+
}).catch(() => {});
|
|
2140
2266
|
nativeModule.recordEvent({
|
|
2141
2267
|
label: error && error.message ? error.message : "Unhandled JavaScript error",
|
|
2142
2268
|
type: "Exception",
|
|
@@ -2159,6 +2285,13 @@ function installErrorHandlers(options = {}) {
|
|
|
2159
2285
|
global.__ansightUnhandledRejectionTrackingInstalled = true;
|
|
2160
2286
|
global.addEventListener("unhandledrejection", (event) => {
|
|
2161
2287
|
const reason = event && event.reason;
|
|
2288
|
+
nativeModule.recordCrashCandidate({
|
|
2289
|
+
runtime: "react-native-javascript",
|
|
2290
|
+
kind: "unhandled_promise_rejection",
|
|
2291
|
+
message: reason && reason.message ? reason.message : String(reason),
|
|
2292
|
+
stack: reason && reason.stack,
|
|
2293
|
+
fatal: false,
|
|
2294
|
+
}).catch(() => {});
|
|
2162
2295
|
nativeModule.recordEvent({
|
|
2163
2296
|
label: reason && reason.message ? reason.message : "Unhandled JavaScript promise rejection",
|
|
2164
2297
|
type: "Exception",
|
|
@@ -2253,6 +2386,16 @@ function recordEvent(input) {
|
|
|
2253
2386
|
return nativeModule.recordEvent(input || {});
|
|
2254
2387
|
}
|
|
2255
2388
|
|
|
2389
|
+
function recordCrashCandidate(input = {}) {
|
|
2390
|
+
const metadata = input.metadata && typeof input.metadata === "object"
|
|
2391
|
+
? JSON.stringify(input.metadata)
|
|
2392
|
+
: input.metadata;
|
|
2393
|
+
return nativeModule.recordCrashCandidate({
|
|
2394
|
+
...input,
|
|
2395
|
+
metadata,
|
|
2396
|
+
});
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2256
2399
|
function screenViewed(name, details) {
|
|
2257
2400
|
return nativeModule.screenViewed(name, details || {});
|
|
2258
2401
|
}
|
|
@@ -2295,6 +2438,7 @@ const Ansight = {
|
|
|
2295
2438
|
recordMetric: (value, channel = 255) => nativeModule.recordMetric(value, channel),
|
|
2296
2439
|
event: recordEvent,
|
|
2297
2440
|
recordEvent,
|
|
2441
|
+
recordCrashCandidate,
|
|
2298
2442
|
screenViewed,
|
|
2299
2443
|
trackRoute,
|
|
2300
2444
|
setAppLifecycleState: (state) => nativeModule.setAppLifecycleState(state),
|
|
@@ -292,6 +292,31 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
+
@objc(recordCrashCandidate:resolver:rejecter:)
|
|
296
|
+
func recordCrashCandidate(
|
|
297
|
+
_ input: NSDictionary,
|
|
298
|
+
resolver resolve: RCTPromiseResolveBlock,
|
|
299
|
+
rejecter reject: RCTPromiseRejectBlock
|
|
300
|
+
) {
|
|
301
|
+
let metadata: [String: String]
|
|
302
|
+
if let metadataJson = stringValue(input, "metadata"),
|
|
303
|
+
let data = metadataJson.data(using: .utf8),
|
|
304
|
+
let decoded = try? JSONDecoder().decode([String: String].self, from: data) {
|
|
305
|
+
metadata = decoded
|
|
306
|
+
} else {
|
|
307
|
+
metadata = [:]
|
|
308
|
+
}
|
|
309
|
+
let candidateId = AnsightRuntime.shared.recordCrashCandidate(
|
|
310
|
+
runtime: stringValue(input, "runtime") ?? "react-native-javascript",
|
|
311
|
+
kind: stringValue(input, "kind") ?? "unhandled_javascript_error",
|
|
312
|
+
message: stringValue(input, "message"),
|
|
313
|
+
stack: stringValue(input, "stack"),
|
|
314
|
+
fatal: boolValue(input, "fatal", defaultValue: false),
|
|
315
|
+
metadata: metadata
|
|
316
|
+
)
|
|
317
|
+
resolve(candidateId.map { ["candidateId": $0] } ?? [:])
|
|
318
|
+
}
|
|
319
|
+
|
|
295
320
|
@objc(screenViewed:details:resolver:rejecter:)
|
|
296
321
|
func screenViewed(
|
|
297
322
|
_ name: NSString,
|
|
@@ -895,6 +920,13 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
895
920
|
if hasBool(dictionary, "enableBatteryLevel") {
|
|
896
921
|
options.enableBatteryLevel = boolValue(dictionary, "enableBatteryLevel", defaultValue: options.enableBatteryLevel)
|
|
897
922
|
}
|
|
923
|
+
if hasBool(dictionary, "enableOpenFileHandleTracking") {
|
|
924
|
+
options.enableOpenFileHandleTracking = boolValue(
|
|
925
|
+
dictionary,
|
|
926
|
+
"enableOpenFileHandleTracking",
|
|
927
|
+
defaultValue: options.enableOpenFileHandleTracking
|
|
928
|
+
)
|
|
929
|
+
}
|
|
898
930
|
if let memory = dictionary?["defaultMemoryChannels"] as? NSDictionary {
|
|
899
931
|
var channels: DefaultMemoryChannels = []
|
|
900
932
|
if boolValue(memory, "managedHeap", defaultValue: boolValue(memory, "javaHeap", defaultValue: false)) {
|
|
@@ -942,9 +974,14 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
942
974
|
"captureGpuBackedSurfaces",
|
|
943
975
|
defaultValue: AnsightSessionJpegCaptureOptions.defaultCaptureGpuBackedSurfaces
|
|
944
976
|
),
|
|
945
|
-
mode:
|
|
946
|
-
|
|
947
|
-
|
|
977
|
+
mode: AnsightSessionJpegCaptureMode(
|
|
978
|
+
rawValue: stringValue(jpeg, "mode") ?? ""
|
|
979
|
+
) ?? .screenshotOnly,
|
|
980
|
+
captureKeyboardPresence: boolValue(
|
|
981
|
+
jpeg,
|
|
982
|
+
"captureKeyboardPresence",
|
|
983
|
+
defaultValue: AnsightSessionJpegCaptureOptions.defaultCaptureKeyboardPresence
|
|
984
|
+
)
|
|
948
985
|
)
|
|
949
986
|
}
|
|
950
987
|
}
|
|
@@ -960,6 +997,21 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
960
997
|
)
|
|
961
998
|
}
|
|
962
999
|
}
|
|
1000
|
+
if let raw = dictionary?["crashCapture"] {
|
|
1001
|
+
if (raw as? Bool) == false {
|
|
1002
|
+
options.crashCapture.enabled = false
|
|
1003
|
+
} else if let crash = raw as? NSDictionary {
|
|
1004
|
+
options.crashCapture = AnsightCrashCaptureOptions(
|
|
1005
|
+
enabled: boolValue(crash, "enabled", defaultValue: true),
|
|
1006
|
+
studioHandoffEnabled: boolValue(crash, "studioHandoffEnabled", defaultValue: true),
|
|
1007
|
+
offlineCaptureAttachmentEnabled: boolValue(crash, "offlineCaptureAttachmentEnabled", defaultValue: true),
|
|
1008
|
+
maximumPendingReports: intValue(crash, "maximumPendingReports", defaultValue: 8),
|
|
1009
|
+
retentionDays: intValue(crash, "retentionDays", defaultValue: 7),
|
|
1010
|
+
maximumBreadcrumbs: intValue(crash, "maximumBreadcrumbs", defaultValue: 64),
|
|
1011
|
+
maximumTraceBytes: intValue(crash, "maximumTraceBytes", defaultValue: 1_048_576)
|
|
1012
|
+
)
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
963
1015
|
if let lifecycle = dictionary?["lifecycleCapture"] as? NSDictionary {
|
|
964
1016
|
options.lifecycleCapture = AnsightLifecycleCaptureOptions(
|
|
965
1017
|
enabled: boolValue(lifecycle, "enabled", defaultValue: options.lifecycleCapture.enabled),
|
|
@@ -1271,9 +1323,14 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
1271
1323
|
"captureGpuBackedSurfaces",
|
|
1272
1324
|
defaultValue: AnsightSessionJpegCaptureOptions.defaultCaptureGpuBackedSurfaces
|
|
1273
1325
|
),
|
|
1274
|
-
mode:
|
|
1275
|
-
|
|
1276
|
-
|
|
1326
|
+
mode: AnsightSessionJpegCaptureMode(
|
|
1327
|
+
rawValue: stringValue(dictionary, "mode") ?? ""
|
|
1328
|
+
) ?? .screenshotOnly,
|
|
1329
|
+
captureKeyboardPresence: boolValue(
|
|
1330
|
+
dictionary,
|
|
1331
|
+
"captureKeyboardPresence",
|
|
1332
|
+
defaultValue: AnsightSessionJpegCaptureOptions.defaultCaptureKeyboardPresence
|
|
1333
|
+
)
|
|
1277
1334
|
)
|
|
1278
1335
|
}
|
|
1279
1336
|
|
|
@@ -1283,6 +1340,8 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
1283
1340
|
"retentionPeriodSeconds": options.retentionPeriodSeconds,
|
|
1284
1341
|
"enableFramesPerSecond": options.enableFramesPerSecond,
|
|
1285
1342
|
"enableBatteryLevel": options.enableBatteryLevel,
|
|
1343
|
+
"enableOpenFileHandleTracking": options.enableOpenFileHandleTracking,
|
|
1344
|
+
"enableJniReferenceCountTracking": false,
|
|
1286
1345
|
"defaultMemoryChannels": [
|
|
1287
1346
|
"managedHeap": options.defaultMemoryChannels.contains(.managedHeap),
|
|
1288
1347
|
"javaHeap": options.defaultMemoryChannels.contains(.managedHeap),
|
|
@@ -1322,6 +1381,7 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
1322
1381
|
"quality": capture.quality,
|
|
1323
1382
|
"maxWidth": capture.maxWidth as Any,
|
|
1324
1383
|
"captureGpuBackedSurfaces": capture.captureGpuBackedSurfaces,
|
|
1384
|
+
"captureKeyboardPresence": capture.captureKeyboardPresence,
|
|
1325
1385
|
"mode": capture.mode.rawValue,
|
|
1326
1386
|
]
|
|
1327
1387
|
} else {
|
|
@@ -33,6 +33,10 @@ RCT_EXTERN_METHOD(recordEvent:(NSDictionary *)input
|
|
|
33
33
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
34
34
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
35
35
|
|
|
36
|
+
RCT_EXTERN_METHOD(recordCrashCandidate:(NSDictionary *)input
|
|
37
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
38
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
39
|
+
|
|
36
40
|
RCT_EXTERN_METHOD(screenViewed:(NSString *)name
|
|
37
41
|
details:(NSDictionary *)details
|
|
38
42
|
resolver:(RCTPromiseResolveBlock)resolve
|