@ansight/react-native 1.3.0-preview.1 → 1.3.0-preview.3

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 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.1`
81
- - Maven: `ai.ansight:ansight-android:1.3.0-preview.1`
80
+ - CocoaPods: `Ansight`, `AnsightObjC` version `1.3.0-preview.3`
81
+ - Maven: `ai.ansight:ansight-android:1.3.0-preview.3`
82
82
 
83
83
  ## Quickstart
84
84
 
@@ -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.1"
71
- implementation("ai.ansight:ansight-android:1.3.0-preview.1")
70
+ findProperty("ansightAndroidVersion") ?: "1.3.0-preview.3"
71
+ implementation("ai.ansight:ansight-android:1.3.0-preview.3")
72
72
  }
package/index.js CHANGED
@@ -1391,7 +1391,7 @@ function summarizeProps(props) {
1391
1391
  }
1392
1392
  const keys = Object.keys(props).filter((key) => key !== "children");
1393
1393
  const summary = { keys };
1394
- ["testID", "nativeID", "accessibilityLabel", "role"].forEach((key) => {
1394
+ ["testID", "nativeID", "accessibilityLabel", "accessibilityRole", "role"].forEach((key) => {
1395
1395
  if (props[key] != null && !isSensitiveKey(key)) {
1396
1396
  summary[key] = String(props[key]);
1397
1397
  }
@@ -1403,6 +1403,42 @@ function summarizeProps(props) {
1403
1403
  return summary;
1404
1404
  }
1405
1405
 
1406
+ function reactSemanticRole(type, props, fiberTag) {
1407
+ const declared = props && (props.accessibilityRole || props.role);
1408
+ if (declared) return String(declared).toLowerCase();
1409
+ if (fiberTag === 6 || /text/i.test(type)) return "text";
1410
+ if (/button|pressable|touchable/i.test(type)) return "button";
1411
+ if (/textinput/i.test(type)) return "textbox";
1412
+ if (/switch/i.test(type)) return "switch";
1413
+ if (/scrollview|flatlist|sectionlist/i.test(type)) return "scrollview";
1414
+ return "view";
1415
+ }
1416
+
1417
+ function reactSupportedActions(type, props) {
1418
+ const actions = [];
1419
+ if (props && typeof props.onPress === "function") actions.push("tap");
1420
+ if (props && (typeof props.onChangeText === "function" || /textinput/i.test(type))) {
1421
+ actions.push("typeText", "focus");
1422
+ }
1423
+ if (/scrollview|flatlist|sectionlist/i.test(type) || (props && typeof props.onScroll === "function")) {
1424
+ actions.push("scroll", "swipe");
1425
+ }
1426
+ return actions;
1427
+ }
1428
+
1429
+ function applyReactTargetability(node, fiber, props, type) {
1430
+ const style = StyleSheet && typeof StyleSheet.flatten === "function"
1431
+ ? StyleSheet.flatten(props && props.style)
1432
+ : props && props.style;
1433
+ node.text = node.label || (node.visual && node.visual.text) || null;
1434
+ node.role = reactSemanticRole(type, props, fiber.tag);
1435
+ node.supportedActions = reactSupportedActions(type, props);
1436
+ node.visible = !(style && style.display === "none") && !(props && props.accessibilityElementsHidden);
1437
+ node.enabled = !(props && (props.disabled === true || props.accessibilityState && props.accessibilityState.disabled === true));
1438
+ node.focusable = !!(props && props.focusable) || node.supportedActions.includes("focus");
1439
+ node.interactable = node.visible && node.enabled && node.supportedActions.length > 0;
1440
+ }
1441
+
1406
1442
  function reactColorToArgbHex(value) {
1407
1443
  if (value == null) {
1408
1444
  return undefined;
@@ -1453,6 +1489,28 @@ function createReactVisual(fiber, props, maxStringLength) {
1453
1489
  return visual;
1454
1490
  }
1455
1491
 
1492
+ function createReactZIndex(props) {
1493
+ const style = StyleSheet && typeof StyleSheet.flatten === "function"
1494
+ ? StyleSheet.flatten(props && props.style)
1495
+ : props && props.style;
1496
+ const zIndex = style && style.zIndex != null ? Number(style.zIndex) : NaN;
1497
+ const elevation = style && style.elevation != null ? Number(style.elevation) : NaN;
1498
+ if (Number.isFinite(zIndex) && zIndex !== 0) return zIndex;
1499
+ if (Number.isFinite(elevation) && elevation !== 0) return elevation;
1500
+ return null;
1501
+ }
1502
+
1503
+ function registerReactType(context, typeName) {
1504
+ const normalizedTypeName = String(typeName || "UnknownComponent").trim() || "UnknownComponent";
1505
+ const existingTypeId = context.typeIdsByName.get(normalizedTypeName);
1506
+ if (existingTypeId != null) return existingTypeId;
1507
+
1508
+ const typeId = context.types.length;
1509
+ context.types.push(normalizedTypeName);
1510
+ context.typeIdsByName.set(normalizedTypeName, typeId);
1511
+ return typeId;
1512
+ }
1513
+
1456
1514
  function nativeTagForFiber(fiber) {
1457
1515
  const stateNode = fiber && fiber.stateNode;
1458
1516
  if (!stateNode) {
@@ -1524,17 +1582,17 @@ function serializeFiber(fiber, context, depth) {
1524
1582
 
1525
1583
  const props = fiber.memoizedProps || fiber.pendingProps || {};
1526
1584
  const type = reactFiberTypeName(fiber);
1527
- const kind = reactFiberKind(fiber.tag);
1528
1585
  const node = {
1529
1586
  id: reactFiberId(fiber),
1530
- type,
1531
- kind,
1587
+ typeId: registerReactType(context, type),
1532
1588
  tag: fiber.tag,
1533
1589
  key: fiber.key == null ? null : String(fiber.key),
1534
1590
  depth,
1535
1591
  visual: createReactVisual(fiber, props, context.maxStringLength),
1536
1592
  children: [],
1537
1593
  };
1594
+ const zIndex = createReactZIndex(props);
1595
+ if (zIndex != null) node.z = zIndex;
1538
1596
 
1539
1597
  if (fiber._debugSource) {
1540
1598
  node.source = {
@@ -1546,7 +1604,7 @@ function serializeFiber(fiber, context, depth) {
1546
1604
 
1547
1605
  const owner = fiber._debugOwner && reactFiberTypeName(fiber._debugOwner);
1548
1606
  if (owner) {
1549
- node.owner = owner;
1607
+ node.ownerTypeId = registerReactType(context, owner);
1550
1608
  }
1551
1609
 
1552
1610
  const nativeTag = nativeTagForFiber(fiber);
@@ -1577,6 +1635,8 @@ function serializeFiber(fiber, context, depth) {
1577
1635
  node.state = sanitizeValue(fiber.memoizedState, context);
1578
1636
  }
1579
1637
 
1638
+ applyReactTargetability(node, fiber, props, type);
1639
+
1580
1640
  if (depth < context.maxDepth) {
1581
1641
  let child = fiber.child;
1582
1642
  while (child) {
@@ -1600,16 +1660,18 @@ function isShadowTreeFiber(fiber) {
1600
1660
 
1601
1661
  function createShadowTreeNode(fiber, context, depth) {
1602
1662
  const props = fiber.memoizedProps || fiber.pendingProps || {};
1663
+ const type = reactFiberTypeName(fiber);
1603
1664
  const node = {
1604
1665
  id: reactFiberId(fiber),
1605
- type: reactFiberTypeName(fiber),
1606
- kind: fiber.tag === 3 ? "root" : fiber.tag === 6 ? "text" : "host",
1666
+ typeId: registerReactType(context, type),
1607
1667
  tag: fiber.tag,
1608
1668
  key: fiber.key == null ? null : String(fiber.key),
1609
1669
  depth,
1610
1670
  visual: createReactVisual(fiber, props, context.maxStringLength),
1611
1671
  children: [],
1612
1672
  };
1673
+ const zIndex = createReactZIndex(props);
1674
+ if (zIndex != null) node.z = zIndex;
1613
1675
 
1614
1676
  const nativeTag = nativeTagForFiber(fiber);
1615
1677
  if (nativeTag != null) {
@@ -1635,6 +1697,8 @@ function createShadowTreeNode(fiber, context, depth) {
1635
1697
  }
1636
1698
  }
1637
1699
 
1700
+ applyReactTargetability(node, fiber, props, type);
1701
+
1638
1702
  return node;
1639
1703
  }
1640
1704
 
@@ -1691,19 +1755,21 @@ async function captureReactVisualTree(rawOptions = {}) {
1691
1755
  maxStringLength: rawOptions.maxStringLength || 180,
1692
1756
  maxValueDepth: rawOptions.maxValueDepth || 2,
1693
1757
  nodesWithNativeTags: [],
1758
+ typeIdsByName: new Map(),
1759
+ types: [],
1694
1760
  count: 0,
1695
1761
  truncated: false,
1696
1762
  visited: new Set(),
1697
1763
  };
1698
1764
  const roots = getReactRoots();
1699
- const rootNodes = roots.roots.map((root, index) => {
1765
+ const rootNodes = roots.roots.map((root) => {
1700
1766
  const node = serializeFiber(root.fiber, context, 0);
1701
1767
  if (node) {
1702
1768
  node.rendererId = root.rendererId;
1703
- node.rootIndex = index;
1704
1769
  }
1705
1770
  return node;
1706
1771
  }).filter(Boolean);
1772
+ const rootTypeId = registerReactType(context, "ReactRoots");
1707
1773
 
1708
1774
  if (includeBounds && context.nodesWithNativeTags.length > 0) {
1709
1775
  await Promise.all(context.nodesWithNativeTags.slice(0, 300).map(async (node) => {
@@ -1715,6 +1781,7 @@ async function captureReactVisualTree(rawOptions = {}) {
1715
1781
  }
1716
1782
 
1717
1783
  return {
1784
+ format: "ansight.react.visual-tree.compact.v2",
1718
1785
  platform: Platform.OS,
1719
1786
  source: "react",
1720
1787
  adapter: "react.fiber",
@@ -1724,11 +1791,11 @@ async function captureReactVisualTree(rawOptions = {}) {
1724
1791
  renderers: roots.renderers,
1725
1792
  root: {
1726
1793
  id: "react:roots",
1727
- type: "ReactRoots",
1728
- kind: "container",
1794
+ typeId: rootTypeId,
1795
+ childCount: rootNodes.length,
1729
1796
  children: rootNodes,
1730
1797
  },
1731
- roots: rootNodes,
1798
+ types: context.types,
1732
1799
  nodeCount: context.count,
1733
1800
  truncated: context.truncated,
1734
1801
  unavailableReason: roots.hookAvailable ? undefined : "React DevTools global hook is not available in this runtime.",
@@ -1748,19 +1815,21 @@ async function captureReactShadowTree(rawOptions = {}) {
1748
1815
  maxStringLength: rawOptions.maxStringLength || 180,
1749
1816
  maxValueDepth: rawOptions.maxValueDepth || 2,
1750
1817
  nodesWithNativeTags: [],
1818
+ typeIdsByName: new Map(),
1819
+ types: [],
1751
1820
  count: 0,
1752
1821
  truncated: false,
1753
1822
  visited: new Set(),
1754
1823
  };
1755
1824
  const roots = getReactRoots();
1756
- const rootNodes = roots.roots.flatMap((root, index) => {
1825
+ const rootNodes = roots.roots.flatMap((root) => {
1757
1826
  const nodes = serializeShadowFiber(root.fiber, context, 0);
1758
1827
  nodes.forEach((node) => {
1759
1828
  node.rendererId = root.rendererId;
1760
- node.rootIndex = index;
1761
1829
  });
1762
1830
  return nodes;
1763
1831
  });
1832
+ const rootTypeId = registerReactType(context, "ReactNativeShadowRoots");
1764
1833
 
1765
1834
  if (includeBounds && context.nodesWithNativeTags.length > 0) {
1766
1835
  await Promise.all(context.nodesWithNativeTags.slice(0, 300).map(async (node) => {
@@ -1772,6 +1841,7 @@ async function captureReactShadowTree(rawOptions = {}) {
1772
1841
  }
1773
1842
 
1774
1843
  return {
1844
+ format: "ansight.react.visual-tree.compact.v2",
1775
1845
  platform: Platform.OS,
1776
1846
  source: "react-native",
1777
1847
  adapter: "react-native.host-fiber",
@@ -1781,11 +1851,11 @@ async function captureReactShadowTree(rawOptions = {}) {
1781
1851
  renderers: roots.renderers,
1782
1852
  root: {
1783
1853
  id: "react:shadow-roots",
1784
- type: "ReactNativeShadowRoots",
1785
- kind: "container",
1854
+ typeId: rootTypeId,
1855
+ childCount: rootNodes.length,
1786
1856
  children: rootNodes,
1787
1857
  },
1788
- roots: rootNodes,
1858
+ types: context.types,
1789
1859
  nodeCount: context.count,
1790
1860
  truncated: context.truncated,
1791
1861
  unavailableReason: roots.hookAvailable ? undefined : "React DevTools global hook is not available in this runtime.",
@@ -1801,13 +1871,18 @@ function flattenReactTree(node, output = []) {
1801
1871
  return output;
1802
1872
  }
1803
1873
 
1804
- function nodeSearchText(node) {
1874
+ function createReactNodeSnapshot(node) {
1875
+ const snapshot = { ...node };
1876
+ delete snapshot.children;
1877
+ return snapshot;
1878
+ }
1879
+
1880
+ function nodeSearchText(node, types) {
1805
1881
  return [
1806
1882
  node.id,
1807
- node.type,
1808
- node.kind,
1883
+ types[node.typeId],
1809
1884
  node.label,
1810
- node.owner,
1885
+ types[node.ownerTypeId],
1811
1886
  node.propsSummary && node.propsSummary.testID,
1812
1887
  node.propsSummary && node.propsSummary.nativeID,
1813
1888
  node.propsSummary && node.propsSummary.accessibilityLabel,
@@ -1815,14 +1890,14 @@ function nodeSearchText(node) {
1815
1890
  ].filter(Boolean).join(" ").toLowerCase();
1816
1891
  }
1817
1892
 
1818
- function matchesReactNode(node, args) {
1893
+ function matchesReactNode(node, args, types) {
1819
1894
  const query = args.query ? String(args.query).toLowerCase() : null;
1820
1895
  const type = args.type ? String(args.type).toLowerCase() : null;
1821
1896
  const testID = args.testID ? String(args.testID).toLowerCase() : null;
1822
1897
  const text = args.text ? String(args.text).toLowerCase() : null;
1823
- const searchText = nodeSearchText(node);
1898
+ const searchText = nodeSearchText(node, types);
1824
1899
  return (!query || searchText.includes(query)) &&
1825
- (!type || String(node.type || "").toLowerCase().includes(type)) &&
1900
+ (!type || String(types[node.typeId] || "").toLowerCase().includes(type)) &&
1826
1901
  (!testID || String((node.propsSummary && node.propsSummary.testID) || "").toLowerCase() === testID) &&
1827
1902
  (!text || searchText.includes(text));
1828
1903
  }
@@ -2043,12 +2118,14 @@ function installReactTools(options = {}) {
2043
2118
  });
2044
2119
  const maxResults = parseInteger(args.maxResults, 50, 1, 500);
2045
2120
  const matches = flattenReactTree(tree.root)
2046
- .filter((node) => node.id !== "react:roots" && matchesReactNode(node, args))
2047
- .slice(0, maxResults);
2121
+ .filter((node) => node.id !== "react:roots" && matchesReactNode(node, args, tree.types))
2122
+ .slice(0, maxResults)
2123
+ .map(createReactNodeSnapshot);
2048
2124
  return {
2049
2125
  success: tree.hookAvailable,
2050
2126
  message: `Found ${matches.length} React component(s).`,
2051
2127
  result: {
2128
+ types: tree.types,
2052
2129
  matches,
2053
2130
  count: matches.length,
2054
2131
  truncated: matches.length === maxResults,
@@ -2060,7 +2137,11 @@ function installReactTools(options = {}) {
2060
2137
  const tree = await captureReactVisualTree(reactToolOptions(options, args));
2061
2138
  const node = flattenReactTree(tree.root).find((candidate) => candidate.id === args.nodeId);
2062
2139
  return node
2063
- ? { success: true, message: "React component captured.", result: node }
2140
+ ? {
2141
+ success: true,
2142
+ message: "React component captured.",
2143
+ result: { types: tree.types, node: createReactNodeSnapshot(node) },
2144
+ }
2064
2145
  : { success: false, message: `React component '${args.nodeId}' was not found.`, errorCode: "react_component_not_found" };
2065
2146
  }
2066
2147
 
@@ -2101,7 +2182,7 @@ function installReactTools(options = {}) {
2101
2182
  result: {
2102
2183
  nodeId: args.nodeId,
2103
2184
  prop,
2104
- type: node.type,
2185
+ type: tree.types[node.typeId],
2105
2186
  },
2106
2187
  };
2107
2188
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ansight/react-native",
3
- "version": "1.3.0-preview.1",
3
+ "version": "1.3.0-preview.3",
4
4
  "description": "React Native bridge for the Ansight mobile SDK.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",