summaryrefslogtreecommitdiffstats
path: root/src/models.ts
diff options
context:
space:
mode:
authorMurilo Ijanc <[email protected]>2026-07-27 20:31:55 -0300
committerMurilo Ijanc <[email protected]>2026-07-27 20:31:55 -0300
commit4ebb31f26efed74f8fcb049fe27ddf7cf5388a08 (patch)
treef21190d570ea0e33f730f96c89288b0b2c6656a9 /src/models.ts
parent986a16c34049187a6c1f39f38bfa59e327093494 (diff)
downloadtypescript-4ebb31f26efed74f8fcb049fe27ddf7cf5388a08.tar.gz
Carry the municipality of a CEP that does not existHEADv0.2.0main
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.
Diffstat (limited to 'src/models.ts')
-rw-r--r--src/models.ts99
1 files changed, 99 insertions, 0 deletions
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;