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 67810873..c6775d6a 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 6b934699..270157ab 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 9d640799..a30d1b4c 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";