From da581ee2865b9b6ecbb07a77cc159f14283b0aae Mon Sep 17 00:00:00 2001 From: jvictordev1 Date: Thu, 13 Nov 2025 08:32:13 -0300 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20adiciona=20valida=C3=A7=C3=A3o=20de?= =?UTF-8?q?=20cns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/utils/cnsValidator.md | 74 +++++++++++++++++++++ docs/utils/index.md | 6 ++ package.json | 2 +- src/index.ts | 1 + src/utils/cnsValidator.ts | 132 +++++++++++++++++++++++++++++++++++++ tests/cnsValidator.test.ts | 40 +++++++++++ 6 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 docs/utils/cnsValidator.md create mode 100644 src/utils/cnsValidator.ts create mode 100644 tests/cnsValidator.test.ts diff --git a/docs/utils/cnsValidator.md b/docs/utils/cnsValidator.md new file mode 100644 index 0000000..60b5c54 --- /dev/null +++ b/docs/utils/cnsValidator.md @@ -0,0 +1,74 @@ +# CnsValidator + +Utilitário para validar CNS com e sem máscara. + +## Instalação e Importação + +```typescript +import { cnsValidator } from '@sysvale/foundry'; +``` + +## Função + +### `cnsValidator()` + +Valida CNS com e sem máscara, indicando se os mesmos são válidos. + +#### Sintaxes + +```typescript +cnsValidator(vlrCNS: string): boolean +``` + +#### Parâmetros + +**Assinatura 1:** + +- **`vlrCNS`** (`string`): CNS (com ou sem máscara) a ser validado + +
+ +#### Retorno + +`boolean` - Resultado da validação, `true` para CNS válido e `false` para inválido + +
+ +#### Exemplos + +**Usando CNS com máscara:** + +```typescript +cnsValidator('728 1376 2535 3587'); // → true + +cnsValidator('111 1111 1111 1111'); // → false +``` + +
+ +**Usando CNS sem máscara:** + +```typescript +cnsValidator('728137625353587'); // → true + +cnsValidator('111111111111111'); // → 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 +cnsValidator(728137625353587); +// → Error: O tipo do parâmetro passado é inválido. + +// ✅ Correto +cnsValidator('728137625353587'); +``` + +## Notas + +- A função é **type-safe** diff --git a/docs/utils/index.md b/docs/utils/index.md index c15b6b9..1db465b 100644 --- a/docs/utils/index.md +++ b/docs/utils/index.md @@ -23,3 +23,9 @@ Função para sanitizar dados de formulário e aplicar transformações antes de Função para validar CPFs com e sem máscara. - [Documentação](./cpfValidator.md) + +### cnsValidator() + +Função para validar CNS com e sem máscara. + +- [Documentação](./cnsValidator.md) diff --git a/package.json b/package.json index 9ce040e..8c70325 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sysvale/foundry", - "version": "1.6.0", + "version": "1.7.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 d4501ee..92d318e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,7 @@ export * from './utils/pluralize'; export * from './utils/commaline'; export * from './utils/sanitizeForm'; export * from './utils/cpfValidator'; +export * from './utils/cnsValidator'; export { maskCpf, removeCpfMask } from './formatters/cpf'; export { maskCns, removeCnsMask } from './formatters/cns'; export { maskPhone, removePhoneMask } from './formatters/phone'; diff --git a/src/utils/cnsValidator.ts b/src/utils/cnsValidator.ts new file mode 100644 index 0000000..ec7e5a7 --- /dev/null +++ b/src/utils/cnsValidator.ts @@ -0,0 +1,132 @@ +function ValidaCNS_PROV(vlrCNS: string) { + let pis; + let resto; + let soma; + + pis = vlrCNS.substring(0, 15); + + if (pis === '') { + return false; + } + + if ( + vlrCNS.substring(0, 1) != '7' && + vlrCNS.substring(0, 1) != '8' && + vlrCNS.substring(0, 1) != '9' + ) { + return false; + } + + soma = + parseInt(pis.substring(0, 1), 10) * 15 + + parseInt(pis.substring(1, 2), 10) * 14 + + parseInt(pis.substring(2, 3), 10) * 13 + + parseInt(pis.substring(3, 4), 10) * 12 + + parseInt(pis.substring(4, 5), 10) * 11 + + parseInt(pis.substring(5, 6), 10) * 10 + + parseInt(pis.substring(6, 7), 10) * 9 + + parseInt(pis.substring(7, 8), 10) * 8 + + parseInt(pis.substring(8, 9), 10) * 7 + + parseInt(pis.substring(9, 10), 10) * 6 + + parseInt(pis.substring(10, 11), 10) * 5 + + parseInt(pis.substring(11, 12), 10) * 4 + + parseInt(pis.substring(12, 13), 10) * 3 + + parseInt(pis.substring(13, 14), 10) * 2 + + parseInt(pis.substring(14, 15), 10) * 1; + + resto = soma % 11; + + if (!resto) { + return true; + } + + return false; +} + +function validaCNS(vlrCNS: string) { + let soma = 0; + let resto = 0; + let dv = 0; + let pis = ''; + let resultado = ''; + const tamCNS = vlrCNS.length; + + if (tamCNS != 15) { + return false; + } + + pis = vlrCNS.substring(0, 11); + soma = + Number(pis.substring(0, 1)) * 15 + + Number(pis.substring(1, 2)) * 14 + + Number(pis.substring(2, 3)) * 13 + + Number(pis.substring(3, 4)) * 12 + + Number(pis.substring(4, 5)) * 11 + + Number(pis.substring(5, 6)) * 10 + + Number(pis.substring(6, 7)) * 9 + + Number(pis.substring(7, 8)) * 8 + + Number(pis.substring(8, 9)) * 7 + + Number(pis.substring(9, 10)) * 6 + + Number(pis.substring(10, 11)) * 5; + resto = soma % 11; + dv = 11 - resto; + + if (dv == 11) { + dv = 0; + } + + if (dv == 10) { + soma = + Number(pis.substring(0, 1)) * 15 + + Number(pis.substring(1, 2)) * 14 + + Number(pis.substring(2, 3)) * 13 + + Number(pis.substring(3, 4)) * 12 + + Number(pis.substring(4, 5)) * 11 + + Number(pis.substring(5, 6)) * 10 + + Number(pis.substring(6, 7)) * 9 + + Number(pis.substring(7, 8)) * 8 + + Number(pis.substring(8, 9)) * 7 + + Number(pis.substring(9, 10)) * 6 + + Number(pis.substring(10, 11)) * 5 + + 2; + resto = soma % 11; + dv = 11 - resto; + resultado = `${pis}001${String(dv)}`; + } else { + resultado = `${pis}000${String(dv)}`; + } + + if (vlrCNS != resultado) { + return false; + } + + return true; +} + +/** + * Valida CNS com e sem máscara. + * + * @param { string } vlrCNS + * @returns { boolean } + */ +export function cnsValidator(vlrCNS: string) { + if (typeof vlrCNS !== 'string') { + throw new Error('O tipo do parâmetro passado é inválido.'); + } + + if (!vlrCNS || !vlrCNS.length) { + return true; + } + + vlrCNS = vlrCNS.replace(/\D/g, ''); + + if (vlrCNS.length != 15) { + return false; + } + + if ([1, 2].indexOf(parseInt(vlrCNS.substring(0, 1))) != -1) { + return validaCNS(vlrCNS); + } + + return ValidaCNS_PROV(vlrCNS); +} diff --git a/tests/cnsValidator.test.ts b/tests/cnsValidator.test.ts new file mode 100644 index 0000000..fd0f78f --- /dev/null +++ b/tests/cnsValidator.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, test } from 'vitest'; +import { cnsValidator } from '../src/utils/cnsValidator'; + +describe('cnsValidator()', () => { + test('retorna true quando string vazia é passada', () => { + expect(cnsValidator('')).toBe(true); + }); + + test('retorna false quando cns inválido com máscara é passado', () => { + expect(cnsValidator('111 1111 1111 1111')).toBe(false); + }); + + test('retorna true quando cns válido com máscara é passado', () => { + expect(cnsValidator('728 1376 2535 3587')).toBe(true); + }); + + test('retorna false quando cns inválido sem máscara é passado', () => { + expect(cnsValidator('111111111111111')).toBe(false); + }); + + test('retorna true quando cns válido sem máscara é passado', () => { + expect(cnsValidator('728137625353587')).toBe(true); + }); + + test('retorna false quando cns possui menos que 15 dígitos', () => { + expect(cnsValidator('11111111111')).toBe(false); + }); + + test('retorna false quando cns possui mais que 15 dígitos', () => { + expect(cnsValidator('7281376253535879')).toBe(false); + }); + + test('retorna false quando cns possui uma letra', () => { + expect(cnsValidator('11111111111111a')).toBe(false); + }); + + test('lança erro quando parâmetro é do tipo number', () => { + expect(() => cnsValidator(12341789324)).toThrowError(); + }); +}); From fe67a69c8d06b6c242030d64255200c7da0ff2cd Mon Sep 17 00:00:00 2001 From: jvictordev1 Date: Thu, 13 Nov 2025 09:58:33 -0300 Subject: [PATCH 2/2] =?UTF-8?q?refactor:=20altera=20variaveis=20para=20ing?= =?UTF-8?q?les=20e=20remove=20valida=C3=A7=C3=A3o=20positiva=20para=20camp?= =?UTF-8?q?o=20vazio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/utils/cnsValidator.md | 4 +- src/utils/cnsValidator.ts | 80 ++++++++++++++++++-------------------- tests/cnsValidator.test.ts | 4 +- 3 files changed, 42 insertions(+), 46 deletions(-) diff --git a/docs/utils/cnsValidator.md b/docs/utils/cnsValidator.md index 60b5c54..ee30370 100644 --- a/docs/utils/cnsValidator.md +++ b/docs/utils/cnsValidator.md @@ -17,14 +17,14 @@ Valida CNS com e sem máscara, indicando se os mesmos são válidos. #### Sintaxes ```typescript -cnsValidator(vlrCNS: string): boolean +cnsValidator(value: string): boolean ``` #### Parâmetros **Assinatura 1:** -- **`vlrCNS`** (`string`): CNS (com ou sem máscara) a ser validado +- **`value`** (`string`): CNS (com ou sem máscara) a ser validado
diff --git a/src/utils/cnsValidator.ts b/src/utils/cnsValidator.ts index ec7e5a7..5f5961e 100644 --- a/src/utils/cnsValidator.ts +++ b/src/utils/cnsValidator.ts @@ -1,23 +1,23 @@ -function ValidaCNS_PROV(vlrCNS: string) { +function checkCnsValue(value: string) { let pis; - let resto; - let soma; + let rest; + let sum; - pis = vlrCNS.substring(0, 15); + pis = value.substring(0, 15); if (pis === '') { return false; } if ( - vlrCNS.substring(0, 1) != '7' && - vlrCNS.substring(0, 1) != '8' && - vlrCNS.substring(0, 1) != '9' + value.substring(0, 1) !== '7' && + value.substring(0, 1) !== '8' && + value.substring(0, 1) !== '9' ) { return false; } - soma = + sum = parseInt(pis.substring(0, 1), 10) * 15 + parseInt(pis.substring(1, 2), 10) * 14 + parseInt(pis.substring(2, 3), 10) * 13 + @@ -34,29 +34,29 @@ function ValidaCNS_PROV(vlrCNS: string) { parseInt(pis.substring(13, 14), 10) * 2 + parseInt(pis.substring(14, 15), 10) * 1; - resto = soma % 11; + rest = sum % 11; - if (!resto) { + if (!rest) { return true; } return false; } -function validaCNS(vlrCNS: string) { - let soma = 0; - let resto = 0; - let dv = 0; +function checkCnsFirstElevenDigits(value: string) { + let sum = 0; + let rest = 0; + let validatorDigit = 0; let pis = ''; - let resultado = ''; - const tamCNS = vlrCNS.length; + let result = ''; + const cnsSize = value.length; - if (tamCNS != 15) { + if (cnsSize !== 15) { return false; } - pis = vlrCNS.substring(0, 11); - soma = + pis = value.substring(0, 11); + sum = Number(pis.substring(0, 1)) * 15 + Number(pis.substring(1, 2)) * 14 + Number(pis.substring(2, 3)) * 13 + @@ -68,15 +68,15 @@ function validaCNS(vlrCNS: string) { Number(pis.substring(8, 9)) * 7 + Number(pis.substring(9, 10)) * 6 + Number(pis.substring(10, 11)) * 5; - resto = soma % 11; - dv = 11 - resto; + rest = sum % 11; + validatorDigit = 11 - rest; - if (dv == 11) { - dv = 0; + if (validatorDigit === 11) { + validatorDigit = 0; } - if (dv == 10) { - soma = + if (validatorDigit === 10) { + sum = Number(pis.substring(0, 1)) * 15 + Number(pis.substring(1, 2)) * 14 + Number(pis.substring(2, 3)) * 13 + @@ -89,14 +89,14 @@ function validaCNS(vlrCNS: string) { Number(pis.substring(9, 10)) * 6 + Number(pis.substring(10, 11)) * 5 + 2; - resto = soma % 11; - dv = 11 - resto; - resultado = `${pis}001${String(dv)}`; + rest = sum % 11; + validatorDigit = 11 - rest; + result = `${pis}001${String(validatorDigit)}`; } else { - resultado = `${pis}000${String(dv)}`; + result = `${pis}000${String(validatorDigit)}`; } - if (vlrCNS != resultado) { + if (value !== result) { return false; } @@ -106,27 +106,23 @@ function validaCNS(vlrCNS: string) { /** * Valida CNS com e sem máscara. * - * @param { string } vlrCNS + * @param { string } value * @returns { boolean } */ -export function cnsValidator(vlrCNS: string) { - if (typeof vlrCNS !== 'string') { +export function cnsValidator(value: string) { + if (typeof value !== 'string') { throw new Error('O tipo do parâmetro passado é inválido.'); } - if (!vlrCNS || !vlrCNS.length) { - return true; - } - - vlrCNS = vlrCNS.replace(/\D/g, ''); + const unmaskedValue = value.replace(/\D/g, ''); - if (vlrCNS.length != 15) { + if (unmaskedValue.length !== 15) { return false; } - if ([1, 2].indexOf(parseInt(vlrCNS.substring(0, 1))) != -1) { - return validaCNS(vlrCNS); + if ([1, 2].indexOf(parseInt(unmaskedValue.substring(0, 1))) != -1) { + return checkCnsFirstElevenDigits(unmaskedValue); } - return ValidaCNS_PROV(vlrCNS); + return checkCnsValue(unmaskedValue); } diff --git a/tests/cnsValidator.test.ts b/tests/cnsValidator.test.ts index fd0f78f..be7fe35 100644 --- a/tests/cnsValidator.test.ts +++ b/tests/cnsValidator.test.ts @@ -2,8 +2,8 @@ import { describe, expect, test } from 'vitest'; import { cnsValidator } from '../src/utils/cnsValidator'; describe('cnsValidator()', () => { - test('retorna true quando string vazia é passada', () => { - expect(cnsValidator('')).toBe(true); + test('retorna false quando string vazia é passada', () => { + expect(cnsValidator('')).toBe(false); }); test('retorna false quando cns inválido com máscara é passado', () => {