#!/usr/bin/env node /** * Minimal CLI: `cepis 01001000`. * * It exists for one concrete case — quickly confirming a local instance * answers before pointing code at it: * * ```sh * cepis --base-url http://localhost:8080 01001000 * ``` * * A single CEP goes through the unit lookup, which carries `geo`; several go * through the batch, which is one request instead of N and has no `geo`. * * It is not the library's main interface; for that, import the package. * * @module */ import { Client, DEFAULT_URL } from "./client.js"; import { CepisError } from "./errors.js"; import type { Address } from "./models.js"; import { VERSION } from "./version.js"; const USAGE = `cepis ${VERSION} — look CEPs up against the cep.is API. usage: cepis [--base-url URL] [--timeout MS] [cep...] --base-url URL API URL (default: ${DEFAULT_URL}) --timeout MS milliseconds allowed per attempt (default: 10000) -h, --help show this message `; interface Options { baseUrl: string; timeoutMs: number; ceps: string[]; } function parseArgs(argv: readonly string[]): Options | null { const options: Options = { baseUrl: DEFAULT_URL, timeoutMs: 10_000, ceps: [], }; for (let i = 0; i < argv.length; i += 1) { const arg = argv[i] as string; if (arg === "-h" || arg === "--help") { return null; } else if (arg === "--base-url") { i += 1; const value = argv[i]; if (value === undefined) { throw new Error("--base-url needs a value"); } options.baseUrl = value; } else if (arg === "--timeout") { i += 1; const value = Number(argv[i]); if (!Number.isFinite(value) || value <= 0) { throw new Error("--timeout needs a positive number of milliseconds"); } options.timeoutMs = value; } else if (arg.startsWith("-")) { throw new Error(`unknown option: ${arg}`); } else { options.ceps.push(arg); } } if (options.ceps.length === 0) { return null; } return options; } function serialise(address: Address | null): unknown { if (address === null) { return null; } return { cep: address.cep, formattedCep: address.formattedCep, logradouro: address.logradouro, complemento: address.complemento, unidade: address.unidade, bairro: address.bairro, localidade: address.localidade, uf: address.uf, estado: address.estado, regiao: address.regiao, ibge: address.ibge, gia: address.gia, ddd: address.ddd, siafi: address.siafi, geo: address.geo, }; } /** Run the CLI. Resolves to the process exit code. */ export async function main(argv: readonly string[]): Promise { let options: Options | null; try { options = parseArgs(argv); } catch (error) { process.stderr.write(`error: ${(error as Error).message}\n\n${USAGE}`); return 2; } if (options === null) { process.stdout.write(USAGE); return 0; } const client = new Client({ baseUrl: options.baseUrl, timeoutMs: options.timeoutMs, }); let addresses: (Address | null)[]; try { addresses = options.ceps.length === 1 ? [await client.lookup(options.ceps[0] as string)] : await client.lookupMany(options.ceps); } catch (error) { if (error instanceof CepisError) { process.stderr.write(`error: ${error.message}\n`); return 1; } throw error; } const output = addresses.map(serialise); process.stdout.write(`${JSON.stringify(output.length > 1 ? output : output[0], null, 2)}\n`); return 0; } process.exitCode = await main(process.argv.slice(2));