@ansight/react-native 1.3.0-preview.9 → 1.4.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 +109 -39
- package/android/build.gradle +2 -2
- package/android/src/main/kotlin/ai/ansight/reactnative/AnsightReactNativeModule.kt +91 -24
- package/index.d.ts +97 -23
- package/index.js +231 -80
- package/ios/AnsightReactNative.swift +97 -31
- package/ios/AnsightReactNativeModule.m +4 -0
- package/network.js +764 -0
- package/package.json +8 -2
- package/react-inspection-hook.js +76 -0
- package/react-tree-geometry.js +61 -0
- package/react-tree-semantics.js +33 -0
- package/session-properties.js +151 -0
package/README.md
CHANGED
|
@@ -8,6 +8,9 @@ handled by the Ansight iOS and Android SDKs. The JavaScript layer normalizes
|
|
|
8
8
|
React Native inputs, forwards runtime calls to the native bridge, and registers
|
|
9
9
|
JavaScript-backed tools for React component-tree inspection.
|
|
10
10
|
|
|
11
|
+
For guarded startup and CLI verification, see the
|
|
12
|
+
[React Native getting-started guide](https://www.ansight.ai/docs/sdk/react-native/setup).
|
|
13
|
+
|
|
11
14
|
## Install
|
|
12
15
|
|
|
13
16
|
### React Native CLI
|
|
@@ -36,7 +39,7 @@ npx expo install @ansight/react-native
|
|
|
36
39
|
|
|
37
40
|
Add the bundled config plugin to the app config. It supplies the iOS camera
|
|
38
41
|
usage description required by QR enrollment and the local-network description
|
|
39
|
-
used when connecting to Ansight
|
|
42
|
+
used when connecting to the local Ansight host:
|
|
40
43
|
|
|
41
44
|
```json
|
|
42
45
|
{
|
|
@@ -45,8 +48,8 @@ used when connecting to Ansight Studio:
|
|
|
45
48
|
[
|
|
46
49
|
"@ansight/react-native",
|
|
47
50
|
{
|
|
48
|
-
"cameraPermission": "Allow $(PRODUCT_NAME) to scan an Ansight
|
|
49
|
-
"localNetworkPermission": "Allow $(PRODUCT_NAME) to connect to Ansight
|
|
51
|
+
"cameraPermission": "Allow $(PRODUCT_NAME) to scan an Ansight enrollment QR code.",
|
|
52
|
+
"localNetworkPermission": "Allow $(PRODUCT_NAME) to connect to the Ansight host on your local network."
|
|
50
53
|
}
|
|
51
54
|
]
|
|
52
55
|
]
|
|
@@ -77,25 +80,48 @@ bundle; its API requires the native Ansight module.
|
|
|
77
80
|
|
|
78
81
|
This package version expects matching native SDK packages:
|
|
79
82
|
|
|
80
|
-
- CocoaPods: `Ansight`, `AnsightObjC` version `1.
|
|
81
|
-
- Maven: `ai.ansight:ansight-android:1.
|
|
83
|
+
- CocoaPods: `Ansight`, `AnsightObjC` version `1.4.0-preview.3`
|
|
84
|
+
- Maven: `ai.ansight:ansight-android:1.4.0-preview.3`
|
|
82
85
|
|
|
83
86
|
## Quickstart
|
|
84
87
|
|
|
85
88
|
```ts
|
|
86
89
|
import Ansight from "@ansight/react-native";
|
|
87
90
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
91
|
+
let ansightStarted = false;
|
|
92
|
+
|
|
93
|
+
export async function startAnsight() {
|
|
94
|
+
if (!__DEV__ || ansightStarted) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
ansightStarted = true;
|
|
98
|
+
|
|
99
|
+
await Ansight.initializeAndActivate({
|
|
100
|
+
useNativeAllInOneDefaults: true,
|
|
101
|
+
clientName: "React Native App",
|
|
102
|
+
toolGuard: "readOnly",
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Call `startAnsight()` once from app bootstrap. Start the local host in one
|
|
108
|
+
terminal and leave it running:
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
ansight host run
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Launch the native development build, then verify the connected session and tool
|
|
115
|
+
catalog from another terminal:
|
|
116
|
+
|
|
117
|
+
```sh
|
|
118
|
+
ansight session list --connected --json
|
|
119
|
+
ansight app tools <session-id> --json
|
|
94
120
|
```
|
|
95
121
|
|
|
96
122
|
The native iOS Simulator or Android emulator runtime registers automatically
|
|
97
|
-
|
|
98
|
-
|
|
123
|
+
through loopback. No account, pairing file, environment variable, host address,
|
|
124
|
+
or build-time host probe is required.
|
|
99
125
|
|
|
100
126
|
`useNativeAllInOneDefaults` defaults to `false`. It only applies the native
|
|
101
127
|
iOS/Android all-in-one defaults: 400 ms sampling, 120 second retention, FPS,
|
|
@@ -151,6 +177,7 @@ The TypeScript `AnsightOptions` surface mirrors Android `AnsightOptions`, iOS
|
|
|
151
177
|
| `secureStorage` | Compatibility alias for native secure-storage allow-list settings. |
|
|
152
178
|
| `remoteTools` | Native visual tree, file, database, preferences, reflection, and secure-storage tool options. |
|
|
153
179
|
| `lifecycle` | JS AppState tracking toggle. Defaults to true. |
|
|
180
|
+
| `networkCapture` | Opt-in `fetch` and `XMLHttpRequest` metadata capture. Accepts `true` or sanitization/capture options. |
|
|
154
181
|
|
|
155
182
|
Use `withOpenFileHandleTracking()` and `withJniReferenceCountTracking()` to
|
|
156
183
|
opt in through the builder. Matching `without...` methods disable the channels
|
|
@@ -200,14 +227,50 @@ await Ansight.initializeAndActivate({
|
|
|
200
227
|
});
|
|
201
228
|
```
|
|
202
229
|
|
|
203
|
-
The touch mode captures native visual trees
|
|
204
|
-
|
|
205
|
-
|
|
230
|
+
The touch mode captures native visual trees only on touch down and touch up.
|
|
231
|
+
Move and cancel events do not trigger capture. Rapid boundaries are coalesced
|
|
232
|
+
and rate-limited to protect screenshot cadence. Native touch capture and
|
|
233
|
+
visual-tree tools/providers must remain enabled.
|
|
206
234
|
|
|
207
235
|
On iOS, `captureGpuBackedSurfaces` defaults to `true` so Metal, SceneKit, and
|
|
208
236
|
similar GPU-backed views are included. Set it to `false` to use a lower-overhead
|
|
209
237
|
capture path when those surfaces are not needed.
|
|
210
238
|
|
|
239
|
+
## Network capture
|
|
240
|
+
|
|
241
|
+
Network capture is explicitly enabled by the React Native layer, which
|
|
242
|
+
instruments `fetch` and `XMLHttpRequest`, then sends a typed metadata record
|
|
243
|
+
through the native bridge. It is opt-in:
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
await Ansight.initializeAndActivate(
|
|
247
|
+
Ansight.createOptionsBuilder()
|
|
248
|
+
.withAnsightDefaults()
|
|
249
|
+
.withNetworkCapture({
|
|
250
|
+
maximumBodyBytes: 64 * 1024,
|
|
251
|
+
additionalSensitiveHeaderNames: ["x-tenant-secret"],
|
|
252
|
+
additionalSensitiveQueryParameterNames: ["session"],
|
|
253
|
+
requestSanitizer: request =>
|
|
254
|
+
request.url.includes("/health") ? null : request,
|
|
255
|
+
})
|
|
256
|
+
.withoutNetworkRequestBodies() // optional, independent opt-out
|
|
257
|
+
.build(),
|
|
258
|
+
);
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Text request and response bodies are included by default after network capture
|
|
262
|
+
is explicitly enabled, with a 64 KiB default per-body limit. Set a larger
|
|
263
|
+
`maximumBodyBytes` when needed; use the request/response body builder methods to
|
|
264
|
+
opt either side out or back in, and `captureBinaryBodies` to explicitly allow
|
|
265
|
+
Base64 binary content. Standard credentials, cloud signed-URL fields, cookies,
|
|
266
|
+
URL user information, and sensitive text-body assignments are redacted before
|
|
267
|
+
the bridge. Capture hooks detach whenever the native host is disconnected.
|
|
268
|
+
|
|
269
|
+
Use `installNetworkCapture(...)` and `uninstallNetworkCapture()` when capture
|
|
270
|
+
must be controlled independently of initialization. `recordNetworkRequest(...)`
|
|
271
|
+
supports custom HTTP stacks, and `sanitizeNetworkRequest(...)` exposes the same
|
|
272
|
+
app-side policy for inspection or testing.
|
|
273
|
+
|
|
211
274
|
## Native Tool Options
|
|
212
275
|
|
|
213
276
|
`remoteTools` configures the native tool suites registered by the bridge. `useNativeAllInOneDefaults: true` enables visual tree tools by default so Studio can pair `ui.get_visual_tree` data with `ui.get_screenshot` frames. Apps that do not use all-in-one defaults can opt in explicitly:
|
|
@@ -256,25 +319,19 @@ compatibility alias for `remoteTools.secureStorage`.
|
|
|
256
319
|
|
|
257
320
|
## Host Connection
|
|
258
321
|
|
|
259
|
-
No connection call is needed for a simulator or emulator.
|
|
260
|
-
device,
|
|
322
|
+
No connection call is needed for a simulator or emulator. For a physical
|
|
323
|
+
device, run `ansight pairing issue --qr`, then open the SDK scanner from a
|
|
324
|
+
developer-only app surface:
|
|
261
325
|
|
|
262
326
|
```ts
|
|
263
327
|
await Ansight.enrollFromQrCode({
|
|
264
328
|
clientName: "My React Native App",
|
|
265
|
-
expectedAppId: "com.example.app",
|
|
266
329
|
});
|
|
267
330
|
```
|
|
268
331
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
```ts
|
|
273
|
-
await Ansight.connect(null, { clientName: "My React Native App" });
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
When Studio is closed or signed out, automatic attempts remain dormant and
|
|
277
|
-
retry later without failing the React Native app.
|
|
332
|
+
The SDK supplies the native app id, stores the installation registration
|
|
333
|
+
privately, and reconnects automatically on later launches. If the host is
|
|
334
|
+
unavailable, retry attempts do not fail the React Native app.
|
|
278
335
|
|
|
279
336
|
If the app already owns a scanner, pass its result through the explicit payload
|
|
280
337
|
API:
|
|
@@ -282,17 +339,17 @@ API:
|
|
|
282
339
|
```ts
|
|
283
340
|
await Ansight.connect(enrollmentPayload, {
|
|
284
341
|
clientName: "My React Native App",
|
|
285
|
-
expectedAppId: "com.example.app",
|
|
286
|
-
hostAddressOverride: "192.168.1.20",
|
|
287
342
|
});
|
|
288
343
|
|
|
289
344
|
await Ansight.clearCachedSession();
|
|
290
345
|
await Ansight.disconnect();
|
|
291
346
|
```
|
|
292
347
|
|
|
293
|
-
`openSession(
|
|
294
|
-
Prefer
|
|
295
|
-
|
|
348
|
+
`openSession(enrollmentPayload, options)` is the low-level direct session path.
|
|
349
|
+
Prefer automatic registration for simulators and emulators or
|
|
350
|
+
`enrollFromQrCode(...)` for physical devices. Use `connect(...)` only when the
|
|
351
|
+
app already owns the scanner; it coordinates saved registration, host
|
|
352
|
+
auto-probe, status, telemetry, and live tool handling.
|
|
296
353
|
|
|
297
354
|
## Runtime API
|
|
298
355
|
|
|
@@ -302,7 +359,7 @@ The bridge exposes the native SDK runtime surface:
|
|
|
302
359
|
| --- | --- |
|
|
303
360
|
| `initialize`, `initializeAndActivate`, `activate`, `deactivate`, `clear` | Runtime lifecycle. |
|
|
304
361
|
| `connect`, `disconnect`, `openSession`, `completeSession`, `closeSession` | Host and live-session control. |
|
|
305
|
-
| `
|
|
362
|
+
| `clearCachedSession` | Clears remembered app-installation registration state. |
|
|
306
363
|
| `status`, `snapshot`, `hostConnectionStatus`, `currentOptions` | Diagnostics and state. |
|
|
307
364
|
| `registerMetricChannel`, `metric`, `recordMetric` | Metric channels and samples. |
|
|
308
365
|
| `event`, `recordEvent`, `screenViewed`, `trackRoute` | App events and screen views. |
|
|
@@ -389,14 +446,27 @@ await Ansight.clearSessionProperties();
|
|
|
389
446
|
When connected, property mutations are sent immediately. When disconnected, the
|
|
390
447
|
latest values are included in the next `session.open`.
|
|
391
448
|
|
|
449
|
+
The bridge automatically adds these property groups:
|
|
450
|
+
|
|
451
|
+
| Group | Properties |
|
|
452
|
+
| --- | --- |
|
|
453
|
+
| `reactNative` | Ansight SDK, React Native and React versions; platform and runtime language; JavaScript engine and available Hermes version/bytecode details; legacy/new architecture; bridgeless state; development mode. |
|
|
454
|
+
| `localization` | Canonical locale, language, optional region, IANA time zone when exposed by `Intl`, and UTC offset in minutes. |
|
|
455
|
+
|
|
456
|
+
App-provided values override an automatic value with the same group and key.
|
|
457
|
+
Clearing all properties, or removing one automatic property, restores the
|
|
458
|
+
current bridge-owned value. `localization` reflects the JavaScript runtime
|
|
459
|
+
locale; if the app selects a different language through an i18n library, set
|
|
460
|
+
`localization.locale`, `language`, and `region` explicitly.
|
|
461
|
+
|
|
392
462
|
## Tool Guards
|
|
393
463
|
|
|
394
|
-
| Value |
|
|
464
|
+
| Value | Maximum policy |
|
|
395
465
|
| --- | --- |
|
|
396
466
|
| `"disabled"` | None |
|
|
397
467
|
| `"readOnly"` | Read |
|
|
398
|
-
| `"readWrite"` |
|
|
399
|
-
| `"fullAccess"` |
|
|
468
|
+
| `"readWrite"` | Write |
|
|
469
|
+
| `"fullAccess"` | Critical |
|
|
400
470
|
|
|
401
471
|
`"full"` is accepted as a compatibility alias for `"fullAccess"`.
|
|
402
472
|
|
|
@@ -411,7 +481,7 @@ const registration = Ansight.registerTool(
|
|
|
411
481
|
name: "State Snapshot",
|
|
412
482
|
description: "Returns current app state.",
|
|
413
483
|
category: "app",
|
|
414
|
-
|
|
484
|
+
policy: "read",
|
|
415
485
|
keywords: "state snapshot",
|
|
416
486
|
argumentsSchema: { type: "object", additionalProperties: true },
|
|
417
487
|
resultSchema: { type: "object", additionalProperties: true },
|
|
@@ -474,7 +544,7 @@ const reportProvider = Ansight.registerArtifactProvider({
|
|
|
474
544
|
await reportProvider.ready;
|
|
475
545
|
```
|
|
476
546
|
|
|
477
|
-
The first provider installs the read
|
|
547
|
+
The first provider installs the `read` policy `artifacts.query` and
|
|
478
548
|
`artifacts.request` JavaScript tools in the native registry. A provider can
|
|
479
549
|
return text, base64, byte arrays, `ArrayBuffer`, or `Uint8Array`. Artifact
|
|
480
550
|
requests require a live Studio tool call; the bridge forwards the returned
|
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.4.0-preview.3"
|
|
71
|
+
implementation("ai.ansight:ansight-android:1.4.0-preview.3")
|
|
72
72
|
}
|
|
@@ -12,6 +12,7 @@ import ai.ansight.runtime.AnsightHostConnectionOptions
|
|
|
12
12
|
import ai.ansight.runtime.AnsightLogCallback
|
|
13
13
|
import ai.ansight.runtime.AnsightLogLevel
|
|
14
14
|
import ai.ansight.runtime.AnsightLogger
|
|
15
|
+
import ai.ansight.runtime.AnsightNetworkRequest
|
|
15
16
|
import ai.ansight.runtime.AnsightOptions
|
|
16
17
|
import ai.ansight.runtime.AnsightOptionsBuilder
|
|
17
18
|
import ai.ansight.runtime.AnsightRuntime
|
|
@@ -28,6 +29,7 @@ import ai.ansight.runtime.HostConnectionRequestKind
|
|
|
28
29
|
import ai.ansight.runtime.HostConnectionCapabilities
|
|
29
30
|
import ai.ansight.runtime.HostConnectionResult
|
|
30
31
|
import ai.ansight.runtime.HostConnectionStatus
|
|
32
|
+
import ai.ansight.runtime.HostConnectionStatusSubscription
|
|
31
33
|
import ai.ansight.runtime.OperationResult
|
|
32
34
|
import ai.ansight.runtime.OpenSessionResult
|
|
33
35
|
import ai.ansight.runtime.PairingOpenOptions
|
|
@@ -36,9 +38,7 @@ import ai.ansight.runtime.RecordedEvent
|
|
|
36
38
|
import ai.ansight.runtime.RecordedMetric
|
|
37
39
|
import ai.ansight.runtime.ToolDefinition
|
|
38
40
|
import ai.ansight.runtime.ToolSchema
|
|
39
|
-
import ai.ansight.runtime.
|
|
40
|
-
import ai.ansight.runtime.ToolSecurity
|
|
41
|
-
import ai.ansight.runtime.ToolSecurityLevel
|
|
41
|
+
import ai.ansight.runtime.ToolPolicy
|
|
42
42
|
import ai.ansight.runtime.sendBinaryTransfer
|
|
43
43
|
import ai.ansight.pairing.AnsightPairing
|
|
44
44
|
import ai.ansight.tools.database.AndroidDatabaseRoot
|
|
@@ -53,6 +53,10 @@ import ai.ansight.tools.reflection.AndroidReflectionToolsOptions
|
|
|
53
53
|
import ai.ansight.tools.reflection.withReflectionTools
|
|
54
54
|
import ai.ansight.tools.securestorage.withSecureStorageTools
|
|
55
55
|
import ai.ansight.tools.visualtree.withVisualTreeTools
|
|
56
|
+
import ai.ansight.tools.visualtree.AndroidVisualTreeActionRequest
|
|
57
|
+
import ai.ansight.tools.visualtree.AndroidVisualTreeInteractionProvider
|
|
58
|
+
import ai.ansight.tools.visualtree.AndroidVisualTreeProvider
|
|
59
|
+
import ai.ansight.tools.visualtree.AndroidVisualTreeProviderRegistry
|
|
56
60
|
import android.app.Activity
|
|
57
61
|
import android.app.Application
|
|
58
62
|
import android.util.Base64
|
|
@@ -104,16 +108,21 @@ class AnsightReactNativeModule(
|
|
|
104
108
|
private val logCallback = AnsightLogCallback { level, message, throwable ->
|
|
105
109
|
emitLogEvent(level, message, throwable)
|
|
106
110
|
}
|
|
111
|
+
private val hostConnectionStatusSubscription: HostConnectionStatusSubscription
|
|
107
112
|
|
|
108
113
|
init {
|
|
109
114
|
reactContext.addLifecycleEventListener(this)
|
|
110
115
|
AnsightLogger.registerCallback(logCallback)
|
|
116
|
+
hostConnectionStatusSubscription = AnsightRuntime.addHostConnectionStatusListener(
|
|
117
|
+
listener = { status, _ -> emitHostConnectionStatusEvent(status) },
|
|
118
|
+
)
|
|
111
119
|
}
|
|
112
120
|
|
|
113
121
|
override fun getName(): String = "AnsightReactNative"
|
|
114
122
|
|
|
115
123
|
override fun invalidate() {
|
|
116
124
|
AnsightLogger.removeCallback(logCallback)
|
|
125
|
+
hostConnectionStatusSubscription.remove()
|
|
117
126
|
reactContext.removeLifecycleEventListener(this)
|
|
118
127
|
super.invalidate()
|
|
119
128
|
}
|
|
@@ -226,6 +235,17 @@ class AnsightReactNativeModule(
|
|
|
226
235
|
}.resolve(promise)
|
|
227
236
|
}
|
|
228
237
|
|
|
238
|
+
@ReactMethod
|
|
239
|
+
fun recordNetworkRequest(input: ReadableMap, promise: Promise) {
|
|
240
|
+
runCatching {
|
|
241
|
+
val request = AnsightNetworkRequest.fromJson(JSONObject(input.toHashMap()))
|
|
242
|
+
?: return@runCatching operationResultMap(
|
|
243
|
+
OperationResult.failure("Network request must use the ansight.network-request.v1 schema."),
|
|
244
|
+
)
|
|
245
|
+
operationResultMap(AnsightRuntime.recordNetworkRequest(request))
|
|
246
|
+
}.resolve(promise)
|
|
247
|
+
}
|
|
248
|
+
|
|
229
249
|
@ReactMethod
|
|
230
250
|
fun recordCrashCandidate(input: ReadableMap, promise: Promise) {
|
|
231
251
|
runCatching {
|
|
@@ -639,6 +659,58 @@ class AnsightReactNativeModule(
|
|
|
639
659
|
},
|
|
640
660
|
replaceExisting = true,
|
|
641
661
|
)
|
|
662
|
+
if (definition.id == "react.get_component_tree") {
|
|
663
|
+
AndroidVisualTreeProviderRegistry.register(
|
|
664
|
+
object : AndroidVisualTreeProvider, AndroidVisualTreeInteractionProvider {
|
|
665
|
+
override val source = "react"
|
|
666
|
+
override val displayName = "React Native"
|
|
667
|
+
|
|
668
|
+
override fun getVisualTree(
|
|
669
|
+
arguments: Map<String, String>,
|
|
670
|
+
context: AndroidToolExecutionContext,
|
|
671
|
+
): AndroidToolResult = executeJavaScriptTool(
|
|
672
|
+
"react.get_component_tree",
|
|
673
|
+
arguments,
|
|
674
|
+
context,
|
|
675
|
+
registration.timeoutMilliseconds,
|
|
676
|
+
)
|
|
677
|
+
|
|
678
|
+
override fun inspectNode(
|
|
679
|
+
arguments: Map<String, String>,
|
|
680
|
+
context: AndroidToolExecutionContext,
|
|
681
|
+
): AndroidToolResult = executeJavaScriptTool(
|
|
682
|
+
"react.get_component",
|
|
683
|
+
arguments,
|
|
684
|
+
context,
|
|
685
|
+
registration.timeoutMilliseconds,
|
|
686
|
+
)
|
|
687
|
+
|
|
688
|
+
override fun performAction(
|
|
689
|
+
request: AndroidVisualTreeActionRequest,
|
|
690
|
+
context: AndroidToolExecutionContext,
|
|
691
|
+
): AndroidToolResult {
|
|
692
|
+
val prop = when (request.action.lowercase()) {
|
|
693
|
+
"tap" -> "onPress"
|
|
694
|
+
"focus" -> "onFocus"
|
|
695
|
+
"setvalue", "typetext" -> "onChangeText"
|
|
696
|
+
"toggle" -> "onValueChange"
|
|
697
|
+
else -> request.options.optString("prop", request.action)
|
|
698
|
+
}
|
|
699
|
+
return executeJavaScriptTool(
|
|
700
|
+
"react.invoke_component_action",
|
|
701
|
+
buildMap {
|
|
702
|
+
put("nodeId", request.nodeId)
|
|
703
|
+
put("prop", prop)
|
|
704
|
+
request.value?.let { put("value", it.toString()) }
|
|
705
|
+
},
|
|
706
|
+
context,
|
|
707
|
+
registration.timeoutMilliseconds,
|
|
708
|
+
)
|
|
709
|
+
}
|
|
710
|
+
},
|
|
711
|
+
replaceExisting = true,
|
|
712
|
+
)
|
|
713
|
+
}
|
|
642
714
|
}
|
|
643
715
|
|
|
644
716
|
private fun emitToolCall(
|
|
@@ -677,6 +749,16 @@ class AnsightReactNativeModule(
|
|
|
677
749
|
}
|
|
678
750
|
}
|
|
679
751
|
|
|
752
|
+
private fun emitHostConnectionStatusEvent(status: HostConnectionStatus) {
|
|
753
|
+
if (listenerCount.get() <= 0) return
|
|
754
|
+
val event = hostConnectionStatusMap(status)
|
|
755
|
+
UiThreadUtil.runOnUiThread {
|
|
756
|
+
reactContext
|
|
757
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
758
|
+
.emit("AnsightHostConnectionStatus", event)
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
680
762
|
private fun configureReactNativeMemoryProfiling(map: ReadableMap?) {
|
|
681
763
|
val options = reactNativeMemoryProfilingOptions(map)
|
|
682
764
|
currentReactNativeMemoryOptions = options
|
|
@@ -1005,14 +1087,14 @@ class AnsightReactNativeModule(
|
|
|
1005
1087
|
name = map.stringValue("name") ?: map.stringValue("id") ?: "",
|
|
1006
1088
|
description = map.stringValue("description") ?: "",
|
|
1007
1089
|
category = map.stringValue("category") ?: "custom",
|
|
1008
|
-
|
|
1090
|
+
policy = toolPolicy(map.stringValue("policy")),
|
|
1009
1091
|
keywords = when {
|
|
1010
1092
|
map.hasArray("keywords") -> map.getArray("keywords").toStringList().joinToString(" ")
|
|
1011
1093
|
else -> map.stringValue("keywords") ?: "react native custom tool"
|
|
1012
1094
|
},
|
|
1013
1095
|
argumentsSchema = schemaFrom(map.getMapOrNull("argumentsSchema")),
|
|
1014
1096
|
resultSchema = schemaFrom(map.getMapOrNull("resultSchema")),
|
|
1015
|
-
|
|
1097
|
+
prerequisiteToolIds = map.getArrayOrNull("prerequisiteToolIds").toStringList(),
|
|
1016
1098
|
).validated()
|
|
1017
1099
|
|
|
1018
1100
|
private fun schemaFrom(map: ReadableMap?): ToolSchema {
|
|
@@ -1044,21 +1126,6 @@ class AnsightReactNativeModule(
|
|
|
1044
1126
|
)
|
|
1045
1127
|
}
|
|
1046
1128
|
|
|
1047
|
-
private fun toolSecurity(map: ReadableMap?): ToolSecurity {
|
|
1048
|
-
if (map == null) {
|
|
1049
|
-
return ToolSecurity.Unspecified
|
|
1050
|
-
}
|
|
1051
|
-
return ToolSecurity(
|
|
1052
|
-
level = when (map.stringValue("level")?.trim()?.lowercase()) {
|
|
1053
|
-
"medium", "moderate" -> ToolSecurityLevel.Medium
|
|
1054
|
-
"high" -> ToolSecurityLevel.High
|
|
1055
|
-
"critical" -> ToolSecurityLevel.Critical
|
|
1056
|
-
else -> ToolSecurityLevel.Low
|
|
1057
|
-
},
|
|
1058
|
-
implications = map.getArrayOrNull("implications").toStringList(),
|
|
1059
|
-
)
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
1129
|
private fun resultPayload(map: ReadableMap): JSONObject? {
|
|
1063
1130
|
if (!map.hasKey("result") || map.isNull("result")) {
|
|
1064
1131
|
return null
|
|
@@ -1327,11 +1394,11 @@ private fun toolGuardName(guard: AnsightToolGuard): String =
|
|
|
1327
1394
|
AnsightToolGuard.FullAccess -> "fullAccess"
|
|
1328
1395
|
}
|
|
1329
1396
|
|
|
1330
|
-
private fun
|
|
1397
|
+
private fun toolPolicy(raw: String?): ToolPolicy =
|
|
1331
1398
|
when (raw?.trim()?.lowercase()) {
|
|
1332
|
-
"write" ->
|
|
1333
|
-
"delete" ->
|
|
1334
|
-
else ->
|
|
1399
|
+
"write" -> ToolPolicy.Write
|
|
1400
|
+
"critical", "delete" -> ToolPolicy.Critical
|
|
1401
|
+
else -> ToolPolicy.Read
|
|
1335
1402
|
}
|
|
1336
1403
|
|
|
1337
1404
|
private fun ReadableMap?.hasKey(name: String): Boolean = this?.hasKey(name) == true
|
package/index.d.ts
CHANGED
|
@@ -1,18 +1,5 @@
|
|
|
1
1
|
export type AnsightLifecycleState = "unknown" | "foreground" | "background";
|
|
2
|
-
export type
|
|
3
|
-
export type AnsightToolSecurityLevel =
|
|
4
|
-
| "unspecified"
|
|
5
|
-
| "low"
|
|
6
|
-
| "medium"
|
|
7
|
-
| "moderate"
|
|
8
|
-
| "high"
|
|
9
|
-
| "critical"
|
|
10
|
-
| "Unspecified"
|
|
11
|
-
| "Low"
|
|
12
|
-
| "Medium"
|
|
13
|
-
| "Moderate"
|
|
14
|
-
| "High"
|
|
15
|
-
| "Critical";
|
|
2
|
+
export type AnsightToolPolicy = "read" | "write" | "critical";
|
|
16
3
|
|
|
17
4
|
export interface AnsightChannel {
|
|
18
5
|
id: number;
|
|
@@ -48,6 +35,75 @@ export interface AnsightTouchCaptureOptions {
|
|
|
48
35
|
moveCaptureFramesPerSecond?: number;
|
|
49
36
|
}
|
|
50
37
|
|
|
38
|
+
export interface AnsightNetworkHeader {
|
|
39
|
+
name: string;
|
|
40
|
+
value: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface AnsightNetworkBody {
|
|
44
|
+
contentType?: string;
|
|
45
|
+
encoding: "utf8" | "base64";
|
|
46
|
+
data: string;
|
|
47
|
+
capturedBytes: number;
|
|
48
|
+
totalBytes?: number;
|
|
49
|
+
truncated: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** V1 network metadata with optional, bounded bodies. */
|
|
53
|
+
export interface AnsightNetworkRequest {
|
|
54
|
+
schema: "ansight.network-request.v1";
|
|
55
|
+
id: string;
|
|
56
|
+
source: string;
|
|
57
|
+
startedAtUtc: string;
|
|
58
|
+
completedAtUtc: string;
|
|
59
|
+
durationMilliseconds: number;
|
|
60
|
+
method: string;
|
|
61
|
+
url: string;
|
|
62
|
+
protocol?: string;
|
|
63
|
+
requestHeaders: AnsightNetworkHeader[];
|
|
64
|
+
requestBodySizeBytes?: number;
|
|
65
|
+
requestBody?: AnsightNetworkBody;
|
|
66
|
+
statusCode?: number;
|
|
67
|
+
reasonPhrase?: string;
|
|
68
|
+
responseHeaders: AnsightNetworkHeader[];
|
|
69
|
+
responseBodySizeBytes?: number;
|
|
70
|
+
responseBody?: AnsightNetworkBody;
|
|
71
|
+
errorType?: string;
|
|
72
|
+
errorMessage?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type AnsightNetworkRequestInput = Partial<AnsightNetworkRequest> & {
|
|
76
|
+
method: string;
|
|
77
|
+
url: string;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export interface AnsightNetworkSanitizationOptions {
|
|
81
|
+
includeRequestHeaders?: boolean;
|
|
82
|
+
includeResponseHeaders?: boolean;
|
|
83
|
+
includeQueryString?: boolean;
|
|
84
|
+
includeBodySizes?: boolean;
|
|
85
|
+
/** Defaults to true. */
|
|
86
|
+
captureRequestBody?: boolean;
|
|
87
|
+
/** Defaults to true. */
|
|
88
|
+
captureResponseBody?: boolean;
|
|
89
|
+
/** Decoded bytes retained per body. Defaults to 64 KiB; larger explicit limits are honored. */
|
|
90
|
+
maximumBodyBytes?: number;
|
|
91
|
+
captureBinaryBodies?: boolean;
|
|
92
|
+
additionalSensitiveHeaderNames?: string[];
|
|
93
|
+
additionalSensitiveQueryParameterNames?: string[];
|
|
94
|
+
urlSanitizer?: (url: string) => string;
|
|
95
|
+
/** Return a replacement request, or null to suppress capture. */
|
|
96
|
+
requestSanitizer?: (
|
|
97
|
+
request: AnsightNetworkRequest,
|
|
98
|
+
) => AnsightNetworkRequest | null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface AnsightNetworkCaptureOptions
|
|
102
|
+
extends AnsightNetworkSanitizationOptions {
|
|
103
|
+
captureFetch?: boolean;
|
|
104
|
+
captureXmlHttpRequest?: boolean;
|
|
105
|
+
}
|
|
106
|
+
|
|
51
107
|
export interface AnsightNativeToolRoot {
|
|
52
108
|
alias: string;
|
|
53
109
|
path: string;
|
|
@@ -155,6 +211,8 @@ export interface AnsightOptions {
|
|
|
155
211
|
remoteTools?: AnsightRemoteToolsOptions;
|
|
156
212
|
additionalChannels?: AnsightChannel[];
|
|
157
213
|
lifecycle?: boolean;
|
|
214
|
+
/** Explicitly opt in to fetch/XMLHttpRequest instrumentation. */
|
|
215
|
+
networkCapture?: boolean | AnsightNetworkCaptureOptions;
|
|
158
216
|
}
|
|
159
217
|
|
|
160
218
|
export interface AnsightCrashCaptureOptions {
|
|
@@ -300,6 +358,12 @@ export class AnsightOptionsBuilder {
|
|
|
300
358
|
withCrashCapture(crashCapture?: AnsightCrashCaptureOptions): this;
|
|
301
359
|
withoutCrashCapture(): this;
|
|
302
360
|
withLifecycleCapture(lifecycleCapture?: NonNullable<AnsightOptions["lifecycleCapture"]>): this;
|
|
361
|
+
withNetworkCapture(networkCapture?: AnsightNetworkCaptureOptions): this;
|
|
362
|
+
withNetworkRequestBodies(maximumBodyBytes?: number): this;
|
|
363
|
+
withoutNetworkRequestBodies(): this;
|
|
364
|
+
withNetworkResponseBodies(maximumBodyBytes?: number): this;
|
|
365
|
+
withoutNetworkResponseBodies(): this;
|
|
366
|
+
withoutNetworkCapture(): this;
|
|
303
367
|
withToolGuard(toolGuard: NonNullable<AnsightOptions["toolGuard"]>): this;
|
|
304
368
|
withToolsDisabled(): this;
|
|
305
369
|
withReadOnlyToolAccess(): this;
|
|
@@ -333,22 +397,16 @@ export class AnsightOptionsBuilder {
|
|
|
333
397
|
build(): AnsightOptions;
|
|
334
398
|
}
|
|
335
399
|
|
|
336
|
-
export interface AnsightToolSecurity {
|
|
337
|
-
level?: AnsightToolSecurityLevel;
|
|
338
|
-
summary?: string;
|
|
339
|
-
implications?: string[];
|
|
340
|
-
}
|
|
341
|
-
|
|
342
400
|
export interface AnsightToolDefinition {
|
|
343
401
|
id: string;
|
|
344
402
|
name: string;
|
|
345
403
|
description?: string;
|
|
346
404
|
category?: string;
|
|
347
|
-
|
|
405
|
+
policy?: AnsightToolPolicy;
|
|
348
406
|
keywords?: string | string[];
|
|
349
407
|
argumentsSchema?: object;
|
|
350
408
|
resultSchema?: object;
|
|
351
|
-
|
|
409
|
+
prerequisiteToolIds?: string[];
|
|
352
410
|
timeoutMilliseconds?: number;
|
|
353
411
|
}
|
|
354
412
|
|
|
@@ -403,7 +461,7 @@ export interface AnsightArtifactDefinition {
|
|
|
403
461
|
fileName?: string;
|
|
404
462
|
estimatedSizeBytes?: number | null;
|
|
405
463
|
argumentsSchema?: object;
|
|
406
|
-
|
|
464
|
+
policy?: AnsightToolPolicy;
|
|
407
465
|
tags?: string[];
|
|
408
466
|
metadata?: Record<string, string>;
|
|
409
467
|
}
|
|
@@ -543,6 +601,18 @@ export function recordMetric(value: number, channel?: number): Promise<AnsightDe
|
|
|
543
601
|
export function event(input: string | { label: string; type?: string; details?: string; channel?: number }): Promise<AnsightDebugSnapshot>;
|
|
544
602
|
export function recordEvent(input: string | { label: string; type?: string; details?: string; channel?: number }): Promise<AnsightDebugSnapshot>;
|
|
545
603
|
export function recordCrashCandidate(input?: AnsightCrashCandidate): Promise<{ candidateId?: string }>;
|
|
604
|
+
export function recordNetworkRequest(
|
|
605
|
+
input: AnsightNetworkRequestInput,
|
|
606
|
+
sanitizationOptions?: AnsightNetworkSanitizationOptions,
|
|
607
|
+
): Promise<AnsightOperationResult>;
|
|
608
|
+
export function sanitizeNetworkRequest(
|
|
609
|
+
input: AnsightNetworkRequestInput,
|
|
610
|
+
sanitizationOptions?: AnsightNetworkSanitizationOptions,
|
|
611
|
+
): AnsightNetworkRequest | null;
|
|
612
|
+
export function installNetworkCapture(
|
|
613
|
+
options?: AnsightNetworkCaptureOptions,
|
|
614
|
+
): AnsightSubscription;
|
|
615
|
+
export function uninstallNetworkCapture(): void;
|
|
546
616
|
export function screenViewed(name: string, details?: Record<string, string>): Promise<AnsightDebugSnapshot>;
|
|
547
617
|
export function trackRoute(name: string, details?: Record<string, string>): Promise<AnsightDebugSnapshot>;
|
|
548
618
|
export function setAppLifecycleState(state: AnsightLifecycleState): Promise<AnsightDebugSnapshot>;
|
|
@@ -617,6 +687,7 @@ declare const Ansight: {
|
|
|
617
687
|
event: typeof event;
|
|
618
688
|
recordEvent: typeof recordEvent;
|
|
619
689
|
recordCrashCandidate: typeof recordCrashCandidate;
|
|
690
|
+
recordNetworkRequest: typeof recordNetworkRequest;
|
|
620
691
|
screenViewed: typeof screenViewed;
|
|
621
692
|
trackRoute: typeof trackRoute;
|
|
622
693
|
setAppLifecycleState: typeof setAppLifecycleState;
|
|
@@ -671,6 +742,9 @@ declare const Ansight: {
|
|
|
671
742
|
installReactTools: typeof installReactTools;
|
|
672
743
|
uninstallReactTools: typeof uninstallReactTools;
|
|
673
744
|
installErrorHandlers: typeof installErrorHandlers;
|
|
745
|
+
installNetworkCapture: typeof installNetworkCapture;
|
|
746
|
+
uninstallNetworkCapture: typeof uninstallNetworkCapture;
|
|
747
|
+
sanitizeNetworkRequest: typeof sanitizeNetworkRequest;
|
|
674
748
|
createReactNavigationTracker: typeof createReactNavigationTracker;
|
|
675
749
|
platform: "ios" | "android" | string;
|
|
676
750
|
};
|