@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
|
@@ -137,8 +137,56 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
private final class ReactNativeVisualTreeProvider: AnsightVisualTreeProvider, AnsightVisualTreeInteractionProvider, @unchecked Sendable {
|
|
141
|
+
let source = "react"
|
|
142
|
+
let displayName = "React Native"
|
|
143
|
+
private weak var module: AnsightReactNative?
|
|
144
|
+
private let timeoutMilliseconds: Int
|
|
145
|
+
|
|
146
|
+
init(module: AnsightReactNative, timeoutMilliseconds: Int) {
|
|
147
|
+
self.module = module
|
|
148
|
+
self.timeoutMilliseconds = timeoutMilliseconds
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
func getVisualTree(arguments: [String: String]) -> AnsightToolExecutionResult {
|
|
152
|
+
module?.executeJavaScriptTool(
|
|
153
|
+
toolId: "react.get_component_tree",
|
|
154
|
+
arguments: arguments,
|
|
155
|
+
timeoutMilliseconds: timeoutMilliseconds
|
|
156
|
+
) ?? .failure("React Native bridge is unavailable.", errorCode: "javascript_bridge_unavailable")
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
func inspectNode(arguments: [String: String]) -> AnsightToolExecutionResult {
|
|
160
|
+
module?.executeJavaScriptTool(
|
|
161
|
+
toolId: "react.get_component",
|
|
162
|
+
arguments: arguments,
|
|
163
|
+
timeoutMilliseconds: timeoutMilliseconds
|
|
164
|
+
) ?? .failure("React Native bridge is unavailable.", errorCode: "javascript_bridge_unavailable")
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
func performAction(_ request: AnsightVisualTreeActionRequest) -> AnsightToolExecutionResult {
|
|
168
|
+
let prop: String
|
|
169
|
+
switch request.action.lowercased() {
|
|
170
|
+
case "tap": prop = "onPress"
|
|
171
|
+
case "focus": prop = "onFocus"
|
|
172
|
+
case "setvalue", "typetext": prop = "onChangeText"
|
|
173
|
+
case "toggle": prop = "onValueChange"
|
|
174
|
+
default:
|
|
175
|
+
if case .string(let configured)? = request.options["prop"] { prop = configured } else { prop = request.action }
|
|
176
|
+
}
|
|
177
|
+
var arguments = ["nodeId": request.nodeId, "prop": prop]
|
|
178
|
+
if let value = request.value?.stringValue { arguments["value"] = value }
|
|
179
|
+
return module?.executeJavaScriptTool(
|
|
180
|
+
toolId: "react.invoke_component_action",
|
|
181
|
+
arguments: arguments,
|
|
182
|
+
timeoutMilliseconds: timeoutMilliseconds
|
|
183
|
+
) ?? .failure("React Native bridge is unavailable.", errorCode: "javascript_bridge_unavailable")
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
140
187
|
private let lock = NSLock()
|
|
141
188
|
private var hasListeners = false
|
|
189
|
+
private var hostConnectionStatusSubscription: HostConnectionStatusSubscription?
|
|
142
190
|
private var activeCustomToolIds: Set<String> = []
|
|
143
191
|
private var pendingToolCalls: [String: PendingToolCall] = [:]
|
|
144
192
|
private var reactNativeMemorySampler: ReactNativeMemorySamplerBox?
|
|
@@ -150,10 +198,14 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
150
198
|
override init() {
|
|
151
199
|
super.init()
|
|
152
200
|
AnsightLogger.registerCallback(logCallback)
|
|
201
|
+
hostConnectionStatusSubscription = AnsightRuntime.shared.addHostConnectionStatusListener { [weak self] status, _ in
|
|
202
|
+
self?.emitHostConnectionStatusEvent(status)
|
|
203
|
+
}
|
|
153
204
|
}
|
|
154
205
|
|
|
155
206
|
deinit {
|
|
156
207
|
AnsightLogger.removeCallback(logCallback)
|
|
208
|
+
hostConnectionStatusSubscription?.remove()
|
|
157
209
|
}
|
|
158
210
|
|
|
159
211
|
override static func requiresMainQueueSetup() -> Bool {
|
|
@@ -161,7 +213,7 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
161
213
|
}
|
|
162
214
|
|
|
163
215
|
override func supportedEvents() -> [String]! {
|
|
164
|
-
["AnsightToolCall", "AnsightLog"]
|
|
216
|
+
["AnsightToolCall", "AnsightLog", "AnsightHostConnectionStatus"]
|
|
165
217
|
}
|
|
166
218
|
|
|
167
219
|
override func startObserving() {
|
|
@@ -292,6 +344,27 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
292
344
|
}
|
|
293
345
|
}
|
|
294
346
|
|
|
347
|
+
@objc(recordNetworkRequest:resolver:rejecter:)
|
|
348
|
+
func recordNetworkRequest(
|
|
349
|
+
_ input: NSDictionary,
|
|
350
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
351
|
+
rejecter reject: RCTPromiseRejectBlock
|
|
352
|
+
) {
|
|
353
|
+
guard JSONSerialization.isValidJSONObject(input),
|
|
354
|
+
let data = try? JSONSerialization.data(withJSONObject: input),
|
|
355
|
+
let request = try? JSONDecoder().decode(AnsightNetworkRequest.self, from: data),
|
|
356
|
+
request.schema == AnsightNetworkRequest.schemaName
|
|
357
|
+
else {
|
|
358
|
+
resolve(operationResultDictionary(
|
|
359
|
+
.failure("Network request must use the ansight.network-request.v1 schema.")
|
|
360
|
+
))
|
|
361
|
+
return
|
|
362
|
+
}
|
|
363
|
+
Task {
|
|
364
|
+
resolve(operationResultDictionary(await AnsightRuntime.shared.recordNetworkRequest(request)))
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
295
368
|
@objc(recordCrashCandidate:resolver:rejecter:)
|
|
296
369
|
func recordCrashCandidate(
|
|
297
370
|
_ input: NSDictionary,
|
|
@@ -676,6 +749,12 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
676
749
|
ReactNativeTool(descriptor: descriptor, module: self, timeoutMilliseconds: timeout),
|
|
677
750
|
replaceExisting: true
|
|
678
751
|
)
|
|
752
|
+
if descriptor.id == "react.get_component_tree" {
|
|
753
|
+
try AnsightVisualTreeProviderRegistry.register(
|
|
754
|
+
ReactNativeVisualTreeProvider(module: self, timeoutMilliseconds: timeout),
|
|
755
|
+
replaceExisting: true
|
|
756
|
+
)
|
|
757
|
+
}
|
|
679
758
|
resolve(["id": descriptor.id, "registered": true])
|
|
680
759
|
} catch {
|
|
681
760
|
reject("ansight_error", error.localizedDescription, error)
|
|
@@ -859,6 +938,17 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
859
938
|
}
|
|
860
939
|
}
|
|
861
940
|
|
|
941
|
+
private func emitHostConnectionStatusEvent(_ status: HostConnectionStatus) {
|
|
942
|
+
let shouldEmit = lock.withLock { hasListeners }
|
|
943
|
+
guard shouldEmit else { return }
|
|
944
|
+
DispatchQueue.main.async {
|
|
945
|
+
self.sendEvent(
|
|
946
|
+
withName: "AnsightHostConnectionStatus",
|
|
947
|
+
body: self.hostConnectionStatusDictionary(status)
|
|
948
|
+
)
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
862
952
|
private func configureReactNativeMemoryProfiling(_ dictionary: NSDictionary?) throws {
|
|
863
953
|
let options = ReactNativeMemoryProfilingOptions(dictionary: dictionary)
|
|
864
954
|
currentReactNativeMemoryOptions = options
|
|
@@ -1137,11 +1227,11 @@ final class AnsightReactNative: RCTEventEmitter {
|
|
|
1137
1227
|
name: stringValue(dictionary, "name") ?? stringValue(dictionary, "id") ?? "",
|
|
1138
1228
|
description: stringValue(dictionary, "description") ?? "",
|
|
1139
1229
|
category: stringValue(dictionary, "category") ?? "custom",
|
|
1140
|
-
|
|
1230
|
+
policy: toolPolicy(stringValue(dictionary, "policy")),
|
|
1141
1231
|
keywords: keywords(dictionary["keywords"]),
|
|
1142
|
-
security: toolSecurity(dictionary["security"] as? NSDictionary),
|
|
1143
1232
|
argumentsSchema: AnsightToolSchema(json: jsonValue(dictionary["argumentsSchema"]) ?? .object([:])),
|
|
1144
|
-
resultSchema: AnsightToolSchema(json: jsonValue(dictionary["resultSchema"]) ?? .object([:]))
|
|
1233
|
+
resultSchema: AnsightToolSchema(json: jsonValue(dictionary["resultSchema"]) ?? .object([:])),
|
|
1234
|
+
prerequisiteToolIds: stringArray(dictionary, "prerequisiteToolIds")
|
|
1145
1235
|
)
|
|
1146
1236
|
}
|
|
1147
1237
|
|
|
@@ -1647,41 +1737,17 @@ private func toolGuard(_ rawValue: String) -> AnsightToolGuard {
|
|
|
1647
1737
|
}
|
|
1648
1738
|
}
|
|
1649
1739
|
|
|
1650
|
-
private func
|
|
1740
|
+
private func toolPolicy(_ rawValue: String?) -> AnsightToolPolicy {
|
|
1651
1741
|
switch rawValue?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() {
|
|
1652
1742
|
case "write":
|
|
1653
1743
|
return .write
|
|
1654
|
-
case "delete":
|
|
1655
|
-
return .
|
|
1744
|
+
case "critical", "delete":
|
|
1745
|
+
return .critical
|
|
1656
1746
|
default:
|
|
1657
1747
|
return .read
|
|
1658
1748
|
}
|
|
1659
1749
|
}
|
|
1660
1750
|
|
|
1661
|
-
private func toolSecurity(_ dictionary: NSDictionary?) -> AnsightToolSecurity {
|
|
1662
|
-
guard let dictionary else {
|
|
1663
|
-
return .unspecified
|
|
1664
|
-
}
|
|
1665
|
-
let level: AnsightToolSecurityLevel
|
|
1666
|
-
switch stringValue(dictionary, "level")?.lowercased() {
|
|
1667
|
-
case "low":
|
|
1668
|
-
level = .low
|
|
1669
|
-
case "medium", "moderate":
|
|
1670
|
-
level = .moderate
|
|
1671
|
-
case "high":
|
|
1672
|
-
level = .high
|
|
1673
|
-
case "critical":
|
|
1674
|
-
level = .critical
|
|
1675
|
-
default:
|
|
1676
|
-
level = .unspecified
|
|
1677
|
-
}
|
|
1678
|
-
return AnsightToolSecurity(
|
|
1679
|
-
level: level,
|
|
1680
|
-
summary: stringValue(dictionary, "summary") ?? "",
|
|
1681
|
-
implications: (dictionary["implications"] as? [Any])?.compactMap { $0 as? String } ?? []
|
|
1682
|
-
)
|
|
1683
|
-
}
|
|
1684
|
-
|
|
1685
1751
|
private func keywords(_ value: Any?) -> String {
|
|
1686
1752
|
if let string = value as? String {
|
|
1687
1753
|
return string
|
|
@@ -33,6 +33,10 @@ RCT_EXTERN_METHOD(recordEvent:(NSDictionary *)input
|
|
|
33
33
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
34
34
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
35
35
|
|
|
36
|
+
RCT_EXTERN_METHOD(recordNetworkRequest:(NSDictionary *)input
|
|
37
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
38
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
39
|
+
|
|
36
40
|
RCT_EXTERN_METHOD(recordCrashCandidate:(NSDictionary *)input
|
|
37
41
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
38
42
|
rejecter:(RCTPromiseRejectBlock)reject)
|