@ansight/react-native 1.1.0-preview.1 → 1.2.0-preview.2
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 -2
- package/android/build.gradle +2 -2
- package/android/src/main/kotlin/ai/ansight/reactnative/AnsightReactNativeModule.kt +2 -2
- package/app.plugin.js +3 -0
- package/expo-plugin.js +56 -0
- package/index.js +62 -1
- package/ios/AnsightReactNativeMemorySampler.mm +5 -6
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@ JavaScript-backed tools for React component-tree inspection.
|
|
|
10
10
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
13
|
+
### React Native CLI
|
|
14
|
+
|
|
13
15
|
```sh
|
|
14
16
|
npm install @ansight/react-native
|
|
15
17
|
```
|
|
@@ -23,10 +25,60 @@ npx react-native run-ios
|
|
|
23
25
|
npx react-native run-android
|
|
24
26
|
```
|
|
25
27
|
|
|
28
|
+
### Expo development builds
|
|
29
|
+
|
|
30
|
+
Ansight contains native iOS and Android code, so it requires an Expo
|
|
31
|
+
development build or production/EAS build. It does not run in Expo Go.
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npx expo install @ansight/react-native
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Add the bundled config plugin to the app config. It supplies the iOS camera
|
|
38
|
+
usage description required by QR enrollment and the local-network description
|
|
39
|
+
used when connecting to Ansight Studio:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"expo": {
|
|
44
|
+
"plugins": [
|
|
45
|
+
[
|
|
46
|
+
"@ansight/react-native",
|
|
47
|
+
{
|
|
48
|
+
"cameraPermission": "Allow $(PRODUCT_NAME) to scan an Ansight Studio enrollment QR code.",
|
|
49
|
+
"localNetworkPermission": "Allow $(PRODUCT_NAME) to connect to Ansight Studio on your local network."
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Omit either message to use the default. Set `cameraPermission` to `false` only
|
|
58
|
+
when the app will never call `enrollFromQrCode` or `scanPairingQrCode`. Setting
|
|
59
|
+
a permission option to `false` prevents Ansight from adding that key; it does
|
|
60
|
+
not remove a value supplied by another plugin or by the app.
|
|
61
|
+
|
|
62
|
+
Generate and rebuild the native projects after installing or updating the SDK
|
|
63
|
+
or changing its plugin options:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
npx expo prebuild --clean
|
|
67
|
+
npx expo run:ios
|
|
68
|
+
npx expo run:android
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
EAS Build applies the same plugin during prebuild. Expo Updates can update the
|
|
72
|
+
JavaScript integration after the native binary has been built with a compatible
|
|
73
|
+
version of `@ansight/react-native`.
|
|
74
|
+
|
|
75
|
+
Expo Web is not currently supported. Do not import this package from a web
|
|
76
|
+
bundle; its API requires the native Ansight module.
|
|
77
|
+
|
|
26
78
|
This package version expects matching native SDK packages:
|
|
27
79
|
|
|
28
|
-
- CocoaPods: `Ansight`, `AnsightObjC` version `1.
|
|
29
|
-
- Maven: `ai.ansight:ansight-android:1.
|
|
80
|
+
- CocoaPods: `Ansight`, `AnsightObjC` version `1.2.0-preview.2`
|
|
81
|
+
- Maven: `ai.ansight:ansight-android:1.2.0-preview.2`
|
|
30
82
|
|
|
31
83
|
## Quickstart
|
|
32
84
|
|
|
@@ -527,3 +579,14 @@ The first-party validation app lives in:
|
|
|
527
579
|
It exercises the native runtime bridge, standard native remote tools,
|
|
528
580
|
JavaScript custom tools, React visual-tree tools, SQLite/file fixtures,
|
|
529
581
|
screenshot capture, and touch/session telemetry.
|
|
582
|
+
|
|
583
|
+
The current-Expo validation app lives in:
|
|
584
|
+
|
|
585
|
+
```text
|
|
586
|
+
/Users/matthewrobbins/Development/git/ansight-sdk-test-apps/react-native/ansight-expo-harness
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
It is pinned to Expo SDK 57 and React Native 0.86 with the New Architecture and
|
|
590
|
+
Hermes enabled. It validates Expo CNG/autolinking, the bundled config plugin,
|
|
591
|
+
Android and iOS native builds, and the same live Studio feature surface as the
|
|
592
|
+
baseline harness.
|
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.
|
|
71
|
-
implementation("ai.ansight:ansight-android:1.
|
|
70
|
+
findProperty("ansightAndroidVersion") ?: "1.2.0-preview.2"
|
|
71
|
+
implementation("ai.ansight:ansight-android:1.2.0-preview.2")
|
|
72
72
|
}
|
|
@@ -267,7 +267,7 @@ class AnsightReactNativeModule(
|
|
|
267
267
|
|
|
268
268
|
@ReactMethod
|
|
269
269
|
fun scanPairingQrCode(options: ReadableMap?, promise: Promise) {
|
|
270
|
-
val activity = currentActivity
|
|
270
|
+
val activity = reactContext.currentActivity
|
|
271
271
|
if (activity == null) {
|
|
272
272
|
promise.reject("ansight_qr_unavailable", "QR pairing is unavailable because no Android activity is available.")
|
|
273
273
|
return
|
|
@@ -1202,7 +1202,7 @@ class AnsightReactNativeModule(
|
|
|
1202
1202
|
?: error("React Native application context is not an Android Application.")
|
|
1203
1203
|
|
|
1204
1204
|
private fun bindCurrentActivity() {
|
|
1205
|
-
val activity: Activity = currentActivity ?: return
|
|
1205
|
+
val activity: Activity = reactContext.currentActivity ?: return
|
|
1206
1206
|
AnsightRuntime.bindActivity(activity)
|
|
1207
1207
|
}
|
|
1208
1208
|
}
|
package/app.plugin.js
ADDED
package/expo-plugin.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_CAMERA_PERMISSION =
|
|
4
|
+
"Allow $(PRODUCT_NAME) to use the camera to scan an Ansight Studio enrollment QR code.";
|
|
5
|
+
const DEFAULT_LOCAL_NETWORK_PERMISSION =
|
|
6
|
+
"Allow $(PRODUCT_NAME) to connect to Ansight Studio on your local network during development.";
|
|
7
|
+
|
|
8
|
+
function withAnsightReactNative(config, options = {}) {
|
|
9
|
+
const { withInfoPlist } = loadExpoConfigPlugins();
|
|
10
|
+
|
|
11
|
+
return withInfoPlist(config, (modConfig) => {
|
|
12
|
+
const infoPlist = modConfig.modResults;
|
|
13
|
+
|
|
14
|
+
applyPermission(
|
|
15
|
+
infoPlist,
|
|
16
|
+
"NSCameraUsageDescription",
|
|
17
|
+
options.cameraPermission,
|
|
18
|
+
DEFAULT_CAMERA_PERMISSION,
|
|
19
|
+
);
|
|
20
|
+
applyPermission(
|
|
21
|
+
infoPlist,
|
|
22
|
+
"NSLocalNetworkUsageDescription",
|
|
23
|
+
options.localNetworkPermission,
|
|
24
|
+
DEFAULT_LOCAL_NETWORK_PERMISSION,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
return modConfig;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function loadExpoConfigPlugins() {
|
|
32
|
+
const modulePath = require.resolve("expo/config-plugins", {
|
|
33
|
+
paths: [process.cwd(), __dirname],
|
|
34
|
+
});
|
|
35
|
+
return require(modulePath);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function applyPermission(infoPlist, key, configuredValue, defaultValue) {
|
|
39
|
+
if (configuredValue === false) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (typeof configuredValue === "string" && configuredValue.trim()) {
|
|
44
|
+
infoPlist[key] = configuredValue;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (typeof infoPlist[key] !== "string" || !infoPlist[key].trim()) {
|
|
49
|
+
infoPlist[key] = defaultValue;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = withAnsightReactNative;
|
|
54
|
+
module.exports.default = withAnsightReactNative;
|
|
55
|
+
module.exports.DEFAULT_CAMERA_PERMISSION = DEFAULT_CAMERA_PERMISSION;
|
|
56
|
+
module.exports.DEFAULT_LOCAL_NETWORK_PERMISSION = DEFAULT_LOCAL_NETWORK_PERMISSION;
|
package/index.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const {
|
|
4
|
+
AppState,
|
|
5
|
+
NativeEventEmitter,
|
|
6
|
+
NativeModules,
|
|
7
|
+
Platform,
|
|
8
|
+
StyleSheet,
|
|
9
|
+
UIManager,
|
|
10
|
+
findNodeHandle,
|
|
11
|
+
processColor,
|
|
12
|
+
} = require("react-native");
|
|
4
13
|
|
|
5
14
|
const nativeModule = NativeModules.AnsightReactNative;
|
|
6
15
|
|
|
@@ -1392,6 +1401,56 @@ function summarizeProps(props) {
|
|
|
1392
1401
|
return summary;
|
|
1393
1402
|
}
|
|
1394
1403
|
|
|
1404
|
+
function reactColorToArgbHex(value) {
|
|
1405
|
+
if (value == null) {
|
|
1406
|
+
return undefined;
|
|
1407
|
+
}
|
|
1408
|
+
try {
|
|
1409
|
+
const processed = typeof processColor === "function" ? processColor(value) : null;
|
|
1410
|
+
if (typeof processed === "number") {
|
|
1411
|
+
return `#${(processed >>> 0).toString(16).padStart(8, "0")}`.toUpperCase();
|
|
1412
|
+
}
|
|
1413
|
+
} catch (_) {
|
|
1414
|
+
// Some dynamic platform colors cannot be represented as one resolved color.
|
|
1415
|
+
}
|
|
1416
|
+
return undefined;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
function reactVisualText(fiber, props, maxStringLength) {
|
|
1420
|
+
if (fiber.tag === 6) {
|
|
1421
|
+
return sanitizeString(String(fiber.memoizedProps || fiber.pendingProps || ""), maxStringLength);
|
|
1422
|
+
}
|
|
1423
|
+
const children = summarizeReactChildren(props.children);
|
|
1424
|
+
const candidate = typeof children === "string" || typeof children === "number"
|
|
1425
|
+
? children
|
|
1426
|
+
: props.placeholder ?? props.title ?? props.accessibilityLabel;
|
|
1427
|
+
return candidate == null ? undefined : sanitizeString(String(candidate), maxStringLength);
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
function createReactVisual(fiber, props, maxStringLength) {
|
|
1431
|
+
const style = StyleSheet && typeof StyleSheet.flatten === "function"
|
|
1432
|
+
? StyleSheet.flatten(props && props.style)
|
|
1433
|
+
: props && props.style;
|
|
1434
|
+
const opacity = style && Number.isFinite(Number(style.opacity))
|
|
1435
|
+
? Math.max(0, Math.min(1, Number(style.opacity)))
|
|
1436
|
+
: 1;
|
|
1437
|
+
const visual = { opacity };
|
|
1438
|
+
const foreground = reactColorToArgbHex(style && style.color);
|
|
1439
|
+
const background = reactColorToArgbHex(style && style.backgroundColor);
|
|
1440
|
+
const text = reactVisualText(fiber, props || {}, maxStringLength);
|
|
1441
|
+
if (foreground) visual.foreground = foreground;
|
|
1442
|
+
if (background) visual.background = background;
|
|
1443
|
+
if (text) visual.text = text;
|
|
1444
|
+
|
|
1445
|
+
if (props && !props.secureTextEntry) {
|
|
1446
|
+
const value = props.value ?? props.defaultValue;
|
|
1447
|
+
if (value != null && ["string", "number", "boolean"].includes(typeof value)) {
|
|
1448
|
+
visual.value = sanitizeString(String(value), maxStringLength);
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
return visual;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1395
1454
|
function nativeTagForFiber(fiber) {
|
|
1396
1455
|
const stateNode = fiber && fiber.stateNode;
|
|
1397
1456
|
if (!stateNode) {
|
|
@@ -1471,6 +1530,7 @@ function serializeFiber(fiber, context, depth) {
|
|
|
1471
1530
|
tag: fiber.tag,
|
|
1472
1531
|
key: fiber.key == null ? null : String(fiber.key),
|
|
1473
1532
|
depth,
|
|
1533
|
+
visual: createReactVisual(fiber, props, context.maxStringLength),
|
|
1474
1534
|
children: [],
|
|
1475
1535
|
};
|
|
1476
1536
|
|
|
@@ -1545,6 +1605,7 @@ function createShadowTreeNode(fiber, context, depth) {
|
|
|
1545
1605
|
tag: fiber.tag,
|
|
1546
1606
|
key: fiber.key == null ? null : String(fiber.key),
|
|
1547
1607
|
depth,
|
|
1608
|
+
visual: createReactVisual(fiber, props, context.maxStringLength),
|
|
1548
1609
|
children: [],
|
|
1549
1610
|
};
|
|
1550
1611
|
|
|
@@ -12,10 +12,6 @@
|
|
|
12
12
|
#include <string>
|
|
13
13
|
#include <unordered_map>
|
|
14
14
|
|
|
15
|
-
@interface RCTBridge ()
|
|
16
|
-
- (void)invokeAsync:(std::function<void()> &&)func;
|
|
17
|
-
@end
|
|
18
|
-
|
|
19
15
|
namespace {
|
|
20
16
|
int64_t heapValue(
|
|
21
17
|
const std::unordered_map<std::string, int64_t>& heapInfo,
|
|
@@ -89,7 +85,7 @@ int64_t heapValue(
|
|
|
89
85
|
|
|
90
86
|
__weak AnsightReactNativeMemorySampler *weakSelf = self;
|
|
91
87
|
__weak RCTBridge *weakBridge = bridge;
|
|
92
|
-
[bridge
|
|
88
|
+
[bridge dispatchBlock:^{
|
|
93
89
|
AnsightReactNativeMemorySampler *strongSelf = weakSelf;
|
|
94
90
|
RCTBridge *strongBridge = weakBridge;
|
|
95
91
|
if (!strongSelf || !strongBridge) {
|
|
@@ -101,6 +97,9 @@ int64_t heapValue(
|
|
|
101
97
|
|
|
102
98
|
@try {
|
|
103
99
|
try {
|
|
100
|
+
// RCTBridgeProxy implements both dispatchBlock:queue: and runtime, so
|
|
101
|
+
// this works with the New Architecture without relying on the legacy
|
|
102
|
+
// RCTCxxBridge-only invokeAsync: selector.
|
|
104
103
|
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)strongBridge;
|
|
105
104
|
auto *runtime = reinterpret_cast<facebook::jsi::Runtime *>(cxxBridge.runtime);
|
|
106
105
|
if (runtime) {
|
|
@@ -124,7 +123,7 @@ int64_t heapValue(
|
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
strongSelf->_refreshScheduled.store(false);
|
|
127
|
-
}];
|
|
126
|
+
} queue:RCTJSThread];
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
@end
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ansight/react-native",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0-preview.2",
|
|
4
4
|
"description": "React Native bridge for the Ansight mobile SDK.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
"README.md",
|
|
26
26
|
"index.js",
|
|
27
27
|
"index.d.ts",
|
|
28
|
+
"app.plugin.js",
|
|
29
|
+
"expo-plugin.js",
|
|
28
30
|
"tsconfig.json",
|
|
29
31
|
"AnsightReactNative.podspec",
|
|
30
32
|
"react-native.config.js"
|
|
@@ -32,6 +34,7 @@
|
|
|
32
34
|
"keywords": [
|
|
33
35
|
"ansight",
|
|
34
36
|
"react-native",
|
|
37
|
+
"expo",
|
|
35
38
|
"sdk",
|
|
36
39
|
"debugging",
|
|
37
40
|
"inspection"
|
|
@@ -41,7 +44,7 @@
|
|
|
41
44
|
"react-native": ">=0.72"
|
|
42
45
|
},
|
|
43
46
|
"scripts": {
|
|
44
|
-
"check": "node --check index.js && tsc --noEmit",
|
|
47
|
+
"check": "node --check index.js && node --check app.plugin.js && node --check expo-plugin.js && tsc --noEmit",
|
|
45
48
|
"typecheck": "tsc --noEmit"
|
|
46
49
|
},
|
|
47
50
|
"devDependencies": {
|