From 6e62985f9c783c61a2a42b5d5d84b16823f84d5e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 27 Sep 2026 19:43:17 +0000 Subject: [PATCH 1/5] feat(states): add getStateCapital, getRegions and getStatesByRegion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The state utils carried each state's region code and name, but not its capital, the IBGE identifier of the region, or a way to list the states of a region. getStateCapital returns the capital as the same { code, name, stateCode } that getMunicipalityByCode gives for it, from a 27 entry table of the IBGE Anuário Estatístico (Tabela 1.1.1.2, "Municípios das Capitais - 2025") joined to the IBGE municipality codes; Brasília stands for the Distrito Federal. getRegions returns the five Grandes Regiões with the IBGE identifier the API de Localidades gives them (1 to 5), and getStatesByRegion lists a region's states sorted by name, as getStates does. Both lookups take the code case-insensitively, like getTimezoneByState. The State type is left as it is, so no published shape changes; the two tables are small and maintained by hand, as CONTRIBUTING now lists. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RLkm9YrtAifc6XCLFVEsdH --- CONTRIBUTING.md | 3 +- docs/pt-br/utilities.md | 52 ++++++++++++++ docs/utilities.md | 52 ++++++++++++++ jsr.json | 3 + src/_internals/constants/regions.ts | 31 +++++++++ src/_internals/constants/state-capitals.ts | 44 ++++++++++++ src/get-regions/get-regions.test.ts | 49 ++++++++++++++ src/get-regions/get-regions.ts | 24 +++++++ .../get-state-capital.test.ts | 67 +++++++++++++++++++ src/get-state-capital/get-state-capital.ts | 41 ++++++++++++ .../get-states-by-region.test.ts | 66 ++++++++++++++++++ .../get-states-by-region.ts | 36 ++++++++++ src/index.test.ts | 7 ++ src/index.ts | 3 + 14 files changed, 477 insertions(+), 1 deletion(-) create mode 100644 src/_internals/constants/regions.ts create mode 100644 src/_internals/constants/state-capitals.ts create mode 100644 src/get-regions/get-regions.test.ts create mode 100644 src/get-regions/get-regions.ts create mode 100644 src/get-state-capital/get-state-capital.test.ts create mode 100644 src/get-state-capital/get-state-capital.ts create mode 100644 src/get-states-by-region/get-states-by-region.test.ts create mode 100644 src/get-states-by-region/get-states-by-region.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5f1e0052..cface8c6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -117,7 +117,8 @@ itself: 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 processo number (Resolução CNJ nº 65/2008, `processo-juridico.ts`), IBAN lengths per country - (`iban.ts`), IBGE state codes (`ibge-uf-codes.ts`), legal nature categories, the CST and CSOSN + (`iban.ts`), IBGE state codes (`ibge-uf-codes.ts`), the state capitals and the regions (IBGE, + `state-capitals.ts`, `regions.ts`), legal nature categories, the CST and CSOSN tables (`src/is-valid-cst`, `src/is-valid-csosn`), the professional councils (`src/is-valid-registro-profissional`), the região fiscal digit of each state (`src/_internals/constants/cpf.ts`) and the voter ID state codes (`src/is-valid-voter-id`). A change to one of diff --git a/docs/pt-br/utilities.md b/docs/pt-br/utilities.md index 19f50360..045d6f52 100644 --- a/docs/pt-br/utilities.md +++ b/docs/pt-br/utilities.md @@ -1780,6 +1780,58 @@ getStateNameByCode(' Rj '); // 'Rio de Janeiro' getStateNameByCode('ZZ'); // null ``` +### 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. + +```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('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). + +### getRegions + +Retorna as cinco Grandes Regiões do Brasil, cada uma com o código (o mesmo `regionCode` de cada estado), o nome e o identificador do IBGE, na ordem desse identificador. Exporta os tipos `Region` e `RegionCode`. + +```javascript +import { getRegions } from '@brazilian-utils/brazilian-utils'; + +getRegions(); +// [ +// { code: 'N', name: 'Norte', ibgeCode: 1 }, +// { code: 'NE', name: 'Nordeste', ibgeCode: 2 }, +// { code: 'SE', name: 'Sudeste', ibgeCode: 3 }, +// { code: 'S', name: 'Sul', ibgeCode: 4 }, +// { code: 'CO', name: 'Centro-Oeste', ibgeCode: 5 }, +// ] +``` + +Fonte: [IBGE, API de Localidades, `regioes`](https://servicodados.ibge.gov.br/api/v1/localidades/regioes). + +### getStatesByRegion + +Retorna os estados de uma região, dado o código dela (`'N'`, `'NE'`, `'SE'`, `'S'` ou `'CO'`), em ordem alfabética, como o `getStates` ordena. + +- A busca ignora maiúsculas e minúsculas e os espaços nas pontas. Retorna `[]` quando nenhuma região corresponde. + +```javascript +import { getStatesByRegion } from '@brazilian-utils/brazilian-utils'; + +getStatesByRegion('S').map((state) => state.code); // ['PR', 'RS', 'SC'] +getStatesByRegion('co').map((state) => state.code); // ['DF', 'GO', 'MT', 'MS'] +getStatesByRegion('X'); // [] +``` + +Fonte: [IBGE, API de Localidades, `estados`](https://servicodados.ibge.gov.br/api/v1/localidades/estados). + ### getTimezoneByState Retorna o nome do fuso horário IANA (zona do tzdata) de um estado brasileiro: o fuso da sua capital. diff --git a/docs/utilities.md b/docs/utilities.md index 3391d5a2..fd2b4c6a 100644 --- a/docs/utilities.md +++ b/docs/utilities.md @@ -1780,6 +1780,58 @@ getStateNameByCode(' Rj '); // 'Rio de Janeiro' getStateNameByCode('ZZ'); // null ``` +### getStateCapital + +Get the capital of a Brazilian state, as the same `{ code, name, stateCode }` (`Municipality`) that `getMunicipalityByCode` returns for it. + +- The match ignores case and surrounding whitespace. Returns `null` when no state matches. +- For the Distrito Federal, which has no municipalities, the capital is Brasília, with the code the IBGE gives the whole district. + +```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('ZZ'); // null +``` + +Source: [IBGE, Anuário Estatístico do Brasil, table 1.1.1.2 (state capitals, 2025)](https://anuario.ibge.gov.br/2024/territorio/posicao-e-extensao.html). + +### getRegions + +Get the five Brazilian regions (Grandes Regiões), each with its code (the same `regionCode` every state carries), name and IBGE identifier, in the order of that identifier. Exports the `Region` and `RegionCode` types. + +```javascript +import { getRegions } from '@brazilian-utils/brazilian-utils'; + +getRegions(); +// [ +// { code: 'N', name: 'Norte', ibgeCode: 1 }, +// { code: 'NE', name: 'Nordeste', ibgeCode: 2 }, +// { code: 'SE', name: 'Sudeste', ibgeCode: 3 }, +// { code: 'S', name: 'Sul', ibgeCode: 4 }, +// { code: 'CO', name: 'Centro-Oeste', ibgeCode: 5 }, +// ] +``` + +Source: [IBGE, API de Localidades, `regioes`](https://servicodados.ibge.gov.br/api/v1/localidades/regioes). + +### getStatesByRegion + +Get the states of a region, given its code (`'N'`, `'NE'`, `'SE'`, `'S'` or `'CO'`), sorted by name the way `getStates` sorts them. + +- The match ignores case and surrounding whitespace. Returns `[]` when no region matches. + +```javascript +import { getStatesByRegion } from '@brazilian-utils/brazilian-utils'; + +getStatesByRegion('S').map((state) => state.code); // ['PR', 'RS', 'SC'] +getStatesByRegion('co').map((state) => state.code); // ['DF', 'GO', 'MT', 'MS'] +getStatesByRegion('X'); // [] +``` + +Source: [IBGE, API de Localidades, `estados`](https://servicodados.ibge.gov.br/api/v1/localidades/estados). + ### getTimezoneByState Get the IANA time zone name (tzdata zone) of a Brazilian state: the zone of its capital. diff --git a/jsr.json b/jsr.json index 171c8055..342685c2 100644 --- a/jsr.json +++ b/jsr.json @@ -90,12 +90,15 @@ "./get-nfse-key-info": "./src/get-nfse-key-info/get-nfse-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-regions": "./src/get-regions/get-regions.ts", "./get-service-item": "./src/get-service-item/get-service-item.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-capital": "./src/get-state-capital/get-state-capital.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", "./get-states": "./src/get-states/get-states.ts", + "./get-states-by-region": "./src/get-states-by-region/get-states-by-region.ts", "./get-timezone-by-state": "./src/get-timezone-by-state/get-timezone-by-state.ts", "./is-business-day": "./src/is-business-day/is-business-day.ts", "./is-holiday": "./src/is-holiday/is-holiday.ts", diff --git a/src/_internals/constants/regions.ts b/src/_internals/constants/regions.ts new file mode 100644 index 00000000..74a77eee --- /dev/null +++ b/src/_internals/constants/regions.ts @@ -0,0 +1,31 @@ +/** + * One of the five Brazilian regions (Grandes Regiões), as `getRegions` returns it: the code the + * IBGE abbreviates it with (`code`, the same as `State["regionCode"]`), its name and its IBGE + * identifier (`ibgeCode`). Every region is its own member of the union, so narrowing by `code` + * narrows the other fields too. + */ +export type Region = + | { readonly code: "N"; readonly name: "Norte"; readonly ibgeCode: 1 } + | { readonly code: "NE"; readonly name: "Nordeste"; readonly ibgeCode: 2 } + | { readonly code: "SE"; readonly name: "Sudeste"; readonly ibgeCode: 3 } + | { readonly code: "S"; readonly name: "Sul"; readonly ibgeCode: 4 } + | { readonly code: "CO"; readonly name: "Centro-Oeste"; readonly ibgeCode: 5 }; + +/** The two letter or one letter code of a region, e.g. `"NE"`. */ +export type RegionCode = Region["code"]; + +/** + * The five regions in the order of their IBGE identifier, the first digit of the code of each of + * their states. + * + * @see Official: https://servicodados.ibge.gov.br/api/v1/localidades/regioes + * IBGE, API de Localidades, `regioes`: `[{"id":1,"sigla":"N","nome":"Norte"}, ... , + * {"id":5,"sigla":"CO","nome":"Centro-Oeste"}]`. + */ +export const REGIONS: readonly Region[] = [ + { code: "N", name: "Norte", ibgeCode: 1 }, + { code: "NE", name: "Nordeste", ibgeCode: 2 }, + { code: "SE", name: "Sudeste", ibgeCode: 3 }, + { code: "S", name: "Sul", ibgeCode: 4 }, + { code: "CO", name: "Centro-Oeste", ibgeCode: 5 }, +]; diff --git a/src/_internals/constants/state-capitals.ts b/src/_internals/constants/state-capitals.ts new file mode 100644 index 00000000..fd196593 --- /dev/null +++ b/src/_internals/constants/state-capitals.ts @@ -0,0 +1,44 @@ +import { type StateCode } from "./states"; + +/** + * The capital of each state, as `[name, ibgeCode]`: the municipality name as the IBGE spells it + * and its 7 digit IBGE code, the same pair `getMunicipalityByCode` finds for that code. For the + * Distrito Federal, whose territory is not divided into municipalities, the entry is Brasília, + * the code the IBGE gives the whole district. + * + * @see Official: https://anuario.ibge.gov.br/2024/territorio/posicao-e-extensao.html + * IBGE, Anuário Estatístico do Brasil, Tabela 1.1.1.2, "Localização geográfica, altitude e + * distância a Brasília, segundo os Municípios das Capitais - 2025": one capital per state, + * written "Porto Velho (RO)" to "Brasília (DF)". + * @see Official: https://geoftp.ibge.gov.br/organizacao_do_territorio/estrutura_territorial/divisao_territorial/2025/DTB_2025.zip + * IBGE, Divisão Territorial Brasileira 2025, the municipality codes. + */ +export const STATE_CAPITALS: Record = { + AC: ["Rio Branco", "1200401"], + AL: ["Maceió", "2704302"], + AM: ["Manaus", "1302603"], + AP: ["Macapá", "1600303"], + BA: ["Salvador", "2927408"], + CE: ["Fortaleza", "2304400"], + DF: ["Brasília", "5300108"], + ES: ["Vitória", "3205309"], + GO: ["Goiânia", "5208707"], + MA: ["São Luís", "2111300"], + MG: ["Belo Horizonte", "3106200"], + MS: ["Campo Grande", "5002704"], + MT: ["Cuiabá", "5103403"], + PA: ["Belém", "1501402"], + PB: ["João Pessoa", "2507507"], + PE: ["Recife", "2611606"], + PI: ["Teresina", "2211001"], + PR: ["Curitiba", "4106902"], + RJ: ["Rio de Janeiro", "3304557"], + RN: ["Natal", "2408102"], + RO: ["Porto Velho", "1100205"], + RR: ["Boa Vista", "1400100"], + RS: ["Porto Alegre", "4314902"], + SC: ["Florianópolis", "4205407"], + SE: ["Aracaju", "2800308"], + SP: ["São Paulo", "3550308"], + TO: ["Palmas", "1721000"], +}; diff --git a/src/get-regions/get-regions.test.ts b/src/get-regions/get-regions.test.ts new file mode 100644 index 00000000..26e0de84 --- /dev/null +++ b/src/get-regions/get-regions.test.ts @@ -0,0 +1,49 @@ +import { describe, expect, expectTypeOf, it, test } from "../_internals/test/runtime"; +import { getStates } from "../get-states/get-states"; +import { getRegions, type Region } from "./get-regions"; + +describe("getRegions", () => { + it("should return the five regions in the order of their IBGE identifier", () => { + expect(getRegions()).toEqual([ + { code: "N", name: "Norte", ibgeCode: 1 }, + { code: "NE", name: "Nordeste", ibgeCode: 2 }, + { code: "SE", name: "Sudeste", ibgeCode: 3 }, + { code: "S", name: "Sul", ibgeCode: 4 }, + { code: "CO", name: "Centro-Oeste", ibgeCode: 5 }, + ]); + }); + + it("should match the region code and name every state carries", () => { + const regions = getRegions(); + + for (const state of getStates()) { + expect(regions.find((region) => region.code === state.regionCode)?.name).toBe( + state.regionName, + ); + } + }); + + it("should give each region the first digit of the IBGE code of its states", () => { + const regions = getRegions(); + + for (const state of getStates()) { + expect(regions.find((region) => region.code === state.regionCode)?.ibgeCode).toBe( + Math.floor(state.ibgeCode / 10), + ); + } + }); + + it("should return fresh objects, so mutating one leaves a later call intact", () => { + const [first] = getRegions(); + + if (first !== undefined) Object.assign(first, { name: "MUTATED" }); + + expect(getRegions()[0]?.name).toBe("Norte"); + }); +}); + +describe("getRegions types", () => { + test("should return Region[]", () => { + expectTypeOf(getRegions).returns.toEqualTypeOf(); + }); +}); diff --git a/src/get-regions/get-regions.ts b/src/get-regions/get-regions.ts new file mode 100644 index 00000000..bc441bac --- /dev/null +++ b/src/get-regions/get-regions.ts @@ -0,0 +1,24 @@ +import { REGIONS, type Region } from "../_internals/constants/regions"; + +export type { Region, RegionCode } from "../_internals/constants/regions"; + +/** + * Retrieves the five Brazilian regions (Grandes Regiões) with their code, name and IBGE + * identifier, in the order of that identifier: Norte (1), Nordeste (2), Sudeste (3), Sul (4) and + * Centro-Oeste (5). The `code` is the same `State["regionCode"]` every state carries. + * + * Each call returns a fresh array of fresh objects, so mutating the result never affects the + * underlying data or later calls. + * + * @returns {Region[]} The five regions. + * + * @example + * ```typescript + * getRegions()[0]; // { code: "N", name: "Norte", ibgeCode: 1 } + * getRegions().map((region) => region.code); // ["N", "NE", "SE", "S", "CO"] + * ``` + * + * @see Official: https://servicodados.ibge.gov.br/api/v1/localidades/regioes + * IBGE, API de Localidades, `regioes`. + */ +export const getRegions = (): Region[] => REGIONS.map((region) => Object.assign({}, region)); diff --git a/src/get-state-capital/get-state-capital.test.ts b/src/get-state-capital/get-state-capital.test.ts new file mode 100644 index 00000000..0fad90b0 --- /dev/null +++ b/src/get-state-capital/get-state-capital.test.ts @@ -0,0 +1,67 @@ +import { STATE_CODES } from "../_internals/constants/state-codes"; +import { anyGarbage } from "../_internals/test/arbitraries"; +import { expectNeverThrows } from "../_internals/test/properties"; +import { describe, expect, expectTypeOf, it, test } from "../_internals/test/runtime"; +import { + getMunicipalityByCode, + type Municipality, +} from "../get-municipality-by-code/get-municipality-by-code"; +import { getStateCapital } from "./get-state-capital"; + +describe("getStateCapital", () => { + it("should return the capital of a state, per the IBGE Anuário table of the state capitals", () => { + expect(getStateCapital("SP")).toEqual({ code: "3550308", name: "São Paulo", stateCode: "SP" }); + expect(getStateCapital("TO")).toEqual({ code: "1721000", name: "Palmas", stateCode: "TO" }); + expect(getStateCapital("RR")).toEqual({ code: "1400100", name: "Boa Vista", stateCode: "RR" }); + }); + + it("should return Brasília for the Distrito Federal", () => { + expect(getStateCapital("DF")).toEqual({ code: "5300108", name: "Brasília", stateCode: "DF" }); + }); + + it("should match case-insensitively and ignore surrounding whitespace", () => { + expect(getStateCapital(" mg ")).toEqual({ + code: "3106200", + name: "Belo Horizonte", + stateCode: "MG", + }); + }); + + it("should return a fresh object, so mutating it leaves a later call intact", () => { + const capital = getStateCapital("PE"); + + if (capital !== null) capital.name = "MUTATED"; + + expect(getStateCapital("PE")?.name).toBe("Recife"); + }); + + it("should return null for a code that is not a state", () => { + expect(getStateCapital("ZZ")).toBeNull(); + expect(getStateCapital("")).toBeNull(); + expect(getStateCapital("__proto__")).toBeNull(); + // @ts-expect-error: intentionally invalid input + expect(getStateCapital(35)).toBeNull(); + }); + + describe("properties", () => { + test("should never throw, regardless of the input", () => { + expectNeverThrows(getStateCapital, anyGarbage); + }); + + test("should give every state a capital that getMunicipalityByCode finds in that state", () => { + for (const stateCode of STATE_CODES) { + const capital = getStateCapital(stateCode); + + expect(capital).not.toBeNull(); + expect(getMunicipalityByCode(capital?.code ?? "")).toEqual(capital); + } + }); + }); +}); + +describe("getStateCapital types", () => { + test("should take a string and return a Municipality or null", () => { + expectTypeOf(getStateCapital).parameter(0).toEqualTypeOf(); + expectTypeOf(getStateCapital).returns.toEqualTypeOf(); + }); +}); diff --git a/src/get-state-capital/get-state-capital.ts b/src/get-state-capital/get-state-capital.ts new file mode 100644 index 00000000..bdb4f50e --- /dev/null +++ b/src/get-state-capital/get-state-capital.ts @@ -0,0 +1,41 @@ +import { type Municipality } from "../_internals/constants/municipalities"; +import { STATE_CAPITALS } from "../_internals/constants/state-capitals"; +import { isStateCode } from "../_internals/is-state-code/is-state-code"; + +export type { Municipality } from "../_internals/constants/municipalities"; + +/** + * Retrieves the capital of a Brazilian state, as the same `{ code, name, stateCode }` that + * `getMunicipalityByCode` returns for it. The match is case-insensitive and ignores leading and + * trailing whitespace, like `getTimezoneByState`. + * + * For the Distrito Federal, which has no municipalities, the capital is Brasília, with the code + * the IBGE gives the whole district. + * + * @param {string} stateCode - The two-letter state code (sigla). + * @returns {Municipality|null} A fresh object with the capital's 7 digit IBGE code, its name and + * its state, or `null` when `stateCode` does not match any Brazilian state. + * + * @example + * ```typescript + * 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 + * ``` + * + * @see Official: https://anuario.ibge.gov.br/2024/territorio/posicao-e-extensao.html + * IBGE, Anuário Estatístico do Brasil, Tabela 1.1.1.2, "Localização geográfica, altitude e + * distância a Brasília, segundo os Municípios das Capitais - 2025". + */ +export const getStateCapital = (stateCode: string): Municipality | null => { + if (typeof stateCode !== "string") return null; + + const normalized = stateCode.trim().toUpperCase(); + + if (!isStateCode(normalized)) return null; + + const [name, code] = STATE_CAPITALS[normalized]; + + return { code, name, stateCode: normalized }; +}; diff --git a/src/get-states-by-region/get-states-by-region.test.ts b/src/get-states-by-region/get-states-by-region.test.ts new file mode 100644 index 00000000..0a23c824 --- /dev/null +++ b/src/get-states-by-region/get-states-by-region.test.ts @@ -0,0 +1,66 @@ +import { anyGarbage } from "../_internals/test/arbitraries"; +import { expectNeverThrows } from "../_internals/test/properties"; +import { describe, expect, expectTypeOf, it, test } from "../_internals/test/runtime"; +import { getStates } from "../get-states/get-states"; +import { getStatesByRegion, type State } from "./get-states-by-region"; + +const codes = (states: State[]): string[] => states.map((state) => state.code); + +describe("getStatesByRegion", () => { + it("should list the states of each region, per the IBGE API de Localidades, sorted by name", () => { + expect(codes(getStatesByRegion("N"))).toEqual(["AC", "AP", "AM", "PA", "RO", "RR", "TO"]); + expect(codes(getStatesByRegion("NE"))).toEqual([ + "AL", + "BA", + "CE", + "MA", + "PB", + "PE", + "PI", + "RN", + "SE", + ]); + expect(codes(getStatesByRegion("SE"))).toEqual(["ES", "MG", "RJ", "SP"]); + expect(codes(getStatesByRegion("S"))).toEqual(["PR", "RS", "SC"]); + expect(codes(getStatesByRegion("CO"))).toEqual(["DF", "GO", "MT", "MS"]); + }); + + it("should match case-insensitively and ignore surrounding whitespace", () => { + expect(codes(getStatesByRegion(" co "))).toEqual(["DF", "GO", "MT", "MS"]); + }); + + it("should cover all 27 states across the five regions", () => { + const all = ["N", "NE", "SE", "S", "CO"].flatMap((region) => codes(getStatesByRegion(region))); + + expect(all.toSorted()).toEqual(codes(getStates()).toSorted()); + }); + + it("should return fresh objects, so mutating one leaves a later call intact", () => { + const [first] = getStatesByRegion("S"); + + if (first !== undefined) Object.assign(first, { name: "MUTATED" }); + + expect(getStatesByRegion("S")[0]?.name).toBe("Paraná"); + }); + + it("should return an empty array for a code that is not a region", () => { + expect(getStatesByRegion("X")).toEqual([]); + expect(getStatesByRegion("")).toEqual([]); + expect(getStatesByRegion("Norte")).toEqual([]); + // @ts-expect-error: intentionally invalid input + expect(getStatesByRegion(1)).toEqual([]); + }); + + describe("properties", () => { + test("should never throw, regardless of the input", () => { + expectNeverThrows(getStatesByRegion, anyGarbage); + }); + }); +}); + +describe("getStatesByRegion types", () => { + test("should take a string and return State[]", () => { + expectTypeOf(getStatesByRegion).parameter(0).toEqualTypeOf(); + expectTypeOf(getStatesByRegion).returns.toEqualTypeOf(); + }); +}); diff --git a/src/get-states-by-region/get-states-by-region.ts b/src/get-states-by-region/get-states-by-region.ts new file mode 100644 index 00000000..2584653a --- /dev/null +++ b/src/get-states-by-region/get-states-by-region.ts @@ -0,0 +1,36 @@ +import { DATA, type State } from "../_internals/constants/states"; + +export type { State } from "../_internals/constants/states"; + +/** + * Retrieves the Brazilian states of a region (Grande Região), given the region code the IBGE + * abbreviates it with: `"N"`, `"NE"`, `"SE"`, `"S"` or `"CO"`, the same `State["regionCode"]` + * every state carries. The match is case-insensitive and ignores leading and trailing whitespace. + * The states come sorted by name, the way `getStates` sorts them. + * + * Each call returns a fresh array of fresh objects, so mutating the result never affects the + * underlying data or later calls. + * + * @param {string} regionCode - The region code. + * @returns {State[]} The states of the region, or an empty array when `regionCode` does not match + * any region. + * + * @example + * ```typescript + * getStatesByRegion("S").map((state) => state.code); // ["PR", "RS", "SC"] + * getStatesByRegion("co").map((state) => state.code); // ["DF", "GO", "MT", "MS"] + * getStatesByRegion("X"); // [] + * ``` + * + * @see Official: https://servicodados.ibge.gov.br/api/v1/localidades/estados + * IBGE, API de Localidades, `estados`: the `regiao` of each state. + */ +export const getStatesByRegion = (regionCode: string): State[] => { + if (typeof regionCode !== "string") return []; + + const normalized = regionCode.trim().toUpperCase(); + + return DATA.filter((state) => state.regionCode === normalized).map((state) => + Object.assign({}, state), + ); +}; diff --git a/src/index.test.ts b/src/index.test.ts index 06a5f7e0..38abdd21 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -106,6 +106,8 @@ import { type PixKeyType, type PixPayloadInfo, type PixPointOfInitiation, + type Region, + type RegionCode, type RegistroProfissionalCouncil, type ServiceItem, type State, @@ -225,10 +227,13 @@ const PUBLIC = [ "getPixPayloadInfo", "getServiceItem", "getStateByCep", + "getRegions", "getStateByIbgeCode", "getStateCodeByName", "getStateNameByCode", + "getStateCapital", "getStates", + "getStatesByRegion", "getTimezoneByState", "isBusinessDay", "isHoliday", @@ -451,6 +456,8 @@ describe("Public API", () => { PixKeyType: PixKeyType; PixPayloadInfo: PixPayloadInfo; PixPointOfInitiation: PixPointOfInitiation; + Region: Region; + RegionCode: RegionCode; RegistroProfissionalCouncil: RegistroProfissionalCouncil; ServiceItem: ServiceItem; State: State; diff --git a/src/index.ts b/src/index.ts index 3bdae2e0..53ec59dc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -188,10 +188,13 @@ export { } from "./get-pix-payload-info/get-pix-payload-info"; export { type ServiceItem, getServiceItem } from "./get-service-item/get-service-item"; export { getStateByCep } from "./get-state-by-cep/get-state-by-cep"; +export { type Region, type RegionCode, getRegions } from "./get-regions/get-regions"; export { getStateByIbgeCode } from "./get-state-by-ibge-code/get-state-by-ibge-code"; export { getStateCodeByName } from "./get-state-code-by-name/get-state-code-by-name"; export { getStateNameByCode } from "./get-state-name-by-code/get-state-name-by-code"; +export { getStateCapital } from "./get-state-capital/get-state-capital"; export { getStates } from "./get-states/get-states"; +export { getStatesByRegion } from "./get-states-by-region/get-states-by-region"; export { getTimezoneByState } from "./get-timezone-by-state/get-timezone-by-state"; export { type BusinessDayOptions, isBusinessDay } from "./is-business-day/is-business-day"; export { type IsHolidayParams, isHoliday } from "./is-holiday/is-holiday"; From 5df70842795676f6f01367efdff8eb3837f2711e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 27 Sep 2026 21:10:59 +0000 Subject: [PATCH 2/5] feat(states): add the capital and the region IBGE code to State MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every state util (getStates, getStateByIbgeCode, getStateByCep, getStatesByRegion) now fills `capital` ({ code, name }, IBGE Anuário table 1.1.1.2 and DTB 2025) and `regionIbgeCode`. Both are optional in the type, so a State built for 2.4.0 still type-checks; they become required in the next major version. getStateCapital reads the capital from the state data, which replaces the separate state-capitals table, and every util copies the capital so a caller cannot mutate the shared data. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RLkm9YrtAifc6XCLFVEsdH --- CONTRIBUTING.md | 4 +- docs/pt-br/utilities.md | 63 ++-- docs/utilities.md | 63 ++-- src/_internals/constants/state-capitals.ts | 44 --- src/_internals/constants/states.ts | 323 ++++++++++++++++-- src/_internals/copy-state/copy-state.test.ts | 19 ++ src/_internals/copy-state/copy-state.ts | 16 + src/get-state-by-cep/get-state-by-cep.test.ts | 12 +- src/get-state-by-cep/get-state-by-cep.ts | 5 +- .../get-state-by-ibge-code.test.ts | 11 +- .../get-state-by-ibge-code.ts | 9 +- src/get-state-capital/get-state-capital.ts | 14 +- .../get-states-by-region.ts | 5 +- src/get-states/get-states.test.ts | 45 +++ src/get-states/get-states.ts | 5 +- 15 files changed, 469 insertions(+), 169 deletions(-) delete mode 100644 src/_internals/constants/state-capitals.ts create mode 100644 src/_internals/copy-state/copy-state.test.ts create mode 100644 src/_internals/copy-state/copy-state.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cface8c6..59bd85d7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -117,8 +117,8 @@ itself: 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 processo number (Resolução CNJ nº 65/2008, `processo-juridico.ts`), IBAN lengths per country - (`iban.ts`), IBGE state codes (`ibge-uf-codes.ts`), the state capitals and the regions (IBGE, - `state-capitals.ts`, `regions.ts`), legal nature categories, the CST and CSOSN + (`iban.ts`), IBGE state codes (`ibge-uf-codes.ts`), the states with their regions and capitals + (IBGE, `states.ts`, `regions.ts`), legal nature categories, the CST and CSOSN tables (`src/is-valid-cst`, `src/is-valid-csosn`), the professional councils (`src/is-valid-registro-profissional`), the região fiscal digit of each state (`src/_internals/constants/cpf.ts`) and the voter ID state codes (`src/is-valid-voter-id`). A change to one of diff --git a/docs/pt-br/utilities.md b/docs/pt-br/utilities.md index 045d6f52..06f78b82 100644 --- a/docs/pt-br/utilities.md +++ b/docs/pt-br/utilities.md @@ -1655,43 +1655,44 @@ convertDateToWords('29/02/1900'); // "" (1900 não é bissexto) ### getStates -Retorna todos os estados brasileiros, cada um com sigla, nome, código da região, nome da região e código IBGE de 2 dígitos (`cUF`). +Retorna todos os estados brasileiros, cada um com sigla, nome, código da região, nome da região, código IBGE de 2 dígitos (`cUF`), o identificador IBGE da região (`regionIbgeCode`) e a capital (`capital`, o código IBGE de 7 dígitos e o nome do município). - Ordenados por nome no locale "pt-BR". - Exporta os tipos `State`, `StateCode` e `StateName`. `State` é uma união discriminada: estreitá-lo pelo `code` também estreita os demais campos. +- `regionIbgeCode` e `capital` vêm sempre preenchidos, mas são opcionais no tipo `State` para que um `State` montado para a 2.4.0 continue compilando; passam a ser obrigatórios na próxima versão major. ```javascript import { getStates } from '@brazilian-utils/brazilian-utils'; getStates(); // [ -// { code: 'AC', name: 'Acre', regionCode: 'N', regionName: 'Norte', ibgeCode: 12 }, -// { code: 'AL', name: 'Alagoas', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 27 }, -// { code: 'AP', name: 'Amapá', regionCode: 'N', regionName: 'Norte', ibgeCode: 16 }, -// { code: 'AM', name: 'Amazonas', regionCode: 'N', regionName: 'Norte', ibgeCode: 13 }, -// { code: 'BA', name: 'Bahia', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 29 }, -// { code: 'CE', name: 'Ceará', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 23 }, -// { code: 'DF', name: 'Distrito Federal', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 53 }, -// { code: 'ES', name: 'Espírito Santo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 32 }, -// { code: 'GO', name: 'Goiás', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 52 }, -// { code: 'MA', name: 'Maranhão', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 21 }, -// { code: 'MT', name: 'Mato Grosso', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 51 }, -// { code: 'MS', name: 'Mato Grosso do Sul', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 50 }, -// { code: 'MG', name: 'Minas Gerais', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 31 }, -// { code: 'PA', name: 'Pará', regionCode: 'N', regionName: 'Norte', ibgeCode: 15 }, -// { code: 'PB', name: 'Paraíba', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 25 }, -// { code: 'PR', name: 'Paraná', regionCode: 'S', regionName: 'Sul', ibgeCode: 41 }, -// { code: 'PE', name: 'Pernambuco', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 26 }, -// { code: 'PI', name: 'Piauí', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 22 }, -// { code: 'RJ', name: 'Rio de Janeiro', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 33 }, -// { code: 'RN', name: 'Rio Grande do Norte', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 24 }, -// { code: 'RS', name: 'Rio Grande do Sul', regionCode: 'S', regionName: 'Sul', ibgeCode: 43 }, -// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11 }, -// { code: 'RR', name: 'Roraima', regionCode: 'N', regionName: 'Norte', ibgeCode: 14 }, -// { code: 'SC', name: 'Santa Catarina', regionCode: 'S', regionName: 'Sul', ibgeCode: 42 }, -// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35 }, -// { code: 'SE', name: 'Sergipe', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 28 }, -// { code: 'TO', name: 'Tocantins', regionCode: 'N', regionName: 'Norte', ibgeCode: 17 }, +// { code: 'AC', name: 'Acre', regionCode: 'N', regionName: 'Norte', ibgeCode: 12, regionIbgeCode: 1, capital: { code: '1200401', name: 'Rio Branco' } }, +// { code: 'AL', name: 'Alagoas', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 27, regionIbgeCode: 2, capital: { code: '2704302', name: 'Maceió' } }, +// { code: 'AP', name: 'Amapá', regionCode: 'N', regionName: 'Norte', ibgeCode: 16, regionIbgeCode: 1, capital: { code: '1600303', name: 'Macapá' } }, +// { code: 'AM', name: 'Amazonas', regionCode: 'N', regionName: 'Norte', ibgeCode: 13, regionIbgeCode: 1, capital: { code: '1302603', name: 'Manaus' } }, +// { code: 'BA', name: 'Bahia', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 29, regionIbgeCode: 2, capital: { code: '2927408', name: 'Salvador' } }, +// { code: 'CE', name: 'Ceará', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 23, regionIbgeCode: 2, capital: { code: '2304400', name: 'Fortaleza' } }, +// { code: 'DF', name: 'Distrito Federal', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 53, regionIbgeCode: 5, capital: { code: '5300108', name: 'Brasília' } }, +// { code: 'ES', name: 'Espírito Santo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 32, regionIbgeCode: 3, capital: { code: '3205309', name: 'Vitória' } }, +// { code: 'GO', name: 'Goiás', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 52, regionIbgeCode: 5, capital: { code: '5208707', name: 'Goiânia' } }, +// { code: 'MA', name: 'Maranhão', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 21, regionIbgeCode: 2, capital: { code: '2111300', name: 'São Luís' } }, +// { code: 'MT', name: 'Mato Grosso', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 51, regionIbgeCode: 5, capital: { code: '5103403', name: 'Cuiabá' } }, +// { code: 'MS', name: 'Mato Grosso do Sul', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 50, regionIbgeCode: 5, capital: { code: '5002704', name: 'Campo Grande' } }, +// { code: 'MG', name: 'Minas Gerais', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 31, regionIbgeCode: 3, capital: { code: '3106200', name: 'Belo Horizonte' } }, +// { code: 'PA', name: 'Pará', regionCode: 'N', regionName: 'Norte', ibgeCode: 15, regionIbgeCode: 1, capital: { code: '1501402', name: 'Belém' } }, +// { code: 'PB', name: 'Paraíba', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 25, regionIbgeCode: 2, capital: { code: '2507507', name: 'João Pessoa' } }, +// { code: 'PR', name: 'Paraná', regionCode: 'S', regionName: 'Sul', ibgeCode: 41, regionIbgeCode: 4, capital: { code: '4106902', name: 'Curitiba' } }, +// { code: 'PE', name: 'Pernambuco', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 26, regionIbgeCode: 2, capital: { code: '2611606', name: 'Recife' } }, +// { code: 'PI', name: 'Piauí', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 22, regionIbgeCode: 2, capital: { code: '2211001', name: 'Teresina' } }, +// { code: 'RJ', name: 'Rio de Janeiro', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 33, regionIbgeCode: 3, capital: { code: '3304557', name: 'Rio de Janeiro' } }, +// { code: 'RN', name: 'Rio Grande do Norte', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 24, regionIbgeCode: 2, capital: { code: '2408102', name: 'Natal' } }, +// { code: 'RS', name: 'Rio Grande do Sul', regionCode: 'S', regionName: 'Sul', ibgeCode: 43, regionIbgeCode: 4, capital: { code: '4314902', name: 'Porto Alegre' } }, +// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11, regionIbgeCode: 1, capital: { code: '1100205', name: 'Porto Velho' } }, +// { code: 'RR', name: 'Roraima', regionCode: 'N', regionName: 'Norte', ibgeCode: 14, regionIbgeCode: 1, capital: { code: '1400100', name: 'Boa Vista' } }, +// { code: 'SC', name: 'Santa Catarina', regionCode: 'S', regionName: 'Sul', ibgeCode: 42, regionIbgeCode: 4, capital: { code: '4205407', name: 'Florianópolis' } }, +// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35, regionIbgeCode: 3, capital: { code: '3550308', name: 'São Paulo' } }, +// { code: 'SE', name: 'Sergipe', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 28, regionIbgeCode: 2, capital: { code: '2800308', name: 'Aracaju' } }, +// { code: 'TO', name: 'Tocantins', regionCode: 'N', regionName: 'Norte', ibgeCode: 17, regionIbgeCode: 1, capital: { code: '1721000', name: 'Palmas' } }, // ] ``` @@ -1711,7 +1712,7 @@ Retorna o estado brasileiro ao qual um CEP pertence, a partir das faixas de CEP import { getStateByCep } from '@brazilian-utils/brazilian-utils'; getStateByCep('01310-100'); -// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35 } +// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35, regionIbgeCode: 3, capital: { code: '3550308', name: 'São Paulo' } } getStateByCep(20040020); // { code: 'RJ', name: 'Rio de Janeiro', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 33 } @@ -1736,10 +1737,10 @@ Retorna o estado brasileiro cujo código IBGE de 2 dígitos (`cUF`, o Código da import { getStateByIbgeCode } from '@brazilian-utils/brazilian-utils'; getStateByIbgeCode('35'); -// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35 } +// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35, regionIbgeCode: 3, capital: { code: '3550308', name: 'São Paulo' } } getStateByIbgeCode(11); -// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11 } +// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11, regionIbgeCode: 1, capital: { code: '1100205', name: 'Porto Velho' } } getStateByIbgeCode('00'); // null getStateByIbgeCode(-35); // null diff --git a/docs/utilities.md b/docs/utilities.md index fd2b4c6a..64097553 100644 --- a/docs/utilities.md +++ b/docs/utilities.md @@ -1655,43 +1655,44 @@ convertDateToWords('29/02/1900'); // "" (1900 is not a leap year) ### getStates -Get all Brazilian states, each with its two-letter code, name, region code, region name and 2-digit IBGE code (`cUF`). +Get all Brazilian states, each with its two-letter code, name, region code, region name, 2-digit IBGE code (`cUF`), the IBGE identifier of its region (`regionIbgeCode`) and its capital (`capital`, the 7-digit IBGE code and the name of the municipality). - Sorted by name in the "pt-BR" locale. - Exports the `State`, `StateCode` and `StateName` types. `State` is a discriminated union: narrowing it by `code` also narrows the other fields. +- `regionIbgeCode` and `capital` are always filled, but optional in the `State` type so that a `State` built for 2.4.0 still type-checks; they become required in the next major version. ```javascript import { getStates } from '@brazilian-utils/brazilian-utils'; getStates(); // [ -// { code: 'AC', name: 'Acre', regionCode: 'N', regionName: 'Norte', ibgeCode: 12 }, -// { code: 'AL', name: 'Alagoas', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 27 }, -// { code: 'AP', name: 'Amapá', regionCode: 'N', regionName: 'Norte', ibgeCode: 16 }, -// { code: 'AM', name: 'Amazonas', regionCode: 'N', regionName: 'Norte', ibgeCode: 13 }, -// { code: 'BA', name: 'Bahia', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 29 }, -// { code: 'CE', name: 'Ceará', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 23 }, -// { code: 'DF', name: 'Distrito Federal', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 53 }, -// { code: 'ES', name: 'Espírito Santo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 32 }, -// { code: 'GO', name: 'Goiás', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 52 }, -// { code: 'MA', name: 'Maranhão', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 21 }, -// { code: 'MT', name: 'Mato Grosso', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 51 }, -// { code: 'MS', name: 'Mato Grosso do Sul', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 50 }, -// { code: 'MG', name: 'Minas Gerais', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 31 }, -// { code: 'PA', name: 'Pará', regionCode: 'N', regionName: 'Norte', ibgeCode: 15 }, -// { code: 'PB', name: 'Paraíba', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 25 }, -// { code: 'PR', name: 'Paraná', regionCode: 'S', regionName: 'Sul', ibgeCode: 41 }, -// { code: 'PE', name: 'Pernambuco', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 26 }, -// { code: 'PI', name: 'Piauí', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 22 }, -// { code: 'RJ', name: 'Rio de Janeiro', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 33 }, -// { code: 'RN', name: 'Rio Grande do Norte', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 24 }, -// { code: 'RS', name: 'Rio Grande do Sul', regionCode: 'S', regionName: 'Sul', ibgeCode: 43 }, -// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11 }, -// { code: 'RR', name: 'Roraima', regionCode: 'N', regionName: 'Norte', ibgeCode: 14 }, -// { code: 'SC', name: 'Santa Catarina', regionCode: 'S', regionName: 'Sul', ibgeCode: 42 }, -// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35 }, -// { code: 'SE', name: 'Sergipe', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 28 }, -// { code: 'TO', name: 'Tocantins', regionCode: 'N', regionName: 'Norte', ibgeCode: 17 }, +// { code: 'AC', name: 'Acre', regionCode: 'N', regionName: 'Norte', ibgeCode: 12, regionIbgeCode: 1, capital: { code: '1200401', name: 'Rio Branco' } }, +// { code: 'AL', name: 'Alagoas', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 27, regionIbgeCode: 2, capital: { code: '2704302', name: 'Maceió' } }, +// { code: 'AP', name: 'Amapá', regionCode: 'N', regionName: 'Norte', ibgeCode: 16, regionIbgeCode: 1, capital: { code: '1600303', name: 'Macapá' } }, +// { code: 'AM', name: 'Amazonas', regionCode: 'N', regionName: 'Norte', ibgeCode: 13, regionIbgeCode: 1, capital: { code: '1302603', name: 'Manaus' } }, +// { code: 'BA', name: 'Bahia', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 29, regionIbgeCode: 2, capital: { code: '2927408', name: 'Salvador' } }, +// { code: 'CE', name: 'Ceará', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 23, regionIbgeCode: 2, capital: { code: '2304400', name: 'Fortaleza' } }, +// { code: 'DF', name: 'Distrito Federal', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 53, regionIbgeCode: 5, capital: { code: '5300108', name: 'Brasília' } }, +// { code: 'ES', name: 'Espírito Santo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 32, regionIbgeCode: 3, capital: { code: '3205309', name: 'Vitória' } }, +// { code: 'GO', name: 'Goiás', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 52, regionIbgeCode: 5, capital: { code: '5208707', name: 'Goiânia' } }, +// { code: 'MA', name: 'Maranhão', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 21, regionIbgeCode: 2, capital: { code: '2111300', name: 'São Luís' } }, +// { code: 'MT', name: 'Mato Grosso', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 51, regionIbgeCode: 5, capital: { code: '5103403', name: 'Cuiabá' } }, +// { code: 'MS', name: 'Mato Grosso do Sul', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 50, regionIbgeCode: 5, capital: { code: '5002704', name: 'Campo Grande' } }, +// { code: 'MG', name: 'Minas Gerais', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 31, regionIbgeCode: 3, capital: { code: '3106200', name: 'Belo Horizonte' } }, +// { code: 'PA', name: 'Pará', regionCode: 'N', regionName: 'Norte', ibgeCode: 15, regionIbgeCode: 1, capital: { code: '1501402', name: 'Belém' } }, +// { code: 'PB', name: 'Paraíba', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 25, regionIbgeCode: 2, capital: { code: '2507507', name: 'João Pessoa' } }, +// { code: 'PR', name: 'Paraná', regionCode: 'S', regionName: 'Sul', ibgeCode: 41, regionIbgeCode: 4, capital: { code: '4106902', name: 'Curitiba' } }, +// { code: 'PE', name: 'Pernambuco', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 26, regionIbgeCode: 2, capital: { code: '2611606', name: 'Recife' } }, +// { code: 'PI', name: 'Piauí', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 22, regionIbgeCode: 2, capital: { code: '2211001', name: 'Teresina' } }, +// { code: 'RJ', name: 'Rio de Janeiro', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 33, regionIbgeCode: 3, capital: { code: '3304557', name: 'Rio de Janeiro' } }, +// { code: 'RN', name: 'Rio Grande do Norte', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 24, regionIbgeCode: 2, capital: { code: '2408102', name: 'Natal' } }, +// { code: 'RS', name: 'Rio Grande do Sul', regionCode: 'S', regionName: 'Sul', ibgeCode: 43, regionIbgeCode: 4, capital: { code: '4314902', name: 'Porto Alegre' } }, +// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11, regionIbgeCode: 1, capital: { code: '1100205', name: 'Porto Velho' } }, +// { code: 'RR', name: 'Roraima', regionCode: 'N', regionName: 'Norte', ibgeCode: 14, regionIbgeCode: 1, capital: { code: '1400100', name: 'Boa Vista' } }, +// { code: 'SC', name: 'Santa Catarina', regionCode: 'S', regionName: 'Sul', ibgeCode: 42, regionIbgeCode: 4, capital: { code: '4205407', name: 'Florianópolis' } }, +// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35, regionIbgeCode: 3, capital: { code: '3550308', name: 'São Paulo' } }, +// { code: 'SE', name: 'Sergipe', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 28, regionIbgeCode: 2, capital: { code: '2800308', name: 'Aracaju' } }, +// { code: 'TO', name: 'Tocantins', regionCode: 'N', regionName: 'Norte', ibgeCode: 17, regionIbgeCode: 1, capital: { code: '1721000', name: 'Palmas' } }, // ] ``` @@ -1711,7 +1712,7 @@ Get the Brazilian state a CEP belongs to, from the CEP ranges the Correios assig import { getStateByCep } from '@brazilian-utils/brazilian-utils'; getStateByCep('01310-100'); -// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35 } +// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35, regionIbgeCode: 3, capital: { code: '3550308', name: 'São Paulo' } } getStateByCep(20040020); // { code: 'RJ', name: 'Rio de Janeiro', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 33 } @@ -1736,10 +1737,10 @@ Get the Brazilian state whose 2-digit IBGE code (`cUF`, the Código da Unidade d import { getStateByIbgeCode } from '@brazilian-utils/brazilian-utils'; getStateByIbgeCode('35'); -// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35 } +// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35, regionIbgeCode: 3, capital: { code: '3550308', name: 'São Paulo' } } getStateByIbgeCode(11); -// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11 } +// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11, regionIbgeCode: 1, capital: { code: '1100205', name: 'Porto Velho' } } getStateByIbgeCode('00'); // null getStateByIbgeCode(-35); // null diff --git a/src/_internals/constants/state-capitals.ts b/src/_internals/constants/state-capitals.ts deleted file mode 100644 index fd196593..00000000 --- a/src/_internals/constants/state-capitals.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { type StateCode } from "./states"; - -/** - * The capital of each state, as `[name, ibgeCode]`: the municipality name as the IBGE spells it - * and its 7 digit IBGE code, the same pair `getMunicipalityByCode` finds for that code. For the - * Distrito Federal, whose territory is not divided into municipalities, the entry is Brasília, - * the code the IBGE gives the whole district. - * - * @see Official: https://anuario.ibge.gov.br/2024/territorio/posicao-e-extensao.html - * IBGE, Anuário Estatístico do Brasil, Tabela 1.1.1.2, "Localização geográfica, altitude e - * distância a Brasília, segundo os Municípios das Capitais - 2025": one capital per state, - * written "Porto Velho (RO)" to "Brasília (DF)". - * @see Official: https://geoftp.ibge.gov.br/organizacao_do_territorio/estrutura_territorial/divisao_territorial/2025/DTB_2025.zip - * IBGE, Divisão Territorial Brasileira 2025, the municipality codes. - */ -export const STATE_CAPITALS: Record = { - AC: ["Rio Branco", "1200401"], - AL: ["Maceió", "2704302"], - AM: ["Manaus", "1302603"], - AP: ["Macapá", "1600303"], - BA: ["Salvador", "2927408"], - CE: ["Fortaleza", "2304400"], - DF: ["Brasília", "5300108"], - ES: ["Vitória", "3205309"], - GO: ["Goiânia", "5208707"], - MA: ["São Luís", "2111300"], - MG: ["Belo Horizonte", "3106200"], - MS: ["Campo Grande", "5002704"], - MT: ["Cuiabá", "5103403"], - PA: ["Belém", "1501402"], - PB: ["João Pessoa", "2507507"], - PE: ["Recife", "2611606"], - PI: ["Teresina", "2211001"], - PR: ["Curitiba", "4106902"], - RJ: ["Rio de Janeiro", "3304557"], - RN: ["Natal", "2408102"], - RO: ["Porto Velho", "1100205"], - RR: ["Boa Vista", "1400100"], - RS: ["Porto Alegre", "4314902"], - SC: ["Florianópolis", "4205407"], - SE: ["Aracaju", "2800308"], - SP: ["São Paulo", "3550308"], - TO: ["Palmas", "1721000"], -}; diff --git a/src/_internals/constants/states.ts b/src/_internals/constants/states.ts index b7275ddb..47ee0176 100644 --- a/src/_internals/constants/states.ts +++ b/src/_internals/constants/states.ts @@ -7,8 +7,15 @@ * * Each member has the two letter code of the state (`code`, e.g. `"SP"`), its full name * (`name`, e.g. `"São Paulo"`), the code and the full name of the region it belongs to - * (`regionCode` and `regionName`, e.g. `"SE"` and `"Sudeste"`) and the 2 digit IBGE code of the - * Federative Unit (`ibgeCode`, the "cUF", e.g. `35`). + * (`regionCode` and `regionName`, e.g. `"SE"` and `"Sudeste"`), the 2 digit IBGE code of the + * Federative Unit (`ibgeCode`, the "cUF", e.g. `35`), the IBGE identifier of its region + * (`regionIbgeCode`, the first digit of `ibgeCode`, e.g. `3`) and its capital (`capital`, the 7 digit + * IBGE code and the name of the municipality, e.g. `{ code: "3550308", name: "São Paulo" }`; for + * the Distrito Federal, which has no municipalities, Brasília with the code of the whole district). + * + * `regionIbgeCode` and `capital` came after 2.4.0: every state util fills them, but they are + * optional in the type so that a `State` built before them still type-checks. They become + * required in the next major version. */ export type State = | { @@ -17,6 +24,8 @@ export type State = readonly regionCode: "N"; readonly regionName: "Norte"; readonly ibgeCode: 12; + readonly regionIbgeCode?: 1; + readonly capital?: { readonly code: "1200401"; readonly name: "Rio Branco" }; } | { readonly code: "AL"; @@ -24,6 +33,8 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 27; + readonly regionIbgeCode?: 2; + readonly capital?: { readonly code: "2704302"; readonly name: "Maceió" }; } | { readonly code: "AP"; @@ -31,6 +42,8 @@ export type State = readonly regionCode: "N"; readonly regionName: "Norte"; readonly ibgeCode: 16; + readonly regionIbgeCode?: 1; + readonly capital?: { readonly code: "1600303"; readonly name: "Macapá" }; } | { readonly code: "AM"; @@ -38,6 +51,8 @@ export type State = readonly regionCode: "N"; readonly regionName: "Norte"; readonly ibgeCode: 13; + readonly regionIbgeCode?: 1; + readonly capital?: { readonly code: "1302603"; readonly name: "Manaus" }; } | { readonly code: "BA"; @@ -45,6 +60,8 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 29; + readonly regionIbgeCode?: 2; + readonly capital?: { readonly code: "2927408"; readonly name: "Salvador" }; } | { readonly code: "CE"; @@ -52,6 +69,8 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 23; + readonly regionIbgeCode?: 2; + readonly capital?: { readonly code: "2304400"; readonly name: "Fortaleza" }; } | { readonly code: "DF"; @@ -59,6 +78,8 @@ export type State = readonly regionCode: "CO"; readonly regionName: "Centro-Oeste"; readonly ibgeCode: 53; + readonly regionIbgeCode?: 5; + readonly capital?: { readonly code: "5300108"; readonly name: "Brasília" }; } | { readonly code: "ES"; @@ -66,6 +87,8 @@ export type State = readonly regionCode: "SE"; readonly regionName: "Sudeste"; readonly ibgeCode: 32; + readonly regionIbgeCode?: 3; + readonly capital?: { readonly code: "3205309"; readonly name: "Vitória" }; } | { readonly code: "GO"; @@ -73,6 +96,8 @@ export type State = readonly regionCode: "CO"; readonly regionName: "Centro-Oeste"; readonly ibgeCode: 52; + readonly regionIbgeCode?: 5; + readonly capital?: { readonly code: "5208707"; readonly name: "Goiânia" }; } | { readonly code: "MA"; @@ -80,6 +105,8 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 21; + readonly regionIbgeCode?: 2; + readonly capital?: { readonly code: "2111300"; readonly name: "São Luís" }; } | { readonly code: "MT"; @@ -87,6 +114,8 @@ export type State = readonly regionCode: "CO"; readonly regionName: "Centro-Oeste"; readonly ibgeCode: 51; + readonly regionIbgeCode?: 5; + readonly capital?: { readonly code: "5103403"; readonly name: "Cuiabá" }; } | { readonly code: "MS"; @@ -94,6 +123,8 @@ export type State = readonly regionCode: "CO"; readonly regionName: "Centro-Oeste"; readonly ibgeCode: 50; + readonly regionIbgeCode?: 5; + readonly capital?: { readonly code: "5002704"; readonly name: "Campo Grande" }; } | { readonly code: "MG"; @@ -101,6 +132,8 @@ export type State = readonly regionCode: "SE"; readonly regionName: "Sudeste"; readonly ibgeCode: 31; + readonly regionIbgeCode?: 3; + readonly capital?: { readonly code: "3106200"; readonly name: "Belo Horizonte" }; } | { readonly code: "PA"; @@ -108,6 +141,8 @@ export type State = readonly regionCode: "N"; readonly regionName: "Norte"; readonly ibgeCode: 15; + readonly regionIbgeCode?: 1; + readonly capital?: { readonly code: "1501402"; readonly name: "Belém" }; } | { readonly code: "PB"; @@ -115,6 +150,8 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 25; + readonly regionIbgeCode?: 2; + readonly capital?: { readonly code: "2507507"; readonly name: "João Pessoa" }; } | { readonly code: "PR"; @@ -122,6 +159,8 @@ export type State = readonly regionCode: "S"; readonly regionName: "Sul"; readonly ibgeCode: 41; + readonly regionIbgeCode?: 4; + readonly capital?: { readonly code: "4106902"; readonly name: "Curitiba" }; } | { readonly code: "PE"; @@ -129,6 +168,8 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 26; + readonly regionIbgeCode?: 2; + readonly capital?: { readonly code: "2611606"; readonly name: "Recife" }; } | { readonly code: "PI"; @@ -136,6 +177,8 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 22; + readonly regionIbgeCode?: 2; + readonly capital?: { readonly code: "2211001"; readonly name: "Teresina" }; } | { readonly code: "RJ"; @@ -143,6 +186,8 @@ export type State = readonly regionCode: "SE"; readonly regionName: "Sudeste"; readonly ibgeCode: 33; + readonly regionIbgeCode?: 3; + readonly capital?: { readonly code: "3304557"; readonly name: "Rio de Janeiro" }; } | { readonly code: "RN"; @@ -150,6 +195,8 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 24; + readonly regionIbgeCode?: 2; + readonly capital?: { readonly code: "2408102"; readonly name: "Natal" }; } | { readonly code: "RS"; @@ -157,6 +204,8 @@ export type State = readonly regionCode: "S"; readonly regionName: "Sul"; readonly ibgeCode: 43; + readonly regionIbgeCode?: 4; + readonly capital?: { readonly code: "4314902"; readonly name: "Porto Alegre" }; } | { readonly code: "RO"; @@ -164,6 +213,8 @@ export type State = readonly regionCode: "N"; readonly regionName: "Norte"; readonly ibgeCode: 11; + readonly regionIbgeCode?: 1; + readonly capital?: { readonly code: "1100205"; readonly name: "Porto Velho" }; } | { readonly code: "RR"; @@ -171,6 +222,8 @@ export type State = readonly regionCode: "N"; readonly regionName: "Norte"; readonly ibgeCode: 14; + readonly regionIbgeCode?: 1; + readonly capital?: { readonly code: "1400100"; readonly name: "Boa Vista" }; } | { readonly code: "SC"; @@ -178,6 +231,8 @@ export type State = readonly regionCode: "S"; readonly regionName: "Sul"; readonly ibgeCode: 42; + readonly regionIbgeCode?: 4; + readonly capital?: { readonly code: "4205407"; readonly name: "Florianópolis" }; } | { readonly code: "SP"; @@ -185,6 +240,8 @@ export type State = readonly regionCode: "SE"; readonly regionName: "Sudeste"; readonly ibgeCode: 35; + readonly regionIbgeCode?: 3; + readonly capital?: { readonly code: "3550308"; readonly name: "São Paulo" }; } | { readonly code: "SE"; @@ -192,6 +249,8 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 28; + readonly regionIbgeCode?: 2; + readonly capital?: { readonly code: "2800308"; readonly name: "Aracaju" }; } | { readonly code: "TO"; @@ -199,6 +258,8 @@ export type State = readonly regionCode: "N"; readonly regionName: "Norte"; readonly ibgeCode: 17; + readonly regionIbgeCode?: 1; + readonly capital?: { readonly code: "1721000"; readonly name: "Palmas" }; }; /** The two letter code of each Brazilian state, as published by the IBGE. */ @@ -210,54 +271,260 @@ export type StateName = State["name"]; /** * Brazilian states published by the IBGE, sorted by name with `localeCompare` in the "pt-BR" * locale. `ibgeCode` is the 2-digit IBGE code of the Federative Unit ("cUF"), the same code - * found in the first field of every DF-e access key (chave de acesso). + * found in the first field of every DF-e access key (chave de acesso). Every entry fills the + * optional fields of `State` too. * * @see Official: https://servicodados.ibge.gov.br/api/docs/localidades + * @see Official: https://servicodados.ibge.gov.br/api/v1/localidades/regioes + * IBGE, API de Localidades, `regioes`: the identifier of each region. + * @see Official: https://anuario.ibge.gov.br/2024/territorio/posicao-e-extensao.html + * IBGE, Anuário Estatístico do Brasil, Tabela 1.1.1.2, "Localização geográfica, altitude e + * distância a Brasília, segundo os Municípios das Capitais - 2025": one capital per state. + * @see Official: https://geoftp.ibge.gov.br/organizacao_do_territorio/estrutura_territorial/divisao_territorial/2025/DTB_2025.zip + * IBGE, Divisão Territorial Brasileira 2025, the municipality codes of the capitals. */ -export const DATA: readonly State[] = [ - { code: "AC", name: "Acre", regionCode: "N", regionName: "Norte", ibgeCode: 12 }, - { code: "AL", name: "Alagoas", regionCode: "NE", regionName: "Nordeste", ibgeCode: 27 }, - { code: "AP", name: "Amapá", regionCode: "N", regionName: "Norte", ibgeCode: 16 }, - { code: "AM", name: "Amazonas", regionCode: "N", regionName: "Norte", ibgeCode: 13 }, - { code: "BA", name: "Bahia", regionCode: "NE", regionName: "Nordeste", ibgeCode: 29 }, - { code: "CE", name: "Ceará", regionCode: "NE", regionName: "Nordeste", ibgeCode: 23 }, +export const DATA: readonly Required[] = [ + { + code: "AC", + name: "Acre", + regionCode: "N", + regionName: "Norte", + ibgeCode: 12, + regionIbgeCode: 1, + capital: { code: "1200401", name: "Rio Branco" }, + }, + { + code: "AL", + name: "Alagoas", + regionCode: "NE", + regionName: "Nordeste", + ibgeCode: 27, + regionIbgeCode: 2, + capital: { code: "2704302", name: "Maceió" }, + }, + { + code: "AP", + name: "Amapá", + regionCode: "N", + regionName: "Norte", + ibgeCode: 16, + regionIbgeCode: 1, + capital: { code: "1600303", name: "Macapá" }, + }, + { + code: "AM", + name: "Amazonas", + regionCode: "N", + regionName: "Norte", + ibgeCode: 13, + regionIbgeCode: 1, + capital: { code: "1302603", name: "Manaus" }, + }, + { + code: "BA", + name: "Bahia", + regionCode: "NE", + regionName: "Nordeste", + ibgeCode: 29, + regionIbgeCode: 2, + capital: { code: "2927408", name: "Salvador" }, + }, + { + code: "CE", + name: "Ceará", + regionCode: "NE", + regionName: "Nordeste", + ibgeCode: 23, + regionIbgeCode: 2, + capital: { code: "2304400", name: "Fortaleza" }, + }, { code: "DF", name: "Distrito Federal", regionCode: "CO", regionName: "Centro-Oeste", ibgeCode: 53, + regionIbgeCode: 5, + capital: { code: "5300108", name: "Brasília" }, + }, + { + code: "ES", + name: "Espírito Santo", + regionCode: "SE", + regionName: "Sudeste", + ibgeCode: 32, + regionIbgeCode: 3, + capital: { code: "3205309", name: "Vitória" }, + }, + { + code: "GO", + name: "Goiás", + regionCode: "CO", + regionName: "Centro-Oeste", + ibgeCode: 52, + regionIbgeCode: 5, + capital: { code: "5208707", name: "Goiânia" }, + }, + { + code: "MA", + name: "Maranhão", + regionCode: "NE", + regionName: "Nordeste", + ibgeCode: 21, + regionIbgeCode: 2, + capital: { code: "2111300", name: "São Luís" }, + }, + { + code: "MT", + name: "Mato Grosso", + regionCode: "CO", + regionName: "Centro-Oeste", + ibgeCode: 51, + regionIbgeCode: 5, + capital: { code: "5103403", name: "Cuiabá" }, }, - { code: "ES", name: "Espírito Santo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 32 }, - { code: "GO", name: "Goiás", regionCode: "CO", regionName: "Centro-Oeste", ibgeCode: 52 }, - { code: "MA", name: "Maranhão", regionCode: "NE", regionName: "Nordeste", ibgeCode: 21 }, - { code: "MT", name: "Mato Grosso", regionCode: "CO", regionName: "Centro-Oeste", ibgeCode: 51 }, { code: "MS", name: "Mato Grosso do Sul", regionCode: "CO", regionName: "Centro-Oeste", ibgeCode: 50, + regionIbgeCode: 5, + capital: { code: "5002704", name: "Campo Grande" }, + }, + { + code: "MG", + name: "Minas Gerais", + regionCode: "SE", + regionName: "Sudeste", + ibgeCode: 31, + regionIbgeCode: 3, + capital: { code: "3106200", name: "Belo Horizonte" }, + }, + { + code: "PA", + name: "Pará", + regionCode: "N", + regionName: "Norte", + ibgeCode: 15, + regionIbgeCode: 1, + capital: { code: "1501402", name: "Belém" }, + }, + { + code: "PB", + name: "Paraíba", + regionCode: "NE", + regionName: "Nordeste", + ibgeCode: 25, + regionIbgeCode: 2, + capital: { code: "2507507", name: "João Pessoa" }, + }, + { + code: "PR", + name: "Paraná", + regionCode: "S", + regionName: "Sul", + ibgeCode: 41, + regionIbgeCode: 4, + capital: { code: "4106902", name: "Curitiba" }, + }, + { + code: "PE", + name: "Pernambuco", + regionCode: "NE", + regionName: "Nordeste", + ibgeCode: 26, + regionIbgeCode: 2, + capital: { code: "2611606", name: "Recife" }, + }, + { + code: "PI", + name: "Piauí", + regionCode: "NE", + regionName: "Nordeste", + ibgeCode: 22, + regionIbgeCode: 2, + capital: { code: "2211001", name: "Teresina" }, + }, + { + code: "RJ", + name: "Rio de Janeiro", + regionCode: "SE", + regionName: "Sudeste", + ibgeCode: 33, + regionIbgeCode: 3, + capital: { code: "3304557", name: "Rio de Janeiro" }, }, - { code: "MG", name: "Minas Gerais", regionCode: "SE", regionName: "Sudeste", ibgeCode: 31 }, - { code: "PA", name: "Pará", regionCode: "N", regionName: "Norte", ibgeCode: 15 }, - { code: "PB", name: "Paraíba", regionCode: "NE", regionName: "Nordeste", ibgeCode: 25 }, - { code: "PR", name: "Paraná", regionCode: "S", regionName: "Sul", ibgeCode: 41 }, - { code: "PE", name: "Pernambuco", regionCode: "NE", regionName: "Nordeste", ibgeCode: 26 }, - { code: "PI", name: "Piauí", regionCode: "NE", regionName: "Nordeste", ibgeCode: 22 }, - { code: "RJ", name: "Rio de Janeiro", regionCode: "SE", regionName: "Sudeste", ibgeCode: 33 }, { code: "RN", name: "Rio Grande do Norte", regionCode: "NE", regionName: "Nordeste", ibgeCode: 24, + regionIbgeCode: 2, + capital: { code: "2408102", name: "Natal" }, + }, + { + code: "RS", + name: "Rio Grande do Sul", + regionCode: "S", + regionName: "Sul", + ibgeCode: 43, + regionIbgeCode: 4, + capital: { code: "4314902", name: "Porto Alegre" }, + }, + { + code: "RO", + name: "Rondônia", + regionCode: "N", + regionName: "Norte", + ibgeCode: 11, + regionIbgeCode: 1, + capital: { code: "1100205", name: "Porto Velho" }, + }, + { + code: "RR", + name: "Roraima", + regionCode: "N", + regionName: "Norte", + ibgeCode: 14, + regionIbgeCode: 1, + capital: { code: "1400100", name: "Boa Vista" }, + }, + { + code: "SC", + name: "Santa Catarina", + regionCode: "S", + regionName: "Sul", + ibgeCode: 42, + regionIbgeCode: 4, + capital: { code: "4205407", name: "Florianópolis" }, + }, + { + code: "SP", + name: "São Paulo", + regionCode: "SE", + regionName: "Sudeste", + ibgeCode: 35, + regionIbgeCode: 3, + capital: { code: "3550308", name: "São Paulo" }, + }, + { + code: "SE", + name: "Sergipe", + regionCode: "NE", + regionName: "Nordeste", + ibgeCode: 28, + regionIbgeCode: 2, + capital: { code: "2800308", name: "Aracaju" }, + }, + { + code: "TO", + name: "Tocantins", + regionCode: "N", + regionName: "Norte", + ibgeCode: 17, + regionIbgeCode: 1, + capital: { code: "1721000", name: "Palmas" }, }, - { code: "RS", name: "Rio Grande do Sul", regionCode: "S", regionName: "Sul", ibgeCode: 43 }, - { code: "RO", name: "Rondônia", regionCode: "N", regionName: "Norte", ibgeCode: 11 }, - { code: "RR", name: "Roraima", regionCode: "N", regionName: "Norte", ibgeCode: 14 }, - { code: "SC", name: "Santa Catarina", regionCode: "S", regionName: "Sul", ibgeCode: 42 }, - { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 35 }, - { code: "SE", name: "Sergipe", regionCode: "NE", regionName: "Nordeste", ibgeCode: 28 }, - { code: "TO", name: "Tocantins", regionCode: "N", regionName: "Norte", ibgeCode: 17 }, ]; diff --git a/src/_internals/copy-state/copy-state.test.ts b/src/_internals/copy-state/copy-state.test.ts new file mode 100644 index 00000000..41e5663e --- /dev/null +++ b/src/_internals/copy-state/copy-state.test.ts @@ -0,0 +1,19 @@ +import { DATA } from "../constants/states"; +import { describe, expect, test } from "../test/runtime"; +import { copyState } from "./copy-state"; + +describe("copyState", () => { + test("should copy every field of the state", () => { + for (const state of DATA) { + expect(copyState(state)).toStrictEqual(state); + } + }); + + test("should return a fresh state and a fresh capital", () => { + const [state] = DATA; + const copy = copyState(state); + + expect(copy).not.toBe(state); + expect(copy.capital).not.toBe(state.capital); + }); +}); diff --git a/src/_internals/copy-state/copy-state.ts b/src/_internals/copy-state/copy-state.ts new file mode 100644 index 00000000..6bb4807d --- /dev/null +++ b/src/_internals/copy-state/copy-state.ts @@ -0,0 +1,16 @@ +import { type State } from "../constants/states"; + +/** + * Copies a state of `DATA` for a caller: a fresh object whose `capital` is a fresh object too, so + * mutating what a state util returns never reaches the shared data or a later call. + * + * @param {Required} state - An entry of `DATA`, which fills every field. + * @returns {Required} A copy of the state, down to its capital. + * + * @example + * ```typescript + * copyState(DATA[0]); // { code: "AC", ..., capital: { code: "1200401", name: "Rio Branco" } } + * ``` + */ +export const copyState = (state: Required): Required => + Object.assign({}, state, { capital: Object.assign({}, state.capital) }); diff --git a/src/get-state-by-cep/get-state-by-cep.test.ts b/src/get-state-by-cep/get-state-by-cep.test.ts index 5c79f868..a0f51f1e 100644 --- a/src/get-state-by-cep/get-state-by-cep.test.ts +++ b/src/get-state-by-cep/get-state-by-cep.test.ts @@ -1,6 +1,6 @@ import * as fc from "fast-check"; -import { type State } from "../_internals/constants/states"; +import { DATA as STATES, type State } from "../_internals/constants/states"; import { anyGarbage, digits, digitsOfOtherLength } from "../_internals/test/arbitraries"; import { expectNeverThrows } from "../_internals/test/properties"; import { describe, expect, expectTypeOf, it, test } from "../_internals/test/runtime"; @@ -42,13 +42,7 @@ const RANGE_BOUNDARIES: [string, string, string][] = [ describe("getStateByCep", () => { it("should return São Paulo for a formatted CEP of Avenida Paulista", () => { - expect(getStateByCep("01310-100")).toEqual({ - code: "SP", - name: "São Paulo", - regionCode: "SE", - regionName: "Sudeste", - ibgeCode: 35, - }); + expect(getStateByCep("01310-100")).toEqual(STATES.find((state) => state.code === "SP")); }); it("should return Rio de Janeiro for a CEP given as a number", () => { @@ -58,6 +52,8 @@ describe("getStateByCep", () => { regionCode: "SE", regionName: "Sudeste", ibgeCode: 33, + regionIbgeCode: 3, + capital: { code: "3304557", name: "Rio de Janeiro" }, }); }); diff --git a/src/get-state-by-cep/get-state-by-cep.ts b/src/get-state-by-cep/get-state-by-cep.ts index ceef8b40..975f7057 100644 --- a/src/get-state-by-cep/get-state-by-cep.ts +++ b/src/get-state-by-cep/get-state-by-cep.ts @@ -1,4 +1,5 @@ import { DATA, type State } from "../_internals/constants/states"; +import { copyState } from "../_internals/copy-state/copy-state"; import { findCepRange } from "../_internals/find-cep-range/find-cep-range"; import { CEP_RANGES } from "./constants"; @@ -31,7 +32,7 @@ export type { State } from "../_internals/constants/states"; * * @example * ```typescript - * getStateByCep("01310-100"); // { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 35 } + * getStateByCep("01310-100"); // { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 35, regionIbgeCode: 3, capital: { code: "3550308", name: "São Paulo" } } * getStateByCep(20040020); // { code: "RJ", name: "Rio de Janeiro", regionCode: "SE", regionName: "Sudeste", ibgeCode: 33 } * getStateByCep("69300-000")?.code; // "RR" * getStateByCep("72800-000")?.code; // "GO" @@ -44,5 +45,5 @@ export const getStateByCep = (value: string | number): State | null => { const range = findCepRange(value, CEP_RANGES); const state = range && DATA.find((entry) => entry.code === range.state); - return state ? { ...state } : null; + return state ? copyState(state) : null; }; diff --git a/src/get-state-by-ibge-code/get-state-by-ibge-code.test.ts b/src/get-state-by-ibge-code/get-state-by-ibge-code.test.ts index d52ef45f..1c881d99 100644 --- a/src/get-state-by-ibge-code/get-state-by-ibge-code.test.ts +++ b/src/get-state-by-ibge-code/get-state-by-ibge-code.test.ts @@ -14,17 +14,14 @@ describe("getStateByIbgeCode", () => { regionCode: "SE", regionName: "Sudeste", ibgeCode: 35, + regionIbgeCode: 3, + capital: { code: "3550308", name: "São Paulo" }, }); }); it("should return São Paulo for the number code 35", () => { - expect(getStateByIbgeCode(35)).toEqual({ - code: "SP", - name: "São Paulo", - regionCode: "SE", - regionName: "Sudeste", - ibgeCode: 35, - }); + expect(getStateByIbgeCode(35)).toEqual(getStateByIbgeCode("35")); + expect(getStateByIbgeCode(35)?.code).toBe("SP"); }); it("should return Rondônia for the code 11, the first cUF in the IBGE table", () => { diff --git a/src/get-state-by-ibge-code/get-state-by-ibge-code.ts b/src/get-state-by-ibge-code/get-state-by-ibge-code.ts index ef4bf099..257383a8 100644 --- a/src/get-state-by-ibge-code/get-state-by-ibge-code.ts +++ b/src/get-state-by-ibge-code/get-state-by-ibge-code.ts @@ -1,4 +1,5 @@ import { DATA, type State } from "../_internals/constants/states"; +import { copyState } from "../_internals/copy-state/copy-state"; import { readLookupDigits } from "../_internals/read-lookup-digits/read-lookup-digits"; export type { State } from "../_internals/constants/states"; @@ -28,9 +29,9 @@ export type { State } from "../_internals/constants/states"; * * @example * ```typescript - * getStateByIbgeCode("35"); // { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 35 } - * getStateByIbgeCode(35); // { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 35 } - * getStateByIbgeCode("11"); // { code: "RO", name: "Rondônia", regionCode: "N", regionName: "Norte", ibgeCode: 11 } + * getStateByIbgeCode("35"); // { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 35, regionIbgeCode: 3, capital: { code: "3550308", name: "São Paulo" } } + * getStateByIbgeCode(35); // { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 35, regionIbgeCode: 3, capital: { code: "3550308", name: "São Paulo" } } + * getStateByIbgeCode("11"); // { code: "RO", name: "Rondônia", regionCode: "N", regionName: "Norte", ibgeCode: 11, regionIbgeCode: 1, capital: { code: "1100205", name: "Porto Velho" } } * getStateByIbgeCode("00"); // null * getStateByIbgeCode(""); // null * getStateByIbgeCode(-35); // null @@ -47,5 +48,5 @@ export const getStateByIbgeCode = (code: string | number): State | null => { const state = DATA.find((entry) => entry.ibgeCode === numericCode); - return state ? { ...state } : null; + return state ? copyState(state) : null; }; diff --git a/src/get-state-capital/get-state-capital.ts b/src/get-state-capital/get-state-capital.ts index bdb4f50e..028e62bc 100644 --- a/src/get-state-capital/get-state-capital.ts +++ b/src/get-state-capital/get-state-capital.ts @@ -1,12 +1,12 @@ import { type Municipality } from "../_internals/constants/municipalities"; -import { STATE_CAPITALS } from "../_internals/constants/state-capitals"; -import { isStateCode } from "../_internals/is-state-code/is-state-code"; +import { DATA } from "../_internals/constants/states"; export type { Municipality } from "../_internals/constants/municipalities"; /** * Retrieves the capital of a Brazilian state, as the same `{ code, name, stateCode }` that - * `getMunicipalityByCode` returns for it. The match is case-insensitive and ignores leading and + * `getMunicipalityByCode` returns for it: the `capital` of the state `getStates` returns, with the + * state code added. The match is case-insensitive and ignores leading and * trailing whitespace, like `getTimezoneByState`. * * For the Distrito Federal, which has no municipalities, the capital is Brasília, with the code @@ -33,9 +33,9 @@ export const getStateCapital = (stateCode: string): Municipality | null => { const normalized = stateCode.trim().toUpperCase(); - if (!isStateCode(normalized)) return null; + const state = DATA.find((entry) => entry.code === normalized); - const [name, code] = STATE_CAPITALS[normalized]; - - return { code, name, stateCode: normalized }; + return state + ? { code: state.capital.code, name: state.capital.name, stateCode: state.code } + : null; }; diff --git a/src/get-states-by-region/get-states-by-region.ts b/src/get-states-by-region/get-states-by-region.ts index 2584653a..f8d484ea 100644 --- a/src/get-states-by-region/get-states-by-region.ts +++ b/src/get-states-by-region/get-states-by-region.ts @@ -1,4 +1,5 @@ import { DATA, type State } from "../_internals/constants/states"; +import { copyState } from "../_internals/copy-state/copy-state"; export type { State } from "../_internals/constants/states"; @@ -30,7 +31,5 @@ export const getStatesByRegion = (regionCode: string): State[] => { const normalized = regionCode.trim().toUpperCase(); - return DATA.filter((state) => state.regionCode === normalized).map((state) => - Object.assign({}, state), - ); + return DATA.filter((state) => state.regionCode === normalized).map((state) => copyState(state)); }; diff --git a/src/get-states/get-states.test.ts b/src/get-states/get-states.test.ts index c628b676..ec5586d9 100644 --- a/src/get-states/get-states.test.ts +++ b/src/get-states/get-states.test.ts @@ -2,6 +2,8 @@ import * as fc from "fast-check"; import { DATA, type State, type StateCode, type StateName } from "../_internals/constants/states"; import { describe, expect, expectTypeOf, it, test } from "../_internals/test/runtime"; +import { getMunicipalityByCode } from "../get-municipality-by-code/get-municipality-by-code"; +import { getRegions } from "../get-regions/get-regions"; import { getStateByIbgeCode } from "../get-state-by-ibge-code/get-state-by-ibge-code"; import { getStateCodeByName } from "../get-state-code-by-name/get-state-code-by-name"; import { getStateNameByCode } from "../get-state-name-by-code/get-state-name-by-code"; @@ -72,6 +74,22 @@ describe("getStates", () => { expect(second).toEqual(DATA.map((state) => Object.assign({}, state))); }); + it("should copy the capital too, so mutating it does not leak between calls", () => { + const [first] = getStates(); + + Object.assign(first?.capital ?? {}, { name: "X" }); + + expect(getStates().at(0)?.capital).toEqual({ code: "1200401", name: "Rio Branco" }); + expect(DATA.at(0)?.capital.name).toBe("Rio Branco"); + }); + + it("should give the Distrito Federal Brasília, the code of the whole district", () => { + expect(getStates().find((state) => state.code === "DF")?.capital).toEqual({ + code: "5300108", + name: "Brasília", + }); + }); + describe("properties", () => { test("should have a code and name that are inverses of each other", () => { fc.assert( @@ -89,6 +107,29 @@ describe("getStates", () => { }), ); }); + + test("should have a capital that getMunicipalityByCode finds in the same state", () => { + fc.assert( + fc.property(stateArbitrary(), (state) => { + expect(getMunicipalityByCode(state.capital?.code ?? "")).toEqual({ + code: state.capital?.code, + name: state.capital?.name, + stateCode: state.code, + }); + }), + ); + }); + + test("should have the regionIbgeCode of its region, the first digit of its ibgeCode", () => { + fc.assert( + fc.property(stateArbitrary(), (state) => { + const region = getRegions().find((entry) => entry.code === state.regionCode); + + expect(state.regionIbgeCode).toBe(region?.ibgeCode); + expect(state.regionIbgeCode).toBe(Math.floor(state.ibgeCode / 10)); + }), + ); + }); }); }); @@ -109,6 +150,10 @@ describe("getStates types", () => { expectTypeOf["regionCode"]>().toEqualTypeOf<"SE">(); expectTypeOf["regionName"]>().toEqualTypeOf<"Sudeste">(); expectTypeOf["ibgeCode"]>().toEqualTypeOf<35>(); + expectTypeOf["regionIbgeCode"]>().toEqualTypeOf<3 | undefined>(); + expectTypeOf["capital"]>().toEqualTypeOf< + { readonly code: "3550308"; readonly name: "São Paulo" } | undefined + >(); expectTypeOf["name"]>().toEqualTypeOf<"Acre">(); expectTypeOf>().toEqualTypeOf(); }); diff --git a/src/get-states/get-states.ts b/src/get-states/get-states.ts index 460e2876..9398119a 100644 --- a/src/get-states/get-states.ts +++ b/src/get-states/get-states.ts @@ -1,4 +1,5 @@ import { DATA, type State } from "../_internals/constants/states"; +import { copyState } from "../_internals/copy-state/copy-state"; export type { State } from "../_internals/constants/states"; @@ -18,9 +19,9 @@ export type { State } from "../_internals/constants/states"; * * @example * ```typescript - * getStates()[0]; // { code: "AC", name: "Acre", regionCode: "N", regionName: "Norte", ibgeCode: 12 } + * getStates()[0]; // { code: "AC", name: "Acre", regionCode: "N", regionName: "Norte", ibgeCode: 12, regionIbgeCode: 1, capital: { code: "1200401", name: "Rio Branco" } } * ``` * * @see Official: https://servicodados.ibge.gov.br/api/docs/localidades */ -export const getStates = (): State[] => DATA.map((state) => Object.assign({}, state)); +export const getStates = (): State[] => DATA.map((state) => copyState(state)); From a45dbbd6ab5bc7deb0e8ec8fc3c0f961b118ed93 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 27 Sep 2026 21:20:09 +0000 Subject: [PATCH 3/5] perf(states): keep the capitals out of the shared state table The capitals in DATA made every state util bundle them, even getStateNameByCode, getStateCodeByName and getAreaCodeInfo (+1.6 KB each). They are back in state-capitals.ts, and completeState adds them, with regionIbgeCode (the first digit of ibgeCode), only in the utils that return a State: getStates, getStateByIbgeCode, getStateByCep and getStatesByRegion. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RLkm9YrtAifc6XCLFVEsdH --- .../complete-state/complete-state.test.ts | 33 +++ .../complete-state/complete-state.ts | 28 ++ src/_internals/constants/state-capitals.ts | 44 +++ src/_internals/constants/states.ts | 260 ++---------------- src/_internals/copy-state/copy-state.test.ts | 19 -- src/_internals/copy-state/copy-state.ts | 16 -- src/get-state-by-cep/get-state-by-cep.test.ts | 5 +- src/get-state-by-cep/get-state-by-cep.ts | 4 +- .../get-state-by-ibge-code.ts | 4 +- src/get-state-capital/get-state-capital.ts | 14 +- .../get-states-by-region.ts | 6 +- src/get-states/get-states.test.ts | 5 +- src/get-states/get-states.ts | 4 +- 13 files changed, 156 insertions(+), 286 deletions(-) create mode 100644 src/_internals/complete-state/complete-state.test.ts create mode 100644 src/_internals/complete-state/complete-state.ts create mode 100644 src/_internals/constants/state-capitals.ts delete mode 100644 src/_internals/copy-state/copy-state.test.ts delete mode 100644 src/_internals/copy-state/copy-state.ts diff --git a/src/_internals/complete-state/complete-state.test.ts b/src/_internals/complete-state/complete-state.test.ts new file mode 100644 index 00000000..ca914b26 --- /dev/null +++ b/src/_internals/complete-state/complete-state.test.ts @@ -0,0 +1,33 @@ +import { DATA } from "../constants/states"; +import { describe, expect, test } from "../test/runtime"; +import { completeState } from "./complete-state"; + +describe("completeState", () => { + test("should keep every field of the state", () => { + for (const state of DATA) { + expect(completeState(state)).toMatchObject(state); + } + }); + + test("should add the region IBGE code and the capital", () => { + const saoPaulo = DATA.find((state) => state.code === "SP"); + + expect(saoPaulo && completeState(saoPaulo)).toStrictEqual({ + code: "SP", + name: "São Paulo", + regionCode: "SE", + regionName: "Sudeste", + ibgeCode: 35, + regionIbgeCode: 3, + capital: { code: "3550308", name: "São Paulo" }, + }); + }); + + test("should return a fresh state, leaving the entry of DATA as it is", () => { + const [state] = DATA; + const completed = completeState(state); + + expect(completed).not.toBe(state); + expect(state).not.toHaveProperty("capital"); + }); +}); diff --git a/src/_internals/complete-state/complete-state.ts b/src/_internals/complete-state/complete-state.ts new file mode 100644 index 00000000..9347ca76 --- /dev/null +++ b/src/_internals/complete-state/complete-state.ts @@ -0,0 +1,28 @@ +import { STATE_CAPITALS } from "../constants/state-capitals"; +import { type State } from "../constants/states"; + +/** + * Completes a state of `DATA` for a caller: a fresh object with the fields `DATA` leaves out, + * the IBGE identifier of its region (the first digit of `ibgeCode`) and its capital, so mutating + * what a state util returns never reaches the shared data or a later call. + * + * @param {State} state - An entry of `DATA`. + * @returns {State} A fresh copy of the state, with `regionIbgeCode` and a fresh `capital`. + * + * @example + * ```typescript + * completeState(DATA[0]); // { code: "AC", ..., regionIbgeCode: 1, capital: { code: "1200401", name: "Rio Branco" } } + * ``` + * + * @see Official: https://servicodados.ibge.gov.br/api/v1/localidades/regioes + * IBGE, API de Localidades, `regioes`: the identifier of each region, the first digit of the code + * of each of its states. + */ +export const completeState = (state: State): State => { + const [name, code] = STATE_CAPITALS[state.code]; + + return Object.assign({}, state, { + regionIbgeCode: Math.floor(state.ibgeCode / 10), + capital: { code, name }, + }); +}; diff --git a/src/_internals/constants/state-capitals.ts b/src/_internals/constants/state-capitals.ts new file mode 100644 index 00000000..fd196593 --- /dev/null +++ b/src/_internals/constants/state-capitals.ts @@ -0,0 +1,44 @@ +import { type StateCode } from "./states"; + +/** + * The capital of each state, as `[name, ibgeCode]`: the municipality name as the IBGE spells it + * and its 7 digit IBGE code, the same pair `getMunicipalityByCode` finds for that code. For the + * Distrito Federal, whose territory is not divided into municipalities, the entry is Brasília, + * the code the IBGE gives the whole district. + * + * @see Official: https://anuario.ibge.gov.br/2024/territorio/posicao-e-extensao.html + * IBGE, Anuário Estatístico do Brasil, Tabela 1.1.1.2, "Localização geográfica, altitude e + * distância a Brasília, segundo os Municípios das Capitais - 2025": one capital per state, + * written "Porto Velho (RO)" to "Brasília (DF)". + * @see Official: https://geoftp.ibge.gov.br/organizacao_do_territorio/estrutura_territorial/divisao_territorial/2025/DTB_2025.zip + * IBGE, Divisão Territorial Brasileira 2025, the municipality codes. + */ +export const STATE_CAPITALS: Record = { + AC: ["Rio Branco", "1200401"], + AL: ["Maceió", "2704302"], + AM: ["Manaus", "1302603"], + AP: ["Macapá", "1600303"], + BA: ["Salvador", "2927408"], + CE: ["Fortaleza", "2304400"], + DF: ["Brasília", "5300108"], + ES: ["Vitória", "3205309"], + GO: ["Goiânia", "5208707"], + MA: ["São Luís", "2111300"], + MG: ["Belo Horizonte", "3106200"], + MS: ["Campo Grande", "5002704"], + MT: ["Cuiabá", "5103403"], + PA: ["Belém", "1501402"], + PB: ["João Pessoa", "2507507"], + PE: ["Recife", "2611606"], + PI: ["Teresina", "2211001"], + PR: ["Curitiba", "4106902"], + RJ: ["Rio de Janeiro", "3304557"], + RN: ["Natal", "2408102"], + RO: ["Porto Velho", "1100205"], + RR: ["Boa Vista", "1400100"], + RS: ["Porto Alegre", "4314902"], + SC: ["Florianópolis", "4205407"], + SE: ["Aracaju", "2800308"], + SP: ["São Paulo", "3550308"], + TO: ["Palmas", "1721000"], +}; diff --git a/src/_internals/constants/states.ts b/src/_internals/constants/states.ts index 47ee0176..06d8611b 100644 --- a/src/_internals/constants/states.ts +++ b/src/_internals/constants/states.ts @@ -271,260 +271,56 @@ export type StateName = State["name"]; /** * Brazilian states published by the IBGE, sorted by name with `localeCompare` in the "pt-BR" * locale. `ibgeCode` is the 2-digit IBGE code of the Federative Unit ("cUF"), the same code - * found in the first field of every DF-e access key (chave de acesso). Every entry fills the - * optional fields of `State` too. + * found in the first field of every DF-e access key (chave de acesso). The optional fields of + * `State` are left out, so that the utils that only read a name or a code do not bundle the + * capitals: `completeState` adds them to what a util returns. * * @see Official: https://servicodados.ibge.gov.br/api/docs/localidades - * @see Official: https://servicodados.ibge.gov.br/api/v1/localidades/regioes - * IBGE, API de Localidades, `regioes`: the identifier of each region. - * @see Official: https://anuario.ibge.gov.br/2024/territorio/posicao-e-extensao.html - * IBGE, Anuário Estatístico do Brasil, Tabela 1.1.1.2, "Localização geográfica, altitude e - * distância a Brasília, segundo os Municípios das Capitais - 2025": one capital per state. - * @see Official: https://geoftp.ibge.gov.br/organizacao_do_territorio/estrutura_territorial/divisao_territorial/2025/DTB_2025.zip - * IBGE, Divisão Territorial Brasileira 2025, the municipality codes of the capitals. */ -export const DATA: readonly Required[] = [ - { - code: "AC", - name: "Acre", - regionCode: "N", - regionName: "Norte", - ibgeCode: 12, - regionIbgeCode: 1, - capital: { code: "1200401", name: "Rio Branco" }, - }, - { - code: "AL", - name: "Alagoas", - regionCode: "NE", - regionName: "Nordeste", - ibgeCode: 27, - regionIbgeCode: 2, - capital: { code: "2704302", name: "Maceió" }, - }, - { - code: "AP", - name: "Amapá", - regionCode: "N", - regionName: "Norte", - ibgeCode: 16, - regionIbgeCode: 1, - capital: { code: "1600303", name: "Macapá" }, - }, - { - code: "AM", - name: "Amazonas", - regionCode: "N", - regionName: "Norte", - ibgeCode: 13, - regionIbgeCode: 1, - capital: { code: "1302603", name: "Manaus" }, - }, - { - code: "BA", - name: "Bahia", - regionCode: "NE", - regionName: "Nordeste", - ibgeCode: 29, - regionIbgeCode: 2, - capital: { code: "2927408", name: "Salvador" }, - }, - { - code: "CE", - name: "Ceará", - regionCode: "NE", - regionName: "Nordeste", - ibgeCode: 23, - regionIbgeCode: 2, - capital: { code: "2304400", name: "Fortaleza" }, - }, +export const DATA: readonly State[] = [ + { code: "AC", name: "Acre", regionCode: "N", regionName: "Norte", ibgeCode: 12 }, + { code: "AL", name: "Alagoas", regionCode: "NE", regionName: "Nordeste", ibgeCode: 27 }, + { code: "AP", name: "Amapá", regionCode: "N", regionName: "Norte", ibgeCode: 16 }, + { code: "AM", name: "Amazonas", regionCode: "N", regionName: "Norte", ibgeCode: 13 }, + { code: "BA", name: "Bahia", regionCode: "NE", regionName: "Nordeste", ibgeCode: 29 }, + { code: "CE", name: "Ceará", regionCode: "NE", regionName: "Nordeste", ibgeCode: 23 }, { code: "DF", name: "Distrito Federal", regionCode: "CO", regionName: "Centro-Oeste", ibgeCode: 53, - regionIbgeCode: 5, - capital: { code: "5300108", name: "Brasília" }, - }, - { - code: "ES", - name: "Espírito Santo", - regionCode: "SE", - regionName: "Sudeste", - ibgeCode: 32, - regionIbgeCode: 3, - capital: { code: "3205309", name: "Vitória" }, - }, - { - code: "GO", - name: "Goiás", - regionCode: "CO", - regionName: "Centro-Oeste", - ibgeCode: 52, - regionIbgeCode: 5, - capital: { code: "5208707", name: "Goiânia" }, - }, - { - code: "MA", - name: "Maranhão", - regionCode: "NE", - regionName: "Nordeste", - ibgeCode: 21, - regionIbgeCode: 2, - capital: { code: "2111300", name: "São Luís" }, - }, - { - code: "MT", - name: "Mato Grosso", - regionCode: "CO", - regionName: "Centro-Oeste", - ibgeCode: 51, - regionIbgeCode: 5, - capital: { code: "5103403", name: "Cuiabá" }, }, + { code: "ES", name: "Espírito Santo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 32 }, + { code: "GO", name: "Goiás", regionCode: "CO", regionName: "Centro-Oeste", ibgeCode: 52 }, + { code: "MA", name: "Maranhão", regionCode: "NE", regionName: "Nordeste", ibgeCode: 21 }, + { code: "MT", name: "Mato Grosso", regionCode: "CO", regionName: "Centro-Oeste", ibgeCode: 51 }, { code: "MS", name: "Mato Grosso do Sul", regionCode: "CO", regionName: "Centro-Oeste", ibgeCode: 50, - regionIbgeCode: 5, - capital: { code: "5002704", name: "Campo Grande" }, - }, - { - code: "MG", - name: "Minas Gerais", - regionCode: "SE", - regionName: "Sudeste", - ibgeCode: 31, - regionIbgeCode: 3, - capital: { code: "3106200", name: "Belo Horizonte" }, - }, - { - code: "PA", - name: "Pará", - regionCode: "N", - regionName: "Norte", - ibgeCode: 15, - regionIbgeCode: 1, - capital: { code: "1501402", name: "Belém" }, - }, - { - code: "PB", - name: "Paraíba", - regionCode: "NE", - regionName: "Nordeste", - ibgeCode: 25, - regionIbgeCode: 2, - capital: { code: "2507507", name: "João Pessoa" }, - }, - { - code: "PR", - name: "Paraná", - regionCode: "S", - regionName: "Sul", - ibgeCode: 41, - regionIbgeCode: 4, - capital: { code: "4106902", name: "Curitiba" }, - }, - { - code: "PE", - name: "Pernambuco", - regionCode: "NE", - regionName: "Nordeste", - ibgeCode: 26, - regionIbgeCode: 2, - capital: { code: "2611606", name: "Recife" }, - }, - { - code: "PI", - name: "Piauí", - regionCode: "NE", - regionName: "Nordeste", - ibgeCode: 22, - regionIbgeCode: 2, - capital: { code: "2211001", name: "Teresina" }, - }, - { - code: "RJ", - name: "Rio de Janeiro", - regionCode: "SE", - regionName: "Sudeste", - ibgeCode: 33, - regionIbgeCode: 3, - capital: { code: "3304557", name: "Rio de Janeiro" }, }, + { code: "MG", name: "Minas Gerais", regionCode: "SE", regionName: "Sudeste", ibgeCode: 31 }, + { code: "PA", name: "Pará", regionCode: "N", regionName: "Norte", ibgeCode: 15 }, + { code: "PB", name: "Paraíba", regionCode: "NE", regionName: "Nordeste", ibgeCode: 25 }, + { code: "PR", name: "Paraná", regionCode: "S", regionName: "Sul", ibgeCode: 41 }, + { code: "PE", name: "Pernambuco", regionCode: "NE", regionName: "Nordeste", ibgeCode: 26 }, + { code: "PI", name: "Piauí", regionCode: "NE", regionName: "Nordeste", ibgeCode: 22 }, + { code: "RJ", name: "Rio de Janeiro", regionCode: "SE", regionName: "Sudeste", ibgeCode: 33 }, { code: "RN", name: "Rio Grande do Norte", regionCode: "NE", regionName: "Nordeste", ibgeCode: 24, - regionIbgeCode: 2, - capital: { code: "2408102", name: "Natal" }, - }, - { - code: "RS", - name: "Rio Grande do Sul", - regionCode: "S", - regionName: "Sul", - ibgeCode: 43, - regionIbgeCode: 4, - capital: { code: "4314902", name: "Porto Alegre" }, - }, - { - code: "RO", - name: "Rondônia", - regionCode: "N", - regionName: "Norte", - ibgeCode: 11, - regionIbgeCode: 1, - capital: { code: "1100205", name: "Porto Velho" }, - }, - { - code: "RR", - name: "Roraima", - regionCode: "N", - regionName: "Norte", - ibgeCode: 14, - regionIbgeCode: 1, - capital: { code: "1400100", name: "Boa Vista" }, - }, - { - code: "SC", - name: "Santa Catarina", - regionCode: "S", - regionName: "Sul", - ibgeCode: 42, - regionIbgeCode: 4, - capital: { code: "4205407", name: "Florianópolis" }, - }, - { - code: "SP", - name: "São Paulo", - regionCode: "SE", - regionName: "Sudeste", - ibgeCode: 35, - regionIbgeCode: 3, - capital: { code: "3550308", name: "São Paulo" }, - }, - { - code: "SE", - name: "Sergipe", - regionCode: "NE", - regionName: "Nordeste", - ibgeCode: 28, - regionIbgeCode: 2, - capital: { code: "2800308", name: "Aracaju" }, - }, - { - code: "TO", - name: "Tocantins", - regionCode: "N", - regionName: "Norte", - ibgeCode: 17, - regionIbgeCode: 1, - capital: { code: "1721000", name: "Palmas" }, }, + { code: "RS", name: "Rio Grande do Sul", regionCode: "S", regionName: "Sul", ibgeCode: 43 }, + { code: "RO", name: "Rondônia", regionCode: "N", regionName: "Norte", ibgeCode: 11 }, + { code: "RR", name: "Roraima", regionCode: "N", regionName: "Norte", ibgeCode: 14 }, + { code: "SC", name: "Santa Catarina", regionCode: "S", regionName: "Sul", ibgeCode: 42 }, + { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 35 }, + { code: "SE", name: "Sergipe", regionCode: "NE", regionName: "Nordeste", ibgeCode: 28 }, + { code: "TO", name: "Tocantins", regionCode: "N", regionName: "Norte", ibgeCode: 17 }, ]; diff --git a/src/_internals/copy-state/copy-state.test.ts b/src/_internals/copy-state/copy-state.test.ts deleted file mode 100644 index 41e5663e..00000000 --- a/src/_internals/copy-state/copy-state.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { DATA } from "../constants/states"; -import { describe, expect, test } from "../test/runtime"; -import { copyState } from "./copy-state"; - -describe("copyState", () => { - test("should copy every field of the state", () => { - for (const state of DATA) { - expect(copyState(state)).toStrictEqual(state); - } - }); - - test("should return a fresh state and a fresh capital", () => { - const [state] = DATA; - const copy = copyState(state); - - expect(copy).not.toBe(state); - expect(copy.capital).not.toBe(state.capital); - }); -}); diff --git a/src/_internals/copy-state/copy-state.ts b/src/_internals/copy-state/copy-state.ts deleted file mode 100644 index 6bb4807d..00000000 --- a/src/_internals/copy-state/copy-state.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { type State } from "../constants/states"; - -/** - * Copies a state of `DATA` for a caller: a fresh object whose `capital` is a fresh object too, so - * mutating what a state util returns never reaches the shared data or a later call. - * - * @param {Required} state - An entry of `DATA`, which fills every field. - * @returns {Required} A copy of the state, down to its capital. - * - * @example - * ```typescript - * copyState(DATA[0]); // { code: "AC", ..., capital: { code: "1200401", name: "Rio Branco" } } - * ``` - */ -export const copyState = (state: Required): Required => - Object.assign({}, state, { capital: Object.assign({}, state.capital) }); diff --git a/src/get-state-by-cep/get-state-by-cep.test.ts b/src/get-state-by-cep/get-state-by-cep.test.ts index a0f51f1e..3e3d850f 100644 --- a/src/get-state-by-cep/get-state-by-cep.test.ts +++ b/src/get-state-by-cep/get-state-by-cep.test.ts @@ -1,9 +1,10 @@ import * as fc from "fast-check"; -import { DATA as STATES, type State } from "../_internals/constants/states"; +import { type State } from "../_internals/constants/states"; import { anyGarbage, digits, digitsOfOtherLength } from "../_internals/test/arbitraries"; import { expectNeverThrows } from "../_internals/test/properties"; import { describe, expect, expectTypeOf, it, test } from "../_internals/test/runtime"; +import { getStateByIbgeCode } from "../get-state-by-ibge-code/get-state-by-ibge-code"; import { CEP_RANGES } from "./constants"; import { getStateByCep } from "./get-state-by-cep"; @@ -42,7 +43,7 @@ const RANGE_BOUNDARIES: [string, string, string][] = [ describe("getStateByCep", () => { it("should return São Paulo for a formatted CEP of Avenida Paulista", () => { - expect(getStateByCep("01310-100")).toEqual(STATES.find((state) => state.code === "SP")); + expect(getStateByCep("01310-100")).toEqual(getStateByIbgeCode(35)); }); it("should return Rio de Janeiro for a CEP given as a number", () => { diff --git a/src/get-state-by-cep/get-state-by-cep.ts b/src/get-state-by-cep/get-state-by-cep.ts index 975f7057..507ca08b 100644 --- a/src/get-state-by-cep/get-state-by-cep.ts +++ b/src/get-state-by-cep/get-state-by-cep.ts @@ -1,5 +1,5 @@ +import { completeState } from "../_internals/complete-state/complete-state"; import { DATA, type State } from "../_internals/constants/states"; -import { copyState } from "../_internals/copy-state/copy-state"; import { findCepRange } from "../_internals/find-cep-range/find-cep-range"; import { CEP_RANGES } from "./constants"; @@ -45,5 +45,5 @@ export const getStateByCep = (value: string | number): State | null => { const range = findCepRange(value, CEP_RANGES); const state = range && DATA.find((entry) => entry.code === range.state); - return state ? copyState(state) : null; + return state ? completeState(state) : null; }; diff --git a/src/get-state-by-ibge-code/get-state-by-ibge-code.ts b/src/get-state-by-ibge-code/get-state-by-ibge-code.ts index 257383a8..43b3e7f9 100644 --- a/src/get-state-by-ibge-code/get-state-by-ibge-code.ts +++ b/src/get-state-by-ibge-code/get-state-by-ibge-code.ts @@ -1,5 +1,5 @@ +import { completeState } from "../_internals/complete-state/complete-state"; import { DATA, type State } from "../_internals/constants/states"; -import { copyState } from "../_internals/copy-state/copy-state"; import { readLookupDigits } from "../_internals/read-lookup-digits/read-lookup-digits"; export type { State } from "../_internals/constants/states"; @@ -48,5 +48,5 @@ export const getStateByIbgeCode = (code: string | number): State | null => { const state = DATA.find((entry) => entry.ibgeCode === numericCode); - return state ? copyState(state) : null; + return state ? completeState(state) : null; }; diff --git a/src/get-state-capital/get-state-capital.ts b/src/get-state-capital/get-state-capital.ts index 028e62bc..bdb4f50e 100644 --- a/src/get-state-capital/get-state-capital.ts +++ b/src/get-state-capital/get-state-capital.ts @@ -1,12 +1,12 @@ import { type Municipality } from "../_internals/constants/municipalities"; -import { DATA } from "../_internals/constants/states"; +import { STATE_CAPITALS } from "../_internals/constants/state-capitals"; +import { isStateCode } from "../_internals/is-state-code/is-state-code"; export type { Municipality } from "../_internals/constants/municipalities"; /** * Retrieves the capital of a Brazilian state, as the same `{ code, name, stateCode }` that - * `getMunicipalityByCode` returns for it: the `capital` of the state `getStates` returns, with the - * state code added. The match is case-insensitive and ignores leading and + * `getMunicipalityByCode` returns for it. The match is case-insensitive and ignores leading and * trailing whitespace, like `getTimezoneByState`. * * For the Distrito Federal, which has no municipalities, the capital is Brasília, with the code @@ -33,9 +33,9 @@ export const getStateCapital = (stateCode: string): Municipality | null => { const normalized = stateCode.trim().toUpperCase(); - const state = DATA.find((entry) => entry.code === normalized); + if (!isStateCode(normalized)) return null; - return state - ? { code: state.capital.code, name: state.capital.name, stateCode: state.code } - : null; + const [name, code] = STATE_CAPITALS[normalized]; + + return { code, name, stateCode: normalized }; }; diff --git a/src/get-states-by-region/get-states-by-region.ts b/src/get-states-by-region/get-states-by-region.ts index f8d484ea..6c585886 100644 --- a/src/get-states-by-region/get-states-by-region.ts +++ b/src/get-states-by-region/get-states-by-region.ts @@ -1,5 +1,5 @@ +import { completeState } from "../_internals/complete-state/complete-state"; import { DATA, type State } from "../_internals/constants/states"; -import { copyState } from "../_internals/copy-state/copy-state"; export type { State } from "../_internals/constants/states"; @@ -31,5 +31,7 @@ export const getStatesByRegion = (regionCode: string): State[] => { const normalized = regionCode.trim().toUpperCase(); - return DATA.filter((state) => state.regionCode === normalized).map((state) => copyState(state)); + return DATA.filter((state) => state.regionCode === normalized).map((state) => + completeState(state), + ); }; diff --git a/src/get-states/get-states.test.ts b/src/get-states/get-states.test.ts index ec5586d9..570268b6 100644 --- a/src/get-states/get-states.test.ts +++ b/src/get-states/get-states.test.ts @@ -1,5 +1,6 @@ import * as fc from "fast-check"; +import { completeState } from "../_internals/complete-state/complete-state"; import { DATA, type State, type StateCode, type StateName } from "../_internals/constants/states"; import { describe, expect, expectTypeOf, it, test } from "../_internals/test/runtime"; import { getMunicipalityByCode } from "../get-municipality-by-code/get-municipality-by-code"; @@ -71,7 +72,7 @@ describe("getStates", () => { const second = getStates(); expect(second.at(0)?.name).not.toBe("X"); - expect(second).toEqual(DATA.map((state) => Object.assign({}, state))); + expect(second).toEqual(DATA.map((state) => completeState(state))); }); it("should copy the capital too, so mutating it does not leak between calls", () => { @@ -80,7 +81,7 @@ describe("getStates", () => { Object.assign(first?.capital ?? {}, { name: "X" }); expect(getStates().at(0)?.capital).toEqual({ code: "1200401", name: "Rio Branco" }); - expect(DATA.at(0)?.capital.name).toBe("Rio Branco"); + expect(DATA.at(0)).not.toHaveProperty("capital"); }); it("should give the Distrito Federal Brasília, the code of the whole district", () => { diff --git a/src/get-states/get-states.ts b/src/get-states/get-states.ts index 9398119a..140de94a 100644 --- a/src/get-states/get-states.ts +++ b/src/get-states/get-states.ts @@ -1,5 +1,5 @@ +import { completeState } from "../_internals/complete-state/complete-state"; import { DATA, type State } from "../_internals/constants/states"; -import { copyState } from "../_internals/copy-state/copy-state"; export type { State } from "../_internals/constants/states"; @@ -24,4 +24,4 @@ export type { State } from "../_internals/constants/states"; * * @see Official: https://servicodados.ibge.gov.br/api/docs/localidades */ -export const getStates = (): State[] => DATA.map((state) => copyState(state)); +export const getStates = (): State[] => DATA.map((state) => completeState(state)); From e8516e874e8e719ece077db9e30699419090ff80 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 27 Sep 2026 21:20:27 +0000 Subject: [PATCH 4/5] docs(contributing): list state-capitals.ts with the state tables Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RLkm9YrtAifc6XCLFVEsdH --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 59bd85d7..775ee0e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -117,8 +117,8 @@ itself: 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 processo number (Resolução CNJ nº 65/2008, `processo-juridico.ts`), IBAN lengths per country - (`iban.ts`), IBGE state codes (`ibge-uf-codes.ts`), the states with their regions and capitals - (IBGE, `states.ts`, `regions.ts`), legal nature categories, the CST and CSOSN + (`iban.ts`), IBGE state codes (`ibge-uf-codes.ts`), the states, their capitals and the regions + (IBGE, `states.ts`, `state-capitals.ts`, `regions.ts`), legal nature categories, the CST and CSOSN tables (`src/is-valid-cst`, `src/is-valid-csosn`), the professional councils (`src/is-valid-registro-profissional`), the região fiscal digit of each state (`src/_internals/constants/cpf.ts`) and the voter ID state codes (`src/is-valid-voter-id`). A change to one of From 5c976201b6d9dda60ed8128d4b2849477e5c9492 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 27 Sep 2026 23:14:40 +0000 Subject: [PATCH 5/5] revert(states): keep State as published, the capital stays in getStateCapital Adding `capital` and `regionIbgeCode` to State grew getStates, getStateByIbgeCode and getStateByCep by about 870 B each for data most callers do not read. State stays as 2.4.0 published it; the capital is one call away with getStateCapital, and the region identifier with getRegions. This reverts commits 5df7084, a45dbbd and e8516e8. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RLkm9YrtAifc6XCLFVEsdH --- CONTRIBUTING.md | 4 +- docs/pt-br/utilities.md | 63 +++++++++-------- docs/utilities.md | 63 +++++++++-------- .../complete-state/complete-state.test.ts | 33 --------- .../complete-state/complete-state.ts | 28 -------- src/_internals/constants/states.ts | 69 +------------------ src/get-state-by-cep/get-state-by-cep.test.ts | 11 +-- src/get-state-by-cep/get-state-by-cep.ts | 5 +- .../get-state-by-ibge-code.test.ts | 11 +-- .../get-state-by-ibge-code.ts | 9 ++- .../get-states-by-region.ts | 3 +- src/get-states/get-states.test.ts | 48 +------------ src/get-states/get-states.ts | 5 +- 13 files changed, 91 insertions(+), 261 deletions(-) delete mode 100644 src/_internals/complete-state/complete-state.test.ts delete mode 100644 src/_internals/complete-state/complete-state.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 775ee0e2..cface8c6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -117,8 +117,8 @@ itself: 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 processo number (Resolução CNJ nº 65/2008, `processo-juridico.ts`), IBAN lengths per country - (`iban.ts`), IBGE state codes (`ibge-uf-codes.ts`), the states, their capitals and the regions - (IBGE, `states.ts`, `state-capitals.ts`, `regions.ts`), legal nature categories, the CST and CSOSN + (`iban.ts`), IBGE state codes (`ibge-uf-codes.ts`), the state capitals and the regions (IBGE, + `state-capitals.ts`, `regions.ts`), legal nature categories, the CST and CSOSN tables (`src/is-valid-cst`, `src/is-valid-csosn`), the professional councils (`src/is-valid-registro-profissional`), the região fiscal digit of each state (`src/_internals/constants/cpf.ts`) and the voter ID state codes (`src/is-valid-voter-id`). A change to one of diff --git a/docs/pt-br/utilities.md b/docs/pt-br/utilities.md index 06f78b82..045d6f52 100644 --- a/docs/pt-br/utilities.md +++ b/docs/pt-br/utilities.md @@ -1655,44 +1655,43 @@ convertDateToWords('29/02/1900'); // "" (1900 não é bissexto) ### getStates -Retorna todos os estados brasileiros, cada um com sigla, nome, código da região, nome da região, código IBGE de 2 dígitos (`cUF`), o identificador IBGE da região (`regionIbgeCode`) e a capital (`capital`, o código IBGE de 7 dígitos e o nome do município). +Retorna todos os estados brasileiros, cada um com sigla, nome, código da região, nome da região e código IBGE de 2 dígitos (`cUF`). - Ordenados por nome no locale "pt-BR". - Exporta os tipos `State`, `StateCode` e `StateName`. `State` é uma união discriminada: estreitá-lo pelo `code` também estreita os demais campos. -- `regionIbgeCode` e `capital` vêm sempre preenchidos, mas são opcionais no tipo `State` para que um `State` montado para a 2.4.0 continue compilando; passam a ser obrigatórios na próxima versão major. ```javascript import { getStates } from '@brazilian-utils/brazilian-utils'; getStates(); // [ -// { code: 'AC', name: 'Acre', regionCode: 'N', regionName: 'Norte', ibgeCode: 12, regionIbgeCode: 1, capital: { code: '1200401', name: 'Rio Branco' } }, -// { code: 'AL', name: 'Alagoas', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 27, regionIbgeCode: 2, capital: { code: '2704302', name: 'Maceió' } }, -// { code: 'AP', name: 'Amapá', regionCode: 'N', regionName: 'Norte', ibgeCode: 16, regionIbgeCode: 1, capital: { code: '1600303', name: 'Macapá' } }, -// { code: 'AM', name: 'Amazonas', regionCode: 'N', regionName: 'Norte', ibgeCode: 13, regionIbgeCode: 1, capital: { code: '1302603', name: 'Manaus' } }, -// { code: 'BA', name: 'Bahia', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 29, regionIbgeCode: 2, capital: { code: '2927408', name: 'Salvador' } }, -// { code: 'CE', name: 'Ceará', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 23, regionIbgeCode: 2, capital: { code: '2304400', name: 'Fortaleza' } }, -// { code: 'DF', name: 'Distrito Federal', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 53, regionIbgeCode: 5, capital: { code: '5300108', name: 'Brasília' } }, -// { code: 'ES', name: 'Espírito Santo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 32, regionIbgeCode: 3, capital: { code: '3205309', name: 'Vitória' } }, -// { code: 'GO', name: 'Goiás', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 52, regionIbgeCode: 5, capital: { code: '5208707', name: 'Goiânia' } }, -// { code: 'MA', name: 'Maranhão', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 21, regionIbgeCode: 2, capital: { code: '2111300', name: 'São Luís' } }, -// { code: 'MT', name: 'Mato Grosso', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 51, regionIbgeCode: 5, capital: { code: '5103403', name: 'Cuiabá' } }, -// { code: 'MS', name: 'Mato Grosso do Sul', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 50, regionIbgeCode: 5, capital: { code: '5002704', name: 'Campo Grande' } }, -// { code: 'MG', name: 'Minas Gerais', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 31, regionIbgeCode: 3, capital: { code: '3106200', name: 'Belo Horizonte' } }, -// { code: 'PA', name: 'Pará', regionCode: 'N', regionName: 'Norte', ibgeCode: 15, regionIbgeCode: 1, capital: { code: '1501402', name: 'Belém' } }, -// { code: 'PB', name: 'Paraíba', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 25, regionIbgeCode: 2, capital: { code: '2507507', name: 'João Pessoa' } }, -// { code: 'PR', name: 'Paraná', regionCode: 'S', regionName: 'Sul', ibgeCode: 41, regionIbgeCode: 4, capital: { code: '4106902', name: 'Curitiba' } }, -// { code: 'PE', name: 'Pernambuco', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 26, regionIbgeCode: 2, capital: { code: '2611606', name: 'Recife' } }, -// { code: 'PI', name: 'Piauí', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 22, regionIbgeCode: 2, capital: { code: '2211001', name: 'Teresina' } }, -// { code: 'RJ', name: 'Rio de Janeiro', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 33, regionIbgeCode: 3, capital: { code: '3304557', name: 'Rio de Janeiro' } }, -// { code: 'RN', name: 'Rio Grande do Norte', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 24, regionIbgeCode: 2, capital: { code: '2408102', name: 'Natal' } }, -// { code: 'RS', name: 'Rio Grande do Sul', regionCode: 'S', regionName: 'Sul', ibgeCode: 43, regionIbgeCode: 4, capital: { code: '4314902', name: 'Porto Alegre' } }, -// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11, regionIbgeCode: 1, capital: { code: '1100205', name: 'Porto Velho' } }, -// { code: 'RR', name: 'Roraima', regionCode: 'N', regionName: 'Norte', ibgeCode: 14, regionIbgeCode: 1, capital: { code: '1400100', name: 'Boa Vista' } }, -// { code: 'SC', name: 'Santa Catarina', regionCode: 'S', regionName: 'Sul', ibgeCode: 42, regionIbgeCode: 4, capital: { code: '4205407', name: 'Florianópolis' } }, -// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35, regionIbgeCode: 3, capital: { code: '3550308', name: 'São Paulo' } }, -// { code: 'SE', name: 'Sergipe', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 28, regionIbgeCode: 2, capital: { code: '2800308', name: 'Aracaju' } }, -// { code: 'TO', name: 'Tocantins', regionCode: 'N', regionName: 'Norte', ibgeCode: 17, regionIbgeCode: 1, capital: { code: '1721000', name: 'Palmas' } }, +// { code: 'AC', name: 'Acre', regionCode: 'N', regionName: 'Norte', ibgeCode: 12 }, +// { code: 'AL', name: 'Alagoas', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 27 }, +// { code: 'AP', name: 'Amapá', regionCode: 'N', regionName: 'Norte', ibgeCode: 16 }, +// { code: 'AM', name: 'Amazonas', regionCode: 'N', regionName: 'Norte', ibgeCode: 13 }, +// { code: 'BA', name: 'Bahia', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 29 }, +// { code: 'CE', name: 'Ceará', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 23 }, +// { code: 'DF', name: 'Distrito Federal', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 53 }, +// { code: 'ES', name: 'Espírito Santo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 32 }, +// { code: 'GO', name: 'Goiás', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 52 }, +// { code: 'MA', name: 'Maranhão', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 21 }, +// { code: 'MT', name: 'Mato Grosso', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 51 }, +// { code: 'MS', name: 'Mato Grosso do Sul', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 50 }, +// { code: 'MG', name: 'Minas Gerais', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 31 }, +// { code: 'PA', name: 'Pará', regionCode: 'N', regionName: 'Norte', ibgeCode: 15 }, +// { code: 'PB', name: 'Paraíba', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 25 }, +// { code: 'PR', name: 'Paraná', regionCode: 'S', regionName: 'Sul', ibgeCode: 41 }, +// { code: 'PE', name: 'Pernambuco', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 26 }, +// { code: 'PI', name: 'Piauí', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 22 }, +// { code: 'RJ', name: 'Rio de Janeiro', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 33 }, +// { code: 'RN', name: 'Rio Grande do Norte', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 24 }, +// { code: 'RS', name: 'Rio Grande do Sul', regionCode: 'S', regionName: 'Sul', ibgeCode: 43 }, +// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11 }, +// { code: 'RR', name: 'Roraima', regionCode: 'N', regionName: 'Norte', ibgeCode: 14 }, +// { code: 'SC', name: 'Santa Catarina', regionCode: 'S', regionName: 'Sul', ibgeCode: 42 }, +// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35 }, +// { code: 'SE', name: 'Sergipe', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 28 }, +// { code: 'TO', name: 'Tocantins', regionCode: 'N', regionName: 'Norte', ibgeCode: 17 }, // ] ``` @@ -1712,7 +1711,7 @@ Retorna o estado brasileiro ao qual um CEP pertence, a partir das faixas de CEP import { getStateByCep } from '@brazilian-utils/brazilian-utils'; getStateByCep('01310-100'); -// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35, regionIbgeCode: 3, capital: { code: '3550308', name: 'São Paulo' } } +// { 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 } @@ -1737,10 +1736,10 @@ Retorna o estado brasileiro cujo código IBGE de 2 dígitos (`cUF`, o Código da import { getStateByIbgeCode } from '@brazilian-utils/brazilian-utils'; getStateByIbgeCode('35'); -// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35, regionIbgeCode: 3, capital: { code: '3550308', name: 'São Paulo' } } +// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35 } getStateByIbgeCode(11); -// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11, regionIbgeCode: 1, capital: { code: '1100205', name: 'Porto Velho' } } +// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11 } getStateByIbgeCode('00'); // null getStateByIbgeCode(-35); // null diff --git a/docs/utilities.md b/docs/utilities.md index 64097553..fd2b4c6a 100644 --- a/docs/utilities.md +++ b/docs/utilities.md @@ -1655,44 +1655,43 @@ convertDateToWords('29/02/1900'); // "" (1900 is not a leap year) ### getStates -Get all Brazilian states, each with its two-letter code, name, region code, region name, 2-digit IBGE code (`cUF`), the IBGE identifier of its region (`regionIbgeCode`) and its capital (`capital`, the 7-digit IBGE code and the name of the municipality). +Get all Brazilian states, each with its two-letter code, name, region code, region name and 2-digit IBGE code (`cUF`). - Sorted by name in the "pt-BR" locale. - Exports the `State`, `StateCode` and `StateName` types. `State` is a discriminated union: narrowing it by `code` also narrows the other fields. -- `regionIbgeCode` and `capital` are always filled, but optional in the `State` type so that a `State` built for 2.4.0 still type-checks; they become required in the next major version. ```javascript import { getStates } from '@brazilian-utils/brazilian-utils'; getStates(); // [ -// { code: 'AC', name: 'Acre', regionCode: 'N', regionName: 'Norte', ibgeCode: 12, regionIbgeCode: 1, capital: { code: '1200401', name: 'Rio Branco' } }, -// { code: 'AL', name: 'Alagoas', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 27, regionIbgeCode: 2, capital: { code: '2704302', name: 'Maceió' } }, -// { code: 'AP', name: 'Amapá', regionCode: 'N', regionName: 'Norte', ibgeCode: 16, regionIbgeCode: 1, capital: { code: '1600303', name: 'Macapá' } }, -// { code: 'AM', name: 'Amazonas', regionCode: 'N', regionName: 'Norte', ibgeCode: 13, regionIbgeCode: 1, capital: { code: '1302603', name: 'Manaus' } }, -// { code: 'BA', name: 'Bahia', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 29, regionIbgeCode: 2, capital: { code: '2927408', name: 'Salvador' } }, -// { code: 'CE', name: 'Ceará', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 23, regionIbgeCode: 2, capital: { code: '2304400', name: 'Fortaleza' } }, -// { code: 'DF', name: 'Distrito Federal', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 53, regionIbgeCode: 5, capital: { code: '5300108', name: 'Brasília' } }, -// { code: 'ES', name: 'Espírito Santo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 32, regionIbgeCode: 3, capital: { code: '3205309', name: 'Vitória' } }, -// { code: 'GO', name: 'Goiás', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 52, regionIbgeCode: 5, capital: { code: '5208707', name: 'Goiânia' } }, -// { code: 'MA', name: 'Maranhão', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 21, regionIbgeCode: 2, capital: { code: '2111300', name: 'São Luís' } }, -// { code: 'MT', name: 'Mato Grosso', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 51, regionIbgeCode: 5, capital: { code: '5103403', name: 'Cuiabá' } }, -// { code: 'MS', name: 'Mato Grosso do Sul', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 50, regionIbgeCode: 5, capital: { code: '5002704', name: 'Campo Grande' } }, -// { code: 'MG', name: 'Minas Gerais', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 31, regionIbgeCode: 3, capital: { code: '3106200', name: 'Belo Horizonte' } }, -// { code: 'PA', name: 'Pará', regionCode: 'N', regionName: 'Norte', ibgeCode: 15, regionIbgeCode: 1, capital: { code: '1501402', name: 'Belém' } }, -// { code: 'PB', name: 'Paraíba', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 25, regionIbgeCode: 2, capital: { code: '2507507', name: 'João Pessoa' } }, -// { code: 'PR', name: 'Paraná', regionCode: 'S', regionName: 'Sul', ibgeCode: 41, regionIbgeCode: 4, capital: { code: '4106902', name: 'Curitiba' } }, -// { code: 'PE', name: 'Pernambuco', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 26, regionIbgeCode: 2, capital: { code: '2611606', name: 'Recife' } }, -// { code: 'PI', name: 'Piauí', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 22, regionIbgeCode: 2, capital: { code: '2211001', name: 'Teresina' } }, -// { code: 'RJ', name: 'Rio de Janeiro', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 33, regionIbgeCode: 3, capital: { code: '3304557', name: 'Rio de Janeiro' } }, -// { code: 'RN', name: 'Rio Grande do Norte', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 24, regionIbgeCode: 2, capital: { code: '2408102', name: 'Natal' } }, -// { code: 'RS', name: 'Rio Grande do Sul', regionCode: 'S', regionName: 'Sul', ibgeCode: 43, regionIbgeCode: 4, capital: { code: '4314902', name: 'Porto Alegre' } }, -// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11, regionIbgeCode: 1, capital: { code: '1100205', name: 'Porto Velho' } }, -// { code: 'RR', name: 'Roraima', regionCode: 'N', regionName: 'Norte', ibgeCode: 14, regionIbgeCode: 1, capital: { code: '1400100', name: 'Boa Vista' } }, -// { code: 'SC', name: 'Santa Catarina', regionCode: 'S', regionName: 'Sul', ibgeCode: 42, regionIbgeCode: 4, capital: { code: '4205407', name: 'Florianópolis' } }, -// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35, regionIbgeCode: 3, capital: { code: '3550308', name: 'São Paulo' } }, -// { code: 'SE', name: 'Sergipe', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 28, regionIbgeCode: 2, capital: { code: '2800308', name: 'Aracaju' } }, -// { code: 'TO', name: 'Tocantins', regionCode: 'N', regionName: 'Norte', ibgeCode: 17, regionIbgeCode: 1, capital: { code: '1721000', name: 'Palmas' } }, +// { code: 'AC', name: 'Acre', regionCode: 'N', regionName: 'Norte', ibgeCode: 12 }, +// { code: 'AL', name: 'Alagoas', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 27 }, +// { code: 'AP', name: 'Amapá', regionCode: 'N', regionName: 'Norte', ibgeCode: 16 }, +// { code: 'AM', name: 'Amazonas', regionCode: 'N', regionName: 'Norte', ibgeCode: 13 }, +// { code: 'BA', name: 'Bahia', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 29 }, +// { code: 'CE', name: 'Ceará', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 23 }, +// { code: 'DF', name: 'Distrito Federal', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 53 }, +// { code: 'ES', name: 'Espírito Santo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 32 }, +// { code: 'GO', name: 'Goiás', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 52 }, +// { code: 'MA', name: 'Maranhão', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 21 }, +// { code: 'MT', name: 'Mato Grosso', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 51 }, +// { code: 'MS', name: 'Mato Grosso do Sul', regionCode: 'CO', regionName: 'Centro-Oeste', ibgeCode: 50 }, +// { code: 'MG', name: 'Minas Gerais', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 31 }, +// { code: 'PA', name: 'Pará', regionCode: 'N', regionName: 'Norte', ibgeCode: 15 }, +// { code: 'PB', name: 'Paraíba', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 25 }, +// { code: 'PR', name: 'Paraná', regionCode: 'S', regionName: 'Sul', ibgeCode: 41 }, +// { code: 'PE', name: 'Pernambuco', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 26 }, +// { code: 'PI', name: 'Piauí', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 22 }, +// { code: 'RJ', name: 'Rio de Janeiro', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 33 }, +// { code: 'RN', name: 'Rio Grande do Norte', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 24 }, +// { code: 'RS', name: 'Rio Grande do Sul', regionCode: 'S', regionName: 'Sul', ibgeCode: 43 }, +// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11 }, +// { code: 'RR', name: 'Roraima', regionCode: 'N', regionName: 'Norte', ibgeCode: 14 }, +// { code: 'SC', name: 'Santa Catarina', regionCode: 'S', regionName: 'Sul', ibgeCode: 42 }, +// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35 }, +// { code: 'SE', name: 'Sergipe', regionCode: 'NE', regionName: 'Nordeste', ibgeCode: 28 }, +// { code: 'TO', name: 'Tocantins', regionCode: 'N', regionName: 'Norte', ibgeCode: 17 }, // ] ``` @@ -1712,7 +1711,7 @@ Get the Brazilian state a CEP belongs to, from the CEP ranges the Correios assig import { getStateByCep } from '@brazilian-utils/brazilian-utils'; getStateByCep('01310-100'); -// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35, regionIbgeCode: 3, capital: { code: '3550308', name: 'São Paulo' } } +// { 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 } @@ -1737,10 +1736,10 @@ Get the Brazilian state whose 2-digit IBGE code (`cUF`, the Código da Unidade d import { getStateByIbgeCode } from '@brazilian-utils/brazilian-utils'; getStateByIbgeCode('35'); -// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35, regionIbgeCode: 3, capital: { code: '3550308', name: 'São Paulo' } } +// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35 } getStateByIbgeCode(11); -// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11, regionIbgeCode: 1, capital: { code: '1100205', name: 'Porto Velho' } } +// { code: 'RO', name: 'Rondônia', regionCode: 'N', regionName: 'Norte', ibgeCode: 11 } getStateByIbgeCode('00'); // null getStateByIbgeCode(-35); // null diff --git a/src/_internals/complete-state/complete-state.test.ts b/src/_internals/complete-state/complete-state.test.ts deleted file mode 100644 index ca914b26..00000000 --- a/src/_internals/complete-state/complete-state.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { DATA } from "../constants/states"; -import { describe, expect, test } from "../test/runtime"; -import { completeState } from "./complete-state"; - -describe("completeState", () => { - test("should keep every field of the state", () => { - for (const state of DATA) { - expect(completeState(state)).toMatchObject(state); - } - }); - - test("should add the region IBGE code and the capital", () => { - const saoPaulo = DATA.find((state) => state.code === "SP"); - - expect(saoPaulo && completeState(saoPaulo)).toStrictEqual({ - code: "SP", - name: "São Paulo", - regionCode: "SE", - regionName: "Sudeste", - ibgeCode: 35, - regionIbgeCode: 3, - capital: { code: "3550308", name: "São Paulo" }, - }); - }); - - test("should return a fresh state, leaving the entry of DATA as it is", () => { - const [state] = DATA; - const completed = completeState(state); - - expect(completed).not.toBe(state); - expect(state).not.toHaveProperty("capital"); - }); -}); diff --git a/src/_internals/complete-state/complete-state.ts b/src/_internals/complete-state/complete-state.ts deleted file mode 100644 index 9347ca76..00000000 --- a/src/_internals/complete-state/complete-state.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { STATE_CAPITALS } from "../constants/state-capitals"; -import { type State } from "../constants/states"; - -/** - * Completes a state of `DATA` for a caller: a fresh object with the fields `DATA` leaves out, - * the IBGE identifier of its region (the first digit of `ibgeCode`) and its capital, so mutating - * what a state util returns never reaches the shared data or a later call. - * - * @param {State} state - An entry of `DATA`. - * @returns {State} A fresh copy of the state, with `regionIbgeCode` and a fresh `capital`. - * - * @example - * ```typescript - * completeState(DATA[0]); // { code: "AC", ..., regionIbgeCode: 1, capital: { code: "1200401", name: "Rio Branco" } } - * ``` - * - * @see Official: https://servicodados.ibge.gov.br/api/v1/localidades/regioes - * IBGE, API de Localidades, `regioes`: the identifier of each region, the first digit of the code - * of each of its states. - */ -export const completeState = (state: State): State => { - const [name, code] = STATE_CAPITALS[state.code]; - - return Object.assign({}, state, { - regionIbgeCode: Math.floor(state.ibgeCode / 10), - capital: { code, name }, - }); -}; diff --git a/src/_internals/constants/states.ts b/src/_internals/constants/states.ts index 06d8611b..b7275ddb 100644 --- a/src/_internals/constants/states.ts +++ b/src/_internals/constants/states.ts @@ -7,15 +7,8 @@ * * Each member has the two letter code of the state (`code`, e.g. `"SP"`), its full name * (`name`, e.g. `"São Paulo"`), the code and the full name of the region it belongs to - * (`regionCode` and `regionName`, e.g. `"SE"` and `"Sudeste"`), the 2 digit IBGE code of the - * Federative Unit (`ibgeCode`, the "cUF", e.g. `35`), the IBGE identifier of its region - * (`regionIbgeCode`, the first digit of `ibgeCode`, e.g. `3`) and its capital (`capital`, the 7 digit - * IBGE code and the name of the municipality, e.g. `{ code: "3550308", name: "São Paulo" }`; for - * the Distrito Federal, which has no municipalities, Brasília with the code of the whole district). - * - * `regionIbgeCode` and `capital` came after 2.4.0: every state util fills them, but they are - * optional in the type so that a `State` built before them still type-checks. They become - * required in the next major version. + * (`regionCode` and `regionName`, e.g. `"SE"` and `"Sudeste"`) and the 2 digit IBGE code of the + * Federative Unit (`ibgeCode`, the "cUF", e.g. `35`). */ export type State = | { @@ -24,8 +17,6 @@ export type State = readonly regionCode: "N"; readonly regionName: "Norte"; readonly ibgeCode: 12; - readonly regionIbgeCode?: 1; - readonly capital?: { readonly code: "1200401"; readonly name: "Rio Branco" }; } | { readonly code: "AL"; @@ -33,8 +24,6 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 27; - readonly regionIbgeCode?: 2; - readonly capital?: { readonly code: "2704302"; readonly name: "Maceió" }; } | { readonly code: "AP"; @@ -42,8 +31,6 @@ export type State = readonly regionCode: "N"; readonly regionName: "Norte"; readonly ibgeCode: 16; - readonly regionIbgeCode?: 1; - readonly capital?: { readonly code: "1600303"; readonly name: "Macapá" }; } | { readonly code: "AM"; @@ -51,8 +38,6 @@ export type State = readonly regionCode: "N"; readonly regionName: "Norte"; readonly ibgeCode: 13; - readonly regionIbgeCode?: 1; - readonly capital?: { readonly code: "1302603"; readonly name: "Manaus" }; } | { readonly code: "BA"; @@ -60,8 +45,6 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 29; - readonly regionIbgeCode?: 2; - readonly capital?: { readonly code: "2927408"; readonly name: "Salvador" }; } | { readonly code: "CE"; @@ -69,8 +52,6 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 23; - readonly regionIbgeCode?: 2; - readonly capital?: { readonly code: "2304400"; readonly name: "Fortaleza" }; } | { readonly code: "DF"; @@ -78,8 +59,6 @@ export type State = readonly regionCode: "CO"; readonly regionName: "Centro-Oeste"; readonly ibgeCode: 53; - readonly regionIbgeCode?: 5; - readonly capital?: { readonly code: "5300108"; readonly name: "Brasília" }; } | { readonly code: "ES"; @@ -87,8 +66,6 @@ export type State = readonly regionCode: "SE"; readonly regionName: "Sudeste"; readonly ibgeCode: 32; - readonly regionIbgeCode?: 3; - readonly capital?: { readonly code: "3205309"; readonly name: "Vitória" }; } | { readonly code: "GO"; @@ -96,8 +73,6 @@ export type State = readonly regionCode: "CO"; readonly regionName: "Centro-Oeste"; readonly ibgeCode: 52; - readonly regionIbgeCode?: 5; - readonly capital?: { readonly code: "5208707"; readonly name: "Goiânia" }; } | { readonly code: "MA"; @@ -105,8 +80,6 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 21; - readonly regionIbgeCode?: 2; - readonly capital?: { readonly code: "2111300"; readonly name: "São Luís" }; } | { readonly code: "MT"; @@ -114,8 +87,6 @@ export type State = readonly regionCode: "CO"; readonly regionName: "Centro-Oeste"; readonly ibgeCode: 51; - readonly regionIbgeCode?: 5; - readonly capital?: { readonly code: "5103403"; readonly name: "Cuiabá" }; } | { readonly code: "MS"; @@ -123,8 +94,6 @@ export type State = readonly regionCode: "CO"; readonly regionName: "Centro-Oeste"; readonly ibgeCode: 50; - readonly regionIbgeCode?: 5; - readonly capital?: { readonly code: "5002704"; readonly name: "Campo Grande" }; } | { readonly code: "MG"; @@ -132,8 +101,6 @@ export type State = readonly regionCode: "SE"; readonly regionName: "Sudeste"; readonly ibgeCode: 31; - readonly regionIbgeCode?: 3; - readonly capital?: { readonly code: "3106200"; readonly name: "Belo Horizonte" }; } | { readonly code: "PA"; @@ -141,8 +108,6 @@ export type State = readonly regionCode: "N"; readonly regionName: "Norte"; readonly ibgeCode: 15; - readonly regionIbgeCode?: 1; - readonly capital?: { readonly code: "1501402"; readonly name: "Belém" }; } | { readonly code: "PB"; @@ -150,8 +115,6 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 25; - readonly regionIbgeCode?: 2; - readonly capital?: { readonly code: "2507507"; readonly name: "João Pessoa" }; } | { readonly code: "PR"; @@ -159,8 +122,6 @@ export type State = readonly regionCode: "S"; readonly regionName: "Sul"; readonly ibgeCode: 41; - readonly regionIbgeCode?: 4; - readonly capital?: { readonly code: "4106902"; readonly name: "Curitiba" }; } | { readonly code: "PE"; @@ -168,8 +129,6 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 26; - readonly regionIbgeCode?: 2; - readonly capital?: { readonly code: "2611606"; readonly name: "Recife" }; } | { readonly code: "PI"; @@ -177,8 +136,6 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 22; - readonly regionIbgeCode?: 2; - readonly capital?: { readonly code: "2211001"; readonly name: "Teresina" }; } | { readonly code: "RJ"; @@ -186,8 +143,6 @@ export type State = readonly regionCode: "SE"; readonly regionName: "Sudeste"; readonly ibgeCode: 33; - readonly regionIbgeCode?: 3; - readonly capital?: { readonly code: "3304557"; readonly name: "Rio de Janeiro" }; } | { readonly code: "RN"; @@ -195,8 +150,6 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 24; - readonly regionIbgeCode?: 2; - readonly capital?: { readonly code: "2408102"; readonly name: "Natal" }; } | { readonly code: "RS"; @@ -204,8 +157,6 @@ export type State = readonly regionCode: "S"; readonly regionName: "Sul"; readonly ibgeCode: 43; - readonly regionIbgeCode?: 4; - readonly capital?: { readonly code: "4314902"; readonly name: "Porto Alegre" }; } | { readonly code: "RO"; @@ -213,8 +164,6 @@ export type State = readonly regionCode: "N"; readonly regionName: "Norte"; readonly ibgeCode: 11; - readonly regionIbgeCode?: 1; - readonly capital?: { readonly code: "1100205"; readonly name: "Porto Velho" }; } | { readonly code: "RR"; @@ -222,8 +171,6 @@ export type State = readonly regionCode: "N"; readonly regionName: "Norte"; readonly ibgeCode: 14; - readonly regionIbgeCode?: 1; - readonly capital?: { readonly code: "1400100"; readonly name: "Boa Vista" }; } | { readonly code: "SC"; @@ -231,8 +178,6 @@ export type State = readonly regionCode: "S"; readonly regionName: "Sul"; readonly ibgeCode: 42; - readonly regionIbgeCode?: 4; - readonly capital?: { readonly code: "4205407"; readonly name: "Florianópolis" }; } | { readonly code: "SP"; @@ -240,8 +185,6 @@ export type State = readonly regionCode: "SE"; readonly regionName: "Sudeste"; readonly ibgeCode: 35; - readonly regionIbgeCode?: 3; - readonly capital?: { readonly code: "3550308"; readonly name: "São Paulo" }; } | { readonly code: "SE"; @@ -249,8 +192,6 @@ export type State = readonly regionCode: "NE"; readonly regionName: "Nordeste"; readonly ibgeCode: 28; - readonly regionIbgeCode?: 2; - readonly capital?: { readonly code: "2800308"; readonly name: "Aracaju" }; } | { readonly code: "TO"; @@ -258,8 +199,6 @@ export type State = readonly regionCode: "N"; readonly regionName: "Norte"; readonly ibgeCode: 17; - readonly regionIbgeCode?: 1; - readonly capital?: { readonly code: "1721000"; readonly name: "Palmas" }; }; /** The two letter code of each Brazilian state, as published by the IBGE. */ @@ -271,9 +210,7 @@ export type StateName = State["name"]; /** * Brazilian states published by the IBGE, sorted by name with `localeCompare` in the "pt-BR" * locale. `ibgeCode` is the 2-digit IBGE code of the Federative Unit ("cUF"), the same code - * found in the first field of every DF-e access key (chave de acesso). The optional fields of - * `State` are left out, so that the utils that only read a name or a code do not bundle the - * capitals: `completeState` adds them to what a util returns. + * found in the first field of every DF-e access key (chave de acesso). * * @see Official: https://servicodados.ibge.gov.br/api/docs/localidades */ diff --git a/src/get-state-by-cep/get-state-by-cep.test.ts b/src/get-state-by-cep/get-state-by-cep.test.ts index 3e3d850f..5c79f868 100644 --- a/src/get-state-by-cep/get-state-by-cep.test.ts +++ b/src/get-state-by-cep/get-state-by-cep.test.ts @@ -4,7 +4,6 @@ import { type State } from "../_internals/constants/states"; import { anyGarbage, digits, digitsOfOtherLength } from "../_internals/test/arbitraries"; import { expectNeverThrows } from "../_internals/test/properties"; import { describe, expect, expectTypeOf, it, test } from "../_internals/test/runtime"; -import { getStateByIbgeCode } from "../get-state-by-ibge-code/get-state-by-ibge-code"; import { CEP_RANGES } from "./constants"; import { getStateByCep } from "./get-state-by-cep"; @@ -43,7 +42,13 @@ const RANGE_BOUNDARIES: [string, string, string][] = [ describe("getStateByCep", () => { it("should return São Paulo for a formatted CEP of Avenida Paulista", () => { - expect(getStateByCep("01310-100")).toEqual(getStateByIbgeCode(35)); + expect(getStateByCep("01310-100")).toEqual({ + code: "SP", + name: "São Paulo", + regionCode: "SE", + regionName: "Sudeste", + ibgeCode: 35, + }); }); it("should return Rio de Janeiro for a CEP given as a number", () => { @@ -53,8 +58,6 @@ describe("getStateByCep", () => { regionCode: "SE", regionName: "Sudeste", ibgeCode: 33, - regionIbgeCode: 3, - capital: { code: "3304557", name: "Rio de Janeiro" }, }); }); diff --git a/src/get-state-by-cep/get-state-by-cep.ts b/src/get-state-by-cep/get-state-by-cep.ts index 507ca08b..ceef8b40 100644 --- a/src/get-state-by-cep/get-state-by-cep.ts +++ b/src/get-state-by-cep/get-state-by-cep.ts @@ -1,4 +1,3 @@ -import { completeState } from "../_internals/complete-state/complete-state"; import { DATA, type State } from "../_internals/constants/states"; import { findCepRange } from "../_internals/find-cep-range/find-cep-range"; import { CEP_RANGES } from "./constants"; @@ -32,7 +31,7 @@ export type { State } from "../_internals/constants/states"; * * @example * ```typescript - * getStateByCep("01310-100"); // { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 35, regionIbgeCode: 3, capital: { code: "3550308", name: "São Paulo" } } + * 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" @@ -45,5 +44,5 @@ export const getStateByCep = (value: string | number): State | null => { const range = findCepRange(value, CEP_RANGES); const state = range && DATA.find((entry) => entry.code === range.state); - return state ? completeState(state) : null; + return state ? { ...state } : null; }; diff --git a/src/get-state-by-ibge-code/get-state-by-ibge-code.test.ts b/src/get-state-by-ibge-code/get-state-by-ibge-code.test.ts index 1c881d99..d52ef45f 100644 --- a/src/get-state-by-ibge-code/get-state-by-ibge-code.test.ts +++ b/src/get-state-by-ibge-code/get-state-by-ibge-code.test.ts @@ -14,14 +14,17 @@ describe("getStateByIbgeCode", () => { regionCode: "SE", regionName: "Sudeste", ibgeCode: 35, - regionIbgeCode: 3, - capital: { code: "3550308", name: "São Paulo" }, }); }); it("should return São Paulo for the number code 35", () => { - expect(getStateByIbgeCode(35)).toEqual(getStateByIbgeCode("35")); - expect(getStateByIbgeCode(35)?.code).toBe("SP"); + expect(getStateByIbgeCode(35)).toEqual({ + code: "SP", + name: "São Paulo", + regionCode: "SE", + regionName: "Sudeste", + ibgeCode: 35, + }); }); it("should return Rondônia for the code 11, the first cUF in the IBGE table", () => { diff --git a/src/get-state-by-ibge-code/get-state-by-ibge-code.ts b/src/get-state-by-ibge-code/get-state-by-ibge-code.ts index 43b3e7f9..ef4bf099 100644 --- a/src/get-state-by-ibge-code/get-state-by-ibge-code.ts +++ b/src/get-state-by-ibge-code/get-state-by-ibge-code.ts @@ -1,4 +1,3 @@ -import { completeState } from "../_internals/complete-state/complete-state"; import { DATA, type State } from "../_internals/constants/states"; import { readLookupDigits } from "../_internals/read-lookup-digits/read-lookup-digits"; @@ -29,9 +28,9 @@ export type { State } from "../_internals/constants/states"; * * @example * ```typescript - * getStateByIbgeCode("35"); // { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 35, regionIbgeCode: 3, capital: { code: "3550308", name: "São Paulo" } } - * getStateByIbgeCode(35); // { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 35, regionIbgeCode: 3, capital: { code: "3550308", name: "São Paulo" } } - * getStateByIbgeCode("11"); // { code: "RO", name: "Rondônia", regionCode: "N", regionName: "Norte", ibgeCode: 11, regionIbgeCode: 1, capital: { code: "1100205", name: "Porto Velho" } } + * getStateByIbgeCode("35"); // { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 35 } + * getStateByIbgeCode(35); // { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 35 } + * getStateByIbgeCode("11"); // { code: "RO", name: "Rondônia", regionCode: "N", regionName: "Norte", ibgeCode: 11 } * getStateByIbgeCode("00"); // null * getStateByIbgeCode(""); // null * getStateByIbgeCode(-35); // null @@ -48,5 +47,5 @@ export const getStateByIbgeCode = (code: string | number): State | null => { const state = DATA.find((entry) => entry.ibgeCode === numericCode); - return state ? completeState(state) : null; + return state ? { ...state } : null; }; diff --git a/src/get-states-by-region/get-states-by-region.ts b/src/get-states-by-region/get-states-by-region.ts index 6c585886..2584653a 100644 --- a/src/get-states-by-region/get-states-by-region.ts +++ b/src/get-states-by-region/get-states-by-region.ts @@ -1,4 +1,3 @@ -import { completeState } from "../_internals/complete-state/complete-state"; import { DATA, type State } from "../_internals/constants/states"; export type { State } from "../_internals/constants/states"; @@ -32,6 +31,6 @@ export const getStatesByRegion = (regionCode: string): State[] => { const normalized = regionCode.trim().toUpperCase(); return DATA.filter((state) => state.regionCode === normalized).map((state) => - completeState(state), + Object.assign({}, state), ); }; diff --git a/src/get-states/get-states.test.ts b/src/get-states/get-states.test.ts index 570268b6..c628b676 100644 --- a/src/get-states/get-states.test.ts +++ b/src/get-states/get-states.test.ts @@ -1,10 +1,7 @@ import * as fc from "fast-check"; -import { completeState } from "../_internals/complete-state/complete-state"; import { DATA, type State, type StateCode, type StateName } from "../_internals/constants/states"; import { describe, expect, expectTypeOf, it, test } from "../_internals/test/runtime"; -import { getMunicipalityByCode } from "../get-municipality-by-code/get-municipality-by-code"; -import { getRegions } from "../get-regions/get-regions"; import { getStateByIbgeCode } from "../get-state-by-ibge-code/get-state-by-ibge-code"; import { getStateCodeByName } from "../get-state-code-by-name/get-state-code-by-name"; import { getStateNameByCode } from "../get-state-name-by-code/get-state-name-by-code"; @@ -72,23 +69,7 @@ describe("getStates", () => { const second = getStates(); expect(second.at(0)?.name).not.toBe("X"); - expect(second).toEqual(DATA.map((state) => completeState(state))); - }); - - it("should copy the capital too, so mutating it does not leak between calls", () => { - const [first] = getStates(); - - Object.assign(first?.capital ?? {}, { name: "X" }); - - expect(getStates().at(0)?.capital).toEqual({ code: "1200401", name: "Rio Branco" }); - expect(DATA.at(0)).not.toHaveProperty("capital"); - }); - - it("should give the Distrito Federal Brasília, the code of the whole district", () => { - expect(getStates().find((state) => state.code === "DF")?.capital).toEqual({ - code: "5300108", - name: "Brasília", - }); + expect(second).toEqual(DATA.map((state) => Object.assign({}, state))); }); describe("properties", () => { @@ -108,29 +89,6 @@ describe("getStates", () => { }), ); }); - - test("should have a capital that getMunicipalityByCode finds in the same state", () => { - fc.assert( - fc.property(stateArbitrary(), (state) => { - expect(getMunicipalityByCode(state.capital?.code ?? "")).toEqual({ - code: state.capital?.code, - name: state.capital?.name, - stateCode: state.code, - }); - }), - ); - }); - - test("should have the regionIbgeCode of its region, the first digit of its ibgeCode", () => { - fc.assert( - fc.property(stateArbitrary(), (state) => { - const region = getRegions().find((entry) => entry.code === state.regionCode); - - expect(state.regionIbgeCode).toBe(region?.ibgeCode); - expect(state.regionIbgeCode).toBe(Math.floor(state.ibgeCode / 10)); - }), - ); - }); }); }); @@ -151,10 +109,6 @@ describe("getStates types", () => { expectTypeOf["regionCode"]>().toEqualTypeOf<"SE">(); expectTypeOf["regionName"]>().toEqualTypeOf<"Sudeste">(); expectTypeOf["ibgeCode"]>().toEqualTypeOf<35>(); - expectTypeOf["regionIbgeCode"]>().toEqualTypeOf<3 | undefined>(); - expectTypeOf["capital"]>().toEqualTypeOf< - { readonly code: "3550308"; readonly name: "São Paulo" } | undefined - >(); expectTypeOf["name"]>().toEqualTypeOf<"Acre">(); expectTypeOf>().toEqualTypeOf(); }); diff --git a/src/get-states/get-states.ts b/src/get-states/get-states.ts index 140de94a..460e2876 100644 --- a/src/get-states/get-states.ts +++ b/src/get-states/get-states.ts @@ -1,4 +1,3 @@ -import { completeState } from "../_internals/complete-state/complete-state"; import { DATA, type State } from "../_internals/constants/states"; export type { State } from "../_internals/constants/states"; @@ -19,9 +18,9 @@ export type { State } from "../_internals/constants/states"; * * @example * ```typescript - * getStates()[0]; // { code: "AC", name: "Acre", regionCode: "N", regionName: "Norte", ibgeCode: 12, regionIbgeCode: 1, capital: { code: "1200401", name: "Rio Branco" } } + * getStates()[0]; // { code: "AC", name: "Acre", regionCode: "N", regionName: "Norte", ibgeCode: 12 } * ``` * * @see Official: https://servicodados.ibge.gov.br/api/docs/localidades */ -export const getStates = (): State[] => DATA.map((state) => completeState(state)); +export const getStates = (): State[] => DATA.map((state) => Object.assign({}, state));