All files / server/src/utils sentryRetry.ts

100% Statements 20/20
90% Branches 9/10
100% Functions 2/2
100% Lines 20/20

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            45x   45x         24x 24x 41x 12x   29x 29x   24x           45x 14x 14x 14x 21x 17x   21x 4x 8x     21x   14x    
import { sleep } from "server/utils/sleep";
 
// Shared retry policy for delivering envelopes to Sentry, used by both the
// browser envelope tunnel and the backend SDK transport. Sentry's edge
// suggests trying again in 30 seconds when its backend is unreachable, so the
// second retry waits that long
export const sentryRetryDelaysMs = [1000 * 2, 1000 * 30];
 
export const retryWithDelays = async <T>(
  delaysMs: number[],
  run: () => Promise<T>,
  shouldRetry: (result: T) => boolean,
): Promise<T> => {
  let result = await run();
  for (const delayMs of delaysMs) {
    if (!shouldRetry(result)) {
      break;
    }
    await sleep(delayMs);
    result = await run();
  }
  return result;
};
 
// Flatten the message chain (cause links and AggregateError members) into one
// line. Attaching the original error as `cause` to a logged Error instead
// would fingerprint each network failure mode into its own Sentry issue
export const flattenErrorMessageChain = (error: unknown): string => {
  const messages: string[] = [];
  let current: unknown = error;
  while (current instanceof Error) {
    if (current.message) {
      messages.push(current.message);
    }
    if (current instanceof AggregateError) {
      for (const inner of current.errors) {
        messages.push(inner instanceof Error ? inner.message : String(inner));
      }
    }
    current = current.cause;
  }
  return messages.length > 0 ? messages.join(" / ") : String(error);
};