@goliapkg/sentori-react-native 0.9.11 → 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 +75 -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 +75 -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;
|
|
@@ -75,20 +107,49 @@ export function stopReplay() {
|
|
|
75
107
|
_nativeMod = null;
|
|
76
108
|
_emptyTickCount = 0;
|
|
77
109
|
_emptyTickLogStride = 1;
|
|
110
|
+
_firstTickLogged = false;
|
|
78
111
|
}
|
|
79
112
|
let _emptyTickCount = 0;
|
|
80
113
|
let _emptyTickLogStride = 1;
|
|
114
|
+
let _firstTickLogged = false;
|
|
81
115
|
function captureTick() {
|
|
82
116
|
if (!_running)
|
|
83
117
|
return;
|
|
84
|
-
|
|
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
|
+
}
|
|
85
140
|
try {
|
|
86
141
|
const maskIds = readMaskIds();
|
|
87
142
|
const snapshot = _nativeMod?.captureWireframe?.(maskIds);
|
|
88
143
|
if (typeof snapshot === 'string' && snapshot.length > 0) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
+
}
|
|
92
153
|
_emptyTickCount = 0;
|
|
93
154
|
_emptyTickLogStride = 1;
|
|
94
155
|
}
|
|
@@ -109,12 +170,16 @@ function captureTick() {
|
|
|
109
170
|
_emptyTickLogStride = Math.max(_emptyTickLogStride * 10, 10);
|
|
110
171
|
}
|
|
111
172
|
}
|
|
112
|
-
tickSpan
|
|
173
|
+
tickSpan?.finish({ status: 'ok' });
|
|
113
174
|
}
|
|
114
175
|
catch (e) {
|
|
115
176
|
if (e instanceof Error)
|
|
116
|
-
tickSpan
|
|
117
|
-
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
|
+
}
|
|
118
183
|
}
|
|
119
184
|
}
|
|
120
185
|
function readMaskIds() {
|
|
@@ -146,10 +211,12 @@ export function drainReplay() {
|
|
|
146
211
|
return '';
|
|
147
212
|
const out = _ring.join('\n');
|
|
148
213
|
_ring = [];
|
|
214
|
+
_lastPushed = null;
|
|
149
215
|
return out;
|
|
150
216
|
}
|
|
151
217
|
export function __resetReplayForTests() {
|
|
152
218
|
stopReplay();
|
|
153
219
|
_ring = [];
|
|
220
|
+
_lastPushed = null;
|
|
154
221
|
}
|
|
155
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 {
|
|
@@ -95,20 +128,49 @@ export function stopReplay(): void {
|
|
|
95
128
|
_nativeMod = null;
|
|
96
129
|
_emptyTickCount = 0;
|
|
97
130
|
_emptyTickLogStride = 1;
|
|
131
|
+
_firstTickLogged = false;
|
|
98
132
|
}
|
|
99
133
|
|
|
100
134
|
let _emptyTickCount = 0;
|
|
101
135
|
let _emptyTickLogStride = 1;
|
|
136
|
+
let _firstTickLogged = false;
|
|
102
137
|
|
|
103
138
|
function captureTick(): void {
|
|
104
139
|
if (!_running) return;
|
|
105
|
-
|
|
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
|
+
}
|
|
106
161
|
try {
|
|
107
162
|
const maskIds = readMaskIds();
|
|
108
163
|
const snapshot = _nativeMod?.captureWireframe?.(maskIds);
|
|
109
164
|
if (typeof snapshot === 'string' && snapshot.length > 0) {
|
|
110
|
-
|
|
111
|
-
|
|
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
|
+
}
|
|
112
174
|
_emptyTickCount = 0;
|
|
113
175
|
_emptyTickLogStride = 1;
|
|
114
176
|
} else if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
@@ -132,10 +194,14 @@ function captureTick(): void {
|
|
|
132
194
|
_emptyTickLogStride = Math.max(_emptyTickLogStride * 10, 10);
|
|
133
195
|
}
|
|
134
196
|
}
|
|
135
|
-
tickSpan
|
|
197
|
+
tickSpan?.finish({ status: 'ok' });
|
|
136
198
|
} catch (e) {
|
|
137
|
-
if (e instanceof Error) tickSpan
|
|
138
|
-
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
|
+
}
|
|
139
205
|
}
|
|
140
206
|
}
|
|
141
207
|
|
|
@@ -172,10 +238,12 @@ export function drainReplay(): string {
|
|
|
172
238
|
if (_ring.length === 0) return '';
|
|
173
239
|
const out = _ring.join('\n');
|
|
174
240
|
_ring = [];
|
|
241
|
+
_lastPushed = null;
|
|
175
242
|
return out;
|
|
176
243
|
}
|
|
177
244
|
|
|
178
245
|
export function __resetReplayForTests(): void {
|
|
179
246
|
stopReplay();
|
|
180
247
|
_ring = [];
|
|
248
|
+
_lastPushed = null;
|
|
181
249
|
}
|