2026-06-16 12:10:28 +08:00
|
|
|
/**
|
|
|
|
|
* ErrorCapture — hooks into the RN global error handler and unhandled promise
|
2026-06-16 17:39:18 +08:00
|
|
|
* rejections to automatically forward errors to BugCollect.captureError.
|
2026-06-16 12:10:28 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// React Native exposes ErrorUtils on the global object
|
|
|
|
|
declare const ErrorUtils: {
|
|
|
|
|
getGlobalHandler(): ((error: Error, isFatal?: boolean) => void) | null
|
|
|
|
|
setGlobalHandler(handler: (error: Error, isFatal?: boolean) => void): void
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-16 13:10:16 +08:00
|
|
|
// React Native / Hermes exposes onunhandledrejection on globalThis
|
|
|
|
|
type UnhandledRejectionHandler = ((event: { reason: unknown }) => void) | null | undefined
|
2026-07-26 23:47:30 +08:00
|
|
|
let previousErrorHandler: ((error: Error, isFatal?: boolean) => void) | null = null
|
|
|
|
|
let previousUnhandledHandler: UnhandledRejectionHandler = null
|
|
|
|
|
let started = false
|
2026-06-16 12:10:28 +08:00
|
|
|
|
|
|
|
|
export const ErrorCapture = {
|
|
|
|
|
start(onError: (error: unknown, meta?: Record<string, unknown>) => void): void {
|
2026-07-26 23:47:30 +08:00
|
|
|
if (started) return
|
|
|
|
|
started = true
|
2026-06-16 12:10:28 +08:00
|
|
|
// JS global error handler
|
2026-07-26 23:47:30 +08:00
|
|
|
previousErrorHandler = typeof ErrorUtils !== 'undefined' ? ErrorUtils.getGlobalHandler() : null
|
2026-06-16 12:10:28 +08:00
|
|
|
if (typeof ErrorUtils !== 'undefined') {
|
|
|
|
|
ErrorUtils.setGlobalHandler((error, isFatal) => {
|
|
|
|
|
onError(error, { isFatal: isFatal ?? false })
|
2026-07-26 23:47:30 +08:00
|
|
|
previousErrorHandler?.(error, isFatal)
|
2026-06-16 12:10:28 +08:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unhandled Promise rejection
|
2026-06-16 13:10:16 +08:00
|
|
|
const g = globalThis as unknown as Record<string, unknown>
|
2026-07-26 23:47:30 +08:00
|
|
|
previousUnhandledHandler = (g['onunhandledrejection'] as UnhandledRejectionHandler) ?? null
|
2026-06-16 13:10:16 +08:00
|
|
|
g['onunhandledrejection'] = (event: { reason: unknown }) => {
|
2026-06-16 12:10:28 +08:00
|
|
|
onError(event.reason, { type: 'unhandledRejection' })
|
2026-07-26 23:47:30 +08:00
|
|
|
previousUnhandledHandler?.(event)
|
2026-06-16 12:10:28 +08:00
|
|
|
}
|
|
|
|
|
},
|
2026-07-26 23:47:30 +08:00
|
|
|
|
|
|
|
|
stop(): void {
|
|
|
|
|
if (!started) return
|
|
|
|
|
if (typeof ErrorUtils !== 'undefined') {
|
|
|
|
|
ErrorUtils.setGlobalHandler(previousErrorHandler ?? (() => undefined))
|
|
|
|
|
}
|
|
|
|
|
const g = globalThis as unknown as Record<string, unknown>
|
|
|
|
|
g['onunhandledrejection'] = previousUnhandledHandler ?? null
|
|
|
|
|
previousErrorHandler = null
|
|
|
|
|
previousUnhandledHandler = null
|
|
|
|
|
started = false
|
|
|
|
|
},
|
2026-06-16 12:10:28 +08:00
|
|
|
}
|