From 4ebb31f26efed74f8fcb049fe27ddf7cf5388a08 Mon Sep 17 00:00:00 2001 From: Murilo Ijanc Date: Mon, 27 Jul 2026 20:31:55 -0300 Subject: Carry the municipality of a CEP that does not exist A CEP that fails is rarely a random number: it is someone who knows the city and guessed the digits, and the classic guess is the -000 that 957 municipalities never received. The API now answers the 404 with the municipality that band belongs to, which is enough to tell the user "Areal/RJ, but not that number" instead of a bare failure. It stays an error. Faixa hangs off CEPNotFound rather than becoming a result, cep_ini/cep_fim are documented as the CEPs that exist there and not as a range to iterate, and an absent or unreadable block yields undefined so that context can never cost the caller the clean not-found it was already getting. --- src/models.ts | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) (limited to 'src/models.ts') diff --git a/src/models.ts b/src/models.ts index 7e20b54..7125336 100644 --- a/src/models.ts +++ b/src/models.ts @@ -246,6 +246,105 @@ export class Address { } } +/** Every field of a {@link Faixa}, as plain data. */ +export interface FaixaFields { + localidade: string; + uf: string; + estado: string; + regiao: string; + ibge: string; + cep_ini: string; + cep_fim: string; + ceps: number; +} + +/** + * The municipality a CEP that does not exist *would* belong to. + * + * It comes attached to {@link CEPNotFound}, on a response that is still a + * **404**. It is context for an error, never an address: nothing here says the + * CEP exists, and code that treats a `faixa` as a result turns a clean + * not-found into a wrong delivery. + * + * It exists because a failing CEP is rarely random — it is someone who knows + * the city and guessed the number, and the classic guess is the `-000` that 957 + * municipalities never received. + * + * `cep_ini` and `cep_fim` are the smallest and the largest CEP that **actually + * exist** in that municipality, not the range the Correios publish for it. For + * Areal/RJ the DNE's own start is `25845000` — precisely the CEP that just + * failed — while `cep_ini` is `25845001`. + * + * The range is **sparse**. Areal's 118 CEPs are scattered across the ~4,900 + * numbers between `25845001` and `25849899`, so the pair is not a list of valid + * CEPs and iterating it produces mostly 404s. `ceps` is there to make that + * density visible: 118 out of 4,900. + */ +export class Faixa implements FaixaFields { + /** Municipality that owns the band the CEP falls in. */ + readonly localidade: string; + /** Two-letter state code, such as `"RJ"`. */ + readonly uf: string; + /** State name, spelled out. */ + readonly estado: string; + /** Region of the country, such as `"Sudeste"`. */ + readonly regiao: string; + /** IBGE municipality code. */ + readonly ibge: string; + /** Smallest CEP that exists there — not the band's published start. */ + readonly cep_ini: string; + /** Largest CEP that exists there — not the band's published end. */ + readonly cep_fim: string; + /** How many CEPs actually exist between `cep_ini` and `cep_fim`. */ + readonly ceps: number; + + constructor(fields: FaixaFields) { + this.localidade = fields.localidade; + this.uf = fields.uf; + this.estado = fields.estado; + this.regiao = fields.regiao; + this.ibge = fields.ibge; + this.cep_ini = fields.cep_ini; + this.cep_fim = fields.cep_fim; + this.ceps = fields.ceps; + } + + /** The municipality as `"Areal/RJ"`, for a message shown to a person. */ + get label(): string { + return this.uf === "" ? this.localidade : `${this.localidade}/${this.uf}`; + } + + /** + * Build from the `faixa` block of a 404 body, or `undefined` when there is + * none to read. + * + * `undefined`, not `null`, because the API **omits** the field rather than + * sending null when the CEP falls in a band no municipality owns. Absent and + * unreadable are the same answer here: a malformed block must never cost the + * caller the not-found error it was already getting. + */ + static fromJson(data: unknown): Faixa | undefined { + if (typeof data !== "object" || data === null || Array.isArray(data)) { + return undefined; + } + const block = data as RawObject; + const localidade = toText(block.localidade).trim(); + if (localidade === "") { + return undefined; + } + return new Faixa({ + localidade, + uf: toText(block.uf), + estado: toText(block.estado), + regiao: toText(block.regiao), + ibge: toText(block.ibge), + cep_ini: toText(block.cep_ini), + cep_fim: toText(block.cep_fim), + ceps: toNumber(block.ceps) ?? 0, + }); + } +} + /** Every field of a {@link Health} report, as plain data. */ export interface HealthFields { status: string; -- cgit v1.3