Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ itself:
`ncm.ts`). When a run changes a file, the workflow opens a pull request whose description, written
by `scripts/data-summary.ts`, lists per table how many entries were added and removed, with a
sample of each. Never edit these files by hand.
- **Generated from a fetchable mirror of an official source**, the same way, but pinned to a
specific revision of the mirror because the official source itself cannot be fetched by a
script: the CEP range of each municipality (Correios "Busca Faixa de CEP", via a community CSV
mirror of its answers, `municipality-cep-ranges.ts`, writing `src/get-municipality-by-cep/constants.ts`).
Cross-checked against the official Correios "Localidades" list before shipping; see the util's
`@see` tags.
- **Maintained by hand**, because the source is a law or a regulation with no machine-readable
form: area codes and their states (Anatel, `area-codes.ts`), service phone prefixes (Anatel,
`service-phone.ts`), national and state holidays (`holidays.ts`), the órgãos and tribunals of the
Expand Down
3 changes: 2 additions & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ A few utils embed an official dataset and weigh far more than everything else co
| Util | Dataset | Minified | Gzipped |
| --- | --- | --- | --- |
| `getMunicipalities` · `getMunicipalityByCode` · `getMunicipality` | 5571 IBGE municipalities, with names and codes | 154.9 - 156.5 KB | 50.3 - 50.4 KB |
| `getCities` | 5571 IBGE municipality names | 154.2 KB | 49.8 KB |
| `getCities` | 5571 IBGE municipality names | 154.3 KB | 49.9 KB |
| `getMunicipalityByCep` | The IBGE municipality table above, plus 5574 Correios CEP ranges | 393.3 KB | 98.8 KB |
| `isValidNcm` | NCM (Nomenclatura Comum do Mercosul) codes | 114.2 KB | 24.6 KB |
| `isValidCbo` · `getCbo` | CBO 2002 occupation titles | 119.1 KB | 30.6 KB |
| `isValidCnae` · `getCnae` | CNAE-Subclasses 2.3 | 93.9 KB | 21.2 KB |
Expand Down
3 changes: 2 additions & 1 deletion docs/pt-br/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ Alguns utilitários embutem uma base de dados oficial e pesam muito mais que tod
| Utilitário | Base de dados | Minificado | Gzip |
| --- | --- | --- | --- |
| `getMunicipalities` · `getMunicipalityByCode` · `getMunicipality` | 5571 municípios do IBGE, com nomes e códigos | 154,9 - 156,5 KB | 50,3 - 50,4 KB |
| `getCities` | nomes dos 5571 municípios do IBGE | 154,2 KB | 49,8 KB |
| `getCities` | nomes dos 5571 municípios do IBGE | 154,3 KB | 49,9 KB |
| `getMunicipalityByCep` | a tabela de municípios do IBGE acima, mais 5574 faixas de CEP dos Correios | 393,3 KB | 98,8 KB |
| `isValidNcm` | códigos NCM (Nomenclatura Comum do Mercosul) | 114,2 KB | 24,6 KB |
| `isValidCbo` · `getCbo` | títulos das ocupações da CBO 2002 | 119,1 KB | 30,6 KB |
| `isValidCnae` · `getCnae` | CNAE-Subclasses 2.3 | 93,9 KB | 21,2 KB |
Expand Down
57 changes: 54 additions & 3 deletions docs/pt-br/utilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -1446,7 +1446,7 @@ convertDateToWords('invalid'); // ""
convertDateToWords('29/02/1900'); // "" (1900 não é bissexto)
```

## Estados e municípios
## Estados e municípios (cidades)

### getStates

Expand Down Expand Up @@ -1492,6 +1492,33 @@ getStates();

Fonte: [IBGE Localidades](https://servicodados.ibge.gov.br/api/docs/localidades)

### getStateByCep

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.
- 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`.

```javascript
import { getStateByCep } from '@brazilian-utils/brazilian-utils';

getStateByCep('01310-100');
// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35 }

getStateByCep(20040020);
// { code: 'RJ', name: 'Rio de Janeiro', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 33 }

getStateByCep('69300-000')?.code; // 'RR'
getStateByCep('72800-000')?.code; // 'GO'
getStateByCep('00999-999'); // null
getStateByCep('12345'); // null
```

Fonte: [Correios, Busca Faixa de CEP](https://buscacepinter.correios.com.br/app/faixa_cep_uf_localidade/index.php)

### getStateByIbgeCode

Retorna o estado brasileiro cujo código IBGE de 2 dígitos (`cUF`, o Código da Unidade da Federação) corresponde ao valor informado.
Expand Down Expand Up @@ -1569,7 +1596,7 @@ Fonte: [IANA Time Zone Database](https://www.iana.org/time-zones)

### getMunicipalities

Retorna os municípios brasileiros publicados pelo IBGE: todos os municípios, ou só os de um estado quando `stateCode` é informado.
Retorna os municípios (cidades) brasileiros publicados pelo IBGE: todos os municípios, ou só os de um estado quando `stateCode` é informado.

- Cada município (`Municipality`) é `{ code, name, stateCode }`, onde `code` é o código IBGE de 7 dígitos. Ordenados por nome no locale "pt-BR".
- Só um `stateCode` omitido (ou `undefined`) pede a lista completa: `null` e `''` retornam `[]`.
Expand Down Expand Up @@ -1608,7 +1635,7 @@ Fonte: [IBGE Localidades](https://servicodados.ibge.gov.br/api/docs/localidades)

### getMunicipalityByCode

Busca um município brasileiro pelo código IBGE de 7 dígitos.
Busca um município (cidade) brasileiro pelo código IBGE de 7 dígitos.

- Aceita o código como string ou número inteiro não negativo.
- Retorna `{ code, name, stateCode }` (`Municipality`), ou `null` quando o código não tem 7 dígitos ou não corresponde a nenhum município.
Expand All @@ -1628,6 +1655,30 @@ getMunicipalityByCode('123'); // null (não tem 7 dígitos)

Fonte: [IBGE Localidades](https://servicodados.ibge.gov.br/api/docs/localidades)

### getMunicipalityByCep

Retorna o município (cidade) brasileiro ao qual um CEP pertence, a partir das faixas de CEP que os Correios atribuem a cada município.

- Funciona offline: nenhuma API de CEP é chamada, então a resposta diz qual município é 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.
- Retorna `{ code, name, stateCode }` (`Municipality`, o mesmo formato que `getMunicipalityByCode` retorna), ou `null` para um CEP inválido ou fora de todas as faixas.
- Alguns municípios foram absorvidos pela faixa de outro, ou não têm faixa própria; um estado que `getStateByCep` resolve ainda pode deixar `getMunicipalityByCep` em `null`.

```javascript
import { getMunicipalityByCep } from '@brazilian-utils/brazilian-utils';

getMunicipalityByCep('01310-100');
// { code: '3550308', name: 'São Paulo', stateCode: 'SP' }

getMunicipalityByCep(20040020);
// { code: '3304557', name: 'Rio de Janeiro', stateCode: 'RJ' }

getMunicipalityByCep('00999-999'); // null
getMunicipalityByCep('12345'); // null
```

Fonte: [Correios, Busca Faixa de CEP](https://buscacepinter.correios.com.br/app/faixa_cep_uf_localidade/index.php)

### getCities

Retorna os nomes das cidades brasileiras: todas as cidades, ou só as de um estado. **Descontinuada:** use `getMunicipalities` no lugar.
Expand Down
57 changes: 54 additions & 3 deletions docs/utilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -1446,7 +1446,7 @@ convertDateToWords('invalid'); // ""
convertDateToWords('29/02/1900'); // "" (1900 is not a leap year)
```

## States and municipalities
## States and municipalities (cities)

### getStates

Expand Down Expand Up @@ -1492,6 +1492,33 @@ getStates();

Source: [IBGE Localidades](https://servicodados.ibge.gov.br/api/docs/localidades)

### getStateByCep

Get the Brazilian state a CEP belongs to, from the CEP ranges the Correios assign to each state (the "Faixa de CEP" of each UF).

- It runs offline: no CEP API is called, so the answer says which state owns the range, not whether the CEP is in use.
- Accepts what `isValidCep` accepts: 8 digits, as a string or a number, with spaces, dots and hyphens ignored. A CEP that starts with `0` has to be a string, and a negative or fractional number is rejected.
- Amazonas, Distrito Federal and Goiás have two ranges each, and no state range covers `00000-000` to `00999-999` nor `78900-000` to `78999-999`.
- A range is the block the state owns, not a promise that every CEP in it is in use: `10000-000` sits unused inside the São Paulo range and still answers São Paulo.
- Returns `null` for an invalid CEP or one outside every range. Exports the `State` type.

```javascript
import { getStateByCep } from '@brazilian-utils/brazilian-utils';

getStateByCep('01310-100');
// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35 }

getStateByCep(20040020);
// { code: 'RJ', name: 'Rio de Janeiro', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 33 }

getStateByCep('69300-000')?.code; // 'RR'
getStateByCep('72800-000')?.code; // 'GO'
getStateByCep('00999-999'); // null
getStateByCep('12345'); // null
```

Source: [Correios, Busca Faixa de CEP](https://buscacepinter.correios.com.br/app/faixa_cep_uf_localidade/index.php)

### getStateByIbgeCode

Get the Brazilian state whose 2-digit IBGE code (`cUF`, the Código da Unidade da Federação) matches the given value.
Expand Down Expand Up @@ -1569,7 +1596,7 @@ Source: [IANA Time Zone Database](https://www.iana.org/time-zones)

### getMunicipalities

Get the Brazilian municipalities published by the IBGE: every municipality, or only those of one state when `stateCode` is given.
Get the Brazilian municipalities (cities) published by the IBGE: every municipality, or only those of one state when `stateCode` is given.

- Each municipality (`Municipality`) is `{ code, name, stateCode }`, where `code` is the 7-digit IBGE code. Sorted by name in the "pt-BR" locale.
- Only an omitted (or `undefined`) `stateCode` asks for the full list: `null` and `''` return `[]`.
Expand Down Expand Up @@ -1608,7 +1635,7 @@ Source: [IBGE Localidades](https://servicodados.ibge.gov.br/api/docs/localidades

### getMunicipalityByCode

Look up a Brazilian municipality by its 7-digit IBGE code.
Look up a Brazilian municipality (city) by its 7-digit IBGE code.

- Accepts the code as a string or a non-negative integer.
- Returns `{ code, name, stateCode }` (`Municipality`), or `null` when the code is not 7 digits long or matches no municipality.
Expand All @@ -1628,6 +1655,30 @@ getMunicipalityByCode('123'); // null (not 7 digits)

Source: [IBGE Localidades](https://servicodados.ibge.gov.br/api/docs/localidades)

### getMunicipalityByCep

Get the Brazilian municipality (city) a CEP belongs to, from the CEP ranges the Correios assign to each municipality.

- It runs offline: no CEP API is called, so the answer says which municipality owns the range, not whether the CEP is in use.
- Accepts what `isValidCep` accepts: 8 digits, as a string or a number, with spaces, dots and hyphens ignored. A CEP that starts with `0` has to be a string, and a negative or fractional number is rejected.
- Returns `{ code, name, stateCode }` (`Municipality`, the same shape `getMunicipalityByCode` returns), or `null` for an invalid CEP or one outside every range.
- Some municipalities were absorbed into another one's range, or have no dedicated range at all; a state `getStateByCep` resolves can still leave `getMunicipalityByCep` at `null`.

```javascript
import { getMunicipalityByCep } from '@brazilian-utils/brazilian-utils';

getMunicipalityByCep('01310-100');
// { code: '3550308', name: 'São Paulo', stateCode: 'SP' }

getMunicipalityByCep(20040020);
// { code: '3304557', name: 'Rio de Janeiro', stateCode: 'RJ' }

getMunicipalityByCep('00999-999'); // null
getMunicipalityByCep('12345'); // null
```

Source: [Correios, Busca Faixa de CEP](https://buscacepinter.correios.com.br/app/faixa_cep_uf_localidade/index.php)

### getCities

Get the names of Brazilian cities: every city, or only those of one state. **Deprecated:** use `getMunicipalities` instead.
Expand Down
2 changes: 2 additions & 0 deletions jsr.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,12 @@
"./get-legal-natures-by-category": "./src/get-legal-natures-by-category/get-legal-natures-by-category.ts",
"./get-municipalities": "./src/get-municipalities/get-municipalities.ts",
"./get-municipality": "./src/get-municipality/get-municipality.ts",
"./get-municipality-by-cep": "./src/get-municipality-by-cep/get-municipality-by-cep.ts",
"./get-municipality-by-code": "./src/get-municipality-by-code/get-municipality-by-code.ts",
"./get-nfe-key-info": "./src/get-nfe-key-info/get-nfe-key-info.ts",
"./get-pix-key-info": "./src/get-pix-key-info/get-pix-key-info.ts",
"./get-pix-payload-info": "./src/get-pix-payload-info/get-pix-payload-info.ts",
"./get-state-by-cep": "./src/get-state-by-cep/get-state-by-cep.ts",
"./get-state-by-ibge-code": "./src/get-state-by-ibge-code/get-state-by-ibge-code.ts",
"./get-state-code-by-name": "./src/get-state-code-by-name/get-state-code-by-name.ts",
"./get-state-name-by-code": "./src/get-state-name-by-code/get-state-name-by-code.ts",
Expand Down
6 changes: 6 additions & 0 deletions reports/api/brazilian-utils.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,9 @@ export function getMunicipality(options: GetMunicipalityByNameParams): Promise<s
// @public @deprecated
export function getMunicipality(options: GetMunicipalityParams): Promise<[string, string] | string | null>;

// @public
export const getMunicipalityByCep: (value: string | number) => Municipality | null;

// @public
export const getMunicipalityByCode: (code: string | number) => Municipality | null;

Expand Down Expand Up @@ -611,6 +614,9 @@ export const getPixKeyInfo: (value: string) => PixKeyInfo | null;
// @public
export const getPixPayloadInfo: (value: string) => PixPayloadInfo | null;

// @public
export const getStateByCep: (value: string | number) => State | null;

// @public
export const getStateByIbgeCode: (code: string | number) => State | null;

Expand Down
67 changes: 65 additions & 2 deletions scripts/cities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,42 @@ const isCity = (value: unknown): value is City =>
"microrregiao" in value &&
"regiao-imediata" in value;

/**
* Alternative spellings kept for a handful of municipalities whose name changed at the IBGE
* more recently than a third party dataset this library cross-checks against, keyed by the
* 7-digit IBGE code. Curated by hand from the mismatches found joining
* https://gist.githubusercontent.com/hugosenari/ec1a7d88f5bdd01844424dbc9aff9590/raw/9aeb90ef777131ffaf1a6f5381d1f163c9c79b09/ceps.csv
* against the IBGE municipalities below (see `scripts/municipality-cep-ranges.ts`); `main`
* below fails the build if a code here stops matching a real municipality, so a future IBGE
* refresh can never leave a stale entry silently in place.
*/
const OTHER_NAMES: Record<string, readonly string[]> = {
"1400605": ["São Luiz"],
"1502954": ["Eldorado dos Carajás"],
"1708254": ["Fortaleza do Tabocão"],
"2108504": ["Pindaré Mirim"],
"2400208": ["Açu"],
"2401206": ["Ares"],
"2401305": ["Augusto Severo"],
"2405306": ["Boa Saúde"],
"2408409": ["Olho-D'Água do Borges"],
"2606903": ["Iguaraci"],
"2608503": ["Lagoa do Itaenga"],
"2800100": ["Amparo de São Francisco"],
"2922250": ["Muquém de São Francisco"],
"2928505": ["Santa Teresinha"],
"3105509": ["Barão de Monte Alto"],
"3122900": ["Dona Eusébia"],
"3145455": ["Olhos D'Água"],
"3165206": ["São Thomé das Letras"],
"3165560": ["Sem Peixe"],
"3506607": ["Biritiba-Mirim"],
"3516101": ["Florínia"],
"3550001": ["São Luis do Paraitinga"],
"4206108": ["Grão Pará"],
"5107800": ["Santo Antônio do Leverger"],
};

const main = async (): Promise<void> => {
const response = await fetchWithRetry(
"https://servicodados.ibge.gov.br/api/v1/localidades/municipios",
Expand Down Expand Up @@ -104,9 +140,24 @@ const main = async (): Promise<void> => {
throw new Error(`IBGE response is missing municipalities for: ${missingStates.join(", ")}`);
}

const knownCodes = new Set(json.map((city) => String(city.id)));
const staleOtherNames = Object.keys(OTHER_NAMES).filter((code) => !knownCodes.has(code));

if (staleOtherNames.length > 0) {
throw new Error(`OTHER_NAMES has codes IBGE no longer lists: ${staleOtherNames.join(", ")}`);
}

const otherNamesBody = Object.entries(OTHER_NAMES)
.sort(([a], [b]) => a.localeCompare(b))
.map(
([code, names]) =>
`\t${JSON.stringify(code)}: [${names.map((name) => JSON.stringify(name)).join(", ")}],`,
)
.join("\n");

await writeFile(
resolve(scriptsDir, "..", "./src/_internals/constants/cities.ts"),
`import type { StateCode } from "./states";
resolve(scriptsDir, "..", "./src/_internals/constants/municipalities.ts"),
`import { type StateCode } from "./states";

/**
* Brazilian municipalities by state, published by the IBGE. \`DATA\` holds, for each state, a
Expand All @@ -127,6 +178,18 @@ export type Municipality = {
export const DATA: Record<StateCode, readonly (readonly [string, string])[]> = {
${body}
};

/**
* Alternative spellings of a municipality name, keyed by its 7-digit IBGE code, for the
* municipalities a source outside the IBGE still spells differently, usually because of a
* recent official rename. A municipality with no variant is simply absent, at no extra cost.
* Curated by \`scripts/cities.ts\`.
*
* @see Official: https://servicodados.ibge.gov.br/api/docs/localidades
*/
export const OTHER_NAMES: Readonly<Record<string, readonly string[]>> = {
${otherNamesBody}
};
`,
);
};
Expand Down
4 changes: 3 additions & 1 deletion scripts/data-summary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ const DATASETS: Record<string, string> = {
"src/_internals/constants/banks.ts": "Banks (Banco Central, STR participants)",
"src/_internals/constants/cbo.ts": "CBO 2002 occupations (Ministério do Trabalho e Emprego)",
"src/_internals/constants/cfop.ts": "CFOP codes (CONFAZ, Convênio SINIEF s/nº 1970)",
"src/_internals/constants/cities.ts": "Municipalities (IBGE)",
"src/_internals/constants/cnae.ts": "CNAE subclasses (IBGE/CONCLA)",
"src/_internals/constants/municipalities.ts": "Municipalities (IBGE)",
"src/_internals/constants/states.ts": "States (IBGE)",
"src/get-municipality-by-cep/constants.ts":
"Municipality CEP ranges (Correios, via a community mirror)",
"src/is-valid-legal-nature/constants.ts": "Legal natures (IBGE/CONCLA)",
"src/is-valid-ncm/constants.ts": "NCM codes (Siscomex)",
};
Expand Down
2 changes: 1 addition & 1 deletion scripts/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ const generatedFiles = [
"./src/_internals/constants/banks.ts",
"./src/_internals/constants/cbo.ts",
"./src/_internals/constants/cfop.ts",
"./src/_internals/constants/cities.ts",
"./src/_internals/constants/cnae.ts",
"./src/_internals/constants/municipalities.ts",
"./src/_internals/constants/states.ts",
"./src/is-valid-legal-nature/constants.ts",
"./src/is-valid-ncm/constants.ts",
Expand Down
Loading
Loading