@goliapkg/sentori-react-native 1.0.0-rc.2 → 1.0.0-rc.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/android/src/main/java/com/sentori/SentoriReplayCapture.kt +125 -57
- package/ios/SentoriReplayCapture.swift +38 -7
- package/lib/native.d.ts +11 -0
- package/lib/native.d.ts.map +1 -1
- package/lib/native.js +16 -0
- package/lib/native.js.map +1 -1
- package/lib/replay.d.ts.map +1 -1
- package/lib/replay.js +60 -0
- package/lib/replay.js.map +1 -1
- package/package.json +1 -1
- package/src/native.ts +41 -0
- package/src/replay.ts +65 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
package com.sentori
|
|
2
2
|
|
|
3
3
|
import android.app.Activity
|
|
4
|
-
import android.graphics.Rect
|
|
5
4
|
import android.view.View
|
|
6
5
|
import android.view.ViewGroup
|
|
7
6
|
import android.widget.EditText
|
|
@@ -25,11 +24,29 @@ import org.json.JSONObject
|
|
|
25
24
|
object SentoriReplayCapture {
|
|
26
25
|
|
|
27
26
|
private const val MAX_NODES = 800
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
//
|
|
27
|
+
private const val MAX_DEPTH = 60
|
|
28
|
+
|
|
29
|
+
// Diagnostic readouts. Mirrors the iOS side. Surfaced via
|
|
30
|
+
// `probe()` so JS can answer "why is the ring shallow?" without
|
|
31
|
+
// parsing logcat.
|
|
32
|
+
//
|
|
33
|
+
// v0.9.12: lastPath + lastNodes
|
|
34
|
+
// v1.0.0-rc.3:
|
|
35
|
+
// * lastDepthMax — deepest descendant the walker reached. If
|
|
36
|
+
// this stays at 2 or 3 we know the recursion bailed early
|
|
37
|
+
// (the rc.2 zero-size-bails-subtree bug).
|
|
38
|
+
// * lastSizeBytes — byte length of the serialised payload. ~50
|
|
39
|
+
// bytes per node is typical; a 1 KB result with 800 nodes
|
|
40
|
+
// would be a red flag.
|
|
41
|
+
// * totalTicks / lastEmptyResultTicks — lifetime counters for
|
|
42
|
+
// ring health, so a thin-but-non-null capture doesn't slip
|
|
43
|
+
// through unnoticed.
|
|
31
44
|
@Volatile private var lastDiagPath: String = "none(not-yet-called)"
|
|
32
45
|
@Volatile private var lastDiagNodes: Int = 0
|
|
46
|
+
@Volatile private var lastDiagDepthMax: Int = 0
|
|
47
|
+
@Volatile private var lastDiagSizeBytes: Int = 0
|
|
48
|
+
@Volatile private var totalTicks: Long = 0
|
|
49
|
+
@Volatile private var totalEmptyResultTicks: Long = 0
|
|
33
50
|
|
|
34
51
|
@JvmStatic
|
|
35
52
|
fun probe(): Map<String, Any> {
|
|
@@ -37,6 +54,10 @@ object SentoriReplayCapture {
|
|
|
37
54
|
return mapOf(
|
|
38
55
|
"lastPath" to lastDiagPath,
|
|
39
56
|
"lastNodes" to lastDiagNodes,
|
|
57
|
+
"lastDepthMax" to lastDiagDepthMax,
|
|
58
|
+
"lastSizeBytes" to lastDiagSizeBytes,
|
|
59
|
+
"totalTicks" to totalTicks,
|
|
60
|
+
"totalEmptyResultTicks" to totalEmptyResultTicks,
|
|
40
61
|
"trackedSource" to SentoriForegroundActivity.lastPath,
|
|
41
62
|
"trackedActivity" to (activity?.javaClass?.name ?: "null"),
|
|
42
63
|
"decorViewFound" to (activity?.window?.decorView != null),
|
|
@@ -61,29 +82,30 @@ object SentoriReplayCapture {
|
|
|
61
82
|
|
|
62
83
|
@JvmStatic
|
|
63
84
|
fun captureWireframe(maskedIds: List<String>): String? {
|
|
85
|
+
totalTicks++
|
|
64
86
|
val activity = SentoriForegroundActivity.current()
|
|
65
87
|
if (activity == null) {
|
|
66
88
|
lastDiagPath = "activity.null"
|
|
89
|
+
totalEmptyResultTicks++
|
|
67
90
|
return null
|
|
68
91
|
}
|
|
69
92
|
val root = activity.window?.decorView
|
|
70
93
|
if (root == null) {
|
|
71
94
|
lastDiagPath = "decorView.null"
|
|
95
|
+
totalEmptyResultTicks++
|
|
72
96
|
return null
|
|
73
97
|
}
|
|
74
98
|
if (root.width <= 0 || root.height <= 0) {
|
|
75
99
|
lastDiagPath = "root.zero-size"
|
|
100
|
+
totalEmptyResultTicks++
|
|
76
101
|
return null
|
|
77
102
|
}
|
|
78
103
|
|
|
79
104
|
val maskedSet = maskedIds.toHashSet()
|
|
80
105
|
val nodes = JSONArray()
|
|
81
|
-
val rect = Rect()
|
|
82
106
|
val rootLoc = IntArray(2).also { root.getLocationInWindow(it) }
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
lastDiagPath = "ok(${SentoriForegroundActivity.lastPath})"
|
|
86
|
-
lastDiagNodes = nodes.length()
|
|
107
|
+
val ctx = WalkContext(rootLoc = rootLoc, maskedSet = maskedSet)
|
|
108
|
+
walk(root, depth = 0, parentMasked = false, ctx = ctx, nodes = nodes)
|
|
87
109
|
|
|
88
110
|
val payload = JSONObject().apply {
|
|
89
111
|
put("ts", System.currentTimeMillis())
|
|
@@ -91,77 +113,123 @@ object SentoriReplayCapture {
|
|
|
91
113
|
put("height", root.height)
|
|
92
114
|
put("nodes", nodes)
|
|
93
115
|
}
|
|
94
|
-
|
|
116
|
+
val serialised = payload.toString()
|
|
117
|
+
|
|
118
|
+
lastDiagPath = "ok(${SentoriForegroundActivity.lastPath})"
|
|
119
|
+
lastDiagNodes = nodes.length()
|
|
120
|
+
lastDiagDepthMax = ctx.depthMax
|
|
121
|
+
lastDiagSizeBytes = serialised.length
|
|
122
|
+
|
|
123
|
+
if (nodes.length() == 0) totalEmptyResultTicks++
|
|
124
|
+
|
|
125
|
+
return serialised
|
|
95
126
|
}
|
|
96
127
|
|
|
128
|
+
/** Per-walk scratch: tracks the deepest descendant reached so
|
|
129
|
+
* the probe can surface whether the recursion ran or bailed.
|
|
130
|
+
* Bundled into one object to keep the recursive signature
|
|
131
|
+
* manageable. */
|
|
132
|
+
private class WalkContext(
|
|
133
|
+
val rootLoc: IntArray,
|
|
134
|
+
val maskedSet: Set<String>,
|
|
135
|
+
var depthMax: Int = 0,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Recursive walker.
|
|
140
|
+
*
|
|
141
|
+
* v1.0.0-rc.3 fix: previously this function returned ENTIRELY
|
|
142
|
+
* when the view itself had `width <= 0 || height <= 0`. That
|
|
143
|
+
* meant any ViewGroup wrapper that happened to measure to zero
|
|
144
|
+
* size during the tick (common on Fabric / RN's intermediate
|
|
145
|
+
* shadow-tree wrappers, and on lazy-layout phases) skipped the
|
|
146
|
+
* whole descendant subtree — Insight 2026-05-17 verify event
|
|
147
|
+
* saw 800-node frames whose subtree was actually thousands of
|
|
148
|
+
* Views deep but only the root + 2-3 wrappers made it into the
|
|
149
|
+
* JSON.
|
|
150
|
+
*
|
|
151
|
+
* Now we separate "emit a node for this view" from "recurse into
|
|
152
|
+
* its children". A zero-size view doesn't get an emitted node
|
|
153
|
+
* (no visual contribution) but its descendants still get walked
|
|
154
|
+
* — they may have real frames.
|
|
155
|
+
*/
|
|
97
156
|
private fun walk(
|
|
98
157
|
view: View,
|
|
158
|
+
depth: Int,
|
|
99
159
|
parentMasked: Boolean,
|
|
100
|
-
|
|
101
|
-
rootLoc: IntArray,
|
|
102
|
-
scratch: Rect,
|
|
160
|
+
ctx: WalkContext,
|
|
103
161
|
nodes: JSONArray,
|
|
104
162
|
) {
|
|
105
163
|
if (nodes.length() >= MAX_NODES) return
|
|
164
|
+
if (depth >= MAX_DEPTH) return
|
|
106
165
|
if (view.visibility != View.VISIBLE || view.alpha < 0.01) return
|
|
107
166
|
|
|
167
|
+
if (depth > ctx.depthMax) ctx.depthMax = depth
|
|
168
|
+
|
|
108
169
|
val viewTag = view.tag as? String
|
|
109
|
-
val isThisMasked = viewTag != null && maskedSet.contains(viewTag)
|
|
170
|
+
val isThisMasked = viewTag != null && ctx.maskedSet.contains(viewTag)
|
|
110
171
|
val masked = parentMasked || isThisMasked
|
|
111
172
|
|
|
112
|
-
val loc = IntArray(2)
|
|
113
|
-
view.getLocationInWindow(loc)
|
|
114
|
-
val x = loc[0] - rootLoc[0]
|
|
115
|
-
val y = loc[1] - rootLoc[1]
|
|
116
173
|
val w = view.width
|
|
117
174
|
val h = view.height
|
|
118
|
-
if (w <= 0 || h <= 0) return
|
|
119
|
-
|
|
120
|
-
val node = JSONObject().apply {
|
|
121
|
-
put("x", x)
|
|
122
|
-
put("y", y)
|
|
123
|
-
put("w", w)
|
|
124
|
-
put("h", h)
|
|
125
|
-
}
|
|
126
175
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
node.put("kind", "text")
|
|
142
|
-
val text = (view.text ?: "").toString().let { if (it.length > 200) it.substring(0, 200) else it }
|
|
143
|
-
node.put("text", text)
|
|
144
|
-
kindEmitted = true
|
|
176
|
+
// Emit a node ONLY when the view has visual extent. A zero-
|
|
177
|
+
// size view contributes nothing to render but its subtree
|
|
178
|
+
// might; recurse below regardless.
|
|
179
|
+
if (w > 0 && h > 0) {
|
|
180
|
+
val loc = IntArray(2)
|
|
181
|
+
view.getLocationInWindow(loc)
|
|
182
|
+
val x = loc[0] - ctx.rootLoc[0]
|
|
183
|
+
val y = loc[1] - ctx.rootLoc[1]
|
|
184
|
+
|
|
185
|
+
val node = JSONObject().apply {
|
|
186
|
+
put("x", x)
|
|
187
|
+
put("y", y)
|
|
188
|
+
put("w", w)
|
|
189
|
+
put("h", h)
|
|
145
190
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
191
|
+
|
|
192
|
+
var kindEmitted = false
|
|
193
|
+
when {
|
|
194
|
+
masked -> {
|
|
195
|
+
node.put("kind", "mask")
|
|
196
|
+
kindEmitted = true
|
|
197
|
+
}
|
|
198
|
+
view is TextView && !view.text.isNullOrEmpty() -> {
|
|
199
|
+
node.put("kind", "text")
|
|
200
|
+
val text = view.text.toString().let { if (it.length > 200) it.substring(0, 200) else it }
|
|
201
|
+
node.put("text", text)
|
|
202
|
+
node.put("color", colorToHex(view.currentTextColor))
|
|
203
|
+
kindEmitted = true
|
|
204
|
+
}
|
|
205
|
+
view is EditText -> {
|
|
206
|
+
node.put("kind", "text")
|
|
207
|
+
val text = (view.text ?: "").toString().let { if (it.length > 200) it.substring(0, 200) else it }
|
|
208
|
+
node.put("text", text)
|
|
209
|
+
kindEmitted = true
|
|
210
|
+
}
|
|
211
|
+
view is ImageView -> {
|
|
212
|
+
node.put("kind", "image")
|
|
213
|
+
kindEmitted = true
|
|
214
|
+
}
|
|
215
|
+
view.background != null -> {
|
|
216
|
+
node.put("kind", "rect")
|
|
217
|
+
// Background drawables don't always expose color directly.
|
|
218
|
+
// Skip color for non-ColorDrawable; renderer falls back to neutral.
|
|
219
|
+
kindEmitted = true
|
|
220
|
+
}
|
|
155
221
|
}
|
|
156
|
-
}
|
|
157
222
|
|
|
158
|
-
|
|
159
|
-
|
|
223
|
+
if (kindEmitted) {
|
|
224
|
+
nodes.put(node)
|
|
225
|
+
}
|
|
160
226
|
}
|
|
161
227
|
|
|
228
|
+
// Always recurse — even zero-size wrappers can host real
|
|
229
|
+
// descendants (the rc.3 fix).
|
|
162
230
|
if (!masked && view is ViewGroup) {
|
|
163
231
|
for (i in 0 until view.childCount) {
|
|
164
|
-
walk(view.getChildAt(i),
|
|
232
|
+
walk(view.getChildAt(i), depth + 1, masked, ctx, nodes)
|
|
165
233
|
}
|
|
166
234
|
}
|
|
167
235
|
}
|
|
@@ -29,22 +29,31 @@ import UIKit
|
|
|
29
29
|
return result
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
///
|
|
33
|
-
///
|
|
34
|
-
///
|
|
35
|
-
///
|
|
32
|
+
/// Diagnostic readouts exposed to JS via `probeWireframe()`.
|
|
33
|
+
///
|
|
34
|
+
/// v0.9.12: lastPath / lastNodes / scene-window counts
|
|
35
|
+
/// v1.0.0-rc.3: + lastDepthMax / lastSizeBytes / totalTicks /
|
|
36
|
+
/// totalEmptyResultTicks — answers "the ring isn't
|
|
37
|
+
/// empty but the dashboard renders nothing, which
|
|
38
|
+
/// layer dropped the data?" without a re-roll.
|
|
36
39
|
@objc public static var lastDiagPath: String = "none(not-yet-called)"
|
|
37
40
|
@objc public static var lastDiagNodes: Int = 0
|
|
38
41
|
@objc public static var lastDiagSceneCount: Int = 0
|
|
39
42
|
@objc public static var lastDiagWindowCount: Int = 0
|
|
43
|
+
@objc public static var lastDiagDepthMax: Int = 0
|
|
44
|
+
@objc public static var lastDiagSizeBytes: Int = 0
|
|
45
|
+
@objc public static var totalTicks: Int = 0
|
|
46
|
+
@objc public static var totalEmptyResultTicks: Int = 0
|
|
40
47
|
private static var loggedFirstResult = false
|
|
41
48
|
|
|
42
49
|
private static func captureSync(maskedIds: Set<String>) -> String? {
|
|
50
|
+
totalTicks += 1
|
|
43
51
|
let (winOpt, path) = resolveKeyWindow()
|
|
44
52
|
lastDiagPath = path
|
|
45
53
|
lastDiagSceneCount = currentSceneCount()
|
|
46
54
|
lastDiagWindowCount = currentWindowCount()
|
|
47
55
|
guard let window = winOpt else {
|
|
56
|
+
totalEmptyResultTicks += 1
|
|
48
57
|
if !loggedFirstResult {
|
|
49
58
|
NSLog(
|
|
50
59
|
"[sentori] wireframe: returning nil — keyWindow path=%@ scenes=%d windows=%d",
|
|
@@ -57,21 +66,29 @@ import UIKit
|
|
|
57
66
|
return nil
|
|
58
67
|
}
|
|
59
68
|
var nodes: [[String: Any]] = []
|
|
69
|
+
var depthMax = 0
|
|
60
70
|
walk(
|
|
61
71
|
view: window,
|
|
72
|
+
depth: 0,
|
|
73
|
+
depthMax: &depthMax,
|
|
62
74
|
parentMasked: false,
|
|
63
75
|
maskedIds: maskedIds,
|
|
64
76
|
window: window,
|
|
65
77
|
nodes: &nodes
|
|
66
78
|
)
|
|
67
79
|
lastDiagNodes = nodes.count
|
|
80
|
+
lastDiagDepthMax = depthMax
|
|
81
|
+
if nodes.isEmpty {
|
|
82
|
+
totalEmptyResultTicks += 1
|
|
83
|
+
}
|
|
68
84
|
if !loggedFirstResult {
|
|
69
85
|
NSLog(
|
|
70
|
-
"[sentori] wireframe: first capture ok — keyWindow path=%@ bounds=%.0fx%.0f nodes=%d",
|
|
86
|
+
"[sentori] wireframe: first capture ok — keyWindow path=%@ bounds=%.0fx%.0f nodes=%d depthMax=%d",
|
|
71
87
|
path,
|
|
72
88
|
window.bounds.width,
|
|
73
89
|
window.bounds.height,
|
|
74
|
-
nodes.count
|
|
90
|
+
nodes.count,
|
|
91
|
+
depthMax
|
|
75
92
|
)
|
|
76
93
|
loggedFirstResult = true
|
|
77
94
|
}
|
|
@@ -82,7 +99,9 @@ import UIKit
|
|
|
82
99
|
"nodes": nodes,
|
|
83
100
|
]
|
|
84
101
|
if let data = try? JSONSerialization.data(withJSONObject: payload, options: []) {
|
|
85
|
-
|
|
102
|
+
let s = String(data: data, encoding: .utf8)
|
|
103
|
+
lastDiagSizeBytes = s?.utf8.count ?? 0
|
|
104
|
+
return s
|
|
86
105
|
}
|
|
87
106
|
return nil
|
|
88
107
|
}
|
|
@@ -157,23 +176,33 @@ import UIKit
|
|
|
157
176
|
"lastNodes": lastDiagNodes,
|
|
158
177
|
"sceneCount": lastDiagSceneCount,
|
|
159
178
|
"windowCount": lastDiagWindowCount,
|
|
179
|
+
"lastDepthMax": lastDiagDepthMax,
|
|
180
|
+
"lastSizeBytes": lastDiagSizeBytes,
|
|
181
|
+
"totalTicks": totalTicks,
|
|
182
|
+
"totalEmptyResultTicks": totalEmptyResultTicks,
|
|
160
183
|
]
|
|
161
184
|
}
|
|
162
185
|
|
|
163
186
|
/// Cap on nodes per snapshot — extremely deep / wide trees can
|
|
164
187
|
/// have thousands of subviews (UICollectionView recyclers).
|
|
165
188
|
private static let MAX_NODES = 800
|
|
189
|
+
private static let MAX_DEPTH = 60
|
|
166
190
|
|
|
167
191
|
private static func walk(
|
|
168
192
|
view: UIView,
|
|
193
|
+
depth: Int,
|
|
194
|
+
depthMax: inout Int,
|
|
169
195
|
parentMasked: Bool,
|
|
170
196
|
maskedIds: Set<String>,
|
|
171
197
|
window: UIWindow,
|
|
172
198
|
nodes: inout [[String: Any]]
|
|
173
199
|
) {
|
|
174
200
|
if nodes.count >= MAX_NODES { return }
|
|
201
|
+
if depth >= MAX_DEPTH { return }
|
|
175
202
|
if view.isHidden || view.alpha < 0.01 { return }
|
|
176
203
|
|
|
204
|
+
if depth > depthMax { depthMax = depth }
|
|
205
|
+
|
|
177
206
|
let isThisMasked = view.accessibilityIdentifier
|
|
178
207
|
.map { maskedIds.contains($0) } ?? false
|
|
179
208
|
let masked = parentMasked || isThisMasked
|
|
@@ -219,6 +248,8 @@ import UIKit
|
|
|
219
248
|
for sub in view.subviews {
|
|
220
249
|
walk(
|
|
221
250
|
view: sub,
|
|
251
|
+
depth: depth + 1,
|
|
252
|
+
depthMax: &depthMax,
|
|
222
253
|
parentMasked: masked,
|
|
223
254
|
maskedIds: maskedIds,
|
|
224
255
|
window: window,
|
package/lib/native.d.ts
CHANGED
|
@@ -106,6 +106,17 @@ export declare function probeNativeWireframe(): {
|
|
|
106
106
|
lastPath: string;
|
|
107
107
|
sceneCount: number;
|
|
108
108
|
windowCount: number;
|
|
109
|
+
/** v1.0.0-rc.3: max recursion depth reached by the walker on
|
|
110
|
+
* the last tick. Healthy on an RN app: 20-40. If it's 2-3 the
|
|
111
|
+
* walker bailed early (zero-size parent, masked root). */
|
|
112
|
+
lastDepthMax: number;
|
|
113
|
+
/** v1.0.0-rc.3: byte length of the last serialised payload. */
|
|
114
|
+
lastSizeBytes: number;
|
|
115
|
+
/** v1.0.0-rc.3: lifetime totals. `totalEmptyResultTicks /
|
|
116
|
+
* totalTicks` is the failure rate. */
|
|
117
|
+
totalTicks: number;
|
|
118
|
+
totalEmptyResultTicks: number;
|
|
119
|
+
raw: Record<string, unknown>;
|
|
109
120
|
};
|
|
110
121
|
/**
|
|
111
122
|
* v1.0.0-rc.2 — JS entry to the `probeScreenshot` native diagnostic.
|
package/lib/native.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAsJH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd,GAAG,IAAI,CAMP;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAQ5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAMzC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE;IACzC,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,GAAG,IAAI,CAMP;AAED,wBAAgB,eAAe,IAAI,IAAI,CAMtC;AAED,+DAA+D;AAC/D,wBAAgB,uBAAuB,IAAI,IAAI,CAM9C;AAED,yEAAyE;AACzE,wBAAgB,oBAAoB,IAAI,IAAI,GAAG,MAAM,CAOpD;AAED,oEAAoE;AACpE,wBAAgB,sBAAsB,IAAI,IAAI,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAMhF;AAED,iEAAiE;AACjE,wBAAgB,wBAAwB,IAAI,IAAI,CAM/C;AAED;;yBAEyB;AACzB,wBAAgB,wBAAwB,IAAI,IAAI,GAAG;IACjD,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB,CAMA;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,+BAA+B,CACnD,SAAS,EAAE,MAAM,EAAE,GAClB,OAAO,CAAC,IAAI,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAoCvD;AAED;;sEAEsE;AACtE,wBAAgB,uBAAuB,IAAI;IACzC,KAAK,EAAE,OAAO,CAAA;IACd,mBAAmB,EAAE,OAAO,CAAA;IAC5B,iBAAiB,EAAE,OAAO,CAAA;CAC3B,CAOA;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,IAAI;IACtC,SAAS,EAAE,OAAO,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB;;+DAE2D;IAC3D,YAAY,EAAE,MAAM,CAAA;IACpB,+DAA+D;IAC/D,aAAa,EAAE,MAAM,CAAA;IACrB;2CACuC;IACvC,UAAU,EAAE,MAAM,CAAA;IAClB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC7B,CAkDA;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,qBAAqB,IAAI;IACvC,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC7B,CAkBA"}
|
package/lib/native.js
CHANGED
|
@@ -221,19 +221,30 @@ export function probeNativeWireframe() {
|
|
|
221
221
|
if (!n || typeof n.probeWireframe !== 'function') {
|
|
222
222
|
return {
|
|
223
223
|
available: false,
|
|
224
|
+
lastDepthMax: 0,
|
|
224
225
|
lastNodes: 0,
|
|
225
226
|
lastPath: 'native.unavailable',
|
|
227
|
+
lastSizeBytes: 0,
|
|
228
|
+
raw: {},
|
|
226
229
|
sceneCount: 0,
|
|
230
|
+
totalEmptyResultTicks: 0,
|
|
231
|
+
totalTicks: 0,
|
|
227
232
|
windowCount: 0,
|
|
228
233
|
};
|
|
229
234
|
}
|
|
230
235
|
try {
|
|
231
236
|
const r = n.probeWireframe();
|
|
237
|
+
const raw = (r ?? {});
|
|
232
238
|
return {
|
|
233
239
|
available: true,
|
|
240
|
+
lastDepthMax: typeof r?.lastDepthMax === 'number' ? r.lastDepthMax : 0,
|
|
234
241
|
lastNodes: typeof r?.lastNodes === 'number' ? r.lastNodes : 0,
|
|
235
242
|
lastPath: typeof r?.lastPath === 'string' ? r.lastPath : 'unknown',
|
|
243
|
+
lastSizeBytes: typeof r?.lastSizeBytes === 'number' ? r.lastSizeBytes : 0,
|
|
244
|
+
raw,
|
|
236
245
|
sceneCount: typeof r?.sceneCount === 'number' ? r.sceneCount : 0,
|
|
246
|
+
totalEmptyResultTicks: typeof r?.totalEmptyResultTicks === 'number' ? r.totalEmptyResultTicks : 0,
|
|
247
|
+
totalTicks: typeof r?.totalTicks === 'number' ? r.totalTicks : 0,
|
|
237
248
|
windowCount: typeof r?.windowCount === 'number' ? r.windowCount : 0,
|
|
238
249
|
};
|
|
239
250
|
}
|
|
@@ -244,9 +255,14 @@ export function probeNativeWireframe() {
|
|
|
244
255
|
}
|
|
245
256
|
return {
|
|
246
257
|
available: false,
|
|
258
|
+
lastDepthMax: 0,
|
|
247
259
|
lastNodes: 0,
|
|
248
260
|
lastPath: 'native.threw',
|
|
261
|
+
lastSizeBytes: 0,
|
|
262
|
+
raw: {},
|
|
249
263
|
sceneCount: 0,
|
|
264
|
+
totalEmptyResultTicks: 0,
|
|
265
|
+
totalTicks: 0,
|
|
250
266
|
windowCount: 0,
|
|
251
267
|
};
|
|
252
268
|
}
|
package/lib/native.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native.js","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"native.js","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"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"}
|
package/lib/replay.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAkEA,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,CAAC,EAAE,KAAK,GAAG,WAAW,CAAC;IAC3B,mCAAmC;IACnC,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,wBAAgB,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CA+CrD;AAED,wBAAgB,UAAU,IAAI,IAAI,CAUjC;
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAkEA,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,CAAC,EAAE,KAAK,GAAG,WAAW,CAAC;IAC3B,mCAAmC;IACnC,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,wBAAgB,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CA+CrD;AAED,wBAAgB,UAAU,IAAI,IAAI,CAUjC;AAuKD;;2BAE2B;AAC3B,wBAAgB,WAAW,IAAI,MAAM,CAMpC;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAI5C"}
|
package/lib/replay.js
CHANGED
|
@@ -111,7 +111,16 @@ export function stopReplay() {
|
|
|
111
111
|
}
|
|
112
112
|
let _emptyTickCount = 0;
|
|
113
113
|
let _emptyTickLogStride = 1;
|
|
114
|
+
let _thinTickCount = 0;
|
|
115
|
+
let _thinTickLogStride = 1;
|
|
116
|
+
let _okTickCount = 0;
|
|
114
117
|
let _firstTickLogged = false;
|
|
118
|
+
/** Anything below this many nodes is suspicious — likely the
|
|
119
|
+
* walker bailed early (zero-size parent, masked root, etc.).
|
|
120
|
+
* Insight 2026-05-18 verify event saw 800-node payloads on some
|
|
121
|
+
* ticks and 1-3-node payloads on others; this threshold flags
|
|
122
|
+
* the latter without spamming on small-but-valid screens. */
|
|
123
|
+
const THIN_RESULT_NODES = 6;
|
|
115
124
|
function captureTick() {
|
|
116
125
|
if (!_running)
|
|
117
126
|
return;
|
|
@@ -152,6 +161,34 @@ function captureTick() {
|
|
|
152
161
|
}
|
|
153
162
|
_emptyTickCount = 0;
|
|
154
163
|
_emptyTickLogStride = 1;
|
|
164
|
+
// v1.0.0-rc.3 — Insight 2026-05-18 report: some ticks land
|
|
165
|
+
// valid non-empty JSON but with only the root View + 1-2 wrappers
|
|
166
|
+
// (the Android zero-size-bails-subtree bug, now fixed natively;
|
|
167
|
+
// this log catches similar regressions). Cheap node-count parse
|
|
168
|
+
// — we only look at one digit-level character class.
|
|
169
|
+
_okTickCount += 1;
|
|
170
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
171
|
+
const nodeCount = countNodesQuick(snapshot);
|
|
172
|
+
const sizeBytes = snapshot.length;
|
|
173
|
+
const isThin = nodeCount < THIN_RESULT_NODES;
|
|
174
|
+
if (isThin) {
|
|
175
|
+
_thinTickCount += 1;
|
|
176
|
+
if (_thinTickCount === 1 || _thinTickCount === _thinTickLogStride) {
|
|
177
|
+
// eslint-disable-next-line no-console
|
|
178
|
+
console.warn(`[sentori] replay tick: thin result nodes=${nodeCount} sizeBytes=${sizeBytes} (thin ticks so far: ${_thinTickCount})`);
|
|
179
|
+
_thinTickLogStride = Math.max(_thinTickLogStride * 10, 10);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
_thinTickCount = 0;
|
|
184
|
+
_thinTickLogStride = 1;
|
|
185
|
+
}
|
|
186
|
+
// First good tick logs the shape so devs see it once.
|
|
187
|
+
if (_okTickCount === 1) {
|
|
188
|
+
// eslint-disable-next-line no-console
|
|
189
|
+
console.warn(`[sentori] replay tick: first ok — nodes=${nodeCount} sizeBytes=${sizeBytes}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
155
192
|
}
|
|
156
193
|
else if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
157
194
|
// v0.9.11 — Insight 2026-05-17 Finding 6: tick fires hundreds
|
|
@@ -182,6 +219,29 @@ function captureTick() {
|
|
|
182
219
|
}
|
|
183
220
|
}
|
|
184
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Approximate node-count parse — counts occurrences of the
|
|
224
|
+
* `"x":` key in the serialised payload. Every node JSON object
|
|
225
|
+
* starts with `{"x":<n>,"y":<n>,"w":<n>,"h":<n>...}` so the
|
|
226
|
+
* occurrence count matches the array length without paying for a
|
|
227
|
+
* full `JSON.parse`. Cheap enough to run inside the 1 Hz tick.
|
|
228
|
+
*/
|
|
229
|
+
function countNodesQuick(payload) {
|
|
230
|
+
let count = 0;
|
|
231
|
+
let i = 0;
|
|
232
|
+
// Skip the outer {"ts":..,"width":..,"height":..,"nodes":[
|
|
233
|
+
// and count `"x":` thereafter. The outer payload doesn't contain
|
|
234
|
+
// a top-level "x" key so any match must be a node.
|
|
235
|
+
const needle = '"x":';
|
|
236
|
+
while (true) {
|
|
237
|
+
const at = payload.indexOf(needle, i);
|
|
238
|
+
if (at < 0)
|
|
239
|
+
break;
|
|
240
|
+
count += 1;
|
|
241
|
+
i = at + needle.length;
|
|
242
|
+
}
|
|
243
|
+
return count;
|
|
244
|
+
}
|
|
185
245
|
function readMaskIds() {
|
|
186
246
|
const q = getRegisteredMaskQuery();
|
|
187
247
|
if (!q)
|
package/lib/replay.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goliapkg/sentori-react-native",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.3",
|
|
4
4
|
"description": "Sentori SDK for React Native \u2014 JS-layer error capture, native crash handlers (iOS / Android), batched transport, fetch + react-navigation tracing.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://sentori.golia.jp",
|
package/src/native.ts
CHANGED
|
@@ -83,6 +83,19 @@ type SentoriNativeModule = {
|
|
|
83
83
|
trackedSource?: string
|
|
84
84
|
trackedActivity?: string
|
|
85
85
|
decorViewFound?: boolean
|
|
86
|
+
/** v1.0.0-rc.3: deepest descendant reached by the walker. If
|
|
87
|
+
* this is 2-3 the walker bailed early; if it matches the
|
|
88
|
+
* expected view-tree depth (~20-40 for RN apps) the capture
|
|
89
|
+
* is healthy. */
|
|
90
|
+
lastDepthMax?: number
|
|
91
|
+
/** v1.0.0-rc.3: byte length of the last serialised payload. */
|
|
92
|
+
lastSizeBytes?: number
|
|
93
|
+
/** v1.0.0-rc.3: lifetime counters — totalTicks is the number
|
|
94
|
+
* of times native captureWireframe ran; totalEmptyResultTicks
|
|
95
|
+
* is the subset that came back null or empty. Ratio surfaces
|
|
96
|
+
* intermittent failures (e.g. 200/240 ≈ 80% failure rate). */
|
|
97
|
+
totalTicks?: number
|
|
98
|
+
totalEmptyResultTicks?: number
|
|
86
99
|
}
|
|
87
100
|
/**
|
|
88
101
|
* v1.0.0-rc.2 — diagnostic mirror of probeWireframe for the
|
|
@@ -356,24 +369,47 @@ export function probeNativeWireframe(): {
|
|
|
356
369
|
lastPath: string
|
|
357
370
|
sceneCount: number
|
|
358
371
|
windowCount: number
|
|
372
|
+
/** v1.0.0-rc.3: max recursion depth reached by the walker on
|
|
373
|
+
* the last tick. Healthy on an RN app: 20-40. If it's 2-3 the
|
|
374
|
+
* walker bailed early (zero-size parent, masked root). */
|
|
375
|
+
lastDepthMax: number
|
|
376
|
+
/** v1.0.0-rc.3: byte length of the last serialised payload. */
|
|
377
|
+
lastSizeBytes: number
|
|
378
|
+
/** v1.0.0-rc.3: lifetime totals. `totalEmptyResultTicks /
|
|
379
|
+
* totalTicks` is the failure rate. */
|
|
380
|
+
totalTicks: number
|
|
381
|
+
totalEmptyResultTicks: number
|
|
382
|
+
raw: Record<string, unknown>
|
|
359
383
|
} {
|
|
360
384
|
const n = native()
|
|
361
385
|
if (!n || typeof n.probeWireframe !== 'function') {
|
|
362
386
|
return {
|
|
363
387
|
available: false,
|
|
388
|
+
lastDepthMax: 0,
|
|
364
389
|
lastNodes: 0,
|
|
365
390
|
lastPath: 'native.unavailable',
|
|
391
|
+
lastSizeBytes: 0,
|
|
392
|
+
raw: {},
|
|
366
393
|
sceneCount: 0,
|
|
394
|
+
totalEmptyResultTicks: 0,
|
|
395
|
+
totalTicks: 0,
|
|
367
396
|
windowCount: 0,
|
|
368
397
|
}
|
|
369
398
|
}
|
|
370
399
|
try {
|
|
371
400
|
const r = n.probeWireframe()
|
|
401
|
+
const raw = (r ?? {}) as Record<string, unknown>
|
|
372
402
|
return {
|
|
373
403
|
available: true,
|
|
404
|
+
lastDepthMax: typeof r?.lastDepthMax === 'number' ? r.lastDepthMax : 0,
|
|
374
405
|
lastNodes: typeof r?.lastNodes === 'number' ? r.lastNodes : 0,
|
|
375
406
|
lastPath: typeof r?.lastPath === 'string' ? r.lastPath : 'unknown',
|
|
407
|
+
lastSizeBytes: typeof r?.lastSizeBytes === 'number' ? r.lastSizeBytes : 0,
|
|
408
|
+
raw,
|
|
376
409
|
sceneCount: typeof r?.sceneCount === 'number' ? r.sceneCount : 0,
|
|
410
|
+
totalEmptyResultTicks:
|
|
411
|
+
typeof r?.totalEmptyResultTicks === 'number' ? r.totalEmptyResultTicks : 0,
|
|
412
|
+
totalTicks: typeof r?.totalTicks === 'number' ? r.totalTicks : 0,
|
|
377
413
|
windowCount: typeof r?.windowCount === 'number' ? r.windowCount : 0,
|
|
378
414
|
}
|
|
379
415
|
} catch (e) {
|
|
@@ -383,9 +419,14 @@ export function probeNativeWireframe(): {
|
|
|
383
419
|
}
|
|
384
420
|
return {
|
|
385
421
|
available: false,
|
|
422
|
+
lastDepthMax: 0,
|
|
386
423
|
lastNodes: 0,
|
|
387
424
|
lastPath: 'native.threw',
|
|
425
|
+
lastSizeBytes: 0,
|
|
426
|
+
raw: {},
|
|
388
427
|
sceneCount: 0,
|
|
428
|
+
totalEmptyResultTicks: 0,
|
|
429
|
+
totalTicks: 0,
|
|
389
430
|
windowCount: 0,
|
|
390
431
|
}
|
|
391
432
|
}
|
package/src/replay.ts
CHANGED
|
@@ -133,8 +133,18 @@ export function stopReplay(): void {
|
|
|
133
133
|
|
|
134
134
|
let _emptyTickCount = 0;
|
|
135
135
|
let _emptyTickLogStride = 1;
|
|
136
|
+
let _thinTickCount = 0;
|
|
137
|
+
let _thinTickLogStride = 1;
|
|
138
|
+
let _okTickCount = 0;
|
|
136
139
|
let _firstTickLogged = false;
|
|
137
140
|
|
|
141
|
+
/** Anything below this many nodes is suspicious — likely the
|
|
142
|
+
* walker bailed early (zero-size parent, masked root, etc.).
|
|
143
|
+
* Insight 2026-05-18 verify event saw 800-node payloads on some
|
|
144
|
+
* ticks and 1-3-node payloads on others; this threshold flags
|
|
145
|
+
* the latter without spamming on small-but-valid screens. */
|
|
146
|
+
const THIN_RESULT_NODES = 6;
|
|
147
|
+
|
|
138
148
|
function captureTick(): void {
|
|
139
149
|
if (!_running) return;
|
|
140
150
|
// v0.9.12 — UNCONDITIONAL first-tick log. Proves the setInterval
|
|
@@ -173,6 +183,38 @@ function captureTick(): void {
|
|
|
173
183
|
}
|
|
174
184
|
_emptyTickCount = 0;
|
|
175
185
|
_emptyTickLogStride = 1;
|
|
186
|
+
|
|
187
|
+
// v1.0.0-rc.3 — Insight 2026-05-18 report: some ticks land
|
|
188
|
+
// valid non-empty JSON but with only the root View + 1-2 wrappers
|
|
189
|
+
// (the Android zero-size-bails-subtree bug, now fixed natively;
|
|
190
|
+
// this log catches similar regressions). Cheap node-count parse
|
|
191
|
+
// — we only look at one digit-level character class.
|
|
192
|
+
_okTickCount += 1;
|
|
193
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
194
|
+
const nodeCount = countNodesQuick(snapshot);
|
|
195
|
+
const sizeBytes = snapshot.length;
|
|
196
|
+
const isThin = nodeCount < THIN_RESULT_NODES;
|
|
197
|
+
if (isThin) {
|
|
198
|
+
_thinTickCount += 1;
|
|
199
|
+
if (_thinTickCount === 1 || _thinTickCount === _thinTickLogStride) {
|
|
200
|
+
// eslint-disable-next-line no-console
|
|
201
|
+
console.warn(
|
|
202
|
+
`[sentori] replay tick: thin result nodes=${nodeCount} sizeBytes=${sizeBytes} (thin ticks so far: ${_thinTickCount})`,
|
|
203
|
+
);
|
|
204
|
+
_thinTickLogStride = Math.max(_thinTickLogStride * 10, 10);
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
_thinTickCount = 0;
|
|
208
|
+
_thinTickLogStride = 1;
|
|
209
|
+
}
|
|
210
|
+
// First good tick logs the shape so devs see it once.
|
|
211
|
+
if (_okTickCount === 1) {
|
|
212
|
+
// eslint-disable-next-line no-console
|
|
213
|
+
console.warn(
|
|
214
|
+
`[sentori] replay tick: first ok — nodes=${nodeCount} sizeBytes=${sizeBytes}`,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
176
218
|
} else if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
177
219
|
// v0.9.11 — Insight 2026-05-17 Finding 6: tick fires hundreds
|
|
178
220
|
// of times but ring stays empty → native returned null/empty.
|
|
@@ -205,6 +247,29 @@ function captureTick(): void {
|
|
|
205
247
|
}
|
|
206
248
|
}
|
|
207
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Approximate node-count parse — counts occurrences of the
|
|
252
|
+
* `"x":` key in the serialised payload. Every node JSON object
|
|
253
|
+
* starts with `{"x":<n>,"y":<n>,"w":<n>,"h":<n>...}` so the
|
|
254
|
+
* occurrence count matches the array length without paying for a
|
|
255
|
+
* full `JSON.parse`. Cheap enough to run inside the 1 Hz tick.
|
|
256
|
+
*/
|
|
257
|
+
function countNodesQuick(payload: string): number {
|
|
258
|
+
let count = 0;
|
|
259
|
+
let i = 0;
|
|
260
|
+
// Skip the outer {"ts":..,"width":..,"height":..,"nodes":[
|
|
261
|
+
// and count `"x":` thereafter. The outer payload doesn't contain
|
|
262
|
+
// a top-level "x" key so any match must be a node.
|
|
263
|
+
const needle = '"x":';
|
|
264
|
+
while (true) {
|
|
265
|
+
const at = payload.indexOf(needle, i);
|
|
266
|
+
if (at < 0) break;
|
|
267
|
+
count += 1;
|
|
268
|
+
i = at + needle.length;
|
|
269
|
+
}
|
|
270
|
+
return count;
|
|
271
|
+
}
|
|
272
|
+
|
|
208
273
|
function readMaskIds(): string[] {
|
|
209
274
|
const q = getRegisteredMaskQuery();
|
|
210
275
|
if (!q) return [];
|