diff --git a/context7.json b/context7.json index 56d07730f..2695103c8 100644 --- a/context7.json +++ b/context7.json @@ -30,7 +30,7 @@ "rules": [ "The package has zero runtime dependencies and ships as ESM plus a UMD build; nothing else needs to be installed to use it.", "Import from the root: import { isValidCpf } from '@brazilian-utils/brazilian-utils'. Every util is also a kebab-case subpath, e.g. '@brazilian-utils/brazilian-utils/is-valid-cpf'.", - "Use the subpaths to lazy-load the dataset-backed utils (getMunicipalities, getMunicipalityByCode, getCodeByMunicipalityName, getMunicipalitiesByAreaCode, getAreaCodeByMunicipalityCode, getCnae, getCbo, getCest, getCfop, getCid10, isValidCid10, getIsbnInfo, formatIsbn, getClassTrib, isValidNcm, getBanks, getNbs, getServiceItem): each one embeds a large official table.", + "Use the subpaths to lazy-load the dataset-backed utils (getMunicipalities, getMunicipalityByCode, getCodeByMunicipalityName, getMunicipalitiesByAreaCode, getAreaCodeByMunicipalityCode, getCnae, getCbo, getCest, getCfop, getCid10, isValidCid10, getIsbnInfo, formatIsbn, getClassTrib, isValidNcm, getBanks, getBankByCode, getBankByIspb, getNbs, getServiceItem, and the deprecated getMunicipality and getCities): each one embeds a large official table.", "Never import the same util from both the root and its subpath in one app: a bundler treats them as two unrelated modules and bundles the dataset twice.", "Public functions never throw on bad input (null, undefined, wrong type): isValid* return false, format* and parse* return '', single-item getters return null, list getters return [].", "The only utils that reject are the async getAddressInfoByCep (GetAddressInfoByCepError: NotFound, Validation, Service) and getCepInfoByAddress (GetCepInfoByAddressError: NotFound, Validation).", diff --git a/docs/guides/address-form.md b/docs/guides/address-form.md index 60d42d36d..9f52bc288 100644 --- a/docs/guides/address-form.md +++ b/docs/guides/address-form.md @@ -6,12 +6,12 @@ keywords: ["CEP lookup", "address by CEP", "autofill address", "getAddressInfoBy Type a CEP and the rest of the address fills itself. Pick the framework: each example runs the code below it, which you can copy as is. -`getAddressInfoByCep` asks the CEP providers and returns the street, neighborhood, city and state, or throws when no one has that CEP. It is asked only once `isValidCep` says the CEP is complete, so a request is not made on every keystroke, and what comes back stays editable: a lookup fills a form, it does not own it. The [document field guide](document-field.md) has the mask that keeps the caret where it belongs. +`getAddressInfoByCep` asks the CEP providers and returns the street, neighborhood, city and state, or throws when no one has that CEP. It is asked only once `isValidCep` says the CEP is complete, so a request is not made on every keystroke, and what comes back stays editable: a lookup fills a form, it does not own it. The [document field guide](guides/document-field.md) has the mask that keeps the caret where it belongs.
-A hook takes the CEP and gives back what is known about it; the form draws that. The CEP field is the one the [document field guide](document-field.md) builds: +A hook takes the CEP and gives back what is known about it; the form draws that. The CEP field is the one the [document field guide](guides/document-field.md) builds:
@@ -29,7 +29,7 @@ A hook takes the CEP and gives back what is known about it; the form draws that.
-A `resource` takes the CEP and gives back what is known about it, reloading when it changes. The CEP field is the one the [document field guide](document-field.md) builds: +A `resource` takes the CEP and gives back what is known about it, reloading when it changes. The CEP field is the one the [document field guide](guides/document-field.md) builds:
@@ -47,7 +47,7 @@ A `resource` takes the CEP and gives back what is known about it, reloading when
-A composable takes the CEP and gives back what is known about it; the form draws that. The CEP field is the one the [document field guide](document-field.md) builds: +A composable takes the CEP and gives back what is known about it; the form draws that. The CEP field is the one the [document field guide](guides/document-field.md) builds:
diff --git a/docs/guides/schema.md b/docs/guides/schema.md index 32c322ae7..5b4a03b21 100644 --- a/docs/guides/schema.md +++ b/docs/guides/schema.md @@ -149,7 +149,7 @@ Each of these builds the document on its own first, so it can be reused wherever
-No schema library at all: `toStandardSchema` gives the validator the interface every form library speaks. +No schema library at all: `toStandardSchema` gives the validator the interface every form library speaks. VeeValidate takes it as `rules` from version 5 on, and Hono validates an object, so its route wraps the validator for the JSON body.
diff --git a/docs/guides/state-city.md b/docs/guides/state-city.md index 2e96dd796..70e53e5a3 100644 --- a/docs/guides/state-city.md +++ b/docs/guides/state-city.md @@ -1,12 +1,12 @@ --- title: "State and city" description: "Pick a state and the cities of that state load on demand, with Brazilian Utils in React, Angular, Vue and plain JavaScript." -keywords: ["state and city select", "IBGE cities", "getCities", "lazy import", "code splitting", "municipalities of a state"] +keywords: ["state and city select", "IBGE municipalities", "getMunicipalities", "lazy import", "code splitting", "municipalities of a state"] --- Pick a state and its cities fill the second select. Pick the framework: each example runs the code below it, which you can copy as is. -The point of this one is when each table is loaded, and the answer is: when someone opens the select that shows it. Not with the page, and not when a state is picked either — a form where the city is filled in by something else, or left alone, never fetches 154 KB of cities. The states are 27 rows, 2.5 KB; the cities are 5,571 of them, 154 KB. Each util is its own subpath, so `await import("@brazilian-utils/brazilian-utils/get-cities")` fetches that table and nothing else. A bundler makes it a chunk of its own; the browser fetches it once and keeps it, so only the first opening waits. +The point of this one is when each table is loaded, and the answer is: when someone opens the select that shows it. Not with the page, and not when a state is picked either. A form where the city is filled in by something else, or left alone, never fetches 148 KB of municipalities. The states are 27 rows, 2.5 KB; the municipalities are 5,571 of them, 148 KB. Each util is its own subpath, so `await import("@brazilian-utils/brazilian-utils/get-municipalities")` fetches that table and nothing else. Each option takes the IBGE `code` of the municipality as its value and the `name` as its label, since names repeat across states (there are two "Pau D'Arco"). A bundler makes it a chunk of its own; the browser fetches it once and keeps it, so only the first opening waits.
diff --git a/docs/pt-br/guides/schema.md b/docs/pt-br/guides/schema.md index b282731eb..2d61efeef 100644 --- a/docs/pt-br/guides/schema.md +++ b/docs/pt-br/guides/schema.md @@ -149,7 +149,7 @@ O `narrow` recebe ele, e diz o que o valor tem que ser quando recusa:
-Sem biblioteca de schema nenhuma: o `toStandardSchema` dá ao validador a interface que toda biblioteca de formulário fala. +Sem biblioteca de schema nenhuma: o `toStandardSchema` dá ao validador a interface que toda biblioteca de formulário fala. O VeeValidate aceita esse schema em `rules` a partir da versão 5, e o Hono valida um objeto, então a rota embrulha o validador para o corpo JSON.
diff --git a/docs/pt-br/guides/state-city.md b/docs/pt-br/guides/state-city.md index 69354f627..9c72b05f0 100644 --- a/docs/pt-br/guides/state-city.md +++ b/docs/pt-br/guides/state-city.md @@ -1,12 +1,12 @@ --- title: "Estado e cidade" description: "Escolha um estado e as cidades dele carregam sob demanda, com Brazilian Utils em React, Angular, Vue e JavaScript puro." -keywords: ["select de estado e cidade", "cidades do IBGE", "getCities", "import lazy", "code splitting", "municípios de um estado"] +keywords: ["select de estado e cidade", "municípios do IBGE", "getMunicipalities", "import lazy", "code splitting", "municípios de um estado"] --- Escolha um estado e as cidades dele preenchem o segundo select. Escolha o framework: cada exemplo roda o código logo abaixo dele, que pode ser copiado do jeito que está. -O ponto aqui é quando cada tabela é carregada, e a resposta é: quando alguém abre o select que a mostra. Nem com a página, nem ao escolher um estado — num formulário em que a cidade vem preenchida de outro lugar, ou é deixada em branco, os 154 KB de cidades nunca são buscados. Os estados são 27 linhas, 2,5 KB; as cidades são 5.571, 154 KB. Cada utilitário é um subpath, então `await import("@brazilian-utils/brazilian-utils/get-cities")` busca essa tabela e nada mais. O bundler transforma isso num chunk separado, e o browser busca uma vez e guarda, então só a primeira abertura espera. +O ponto aqui é quando cada tabela é carregada, e a resposta é: quando alguém abre o select que a mostra. Nem com a página, nem ao escolher um estado. Num formulário em que a cidade vem preenchida de outro lugar, ou é deixada em branco, os 148 KB de municípios nunca são buscados. Os estados são 27 linhas, 2,5 KB; os municípios são 5.571, 148 KB. Cada utilitário é um subpath, então `await import("@brazilian-utils/brazilian-utils/get-municipalities")` busca essa tabela e nada mais. Cada opção usa o `code` IBGE do município como valor e o `name` como rótulo, já que os nomes se repetem entre estados (existem dois "Pau D'Arco"). O bundler transforma isso num chunk separado, e o browser busca uma vez e guarda, então só a primeira abertura espera.
@@ -93,4 +93,4 @@ Sem build: salve como um arquivo `.html` e abra. Cada subpath é um módulo pró
-A [introdução](pt-br/getting-started.md#bundle-size) lista todos os utilitários que embutem uma tabela e valem um subpath próprio. +A [introdução](pt-br/getting-started.md#tamanho-do-bundle) lista todos os utilitários que embutem uma tabela e valem um subpath próprio. diff --git a/docs/pt-br/utilities.md b/docs/pt-br/utilities.md index 73bd51504..a51b6c474 100644 --- a/docs/pt-br/utilities.md +++ b/docs/pt-br/utilities.md @@ -26,7 +26,7 @@ Estas regras valem para todas as funções, a não ser que a seção diga o cont Valida um CPF. - Retorna `false` para um número reservado (todos os dígitos iguais, como `00000000000`) e para um dígito verificador errado. -- Os números reservados são os que o [leiaute DJE](http://normas.receita.fazenda.gov.br/sijut2consulta/anexoOutros.action?idArquivoBinario=36307) da Receita Federal lista como inválidos. A norma do CPF, a [IN RFB nº 2.172/2024](https://normas.receita.fazenda.gov.br/sijut2consulta/link.action?idAto=135611), não traz regra de dígito verificador; a regra é a do manual da e-Financeira da Receita Federal (Anexo II, `REGRA_VALIDA_CPF`, aprovado pelo Ato Declaratório Executivo Cofis nº 10/2026). +- Os números reservados são os que o [leiaute DJE](http://normas.receita.fazenda.gov.br/sijut2consulta/anexoOutros.action?idArquivoBinario=36307) da Receita Federal lista como inválidos. A norma do CPF, a [IN RFB nº 2.172/2024](https://normas.receita.fazenda.gov.br/sijut2consulta/link.action?idAto=135611), não traz regra de dígito verificador; a regra é a do [manual da e-Financeira](http://sped.rfb.gov.br/estatico/33/BBD9A14FFF8CB38ECEEC2FC2D3713AE716DB11/Manual%20e-Financeira%20v%202.1.2-%20Anexo%20II%20-%20Leiautes%20Gerais.pdf) da Receita Federal (Anexo II, `REGRA_VALIDA_CPF`, aprovado pelo Ato Declaratório Executivo Cofis nº 10/2026). ```javascript import { isValidCpf } from '@brazilian-utils/brazilian-utils'; @@ -39,7 +39,7 @@ isValidCpf('111 444 777 35'); // true (máscara com espaços) Formata um CPF. -- **Opções** (`FormatCpfOptions`): `pad` preenche o valor com zeros à esquerda até 11 dígitos antes de aplicar a máscara (padrão `false`); `obfuscate` esconde os 3 primeiros dígitos e os 2 dígitos verificadores. +- **Opções** (`FormatCpfOptions`): `pad` preenche o valor com zeros à esquerda até 11 dígitos antes de aplicar a máscara (padrão `false`); `obfuscate` esconde os 3 primeiros dígitos e os 2 dígitos verificadores. Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. - `obfuscate` é aplicada após o `pad`. Segue a regra que as Leis de Diretrizes Orçamentárias definem para a divulgação do CPF: "ocultar os três primeiros dígitos e os dois dígitos verificadores" ([Lei nº 14.194/2021, art. 149](https://www.planalto.gov.br/ccivil_03/_ato2019-2022/2021/lei/L14194.htm), regra criada pela Lei nº 12.309/2010, art. 87, § 5º; a LDO de 2026, [Lei nº 15.321/2025, art. 163](https://www.planalto.gov.br/ccivil_03/_ato2023-2026/2025/lei/L15321.htm#art163), a repete). ```javascript @@ -68,7 +68,7 @@ Gera um CPF válido aleatório. - `state` ignora maiúsculas/minúsculas e espaços nas pontas (`'sp'` é `'SP'`). Sem `state`, ou com um código desconhecido, um dígito de região fiscal aleatório é sorteado. ```javascript -import { generateCpf } from '@brazilian-utils/brazilian-utils' +import { generateCpf } from '@brazilian-utils/brazilian-utils'; generateCpf(); generateCpf('SP'); // o 9º dígito é 8, o código da região fiscal de SP @@ -115,6 +115,7 @@ Fonte: [Receita Federal, "Cadastros: CPF e CNPJ"](https://www.gov.br/receitafede Valida um CNPJ. - **Opções** (`IsValidCnpjOptions`): `version` escolhe o formato aceito: `1` (padrão) apenas numérico, `2` numérico e alfanumérico. Qualquer outro valor é lido como `1`. +- Desde julho de 2026 os CNPJs novos podem ser alfanuméricos, e a `version: 1` padrão os rejeita: passe `version: 2` para aceitá-los. - Um número reservado (todos os dígitos iguais) é rejeitado nas duas versões; a versão `2` não tem lista de reservados para letras. - O conjunto oficial de caracteres do CNPJ alfanumérico são as letras maiúsculas de `A` a `Z` e os algarismos (os 2 dígitos verificadores são sempre algarismos). Uma letra minúscula só é aceita como normalização da entrada, como um caractere de máscara: a entrada é convertida para maiúsculas antes. - O Ex1 da pergunta 23 do perguntas e respostas da Receita Federal sobre o CNPJ alfanumérico, `AA345678/0003-29`, tem erro de impressão: os dígitos verificadores dele são `86`, então ele é rejeitado. @@ -132,8 +133,8 @@ Fonte: [Instrução Normativa RFB nº 2.229/2024](http://normas.receita.fazenda. Formata um CNPJ. -- **Opções** (`FormatCnpjOptions`): `pad` preenche o valor com zeros à esquerda até 14 caracteres antes de aplicar a máscara (padrão `false`); `version` escolhe o formato, `1` (padrão) apenas numérico, `2` alfanumérico; `obfuscate` esconde os 2 primeiros dígitos e os 2 dígitos verificadores. -- A versão `2` mantém letras e dígitos, com uma letra minúscula convertida para maiúscula antes, já que o conjunto oficial é de `A` a `Z`; a versão `1` mantém apenas dígitos. +- **Opções** (`FormatCnpjOptions`): `pad` preenche o valor com zeros à esquerda até 14 caracteres antes de aplicar a máscara (padrão `false`); `version` escolhe o formato, `1` (padrão) apenas numérico, `2` alfanumérico; `obfuscate` esconde os 2 primeiros dígitos e os 2 dígitos verificadores. Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. +- A versão `2` mantém letras e dígitos, com uma letra minúscula convertida para maiúscula antes, já que o conjunto oficial é de `A` a `Z`; a versão `1` mantém apenas dígitos. Desde julho de 2026 os CNPJs novos podem ser alfanuméricos, então passe `version: 2` para manter as letras deles. - `obfuscate` vale para as duas versões e é aplicada após o `pad`. É uma convenção desta biblioteca, não uma regra oficial: nenhuma lei ou ato da Receita Federal define mascaramento para o CNPJ, cujos dados são públicos, a [ANPD](https://www.gov.br/anpd/pt-br/centrais-de-conteudo/documentos-tecnicos-orientativos/estudo_tecnico_sobre_anonimizacao_de_dados_na_lgpd_uma_visao_de_processo_baseado_em_risco_e_tecnicas_computacionais.pdf) diz que "não há um padrão para o mascaramento" e as [regras do Pix do Banco Central](https://www.bcb.gov.br/content/estabilidadefinanceira/pix/Regulamento_Pix/IV_RequisitosMinimosparaExperienciadoUsuario.pdf) mostram o CNPJ inteiro onde mascaram o CPF; ela esconde os 2 primeiros caracteres e os 2 dígitos verificadores, à semelhança da regra do CPF. ```javascript @@ -149,7 +150,7 @@ formatCnpj('12345678000195', { obfuscate: true }); // **.345.678/0001-** Remove a formatação do CNPJ, retorna um valor normalizado e limita o resultado a 14 caracteres. -- **Opções** (`ParseCnpjOptions`): `version` escolhe o formato: `1` (padrão) mantém apenas dígitos, `2` mantém letras e dígitos, com uma letra minúscula convertida para maiúscula, já que o conjunto oficial é de `A` a `Z` (`parseCnpj('12.abc.345/01de-35', { version: 2 })` retorna `'12ABC34501DE35'`). +- **Opções** (`ParseCnpjOptions`): `version` escolhe o formato: `1` (padrão) mantém apenas dígitos, `2` mantém letras e dígitos, com uma letra minúscula convertida para maiúscula, já que o conjunto oficial é de `A` a `Z` (`parseCnpj('12.abc.345/01de-35', { version: 2 })` retorna `'12ABC34501DE35'`). Desde julho de 2026 os CNPJs novos podem ser alfanuméricos, então o padrão descarta as letras deles: passe `version: 2` para mantê-las. ```javascript import { parseCnpj } from '@brazilian-utils/brazilian-utils'; @@ -167,11 +168,11 @@ Gera um CNPJ válido aleatório. - Um bloco de ordem aleatório nunca é `0000`: os estabelecimentos de uma raiz são numerados a partir de `0001`, a matriz, então esse bloco nunca é atribuído. ```javascript -import { generateCnpj } from '@brazilian-utils/brazilian-utils' +import { generateCnpj } from '@brazilian-utils/brazilian-utils'; generateCnpj(); generateCnpj(2); // CNPJ alfanumérico, ex. 'Q0SLFMBD7VX439' -generateCnpj({ branch: 3 }); // bloco de ordem '0003', ex. '12345678000372' +generateCnpj({ branch: 3 }); // bloco de ordem '0003', ex. '12345678000357' generateCnpj({ version: 2, branch: 1 }); // CNPJ alfanumérico cujo bloco de ordem é '0001' ``` @@ -216,7 +217,8 @@ Fonte: [Instrução Normativa RFB nº 2.229/2024](http://normas.receita.fazenda. Valida um CEP ([código de endereçamento postal](https://pt.wikipedia.org/wiki/C%C3%B3digo_de_Endere%C3%A7amento_Postal)). - Aceita `string` ou `number`. Um CEP que começa com `0` precisa ser string, já que um número não preserva o zero à esquerda, e um número só é lido quando é um inteiro seguro não negativo. -- Espaços, pontos e hífens são ignorados. Qualquer outro caractere invalida o valor. +- Espaços, pontos, hífens e barras são ignorados. Qualquer outro caractere invalida o valor. +- `getAddressInfoByCep` e `formatCep` com `pad: true` são mais tolerantes com números: preenchem um número com zeros à esquerda até 8 dígitos (`1310100` vira `01310-100`), enquanto `isValidCep` e `getStateByCep` leem `1310100` como 7 dígitos e o rejeitam. ```javascript import { isValidCep } from '@brazilian-utils/brazilian-utils'; @@ -235,7 +237,7 @@ isValidCep('12345'); // false (tamanho inválido) Formata um CEP ([código de endereçamento postal](https://pt.wikipedia.org/wiki/C%C3%B3digo_de_Endere%C3%A7amento_Postal)). -- **Opções** (`FormatCepOptions`): `pad` preenche o valor com zeros à esquerda até 8 dígitos antes de aplicar a máscara (padrão `false`). +- **Opções** (`FormatCepOptions`): `pad` preenche o valor com zeros à esquerda até 8 dígitos antes de aplicar a máscara (padrão `false`). Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. - Um CEP que começa com `0` passado como número perde esse zero: passe uma string ou use `pad`. Um número só é lido quando é um inteiro seguro não negativo; qualquer outro número retorna `''`. ```javascript @@ -258,7 +260,7 @@ parseCep('92500-000'); // 92500000 ### generateCep -Gera um CEP aleatório. Um CEP não tem dígito verificador, então toda string de 8 dígitos é estruturalmente válida. +Gera um CEP aleatório. Um CEP não tem dígito verificador, então o CEP é sorteado dentro das faixas que os Correios atribuem aos estados, com a mesma chance para cada CEP. Ele sempre pertence a um estado, então `getStateByCep` nunca responde `null` para ele; `00000-000` a `00999-999` e `78900-000` a `78999-999`, que nenhum estado possui, nunca são gerados. Uma faixa é o bloco que um estado possui, não uma garantia de que todo CEP dela está em uso, então o CEP pode não ser o de um endereço real. ```javascript import { generateCep } from '@brazilian-utils/brazilian-utils'; @@ -271,26 +273,32 @@ generateCep(); // '92500000' Busca o endereço de um CEP em vários provedores ao mesmo tempo e resolve com a primeira resposta bem-sucedida. O resultado é um `AddressInfo`: `cep`, `state`, `city`, `neighborhood` e `street`. - **Opções** (`GetAddressInfoByCepOptions`): - - `providers` (`CepProvider[]`) lista os provedores a disputar (padrão `['viacep', 'brasilapi']`). `'widenet'` está descontinuado e fica fora da lista padrão. + - `providers` (`CepProvider[]`) lista os provedores a disputar (padrão `['viacep', 'brasilapi']`). `'widenet'` está descontinuado, fica fora da lista padrão e costuma estar indisponível: seu endpoint agora redireciona para `ws.apicep.com`, que respondia 502 na última verificação, então ele só acrescenta um provedor que falha à disputa. - `timeoutMs` (`number`) limita a busca inteira, tentativas incluídas (padrão: sem limite). Quando o tempo acaba, todas as requisições são abortadas e a chamada rejeita com `GetAddressInfoByCepServiceError`. - `signal` (`AbortSignal`) cancela a busca; a chamada rejeita com `signal.reason`, como o `fetch`. - Aceita string ou número. Uma string tem removido todo caractere que não é dígito (`'CEP 01310-100'` é `01310100`) e precisa sobrar com 8 dígitos. Um número é preenchido com zeros à esquerda até 8 dígitos, já que não carrega o zero inicial de um CEP de São Paulo, mas só a partir de `1000000` (`01000-000`, o menor CEP que os Correios atribuem). Um número menor, negativo ou fracionário é rejeitado com `GetAddressInfoByCepValidationError` antes de qualquer requisição. - Repete falhas transitórias de rede por provedor. - Rejeita com `GetAddressInfoByCepValidationError` quando o CEP é inválido, `providers` não nomeia nenhum provedor conhecido ou `timeoutMs` não é um número finito positivo, com `GetAddressInfoByCepNotFoundError` quando todos os provedores falharam e pelo menos um informou que o CEP é desconhecido, e com `GetAddressInfoByCepServiceError` quando todos os provedores falharam por outro motivo. - A BrasilAPI responde 404 tanto para um CEP desconhecido quanto quando os serviços por trás dela estão fora do ar, então o 404 dela só conta como "CEP desconhecido" quando nenhum outro provedor deixou de responder. +- Um endereço só é aceito quando concorda com o CEP pedido: seus dígitos, preenchidos com zeros à esquerda até 8 (um provedor que responde `1310100` para `01310-100` quer dizer o mesmo CEP), precisam ser o CEP e o estado dele, quando informado, precisa ser o estado dono da faixa do CEP (veja `getStateByCep`). Caso contrário, esse provedor conta como não conhecendo o CEP. A BrasilAPI, por exemplo, respondeu o `99999-999`, um CEP do Rio Grande do Sul, com uma cidade do Paraná. +- Quando a busca termina, as requisições dos provedores que perderam a disputa são abortadas. - Os três estendem `GetAddressInfoByCepError`, então um único `catch` cobre todos. ```javascript -import { getAddressInfoByCep } from '@brazilian-utils/brazilian-utils'; +import { getAddressInfoByCep, GetAddressInfoByCepNotFoundError } from '@brazilian-utils/brazilian-utils'; // Usando os provedores padrão (['viacep', 'brasilapi']) const address = await getAddressInfoByCep('01310100'); // { cep: '01310100', state: 'SP', city: 'São Paulo', neighborhood: 'Bela Vista', street: 'Avenida Paulista' } -// Usando provedores específicos -const addressFromProviders = await getAddressInfoByCep('01310-100', { - providers: ['viacep', 'brasilapi'] -}); +// Usando um provedor específico, e distinguindo um CEP desconhecido de uma falha +try { + await getAddressInfoByCep('01310-100', { providers: ['brasilapi'] }); +} catch (error) { + if (error instanceof GetAddressInfoByCepNotFoundError) { + // nenhum provedor conhece o CEP + } +} // Usando número como entrada (será preenchido automaticamente com zeros à esquerda) const addressFromNumber = await getAddressInfoByCep(1310100); @@ -303,10 +311,11 @@ const addressWithinFiveSeconds = await getAddressInfoByCep('01310100', { timeout Busca os CEPs de um endereço na ViaCEP. Resolve com um array de `CepAddressInfo`. -- O argumento (`GetCepInfoByAddressParams`) traz `federalUnit`, `city` e `street`. `federalUnit` pode estar em minúsculas; `city` e `street` têm os espaços nas pontas removidos e os acentos retirados antes da consulta. -- Rejeita com `GetCepInfoByAddressValidationError` quando a UF, a cidade ou a rua está ausente ou inválida, com `GetCepInfoByAddressNotFoundError` quando nenhum endereço corresponde à busca, e com `GetCepInfoByAddressError` quando a ViaCEP responde com um status de erro HTTP. +- O argumento (`GetCepInfoByAddressParams`) traz `federalUnit`, `city` e `street`. `federalUnit` pode estar em minúsculas; `city` e `street` têm os espaços nas pontas removidos e os acentos retirados antes da consulta, e cada um precisa ser uma string com pelo menos 3 caracteres depois disso, o mínimo que a ViaCEP aceita. +- Rejeita com `GetCepInfoByAddressValidationError` quando a UF, a cidade ou a rua está ausente ou inválida (um valor em branco, um valor que não é string, ou uma cidade ou rua com menos de 3 caracteres, todos rejeitados antes de qualquer requisição), com `GetCepInfoByAddressNotFoundError` quando nenhum endereço corresponde à busca, e com `GetCepInfoByAddressError` quando a ViaCEP responde com um status de erro HTTP. - Repete falhas transitórias de rede, como `getAddressInfoByCep`. - Cada item traz a resposta da ViaCEP sem alterações, com os nomes de campo da própria ViaCEP. +- A ViaCEP limita a lista a 50 endereços, então um nome de rua curto que corresponde a mais ruas retorna só os 50 primeiros. ```javascript import { getCepInfoByAddress } from '@brazilian-utils/brazilian-utils'; @@ -342,15 +351,18 @@ const ceps = await getCepInfoByAddress({ Valida um boleto ([meio de pagamento brasileiro](https://pt.wikipedia.org/wiki/Boleto_banc%C3%A1rio)). -- Aceita a linha digitável de 47 dígitos da "cobrança bancária" e, do "boleto de arrecadação", seja a linha digitável de 48 dígitos, seja o código de barras de 44 dígitos. +- Aceita a linha digitável de 47 dígitos da "cobrança bancária", o seu código de barras de 44 dígitos (código do banco, código de moeda `9`, o dígito verificador módulo 11 na posição 5, fator de vencimento, valor e campo livre) e, do "boleto de arrecadação", seja a linha digitável de 48 dígitos, seja o código de barras de 44 dígitos. Um valor de 44 dígitos começando por `8` é sempre um código de barras de arrecadação (o `8` é o identificador de produto da arrecadação na FEBRABAN), então o código de barras de cobrança bancária de um código de banco de `800` a `899` (só existe o `804`) não é aceito na forma de código de barras, pois os dois não se distinguiriam; a linha digitável de 47 dígitos dele é aceita. Até a 2.4.0 o código de barras da cobrança bancária era rejeitado. +- Os caracteres de máscara usuais (espaço, `.`, `-` e `/`) são aceitos entre os dígitos; qualquer outro caractere invalida o valor, então `abc` + uma linha digitável + `zzz` é rejeitado, não lido como os seus dígitos (até a 2.4.0 todo não dígito era descartado). - O código de moeda (posição 4 do código de barras e da linha digitável da cobrança bancária) precisa ser `9` (real), o único código que a Carta-Circular BCB nº 2.926/2000 atribui. A única exceção é o boleto da "Situação 2" da Convenção da Cobrança da FEBRABAN, emitido por instituição identificada apenas pelo ISPB: código de banco `988`, código de moeda `0`, fator de vencimento `0000` e o ISPB, completado com zeros, no lugar do valor. Qualquer outro dígito é rejeitado. ```javascript import { isValidBoleto } from '@brazilian-utils/brazilian-utils'; isValidBoleto('00190000090114971860168524522114675860000102656'); // true +isValidBoleto('00196758600001026560000001149718606852452211'); // true (código de barras da cobrança bancária) isValidBoleto('846100000005246100291102005460339004695895061080'); // true (boleto de arrecadação) isValidBoleto('00170000010114971860168524522114275860000102656'); // false (código de moeda 7) +isValidBoleto('abc00190000090114971860168524522114675860000102656zzz'); // false (letras em volta dos dígitos) isValidBoleto('98800000060114971860168524522114100000018236120'); // true (Situação 2: banco 988, moeda 0, ISPB) ``` @@ -360,7 +372,7 @@ Fonte: [Carta-Circular BCB nº 2.926/2000](https://www.bcb.gov.br/pre/normativos Formata um número de boleto. -- **Opções** (`FormatBoletoOptions`): `pad` preenche o valor com zeros à esquerda até o tamanho do padrão antes de aplicar a máscara (padrão `false`). +- **Opções** (`FormatBoletoOptions`): `pad` preenche o valor com zeros à esquerda até o tamanho do padrão antes de aplicar a máscara (padrão `false`). Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. - Uma linha digitável de 48 dígitos que começa com `8` recebe a máscara de arrecadação: quatro blocos de 11 dígitos, cada um seguido do seu dígito verificador. O código de barras de arrecadação de 44 dígitos mantém a máscara de "cobrança bancária". ```javascript @@ -384,6 +396,8 @@ import { parseBoleto } from '@brazilian-utils/brazilian-utils'; parseBoleto('00190.00009 01149.718601 68524.522114 6 75860000102656'); // 00190000090114971860168524522114675860000102656 ``` +Fonte: [Carta-Circular BCB nº 2.926/2000](https://www.bcb.gov.br/pre/normativos/c_circ/2000/pdf/c_circ_2926_v1_O.pdf), [FEBRABAN, Layout Padrão de Arrecadação, Versão 08](https://cmsarquivos.febraban.org.br/Arquivos/documentos/PDF/Layout%20-%20C%C3%B3digo%20de%20Barras%20-%20Vers%C3%A3o%208%20-%2011_05_2026.pdf) (vigente desde 01/06/2026). + ### generateBoleto Gera um boleto válido aleatório. @@ -398,13 +412,17 @@ generateBoleto(); // "00190000090114971860168524522114675860000102656" generateBoleto({ type: 'arrecadacao' }); // "846100000005246100291102005460339004695895061080" ``` +Fonte: [Carta-Circular BCB nº 2.926/2000](https://www.bcb.gov.br/pre/normativos/c_circ/2000/pdf/c_circ_2926_v1_O.pdf), [FEBRABAN, Layout Padrão de Arrecadação, Versão 08](https://cmsarquivos.febraban.org.br/Arquivos/documentos/PDF/Layout%20-%20C%C3%B3digo%20de%20Barras%20-%20Vers%C3%A3o%208%20-%2011_05_2026.pdf) (vigente desde 01/06/2026). + ### getBoletoInfo Extrai informações de um boleto (valor, data de vencimento, código do banco). Retorna `null` quando o valor não é um boleto válido. - **Opções** (`GetBoletoInfoOptions`): `referenceDate` resolve o ciclo do "fator de vencimento" a partir dessa data em vez de agora. +- Lê a linha digitável de 47 dígitos e o código de barras de 44 dígitos de um boleto de cobrança bancária, e as formas de arrecadação, do mesmo modo que `isValidBoleto` as aceita. - Retorna um `BoletoInfo`: `amount` em centavos, `expirationDate` e o `bankCode` de três dígitos. `expirationDate` é `null` quando o boleto não traz fator de vencimento (um fator abaixo de `1000`). - O ciclo do fator de vencimento reiniciou em 22/02/2025, então um fator pode significar uma de duas datas separadas por 9000 dias. Não há comunicado da FEBRABAN publicado sobre o reinício; a regra está em manuais de banco, como o [do Bradesco](https://banco.bradesco/assets/pessoajuridica/pdf/4008-524-0121-layout-cobranca-versao-portugues.pdf) (Versão 17). `referenceDate` escolhe entre elas; informe-a sempre que a resposta precisar ser estável. +- As janelas são de 3000 dias para trás e 5500 dias para a frente de `referenceDate`: um boleto que venceu até 3499 dias (cerca de 9,5 anos) antes dela mantém a data, e um que venceu 3500 dias (cerca de 9,6 anos) ou mais antes dela é lido como o próximo ciclo (uma data no futuro), então, para ler um boleto antigo, informe uma `referenceDate` próxima da data de emissão. Uma `referenceDate` que não seja um `Date` válido é ignorada e usa-se agora. - Um boleto de arrecadação tem `bankCode: ''` e `expirationDate: null`, mais `type: 'arrecadacao'`, `segment`, `value` (o valor em reais) e `hasEffectiveValue`. - O boleto da "Situação 2" da Convenção da Cobrança da FEBRABAN (código de banco `988`, código de moeda `0`) traz o ISPB do emissor no lugar do valor: ele volta como `ispb`, com `amount: 0`. @@ -414,6 +432,9 @@ import { getBoletoInfo } from '@brazilian-utils/brazilian-utils'; getBoletoInfo('00190000090114971860168524522114675860000102656'); // { amount: 102656, expirationDate: Date, bankCode: '001' } +getBoletoInfo('00196758600001026560000001149718606852452211'); +// o mesmo boleto lido do código de barras de 44 dígitos + getBoletoInfo('00190000090114971860168524522114675860000102656', { referenceDate: new Date(2018, 6, 1) }); @@ -484,10 +505,10 @@ Fonte: [Manual de Padrões para Iniciação do Pix](https://www.bcb.gov.br/conte Valida um payload de BR Code Pix (a string por trás de um QR Code Pix e do "Pix copia e cola") pelo Manual de Padrões para Iniciação do Pix e, onde ele é omisso, pela especificação EMV de QR Code em que ele se apoia. - O payload precisa começar pelo format indicator `000201`. -- A estrutura TLV, o CRC-16 e os objetos obrigatórios (format indicator, category code de 4 dígitos, moeda, país, nome e cidade do recebedor) são verificados. +- A estrutura TLV, o CRC-16 e os objetos obrigatórios (format indicator, category code de 4 dígitos, moeda, país, nome e cidade do recebedor) são verificados. Um ID de objeto não pode se repetir no mesmo nível, e o CRC (`63`) precisa ser o último objeto do payload, não oito caracteres dentro de outro. - Um template "Merchant Account Information" (IDs 26 a 51) precisa trazer o GUI `br.gov.bcb.pix` com uma chave (estático) ou a URL do PSP (dinâmico), nunca os dois. - A chave vem na forma do DICT (§2.5.1): a que `getPixKeyInfo` devolve sem mudar, então `12345678909` passa e `123.456.789-09` não. Se ela está registrada não dá para saber pelo payload. A URL do PSP tem no máximo 77 caracteres (§2.5.2). -- O nome do recebedor tem no máximo 25 caracteres e a cidade no máximo 15; o país é `BR` em maiúsculas. +- O nome do recebedor tem no máximo 25 caracteres e a cidade no máximo 15; o país é `BR` em maiúsculas. Os caracteres deles não são restritos (nenhum dos manuais restringe, e o EMV os tipa como `ans`), então um nome com acento é aceito, embora `generatePixPayload` reduza os dois a ASCII imprimível. - Nenhum manual do BCB diz se o CRC ou o `BR` podem estar em minúsculas: a única regra de caixa que eles dão é a do GUI, e todos os exemplos oficiais escrevem os dois em maiúsculas. Aceitar CRC em minúsculas (`1d3d`) e rejeitar `br` são escolhas desta biblioteca, como na 2.4.0. - O objeto `01` (Point of Initiation Method) é opcional e precisa ser `11` ou `12` quando presente. - O objeto `62` (Additional Data Field) é obrigatório e traz o `txid` (62-05), "sempre presente em um BR Code": `***` ou de 1 a 25 letras e dígitos (§2.6.2); com URL do PSP qualquer valor vale, já que o §2.7 manda o pagador ignorá-lo. O `-` do exemplo `RP12345678-2019` do Manual do BR Code está fora do conjunto de caracteres do Pix do §2.6.2, então esse exemplo estático é rejeitado. @@ -574,10 +595,11 @@ Fonte: [Manual do BR Code](https://www.bcb.gov.br/content/estabilidadefinanceira Valida uma chave de acesso de DF-e. Cobre todo DF-e com chave de acesso de 44 caracteres; o CF-e-SAT (59) fica de fora. - Modelos: NF-e (55), NFC-e (65), CT-e (57), MDF-e (58), CT-e OS (67), GTV-e (64), BP-e (63), NF3e (66) e NFCom (62). -- Todo caractere é dígito, exceto as posições 7 a 18, a raiz e a ordem do CNPJ do emitente, que podem trazer as letras de um CNPJ alfanumérico: os schemas atuais (NF-e PL_010 `TChNFe`, CT-e PL_CTe_400_RTC, MDF-e 3.00b, NFCom) tipam a chave como `[0-9]{6}[0-9A-Z]{12}[0-9]{26}`, em produção na NF-e a partir de 01/07/2026 (NT 2026.004). Uma letra em qualquer outra posição, incluídos os dígitos verificadores do CNPJ nas posições 19 e 20, é rejeitada. O schema só admite maiúsculas; minúsculas são lidas como maiúsculas, como `isValidCnpj` faz com `{ version: 2 }`. +- Todo caractere é dígito, exceto as posições 7 a 18, a raiz e a ordem do CNPJ do emitente, que podem trazer as letras de um CNPJ alfanumérico: os schemas atuais (NF-e PL_010 `TChNFe`, CT-e PL_CTe_400_RTC, MDF-e 3.00b, NFCom) tipam a chave como `[0-9]{6}[0-9A-Z]{12}[0-9]{26}`, em produção na NF-e a partir de 01/07/2026 (NT 2026.004). Uma letra em qualquer outra posição, incluídos os dígitos verificadores do CNPJ nas posições 19 e 20, é rejeitada. O schema só admite maiúsculas; minúsculas são lidas como maiúsculas, como `isValidCnpj` faz com `{ version: 2 }`. Uma letra não ASCII que vira letra ASCII em maiúsculas (`ſ`, `ß`) é rejeitada. - Os 44 caracteres podem ser agrupados de 4 em 4 por espaço, `.`, `-` ou `/`. Os prefixos `Id` do XML (`NFe`, `CTe`, `MDFe`, `BPe`, `NF3e`, `NFCom`) são removidos antes. - `tpEmis` precisa ser um dos que o MOC do modelo atribui (tabela abaixo). -- Para NF-e e NFC-e o `cNF` precisa passar na regra B03-10 do MOC (sem valores repetidos ou sequenciais, diferente do número do documento). +- Para NF-e e NFC-e o `cNF` precisa passar na regra B03-10 do MOC (sem valores repetidos ou sequenciais, diferente do número do documento). A regra vale para os documentos enviados depois da NT 2019.001, e os softwares de NF-e costumavam usar um `cNF` igual ao número do documento antes dela, então uma chave autorizada antes pode ser rejeitada. +- Os dígitos verificadores do CPF ou CNPJ do emitente não são conferidos, só o dígito verificador da própria chave. Leia a chave com `getNfeKeyInfo` e passe o `taxId` dela ao `isValidCnpj`, ou os 11 últimos dígitos de um `taxId` preenchido com zeros ao `isValidCpf`, para conferir também o emitente. - Um número de documento todo zerado é rejeitado. O dígito verificador é um módulo 11 sobre os 43 primeiros caracteres, cada um valendo seu código ASCII menos 48 (`A` = 17 ... `Z` = 42), como a NT Conjunta 2025.001 define. | Modelo | `tpEmis` aceitos | @@ -614,9 +636,11 @@ Fonte: [MOC da NF-e](https://www.confaz.fazenda.gov.br/legislacao/arquivo-manuai Formata uma chave de acesso de DF-e (Documento Fiscal eletrônico) em grupos de 4 caracteres separados por espaço. É a forma em que o DANFE, o DACTE, o DAMDFE, o DABPE, o DANF3E e o DANFE-COM a imprimem. -- **Opções** (`FormatNfeKeyOptions`): `pad` preenche o valor com zeros à esquerda até os 44 caracteres de uma chave de acesso completa (padrão `false`). +- **Opções** (`FormatNfeKeyOptions`): `pad` preenche o valor com zeros à esquerda até os 44 caracteres de uma chave de acesso completa (padrão `false`). Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. - Uma chave com máscara ou parcial é agrupada até onde os caracteres vão. - As letras de um CNPJ alfanumérico são mantidas, em maiúsculas, nas posições 7 a 18; uma letra em qualquer outra posição é descartada. +- Os prefixos `NFe`, `CTe`, `MDFe`, `BPe`, `NF3e` e `NFCom` do atributo `Id` do XML são removidos antes, como o `parseNfeKey` os lê. +- Um valor que não seja string nem inteiro seguro não negativo (`-1`, `1.5`, um bigint, um objeto) resulta em `''`. - Use `isValidNfeKey` para verificar uma chave. ```javascript @@ -628,6 +652,9 @@ formatNfeKey('35170458716523000119550010000000121000123458'); formatNfeKey('35260712abc34501de35550010000001231102030403'); // '3526 0712 ABC3 4501 DE35 5500 1000 0001 2311 0203 0403' (CNPJ alfanumérico) +formatNfeKey('NF3e35170458716523000119550010000000121000123458'); +// '3517 0458 7165 2300 0119 5500 1000 0000 1210 0012 3458' (prefixo do Id do XML) + formatNfeKey('12345'); // '1234 5' formatNfeKey('12345', { pad: true }); @@ -689,7 +716,7 @@ Verifica se a chave de acesso de uma NFS-e nacional, a Nota Fiscal de Serviço e - A chave é um bloco único de 50 caracteres, `Cód.Mun.(7) Amb.Ger.(1) Tipo de Inscrição Federal(1) Inscrição Federal(14) nNFSe(13) AAMM(4) Cód.Num.(9) DV(1)`, todos dígitos exceto um CNPJ alfanumérico na Inscrição Federal. - O literal `NFS` que o atributo `Id` de `infNFSe` coloca antes da chave é retirado, junto com os espaços nas extremidades. -- A chave não tem máscara impressa, já que o DANFSe a imprime em um único bloco, então, diferente do `isValidNfeKey`, um separador em qualquer ponto dela é rejeitado em vez de removido. +- O DANFSe imprime a chave em um único bloco, então ela não tem máscara impressa. As fronteiras entre os 8 campos aceitam os caracteres de máscara que o `isValidCpf` lê (espaço, `.`, `-` ou `/`, isolados ou em sequência), enquanto um separador dentro de um campo invalida o valor. - O código do município precisa começar com um código IBGE de UF; ele não é consultado na tabela do IBGE. - O `ambGer` precisa ser `1` (o sistema do município) ou `2` (o Sistema Nacional NFS-e), e o tipo de inscrição `1` (um CPF, preenchido com `000` à esquerda) ou `2` (um CNPJ, numérico ou alfanumérico), com um CPF ou CNPJ cujos próprios dígitos verificadores sejam válidos. Letras só são aceitas em um CNPJ, e minúsculas são lidas como maiúsculas, como o `isValidCnpj` com `{ version: 2 }` as lê. - O `nNFSe` não pode ser todo de zeros e o mês precisa estar entre 01 e 12. @@ -705,7 +732,7 @@ isValidNfseKey('NFS35503082258716523000119000000000001226011357924683'); // true isValidNfseKey('43149021100040364478829000000000105725120484407255'); // true (emitente com CPF, RS) isValidNfseKey('35503082212ABC34501DE35000000000001226091357924682'); // true (emitente com CNPJ alfanumérico) isValidNfseKey('35503082258716523000119000000000001226011357924684'); // false (dígito verificador) -isValidNfseKey('3550308 2 2 58716523000119 0000000000012 2601 135792468 3'); // false (a chave não tem máscara) +isValidNfseKey('3550308 2 2 58716523000119 0000000000012 2601 135792468 3'); // true (separadores entre os campos) ``` ### parseNfseKey @@ -769,7 +796,7 @@ Valida uma Inscrição SUFRAMA. É o número de registro que a Superintendência - Retorna `false` para um código de setor `00` e para um dígito verificador módulo 11 errado. - Os códigos de setor e de localidade não são conferidos com uma tabela, pois o manual os lista apenas como exemplos. - A regra vem do Manual de Orientação do Contribuinte da NF-e (CONFAZ/ENCAT), não da SUFRAMA, cuja Resolução CAS nº 64/2021, art. 5º, só chama a inscrição de "um número de identificação e controle" e não traz layout nem dígito verificador. -- Além dos caracteres de máscara usuais, `(`, `)`, `,` e `*` também são ignorados. +- Espaços, `.`, `-` e `/` são aceitos entre os campos, como em `isValidCpf`. Qualquer outro caractere invalida o valor. ```javascript import { isValidSuframa } from '@brazilian-utils/brazilian-utils'; @@ -785,7 +812,7 @@ isValidSuframa('001234560'); // false (setor 00) Formata uma Inscrição SUFRAMA. -- **Opções** (`FormatSuframaOptions`): `pad` completa o valor com zeros à esquerda até os 9 dígitos antes de aplicar a máscara (padrão `false`), o que devolve o zero à esquerda de um valor de 8 dígitos. +- **Opções** (`FormatSuframaOptions`): `pad` completa o valor com zeros à esquerda até os 9 dígitos antes de aplicar a máscara (padrão `false`), o que devolve o zero à esquerda de um valor de 8 dígitos. Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. - A máscara é progressiva, como nas outras funções `format`, então um valor de 8 dígitos sem `pad` é agrupado uma posição antes: use `pad: true` para um valor lido direto do campo `ISUF`, que pode vir com 8 dígitos. ```javascript @@ -824,7 +851,7 @@ Fonte: [Manual de Orientação do Contribuinte da NF-e 7.0, Visão Geral](https: ### isValidPhone -Valida um número de telefone (celular ou fixo). Um código de país brasileiro (`+55`, `0055` ou um `55` isolado) é aceito e removido antes, como em `parsePhone`. +Valida um número de telefone (celular ou fixo). Um código de país brasileiro (`+55`, `0055` ou um `55` isolado) é aceito e removido antes, como em `parsePhone`. Qualquer caractere além de dígitos, espaços e `()+.-/` (uma letra, por exemplo) torna o valor inválido; até a 2.4.0 esses caracteres eram descartados. - **Opções** (`IsValidPhoneOptions`): `accept` (`PhoneType[]`, padrão `['mobile', 'landline']`) define quais tipos de número são aceitos; inclua `'service'` para os números que `isValidServicePhone` reconhece. `version` (`PhoneVersion`, padrão `1`) é repassado a `isValidMobilePhone`. - Um celular precisa começar com 7, 8 ou 9 nas duas versões (Resolução Anatel 749/2022, art. 12, I, "a"), então um 6 inicial é rejeitado; até a 2.4.0 a versão padrão o aceitava. @@ -850,7 +877,10 @@ Formata um número de telefone de acordo com os padrões brasileiros. Se `value` - **Opções** (`FormatPhoneOptions`): `mask` (`PhoneMask`, padrão `"sn"`) escolhe um dos padrões abaixo. Uma `mask` desconhecida recai para `"sn"`. `obfuscate` (padrão `false`) esconde o número do assinante em todas as máscaras. - `"sn"`: apenas o número assinante, 9 dígitos. `"nanp"`: DDD mais número assinante, 11 dígitos para celular e 10 para fixo; outros tamanhos mantêm o agrupamento de 11 dígitos. -- `"e164"` e `"international"` removem antes o código de país, como `parsePhone`, e recaem para `"service"` para um número de serviço. +- `"e164"` e `"international"` removem antes o código de país, como `parsePhone`, e recaem para `"service"` para um número de serviço. `"e164"` mantém no máximo os 11 dígitos nacionais, como `"international"` (até a 2.4.0 mantinha todos). +- `"sn"` e `"nanp"` também removem um `+55` ou `0055` explícito, então `'+5511987654321'` resulta em `(11) 98765-4321` em `"nanp"` (até a 2.4.0 resultava em `(55) 11987-6543`); um `55` isolado permanece, pois pode ser o DDD. +- Um número é lido quando é string ou inteiro seguro não negativo; qualquer outro número (negativo, fracionário, não finito ou inseguro) resulta em string vazia. +- Na máscara `"service"` com `obfuscate`, um valor que ainda é só o prefixo de serviço o mantém (`0800` continua `0800`), pois o prefixo indica um serviço, e não um assinante; um valor curto demais para ser reconhecido (`080`) fica todo oculto (`***`). - `"service"`: os Códigos Não Geográficos (`0800 123 4567`) e os números abreviados `300X`/`400X` (`4004-1234`). - `"auto"`: `"service"` para um número de serviço, `"international"` quando `value` traz código de país, senão `"nanp"` para mais de 9 dígitos, ou `"sn"`. - O `obfuscate` é uma convenção desta biblioteca, não uma regra oficial: nenhuma lei, ato da Anatel ou orientação da [ANPD](https://www.gov.br/anpd/pt-br/centrais-de-conteudo/documentos-tecnicos-orientativos/estudo_tecnico_sobre_anonimizacao_de_dados_na_lgpd_uma_visao_de_processo_baseado_em_risco_e_tecnicas_computacionais.pdf) define quais dígitos de um telefone mostrar ("não há um padrão para o mascaramento"), e o [Banco Central](https://www.bcb.gov.br/content/estabilidadefinanceira/pix/Regulamento_Pix/IV_RequisitosMinimosparaExperienciadoUsuario.pdf) proíbe mascarar a chave Pix, inclusive telefone, no retorno da consulta ao DICT. @@ -868,6 +898,8 @@ formatPhone('1130000000', { mask: 'nanp' }); // (11) 3000-0000 (fixo de 10 dígi formatPhone('1130000000', { mask: 'auto' }); // (11) 3000-0000 (fixo de 10 dígitos) formatPhone('11987654321', { mask: 'e164' }); // +5511987654321 formatPhone('+5511987654321', { mask: 'international' }); // +55 11 98765-4321 +formatPhone('+55 11 9', { mask: 'auto' }); // +55 11 9 (digitado após o +55, o 55 não é lido como DDD) +formatPhone('+5511987654321', { mask: 'nanp' }); // (11) 98765-4321 formatPhone('08001234567', { mask: 'service' }); // 0800 123 4567 formatPhone('40041234', { mask: 'service' }); // 4004-1234 formatPhone('+5511987654321', { mask: 'auto' }); // +55 11 98765-4321 ("auto" detecta o prefixo +55 e escolhe "international") @@ -890,7 +922,8 @@ Fonte: [ITU-T E.164](https://www.itu.int/rec/T-REC-E.164), [Resolução Anatel n Remove a formatação do telefone, mantém apenas os dígitos e limita o resultado a 11 dígitos. -- Um código de país brasileiro (`+55`, `0055` ou um `55` isolado) é removido antes, mas só quando sobram 10 ou 11 dígitos (DDD mais número assinante), então o DDD 55 não é confundido com ele. +- Um código de país explícito (`+55` ou `0055`) é sempre removido antes, mesmo com o número ainda sendo digitado (`+55 11 9` resulta em `119`; até a 2.4.0 resultava em `55119`). Um `55` isolado só é removido quando sobram 10 ou 11 dígitos (DDD mais número assinante), então o DDD 55 não é confundido com ele. +- Aceita string ou número inteiro seguro não negativo; qualquer outro número (negativo, fracionário, não finito ou inseguro) resulta em string vazia. ```javascript import { parsePhone } from '@brazilian-utils/brazilian-utils'; @@ -898,6 +931,7 @@ import { parsePhone } from '@brazilian-utils/brazilian-utils'; parsePhone('(11) 90000-0000'); // 11900000000 parsePhone('+55 (11) 98765-4321'); // 11987654321 parsePhone('5511987654321'); // 11987654321 +parsePhone('+55 11 9'); // 119 (código de país explícito, número ainda curto) parsePhone('55987654321'); // 55987654321 (DDD 55, não confundido com o código de país +55) ``` @@ -905,7 +939,7 @@ parsePhone('55987654321'); // 55987654321 (DDD 55, não confundido com o código Gera um telefone brasileiro aleatório. Aceita `'mobile'`, `'landline'` ou `'service'` (`GeneratePhoneType`); sem o tipo, gera um celular ou um fixo ao acaso, nunca um número de serviço. -- Um celular começa com 9 depois do DDD (válido nas duas versões de `isValidMobilePhone`); um fixo tem 8 dígitos depois do DDD, começando com 2 a 6; um número de serviço não tem DDD. +- Um celular começa com 9 depois do DDD (válido nas duas versões de `isValidMobilePhone`); um fixo tem 8 dígitos depois do DDD, começando com 2 a 5 (a faixa que continua válida depois que a Resolução Anatel 777/2025 a reduz em 1º de março de 2027; até a 2.4.0 podia sair um 6); um número de serviço não tem DDD. ```javascript import { generatePhone } from '@brazilian-utils/brazilian-utils'; @@ -918,7 +952,7 @@ generatePhone('service'); // '08001234567' ou '40041234' ### isValidMobilePhone -Valida um número de telefone celular. Um código de país brasileiro (`+55`, `0055` ou um `55` isolado) é aceito e removido antes, como em `parsePhone`. +Valida um número de telefone celular. Um código de país brasileiro (`+55`, `0055` ou um `55` isolado) é aceito e removido antes, como em `parsePhone`. Qualquer caractere além de dígitos, espaços e `()+.-/` (uma letra, por exemplo) torna o valor inválido; até a 2.4.0 esses caracteres eram descartados. - **Opções** (`IsValidMobilePhoneOptions`): `version` (`PhoneVersion`, padrão `1`) escolhe a regra de numeração. As duas seguem a Resolução Anatel 749/2022, art. 12, I, "a" (`"7", "8" e "9": Serviço Móvel Pessoal (SMP)`) e aceitam só 7, 8 ou 9 como primeiro dígito; `1` também aceita a série `700`, `2` a rejeita por ser de satélite (art. 12, II, "a"). - Até a 2.4.0 a versão `1` também aceitava 6 como primeiro dígito, que não é SMP. @@ -939,7 +973,7 @@ Fonte: [Resolução Anatel nº 749/2022](https://informacoes.anatel.gov.br/legis ### isValidLandlinePhone -Valida um número de telefone fixo. Um código de país brasileiro (`+55`, `0055` ou um `55` isolado) é aceito e removido antes, como em `parsePhone`. +Valida um número de telefone fixo. Um código de país brasileiro (`+55`, `0055` ou um `55` isolado) é aceito e removido antes, como em `parsePhone`. Qualquer caractere além de dígitos, espaços e `()+.-/` (uma letra, por exemplo) torna o valor inválido; até a 2.4.0 esses caracteres eram descartados. - O número é o DDD mais 8 dígitos começando com `2` a `6`, a faixa de STFC e SCM da Resolução Anatel 749/2022, art. 11, I, "a". - Mudança agendada, ainda não aplicada: a partir de 1º de março de 2027 a Resolução Anatel 777/2025, art. 21, deixa só `2` a `5` para o STFC, e o SCM passa para números de 9 dígitos começando com `6`. A partir dessa data, um fixo começando com `6` terá de ser rejeitado. @@ -955,7 +989,7 @@ Fonte: [Resolução Anatel nº 749/2022](https://informacoes.anatel.gov.br/legis ### isValidServicePhone -Valida um número de serviço brasileiro, discado sem DDD. Apenas a estrutura é verificada: o número não precisa estar atribuído a ninguém. +Valida um número de serviço brasileiro, discado sem DDD. Apenas a estrutura é verificada: o número não precisa estar atribuído a ninguém. Um código de país brasileiro (`+55`, `0055` ou um `55` isolado) é aceito e removido antes, como em `isValidPhone` com `accept: ['service']` (até a 2.4.0 `+55 0800 123 4567` era rejeitado aqui). Qualquer caractere além de dígitos, espaços e `()+.-/` (uma letra, por exemplo) torna o valor inválido; até a 2.4.0 esses caracteres eram descartados. - Os Códigos Não Geográficos `0300`, `0303`, `0500`, `0800` e `0900` seguidos de 7 dígitos (11 no total): as séries de 10 dígitos da Resolução Anatel 749/2022, art. 18, discadas atrás do prefixo `0` (art. 28). - Os números abreviados `300X`/`400X`, com 8 dígitos. Outros prefixos de operadora, como `4020` e `4062`, são rejeitados. @@ -967,6 +1001,7 @@ import { isValidServicePhone } from '@brazilian-utils/brazilian-utils'; isValidServicePhone('0800 123 4567'); // true isValidServicePhone('4004-1234'); // true isValidServicePhone('190'); // true +isValidServicePhone('+55 0800 123 4567'); // true (código de país aceito) isValidServicePhone('11987654321'); // false (número geográfico) ``` @@ -1062,9 +1097,9 @@ Fonte: [Resolução Anatel nº 749/2022](https://informacoes.anatel.gov.br/legis ### isValidLicensePlate -Valida uma placa de veículo. Aceita o formato antigo brasileiro (`ABC-1234`) e o formato Mercosul (`ABC1D23`), com ou sem hífen ou espaço, em maiúsculas ou minúsculas. +Valida uma placa de veículo. Aceita o formato antigo brasileiro (`ABC-1234`) e o formato Mercosul (`ABC1D23`), com ou sem máscara, em maiúsculas ou minúsculas. A máscara (espaço, `.`, `-` ou `/`, isolados ou em sequência) só é aceita entre o terceiro caractere e os quatro últimos; qualquer outro caractere (`@`, um emoji, um separador em outra posição) invalida a placa em vez de ser removido. -A opção `format` restringe a validação a um deles: `"LLLNNNN"` para o formato antigo ou `"LLLNLNN"` para o Mercosul, os nomes que `getFormatLicensePlate` retorna. Funciona como o argumento `type` do `is_valid` da biblioteca Python, cujos valores lá se chamam `"old_format"` e `"mercosul"`. Esses nomes não são formatos aqui: sem `format`, ou com qualquer outro valor, uma placa em qualquer um dos dois formatos é válida. +A opção `format` do segundo argumento (`IsValidLicensePlateOptions`) restringe a validação a um deles: `"LLLNNNN"` para o formato antigo ou `"LLLNLNN"` para o Mercosul, os nomes que `getFormatLicensePlate` retorna. Funciona como o argumento `type` do `is_valid` da biblioteca Python, cujos valores lá se chamam `"old_format"` e `"mercosul"`. Esses nomes não são formatos aqui: sem `format`, ou com qualquer outro valor, uma placa em qualquer um dos dois formatos é válida. ```javascript import { isValidLicensePlate } from '@brazilian-utils/brazilian-utils'; @@ -1075,6 +1110,8 @@ isValidLicensePlate('ABC 1234'); // true (máscara com espaço) isValidLicensePlate('ABC1D23'); // true (formato Mercosul) isValidLicensePlate('ABC12D3'); // false (não é uma sequência Mercosul) isValidLicensePlate('ABC1234EXTRA'); // false (caracteres em excesso) +isValidLicensePlate('A-BC1234'); // false (a máscara só fica depois do terceiro caractere) +isValidLicensePlate('ABC1234!'); // false (qualquer outro caractere é rejeitado) isValidLicensePlate('ABC1D23', { format: 'LLLNLNN' }); // true isValidLicensePlate('ABC1234', { format: 'LLLNLNN' }); // false (placa no formato antigo) isValidLicensePlate('ABC-1234', { format: 'LLLNNNN' }); // true @@ -1087,12 +1124,15 @@ Fonte: [Resolução CONTRAN nº 969/2022](https://www.gov.br/transportes/pt-br/a Formata uma placa. Placas antigas brasileiras (`LLLNNNN`) recebem hífen; placas Mercosul (`LLLNLNN`) são retornadas sem separador. - Retorna `''` quando o valor não pode iniciar uma placa válida. +- Um valor parcial é formatado progressivamente, então a função serve como máscara de entrada: o hífen aparece assim que o quarto caractere é um dígito, e um quinto caractere que é letra mantém a forma Mercosul. ```javascript import { formatLicensePlate } from '@brazilian-utils/brazilian-utils'; formatLicensePlate('abc1234'); // 'ABC-1234' formatLicensePlate('abc1d23'); // 'ABC1D23' +formatLicensePlate('abc1'); // 'ABC-1' (um valor parcial é formatado até onde vai) +formatLicensePlate('abc1d'); // 'ABC1D' ``` ### parseLicensePlate @@ -1109,14 +1149,15 @@ parseLicensePlate('abc-1234'); // 'ABC1234' Gera uma placa válida aleatória no formato escolhido. -- `format` (`GenerateLicensePlateFormat`): `'LLLNLNN'` (Mercosul, o padrão) ou `'LLLNNNN'` (o formato antigo brasileiro). Qualquer outro valor cai no padrão. +- `format` (`GenerateLicensePlateFormat`, um alias de `LicensePlateFormat`): `'LLLNLNN'` (Mercosul, o padrão) ou `'LLLNNNN'` (o formato antigo brasileiro). Qualquer outro valor cai no padrão. +- A letra da quinta posição de uma placa Mercosul é sorteada de `K` a `Z`: `A` a `J` só são usadas para converter uma placa do formato antigo (Anexo II, item 2, da Resolução CONTRAN nº 969/2022), então uma placa nova nunca as tem. ```javascript import { generateLicensePlate } from '@brazilian-utils/brazilian-utils'; -generateLicensePlate(); // 'ABC1D23' (Mercosul, o padrão) +generateLicensePlate(); // 'ABC1K23' (Mercosul, o padrão) generateLicensePlate('LLLNNNN'); // 'ABC1234' -generateLicensePlate('LLLNNLN'); // 'ABC1D23' (um formato fora dos dois em circulação cai no padrão) +generateLicensePlate('LLLNNLN'); // 'ABC1K23' (um formato fora dos dois em circulação cai no padrão) ``` Fonte: [Resolução CONTRAN nº 969/2022](https://www.gov.br/transportes/pt-br/assuntos/transito/conteudo-contran/resolucoes/resolucao9692022.pdf). @@ -1160,7 +1201,8 @@ Fonte: [Resolução CONTRAN nº 969/2022](https://www.gov.br/transportes/pt-br/a Valida um RENAVAM (Registro Nacional de Veículos Automotores). Aceita o formato antigo (9 dígitos) e o formato novo (11 dígitos). -- Espaços, pontos e hífens são ignorados; qualquer outro caractere invalida o valor. +- Espaços, pontos, hífens e barras são ignorados; qualquer outro caractere invalida o valor. +- Um número perde os zeros à esquerda, então um RENAVAM de 11 dígitos que começa com `0` só é aceito como string: `isValidRenavam('08794266580')` é `true` e `isValidRenavam(8794266580)` é `false`. - O dígito verificador é o da Portaria DENATRAN nº 27/2013, art. 1º: "10 dígitos e um dígito verificador, calculado através do módulo 11, peso 9", lido como os pesos 3, 2, 9, 8, 7, 6, 5, 4, 3 e 2. A portaria não escreve os pesos um a um nem diz o que fazer com resto 0, 1 ou 10, o que segue o [validation-br](https://github.com/klawdyo/validation-br/blob/main/src/renavam.ts) e o [brutils](https://github.com/brazilian-utils/python/blob/main/brutils/renavam.py); completar com zeros um código de 9 dígitos até 11 é prática de mercado. ```javascript @@ -1168,7 +1210,7 @@ import { isValidRenavam } from '@brazilian-utils/brazilian-utils'; isValidRenavam('639884962'); // true (9 dígitos, formato antigo) isValidRenavam('00639884962'); // true (11 dígitos, formato novo) -isValidRenavam('0063988.4962'); // true (pontos e hífens são ignorados) +isValidRenavam('0063988.4962'); // true (o ponto é ignorado) isValidRenavam('12345678901'); // false (checksum inválido) isValidRenavam('00000000000'); // false (dígitos repetidos) isValidRenavam('ab00639884962'); // false (letras são rejeitadas) @@ -1206,7 +1248,7 @@ isValidPis('12056412547'); // false Formata um PIS. -- **Opções** (`FormatPisOptions`): `pad` completa o valor com zeros à esquerda até 11 dígitos antes de aplicar a máscara (padrão `false`); `obfuscate` esconde os 3 primeiros dígitos e o dígito verificador. +- **Opções** (`FormatPisOptions`): `pad` completa o valor com zeros à esquerda até 11 dígitos antes de aplicar a máscara (padrão `false`); `obfuscate` esconde os 3 primeiros dígitos e o dígito verificador. Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. - O `obfuscate` é aplicado depois do `pad`. - Nenhuma autoridade publica uma regra de mascaramento para o PIS, então o `obfuscate` usa a que as Leis de Diretrizes Orçamentárias definem para a divulgação do CPF ("ocultar os três primeiros dígitos e os dois dígitos verificadores", Lei nº 14.194/2021, art. 149, regra criada pela Lei nº 12.309/2010, art. 87, § 5º), um número com a mesma estrutura. @@ -1265,13 +1307,13 @@ Fonte: [Resolução CNJ nº 65/2008](https://atos.cnj.jus.br/atos/detalhar/119), Formata um número de processo jurídico na máscara do CNJ `NNNNNNN-DD.AAAA.J.TR.OOOO`. -- **Opções** (`FormatProcessoJuridicoOptions`): `pad` completa o valor com zeros à esquerda até 20 dígitos antes de aplicar a máscara (padrão `false`). +- **Opções** (`FormatProcessoJuridicoOptions`): `pad` completa o valor com zeros à esquerda até 20 dígitos antes de aplicar a máscara (padrão `false`). Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. ```javascript import { formatProcessoJuridico } from '@brazilian-utils/brazilian-utils'; -formatProcessoJuridico('00020802520125150049'); // 0002080-25.2012.5.15.0049 -formatProcessoJuridico('20802520125150049', { pad: true }); // 0002080-25.2012.5.15.0049 +formatProcessoJuridico('00020802520125150049'); // '0002080-25.2012.5.15.0049' +formatProcessoJuridico('20802520125150049', { pad: true }); // '0002080-25.2012.5.15.0049' ``` Fonte: [Resolução CNJ nº 65/2008](https://atos.cnj.jus.br/atos/detalhar/119). @@ -1283,7 +1325,7 @@ Remove a formatação do processo jurídico, mantém apenas os dígitos e limita ```javascript import { parseProcessoJuridico } from '@brazilian-utils/brazilian-utils'; -parseProcessoJuridico('0002080-25.2012.5.15.0049'); // 00020802520125150049 +parseProcessoJuridico('0002080-25.2012.5.15.0049'); // '00020802520125150049' ``` ### generateProcessoJuridico @@ -1345,7 +1387,8 @@ Bancos validados apenas pela estrutura, já que não se conhece regra de dígito | PagBank | `290` | | Sicredi | `748` | | BMG | `318` | | Sicoob | `756` | -- Todo outro banco da lista usa o fallback genérico: `digit` precisa bater com mod10 ou mod11 sobre a conta. Um `digit` de 2 caracteres encadeia mod10 e depois mod11. +- Todo outro banco da lista usa o fallback genérico: `digit` precisa bater com mod10 ou mod11 sobre a conta. Um `digit` de 2 caracteres encadeia mod10 e depois mod11. Nenhuma regra publicada o sustenta e até três dos dez dígitos passam para uma mesma conta, então `true` para um banco fora das tabelas acima diz que a estrutura é plausível, não que o dígito verificador foi comprovado. +- Para o Banco do Brasil o dígito da agência não é verificado: uma agência de 5 dígitos (`1584-9`) só é conferida no tamanho, seja qual for o último dígito. ```javascript import { isValidBankAccount } from '@brazilian-utils/brazilian-utils'; @@ -1511,6 +1554,8 @@ formatIban('BR15'); // 'BR15' formatIban('BR15 0000-0000.0000/1093 2840 814P-2'); // 'BR15 0000 0000 0000 1093 2840 814P 2' (só letras e dígitos são lidos) ``` +Fonte: [Diretrizes de Implementação do IBAN no Brasil](https://www.bcb.gov.br/content/estabilidadefinanceira/Documents/sistema_pagamentos_brasileiro/IBAN-Guidelines_%20port.pdf), [Resolução BCB nº 585/2026](https://www.bcb.gov.br/estabilidadefinanceira/exibenormativo?tipo=Resolu%C3%A7%C3%A3o%20BCB&numero=585), que revogou a [Circular BCB nº 3.625/2013](https://www.bcb.gov.br/pre/normativos/circ/2013/pdf/circ_3625_v1_O.pdf), [ISO 13616-1:2020](https://www.iso.org/standard/81090.html). + ### parseIban Remove a formatação do IBAN, mantém as letras e os dígitos, coloca o resultado em maiúsculas e o limita aos 29 caracteres de um IBAN brasileiro. @@ -1522,6 +1567,8 @@ parseIban('BR15 0000 0000 0000 1093 2840 814P 2'); // 'BR15000000000000109328408 parseIban('br15-0000.0000/0000 1093 2840 814p-2'); // 'BR1500000000000010932840814P2' ``` +Fonte: [Diretrizes de Implementação do IBAN no Brasil](https://www.bcb.gov.br/content/estabilidadefinanceira/Documents/sistema_pagamentos_brasileiro/IBAN-Guidelines_%20port.pdf), [Resolução BCB nº 585/2026](https://www.bcb.gov.br/estabilidadefinanceira/exibenormativo?tipo=Resolu%C3%A7%C3%A3o%20BCB&numero=585), que revogou a [Circular BCB nº 3.625/2013](https://www.bcb.gov.br/pre/normativos/circ/2013/pdf/circ_3625_v1_O.pdf), [ISO 13616-1:2020](https://www.iso.org/standard/81090.html). + ### getIbanInfo Interpreta um IBAN brasileiro em seus campos. Retorna um objeto `IbanInfo`, ou `null` sempre que `isValidIban` retornaria `false`. @@ -1557,7 +1604,7 @@ Formata um número ou uma string numérica no padrão BRL (`1.234,56`). Um `numb - **Opções** (`FormatCurrencyOptions`): `symbol` (padrão `false`) prefixa o resultado com `R$`; `precision` (padrão 2) define as casas decimais, limitada de 0 a 20. - Uma `string` é lida como `parseCurrency` a lê, com uma diferença: um valor sem nenhum separador permanece em unidades inteiras, então `'1234'` vira `1.234,00`. -- Retorna `''` para um valor não finito ou que não pode ser convertido em número. +- Retorna `''` para um valor não finito ou que não pode ser convertido em número. Uma string é lida por `parseCurrency`, então uma string sem nenhum dígito é lida como `0` e formata como `0,00` (`'abc'`), e `null` também dá `0,00`. ```javascript import { formatCurrency } from '@brazilian-utils/brazilian-utils'; @@ -1573,6 +1620,8 @@ formatCurrency('-10.5'); // -10,50 (o "-" inicial é preservado) formatCurrency(Number.NaN); // "" (números não finitos viram string vazia) ``` +Fonte: [Lei nº 9.069/1995, art. 1º](https://www.planalto.gov.br/ccivil_03/leis/l9069.htm), que define o símbolo `R$` e a vírgula antes dos centavos. Baseado em: os dados de locale pt-BR do [CLDR](https://cldr.unicode.org/) por trás do `Intl.NumberFormat`, para o agrupamento com `.`. + ### parseCurrency Converte uma string de moeda no padrão BRL em número. @@ -1580,6 +1629,7 @@ Converte uma string de moeda no padrão BRL em número. - **Opções** (`ParseCurrencyOptions`): `precision` (padrão 2) é a quantidade de dígitos lidos como subunidades monetárias, limitada de 0 a 20. - O último `,` ou `.` seguido de 1 a 2 dígitos (até `precision`, quando maior) é o separador decimal; todo outro `,` ou `.` é separador de milhar. - Um valor sem nenhum separador é lido como centavos e dividido por `10 ** precision`. +- Só um `-` antes do primeiro dígito torna o resultado negativo: `'(R$ 1,00)'` e `'1,00-'` viram `1`, e os caracteres que não são dígitos nem separadores são descartados, então `'1e5'` vira `0.15`. ```javascript import { parseCurrency } from '@brazilian-utils/brazilian-utils'; @@ -1595,6 +1645,8 @@ parseCurrency('R$ 1,001', { precision: 3 }); // 1.001 parseCurrency(''); // 0 ``` +Fonte: [Lei nº 9.069/1995, art. 1º](https://www.planalto.gov.br/ccivil_03/leis/l9069.htm), que define o símbolo `R$` e a vírgula antes dos centavos. Baseado em: os dados de locale pt-BR do [CLDR](https://cldr.unicode.org/) por trás do `Intl.NumberFormat`, para o agrupamento com `.`. + ### convertNumberToWords Escreve um número inteiro por extenso em português do Brasil: `1235` vira `"mil duzentos e trinta e cinco"`. @@ -1602,6 +1654,7 @@ Escreve um número inteiro por extenso em português do Brasil: `1235` vira `"mi - **Opções** (`ConvertNumberToWordsOptions`): `gender` (padrão `"masculine"`) concorda "um/dois" e a centena ("duzentos/duzentas") com o substantivo que o número qualifica. - Aceita inteiros de `-999999999999999` a `999999999999999` (999 trilhões). Um valor não inteiro é truncado em direção a zero. - Retorna `""` para um valor fora desse intervalo ou não finito. +- O último grupo leva um "e" antes dele só quando é menor que 100 ou uma centena redonda: `1200` é `"mil e duzentos"` e `1100` é `"mil e cem"`, já `1235` é `"mil duzentos e trinta e cinco"` e `1101` é `"mil cento e um"`. ```javascript import { convertNumberToWords } from '@brazilian-utils/brazilian-utils'; @@ -1621,6 +1674,9 @@ Escreve um valor em reais por extenso, como em cheques e contratos: `1523.45` vi - O `value` é truncado (não arredondado) para 2 casas decimais. - Retorna `""` para uma entrada inválida ou um valor acima de 999 trilhões de reais. +- O singular vale para exatamente um (`"um real"`, `"um centavo"`), e um milhão, bilhão ou trilhão redondo de reais leva "de": `"um milhão de reais"`. +- Um valor que trunca para nada é `"zero reais"`, mesmo se negativo (`-0.001`); qualquer outro valor negativo recebe o prefixo `"menos"`. +- Acima de cerca de 90 trilhões de reais (`Number.MAX_SAFE_INTEGER / 100`) um número não guarda centavos, então o valor é lido como reais inteiros. ```javascript import { convertCurrencyToWords } from '@brazilian-utils/brazilian-utils'; @@ -1707,7 +1763,7 @@ Fonte: [IBGE Localidades](https://servicodados.ibge.gov.br/api/docs/localidades) Retorna o estado brasileiro ao qual um CEP pertence, a partir das faixas de CEP que os Correios atribuem a cada UF (a "Faixa de CEP" de cada UF). - Funciona offline: nenhuma API de CEP é chamada, então a resposta diz qual estado é dono da faixa, não se o CEP está em uso. -- Aceita o que o `isValidCep` aceita: 8 dígitos, como string ou número, ignorando espaços, pontos e hifens. Um CEP que começa com `0` precisa ser uma string, e um número negativo ou fracionário é rejeitado. +- Aceita o que o `isValidCep` aceita: 8 dígitos, como string ou número, ignorando espaços, pontos, hífens e barras. Um CEP que começa com `0` precisa ser uma string, e um número negativo ou fracionário é rejeitado. `getAddressInfoByCep` e `formatCep` com `pad: true` preenchem números com zeros à esquerda (`1310100` vira `01310-100`). - Amazonas, Distrito Federal e Goiás têm duas faixas cada, e nenhuma faixa estadual cobre `00000-000` a `00999-999` nem `78900-000` a `78999-999`. - A faixa é o bloco que pertence ao estado, não uma garantia de que todo CEP dentro dela está em uso: `10000-000` está sem uso dentro da faixa de São Paulo e ainda assim responde São Paulo. - Retorna `null` para um CEP inválido ou fora de todas as faixas. Exporta o tipo `State`. @@ -1769,6 +1825,8 @@ getStateCodeByName(' Rio de Janeiro '); // 'RJ' getStateCodeByName('Neverland'); // null ``` +Fonte: [IBGE, API de Localidades, `estados`](https://servicodados.ibge.gov.br/api/v1/localidades/estados) + ### getStateNameByCode Retorna o nome completo de um estado brasileiro a partir da sigla. @@ -1785,22 +1843,25 @@ getStateNameByCode(' Rj '); // 'Rio de Janeiro' getStateNameByCode('ZZ'); // null ``` +Fonte: [IBGE, API de Localidades, `estados`](https://servicodados.ibge.gov.br/api/v1/localidades/estados) + ### getStateCapital Retorna a capital de um estado brasileiro, no mesmo formato `{ code, name, stateCode }` (`Municipality`) que o `getMunicipalityByCode` retorna para ela. - A busca ignora maiúsculas e minúsculas e os espaços nas pontas. Retorna `null` quando nenhum estado corresponde. -- Para o Distrito Federal, que não tem municípios, a capital é Brasília, com o código que o IBGE dá ao distrito todo. +- O Distrito Federal não é dividido em municípios, mas o IBGE o codifica como um só, Brasília, e essa é a sua capital. ```javascript import { getStateCapital } from '@brazilian-utils/brazilian-utils'; getStateCapital('SP'); // { code: '3550308', name: 'São Paulo', stateCode: 'SP' } getStateCapital('to'); // { code: '1721000', name: 'Palmas', stateCode: 'TO' } +getStateCapital('DF'); // { code: '5300108', name: 'Brasília', stateCode: 'DF' } getStateCapital('ZZ'); // null ``` -Fonte: [IBGE, Anuário Estatístico do Brasil, tabela 1.1.1.2 (capitais, 2025)](https://anuario.ibge.gov.br/2024/territorio/posicao-e-extensao.html). +Fonte: [IBGE, Anuário Estatístico do Brasil, tabela 1.1.1.2 (capitais, 2025)](https://anuario.ibge.gov.br/2024/territorio/posicao-e-extensao.html) ### getRegions @@ -1841,6 +1902,7 @@ Fonte: [IBGE, API de Localidades, `estados`](https://servicodados.ibge.gov.br/ap Retorna o nome do fuso horário IANA (zona do tzdata) de um estado brasileiro: o fuso da sua capital. +- Alguns estados abrangem mais de um fuso, e o fuso da capital não diz nada sobre o resto: o oeste do Amazonas (`America/Eirunepe`, UTC-5) e o oeste do Pará (`America/Santarem`, UTC-3) não são representados, e Fernando de Noronha (`America/Noronha`, UTC-2), distrito de Pernambuco, resolve como Recife (UTC-3). Os deslocamentos seguem as capitais: Acre UTC-5; Amazonas, Roraima, Rondônia, Mato Grosso e Mato Grosso do Sul UTC-4; os demais estados UTC-3. - A comparação não diferencia maiúsculas de minúsculas e remove os espaços nas pontas. - Retorna `null` quando nenhum estado corresponde. @@ -1854,7 +1916,7 @@ getTimezoneByState('PE'); // 'America/Recife' getTimezoneByState('ZZ'); // null ``` -Fonte: [IANA Time Zone Database](https://www.iana.org/time-zones) +Fonte: [IANA Time Zone Database](https://www.iana.org/time-zones), [Decreto 2.784/1913](https://www.planalto.gov.br/ccivil_03/decreto/historicos/dpl/dpl2784-1913.htm), que fixou a hora legal do Brasil, alterado pela [Lei 11.662/2008](https://www.planalto.gov.br/ccivil_03/_ato2007-2010/2008/lei/l11662.htm) e pela [Lei 12.876/2013](https://www.planalto.gov.br/ccivil_03/_ato2011-2014/2013/lei/l12876.htm). ### getMunicipalities @@ -2046,7 +2108,7 @@ Retorna os feriados brasileiros de um ano: os nacionais e, com um `stateCode`, t - O "Dia da Consciência Negra", 20/11, é nacional a partir de 2024. - O primeiro turno das eleições, "Eleições (primeiro turno)", é feriado nacional nos anos pares a partir de 1998 (Código Eleitoral, art. 380): o primeiro domingo de outubro, ou 15/11 em 2020 (EC nº 107/2020). O segundo turno fica de fora, porque só acontece onde é necessário. Por cair num domingo, nunca muda uma contagem de dias úteis. Antes de 1998, a Lei nº 1.266/1950 fazia do dia das eleições gerais um feriado nacional, então as que caíram num dia de semana também são listadas: 3/10 de 1955 e 1958 ("Eleições gerais") e de 1990 e 1994 ("Eleições (primeiro turno)"). O 3/10/1960 fica de fora, porque nenhum texto oficial encontrado data a eleição presidencial daquele ano, assim como a eleição municipal de 3/10/1996. - Cada feriado nacional de data fixa só é listado nos anos em que uma norma federal o declarava (a Sexta-feira Santa, que as portarias do calendário federal listam como feriado nacional todo ano, é listada em todos os anos): Nossa Senhora Aparecida a partir de 1980, Natal a partir de 1922, Dia do trabalhador a partir de 1925, Tiradentes até 1930, de 1933 a 1948 e a partir de 1951, e Finados até 1948 e a partir de 2003. A Lei nº 662/1949 deixou Finados fora dos feriados nacionais que o Decreto-lei nº 486/1938 listava, e só a Lei nº 10.607/2002 o recolocou (o parecer da Câmara sobre o projeto: "Só inova ao sugerir o dia de finados"); até a 2.4.0 ele era listado em todos os anos. As outras "festas nacionais" do primeiro calendário republicano (Decreto nº 155-B/1890 e Decreto nº 3/1891: 24/2, 3/5, 13/5, 14/7 e 12/10) são listadas até 1930, e o 3/5 (de 1936 a 1938), o 16/7 e o 12/10 (em 1936 e 1937) de novo pela Lei nº 108/1935. -- As entradas `"optional"` são os pontos facultativos de dia inteiro do calendário federal (Portarias MGI nº 8.617/2023, 9.783/2024 e 11.460/2025, de 2024 a 2026, que listam as duas datas de Carnaval como ponto facultativo, nunca como feriado nacional): a segunda e a terça-feira de Carnaval e o Corpus Christi, os mesmos três dias que o mercado financeiro não conta como úteis (Resolução CMN nº 4.880/2020), mais os estaduais que uma norma estadual declara (o 08/12 do AM, que o estado declara para as suas repartições por decreto, e o 06/03 de PE em 2008 e 2009). O `includeOptional` liga e desliga exatamente esses. Os parciais ficam de fora: a Quarta-feira de Cinzas (até as 14h), 28/10 (Dia do Servidor Público) e as tardes de 24/12 e 31/12. +- As entradas `"optional"` são os pontos facultativos de dia inteiro do calendário federal (Portarias MGI nº 8.617/2023, 9.783/2024 e 11.460/2025, de 2024 a 2026, que listam as duas datas de Carnaval como ponto facultativo, nunca como feriado nacional): a segunda e a terça-feira de Carnaval e o Corpus Christi, os mesmos três dias que o mercado financeiro não conta como úteis (Resolução CMN nº 4.880/2020), mais os estaduais que uma norma estadual declara (o 08/12 do AM a partir de 1999, que o estado declara para as suas repartições por decreto, e o 06/03 de PE em 2008 e 2009). O `includeOptional` liga e desliga exatamente esses. Os parciais ficam de fora: a Quarta-feira de Cinzas (até as 14h), 28/10 (Dia do Servidor Público) e as tardes de 24/12 e 31/12. - As regras por estado (o deslocamento para domingo em SC da data que cai de segunda a sábado, como o Decreto SC nº 1.460/2018 fez com o 11/08 que caiu num sábado; a data magna de PE no primeiro domingo de março de 2010 a 2017; o 30/11 de AL antecipado para segunda quando cai na terça e adiado para sexta quando cai na quinta, o Corpus Christi no DF, no MA (desde 2024) e no RJ (desde 2026), e a terça-feira de Carnaval no RJ com tipo `"state"`, datas que deixaram de ser feriado) seguem a lei de cada estado; veja a fonte para a lista. O 16/09 de AL é feriado estadual a partir de 2011, como os decretos de calendário do estado o chamam antes da Lei AL nº 9.358/2024. Uma lei estadual que o STF derrubou não tem entrada em nenhum ano: o 18/06 de RO (ADI 3940) e o 25/07 do AP (ADI 4820). - Outros deslocamentos não são aplicados e a data da lei é a retornada: a lei do AC adia para a sexta-feira os feriados que caem de terça a quinta (Lei AC nº 2.126/2009), mas os próprios decretos anuais do estado a aplicam de forma desigual (em 2026 o 20/1 é adiado e o 17/11, uma terça, fica na data). - As três datas de GO (26/7, 24/10, 28/10) são os "feriados estaduais" do estatuto dos servidores do estado, listados a partir de 1986 (Lei GO nº 9.990/1986, depois Lei GO nº 10.460/1988 e Lei GO nº 20.756/2020, art. 269, II); os mesmos estatutos faziam do 2/11 feriado em GO de 1986 a 2002, os anos em que ele não era nacional. Não foi achada lei goiana que fixe uma data magna como feriado civil. O governador transfere o 26/7 por decreto todo ano (2025: 28/7; 2026: 20/7), e o 28/10 na maioria dos anos (2025: 27/10; 2026: 30/10), então a data da lei, que é a retornada aqui, muitas vezes não é o dia observado. @@ -2057,7 +2119,7 @@ Retorna os feriados brasileiros de um ano: os nacionais e, com um `stateCode`, t ```javascript import { getHolidays } from '@brazilian-utils/brazilian-utils'; -// Obtém todos os feriados nacionais de 2024 +// Obtém os feriados de 2024, nacionais e facultativos getHolidays(2024); // [ // { name: 'Ano novo', date: Date('2024-01-01'), type: 'national' }, @@ -2065,9 +2127,16 @@ getHolidays(2024); // { name: 'Carnaval (terça-feira)', date: Date('2024-02-13'), type: 'optional' }, // { name: 'Sexta-feira Santa', date: Date('2024-03-29'), type: 'national' }, // { name: 'Páscoa', date: Date('2024-03-31'), type: 'religious' }, +// { name: 'Tiradentes', date: Date('2024-04-21'), type: 'national' }, +// { name: 'Dia do trabalhador', date: Date('2024-05-01'), type: 'national' }, +// { name: 'Corpus Christi', date: Date('2024-05-30'), type: 'optional' }, +// { name: 'Independência do Brasil', date: Date('2024-09-07'), type: 'national' }, // { name: 'Eleições (primeiro turno)', date: Date('2024-10-06'), type: 'national' }, +// { name: 'Nossa Senhora Aparecida', date: Date('2024-10-12'), type: 'national' }, +// { name: 'Finados', date: Date('2024-11-02'), type: 'national' }, +// { name: 'Proclamação da República', date: Date('2024-11-15'), type: 'national' }, // { name: 'Dia da Consciência Negra', date: Date('2024-11-20'), type: 'national' }, -// // ... mais feriados +// { name: 'Natal', date: Date('2024-12-25'), type: 'national' }, // ] // Obtém feriados para um estado específico @@ -2084,6 +2153,8 @@ Verifica se uma data é feriado brasileiro. Aceita `{ targetDate, stateCode? }` - A verificação usa a data de calendário local de `targetDate`, não o seu instante UTC. - `stateCode` também considera os feriados daquele estado, lido como em `getHolidays` (maiúsculas/minúsculas e espaços nas pontas são ignorados). - Retorna `false` quando `targetDate` está ausente ou não é um `Date` válido, ou quando `stateCode` está presente e não é uma sigla de estado (`'XX'`, `''`, um valor que não é string), mesmo num feriado nacional. +- Retorna `false` para um ano fora de 1900 a 2099, em que o `getHolidays` não lista nada. +- As entradas `"optional"` e `"religious"` do `getHolidays` contam: segunda e terça de Carnaval, Corpus Christi e Páscoa fazem o `isHoliday` retornar true. Aqui não há `includeOptional`, ao contrário do `isBusinessDay`. ```javascript import { isHoliday } from '@brazilian-utils/brazilian-utils'; @@ -2228,7 +2299,7 @@ isValidPassport('AB-123.456'); // true (símbolos são ignorados) isValidPassport('12345678'); // false ``` -Fonte: [Polícia Federal](https://www.gov.br/pf/pt-br/assuntos/passaporte) e seu [FAQ](https://www.gov.br/pf/pt-br/assuntos/passaporte/ajuda/duvidas_/caderneta/caderneta-numero-onde-fica-e). +Fonte: [FAQ da Polícia Federal](https://www.gov.br/pf/pt-br/assuntos/passaporte/ajuda/duvidas_/caderneta/caderneta-numero-onde-fica-e). ### formatPassport @@ -2266,7 +2337,7 @@ generatePassport(); // 'RY393097' ### isValidCnh -Valida uma CNH. Espaços, pontos e hífens são ignorados; qualquer outro caractere invalida o valor. +Valida uma CNH. Espaços, pontos, hífens e barras são ignorados; qualquer outro caractere invalida o valor. - Um valor cujos 11 dígitos são todos iguais é rejeitado, então `'11111111111'` é inválido. - O primeiro dígito verificador mantém o resto 1 como `1`, como nos números reais de registro. O art. 4º, § 1º, da Resolução CONTRAN nº 886/2021, em que o resto 0 ou 1 dá `0`, fala em "O dígito verificador" sem dizer de qual número: escrito no singular logo depois do Número do Espelho da CNH (o único número do artigo com um só dígito verificador), ele se lê melhor como a regra desse dígito, mas a redação é genérica e não traz pesos, então não serve de fonte para os 2 dígitos verificadores do número de registro; nenhum texto oficial publica os pesos deles. As Resoluções CONTRAN nº 976/2022, nº 998/2023 e nº 1.006/2024 alteram a Resolução nº 886/2021, nenhuma delas no art. 4º. A Resolução CONTRAN nº 1.020/2025, a norma de habilitação mais nova, repete o layout no art. 10 ("nove caracteres e dois dígitos verificadores") sem regra de dígito verificador e não revoga a 886 (art. 140). @@ -2285,7 +2356,7 @@ Fonte: [Resolução CONTRAN nº 886/2021, art. 4º](https://www.gov.br/transport Formata uma CNH. -- **Opções** (`FormatCnhOptions`): `pad` completa o valor com zeros à esquerda até os 11 dígitos antes de aplicar a máscara (padrão `false`); `obfuscate` esconde os 3 primeiros dígitos e os 2 dígitos verificadores. +- **Opções** (`FormatCnhOptions`): `pad` completa o valor com zeros à esquerda até os 11 dígitos antes de aplicar a máscara (padrão `false`); `obfuscate` esconde os 3 primeiros dígitos e os 2 dígitos verificadores. Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. - O `obfuscate` é aplicado depois do `pad`. - Nenhuma autoridade publica uma regra de mascaramento para a CNH, então o `obfuscate` usa a que as Leis de Diretrizes Orçamentárias definem para a divulgação do CPF ("ocultar os três primeiros dígitos e os dois dígitos verificadores", Lei nº 14.194/2021, art. 149, regra criada pela Lei nº 12.309/2010, art. 87, § 5º), um número com a mesma estrutura. @@ -2323,16 +2394,20 @@ Fonte: [Lei nº 14.194/2021, art. 149](https://www.planalto.gov.br/ccivil_03/_at ### isValidLegalNature -Valida se um código de natureza jurídica existe na lista oficial, a tabela "Natureza Jurídica 2021" do IBGE/CONCLA. Somente hífens, pontos e espaços são tolerados ao redor dos 4 dígitos. +Valida se um código de natureza jurídica existe na lista oficial, a tabela "Natureza Jurídica 2021" do IBGE/CONCLA. Aceita uma string com os 4 dígitos ou com a máscara `NNN-N`, ou um inteiro seguro não negativo. +- Uma sequência de separadores (espaço, `.`, `-` ou `/`) só é lida entre o terceiro e o quarto dígito, então `'2-0-6-2'` é rejeitado. - Os 92 códigos em vigor são aceitos, mais os 8 que uma revisão anterior extinguiu. `getLegalNature` distingue os dois (`legacy: true`). ```javascript import { isValidLegalNature } from '@brazilian-utils/brazilian-utils'; isValidLegalNature('2062'); // true +isValidLegalNature(2062); // true +isValidLegalNature('206-2'); // true isValidLegalNature('2208'); // true (extinto por uma revisão anterior, ainda aceito) isValidLegalNature('9999'); // false +isValidLegalNature('2-0-6-2'); // false (um separador só cabe depois do terceiro dígito) ``` Fonte: [CONCLA, Natureza Jurídica 2021](https://concla.ibge.gov.br/estrutura/natjur-estrutura/natureza-juridica-2021) e seu [PDF de estrutura detalhada](https://concla.ibge.gov.br/images/concla/documentacao/CONCLA-TNJ2021-EstruturaDetalhada.pdf). @@ -2341,7 +2416,7 @@ Fonte: [CONCLA, Natureza Jurídica 2021](https://concla.ibge.gov.br/estrutura/na Formata um código de natureza jurídica. Use `isValidLegalNature` para verificar um código. -- **Opções** (`FormatLegalNatureOptions`): `pad` primeiro completa o valor com zeros à esquerda até os 4 dígitos de um código completo (padrão `false`). +- **Opções** (`FormatLegalNatureOptions`): `pad` primeiro completa o valor com zeros à esquerda até os 4 dígitos de um código completo (padrão `false`). Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. ```javascript import { formatLegalNature } from '@brazilian-utils/brazilian-utils'; @@ -2377,7 +2452,7 @@ generateLegalNature(); // '2062' Busca um código de natureza jurídica na tabela oficial do IBGE/CONCLA. Retorna `null` para um código desconhecido. - A entrada (`LegalNature`) também traz a categoria do CONCLA do código, dada pelo seu primeiro dígito. -- Um código que uma revisão anterior extinguiu retorna com `legacy: true` e o `currentCode` a que corresponde hoje, ou `currentCode: null` quando não há sucessor. Os códigos em vigor têm `legacy: false` e nenhum `currentCode`. +- Um código que uma revisão anterior extinguiu retorna com `legacy: true` e o `currentCode` a que corresponde hoje, ou `currentCode: null` quando não há sucessor (`2100`, `3050` e `3123`). Os códigos em vigor têm `legacy: false` e nenhum `currentCode`. | Código extinto | Descrição | Corresponde a | | --- | --- | --- | @@ -2466,7 +2541,7 @@ Valida um título de eleitor. Um título tem no máximo 12 dígitos, então um v - Um título é um número sequencial de 8 dígitos, um código de unidade federativa de 2 dígitos (`01` a `28`) e 2 dígitos verificadores. - O TSE despreza os zeros à esquerda do número sequencial na emissão, então um valor mais curto é lido como o título sem eles e completado com zeros à esquerda até 12 dígitos antes da validação (`123450159` é validado como `000123450159`). É preciso ao menos um dígito sequencial: o menor valor aceito tem 5 dígitos. -- Espaços e pontos são aceitos ao redor e entre os grupos. Qualquer outro caractere, inclusive um hífen, invalida o valor. +- Espaços, pontos, hífens e barras são aceitos ao redor e entre os grupos. Qualquer outro caractere invalida o valor. - A Resolução TSE nº 23.659/2021, art. 36, que revogou a Resolução TSE nº 21.538/2003 (art. 140), fixa o layout, a tabela das unidades federativas e dois dígitos verificadores "determinados com base no 'Módulo 11'". Ela não traz pesos nem regra por estado: os pesos e a regra que troca o resto 0 por 1 para São Paulo (`01`) e Minas Gerais (`02`) não têm fonte oficial e seguem as referências da comunidade abaixo. ```javascript @@ -2487,7 +2562,7 @@ Fonte: [Resolução TSE nº 23.659/2021, art. 36](https://www.tse.jus.br/legisla Formata um título de eleitor com o agrupamento de 12 dígitos `0000 0000 00 00`. -- **Opções** (`FormatVoterIdOptions`): `pad` completa o valor com zeros à esquerda até 12 dígitos, restaurando os zeros de um título emitido sem eles; `obfuscate` esconde os 3 primeiros dígitos e os 2 dígitos verificadores, deixando visível o código da unidade federativa. A máscara esconde por posição, então use `pad` junto com `obfuscate` para um título passado como número, que perdeu os zeros à esquerda: sem ele a máscara cai sobre os dígitos verificadores. +- **Opções** (`FormatVoterIdOptions`): `pad` completa o valor com zeros à esquerda até 12 dígitos, restaurando os zeros de um título emitido sem eles; `obfuscate` esconde os 3 primeiros dígitos e os 2 dígitos verificadores, deixando visível o código da unidade federativa. A máscara esconde por posição, então use `pad` junto com `obfuscate` para um título passado como número, que perdeu os zeros à esquerda: sem ele a máscara cai sobre os dígitos verificadores. Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. - Sem `pad`, um valor mais curto é formatado a partir da esquerda, como um título digitado pela metade. - Os dígitos além do 12º são descartados. - Nenhuma autoridade publica uma regra de mascaramento para o título de eleitor, então o `obfuscate` usa a que as Leis de Diretrizes Orçamentárias definem para a divulgação do CPF ("ocultar os três primeiros dígitos e os dois dígitos verificadores", Lei nº 14.194/2021, art. 149, regra criada pela Lei nº 12.309/2010, art. 87, § 5º), um número com a mesma estrutura. @@ -2557,7 +2632,7 @@ Fonte: [rotinas de validação do DATASUS](https://web.archive.org/web/201901060 Formata um número de CNS (Cartão Nacional de Saúde) nos grupos de exibição usuais de 3-4-4-4 dígitos separados por espaço. -- **Opções** (`FormatCnsOptions`): `pad` completa o valor com zeros à esquerda até as 15 posições do padrão antes de aplicar a máscara (padrão `false`). +- **Opções** (`FormatCnsOptions`): `pad` completa o valor com zeros à esquerda até as 15 posições do padrão antes de aplicar a máscara (padrão `false`). Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. ```javascript import { formatCns } from '@brazilian-utils/brazilian-utils'; @@ -2622,16 +2697,16 @@ Fonte: [art. 473 do Código Nacional de Normas da Corregedoria Nacional de Justi Formata a matrícula de uma certidão de registro civil na máscara impressa do art. 473. Os 32 dígitos são agrupados em 6 2 2 4 1 5 3 7 2 e separados por espaços. -- **Opções** (`FormatCertidaoOptions`): `pad` completa o valor com zeros à esquerda até 32 dígitos (padrão `false`). +- **Opções** (`FormatCertidaoOptions`): `pad` completa o valor com zeros à esquerda até 32 dígitos (padrão `false`). Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. - Um número é aceito quando é um inteiro seguro não negativo, então uma matrícula completa de 32 dígitos precisa ser uma string. Qualquer outro número retorna `''`. ```javascript import { formatCertidao } from '@brazilian-utils/brazilian-utils'; -formatCertidao('10453901552013100012021000012321'); // 104539 01 55 2013 1 00012 021 0000123 21 -formatCertidao('104539.01.55.2013.1.00012.021.0000123-21'); // 104539 01 55 2013 1 00012 021 0000123 21 -formatCertidao('1552010100020112000012087', { pad: true }); // 000000 01 55 2010 1 00020 112 0000120 87 -formatCertidao(104539015520); // 104539 01 55 20 (um número é lido como a string dos seus dígitos) +formatCertidao('10453901552013100012021000012321'); // '104539 01 55 2013 1 00012 021 0000123 21' +formatCertidao('104539.01.55.2013.1.00012.021.0000123-21'); // '104539 01 55 2013 1 00012 021 0000123 21' +formatCertidao('1552010100020112000012087', { pad: true }); // '000000 01 55 2010 1 00020 112 0000120 87' +formatCertidao(104539015520); // '104539 01 55 20' (um número é lido como a string dos seus dígitos) formatCertidao(1045390155.2); // '' (não é um inteiro seguro não negativo) ``` @@ -2699,6 +2774,7 @@ Fonte: [art. 473 do Código Nacional de Normas da Corregedoria Nacional de Justi Valida um número de CEI (Cadastro Específico do INSS). O CEI identifica o empregador sem CNPJ, como uma obra ou um produtor rural. - Layout: 12 dígitos impressos como `00.000.00000/00`, 11 dígitos de base e um dígito verificador. +- Um número perde os zeros à esquerda, então um valor que começa com `0` só é aceito como string: `isValidCei('000000336854')` é `true` e `isValidCei(336854)` é `false`. - Só os 12 dígitos são oficiais: nenhuma norma, leiaute ou manual da Receita Federal publica o dígito verificador, que segue as referências da comunidade abaixo e confere com a base aberta do CNO e com o exemplo `000000336854` do SERPRO. ```javascript @@ -2718,7 +2794,7 @@ Fonte: [SERPRO, cadastro CNO](https://bcadastros.serpro.gov.br/documentacao/cada Formata um número de CEI (Cadastro Específico do INSS) na máscara usual `00.000.00000/00`. -- **Opções** (`FormatCeiOptions`): `pad` completa o valor com zeros à esquerda até 12 dígitos (padrão `false`). +- **Opções** (`FormatCeiOptions`): `pad` completa o valor com zeros à esquerda até 12 dígitos (padrão `false`). Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. ```javascript import { formatCei } from '@brazilian-utils/brazilian-utils'; @@ -2742,7 +2818,7 @@ parseCei('27.729.71181/87'); // '277297118187' Valida um número de CNO (Cadastro Nacional de Obras). O CNO substituiu o CEI para obras e manteve a mesma numeração. -- Mesmas regras de `isValidCei`. +- Mesmas regras de `isValidCei`, inclusive os zeros à esquerda de um número. ```javascript import { isValidCno } from '@brazilian-utils/brazilian-utils'; @@ -2787,6 +2863,7 @@ Valida um número de CAEPF (Cadastro de Atividade Econômica da Pessoa Física). - Layout: 14 dígitos impressos como `000.000.000/000-00`: a base de 9 dígitos do CPF do titular, um número de ordem de 3 dígitos e 2 dígitos verificadores. - Os dois dígitos verificadores seguem o módulo 11 do CNPJ; o par é então somado a 12, com retorno a zero acima de 99. +- Um número perde os zeros à esquerda, então um valor que começa com `0` só é aceito como string: `isValidCaepf('00000002500171')` é `true` e `isValidCaepf(2500171)` é `false`. - Só as 14 posições e a base do CPF são oficiais (SERPRO: "9 primeiros números do CPF + número de inscrição resumido" de 5 posições). A divisão dessas 5 em número de ordem e 2 dígitos verificadores, a regra do dígito e a soma de 12 vêm das referências da comunidade abaixo; elas conferem com o exemplo `00000002500171` do SERPRO. ```javascript @@ -2834,7 +2911,7 @@ parseCaepf('293.118.610/001-84'); // '29311861000184' Valida um código CBO (Classificação Brasileira de Ocupações) contra a tabela oficial da CBO 2002. - Aceita uma string com os 6 dígitos ou com a máscara `NNNN-NN`, ou um número. -- Uma string mascarada precisa de um único separador (espaço, `.`, `-` ou `/`) entre os grupos. Qualquer outra string é rejeitada, em vez de ter seus dígitos extraídos. +- Uma string mascarada pode ter qualquer sequência de separadores (espaço, `.`, `-` ou `/`) entre os grupos. Qualquer outra string é rejeitada, em vez de ter seus dígitos extraídos. - Dígitos sem máscara são completados com zeros à esquerda até 6, como string ou como número. Um valor mascarado é lido como foi escrito. ```javascript @@ -2850,7 +2927,26 @@ isValidCbo('2124abc05'); // false (não é uma forma documentada) isValidCbo(-212405); // false (não é um inteiro seguro não negativo) ``` -Fonte: [tabelas da CBO 2002 publicadas pelo MTE ("Estrutura CBO (CSV)", arquivos de 10/07/2026, 2.725 ocupações)](https://cbo.mte.gov.br/cbosite/pages/downloads.jsf). Até a 2.4.0 a tabela vinha da versão mais antiga do gov.br (06/06/2025): faltavam 37 ocupações, e 6 que o MTE retirou depois (225142, 322105, 322115, 322120, 322125 e 782820) deixam de ser válidas. +Fonte: [tabelas da CBO 2002 publicadas pelo MTE ("Estrutura CBO (CSV)", arquivos de 10/07/2026, 2.725 ocupações)](https://cbo.mte.gov.br/cbosite/pages/downloads.jsf). + +### formatCbo + +Formata um código CBO (Classificação Brasileira de Ocupações) na máscara `NNNN-NN`. Só a estrutura muda; use `isValidCbo` para conferir um código com a tabela. + +- **Opções** (`FormatCboOptions`): `pad` (padrão `false`) completa antes o valor com zeros à esquerda até os 6 dígitos de um código completo. Sem ele a máscara é aplicada até onde o valor vai. Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. +- Caracteres fora da máscara são descartados, e um número só é lido como a string dos seus dígitos quando é um inteiro seguro não negativo: um número negativo, fracionário ou inseguro retorna `''`. Retorna `''` quando não há dígito algum. + +```javascript +import { formatCbo } from '@brazilian-utils/brazilian-utils'; + +formatCbo('212405'); // 2124-05 +formatCbo('21240'); // 2124-0 (máscara aplicada até onde o valor vai) +formatCbo('10205', { pad: true }); // 0102-05 (completado até 6 dígitos antes) +formatCbo('abc212405'); // 2124-05 (só os dígitos são lidos) +formatCbo(-212405); // '' (não é um inteiro seguro não negativo) +``` + +Fonte: [tabelas da CBO 2002 publicadas pelo MTE](https://cbo.mte.gov.br/cbosite/pages/downloads.jsf), que imprimem os códigos como `NNNN-NN`. ### parseCbo @@ -2880,7 +2976,7 @@ getCbo('000000'); // null getCbo('2124abc05'); // null (não é uma forma documentada) ``` -Fonte: [tabelas da CBO 2002 publicadas pelo MTE ("Estrutura CBO (CSV)", arquivos de 10/07/2026, 2.725 ocupações)](https://cbo.mte.gov.br/cbosite/pages/downloads.jsf). Até a 2.4.0 a tabela vinha da versão mais antiga do gov.br (06/06/2025): faltavam 37 ocupações, e 6 que o MTE retirou depois (225142, 322105, 322115, 322120, 322125 e 782820) deixam de ser válidas. +Fonte: [tabelas da CBO 2002 publicadas pelo MTE ("Estrutura CBO (CSV)", arquivos de 10/07/2026, 2.725 ocupações)](https://cbo.mte.gov.br/cbosite/pages/downloads.jsf). ### isValidCnae @@ -2906,7 +3002,7 @@ Fonte: [CNAE-Subclasses 2.3 na CONCLA/IBGE](https://concla.ibge.gov.br/busca-onl Formata um código de subclasse CNAE (Classificação Nacional de Atividades Econômicas). Só a estrutura muda; use `isValidCnae` para conferir um código com a tabela. -- **Opções** (`FormatCnaeOptions`): `pad` (padrão `false`) completa antes o valor com zeros à esquerda até os 7 dígitos de um código completo. Sem ele a máscara é aplicada até onde o valor vai. +- **Opções** (`FormatCnaeOptions`): `pad` (padrão `false`) completa antes o valor com zeros à esquerda até os 7 dígitos de um código completo. Sem ele a máscara é aplicada até onde o valor vai. Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. - Caracteres fora da máscara são descartados, e um número só é lido como a string dos seus dígitos quando é um inteiro seguro não negativo: um número negativo, fracionário ou inseguro retorna `''`, já que o sinal e o ponto decimal não são caracteres da máscara. Retorna `''` quando não há dígito algum. ```javascript @@ -2978,7 +3074,7 @@ Fonte: [nomenclatura NCM publicada pelo Portal Único Siscomex](https://portalun Formata um código NCM (Nomenclatura Comum do Mercosul). Só a estrutura muda; use `isValidNcm` para conferir um código com a tabela. -- **Opções** (`FormatNcmOptions`): `pad` (padrão `false`) completa antes o valor com zeros à esquerda até os 8 dígitos de um código completo. +- **Opções** (`FormatNcmOptions`): `pad` (padrão `false`) completa antes o valor com zeros à esquerda até os 8 dígitos de um código completo. Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. - Mesmas regras de `formatCnae`, com a máscara `NNNN.NN.NN`. ```javascript @@ -3010,7 +3106,7 @@ parseNcm('8471'); // '8471' (um código parcial é mantido como está) Valida um código NBS (Nomenclatura Brasileira de Serviços, Intangíveis e Outras Operações que Produzam Variações no Patrimônio) contra a tabela oficial da NBS 2.0, o código que a NFS-e nacional leva em `cNBS`. - O código tem 9 dígitos, impressos como `N.NNNN.NN.NN`: o algarismo 1, o capítulo, a posição, os dois níveis de subposição, o item e o subitem. -- Aceita uma string com os 9 dígitos ou com a máscara, com um único separador entre os grupos e espaços opcionais nas extremidades, ou um inteiro seguro não negativo. Qualquer outra string é rejeitada em vez de ter os dígitos pinçados. +- Aceita uma string com os 9 dígitos ou com a máscara, com qualquer sequência de separadores (espaço, `.`, `-` ou `/`) entre os grupos e espaços opcionais nas extremidades, ou um inteiro seguro não negativo. Qualquer outra string é rejeitada em vez de ter os dígitos pinçados. - Só códigos completos são válidos: os títulos de capítulo (`1.01`), posição (`1.0101`) e subposição (`1.0101.1`) não classificam nada por si sós. - O ANEXO B do Sistema Nacional NFS-e lista os mesmos 920 códigos menos três (`1.0402.29.00`, `1.0403.29.00` e `1.0904.40.00`), então um código válido aqui ainda pode ser recusado pela NFS-e. @@ -3029,7 +3125,7 @@ isValidNbs('1.0101abc11.00'); // false (não é uma forma documentada) Formata um código NBS (Nomenclatura Brasileira de Serviços) na máscara `N.NNNN.NN.NN` em que a nomenclatura o imprime. Só a estrutura muda; use `isValidNbs` para conferir um código com a tabela. -- Todo código NBS começa com 1, então, diferente do `formatNcm`, não há opção `pad`. +- **Opções** (`FormatNbsOptions`): `pad` (padrão `false`) completa antes o valor com zeros à esquerda até os 9 dígitos de um código completo (todo código NBS começa com 1, então só serve para largura fixa). Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. - No resto, mesmas regras de `formatCnae`: a máscara é aplicada até onde o valor vai, os caracteres fora dela são descartados e um número só é lido como a string dos seus dígitos quando é um inteiro seguro não negativo; qualquer outro número retorna `''`. ```javascript @@ -3038,10 +3134,23 @@ import { formatNbs } from '@brazilian-utils/brazilian-utils'; formatNbs('101011100'); // 1.0101.11.00 formatNbs(101011100); // 1.0101.11.00 formatNbs('10101'); // 1.0101 (mascarado até onde vai) +formatNbs('1', { pad: true }); // 0.0000.00.01 (completado até 9 dígitos antes) formatNbs('abc101011100'); // 1.0101.11.00 (só os dígitos são lidos) formatNbs(-101011100); // '' (não é um inteiro seguro não negativo) ``` +### parseNbs + +Remove a formatação do NBS (Nomenclatura Brasileira de Serviços), mantém apenas os dígitos e limita o resultado aos 9 dígitos de um código completo. + +- Mesmas regras de `parseCbo`: nada é completado com zeros à esquerda aqui. + +```javascript +import { parseNbs } from '@brazilian-utils/brazilian-utils'; + +parseNbs('1.0101.11.00'); // '101011100' +``` + ### getNbs Consulta um código NBS (Nomenclatura Brasileira de Serviços) e retorna a sua descrição oficial. O resultado é um registro `Nbs`: `{ code, description }`. @@ -3067,7 +3176,7 @@ Verifica se um valor é um subitem em vigor da lista de serviços anexa à Lei C - A lei numera o subitem como o item, um ponto e dois dígitos, de `1.01` a `40.01`. - Aceita essa forma, o item preenchido com zero (`'01.01'`) ou os dígitos puros (`'0101'`, `'101'` ou o inteiro `101`), que são os quatro primeiros dígitos do código `cTribNac` da NFS-e nacional, com espaços opcionais nas extremidades. -- O ponto é o único separador que a lei imprime entre o item e o subitem, então, ao contrário dos códigos com máscara de agrupamento impressa (`isValidCfop`, `isValidNbs`), nada mais é aceito no lugar dele e `'1-01'` é rejeitado. +- Qualquer sequência de separadores (espaço, `.`, `-` ou `/`) é aceita entre o item e o subitem, como nos outros códigos com máscara de agrupamento impressa (`isValidCfop`, `isValidNbs`), então `'1-01'` e `'1 01'` também são válidos. - Um número só é lido quando é um inteiro seguro não negativo, então o decimal `1.01` é rejeitado: escreva a forma com ponto como string. - Os subitens vetados (`3.01`, `7.14`, `7.15`, `13.01` e `17.07`), os títulos de item, os códigos nacionais de 6 dígitos em que um subitem se desdobra e o item 99 da lista nacional, que não faz parte da lei, não são válidos. Códigos municipais de serviço estão fora do escopo. @@ -3105,7 +3214,7 @@ Fonte: [Lei Complementar 116/2003](https://www.planalto.gov.br/ccivil_03/leis/lc Valida um código CFOP (Código Fiscal de Operações e Prestações) contra a tabela oficial, o Anexo II consolidado do Convênio SINIEF s/nº 1970 em vigor. - Só os códigos operáveis contam: os títulos de grupo e subgrupo, os códigos terminados em `00` e `50`, são rejeitados. -- Aceita uma string com os 4 dígitos ou com a forma `N.NNN`, com um único separador (espaço, `.`, `-` ou `/`), ou um número. Qualquer outra string é rejeitada. +- Aceita uma string com os 4 dígitos ou com a forma `N.NNN`, com qualquer sequência de separadores (espaço, `.`, `-` ou `/`) entre os grupos, ou um número. Qualquer outra string é rejeitada. - Nenhum código CFOP começa com zero, então nada é completado. ```javascript @@ -3122,6 +3231,25 @@ isValidCfop(-5102); // false (não é um inteiro seguro não negativo) Fonte: [Anexo II consolidado do Convênio SINIEF s/nº 1970](https://www.confaz.fazenda.gov.br/legislacao/ajustes/sinief/cfop_cvsn_1-6.24), última alteração pelo [Ajuste SINIEF 39/25](https://www.confaz.fazenda.gov.br/legislacao/ajustes/2025/AJ039_25). +### formatCfop + +Formata um código CFOP (Código Fiscal de Operações e Prestações) na forma `N.NNN` que o anexo imprime. Só a estrutura muda; use `isValidCfop` para conferir um código com a tabela. + +- **Opções** (`FormatCfopOptions`): `pad` (padrão `false`) completa antes o valor com zeros à esquerda até os 4 dígitos de um código completo (nenhum CFOP começa com zero, então só serve para largura fixa). Sem ele a máscara é aplicada até onde o valor vai. Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. +- Caracteres fora da máscara são descartados, e um número só é lido como a string dos seus dígitos quando é um inteiro seguro não negativo: um número negativo, fracionário ou inseguro retorna `''`. Retorna `''` quando não há dígito algum. + +```javascript +import { formatCfop } from '@brazilian-utils/brazilian-utils'; + +formatCfop('5102'); // 5.102 +formatCfop('51'); // 5.1 (máscara aplicada até onde o valor vai) +formatCfop('102', { pad: true }); // 0.102 (completado até 4 dígitos antes) +formatCfop('abc5102'); // 5.102 (só os dígitos são lidos) +formatCfop(-5102); // '' (não é um inteiro seguro não negativo) +``` + +Fonte: [Convênio SINIEF s/nº 1970, Anexo II](https://www.confaz.fazenda.gov.br/legislacao/ajustes/sinief/cfop_cvsn_1-6.24), que imprime os códigos como `N.NNN`. + ### parseCfop Remove a formatação do CFOP (Código Fiscal de Operações e Prestações), mantém apenas os dígitos e limita o resultado a 4 dígitos. @@ -3159,7 +3287,7 @@ Valida um CEST (Código Especificador da Substituição Tributária) contra os a - Só os itens em vigor contam: um item que os anexos marcam como revogado é rejeitado. - A verificação é só do código: ela não diz se o código combina com um dado NCM, nem se um estado aplica o regime de substituição tributária a ele. - Um CEST tem 7 dígitos: os dois primeiros são o segmento, do terceiro ao quinto o item do segmento e os dois últimos a especificação do item (cláusula sexta, IV). -- Aceita uma string com os 7 dígitos ou com a forma `NN.NNN.NN` que os anexos imprimem, com um único separador entre os grupos e espaços opcionais nas extremidades, ou um inteiro seguro não negativo. Qualquer outra string é rejeitada em vez de ter os dígitos pinçados. +- Aceita uma string com os 7 dígitos ou com a forma `NN.NNN.NN` que os anexos imprimem, com qualquer sequência de separadores (espaço, `.`, `-` ou `/`) entre os grupos e espaços opcionais nas extremidades, ou um inteiro seguro não negativo. Qualquer outra string é rejeitada em vez de ter os dígitos pinçados. - O zero à esquerda dos segmentos 01 a 09 faz parte do código, então um valor escrito apenas com dígitos é completado com zeros à esquerda até 7, como string ou como número: `100100`, `'100100'` e `'0100100'` são o mesmo código. Um valor mascarado é lido como foi escrito. ```javascript @@ -3178,7 +3306,7 @@ isValidCest(-100100); // false (não é um inteiro seguro não negativo) Formata um CEST (Código Especificador da Substituição Tributária) na forma `NN.NNN.NN` que os anexos do Convênio ICMS 142/18 imprimem. Só a estrutura muda; use `isValidCest` para conferir um código com os anexos. -- **Opções** (`FormatCestOptions`): `pad` (padrão `false`) completa antes o valor com zeros à esquerda até os 7 dígitos de um código completo. +- **Opções** (`FormatCestOptions`): `pad` (padrão `false`) completa antes o valor com zeros à esquerda até os 7 dígitos de um código completo. Um valor vazio, ou sem dígitos, devolve `''` mesmo com `pad`. - Mesmas regras de `formatNcm`: sem `pad` a máscara é aplicada até onde o valor vai, que é o que um campo sendo digitado precisa, os caracteres fora dela são descartados e um número é lido como a string dos seus dígitos, ou seja, só é completado com `pad: true`. Um número só é lido quando é um inteiro seguro não negativo; qualquer outro número retorna `''`. ```javascript @@ -3230,14 +3358,14 @@ Valida um código de CST (Código de Situação Tributária) para um tributo. In | Tributo | Formato | Códigos aceitos | | --- | --- | --- | -| `icms` | 3 dígitos (origem + CST) | origem `0`-`8` + um de `00`, `02`, `10`, `15`, `20`, `30`, `40`, `41`, `50`, `51`, `53`, `60`, `61`, `70`, `90` | +| `icms` | 2 dígitos (Tabela B) ou 3 dígitos (origem + CST) | o código da Tabela B sozinho, ou origem `0`-`8` + um de `00`, `02`, `10`, `15`, `20`, `30`, `40`, `41`, `50`, `51`, `53`, `60`, `61`, `70`, `90` | | `ipi` | 2 dígitos | `00`, `01`, `02`, `03`, `04`, `05`, `49`, `50`, `51`, `52`, `53`, `54`, `55`, `99` | | `pis` | 2 dígitos | `01`-`09`, `49`, `50`-`56`, `60`-`67`, `70`-`75`, `98`, `99` | | `cofins` | 2 dígitos | mesma tabela do `pis` | -- **Opções** (`IsValidCstOptions`): `tax` escolhe a tabela. Omitido, ou fora desses quatro valores, todas as tabelas são aceitas. -- Aceita uma string com os 2 dígitos de um código da Tabela B ou os 3 dígitos da forma do ICMS, ou um número. A forma do ICMS pode ter um único separador (espaço, `.`, `-` ou `/`) depois do dígito de origem. -- Um único dígito é completado até a forma de 3 dígitos do ICMS; uma string de 2 dígitos é um código da Tabela B, enquanto o número `7` é o código ICMS `007`. +- **Opções** (`IsValidCstOptions`): `tax` escolhe a tabela. Omitido, ou fora desses quatro valores, todas as tabelas são aceitas; um `options` `null` ou que não seja objeto é lido como ausente. +- Aceita uma string com os 2 dígitos de um código da Tabela B ou os 3 dígitos da forma do ICMS, ou um número. A forma do ICMS pode ter qualquer sequência de separadores (espaço, `.`, `-` ou `/`) depois do dígito de origem. +- Um único dígito é completado até a forma de 3 dígitos do ICMS; um valor de 2 dígitos é um código da Tabela B e nunca é lido como origem mais um dígito (`'10'` é o código da Tabela B `10`), enquanto o número `7` é lido como o código ICMS `007`, que não está na tabela, então dá `false`. ```javascript import { isValidCst } from '@brazilian-utils/brazilian-utils'; @@ -3247,6 +3375,7 @@ isValidCst(0, { tax: 'icms' }); // true (um único dígito é completado até a isValidCst('0', { tax: 'icms' }); // true (completado do mesmo jeito que um número) isValidCst('110', { tax: 'icms' }); // true isValidCst('002', { tax: 'icms' }); // true (monofasia de combustíveis) +isValidCst('60', { tax: 'icms' }); // true (um código da Tabela B sozinho, como o campo CST da NF-e o traz) isValidCst('06', { tax: 'pis' }); // true isValidCst('99', { tax: 'ipi' }); // true isValidCst('110'); // true (encontrado na tabela icms, tax omitido) @@ -3316,7 +3445,7 @@ getCstIbsCbs('cst200'); // null (não é uma forma documentada) Valida um cClassTrib (Código de Classificação Tributária do IBS e da CBS) contra a tabela oficial, o código que o campo `cClassTrib` leva ao lado do CST-IBS/CBS. -- **Opções** (`IsValidClassTribOptions`): `cst` é o CST-IBS/CBS que o documento leva, validado também contra a classificação. Omita-o para validar só o cClassTrib. +- **Opções** (`IsValidClassTribOptions`): `cst` é o CST-IBS/CBS que o documento leva, validado também contra a classificação. Omita-o para validar só o cClassTrib; um `options` `null` ou que não seja objeto é lido como ausente. - Toda classificação pertence a exatamente um CST-IBS/CBS, os 3 primeiros dígitos do seu código, e um documento que leva um cClassTrib com outro CST é rejeitado (rejeição 1024, "Classificação Tributária do IBS e da CBS incompatível com o CST informado"). Um `cst` informado que não seja o CST da classificação, seja ele qual for, torna o resultado `false`. - Só contam as classificações vigentes: o Informe Técnico 2025.002 exclui uma classificação encerrando sua vigência (`dFimVig`), como a v.1.60 fez com `220001`, `220002` e `220003`, e essas são rejeitadas. São 161 vigentes na versão publicada em 23/06/2026. - Aceita uma string de dígitos puros, com espaços opcionais nas extremidades, ou um inteiro seguro não negativo. O campo é numérico com 6 dígitos e não tem máscara, então qualquer outra string é rejeitada. @@ -3582,10 +3711,10 @@ Fonte: [tabelas da CID-10 V2008 que o DATASUS publica em CSV](http://www2.datasu Transforma em maiúscula a primeira letra de cada palavra, do jeito que se escreve um nome, uma razão social ou um endereço brasileiro, sem precisar de opções. -- **Opções** (`CapitalizeOptions`): `lowerCaseWords`, palavras mantidas em minúsculas entre duas palavras, por padrão preposições e artigos como `de`, `da`, `do`, `e`; `upperCaseWords`, palavras sempre em maiúsculas, por padrão designações societárias e abreviações como `LTDA`, `S.A.`, `ME`, `CNPJ` e algarismos romanos. Uma lista substitui a padrão. +- **Opções** (`CapitalizeOptions`): `lowerCaseWords`, palavras mantidas em minúsculas entre duas palavras, por padrão as preposições e a conjunção `e`, como `de`, `da`, `do`, `ao`, `para`, `pelo`, `sobre`, `até` (os artigos são só `a` e `o`); `upperCaseWords`, palavras sempre em maiúsculas, por padrão designações societárias e abreviações como `LTDA`, `S.A.`, `ME`, `CNPJ` e algarismos romanos. Uma lista substitui a padrão. - Palavras se separam em espaços, `-`, `/`, apóstrofos e pontuação colada; espaços repetidos viram um só. - Palavra minúscula que é a primeira, a última ou precede pontuação é designativo e mantém a maiúscula. -- `ME` só vira maiúsculas como designação (última palavra ou antes de outra); `SA` sem pontos fica como está (o sobrenome Sá). Sigla de estado após `/` vira maiúsculas mesmo com `upperCaseWords` informado, assim como a que termina o valor depois de `-` ou `–` entre espaços ou de `, `, o "Cidade – UF" dos Correios. +- `ME` só vira maiúsculas como designação (última palavra ou antes de outra); `S.A` sem o ponto final também é designação, já `SA` sem pontos fica como está (o sobrenome Sá). Sigla de estado após `/` vira maiúsculas mesmo com `upperCaseWords` informado, assim como a que termina o valor depois de `-` ou `–` entre espaços ou de `, `, o "Cidade – UF" dos Correios. ```javascript import { capitalize } from '@brazilian-utils/brazilian-utils'; @@ -3603,6 +3732,8 @@ capitalize('fulano comércio me'); // Fulano Comércio ME ("ME" como última pal capitalize('não-me-toque'); // Não-Me-Toque (em qualquer outro lugar "me" é palavra comum) capitalize('(empresa) ltda'); // (Empresa) LTDA capitalize('luiz von schmidt'); // Luiz von Schmidt +capitalize('casa para todos'); // Casa para Todos (preposições contraídas como "ao", "às", "pelo" e "sobre" também ficam minúsculas) +capitalize('empresa s.a'); // Empresa S.A capitalize('santana/rs'); // Santana/RS ("RS" é sigla de estado logo depois de uma "/") capitalize('porto alegre/rs'); // Porto Alegre/RS capitalize('brasília - df'); // Brasília - DF (sigla de estado como última palavra depois de " - ", " – " ou ", ") @@ -3653,6 +3784,7 @@ Valida uma inscrição estadual para um estado. **Descontinuada:** a forma posic - AM: a regra do dígito verificador da página tem dois ramos e não define "Resto"; a biblioteca o lê como a soma módulo 11 e dá 0 a uma soma 0 ou 1, a regra comum de módulo 11. - MG: um primeiro dígito verificador 10, de uma soma que já é múltiplo de dez, é lido como 0. - PE: o formato eFisco de 9 dígitos e o antigo formato CACEPE de 14 dígitos, ambos na página do SINTEGRA (a Portaria SF nº 087/2007 converteu os números antigos, mas não fixou data a partir da qual deixam de valer). + - RO: o formato de 14 dígitos que a página do SINTEGRA dá para as inscrições desde 01/08/2000. O formato de 9 dígitos que ela ainda imprime para a fórmula anterior (`101.62521-3`) é rejeitado: esses números foram convertidos para 13 dígitos mais o dígito verificador (`0000000062521-3`). - AL: o terceiro dígito, o tipo de empresa, deve ser 0, 3, 5, 7 ou 8, os valores que a página do SINTEGRA lista. - Uma inscrição só de zeros é aceita em todo estado cuja fórmula publicada produz dígito verificador 0 para ela: AM, CE, ES, MG, PB, PE (9 dígitos), PI, PR, RJ, RS, SC, SE e SP, mais BA com 8 ou 9 dígitos, MT com 9 ou 11 dígitos e TO com 9 dígitos. @@ -3676,14 +3808,17 @@ Fonte: [páginas dos estados no SINTEGRA](http://www.sintegra.gov.br/insc_est.ht Valida um endereço de e-mail. Um subconjunto prático da definição do HTML da WHATWG. -- Parte local: letras, dígitos e `_'+-.`, sem ponto no início ou no fim e sem dois pontos seguidos. -- Domínio: pelo menos um ponto, rótulos de até 63 caracteres, rótulo final de 2 a 63 letras; partes locais entre aspas e literais de endereço são rejeitados. +- Parte local: letras, dígitos e `_'+-.`, sem ponto no início ou no fim, sem apóstrofo no fim e sem dois pontos seguidos, com no máximo 64 caracteres. O endereço todo é limitado a 254 caracteres (RFC 5321, seção 4.5.3.1); até a 2.4.0 não havia limite em nenhum dos dois. +- Domínio: pelo menos um ponto, rótulos de até 63 caracteres, rótulo final de 2 a 63 letras ou um rótulo punycode (`xn--`, até 63 caracteres, então `user@example.xn--p1ai` é válido). +- Rejeitados, embora a WHATWG permita alguns: partes locais entre aspas, literais de endereço, domínios de um só rótulo como `user@localhost`, e os caracteres `! # $ % & * = ? ^ { | } ~`, a barra e a crase na parte local. ```javascript import { isValidEmail } from '@brazilian-utils/brazilian-utils'; isValidEmail('john.doe@hotmail.com'); // true isValidEmail('invalid.email'); // false +isValidEmail('user@example.xn--p1ai'); // true (domínio de topo punycode) +isValidEmail('a%b@example.com'); // false (% fica fora do conjunto aceito na parte local) ``` Fonte: [HTML da WHATWG, valid e-mail address](https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address) e [RFC 5322](https://www.rfc-editor.org/rfc/rfc5322). @@ -3719,6 +3854,8 @@ Fonte: [ISO/IEC 7812-1](https://www.iso.org/standard/70484.html). Verifica a estrutura de um número de registro em conselho profissional (registro/inscrição profissional). Só a quantidade de dígitos e a UF são conferidas, nunca o dígito verificador, nem no CRC. +- Os separadores da máscara (espaço, `.`, `-` e `/`) são ignorados em qualquer posição; qualquer outro caractere (`@`, um emoji) invalida o valor em vez de ser removido. + - Recebe um objeto (`IsValidRegistroProfissionalParams`): `value`, `council` (`RegistroProfissionalCouncil`: `"OAB"`, `"CRM"`, `"CRO"`, `"CRP"` ou `"CRC"`) e `stateCode` opcional (UF esperada, sem diferenciar maiúsculas/minúsculas e ignorando espaços nas pontas). - `"OAB"` e `"CRM"`: 4 a 6 dígitos mais a UF (`123456/SP`, `123456-SP`); `"CRO"`: 3 a 6 dígitos (`12345/SP`), ou a forma da Consolidação das Normas do CFO (Resolução CFO-63/2005), art. 115, § 1º: a sigla do Conselho Regional antes, ligada por hífen à categoria (`TPD`, `TSB`, `ASB`, `APD`, `CLM`/`CLF`, `LPM`/`LPF`, `PV`, `T`) quando houver, depois o número, seguido de `-IS` na secundária ou `-R` na remida (`CRO-SP 12345`, `CRO-SP-TPD 1234`, `CRO-SP 12345-IS`). Até a 2.4.0 essa forma era rejeitada. - `"CRP"`: código regional de 2 dígitos (`01` a `24`) mais 4 a 6 dígitos (`06/12345`); `stateCode` é ignorado. O sistema CFP tem 24 regionais; o CRP-25 (Amapá) é só uma proposta. @@ -3731,6 +3868,7 @@ import { isValidRegistroProfissional } from '@brazilian-utils/brazilian-utils'; isValidRegistroProfissional({ value: '123456/SP', council: 'OAB' }); // true isValidRegistroProfissional({ value: '123456-RJ', council: 'OAB', stateCode: 'SP' }); // false (UF divergente) isValidRegistroProfissional({ value: '123456', council: 'OAB' }); // false (sem UF) +isValidRegistroProfissional({ value: '12@3456/SP', council: 'OAB' }); // false (um caractere fora da máscara) isValidRegistroProfissional({ value: 'CRO-SP-TPD 1234', council: 'CRO' }); // true (art. 115 das normas do CFO) isValidRegistroProfissional({ value: '06/12345', council: 'CRP' }); // true isValidRegistroProfissional({ value: 'SP-123456/O-3', council: 'CRC' }); // true @@ -3749,7 +3887,7 @@ Valida um VIN (Vehicle Identification Number / chassi). Por padrão confere 17 c - A exclusão de `I`, `O` e `Q` vem da ISO 3779, não da resolução: ela não lista caractere proibido e remete a gravação à ABNT NBR 6066:2022 (art. 5º), norma paga sem cópia oficial gratuita. Os VINs de regularização do Anexo II dela (WMI `XXX`) são escritos sem essas letras, então passam. - **Opções** (`IsValidVinOptions`): `checkDigit: true` também exige as regras norte-americanas do 49 CFR 565.15, o dígito verificador na 9ª posição e um código de ano-modelo diferente de `U`, `Z` e `0` na 10ª. Use para o VIN de um veículo fabricado para os Estados Unidos ou o Canadá. - As normas brasileiras não exigem o dígito verificador, e muitos VINs fabricados no Brasil não o têm. Até a 2.4.0 ele era sempre exigido; passe `{ checkDigit: true }` para manter esse comportamento. -- Não diferencia maiúsculas de minúsculas e ignora espaços ao redor; um valor de um único caractere repetido é rejeitado. +- Não diferencia maiúsculas de minúsculas e ignora espaços ao redor; um valor de um único caractere repetido é rejeitado. Um VIN é impresso em uma sequência contínua, então espaço, `.`, `-` ou `/` entre os caracteres é rejeitado em vez de removido. Só contam letras ASCII e algarismos: uma letra não ASCII que vira letra ASCII em maiúsculas (`ſ`, `ß`) é rejeitada. ```javascript import { isValidVin } from '@brazilian-utils/brazilian-utils'; @@ -3762,6 +3900,7 @@ isValidVin('1HGCM82633A004353', { checkDigit: true }); // false (dígito verific isValidVin('00000000000000000'); // false (todos os caracteres iguais) isValidVin('1HGCM8263IA004352'); // false (contém a letra excluída I) isValidVin('1HGCM82633A00435'); // false (16 caracteres) +isValidVin('1HGCM 82633 A004352'); // false (um separador entre os caracteres) ``` Fonte: [ISO 3779:2009](https://www.iso.org/standard/52200.html), [49 CFR 565.15](https://www.ecfr.gov/current/title-49/section-565.15) e [Resolução CONTRAN nº 968/2022](https://www.gov.br/transportes/pt-br/assuntos/transito/conteudo-contran/resolucoes/resolucao9682022.pdf). @@ -3772,8 +3911,8 @@ Fonte: [ISO 3779:2009](https://www.iso.org/standard/52200.html), [49 CFR 565.15] Embrulha um utilitário `isValid*` em um [Standard Schema](https://standardschema.dev), o formato de validador que bibliotecas de formulário, roteadores e frameworks de API aceitam: TanStack Form, react-hook-form, tRPC, Hono e outros. -- `config.options` é repassado ao validador a cada chamada, e `config.message` é a mensagem da issue (padrão `'Invalid value'`). Os dois fazem parte de `ToStandardSchemaOptions`. -- Valida de forma síncrona e não transforma: um valor válido volta como foi passado, um inválido gera uma única issue. +- O segundo argumento (`ToStandardSchemaOptions`) recebe `options`, repassado ao validador a cada chamada, e `message`, a mensagem da issue (padrão `'Invalid value'`). +- Valida de forma síncrona e não transforma: um valor válido volta como foi passado, um inválido gera uma única issue, e um validador que lança um erro conta como inválido. - Validadores que recebem um objeto (`isValidBankAccount`, `isValidRegistroProfissional`, `isValidIe`) funcionam do mesmo jeito. Embrulhe o `isValidIe`, que tem sobrecarga, em uma arrow function: `toStandardSchema((params) => isValidIe(params))`. - Os tipos da especificação (`StandardSchemaV1`, `StandardSchemaV1Result`, `StandardSchemaV1Issue` e os demais) também são exportados, então nada mais é instalado. diff --git a/docs/snippets/address-form/vanilla/address-form.html b/docs/snippets/address-form/vanilla/address-form.html index d3ff5dfc0..687117861 100644 --- a/docs/snippets/address-form/vanilla/address-form.html +++ b/docs/snippets/address-form/vanilla/address-form.html @@ -62,6 +62,7 @@ // Asking before the CEP is complete is asking for nothing. if (!isValidCep(cep.value)) { status.textContent = ""; + cep.removeAttribute("aria-busy"); return; } diff --git a/docs/snippets/document-field/templates/schema/standard.ts.tmpl b/docs/snippets/document-field/templates/schema/standard.ts.tmpl index 6aec15946..7736c375d 100644 --- a/docs/snippets/document-field/templates/schema/standard.ts.tmpl +++ b/docs/snippets/document-field/templates/schema/standard.ts.tmpl @@ -14,7 +14,7 @@ export const @@kind@@Schema = toStandardSchema(isValid, { // Everything that speaks the interface takes it as it is, next to a Zod, Valibot or ArkType // schema. A few of them, all with the same @@kind@@Schema: -/** VeeValidate: the rules of a field. */ +/** VeeValidate 5 (beta) or later: the rules of a field. Version 4 does not take a Standard Schema here. */ export const use@@Name@@Field = () => useField("@@kind@@", @@kind@@Schema); /** TanStack Form: a field's validator, `validators={{ onChange: @@kind@@Schema }}`. */ @@ -30,8 +30,11 @@ export const @@kind@@Procedure = initTRPC .procedure.input(@@kind@@Schema) .query(({ input }) => input); -/** Hono: what a route takes. */ -export const @@kind@@Route = sValidator("param", @@kind@@Schema); +/** Hono: what a route takes. The JSON body is an object, so the schema is one of an object. */ +export const @@kind@@Route = sValidator( + "json", + toStandardSchema<{ @@kind@@: string }>(({ @@kind@@ }) => @@validatorContext@@), +); // react-hook-form takes one through `standardSchemaResolver`, once the schema covers the whole // form: `useForm({ resolver: standardSchemaResolver(signupSchema) })`. diff --git a/docs/snippets/live/run.js b/docs/snippets/live/run.js index 3416f144f..e14b017d2 100644 --- a/docs/snippets/live/run.js +++ b/docs/snippets/live/run.js @@ -17,7 +17,7 @@ imports: { "@brazilian-utils/brazilian-utils": CDN + "@brazilian-utils/brazilian-utils/+esm", "@brazilian-utils/brazilian-utils/get-states": CDN + "@brazilian-utils/brazilian-utils/get-states/+esm", - "@brazilian-utils/brazilian-utils/get-cities": CDN + "@brazilian-utils/brazilian-utils/get-cities/+esm", + "@brazilian-utils/brazilian-utils/get-municipalities": CDN + "@brazilian-utils/brazilian-utils/get-municipalities/+esm", // esm.sh, not jsDelivr, for React: jsDelivr's react-dom imports its own copy of react. react: "https://esm.sh/react@19.3.0", "react/jsx-runtime": "https://esm.sh/react@19.3.0/jsx-runtime", diff --git a/docs/snippets/state-city/angular/cities-of-state.ts b/docs/snippets/state-city/angular/cities-of-state.ts index 80cacccce..9cfd86d0b 100644 --- a/docs/snippets/state-city/angular/cities-of-state.ts +++ b/docs/snippets/state-city/angular/cities-of-state.ts @@ -2,8 +2,8 @@ import { resource, type Signal } from "@angular/core"; import type { StateCode } from "@brazilian-utils/brazilian-utils"; /** - * The cities of a state, fetched the first time that state's select is opened. The table is - * 154 KB, so nothing is fetched until someone means to pick a city. What it is about is the state + * The municipalities of a state, fetched the first time that state's select is opened. The table is + * 148 KB, so nothing is fetched until someone means to pick a city. What it is about is the state * whose cities were asked for, so picking another state puts the resource back to waiting. * * A resource aborts a load it no longer wants and drops its answer, which is what the `abortSignal` @@ -14,9 +14,11 @@ export function citiesOfState(asked: Signal) { return resource({ params: () => asked() || undefined, loader: async ({ params }) => { - const { getCities } = await import("@brazilian-utils/brazilian-utils/get-cities"); + const { getMunicipalities } = await import( + "@brazilian-utils/brazilian-utils/get-municipalities" + ); - return getCities(params as StateCode); + return getMunicipalities(params as StateCode); }, }); } diff --git a/docs/snippets/state-city/angular/state-city.ts b/docs/snippets/state-city/angular/state-city.ts index 62c2ee5e3..e99d0a8f1 100644 --- a/docs/snippets/state-city/angular/state-city.ts +++ b/docs/snippets/state-city/angular/state-city.ts @@ -29,8 +29,8 @@ import { states } from "./states"; (focus)="askedForCities.set(state())" > - @for (city of cityList(); track city) { - + @for (city of cityList(); track city.code) { + } `, diff --git a/docs/snippets/state-city/react/state-city.tsx b/docs/snippets/state-city/react/state-city.tsx index b979c5f2b..2f9e1d453 100644 --- a/docs/snippets/state-city/react/state-city.tsx +++ b/docs/snippets/state-city/react/state-city.tsx @@ -31,8 +31,8 @@ export function StateCity() { diff --git a/docs/snippets/state-city/react/use-cities-of-state.ts b/docs/snippets/state-city/react/use-cities-of-state.ts index a859c6056..0f8e36861 100644 --- a/docs/snippets/state-city/react/use-cities-of-state.ts +++ b/docs/snippets/state-city/react/use-cities-of-state.ts @@ -1,15 +1,17 @@ import { useEffect, useRef, useState } from "react"; -import type { StateCode } from "@brazilian-utils/brazilian-utils"; +import type { Municipality, StateCode } from "@brazilian-utils/brazilian-utils"; /** - * The cities of a state, fetched the first time that state's select is opened. The table is - * 154 KB, so nothing is fetched until someone means to pick a city, and the browser keeps the + * The municipalities of a state, fetched the first time that state's select is opened. The table is + * 148 KB, so nothing is fetched until someone means to pick a city, and the browser keeps the * module once it has it. `import()` takes no signal, so the module is not stopped, only what is * done with it: a table that arrives for a state that is no longer picked, or after the component - * is gone, is dropped. + * is gone, is dropped. The abort also forgets which state was asked for, so picking a state again + * after leaving it while its table was on its way asks again instead of waiting for an answer that + * was dropped. */ export function useCitiesOfState(state: string) { - const [loaded, setLoaded] = useState({ state: "", cities: [] as string[] }); + const [loaded, setLoaded] = useState({ state: "", cities: [] as Municipality[] }); const [asked, setAsked] = useState(""); const pending = useRef(null); @@ -19,7 +21,13 @@ export function useCitiesOfState(state: string) { const loading = state !== "" && asked === state && cities.length === 0; // What is on its way is dropped when another state is picked and when the component goes. - useEffect(() => () => pending.current?.abort(), [state]); + useEffect( + () => () => { + pending.current?.abort(); + setAsked(""); + }, + [state], + ); const load = async () => { if (!state || loading || cities.length > 0) return; @@ -29,11 +37,13 @@ export function useCitiesOfState(state: string) { pending.current = controller; setAsked(state); - const { getCities } = await import("@brazilian-utils/brazilian-utils/get-cities"); + const { getMunicipalities } = await import( + "@brazilian-utils/brazilian-utils/get-municipalities" + ); if (controller.signal.aborted) return; - setLoaded({ state, cities: getCities(state as StateCode) }); + setLoaded({ state, cities: getMunicipalities(state as StateCode) }); }; return { cities, loading, load }; diff --git a/docs/snippets/state-city/vanilla/state-city.html b/docs/snippets/state-city/vanilla/state-city.html index 3eb0f2c5c..9047124ea 100644 --- a/docs/snippets/state-city/vanilla/state-city.html +++ b/docs/snippets/state-city/vanilla/state-city.html @@ -15,7 +15,7 @@