@ansight/react-native 1.3.0-preview.1 → 1.3.0-preview.11
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 +65 -3
- package/android/build.gradle +2 -2
- package/android/src/main/kotlin/ai/ansight/reactnative/AnsightReactNativeModule.kt +97 -8
- package/index.d.ts +127 -2
- package/index.js +375 -34
- package/ios/AnsightReactNative.swift +104 -7
- package/ios/AnsightReactNativeModule.m +8 -0
- package/network.js +764 -0
- package/package.json +5 -2
- package/session-properties.js +151 -0
package/index.js
CHANGED
|
@@ -10,6 +10,15 @@ const {
|
|
|
10
10
|
findNodeHandle,
|
|
11
11
|
processColor,
|
|
12
12
|
} = require("react-native");
|
|
13
|
+
const { version: reactVersion } = require("react");
|
|
14
|
+
const {
|
|
15
|
+
createAutomaticSessionProperties,
|
|
16
|
+
mergeSessionProperties,
|
|
17
|
+
} = require("./session-properties");
|
|
18
|
+
const {
|
|
19
|
+
installNetworkCapture: installNetworkCaptureCore,
|
|
20
|
+
sanitizeNetworkRequest,
|
|
21
|
+
} = require("./network");
|
|
13
22
|
|
|
14
23
|
const nativeModule = NativeModules.AnsightReactNative;
|
|
15
24
|
|
|
@@ -33,6 +42,9 @@ const hostConnectionStatusListeners = new Set();
|
|
|
33
42
|
let lastHostConnectionStatusKey = null;
|
|
34
43
|
const logListeners = new Set();
|
|
35
44
|
let logEventSubscription = null;
|
|
45
|
+
let networkCaptureSubscription = null;
|
|
46
|
+
let networkCaptureRegistration = null;
|
|
47
|
+
let networkConnectionEventSubscription = null;
|
|
36
48
|
const REACT_COMPONENT_TREE_TOOL_ID = "react.get_component_tree";
|
|
37
49
|
const REACT_SHADOW_TREE_TOOL_ID = "react.get_shadow_tree";
|
|
38
50
|
|
|
@@ -44,7 +56,28 @@ function normalizePairingPayload(payload) {
|
|
|
44
56
|
}
|
|
45
57
|
|
|
46
58
|
function normalizeOptions(options = {}) {
|
|
47
|
-
|
|
59
|
+
const normalized = {
|
|
60
|
+
...options,
|
|
61
|
+
customProperties: mergeSessionProperties(
|
|
62
|
+
automaticSessionProperties(),
|
|
63
|
+
options.customProperties
|
|
64
|
+
),
|
|
65
|
+
};
|
|
66
|
+
delete normalized.networkCapture;
|
|
67
|
+
return normalized;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function automaticSessionProperties() {
|
|
71
|
+
return createAutomaticSessionProperties({
|
|
72
|
+
platform: Platform,
|
|
73
|
+
reactVersion,
|
|
74
|
+
runtimeGlobal: global,
|
|
75
|
+
developmentMode: typeof __DEV__ !== "undefined" && __DEV__,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function automaticSessionPropertyValue(group, key) {
|
|
80
|
+
return automaticSessionProperties()[group]?.[key];
|
|
48
81
|
}
|
|
49
82
|
|
|
50
83
|
function cloneOptions(options = {}) {
|
|
@@ -61,6 +94,9 @@ function cloneOptions(options = {}) {
|
|
|
61
94
|
if (options.touchCapture && typeof options.touchCapture === "object") {
|
|
62
95
|
clone.touchCapture = { ...options.touchCapture };
|
|
63
96
|
}
|
|
97
|
+
if (options.crashCapture && typeof options.crashCapture === "object") {
|
|
98
|
+
clone.crashCapture = { ...options.crashCapture };
|
|
99
|
+
}
|
|
64
100
|
if (options.lifecycleCapture) {
|
|
65
101
|
clone.lifecycleCapture = { ...options.lifecycleCapture };
|
|
66
102
|
}
|
|
@@ -70,6 +106,17 @@ function cloneOptions(options = {}) {
|
|
|
70
106
|
if (options.hostConnection) {
|
|
71
107
|
clone.hostConnection = { ...options.hostConnection };
|
|
72
108
|
}
|
|
109
|
+
if (options.networkCapture && typeof options.networkCapture === "object") {
|
|
110
|
+
clone.networkCapture = {
|
|
111
|
+
...options.networkCapture,
|
|
112
|
+
additionalSensitiveHeaderNames: options.networkCapture.additionalSensitiveHeaderNames
|
|
113
|
+
? [...options.networkCapture.additionalSensitiveHeaderNames]
|
|
114
|
+
: undefined,
|
|
115
|
+
additionalSensitiveQueryParameterNames: options.networkCapture.additionalSensitiveQueryParameterNames
|
|
116
|
+
? [...options.networkCapture.additionalSensitiveQueryParameterNames]
|
|
117
|
+
: undefined,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
73
120
|
if (options.secureStorage) {
|
|
74
121
|
clone.secureStorage = {
|
|
75
122
|
...options.secureStorage,
|
|
@@ -157,6 +204,8 @@ class AnsightOptionsBuilder {
|
|
|
157
204
|
retentionPeriodSeconds: 120,
|
|
158
205
|
enableFramesPerSecond: true,
|
|
159
206
|
enableBatteryLevel: false,
|
|
207
|
+
enableOpenFileHandleTracking: false,
|
|
208
|
+
enableJniReferenceCountTracking: false,
|
|
160
209
|
sessionJpegCapture: {
|
|
161
210
|
intervalMilliseconds: 2000,
|
|
162
211
|
quality: 60,
|
|
@@ -209,6 +258,26 @@ class AnsightOptionsBuilder {
|
|
|
209
258
|
return this;
|
|
210
259
|
}
|
|
211
260
|
|
|
261
|
+
withOpenFileHandleTracking() {
|
|
262
|
+
this._options.enableOpenFileHandleTracking = true;
|
|
263
|
+
return this;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
withoutOpenFileHandleTracking() {
|
|
267
|
+
this._options.enableOpenFileHandleTracking = false;
|
|
268
|
+
return this;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
withJniReferenceCountTracking() {
|
|
272
|
+
this._options.enableJniReferenceCountTracking = true;
|
|
273
|
+
return this;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
withoutJniReferenceCountTracking() {
|
|
277
|
+
this._options.enableJniReferenceCountTracking = false;
|
|
278
|
+
return this;
|
|
279
|
+
}
|
|
280
|
+
|
|
212
281
|
withRetentionPeriodSeconds(retentionPeriodSeconds) {
|
|
213
282
|
this._options.retentionPeriodSeconds = retentionPeriodSeconds;
|
|
214
283
|
return this;
|
|
@@ -269,6 +338,7 @@ class AnsightOptionsBuilder {
|
|
|
269
338
|
maxWidth: arguments.length > 2 ? arguments[2] : 480,
|
|
270
339
|
captureGpuBackedSurfaces: arguments.length > 3 ? arguments[3] : true,
|
|
271
340
|
mode: arguments.length > 4 ? arguments[4] : "screenshotOnly",
|
|
341
|
+
captureKeyboardPresence: arguments.length > 5 ? arguments[5] : false,
|
|
272
342
|
};
|
|
273
343
|
return this;
|
|
274
344
|
}
|
|
@@ -276,6 +346,7 @@ class AnsightOptionsBuilder {
|
|
|
276
346
|
intervalMilliseconds: 2000,
|
|
277
347
|
quality: 60,
|
|
278
348
|
maxWidth: 480,
|
|
349
|
+
captureKeyboardPresence: false,
|
|
279
350
|
mode: "screenshotOnly",
|
|
280
351
|
...(optionsOrIntervalMilliseconds || {}),
|
|
281
352
|
};
|
|
@@ -297,11 +368,67 @@ class AnsightOptionsBuilder {
|
|
|
297
368
|
return this;
|
|
298
369
|
}
|
|
299
370
|
|
|
371
|
+
withCrashCapture(crashCapture = {}) {
|
|
372
|
+
this._options.crashCapture = { ...crashCapture, enabled: true };
|
|
373
|
+
return this;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
withoutCrashCapture() {
|
|
377
|
+
this._options.crashCapture = false;
|
|
378
|
+
return this;
|
|
379
|
+
}
|
|
380
|
+
|
|
300
381
|
withLifecycleCapture(lifecycleCapture = {}) {
|
|
301
382
|
this._options.lifecycleCapture = { ...lifecycleCapture };
|
|
302
383
|
return this;
|
|
303
384
|
}
|
|
304
385
|
|
|
386
|
+
withNetworkCapture(networkCapture = {}) {
|
|
387
|
+
this._options.networkCapture = { ...networkCapture };
|
|
388
|
+
return this;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
withNetworkRequestBodies(maximumBodyBytes) {
|
|
392
|
+
if (!this._options.networkCapture || typeof this._options.networkCapture !== "object") return this;
|
|
393
|
+
const current = this._options.networkCapture;
|
|
394
|
+
this._options.networkCapture = {
|
|
395
|
+
...current,
|
|
396
|
+
captureRequestBody: true,
|
|
397
|
+
...(maximumBodyBytes == null ? {} : { maximumBodyBytes }),
|
|
398
|
+
};
|
|
399
|
+
return this;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
withoutNetworkRequestBodies() {
|
|
403
|
+
if (!this._options.networkCapture || typeof this._options.networkCapture !== "object") return this;
|
|
404
|
+
const current = this._options.networkCapture;
|
|
405
|
+
this._options.networkCapture = { ...current, captureRequestBody: false };
|
|
406
|
+
return this;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
withNetworkResponseBodies(maximumBodyBytes) {
|
|
410
|
+
if (!this._options.networkCapture || typeof this._options.networkCapture !== "object") return this;
|
|
411
|
+
const current = this._options.networkCapture;
|
|
412
|
+
this._options.networkCapture = {
|
|
413
|
+
...current,
|
|
414
|
+
captureResponseBody: true,
|
|
415
|
+
...(maximumBodyBytes == null ? {} : { maximumBodyBytes }),
|
|
416
|
+
};
|
|
417
|
+
return this;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
withoutNetworkResponseBodies() {
|
|
421
|
+
if (!this._options.networkCapture || typeof this._options.networkCapture !== "object") return this;
|
|
422
|
+
const current = this._options.networkCapture;
|
|
423
|
+
this._options.networkCapture = { ...current, captureResponseBody: false };
|
|
424
|
+
return this;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
withoutNetworkCapture() {
|
|
428
|
+
this._options.networkCapture = false;
|
|
429
|
+
return this;
|
|
430
|
+
}
|
|
431
|
+
|
|
305
432
|
withToolGuard(toolGuard) {
|
|
306
433
|
this._options.toolGuard = toolGuard;
|
|
307
434
|
return this;
|
|
@@ -544,6 +671,7 @@ function addHostConnectionStatusListener(listener, options = {}) {
|
|
|
544
671
|
|
|
545
672
|
async function notifyAfterHostConnectionChange(operation) {
|
|
546
673
|
const result = await operation();
|
|
674
|
+
await refreshNetworkCaptureConnection();
|
|
547
675
|
await emitHostConnectionStatusChangedIfNeeded();
|
|
548
676
|
return result;
|
|
549
677
|
}
|
|
@@ -1391,7 +1519,7 @@ function summarizeProps(props) {
|
|
|
1391
1519
|
}
|
|
1392
1520
|
const keys = Object.keys(props).filter((key) => key !== "children");
|
|
1393
1521
|
const summary = { keys };
|
|
1394
|
-
["testID", "nativeID", "accessibilityLabel", "role"].forEach((key) => {
|
|
1522
|
+
["testID", "nativeID", "accessibilityLabel", "accessibilityRole", "role"].forEach((key) => {
|
|
1395
1523
|
if (props[key] != null && !isSensitiveKey(key)) {
|
|
1396
1524
|
summary[key] = String(props[key]);
|
|
1397
1525
|
}
|
|
@@ -1403,6 +1531,42 @@ function summarizeProps(props) {
|
|
|
1403
1531
|
return summary;
|
|
1404
1532
|
}
|
|
1405
1533
|
|
|
1534
|
+
function reactSemanticRole(type, props, fiberTag) {
|
|
1535
|
+
const declared = props && (props.accessibilityRole || props.role);
|
|
1536
|
+
if (declared) return String(declared).toLowerCase();
|
|
1537
|
+
if (fiberTag === 6 || /text/i.test(type)) return "text";
|
|
1538
|
+
if (/button|pressable|touchable/i.test(type)) return "button";
|
|
1539
|
+
if (/textinput/i.test(type)) return "textbox";
|
|
1540
|
+
if (/switch/i.test(type)) return "switch";
|
|
1541
|
+
if (/scrollview|flatlist|sectionlist/i.test(type)) return "scrollview";
|
|
1542
|
+
return "view";
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
function reactSupportedActions(type, props) {
|
|
1546
|
+
const actions = [];
|
|
1547
|
+
if (props && typeof props.onPress === "function") actions.push("tap");
|
|
1548
|
+
if (props && (typeof props.onChangeText === "function" || /textinput/i.test(type))) {
|
|
1549
|
+
actions.push("typeText", "focus");
|
|
1550
|
+
}
|
|
1551
|
+
if (/scrollview|flatlist|sectionlist/i.test(type) || (props && typeof props.onScroll === "function")) {
|
|
1552
|
+
actions.push("scroll", "swipe");
|
|
1553
|
+
}
|
|
1554
|
+
return actions;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
function applyReactTargetability(node, fiber, props, type) {
|
|
1558
|
+
const style = StyleSheet && typeof StyleSheet.flatten === "function"
|
|
1559
|
+
? StyleSheet.flatten(props && props.style)
|
|
1560
|
+
: props && props.style;
|
|
1561
|
+
node.text = node.label || (node.visual && node.visual.text) || null;
|
|
1562
|
+
node.role = reactSemanticRole(type, props, fiber.tag);
|
|
1563
|
+
node.supportedActions = reactSupportedActions(type, props);
|
|
1564
|
+
node.visible = !(style && style.display === "none") && !(props && props.accessibilityElementsHidden);
|
|
1565
|
+
node.enabled = !(props && (props.disabled === true || props.accessibilityState && props.accessibilityState.disabled === true));
|
|
1566
|
+
node.focusable = !!(props && props.focusable) || node.supportedActions.includes("focus");
|
|
1567
|
+
node.interactable = node.visible && node.enabled && node.supportedActions.length > 0;
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1406
1570
|
function reactColorToArgbHex(value) {
|
|
1407
1571
|
if (value == null) {
|
|
1408
1572
|
return undefined;
|
|
@@ -1453,6 +1617,28 @@ function createReactVisual(fiber, props, maxStringLength) {
|
|
|
1453
1617
|
return visual;
|
|
1454
1618
|
}
|
|
1455
1619
|
|
|
1620
|
+
function createReactZIndex(props) {
|
|
1621
|
+
const style = StyleSheet && typeof StyleSheet.flatten === "function"
|
|
1622
|
+
? StyleSheet.flatten(props && props.style)
|
|
1623
|
+
: props && props.style;
|
|
1624
|
+
const zIndex = style && style.zIndex != null ? Number(style.zIndex) : NaN;
|
|
1625
|
+
const elevation = style && style.elevation != null ? Number(style.elevation) : NaN;
|
|
1626
|
+
if (Number.isFinite(zIndex) && zIndex !== 0) return zIndex;
|
|
1627
|
+
if (Number.isFinite(elevation) && elevation !== 0) return elevation;
|
|
1628
|
+
return null;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
function registerReactType(context, typeName) {
|
|
1632
|
+
const normalizedTypeName = String(typeName || "UnknownComponent").trim() || "UnknownComponent";
|
|
1633
|
+
const existingTypeId = context.typeIdsByName.get(normalizedTypeName);
|
|
1634
|
+
if (existingTypeId != null) return existingTypeId;
|
|
1635
|
+
|
|
1636
|
+
const typeId = context.types.length;
|
|
1637
|
+
context.types.push(normalizedTypeName);
|
|
1638
|
+
context.typeIdsByName.set(normalizedTypeName, typeId);
|
|
1639
|
+
return typeId;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1456
1642
|
function nativeTagForFiber(fiber) {
|
|
1457
1643
|
const stateNode = fiber && fiber.stateNode;
|
|
1458
1644
|
if (!stateNode) {
|
|
@@ -1524,17 +1710,17 @@ function serializeFiber(fiber, context, depth) {
|
|
|
1524
1710
|
|
|
1525
1711
|
const props = fiber.memoizedProps || fiber.pendingProps || {};
|
|
1526
1712
|
const type = reactFiberTypeName(fiber);
|
|
1527
|
-
const kind = reactFiberKind(fiber.tag);
|
|
1528
1713
|
const node = {
|
|
1529
1714
|
id: reactFiberId(fiber),
|
|
1530
|
-
type,
|
|
1531
|
-
kind,
|
|
1715
|
+
typeId: registerReactType(context, type),
|
|
1532
1716
|
tag: fiber.tag,
|
|
1533
1717
|
key: fiber.key == null ? null : String(fiber.key),
|
|
1534
1718
|
depth,
|
|
1535
1719
|
visual: createReactVisual(fiber, props, context.maxStringLength),
|
|
1536
1720
|
children: [],
|
|
1537
1721
|
};
|
|
1722
|
+
const zIndex = createReactZIndex(props);
|
|
1723
|
+
if (zIndex != null) node.z = zIndex;
|
|
1538
1724
|
|
|
1539
1725
|
if (fiber._debugSource) {
|
|
1540
1726
|
node.source = {
|
|
@@ -1546,7 +1732,7 @@ function serializeFiber(fiber, context, depth) {
|
|
|
1546
1732
|
|
|
1547
1733
|
const owner = fiber._debugOwner && reactFiberTypeName(fiber._debugOwner);
|
|
1548
1734
|
if (owner) {
|
|
1549
|
-
node.
|
|
1735
|
+
node.ownerTypeId = registerReactType(context, owner);
|
|
1550
1736
|
}
|
|
1551
1737
|
|
|
1552
1738
|
const nativeTag = nativeTagForFiber(fiber);
|
|
@@ -1577,6 +1763,8 @@ function serializeFiber(fiber, context, depth) {
|
|
|
1577
1763
|
node.state = sanitizeValue(fiber.memoizedState, context);
|
|
1578
1764
|
}
|
|
1579
1765
|
|
|
1766
|
+
applyReactTargetability(node, fiber, props, type);
|
|
1767
|
+
|
|
1580
1768
|
if (depth < context.maxDepth) {
|
|
1581
1769
|
let child = fiber.child;
|
|
1582
1770
|
while (child) {
|
|
@@ -1600,16 +1788,18 @@ function isShadowTreeFiber(fiber) {
|
|
|
1600
1788
|
|
|
1601
1789
|
function createShadowTreeNode(fiber, context, depth) {
|
|
1602
1790
|
const props = fiber.memoizedProps || fiber.pendingProps || {};
|
|
1791
|
+
const type = reactFiberTypeName(fiber);
|
|
1603
1792
|
const node = {
|
|
1604
1793
|
id: reactFiberId(fiber),
|
|
1605
|
-
|
|
1606
|
-
kind: fiber.tag === 3 ? "root" : fiber.tag === 6 ? "text" : "host",
|
|
1794
|
+
typeId: registerReactType(context, type),
|
|
1607
1795
|
tag: fiber.tag,
|
|
1608
1796
|
key: fiber.key == null ? null : String(fiber.key),
|
|
1609
1797
|
depth,
|
|
1610
1798
|
visual: createReactVisual(fiber, props, context.maxStringLength),
|
|
1611
1799
|
children: [],
|
|
1612
1800
|
};
|
|
1801
|
+
const zIndex = createReactZIndex(props);
|
|
1802
|
+
if (zIndex != null) node.z = zIndex;
|
|
1613
1803
|
|
|
1614
1804
|
const nativeTag = nativeTagForFiber(fiber);
|
|
1615
1805
|
if (nativeTag != null) {
|
|
@@ -1635,6 +1825,8 @@ function createShadowTreeNode(fiber, context, depth) {
|
|
|
1635
1825
|
}
|
|
1636
1826
|
}
|
|
1637
1827
|
|
|
1828
|
+
applyReactTargetability(node, fiber, props, type);
|
|
1829
|
+
|
|
1638
1830
|
return node;
|
|
1639
1831
|
}
|
|
1640
1832
|
|
|
@@ -1691,19 +1883,21 @@ async function captureReactVisualTree(rawOptions = {}) {
|
|
|
1691
1883
|
maxStringLength: rawOptions.maxStringLength || 180,
|
|
1692
1884
|
maxValueDepth: rawOptions.maxValueDepth || 2,
|
|
1693
1885
|
nodesWithNativeTags: [],
|
|
1886
|
+
typeIdsByName: new Map(),
|
|
1887
|
+
types: [],
|
|
1694
1888
|
count: 0,
|
|
1695
1889
|
truncated: false,
|
|
1696
1890
|
visited: new Set(),
|
|
1697
1891
|
};
|
|
1698
1892
|
const roots = getReactRoots();
|
|
1699
|
-
const rootNodes = roots.roots.map((root
|
|
1893
|
+
const rootNodes = roots.roots.map((root) => {
|
|
1700
1894
|
const node = serializeFiber(root.fiber, context, 0);
|
|
1701
1895
|
if (node) {
|
|
1702
1896
|
node.rendererId = root.rendererId;
|
|
1703
|
-
node.rootIndex = index;
|
|
1704
1897
|
}
|
|
1705
1898
|
return node;
|
|
1706
1899
|
}).filter(Boolean);
|
|
1900
|
+
const rootTypeId = registerReactType(context, "ReactRoots");
|
|
1707
1901
|
|
|
1708
1902
|
if (includeBounds && context.nodesWithNativeTags.length > 0) {
|
|
1709
1903
|
await Promise.all(context.nodesWithNativeTags.slice(0, 300).map(async (node) => {
|
|
@@ -1715,6 +1909,7 @@ async function captureReactVisualTree(rawOptions = {}) {
|
|
|
1715
1909
|
}
|
|
1716
1910
|
|
|
1717
1911
|
return {
|
|
1912
|
+
format: "ansight.react.visual-tree.compact.v2",
|
|
1718
1913
|
platform: Platform.OS,
|
|
1719
1914
|
source: "react",
|
|
1720
1915
|
adapter: "react.fiber",
|
|
@@ -1724,11 +1919,11 @@ async function captureReactVisualTree(rawOptions = {}) {
|
|
|
1724
1919
|
renderers: roots.renderers,
|
|
1725
1920
|
root: {
|
|
1726
1921
|
id: "react:roots",
|
|
1727
|
-
|
|
1728
|
-
|
|
1922
|
+
typeId: rootTypeId,
|
|
1923
|
+
childCount: rootNodes.length,
|
|
1729
1924
|
children: rootNodes,
|
|
1730
1925
|
},
|
|
1731
|
-
|
|
1926
|
+
types: context.types,
|
|
1732
1927
|
nodeCount: context.count,
|
|
1733
1928
|
truncated: context.truncated,
|
|
1734
1929
|
unavailableReason: roots.hookAvailable ? undefined : "React DevTools global hook is not available in this runtime.",
|
|
@@ -1748,19 +1943,21 @@ async function captureReactShadowTree(rawOptions = {}) {
|
|
|
1748
1943
|
maxStringLength: rawOptions.maxStringLength || 180,
|
|
1749
1944
|
maxValueDepth: rawOptions.maxValueDepth || 2,
|
|
1750
1945
|
nodesWithNativeTags: [],
|
|
1946
|
+
typeIdsByName: new Map(),
|
|
1947
|
+
types: [],
|
|
1751
1948
|
count: 0,
|
|
1752
1949
|
truncated: false,
|
|
1753
1950
|
visited: new Set(),
|
|
1754
1951
|
};
|
|
1755
1952
|
const roots = getReactRoots();
|
|
1756
|
-
const rootNodes = roots.roots.flatMap((root
|
|
1953
|
+
const rootNodes = roots.roots.flatMap((root) => {
|
|
1757
1954
|
const nodes = serializeShadowFiber(root.fiber, context, 0);
|
|
1758
1955
|
nodes.forEach((node) => {
|
|
1759
1956
|
node.rendererId = root.rendererId;
|
|
1760
|
-
node.rootIndex = index;
|
|
1761
1957
|
});
|
|
1762
1958
|
return nodes;
|
|
1763
1959
|
});
|
|
1960
|
+
const rootTypeId = registerReactType(context, "ReactNativeShadowRoots");
|
|
1764
1961
|
|
|
1765
1962
|
if (includeBounds && context.nodesWithNativeTags.length > 0) {
|
|
1766
1963
|
await Promise.all(context.nodesWithNativeTags.slice(0, 300).map(async (node) => {
|
|
@@ -1772,6 +1969,7 @@ async function captureReactShadowTree(rawOptions = {}) {
|
|
|
1772
1969
|
}
|
|
1773
1970
|
|
|
1774
1971
|
return {
|
|
1972
|
+
format: "ansight.react.visual-tree.compact.v2",
|
|
1775
1973
|
platform: Platform.OS,
|
|
1776
1974
|
source: "react-native",
|
|
1777
1975
|
adapter: "react-native.host-fiber",
|
|
@@ -1781,11 +1979,11 @@ async function captureReactShadowTree(rawOptions = {}) {
|
|
|
1781
1979
|
renderers: roots.renderers,
|
|
1782
1980
|
root: {
|
|
1783
1981
|
id: "react:shadow-roots",
|
|
1784
|
-
|
|
1785
|
-
|
|
1982
|
+
typeId: rootTypeId,
|
|
1983
|
+
childCount: rootNodes.length,
|
|
1786
1984
|
children: rootNodes,
|
|
1787
1985
|
},
|
|
1788
|
-
|
|
1986
|
+
types: context.types,
|
|
1789
1987
|
nodeCount: context.count,
|
|
1790
1988
|
truncated: context.truncated,
|
|
1791
1989
|
unavailableReason: roots.hookAvailable ? undefined : "React DevTools global hook is not available in this runtime.",
|
|
@@ -1801,13 +1999,18 @@ function flattenReactTree(node, output = []) {
|
|
|
1801
1999
|
return output;
|
|
1802
2000
|
}
|
|
1803
2001
|
|
|
1804
|
-
function
|
|
2002
|
+
function createReactNodeSnapshot(node) {
|
|
2003
|
+
const snapshot = { ...node };
|
|
2004
|
+
delete snapshot.children;
|
|
2005
|
+
return snapshot;
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
function nodeSearchText(node, types) {
|
|
1805
2009
|
return [
|
|
1806
2010
|
node.id,
|
|
1807
|
-
node.
|
|
1808
|
-
node.kind,
|
|
2011
|
+
types[node.typeId],
|
|
1809
2012
|
node.label,
|
|
1810
|
-
node.
|
|
2013
|
+
types[node.ownerTypeId],
|
|
1811
2014
|
node.propsSummary && node.propsSummary.testID,
|
|
1812
2015
|
node.propsSummary && node.propsSummary.nativeID,
|
|
1813
2016
|
node.propsSummary && node.propsSummary.accessibilityLabel,
|
|
@@ -1815,14 +2018,14 @@ function nodeSearchText(node) {
|
|
|
1815
2018
|
].filter(Boolean).join(" ").toLowerCase();
|
|
1816
2019
|
}
|
|
1817
2020
|
|
|
1818
|
-
function matchesReactNode(node, args) {
|
|
2021
|
+
function matchesReactNode(node, args, types) {
|
|
1819
2022
|
const query = args.query ? String(args.query).toLowerCase() : null;
|
|
1820
2023
|
const type = args.type ? String(args.type).toLowerCase() : null;
|
|
1821
2024
|
const testID = args.testID ? String(args.testID).toLowerCase() : null;
|
|
1822
2025
|
const text = args.text ? String(args.text).toLowerCase() : null;
|
|
1823
|
-
const searchText = nodeSearchText(node);
|
|
2026
|
+
const searchText = nodeSearchText(node, types);
|
|
1824
2027
|
return (!query || searchText.includes(query)) &&
|
|
1825
|
-
(!type || String(node.
|
|
2028
|
+
(!type || String(types[node.typeId] || "").toLowerCase().includes(type)) &&
|
|
1826
2029
|
(!testID || String((node.propsSummary && node.propsSummary.testID) || "").toLowerCase() === testID) &&
|
|
1827
2030
|
(!text || searchText.includes(text));
|
|
1828
2031
|
}
|
|
@@ -2043,12 +2246,14 @@ function installReactTools(options = {}) {
|
|
|
2043
2246
|
});
|
|
2044
2247
|
const maxResults = parseInteger(args.maxResults, 50, 1, 500);
|
|
2045
2248
|
const matches = flattenReactTree(tree.root)
|
|
2046
|
-
.filter((node) => node.id !== "react:roots" && matchesReactNode(node, args))
|
|
2047
|
-
.slice(0, maxResults)
|
|
2249
|
+
.filter((node) => node.id !== "react:roots" && matchesReactNode(node, args, tree.types))
|
|
2250
|
+
.slice(0, maxResults)
|
|
2251
|
+
.map(createReactNodeSnapshot);
|
|
2048
2252
|
return {
|
|
2049
2253
|
success: tree.hookAvailable,
|
|
2050
2254
|
message: `Found ${matches.length} React component(s).`,
|
|
2051
2255
|
result: {
|
|
2256
|
+
types: tree.types,
|
|
2052
2257
|
matches,
|
|
2053
2258
|
count: matches.length,
|
|
2054
2259
|
truncated: matches.length === maxResults,
|
|
@@ -2060,7 +2265,11 @@ function installReactTools(options = {}) {
|
|
|
2060
2265
|
const tree = await captureReactVisualTree(reactToolOptions(options, args));
|
|
2061
2266
|
const node = flattenReactTree(tree.root).find((candidate) => candidate.id === args.nodeId);
|
|
2062
2267
|
return node
|
|
2063
|
-
? {
|
|
2268
|
+
? {
|
|
2269
|
+
success: true,
|
|
2270
|
+
message: "React component captured.",
|
|
2271
|
+
result: { types: tree.types, node: createReactNodeSnapshot(node) },
|
|
2272
|
+
}
|
|
2064
2273
|
: { success: false, message: `React component '${args.nodeId}' was not found.`, errorCode: "react_component_not_found" };
|
|
2065
2274
|
}
|
|
2066
2275
|
|
|
@@ -2101,7 +2310,7 @@ function installReactTools(options = {}) {
|
|
|
2101
2310
|
result: {
|
|
2102
2311
|
nodeId: args.nodeId,
|
|
2103
2312
|
prop,
|
|
2104
|
-
type: node.
|
|
2313
|
+
type: tree.types[node.typeId],
|
|
2105
2314
|
},
|
|
2106
2315
|
};
|
|
2107
2316
|
}
|
|
@@ -2137,6 +2346,14 @@ function installErrorHandlers(options = {}) {
|
|
|
2137
2346
|
|
|
2138
2347
|
if (global.ErrorUtils && global.ErrorUtils.setGlobalHandler) {
|
|
2139
2348
|
global.ErrorUtils.setGlobalHandler((error, isFatal) => {
|
|
2349
|
+
nativeModule.recordCrashCandidate({
|
|
2350
|
+
runtime: "react-native-javascript",
|
|
2351
|
+
kind: "unhandled_javascript_error",
|
|
2352
|
+
message: error && error.message,
|
|
2353
|
+
stack: error && error.stack,
|
|
2354
|
+
fatal: !!isFatal,
|
|
2355
|
+
metadata: JSON.stringify({ name: error && error.name }),
|
|
2356
|
+
}).catch(() => {});
|
|
2140
2357
|
nativeModule.recordEvent({
|
|
2141
2358
|
label: error && error.message ? error.message : "Unhandled JavaScript error",
|
|
2142
2359
|
type: "Exception",
|
|
@@ -2159,6 +2376,13 @@ function installErrorHandlers(options = {}) {
|
|
|
2159
2376
|
global.__ansightUnhandledRejectionTrackingInstalled = true;
|
|
2160
2377
|
global.addEventListener("unhandledrejection", (event) => {
|
|
2161
2378
|
const reason = event && event.reason;
|
|
2379
|
+
nativeModule.recordCrashCandidate({
|
|
2380
|
+
runtime: "react-native-javascript",
|
|
2381
|
+
kind: "unhandled_promise_rejection",
|
|
2382
|
+
message: reason && reason.message ? reason.message : String(reason),
|
|
2383
|
+
stack: reason && reason.stack,
|
|
2384
|
+
fatal: false,
|
|
2385
|
+
}).catch(() => {});
|
|
2162
2386
|
nativeModule.recordEvent({
|
|
2163
2387
|
label: reason && reason.message ? reason.message : "Unhandled JavaScript promise rejection",
|
|
2164
2388
|
type: "Exception",
|
|
@@ -2180,6 +2404,7 @@ function installErrorHandlers(options = {}) {
|
|
|
2180
2404
|
|
|
2181
2405
|
async function initialize(options = {}) {
|
|
2182
2406
|
const result = await nativeModule.initialize(normalizeOptions(options));
|
|
2407
|
+
await configureNetworkCapture(options.networkCapture);
|
|
2183
2408
|
if (options.lifecycle !== false) {
|
|
2184
2409
|
startAppStateTracking();
|
|
2185
2410
|
}
|
|
@@ -2189,6 +2414,7 @@ async function initialize(options = {}) {
|
|
|
2189
2414
|
|
|
2190
2415
|
async function initializeAndActivate(options = {}) {
|
|
2191
2416
|
const result = await nativeModule.initializeAndActivate(normalizeOptions(options));
|
|
2417
|
+
await configureNetworkCapture(options.networkCapture);
|
|
2192
2418
|
if (options.lifecycle !== false) {
|
|
2193
2419
|
startAppStateTracking();
|
|
2194
2420
|
}
|
|
@@ -2253,6 +2479,103 @@ function recordEvent(input) {
|
|
|
2253
2479
|
return nativeModule.recordEvent(input || {});
|
|
2254
2480
|
}
|
|
2255
2481
|
|
|
2482
|
+
function recordCrashCandidate(input = {}) {
|
|
2483
|
+
const metadata = input.metadata && typeof input.metadata === "object"
|
|
2484
|
+
? JSON.stringify(input.metadata)
|
|
2485
|
+
: input.metadata;
|
|
2486
|
+
return nativeModule.recordCrashCandidate({
|
|
2487
|
+
...input,
|
|
2488
|
+
metadata,
|
|
2489
|
+
});
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
function recordNetworkRequest(input, sanitizationOptions = {}) {
|
|
2493
|
+
const sanitized = sanitizeNetworkRequest(input, sanitizationOptions, global);
|
|
2494
|
+
if (!sanitized) {
|
|
2495
|
+
return Promise.resolve({ success: false, message: "Network request capture was suppressed by the sanitizer." });
|
|
2496
|
+
}
|
|
2497
|
+
return nativeModule.recordNetworkRequest(sanitized);
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
function installNetworkCapture(options = {}) {
|
|
2501
|
+
uninstallNetworkCapture();
|
|
2502
|
+
const registration = { options };
|
|
2503
|
+
networkCaptureRegistration = registration;
|
|
2504
|
+
ensureNetworkConnectionEventSubscription();
|
|
2505
|
+
void refreshNetworkCaptureConnection();
|
|
2506
|
+
return {
|
|
2507
|
+
remove() {
|
|
2508
|
+
if (networkCaptureRegistration === registration) {
|
|
2509
|
+
uninstallNetworkCapture();
|
|
2510
|
+
}
|
|
2511
|
+
},
|
|
2512
|
+
};
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
function uninstallNetworkCapture() {
|
|
2516
|
+
networkCaptureRegistration = null;
|
|
2517
|
+
if (networkConnectionEventSubscription) {
|
|
2518
|
+
networkConnectionEventSubscription.remove();
|
|
2519
|
+
networkConnectionEventSubscription = null;
|
|
2520
|
+
}
|
|
2521
|
+
detachNetworkCapture();
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2524
|
+
function detachNetworkCapture() {
|
|
2525
|
+
if (!networkCaptureSubscription) return;
|
|
2526
|
+
networkCaptureSubscription.remove();
|
|
2527
|
+
networkCaptureSubscription = null;
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
async function refreshNetworkCaptureConnection() {
|
|
2531
|
+
const registration = networkCaptureRegistration;
|
|
2532
|
+
if (!registration) {
|
|
2533
|
+
detachNetworkCapture();
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
try {
|
|
2537
|
+
const status = await nativeModule.hostConnectionStatus();
|
|
2538
|
+
if (networkCaptureRegistration !== registration) return;
|
|
2539
|
+
applyNetworkConnectionStatus(status);
|
|
2540
|
+
} catch {
|
|
2541
|
+
if (networkCaptureRegistration === registration) detachNetworkCapture();
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
function ensureNetworkConnectionEventSubscription() {
|
|
2546
|
+
if (networkConnectionEventSubscription) return;
|
|
2547
|
+
const emitter = new NativeEventEmitter(nativeModule);
|
|
2548
|
+
networkConnectionEventSubscription = emitter.addListener(
|
|
2549
|
+
"AnsightHostConnectionStatus",
|
|
2550
|
+
applyNetworkConnectionStatus,
|
|
2551
|
+
);
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
function applyNetworkConnectionStatus(status) {
|
|
2555
|
+
const registration = networkCaptureRegistration;
|
|
2556
|
+
if (!registration || !status || status.isConnected !== true) {
|
|
2557
|
+
detachNetworkCapture();
|
|
2558
|
+
return;
|
|
2559
|
+
}
|
|
2560
|
+
if (!networkCaptureSubscription) {
|
|
2561
|
+
networkCaptureSubscription = installNetworkCaptureCore({
|
|
2562
|
+
globalObject: global,
|
|
2563
|
+
options: registration.options,
|
|
2564
|
+
sourcePrefix: "react-native",
|
|
2565
|
+
capture: (request) => nativeModule.recordNetworkRequest(request),
|
|
2566
|
+
});
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
async function configureNetworkCapture(value) {
|
|
2571
|
+
uninstallNetworkCapture();
|
|
2572
|
+
if (!value) return;
|
|
2573
|
+
const registration = { options: typeof value === "object" ? value : {} };
|
|
2574
|
+
networkCaptureRegistration = registration;
|
|
2575
|
+
ensureNetworkConnectionEventSubscription();
|
|
2576
|
+
await refreshNetworkCaptureConnection();
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2256
2579
|
function screenViewed(name, details) {
|
|
2257
2580
|
return nativeModule.screenViewed(name, details || {});
|
|
2258
2581
|
}
|
|
@@ -2295,6 +2618,8 @@ const Ansight = {
|
|
|
2295
2618
|
recordMetric: (value, channel = 255) => nativeModule.recordMetric(value, channel),
|
|
2296
2619
|
event: recordEvent,
|
|
2297
2620
|
recordEvent,
|
|
2621
|
+
recordCrashCandidate,
|
|
2622
|
+
recordNetworkRequest,
|
|
2298
2623
|
screenViewed,
|
|
2299
2624
|
trackRoute,
|
|
2300
2625
|
setAppLifecycleState: (state) => nativeModule.setAppLifecycleState(state),
|
|
@@ -2329,12 +2654,21 @@ const Ansight = {
|
|
|
2329
2654
|
captureScreenFrame: (options = {}) => nativeModule.captureScreenFrame(options || {}),
|
|
2330
2655
|
enableTouchCapture: () => nativeModule.enableTouchCapture(),
|
|
2331
2656
|
disableTouchCapture: () => nativeModule.disableTouchCapture(),
|
|
2332
|
-
updateSessionProperties: (properties) => nativeModule.updateSessionProperties(
|
|
2333
|
-
|
|
2334
|
-
|
|
2657
|
+
updateSessionProperties: (properties) => nativeModule.updateSessionProperties(
|
|
2658
|
+
mergeSessionProperties(automaticSessionProperties(), properties || {})
|
|
2659
|
+
),
|
|
2660
|
+
clearSessionProperties: () => nativeModule.updateSessionProperties(automaticSessionProperties()),
|
|
2661
|
+
updateCustomProperties: (properties) => nativeModule.updateSessionProperties(
|
|
2662
|
+
mergeSessionProperties(automaticSessionProperties(), properties || {})
|
|
2663
|
+
),
|
|
2335
2664
|
registerCustomProperty: (group, key, value) => nativeModule.registerCustomProperty(group, key, value),
|
|
2336
|
-
removeCustomProperty: (group, key) =>
|
|
2337
|
-
|
|
2665
|
+
removeCustomProperty: (group, key) => {
|
|
2666
|
+
const automaticValue = automaticSessionPropertyValue(group, key);
|
|
2667
|
+
return automaticValue == null
|
|
2668
|
+
? nativeModule.removeCustomProperty(group, key)
|
|
2669
|
+
: nativeModule.registerCustomProperty(group, key, automaticValue);
|
|
2670
|
+
},
|
|
2671
|
+
clearCustomProperties: () => nativeModule.updateSessionProperties(automaticSessionProperties()),
|
|
2338
2672
|
registerTool,
|
|
2339
2673
|
unregisterTool,
|
|
2340
2674
|
registerArtifactProvider,
|
|
@@ -2356,6 +2690,9 @@ const Ansight = {
|
|
|
2356
2690
|
installReactTools,
|
|
2357
2691
|
uninstallReactTools,
|
|
2358
2692
|
installErrorHandlers,
|
|
2693
|
+
installNetworkCapture,
|
|
2694
|
+
uninstallNetworkCapture,
|
|
2695
|
+
sanitizeNetworkRequest,
|
|
2359
2696
|
createReactNavigationTracker,
|
|
2360
2697
|
platform: Platform.OS,
|
|
2361
2698
|
};
|
|
@@ -2374,3 +2711,7 @@ module.exports.registerArtifactProviders = registerArtifactProviders;
|
|
|
2374
2711
|
module.exports.unregisterArtifactProvider = unregisterArtifactProvider;
|
|
2375
2712
|
module.exports.listRegisteredArtifactProviders = listRegisteredArtifactProviders;
|
|
2376
2713
|
module.exports.clearArtifactProviders = clearArtifactProviders;
|
|
2714
|
+
module.exports.recordNetworkRequest = recordNetworkRequest;
|
|
2715
|
+
module.exports.installNetworkCapture = installNetworkCapture;
|
|
2716
|
+
module.exports.uninstallNetworkCapture = uninstallNetworkCapture;
|
|
2717
|
+
module.exports.sanitizeNetworkRequest = sanitizeNetworkRequest;
|