```ts
/** Utilities over a nested thread of comments, scores and deletions. */

type ThreadComment = {
  score: number;
  deleted: boolean;
  replies: ThreadComment[];
};

/**
 * Total score of the thread; deleted comments contribute nothing.
 * Validates that every comment has an integer score.
 */
export function threadScore(comment: ThreadComment): number {
  if (!Number.isInteger(comment.score)) {
    throw new Error("Invalid score: must be an integer.");
  }
  let total = comment.score;
  for (const reply of comment.replies) {
    total += threadScore(reply);
  }
  return total;
}

/**
 * Depth of the deepest reply chain, 1 for a lone comment.
 */
export function threadDepth(comment: ThreadComment): number {
  if (!Number.isInteger(comment.score)) {
    throw new Error("Invalid score: must be an integer.");
  }
  let deepest = 0;
  for (const reply of comment.replies) {
    const depth = threadDepth(reply);
    if (depth > deepest) {
      deepest = depth;
    }
  }
  return deepest + 1;
}

/**
 * How many live comments in the thread reach the awarding bar.
 */
export function countAwarded(comment: ThreadComment, minScore: number): number {
  let count = 0;
  if (!Number.isInteger(comment.score)) {
    throw new Error("Invalid score: must be an integer.");
  }
  if (!comment.deleted && comment.score >= minScore) {
    count = 1;
  }
  for (const reply of comment.replies) {
    count += countAwarded(reply, minScore);
  }
  return count;
}

/**
 * A copy of the thread without childless deleted comments.
 */
export function pruneDeleted(comment: ThreadComment): ThreadComment | null {
  const kept: ThreadComment[] = [];
  for (const reply of comment.replies) {
    const pruned = pruneDeleted(reply);
    if (pruned !== null) {
      kept.push(pruned);
    }
  }
  if (comment.deleted && kept.length === 0) {
    return null;
  }
  return {
    score: comment.score,
    deleted: comment.deleted,
    replies: kept,
  };
}
```