@goliapkg/sentori-react-native 0.9.10 → 1.0.0-rc.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/android/src/main/java/com/sentori/SentoriModule.kt +5 -0
- package/android/src/main/java/com/sentori/SentoriReplayCapture.kt +33 -3
- package/ios/SentoriModule.swift +8 -0
- package/ios/SentoriReplayCapture.swift +103 -9
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.bak +64 -0
- package/lib/index.js.map +1 -1
- package/lib/native.d.ts +28 -0
- package/lib/native.d.ts.map +1 -1
- package/lib/native.js +56 -0
- package/lib/native.js.map +1 -1
- package/lib/replay.d.ts.map +1 -1
- package/lib/replay.js +98 -8
- package/lib/replay.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/native.ts +77 -0
- package/src/replay.ts +102 -7
|
@@ -34,6 +34,11 @@ class SentoriModule : Module() {
|
|
|
34
34
|
SentoriReplayCapture.captureWireframe(maskedIds)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
// v0.9.12 — diagnostic readout for replay. See iOS side.
|
|
38
|
+
Function("probeWireframe") {
|
|
39
|
+
SentoriReplayCapture.probe()
|
|
40
|
+
}
|
|
41
|
+
|
|
37
42
|
// v0.9.4 #1 — Mobile Vitals exposure.
|
|
38
43
|
Function("markJsBridgeReady") {
|
|
39
44
|
SentoriMobileVitals.markJsBridgeReady()
|
|
@@ -29,6 +29,22 @@ object SentoriReplayCapture {
|
|
|
29
29
|
|
|
30
30
|
@Volatile private var lastActivity: WeakReference<Activity>? = null
|
|
31
31
|
|
|
32
|
+
// v0.9.12 — diagnostic readout so the JS side can ask "why is the
|
|
33
|
+
// ring empty?" without parsing logcat. Mirrors the iOS side.
|
|
34
|
+
@Volatile private var lastDiagPath: String = "none(not-yet-called)"
|
|
35
|
+
@Volatile private var lastDiagNodes: Int = 0
|
|
36
|
+
|
|
37
|
+
@JvmStatic
|
|
38
|
+
fun probe(): Map<String, Any> {
|
|
39
|
+
val activity = lastActivity?.get()
|
|
40
|
+
return mapOf(
|
|
41
|
+
"lastPath" to lastDiagPath,
|
|
42
|
+
"lastNodes" to lastDiagNodes,
|
|
43
|
+
"sceneCount" to if (activity != null) 1 else 0,
|
|
44
|
+
"windowCount" to (activity?.window?.let { 1 } ?: 0),
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
32
48
|
@JvmStatic
|
|
33
49
|
fun setActivity(activity: Activity?) {
|
|
34
50
|
lastActivity = activity?.let { WeakReference(it) }
|
|
@@ -52,9 +68,20 @@ object SentoriReplayCapture {
|
|
|
52
68
|
|
|
53
69
|
@JvmStatic
|
|
54
70
|
fun captureWireframe(maskedIds: List<String>): String? {
|
|
55
|
-
val activity = lastActivity?.get()
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
val activity = lastActivity?.get()
|
|
72
|
+
if (activity == null) {
|
|
73
|
+
lastDiagPath = "activity.null"
|
|
74
|
+
return null
|
|
75
|
+
}
|
|
76
|
+
val root = activity.window?.decorView
|
|
77
|
+
if (root == null) {
|
|
78
|
+
lastDiagPath = "decorView.null"
|
|
79
|
+
return null
|
|
80
|
+
}
|
|
81
|
+
if (root.width <= 0 || root.height <= 0) {
|
|
82
|
+
lastDiagPath = "root.zero-size"
|
|
83
|
+
return null
|
|
84
|
+
}
|
|
58
85
|
|
|
59
86
|
val maskedSet = maskedIds.toHashSet()
|
|
60
87
|
val nodes = JSONArray()
|
|
@@ -62,6 +89,9 @@ object SentoriReplayCapture {
|
|
|
62
89
|
val rootLoc = IntArray(2).also { root.getLocationInWindow(it) }
|
|
63
90
|
walk(root, false, maskedSet, rootLoc, rect, nodes)
|
|
64
91
|
|
|
92
|
+
lastDiagPath = "activity.resumed"
|
|
93
|
+
lastDiagNodes = nodes.length()
|
|
94
|
+
|
|
65
95
|
val payload = JSONObject().apply {
|
|
66
96
|
put("ts", System.currentTimeMillis())
|
|
67
97
|
put("width", root.width)
|
package/ios/SentoriModule.swift
CHANGED
|
@@ -30,6 +30,14 @@ public class SentoriModule: Module {
|
|
|
30
30
|
return SentoriReplayCapture.captureWireframe(maskedIds: maskedIds)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// v0.9.12 — diagnostic readout for replay. Returns the last
|
|
34
|
+
// keyWindow resolution path + scene/window counts so a single
|
|
35
|
+
// JS-side button can answer "why is my ring empty?" without
|
|
36
|
+
// re-rolling the pod. See SentoriReplayCapture.swift.
|
|
37
|
+
Function("probeWireframe") { () -> [String: Any] in
|
|
38
|
+
return SentoriReplayCapture.probe()
|
|
39
|
+
}
|
|
40
|
+
|
|
33
41
|
// v0.9.4 #1 — Mobile Vitals exposure.
|
|
34
42
|
Function("markJsBridgeReady") {
|
|
35
43
|
SentoriMobileVitals.markJsBridgeReady()
|
|
@@ -29,8 +29,33 @@ import UIKit
|
|
|
29
29
|
return result
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/// Last path the keyWindow lookup took. Exposed to JS via
|
|
33
|
+
/// `probeWireframe()` so the failure-mode diagnostic in Metro can
|
|
34
|
+
/// tell scene-race from "no window at all" without re-rolling the
|
|
35
|
+
/// pod. Updated on every captureSync call.
|
|
36
|
+
@objc public static var lastDiagPath: String = "none(not-yet-called)"
|
|
37
|
+
@objc public static var lastDiagNodes: Int = 0
|
|
38
|
+
@objc public static var lastDiagSceneCount: Int = 0
|
|
39
|
+
@objc public static var lastDiagWindowCount: Int = 0
|
|
40
|
+
private static var loggedFirstResult = false
|
|
41
|
+
|
|
32
42
|
private static func captureSync(maskedIds: Set<String>) -> String? {
|
|
33
|
-
|
|
43
|
+
let (winOpt, path) = resolveKeyWindow()
|
|
44
|
+
lastDiagPath = path
|
|
45
|
+
lastDiagSceneCount = currentSceneCount()
|
|
46
|
+
lastDiagWindowCount = currentWindowCount()
|
|
47
|
+
guard let window = winOpt else {
|
|
48
|
+
if !loggedFirstResult {
|
|
49
|
+
NSLog(
|
|
50
|
+
"[sentori] wireframe: returning nil — keyWindow path=%@ scenes=%d windows=%d",
|
|
51
|
+
path,
|
|
52
|
+
lastDiagSceneCount,
|
|
53
|
+
lastDiagWindowCount
|
|
54
|
+
)
|
|
55
|
+
loggedFirstResult = true
|
|
56
|
+
}
|
|
57
|
+
return nil
|
|
58
|
+
}
|
|
34
59
|
var nodes: [[String: Any]] = []
|
|
35
60
|
walk(
|
|
36
61
|
view: window,
|
|
@@ -39,6 +64,17 @@ import UIKit
|
|
|
39
64
|
window: window,
|
|
40
65
|
nodes: &nodes
|
|
41
66
|
)
|
|
67
|
+
lastDiagNodes = nodes.count
|
|
68
|
+
if !loggedFirstResult {
|
|
69
|
+
NSLog(
|
|
70
|
+
"[sentori] wireframe: first capture ok — keyWindow path=%@ bounds=%.0fx%.0f nodes=%d",
|
|
71
|
+
path,
|
|
72
|
+
window.bounds.width,
|
|
73
|
+
window.bounds.height,
|
|
74
|
+
nodes.count
|
|
75
|
+
)
|
|
76
|
+
loggedFirstResult = true
|
|
77
|
+
}
|
|
42
78
|
let payload: [String: Any] = [
|
|
43
79
|
"ts": Int(Date().timeIntervalSince1970 * 1000),
|
|
44
80
|
"width": Double(window.bounds.width),
|
|
@@ -51,19 +87,77 @@ import UIKit
|
|
|
51
87
|
return nil
|
|
52
88
|
}
|
|
53
89
|
|
|
54
|
-
|
|
90
|
+
/// Four-tier window resolution. The previous single-pass loop
|
|
91
|
+
/// returned nil whenever the first connected scene was a
|
|
92
|
+
/// `.background` or `.unattached` SwiftUI/preview scene that had
|
|
93
|
+
/// no windows yet — common on iOS 26 cold-start where the JS
|
|
94
|
+
/// thread spins up the replay tick before scene activation
|
|
95
|
+
/// settles (the v0.9.6 default 1 Hz fires within ~200 ms).
|
|
96
|
+
private static func resolveKeyWindow() -> (UIWindow?, String) {
|
|
55
97
|
if #available(iOS 13.0, *) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
98
|
+
let scenes = Array(UIApplication.shared.connectedScenes)
|
|
99
|
+
// Pass 1: foregroundActive scene with a key window.
|
|
100
|
+
for scene in scenes where scene.activationState == .foregroundActive {
|
|
101
|
+
if let ws = scene as? UIWindowScene,
|
|
102
|
+
let key = ws.windows.first(where: { $0.isKeyWindow }) {
|
|
103
|
+
return (key, "scene.fg.key")
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Pass 2: foregroundActive scene's first window (no key set yet).
|
|
107
|
+
for scene in scenes where scene.activationState == .foregroundActive {
|
|
108
|
+
if let ws = scene as? UIWindowScene, let win = ws.windows.first {
|
|
109
|
+
return (win, "scene.fg.first")
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Pass 3: foregroundInactive (mid-transition) scene with any window.
|
|
113
|
+
for scene in scenes where scene.activationState == .foregroundInactive {
|
|
114
|
+
if let ws = scene as? UIWindowScene, let win = ws.windows.first {
|
|
115
|
+
return (win, "scene.fgi.first")
|
|
60
116
|
}
|
|
61
|
-
|
|
62
|
-
|
|
117
|
+
}
|
|
118
|
+
// Pass 4: any scene at all with windows.
|
|
119
|
+
for scene in scenes {
|
|
120
|
+
if let ws = scene as? UIWindowScene, let win = ws.windows.first {
|
|
121
|
+
return (win, "scene.any.first")
|
|
63
122
|
}
|
|
64
123
|
}
|
|
124
|
+
// Fallthrough → legacy windows list.
|
|
125
|
+
}
|
|
126
|
+
if let leg = UIApplication.shared.windows.first {
|
|
127
|
+
return (leg, "legacy.first")
|
|
65
128
|
}
|
|
66
|
-
return
|
|
129
|
+
return (nil, "none")
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private static func currentSceneCount() -> Int {
|
|
133
|
+
if #available(iOS 13.0, *) {
|
|
134
|
+
return UIApplication.shared.connectedScenes.count
|
|
135
|
+
}
|
|
136
|
+
return 0
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private static func currentWindowCount() -> Int {
|
|
140
|
+
if #available(iOS 13.0, *) {
|
|
141
|
+
return UIApplication.shared.connectedScenes.reduce(0) { acc, scene in
|
|
142
|
+
acc + ((scene as? UIWindowScene)?.windows.count ?? 0)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return UIApplication.shared.windows.count
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// JS-side probe. Returns a dict the example/dashboard can render
|
|
149
|
+
/// to ask "why is the ring empty?" without parsing Metro logs.
|
|
150
|
+
/// `lastNodes == 0 && lastPath != "none"` means the window walk
|
|
151
|
+
/// happened but the tree was empty (unusual — backgrounded?).
|
|
152
|
+
/// `lastPath == "none(...)"` means no UIWindow was reachable at
|
|
153
|
+
/// the moment of the last tick.
|
|
154
|
+
@objc public static func probe() -> [String: Any] {
|
|
155
|
+
return [
|
|
156
|
+
"lastPath": lastDiagPath,
|
|
157
|
+
"lastNodes": lastDiagNodes,
|
|
158
|
+
"sceneCount": lastDiagSceneCount,
|
|
159
|
+
"windowCount": lastDiagWindowCount,
|
|
160
|
+
]
|
|
67
161
|
}
|
|
68
162
|
|
|
69
163
|
/// Cap on nodes per snapshot — extremely deep / wide trees can
|
package/lib/index.d.ts
CHANGED
|
@@ -61,7 +61,8 @@ export { getColdStartMs, markTimeToFullDisplay, type TimeToFullDisplayHandle, }
|
|
|
61
61
|
export { MomentHandle, type MomentProperties, startMoment } from '@goliapkg/sentori-core';
|
|
62
62
|
export { bindState, recordState, type StateSnapshot, unbindState, } from './state-snapshots';
|
|
63
63
|
export { RageTapCapture } from './rage-tap';
|
|
64
|
-
export { startAnrWatchdog, stopAnrWatchdog, triggerNativeCrash, } from './native';
|
|
64
|
+
export { probeNativeWireframe, startAnrWatchdog, stopAnrWatchdog, triggerNativeCrash, } from './native';
|
|
65
|
+
export { drainReplay, startReplay, stopReplay } from './replay';
|
|
65
66
|
export { endSession, markSessionCrashed, startSession, } from './session-tracker';
|
|
66
67
|
export { type NavigationRefLike, useTraceNavigation } from './navigation';
|
|
67
68
|
export type { Event, SentoriError, Frame, Breadcrumb, BreadcrumbType, Device, DeviceOS, App, User, Tags, EventKind, Platform, } from './types';
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAkB,KAAK,oBAAoB,EAAE,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAOxG,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EACL,cAAc,EACd,qBAAqB,EAEtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAO5C,eAAO,MAAM,OAAO;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAkB,KAAK,oBAAoB,EAAE,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAOxG,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EACL,cAAc,EACd,qBAAqB,EAEtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAO5C,eAAO,MAAM,OAAO;;;;;;;;;;aAgGsR,CAAC;eAAmB,CAAC;YAAgB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;CAlE/U,CAAC;AAEF,eAAe,OAAO,CAAC;AAEvB,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,WAAW,EAAE,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,OAAO,EACP,gBAAgB,EAChB,OAAO,GACR,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,KAAK,oBAAoB,EAAE,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxG,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,KAAK,uBAAuB,GAC7B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EACL,SAAS,EACT,WAAW,EACX,KAAK,aAAa,EAClB,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAChE,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,YAAY,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE1E,YAAY,EACV,KAAK,EACL,YAAY,EACZ,KAAK,EACL,UAAU,EACV,cAAc,EACd,MAAM,EACN,QAAQ,EACR,GAAG,EACH,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,QAAQ,GACT,MAAM,SAAS,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -57,7 +57,8 @@ export { getColdStartMs, markTimeToFullDisplay, } from './mobile-vitals';
|
|
|
57
57
|
export { MomentHandle, startMoment } from '@goliapkg/sentori-core';
|
|
58
58
|
export { bindState, recordState, unbindState, } from './state-snapshots';
|
|
59
59
|
export { RageTapCapture } from './rage-tap';
|
|
60
|
-
export { startAnrWatchdog, stopAnrWatchdog, triggerNativeCrash, } from './native';
|
|
60
|
+
export { probeNativeWireframe, startAnrWatchdog, stopAnrWatchdog, triggerNativeCrash, } from './native';
|
|
61
|
+
export { drainReplay, startReplay, stopReplay } from './replay';
|
|
61
62
|
export { endSession, markSessionCrashed, startSession, } from './session-tracker';
|
|
62
63
|
export { useTraceNavigation } from './navigation';
|
|
63
64
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.bak
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { init } from './init';
|
|
2
|
+
import { addBreadcrumb } from './breadcrumbs';
|
|
3
|
+
import { captureError, captureException, captureStep, getUser, sendUserFeedback, setUser, } from './capture';
|
|
4
|
+
import { ErrorBoundary } from './error-boundary';
|
|
5
|
+
import { FeedbackButton } from './feedback-widget';
|
|
6
|
+
import { clearAllFeatureFlags, clearFeatureFlag, getFeatureFlags, setFeatureFlag, } from './feature-flags';
|
|
7
|
+
import { clearMaskQuery, registerMaskQuery } from './mask';
|
|
8
|
+
import { measureFn } from './measure';
|
|
9
|
+
import { getColdStartMs, markTimeToFullDisplay, } from './mobile-vitals';
|
|
10
|
+
import { bindState, recordState, unbindState } from './state-snapshots';
|
|
11
|
+
import { startMoment } from '@goliapkg/sentori-core';
|
|
12
|
+
import { flushMetrics, recordMetric } from './metrics';
|
|
13
|
+
import { RageTapCapture } from './rage-tap';
|
|
14
|
+
import { endSession, markSessionCrashed, startSession, } from './session-tracker';
|
|
15
|
+
export const sentori = {
|
|
16
|
+
init,
|
|
17
|
+
addBreadcrumb,
|
|
18
|
+
setUser,
|
|
19
|
+
getUser,
|
|
20
|
+
captureError,
|
|
21
|
+
captureException,
|
|
22
|
+
captureStep,
|
|
23
|
+
sendUserFeedback,
|
|
24
|
+
recordMetric,
|
|
25
|
+
flushMetrics,
|
|
26
|
+
measureFn,
|
|
27
|
+
startMoment,
|
|
28
|
+
bindState,
|
|
29
|
+
recordState,
|
|
30
|
+
unbindState,
|
|
31
|
+
markTimeToFullDisplay,
|
|
32
|
+
getColdStartMs,
|
|
33
|
+
setFeatureFlag,
|
|
34
|
+
clearFeatureFlag,
|
|
35
|
+
clearAllFeatureFlags,
|
|
36
|
+
getFeatureFlags,
|
|
37
|
+
ErrorBoundary,
|
|
38
|
+
FeedbackButton,
|
|
39
|
+
RageTapCapture,
|
|
40
|
+
registerMaskQuery,
|
|
41
|
+
clearMaskQuery,
|
|
42
|
+
startSession,
|
|
43
|
+
endSession,
|
|
44
|
+
markSessionCrashed,
|
|
45
|
+
};
|
|
46
|
+
export default sentori;
|
|
47
|
+
export { init, init as initSentori } from './init';
|
|
48
|
+
export { addBreadcrumb } from './breadcrumbs';
|
|
49
|
+
export { captureError, captureException, captureStep, getUser, sendUserFeedback, setUser, } from './capture';
|
|
50
|
+
export { ErrorBoundary } from './error-boundary';
|
|
51
|
+
export { FeedbackButton } from './feedback-widget';
|
|
52
|
+
export { clearAllFeatureFlags, clearFeatureFlag, getFeatureFlags, setFeatureFlag, } from './feature-flags';
|
|
53
|
+
export { clearMaskQuery, registerMaskQuery } from './mask';
|
|
54
|
+
export { flushMetrics, recordMetric } from './metrics';
|
|
55
|
+
export { measureFn } from './measure';
|
|
56
|
+
export { getColdStartMs, markTimeToFullDisplay, } from './mobile-vitals';
|
|
57
|
+
export { MomentHandle, startMoment } from '@goliapkg/sentori-core';
|
|
58
|
+
export { bindState, recordState, unbindState, } from './state-snapshots';
|
|
59
|
+
export { RageTapCapture } from './rage-tap';
|
|
60
|
+
export { probeNativeWireframe, startAnrWatchdog, stopAnrWatchdog, triggerNativeCrash, } from './native';
|
|
61
|
+
export { drainReplay, startReplay, stopReplay } from './replay';
|
|
62
|
+
export { endSession, markSessionCrashed, startSession, } from './session-tracker';
|
|
63
|
+
export { useTraceNavigation } from './navigation';
|
|
64
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/lib/native.d.ts
CHANGED
|
@@ -78,5 +78,33 @@ export declare function captureNativeScreenshotWithMask(maskedIds: string[]): Pr
|
|
|
78
78
|
export declare function describeWireframeNative(): {
|
|
79
79
|
bound: boolean;
|
|
80
80
|
hasCaptureWireframe: boolean;
|
|
81
|
+
hasProbeWireframe: boolean;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* v0.9.12 — JS entry to the native `probeWireframe` diagnostic. Safe
|
|
85
|
+
* to call before the first replay tick — returns the not-yet-called
|
|
86
|
+
* sentinel. When the ring stays empty, this is the single call that
|
|
87
|
+
* answers "why" without redeploying the pod.
|
|
88
|
+
*
|
|
89
|
+
* path meaning
|
|
90
|
+
* ─────────────── ───────────────────────────────────────────────
|
|
91
|
+
* none(not-yet…) captureWireframe has never run yet
|
|
92
|
+
* scene.fg.key iOS: resolved via foregroundActive scene's key window
|
|
93
|
+
* scene.fg.first iOS: foregroundActive scene's first window (no key)
|
|
94
|
+
* scene.fgi.first iOS: foregroundInactive scene mid-transition
|
|
95
|
+
* scene.any.first iOS: had to fall back to any window
|
|
96
|
+
* legacy.first iOS: legacy UIApplication.windows path
|
|
97
|
+
* none iOS: no UIWindow reachable at the tick instant
|
|
98
|
+
* activity.null Android: no resumed Activity registered
|
|
99
|
+
* decorView.null Android: activity has no decor view yet
|
|
100
|
+
* root.zero-size Android: decorView size <= 0 (mid-layout)
|
|
101
|
+
* activity.resumed Android: ok
|
|
102
|
+
*/
|
|
103
|
+
export declare function probeNativeWireframe(): {
|
|
104
|
+
available: boolean;
|
|
105
|
+
lastNodes: number;
|
|
106
|
+
lastPath: string;
|
|
107
|
+
sceneCount: number;
|
|
108
|
+
windowCount: number;
|
|
81
109
|
};
|
|
82
110
|
//# sourceMappingURL=native.d.ts.map
|
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;AA8HH,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;CACpB,CAiCA"}
|
package/lib/native.js
CHANGED
|
@@ -193,6 +193,62 @@ export function describeWireframeNative() {
|
|
|
193
193
|
return {
|
|
194
194
|
bound: n !== null,
|
|
195
195
|
hasCaptureWireframe: Boolean(n?.captureWireframe),
|
|
196
|
+
hasProbeWireframe: Boolean(n?.probeWireframe),
|
|
196
197
|
};
|
|
197
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* v0.9.12 — JS entry to the native `probeWireframe` diagnostic. Safe
|
|
201
|
+
* to call before the first replay tick — returns the not-yet-called
|
|
202
|
+
* sentinel. When the ring stays empty, this is the single call that
|
|
203
|
+
* answers "why" without redeploying the pod.
|
|
204
|
+
*
|
|
205
|
+
* path meaning
|
|
206
|
+
* ─────────────── ───────────────────────────────────────────────
|
|
207
|
+
* none(not-yet…) captureWireframe has never run yet
|
|
208
|
+
* scene.fg.key iOS: resolved via foregroundActive scene's key window
|
|
209
|
+
* scene.fg.first iOS: foregroundActive scene's first window (no key)
|
|
210
|
+
* scene.fgi.first iOS: foregroundInactive scene mid-transition
|
|
211
|
+
* scene.any.first iOS: had to fall back to any window
|
|
212
|
+
* legacy.first iOS: legacy UIApplication.windows path
|
|
213
|
+
* none iOS: no UIWindow reachable at the tick instant
|
|
214
|
+
* activity.null Android: no resumed Activity registered
|
|
215
|
+
* decorView.null Android: activity has no decor view yet
|
|
216
|
+
* root.zero-size Android: decorView size <= 0 (mid-layout)
|
|
217
|
+
* activity.resumed Android: ok
|
|
218
|
+
*/
|
|
219
|
+
export function probeNativeWireframe() {
|
|
220
|
+
const n = native();
|
|
221
|
+
if (!n || typeof n.probeWireframe !== 'function') {
|
|
222
|
+
return {
|
|
223
|
+
available: false,
|
|
224
|
+
lastNodes: 0,
|
|
225
|
+
lastPath: 'native.unavailable',
|
|
226
|
+
sceneCount: 0,
|
|
227
|
+
windowCount: 0,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
const r = n.probeWireframe();
|
|
232
|
+
return {
|
|
233
|
+
available: true,
|
|
234
|
+
lastNodes: typeof r?.lastNodes === 'number' ? r.lastNodes : 0,
|
|
235
|
+
lastPath: typeof r?.lastPath === 'string' ? r.lastPath : 'unknown',
|
|
236
|
+
sceneCount: typeof r?.sceneCount === 'number' ? r.sceneCount : 0,
|
|
237
|
+
windowCount: typeof r?.windowCount === 'number' ? r.windowCount : 0,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
catch (e) {
|
|
241
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
242
|
+
// eslint-disable-next-line no-console
|
|
243
|
+
console.warn('[sentori] probeWireframe threw', e);
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
available: false,
|
|
247
|
+
lastNodes: 0,
|
|
248
|
+
lastPath: 'native.threw',
|
|
249
|
+
sceneCount: 0,
|
|
250
|
+
windowCount: 0,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
198
254
|
//# sourceMappingURL=native.js.map
|
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":"
|
|
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;AAsGD;;2BAE2B;AAC3B,wBAAgB,WAAW,IAAI,MAAM,CAMpC;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAI5C"}
|
package/lib/replay.js
CHANGED
|
@@ -29,6 +29,25 @@ const MIN_TICK_PERIOD_MS = 250;
|
|
|
29
29
|
let _ring = [];
|
|
30
30
|
let _timer = null;
|
|
31
31
|
let _running = false;
|
|
32
|
+
/**
|
|
33
|
+
* v0.9.13 — frame-level delta encoding: when the new snapshot matches
|
|
34
|
+
* the last one byte-for-byte (static UI, no animation, off-screen
|
|
35
|
+
* app), skip pushing it. The ring stays meaningful (one frame =
|
|
36
|
+
* one *change*), the attachment shrinks proportionally, and a real
|
|
37
|
+
* idle phase no longer evicts a useful pre-error frame.
|
|
38
|
+
*
|
|
39
|
+
* We only check against the most-recently-pushed snapshot, not the
|
|
40
|
+
* whole ring — that's cheap (one string comparison per tick) and
|
|
41
|
+
* catches the dominant case (idle screens). True content changes
|
|
42
|
+
* fall through and push as before.
|
|
43
|
+
*
|
|
44
|
+
* Budget verification on the iOS showcase (apps/ios-showcase): 60
|
|
45
|
+
* frames at ~120 bytes each → ≈ 7 KB raw NDJSON, well under the
|
|
46
|
+
* 500 KB attachment cap. Heavier RN apps with 200+ visible nodes
|
|
47
|
+
* per frame can land in the 400 KB band; future work in v1.x adds
|
|
48
|
+
* native gzip on upload if real-world traffic ever pushes the cap.
|
|
49
|
+
*/
|
|
50
|
+
let _lastPushed = null;
|
|
32
51
|
/** Native module ref, resolved once on first start. Caching here
|
|
33
52
|
* avoids the cost of `requireNativeModule('Sentori')` on every
|
|
34
53
|
* capture tick (Metro's require cache makes this cheap, but the
|
|
@@ -64,7 +83,20 @@ export function startReplay(opts) {
|
|
|
64
83
|
_timer = setInterval(() => {
|
|
65
84
|
captureTick();
|
|
66
85
|
}, period);
|
|
67
|
-
|
|
86
|
+
// v0.9.12 — Insight 2026-05-17 report: 0.9.11 emitted the
|
|
87
|
+
// "starting bound=true" line then went silent. Root cause was the
|
|
88
|
+
// `.unref?.()` call that used to live here. Hermes 0.81 doesn't
|
|
89
|
+
// ship a Timer object with the Node-style `unref` method, and the
|
|
90
|
+
// optional-chained call ended up dereferencing a `prototype`
|
|
91
|
+
// property on `undefined` — throwing synchronously inside
|
|
92
|
+
// startReplay, which RN's bridge swallowed silently. Net effect:
|
|
93
|
+
// setInterval registered, captureTick never invoked. Drop the
|
|
94
|
+
// call; replay tick lifecycle is bound to the app process, no
|
|
95
|
+
// event-loop tweak needed.
|
|
96
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
97
|
+
// eslint-disable-next-line no-console
|
|
98
|
+
console.warn('[sentori] replay: scheduled tick period=', period, 'ms');
|
|
99
|
+
}
|
|
68
100
|
}
|
|
69
101
|
export function stopReplay() {
|
|
70
102
|
_running = false;
|
|
@@ -73,25 +105,81 @@ export function stopReplay() {
|
|
|
73
105
|
_timer = null;
|
|
74
106
|
}
|
|
75
107
|
_nativeMod = null;
|
|
108
|
+
_emptyTickCount = 0;
|
|
109
|
+
_emptyTickLogStride = 1;
|
|
110
|
+
_firstTickLogged = false;
|
|
76
111
|
}
|
|
112
|
+
let _emptyTickCount = 0;
|
|
113
|
+
let _emptyTickLogStride = 1;
|
|
114
|
+
let _firstTickLogged = false;
|
|
77
115
|
function captureTick() {
|
|
78
116
|
if (!_running)
|
|
79
117
|
return;
|
|
80
|
-
|
|
118
|
+
// v0.9.12 — UNCONDITIONAL first-tick log. Proves the setInterval
|
|
119
|
+
// callback is firing at all, before any other code that could
|
|
120
|
+
// throw. 0.9.11's diagnostic was inside a `else if (snapshot==null)`
|
|
121
|
+
// branch that could only surface AFTER the native call returned;
|
|
122
|
+
// useless when the bug is that the tick body never enters.
|
|
123
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__ && !_firstTickLogged) {
|
|
124
|
+
// eslint-disable-next-line no-console
|
|
125
|
+
console.warn('[sentori] replay tick: FIRST INVOCATION');
|
|
126
|
+
_firstTickLogged = true;
|
|
127
|
+
}
|
|
128
|
+
// 0.9.11 called startSpan OUTSIDE the catch block. If
|
|
129
|
+
// `@goliapkg/sentori-core` failed to initialise (or startSpan
|
|
130
|
+
// threw for any other reason on the first tick) the whole tick
|
|
131
|
+
// callback died silently. Wrap so worst case is "no span for this
|
|
132
|
+
// tick" not "no ticks for the session".
|
|
133
|
+
let tickSpan = null;
|
|
134
|
+
try {
|
|
135
|
+
tickSpan = startSpan('sentori.replay.tick', { name: 'tick' });
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// never fatal
|
|
139
|
+
}
|
|
81
140
|
try {
|
|
82
141
|
const maskIds = readMaskIds();
|
|
83
142
|
const snapshot = _nativeMod?.captureWireframe?.(maskIds);
|
|
84
143
|
if (typeof snapshot === 'string' && snapshot.length > 0) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
144
|
+
// v0.9.13 — skip pushing if the frame is identical to the last
|
|
145
|
+
// pushed one. See _lastPushed comment for the rationale.
|
|
146
|
+
if (snapshot !== _lastPushed) {
|
|
147
|
+
_ring.push(snapshot);
|
|
148
|
+
_lastPushed = snapshot;
|
|
149
|
+
while (_ring.length > RING_SIZE) {
|
|
150
|
+
_ring.shift();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
_emptyTickCount = 0;
|
|
154
|
+
_emptyTickLogStride = 1;
|
|
155
|
+
}
|
|
156
|
+
else if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
157
|
+
// v0.9.11 — Insight 2026-05-17 Finding 6: tick fires hundreds
|
|
158
|
+
// of times but ring stays empty → native returned null/empty.
|
|
159
|
+
// Log on a back-off schedule (1st, 10th, 100th, …) so the
|
|
160
|
+
// diagnostic is visible without spamming Metro at 1 Hz for a
|
|
161
|
+
// 15-minute session.
|
|
162
|
+
_emptyTickCount += 1;
|
|
163
|
+
if (_emptyTickCount === 1 || _emptyTickCount === _emptyTickLogStride) {
|
|
164
|
+
// eslint-disable-next-line no-console
|
|
165
|
+
console.warn('[sentori] replay tick: native returned', snapshot === null
|
|
166
|
+
? 'null'
|
|
167
|
+
: typeof snapshot === 'string'
|
|
168
|
+
? `empty (length=${snapshot.length})`
|
|
169
|
+
: typeof snapshot, `(empty ticks so far: ${_emptyTickCount})`);
|
|
170
|
+
_emptyTickLogStride = Math.max(_emptyTickLogStride * 10, 10);
|
|
171
|
+
}
|
|
88
172
|
}
|
|
89
|
-
tickSpan
|
|
173
|
+
tickSpan?.finish({ status: 'ok' });
|
|
90
174
|
}
|
|
91
175
|
catch (e) {
|
|
92
176
|
if (e instanceof Error)
|
|
93
|
-
tickSpan
|
|
94
|
-
tickSpan
|
|
177
|
+
tickSpan?.setTag('error.message', e.message);
|
|
178
|
+
tickSpan?.finish({ status: 'error' });
|
|
179
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
180
|
+
// eslint-disable-next-line no-console
|
|
181
|
+
console.warn('[sentori] replay tick: threw', e);
|
|
182
|
+
}
|
|
95
183
|
}
|
|
96
184
|
}
|
|
97
185
|
function readMaskIds() {
|
|
@@ -123,10 +211,12 @@ export function drainReplay() {
|
|
|
123
211
|
return '';
|
|
124
212
|
const out = _ring.join('\n');
|
|
125
213
|
_ring = [];
|
|
214
|
+
_lastPushed = null;
|
|
126
215
|
return out;
|
|
127
216
|
}
|
|
128
217
|
export function __resetReplayForTests() {
|
|
129
218
|
stopReplay();
|
|
130
219
|
_ring = [];
|
|
220
|
+
_lastPushed = null;
|
|
131
221
|
}
|
|
132
222
|
//# sourceMappingURL=replay.js.map
|
package/lib/replay.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,EAAE;AACF,mEAAmE;AACnE,mEAAmE;AACnE,kEAAkE;AAClE,kEAAkE;AAClE,4DAA4D;AAC5D,EAAE;AACF,gCAAgC;AAChC,kEAAkE;AAClE,iEAAiE;AACjE,+DAA+D;AAC/D,6DAA6D;AAC7D,wDAAwD;AACxD,iEAAiE;AACjE,iEAAiE;AACjE,4DAA4D;AAC5D,wBAAwB;AAExB,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAInD,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAC9B,MAAM,SAAS,GAAG,EAAE,CAAC;AAErB;;;wCAGwC;AACxC,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAE/B,IAAI,KAAK,GAAa,EAAE,CAAC;AACzB,IAAI,MAAM,GAA0C,IAAI,CAAC;AACzD,IAAI,QAAQ,GAAG,KAAK,CAAC;AAErB;;;;uCAIuC;AACvC,IAAI,UAAU,GAA8B,IAAI,CAAC;AAQjD,MAAM,UAAU,WAAW,CAAC,IAAmB;IAC7C,IAAI,QAAQ;QAAE,OAAO;IACrB,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW;QAAE,OAAO;IACtC,iEAAiE;IACjE,gEAAgE;IAChE,6DAA6D;IAC7D,8DAA8D;IAC9D,iEAAiE;IACjE,qEAAqE;IACrE,MAAM,IAAI,GAAG,uBAAuB,EAAE,CAAC;IACvC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAChB,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,EAAE,CAAC;YAC9C,sCAAsC;YACtC,OAAO,CAAC,IAAI,CACV,4GAA4G,CAC7G,CAAC;QACJ,CAAC;QACD,OAAO;IACT,CAAC;IACD,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,EAAE,CAAC;QAC9C,sCAAsC;QACtC,OAAO,CAAC,IAAI,CACV,4BAA4B,EAC5B,QAAQ,EAAE,IAAI,CAAC,KAAK,EACpB,sBAAsB,EAAE,IAAI,CAAC,mBAAmB,CACjD,CAAC;IACJ,CAAC;IACD,QAAQ,GAAG,IAAI,CAAC;IAChB,UAAU,GAAG,gBAAgB,EAAE,CAAC;IAChC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,CAAC,KAAK,CAAC,gBAAgB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3F,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE;QACxB,WAAW,EAAE,CAAC;IAChB,CAAC,EAAE,MAAM,CAAC,CAAC;
|
|
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": "0.
|
|
3
|
+
"version": "1.0.0-rc.1",
|
|
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/index.ts
CHANGED
|
@@ -102,10 +102,12 @@ export {
|
|
|
102
102
|
} from './state-snapshots';
|
|
103
103
|
export { RageTapCapture } from './rage-tap';
|
|
104
104
|
export {
|
|
105
|
+
probeNativeWireframe,
|
|
105
106
|
startAnrWatchdog,
|
|
106
107
|
stopAnrWatchdog,
|
|
107
108
|
triggerNativeCrash,
|
|
108
109
|
} from './native';
|
|
110
|
+
export { drainReplay, startReplay, stopReplay } from './replay';
|
|
109
111
|
export {
|
|
110
112
|
endSession,
|
|
111
113
|
markSessionCrashed,
|
package/src/native.ts
CHANGED
|
@@ -67,6 +67,20 @@ type SentoriNativeModule = {
|
|
|
67
67
|
* snapshot string or null on failure.
|
|
68
68
|
*/
|
|
69
69
|
captureWireframe?: (maskedIds: string[]) => null | string
|
|
70
|
+
/**
|
|
71
|
+
* v0.9.12 — diagnostic readout for the wireframe path. Cheap
|
|
72
|
+
* synchronous call that returns the path the last `captureWireframe`
|
|
73
|
+
* tick took plus scene/window counts at that moment. Used by the
|
|
74
|
+
* example app's debug button and the Insight verify flow to tell
|
|
75
|
+
* "no window resolvable" from "window walked but tree empty" without
|
|
76
|
+
* shipping a new pod.
|
|
77
|
+
*/
|
|
78
|
+
probeWireframe?: () => {
|
|
79
|
+
lastPath: string
|
|
80
|
+
lastNodes: number
|
|
81
|
+
sceneCount: number
|
|
82
|
+
windowCount: number
|
|
83
|
+
}
|
|
70
84
|
/**
|
|
71
85
|
* Phase 22 sub-D / sub-E: cross-platform main-thread watchdog.
|
|
72
86
|
* Android: 5 s / 1 s defaults (matches the OS ANR threshold).
|
|
@@ -295,10 +309,73 @@ export async function captureNativeScreenshotWithMask(
|
|
|
295
309
|
export function describeWireframeNative(): {
|
|
296
310
|
bound: boolean
|
|
297
311
|
hasCaptureWireframe: boolean
|
|
312
|
+
hasProbeWireframe: boolean
|
|
298
313
|
} {
|
|
299
314
|
const n = native()
|
|
300
315
|
return {
|
|
301
316
|
bound: n !== null,
|
|
302
317
|
hasCaptureWireframe: Boolean(n?.captureWireframe),
|
|
318
|
+
hasProbeWireframe: Boolean(n?.probeWireframe),
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* v0.9.12 — JS entry to the native `probeWireframe` diagnostic. Safe
|
|
324
|
+
* to call before the first replay tick — returns the not-yet-called
|
|
325
|
+
* sentinel. When the ring stays empty, this is the single call that
|
|
326
|
+
* answers "why" without redeploying the pod.
|
|
327
|
+
*
|
|
328
|
+
* path meaning
|
|
329
|
+
* ─────────────── ───────────────────────────────────────────────
|
|
330
|
+
* none(not-yet…) captureWireframe has never run yet
|
|
331
|
+
* scene.fg.key iOS: resolved via foregroundActive scene's key window
|
|
332
|
+
* scene.fg.first iOS: foregroundActive scene's first window (no key)
|
|
333
|
+
* scene.fgi.first iOS: foregroundInactive scene mid-transition
|
|
334
|
+
* scene.any.first iOS: had to fall back to any window
|
|
335
|
+
* legacy.first iOS: legacy UIApplication.windows path
|
|
336
|
+
* none iOS: no UIWindow reachable at the tick instant
|
|
337
|
+
* activity.null Android: no resumed Activity registered
|
|
338
|
+
* decorView.null Android: activity has no decor view yet
|
|
339
|
+
* root.zero-size Android: decorView size <= 0 (mid-layout)
|
|
340
|
+
* activity.resumed Android: ok
|
|
341
|
+
*/
|
|
342
|
+
export function probeNativeWireframe(): {
|
|
343
|
+
available: boolean
|
|
344
|
+
lastNodes: number
|
|
345
|
+
lastPath: string
|
|
346
|
+
sceneCount: number
|
|
347
|
+
windowCount: number
|
|
348
|
+
} {
|
|
349
|
+
const n = native()
|
|
350
|
+
if (!n || typeof n.probeWireframe !== 'function') {
|
|
351
|
+
return {
|
|
352
|
+
available: false,
|
|
353
|
+
lastNodes: 0,
|
|
354
|
+
lastPath: 'native.unavailable',
|
|
355
|
+
sceneCount: 0,
|
|
356
|
+
windowCount: 0,
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
try {
|
|
360
|
+
const r = n.probeWireframe()
|
|
361
|
+
return {
|
|
362
|
+
available: true,
|
|
363
|
+
lastNodes: typeof r?.lastNodes === 'number' ? r.lastNodes : 0,
|
|
364
|
+
lastPath: typeof r?.lastPath === 'string' ? r.lastPath : 'unknown',
|
|
365
|
+
sceneCount: typeof r?.sceneCount === 'number' ? r.sceneCount : 0,
|
|
366
|
+
windowCount: typeof r?.windowCount === 'number' ? r.windowCount : 0,
|
|
367
|
+
}
|
|
368
|
+
} catch (e) {
|
|
369
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
370
|
+
// eslint-disable-next-line no-console
|
|
371
|
+
console.warn('[sentori] probeWireframe threw', e)
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
available: false,
|
|
375
|
+
lastNodes: 0,
|
|
376
|
+
lastPath: 'native.threw',
|
|
377
|
+
sceneCount: 0,
|
|
378
|
+
windowCount: 0,
|
|
379
|
+
}
|
|
303
380
|
}
|
|
304
381
|
}
|
package/src/replay.ts
CHANGED
|
@@ -37,6 +37,26 @@ let _ring: string[] = [];
|
|
|
37
37
|
let _timer: ReturnType<typeof setInterval> | null = null;
|
|
38
38
|
let _running = false;
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* v0.9.13 — frame-level delta encoding: when the new snapshot matches
|
|
42
|
+
* the last one byte-for-byte (static UI, no animation, off-screen
|
|
43
|
+
* app), skip pushing it. The ring stays meaningful (one frame =
|
|
44
|
+
* one *change*), the attachment shrinks proportionally, and a real
|
|
45
|
+
* idle phase no longer evicts a useful pre-error frame.
|
|
46
|
+
*
|
|
47
|
+
* We only check against the most-recently-pushed snapshot, not the
|
|
48
|
+
* whole ring — that's cheap (one string comparison per tick) and
|
|
49
|
+
* catches the dominant case (idle screens). True content changes
|
|
50
|
+
* fall through and push as before.
|
|
51
|
+
*
|
|
52
|
+
* Budget verification on the iOS showcase (apps/ios-showcase): 60
|
|
53
|
+
* frames at ~120 bytes each → ≈ 7 KB raw NDJSON, well under the
|
|
54
|
+
* 500 KB attachment cap. Heavier RN apps with 200+ visible nodes
|
|
55
|
+
* per frame can land in the 400 KB band; future work in v1.x adds
|
|
56
|
+
* native gzip on upload if real-world traffic ever pushes the cap.
|
|
57
|
+
*/
|
|
58
|
+
let _lastPushed: null | string = null;
|
|
59
|
+
|
|
40
60
|
/** Native module ref, resolved once on first start. Caching here
|
|
41
61
|
* avoids the cost of `requireNativeModule('Sentori')` on every
|
|
42
62
|
* capture tick (Metro's require cache makes this cheap, but the
|
|
@@ -83,7 +103,20 @@ export function startReplay(opts: ReplayOptions): void {
|
|
|
83
103
|
_timer = setInterval(() => {
|
|
84
104
|
captureTick();
|
|
85
105
|
}, period);
|
|
86
|
-
|
|
106
|
+
// v0.9.12 — Insight 2026-05-17 report: 0.9.11 emitted the
|
|
107
|
+
// "starting bound=true" line then went silent. Root cause was the
|
|
108
|
+
// `.unref?.()` call that used to live here. Hermes 0.81 doesn't
|
|
109
|
+
// ship a Timer object with the Node-style `unref` method, and the
|
|
110
|
+
// optional-chained call ended up dereferencing a `prototype`
|
|
111
|
+
// property on `undefined` — throwing synchronously inside
|
|
112
|
+
// startReplay, which RN's bridge swallowed silently. Net effect:
|
|
113
|
+
// setInterval registered, captureTick never invoked. Drop the
|
|
114
|
+
// call; replay tick lifecycle is bound to the app process, no
|
|
115
|
+
// event-loop tweak needed.
|
|
116
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
117
|
+
// eslint-disable-next-line no-console
|
|
118
|
+
console.warn('[sentori] replay: scheduled tick period=', period, 'ms');
|
|
119
|
+
}
|
|
87
120
|
}
|
|
88
121
|
|
|
89
122
|
export function stopReplay(): void {
|
|
@@ -93,22 +126,82 @@ export function stopReplay(): void {
|
|
|
93
126
|
_timer = null;
|
|
94
127
|
}
|
|
95
128
|
_nativeMod = null;
|
|
129
|
+
_emptyTickCount = 0;
|
|
130
|
+
_emptyTickLogStride = 1;
|
|
131
|
+
_firstTickLogged = false;
|
|
96
132
|
}
|
|
97
133
|
|
|
134
|
+
let _emptyTickCount = 0;
|
|
135
|
+
let _emptyTickLogStride = 1;
|
|
136
|
+
let _firstTickLogged = false;
|
|
137
|
+
|
|
98
138
|
function captureTick(): void {
|
|
99
139
|
if (!_running) return;
|
|
100
|
-
|
|
140
|
+
// v0.9.12 — UNCONDITIONAL first-tick log. Proves the setInterval
|
|
141
|
+
// callback is firing at all, before any other code that could
|
|
142
|
+
// throw. 0.9.11's diagnostic was inside a `else if (snapshot==null)`
|
|
143
|
+
// branch that could only surface AFTER the native call returned;
|
|
144
|
+
// useless when the bug is that the tick body never enters.
|
|
145
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__ && !_firstTickLogged) {
|
|
146
|
+
// eslint-disable-next-line no-console
|
|
147
|
+
console.warn('[sentori] replay tick: FIRST INVOCATION');
|
|
148
|
+
_firstTickLogged = true;
|
|
149
|
+
}
|
|
150
|
+
// 0.9.11 called startSpan OUTSIDE the catch block. If
|
|
151
|
+
// `@goliapkg/sentori-core` failed to initialise (or startSpan
|
|
152
|
+
// threw for any other reason on the first tick) the whole tick
|
|
153
|
+
// callback died silently. Wrap so worst case is "no span for this
|
|
154
|
+
// tick" not "no ticks for the session".
|
|
155
|
+
let tickSpan: ReturnType<typeof startSpan> | null = null;
|
|
156
|
+
try {
|
|
157
|
+
tickSpan = startSpan('sentori.replay.tick', { name: 'tick' });
|
|
158
|
+
} catch {
|
|
159
|
+
// never fatal
|
|
160
|
+
}
|
|
101
161
|
try {
|
|
102
162
|
const maskIds = readMaskIds();
|
|
103
163
|
const snapshot = _nativeMod?.captureWireframe?.(maskIds);
|
|
104
164
|
if (typeof snapshot === 'string' && snapshot.length > 0) {
|
|
105
|
-
|
|
106
|
-
|
|
165
|
+
// v0.9.13 — skip pushing if the frame is identical to the last
|
|
166
|
+
// pushed one. See _lastPushed comment for the rationale.
|
|
167
|
+
if (snapshot !== _lastPushed) {
|
|
168
|
+
_ring.push(snapshot);
|
|
169
|
+
_lastPushed = snapshot;
|
|
170
|
+
while (_ring.length > RING_SIZE) {
|
|
171
|
+
_ring.shift();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
_emptyTickCount = 0;
|
|
175
|
+
_emptyTickLogStride = 1;
|
|
176
|
+
} else if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
177
|
+
// v0.9.11 — Insight 2026-05-17 Finding 6: tick fires hundreds
|
|
178
|
+
// of times but ring stays empty → native returned null/empty.
|
|
179
|
+
// Log on a back-off schedule (1st, 10th, 100th, …) so the
|
|
180
|
+
// diagnostic is visible without spamming Metro at 1 Hz for a
|
|
181
|
+
// 15-minute session.
|
|
182
|
+
_emptyTickCount += 1;
|
|
183
|
+
if (_emptyTickCount === 1 || _emptyTickCount === _emptyTickLogStride) {
|
|
184
|
+
// eslint-disable-next-line no-console
|
|
185
|
+
console.warn(
|
|
186
|
+
'[sentori] replay tick: native returned',
|
|
187
|
+
snapshot === null
|
|
188
|
+
? 'null'
|
|
189
|
+
: typeof snapshot === 'string'
|
|
190
|
+
? `empty (length=${snapshot.length})`
|
|
191
|
+
: typeof snapshot,
|
|
192
|
+
`(empty ticks so far: ${_emptyTickCount})`,
|
|
193
|
+
);
|
|
194
|
+
_emptyTickLogStride = Math.max(_emptyTickLogStride * 10, 10);
|
|
195
|
+
}
|
|
107
196
|
}
|
|
108
|
-
tickSpan
|
|
197
|
+
tickSpan?.finish({ status: 'ok' });
|
|
109
198
|
} catch (e) {
|
|
110
|
-
if (e instanceof Error) tickSpan
|
|
111
|
-
tickSpan
|
|
199
|
+
if (e instanceof Error) tickSpan?.setTag('error.message', e.message);
|
|
200
|
+
tickSpan?.finish({ status: 'error' });
|
|
201
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
202
|
+
// eslint-disable-next-line no-console
|
|
203
|
+
console.warn('[sentori] replay tick: threw', e);
|
|
204
|
+
}
|
|
112
205
|
}
|
|
113
206
|
}
|
|
114
207
|
|
|
@@ -145,10 +238,12 @@ export function drainReplay(): string {
|
|
|
145
238
|
if (_ring.length === 0) return '';
|
|
146
239
|
const out = _ring.join('\n');
|
|
147
240
|
_ring = [];
|
|
241
|
+
_lastPushed = null;
|
|
148
242
|
return out;
|
|
149
243
|
}
|
|
150
244
|
|
|
151
245
|
export function __resetReplayForTests(): void {
|
|
152
246
|
stopReplay();
|
|
153
247
|
_ring = [];
|
|
248
|
+
_lastPushed = null;
|
|
154
249
|
}
|