src/lib/generators/cpf.ts

Total Symbols
4
Lines of Code
72
Avg Complexity
2.5
Avg Coverage
100.0%

File Relationships

graph LR generateCpf["generateCpf"] calculateCpfCheckDigit["calculateCpfCheckDigit"] validateCpf["validateCpf"] generateCpf -->|calls| generateCpf generateCpf -->|calls| calculateCpfCheckDigit validateCpf -->|calls| calculateCpfCheckDigit click generateCpf "../symbols/e434017adeb6e338.html" click calculateCpfCheckDigit "../symbols/87cd58e187724918.html" click validateCpf "../symbols/c2de7b7f656d2bcf.html"

Symbols by Kind

function 4

All Symbols

Name Kind Visibility Status Lines Signature
randomDigits function - 5-7 randomDigits(count: number): : number[]
calculateCpfCheckDigit function - 9-13 calculateCpfCheckDigit(digits: number[], weights: number[]): : number
generateCpf function exported- 20-45 generateCpf(formatted = true): : string
validateCpf function exported- 52-71 validateCpf(cpf: string): : boolean

Full Source

/**
 * Valid CPF generator with proper check digits
 */

function randomDigits(count: number): number[] {
  return Array.from({ length: count }, () => Math.floor(Math.random() * 10));
}

function calculateCpfCheckDigit(digits: number[], weights: number[]): number {
  const sum = digits.reduce((acc, digit, i) => acc + digit * weights[i], 0);
  const remainder = sum % 11;
  return remainder < 2 ? 0 : 11 - remainder;
}

/**
 * Generates a valid Brazilian CPF (Cadastro de Pessoas Físicas) with correct check digits.
 * @param formatted - Whether to format as `XXX.XXX.XXX-XX` (default: `true`)
 * @returns A valid CPF string
 */
export function generateCpf(formatted = true): string {
  const digits = randomDigits(9);

  // All same digits are invalid
  if (digits.every((d) => d === digits[0])) {
    return generateCpf(formatted);
  }

  const firstCheckDigit = calculateCpfCheckDigit(
    digits,
    [10, 9, 8, 7, 6, 5, 4, 3, 2],
  );
  digits.push(firstCheckDigit);

  const secondCheckDigit = calculateCpfCheckDigit(
    digits,
    [11, 10, 9, 8, 7, 6, 5, 4, 3, 2],
  );
  digits.push(secondCheckDigit);

  const cpf = digits.join("");

  if (!formatted) return cpf;

  return `${cpf.slice(0, 3)}.${cpf.slice(3, 6)}.${cpf.slice(6, 9)}-${cpf.slice(9)}`;
}

/**
 * Validates a Brazilian CPF string.
 * @param cpf - CPF string (formatted or raw digits)
 * @returns `true` if the CPF has valid check digits
 */
export function validateCpf(cpf: string): boolean {
  const cleaned = cpf.replace(/\D/g, "");

  if (cleaned.length !== 11) return false;
  if (/^(\d)\1{10}$/.test(cleaned)) return false;

  const digits = cleaned.split("").map(Number);

  const first = calculateCpfCheckDigit(
    digits.slice(0, 9),
    [10, 9, 8, 7, 6, 5, 4, 3, 2],
  );
  if (first !== digits[9]) return false;

  const second = calculateCpfCheckDigit(
    digits.slice(0, 10),
    [11, 10, 9, 8, 7, 6, 5, 4, 3, 2],
  );
  return second === digits[10];
}