diff options
Diffstat (limited to 'src/client.ts')
| -rw-r--r-- | src/client.ts | 55 |
1 files changed, 44 insertions, 11 deletions
diff --git a/src/client.ts b/src/client.ts index c7dac15..3e48d7a 100644 --- a/src/client.ts +++ b/src/client.ts @@ -19,7 +19,7 @@ import { RateLimited, ServerError, } from "./errors.js"; -import { Address, Health, type RawObject } from "./models.js"; +import { Address, Faixa, Health, type RawObject } from "./models.js"; import { VERSION } from "./version.js"; /** Where the public API lives. */ @@ -235,6 +235,19 @@ export class Client { * message: the format already passed local validation, so the server knows * something this client does not. * + * A CEP that does not exist throws {@link CEPNotFound}, which may carry a + * {@link Faixa} naming the municipality the number would belong to: + * + * ```ts + * try { + * await client.lookup("25845000"); + * } catch (error) { + * if (error instanceof CEPNotFound && error.faixa) { + * error.faixa.localidade; // "Areal" — still a 404, still no address + * } + * } + * ``` + * * @throws {InvalidCEP} the CEP is not 8 digits — no request is made. * @throws {CEPNotFound} well-formed, but not in the database. * @throws {RateLimited} 429 after every attempt. @@ -247,7 +260,13 @@ export class Client { const reply = await this.request(`/${number}`); if (reply.status === 404) { - throw new CEPNotFound(`CEP ${number} does not exist in the DNE database`, number); + const faixa = Faixa.fromJson(errorBody(reply.body)?.faixa); + const belongs = faixa === undefined ? "" : ` — that band belongs to ${faixa.label}`; + throw new CEPNotFound( + `CEP ${number} does not exist in the DNE database${belongs}`, + number, + faixa, + ); } if (reply.status === 400) { throw new InvalidCEP(errorMessage(reply.body, `CEP ${number} rejected`)); @@ -417,17 +436,31 @@ export class Client { } } -function errorMessage(body: string, fallback: string): string { +/** + * The body of an error response as an object, or `undefined` when it is not + * one. + * + * Never throws. An error response is already the bad news; a proxy answering + * with HTML in its place must not upgrade that into a second, different + * failure. + */ +function errorBody(body: string): RawObject | undefined { + let data: unknown; try { - const data: unknown = JSON.parse(body); - if (typeof data === "object" && data !== null && !Array.isArray(data)) { - const message = (data as RawObject).mensagem; - if (typeof message === "string" && message !== "") { - return message; - } - } + data = JSON.parse(body); } catch { - return fallback; + return undefined; + } + if (typeof data !== "object" || data === null || Array.isArray(data)) { + return undefined; + } + return data as RawObject; +} + +function errorMessage(body: string, fallback: string): string { + const message = errorBody(body)?.mensagem; + if (typeof message === "string" && message !== "") { + return message; } return fallback; } |