@goliapkg/sentori-react-native 0.8.5 → 0.9.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/src/native.ts CHANGED
@@ -11,6 +11,41 @@ type SentoriNativeModule = {
11
11
  release: string
12
12
  token: string
13
13
  }) => void
14
+ /**
15
+ * v0.9.4 #1 — cold start measurement. iOS:
16
+ * `mach_absolute_time` from `applicationDidFinishLaunching` to first
17
+ * JS bridge ready. Android: `Process.getStartElapsedRealtime()`.
18
+ * Returns null when native side hasn't captured yet.
19
+ */
20
+ getColdStartMs?: () => null | number
21
+ /**
22
+ * v0.9.4 #1 — call once at JS init() to finalize the cold-start
23
+ * measurement. iOS subtracts from the app-delegate anchor;
24
+ * Android uses Process.getStartElapsedRealtime() so the call is
25
+ * idempotent if missed.
26
+ */
27
+ markJsBridgeReady?: () => void
28
+ /**
29
+ * v0.9.4 #1 — slow/frozen frame counters since the most recent
30
+ * navigation transition. Native side hooks `CADisplayLink` (iOS)
31
+ * / `Choreographer.FrameCallback` (Android). Frame > 16.67ms =
32
+ * slow; > 700ms = frozen.
33
+ */
34
+ getFrameCounters?: () => null | { frozen: number; slow: number }
35
+ /** Reset counters on navigation transition (called by useTraceNavigation). */
36
+ resetFrameCounters?: () => void
37
+ /**
38
+ * v0.9.5 #8 — read the most-recent native exception recorded by
39
+ * `SentoriNativeExceptionBridge` within the last 1 s. Used by the
40
+ * JS-side capture path to attach native stack info to a JSError
41
+ * that RN wrapped from a swallowed NSException / Java Exception.
42
+ */
43
+ getRecentNativeException?: () => null | {
44
+ ageMs: number
45
+ name: string
46
+ reason: string
47
+ stack: string[]
48
+ }
14
49
  /**
15
50
  * v0.7.3 — JS-triggered screenshot with consumer-supplied mask IDs.
16
51
  * `maskedIds` are RN `nativeID` strings; native walks the view
@@ -127,6 +162,59 @@ export function stopAnrWatchdog(): void {
127
162
  }
128
163
  }
129
164
 
165
+ /** v0.9.4 #1 — finalize cold-start measurement. Idempotent. */
166
+ export function markNativeJsBridgeReady(): void {
167
+ try {
168
+ native()?.markJsBridgeReady?.()
169
+ } catch {
170
+ // ignore
171
+ }
172
+ }
173
+
174
+ /** v0.9.4 #1 — read cold start ms once. null when native unavailable. */
175
+ export function getNativeColdStartMs(): null | number {
176
+ try {
177
+ const v = native()?.getColdStartMs?.()
178
+ return typeof v === 'number' && Number.isFinite(v) ? v : null
179
+ } catch {
180
+ return null
181
+ }
182
+ }
183
+
184
+ /** v0.9.4 #1 — read slow/frozen frame counters since last reset. */
185
+ export function getNativeFrameCounters(): null | { frozen: number; slow: number } {
186
+ try {
187
+ return native()?.getFrameCounters?.() ?? null
188
+ } catch {
189
+ return null
190
+ }
191
+ }
192
+
193
+ /** v0.9.4 #1 — reset frame counters on navigation transition. */
194
+ export function resetNativeFrameCounters(): void {
195
+ try {
196
+ native()?.resetFrameCounters?.()
197
+ } catch {
198
+ // ignore
199
+ }
200
+ }
201
+
202
+ /** v0.9.5 #8 — fetch the most-recent native exception from
203
+ * SentoriNativeExceptionBridge (within last ~1 s). null if none or
204
+ * bridge not linked. */
205
+ export function getRecentNativeException(): null | {
206
+ ageMs: number
207
+ name: string
208
+ reason: string
209
+ stack: string[]
210
+ } {
211
+ try {
212
+ return native()?.getRecentNativeException?.() ?? null
213
+ } catch {
214
+ return null
215
+ }
216
+ }
217
+
130
218
  /**
131
219
  * v0.7.3 — drives the native screenshot path. JS side passes the
132
220
  * current list of mask `nativeID`s (read from the consumer's
package/src/navigation.ts CHANGED
@@ -24,6 +24,10 @@ import { useEffect, useRef } from 'react';
24
24
 
25
25
  import { setActiveSpan, startSpan, type SpanHandle } from '@goliapkg/sentori-core';
26
26
 
27
+ import {
28
+ getNativeFrameCounters,
29
+ resetNativeFrameCounters,
30
+ } from './native';
27
31
  import { captureStep } from './trail';
28
32
 
29
33
  /** Minimal contract: anything with `addListener('state', cb)` and
@@ -101,11 +105,21 @@ export function useTraceNavigation(navigationRef: NavigationRefLike): void {
101
105
  const enteredAt = lastRouteEnteredAtRef.current;
102
106
  if (!span) return null;
103
107
  const dwellMs = enteredAt !== null ? Math.max(0, Date.now() - enteredAt) : null;
108
+ // v0.9.4 #1 — drain native frame counters at screen-leave.
109
+ // Empty/null when native module not linked; tags omitted.
110
+ const fc = getNativeFrameCounters();
111
+ const finishTags: Record<string, string> = {};
112
+ if (dwellMs !== null) finishTags['nav.dwell_ms'] = String(dwellMs);
113
+ if (fc) {
114
+ finishTags['vital.slow_frames'] = String(fc.slow);
115
+ finishTags['vital.frozen_frames'] = String(fc.frozen);
116
+ }
104
117
  span.finish({
105
118
  status: 'ok',
106
- // Tag values are strings on the wire — cast at finish-time.
107
- tags: dwellMs !== null ? { 'nav.dwell_ms': String(dwellMs) } : undefined,
119
+ tags: Object.keys(finishTags).length > 0 ? finishTags : undefined,
108
120
  });
121
+ // Reset counters for the next screen.
122
+ resetNativeFrameCounters();
109
123
  return dwellMs;
110
124
  };
111
125