diff --git a/docs/utils/cpfValidator.md b/docs/utils/cpfValidator.md
new file mode 100644
index 0000000..6eabd55
--- /dev/null
+++ b/docs/utils/cpfValidator.md
@@ -0,0 +1,74 @@
+# CpfValidator
+
+Utilitário para validar CPFs com e sem máscara.
+
+## Instalação e Importação
+
+```typescript
+import { cpfValidator } from '@sysvale/foundry';
+```
+
+## Função
+
+### `cpfValidator()`
+
+Valida CPFs com e sem máscara, indicando se os mesmos são válidos.
+
+#### Sintaxes
+
+```typescript
+cpfValidator(value: string): boolean
+```
+
+#### Parâmetros
+
+**Assinatura 1:**
+
+- **`value`** (`string`): CPF (com ou sem máscara) a ser validado
+
+
+
+#### Retorno
+
+`boolean` - Resultado da validação, `true` para CPF válido e `false` para inválido
+
+
+
+#### Exemplos
+
+**Usando CPF com máscara:**
+
+```typescript
+cpfValidator('252.512.510-09'); // → true
+
+cpfValidator('000.000.000-00'); // → false
+```
+
+
+
+**Usando CPF sem máscara:**
+
+```typescript
+cpfValidator('25251251009'); // → true
+
+cpfValidator('00000000000'); // → false
+```
+
+
+
+#### Tratamento de Erros
+
+A função lança um erro quando os parâmetros obrigatórios não são fornecidos:
+
+```typescript
+// ❌ Erro: tipagem do parâmetro é inválida
+cpfValidator(71234567823);
+// → Error: O tipo do parâmetro passado é inválido.
+
+// ✅ Correto
+cpfValidator('71234567823'); // → false
+```
+
+## Notas
+
+- A função é **type-safe**
diff --git a/docs/utils/index.md b/docs/utils/index.md
index 6230e10..c15b6b9 100644
--- a/docs/utils/index.md
+++ b/docs/utils/index.md
@@ -17,3 +17,9 @@ Função para formatar listas de strings com vírgulas e conjunção "e".
Função para sanitizar dados de formulário e aplicar transformações antes de enviá-los ao backend.
- [Documentação](./sanitizeForm.md)
+
+### cpfValidator()
+
+Função para validar CPFs com e sem máscara.
+
+- [Documentação](./cpfValidator.md)
diff --git a/package-lock.json b/package-lock.json
index e9a6e8a..ca94521 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@sysvale/foundry",
- "version": "1.0.0",
+ "version": "1.5.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@sysvale/foundry",
- "version": "1.0.0",
+ "version": "1.5.1",
"license": "Apache-2.0",
"devDependencies": {
"@eslint/js": "^9.32.0",
diff --git a/package.json b/package.json
index 363141f..9ce040e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@sysvale/foundry",
- "version": "1.5.1",
+ "version": "1.6.0",
"description": "A forge for composables, helpers, and front-end utilities.",
"type": "module",
"main": "./dist/foundry.cjs.js",
diff --git a/src/index.ts b/src/index.ts
index 49cfc76..d4501ee 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,6 +1,7 @@
export * from './utils/pluralize';
export * from './utils/commaline';
export * from './utils/sanitizeForm';
+export * from './utils/cpfValidator';
export { maskCpf, removeCpfMask } from './formatters/cpf';
export { maskCns, removeCnsMask } from './formatters/cns';
export { maskPhone, removePhoneMask } from './formatters/phone';
diff --git a/src/utils/cpfValidator.ts b/src/utils/cpfValidator.ts
new file mode 100644
index 0000000..9ba8842
--- /dev/null
+++ b/src/utils/cpfValidator.ts
@@ -0,0 +1,65 @@
+function calcChecker1(firstNineDigits: string) {
+ let sum = 0;
+
+ for (let j = 0; j < 9; ++j) {
+ sum += Number(firstNineDigits.charAt(j)) * (10 - j);
+ }
+
+ const lastSumChecker1 = sum % 11;
+ const checker1 = lastSumChecker1 < 2 ? 0 : 11 - lastSumChecker1;
+
+ return checker1;
+}
+
+function calcChecker2(cpfWithChecker1: string) {
+ let sum = 0;
+
+ for (let k = 0; k < 10; ++k) {
+ sum += Number(cpfWithChecker1.charAt(k)) * (11 - k);
+ }
+ const lastSumChecker2 = sum % 11;
+ const checker2 = lastSumChecker2 < 2 ? 0 : 11 - lastSumChecker2;
+
+ return checker2;
+}
+
+function cleaner(value: string) {
+ return value.replace(/[^\d]/g, '');
+}
+
+/**
+ * Valida CPFs com e sem máscara.
+ *
+ * @param { string } value
+ * @returns { boolean }
+ */
+export function cpfValidator(value: string): boolean {
+ if (typeof value !== 'string') {
+ throw new Error('O tipo do parâmetro passado é inválido.');
+ }
+
+ if (!value) {
+ return true;
+ }
+
+ const cleanCPF = cleaner(value);
+ const firstNineDigits = cleanCPF.substring(0, 9);
+ const checker = cleanCPF.substring(9, 11);
+ let result = false;
+
+ for (let i = 0; i < 10; i++) {
+ if (firstNineDigits + checker === Array(12).join(i.toString())) {
+ return false;
+ }
+ }
+
+ const checker1 = calcChecker1(firstNineDigits);
+ const checker2 = calcChecker2(`${firstNineDigits}${checker1}`);
+
+ if (checker.toString() === checker1.toString() + checker2.toString()) {
+ result = true;
+ } else {
+ result = false;
+ }
+ return result;
+}
diff --git a/tests/cpfValidator.test.ts b/tests/cpfValidator.test.ts
new file mode 100644
index 0000000..e15e750
--- /dev/null
+++ b/tests/cpfValidator.test.ts
@@ -0,0 +1,36 @@
+import { describe, expect, test } from 'vitest';
+import { cpfValidator } from '../src/utils/cpfValidator';
+
+describe('cpfValidator()', () => {
+ test('retorna true quando string vazia é passada', () => {
+ expect(cpfValidator('')).toBe(true);
+ });
+
+ test('retorna false quando cpf inválido com máscara é passado', () => {
+ expect(cpfValidator('111.111.111-11')).toBe(false);
+ });
+
+ test('retorna true quando cpf válido com máscara é passado', () => {
+ expect(cpfValidator('252.512.510-09')).toBe(true);
+ });
+
+ test('retorna false quando cpf inválido sem máscara é passado', () => {
+ expect(cpfValidator('11111111111')).toBe(false);
+ });
+
+ test('retorna true quando cpf válido sem máscara é passado', () => {
+ expect(cpfValidator('25251251009')).toBe(true);
+ });
+
+ test('retorna false quando cpf possui menos que 11 dígitos', () => {
+ expect(cpfValidator('2525125100')).toBe(false);
+ });
+
+ test('retorna false quando cpf possui uma letra', () => {
+ expect(cpfValidator('25251251a09')).toBe(false);
+ });
+
+ test('lança erro quando parâmetro é do tipo number', () => {
+ expect(() => cpfValidator(12341789324)).toThrowError();
+ });
+});