@realtime-inspector/client-health-react-native
v0.7.0
Published
Device & app performance telemetry as Replay Model primitives, so "what was happening on the user's device" lands on the same replay timeline as provider events. See `docs/06-providers/client-health-plugin.md`.
Readme
@realtime-inspector/client-health-react-native
Device & app performance telemetry as Replay Model primitives, so "what was
happening on the user's device" lands on the same replay timeline as provider
events. See docs/06-providers/client-health-plugin.md.
This package is app-agnostic — it knows nothing about any consuming app.
The native layer is reached through one swappable seam (NativeHealthCollector),
so the plugin runs JS-only (Phase A) or with the bundled Nitro module (Phase B).
Usage
JS-only (no native code, ships immediately):
import { createInspector } from '@realtime-inspector/core'
import { createClientHealthPlugin, createClientHealthSampler } from '@realtime-inspector/client-health-react-native'
const inspector = createInspector({
endpoint: '…',
sampler: createClientHealthSampler(),
})
inspector.use(createClientHealthPlugin()) // JS FPS + JS stall + incidentsThere is no fidelity-mode switch. Collection cadence follows the inspector's
collectionIntervalMs, and how much survives is decided by the core metric-norm
gate plus server-pushed sampling rates (delivered via onAck) — not a client
setMode. See docs/09-operations/production-modes.md.
With the native collector (full CPU/RAM/thermal/battery/UI FPS):
import { createNativeHealthCollector } from '@reincraft/plugin-client-health-react-native/native'
inspector.use(createClientHealthPlugin({ collectors: [createNativeHealthCollector()] }))The ./native entry imports react-native-nitro-modules; keep it out of any
non-RN bundle. If the native module isn't present, omit collectors and the
plugin degrades to JS-only.
What it emits
- Metrics (
metricSample, sampleable):client_health.memory.*,client_health.cpu.*,client_health.fps.{ui,js},client_health.frames.dropped(raw cumulative),client_health.thread.{js,main}_stall_ms,client_health.battery.level,client_health.thermal.state, plus the observer-effect self-metricclient_health.sdk.collect_ms. - Incidents (
event, lossless):client_health.memory.warning,client_health.thermal.critical,client_health.battery.saver_changed. These are genuine OS/library PUSH facts. The metric-DERIVED incidents (fps.drop,thread.{js,main}_stall) were removed — an out-of-norm gauge is the signal now and the backend infers the episode (docs/99-wip/metric-norm-gate.md).
Perspective-scoped: the device IS the perspective — no entityRef in V1.
Native module (Phase B) — build follow-up
The from-scratch Nitro module lives in ios/, android/, and the spec in
src/specs/client-health.nitro.ts. It is not built or verified in this repo
(no app context).
Native autolinking is DISABLED by default (react-native.config.js sets both
platforms to null). Without it, a consumer that linked this package would try
to compile ios/HybridClientHealth.swift, which references the not-yet-generated
HybridClientHealthSpec and fail the build. So the JS-only path works in any app
out of the box; the native module is explicit opt-in.
To wire the native module in a consumer:
bun nitrogen— codegensHybridClientHealthSpec+ struct classes from the spec.- Remove
react-native.config.js(or re-enable the platforms). - iOS:
pod install(picks upReincraftClientHealth.podspec). - Android: autolinking via
android/build.gradle.
Aggregation (UI FPS, dropped frames, main-thread stall) runs natively; only one
struct per tick crosses the bridge. Native sources reference proven platform APIs
(task_vm_info, host_cpu_load_info, ProcessInfo.thermalState, CADisplayLink
on iOS; ActivityManager PSS, /proc/stat, PowerManager, Choreographer on
Android) but the module is independent of any app.
