@goliapkg/sentori-react-native 4.0.0 → 5.1.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/MIGRATION.md +131 -0
- package/README.md +40 -30
- package/android/build.gradle +21 -0
- package/android/src/main/java/com/sentori/SentoriCrashHandler.kt +29 -0
- package/android/src/main/java/com/sentori/SentoriModule.kt +8 -0
- package/android/src/main/java/com/sentori/SentoriScreenshotCapture.kt +36 -4
- package/android/src/test/java/com/sentori/SentoriCrashHandlerTest.kt +7 -2
- package/android/src/test/resources/robolectric.properties +10 -0
- package/ios/SentoriCrashHandler.swift +48 -4
- package/ios/SentoriModule.swift +12 -0
- package/ios/SentoriScreenshotCapture.swift +34 -3
- package/lib/config.d.ts +17 -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 +32 -109
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +54 -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 +100 -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/mask.d.ts +5 -13
- package/lib/mask.d.ts.map +1 -1
- package/lib/mask.js +29 -30
- package/lib/mask.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/native.d.ts +4 -11
- package/lib/native.d.ts.map +1 -1
- package/lib/native.js +23 -0
- package/lib/native.js.map +1 -1
- 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 +56 -23
- package/lib/rage-tap.js.map +1 -1
- package/lib/replay-screens.d.ts +15 -0
- package/lib/replay-screens.d.ts.map +1 -0
- package/lib/replay-screens.js +87 -0
- package/lib/replay-screens.js.map +1 -0
- package/lib/replay.d.ts.map +1 -1
- package/lib/replay.js +3 -24
- package/lib/replay.js.map +1 -1
- package/lib/responsiveness-detector.d.ts +18 -0
- package/lib/responsiveness-detector.d.ts.map +1 -0
- package/lib/responsiveness-detector.js +55 -0
- package/lib/responsiveness-detector.js.map +1 -0
- 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 +4 -3
- package/src/__tests__/iron-rule.test.ts +128 -0
- package/src/__tests__/replay-screens.test.ts +63 -0
- package/src/__tests__/responsiveness-detector.test.ts +74 -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 +20 -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 +84 -215
- package/src/init.ts +115 -449
- package/src/long-task-monitor.ts +42 -64
- package/src/mask.ts +29 -31
- package/src/mobile-vitals.ts +22 -82
- package/src/native-pending.ts +92 -0
- package/src/native.ts +33 -0
- package/src/navigation.ts +34 -142
- package/src/netinfo.ts +3 -3
- package/src/push.ts +2 -5
- package/src/rage-tap.tsx +72 -25
- package/src/replay-screens.ts +95 -0
- package/src/replay.ts +3 -20
- package/src/responsiveness-detector.ts +66 -0
- 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/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/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/src/init.ts
CHANGED
|
@@ -1,472 +1,138 @@
|
|
|
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).
|
|
2
5
|
|
|
3
|
-
import {
|
|
6
|
+
import { safeFn, setLogLevel } from '@goliapkg/sentori-core';
|
|
7
|
+
import type { InitConfig } from '@goliapkg/sentori-core';
|
|
8
|
+
|
|
9
|
+
import { setConfig } from './config';
|
|
10
|
+
import { registerEmitHook } from './emit-hooks';
|
|
4
11
|
import { installGlobalHandler } from './handlers/global';
|
|
5
12
|
import { installLifecycleHandler } from './handlers/lifecycle';
|
|
6
|
-
import { installPromiseHandler } from './handlers/promise';
|
|
7
13
|
import { installNetworkHandler } from './handlers/network';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
markLaunchCompleted,
|
|
11
|
-
runLaunchCrashGuard,
|
|
12
|
-
} from './launch-crash-guard';
|
|
13
|
-
import { getInstallId } from './install-id';
|
|
14
|
-
import { startMetricsTimer } from './metrics';
|
|
15
|
-
import {
|
|
16
|
-
emitColdStart,
|
|
17
|
-
markColdStartT0,
|
|
18
|
-
startRuntimeMetricsTimer,
|
|
19
|
-
} from './runtime-metrics';
|
|
20
|
-
import { startFpsInstrument } from './runtime-metrics-fps';
|
|
21
|
-
import { startHeapInstrument } from './runtime-metrics-heap';
|
|
22
|
-
import { startNetworkBytesInstrument } from './runtime-metrics-network';
|
|
23
|
-
import { startTrackTimer } from './track';
|
|
24
|
-
import { drainNativePending, markNativeJsBridgeReady, setNativeConfig } from './native';
|
|
25
|
-
import { getColdStartMs } from './mobile-vitals';
|
|
26
|
-
import { startHeartbeat, type HeartbeatOptions } from './heartbeat';
|
|
27
|
-
import { startSpan } from '@goliapkg/sentori-core';
|
|
28
|
-
import { startControlChannel } from './control-channel';
|
|
14
|
+
import { installPromiseHandler } from './handlers/promise';
|
|
29
15
|
import { startLongTaskMonitor } from './long-task-monitor';
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/** Project token starting with `st_pk_`. Required. */
|
|
47
|
-
token: string;
|
|
48
|
-
/** Release identifier, e.g. `myapp@1.2.3+456`. Required. */
|
|
49
|
-
release: string;
|
|
50
|
-
/** Environment label. Defaults to `dev` if `__DEV__`, else `prod`. */
|
|
51
|
-
environment?: string;
|
|
52
|
-
/** Override ingestion URL (self-hosted). Default: https://ingest.sentori.golia.jp */
|
|
53
|
-
ingestUrl?: string;
|
|
54
|
-
/** Toggle individual capture sources. All enabled by default. */
|
|
55
|
-
capture?: {
|
|
56
|
-
globalErrors?: boolean;
|
|
57
|
-
promiseRejections?: boolean;
|
|
58
|
-
network?:
|
|
59
|
-
| boolean
|
|
60
|
-
| {
|
|
61
|
-
/** v0.9.0 #11 — auto-extract GraphQL `operationName` from
|
|
62
|
-
* POST request bodies and use it as the breadcrumb / span
|
|
63
|
-
* name (instead of `POST /graphql`). Default `true`. */
|
|
64
|
-
graphql?: boolean;
|
|
65
|
-
};
|
|
66
|
-
/** Session tracking: opens a session on init and on each
|
|
67
|
-
* foreground (`AppState` → `active`), ends it on background.
|
|
68
|
-
* Drives crash-free rate. Set `false` to opt out. */
|
|
69
|
-
sessions?: boolean;
|
|
70
|
-
/** Capture a screenshot of the current screen on
|
|
71
|
-
* `captureException`. Opt-in. The capture runs through the
|
|
72
|
-
* bundled native module — no extra peer dep required since
|
|
73
|
-
* v0.7.3. To redact PII regions, register a mask query via
|
|
74
|
-
* `sentori.registerMaskQuery(() => string[])` and put
|
|
75
|
-
* `nativeID="..."` on the `<View>`s the SDK should black out.
|
|
76
|
-
* The image is webp q=70 / jpeg q=70 at 480 px max, < 100 KB
|
|
77
|
-
* typical. */
|
|
78
|
-
screenshot?: boolean;
|
|
79
|
-
/** Phase 46: record the last N steps (route changes, custom
|
|
80
|
-
* breadcrumbs) leading up to a crash. On `captureException`
|
|
81
|
-
* the buffer is sealed and uploaded as a `sessionTrail`
|
|
82
|
-
* attachment. Defaults to false. */
|
|
83
|
-
sessionTrail?: boolean;
|
|
84
|
-
/** v2.0 W3 — when `true`, every `sentori.track(name, props)`
|
|
85
|
-
* also pushes a `{ type: 'track', data: { name, props } }`
|
|
86
|
-
* breadcrumb so a subsequent `captureException` /
|
|
87
|
-
* `captureMessage` carries the customer journey leading up to
|
|
88
|
-
* the failure. Defaults to `false` to preserve v1 customer
|
|
89
|
-
* breadcrumb shape on upgrade; recommended `true` for new
|
|
90
|
-
* integrations. See `docs/recipes/track-and-metrics.md`. */
|
|
91
|
-
trackAutoBreadcrumb?: boolean;
|
|
92
|
-
/** v2.1 W2 — auto-instrument runtime metrics (FPS, JS heap,
|
|
93
|
-
* cold-start, route nav timing, network bytes). Drains the
|
|
94
|
-
* shared `@goliapkg/sentori-core` ring to
|
|
95
|
-
* `/v1/runtime-metrics:batch` every 30 s. Defaults to `true`
|
|
96
|
-
* in W2 part 2 — cold-start only, ~6 emits per session
|
|
97
|
-
* per device, ~zero main-thread cost. Higher-cost instruments
|
|
98
|
-
* (FPS / route-nav) land in W2 part 3 with per-tick perf
|
|
99
|
-
* budget tests as stop-ship gates per
|
|
100
|
-
* `.claude/CLAUDE.md` performance bedrock. */
|
|
101
|
-
runtimeMetrics?: boolean;
|
|
102
|
-
/** v0.9.1 +S4 — pre-crash sentinel. Subscribes to JS-thread
|
|
103
|
-
* frame timing; when ≥ 50% of a 60-frame window misses the
|
|
104
|
-
* budget (default 32 ms / < 30 fps), emits a `kind: nearCrash`
|
|
105
|
-
* event proactively so dashboards see the "about-to-die"
|
|
106
|
-
* signal before an actual crash. */
|
|
107
|
-
preCrashSentinel?: boolean;
|
|
108
|
-
sentinelChannels?: PreCrashChannel[];
|
|
109
|
-
/** v0.9.6 #4 — JS-thread long-task monitor. setInterval(50ms)
|
|
110
|
-
* tick detects JS thread stalls ≥ 200ms (configurable) and
|
|
111
|
-
* emits a `sentori.longtask` span. Pairs with
|
|
112
|
-
* `preCrashSentinel` (slow frames < 32ms) to cover the
|
|
113
|
-
* "JS thread is stuck" spectrum. */
|
|
114
|
-
longTaskMonitor?: boolean | { thresholdMs?: number };
|
|
115
|
-
/** v0.9.6 #2 — wireframe session replay. Native walks the iOS
|
|
116
|
-
* UIView / Android View hierarchy at 1 Hz and serializes
|
|
117
|
-
* visible nodes; captureException flushes the last 60 s as a
|
|
118
|
-
* `replay` attachment. Set to `'wireframe'` to enable. */
|
|
119
|
-
replay?: 'off' | 'wireframe' | { hz?: number; mode: 'off' | 'wireframe' };
|
|
120
|
-
/** v1.1 #4 升级 — JS sample profiler. setInterval(50ms) idle-tick
|
|
121
|
-
* sampler aggregates frame counts → emits sentori.profile span
|
|
122
|
-
* every 60s with `flameData` (frame → tick count). Pairs with
|
|
123
|
-
* longTaskMonitor (≥200ms outliers) — sample profiler 看 idle
|
|
124
|
-
* 分布、long-task 看 outliers。 */
|
|
125
|
-
sampleProfiler?: boolean | { flushMs?: number; sampleMs?: number };
|
|
126
|
-
/** Analytics v1 — live-presence heartbeat. Foreground 1/min
|
|
127
|
-
* default; opt out with `false`, or pass an options object
|
|
128
|
-
* to tune. Powers the Audience > Live dashboard.
|
|
129
|
-
* Iron-rule budget: < 1 KB / min, < 1 ms / call. */
|
|
130
|
-
heartbeat?: boolean | HeartbeatOptions;
|
|
131
|
-
/** v0.9.0 #3 — launch-crash loop guard. When two consecutive
|
|
132
|
-
* launches don't reach `markLaunchCompleted()` (typical of an
|
|
133
|
-
* OTA update with a fatal bug), invoke the host callback with
|
|
134
|
-
* a 200 ms timeout to decide rollback / reset / continue. */
|
|
135
|
-
launchCrashGuard?: {
|
|
136
|
-
enabled: boolean;
|
|
137
|
-
onLaunchCrashDetected?: (
|
|
138
|
-
info: import('./launch-crash-guard').LaunchCrashInfo,
|
|
139
|
-
) =>
|
|
140
|
-
| import('./launch-crash-guard').LaunchCrashAction
|
|
141
|
-
| Promise<import('./launch-crash-guard').LaunchCrashAction>;
|
|
142
|
-
threshold?: number;
|
|
143
|
-
timeoutMs?: number;
|
|
144
|
-
};
|
|
145
|
-
};
|
|
146
|
-
/** Phase 44 sub-B: client-side sampling. Each rate is `[0, 1]`;
|
|
147
|
-
* absent / null keeps everything. Defaults to 1.0 for both
|
|
148
|
-
* (no drop). Set traces to e.g. 0.1 once the app's at user
|
|
149
|
-
* volume to keep ingest budget under control without changing
|
|
150
|
-
* the server-side quota. Decisions are made per-event for
|
|
151
|
-
* errors and per-trace (all spans together) for traces. */
|
|
152
|
-
sampling?: {
|
|
153
|
-
errors?: null | number;
|
|
154
|
-
traces?: null | number;
|
|
155
|
-
/** v2.0 — sampling rate for `kind: 'message'` events emitted
|
|
156
|
-
* via `sentori.captureMessage()`. `null` / absent → keep all. */
|
|
157
|
-
messages?: null | number;
|
|
158
|
-
};
|
|
159
|
-
/** v2.3 — canonical sampling field (renamed from `sampling`).
|
|
160
|
-
* Same shape; if both are passed, `sample` wins. The older
|
|
161
|
-
* `sampling` stays accepted indefinitely as a back-compat alias —
|
|
162
|
-
* this is the kind of rename that's only worth doing if it's
|
|
163
|
-
* also zero-cost for existing callers. */
|
|
164
|
-
sample?: {
|
|
165
|
-
errors?: null | number;
|
|
166
|
-
traces?: null | number;
|
|
167
|
-
messages?: null | number;
|
|
168
|
-
};
|
|
169
|
-
/** v2.3 — Sentori SDK's own console output gate. Default `'warn'`:
|
|
170
|
-
* SDK is silent unless something is genuinely broken (transport
|
|
171
|
-
* sustained failure, native module not found, internal SDK
|
|
172
|
-
* exception). Set to `'silent'` for absolute silence; bump to
|
|
173
|
-
* `'info'` / `'debug'` when debugging Sentori itself. */
|
|
174
|
-
logLevel?: import('@goliapkg/sentori-core').LogLevel;
|
|
175
|
-
/** v2.3 — fires once after init completes. Use this to know the
|
|
176
|
-
* SDK is live instead of scanning the console. The `ReadyInfo`
|
|
177
|
-
* carries native-module bind status + cold-start timing. */
|
|
178
|
-
onReady?: (info: ReadyInfo) => void;
|
|
179
|
-
/** v2.3 — mutate-or-drop hook on each outbound event (sync).
|
|
180
|
-
* Return the event to ship it (possibly mutated), or `null` to
|
|
181
|
-
* drop. See `BeforeSendHook` for the throwing / non-event
|
|
182
|
-
* fallback policy. Used for host-side PII scrubbing the SDK
|
|
183
|
-
* can't do automatically; server-side privacy_lab still runs
|
|
184
|
-
* regardless. */
|
|
185
|
-
beforeSend?: import('@goliapkg/sentori-core').BeforeSendHook;
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const DEFAULT_INGEST_URL = 'https://ingest.sentori.golia.jp';
|
|
189
|
-
|
|
190
|
-
export const init = (options: InitOptions): void => {
|
|
191
|
-
if (!options.token || !options.token.startsWith('st_pk_')) {
|
|
192
|
-
throw new Error("Sentori: token is required and must start with 'st_pk_'");
|
|
16
|
+
import { checkColdStart } from './mobile-vitals';
|
|
17
|
+
import { markNativeJsBridgeReady, setNativeConfig } from './native';
|
|
18
|
+
import { shipNativePending } from './native-pending';
|
|
19
|
+
import { drainReplay, startReplay } from './replay';
|
|
20
|
+
import { drainScreenReplay, startScreenReplay } from './replay-screens';
|
|
21
|
+
import { drainOfflineQueue, startTransport, uploadAttachment } from './transport';
|
|
22
|
+
|
|
23
|
+
let _initialized = false;
|
|
24
|
+
|
|
25
|
+
export const init = safeFn('init', (config: InitConfig): void => {
|
|
26
|
+
if (_initialized) return;
|
|
27
|
+
|
|
28
|
+
if (!config || typeof config.token !== 'string' || config.token.length === 0) {
|
|
29
|
+
// eslint-disable-next-line no-console
|
|
30
|
+
console.warn('[sentori] init skipped: token missing — SDK is a no-op');
|
|
31
|
+
return;
|
|
193
32
|
}
|
|
194
|
-
if (
|
|
195
|
-
|
|
33
|
+
if (typeof config.ingestUrl !== 'string' || config.ingestUrl.length === 0) {
|
|
34
|
+
// eslint-disable-next-line no-console
|
|
35
|
+
console.warn('[sentori] init skipped: ingestUrl missing — SDK is a no-op');
|
|
36
|
+
return;
|
|
196
37
|
}
|
|
197
38
|
|
|
198
|
-
|
|
199
|
-
// are gated correctly. Default 'warn' from logger.ts; an explicit
|
|
200
|
-
// host setting overrides.
|
|
201
|
-
setLogLevel(options.logLevel);
|
|
202
|
-
|
|
203
|
-
const env =
|
|
204
|
-
options.environment ??
|
|
205
|
-
(typeof __DEV__ !== 'undefined' && __DEV__ ? 'dev' : 'prod');
|
|
206
|
-
|
|
207
|
-
// v0.9.0 #3 — launch-crash guard. Fires *before* any other setup so
|
|
208
|
-
// a known-bad bundle can roll back instead of running JS that's
|
|
209
|
-
// about to die again. AsyncStorage-backed; if the host doesn't have
|
|
210
|
-
// it the guard is a no-op.
|
|
211
|
-
const lcg = options.capture?.launchCrashGuard;
|
|
212
|
-
if (lcg?.enabled) {
|
|
213
|
-
void runLaunchCrashGuard(
|
|
214
|
-
lcg,
|
|
215
|
-
options.release,
|
|
216
|
-
getBundleInfo()?.id ?? null,
|
|
217
|
-
);
|
|
218
|
-
}
|
|
39
|
+
_initialized = true;
|
|
219
40
|
|
|
220
41
|
setConfig({
|
|
221
|
-
token:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
42
|
+
token: config.token,
|
|
43
|
+
ingestUrl: config.ingestUrl.replace(/\/+$/, ''),
|
|
44
|
+
release: config.release ?? '',
|
|
45
|
+
environment: config.environment ?? 'production',
|
|
225
46
|
enabled: true,
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
// carries the customer journey. Defaults false to preserve v1
|
|
236
|
-
// breadcrumb shape on upgrade.
|
|
237
|
-
trackAutoBreadcrumb: options.capture?.trackAutoBreadcrumb === true,
|
|
238
|
-
// v2.3 — host-side beforeSend hook (sync). Stored on Config so
|
|
239
|
-
// capture.ts can pull it without re-resolving the InitOptions.
|
|
240
|
-
beforeSend: options.beforeSend,
|
|
47
|
+
detect: {
|
|
48
|
+
rageTap: config.detect?.rageTap ?? true,
|
|
49
|
+
longFreeze: config.detect?.longFreeze ?? true,
|
|
50
|
+
slowColdStart: config.detect?.slowColdStart ?? true,
|
|
51
|
+
slowApi: config.detect?.slowApi ?? false,
|
|
52
|
+
},
|
|
53
|
+
replaySeconds: config.replaySeconds ?? 30,
|
|
54
|
+
replayScreens: config.replayScreens ?? false,
|
|
55
|
+
beforeSend: config.beforeSend,
|
|
241
56
|
});
|
|
57
|
+
setLogLevel(config.logLevel ?? 'warn');
|
|
58
|
+
|
|
59
|
+
// JS-side error capture + the signal-ring feeders.
|
|
60
|
+
installGlobalHandler();
|
|
61
|
+
installPromiseHandler();
|
|
62
|
+
installNetworkHandler();
|
|
63
|
+
installLifecycleHandler();
|
|
64
|
+
|
|
65
|
+
// Warn-scenario detectors (design.md §3 minimum set). rage_tap
|
|
66
|
+
// rides the RageTapCapture component; the rest start here.
|
|
67
|
+
if (config.detect?.longFreeze !== false) startLongTaskMonitor();
|
|
68
|
+
|
|
69
|
+
// B-type replay: a rolling in-memory wireframe ring; an error/warn
|
|
70
|
+
// going out drains it into a replay attachment on that event.
|
|
71
|
+
const replaySeconds = config.replaySeconds ?? 30;
|
|
72
|
+
if (replaySeconds > 0) {
|
|
73
|
+
startReplay({ mode: 'wireframe' });
|
|
74
|
+
// Visual ring is opt-in: screenshots can carry user content.
|
|
75
|
+
if (config.replayScreens === true) startScreenReplay(replaySeconds);
|
|
76
|
+
registerEmitHook((event) => {
|
|
77
|
+
if (event.kind !== 'error' && event.kind !== 'warn') return;
|
|
78
|
+
if (!event.id) return;
|
|
79
|
+
const lines = drainReplay();
|
|
80
|
+
if (lines) {
|
|
81
|
+
const base64 = base64Encode(lines);
|
|
82
|
+
if (base64) {
|
|
83
|
+
void uploadAttachment(
|
|
84
|
+
event.id,
|
|
85
|
+
'replay',
|
|
86
|
+
{ base64, mediaType: 'application/x-sentori-replay' },
|
|
87
|
+
{ source: 'js' },
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const frames = drainScreenReplay();
|
|
92
|
+
if (frames) {
|
|
93
|
+
const base64 = base64Encode(frames);
|
|
94
|
+
if (base64) {
|
|
95
|
+
void uploadAttachment(
|
|
96
|
+
event.id,
|
|
97
|
+
'screens',
|
|
98
|
+
{ base64, mediaType: 'application/x-sentori-screens' },
|
|
99
|
+
{ source: 'js' },
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
242
105
|
|
|
243
|
-
//
|
|
244
|
-
//
|
|
106
|
+
// Native side: hand over release/environment for the crash-file
|
|
107
|
+
// writer, mark the bridge live, then drain crashes from previous
|
|
108
|
+
// launches. All fire-and-forget — init stays synchronous and fast
|
|
109
|
+
// (< 50 ms budget; the work below happens off the critical path).
|
|
245
110
|
setNativeConfig({
|
|
246
|
-
|
|
247
|
-
release:
|
|
248
|
-
|
|
111
|
+
environment: config.environment ?? 'production',
|
|
112
|
+
release: config.release ?? '',
|
|
113
|
+
token: config.token,
|
|
249
114
|
});
|
|
250
|
-
// v0.9.4 #1 — finalize cold-start measurement. iOS uses the
|
|
251
|
-
// delta from `applicationDidFinishLaunching` to this call;
|
|
252
|
-
// Android uses Process.getStartElapsedRealtime() so the value is
|
|
253
|
-
// computed at this point and cached.
|
|
254
115
|
markNativeJsBridgeReady();
|
|
255
|
-
// Emit a one-off cold-start span. Server aggregates these per
|
|
256
|
-
// release for the Mobile Vitals dashboard. No-op when native
|
|
257
|
-
// module isn't linked.
|
|
258
|
-
const coldMs = getColdStartMs();
|
|
259
|
-
if (coldMs !== null && coldMs > 0 && coldMs < 60_000) {
|
|
260
|
-
const span = startSpan('sentori.cold_start', {
|
|
261
|
-
name: 'cold-start',
|
|
262
|
-
parent: null,
|
|
263
|
-
startNowMs: Date.now() - coldMs,
|
|
264
|
-
tags: { 'vital.kind': 'cold_start' },
|
|
265
|
-
});
|
|
266
|
-
span.finish({ status: 'ok' });
|
|
267
|
-
}
|
|
268
116
|
|
|
269
117
|
startTransport();
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
// can opt out with `capture: { runtimeMetrics: false }`. The
|
|
282
|
-
// ring + emit live in `@goliapkg/sentori-core`; we own the
|
|
283
|
-
// 30 s flusher + the cold-start one-shot here. FPS / heap /
|
|
284
|
-
// route-nav / network instruments ship in W2 part 3 (each
|
|
285
|
-
// gated on its own per-tick perf budget CI test).
|
|
286
|
-
if (options.capture?.runtimeMetrics !== false) {
|
|
287
|
-
markColdStartT0();
|
|
288
|
-
startRuntimeMetricsTimer();
|
|
289
|
-
// Defer the emit one tick so React's first paint settles
|
|
290
|
-
// before we stamp "cold-start ended". 0-delay setTimeout puts
|
|
291
|
-
// the call after the current microtask queue drains.
|
|
292
|
-
setTimeout(emitColdStart, 0);
|
|
293
|
-
// FPS via rAF (per-tick budget < 0.5 ms — see
|
|
294
|
-
// runtime-metrics-fps.ts header). Heap is a 30 s polling
|
|
295
|
-
// tick; no-op when performance.memory isn't exposed (most
|
|
296
|
-
// RN engines today; we ship the wiring anyway so the same
|
|
297
|
-
// SDK works on web targets via @goliapkg/sentori-javascript).
|
|
298
|
-
startFpsInstrument();
|
|
299
|
-
startHeapInstrument();
|
|
300
|
-
// Network bytes counters drain every 30 s. The counters
|
|
301
|
-
// themselves are incremented inline by handlers/network.ts
|
|
302
|
-
// on every fetch round-trip — see the recordNetworkBytes
|
|
303
|
-
// call site there. No-op when `capture.network: false` is
|
|
304
|
-
// set (no fetch patch → no counter increments).
|
|
305
|
-
startNetworkBytesInstrument();
|
|
306
|
-
// Route-nav dwell timing emits inline from `navigation.ts`'s
|
|
307
|
-
// useTraceNavigation state listener — no extra start call
|
|
308
|
-
// needed; the host already mounts that hook for tracing.
|
|
309
|
-
}
|
|
310
|
-
// v1.1 chunk S1 — warm the install-id cache. Fire-and-forget;
|
|
311
|
-
// any event captured before the first resolve simply omits
|
|
312
|
-
// `device.installId`. Subsequent captures pick it up via the
|
|
313
|
-
// sync `peekInstallId()` read in collectDevice().
|
|
314
|
-
void getInstallId();
|
|
315
|
-
// v0.9.1 +S4 — pre-crash sentinel. Off by default; opt-in via
|
|
316
|
-
// `capture.preCrashSentinel: true`.
|
|
317
|
-
if (options.capture?.preCrashSentinel === true) {
|
|
318
|
-
startPreCrashSentinel({
|
|
319
|
-
enabled: true,
|
|
320
|
-
channels: options.capture.sentinelChannels,
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
// v0.9.6 #4 — long-task monitor. Off by default.
|
|
324
|
-
const lt = options.capture?.longTaskMonitor;
|
|
325
|
-
if (lt) {
|
|
326
|
-
startLongTaskMonitor({
|
|
327
|
-
enabled: true,
|
|
328
|
-
thresholdMs: typeof lt === 'object' ? lt.thresholdMs : undefined,
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
// v0.9.6 #2 — wireframe replay. Off by default.
|
|
332
|
-
const rp = options.capture?.replay;
|
|
333
|
-
if (rp === 'wireframe') {
|
|
334
|
-
startReplay({ mode: 'wireframe' });
|
|
335
|
-
} else if (rp && typeof rp === 'object' && rp.mode === 'wireframe') {
|
|
336
|
-
startReplay({ hz: rp.hz, mode: 'wireframe' });
|
|
337
|
-
}
|
|
338
|
-
// v1.1 #4 升级 — JS sample profiler. Off by default.
|
|
339
|
-
const sp = options.capture?.sampleProfiler;
|
|
340
|
-
if (sp) {
|
|
341
|
-
startSampleProfiler({
|
|
342
|
-
enabled: true,
|
|
343
|
-
flushMs: typeof sp === 'object' ? sp.flushMs : undefined,
|
|
344
|
-
sampleMs: typeof sp === 'object' ? sp.sampleMs : undefined,
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const capture = options.capture ?? {};
|
|
349
|
-
if (capture.globalErrors !== false) installGlobalHandler();
|
|
350
|
-
if (capture.promiseRejections !== false) installPromiseHandler();
|
|
351
|
-
if (capture.network !== false) {
|
|
352
|
-
const netOpts = typeof capture.network === 'object' ? capture.network : undefined;
|
|
353
|
-
installNetworkHandler({ graphql: netOpts?.graphql });
|
|
354
|
-
}
|
|
355
|
-
if (capture.sessions !== false) {
|
|
356
|
-
// Open the cold-start session now (RN doesn't fire an AppState
|
|
357
|
-
// `change` for the initial `active` state), then bind AppState so
|
|
358
|
-
// background ends it and the next foreground opens a fresh one.
|
|
359
|
-
startSession();
|
|
360
|
-
installLifecycleHandler();
|
|
361
|
-
}
|
|
362
|
-
// Analytics v1 — live-presence heartbeat. Foreground 1/min by
|
|
363
|
-
// default; pass `capture.heartbeat = false` to opt out, or
|
|
364
|
-
// `{ intervalMs: 30_000 }` to override. The default budget is well
|
|
365
|
-
// under the "几乎不能造成性能抖动" rule (CLAUDE.md).
|
|
366
|
-
const heartbeatOpt = capture.heartbeat;
|
|
367
|
-
if (heartbeatOpt !== false) {
|
|
368
|
-
startHeartbeat(typeof heartbeatOpt === 'object' ? heartbeatOpt : {});
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// Drain events persisted from previous session (best-effort):
|
|
372
|
-
// - native crashes from <Documents>/sentori/pending/*.json
|
|
373
|
-
// - JS transport offline queue from AsyncStorage
|
|
374
|
-
drainNativePending()
|
|
375
|
-
.then(async (items) => {
|
|
376
|
-
for (const json of items) {
|
|
377
|
-
try {
|
|
378
|
-
const event = JSON.parse(json) as Event & {
|
|
379
|
-
_pendingAttachments?: PendingAttachment[];
|
|
380
|
-
};
|
|
381
|
-
// Phase 42 sub-E.05 / F.09: the native crash handler couldn't
|
|
382
|
-
// upload attachments at crash time (the app was dying); it
|
|
383
|
-
// base64-encoded them into `_pendingAttachments` instead.
|
|
384
|
-
// On next launch we upload each before enqueueing the event,
|
|
385
|
-
// so the dashboard sees the refs in `event.attachments[]`.
|
|
386
|
-
if (event._pendingAttachments && event._pendingAttachments.length > 0) {
|
|
387
|
-
for (const p of event._pendingAttachments) {
|
|
388
|
-
const meta = await uploadAttachment(
|
|
389
|
-
event.id,
|
|
390
|
-
p.kind,
|
|
391
|
-
{ base64: p.base64, mediaType: p.mediaType },
|
|
392
|
-
{ source: p.source },
|
|
393
|
-
);
|
|
394
|
-
if (meta) {
|
|
395
|
-
if (!event.attachments) event.attachments = [];
|
|
396
|
-
event.attachments.push(meta);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
delete event._pendingAttachments;
|
|
400
|
-
}
|
|
401
|
-
enqueue(event);
|
|
402
|
-
} catch {
|
|
403
|
-
// skip malformed
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
})
|
|
407
|
-
.catch(() => {});
|
|
408
|
-
drainOfflineQueue().catch(() => {});
|
|
409
|
-
|
|
410
|
-
// v0.9.0 #3 — init reached the end without throwing. Schedule the
|
|
411
|
-
// "launch completed" marker after one tick so any synchronous user
|
|
412
|
-
// code right after `init()` gets to run first; we want the marker to
|
|
413
|
-
// confirm the JS bridge stayed alive, not just that `init()` returned.
|
|
414
|
-
if (lcg?.enabled) {
|
|
415
|
-
setTimeout(() => {
|
|
416
|
-
void markLaunchCompleted(getBundleInfo()?.id ?? null);
|
|
417
|
-
}, 2_000);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// v2.3 — onReady callback. Fires after setConfig + native bind +
|
|
421
|
-
// transport start are all settled. The drain-pending work is
|
|
422
|
-
// still in flight (it's async) but the SDK is ready to accept
|
|
423
|
-
// new captures. Host wires this to know the SDK is live without
|
|
424
|
-
// scanning the console. Wrapped in try/catch — host callback
|
|
425
|
-
// throwing must not propagate (NEVER rule).
|
|
426
|
-
if (options.onReady) {
|
|
427
|
-
const nativeMod = (() => {
|
|
428
|
-
try {
|
|
429
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
430
|
-
const { native } = require('./native');
|
|
431
|
-
const n = native?.();
|
|
432
|
-
return n ?? null;
|
|
433
|
-
} catch {
|
|
434
|
-
return null;
|
|
435
|
-
}
|
|
436
|
-
})();
|
|
437
|
-
const info: ReadyInfo = {
|
|
438
|
-
sdkVersion: SDK_VERSION,
|
|
439
|
-
coldStartMs: coldMs ?? undefined,
|
|
440
|
-
native: {
|
|
441
|
-
bound: !!nativeMod,
|
|
442
|
-
methods: nativeMod ? Object.keys(nativeMod).sort() : [],
|
|
443
|
-
},
|
|
118
|
+
checkColdStart(config.detect?.slowColdStart !== false);
|
|
119
|
+
void shipNativePending().catch(() => undefined);
|
|
120
|
+
void drainOfflineQueue();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
/** RN's Hermes has no btoa in older releases; go through base64.ts. */
|
|
124
|
+
const base64Encode = (text: string): string | null => {
|
|
125
|
+
try {
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
127
|
+
const { encodeBase64 } = require('./base64') as {
|
|
128
|
+
encodeBase64: (s: string) => string;
|
|
444
129
|
};
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
// Host's onReady threw. NEVER rule — swallow.
|
|
449
|
-
}
|
|
130
|
+
return encodeBase64(text);
|
|
131
|
+
} catch {
|
|
132
|
+
return null;
|
|
450
133
|
}
|
|
451
134
|
};
|
|
452
135
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const SDK_VERSION = '2.3.0';
|
|
456
|
-
|
|
457
|
-
/**
|
|
458
|
-
* Phase 42 sub-E.05: shape of each entry in the native crash JSON's
|
|
459
|
-
* `_pendingAttachments` array. Mirrors what
|
|
460
|
-
* `SentoriCrashHandler.write` writes on iOS and (sub-F) what
|
|
461
|
-
* `SentoriCrashWriter` writes on Android.
|
|
462
|
-
*/
|
|
463
|
-
type PendingAttachment = {
|
|
464
|
-
base64: string;
|
|
465
|
-
kind: AttachmentKind;
|
|
466
|
-
mediaType: string;
|
|
467
|
-
source: AttachmentSource;
|
|
136
|
+
export const __resetForTests = (): void => {
|
|
137
|
+
_initialized = false;
|
|
468
138
|
};
|
|
469
|
-
|
|
470
|
-
// Keep AttachmentMeta in the imports — it's part of the public type
|
|
471
|
-
// surface re-exported from this module's bundle.
|
|
472
|
-
export type { AttachmentMeta };
|