All files / core crashHandlers.ts

95.23% Statements 20/21
83.33% Branches 5/6
87.5% Functions 7/8
95.23% Lines 20/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74                          1x     3x       4x 4x         4x 4x 2x 2x 2x               4x   4x             4x     1x 1x                   5x 1x   4x 4x 4x         4x    
import { recordCrash } from "./crashLog";
 
// Installe les capteurs globaux d'erreurs JS. A appeler UNE seule fois, le plus
// tot possible (au chargement du module App), avant tout rendu. Voir
// OBSERVABILITE_CRASH.md (approche A, ยง3). Zero dependance externe.
 
type GlobalErrorHandler = (error: unknown, isFatal?: boolean) => void;
 
type ErrorUtilsLike = {
  getGlobalHandler?: () => GlobalErrorHandler | undefined;
  setGlobalHandler?: (handler: GlobalErrorHandler) => void;
};
 
let installed = false;
 
function toError(value: unknown): Error {
  return value instanceof Error ? value : new Error(String(value));
}
 
function installGlobalErrorHandler(): void {
  const errorUtils = (globalThis as unknown as { ErrorUtils?: ErrorUtilsLike }).ErrorUtils;
  Iif (!errorUtils?.setGlobalHandler) {
    return;
  }
  // On CHAINE le handler precedent : on ne casse ni l'ecran rouge de dev ni le
  // comportement de crash natif en prod, on ne fait que journaliser au passage.
  const previous = errorUtils.getGlobalHandler?.();
  errorUtils.setGlobalHandler((error, isFatal) => {
    const err = toError(error);
    void recordCrash({ kind: "fatal", message: err.message, stack: err.stack, isFatal });
    previous?.(error, isFatal);
  });
}
 
function installRejectionTracking(): void {
  // Best-effort : le suivi des rejets de promesse non geres depend du polyfill
  // "promise" embarque par React Native. Absent dans certains environnements
  // (ex. Jest) -> on ignore, les exceptions fatales restent captees.
  try {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const tracking = require("promise/setimmediate/rejection-tracking") as {
      enable: (options: {
        allRejections?: boolean;
        onUnhandled?: (id: number, error: unknown) => void;
        onHandled?: (id: number) => void;
      }) => void;
    };
    tracking.enable({
      allRejections: true,
      onUnhandled: (_id, error) => {
        const err = toError(error);
        void recordCrash({ kind: "rejection", message: err.message, stack: err.stack });
      },
      onHandled: () => {},
    });
  } catch {
    // Module indisponible : suivi des rejets non installe (sans incidence sur le reste).
  }
}
 
export function installCrashHandlers(): void {
  if (installed) {
    return;
  }
  installed = true;
  installGlobalErrorHandler();
  installRejectionTracking();
}
 
// Reservoir de test uniquement : permet de reinstaller les handlers entre tests.
export function __resetCrashHandlersForTests(): void {
  installed = false;
}