@ansight/react-native 1.0.2-preview.8 → 1.2.0-preview.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -40
- package/android/build.gradle +3 -1
- package/android/src/main/kotlin/ai/ansight/reactnative/AnsightReactNativeModule.kt +53 -14
- package/app.plugin.js +3 -0
- package/expo-plugin.js +56 -0
- package/index.d.ts +9 -6
- package/index.js +76 -13
- package/ios/AnsightReactNative.swift +19 -13
- package/ios/AnsightReactNativeMemorySampler.mm +5 -6
- package/ios/AnsightReactNativeModule.m +4 -0
- 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,41 +25,78 @@ 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.0
|
|
29
|
-
- Maven: `ai.ansight:ansight-android:1.0
|
|
80
|
+
- CocoaPods: `Ansight`, `AnsightObjC` version `1.2.0-preview.1`
|
|
81
|
+
- Maven: `ai.ansight:ansight-android:1.2.0-preview.1`
|
|
30
82
|
|
|
31
83
|
## Quickstart
|
|
32
84
|
|
|
33
85
|
```ts
|
|
34
86
|
import Ansight from "@ansight/react-native";
|
|
35
87
|
|
|
36
|
-
const isDevelopmentOnly = __DEV__;
|
|
37
|
-
|
|
38
88
|
await Ansight.initializeAndActivate({
|
|
39
|
-
useNativeAllInOneDefaults:
|
|
89
|
+
useNativeAllInOneDefaults: __DEV__,
|
|
40
90
|
clientName: "My React Native App",
|
|
41
|
-
|
|
42
|
-
enabled: isDevelopmentOnly,
|
|
43
|
-
initialDelayMilliseconds: 1000,
|
|
44
|
-
probeIntervalMilliseconds: 5000,
|
|
45
|
-
reconnectDelayMilliseconds: 10000,
|
|
46
|
-
clientName: "My React Native App",
|
|
47
|
-
},
|
|
48
|
-
hostConnection: isDevelopmentOnly ? {
|
|
49
|
-
bundledDeveloperConfigJson: process.env.EXPO_PUBLIC_ANSIGHT_PAIRING_CONFIG_JSON,
|
|
50
|
-
} : undefined,
|
|
51
|
-
toolGuard: isDevelopmentOnly ? "readOnly" : "disabled",
|
|
91
|
+
toolGuard: __DEV__ ? "readOnly" : "disabled",
|
|
52
92
|
lifecycle: true,
|
|
53
93
|
});
|
|
54
|
-
|
|
55
|
-
await Ansight.connect(null, {
|
|
56
|
-
clientName: "My React Native App",
|
|
57
|
-
expectedAppId: "com.example.app",
|
|
58
|
-
});
|
|
59
94
|
```
|
|
60
95
|
|
|
96
|
+
The native iOS Simulator or Android emulator runtime registers automatically
|
|
97
|
+
with a running, signed-in Studio. No pairing file, environment variable, host
|
|
98
|
+
address, or build-time Studio probe is required.
|
|
99
|
+
|
|
61
100
|
`useNativeAllInOneDefaults` defaults to `false`. It only applies the native
|
|
62
101
|
iOS/Android all-in-one defaults: 400 ms sampling, 120 second retention, FPS,
|
|
63
102
|
touch capture, 2000 ms JPEG capture at quality 60 and max width 480, host
|
|
@@ -92,7 +131,6 @@ The TypeScript `AnsightOptions` surface mirrors Android `AnsightOptions`, iOS
|
|
|
92
131
|
| Option | Purpose |
|
|
93
132
|
| --- | --- |
|
|
94
133
|
| `useNativeAllInOneDefaults` | Applies native iOS/Android all-in-one defaults when true. Defaults to false. This enables the native visual-tree/screenshot tools required by Studio visual tree inspection unless `remoteTools.visualTree` is explicitly false. Configure `toolGuard`, capture options, and `hostConnection` separately. |
|
|
95
|
-
| `pairingConfigJson` | Legacy top-level pairing JSON. Prefer `hostConnection.*`. |
|
|
96
134
|
| `clientName` | Default client name for host auto-probe and connections. |
|
|
97
135
|
| `sampleFrequencyMilliseconds` | Built-in telemetry sampling interval. |
|
|
98
136
|
| `retentionPeriodSeconds` | Local metric/event retention window. |
|
|
@@ -107,14 +145,14 @@ The TypeScript `AnsightOptions` surface mirrors Android `AnsightOptions`, iOS
|
|
|
107
145
|
| `toolGuard` | `"disabled"`, `"readOnly"`, `"readWrite"`, or `"fullAccess"`. |
|
|
108
146
|
| `customProperties` | Grouped string properties sent with `session.open`. |
|
|
109
147
|
| `hostAutoProbe` | Remembered-host retry settings for reconnecting after the host disappears and later reappears. |
|
|
110
|
-
| `hostConnection` |
|
|
148
|
+
| `hostConnection` | Enrollment reconnect and network policy. |
|
|
111
149
|
| `secureStorage` | Compatibility alias for native secure-storage allow-list settings. |
|
|
112
150
|
| `remoteTools` | Native visual tree, file, database, preferences, reflection, and secure-storage tool options. |
|
|
113
151
|
| `lifecycle` | JS AppState tracking toggle. Defaults to true. |
|
|
114
152
|
|
|
115
153
|
Cellular host connections are disabled by default. The restriction applies to
|
|
116
|
-
|
|
117
|
-
|
|
154
|
+
enrollment and reconnect requests. Opt in only for a trusted development host
|
|
155
|
+
or personal hotspot:
|
|
118
156
|
|
|
119
157
|
```ts
|
|
120
158
|
const options = Ansight.createOptionsBuilder()
|
|
@@ -124,7 +162,7 @@ const options = Ansight.createOptionsBuilder()
|
|
|
124
162
|
|
|
125
163
|
You can also set `hostConnection.allowCellularConnections: true` directly.
|
|
126
164
|
This may consume mobile data and permits connection attempts over a broader or
|
|
127
|
-
carrier-managed network;
|
|
165
|
+
carrier-managed network; use it only with a trusted development host.
|
|
128
166
|
|
|
129
167
|
Example:
|
|
130
168
|
|
|
@@ -206,36 +244,36 @@ compatibility alias for `remoteTools.secureStorage`.
|
|
|
206
244
|
|
|
207
245
|
## Host Connection
|
|
208
246
|
|
|
209
|
-
|
|
247
|
+
No connection call is needed for a simulator or emulator. On a physical
|
|
248
|
+
device, scan the QR displayed by Studio once:
|
|
210
249
|
|
|
211
250
|
```ts
|
|
212
|
-
await Ansight.
|
|
251
|
+
await Ansight.enrollFromQrCode({
|
|
213
252
|
clientName: "My React Native App",
|
|
214
253
|
expectedAppId: "com.example.app",
|
|
215
254
|
});
|
|
216
255
|
```
|
|
217
256
|
|
|
218
|
-
|
|
257
|
+
After physical-device enrollment, `connect(null, options)` and the runtime
|
|
258
|
+
connection loop use the remembered registration:
|
|
219
259
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
4. `hostConnection.bundledConfigJson`
|
|
260
|
+
```ts
|
|
261
|
+
await Ansight.connect(null, { clientName: "My React Native App" });
|
|
262
|
+
```
|
|
224
263
|
|
|
225
|
-
|
|
264
|
+
When Studio is closed or signed out, automatic attempts remain dormant and
|
|
265
|
+
retry later without failing the React Native app.
|
|
266
|
+
|
|
267
|
+
If the app already owns a scanner, pass its result through the explicit payload
|
|
268
|
+
API:
|
|
226
269
|
|
|
227
270
|
```ts
|
|
228
|
-
await Ansight.connect(
|
|
271
|
+
await Ansight.connect(enrollmentPayload, {
|
|
229
272
|
clientName: "My React Native App",
|
|
230
273
|
expectedAppId: "com.example.app",
|
|
231
274
|
hostAddressOverride: "192.168.1.20",
|
|
232
275
|
});
|
|
233
276
|
|
|
234
|
-
await Ansight.savePairingConfig(pairingJson, {
|
|
235
|
-
expectedAppId: "com.example.app",
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
await Ansight.clearSavedPairing();
|
|
239
277
|
await Ansight.clearCachedSession();
|
|
240
278
|
await Ansight.disconnect();
|
|
241
279
|
```
|
|
@@ -541,3 +579,14 @@ The first-party validation app lives in:
|
|
|
541
579
|
It exercises the native runtime bridge, standard native remote tools,
|
|
542
580
|
JavaScript custom tools, React visual-tree tools, SQLite/file fixtures,
|
|
543
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
|
@@ -66,5 +66,7 @@ repositories {
|
|
|
66
66
|
|
|
67
67
|
dependencies {
|
|
68
68
|
implementation("com.facebook.react:react-android")
|
|
69
|
-
|
|
69
|
+
def ansightAndroidVersion =
|
|
70
|
+
findProperty("ansightAndroidVersion") ?: "1.2.0-preview.1"
|
|
71
|
+
implementation("ai.ansight:ansight-android:1.2.0-preview.1")
|
|
70
72
|
}
|
|
@@ -38,6 +38,7 @@ import ai.ansight.runtime.ToolScope
|
|
|
38
38
|
import ai.ansight.runtime.ToolSecurity
|
|
39
39
|
import ai.ansight.runtime.ToolSecurityLevel
|
|
40
40
|
import ai.ansight.runtime.sendBinaryTransfer
|
|
41
|
+
import ai.ansight.pairing.AnsightPairing
|
|
41
42
|
import ai.ansight.tools.database.AndroidDatabaseRoot
|
|
42
43
|
import ai.ansight.tools.database.AndroidDatabaseToolsOptions
|
|
43
44
|
import ai.ansight.tools.database.withDatabaseTools
|
|
@@ -74,6 +75,7 @@ import java.util.concurrent.CountDownLatch
|
|
|
74
75
|
import java.util.concurrent.Executors
|
|
75
76
|
import java.util.concurrent.TimeUnit
|
|
76
77
|
import java.util.concurrent.atomic.AtomicInteger
|
|
78
|
+
import java.util.concurrent.atomic.AtomicBoolean
|
|
77
79
|
import java.util.Locale
|
|
78
80
|
|
|
79
81
|
class AnsightReactNativeModule(
|
|
@@ -263,6 +265,56 @@ class AnsightReactNativeModule(
|
|
|
263
265
|
}
|
|
264
266
|
}
|
|
265
267
|
|
|
268
|
+
@ReactMethod
|
|
269
|
+
fun scanPairingQrCode(options: ReadableMap?, promise: Promise) {
|
|
270
|
+
val activity = reactContext.currentActivity
|
|
271
|
+
if (activity == null) {
|
|
272
|
+
promise.reject("ansight_qr_unavailable", "QR pairing is unavailable because no Android activity is available.")
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
val completed = AtomicBoolean(false)
|
|
277
|
+
AnsightPairing.scanQrCode(
|
|
278
|
+
activity = activity,
|
|
279
|
+
onPayload = { payload ->
|
|
280
|
+
if (!completed.compareAndSet(false, true)) {
|
|
281
|
+
return@scanQrCode
|
|
282
|
+
}
|
|
283
|
+
if (payload.isNullOrBlank()) {
|
|
284
|
+
promise.resolve(
|
|
285
|
+
hostConnectionResultMap(
|
|
286
|
+
HostConnectionResult.failure(
|
|
287
|
+
message = "QR pairing canceled.",
|
|
288
|
+
reasonCode = "pairing_canceled",
|
|
289
|
+
),
|
|
290
|
+
),
|
|
291
|
+
)
|
|
292
|
+
return@scanQrCode
|
|
293
|
+
}
|
|
294
|
+
backgroundExecutor.execute {
|
|
295
|
+
runCatching {
|
|
296
|
+
hostConnectionResultMap(
|
|
297
|
+
AnsightRuntime.connect(
|
|
298
|
+
HostConnectionRequest(
|
|
299
|
+
kind = HostConnectionRequestKind.QrCode,
|
|
300
|
+
payload = payload,
|
|
301
|
+
clientName = options.stringValue("clientName"),
|
|
302
|
+
expectedAppId = options.stringValue("expectedAppId"),
|
|
303
|
+
hostAddressOverride = options.stringValue("hostAddressOverride"),
|
|
304
|
+
),
|
|
305
|
+
),
|
|
306
|
+
)
|
|
307
|
+
}.resolve(promise)
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
onError = { error ->
|
|
311
|
+
if (completed.compareAndSet(false, true)) {
|
|
312
|
+
promise.reject("ansight_qr_error", error.message, error)
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
|
|
266
318
|
@ReactMethod
|
|
267
319
|
fun openSession(pairingPayload: String?, options: ReadableMap?, promise: Promise) {
|
|
268
320
|
backgroundExecutor.execute {
|
|
@@ -616,23 +668,14 @@ class AnsightReactNativeModule(
|
|
|
616
668
|
|
|
617
669
|
private fun buildOptions(map: ReadableMap?): AnsightOptions {
|
|
618
670
|
val useNativeAllInOneDefaults = map.booleanValue("useNativeAllInOneDefaults", false)
|
|
619
|
-
val pairingConfigJson = map.stringValue("pairingConfigJson")
|
|
620
671
|
val clientName = map.stringValue("clientName")
|
|
621
672
|
var options = if (useNativeAllInOneDefaults) {
|
|
622
673
|
AnsightDeveloperMode.options(
|
|
623
|
-
bundledDeveloperConfigJson = pairingConfigJson,
|
|
624
674
|
clientName = clientName,
|
|
625
675
|
)
|
|
626
676
|
} else {
|
|
627
677
|
AnsightOptions()
|
|
628
678
|
}
|
|
629
|
-
if (!useNativeAllInOneDefaults && !pairingConfigJson.isNullOrBlank()) {
|
|
630
|
-
options = options.copy(
|
|
631
|
-
hostConnection = options.hostConnection.copy(
|
|
632
|
-
bundledConfigJson = pairingConfigJson,
|
|
633
|
-
),
|
|
634
|
-
)
|
|
635
|
-
}
|
|
636
679
|
if (useNativeAllInOneDefaults && !map.hasString("toolGuard")) {
|
|
637
680
|
options = options.copy(toolGuard = AnsightToolGuard.ReadOnly)
|
|
638
681
|
}
|
|
@@ -747,7 +790,6 @@ class AnsightReactNativeModule(
|
|
|
747
790
|
hostConnection = AnsightHostConnectionOptions(
|
|
748
791
|
savedConfigKey = host.stringValue("savedConfigKey") ?: options.hostConnection.savedConfigKey,
|
|
749
792
|
bundledConfigJson = host.stringValue("bundledConfigJson") ?: options.hostConnection.bundledConfigJson,
|
|
750
|
-
bundledDeveloperConfigJson = host.stringValue("bundledDeveloperConfigJson") ?: options.hostConnection.bundledDeveloperConfigJson,
|
|
751
793
|
discoveryPort = host.optionalInt("discoveryPort") ?: options.hostConnection.discoveryPort,
|
|
752
794
|
allowCellularConnections = host.booleanValue(
|
|
753
795
|
"allowCellularConnections",
|
|
@@ -1018,7 +1060,6 @@ class AnsightReactNativeModule(
|
|
|
1018
1060
|
putString("configId", result.configId)
|
|
1019
1061
|
putString("appId", result.appId)
|
|
1020
1062
|
putString("resolvedHostAddress", result.resolvedHostAddress)
|
|
1021
|
-
putBoolean("usedEmbeddedDeveloperPairing", result.usedEmbeddedDeveloperPairing)
|
|
1022
1063
|
putString("discoverySource", result.discoverySource)
|
|
1023
1064
|
putString("reasonCode", result.reasonCode)
|
|
1024
1065
|
putString("hostId", result.hostId)
|
|
@@ -1038,7 +1079,6 @@ class AnsightReactNativeModule(
|
|
|
1038
1079
|
putString("hostId", session.hostId)
|
|
1039
1080
|
putString("hostName", session.hostName)
|
|
1040
1081
|
putBoolean("accepted", session.accepted)
|
|
1041
|
-
putBoolean("usedEmbeddedDeveloperPairing", session.usedEmbeddedDeveloperPairing)
|
|
1042
1082
|
putString("discoverySource", session.discoverySource)
|
|
1043
1083
|
}
|
|
1044
1084
|
}
|
|
@@ -1097,7 +1137,6 @@ class AnsightReactNativeModule(
|
|
|
1097
1137
|
putMap("hostConnection", mapOf(
|
|
1098
1138
|
"savedConfigKey" to options.hostConnection.savedConfigKey,
|
|
1099
1139
|
"hasBundledConfigJson" to (options.hostConnection.bundledConfigJson != null),
|
|
1100
|
-
"hasBundledDeveloperConfigJson" to (options.hostConnection.bundledDeveloperConfigJson != null),
|
|
1101
1140
|
"discoveryPort" to options.hostConnection.discoveryPort,
|
|
1102
1141
|
"allowCellularConnections" to options.hostConnection.allowCellularConnections,
|
|
1103
1142
|
"connectionProfileRetentionSeconds" to options.hostConnection.connectionProfileRetentionSeconds,
|
|
@@ -1163,7 +1202,7 @@ class AnsightReactNativeModule(
|
|
|
1163
1202
|
?: error("React Native application context is not an Android Application.")
|
|
1164
1203
|
|
|
1165
1204
|
private fun bindCurrentActivity() {
|
|
1166
|
-
val activity: Activity = currentActivity ?: return
|
|
1205
|
+
val activity: Activity = reactContext.currentActivity ?: return
|
|
1167
1206
|
AnsightRuntime.bindActivity(activity)
|
|
1168
1207
|
}
|
|
1169
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.d.ts
CHANGED
|
@@ -104,9 +104,6 @@ export interface AnsightOptions {
|
|
|
104
104
|
* `hostAutoProbe`, and `hostConnection` explicitly for the workflow.
|
|
105
105
|
*/
|
|
106
106
|
useNativeAllInOneDefaults?: boolean;
|
|
107
|
-
pairingConfig?: string | object;
|
|
108
|
-
bundledPairingConfig?: string | object;
|
|
109
|
-
pairingConfigJson?: string;
|
|
110
107
|
clientName?: string;
|
|
111
108
|
sampleFrequencyMilliseconds?: number;
|
|
112
109
|
retentionPeriodSeconds?: number;
|
|
@@ -141,7 +138,6 @@ export interface AnsightOptions {
|
|
|
141
138
|
hostConnection?: {
|
|
142
139
|
savedConfigKey?: string;
|
|
143
140
|
bundledConfigJson?: string;
|
|
144
|
-
bundledDeveloperConfigJson?: string;
|
|
145
141
|
discoveryPort?: number;
|
|
146
142
|
allowCellularConnections?: boolean;
|
|
147
143
|
connectionProfileRetentionSeconds?: number;
|
|
@@ -209,7 +205,6 @@ export interface AnsightHostConnectionResult extends AnsightOperationResult {
|
|
|
209
205
|
configId?: string;
|
|
210
206
|
appId?: string;
|
|
211
207
|
resolvedHostAddress?: string;
|
|
212
|
-
usedEmbeddedDeveloperPairing?: boolean;
|
|
213
208
|
discoverySource?: string;
|
|
214
209
|
hostId?: string;
|
|
215
210
|
hostName?: string;
|
|
@@ -290,7 +285,6 @@ export class AnsightOptionsBuilder {
|
|
|
290
285
|
) => NonNullable<AnsightOptions["hostConnection"]> | void
|
|
291
286
|
): this;
|
|
292
287
|
withBundledHostConnection(options?: {
|
|
293
|
-
bundledDeveloperConfigJson?: string;
|
|
294
288
|
bundledConfigJson?: string;
|
|
295
289
|
}): this;
|
|
296
290
|
withHostConnectionDiscoveryPort(discoveryPort: number): this;
|
|
@@ -475,6 +469,10 @@ export interface AnsightConnectOptions {
|
|
|
475
469
|
hostAddressOverride?: string;
|
|
476
470
|
}
|
|
477
471
|
|
|
472
|
+
export interface AnsightScanPairingOptions extends AnsightConnectOptions {
|
|
473
|
+
title?: string;
|
|
474
|
+
}
|
|
475
|
+
|
|
478
476
|
export interface AnsightOpenSessionOptions extends AnsightConnectOptions {}
|
|
479
477
|
|
|
480
478
|
export interface AnsightSavePairingOptions {
|
|
@@ -516,6 +514,9 @@ export function screenViewed(name: string, details?: Record<string, string>): Pr
|
|
|
516
514
|
export function trackRoute(name: string, details?: Record<string, string>): Promise<AnsightDebugSnapshot>;
|
|
517
515
|
export function setAppLifecycleState(state: AnsightLifecycleState): Promise<AnsightDebugSnapshot>;
|
|
518
516
|
export function connect(pairingPayload?: string | object | null, options?: AnsightConnectOptions): Promise<AnsightHostConnectionResult>;
|
|
517
|
+
/** Scans a one-use Studio invite, registers this installation, and saves automatic reconnect state. */
|
|
518
|
+
export function enrollFromQrCode(options?: AnsightScanPairingOptions): Promise<AnsightHostConnectionResult>;
|
|
519
|
+
export function scanPairingQrCode(options?: AnsightScanPairingOptions): Promise<AnsightHostConnectionResult>;
|
|
519
520
|
export function openSession(pairingPayload: string | object, options?: AnsightOpenSessionOptions): Promise<AnsightHostConnectionResult>;
|
|
520
521
|
export function disconnect(): Promise<AnsightHostConnectionResult>;
|
|
521
522
|
export function completeSession(): Promise<AnsightOperationResult>;
|
|
@@ -586,6 +587,8 @@ declare const Ansight: {
|
|
|
586
587
|
trackRoute: typeof trackRoute;
|
|
587
588
|
setAppLifecycleState: typeof setAppLifecycleState;
|
|
588
589
|
connect: typeof connect;
|
|
590
|
+
enrollFromQrCode: typeof enrollFromQrCode;
|
|
591
|
+
scanPairingQrCode: typeof scanPairingQrCode;
|
|
589
592
|
openSession: typeof openSession;
|
|
590
593
|
disconnect: typeof disconnect;
|
|
591
594
|
completeSession: typeof completeSession;
|
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
|
|
|
@@ -35,16 +44,7 @@ function normalizePairingPayload(payload) {
|
|
|
35
44
|
}
|
|
36
45
|
|
|
37
46
|
function normalizeOptions(options = {}) {
|
|
38
|
-
|
|
39
|
-
if (normalized.pairingConfig != null) {
|
|
40
|
-
normalized.pairingConfigJson = normalizePairingPayload(normalized.pairingConfig);
|
|
41
|
-
delete normalized.pairingConfig;
|
|
42
|
-
}
|
|
43
|
-
if (normalized.bundledPairingConfig != null) {
|
|
44
|
-
normalized.pairingConfigJson = normalizePairingPayload(normalized.bundledPairingConfig);
|
|
45
|
-
delete normalized.bundledPairingConfig;
|
|
46
|
-
}
|
|
47
|
-
return normalized;
|
|
47
|
+
return { ...options };
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
function cloneOptions(options = {}) {
|
|
@@ -383,10 +383,9 @@ class AnsightOptionsBuilder {
|
|
|
383
383
|
return this;
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
-
withBundledHostConnection({
|
|
386
|
+
withBundledHostConnection({ bundledConfigJson = undefined } = {}) {
|
|
387
387
|
return this.configureHostConnection((hostConnection) => ({
|
|
388
388
|
...hostConnection,
|
|
389
|
-
bundledDeveloperConfigJson,
|
|
390
389
|
bundledConfigJson,
|
|
391
390
|
}));
|
|
392
391
|
}
|
|
@@ -1402,6 +1401,56 @@ function summarizeProps(props) {
|
|
|
1402
1401
|
return summary;
|
|
1403
1402
|
}
|
|
1404
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
|
+
|
|
1405
1454
|
function nativeTagForFiber(fiber) {
|
|
1406
1455
|
const stateNode = fiber && fiber.stateNode;
|
|
1407
1456
|
if (!stateNode) {
|
|
@@ -1481,6 +1530,7 @@ function serializeFiber(fiber, context, depth) {
|
|
|
1481
1530
|
tag: fiber.tag,
|
|
1482
1531
|
key: fiber.key == null ? null : String(fiber.key),
|
|
1483
1532
|
depth,
|
|
1533
|
+
visual: createReactVisual(fiber, props, context.maxStringLength),
|
|
1484
1534
|
children: [],
|
|
1485
1535
|
};
|
|
1486
1536
|
|
|
@@ -1555,6 +1605,7 @@ function createShadowTreeNode(fiber, context, depth) {
|
|
|
1555
1605
|
tag: fiber.tag,
|
|
1556
1606
|
key: fiber.key == null ? null : String(fiber.key),
|
|
1557
1607
|
depth,
|
|
1608
|
+
visual: createReactVisual(fiber, props, context.maxStringLength),
|
|
1558
1609
|
children: [],
|
|
1559
1610
|
};
|
|
1560
1611
|
|
|
@@ -2181,6 +2232,14 @@ function connect(pairingPayload, options = {}) {
|
|
|
2181
2232
|
return notifyAfterHostConnectionChange(() => nativeModule.connect(normalizePairingPayload(pairingPayload), options));
|
|
2182
2233
|
}
|
|
2183
2234
|
|
|
2235
|
+
function scanPairingQrCode(options = {}) {
|
|
2236
|
+
return notifyAfterHostConnectionChange(() => nativeModule.scanPairingQrCode(options));
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2239
|
+
function enrollFromQrCode(options = {}) {
|
|
2240
|
+
return scanPairingQrCode(options);
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2184
2243
|
function openSession(pairingPayload, options = {}) {
|
|
2185
2244
|
return notifyAfterHostConnectionChange(() => nativeModule.openSession(normalizePairingPayload(pairingPayload), options));
|
|
2186
2245
|
}
|
|
@@ -2238,6 +2297,8 @@ const Ansight = {
|
|
|
2238
2297
|
trackRoute,
|
|
2239
2298
|
setAppLifecycleState: (state) => nativeModule.setAppLifecycleState(state),
|
|
2240
2299
|
connect,
|
|
2300
|
+
enrollFromQrCode,
|
|
2301
|
+
scanPairingQrCode,
|
|
2241
2302
|
openSession,
|
|
2242
2303
|
disconnect: () => notifyAfterHostConnectionChange(() => nativeModule.disconnect()),
|
|
2243
2304
|
completeSession: () => notifyAfterHostConnectionChange(() => nativeModule.completeSession()),
|
|
@@ -2302,6 +2363,8 @@ module.exports.default = Ansight;
|
|
|
2302
2363
|
module.exports.AnsightOptionsBuilder = AnsightOptionsBuilder;
|
|
2303
2364
|
module.exports.createOptionsBuilder = createOptionsBuilder;
|
|
2304
2365
|
module.exports.notifyHostConnectionConfigChanged = Ansight.notifyHostConnectionConfigChanged;
|
|
2366
|
+
module.exports.enrollFromQrCode = Ansight.enrollFromQrCode;
|
|
2367
|
+
module.exports.scanPairingQrCode = Ansight.scanPairingQrCode;
|
|
2305
2368
|
module.exports.addHostConnectionStatusListener = addHostConnectionStatusListener;
|
|
2306
2369
|
module.exports.addLogListener = addLogListener;
|
|
2307
2370
|
module.exports.registerArtifactProvider = registerArtifactProvider;
|
|
@@ -350,6 +350,24 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
350
350
|
}
|
|
351
351
|
}
|
|
352
352
|
|
|
353
|
+
@objc(scanPairingQrCode:resolver:rejecter:)
|
|
354
|
+
func scanPairingQrCode(
|
|
355
|
+
_ options: NSDictionary?,
|
|
356
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
357
|
+
rejecter reject: @escaping RCTPromiseRejectBlock
|
|
358
|
+
) {
|
|
359
|
+
Task {
|
|
360
|
+
let request = HostConnectionRequest.qrCode(
|
|
361
|
+
title: stringValue(options, "title") ?? "Scan Ansight Enrollment QR",
|
|
362
|
+
clientName: stringValue(options, "clientName"),
|
|
363
|
+
expectedAppId: stringValue(options, "expectedAppId"),
|
|
364
|
+
hostAddressOverride: stringValue(options, "hostAddressOverride"),
|
|
365
|
+
sourceDescription: "React Native native QR scanner"
|
|
366
|
+
)
|
|
367
|
+
resolve(hostConnectionResultDictionary(await AnsightRuntime.shared.connect(request)))
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
353
371
|
@objc(openSession:options:resolver:rejecter:)
|
|
354
372
|
func openSession(
|
|
355
373
|
_ pairingPayload: NSString?,
|
|
@@ -411,7 +429,7 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
411
429
|
AnsightRuntime.shared.clearSavedPairing()
|
|
412
430
|
resolve(hostConnectionResultDictionary(HostConnectionResult(
|
|
413
431
|
success: true,
|
|
414
|
-
message: "Saved
|
|
432
|
+
message: "Saved registration cleared.",
|
|
415
433
|
kind: .savedConfig,
|
|
416
434
|
source: .savedConfig
|
|
417
435
|
)))
|
|
@@ -859,13 +877,6 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
859
877
|
let useNativeAllInOneDefaults = boolValue(dictionary, "useNativeAllInOneDefaults", defaultValue: false)
|
|
860
878
|
var options = useNativeAllInOneDefaults ? AnsightOptions.ansightDeveloperDefaults : AnsightOptions()
|
|
861
879
|
|
|
862
|
-
if let value = stringValue(dictionary, "pairingConfigJson") {
|
|
863
|
-
if useNativeAllInOneDefaults {
|
|
864
|
-
options.hostConnection.bundledDeveloperConfigJson = value
|
|
865
|
-
} else {
|
|
866
|
-
options.hostConnection.bundledConfigJson = value
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
880
|
if useNativeAllInOneDefaults && stringValue(dictionary, "toolGuard") == nil {
|
|
870
881
|
options.toolGuard = .readOnly
|
|
871
882
|
}
|
|
@@ -983,7 +994,6 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
983
994
|
"allowCellularConnections",
|
|
984
995
|
defaultValue: options.hostConnection.allowCellularConnections
|
|
985
996
|
),
|
|
986
|
-
bundledDeveloperConfigJson: stringValue(host, "bundledDeveloperConfigJson") ?? options.hostConnection.bundledDeveloperConfigJson,
|
|
987
997
|
bundledConfigJson: stringValue(host, "bundledConfigJson") ?? options.hostConnection.bundledConfigJson
|
|
988
998
|
)
|
|
989
999
|
}
|
|
@@ -1092,7 +1102,6 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
1092
1102
|
"executableTools": snapshot.executableTools,
|
|
1093
1103
|
"toolDiscoveryEnabled": snapshot.toolDiscoveryEnabled,
|
|
1094
1104
|
"toolExecutionEnabled": snapshot.toolExecutionEnabled,
|
|
1095
|
-
"embeddedDeveloperPairingAvailable": snapshot.embeddedDeveloperPairingAvailable,
|
|
1096
1105
|
"detectedBundledTools": snapshot.detectedBundledTools,
|
|
1097
1106
|
"touchesRecorded": snapshot.touchesCaptured,
|
|
1098
1107
|
"touchesCaptured": snapshot.touchesCaptured,
|
|
@@ -1177,7 +1186,6 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
1177
1186
|
}
|
|
1178
1187
|
if let session = result.openSession {
|
|
1179
1188
|
dictionary["accepted"] = session.accepted
|
|
1180
|
-
dictionary["usedEmbeddedDeveloperPairing"] = session.usedEmbeddedDeveloperPairing
|
|
1181
1189
|
if let value = session.sessionId {
|
|
1182
1190
|
dictionary["sessionId"] = value
|
|
1183
1191
|
}
|
|
@@ -1208,7 +1216,6 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
1208
1216
|
"success": result.success,
|
|
1209
1217
|
"message": result.message,
|
|
1210
1218
|
"accepted": result.accepted,
|
|
1211
|
-
"usedEmbeddedDeveloperPairing": result.usedEmbeddedDeveloperPairing,
|
|
1212
1219
|
]
|
|
1213
1220
|
if let value = result.sessionId {
|
|
1214
1221
|
dictionary["sessionId"] = value
|
|
@@ -1300,7 +1307,6 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
1300
1307
|
"connectionProfileRetentionSeconds": options.hostConnection.connectionProfileRetentionSeconds,
|
|
1301
1308
|
"discoveryPort": options.hostConnection.discoveryPort as Any,
|
|
1302
1309
|
"allowCellularConnections": options.hostConnection.allowCellularConnections,
|
|
1303
|
-
"hasBundledDeveloperConfigJson": options.hostConnection.bundledDeveloperConfigJson != nil,
|
|
1304
1310
|
"hasBundledConfigJson": options.hostConnection.bundledConfigJson != nil,
|
|
1305
1311
|
],
|
|
1306
1312
|
]
|
|
@@ -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
|
|
@@ -47,6 +47,10 @@ RCT_EXTERN_METHOD(connect:(NSString *)pairingPayload
|
|
|
47
47
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
48
48
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
49
49
|
|
|
50
|
+
RCT_EXTERN_METHOD(scanPairingQrCode:(NSDictionary *)options
|
|
51
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
52
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
53
|
+
|
|
50
54
|
RCT_EXTERN_METHOD(openSession:(NSString *)pairingPayload
|
|
51
55
|
options:(NSDictionary *)options
|
|
52
56
|
resolver:(RCTPromiseResolveBlock)resolve
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ansight/react-native",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0-preview.1",
|
|
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": {
|