{"version":3,"file":"locations.cjs","names":[],"sources":["../../src/br/locations.ts"],"sourcesContent":["import rawLocations from \"./data/br-locations.json\";\n\n/** The 27 Brazilian federative units (26 states + Federal District). */\nexport type UF =\n    | \"AC\"\n    | \"AL\"\n    | \"AP\"\n    | \"AM\"\n    | \"BA\"\n    | \"CE\"\n    | \"DF\"\n    | \"ES\"\n    | \"GO\"\n    | \"MA\"\n    | \"MT\"\n    | \"MS\"\n    | \"MG\"\n    | \"PA\"\n    | \"PB\"\n    | \"PR\"\n    | \"PE\"\n    | \"PI\"\n    | \"RJ\"\n    | \"RN\"\n    | \"RS\"\n    | \"RO\"\n    | \"RR\"\n    | \"SC\"\n    | \"SP\"\n    | \"SE\"\n    | \"TO\";\n\n/** The five Brazilian macro-regions (IBGE). */\nexport type BrRegion = \"Norte\" | \"Nordeste\" | \"Centro-Oeste\" | \"Sudeste\" | \"Sul\";\n\n/** A municipality, identified by the code that survives its renames. */\nexport interface BrazilMunicipality {\n    /** 7-digit IBGE code, e.g. `\"3550308\"`. Stable across a rename. */\n    id: string;\n    /** Current IBGE name, e.g. `\"São Paulo\"`. */\n    name: string;\n}\n\n/**\n * An administrative region of the Federal District.\n *\n * The DF has exactly one municipality — Brasília — and 35 administrative\n * regions inside it. Nobody in the DF writes \"Brasília\" in an address field,\n * so the regions are listed and resolvable; they are not municipalities, and\n * `municipalityId` is what they geocode through.\n */\nexport interface BrazilAdministrativeRegion {\n    /** IBGE subdistrict code, e.g. `\"53001080515\"` for Ceilândia. */\n    id: string;\n    /** IBGE name, e.g. `\"Sudoeste/Octogonal\"`. */\n    name: string;\n    /** The municipality it belongs to — Brasília (`\"5300108\"`) for all 35. */\n    municipalityId: string;\n}\n\n/** A federative unit with its display name and city list. */\nexport interface BrazilState {\n    /** Two-letter acronym, e.g. `\"SP\"`. */\n    uf: UF;\n    /** Full name, e.g. `\"São Paulo\"`. */\n    name: string;\n    /** Macro-region the state belongs to. */\n    region: BrRegion;\n    /** Municipality names within the state, alphabetically. */\n    cities: string[];\n    /** The same municipalities carrying their IBGE codes, same order. */\n    municipalities: readonly BrazilMunicipality[];\n    /** Administrative regions — 35 for `\"DF\"`, empty everywhere else. */\n    administrativeRegions: readonly BrazilAdministrativeRegion[];\n}\n\n/** A `{ value, label }` option, handy for `<Select>` / `<Combobox>`. */\nexport interface Choice {\n    value: string;\n    label: string;\n}\n\n/** IBGE macro-region of each federative unit. */\nconst REGION_BY_UF: Record<UF, BrRegion> = {\n    AC: \"Norte\",\n    AP: \"Norte\",\n    AM: \"Norte\",\n    PA: \"Norte\",\n    RO: \"Norte\",\n    RR: \"Norte\",\n    TO: \"Norte\",\n    AL: \"Nordeste\",\n    BA: \"Nordeste\",\n    CE: \"Nordeste\",\n    MA: \"Nordeste\",\n    PB: \"Nordeste\",\n    PE: \"Nordeste\",\n    PI: \"Nordeste\",\n    RN: \"Nordeste\",\n    SE: \"Nordeste\",\n    DF: \"Centro-Oeste\",\n    GO: \"Centro-Oeste\",\n    MT: \"Centro-Oeste\",\n    MS: \"Centro-Oeste\",\n    ES: \"Sudeste\",\n    MG: \"Sudeste\",\n    RJ: \"Sudeste\",\n    SP: \"Sudeste\",\n    PR: \"Sul\",\n    RS: \"Sul\",\n    SC: \"Sul\",\n};\n\n/** One `[IBGE code, name]` row as `br-locations.json` stores it. */\ntype RawEntry = [string, string];\n\ninterface RawState {\n    uf: string;\n    name: string;\n    cities: RawEntry[];\n}\n\ninterface RawLocations {\n    /**\n     * The two IBGE vintages this file was built from: `roster` is the day the\n     * municipality list was read, `mesh` the boundary release it was joined to.\n     */\n    vintage: { roster: string; mesh: string };\n    states: RawState[];\n    /** Administrative regions keyed by the municipality that contains them. */\n    administrativeRegions: Record<string, RawEntry[]>;\n    /** Every former name, mapped to the IBGE code that still answers to it. */\n    aliases: RawEntry[];\n    /** Municipalities the roster lists and the mesh has no geometry for. */\n    pendingGeometry: string[];\n}\n\nconst RAW = rawLocations as unknown as RawLocations;\n\n/**\n * Strip a name down to what two spellings of the same place share.\n *\n * Accents, case and apostrophes are exactly what differs between the spelling a\n * user typed, the one an app saved five years ago and the one IBGE publishes\n * today — `\"Sant'Ana do Livramento\"` and `\"Santana do Livramento\"` are the same\n * municipality, and `\"acu\"` should find `\"Açu\"`.\n *\n * @param value - A municipality, region or alias name.\n * @returns The comparable form: lowercase, unaccented, apostrophes dropped.\n */\nfunction foldName(value: string): string {\n    return value\n        .trim()\n        .normalize(\"NFD\")\n        .replace(/[\\u0300-\\u036f]/g, \"\")\n        .replace(/['\\u2019\\u0060]/g, \"\")\n        .toLowerCase();\n}\n\n/** The federative unit each IBGE code starts with. */\nconst UF_BY_CODE: Record<string, UF> = {\n    \"11\": \"RO\",\n    \"12\": \"AC\",\n    \"13\": \"AM\",\n    \"14\": \"RR\",\n    \"15\": \"PA\",\n    \"16\": \"AP\",\n    \"17\": \"TO\",\n    \"21\": \"MA\",\n    \"22\": \"PI\",\n    \"23\": \"CE\",\n    \"24\": \"RN\",\n    \"25\": \"PB\",\n    \"26\": \"PE\",\n    \"27\": \"AL\",\n    \"28\": \"SE\",\n    \"29\": \"BA\",\n    \"31\": \"MG\",\n    \"32\": \"ES\",\n    \"33\": \"RJ\",\n    \"35\": \"SP\",\n    \"41\": \"PR\",\n    \"42\": \"SC\",\n    \"43\": \"RS\",\n    \"50\": \"MS\",\n    \"51\": \"MT\",\n    \"52\": \"GO\",\n    \"53\": \"DF\",\n};\n\n/**\n * The municipality an IBGE code belongs to.\n *\n * A municipality code is 7 digits and a subdistrict code extends it with 4 more\n * — `\"53001080515\"` (Ceilândia) is Brasília's `\"5300108\"` plus `\"0515\"` — so the\n * containing municipality is a prefix, not a lookup.\n *\n * @param id - A municipality or subdistrict IBGE code.\n * @returns The 7-digit municipality code.\n */\nfunction municipalityIdOf(id: string): string {\n    return id.slice(0, 7);\n}\n\n/** Administrative regions of a municipality, or `[]` when it has none. */\nfunction regionsOf(municipalityId: string): readonly BrazilAdministrativeRegion[] {\n    return (RAW.administrativeRegions[municipalityId] ?? []).map(([id, name]) => ({\n        id,\n        name,\n        municipalityId,\n    }));\n}\n\n/** Normalized, frozen list of all 27 states (built once at module load). */\nconst STATES: readonly BrazilState[] = RAW.states\n    .map((entry) => {\n        const uf = entry.uf as UF;\n        const municipalities = entry.cities.map(([id, name]) => ({ id, name }));\n        return {\n            uf,\n            name: entry.name,\n            region: REGION_BY_UF[uf],\n            cities: municipalities.map((m) => m.name),\n            municipalities,\n            administrativeRegions: municipalities.flatMap((m) => regionsOf(m.id)),\n        };\n    })\n    .sort((a, b) => a.name.localeCompare(b.name, \"pt-BR\"));\n\nconst STATE_BY_UF = new Map<UF, BrazilState>(STATES.map((s) => [s.uf, s]));\n\n/**\n * Every name that resolves to a municipality, folded, keyed by `<UF>:<name>`.\n *\n * Three kinds of name land here and the precedence matters: a current IBGE name\n * wins, then a former name from `aliases`, then an administrative region of the\n * DF. A rename never shadows a live municipality — `Campo Grande` is a live\n * municipality in both MS and RN, and in RN it is also the former name of\n * nothing, so the live entry has to be written last.\n */\nconst ID_BY_FOLDED_NAME = new Map<string, string>();\nfor (const [name, id] of RAW.aliases) {\n    const uf = UF_BY_CODE[id.slice(0, 2)];\n    if (uf !== undefined) ID_BY_FOLDED_NAME.set(`${uf}:${foldName(name)}`, municipalityIdOf(id));\n}\nfor (const state of STATES) {\n    for (const region of state.administrativeRegions) {\n        ID_BY_FOLDED_NAME.set(`${state.uf}:${foldName(region.name)}`, region.municipalityId);\n    }\n}\nfor (const state of STATES) {\n    for (const municipality of state.municipalities) {\n        ID_BY_FOLDED_NAME.set(`${state.uf}:${foldName(municipality.name)}`, municipality.id);\n    }\n}\n\nconst MUNICIPALITY_BY_ID = new Map<string, BrazilMunicipality>(\n    STATES.flatMap((s) => s.municipalities).map((m) => [m.id, m]),\n);\n\n/** All 27 federative units, sorted by name. */\nexport function listStates(): readonly BrazilState[] {\n    return STATES;\n}\n\n/** Look up a single state by acronym (case-insensitive). Returns `null` if unknown. */\nexport function getState(uf: string): BrazilState | null {\n    const normalized = normalizeUf(uf);\n    return normalized ? (STATE_BY_UF.get(normalized) ?? null) : null;\n}\n\n/**\n * City names for a federative unit (case-insensitive acronym). Returns an empty\n * array for an unknown UF — \"no matches\" is a valid result, not an error.\n */\nexport function citiesByUf(uf: string): string[] {\n    return getState(uf)?.cities ?? [];\n}\n\n/** States belonging to a macro-region. */\nexport function statesByRegion(region: BrRegion): readonly BrazilState[] {\n    return STATES.filter((s) => s.region === region);\n}\n\n/** True when `value` is one of the 27 valid acronyms (case-insensitive). */\nexport function isValidUf(value: string): boolean {\n    return normalizeUf(value) !== null;\n}\n\n/**\n * Normalize an acronym to canonical uppercase form, or `null` if it is not a\n * valid UF. `\"sp\"` → `\"SP\"`, `\"xx\"` → `null`.\n */\nexport function normalizeUf(value: string): UF | null {\n    const upper = value.trim().toUpperCase();\n    return (REGION_BY_UF as Record<string, BrRegion>)[upper] ? (upper as UF) : null;\n}\n\n/** True when `city` exists within `uf` (both case-insensitive). */\nexport function isValidCity(uf: string, city: string): boolean {\n    const target = city.trim().toLowerCase();\n    return citiesByUf(uf).some((c) => c.toLowerCase() === target);\n}\n\n/** `{ value: uf, label: name }` options for every state, for a `<Select>`. */\nexport function ufChoices(): Choice[] {\n    return STATES.map((s) => ({ value: s.uf, label: s.name }));\n}\n\n/** `{ value, label }` options for every city in a UF (value === label). */\nexport function cityChoices(uf: string): Choice[] {\n    return citiesByUf(uf).map((c) => ({ value: c, label: c }));\n}\n\n/**\n * Municipalities of a federative unit, each carrying its IBGE code.\n *\n * The code is what a backend wants stored: it survives the renames that keep\n * happening (`Presidente Juscelino` became `Serra Caiada` in 2013) and it is the\n * key every other dataset in this module joins on.\n *\n * @param uf - Federative unit acronym, case-insensitive.\n * @returns The municipalities, alphabetically. Empty for an unknown UF.\n */\nexport function municipalitiesByUf(uf: string): readonly BrazilMunicipality[] {\n    return getState(uf)?.municipalities ?? [];\n}\n\n/**\n * Administrative regions of a federative unit.\n *\n * Only the Federal District has any — 35 of them, inside its single\n * municipality. Every other UF answers `[]`, which is a valid result and not an\n * error.\n *\n * @param uf - Federative unit acronym, case-insensitive.\n * @returns The regions, alphabetically. Empty outside the DF.\n */\nexport function administrativeRegionsByUf(uf: string): readonly BrazilAdministrativeRegion[] {\n    return getState(uf)?.administrativeRegions ?? [];\n}\n\n/**\n * Find the municipality a name refers to, however it was written down.\n *\n * Four kinds of name resolve, which is the point — an address saved years ago\n * has to keep pointing at the same place:\n *\n * 1. The current IBGE name (`\"Serra Caiada\"`).\n * 2. A former name (`\"Presidente Juscelino\"`), from the alias table the\n *    generator builds by diffing vintages.\n * 3. A different spelling — accents, case and apostrophes are folded, so\n *    `\"sant'ana do livramento\"` finds `\"Sant'Ana do Livramento\"`.\n * 4. An administrative region of the DF (`\"Ceilândia\"`), which resolves to\n *    Brasília — the municipality it is part of.\n *\n * A live municipality always wins over a former name, so a name that is current\n * in one UF and historical in another resolves to the live one in each.\n *\n * @param uf - Federative unit acronym, case-insensitive.\n * @param name - The municipality, region or former name.\n * @returns The municipality, or `null` when nothing in that UF answers to it.\n */\nexport function resolveMunicipality(uf: string, name: string): BrazilMunicipality | null {\n    const normalized = normalizeUf(uf);\n    if (normalized === null) return null;\n    const id = ID_BY_FOLDED_NAME.get(`${normalized}:${foldName(name)}`);\n    return id === undefined ? null : (MUNICIPALITY_BY_ID.get(id) ?? null);\n}\n\n/**\n * The IBGE vintages the bundled datasets were built from.\n *\n * `roster` is the day the municipality list was read and `mesh` the boundary\n * release it was joined to. They differ on purpose: IBGE publishes new\n * municipalities before it publishes their boundaries, and the gap is what\n * {@link pendingGeometryIds} enumerates.\n *\n * @returns The two vintages, as they were written into the data file.\n */\nexport function datasetVintage(): { roster: string; mesh: string } {\n    return RAW.vintage;\n}\n\n/**\n * Municipalities that exist in the roster and have no boundary yet.\n *\n * One today: Boa Esperança do Norte (MT), installed in 2023, for which IBGE\n * serves no mesh at any endpoint. It is listed and selectable, and it is the one\n * municipality `geocodeMunicipality` cannot place — so a caller that must plot\n * every selection can check this list up front instead of discovering it as an\n * empty result.\n *\n * @returns The IBGE codes, in the order the data file declares them.\n */\nexport function pendingGeometryIds(): readonly string[] {\n    return RAW.pendingGeometry;\n}\n"],"mappings":"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"}