├── .prettierignore ├── .npmignore ├── .prettierrc.js ├── .gitignore ├── jest.config.js ├── test ├── utils-randomLetter.test.ts ├── utils-sumToDV.test.ts ├── utils-checkRepeatedSequence.test.ts ├── utils-removeFromPosition.test.ts ├── utils-insertAtPosition.test.ts ├── utils-applyMask.test.ts ├── utils-fakeNumber.test.ts ├── utils-sumElementsByMultipliers.test.ts ├── index.test.ts ├── cnh.test.ts ├── utils-clearValue.test.ts ├── renavam.test.ts ├── tituloEleitor.test.ts ├── postalCode.test.ts ├── cpf.test.ts ├── pisPasep.test.ts ├── nup17.test.ts ├── judicialProcess.test.ts └── cnpj.test.ts ├── src ├── ValidationBRError.ts ├── index.ts ├── renavam.ts ├── pisPasep.ts ├── cnh.ts ├── postalCode.ts ├── cpf.ts ├── tituloEleitor.ts ├── nup17.ts ├── cnpj.ts ├── judicialProcess.ts └── utils.ts ├── tsconfig.json ├── .github └── workflows │ └── test.yml ├── package.json ├── eslint.config.js └── readme.md /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | node_modules 3 | coverage 4 | docs 5 | .github 6 | dist/*.test.js 7 | dist/*.test.js.map 8 | dist/*.test.d.ts -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: 'all', 4 | singleQuote: true, 5 | printWidth: 100, 6 | tabWidth: 2, 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | node_modules 4 | 5 | *.log 6 | yarn-error.log* 7 | yarn-debug.log* 8 | 9 | dist/ 10 | 11 | coverage 12 | 13 | .eslintcache 14 | 15 | *.tgz 16 | 17 | .yarn-integrity 18 | 19 | .env 20 | 21 | docs/* 22 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: ['ts', 'js'], 3 | transform: { 4 | '^.+\\.(ts|tsx)$': ['ts-jest', { /* ts-jest config goes here in Jest */ }], 5 | }, 6 | testRegex: '(/test/.*\\.(test|spec))\\.(jsx?|tsx?)$', 7 | testEnvironment: 'node', 8 | coverageDirectory: './coverage/', 9 | collectCoverage: true, 10 | coverageProvider: 'v8', 11 | } 12 | -------------------------------------------------------------------------------- /test/utils-randomLetter.test.ts: -------------------------------------------------------------------------------- 1 | import { randomLetter } from '../src/utils' 2 | 3 | describe('randomLetter()', () => { 4 | test('forceLength = false', () => { 5 | const list = [...Array(1000)] 6 | 7 | list.forEach(() => { 8 | const letter = randomLetter() 9 | 10 | expect(letter).toMatch(/^[A-Z]{1}$/) 11 | expect(typeof letter).toBe('string') 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /src/ValidationBRError.ts: -------------------------------------------------------------------------------- 1 | export default class ValidationBRError extends Error { 2 | static INVALID_DV = new ValidationBRError('Dígito verificador inválido') 3 | static EMPTY_VALUE = new ValidationBRError('Valor não preenchido') 4 | static MAX_LEN_EXCEDEED = new ValidationBRError('Número de caracteres excedido') 5 | static REPEATED_SEQUENCE = new ValidationBRError('Sequência de números repetidos não permitida') 6 | } 7 | -------------------------------------------------------------------------------- /test/utils-sumToDV.test.ts: -------------------------------------------------------------------------------- 1 | import { sumToDV } from '../src/utils' 2 | 3 | describe('sumToDV()', () => { 4 | test('Os resultados devem ser os esperados', () => { 5 | const list = [ 6 | { value: 102, expected: 8 }, 7 | { value: 120, expected: 1 }, 8 | { value: 162, expected: 3 }, 9 | { value: 179, expected: 8 }, 10 | ] 11 | 12 | list.forEach((item) => { 13 | expect(sumToDV(item.value)).toBe(item.expected) 14 | }) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /test/utils-checkRepeatedSequence.test.ts: -------------------------------------------------------------------------------- 1 | import { checkRepeatedSequence } from "../src/utils" 2 | 3 | test.each([ 4 | '11111111', 5 | '1111', 6 | ])('testa se %s tem todos os valores iguais', (value) => { 7 | expect(checkRepeatedSequence(value)).toBeTruthy() 8 | }) 9 | 10 | test.each([ 11 | '12345678', 12 | '11111112', 13 | '1234', 14 | '2111', 15 | ])('testa se %s não tem todos os valores iguais', (value) => { 16 | expect(checkRepeatedSequence(value)).toBeFalsy() 17 | }) -------------------------------------------------------------------------------- /test/utils-removeFromPosition.test.ts: -------------------------------------------------------------------------------- 1 | import { removeFromPosition } from '../src/utils' 2 | 3 | describe('removeFromPosition()', () => { 4 | test('', () => { 5 | // 6 | const list = [ 7 | { value: 'Jossé', start: 2, end: 3, expected: 'José' }, 8 | { value: 'Cláuudio', start: 4, end: 5, expected: 'Cláudio' }, 9 | ] 10 | 11 | list.forEach((item) => { 12 | expect(removeFromPosition(item.value, item.start, item.end)).toBe(item.expected) 13 | }) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /test/utils-insertAtPosition.test.ts: -------------------------------------------------------------------------------- 1 | import { insertAtPosition } from '../src/utils' 2 | 3 | describe('insertAtPosition()', () => { 4 | test('Deve inserir um caractere em uma determinada posição da string', () => { 5 | // 6 | const list = [ 7 | { value: 'AAABBB', insert: 'C', position: 3, expected: 'AAACBBB' }, 8 | { value: 'J Med', insert: 'Cl ', position: 2, expected: 'J Cl Med' }, 9 | ] 10 | 11 | list.forEach((item) => { 12 | expect(insertAtPosition(item.value, item.insert, item.position)).toBe(item.expected) 13 | }) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /test/utils-applyMask.test.ts: -------------------------------------------------------------------------------- 1 | import { applyMask } from '../src/utils' 2 | 3 | describe('applyMask()', () => { 4 | const items = [ 5 | { value: '123456', mask: '00000-0', expected: '12345-6' }, 6 | { value: '12345', mask: '00000-0', expected: '01234-5' }, 7 | { value: '123456789', mask: '00000-0', expected: '12345-6' }, 8 | { value: 123456789, mask: '00000-0', expected: '12345-6' }, 9 | ] 10 | 11 | test('Máscara deve ser aplicada com o valor e tamanho correto', () => { 12 | items.forEach((item) => { 13 | expect(item.expected.length).toBe(item.mask.length) 14 | expect(applyMask(item.value, item.mask)).toBe(item.expected) 15 | }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "src", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "target": "ES2018", 7 | "moduleResolution": "node", 8 | "sourceMap": true, 9 | "outDir": "./dist", 10 | "declaration": true, 11 | "noImplicitAny": true, 12 | "noImplicitReturns": true, 13 | "noImplicitThis": true, 14 | "strict": true, 15 | "allowJs": false, 16 | "types": ["node", "jest"], 17 | "typeRoots": ["node_modules/@types", "index.d.ts"], 18 | "lib": ["ES2022"], 19 | "paths": { 20 | "*": ["node_modules/*"] 21 | } 22 | }, 23 | "exclude": ["./tsconfig.json"], 24 | "include": ["./src/**/*", "./test/**/*"] 25 | } 26 | -------------------------------------------------------------------------------- /test/utils-fakeNumber.test.ts: -------------------------------------------------------------------------------- 1 | import { fakeNumber } from'../src/utils' 2 | 3 | describe('fakeNumber()', () => { 4 | test.each([...Array(10)])('forceLength = true', () => { 5 | const num = fakeNumber(4, true) 6 | 7 | expect(num).toHaveLength(4) 8 | expect(typeof num).toBe('string') 9 | expect(num).toMatch(/^[\d]+$/) 10 | }) 11 | 12 | test.each([...Array(10)])('forceLength = false', () => { 13 | const num = fakeNumber(4) 14 | 15 | expect(+num).toBeLessThanOrEqual(9999) 16 | expect(+num).toBeGreaterThanOrEqual(0) 17 | expect(typeof num).toBe('string') 18 | }) 19 | 20 | 21 | test.each([...Array(10)])('isAlpha = true', () => { 22 | const num = fakeNumber(4, true, true) 23 | 24 | expect(num).toHaveLength(4) 25 | expect(typeof num).toBe('string') 26 | expect(num).toMatch(/^[0-9A-Z]+$/) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /test/utils-sumElementsByMultipliers.test.ts: -------------------------------------------------------------------------------- 1 | import { sumElementsByMultipliers } from '../src/utils' 2 | 3 | describe('sumElementsByMultipliers()', () => { 4 | test('Tipo do retorno', () => { 5 | const sum = sumElementsByMultipliers('1234', [9, 8, 7, 6]) 6 | 7 | expect(typeof sum).toBe('number') 8 | expect(sum).toBe(70) 9 | }) 10 | 11 | test.each([ 12 | { input: '1234', multipliers: [9, 8, 7, 6], expected: 70 }, 13 | // cnpj 14 | { input: '112223330001', multipliers: [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2], expected: 102 }, 15 | // cpf 16 | { input: '280012389', multipliers: [10, 9, 8, 7, 6, 5, 4, 3, 2], expected: 162 }, 17 | { input: '2800123893', multipliers: [11, 10, 9, 8, 7, 6, 5, 4, 3, 2], expected: 201 }, 18 | // titulo 19 | ])('Valores retornados', (item) => { 20 | const sum = sumElementsByMultipliers(item.input, item.multipliers) 21 | expect(sum).toBe(item.expected) 22 | 23 | }) 24 | }) -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | # on: [push] 4 | on: 5 | push: 6 | branches: [main, dev] 7 | pull_request: 8 | branches: [main, dev] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [18.x, 20.x, 22.x] 17 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 18 | 19 | steps: 20 | # Checks-out the repository under $GITHUB_WORKSPACE 21 | - uses: actions/checkout@v2 22 | 23 | # Install Node Js 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v2 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | 29 | # Install Node Modules 30 | - name: Install Node Modules 31 | run: npm install 32 | 33 | # Runs a set of commands using the runners shell 34 | - name: Build typescript code 35 | run: npm run build 36 | 37 | # Runs a set of commands using the runners shell 38 | - name: Runs Unit Tests 39 | run: npm run test 40 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { validate as cnh } from './cnh' 2 | import { validate as cnpj } from './cnpj' 3 | import { validate as cpf } from './cpf' 4 | import { validate as judicialProcess } from './judicialProcess' 5 | import { validate as nup17 } from './nup17' 6 | import { validate as pisPasep } from './pisPasep' 7 | import { validate as postalCode } from './postalCode' 8 | import { validate as renavam } from './renavam' 9 | import { validate as tituloEleitor } from './tituloEleitor' 10 | 11 | export const isCNH = (value: string | number): boolean => cnh(value) 12 | export const isCNPJ = (value: string | number): boolean => cnpj(value) 13 | export const isCPF = (value: string | number): boolean => cpf(value) 14 | export const isJudicialProcess = (value: string): boolean => judicialProcess(value) 15 | export const isPIS = (value: string): boolean => pisPasep(value) 16 | export const isPostalCode = (value: string): boolean => postalCode(value) 17 | export const isRenavam = (value: string): boolean => renavam(value) 18 | export const isTituloEleitor = (value: string | number): boolean => tituloEleitor(value) 19 | export const isNUP17 = (value: string): boolean => nup17(value) 20 | 21 | export default { 22 | isCNH, 23 | isCNPJ, 24 | isCPF, 25 | isJudicialProcess, 26 | isPIS, 27 | isPostalCode, 28 | isRenavam, 29 | isTituloEleitor, 30 | isNUP17, 31 | } 32 | -------------------------------------------------------------------------------- /test/index.test.ts: -------------------------------------------------------------------------------- 1 | import ValidateBR, { 2 | isCNH, 3 | isCNPJ, 4 | isCPF, 5 | isJudicialProcess, 6 | isNUP17, 7 | isPIS, 8 | isPostalCode, 9 | isRenavam, 10 | isTituloEleitor, 11 | } from '../src/index' 12 | 13 | describe('ValidateBR', () => { 14 | test('Deve importar isCNH', () => { 15 | expect(ValidateBR.isCNH).toBeDefined() 16 | expect(isCNH).toBeDefined() 17 | 18 | expect(ValidateBR.isCNH('69044271146')).toBeTruthy() 19 | expect(isCNH('69044271146')).toBeTruthy() 20 | }) 21 | 22 | test('Deve importar isCNPJ', () => { 23 | expect(ValidateBR.isCNPJ).toBeDefined() 24 | expect(isCNPJ).toBeDefined() 25 | 26 | expect(ValidateBR.isCNPJ('32432147000147')).toBeTruthy() 27 | expect(isCNPJ('32432147000147')).toBeTruthy() 28 | }) 29 | 30 | test('Deve importar isCPF', () => { 31 | expect(ValidateBR.isCPF).toBeDefined() 32 | expect(isCPF).toBeDefined() 33 | 34 | expect(ValidateBR.isCPF('15886489070')).toBeTruthy() 35 | expect(isCPF('15886489070')).toBeTruthy() 36 | }) 37 | 38 | test('Deve importar isJudicialProcess', () => { 39 | expect(ValidateBR.isJudicialProcess).toBeDefined() 40 | expect(isJudicialProcess).toBeDefined() 41 | 42 | expect(ValidateBR.isJudicialProcess('08002732820164058400')).toBeTruthy() 43 | expect(isJudicialProcess('08002732820164058400')).toBeTruthy() 44 | }) 45 | 46 | test('Deve importar isNUP17', () => { 47 | expect(ValidateBR.isNUP17).toBeDefined() 48 | expect(isNUP17).toBeDefined() 49 | 50 | expect(ValidateBR.isNUP17('23037001462202165')).toBeTruthy() 51 | expect(isNUP17('23037001462202165')).toBeTruthy() 52 | }) 53 | 54 | test('Deve importar isPIS', () => { 55 | expect(ValidateBR.isPIS).toBeDefined() 56 | expect(isPIS).toBeDefined() 57 | 58 | expect(ValidateBR.isPIS('23795126955')).toBeTruthy() 59 | expect(isPIS('23795126955')).toBeTruthy() 60 | }) 61 | 62 | test('Deve importar isPostalCode', () => { 63 | expect(ValidateBR.isPostalCode).toBeDefined() 64 | expect(isPostalCode).toBeDefined() 65 | 66 | expect(ValidateBR.isPostalCode('PN718252423BR')).toBeTruthy() 67 | expect(isPostalCode('PN718252423BR')).toBeTruthy() 68 | }) 69 | 70 | test('Deve importar isRenavam', () => { 71 | expect(ValidateBR.isRenavam).toBeDefined() 72 | expect(isRenavam).toBeDefined() 73 | 74 | expect(ValidateBR.isRenavam('80499688374')).toBeTruthy() 75 | expect(isRenavam('80499688374')).toBeTruthy() 76 | }) 77 | 78 | test('Deve importar isTituloEleitor', () => { 79 | expect(ValidateBR.isTituloEleitor).toBeDefined() 80 | expect(isTituloEleitor).toBeDefined() 81 | 82 | expect(ValidateBR.isTituloEleitor('153036161686')).toBeTruthy() 83 | expect(isTituloEleitor('153036161686')).toBeTruthy() 84 | }) 85 | }) 86 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "validation-br", 3 | "description": "Brazilian personal documents validation - cpf, cnpj, titulo, pis/pasep, cnh, renavam, processos judiciais, protocolo federal, código de rastreamento dos correios", 4 | "version": "1.5.2", 5 | "main": "dist/index.js", 6 | "types": "./dist/index.d.ts", 7 | "author": "Cláudio Medeiros ", 8 | "license": "MIT", 9 | "scripts": { 10 | "build": "tsc", 11 | "test": "jest", 12 | "test:watch": "jest --watch", 13 | "test:coverage": "jest --collectCoverageFrom=src/**/*.ts --coverage", 14 | "format": "prettier --write \"./**/*.{js,ts,json}\"", 15 | "prepublish": "npm run build" 16 | }, 17 | "lint-staged": { 18 | "*.{js,ts}": [ 19 | "prettier --write", 20 | "eslint --cache --fix" 21 | ] 22 | }, 23 | "devDependencies": { 24 | "@types/jest": "^27.4.0", 25 | "@types/node": "^14.14.2", 26 | "jest": "^29.7.0", 27 | "ts-jest": "^29.2.5", 28 | "ts-node": "^9.0.0", 29 | "typescript": "^5.6.2" 30 | }, 31 | "repository": { 32 | "url": "https://github.com/klawdyo/validation-br" 33 | }, 34 | "bugs": { 35 | "url": "https://github.com/klawdyo/validation-br/issues" 36 | }, 37 | "homepage": "https://github.com/klawdyo/validation-br#readme", 38 | "keywords": [ 39 | "validation-br", 40 | "validation", 41 | "personal documents", 42 | "document", 43 | "brazil", 44 | "brasil", 45 | "brazilian", 46 | "brasileiro", 47 | "cpf", 48 | "cnpj", 49 | "pis", 50 | "pasep", 51 | "cnh", 52 | "titulo eleitor", 53 | "eleitoral", 54 | "titulo", 55 | "renavam", 56 | "processos", 57 | "judiciais", 58 | "judicial", 59 | "justiça", 60 | "protocolo", 61 | "federal", 62 | "fake", 63 | "falso", 64 | "mock", 65 | "faker", 66 | "mockup", 67 | "mask", 68 | "mascara", 69 | "fake cpf", 70 | "fake cnpj", 71 | "fake cnh", 72 | "fake pis", 73 | "fake pasep", 74 | "fake cadunico", 75 | "fake renavam", 76 | "fake titulo", 77 | "fake titulo eleitor", 78 | "fake titulo eleitoral", 79 | "fake número processo", 80 | "processo justiça", 81 | "processo judicial", 82 | "valida", 83 | "validar", 84 | "validação", 85 | "valide", 86 | "validate", 87 | "format", 88 | "formatar", 89 | "formatador", 90 | "formatter", 91 | "nest", 92 | "nestjs", 93 | "yup", 94 | "next", 95 | "nextjs", 96 | "typeorm", 97 | "class-validator", 98 | "vuelidate", 99 | "vue", 100 | "vue3", 101 | "indicative", 102 | "adonis", 103 | "adonisjs", 104 | "adonis4", 105 | "adonisjs4" 106 | ] 107 | } 108 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // root: true, 3 | // extends: ['airbnb-base', 'prettier'], 4 | // parserOptions: { 5 | // ecmaVersion: 2018, 6 | // sourceType: 'module', 7 | // }, 8 | // env: { 9 | // node: true, 10 | // jest: true, 11 | // }, 12 | settings: { 13 | 'import/resolver': { 14 | node: { 15 | extensions: ['.js', '.ts'], 16 | }, 17 | }, 18 | }, 19 | rules: { 20 | // '@typescript-eslint/no-unused-vars': ['error', { varsIgnorePattern: '^_', args: 'none' }], 21 | 'array-callback-return': 'off', 22 | 'arrow-body-style': 'off', 23 | curly: ['error', 'multi-line', 'consistent'], 24 | 'class-methods-use-this': 'off', 25 | 'consistent-return': 'warn', 26 | 'default-case': 'off', 27 | // 'import/extensions': ['error', 'never'], 28 | // 'import/first': 'warn', 29 | // 'import/no-named-as-default': 'warn', 30 | // 'import/no-named-as-default-member': 'warn', 31 | // 'import/no-unresolved': 'warn', 32 | // 'import/prefer-default-export': 'off', 33 | 'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }], 34 | 'max-classes-per-file': 'off', 35 | 'max-len': ['error', 120, 2, { ignoreUrls: true, ignoreTemplateLiterals: true }], 36 | 'newline-per-chained-call': 'warn', 37 | 'no-await-in-loop': 'off', 38 | 'no-confusing-arrow': 'off', 39 | 'no-console': 'off', 40 | 'no-continue': 'off', 41 | 'no-else-return': ['error', { allowElseIf: true }], 42 | 'no-loop-func': 'off', 43 | 'no-mixed-operators': 'off', 44 | 'no-nested-ternary': 'off', 45 | // 'no-only-tests/no-only-tests': 'error', 46 | 'no-param-reassign': 'off', 47 | 'no-plusplus': 'off', 48 | 'no-prototype-builtins': 'off', 49 | 'no-restricted-syntax': 'off', 50 | 'no-return-assign': 'warn', 51 | 'no-sequences': 'warn', 52 | 'no-shadow': 'off', 53 | 'no-underscore-dangle': 'off', 54 | 'no-unexpected-multiline': 'off', 55 | 'no-unused-vars': 'off', 56 | 'no-use-before-define': ['warn', { functions: false, classes: false }], 57 | 'prefer-destructuring': 'off', 58 | semi: ['error', 'always'], 59 | 'sort-imports': 'off', 60 | 'no-unused-expressions': 'off', 61 | quotes: ['error', 'single', { avoidEscape: true }], 62 | // '@typescript-eslint/ban-types': 'error', 63 | }, 64 | // overrides: [ 65 | // { 66 | // files: ['*.ts'], 67 | // rules: { 68 | // 'no-dupe-class-members': 'off', 69 | // 'no-undef': 'off', 70 | // '@typescript-eslint/explicit-function-return-type': 'error', 71 | // }, 72 | // }, 73 | // { 74 | // files: ['*.d.ts'], 75 | // rules: { 76 | // 'no-useless-constructor': 'off', // crashes on constructor declaration in .d.ts files 77 | // }, 78 | // }, 79 | // ], 80 | // plugins: ['no-only-tests', '@typescript-eslint'], 81 | ignores: ['dist/*', '/node_modules/**'], 82 | }; 83 | -------------------------------------------------------------------------------- /test/cnh.test.ts: -------------------------------------------------------------------------------- 1 | import isCNH, { dv, fake, mask, validate, validateOrFail } from '../src/cnh' 2 | import * as _cnh from '../src/cnh' 3 | 4 | describe('CNH', () => { 5 | test('isCNH() - Números válidos', () => { 6 | const list = [ 7 | // como inteiro 8 | 50195131143, 9 | 58316794534, 10 | 50195471165, 11 | // como string 12 | '69044271146', 13 | '46190906839', 14 | // com máscara 15 | '624729276-37', 16 | ] 17 | 18 | list.forEach((cnh) => { 19 | expect(isCNH(cnh)).toBeTruthy() 20 | expect(_cnh.validate(cnh)).toBeTruthy() 21 | }) 22 | 23 | // t.end() 24 | }) 25 | 26 | test('validate() - Números válidos', () => { 27 | const list = [ 28 | // como inteiro 29 | 50195131143, 30 | 58316794534, 31 | // como string 32 | '69044271146', 33 | '46190906839', 34 | // com máscara 35 | '624729276-37', 36 | ] 37 | 38 | list.forEach((cnh) => { 39 | // t.true(isCNH(cnh), `CNH ${cnh} deve ser válida`) 40 | expect(validate(cnh)).toBeTruthy() 41 | }) 42 | }) 43 | 44 | test('validate() - Números inválidos', () => { 45 | const list = ['50195471143', '58316474534', '69044471146', '33333333333', '88888888888'] 46 | 47 | list.forEach((cnh) => { 48 | expect(validate(cnh)).toBeFalsy() 49 | }) 50 | }) 51 | 52 | test('validateOrFail() - Números inválidos', () => { 53 | const list = ['50195471143', '58316474534', '69044471146', '33333333333', '88888888888'] 54 | 55 | list.forEach((cnh) => { 56 | expect(() => validateOrFail(cnh)).toThrow() 57 | }) 58 | }) 59 | 60 | test('Parâmetro não informado', () => { 61 | expect(isCNH('')).toBeFalsy() 62 | expect(validate('')).toBeFalsy() 63 | expect(() => validateOrFail('')).toThrow() 64 | expect(() => dv('')).toThrow() 65 | }) 66 | 67 | test('fake() - Gera fakes sem máscara', () => { 68 | for (let i = 0; i < 5; i += 1) { 69 | const cnh = fake() 70 | expect(validate(cnh)).toBeTruthy() 71 | expect(cnh).toHaveLength(11) 72 | } 73 | }) 74 | 75 | test('fake() - Gera fakes com máscara', () => { 76 | for (let i = 0; i < 5; i += 1) { 77 | const cnh = fake(true) 78 | expect(validate(cnh)).toBeTruthy() 79 | expect(cnh).toHaveLength(12) 80 | } 81 | 82 | // t.end() 83 | }) 84 | 85 | test('dv() - Verificando se o DV gerado está correto', () => { 86 | const list = [ 87 | { num: '501954711', expected: '65' }, 88 | { num: 583164745, expected: '75' }, 89 | { num: 690444711, expected: '17' }, 90 | ] 91 | 92 | list.forEach((item) => { 93 | const calcDv = dv(item.num) 94 | 95 | expect(calcDv).toBe(item.expected) 96 | expect(typeof calcDv).toBe('string') 97 | }) 98 | }) 99 | 100 | test('mask() - Testando se a máscara foi gerada corretamente', () => { 101 | const list = [ 102 | { value: 50195471143, expected: '501954711-43' }, 103 | { value: 58316474534, expected: '583164745-34' }, 104 | { value: 69044471146, expected: '690444711-46' }, 105 | ] 106 | 107 | list.forEach((item) => { 108 | const masked = mask(item.value) 109 | 110 | expect(masked).toBe(item.expected) 111 | expect(masked).toHaveLength(12) 112 | }) 113 | }) 114 | }) 115 | -------------------------------------------------------------------------------- /test/utils-clearValue.test.ts: -------------------------------------------------------------------------------- 1 | import { clearValue } from '../src/utils' 2 | 3 | describe('clearValue()', () => { 4 | test('Deve limpar o valor - Strings do Mesmo tamanho', () => { 5 | // 6 | const valid = [ 7 | { value: 1234567890, size: 10, expected: '1234567890' }, 8 | { value: '1234567890', size: 10, expected: '1234567890' }, 9 | { value: '12.345.678-90', size: 10, expected: '1234567890' }, 10 | ] 11 | 12 | valid.forEach((item) => { 13 | expect(clearValue(item.value, item.size)).toBe(item.expected) 14 | }) 15 | }) 16 | 17 | test('Deve limpar os caracteres não numéricos sem verificar o tamanho da string', () => { 18 | // 19 | const valid = [ 20 | { value: 1234567890123, size: null, expected: '1234567890123' }, 21 | { value: '4567890', size: null, expected: '4567890' }, 22 | { value: '345.678-90', size: null, expected: '34567890' }, 23 | ] 24 | 25 | valid.forEach((item) => { 26 | expect(clearValue(item.value, item.size)).toBe(item.expected) 27 | }) 28 | }) 29 | 30 | test('Deve limpar o valor os caracteres não numéricos e completar com zeros à esquerda', () => { 31 | // 32 | const valid = [ 33 | { value: 1234, size: 10, expected: '0000001234' }, 34 | { value: '1234', size: 10, expected: '0000001234' }, 35 | { value: '123-4', size: 10, expected: '0000001234' }, 36 | ] 37 | 38 | valid.forEach((item) => { 39 | expect(clearValue(item.value, item.size, { fillZerosAtLeft: true })).toBe(item.expected) 40 | }) 41 | 42 | // Não completa com zeros 43 | expect(clearValue(1234, 6, { fillZerosAtLeft: false })).toBe('1234') 44 | expect(clearValue('1234', 6, { fillZerosAtLeft: false })).toBe('1234') 45 | }) 46 | 47 | test('Deve limpar o valor os caracteres não numéricos e remover os caracteres que passarem de size', () => { 48 | // 49 | const valid = [ 50 | { value: 123456789, size: 4, expected: '1234' }, 51 | { value: '1234567890', size: 4, expected: '1234' }, 52 | { value: '12.345.678-90', size: 4, expected: '1234' }, 53 | ] 54 | 55 | valid.forEach((item) => { 56 | expect(clearValue(item.value, item.size, { trimAtRight: true })).toBe(item.expected) 57 | }) 58 | 59 | // Não corta à direita 60 | expect(clearValue(12345678, 6, { trimAtRight: false })).toBe('12345678') 61 | expect(clearValue('12345678', 6, { trimAtRight: false })).toBe('12345678') 62 | }) 63 | 64 | test('Deve retornar erro se receber um valor vazio', () => { 65 | expect(clearValue('1112', 4, { rejectEmpty: true })).toBe('1112') 66 | expect(() => clearValue('', null, { rejectEmpty: true })).toThrow() 67 | }) 68 | 69 | test('Deve retornar erro se receber uma sequência de números iguais', () => { 70 | expect(clearValue('1112', 4, { rejectEqualSequence: true })).toBe('1112') 71 | expect(() => clearValue('1111', 4, { rejectEqualSequence: true })).toThrow() 72 | }) 73 | 74 | test('Deve retornar erro se receber um valor com mais caracteres que o máximo', () => { 75 | expect(clearValue('1234', 4, { rejectHigherLength: true })).toBe('1234') 76 | expect(() => clearValue('12345', 4, { rejectHigherLength: true })).toThrow() 77 | }) 78 | 79 | test('Deve retornar o mesmo valor inicial', () => { 80 | expect(clearValue(1234, 4)).toBe('1234') 81 | expect(clearValue('1234', 4)).toBe('1234') 82 | expect(clearValue(1234, 4, {})).toBe('1234') 83 | expect(clearValue('1234', 4, {})).toBe('1234') 84 | }) 85 | }) 86 | -------------------------------------------------------------------------------- /test/renavam.test.ts: -------------------------------------------------------------------------------- 1 | import isRenavam, { dv, fake, mask, validate, validateOrFail } from '../src/renavam' 2 | import * as _renavam from '../src/renavam' 3 | 4 | describe('Renavam', () => { 5 | test('isRenavam() - Números válidos', () => { 6 | const list = [ 7 | // valores com máscara 8 | '1952519770-3', 9 | '3394038959-9', 10 | // valores como inteiros 11 | 3607626105, 12 | 64090416160, 13 | // valores como string sem máscara 14 | '80499688374', 15 | '40650543741', 16 | ] 17 | 18 | list.forEach((renavam) => { 19 | expect(isRenavam(renavam)).toBeTruthy() 20 | expect(_renavam.validate(renavam)).toBeTruthy() 21 | }) 22 | }) 23 | 24 | test('validate() - Números válidos', () => { 25 | const list = [ 26 | // valores com máscara 27 | '1952519770-3', 28 | '3394038959-9', 29 | // valores como inteiros 30 | 3607626105, 31 | 64090416160, 32 | // valores como string sem máscara 33 | '80499688374', 34 | '40650543741', 35 | ] 36 | 37 | list.forEach((renavam) => { 38 | expect(validate(renavam)).toBeTruthy() 39 | }) 40 | }) 41 | 42 | test('validate() - Números inválidos', () => { 43 | const list = ['19525227703', '33940229599', '03607226105', '64090226160', '80499228374'] 44 | 45 | list.forEach((renavam) => { 46 | expect(validate(renavam)).toBeFalsy() 47 | }) 48 | }) 49 | 50 | test('validateOrFail() - Números inválidos', () => { 51 | const list = ['19525227703', '33940229599', '03607226105', '64090226160', '80499228374'] 52 | 53 | list.forEach((renavam) => { 54 | expect(() => validateOrFail(renavam)).toThrow() 55 | }) 56 | }) 57 | 58 | test('Parâmetro não informado', () => { 59 | expect(isRenavam('')).toBeFalsy() 60 | expect(validate('')).toBeFalsy() 61 | expect(() => validateOrFail('')).toThrow() 62 | expect(() => dv('')).toThrow() 63 | }) 64 | 65 | test('fake() - Gera fakes sem máscara', () => { 66 | for (let i = 0; i < 5; i += 1) { 67 | const renavam = fake() 68 | 69 | expect(validate(renavam)).toBeTruthy() 70 | expect(renavam).toHaveLength(11) 71 | } 72 | }) 73 | 74 | test('fake() - Gera fakes com máscara', () => { 75 | for (let i = 0; i < 5; i += 1) { 76 | const renavam = fake(true) 77 | 78 | expect(validate(renavam)).toBeTruthy() 79 | expect(renavam).toHaveLength(12) 80 | } 81 | }) 82 | 83 | test('renavam.dv() - Verificando se o DV gerado está correto', () => { 84 | const list = [ 85 | { num: '1952519770', expected: '3' }, 86 | { num: 952519770, expected: '6' }, 87 | { num: 52519770, expected: '2' }, 88 | ] 89 | 90 | list.forEach((item) => { 91 | const calcDv = dv(item.num) 92 | 93 | expect(calcDv).toBe(item.expected) 94 | expect(typeof calcDv).toBe('string') 95 | }) 96 | }) 97 | 98 | test('mask() - Testando se a máscara foi gerada corretamente', () => { 99 | const list = [ 100 | { num: '19525197703', expected: '1952519770-3' }, 101 | { num: 9525197703, expected: '0952519770-3' }, 102 | { num: 525197703, expected: '0052519770-3' }, 103 | ] 104 | 105 | list.forEach((item) => { 106 | const masked = mask(item.num) 107 | 108 | expect(masked).toBe(item.expected) 109 | expect(masked).toHaveLength(12) 110 | }) 111 | }) 112 | }) 113 | -------------------------------------------------------------------------------- /src/renavam.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * RENAVAM 3 | * Funções auxiliares para cálculo de máscaras, validação, dígito verificador e criaçãode 4 | * números fake. 5 | * 6 | * @doc 7 | * - O número de RENAVAM deve possuir 11 caracteres 8 | * 9 | * - Os caracteres de 1 a 10 são a numeração documento 10 | * 11 | * - O caractere 11 é o dígito verificador. 12 | * 13 | * 1) Partes do número 14 | * _______________________________________________ 15 | * | Número | D V | 16 | * | 2 6 8 2 7 6 4 9 9 6 - 0 | 17 | * |_________________________________________|_____| 18 | * 19 | * 2) Cálculo do DV. 20 | * 21 | * - Soma-se o produto das algarismos 3 a 10 pelos números 3, 2, 9, 8, 7, 6, 5, 4, 3, 2 22 | * 23 | * 2 6 8 2 7 6 4 9 9 6 24 | * x x x x x x x x x x 25 | * 3 2 9 8 7 6 5 4 3 2 26 | * = 6 +12 +72 +16 +49 +12 +20 +36 +27 +12 = 234 27 | * 28 | * - O somatório encontrado é multiplicado por 10 e ao resultado 29 | * é aplicado o cálculo do MOD 11. 30 | * 31 | * ( 234 * 10 ) / 11 tem resto 8. DV = 8. Caso o resto seja maior ou igual a 32 | * 10, DV será 0. 33 | * 34 | * 35 | */ 36 | 37 | import ValidationBRError from './ValidationBRError' 38 | import { sumElementsByMultipliers, clearValue, fakeNumber, applyMask } from './utils' 39 | 40 | /** 41 | * dv() 42 | * Calcula o dígito verificador 43 | * 44 | * @param {Number|String} value 45 | * @returns {String} 46 | */ 47 | export const dv = (value: string | number): string => { 48 | const renavam = clearValue(value, 10, { 49 | fillZerosAtLeft: true, 50 | trimAtRight: true, 51 | rejectEmpty: true, 52 | }) 53 | 54 | const sum1 = sumElementsByMultipliers(renavam, [3, 2, 9, 8, 7, 6, 5, 4, 3, 2]) * 10 55 | const dv1 = sum1 % 11 >= 10 ? 0 : sum1 % 11 56 | 57 | return `${dv1}` 58 | } 59 | 60 | /** 61 | * Aplica uma máscara ao número informado 62 | * 63 | * @param {String} value Número de Processo 64 | * @returns {String} Valor com a máscara 65 | */ 66 | export const mask = (value: string | number): string => applyMask(value, '0000000000-0') 67 | 68 | /** 69 | * fake() 70 | * Gera um número válido 71 | * 72 | * @returns {String} 73 | */ 74 | export const fake = (withMask: boolean = false): string => { 75 | const value = fakeNumber(10, true) 76 | 77 | const renavam = `${value}${dv(value)}` 78 | 79 | if (withMask) return mask(renavam) 80 | 81 | return renavam 82 | } 83 | 84 | /** 85 | * validateOrFail() 86 | * Valida se um número é válido e 87 | * retorna uma exceção se não estiver 88 | * 89 | * @param {String|Number} value Número a ser validado 90 | * @returns {Boolean} 91 | */ 92 | export const validateOrFail = (value: string | number): boolean => { 93 | const renavam = clearValue(value, 11, { 94 | fillZerosAtLeft: true, 95 | rejectEmpty: true, 96 | rejectHigherLength: true, 97 | rejectEqualSequence: true, 98 | }) 99 | 100 | if (dv(renavam) !== renavam.substring(10, 11)) { 101 | throw ValidationBRError.INVALID_DV 102 | } 103 | 104 | return true 105 | } 106 | 107 | /** 108 | * validate() 109 | * Valida se um número é válido 110 | * 111 | * @param {String|Number} value Número a ser validado 112 | * @returns {Boolean} 113 | */ 114 | export const validate = (value: string | number): boolean => { 115 | try { 116 | return validateOrFail(value) 117 | } catch (error) { 118 | return false 119 | } 120 | } 121 | 122 | export default validate 123 | -------------------------------------------------------------------------------- /src/pisPasep.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * isPIS() 3 | * Calcula se um código de PIS/PASEP/NIS/NIT no formato 268.27649.96-0 é válido. Não 4 | * valida o formato, portanto, 26827649960 é equivalente a 268.27649.96-0 para efeitos 5 | * desta validação. 6 | * 7 | * @doc 8 | * - O número de PIS deve possuir 11 caracteres 9 | * 10 | * - Os caracteres de 1 a 10 são a numeração documento 11 | * 12 | * - O caractere 11 é o dígito verificador. 13 | * 14 | * 1) Partes do número 15 | * _______________________________________________ 16 | * | Número | D V | 17 | * | 2 6 8 . 2 7 6 4 9 . 9 6 - 0 | 18 | * |_________________________________________|_____| 19 | * 20 | * 2) Cálculo do DV. 21 | * 22 | * - Soma-se o produto das algarismos 3 a 10 pelos números 3, 2, 9, 8, 7, 6, 5, 4, 3, 2 23 | * 24 | * 2 6 8 2 7 6 4 9 9 6 25 | * x x x x x x x x x x 26 | * 3 2 9 8 7 6 5 4 3 2 27 | * = 6 +12 +72 +16 +49 +12 +20 +36 +27 +12 = 234 28 | * 29 | * - O somatório encontrado é dividido por 11 e o resultado é subtraído de 11 30 | * 234 / 11 tem resto 3. 11 - 3 = 8. DV1 é 8. 31 | * Obs.: Caso o cálculo de DV1 retorne 0, o resultado será 5. 32 | * Caso retorne 1, o resto será 0 33 | * 34 | * 35 | * 36 | * 37 | * Fonte: http://www.macoratti.net/alg_pis.htm 38 | * 39 | * @param {String} value Objeto postal no formato 268.27649.96-0 40 | * @returns {Boolean} 41 | */ 42 | 43 | import ValidationBRError from './ValidationBRError' 44 | import { sumElementsByMultipliers, sumToDV, clearValue, fakeNumber, applyMask } from './utils' 45 | 46 | /** 47 | * dv() 48 | * Calcula o dígito verificador 49 | * 50 | * @param {Number|String} value 51 | * @returns {String} 52 | */ 53 | export const dv = (value: string | number): string => { 54 | const pis = clearValue(value, 10, { 55 | trimAtRight: true, 56 | rejectEmpty: true, 57 | }) 58 | 59 | const sum = sumElementsByMultipliers(pis, [3, 2, 9, 8, 7, 6, 5, 4, 3, 2]) 60 | 61 | return String(sumToDV(sum)) 62 | } 63 | 64 | /** 65 | * Aplica uma máscara ao número informado 66 | * 67 | * @param {String} value Número de Processo 68 | * @returns {String} Valor com a máscara 69 | */ 70 | export const mask = (value: string | number): string => applyMask(value, '000.00000.00-0') 71 | 72 | /** 73 | * fake() 74 | * Gera um número válido 75 | * 76 | * @returns {String} 77 | */ 78 | export const fake = (withMask: boolean = false): string => { 79 | const num = fakeNumber(10, true) 80 | 81 | const pis = `${num}${dv(num)}` 82 | 83 | if (withMask) return mask(pis) 84 | return pis 85 | } 86 | 87 | /** 88 | * validateOrFail() 89 | * Valida se um número é válido e 90 | * retorna uma exceção se não estiver 91 | * 92 | * @param {String|Number} value Número a ser validado 93 | * @returns {Boolean} 94 | */ 95 | export const validateOrFail = (value: string | number): boolean => { 96 | const pis = clearValue(value, 11, { 97 | fillZerosAtLeft: true, 98 | rejectEmpty: true, 99 | rejectHigherLength: true, 100 | rejectEqualSequence: true, 101 | }) 102 | 103 | if (dv(pis) !== pis.substring(10, 11)) { 104 | throw ValidationBRError.INVALID_DV 105 | } 106 | 107 | return true 108 | } 109 | 110 | /** 111 | * validate() 112 | * Valida se um número é válido 113 | * 114 | * @param {String|Number} value Número a ser validado 115 | * @returns {Boolean} 116 | */ 117 | export const validate = (value: string | number): boolean => { 118 | try { 119 | return validateOrFail(value) 120 | } catch (error) { 121 | return false 122 | } 123 | } 124 | 125 | export default validate 126 | -------------------------------------------------------------------------------- /test/tituloEleitor.test.ts: -------------------------------------------------------------------------------- 1 | import isTituloEleitor, { dv, fake, mask, validate, validateOrFail } from '../src/tituloEleitor' 2 | import * as _tituloEleitor from '../src/tituloEleitor' 3 | 4 | describe('TituloEleitor', () => { 5 | test('isTituloEleitor() - Números válidos', () => { 6 | const list = [ 7 | // masked 8 | '1023.8501.0671', 9 | '8365.7137.1619', 10 | // string 11 | '153036161686', 12 | '525028881694', 13 | // integer 14 | 11122223360, 15 | 1122223336, 16 | ] 17 | 18 | list.forEach((tituloEleitor) => { 19 | expect(isTituloEleitor(tituloEleitor)).toBeTruthy() 20 | expect(_tituloEleitor.validate(tituloEleitor)).toBeTruthy() 21 | }) 22 | }) 23 | 24 | test('validate() - Números válidos', () => { 25 | const list = [ 26 | // masked 27 | '1023.8501.0671', 28 | '8365.7137.1619', 29 | // string 30 | '153036161686', 31 | '525028881694', 32 | // integer 33 | 11122223360, 34 | 1122223336, 35 | ] 36 | 37 | list.forEach((tituloEleitor) => { 38 | expect(validate(tituloEleitor)).toBeTruthy() 39 | }) 40 | }) 41 | 42 | test('validate() - Números inválidos', () => { 43 | const list = [ 44 | '836531371619', 45 | '743620641660', 46 | '153016161686', 47 | '525078881694', 48 | '026367681660', 49 | '558647441635', 50 | '222222222222', 51 | ] 52 | 53 | list.forEach((tituloEleitor) => { 54 | expect(validate(tituloEleitor)).toBeFalsy() 55 | }) 56 | }) 57 | 58 | test('validateOrFail() - Números inválidos', () => { 59 | const list = [ 60 | '836531371619', 61 | '743620641660', 62 | '153016161686', 63 | '525078881694', 64 | '026367681660', 65 | '558647441635', 66 | '222222222222', 67 | ] 68 | 69 | list.forEach((tituloEleitor) => { 70 | expect(() => validateOrFail(tituloEleitor)).toThrow() 71 | }) 72 | }) 73 | 74 | test('Parâmetro não informado', () => { 75 | expect(isTituloEleitor('')).toBeFalsy() 76 | expect(validate('')).toBeFalsy() 77 | expect(() => validateOrFail('')).toThrow() 78 | expect(() => dv('')).toThrow() 79 | }) 80 | 81 | test('fake() - Gera fakes sem máscara', () => { 82 | for (let i = 0; i < 5; i += 1) { 83 | const tituloEleitor = fake() 84 | 85 | expect(validate(tituloEleitor)).toBeTruthy() 86 | expect(tituloEleitor).toHaveLength(12) 87 | } 88 | }) 89 | 90 | test('fake() - Gera fakes com máscara', () => { 91 | for (let i = 0; i < 5; i += 1) { 92 | const tituloEleitor = fake(true) 93 | 94 | expect(validate(tituloEleitor)).toBeTruthy() 95 | expect(tituloEleitor).toHaveLength(14) 96 | } 97 | }) 98 | 99 | test('dv() - Verificando se o DV gerado está correto', () => { 100 | const list = [ 101 | { num: '1023850106', expected: '71' }, 102 | { num: '8365713716', expected: '19' }, 103 | { num: '7436506416', expected: '60' }, 104 | { num: 11222233, expected: '36' }, 105 | ] 106 | 107 | list.forEach((item) => { 108 | const calcDv = dv(item.num) 109 | 110 | expect(calcDv).toBe(item.expected) 111 | expect(typeof calcDv).toBe('string') 112 | }) 113 | }) 114 | 115 | test('mask() - Testando se a máscara foi gerada corretamente', () => { 116 | const list = [ 117 | { num: '102385010671', expected: '1023.8501.0671' }, 118 | { num: '836571371619', expected: '8365.7137.1619' }, 119 | { num: '743650641660', expected: '7436.5064.1660' }, 120 | { num: 11122223360, expected: '0111.2222.3360' }, 121 | { num: 1122223336, expected: '0011.2222.3336' }, 122 | ] 123 | 124 | list.forEach((item) => { 125 | const masked = mask(item.num) 126 | 127 | expect(masked).toBe(item.expected) 128 | expect(masked).toHaveLength(14) 129 | }) 130 | }) 131 | }) 132 | -------------------------------------------------------------------------------- /test/postalCode.test.ts: -------------------------------------------------------------------------------- 1 | import isPostalCode, { dv, fake, mask, validate, validateOrFail } from '../src/postalCode' 2 | import * as _postalCode from '../src/postalCode' 3 | 4 | describe('PostalCode', () => { 5 | test('isPostalCode() - Números válidos', () => { 6 | const list = [ 7 | 'PN718252423BR', 8 | 'PO925539762BR', 9 | 'JT194690698BR', 10 | 'SV143851674BR', 11 | 'RG727348650CN', 12 | 'RY747622885CN', 13 | 'RY728187035CN', 14 | 'RH940629235CN', 15 | 'RY686586175CN', 16 | 'RY648001205CN', 17 | 'UJ776469464CN', 18 | 'LZ667841882CN', 19 | 'RS737783818NL', 20 | ] 21 | 22 | list.forEach((postalCode) => { 23 | expect(isPostalCode(postalCode)).toBeTruthy() 24 | expect(_postalCode.validate(postalCode)).toBeTruthy() 25 | }) 26 | }) 27 | 28 | test('validate() - Números válidos', () => { 29 | const list = [ 30 | 'PN718252423BR', 31 | 'PO925539762BR', 32 | 'JT194690698BR', 33 | 'SV143851674BR', 34 | 'RG727348650CN', 35 | 'RY747622885CN', 36 | 'RY728187035CN', 37 | 'RH940629235CN', 38 | 'RY686586175CN', 39 | 'RY648001205CN', 40 | 'UJ776469464CN', 41 | 'LZ667841882CN', 42 | 'RS737783818NL', 43 | ] 44 | 45 | list.forEach((postalCode) => { 46 | expect(validate(postalCode)).toBeTruthy() 47 | }) 48 | }) 49 | 50 | test('validate() - Números inválidos', () => { 51 | const list = [ 52 | 'PO925524762BR', 53 | 'JT194624698BR', 54 | 'SV143824674BR', 55 | 'RG727324650CN', 56 | 'RY747624885CN', 57 | 'RY728114035CN', 58 | ] 59 | 60 | list.forEach((postalCode) => { 61 | expect(validate(postalCode)).toBeFalsy() 62 | }) 63 | }) 64 | 65 | test('validateOrFail() - Números inválidos', () => { 66 | const list = [ 67 | 'PO925524762BR', 68 | 'JT194624698BR', 69 | 'SV143824674BR', 70 | 'RG727324650CN', 71 | 'RY747624885CN', 72 | 'RY728114035CN', 73 | ] 74 | 75 | list.forEach((postalCode) => { 76 | expect(() => validateOrFail(postalCode)).toThrow() 77 | }) 78 | }) 79 | 80 | test('Parâmetro não informado', () => { 81 | expect(isPostalCode('')).toBeFalsy() 82 | expect(validate('')).toBeFalsy() 83 | expect(() => validateOrFail('')).toThrow() 84 | expect(() => dv('')).toThrow() 85 | }) 86 | 87 | test('fake() - Gera fakes sem máscara', () => { 88 | for (let i = 0; i < 5; i += 1) { 89 | const postalCode = fake() 90 | 91 | expect(validate(postalCode)).toBeTruthy() 92 | expect(postalCode).toHaveLength(13) 93 | } 94 | }) 95 | 96 | test('fake() - Gera fakes com máscara', () => { 97 | for (let i = 0; i < 5; i += 1) { 98 | const postalCode = fake(true) 99 | 100 | expect(validate(postalCode)).toBeTruthy() 101 | expect(postalCode).toHaveLength(13) 102 | } 103 | }) 104 | 105 | test('dv() - Verificando se o DV gerado está correto', () => { 106 | const list = [ 107 | { num: 'PN718252423BR', expected: '3' }, 108 | { num: 'PO925539762BR', expected: '2' }, 109 | { num: 'JT194690698BR', expected: '8' }, 110 | ] 111 | 112 | list.forEach((item) => { 113 | const calcDv = dv(item.num) 114 | 115 | expect(calcDv).toBe(item.expected) 116 | expect(typeof calcDv).toBe('string') 117 | }) 118 | }) 119 | 120 | test('mask() - Testando se a máscara foi gerada corretamente', () => { 121 | const list = [ 122 | { num: 'pn718252423br', expected: 'PN718252423BR' }, 123 | { num: 'po925539762br', expected: 'PO925539762BR' }, 124 | { num: 'jt194690698br', expected: 'JT194690698BR' }, 125 | ] 126 | 127 | list.forEach((item) => { 128 | const masked = mask(item.num) 129 | 130 | expect(masked).toBe(item.expected) 131 | expect(masked).toHaveLength(13) 132 | }) 133 | }) 134 | }) 135 | -------------------------------------------------------------------------------- /test/cpf.test.ts: -------------------------------------------------------------------------------- 1 | import isCPF, { dv, fake, mask, validate, validateOrFail } from '../src/cpf' 2 | import * as _cpf from '../src/cpf' 3 | 4 | describe('CPF', () => { 5 | test('isCPF() - Números válidos', () => { 6 | const list = [ 7 | // masked 8 | '133.782.710-00', 9 | '400.448.260-79', 10 | // integer 11 | 8796742020, // começa com zero 12 | 74172316085, 13 | // string 14 | '15886489070', 15 | '90889477086', 16 | ] 17 | 18 | list.forEach((cpf) => { 19 | expect(isCPF(cpf)).toBeTruthy() 20 | expect(_cpf.validate(cpf)).toBeTruthy() 21 | }) 22 | }) 23 | 24 | test('validate() - Números válidos', () => { 25 | const list = [ 26 | // masked 27 | '133.782.710-00', 28 | '400.448.260-79', 29 | // integer 30 | 8796742020, // começa com zero 31 | 74172316085, 32 | // string 33 | '15886489070', 34 | '90889477086', 35 | ] 36 | 37 | list.forEach((cpf) => { 38 | expect(validate(cpf)).toBeTruthy() 39 | }) 40 | }) 41 | 42 | test('validate() - Números inválidos', () => { 43 | const list = [ 44 | '287.967.420-20', 45 | '333.782.710-00', 46 | '200.448.260-79', 47 | '24172316085', 48 | '25886489070', 49 | '20889477086', 50 | '11111111111', 51 | ] 52 | 53 | list.forEach((cpf) => { 54 | expect(validate(cpf)).toBeFalsy() 55 | }) 56 | }) 57 | 58 | test('validateOrFail() - Números inválidos', () => { 59 | const list = [ 60 | '287.967.420-20', 61 | '333.782.710-00', 62 | '200.448.260-79', 63 | '24172316085', 64 | '25886489070', 65 | '20889477086', 66 | '11111111111', 67 | ] 68 | 69 | list.forEach((cpf) => { 70 | expect(() => validateOrFail(cpf)).toThrow() 71 | }) 72 | }) 73 | 74 | test('validate() - Números válidos com caracteres adicionais', () => { 75 | const list = [ 76 | '24172316085000000', 77 | '25886489070999999', 78 | '20889477086888888', 79 | '11111111111777777', 80 | ] 81 | 82 | list.forEach((cpf) => { 83 | expect(validate(cpf)).toBeFalsy() 84 | }) 85 | }) 86 | 87 | test('Parâmetro não informado', () => { 88 | expect(isCPF('')).toBeFalsy() 89 | expect(validate('')).toBeFalsy() 90 | expect(() => validateOrFail('')).toThrow() 91 | expect(() => dv('')).toThrow() 92 | }) 93 | 94 | test('fake() - Gera fakes sem máscara', () => { 95 | for (let i = 0; i < 5; i += 1) { 96 | const cpf = fake() 97 | expect(validate(cpf)).toBeTruthy() 98 | expect(cpf).toHaveLength(11) 99 | } 100 | }) 101 | 102 | test('fake() - Gera fakes com máscara', () => { 103 | for (let i = 0; i < 5; i += 1) { 104 | const cpf = fake(true) 105 | expect(validate(cpf)).toBeTruthy() 106 | expect(cpf).toHaveLength(14) 107 | } 108 | }) 109 | 110 | test('dv() - Verificando se o DV gerado está correto', () => { 111 | const list = [ 112 | { num: '741723160', expected: '85' }, 113 | { num: 158864890, expected: '70' }, 114 | { num: '908894770', expected: '86' }, 115 | ] 116 | 117 | list.forEach((item) => { 118 | const calcDv = dv(item.num) 119 | 120 | expect(calcDv).toBe(item.expected) 121 | expect(typeof calcDv).toBe('string') 122 | }) 123 | }) 124 | 125 | test('mask() - Testando se a máscara foi gerada corretamente', () => { 126 | const list = [ 127 | { num: '74172316085', expected: '741.723.160-85' }, 128 | { num: 15886489070, expected: '158.864.890-70' }, 129 | { num: '90889477086', expected: '908.894.770-86' }, 130 | { num: 889477086, expected: '008.894.770-86' }, 131 | ] 132 | 133 | list.forEach((item) => { 134 | const masked = mask(item.num) 135 | 136 | expect(masked).toBe(item.expected) 137 | expect(masked).toHaveLength(14) 138 | }) 139 | }) 140 | }) 141 | -------------------------------------------------------------------------------- /test/pisPasep.test.ts: -------------------------------------------------------------------------------- 1 | import isPIS, { dv, fake, mask, validate, validateOrFail } from '../src/pisPasep' 2 | import * as _pisPasep from '../src/pisPasep' 3 | 4 | describe('PIS', () => { 5 | test('isPIS() - Números válidos', () => { 6 | const list = [ 7 | // string 8 | '71282677380', 9 | '23795126955', 10 | // integer 11 | 50012973803, 12 | 27890141144, 13 | // masked 14 | '268.27649.96-0', 15 | '613.01862.91-7', 16 | ] 17 | 18 | list.forEach((pis) => { 19 | expect(isPIS(pis)).toBeTruthy() 20 | expect(_pisPasep.validate(pis)).toBeTruthy() 21 | }) 22 | }) 23 | 24 | test('validate() - Números válidos', () => { 25 | const list = [ 26 | // string 27 | '71282677380', 28 | '23795126955', 29 | // integer 30 | 50012973803, 31 | 27890141144, 32 | // masked 33 | '268.27649.96-0', 34 | '613.01862.91-7', 35 | ] 36 | 37 | list.forEach((pis) => { 38 | expect(validate(pis)).toBeTruthy() 39 | }) 40 | }) 41 | 42 | test('validate() - Números válidos com caracteres adicionais', () => { 43 | const list = [ 44 | // string 45 | '712826773809', 46 | '237951269559', 47 | // integer 48 | 500129738039, 49 | 278901411449, 50 | // masked 51 | '268.27649.96-09', 52 | '613.01862.91-79', 53 | ] 54 | 55 | list.forEach((pis) => { 56 | expect(validate(pis)).toBeFalsy() 57 | }) 58 | }) 59 | test('validate() - Números inválidos', () => { 60 | const list = [ 61 | '712.82677.38-2', 62 | '237.95126.95-4', 63 | '500.12973.80-1', 64 | '278.90141.14-9', 65 | '268.27649.96-2', 66 | '613.01862.91-4', 67 | '111.11111.11-1', 68 | ] 69 | 70 | list.forEach((pis) => { 71 | expect(validate(pis)).toBeFalsy() 72 | }) 73 | }) 74 | 75 | test('validateOrFail() - Números inválidos', () => { 76 | const list = [ 77 | '712.82677.38-2', 78 | '237.95126.95-4', 79 | '500.12973.80-1', 80 | '278.90141.14-9', 81 | '268.27649.96-2', 82 | '613.01862.91-4', 83 | '111.11111.11-1', 84 | ] 85 | 86 | list.forEach((pis) => { 87 | expect(() => validateOrFail(pis)).toThrow() 88 | }) 89 | }) 90 | 91 | test('Parâmetro não informado', () => { 92 | expect(isPIS('')).toBeFalsy() 93 | expect(validate('')).toBeFalsy() 94 | expect(() => validateOrFail('')).toThrow() 95 | expect(() => dv('')).toThrow() 96 | }) 97 | 98 | test('fake() - Gera fakes sem máscara', () => { 99 | for (let i = 0; i < 5; i += 1) { 100 | const pis = fake() 101 | expect(validate(pis)).toBeTruthy() 102 | expect(pis).toHaveLength(11) 103 | } 104 | }) 105 | 106 | test('fake() - Gera fakes com máscara', () => { 107 | for (let i = 0; i < 5; i += 1) { 108 | const pis = fake(true) 109 | expect(validate(pis)).toBeTruthy() 110 | expect(pis).toHaveLength(14) 111 | } 112 | }) 113 | 114 | test('dv() - Verificando se o DV gerado está correto', () => { 115 | const list = [ 116 | { num: '7128267738', expected: '0' }, 117 | { num: 2379512695, expected: '5' }, 118 | { num: '5001297380', expected: '3' }, 119 | ] 120 | 121 | list.forEach((item) => { 122 | const calcDv = dv(item.num) 123 | 124 | expect(calcDv).toBe(item.expected) 125 | expect(typeof calcDv).toBe('string') 126 | }) 127 | }) 128 | 129 | test('mask() - Testando se a máscara foi gerada corretamente', () => { 130 | const list = [ 131 | { num: '71282677380', expected: '712.82677.38-0' }, 132 | { num: 23795126955, expected: '237.95126.95-5' }, 133 | { num: '50012973803', expected: '500.12973.80-3' }, 134 | ] 135 | 136 | list.forEach((item) => { 137 | const masked = mask(item.num) 138 | 139 | expect(masked).toBe(item.expected) 140 | expect(masked).toHaveLength(14) 141 | }) 142 | }) 143 | }) 144 | -------------------------------------------------------------------------------- /src/cnh.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * isCNH() 3 | * Calcula se uma CNH é válida 4 | * 5 | * @doc 6 | * CNH deve possuir 11 caracteres 7 | * 8 | * - Os caracteres 1 a 9 são números sequenciais. 9 | * 10 | * 11 | * - Os caracteres 10 e 11 são dígitos verificadores. 12 | * 13 | * 1) Partes do número 14 | * ____________________________ ______ 15 | * | Número | D V | 16 | * | 5 8 3 1 6 7 9 4 5 3 4 | 17 | * |____________________________|_____| 18 | * 19 | * 2) Cálculo do primeiro DV. 20 | * 21 | * - Soma-se o produto das algarismos 1 a 9 pelos números 2, 3, 4, 5, 6, 7, 8, 9, 10. 22 | * 23 | * 5 8 3 1 6 7 9 4 5 24 | * x x x x x x x x x 25 | * 2 3 4 5 6 7 8 9 10 26 | * = 10 +24 +12 +5 +36 +49 +72 +36 +50 = 294 27 | * 28 | * - O somatório encontrado é dividido por 11. O DV1 é 11 subtraído do resto da divisão. Se o 29 | * resto for 10, o DV1 é 0. 30 | * 31 | * 2.1) 294 / 11 tem resto igual a 8. 11-7 = 3 32 | * DV1 = 3 33 | * 34 | * 3) Cálculo do segundo DV 35 | * 36 | * - Soma-se o produto das algarismos 1 a 9 juntamente com o 10 caractere 37 | * que é o DV1, pelos números 3, 4, 5, 6, 7, 8, 9, 10, 11, 2. O DV1 será 38 | * multiplicado por 2 e ficará no final. 39 | * 40 | * 5 8 3 1 6 7 9 4 5 3 41 | * x x x x x x x x x x 42 | * 3 4 5 6 7 8 9 10 11 2 43 | * = 10 +24 +12 +5 +36 +49 +72 +36 +50 +6 = 348 44 | * 45 | * 3.1) 348 / 11 tem resto igual a 7. 11 - 7 = 4. 46 | * DV2 = 4 47 | * 48 | * - O somatório encontrado é dividido por 11. O DV2 é 11 subtraído do resto da divisão. Se o 49 | * resto for 10, o DV2 é 0. 50 | * 51 | * Fonte: https://www.devmedia.com.br/forum/validacao-de-cnh/372972 52 | * 53 | * @param {String} value Título eleitoral 54 | * @returns {Boolean} 55 | */ 56 | 57 | import ValidationBRError from './ValidationBRError' 58 | import { sumElementsByMultipliers, sumToDV, clearValue, applyMask, fakeNumber } from './utils' 59 | 60 | /** 61 | * Calcula o Dígito Verificador de um RENAVAM informado 62 | * 63 | * @returns String Número fake de um cnh válido 64 | */ 65 | export const dv = (value: string | number): string => { 66 | const cnh = clearValue(value, 9, { 67 | trimAtRight: true, 68 | rejectEmpty: true, 69 | }) 70 | 71 | const sum1 = sumElementsByMultipliers(cnh.substring(0, 9), [2, 3, 4, 5, 6, 7, 8, 9, 10]) 72 | const dv1 = sumToDV(sum1) 73 | 74 | const sum2 = sumElementsByMultipliers(cnh.substring(0, 9) + dv1, [3, 4, 5, 6, 7, 8, 9, 10, 11, 2]) 75 | const dv2 = sumToDV(sum2) 76 | 77 | return `${dv1}${dv2}` 78 | } 79 | 80 | /** 81 | * validateOrFail() 82 | * Valida se um número é válido e 83 | * retorna uma exceção se não estiver 84 | * 85 | * @param {String|Number} value Número a ser validado 86 | * @returns {Boolean} 87 | */ 88 | export const validateOrFail = (value: string | number): boolean => { 89 | const cnh = clearValue(value, 11, { 90 | fillZerosAtLeft: true, 91 | rejectEmpty: true, 92 | rejectHigherLength: true, 93 | rejectEqualSequence: true, 94 | }) 95 | 96 | if (dv(cnh) !== cnh.substring(9, 11)) { 97 | throw ValidationBRError.INVALID_DV 98 | } 99 | 100 | return true 101 | } 102 | 103 | /** 104 | * validate() 105 | * Valida se um número é válido 106 | * 107 | * @param {String|Number} value Número a ser validado 108 | * @returns {Boolean} 109 | */ 110 | export const validate = (value: string | number): boolean => { 111 | try { 112 | return validateOrFail(value) 113 | } catch (error) { 114 | return false 115 | } 116 | } 117 | 118 | /** 119 | * Aplica uma máscara a uma string 120 | * 121 | * @returns String string com a máscara aplicada 122 | */ 123 | export const mask = (value: string | number): string => applyMask(value, '000000000-00') 124 | 125 | /** 126 | * Cria um número fake 127 | * 128 | * @returns String Número fake porém válido 129 | */ 130 | export const fake = (withMask: boolean = false): string => { 131 | const value = fakeNumber(9, true) 132 | 133 | const cnh = `${value}${dv(value)}` 134 | 135 | if (withMask) return mask(cnh) 136 | 137 | return cnh 138 | } 139 | 140 | export default validate 141 | -------------------------------------------------------------------------------- /src/postalCode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * isPostalCode() 3 | * Calcula se um código de rastreamento postal no formato JT194690698BR é válido. 4 | * 5 | * @doc 6 | * - O número de registro postal deve possuir 13 caracters no formato JT194690698BR. 7 | * 8 | * - Os caracteres 1 e 2 informam o tipo do objeto. Ex.: SX é Sedex, RC é carta registrada etc. 9 | * 10 | * - Os caracteres de 3 a 10 são a numeração sequencial do tipo do objeto. 11 | * 12 | * - O caractere 11 é o dígito verificador. 13 | * 14 | * - Os caracteres 12 e 13 representa o código do País de onde a postagem partiu. 15 | * 16 | * 1) Partes do número 17 | * ______ ___________________________ ______ _______ 18 | * | Tipo | Número | DV | País | 19 | * | J T 1 9 4 6 9 0 6 9 8 B R | 20 | * |______|___________________________|______|_______| 21 | * 22 | * 2) Cálculo do DV. 23 | * 24 | * - Soma-se o produto das algarismos 3 a 10 pelos números 8, 6, 4, 2, 3, 5, 9, 7 25 | * 26 | * 1 9 4 6 9 0 6 9 27 | * x x x x x x x x 28 | * 8 6 4 2 3 5 9 7 29 | * = 8 +54 +16 +12 +18 +0 +54 +63 = 234 30 | * 31 | * - O somatório encontrado é dividido por 11 e o resultado é subtraído de 11 32 | * 234 / 11 tem resto 3. 11 - 3 = 8. DV1 é 8. 33 | * Obs.: Caso o cálculo de DV1 retorne 0, o resultado será 5. 34 | * Caso retorne 1, o resto será 0 35 | * 36 | * 37 | * 38 | * 39 | * Fonte: 40 | * 41 | * @param {String} value Objeto postal no formato JT194690698BR 42 | * @returns {Boolean} 43 | */ 44 | 45 | import ValidationBRError from './ValidationBRError' 46 | import { sumElementsByMultipliers, clearValue, fakeNumber, randomLetter } from './utils' 47 | 48 | /** 49 | * dv() 50 | * Calcula o dígito verificador 51 | * 52 | * @param {Number|String} value 53 | * @returns {String} 54 | */ 55 | export const dv = (value: string | number): string => { 56 | if (!value) throw ValidationBRError.EMPTY_VALUE 57 | 58 | const postalCode = String(value) 59 | .replace(/[^0-9]+/gi, '') 60 | .padStart(8, '0') 61 | .substring(0, 8) 62 | 63 | const sum = sumElementsByMultipliers(postalCode, [8, 6, 4, 2, 3, 5, 9, 7]) 64 | 65 | const rest = sum % 11 66 | // const specificities = { 0: { dv: 5 }, 1: { dv: 0 } } 67 | 68 | const specificities = [ 69 | { rest: 0, dv: 5 }, 70 | { rest: 1, dv: 0 }, 71 | ] 72 | 73 | const specifity = specificities.find((item) => item.rest === rest) 74 | 75 | const DV = specifity ? specifity.dv : 11 - rest 76 | 77 | return String(DV) 78 | } 79 | 80 | /** 81 | * Aplica uma máscara ao número informado 82 | * 83 | * @param {String} value Número de Processo 84 | * @returns {String} Valor com a máscara 85 | */ 86 | export const mask = (value: string | number): string => String(value).toLocaleUpperCase() 87 | 88 | /** 89 | * fake() 90 | * Gera um número válido 91 | * 92 | * @returns {String} 93 | */ 94 | export const fake = (withMask: boolean = false): string => { 95 | const num = fakeNumber(8, true) 96 | 97 | const postalCode = `${randomLetter()}${randomLetter()}${num}${dv(num)}BR` 98 | 99 | if (withMask) return mask(postalCode) 100 | return postalCode 101 | } 102 | 103 | /** 104 | * validateOrFail() 105 | * Valida se um número é válido e 106 | * retorna uma exceção se não estiver 107 | * 108 | * @param {String} value Número a ser validado 109 | * @returns {Boolean} 110 | */ 111 | export const validateOrFail = (value: string): boolean => { 112 | if (!/^[a-z]{2}([\d]{9})[a-z]{2}$/gi.test(String(value))) { 113 | throw new Error('O número não está no formato "XX000000000XX"') 114 | } 115 | 116 | const postalCode = clearValue(value.substring(2, 11), 9) 117 | 118 | if (dv(value.substring(2, 11)) !== postalCode.substring(8, 9)) { 119 | throw ValidationBRError.INVALID_DV 120 | } 121 | 122 | return true 123 | } 124 | 125 | /** 126 | * validate() 127 | * Valida se um número é válido 128 | * 129 | * @param {String} value Número a ser validado 130 | * @returns {Boolean} 131 | */ 132 | export const validate = (value: string): boolean => { 133 | try { 134 | return validateOrFail(value) 135 | } catch (error) { 136 | return false 137 | } 138 | } 139 | 140 | export default validate 141 | -------------------------------------------------------------------------------- /test/nup17.test.ts: -------------------------------------------------------------------------------- 1 | import isNUP17, { dv, fake, mask, validate, validateOrFail } from '../src/nup17' 2 | import * as _nup17 from '../src/nup17' 3 | 4 | describe('NUP17', () => { 5 | test('isNUP17() - Números válidos', () => { 6 | const list = [ 7 | // Masked 8 | '23037.001380/2021-11', 9 | '23037.001434/2021-48', 10 | '23037.001321/2021-42', 11 | // String 12 | '23037001462202165', 13 | '23037001537202116', 14 | '23037001086202117', 15 | ] 16 | 17 | list.forEach((nup17) => { 18 | expect(isNUP17(nup17)).toBeTruthy() 19 | expect(_nup17.validate(nup17)).toBeTruthy() 20 | }) 21 | }) 22 | 23 | test('validate() - Números válidos', () => { 24 | const list = [ 25 | // Masked 26 | '23037.001380/2021-11', 27 | '23037.001434/2021-48', 28 | '23037.001321/2021-42', 29 | // String 30 | '23037001462202165', 31 | '23037001537202116', 32 | '23037001086202117', 33 | ] 34 | 35 | list.forEach((nup17) => { 36 | expect(validate(nup17)).toBeTruthy() 37 | }) 38 | }) 39 | 40 | test('validate() - Números inválidos', () => { 41 | const list = [ 42 | // Masked 43 | '23037001380202112', 44 | '23037001434202142', 45 | '23037001462202162', 46 | '23037001537202112', 47 | ] 48 | 49 | list.forEach((nup17) => { 50 | expect(validate(nup17)).toBeFalsy() 51 | }) 52 | }) 53 | 54 | test('validateOrFail() - Números inválidos', () => { 55 | const list = [ 56 | // Masked 57 | '23037001380202112', 58 | '23037001434202142', 59 | '23037001462202162', 60 | '23037001537202112', 61 | ] 62 | 63 | list.forEach((nup17) => { 64 | expect(() => validateOrFail(nup17)).toThrow() 65 | }) 66 | }) 67 | 68 | test('validateOrFail() - Números válidos com caracteres adicionais', () => { 69 | const list = [ 70 | // Números válidos com 1 caractere a mais no final 71 | '230370014622021650', 72 | '230370015372021160', 73 | '230370010862021170', 74 | ] 75 | 76 | list.forEach((nup17) => { 77 | expect(() => validateOrFail(nup17)).toThrow() 78 | }) 79 | }) 80 | 81 | test('Parâmetro não informado', () => { 82 | expect(isNUP17('')).toBeFalsy() 83 | expect(validate('')).toBeFalsy() 84 | expect(() => validateOrFail('')).toThrow() 85 | expect(() => dv('')).toThrow() 86 | }) 87 | 88 | test('fake() - Gera fakes sem máscara', () => { 89 | for (let i = 0; i < 5; i += 1) { 90 | const nup17 = fake() 91 | expect(validate(nup17)).toBeTruthy() 92 | expect(nup17).toHaveLength(17) 93 | } 94 | }) 95 | 96 | test('fake() - Gera fakes com máscara', () => { 97 | for (let i = 0; i < 5; i += 1) { 98 | const nup17 = fake(true) 99 | expect(validate(nup17)).toBeTruthy() 100 | expect(nup17).toHaveLength(20) 101 | } 102 | }) 103 | 104 | test('dv() - Verificando se o DV gerado está correto', () => { 105 | const list = [ 106 | { num: '23037.001380/2021', expected: '11' }, 107 | { num: '23037.001434/2021', expected: '48' }, 108 | { num: '23037.001321/2021', expected: '42' }, 109 | { num: '230370014622021', expected: '65' }, 110 | { num: '230370015372021', expected: '16' }, 111 | { num: '230370010862021', expected: '17' }, 112 | ] 113 | 114 | list.forEach((item) => { 115 | const calcDv = dv(item.num) 116 | 117 | expect(calcDv).toBe(item.expected) 118 | expect(typeof calcDv).toBe('string') 119 | }) 120 | }) 121 | 122 | test('mask() - Testando se a máscara foi gerada corretamente', () => { 123 | const list = [ 124 | { num: '23037001380202111', expected: '23037.001380/2021-11' }, 125 | { num: '23037001434202148', expected: '23037.001434/2021-48' }, 126 | { num: '23037001321202142', expected: '23037.001321/2021-42' }, 127 | { num: '23037001462202165', expected: '23037.001462/2021-65' }, 128 | { num: '23037001537202116', expected: '23037.001537/2021-16' }, 129 | { num: '23037001086202117', expected: '23037.001086/2021-17' }, 130 | ] 131 | 132 | list.forEach((item) => { 133 | const masked = mask(item.num) 134 | 135 | expect(masked).toBe(item.expected) 136 | expect(masked).toHaveLength(20) 137 | }) 138 | }) 139 | }) 140 | -------------------------------------------------------------------------------- /src/cpf.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * isCPF() 3 | * Calcula se um CPF é válido 4 | * 5 | * @doc 6 | * CPF deve possuir 11 dígitos. 7 | * 8 | * - Os caracteres 1 a 8 são números sequenciais definidos pela Receita Federal 9 | * 10 | * - O caractere 9 refere-se à região fiscal emissora do documento 11 | * 1 – DF, GO, MS, MT e TO 12 | * 2 – AC, AM, AP, PA, RO e RR 13 | * 3 – CE, MA e PI 14 | * 4 – AL, PB, PE, RN 15 | * 5 – BA e SE 16 | * 6 – MG 17 | * 7 – ES e RJ 18 | * 8 – SP 19 | * 9 – PR e SC 20 | * 0 – RS 21 | * 22 | * - Os caracteres 10 e 11 são dígitos verificadores. 23 | * 24 | * 1) Partes do número 25 | * ------------------------------------------------ 26 | * | Número | R | DV | 27 | * 2 8 0 . 0 1 2 . 3 8 9 - 3 8 28 | * 29 | * 2) Cálculo do primeiro DV. 30 | * 31 | * - Soma-se o produto das algarismos 1 a 9 pelos números 10, 9, 8, 7, 6, 5, 4, 3, 2 32 | * 33 | * 2 8 0 0 1 2 3 8 9 34 | * x x x x x x x x x 35 | * 10 9 8 7 6 5 4 3 2 36 | * = 20 +72 +0 +0 +6 +10 +12 +24 +18 = 162 37 | * 38 | * - O somatório encontrado é dividido por 11 e o resultado é subtraído de 11 39 | * 162 / 11 tem resto 8. 11 - 8 = 3. DV1 é 3. 40 | * Obs.: Caso o cálculo de DV1 retorne 10, o resultado será 0. 41 | * 42 | * 3) Cálculo do segundo DV. 43 | * 44 | * - Soma-se o produto das algarismos 1 a 10 pelos números 11, 10, 9, 8, 7, 6, 5, 4, 3, 2 45 | * 46 | * 2 8 0 0 1 2 3 8 9 3 47 | * x x x x x x x x x x 48 | * 11 10 9 8 7 6 5 4 3 2 49 | * = 22 +80 +0 +0 +7 +12 +15 +32 +27 = 201 50 | * 51 | * - O somatório encontrado é dividido por 11 e o resultado é subtraído de 11 52 | * 201 / 11 tem resto 3. 11 - 3 = 8. DV2 é 8. 53 | * Obs.: Caso o cálculo de DV2 retorne 10, o resultado será 0. 54 | * 55 | * Fonte: http://clubes.obmep.org.br/blog/a-matematica-nos-documentos-cpf/ 56 | * 57 | * @param {String} value Título eleitoral 58 | * @returns {Boolean} 59 | */ 60 | 61 | import ValidationBRError from './ValidationBRError' 62 | import { sumElementsByMultipliers, sumToDV, clearValue, fakeNumber, applyMask } from './utils' 63 | 64 | /** 65 | * dv() 66 | * Calcula o dígito verificador 67 | * 68 | * @param {Number|String} value 69 | * @returns {String} 70 | */ 71 | export const dv = (value: string | number): string => { 72 | const cpf = clearValue(value, 9, { 73 | trimAtRight: true, 74 | rejectEmpty: true, 75 | }) 76 | 77 | const sum1 = sumElementsByMultipliers(cpf, [10, 9, 8, 7, 6, 5, 4, 3, 2]) 78 | const dv1 = sumToDV(sum1) 79 | 80 | const sum2 = sumElementsByMultipliers(cpf + dv1, [11, 10, 9, 8, 7, 6, 5, 4, 3, 2]) 81 | const dv2 = sumToDV(sum2) 82 | 83 | return `${dv1}${dv2}` 84 | } 85 | 86 | /** 87 | * Aplica uma máscara ao número informado 88 | * 89 | * @param {String} value Número de Processo 90 | * @returns {String} Valor com a máscara 91 | */ 92 | export const mask = (value: string | number): string => applyMask(value, '000.000.000-00') 93 | 94 | /** 95 | * fake() 96 | * Gera um número válido 97 | * 98 | * @returns {String} 99 | */ 100 | export const fake = (withMask: boolean = false): string => { 101 | const num = fakeNumber(9, true) 102 | 103 | const cpf = `${num}${dv(num)}` 104 | 105 | if (withMask) return mask(cpf) 106 | return cpf 107 | } 108 | 109 | /** 110 | * validateOrFail() 111 | * Valida se um número é válido e 112 | * retorna uma exceção se não estiver 113 | * 114 | * @param {String|Number} value Número a ser validado 115 | * @returns {Boolean} 116 | */ 117 | export const validateOrFail = (value: string | number): boolean => { 118 | const cpf = clearValue(value, 11, { 119 | fillZerosAtLeft: true, 120 | rejectEmpty: true, 121 | rejectHigherLength: true, 122 | rejectEqualSequence: true, 123 | }) 124 | 125 | if (dv(cpf) !== cpf.substring(9, 11)) { 126 | throw ValidationBRError.INVALID_DV 127 | } 128 | 129 | return true 130 | } 131 | 132 | /** 133 | * validate() 134 | * Valida se um número é válido 135 | * 136 | * @param {String|Number} value Número a ser validado 137 | * @returns {Boolean} 138 | */ 139 | export const validate = (value: string | number): boolean => { 140 | try { 141 | return validateOrFail(value) 142 | } catch (error) { 143 | return false 144 | } 145 | } 146 | 147 | export default validate 148 | -------------------------------------------------------------------------------- /src/tituloEleitor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * isTitulo() 3 | * Calcula se um título eleitoral é válido 4 | * 5 | * @doc 6 | * Título de eleitor deve possuir 12 dígitos. 7 | * 8 | * - Os caracteres 1 a 8 são números sequenciais. 9 | * 10 | * - Os caracteres 9 e 10 representam os estados da federação onde o título 11 | * foi emitido (01 = SP, 02 = MG, 03 = RJ, 04 = RS, 05 = BA, 06 = PR, 07 = CE, 08 = PE, 12 | * 09 = SC, 10 = GO, 11 = MA12 = PB, 13 = PA, 14 = ES, 15 = PI, 16 = RN, 17 = AL, 13 | * 18 = MT, 19 = MS, 20 = DF, 21 = SE, 22 = AM, 23 = RO, 24 = AC, 25 = AP, 26 = RR, 14 | * 27 = TO, 28 = Exterior(ZZ). 15 | * 16 | * - Os caracteres 11 e 12 são dígitos verificadores. 17 | * 18 | * 1) Partes do número 19 | * ------------------------------------------------ 20 | * | Número Sequencial | UF | DV | 21 | * 1 0 2 3 8 5 0 1 0 6 7 1 22 | * 23 | * 2) Cálculo do primeiro DV. 24 | * 25 | * - Soma-se o produto das algarismos 1 a 8 pelos números 2, 3, 4, 5, 6, 7, 8 e 9. 26 | * 27 | * 1 0 2 3 8 5 0 1 28 | * x x x x x x x x 29 | * 2 3 4 5 6 7 8 9 30 | * = 2 + 0 + 8 +15 +48 +35 + 0 + 9 = 117 31 | * 32 | * - O somatório encontrado é dividido por 11. O DV1 é o resto da divisão. Se o 33 | * resto for 10, o DV1 é 0. 34 | * 35 | * 2.1) 117 / 11 tem resto igual a 7. 36 | * 37 | * 3) Cálculo do segundo DV 38 | * 39 | * - Soma-se o produto dos algarismos 9 a 11 (relativos aos 2 dígitos da UF e o novo 40 | * DV1 que acabou de ser calculado) e os multiplicam pelos números 7, 8 e 9. Se o 41 | * resto for 10, DV2 será 0. 42 | * 0 6 7 43 | * x x x 44 | * 7 8 9 45 | * = 0 +48 +63 = 111 46 | * 47 | * 3.1) 111 / 11 tem resto igual a 1. 48 | * 49 | * Fonte: http://clubes.obmep.org.br/blog/a-matematica-nos-documentos-titulo-de-eleitor/ 50 | * 51 | * @param {String} value Título eleitoral 52 | * @returns {Boolean} 53 | */ 54 | 55 | import ValidationBRError from './ValidationBRError' 56 | import { sumElementsByMultipliers, clearValue, fakeNumber, applyMask } from './utils' 57 | 58 | /** 59 | * dv() 60 | * Calcula o dígito verificador 61 | * 62 | * @param {Number|String} value 63 | * @returns {String} 64 | */ 65 | export const dv = (value: string | number): string => { 66 | const titulo = clearValue(value, 10, { 67 | fillZerosAtLeft: true, 68 | trimAtRight: true, 69 | rejectEmpty: true, 70 | }) 71 | 72 | const sum1 = sumElementsByMultipliers(titulo.substring(0, 8), [2, 3, 4, 5, 6, 7, 8, 9]) 73 | const dv1 = sum1 % 11 >= 10 ? 0 : sum1 % 11 74 | 75 | const sum2 = sumElementsByMultipliers(titulo.substring(8, 10) + dv1, [7, 8, 9]) 76 | const dv2 = sum2 % 11 >= 10 ? 0 : sum2 % 11 77 | 78 | return `${dv1}${dv2}` 79 | } 80 | 81 | /** 82 | * Aplica uma máscara ao número informado 83 | * 84 | * @param {String} value Número de Processo 85 | * @returns {String} Valor com a máscara 86 | */ 87 | export const mask = (value: string | number): string => applyMask(value, '0000.0000.0000') 88 | 89 | /** 90 | * fake() 91 | * Gera um número válido 92 | * 93 | * @returns {String} 94 | */ 95 | export const fake = (withMask: boolean = false): string => { 96 | const num = fakeNumber(8, true) 97 | 98 | const uf = (Math.random() * 27 + 1).toFixed(0).padStart(2, '0') 99 | 100 | const titulo = `${num}${uf}${dv(num + uf)}` 101 | 102 | if (withMask) return mask(titulo) 103 | return titulo 104 | } 105 | 106 | /** 107 | * validateOrFail() 108 | * Valida se um número é válido e 109 | * retorna uma exceção se não estiver 110 | * 111 | * @param {String|Number} value Número a ser validado 112 | * @returns {Boolean} 113 | */ 114 | export const validateOrFail = (value: string | number): boolean => { 115 | const titulo = clearValue(value, 12, { 116 | fillZerosAtLeft: true, 117 | rejectEmpty: true, 118 | rejectHigherLength: true, 119 | rejectEqualSequence: true, 120 | }) 121 | 122 | if (dv(titulo) !== titulo.substring(10, 12)) { 123 | throw ValidationBRError.INVALID_DV 124 | } 125 | 126 | return true 127 | } 128 | 129 | /** 130 | * validate() 131 | * Valida se um número é válido 132 | * 133 | * @param {String|Number} value Número a ser validado 134 | * @returns {Boolean} 135 | */ 136 | export const validate = (value: string | number): boolean => { 137 | try { 138 | return validateOrFail(value) 139 | } catch (error) { 140 | return false 141 | } 142 | } 143 | 144 | export default validate 145 | -------------------------------------------------------------------------------- /test/judicialProcess.test.ts: -------------------------------------------------------------------------------- 1 | import isJudicialProcess, { 2 | dv, 3 | fake, 4 | mask, 5 | validate, 6 | validateOrFail, 7 | _getSubCourt, 8 | } from '../src/judicialProcess' 9 | 10 | import * as _judicialProcess from '../src/judicialProcess' 11 | 12 | describe('JudicialProcess', () => { 13 | test('isJudicialProcess() - Números válidos', () => { 14 | const list = [ 15 | '20802520125150049', 16 | '61052838320098130024', 17 | '00110060720168200100', 18 | '08002785520134058400', 19 | '08002732820164058400', 20 | ] 21 | 22 | list.forEach((judicialProcess) => { 23 | expect(isJudicialProcess(judicialProcess)).toBeTruthy() 24 | expect(_judicialProcess.validate(judicialProcess)).toBeTruthy() 25 | }) 26 | }) 27 | 28 | test('validate() - Números válidos', () => { 29 | const list = [ 30 | '20802520125150049', 31 | '61052838320098130024', 32 | '00110060720168200100', 33 | '08002785520134058400', 34 | '08002732820164058400', 35 | ] 36 | 37 | list.forEach((judicialProcess) => { 38 | expect(validate(judicialProcess)).toBeTruthy() 39 | }) 40 | }) 41 | 42 | test('validate() - Números inválidos', () => { 43 | const list = [ 44 | '20802520125150044', 45 | '61052838320098130023', 46 | '00110060720168200102', 47 | '08002785520134058401', 48 | '08002732820164058406', 49 | '08002732820160058400', // Órgão judiciário igual a 0 50 | ] 51 | 52 | list.forEach((judicialProcess) => { 53 | expect(validate(judicialProcess)).toBeFalsy() 54 | }) 55 | }) 56 | 57 | test('validateOrFail() - Números inválidos', () => { 58 | const list = [ 59 | '20802520125150044', 60 | '61052838320098130023', 61 | '00110060720168200102', 62 | '08002785520134058401', 63 | '08002732820164058406', 64 | '08002732820160058400', // Órgão judiciário igual a 0 65 | ] 66 | 67 | list.forEach((judicialProcess) => { 68 | expect(() => validateOrFail(judicialProcess)).toThrow() 69 | }) 70 | }) 71 | 72 | test('Parâmetro não informado', () => { 73 | expect(isJudicialProcess('')).toBeFalsy() 74 | expect(validate('')).toBeFalsy() 75 | expect(() => validateOrFail('')).toThrow() 76 | expect(() => dv('')).toThrow() 77 | }) 78 | 79 | test('fake() - Gera fakes sem máscara', () => { 80 | for (let i = 0; i < 500; i += 1) { 81 | const judicialProcess = fake() 82 | expect(validate(judicialProcess)).toBeTruthy() 83 | expect(judicialProcess).toHaveLength(20) 84 | } 85 | }) 86 | 87 | test('fake() - Gera fakes com máscara', () => { 88 | for (let i = 0; i < 500; i += 1) { 89 | const judicialProcess = fake(true) 90 | expect(validate(judicialProcess)).toBeTruthy() 91 | expect(judicialProcess).toHaveLength(25) 92 | } 93 | }) 94 | 95 | test('dv() - Verificando se o DV gerado está correto', () => { 96 | const list = [ 97 | { num: '000208020125150049', expected: '25' }, 98 | { num: '610528320098130024', expected: '83' }, 99 | { num: '001100620168200100', expected: '07' }, 100 | { num: '080027820134058400', expected: '55' }, 101 | { num: '080027320164058400', expected: '28' }, 102 | ] 103 | 104 | list.forEach((item) => { 105 | const calcDv = dv(item.num) 106 | 107 | expect(calcDv).toBe(item.expected) 108 | expect(typeof calcDv).toBe('string') 109 | }) 110 | }) 111 | 112 | test('mask() - Testando se a máscara foi gerada corretamente', () => { 113 | const list = [ 114 | { num: '20802520125150049', expected: '0002080-25.2012.5.15.0049' }, 115 | { num: '61052838320098130024', expected: '6105283-83.2009.8.13.0024' }, 116 | { num: '00110060720168200100', expected: '0011006-07.2016.8.20.0100' }, 117 | { num: '08002785520134058400', expected: '0800278-55.2013.4.05.8400' }, 118 | { num: '08002732820164058400', expected: '0800273-28.2016.4.05.8400' }, 119 | ] 120 | 121 | list.forEach((item) => { 122 | const masked = mask(item.num) 123 | 124 | expect(masked).toBe(item.expected) 125 | expect(masked).toHaveLength(25) 126 | }) 127 | }) 128 | 129 | test('_getSubCourt() - Valor diferente de zero', () => { 130 | expect(_getSubCourt('01')).toBe('01') 131 | expect(_getSubCourt('02')).toBe('02') 132 | expect(_getSubCourt('03')).toBe('03') 133 | expect(_getSubCourt('04')).toBe('04') 134 | expect(_getSubCourt('05')).toBe('05') 135 | expect(_getSubCourt('06')).toBe('06') 136 | expect(_getSubCourt('07')).toBe('07') 137 | expect(_getSubCourt('08')).toBe('08') 138 | expect(_getSubCourt('09')).toBe('09') 139 | }) 140 | 141 | test('_getSubCourt() - Valor igual a zero', () => { 142 | expect(_getSubCourt('00')).toBe('01') 143 | expect(_getSubCourt('0')).toBe('01') 144 | }) 145 | 146 | test('_getSubCourt() - Valor vazio', () => { 147 | expect(_getSubCourt().length).toBe(2) 148 | }) 149 | }) 150 | -------------------------------------------------------------------------------- /test/cnpj.test.ts: -------------------------------------------------------------------------------- 1 | import isCNPJ, { dv, fake, mask, validate, validateOrFail } from '../src/cnpj' 2 | import * as _cnpj from '../src/cnpj' 3 | 4 | describe('CNPJ', () => { 5 | test.each([ 6 | // Com máscara 7 | '11.222.333/0001-81', 8 | '73.797.980/0001-79', 9 | '06.946.762/0001-61', 10 | '96.051.576/0001-57', 11 | '55.585.709/0001-98', 12 | // inteiro 13 | 99360938000180, 14 | 23693443000100, 15 | // string 16 | '32432147000147', 17 | '91951438000100', 18 | ])('isCNPJ() - Números válidos', (cnpj) => { 19 | expect(isCNPJ(cnpj)).toBeTruthy() 20 | expect(_cnpj.validate(cnpj)).toBeTruthy() 21 | }) 22 | 23 | test.each([ 24 | // Com máscara 25 | '11.222.333/0001-81', 26 | '73.797.980/0001-79', 27 | '06.946.762/0001-61', 28 | '96.051.576/0001-57', 29 | '55.585.709/0001-98', 30 | // inteiro 31 | 99360938000180, 32 | 23693443000100, 33 | // string 34 | '32432147000147', 35 | '91951438000100', 36 | ])('validate() - Números válidos', (cnpj) => { 37 | 38 | expect(validate(cnpj)).toBeTruthy() 39 | }) 40 | 41 | test.each([ 42 | '53.797.980/0001-79', 43 | '36.946.762/0001-61', 44 | '26.051.576/0001-57', 45 | '85.585.709/0001-98', 46 | '39360938000180', 47 | '93693443000100', 48 | '12432147000147', 49 | '61951438000100', 50 | '11111111111111', 51 | ])('validate() - Números inválidos', (cnpj) => { 52 | expect(validate(cnpj)).toBeFalsy() 53 | }) 54 | 55 | test.each([ 56 | '53.797.980/0001-79', 57 | '36.946.762/0001-61', 58 | '26.051.576/0001-57', 59 | '85.585.709/0001-98', 60 | '39360938000180', 61 | '93693443000100', 62 | '12432147000147', 63 | '61951438000100', 64 | '11111111111111', 65 | ])('validateOrFail() - Números inválidos', (cnpj) => { 66 | expect(() => validateOrFail(cnpj)).toThrow() 67 | 68 | 69 | }) 70 | 71 | test('Parâmetro não informado', () => { 72 | expect(isCNPJ('')).toBeFalsy() 73 | expect(validate('')).toBeFalsy() 74 | expect(() => validateOrFail('')).toThrow() 75 | expect(() => dv('')).toThrow() 76 | }) 77 | 78 | test('fake() - Gera fakes sem máscara', () => { 79 | for (let i = 0; i < 5; i += 1) { 80 | const cnpj = fake() 81 | expect(validate(cnpj)).toBeTruthy() 82 | expect(cnpj).toHaveLength(14) 83 | } 84 | }) 85 | 86 | test('fake() - Gera fakes com máscara', () => { 87 | for (let i = 0; i < 5; i += 1) { 88 | const cnpj = fake(true) 89 | expect(validate(cnpj)).toBeTruthy() 90 | expect(cnpj).toHaveLength(18) 91 | } 92 | }) 93 | 94 | test('fake() - Gera fakes com máscara usando opções como objeto', () => { 95 | for (let i = 0; i < 5; i += 1) { 96 | const cnpj = fake({ withMask: true, alphanumeric: false }) 97 | expect(validate(cnpj)).toBeTruthy() 98 | expect(cnpj).toHaveLength(18) 99 | expect(cnpj).toMatch(/^\d{2}\.\d{3}\.\d{3}\/\d{4}\-\d{2}$/) 100 | } 101 | }) 102 | 103 | test('fake() - Gera fakes sem máscara usando opções como objeto', () => { 104 | for (let i = 0; i < 5; i += 1) { 105 | const cnpj = fake({ withMask: false, alphanumeric: false }) 106 | expect(cnpj).toMatch(/^\d{14}$/) 107 | expect(validate(cnpj)).toBeTruthy() 108 | expect(cnpj).toHaveLength(14) 109 | } 110 | }) 111 | 112 | test('fake() - Gera CNPJs alfanuméricos', () => { 113 | for (let i = 0; i < 5; i += 1) { 114 | const cnpj = fake({ withMask: false, alphanumeric: true }) 115 | expect(cnpj).toMatch(/^[0-9A-Z]{14}$/) 116 | expect(validate(cnpj)).toBeTruthy() 117 | expect(cnpj).toHaveLength(14) 118 | } 119 | }) 120 | 121 | test.each([ 122 | { num: '112223330001', expected: '81' }, 123 | { num: 993609380001, expected: '80' }, 124 | { num: '324321470001', expected: '47' }, 125 | { num: '132496630001', expected: '96' }, 126 | { num: '752827070001', expected: '37' }, 127 | { num: '265066480001', expected: '28' }, 128 | { num: '708032680001', expected: '47' }, 129 | { num: '195255840001', expected: '47' }, 130 | { num: '888634370001', expected: '08' }, 131 | { num: '060757490001', expected: '84' }, 132 | { num: '554120850001', expected: '07' }, 133 | { num: '754097240001', expected: '92' }, 134 | 135 | ])('dv() - Verificando se o DV gerado está correto', (item) => { 136 | const calcDv = dv(item.num) 137 | expect(calcDv).toBe(item.expected) 138 | expect(typeof calcDv).toBe('string') 139 | }) 140 | 141 | test.each([ 142 | { value: '11222333000181', expected: '11.222.333/0001-81' }, 143 | { value: 99360938000180, expected: '99.360.938/0001-80' }, 144 | { value: '32432147000147', expected: '32.432.147/0001-47' }, 145 | { value: 432147000147, expected: '00.432.147/0001-47' }, 146 | ])('mask() - Testando se a máscara foi gerada corretamente', (item) => { 147 | const masked = mask(item.value) 148 | expect(masked).toBe(item.expected) 149 | expect(masked).toHaveLength(18) 150 | }) 151 | }) 152 | 153 | describe('CNPJ alfanumérico', () => { 154 | 155 | test.each([ 156 | { num: 'A12223330001', expected: '50' }, 157 | { num: 'B12223330001', expected: '03' }, 158 | { num: 'C12223330001', expected: '67' }, 159 | { num: 'D12223330001', expected: '10' }, 160 | { num: 'E12223330001', expected: '74' }, 161 | ])('dv() - Verificando se o DV gerado de %s está correto', (item) => { 162 | const calcDv = dv(item.num) 163 | expect(calcDv).toBe(item.expected) 164 | expect(typeof calcDv).toBe('string') 165 | }) 166 | }) 167 | -------------------------------------------------------------------------------- /src/nup17.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * isFederalProtocol() 3 | * Calcula se é um número válido de protocolo do Governo Federal Brasileiro 4 | * 5 | * @doc 6 | * O Número Unificado de Protocolo de processos do Governo Federal, também conhecido 7 | * como NUP17, deve ter 17 caracteres, incluindo o dígito verificador de 2 caracteres. 8 | * 9 | * 1) Partes do número 10 | * 11 | * - Os caracteres 1 a 5 são um código do órgão que gerou o protocolo. 12 | * 13 | * - Os caracteres 6 a 11 são o número sequencial do protocolo, sendo que 14 | * cada órgão emissor tem sua própria sequência e esta é reiniciada a cada ano. 15 | * 16 | * - Os caracteres 12 a 15 são referentes ao ano de protocolo 17 | * 18 | * - Os caracteres 16 a 17 são referentes ao Dígito Verificador 19 | * 20 | * 1.2) Exemplo 21 | * --------------------------------------------------------------- 22 | * | Código do órgão | Número Sequencial | Ano | D V 23 | * 2 3 0 3 7 . 0 0 1 4 6 2 / 2 0 2 1 - 6 5 24 | * 25 | * 2) Cálculo do primeiro DV. 26 | * 27 | * - Soma-se o produto das algarismos 1 a 15 pelos números 28 | * 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2 29 | * 30 | * 2 3 0 3 7 0 0 1 4 6 2 2 0 2 1 31 | * x x x x x x x x x x x x x x x 32 | * 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 33 | * = 32 +45 +0 +39 +84 +0 +0 +9 +32 +42 +12 +10 +0 +6 +2 = 313 34 | * 35 | * - O somatório encontrado é dividido por 11. O resto da divisão é subtraído de 11. 36 | * 313 / 11 tem resto 5. 11 - 5 = 6. DV1 é 6. 37 | * Obs.: Caso o cálculo de DV1 retorne 10, o resultado será 0. Caso retorne 11, o DV 38 | * será 1. Ou seja, se for maior ou igual a 10, desconsidere a casa das dezenas 39 | * 40 | * 3) Cálculo do segundo DV. 41 | * 42 | * - Acrescenta o valor do DV1 ao número e faz o somatório dos produtos pelos números 43 | * 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2 44 | * 45 | * 2 3 0 3 7 0 0 1 4 6 2 2 0 2 1 6 46 | * x x x x x x x x x x x x x x x x 47 | * 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 48 | * = 34 +48 +0 +42 +91 +0 +0 +10 +36 +48 +14 +12 +0 +8 +3 +12 = 358 49 | * 50 | * - O somatório encontrado é dividido por 11. O resto da divisão é subtraído de 11. 51 | * 358 / 11 tem resto 6. 11 - 6 = 1. DV1 é 5. 52 | * Obs.: Caso o cálculo de DV1 retorne 10, o resultado será 0. Caso retorne 11, o DV 53 | * será 1. Ou seja, se for maior ou igual a 10, desconsidere a casa das dezenas. 54 | * 55 | * = DV = 65 56 | * 57 | * Fonte: https://www.gov.br/compras/pt-br/acesso-a-informacao/legislacao/portarias/portaria-interministerial-no-11-de-25-de-novembro-de-2019 58 | * 59 | * @param {String} value Título eleitoral 60 | * @returns {Boolean} 61 | */ 62 | 63 | import ValidationBRError from './ValidationBRError' 64 | import { sumElementsByMultipliers, clearValue, fakeNumber, applyMask } from './utils' 65 | 66 | /** 67 | * dv() 68 | * Calcula o dígito verificador 69 | * 70 | * @param {String} value 71 | * @returns {String} 72 | */ 73 | export const dv = (value: string): string => { 74 | const nup = clearValue(value, 15, { rejectEmpty: true, trimAtRight: true }) 75 | const nupReverse = nup.split('').reverse().join('') 76 | 77 | const sum1 = sumElementsByMultipliers( 78 | nupReverse, 79 | [...Array(15)].map((_, i) => i + 2), 80 | ) 81 | 82 | const dv1 = _specificSumToDV(sum1) 83 | 84 | const sum2 = sumElementsByMultipliers( 85 | dv1 + nupReverse, 86 | [...Array(16)].map((_, i) => i + 2), 87 | ) 88 | 89 | const dv2 = _specificSumToDV(sum2) 90 | 91 | return `${dv1}${dv2}` 92 | } 93 | 94 | /** 95 | * Aplica uma máscara ao número informado 96 | * 97 | * @param {String} value Número de Processo 98 | * @returns {String} Valor com a máscara 99 | */ 100 | export const mask = (value: string): string => applyMask(value, '00000.000000/0000-00') 101 | 102 | /** 103 | * fake() 104 | * Gera um número válido 105 | * 106 | * @param {Boolean} withMask Define se o número deve ser gerado com ou sem máscara 107 | * @returns {String} 108 | */ 109 | export const fake = (withMask: boolean = false): string => { 110 | const num = fakeNumber(15, true) 111 | 112 | const nup = `${num}${dv(String(num))}` 113 | 114 | if (withMask) return mask(nup) 115 | return nup 116 | } 117 | 118 | /** 119 | * validateOrFail() 120 | * Valida se um número é válido e 121 | * retorna uma exceção se não estiver 122 | * 123 | * @param {String} value Número a ser validado 124 | * @returns {Boolean} 125 | */ 126 | export const validateOrFail = (value: string): boolean => { 127 | const nup = clearValue(value, 17, { 128 | rejectEmpty: true, 129 | rejectHigherLength: true, 130 | }) 131 | 132 | if (dv(nup) !== nup.substring(15, 17)) { 133 | throw ValidationBRError.INVALID_DV 134 | } 135 | 136 | return true 137 | } 138 | 139 | /** 140 | * validate() 141 | * Valida se um número é válido 142 | * 143 | * @param {String} value Número a ser validado 144 | * @returns {Boolean} 145 | */ 146 | export const validate = (value: string): boolean => { 147 | try { 148 | return validateOrFail(value) 149 | } catch (error) { 150 | return false 151 | } 152 | } 153 | 154 | export default validate 155 | 156 | function _specificSumToDV(sum: number): number { 157 | const rest = 11 - (sum % 11) 158 | const exceptions = [ 159 | { rest: 11, dv: 1 }, 160 | { rest: 10, dv: 0 }, 161 | ] 162 | 163 | const inExceptions = exceptions.find((item) => item.rest === rest) 164 | 165 | return !inExceptions ? rest : inExceptions.dv 166 | } 167 | -------------------------------------------------------------------------------- /src/cnpj.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * isCNPJ() 3 | * Calcula se um CNPJ é válido 4 | * 5 | * A partir da Nota Técnica conjunta COCAD/SUARA/RFB nº 49 de 14 de maio de 2024, CNPJ passa 6 | * a poder ser criado com letras e números, ao invés de apenas números. Esta alteração entra 7 | * em vigor em 2026. 8 | * 9 | * 10 | * @doc 11 | * - CNPJ deve possuir 14 dígitos no formato AA.AAA.AAA/AAAA-NN, onde A representa letras 12 | * ou números e N representa números (Nota Técnica conjunta COCAD/SUARA/RFB nº 49 de 14 de maio de 2024) 13 | * 14 | * - Os caracteres 1 a 8 são a identificação da empresa definida pela Receita Federal. Podem ser letras ou números 15 | * 16 | * - Os caracteres 9 a 12 são a identificação das filiais da empresa. Podendo ser letras ou números 17 | * 18 | * - Os caracteres 13 e 14 são os dígitos verificadores 19 | * 20 | * 1) Partes do número 21 | * _______________________________ _______________ _______ 22 | * | Número | Filiais | DV | 23 | * | 1 1 . 2 2 2 . 3 3 3 / 0 0 0 1 - X Y | 24 | * |_______________________________|_______________|_______| 25 | * 26 | * 27 | * 2.1) Conversão dos números para tabela ASCII 28 | * Converte os caracteres do CNPJ em valores numéricos, mesmo que alguns deles 29 | * sejam numéricos. A conversão será baseada na tabela ASCII 30 | * 31 | * Tabela ASCII 32 | * 0 = 48 1 = 49 2 = 50 3 = 51 4 = 52 33 | * 5 = 53 6 = 54 7 = 55 8 = 56 9 = 57 34 | * A = 65 B = 66 C = 67 D = 68 E = 69 35 | * F = 70 G = 71 H = 72 I = 73 J = 74 36 | * K = 75 L = 76 M = 77 N = 78 O = 79 37 | * P = 80 Q = 81 R = 82 S = 83 T = 84 38 | * U = 85 V = 86 W = 87 X = 88 Y = 89 39 | * Z = 90 40 | * 41 | * Ao converter cada dígito do CNPJ para o seu equivalente na tabela ASCII, subtraia de 48 42 | * para obter o número que será multiplicado. 43 | * Como o "0" é 48 e deve-se subtrair de 48, não há mudanças nos números. 44 | * 45 | * 46 | * 47 | * 2) Cálculo do primeiro DV. 48 | * 49 | * - Soma-se o produto das algarismos 1 a 12 pelos números 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2 50 | * 51 | * 1 1 2 2 2 3 3 3 0 0 0 1 52 | * x x x x x x x x x x x x 53 | * 5 4 3 2 9 8 7 6 5 4 3 2 54 | * = 5 +4 +6 +4 +18 +24 +21 +18 +0 +0 +0 +2 = 102 55 | * 56 | * - O somatório encontrado é dividido por 11 e o resultado é subtraído de 11 57 | * 102 / 11 tem resto 8. 11 - 3 = 8. DV1 é 8. 58 | * Obs.: Caso o cálculo de DV1 retorne 10, o resultado será 0. 59 | * 60 | * 3) Cálculo do segundo DV. 61 | * 62 | * - Soma-se o produto das algarismos 1 a 13 (incluindo o DV1 calculado) pelos 63 | * números 6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2. 64 | * 65 | * 1 1 2 2 2 3 3 3 0 0 0 1 8 66 | * x x x x x x x x x x x x x 67 | * 6 5 4 3 2 9 8 7 6 5 4 3 2 68 | * = 6 +5 +8 +6 +4 +27 +24 +21 +0 +0 +0 +3 +16 = 120 69 | * 70 | * - O somatório encontrado é dividido por 11 e o resultado é subtraído de 11 71 | * 120 / 11 tem resto 10. 11 - 10 = 1. DV2 é 1. 72 | * Obs.: Caso o cálculo de DV2 retorne 10, o resultado será 0. 73 | * 74 | * Fonte: http://www.macoratti.net/alg_cnpj.htm 75 | * 76 | * @param {String} value Título eleitoral 77 | * @returns {Boolean} 78 | */ 79 | 80 | import ValidationBRError from './ValidationBRError' 81 | import { sumElementsByMultipliers, sumToDV, clearValue, fakeNumber, applyMask } from './utils' 82 | 83 | type FakeInput = { 84 | withMask?: boolean; 85 | alphanumeric?: boolean 86 | } 87 | 88 | export function dv(value: string | number): string { 89 | const cnpj = clearValue(value, 12, { 90 | trimAtRight: true, 91 | rejectEmpty: true, 92 | }) 93 | 94 | const dv1Factors = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] 95 | const dv1 = sumToDvWithAlpha(cnpj.substring(0, 12), dv1Factors) 96 | 97 | const dv2Factors = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] 98 | const dv2 = sumToDvWithAlpha(cnpj.substring(0, 12) + dv1, dv2Factors) 99 | 100 | return `${dv1}${dv2}` 101 | } 102 | 103 | /** 104 | * Aplica uma máscara ao número informado 105 | * 106 | * @param {String} value Número de Processo 107 | * @returns {String} Valor com a máscara 108 | */ 109 | export function mask(value: string | number): string { 110 | return applyMask(value, '00.000.000/0000-00'); 111 | } 112 | 113 | /** 114 | * 115 | * 116 | */ 117 | export function fake(options?: FakeInput): string; 118 | export function fake(withMask?: boolean): string; 119 | export function fake(input: FakeInput | boolean = false): string { 120 | 121 | const options = typeof input === 'boolean' 122 | ? { withMask: input, alphanumeric: true } 123 | : { withMask: false, alphanumeric: true, ...input } 124 | 125 | const num = fakeNumber(12, true, options.alphanumeric); 126 | 127 | const cnpj = `${num}${dv(num)}`; 128 | 129 | if (options.withMask) return mask(cnpj) 130 | return cnpj 131 | } 132 | 133 | /** 134 | * validateOrFail() 135 | * Valida se um número é válido e 136 | * retorna uma exceção se não estiver 137 | * 138 | * @param {String|Number} value Número a ser validado 139 | * @returns {Boolean} 140 | */ 141 | export function validateOrFail(value: string | number): boolean { 142 | const cnpj = clearValue(value, 14, { 143 | fillZerosAtLeft: false, 144 | rejectEmpty: true, 145 | rejectHigherLength: true, 146 | rejectEqualSequence: true, 147 | }) 148 | 149 | if (dv(cnpj) !== cnpj.substring(12, 14)) { 150 | throw ValidationBRError.INVALID_DV 151 | } 152 | 153 | return true 154 | } 155 | 156 | /** 157 | * validate() 158 | * Valida se um número é válido 159 | * 160 | * @param {String|Number} value Número a ser validado 161 | * @returns {Boolean} 162 | */ 163 | export function validate(value: string | number): boolean { 164 | try { 165 | return validateOrFail(value) 166 | } catch (error) { 167 | return false 168 | } 169 | } 170 | 171 | export default validate 172 | 173 | 174 | /** 175 | * 176 | * Converte o número para 177 | * 178 | * 179 | */ 180 | function asciiTableConverter(character: string): number { 181 | if (/^\d$/.test(character)) return +character; 182 | const ascii = character.toLocaleUpperCase().charCodeAt(0) - 48; 183 | 184 | return ascii; 185 | } 186 | 187 | /** 188 | * 189 | * 190 | * 191 | */ 192 | function sumToDvWithAlpha(value: string, multiplier: number[]) { 193 | const sum = [...value] 194 | .map(character => asciiTableConverter(character)) 195 | .reduce((sum: number, asciiChar: any, index: number) => sum + asciiChar * multiplier[index], 0); 196 | 197 | return sumToDV(sum); 198 | } -------------------------------------------------------------------------------- /src/judicialProcess.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * judicialProcess 3 | * Valida, mascara e cria números de processos judiciais 4 | * 5 | * @link 6 | * https://juslaboris.tst.jus.br/bitstream/handle/20.500.12178/30318/2008_res0065_cnj_rep01.pdf?sequence=2 7 | * http://ghiorzi.org/DVnew.htm#f 8 | * 9 | * @doc 10 | * Os números de processos judiciais são usados a partir de 2010 para unificar a 11 | * numeração de processos no Brasil e são usados em todos os tribunais. 12 | * 13 | * O número do processo, sem os caracteres especiais, devem possuir até 20 números 14 | * e deve seguir o padrão abaixo: 15 | * 16 | * 1) Partes do número 17 | * 0002080-25.2012.5.15.0049 18 | * NNNNNNN-DD.AAAA.J.TR.OOOO 19 | * |______|__|____|_|__|____| 20 | * | | | | | |----> Unidade de origem do processo com 4 caracteres 21 | * | | | | |--------> TR=Tribunal do segmento do poder judiciário com 2 caracteres 22 | * | | | |----------> J=Órgão do poder Judiciário com 1 caractere 23 | * | | |-------------> Ano do ajuizamento do processo com 4 caracteres 24 | * | |-----------------> Dígito verificador com 2 caracteres 25 | * |----------------------> Número sequencial do Processo, por unidade de 26 | * origem, reiniciado anualmente com 7 caracteres 27 | * 28 | * Órgãos do Poder Judiciário 29 | * 1 - Supremo Tribunal Federal 30 | * 2 - Conselho Nacional de Justiça 31 | * 3 - Superior Tribunal de Justiça 32 | * 4 - Justiça Federal 33 | * 5 - Justiça do Trabalho 34 | * 6 - Justiça Eleitoral 35 | * 7 - Justiça Militar da União 36 | * 8 - Justiça dos Estados e do Distrito Federal e Territórios 37 | * 9 - Justiça Militar Estadual 38 | * 39 | * 40 | * 2) Dígito Verificador 41 | * 42 | * O algoritmo usado para o cálculo do DV chama-se Módulo 97 de Base 10 (ISO 7064). 43 | * 44 | * Nota: O número do processo possui 20 caracteres e ultrapassa o tamanho máximo 45 | * do inteiro em javascript, impedindo que façamos o cálculo diretamente, desta 46 | * forma, será nacessária uma fatoração para que o resultado seja o correto. 47 | * 48 | * 2.1) Cálculo do DV 49 | * - Caso o DV seja conhecido, ele precisa ser removido do número e colocado 50 | * como "00" ao final. Caso não esteja incluso no número, adicione '00' ao final. 51 | * 52 | * Ex.: O processo "00020802520125150049", cujo dv é "25", será calculado como 53 | * "000208020125150049" e receberá "00" ao final. O número usado para o cálculo 54 | * do DV será "00020802012515004900" 55 | * 56 | * 2.2) Etapas de Cálculo 57 | * 58 | * 00020802012515004900 59 | * ↓↓ 60 | * DV ao final como "00" 61 | * 62 | * - Aplicamos o MOD 97 aos caracteres de 0 a 7 para calcular a primeira parte 63 | * part1 = 0002080 % 97 = 43 64 | * 65 | * - Concatenamos part1 ao ano, órgão do poder judiciário e tribunal e aplicamos o MOD 97 66 | * para obtermos o valor da part2 67 | * part2 = ( part1 +''+ 2012 +''+ 5 +''+ 15 ) % 97 = 26 68 | * 69 | * - Concatemos part2 ao código do órgão de origem e ao "00" do final e aplicamos 70 | * o MOD 97 ao resultado 71 | * part3 = ( part2 + '0049' + '00') % 97 = 73 72 | * 73 | * - Subtraímos o resultado de 98 74 | * dv = 98 - 73 = 25 75 | * 76 | * O Dígito verificador é 25 e deve ser aplicado após o 7º caractere do número do processo 77 | * 78 | * Fonte: https://juslaboris.tst.jus.br/bitstream/handle/20.500.12178/30318/2008_res0065_cnj_rep01.pdf?sequence=2 79 | */ 80 | 81 | import ValidationBRError from './ValidationBRError' 82 | import { clearValue, fakeNumber, applyMask, insertAtPosition, removeFromPosition } from './utils' 83 | 84 | /** 85 | * 86 | * 87 | */ 88 | export const dv = (value: string): string => { 89 | const judicialProcess = clearValue(value, 18, { trimAtRight: true, rejectEmpty: true }) 90 | 91 | const num = judicialProcess.substring(0, 7) 92 | const yearAndCourt = judicialProcess.substring(7, 14) 93 | const origin = judicialProcess.substring(14, 18) 94 | 95 | return String( 96 | 98 - (Number(`${Number(`${Number(num) % 97}${yearAndCourt}`) % 97}${origin}00`) % 97), 97 | ).padStart(2, '0') 98 | } 99 | 100 | /** 101 | * Aplica uma máscara ao número informado 102 | * 103 | * @param {String} value Número de Processo 104 | * @returns {String} Valor com a máscara 105 | */ 106 | export const mask = (value: string | number): string => 107 | applyMask(value, '0000000-00.0000.0.00.0000') 108 | 109 | /** 110 | * 111 | * 112 | */ 113 | export const fake = (withMask: boolean = false): string => { 114 | const num = fakeNumber(7, true) 115 | const year = new Date().getFullYear() - +fakeNumber(1) 116 | 117 | let courte1 = fakeNumber(1, true) // Não pode ser '0' 118 | courte1 = courte1 === '0' ? '1' : courte1 119 | 120 | const courte2 = _getSubCourt() 121 | 122 | const courte = `${courte1}${courte2}` 123 | 124 | const origin = fakeNumber(4, true) 125 | 126 | const judicialProcess = `${num}${year}${courte}${origin}` 127 | const digits = dv(judicialProcess) 128 | 129 | const finalNumber = insertAtPosition(judicialProcess, digits, 7) 130 | 131 | if (withMask) return mask(finalNumber) 132 | return finalNumber 133 | } 134 | 135 | /** 136 | * validateOrFail() 137 | * Valida se um número é válido e 138 | * retorna uma exceção se não estiver 139 | * 140 | * @param {String} value Número a ser validado 141 | * @returns {Boolean} 142 | */ 143 | export const validateOrFail = (value: string): boolean => { 144 | const judicialProcess = clearValue(value, 20, { 145 | fillZerosAtLeft: true, 146 | rejectEmpty: true, 147 | rejectHigherLength: true, 148 | }) 149 | const processWithoutDV = removeFromPosition(judicialProcess, 7, 9) 150 | 151 | if (processWithoutDV.substring(11, 12) === '0') { 152 | throw new Error('Código do Órgão Judiciário não pode ser "0"') 153 | } 154 | 155 | if (dv(processWithoutDV) !== judicialProcess.substring(7, 9)) { 156 | throw ValidationBRError.INVALID_DV 157 | } 158 | 159 | return true 160 | } 161 | 162 | /** 163 | * validate() 164 | * Valida se um número é válido 165 | * 166 | * @param {String} value Número a ser validado 167 | * @returns {Boolean} 168 | */ 169 | export const validate = (value: string): boolean => { 170 | try { 171 | return validateOrFail(value) 172 | } catch (error) { 173 | return false 174 | } 175 | } 176 | 177 | // //////////////////////////////////////////// 178 | // 179 | // Funções auxiliares 180 | // 181 | // //////////////////////////////////////////// 182 | 183 | /** 184 | * Gera um número fake da sub corte de acordo com as regras: 185 | * - Precisa ser uma string numérica de 2 dígitos. 186 | * - Não pode ser '0'. CAso seja zero, mude para '01'. 187 | * 188 | * A função aceita um parâmetro para viabilizar os testes. Caso 189 | * não seja definido, será gerado aleatoriamente. 190 | * 191 | * @param 192 | * 193 | */ 194 | export function _getSubCourt(courte: string | undefined = undefined): string { 195 | courte = courte ?? fakeNumber(2, true).toString() 196 | return +courte === 0 ? '01' : courte 197 | } 198 | 199 | export default validate 200 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import ValidationBRError from './ValidationBRError' 2 | 3 | /** 4 | * Calcula o DV verificador a partir das regras do MOD11: 5 | * O valor da soma é dividido por 11. O resultado é o resto da divisão. Caso o resto seja 6 | * menor que 2, ou seja, o valor da divisão seja 10 ou 11, o resultado é 0. 7 | * 8 | * @param {Integer} sum Soma 9 | * @returns {Integer} 10 | */ 11 | export function sumToDV(sum: number): number { 12 | return sum % 11 < 2 ? 0 : 11 - (sum % 11) 13 | } 14 | 15 | /** 16 | * Checa se o número repassado possui todos os digitos iguais 17 | * 18 | * @example 19 | * checkRepeatedSequence(12345678) 20 | * // -> false 21 | * checkRepeatedSequence(11111111) 22 | * // -> true 23 | * 24 | */ 25 | export function checkRepeatedSequence(value: string) { 26 | return [...value].every(digit => digit === value[0]) 27 | } 28 | 29 | /** 30 | * Multiplica os elementos de uma string com os elementos de outra, ou de um array 31 | * e soma o resultado ao final 32 | * 33 | * @example 34 | * sumElementsByMultipliers('123', '987') //-> 46 35 | * sumElementsByMultipliers('123', [9, 8, 7]) //-> 46 36 | * 37 | * @param {String} value 38 | * @param {String|Array} multiplier 39 | * @returns {Integer} Somatório 40 | */ 41 | export function sumElementsByMultipliers(value: string, multiplier: number[]): number { 42 | return multiplier.reduce( 43 | (accu: number, curr: any, i: number) => accu + curr * Number(value[i]), 44 | 0, 45 | ) 46 | } 47 | 48 | /** 49 | * fakeNumber() 50 | * Cria um número aleatório com o número de caracteres 51 | * 52 | * @example 53 | * fakeNumber(8, true) // -> 00083159 54 | * fakeNumber(4) // -> 831 55 | * 56 | * @param {Integer} length 57 | * @param {Boolean} forceLength Adiciona zeros à esquerda para ter os números de caractes exatos 58 | * @returns {String} 59 | */ 60 | export function fakeNumber(length: number, forceLength = false, isAlpha = false): string { 61 | let value: string; 62 | 63 | if (isAlpha) value = Math.round(Math.random() * 36 ** length).toString(36).toLocaleUpperCase() 64 | else value = Math.floor(Math.random() * 10 ** length).toString() 65 | 66 | if (forceLength) return String(value).padStart(length, '0') 67 | 68 | return String(value) 69 | } 70 | 71 | /** 72 | * Limpa um número informado, retirando caracteres diferentes de números, 73 | * preenchendo com zeros à esquerda se for menor que o tamanho exato e 74 | * removendo uma parte do número se for maior que tamanho definido. 75 | * 76 | * 1) Retira caracteres não-numéricos 77 | * 2) Preenche com zeros à esquerda se 'value' for menor que 'length' 78 | * 3) Remove caracteres à direita se 'value' for maior que 'length' 79 | * 80 | * @example 81 | * clearValue(12345-6, 6) // -> 123456 82 | * clearValue(12345678, 3) // -> 123 83 | * clearValue(12345, 10) // -> 0000001234 84 | * 85 | * @param {Number|String} value 86 | * @param {Number} length Tamanho exato. Se for null, só retira os caracteres não-numéricos 87 | * @returns {String} Número com o tamanho exato 88 | */ 89 | export function clearValue( 90 | value: string | number, 91 | length: number | null = null, 92 | options?: ClearValueOptions, 93 | ): string { 94 | let clearedValue = String(value).replace(/([/.-]+)/gi, '') 95 | 96 | if (options) { 97 | const shouldRejectEmpty = options.rejectEmpty === true && clearedValue.length === 0; 98 | if (shouldRejectEmpty) { 99 | throw ValidationBRError.EMPTY_VALUE 100 | } 101 | 102 | const shouldRejectHigherLength = options.rejectHigherLength === true && length && clearedValue.length > length; 103 | if (shouldRejectHigherLength) { 104 | throw ValidationBRError.MAX_LEN_EXCEDEED 105 | } 106 | 107 | const shouldRejectEqualSequence = options.rejectEqualSequence === true && length 108 | if (shouldRejectEqualSequence) { 109 | if (checkRepeatedSequence(clearedValue)) throw ValidationBRError.REPEATED_SEQUENCE 110 | } 111 | 112 | if (length && options.fillZerosAtLeft) clearedValue = clearedValue.padStart(length, '0') 113 | if (length && options.trimAtRight) clearedValue = clearedValue.substring(0, length) 114 | } 115 | 116 | return clearedValue 117 | } 118 | 119 | /** 120 | * insertAtPosition() 121 | * Insere um conjunto de caracteres em um local específico de uma string 122 | * 123 | * @example 124 | * insertAtPosition('AAABBB', 'C', 3) // -> AAACBBB 125 | * insertAtPosition('000011122223445555', 99, 7) // -> 00001119922223445555 126 | * 127 | * @param {String|Number} value Valor original 128 | * @param {String|Number} insertValue Valor que será inserido 129 | * @param {String|Number} position Posição que receberá o novo valor 130 | * @returns {String} 131 | * 132 | */ 133 | export function insertAtPosition(value: string, insertValue: string, position: number): string { 134 | return `${value.substring(0, position)}${insertValue}${value.substring(position)}` 135 | } 136 | 137 | /** 138 | * removeFromPosition() 139 | * Retira um conjunto de caracteres de um local específico de uma string 140 | * 141 | * @example 142 | * removeFromPosition('00001119922223445555', 7,9) // -> 000011122223445555 143 | * removeFromPosition('AAACBBB', 3,4) // -> AAABBB 144 | * 145 | * @param {String|Number} value Valor original 146 | * @param {String|Number} startPosition 147 | * @param {String|Number} endPosition 148 | * @returns {String} 149 | * 150 | */ 151 | export function removeFromPosition( 152 | value: string, 153 | startPosition: number, 154 | endPosition: number, 155 | ): string { 156 | return [value.slice(0, startPosition), value.slice(endPosition)].join('') 157 | } 158 | 159 | /** 160 | * applyMask() 161 | * Aplica uma máscara a uma string 162 | * 163 | * @example 164 | * applyMask('59650000', '00.000-000') // -> 59.650-000 165 | * applyMask('99877665544', '(00) 0 0000-0000') // -> (99) 8 7766-5544 166 | * 167 | * @param {String|Number} value Valor original 168 | * @param {String} mask 169 | * @returns {String} 170 | * 171 | */ 172 | export function applyMask(value: string | number, mask: string): string { 173 | const maskLen = clearValue(mask).length 174 | let masked = clearValue(value, maskLen, { fillZerosAtLeft: true, trimAtRight: true }) 175 | const specialChars = ['/', '-', '.', '(', ')', ' '] 176 | 177 | for (let position = 0; position < mask.length; position += 1) { 178 | const current = mask[position] 179 | if (specialChars.includes(current)) masked = insertAtPosition(masked, current, position) 180 | } 181 | 182 | return masked 183 | } 184 | 185 | /** 186 | * randomLetter() 187 | * Pega uma letra maiúscula aleatoriamente 188 | * 189 | * @example 190 | * randomLetter() // -> A 191 | * randomLetter() // -> S 192 | * 193 | * @returns {String} 194 | */ 195 | export function randomLetter(): string { 196 | const idx = Math.floor(1 + Math.random() * 26) 197 | return String.fromCharCode(idx + 64) 198 | } 199 | 200 | /** 201 | * Opções do clearValue 202 | */ 203 | interface ClearValueOptions { 204 | // Preenche 0 à esquerda se for menor que o limite 205 | fillZerosAtLeft?: boolean; 206 | 207 | // Corta à direita caso sejam superiores ao limite 208 | trimAtRight?: boolean; 209 | 210 | // Permite número vazio? 211 | rejectEmpty?: boolean; 212 | 213 | // Rejeita se o número for maior que o tamanho definido 214 | rejectHigherLength?: boolean; 215 | 216 | // Rejeita uma sequência de números iguais 217 | rejectEqualSequence?: boolean; 218 | } 219 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # validation-br 2 | 3 | Biblioteca de validação de documentos pessoais do Brasil com suporte a CPF, CNPJ (numérico e alfanumérico), Título Eleitoral, PIS/PASEP, CNH. Também valida numerações de outros tipos de registros como RENAVAM, Processos Judiciais, Número de Protocolo do Governo Federal e Objetos registrados de Rastreamento dos Correios. 4 | 5 | Validation-BR também permite criação de números fake para facilitar o desenvolvimento e testes, além de aplicar máscaras e calcular somente os dígitos verificadores. 6 | 7 | # Instalação 8 | 9 | ```sh 10 | # Usando yarn 11 | yarn add validation-br 12 | 13 | ## OU 14 | # Usando npm 15 | npm install validation-br 16 | 17 | ``` 18 | 19 | # Importação 20 | 21 | ## Importação direta 22 | 23 | Permite realizar diretamente uma validação a partir do objeto principal 24 | 25 | ```js 26 | // Modules 27 | const { isCPF, isCNPJ } = require('validation-br') 28 | 29 | // ES6 30 | import { isCPF, isCNPJ } from 'validation-br' 31 | ``` 32 | 33 | ## Importação de submódulos 34 | 35 | Importando os submódulos, é possível criar máscaras, números fake para testes de desenvolvimento e calcular dígitos verificadores. 36 | 37 | ### Exemplos 38 | 39 | ```js 40 | // ES6 41 | import { dv, fake, mask, validate } from 'validation-br/dist/cpf' 42 | // ou 43 | import * as cpf from 'validation-br/dist/cpf' 44 | 45 | // CommonJS 46 | const cpf = require('validation-br/dist/cpf') 47 | const { dv, fake, mask, validate } = require('validation-br/dist/cpf') 48 | 49 | // Calculo do dígito verificador de um CPF. Os métodos aceitam inteiros e strings, inclusive com máscaras. 50 | cpf.dv(906259666) // -> '51' 51 | cpf.dv('906259666') // -> '51' 52 | cpf.dv('906.259.666') // -> '51' 53 | 54 | // Cria um número fake de CPF para fins de testes. 55 | cpf.fake() // -> 90625966651 56 | // Passe um parâmetro true para gerar o número com máscara 57 | cpf.fake(true) // -> 906.259.666-51 58 | 59 | // Aplique uma máscara a um cpf 60 | cpf.mask(90625966651) // -> 906.259.666-51 61 | 62 | // Valida um número 63 | cpf.validate('01234567890') // -> true 64 | 65 | // Valida um número e retorna exceção se a validação falhar 66 | cpf.validateOrFail('01234567890') // -> true 67 | ``` 68 | 69 | ## Tabela de Conteúdo 70 | 71 | ### Funções de Validação 72 | 73 | - [isCNH](#isCNH-value-) - Validação do CNH 74 | - [isCPF](#isCPF-value-) - Validação do CPF 75 | - [isCNPJ](#isCNPJ-value-) - Validação do CNPJ 76 | - [isNUP17](#isNUP17-value-) - Validação de Número Unificado de Protocolo do Governo Federal 77 | - [isJudicialProcess](#isJudicialProcess-value-) - Validação de Números de Processos Judiciais 78 | - [isPIS](#isPIS-value-) - Validação de PIS, PASEP, NIS e NIT 79 | - [isPostalCode](#isPostalCode-value-) - Validação de Objetos Registrados dos Correios 80 | - [isRenavam](#isRenavam-value-) - Validação de RENAVAM 81 | - [isTituloEleitor](#isTituloEleitor-value-) - Validação do Título de Eleitor 82 | 83 | ### Usando em outras bibliotecas de validação 84 | 85 | Validation-BR pode ser utilizado em conjunto com quaisquer bibliotecas de validação que permita estender seus métodos. 86 | Abaixo seguem alguns exemplos. Sinta-se convidado a adicionar a sua biblioteca favorita em nosso wiki. 87 | 88 | - [Vuelidate](https://github.com/klawdyo/validation-br/wiki/Vuelidate) - Usado para validação de estado no vuejs 89 | - [Class-Validator](https://github.com/klawdyo/validation-br/wiki/ClassValidator) - Usado em nest, typeorm E mais uma infinidade de frameworks 90 | - [Indicative](https://github.com/klawdyo/validation-br/wiki/Indicative) - Indicative é a biblioteca padrão de validação usada no Adonis. 91 | - [Joi](https://github.com/klawdyo/validation-br/wiki/Joi) - Joi é um validador de esquemas usado em aplicações node, react, vue etc. 92 | - [Yup](https://github.com/klawdyo/validation-br/wiki/Yup) - Yup é usado para validar estado em aplicações react. 93 | 94 | ### isCNH( `value` ) 95 | 96 | Valida o documento da carteira nacional de habilitação. 97 | 98 | ```js 99 | // Importação somente da validação 100 | import { isCNH } from 'validation-br' 101 | // ou 102 | // Importação do submódulo 103 | import { validate, mask } from 'validation-br/dist/cnh' 104 | // ou 105 | import * as cnh from 'validation-br/dist/cnh' 106 | 107 | // Valida 108 | isCNH('69044271146') //-> true 109 | isCNH('62472927637') //-> true 110 | isCNH('46190476839') //-> false 111 | cnh.validate('62472927637') //-> true 112 | cnh.validateOrFail('62472927637') //-> true 113 | 114 | // Número fake com e sem máscara 115 | cnh.fake() // -> 62472927637 116 | cnh.fake(true) // -> 624729276-37 117 | 118 | // Aplica uma máscara 119 | cnh.mask('62472927637') // -> 624729276-37 120 | 121 | // Calcula o DV 122 | cnh.dv('624729276') // -> '37' 123 | ``` 124 | 125 | ### isCNPJ( `value` ) 126 | 127 | Valida um CNPJ 128 | 129 | > A partir da [Nota Técnica conjunta COCAD/SUARA/RFB nº 49 de 14 de maio de 2024](https://github.com/user-attachments/files/15851229/Nota.COCAD.SUARA.2024.05.49.CNPJ.Alfanumerico-1.pdf), os números de CNPJ poderão ser alfanuméricos. A alteração entra em uso em 2026. 130 | 131 | ```js 132 | // Importação somente da validação 133 | import { isCNPJ } from 'validation-br' 134 | // ou 135 | // Importação do submódulo 136 | import { validate, mask } from 'validation-br/dist/cnpj' 137 | // ou 138 | import * as cnpj from 'validation-br/dist/cnpj' 139 | 140 | // Valida 141 | isCNPJ('73.797.980/0001-79') //-> true 142 | isCNPJ('55585709000198') //-> true 143 | isCNPJ('99362238000180') //-> false 144 | cnpj.validate('99362238000180') //-> true 145 | cnpj.validateOrFail('99362238000180') //-> true 146 | 147 | // Número fake com e sem máscara 148 | cnpj.fake() // -> 55585709000198 149 | cnpj.fake(true) // -> 55.585.709/0001-98 150 | cnpj.fake({ withMask: true}) // -> 55.585.709/0001-98 151 | cnpj.fake({ withMask: true, alphanumeric: true}) // -> A1.222.333/0001-50 152 | cnpj.fake({ withMask: false, alphanumeric: true}) // -> A1222333/0001-50 153 | 154 | // Aplica uma máscara 155 | cnpj.mask('99362238000180') // -> 99.362.238/0001-80 156 | 157 | // Calcula o DV 158 | cnpj.dv('993622380001') // -> '80' 159 | ``` 160 | 161 | ### isCPF( `value` ) 162 | 163 | Valida um CPF 164 | 165 | ```js 166 | // Importação somente da validação 167 | import { isCPF } from 'validation-br' 168 | // ou 169 | // Importação do submódulo 170 | import { validate, mask } from 'validation-br/dist/cpf' 171 | // ou 172 | import * as cpf from 'validation-br/dist/cpf' 173 | 174 | // Valida 175 | isCPF('01234567890') //-> true 176 | isCPF('012.345.678-90') //-> true 177 | isCPF('01234567891') //-> false 178 | cpf.validate('01234567890') //-> true 179 | cpf.validateOrFail('01234567890') //-> true 180 | 181 | // Número fake com e sem máscara 182 | cpf.fake() // -> 01234567891 183 | cpf.fake(true) // -> 012.345.678-91 184 | 185 | // Aplica uma máscara 186 | cpf.mask('01234567890') // -> 012.345.678-90 187 | 188 | // Calcula o DV 189 | cpf.dv('012345678') // -> '90' 190 | ``` 191 | 192 | ### isJudicialProcess( `value` ) 193 | 194 | Valida números de processo da esfera judicial. Esta padronização foi adotada em 2010 e de lá para cá todos os processos judiciais abertos no país seguem o mesmo padrão, seja eleitoral, cível, militar etc. 195 | 196 | O número é composto por 6 partes: 197 | 198 | 1. Número sequencial dado pelo órgão de registro, reiniciado a cada ano, com até 7 caracteres 199 | 2. Dígito verificador com 2 caracteres 200 | 3. Ano de registro com 4 caracteres 201 | 4. Órgão do poder judiciário com 1 caractere, sendo eles: 202 | 203 | - 1 - Supremo Tribunal Federal 204 | - 2 - Conselho Nacional de Justiça 205 | - 3 - Superior Tribunal de Justiça 206 | - 4 - Justiça Federal 207 | - 5 - Justiça do Trabalho 208 | - 6 - Justiça Eleitoral 209 | - 7 - Justiça Militar da União 210 | - 8 - Justiça dos Estados e do Distrito Federal e Territórios 211 | - 9 - Justiça Militar Estadual 212 | 213 | 5. Tribunal do segmento do poder judiciário com 2 caracteres 214 | 6. Código da unidade de origem do processo com 4 caracteres 215 | 216 | ```js 217 | // Importação somente da validação 218 | import { isJudicialProcess } from 'validation-br' 219 | // ou 220 | // Importação do submódulo 221 | import { validate, mask } from 'validation-br/dist/judicialProcess' 222 | // ou 223 | import * as judicialProcess from 'validation-br/dist/judicialProcess' 224 | 225 | // Valida 226 | isJudicialProcess('20802520125150049') //-> true 227 | isJudicialProcess('0011006-07.2016.8.20.0100') //-> true 228 | isJudicialProcess('00110060720168200101') //-> false 229 | judicialProcess.validate('00110060720168200100') //-> true 230 | judicialProcess.validateOrFail('00110060720168200100') //-> true 231 | 232 | // Número fake com e sem máscara 233 | judicialProcess.fake() // -> 00110060720168200100 234 | judicialProcess.fake(true) // -> 0011006-07.2016.8.20.0100 235 | 236 | // Aplica uma máscara 237 | judicialProcess.mask('00110060720168200100') // -> 0011006-07.2016.8.20.0100 238 | 239 | // Calcula o DV. 240 | // Obs.: Antes do cálculo, é necessário que o número do processo não possua o dígito verificador para que o resultado seja correto. Isso é necessário pois o DV fica no meio da numeração, na posição 8 e 9. 241 | judicialProcess.dv('001100620168200100') // -> '07' 242 | ``` 243 | 244 | ### isNUP17( `value` ) 245 | 246 | Válida um Número Unificado de Protocolo de 17 dígitos. Esta numeração é usada pelo Governo Federal como forma única de numerar processos em todas os órgãos do executivo. 247 | 248 | 1. Os primeiros 5 dígitos correspondem código do órgão 249 | 2. Os dígitos de 6 a 11 são um número sequencial dado pelo órgão em questão e é reiniciado a cada ano 250 | 3. Os dígitos 12 a 15 representam o ano de registro do protocolo 251 | 4. Os caracteres 16 a 17 são o dígito verificador 252 | 253 | ```js 254 | // Importação somente da validação 255 | import { isNUP17 } from 'validation-br' 256 | // ou 257 | // Importação do submódulo 258 | import { validate, mask } from 'validation-br/dist/nup17' 259 | // ou 260 | import * as nup from 'validation-br/dist/nup17' 261 | 262 | // Valida 263 | isNUP17('23037001462202165') //-> true 264 | isNUP17('23037.001462/2021-65') //-> true 265 | isNUP17('23037.001462/2021-66') //-> false 266 | nup.validate('23037.001462/2021-65') //-> true 267 | nup.validateOrFail('23037.001462/2021-65') //-> true 268 | 269 | // Número fake com e sem máscara 270 | nup.fake() // -> 71282677380 271 | nup.fake(true) // -> 712.82677.38-0 272 | 273 | // Aplica uma máscara 274 | nup.mask('23037001462202165') // -> 23037.001462/2021-65 275 | 276 | // Calcula o DV 277 | nup.dv('230370014622021') // -> '65' 278 | ``` 279 | 280 | ### isPIS( `value` ) 281 | 282 | Valida códigos PIS, PASEP, NIS e NIT, que usam o mesmo algoritmo. Aceita números com e sem pontos e traços. 283 | 284 | ```js 285 | // Importação somente da validação 286 | import { isPIS } from 'validation-br' 287 | // ou 288 | // Importação do submódulo 289 | import { validate, mask } from 'validation-br/dist/pisPasep' 290 | // ou 291 | import * as pis from 'validation-br/dist/pisPasep' 292 | 293 | // Valida 294 | isPIS('71282677380') //-> true 295 | isPIS('237.95126.95-5') //-> true 296 | isPIS('500.12973.80-1') //-> false 297 | pis.validate('71282677380') //-> true 298 | pis.validateOrFail('71282677380') //-> true 299 | 300 | // Número fake com e sem máscara 301 | pis.fake() // -> 71282677380 302 | pis.fake(true) // -> 712.82677.38-0 303 | 304 | // Aplica uma máscara 305 | pis.mask('71282677380') // -> 712.82677.38-0 306 | 307 | // Calcula o DV 308 | pis.dv('7128267738') // -> '0' 309 | ``` 310 | 311 | ### isPostalCode( `value` ) 312 | 313 | Valida um código de rastreamento de objetos postais no formato XX00000000DYY, onde: 314 | 315 | - XX: O código do objeto postal com 2 dígitos; 316 | - 00000000: Número sequencial do objeto com 8 dígitos; 317 | - D: Dígito Verificador 318 | - YY: País de origem do objeto com 2 dígitos. 319 | 320 | ```js 321 | // Importação somente da validação 322 | import { isPostalCode } from 'validation-br' 323 | // ou 324 | // Importação do submódulo 325 | import { validate, mask } from 'validation-br/dist/postalCode' 326 | // ou 327 | import * as postalCode from 'validation-br/dist/postalCode' 328 | 329 | // Valida 330 | isPostalCode('PN718252423BR') //-> true 331 | isPostalCode('RY728187035CN') //-> true 332 | isPostalCode('JT194624698BR') //-> false 333 | postalCode.validate('PN718252423BR') //-> true 334 | postalCode.validateOrFail('PN718252423BR') //-> true 335 | 336 | // Número fake com e sem máscara. 337 | postalCode.fake() // -> PN718252423BR 338 | postalCode.fake(true) // -> PN718252423BR 339 | 340 | // Aplica uma máscara 341 | // No caso de PostalCode, a máscara apenas coloca as letras em maiúsculas 342 | postalCode.mask('pn718252423br') // -> PN718252423BR 343 | 344 | // Calcula o DV 345 | postalCode.dv('PN718252423BR') // -> '3' 346 | ``` 347 | 348 | ### isRenavam( `value` ) 349 | 350 | Valida o número de um RENAVAM de 11 dígitos 351 | 352 | ```js 353 | // Importação somente da validação 354 | import { isRenavam } from 'validation-br' 355 | // ou 356 | // Importação do submódulo 357 | import { validate, mask } from 'validation-br/dist/renavam' 358 | // ou 359 | import * as renavam from 'validation-br/dist/renavam' 360 | 361 | // Valida 362 | isRenavam('14283256656') //-> true 363 | isRenavam('95059845976') //-> true 364 | isRenavam('67747331626') //-> false 365 | renavam.validate('95059845976') //-> true 366 | renavam.validateOrFail('95059845976') //-> true 367 | 368 | // Número fake com e sem máscara 369 | renavam.fake() // -> 95059845976 370 | renavam.fake(true) // -> 9505984597-6 371 | 372 | // Aplica uma máscara 373 | renavam.mask('95059845976') // -> 9505984597-6 374 | 375 | // Calcula o DV 376 | renavam.dv('950598459') // -> '76' 377 | ``` 378 | 379 | ### isTituloEleitor( `value` ) 380 | 381 | Valida um título eleitoral 382 | 383 | ```js 384 | // Importação somente da validação 385 | import { isTituloEleitor } from 'validation-br' 386 | // ou 387 | // Importação do submódulo 388 | import { validate, mask } from 'validation-br/dist/tituloEleitor' 389 | // ou 390 | import * as titulo from 'validation-br/dist/tituloEleitor' 391 | 392 | // Valida 393 | isTituloEleitor('743650641660') //-> true 394 | isTituloEleitor('525028881694') //-> true 395 | isTituloEleitor('153016161686') //-> false 396 | titulo.validate('01234567890') //-> true 397 | titulo.validateOrFail('01234567890') //-> true 398 | 399 | // Número fake com e sem máscara 400 | titulo.fake() // -> 153016161686 401 | titulo.fake(true) // -> 1530.1616.1686 402 | 403 | // Aplica uma máscara 404 | titulo.mask('525028881694') // -> 5250.2888.1694 405 | 406 | // Calcula o DV 407 | titulo.dv('5250288816') // -> '94' 408 | ``` 409 | 410 | # Testes 411 | 412 | Todos os testes passando com 100% de cobertura 413 | 414 | ![Testes passando com 100% de cobertura](https://github.com/user-attachments/assets/86c75eda-077f-4386-ba9c-20b588b6bd36) 415 | 416 | 417 | # Github Actions 418 | 419 | Github actions executados nas versões 18, 20 e 22 do Node. 420 | 421 | ![Github actions executados nas versões 18, 20 e 22 do Node](https://github.com/user-attachments/assets/34b2b82d-67e3-4c00-b9c6-7160279123d2) 422 | 423 | # Changelog 424 | 425 | - **16/12/2023**: 426 | - 1.5.0 427 | - CNPJ alfanumérico 428 | - Removidos github actions dos node 12, 14 e 16 e acrescentado o 22 429 | - **16/12/2023**: 430 | - 1.4.5 431 | - Corrige o caminho da definição dos types. (Thanks @ishigami) 432 | - **30/12/2022**: 433 | - 1.4.4 434 | - Correção de bug quando o documento válido tinha caracteres adicionais 435 | - Refatoração de `clearValue()` para comportar configurações opcionais 436 | - **01/10/2022**: 437 | - 1.4.1 438 | - Correção na importação principal dos módulos 439 | - Refatoração do isJudicialProcess para permitir 100% de cobertura dos testes 440 | - Inclusão de mais testes unitários para atingir 100% de cobertura 441 | - **10/01/2022**: 442 | - 1.1.0 - Adicionado NUP17 - Número Unificado de Protocolo de 17 dígitos do Governo Federal 443 | - **09/01/2022**: 444 | - 1.0.0 - Biblioteca convertida para Typescript e testes convertidos para Jest 445 | - **08/01/2022**: 446 | - 0.21.1 - Adicionadas as funções isRenavam e isJudicialProcess 447 | - **16/09/2021**: 448 | - 0.5.0 - Adicionadas as funções isCPF, isCNPJ e isTituloEleitor 449 | - 0.7.0 - Adicionadas as funções isPostalCode e isCNH 450 | - 0.8.0 - Adicionada a função isPIS 451 | 452 | # Referências 453 | 454 | - [Cálculo do DV do CPF](http://clubes.obmep.org.br/blog/a-matematica-nos-documentos-cpf/) 455 | - [Cálculo do DV do CNPJ](http://www.macoratti.net/alg_cnpj.htm) 456 | - [Cálculo do DV do Título Eleitoral](http://clubes.obmep.org.br/blog/a-matematica-nos-documentos-titulo-de-eleitor/) 457 | - [Cálculo do PIS](http://www.macoratti.net/alg_pis.htm) 458 | - [Diferença entre PIS, PASEP, NIS e NIT](https://www.jornalcontabil.com.br/entenda-de-uma-vez-a-diferenca-entre-pis-pasep-nit-e-nis/#:~:text=NIS%20%E2%80%93%20N%C3%BAmero%20de%20Identifica%C3%A7%C3%A3o%20Social,do%20Patrim%C3%B4nio%20do%20Servidor%20P%C3%BAblico) 459 | - [Documentação Oficial de Numeração de Processos Judiciais](https://juslaboris.tst.jus.br/bitstream/handle/20.500.12178/30318/2008_res0065_cnj_rep01.pdf?sequence=2) 460 | - [Cálculos de DV](http://ghiorzi.org/DVnew.htm) 461 | - [Cálculo do NUP17](https://www.gov.br/compras/pt-br/acesso-a-informacao/legislacao/portarias/portaria-interministerial-no-11-de-25-de-novembro-de-2019) 462 | --------------------------------------------------------------------------------