@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.
@@ -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() ?: return null
56
- val root = activity.window?.decorView ?: return null
57
- if (root.width <= 0 || root.height <= 0) return null
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)
@@ -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
- guard let window = keyWindow() else { return nil }
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
- private static func keyWindow() -> UIWindow? {
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
- for scene in UIApplication.shared.connectedScenes {
57
- guard let ws = scene as? UIWindowScene else { continue }
58
- if let key = ws.windows.first(where: { $0.isKeyWindow }) {
59
- return key
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
- if let first = ws.windows.first {
62
- return first
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 UIApplication.shared.windows.first
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';
@@ -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;;;;;;;;;;aA8F+W,CAAC;eAAmB,CAAC;YAAgB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;CAhExa,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,gBAAgB,EAChB,eAAe,EACf,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAClB,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"}
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
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAgHH,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;CAC7B,CAMA"}
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":"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"}
1
+ {"version":3,"file":"native.js","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AA8CA,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,CAkCrD;AAED,wBAAgB,UAAU,IAAI,IAAI,CASjC;AAsED;;2BAE2B;AAC3B,wBAAgB,WAAW,IAAI,MAAM,CAKpC;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C"}
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
- _timer.unref?.();
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
- const tickSpan = startSpan('sentori.replay.tick', { name: 'tick' });
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
- _ring.push(snapshot);
90
- while (_ring.length > RING_SIZE)
91
- _ring.shift();
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.finish({ status: 'ok' });
173
+ tickSpan?.finish({ status: 'ok' });
113
174
  }
114
175
  catch (e) {
115
176
  if (e instanceof Error)
116
- tickSpan.setTag('error.message', e.message);
117
- tickSpan.finish({ status: 'error' });
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":"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"}
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;;;;;;;;;;;;;;;;;GAiBG;AACH,IAAI,WAAW,GAAkB,IAAI,CAAC;AAEtC;;;;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;IACX,0DAA0D;IAC1D,kEAAkE;IAClE,gEAAgE;IAChE,kEAAkE;IAClE,6DAA6D;IAC7D,0DAA0D;IAC1D,iEAAiE;IACjE,8DAA8D;IAC9D,8DAA8D;IAC9D,2BAA2B;IAC3B,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,EAAE,CAAC;QAC9C,sCAAsC;QACtC,OAAO,CAAC,IAAI,CAAC,0CAA0C,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,QAAQ,GAAG,KAAK,CAAC;IACjB,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,aAAa,CAAC,MAAM,CAAC,CAAC;QACtB,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC;IACD,UAAU,GAAG,IAAI,CAAC;IAClB,eAAe,GAAG,CAAC,CAAC;IACpB,mBAAmB,GAAG,CAAC,CAAC;IACxB,gBAAgB,GAAG,KAAK,CAAC;AAC3B,CAAC;AAED,IAAI,eAAe,GAAG,CAAC,CAAC;AACxB,IAAI,mBAAmB,GAAG,CAAC,CAAC;AAC5B,IAAI,gBAAgB,GAAG,KAAK,CAAC;AAE7B,SAAS,WAAW;IAClB,IAAI,CAAC,QAAQ;QAAE,OAAO;IACtB,iEAAiE;IACjE,8DAA8D;IAC9D,qEAAqE;IACrE,iEAAiE;IACjE,2DAA2D;IAC3D,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACnE,sCAAsC;QACtC,OAAO,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;QACxD,gBAAgB,GAAG,IAAI,CAAC;IAC1B,CAAC;IACD,sDAAsD;IACtD,8DAA8D;IAC9D,+DAA+D;IAC/D,kEAAkE;IAClE,wCAAwC;IACxC,IAAI,QAAQ,GAAwC,IAAI,CAAC;IACzD,IAAI,CAAC;QACH,QAAQ,GAAG,SAAS,CAAC,qBAAqB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,cAAc;IAChB,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,UAAU,EAAE,gBAAgB,EAAE,CAAC,OAAO,CAAC,CAAC;QACzD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxD,+DAA+D;YAC/D,yDAAyD;YACzD,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;gBAC7B,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACrB,WAAW,GAAG,QAAQ,CAAC;gBACvB,OAAO,KAAK,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;oBAChC,KAAK,CAAC,KAAK,EAAE,CAAC;gBAChB,CAAC;YACH,CAAC;YACD,eAAe,GAAG,CAAC,CAAC;YACpB,mBAAmB,GAAG,CAAC,CAAC;QAC1B,CAAC;aAAM,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,EAAE,CAAC;YACrD,8DAA8D;YAC9D,8DAA8D;YAC9D,0DAA0D;YAC1D,6DAA6D;YAC7D,qBAAqB;YACrB,eAAe,IAAI,CAAC,CAAC;YACrB,IAAI,eAAe,KAAK,CAAC,IAAI,eAAe,KAAK,mBAAmB,EAAE,CAAC;gBACrE,sCAAsC;gBACtC,OAAO,CAAC,IAAI,CACV,wCAAwC,EACxC,QAAQ,KAAK,IAAI;oBACf,CAAC,CAAC,MAAM;oBACR,CAAC,CAAC,OAAO,QAAQ,KAAK,QAAQ;wBAC5B,CAAC,CAAC,iBAAiB,QAAQ,CAAC,MAAM,GAAG;wBACrC,CAAC,CAAC,OAAO,QAAQ,EACrB,wBAAwB,eAAe,GAAG,CAC3C,CAAC;gBACF,mBAAmB,GAAG,IAAI,CAAC,GAAG,CAAC,mBAAmB,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;YAC/D,CAAC;QACH,CAAC;QACD,QAAQ,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,KAAK;YAAE,QAAQ,EAAE,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;QACrE,QAAQ,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;QACtC,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,EAAE,CAAC;YAC9C,sCAAsC;YACtC,OAAO,CAAC,IAAI,CAAC,8BAA8B,EAAE,CAAC,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,WAAW;IAClB,MAAM,CAAC,GAAG,sBAAsB,EAAE,CAAC;IACnC,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,IAAI,CAAC;QACH,OAAO,CAAC,EAAE,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAMD,SAAS,gBAAgB;IACvB,IAAI,CAAC;QACH,iEAAiE;QACjE,MAAM,IAAI,GAAG,OAAO,CAAC,mBAAmB,CAEvC,CAAC;QACF,OAAO,IAAI,CAAC,mBAAmB,CAAqB,SAAS,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;2BAE2B;AAC3B,MAAM,UAAU,WAAW;IACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,KAAK,GAAG,EAAE,CAAC;IACX,WAAW,GAAG,IAAI,CAAC;IACnB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,UAAU,EAAE,CAAC;IACb,KAAK,GAAG,EAAE,CAAC;IACX,WAAW,GAAG,IAAI,CAAC;AACrB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goliapkg/sentori-react-native",
3
- "version": "0.9.11",
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
- (_timer as unknown as { unref?: () => void }).unref?.();
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
- const tickSpan = startSpan('sentori.replay.tick', { name: 'tick' });
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
- _ring.push(snapshot);
111
- while (_ring.length > RING_SIZE) _ring.shift();
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.finish({ status: 'ok' });
197
+ tickSpan?.finish({ status: 'ok' });
136
198
  } catch (e) {
137
- if (e instanceof Error) tickSpan.setTag('error.message', e.message);
138
- tickSpan.finish({ status: 'error' });
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
  }