@goliapkg/sentori-react-native 4.0.0 → 5.0.0
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/build.gradle +21 -0
- package/android/src/main/java/com/sentori/SentoriCrashHandler.kt +29 -0
- package/android/src/test/java/com/sentori/SentoriCrashHandlerTest.kt +7 -2
- package/android/src/test/resources/robolectric.properties +10 -0
- package/lib/config.d.ts +16 -47
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +3 -0
- package/lib/config.js.map +1 -1
- package/lib/device.d.ts +5 -0
- package/lib/device.d.ts.map +1 -0
- package/lib/device.js +29 -0
- package/lib/device.js.map +1 -0
- package/lib/emit-hooks.d.ts +7 -0
- package/lib/emit-hooks.d.ts.map +1 -0
- package/lib/emit-hooks.js +24 -0
- package/lib/emit-hooks.js.map +1 -0
- package/lib/error-boundary.d.ts.map +1 -1
- package/lib/error-boundary.js +1 -1
- package/lib/error-boundary.js.map +1 -1
- package/lib/feature-flags.d.ts.map +1 -1
- package/lib/handlers/dev-symbolicate.d.ts +1 -1
- package/lib/handlers/dev-symbolicate.d.ts.map +1 -1
- package/lib/handlers/dev-symbolicate.js +2 -0
- package/lib/handlers/dev-symbolicate.js.map +1 -1
- package/lib/handlers/global.js +1 -1
- package/lib/handlers/global.js.map +1 -1
- package/lib/handlers/lifecycle.js +3 -3
- package/lib/handlers/lifecycle.js.map +1 -1
- package/lib/handlers/network.d.ts +2 -2
- package/lib/handlers/network.d.ts.map +1 -1
- package/lib/handlers/network.js +69 -187
- package/lib/handlers/network.js.map +1 -1
- package/lib/handlers/promise.js +1 -1
- package/lib/handlers/promise.js.map +1 -1
- package/lib/handlers/screenshot.d.ts.map +1 -1
- package/lib/handlers/screenshot.js +3 -12
- package/lib/handlers/screenshot.js.map +1 -1
- package/lib/index.d.ts +31 -109
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +51 -103
- package/lib/index.js.map +1 -1
- package/lib/init.d.ts +3 -150
- package/lib/init.d.ts.map +1 -1
- package/lib/init.js +88 -261
- package/lib/init.js.map +1 -1
- package/lib/long-task-monitor.d.ts +1 -7
- package/lib/long-task-monitor.d.ts.map +1 -1
- package/lib/long-task-monitor.js +37 -58
- package/lib/long-task-monitor.js.map +1 -1
- package/lib/mobile-vitals.d.ts +3 -31
- package/lib/mobile-vitals.d.ts.map +1 -1
- package/lib/mobile-vitals.js +22 -61
- package/lib/mobile-vitals.js.map +1 -1
- package/lib/native-pending.d.ts +3 -0
- package/lib/native-pending.d.ts.map +1 -0
- package/lib/native-pending.js +64 -0
- package/lib/native-pending.js.map +1 -0
- package/lib/navigation.d.ts +3 -17
- package/lib/navigation.d.ts.map +1 -1
- package/lib/navigation.js +32 -133
- package/lib/navigation.js.map +1 -1
- package/lib/netinfo.d.ts +1 -1
- package/lib/netinfo.d.ts.map +1 -1
- package/lib/netinfo.js +2 -2
- package/lib/netinfo.js.map +1 -1
- package/lib/push.d.ts.map +1 -1
- package/lib/push.js +2 -4
- package/lib/push.js.map +1 -1
- package/lib/rage-tap.d.ts.map +1 -1
- package/lib/rage-tap.js +28 -23
- package/lib/rage-tap.js.map +1 -1
- package/lib/replay.d.ts.map +1 -1
- package/lib/replay.js +3 -24
- package/lib/replay.js.map +1 -1
- package/lib/scope.d.ts +7 -0
- package/lib/scope.d.ts.map +1 -0
- package/lib/scope.js +41 -0
- package/lib/scope.js.map +1 -0
- package/lib/transport.d.ts +19 -92
- package/lib/transport.d.ts.map +1 -1
- package/lib/transport.js +75 -288
- package/lib/transport.js.map +1 -1
- package/lib/verbs.d.ts +16 -0
- package/lib/verbs.d.ts.map +1 -0
- package/lib/verbs.js +153 -0
- package/lib/verbs.js.map +1 -0
- package/package.json +3 -3
- package/src/__tests__/iron-rule.test.ts +128 -0
- package/src/__tests__/screenshot.test.ts +0 -2
- package/src/__tests__/transport.test.ts +66 -213
- package/src/__tests__/verbs.test.ts +91 -0
- package/src/config.ts +19 -47
- package/src/device.ts +36 -0
- package/src/emit-hooks.ts +30 -0
- package/src/error-boundary.tsx +1 -1
- package/src/handlers/dev-symbolicate.ts +2 -1
- package/src/handlers/global.ts +1 -1
- package/src/handlers/lifecycle.ts +3 -3
- package/src/handlers/network.ts +95 -211
- package/src/handlers/promise.ts +1 -1
- package/src/handlers/screenshot.ts +3 -11
- package/src/index.ts +81 -215
- package/src/init.ts +94 -446
- package/src/long-task-monitor.ts +42 -64
- package/src/mobile-vitals.ts +22 -82
- package/src/native-pending.ts +92 -0
- package/src/navigation.ts +34 -142
- package/src/netinfo.ts +3 -3
- package/src/push.ts +2 -5
- package/src/rage-tap.tsx +29 -26
- package/src/replay.ts +3 -20
- package/src/scope.ts +50 -0
- package/src/transport.ts +84 -371
- package/src/verbs.ts +198 -0
- package/lib/breadcrumbs.d.ts +0 -18
- package/lib/breadcrumbs.d.ts.map +0 -1
- package/lib/breadcrumbs.js +0 -42
- package/lib/breadcrumbs.js.map +0 -1
- package/lib/bundle-info.d.ts +0 -12
- package/lib/bundle-info.d.ts.map +0 -1
- package/lib/bundle-info.js +0 -73
- package/lib/bundle-info.js.map +0 -1
- package/lib/capture.d.ts +0 -140
- package/lib/capture.d.ts.map +0 -1
- package/lib/capture.js +0 -511
- package/lib/capture.js.map +0 -1
- package/lib/compat/sentry.d.ts +0 -126
- package/lib/compat/sentry.d.ts.map +0 -1
- package/lib/compat/sentry.js +0 -332
- package/lib/compat/sentry.js.map +0 -1
- package/lib/control-channel.d.ts +0 -6
- package/lib/control-channel.d.ts.map +0 -1
- package/lib/control-channel.js +0 -96
- package/lib/control-channel.js.map +0 -1
- package/lib/expo-compat.d.ts +0 -270
- package/lib/expo-compat.d.ts.map +0 -1
- package/lib/expo-compat.js +0 -500
- package/lib/expo-compat.js.map +0 -1
- package/lib/feedback-widget.d.ts +0 -35
- package/lib/feedback-widget.d.ts.map +0 -1
- package/lib/feedback-widget.js +0 -193
- package/lib/feedback-widget.js.map +0 -1
- package/lib/feedback.d.ts +0 -2
- package/lib/feedback.d.ts.map +0 -1
- package/lib/feedback.js +0 -17
- package/lib/feedback.js.map +0 -1
- package/lib/heartbeat.d.ts +0 -9
- package/lib/heartbeat.d.ts.map +0 -1
- package/lib/heartbeat.js +0 -138
- package/lib/heartbeat.js.map +0 -1
- package/lib/index.js.bak +0 -64
- package/lib/install-id.d.ts +0 -17
- package/lib/install-id.d.ts.map +0 -1
- package/lib/install-id.js +0 -125
- package/lib/install-id.js.map +0 -1
- package/lib/lifecycle.d.ts +0 -28
- package/lib/lifecycle.d.ts.map +0 -1
- package/lib/lifecycle.js +0 -73
- package/lib/lifecycle.js.map +0 -1
- package/lib/mask.d.ts +0 -16
- package/lib/mask.d.ts.map +0 -1
- package/lib/mask.js +0 -36
- package/lib/mask.js.map +0 -1
- package/lib/measure.d.ts +0 -4
- package/lib/measure.d.ts.map +0 -1
- package/lib/measure.js +0 -25
- package/lib/measure.js.map +0 -1
- package/lib/metrics.d.ts +0 -24
- package/lib/metrics.d.ts.map +0 -1
- package/lib/metrics.js +0 -81
- package/lib/metrics.js.map +0 -1
- package/lib/pre-crash-sentinel.d.ts +0 -12
- package/lib/pre-crash-sentinel.d.ts.map +0 -1
- package/lib/pre-crash-sentinel.js +0 -116
- package/lib/pre-crash-sentinel.js.map +0 -1
- package/lib/report-security.d.ts +0 -40
- package/lib/report-security.d.ts.map +0 -1
- package/lib/report-security.js +0 -159
- package/lib/report-security.js.map +0 -1
- package/lib/runtime-metrics-fps.d.ts +0 -19
- package/lib/runtime-metrics-fps.d.ts.map +0 -1
- package/lib/runtime-metrics-fps.js +0 -128
- package/lib/runtime-metrics-fps.js.map +0 -1
- package/lib/runtime-metrics-heap.d.ts +0 -13
- package/lib/runtime-metrics-heap.d.ts.map +0 -1
- package/lib/runtime-metrics-heap.js +0 -67
- package/lib/runtime-metrics-heap.js.map +0 -1
- package/lib/runtime-metrics-network.d.ts +0 -23
- package/lib/runtime-metrics-network.d.ts.map +0 -1
- package/lib/runtime-metrics-network.js +0 -99
- package/lib/runtime-metrics-network.js.map +0 -1
- package/lib/runtime-metrics.d.ts +0 -43
- package/lib/runtime-metrics.d.ts.map +0 -1
- package/lib/runtime-metrics.js +0 -115
- package/lib/runtime-metrics.js.map +0 -1
- package/lib/sample-profiler.d.ts +0 -21
- package/lib/sample-profiler.d.ts.map +0 -1
- package/lib/sample-profiler.js +0 -152
- package/lib/sample-profiler.js.map +0 -1
- package/lib/sentinel-context.d.ts +0 -4
- package/lib/sentinel-context.d.ts.map +0 -1
- package/lib/sentinel-context.js +0 -52
- package/lib/sentinel-context.js.map +0 -1
- package/lib/session-tracker.d.ts +0 -6
- package/lib/session-tracker.d.ts.map +0 -1
- package/lib/session-tracker.js +0 -50
- package/lib/session-tracker.js.map +0 -1
- package/lib/stack.d.ts +0 -3
- package/lib/stack.d.ts.map +0 -1
- package/lib/stack.js +0 -7
- package/lib/stack.js.map +0 -1
- package/lib/state-snapshots.d.ts +0 -33
- package/lib/state-snapshots.d.ts.map +0 -1
- package/lib/state-snapshots.js +0 -109
- package/lib/state-snapshots.js.map +0 -1
- package/lib/track.d.ts +0 -34
- package/lib/track.d.ts.map +0 -1
- package/lib/track.js +0 -107
- package/lib/track.js.map +0 -1
- package/lib/trail.d.ts +0 -12
- package/lib/trail.d.ts.map +0 -1
- package/lib/trail.js +0 -28
- package/lib/trail.js.map +0 -1
- package/lib/trust-score.d.ts +0 -20
- package/lib/trust-score.d.ts.map +0 -1
- package/lib/trust-score.js +0 -151
- package/lib/trust-score.js.map +0 -1
- package/lib/types.d.ts +0 -2
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js +0 -4
- package/lib/types.js.map +0 -1
- package/lib/uuid.d.ts +0 -2
- package/lib/uuid.d.ts.map +0 -1
- package/lib/uuid.js +0 -3
- package/lib/uuid.js.map +0 -1
- package/src/__tests__/before-send.test.ts +0 -72
- package/src/__tests__/breadcrumbs.test.ts +0 -44
- package/src/__tests__/capture-replay.test.ts +0 -150
- package/src/__tests__/compat-sentry.test.ts +0 -121
- package/src/__tests__/control-channel.test.ts +0 -111
- package/src/__tests__/install-id.test.ts +0 -60
- package/src/__tests__/long-task-monitor.test.ts +0 -59
- package/src/__tests__/measure.test.ts +0 -45
- package/src/__tests__/navigation.test.ts +0 -175
- package/src/__tests__/push.test.ts +0 -238
- package/src/__tests__/report-security.test.ts +0 -106
- package/src/__tests__/runtime-metrics-instruments.test.ts +0 -109
- package/src/__tests__/runtime-metrics-network.test.ts +0 -78
- package/src/__tests__/sample-profiler.test.ts +0 -59
- package/src/__tests__/stack.test.ts +0 -45
- package/src/__tests__/state-snapshots.test.ts +0 -119
- package/src/__tests__/tracing.test.ts +0 -201
- package/src/__tests__/track.test.ts +0 -91
- package/src/__tests__/uuid.test.ts +0 -41
- package/src/breadcrumbs.ts +0 -57
- package/src/bundle-info.ts +0 -95
- package/src/capture.ts +0 -660
- package/src/compat/sentry.ts +0 -490
- package/src/control-channel.ts +0 -98
- package/src/expo-compat.ts +0 -698
- package/src/feedback-widget.tsx +0 -316
- package/src/feedback.ts +0 -21
- package/src/heartbeat.ts +0 -157
- package/src/install-id.ts +0 -146
- package/src/lifecycle.ts +0 -76
- package/src/mask.ts +0 -41
- package/src/measure.ts +0 -28
- package/src/metrics.ts +0 -94
- package/src/pre-crash-sentinel.ts +0 -140
- package/src/report-security.ts +0 -165
- package/src/runtime-metrics-fps.ts +0 -137
- package/src/runtime-metrics-heap.ts +0 -78
- package/src/runtime-metrics-network.ts +0 -103
- package/src/runtime-metrics.ts +0 -122
- package/src/sample-profiler.ts +0 -169
- package/src/sentinel-context.ts +0 -60
- package/src/session-tracker.ts +0 -53
- package/src/stack.ts +0 -10
- package/src/state-snapshots.ts +0 -133
- package/src/track.ts +0 -123
- package/src/trail.ts +0 -32
- package/src/trust-score.ts +0 -176
- package/src/types.ts +0 -21
- package/src/uuid.ts +0 -2
package/lib/init.js
CHANGED
|
@@ -1,279 +1,106 @@
|
|
|
1
|
-
|
|
1
|
+
// sentori.init(config) — the single configuration entry point
|
|
2
|
+
// (design.md §4). Synchronous, never throws; a bad config degrades
|
|
3
|
+
// every verb to a no-op with one console.warn, never a crash
|
|
4
|
+
// (failure-isolation iron rule).
|
|
5
|
+
import { safeFn, setLogLevel } from '@goliapkg/sentori-core';
|
|
2
6
|
import { setConfig } from './config';
|
|
7
|
+
import { registerEmitHook } from './emit-hooks';
|
|
3
8
|
import { installGlobalHandler } from './handlers/global';
|
|
4
9
|
import { installLifecycleHandler } from './handlers/lifecycle';
|
|
5
|
-
import { installPromiseHandler } from './handlers/promise';
|
|
6
10
|
import { installNetworkHandler } from './handlers/network';
|
|
7
|
-
import {
|
|
8
|
-
import { markLaunchCompleted, runLaunchCrashGuard, } from './launch-crash-guard';
|
|
9
|
-
import { getInstallId } from './install-id';
|
|
10
|
-
import { startMetricsTimer } from './metrics';
|
|
11
|
-
import { emitColdStart, markColdStartT0, startRuntimeMetricsTimer, } from './runtime-metrics';
|
|
12
|
-
import { startFpsInstrument } from './runtime-metrics-fps';
|
|
13
|
-
import { startHeapInstrument } from './runtime-metrics-heap';
|
|
14
|
-
import { startNetworkBytesInstrument } from './runtime-metrics-network';
|
|
15
|
-
import { startTrackTimer } from './track';
|
|
16
|
-
import { drainNativePending, markNativeJsBridgeReady, setNativeConfig } from './native';
|
|
17
|
-
import { getColdStartMs } from './mobile-vitals';
|
|
18
|
-
import { startHeartbeat } from './heartbeat';
|
|
19
|
-
import { startSpan } from '@goliapkg/sentori-core';
|
|
20
|
-
import { startControlChannel } from './control-channel';
|
|
11
|
+
import { installPromiseHandler } from './handlers/promise';
|
|
21
12
|
import { startLongTaskMonitor } from './long-task-monitor';
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
13
|
+
import { checkColdStart } from './mobile-vitals';
|
|
14
|
+
import { markNativeJsBridgeReady, setNativeConfig } from './native';
|
|
15
|
+
import { shipNativePending } from './native-pending';
|
|
16
|
+
import { drainReplay, startReplay } from './replay';
|
|
17
|
+
import { drainOfflineQueue, startTransport, uploadAttachment } from './transport';
|
|
18
|
+
let _initialized = false;
|
|
19
|
+
export const init = safeFn('init', (config) => {
|
|
20
|
+
if (_initialized)
|
|
21
|
+
return;
|
|
22
|
+
if (!config || typeof config.token !== 'string' || config.token.length === 0) {
|
|
23
|
+
// eslint-disable-next-line no-console
|
|
24
|
+
console.warn('[sentori] init skipped: token missing — SDK is a no-op');
|
|
25
|
+
return;
|
|
35
26
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const env = options.environment ??
|
|
41
|
-
(typeof __DEV__ !== 'undefined' && __DEV__ ? 'dev' : 'prod');
|
|
42
|
-
// v0.9.0 #3 — launch-crash guard. Fires *before* any other setup so
|
|
43
|
-
// a known-bad bundle can roll back instead of running JS that's
|
|
44
|
-
// about to die again. AsyncStorage-backed; if the host doesn't have
|
|
45
|
-
// it the guard is a no-op.
|
|
46
|
-
const lcg = options.capture?.launchCrashGuard;
|
|
47
|
-
if (lcg?.enabled) {
|
|
48
|
-
void runLaunchCrashGuard(lcg, options.release, getBundleInfo()?.id ?? null);
|
|
27
|
+
if (typeof config.ingestUrl !== 'string' || config.ingestUrl.length === 0) {
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.warn('[sentori] init skipped: ingestUrl missing — SDK is a no-op');
|
|
30
|
+
return;
|
|
49
31
|
}
|
|
32
|
+
_initialized = true;
|
|
50
33
|
setConfig({
|
|
51
|
-
token:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
34
|
+
token: config.token,
|
|
35
|
+
ingestUrl: config.ingestUrl.replace(/\/+$/, ''),
|
|
36
|
+
release: config.release ?? '',
|
|
37
|
+
environment: config.environment ?? 'production',
|
|
55
38
|
enabled: true,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
// `type: 'track'` breadcrumb so a subsequent captureException
|
|
65
|
-
// carries the customer journey. Defaults false to preserve v1
|
|
66
|
-
// breadcrumb shape on upgrade.
|
|
67
|
-
trackAutoBreadcrumb: options.capture?.trackAutoBreadcrumb === true,
|
|
68
|
-
// v2.3 — host-side beforeSend hook (sync). Stored on Config so
|
|
69
|
-
// capture.ts can pull it without re-resolving the InitOptions.
|
|
70
|
-
beforeSend: options.beforeSend,
|
|
39
|
+
detect: {
|
|
40
|
+
rageTap: config.detect?.rageTap ?? true,
|
|
41
|
+
longFreeze: config.detect?.longFreeze ?? true,
|
|
42
|
+
slowColdStart: config.detect?.slowColdStart ?? true,
|
|
43
|
+
slowApi: config.detect?.slowApi ?? false,
|
|
44
|
+
},
|
|
45
|
+
replaySeconds: config.replaySeconds ?? 30,
|
|
46
|
+
beforeSend: config.beforeSend,
|
|
71
47
|
});
|
|
72
|
-
|
|
73
|
-
//
|
|
48
|
+
setLogLevel(config.logLevel ?? 'warn');
|
|
49
|
+
// JS-side error capture + the signal-ring feeders.
|
|
50
|
+
installGlobalHandler();
|
|
51
|
+
installPromiseHandler();
|
|
52
|
+
installNetworkHandler();
|
|
53
|
+
installLifecycleHandler();
|
|
54
|
+
// Warn-scenario detectors (design.md §3 minimum set). rage_tap
|
|
55
|
+
// rides the RageTapCapture component; the rest start here.
|
|
56
|
+
if (config.detect?.longFreeze !== false)
|
|
57
|
+
startLongTaskMonitor();
|
|
58
|
+
// B-type replay: a rolling in-memory wireframe ring; an error/warn
|
|
59
|
+
// going out drains it into a replay attachment on that event.
|
|
60
|
+
const replaySeconds = config.replaySeconds ?? 30;
|
|
61
|
+
if (replaySeconds > 0) {
|
|
62
|
+
startReplay({ mode: 'wireframe' });
|
|
63
|
+
registerEmitHook((event) => {
|
|
64
|
+
if (event.kind !== 'error' && event.kind !== 'warn')
|
|
65
|
+
return;
|
|
66
|
+
if (!event.id)
|
|
67
|
+
return;
|
|
68
|
+
const lines = drainReplay();
|
|
69
|
+
if (!lines)
|
|
70
|
+
return;
|
|
71
|
+
const base64 = base64Encode(lines);
|
|
72
|
+
if (!base64)
|
|
73
|
+
return;
|
|
74
|
+
void uploadAttachment(event.id, 'replay', { base64, mediaType: 'application/x-sentori-replay' }, { source: 'js' });
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// Native side: hand over release/environment for the crash-file
|
|
78
|
+
// writer, mark the bridge live, then drain crashes from previous
|
|
79
|
+
// launches. All fire-and-forget — init stays synchronous and fast
|
|
80
|
+
// (< 50 ms budget; the work below happens off the critical path).
|
|
74
81
|
setNativeConfig({
|
|
75
|
-
|
|
76
|
-
release:
|
|
77
|
-
|
|
82
|
+
environment: config.environment ?? 'production',
|
|
83
|
+
release: config.release ?? '',
|
|
84
|
+
token: config.token,
|
|
78
85
|
});
|
|
79
|
-
// v0.9.4 #1 — finalize cold-start measurement. iOS uses the
|
|
80
|
-
// delta from `applicationDidFinishLaunching` to this call;
|
|
81
|
-
// Android uses Process.getStartElapsedRealtime() so the value is
|
|
82
|
-
// computed at this point and cached.
|
|
83
86
|
markNativeJsBridgeReady();
|
|
84
|
-
// Emit a one-off cold-start span. Server aggregates these per
|
|
85
|
-
// release for the Mobile Vitals dashboard. No-op when native
|
|
86
|
-
// module isn't linked.
|
|
87
|
-
const coldMs = getColdStartMs();
|
|
88
|
-
if (coldMs !== null && coldMs > 0 && coldMs < 60_000) {
|
|
89
|
-
const span = startSpan('sentori.cold_start', {
|
|
90
|
-
name: 'cold-start',
|
|
91
|
-
parent: null,
|
|
92
|
-
startNowMs: Date.now() - coldMs,
|
|
93
|
-
tags: { 'vital.kind': 'cold_start' },
|
|
94
|
-
});
|
|
95
|
-
span.finish({ status: 'ok' });
|
|
96
|
-
}
|
|
97
87
|
startTransport();
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// v2.1 W2 — runtime metrics auto-instrument. Defaults on; host
|
|
109
|
-
// can opt out with `capture: { runtimeMetrics: false }`. The
|
|
110
|
-
// ring + emit live in `@goliapkg/sentori-core`; we own the
|
|
111
|
-
// 30 s flusher + the cold-start one-shot here. FPS / heap /
|
|
112
|
-
// route-nav / network instruments ship in W2 part 3 (each
|
|
113
|
-
// gated on its own per-tick perf budget CI test).
|
|
114
|
-
if (options.capture?.runtimeMetrics !== false) {
|
|
115
|
-
markColdStartT0();
|
|
116
|
-
startRuntimeMetricsTimer();
|
|
117
|
-
// Defer the emit one tick so React's first paint settles
|
|
118
|
-
// before we stamp "cold-start ended". 0-delay setTimeout puts
|
|
119
|
-
// the call after the current microtask queue drains.
|
|
120
|
-
setTimeout(emitColdStart, 0);
|
|
121
|
-
// FPS via rAF (per-tick budget < 0.5 ms — see
|
|
122
|
-
// runtime-metrics-fps.ts header). Heap is a 30 s polling
|
|
123
|
-
// tick; no-op when performance.memory isn't exposed (most
|
|
124
|
-
// RN engines today; we ship the wiring anyway so the same
|
|
125
|
-
// SDK works on web targets via @goliapkg/sentori-javascript).
|
|
126
|
-
startFpsInstrument();
|
|
127
|
-
startHeapInstrument();
|
|
128
|
-
// Network bytes counters drain every 30 s. The counters
|
|
129
|
-
// themselves are incremented inline by handlers/network.ts
|
|
130
|
-
// on every fetch round-trip — see the recordNetworkBytes
|
|
131
|
-
// call site there. No-op when `capture.network: false` is
|
|
132
|
-
// set (no fetch patch → no counter increments).
|
|
133
|
-
startNetworkBytesInstrument();
|
|
134
|
-
// Route-nav dwell timing emits inline from `navigation.ts`'s
|
|
135
|
-
// useTraceNavigation state listener — no extra start call
|
|
136
|
-
// needed; the host already mounts that hook for tracing.
|
|
137
|
-
}
|
|
138
|
-
// v1.1 chunk S1 — warm the install-id cache. Fire-and-forget;
|
|
139
|
-
// any event captured before the first resolve simply omits
|
|
140
|
-
// `device.installId`. Subsequent captures pick it up via the
|
|
141
|
-
// sync `peekInstallId()` read in collectDevice().
|
|
142
|
-
void getInstallId();
|
|
143
|
-
// v0.9.1 +S4 — pre-crash sentinel. Off by default; opt-in via
|
|
144
|
-
// `capture.preCrashSentinel: true`.
|
|
145
|
-
if (options.capture?.preCrashSentinel === true) {
|
|
146
|
-
startPreCrashSentinel({
|
|
147
|
-
enabled: true,
|
|
148
|
-
channels: options.capture.sentinelChannels,
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
// v0.9.6 #4 — long-task monitor. Off by default.
|
|
152
|
-
const lt = options.capture?.longTaskMonitor;
|
|
153
|
-
if (lt) {
|
|
154
|
-
startLongTaskMonitor({
|
|
155
|
-
enabled: true,
|
|
156
|
-
thresholdMs: typeof lt === 'object' ? lt.thresholdMs : undefined,
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
// v0.9.6 #2 — wireframe replay. Off by default.
|
|
160
|
-
const rp = options.capture?.replay;
|
|
161
|
-
if (rp === 'wireframe') {
|
|
162
|
-
startReplay({ mode: 'wireframe' });
|
|
163
|
-
}
|
|
164
|
-
else if (rp && typeof rp === 'object' && rp.mode === 'wireframe') {
|
|
165
|
-
startReplay({ hz: rp.hz, mode: 'wireframe' });
|
|
166
|
-
}
|
|
167
|
-
// v1.1 #4 升级 — JS sample profiler. Off by default.
|
|
168
|
-
const sp = options.capture?.sampleProfiler;
|
|
169
|
-
if (sp) {
|
|
170
|
-
startSampleProfiler({
|
|
171
|
-
enabled: true,
|
|
172
|
-
flushMs: typeof sp === 'object' ? sp.flushMs : undefined,
|
|
173
|
-
sampleMs: typeof sp === 'object' ? sp.sampleMs : undefined,
|
|
174
|
-
});
|
|
88
|
+
checkColdStart(config.detect?.slowColdStart !== false);
|
|
89
|
+
void shipNativePending().catch(() => undefined);
|
|
90
|
+
void drainOfflineQueue();
|
|
91
|
+
});
|
|
92
|
+
/** RN's Hermes has no btoa in older releases; go through base64.ts. */
|
|
93
|
+
const base64Encode = (text) => {
|
|
94
|
+
try {
|
|
95
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
96
|
+
const { encodeBase64 } = require('./base64');
|
|
97
|
+
return encodeBase64(text);
|
|
175
98
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
installGlobalHandler();
|
|
179
|
-
if (capture.promiseRejections !== false)
|
|
180
|
-
installPromiseHandler();
|
|
181
|
-
if (capture.network !== false) {
|
|
182
|
-
const netOpts = typeof capture.network === 'object' ? capture.network : undefined;
|
|
183
|
-
installNetworkHandler({ graphql: netOpts?.graphql });
|
|
184
|
-
}
|
|
185
|
-
if (capture.sessions !== false) {
|
|
186
|
-
// Open the cold-start session now (RN doesn't fire an AppState
|
|
187
|
-
// `change` for the initial `active` state), then bind AppState so
|
|
188
|
-
// background ends it and the next foreground opens a fresh one.
|
|
189
|
-
startSession();
|
|
190
|
-
installLifecycleHandler();
|
|
191
|
-
}
|
|
192
|
-
// Analytics v1 — live-presence heartbeat. Foreground 1/min by
|
|
193
|
-
// default; pass `capture.heartbeat = false` to opt out, or
|
|
194
|
-
// `{ intervalMs: 30_000 }` to override. The default budget is well
|
|
195
|
-
// under the "几乎不能造成性能抖动" rule (CLAUDE.md).
|
|
196
|
-
const heartbeatOpt = capture.heartbeat;
|
|
197
|
-
if (heartbeatOpt !== false) {
|
|
198
|
-
startHeartbeat(typeof heartbeatOpt === 'object' ? heartbeatOpt : {});
|
|
199
|
-
}
|
|
200
|
-
// Drain events persisted from previous session (best-effort):
|
|
201
|
-
// - native crashes from <Documents>/sentori/pending/*.json
|
|
202
|
-
// - JS transport offline queue from AsyncStorage
|
|
203
|
-
drainNativePending()
|
|
204
|
-
.then(async (items) => {
|
|
205
|
-
for (const json of items) {
|
|
206
|
-
try {
|
|
207
|
-
const event = JSON.parse(json);
|
|
208
|
-
// Phase 42 sub-E.05 / F.09: the native crash handler couldn't
|
|
209
|
-
// upload attachments at crash time (the app was dying); it
|
|
210
|
-
// base64-encoded them into `_pendingAttachments` instead.
|
|
211
|
-
// On next launch we upload each before enqueueing the event,
|
|
212
|
-
// so the dashboard sees the refs in `event.attachments[]`.
|
|
213
|
-
if (event._pendingAttachments && event._pendingAttachments.length > 0) {
|
|
214
|
-
for (const p of event._pendingAttachments) {
|
|
215
|
-
const meta = await uploadAttachment(event.id, p.kind, { base64: p.base64, mediaType: p.mediaType }, { source: p.source });
|
|
216
|
-
if (meta) {
|
|
217
|
-
if (!event.attachments)
|
|
218
|
-
event.attachments = [];
|
|
219
|
-
event.attachments.push(meta);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
delete event._pendingAttachments;
|
|
223
|
-
}
|
|
224
|
-
enqueue(event);
|
|
225
|
-
}
|
|
226
|
-
catch {
|
|
227
|
-
// skip malformed
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
})
|
|
231
|
-
.catch(() => { });
|
|
232
|
-
drainOfflineQueue().catch(() => { });
|
|
233
|
-
// v0.9.0 #3 — init reached the end without throwing. Schedule the
|
|
234
|
-
// "launch completed" marker after one tick so any synchronous user
|
|
235
|
-
// code right after `init()` gets to run first; we want the marker to
|
|
236
|
-
// confirm the JS bridge stayed alive, not just that `init()` returned.
|
|
237
|
-
if (lcg?.enabled) {
|
|
238
|
-
setTimeout(() => {
|
|
239
|
-
void markLaunchCompleted(getBundleInfo()?.id ?? null);
|
|
240
|
-
}, 2_000);
|
|
241
|
-
}
|
|
242
|
-
// v2.3 — onReady callback. Fires after setConfig + native bind +
|
|
243
|
-
// transport start are all settled. The drain-pending work is
|
|
244
|
-
// still in flight (it's async) but the SDK is ready to accept
|
|
245
|
-
// new captures. Host wires this to know the SDK is live without
|
|
246
|
-
// scanning the console. Wrapped in try/catch — host callback
|
|
247
|
-
// throwing must not propagate (NEVER rule).
|
|
248
|
-
if (options.onReady) {
|
|
249
|
-
const nativeMod = (() => {
|
|
250
|
-
try {
|
|
251
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
252
|
-
const { native } = require('./native');
|
|
253
|
-
const n = native?.();
|
|
254
|
-
return n ?? null;
|
|
255
|
-
}
|
|
256
|
-
catch {
|
|
257
|
-
return null;
|
|
258
|
-
}
|
|
259
|
-
})();
|
|
260
|
-
const info = {
|
|
261
|
-
sdkVersion: SDK_VERSION,
|
|
262
|
-
coldStartMs: coldMs ?? undefined,
|
|
263
|
-
native: {
|
|
264
|
-
bound: !!nativeMod,
|
|
265
|
-
methods: nativeMod ? Object.keys(nativeMod).sort() : [],
|
|
266
|
-
},
|
|
267
|
-
};
|
|
268
|
-
try {
|
|
269
|
-
options.onReady(info);
|
|
270
|
-
}
|
|
271
|
-
catch {
|
|
272
|
-
// Host's onReady threw. NEVER rule — swallow.
|
|
273
|
-
}
|
|
99
|
+
catch {
|
|
100
|
+
return null;
|
|
274
101
|
}
|
|
275
102
|
};
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
103
|
+
export const __resetForTests = () => {
|
|
104
|
+
_initialized = false;
|
|
105
|
+
};
|
|
279
106
|
//# sourceMappingURL=init.js.map
|
package/lib/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
export
|
|
2
|
-
enabled: boolean;
|
|
3
|
-
/** Threshold ms above which a tick lag becomes a longtask span.
|
|
4
|
-
* Default 200ms. Lower → noisier. */
|
|
5
|
-
thresholdMs?: number;
|
|
6
|
-
};
|
|
7
|
-
export declare function startLongTaskMonitor(opts: LongTaskMonitorOptions): void;
|
|
1
|
+
export declare function startLongTaskMonitor(): void;
|
|
8
2
|
export declare function stopLongTaskMonitor(): void;
|
|
9
3
|
/** Test-only. */
|
|
10
4
|
export declare function __resetLongTaskMonitorForTests(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"long-task-monitor.d.ts","sourceRoot":"","sources":["../src/long-task-monitor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"long-task-monitor.d.ts","sourceRoot":"","sources":["../src/long-task-monitor.ts"],"names":[],"mappings":"AA8BA,wBAAgB,oBAAoB,IAAI,IAAI,CAO3C;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C;AA0BD,iBAAiB;AACjB,wBAAgB,8BAA8B,IAAI,IAAI,CAKrD"}
|
package/lib/long-task-monitor.js
CHANGED
|
@@ -1,48 +1,35 @@
|
|
|
1
|
-
//
|
|
1
|
+
// long_freeze — detected warn scenario (design.md §3, category B:
|
|
2
|
+
// 「app 卡住了 / 死了几秒」), JS-thread side.
|
|
2
3
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
4
|
+
// Mini-spec: a setInterval(50 ms) tick measures wall-clock drift.
|
|
5
|
+
// Drift ≤ 2 s ⇒ a `freeze` signal in the ring (context only —
|
|
6
|
+
// brief jank is not worth an event). Drift > 2 s ⇒ one `warn`
|
|
7
|
+
// event, scenario `long_freeze`, surface = current screen, with the
|
|
8
|
+
// blocked duration. Rate-limited to 6 events/minute so a freeze
|
|
9
|
+
// storm cannot flood ingest.
|
|
9
10
|
//
|
|
10
|
-
// What it
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// that vary per RN minor. We could swizzle / vendor headers but
|
|
18
|
-
// that's an Insight-build-config burden. The long-task monitor
|
|
19
|
-
// gets ~80% of the practical value (find slow renders, expensive
|
|
20
|
-
// reducers, accidental sync work in render path) with zero native
|
|
21
|
-
// code + zero RN-version sensitivity.
|
|
22
|
-
//
|
|
23
|
-
// Pairs naturally with +S4 (pre-crash sentinel, RAF frame budget,
|
|
24
|
-
// fires at the slow-frame threshold) — long-task monitor fires
|
|
25
|
-
// further down the slowness scale.
|
|
26
|
-
import { startSpan } from '@goliapkg/sentori-core';
|
|
11
|
+
// What it cannot do: capture the stack DURING the block — JS is
|
|
12
|
+
// single-threaded; by the time our tick runs the busy code is gone.
|
|
13
|
+
// The native watchdogs (HangWatchdog / AnrWatchdog) cover the
|
|
14
|
+
// UI-thread side of the same scenario.
|
|
15
|
+
import { pushSignal } from '@goliapkg/sentori-core';
|
|
16
|
+
import { currentScreen } from './navigation';
|
|
17
|
+
import { warnDetected } from './verbs';
|
|
27
18
|
const TICK_INTERVAL_MS = 50;
|
|
28
|
-
const
|
|
29
|
-
const
|
|
19
|
+
const SIGNAL_THRESHOLD_MS = 200; // ring entry: noticeable jank
|
|
20
|
+
const WARN_THRESHOLD_MS = 2_000; // warn event: a real freeze
|
|
21
|
+
const MAX_WARNS_PER_MIN = 6;
|
|
30
22
|
let _timer = null;
|
|
31
23
|
let _lastTick = 0;
|
|
32
|
-
let
|
|
33
|
-
let
|
|
34
|
-
export function startLongTaskMonitor(
|
|
24
|
+
let _warnWindowStart = 0;
|
|
25
|
+
let _warnsThisWindow = 0;
|
|
26
|
+
export function startLongTaskMonitor() {
|
|
35
27
|
if (_timer !== null)
|
|
36
28
|
return;
|
|
37
|
-
if (!opts.enabled)
|
|
38
|
-
return;
|
|
39
|
-
const threshold = opts.thresholdMs ?? LONGTASK_THRESHOLD_MS;
|
|
40
29
|
_lastTick = Date.now();
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
_timer = setInterval(
|
|
44
|
-
tick(threshold);
|
|
45
|
-
}, TICK_INTERVAL_MS);
|
|
30
|
+
_warnWindowStart = _lastTick;
|
|
31
|
+
_warnsThisWindow = 0;
|
|
32
|
+
_timer = setInterval(tick, TICK_INTERVAL_MS);
|
|
46
33
|
_timer.unref?.();
|
|
47
34
|
}
|
|
48
35
|
export function stopLongTaskMonitor() {
|
|
@@ -51,38 +38,30 @@ export function stopLongTaskMonitor() {
|
|
|
51
38
|
_timer = null;
|
|
52
39
|
}
|
|
53
40
|
}
|
|
54
|
-
function tick(
|
|
41
|
+
function tick() {
|
|
55
42
|
const now = Date.now();
|
|
56
43
|
const elapsed = now - _lastTick;
|
|
57
44
|
_lastTick = now;
|
|
58
45
|
const lag = elapsed - TICK_INTERVAL_MS;
|
|
59
|
-
if (lag <=
|
|
46
|
+
if (lag <= SIGNAL_THRESHOLD_MS)
|
|
47
|
+
return;
|
|
48
|
+
pushSignal('freeze', { blockedMs: Math.round(lag) });
|
|
49
|
+
if (lag <= WARN_THRESHOLD_MS)
|
|
60
50
|
return;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
_emitWindowStart = now;
|
|
65
|
-
_emitsThisWindow = 0;
|
|
51
|
+
if (now - _warnWindowStart >= 60_000) {
|
|
52
|
+
_warnWindowStart = now;
|
|
53
|
+
_warnsThisWindow = 0;
|
|
66
54
|
}
|
|
67
|
-
if (
|
|
55
|
+
if (_warnsThisWindow >= MAX_WARNS_PER_MIN)
|
|
68
56
|
return;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
name: 'js.longtask',
|
|
72
|
-
startNowMs: now - lag,
|
|
73
|
-
tags: {
|
|
74
|
-
'profile.kind': 'longtask',
|
|
75
|
-
'profile.duration_ms': String(Math.round(lag)),
|
|
76
|
-
'profile.tick_interval_ms': String(TICK_INTERVAL_MS),
|
|
77
|
-
},
|
|
78
|
-
});
|
|
79
|
-
span.finish({ endNowMs: now, status: 'ok' });
|
|
57
|
+
_warnsThisWindow += 1;
|
|
58
|
+
warnDetected('long_freeze', { screen: currentScreen() }, { blockedMs: Math.round(lag), thread: 'js' });
|
|
80
59
|
}
|
|
81
60
|
/** Test-only. */
|
|
82
61
|
export function __resetLongTaskMonitorForTests() {
|
|
83
62
|
stopLongTaskMonitor();
|
|
84
63
|
_lastTick = 0;
|
|
85
|
-
|
|
86
|
-
|
|
64
|
+
_warnWindowStart = 0;
|
|
65
|
+
_warnsThisWindow = 0;
|
|
87
66
|
}
|
|
88
67
|
//# sourceMappingURL=long-task-monitor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"long-task-monitor.js","sourceRoot":"","sources":["../src/long-task-monitor.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"long-task-monitor.js","sourceRoot":"","sources":["../src/long-task-monitor.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,qCAAqC;AACrC,EAAE;AACF,kEAAkE;AAClE,8DAA8D;AAC9D,8DAA8D;AAC9D,oEAAoE;AACpE,gEAAgE;AAChE,6BAA6B;AAC7B,EAAE;AACF,gEAAgE;AAChE,oEAAoE;AACpE,8DAA8D;AAC9D,uCAAuC;AAEvC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,MAAM,mBAAmB,GAAG,GAAG,CAAC,CAAC,8BAA8B;AAC/D,MAAM,iBAAiB,GAAG,KAAK,CAAC,CAAC,4BAA4B;AAC7D,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAE5B,IAAI,MAAM,GAA0C,IAAI,CAAC;AACzD,IAAI,SAAS,GAAG,CAAC,CAAC;AAClB,IAAI,gBAAgB,GAAG,CAAC,CAAC;AACzB,IAAI,gBAAgB,GAAG,CAAC,CAAC;AAEzB,MAAM,UAAU,oBAAoB;IAClC,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO;IAC5B,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,gBAAgB,GAAG,SAAS,CAAC;IAC7B,gBAAgB,GAAG,CAAC,CAAC;IACrB,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IAC5C,MAA4C,CAAC,KAAK,EAAE,EAAE,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,aAAa,CAAC,MAAM,CAAC,CAAC;QACtB,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED,SAAS,IAAI;IACX,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,OAAO,GAAG,GAAG,GAAG,SAAS,CAAC;IAChC,SAAS,GAAG,GAAG,CAAC;IAChB,MAAM,GAAG,GAAG,OAAO,GAAG,gBAAgB,CAAC;IACvC,IAAI,GAAG,IAAI,mBAAmB;QAAE,OAAO;IAEvC,UAAU,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACrD,IAAI,GAAG,IAAI,iBAAiB;QAAE,OAAO;IAErC,IAAI,GAAG,GAAG,gBAAgB,IAAI,MAAM,EAAE,CAAC;QACrC,gBAAgB,GAAG,GAAG,CAAC;QACvB,gBAAgB,GAAG,CAAC,CAAC;IACvB,CAAC;IACD,IAAI,gBAAgB,IAAI,iBAAiB;QAAE,OAAO;IAClD,gBAAgB,IAAI,CAAC,CAAC;IAEtB,YAAY,CACV,aAAa,EACb,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE,EAC3B,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAC7C,CAAC;AACJ,CAAC;AAED,iBAAiB;AACjB,MAAM,UAAU,8BAA8B;IAC5C,mBAAmB,EAAE,CAAC;IACtB,SAAS,GAAG,CAAC,CAAC;IACd,gBAAgB,GAAG,CAAC,CAAC;IACrB,gBAAgB,GAAG,CAAC,CAAC;AACvB,CAAC"}
|
package/lib/mobile-vitals.d.ts
CHANGED
|
@@ -1,35 +1,7 @@
|
|
|
1
|
-
/** Read the native-side cold start measurement once. Cached.
|
|
2
|
-
* null when the native module isn't linked (Expo Go / tests). */
|
|
1
|
+
/** Read the native-side cold start measurement once. Cached. */
|
|
3
2
|
export declare function getColdStartMs(): null | number;
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
* point the screen is functionally "ready" (data fetched, images
|
|
7
|
-
* loaded, etc.) so the dashboard can show real perceived load time
|
|
8
|
-
* vs auto-detected TTID.
|
|
9
|
-
*
|
|
10
|
-
* const h = sentori.markTimeToFullDisplay('Home');
|
|
11
|
-
* // ...data fetched, images rendered...
|
|
12
|
-
* h.end();
|
|
13
|
-
*
|
|
14
|
-
* If `.end()` is never called, the handle's span is finished as
|
|
15
|
-
* `cancelled` at the next `markTimeToFullDisplay` call (one TTFD in
|
|
16
|
-
* flight at a time is the typical case).
|
|
17
|
-
*/
|
|
18
|
-
export type TimeToFullDisplayHandle = {
|
|
19
|
-
end: (opts?: {
|
|
20
|
-
status?: 'cancelled' | 'error' | 'ok';
|
|
21
|
-
}) => void;
|
|
22
|
-
cancel: () => void;
|
|
23
|
-
};
|
|
24
|
-
export declare function markTimeToFullDisplay(route: string): TimeToFullDisplayHandle;
|
|
25
|
-
/** v0.9.4 #1 — read the per-screen slow/frozen frame counts since
|
|
26
|
-
* the most recent navigation transition. Native module reads
|
|
27
|
-
* CADisplayLink / Choreographer counters; returns null when not
|
|
28
|
-
* linked. */
|
|
29
|
-
export declare function getFrameCounters(): null | {
|
|
30
|
-
slow: number;
|
|
31
|
-
frozen: number;
|
|
32
|
-
};
|
|
3
|
+
/** Called once from init(): signal always, warn when slow. */
|
|
4
|
+
export declare function checkColdStart(detectEnabled: boolean): void;
|
|
33
5
|
/** Test-only. */
|
|
34
6
|
export declare function __resetMobileVitalsForTests(): void;
|
|
35
7
|
//# sourceMappingURL=mobile-vitals.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mobile-vitals.d.ts","sourceRoot":"","sources":["../src/mobile-vitals.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mobile-vitals.d.ts","sourceRoot":"","sources":["../src/mobile-vitals.ts"],"names":[],"mappings":"AAqBA,gEAAgE;AAChE,wBAAgB,cAAc,IAAI,IAAI,GAAG,MAAM,CAS9C;AAED,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,aAAa,EAAE,OAAO,GAAG,IAAI,CAO3D;AAED,iBAAiB;AACjB,wBAAgB,2BAA2B,IAAI,IAAI,CAGlD"}
|