@cphtech-dk/expo-crash-capture
v0.1.1
Published
Next-launch native crash capture (Android exit reasons, iOS MetricKit) for @cphtech-dk/app-monitor.
Readme
@cphtech-dk/expo-crash-capture
Next-launch native crash capture for Expo apps. Reads the OS's own record of
why the process last exited — Android's ApplicationExitInfo, iOS's
MetricKit — since neither platform gives an app any hook that runs during a
native (non-JS) crash.
This is a separate package from @cphtech-dk/app-monitor on purpose: the SDK
depends on neither react-native nor any expo-* package (see
ADR-0007), so native crash
capture has to be something a consumer opts into, not something the SDK pulls
in for everyone.
Install
bun add @cphtech-dk/expo-crash-capture
npx expo prebuildRequires a dev client or production build — this is native code, so it is not available in Expo Go.
Wiring it into @cphtech-dk/app-monitor
Pass a loader that imports this package by its literal name:
import { init } from "@cphtech-dk/app-monitor/native";
init({
endpoint: "...",
apiKey: "...",
nativeCrashReader: () => import("@cphtech-dk/expo-crash-capture"),
});That literal specifier matters: Metro (React Native's bundler) only resolves
import() calls whose argument it can see as a string at bundle time. It
cannot resolve one built from string concatenation or passed as a variable —
so init()'s built-in fallback, which builds the specifier from parts to stay
polite to other bundlers, silently finds nothing under Metro even when this
package is installed. nativeCrashReader is not an optional tuning knob; on
React Native it is the difference between this package actually being used
and init() quietly no-op'ing every launch. See
@cphtech-dk/app-monitor's NativeConfig.nativeCrashReader doc comment and
ADR-0011 for the full story.
API
import { getLastExitReasons } from "@cphtech-dk/expo-crash-capture";
const records = await getLastExitReasons();Resolves to [] on Expo Go, on iOS without MetricKit, on Android below API 30
(Android 11), or if nothing crashed since the last call — never rejects. Each
call reports only records newer than the last one it already returned (tracked
in SharedPreferences / UserDefaults), so calling it more than once — e.g.
once from @cphtech-dk/app-monitor and once from the host app — does not
duplicate reports.
See src/types.ts for CrashRecord.
On Android 12+ a native crash's trace is the OS's protobuf tombstone, not
text. The module decodes it (TombstoneProto.kt — signal, cause, abort
message, the crashing thread's top frames; memory dumps and the memory map are
skipped without being read into memory) and renders it in the shape debuggerd
logs, with topFrame = SIGNAME lib.so Symbol so one crash site stays one
group. Text traces (Android 11, JVM crashes, ANRs) are passed through as before.
