@br-validators/cli 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -88,6 +88,50 @@ br-validators generate placa --format mercosul --seed 3
88
88
 
89
89
  ---
90
90
 
91
+ ## Reference data lookup
92
+
93
+ Offline embedded datasets — delegates to `@br-validators/core/*`.
94
+
95
+ ### Bacen banks
96
+
97
+ ```bash
98
+ br-validators bancos lookup 001 --json
99
+ br-validators bancos lookup 18236120 --verbose
100
+ br-validators bancos list --limit 20 --json
101
+ ```
102
+
103
+ ### Fiscal (26c)
104
+
105
+ ```bash
106
+ br-validators natureza-juridica lookup 2062 --json
107
+ br-validators nbs lookup 1.1502.50.00 --verbose
108
+ br-validators cest lookup 0302100 --json
109
+ ```
110
+
111
+ ### Trade (26d)
112
+
113
+ ```bash
114
+ br-validators moedas lookup BRL --json
115
+ br-validators paises-bacen lookup 1058 --verbose
116
+ br-validators incoterms lookup FOB --json
117
+ ```
118
+
119
+ ### Logistics (26e)
120
+
121
+ ```bash
122
+ br-validators portos lookup BRSSZ --json
123
+ br-validators aeroportos lookup GRU --verbose
124
+ br-validators aeroportos lookup SBGR --json
125
+ ```
126
+
127
+ | Exit code | Meaning |
128
+ |-----------|---------|
129
+ | `0` | Record found |
130
+ | `1` | Not found |
131
+ | `2` | Usage error |
132
+
133
+ ---
134
+
91
135
  ## Flags
92
136
 
93
137
  | Flag | Description |
@@ -97,6 +141,8 @@ br-validators generate placa --format mercosul --seed 3
97
141
  | `--file` / `-f` | Read value from file |
98
142
  | `--source` | Print official source URL (per-type) |
99
143
  | `--uf` | Required for IE / detect / sanitize IE |
144
+ | `--verbose` | Include dataset capture date (`bancos`, reference `lookup` commands) |
145
+ | `--limit` | Max rows for `bancos list` |
100
146
 
101
147
  ### CI
102
148
 
package/dist/index.js CHANGED
@@ -9,12 +9,8 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
9
9
  // src/program.ts
10
10
  import { Command } from "commander";
11
11
 
12
- // src/commands/brcode.ts
13
- import {
14
- BRCODE_OFFICIAL_SOURCE_URL,
15
- parseBrCode,
16
- validateBrCode
17
- } from "@br-validators/core";
12
+ // src/commands/bancos/list.ts
13
+ import { BANCOS_DATA_VERSION as BANCOS_DATA_VERSION2, getBancos } from "@br-validators/core/bancos";
18
14
 
19
15
  // src/constants.ts
20
16
  var SUPPORTED_TYPES = ["cnpj", "cpf", "cep", "telefone", "cnh", "renavam", "titulo-eleitor", "nfe-chave", "brcode", "placa", "pis-pasep", "pix", "boleto", "cartao", "ie"];
@@ -24,7 +20,289 @@ var EXIT = {
24
20
  USAGE: 2
25
21
  };
26
22
 
23
+ // src/commands/bancos/lookup.ts
24
+ import {
25
+ BANCOS_DATA_VERSION,
26
+ getBancoPorCodigo,
27
+ getBancoPorIspb
28
+ } from "@br-validators/core/bancos";
29
+ function normalizeBancosLookupInput(raw) {
30
+ const digits = raw.replace(/\D/g, "");
31
+ if (digits.length === 8) {
32
+ return { kind: "ispb", value: digits.padStart(8, "0") };
33
+ }
34
+ if (digits.length >= 1 && digits.length <= 3) {
35
+ return { kind: "codigo", value: digits.padStart(3, "0").slice(-3) };
36
+ }
37
+ return null;
38
+ }
39
+ function lookupBanco(raw) {
40
+ const normalized = normalizeBancosLookupInput(raw);
41
+ if (!normalized) {
42
+ return void 0;
43
+ }
44
+ return normalized.kind === "ispb" ? getBancoPorIspb(normalized.value) : getBancoPorCodigo(normalized.value);
45
+ }
46
+ function formatBancoHuman(banco) {
47
+ return `${banco.codigo} \u2014 ${banco.nome} (ISPB ${banco.ispb})`;
48
+ }
49
+ function runBancosLookupCommand(input, options, io = { stdout: [], stderr: [] }) {
50
+ const normalized = normalizeBancosLookupInput(input);
51
+ if (!normalized) {
52
+ io.stderr.push("Invalid bank code or ISPB. Use 3-digit COMPE (e.g. 001) or 8-digit ISPB.");
53
+ return EXIT.USAGE;
54
+ }
55
+ const banco = lookupBanco(input);
56
+ if (!banco) {
57
+ io.stderr.push(`Bank not found: ${input}`);
58
+ return EXIT.INVALID;
59
+ }
60
+ if (options.json) {
61
+ const payload = { ok: true, banco };
62
+ if (options.verbose) {
63
+ payload.capturadoEm = BANCOS_DATA_VERSION.capturadoEm;
64
+ }
65
+ io.stdout.push(JSON.stringify(payload, null, 2));
66
+ return EXIT.OK;
67
+ }
68
+ io.stdout.push(formatBancoHuman(banco));
69
+ if (options.verbose) {
70
+ io.stdout.push(`capturadoEm: ${BANCOS_DATA_VERSION.capturadoEm}`);
71
+ }
72
+ return EXIT.OK;
73
+ }
74
+ function runBancosLookup(value, options, io = { stdout: [], stderr: [] }) {
75
+ if (!value?.trim()) {
76
+ io.stderr.push("Missing bank code or ISPB. Pass COMPE (001) or ISPB (8 digits).");
77
+ return EXIT.USAGE;
78
+ }
79
+ return runBancosLookupCommand(value.trim(), options, io);
80
+ }
81
+
82
+ // src/commands/bancos/list.ts
83
+ function sliceBancos(limit) {
84
+ const all = getBancos();
85
+ if (limit === void 0 || !Number.isFinite(limit) || limit <= 0) {
86
+ return all;
87
+ }
88
+ return all.slice(0, limit);
89
+ }
90
+ function runBancosListCommand(options, io = { stdout: [], stderr: [] }) {
91
+ const bancos = sliceBancos(options.limit);
92
+ if (options.json) {
93
+ const payload = {
94
+ ok: true,
95
+ total: bancos.length,
96
+ bancos
97
+ };
98
+ if (options.verbose) {
99
+ payload.capturadoEm = BANCOS_DATA_VERSION2.capturadoEm;
100
+ }
101
+ io.stdout.push(JSON.stringify(payload, null, 2));
102
+ return EXIT.OK;
103
+ }
104
+ for (const banco of bancos) {
105
+ io.stdout.push(formatBancoHuman(banco));
106
+ }
107
+ if (options.verbose) {
108
+ io.stdout.push(`capturadoEm: ${BANCOS_DATA_VERSION2.capturadoEm}`);
109
+ }
110
+ return EXIT.OK;
111
+ }
112
+ function runBancosList(options, io = { stdout: [], stderr: [] }) {
113
+ return runBancosListCommand(options, io);
114
+ }
115
+
116
+ // src/commands/reference-lookup/registry.ts
117
+ import {
118
+ CEST_DATA_VERSION,
119
+ getCestPorCodigo
120
+ } from "@br-validators/core/cest";
121
+ import {
122
+ INCOTERMS_DATA_VERSION,
123
+ getIncotermPorCodigo
124
+ } from "@br-validators/core/incoterms";
125
+ import {
126
+ MOEDAS_DATA_VERSION,
127
+ getMoedaPorCodigo
128
+ } from "@br-validators/core/moedas";
129
+ import {
130
+ NATUREZA_JURIDICA_DATA_VERSION,
131
+ getNaturezaJuridicaPorCodigo
132
+ } from "@br-validators/core/natureza-juridica";
133
+ import { NBS_DATA_VERSION, getNbsPorCodigo } from "@br-validators/core/nbs";
134
+ import {
135
+ PAISES_BACEN_DATA_VERSION,
136
+ getPaisPorCodigoBacen
137
+ } from "@br-validators/core/paises-bacen";
138
+ import {
139
+ AEROPORTOS_DATA_VERSION,
140
+ getAeroportoPorIata,
141
+ getAeroportoPorIcao
142
+ } from "@br-validators/core/aeroportos";
143
+ import { PORTOS_DATA_VERSION, getPortoPorCodigo } from "@br-validators/core/portos";
144
+ var REFERENCE_LOOKUP_COMMANDS = [
145
+ "natureza-juridica",
146
+ "nbs",
147
+ "cest",
148
+ "moedas",
149
+ "paises-bacen",
150
+ "incoterms",
151
+ "portos",
152
+ "aeroportos"
153
+ ];
154
+ function lookupAeroporto(input) {
155
+ const normalized = input.trim().toUpperCase();
156
+ if (/^[A-Z0-9]{3}$/.test(normalized)) {
157
+ return getAeroportoPorIata(normalized);
158
+ }
159
+ if (/^[A-Z]{4}$/.test(normalized)) {
160
+ return getAeroportoPorIcao(normalized);
161
+ }
162
+ return void 0;
163
+ }
164
+ var REFERENCE_LOOKUP_MODULES = {
165
+ "natureza-juridica": {
166
+ command: "natureza-juridica",
167
+ description: "RFB legal nature codes \u2014 offline lookup",
168
+ resultKey: "naturezaJuridica",
169
+ capturadoEm: NATUREZA_JURIDICA_DATA_VERSION.capturadoEm,
170
+ lookup: (input) => getNaturezaJuridicaPorCodigo(input),
171
+ formatHuman: (result) => {
172
+ const row = result;
173
+ return `${row.codigo} \u2014 ${row.descricao}`;
174
+ }
175
+ },
176
+ nbs: {
177
+ command: "nbs",
178
+ description: "NFSe NBS service codes \u2014 offline lookup",
179
+ resultKey: "nbs",
180
+ capturadoEm: NBS_DATA_VERSION.capturadoEm,
181
+ lookup: (input) => getNbsPorCodigo(input),
182
+ formatHuman: (result) => {
183
+ const row = result;
184
+ return `${row.codigo} \u2014 ${row.descricao}`;
185
+ }
186
+ },
187
+ cest: {
188
+ command: "cest",
189
+ description: "CONFAZ CEST ST codes \u2014 offline lookup",
190
+ resultKey: "cest",
191
+ capturadoEm: CEST_DATA_VERSION.capturadoEm,
192
+ lookup: (input) => getCestPorCodigo(input),
193
+ formatHuman: (result) => {
194
+ const row = result;
195
+ return `${row.codigo} \u2014 ${row.descricao}`;
196
+ }
197
+ },
198
+ moedas: {
199
+ command: "moedas",
200
+ description: "ISO 4217 + Bacen PTAX currencies \u2014 offline lookup",
201
+ resultKey: "moeda",
202
+ capturadoEm: MOEDAS_DATA_VERSION.capturadoEm,
203
+ lookup: (input) => getMoedaPorCodigo(input),
204
+ formatHuman: (result) => {
205
+ const row = result;
206
+ const symbol = row.simbolo ?? "\u2014";
207
+ return `${row.codigo} \u2014 ${row.nome} (${symbol})`;
208
+ }
209
+ },
210
+ "paises-bacen": {
211
+ command: "paises-bacen",
212
+ description: "NF-e Bacen country codes \u2014 offline lookup",
213
+ resultKey: "pais",
214
+ capturadoEm: PAISES_BACEN_DATA_VERSION.capturadoEm,
215
+ lookup: (input) => getPaisPorCodigoBacen(input),
216
+ formatHuman: (result) => {
217
+ const row = result;
218
+ return `${row.codigo} \u2014 ${row.nome}`;
219
+ }
220
+ },
221
+ incoterms: {
222
+ command: "incoterms",
223
+ description: "ICC Incoterms 2020 \u2014 offline lookup",
224
+ resultKey: "incoterm",
225
+ capturadoEm: INCOTERMS_DATA_VERSION.capturadoEm,
226
+ lookup: (input) => getIncotermPorCodigo(input),
227
+ formatHuman: (result) => {
228
+ const row = result;
229
+ return `${row.codigo} \u2014 ${row.nome} (${row.edicao})`;
230
+ }
231
+ },
232
+ portos: {
233
+ command: "portos",
234
+ description: "ANTAQ port installations \u2014 offline lookup",
235
+ resultKey: "porto",
236
+ capturadoEm: PORTOS_DATA_VERSION.capturadoEm,
237
+ lookup: (input) => getPortoPorCodigo(input),
238
+ formatHuman: (result) => {
239
+ const row = result;
240
+ return `${row.codigo} \u2014 ${row.nome} (${row.uf})`;
241
+ }
242
+ },
243
+ aeroportos: {
244
+ command: "aeroportos",
245
+ description: "ANAC public aerodromos \u2014 offline lookup by IATA or ICAO",
246
+ resultKey: "aeroporto",
247
+ capturadoEm: AEROPORTOS_DATA_VERSION.capturadoEm,
248
+ lookup: lookupAeroporto,
249
+ formatHuman: (result) => {
250
+ const row = result;
251
+ const iata = row.iata ?? "\u2014";
252
+ return `${iata}/${row.icao} \u2014 ${row.nome} (${row.uf})`;
253
+ }
254
+ }
255
+ };
256
+ function isReferenceLookupCommand(value) {
257
+ return REFERENCE_LOOKUP_COMMANDS.includes(value);
258
+ }
259
+
260
+ // src/commands/reference-lookup/lookup.ts
261
+ function runReferenceLookupCommand(command, input, options, io = { stdout: [], stderr: [] }) {
262
+ const module = REFERENCE_LOOKUP_MODULES[command];
263
+ const trimmed = input.trim();
264
+ if (trimmed.length === 0) {
265
+ io.stderr.push(`Missing code. Pass a lookup value for ${command}.`);
266
+ return EXIT.USAGE;
267
+ }
268
+ const result = module.lookup(trimmed);
269
+ if (!result) {
270
+ io.stderr.push(`Not found: ${trimmed}`);
271
+ return EXIT.INVALID;
272
+ }
273
+ if (options.json) {
274
+ const payload = {
275
+ ok: true,
276
+ [module.resultKey]: result,
277
+ ...options.verbose ? { capturadoEm: module.capturadoEm } : {}
278
+ };
279
+ io.stdout.push(JSON.stringify(payload, null, 2));
280
+ return EXIT.OK;
281
+ }
282
+ io.stdout.push(module.formatHuman(result));
283
+ if (options.verbose) {
284
+ io.stdout.push(`capturadoEm: ${module.capturadoEm}`);
285
+ }
286
+ return EXIT.OK;
287
+ }
288
+ function runReferenceLookup(command, value, options, io = { stdout: [], stderr: [] }) {
289
+ if (!isReferenceLookupCommand(command)) {
290
+ io.stderr.push(`Unknown reference lookup command: ${command}`);
291
+ return EXIT.USAGE;
292
+ }
293
+ if (!value?.trim()) {
294
+ io.stderr.push(`Missing code. Usage: br-validators ${command} lookup <codigo>`);
295
+ return EXIT.USAGE;
296
+ }
297
+ return runReferenceLookupCommand(command, value.trim(), options, io);
298
+ }
299
+
27
300
  // src/commands/brcode.ts
301
+ import {
302
+ BRCODE_OFFICIAL_SOURCE_URL,
303
+ parseBrCode,
304
+ validateBrCode
305
+ } from "@br-validators/core";
28
306
  function resolveInput(value, fileContent) {
29
307
  const input = value ?? fileContent?.trim();
30
308
  if (!input) {
@@ -1670,11 +1948,55 @@ function handleGenerateCli(type, opts, io = { stdout: [], stderr: [] }) {
1670
1948
  io
1671
1949
  );
1672
1950
  }
1951
+ function handleBancosLookupCli(value, opts, io = { stdout: [], stderr: [] }) {
1952
+ return runBancosLookup(
1953
+ value,
1954
+ {
1955
+ json: Boolean(opts.json),
1956
+ verbose: Boolean(opts.verbose)
1957
+ },
1958
+ io
1959
+ );
1960
+ }
1961
+ function handleBancosListCli(opts, io = { stdout: [], stderr: [] }) {
1962
+ return runBancosList(
1963
+ {
1964
+ json: Boolean(opts.json),
1965
+ verbose: Boolean(opts.verbose),
1966
+ limit: opts.limit
1967
+ },
1968
+ io
1969
+ );
1970
+ }
1971
+ function handleReferenceLookupCli(command, value, opts, io = { stdout: [], stderr: [] }) {
1972
+ return runReferenceLookup(
1973
+ command,
1974
+ value,
1975
+ {
1976
+ json: Boolean(opts.json),
1977
+ verbose: Boolean(opts.verbose)
1978
+ },
1979
+ io
1980
+ );
1981
+ }
1673
1982
  function writeCliIo(io) {
1674
1983
  for (const line of io.stdout) console.log(line);
1675
1984
  for (const line of io.stderr) console.error(line);
1676
1985
  }
1677
1986
 
1987
+ // src/commands/reference-lookup/register-program.ts
1988
+ function registerReferenceLookupCommands(program) {
1989
+ for (const command of REFERENCE_LOOKUP_COMMANDS) {
1990
+ const module = REFERENCE_LOOKUP_MODULES[command];
1991
+ const root = program.command(command).description(module.description);
1992
+ root.command("lookup").description(`Resolve ${command} by official code`).argument("<codigo>", "Lookup code").option("--json", "JSON output").option("--verbose", "Include dataset capture date").action((codigo, opts) => {
1993
+ const io = { stdout: [], stderr: [] };
1994
+ process.exitCode = handleReferenceLookupCli(command, codigo, opts, io);
1995
+ writeCliIo(io);
1996
+ });
1997
+ }
1998
+ }
1999
+
1678
2000
  // src/program.ts
1679
2001
  function createProgram() {
1680
2002
  const program = new Command();
@@ -1843,6 +2165,18 @@ function createProgram() {
1843
2165
  writeCliIo(io);
1844
2166
  });
1845
2167
  }
2168
+ const bancos = program.command("bancos").description("Bacen STR participants \u2014 offline lookup");
2169
+ bancos.command("lookup").description("Resolve bank by COMPE (3 digits) or ISPB (8 digits)").argument("<codigoOuIspb>", "COMPE or ISPB").option("--json", "JSON output").option("--verbose", "Include dataset capture date (BANCOS_DATA_VERSION)").action((codigoOuIspb, opts) => {
2170
+ const io = { stdout: [], stderr: [] };
2171
+ process.exitCode = handleBancosLookupCli(codigoOuIspb, opts, io);
2172
+ writeCliIo(io);
2173
+ });
2174
+ bancos.command("list").description("List STR participants (optional --limit)").option("--json", "JSON output").option("--verbose", "Include dataset capture date (BANCOS_DATA_VERSION)").option("--limit <n>", "Maximum rows to print", (v) => Number(v)).action((opts) => {
2175
+ const io = { stdout: [], stderr: [] };
2176
+ process.exitCode = handleBancosListCli(opts, io);
2177
+ writeCliIo(io);
2178
+ });
2179
+ registerReferenceLookupCommands(program);
1846
2180
  program.command("detect").description("Detect document type from raw input").argument("[value]", "Raw value to classify").option("--uf <uf>", "State code for Inscri\xE7\xE3o Estadual detection").option("--json", "JSON output").option("-q, --quiet", "Exit code only").option("-f, --file <path>", "Read value from file").action((value, opts) => {
1847
2181
  const io = { stdout: [], stderr: [] };
1848
2182
  process.exitCode = handleDetectCli(value, opts, io);
@@ -13,6 +13,286 @@ var EXIT = {
13
13
  USAGE: 2
14
14
  };
15
15
 
16
+ // src/commands/reference-lookup/registry.ts
17
+ import {
18
+ CEST_DATA_VERSION,
19
+ getCestPorCodigo
20
+ } from "@br-validators/core/cest";
21
+ import {
22
+ INCOTERMS_DATA_VERSION,
23
+ getIncotermPorCodigo
24
+ } from "@br-validators/core/incoterms";
25
+ import {
26
+ MOEDAS_DATA_VERSION,
27
+ getMoedaPorCodigo
28
+ } from "@br-validators/core/moedas";
29
+ import {
30
+ NATUREZA_JURIDICA_DATA_VERSION,
31
+ getNaturezaJuridicaPorCodigo
32
+ } from "@br-validators/core/natureza-juridica";
33
+ import { NBS_DATA_VERSION, getNbsPorCodigo } from "@br-validators/core/nbs";
34
+ import {
35
+ PAISES_BACEN_DATA_VERSION,
36
+ getPaisPorCodigoBacen
37
+ } from "@br-validators/core/paises-bacen";
38
+ import {
39
+ AEROPORTOS_DATA_VERSION,
40
+ getAeroportoPorIata,
41
+ getAeroportoPorIcao
42
+ } from "@br-validators/core/aeroportos";
43
+ import { PORTOS_DATA_VERSION, getPortoPorCodigo } from "@br-validators/core/portos";
44
+ var REFERENCE_LOOKUP_COMMANDS = [
45
+ "natureza-juridica",
46
+ "nbs",
47
+ "cest",
48
+ "moedas",
49
+ "paises-bacen",
50
+ "incoterms",
51
+ "portos",
52
+ "aeroportos"
53
+ ];
54
+ function lookupAeroporto(input) {
55
+ const normalized = input.trim().toUpperCase();
56
+ if (/^[A-Z0-9]{3}$/.test(normalized)) {
57
+ return getAeroportoPorIata(normalized);
58
+ }
59
+ if (/^[A-Z]{4}$/.test(normalized)) {
60
+ return getAeroportoPorIcao(normalized);
61
+ }
62
+ return void 0;
63
+ }
64
+ var REFERENCE_LOOKUP_MODULES = {
65
+ "natureza-juridica": {
66
+ command: "natureza-juridica",
67
+ description: "RFB legal nature codes \u2014 offline lookup",
68
+ resultKey: "naturezaJuridica",
69
+ capturadoEm: NATUREZA_JURIDICA_DATA_VERSION.capturadoEm,
70
+ lookup: (input) => getNaturezaJuridicaPorCodigo(input),
71
+ formatHuman: (result) => {
72
+ const row = result;
73
+ return `${row.codigo} \u2014 ${row.descricao}`;
74
+ }
75
+ },
76
+ nbs: {
77
+ command: "nbs",
78
+ description: "NFSe NBS service codes \u2014 offline lookup",
79
+ resultKey: "nbs",
80
+ capturadoEm: NBS_DATA_VERSION.capturadoEm,
81
+ lookup: (input) => getNbsPorCodigo(input),
82
+ formatHuman: (result) => {
83
+ const row = result;
84
+ return `${row.codigo} \u2014 ${row.descricao}`;
85
+ }
86
+ },
87
+ cest: {
88
+ command: "cest",
89
+ description: "CONFAZ CEST ST codes \u2014 offline lookup",
90
+ resultKey: "cest",
91
+ capturadoEm: CEST_DATA_VERSION.capturadoEm,
92
+ lookup: (input) => getCestPorCodigo(input),
93
+ formatHuman: (result) => {
94
+ const row = result;
95
+ return `${row.codigo} \u2014 ${row.descricao}`;
96
+ }
97
+ },
98
+ moedas: {
99
+ command: "moedas",
100
+ description: "ISO 4217 + Bacen PTAX currencies \u2014 offline lookup",
101
+ resultKey: "moeda",
102
+ capturadoEm: MOEDAS_DATA_VERSION.capturadoEm,
103
+ lookup: (input) => getMoedaPorCodigo(input),
104
+ formatHuman: (result) => {
105
+ const row = result;
106
+ const symbol = row.simbolo ?? "\u2014";
107
+ return `${row.codigo} \u2014 ${row.nome} (${symbol})`;
108
+ }
109
+ },
110
+ "paises-bacen": {
111
+ command: "paises-bacen",
112
+ description: "NF-e Bacen country codes \u2014 offline lookup",
113
+ resultKey: "pais",
114
+ capturadoEm: PAISES_BACEN_DATA_VERSION.capturadoEm,
115
+ lookup: (input) => getPaisPorCodigoBacen(input),
116
+ formatHuman: (result) => {
117
+ const row = result;
118
+ return `${row.codigo} \u2014 ${row.nome}`;
119
+ }
120
+ },
121
+ incoterms: {
122
+ command: "incoterms",
123
+ description: "ICC Incoterms 2020 \u2014 offline lookup",
124
+ resultKey: "incoterm",
125
+ capturadoEm: INCOTERMS_DATA_VERSION.capturadoEm,
126
+ lookup: (input) => getIncotermPorCodigo(input),
127
+ formatHuman: (result) => {
128
+ const row = result;
129
+ return `${row.codigo} \u2014 ${row.nome} (${row.edicao})`;
130
+ }
131
+ },
132
+ portos: {
133
+ command: "portos",
134
+ description: "ANTAQ port installations \u2014 offline lookup",
135
+ resultKey: "porto",
136
+ capturadoEm: PORTOS_DATA_VERSION.capturadoEm,
137
+ lookup: (input) => getPortoPorCodigo(input),
138
+ formatHuman: (result) => {
139
+ const row = result;
140
+ return `${row.codigo} \u2014 ${row.nome} (${row.uf})`;
141
+ }
142
+ },
143
+ aeroportos: {
144
+ command: "aeroportos",
145
+ description: "ANAC public aerodromos \u2014 offline lookup by IATA or ICAO",
146
+ resultKey: "aeroporto",
147
+ capturadoEm: AEROPORTOS_DATA_VERSION.capturadoEm,
148
+ lookup: lookupAeroporto,
149
+ formatHuman: (result) => {
150
+ const row = result;
151
+ const iata = row.iata ?? "\u2014";
152
+ return `${iata}/${row.icao} \u2014 ${row.nome} (${row.uf})`;
153
+ }
154
+ }
155
+ };
156
+ function isReferenceLookupCommand(value) {
157
+ return REFERENCE_LOOKUP_COMMANDS.includes(value);
158
+ }
159
+
160
+ // src/commands/bancos/list.ts
161
+ import { BANCOS_DATA_VERSION as BANCOS_DATA_VERSION2, getBancos } from "@br-validators/core/bancos";
162
+
163
+ // src/commands/bancos/lookup.ts
164
+ import {
165
+ BANCOS_DATA_VERSION,
166
+ getBancoPorCodigo,
167
+ getBancoPorIspb
168
+ } from "@br-validators/core/bancos";
169
+ function normalizeBancosLookupInput(raw) {
170
+ const digits = raw.replace(/\D/g, "");
171
+ if (digits.length === 8) {
172
+ return { kind: "ispb", value: digits.padStart(8, "0") };
173
+ }
174
+ if (digits.length >= 1 && digits.length <= 3) {
175
+ return { kind: "codigo", value: digits.padStart(3, "0").slice(-3) };
176
+ }
177
+ return null;
178
+ }
179
+ function lookupBanco(raw) {
180
+ const normalized = normalizeBancosLookupInput(raw);
181
+ if (!normalized) {
182
+ return void 0;
183
+ }
184
+ return normalized.kind === "ispb" ? getBancoPorIspb(normalized.value) : getBancoPorCodigo(normalized.value);
185
+ }
186
+ function formatBancoHuman(banco) {
187
+ return `${banco.codigo} \u2014 ${banco.nome} (ISPB ${banco.ispb})`;
188
+ }
189
+ function runBancosLookupCommand(input, options, io = { stdout: [], stderr: [] }) {
190
+ const normalized = normalizeBancosLookupInput(input);
191
+ if (!normalized) {
192
+ io.stderr.push("Invalid bank code or ISPB. Use 3-digit COMPE (e.g. 001) or 8-digit ISPB.");
193
+ return EXIT.USAGE;
194
+ }
195
+ const banco = lookupBanco(input);
196
+ if (!banco) {
197
+ io.stderr.push(`Bank not found: ${input}`);
198
+ return EXIT.INVALID;
199
+ }
200
+ if (options.json) {
201
+ const payload = { ok: true, banco };
202
+ if (options.verbose) {
203
+ payload.capturadoEm = BANCOS_DATA_VERSION.capturadoEm;
204
+ }
205
+ io.stdout.push(JSON.stringify(payload, null, 2));
206
+ return EXIT.OK;
207
+ }
208
+ io.stdout.push(formatBancoHuman(banco));
209
+ if (options.verbose) {
210
+ io.stdout.push(`capturadoEm: ${BANCOS_DATA_VERSION.capturadoEm}`);
211
+ }
212
+ return EXIT.OK;
213
+ }
214
+ function runBancosLookup(value, options, io = { stdout: [], stderr: [] }) {
215
+ if (!value?.trim()) {
216
+ io.stderr.push("Missing bank code or ISPB. Pass COMPE (001) or ISPB (8 digits).");
217
+ return EXIT.USAGE;
218
+ }
219
+ return runBancosLookupCommand(value.trim(), options, io);
220
+ }
221
+
222
+ // src/commands/bancos/list.ts
223
+ function sliceBancos(limit) {
224
+ const all = getBancos();
225
+ if (limit === void 0 || !Number.isFinite(limit) || limit <= 0) {
226
+ return all;
227
+ }
228
+ return all.slice(0, limit);
229
+ }
230
+ function runBancosListCommand(options, io = { stdout: [], stderr: [] }) {
231
+ const bancos = sliceBancos(options.limit);
232
+ if (options.json) {
233
+ const payload = {
234
+ ok: true,
235
+ total: bancos.length,
236
+ bancos
237
+ };
238
+ if (options.verbose) {
239
+ payload.capturadoEm = BANCOS_DATA_VERSION2.capturadoEm;
240
+ }
241
+ io.stdout.push(JSON.stringify(payload, null, 2));
242
+ return EXIT.OK;
243
+ }
244
+ for (const banco of bancos) {
245
+ io.stdout.push(formatBancoHuman(banco));
246
+ }
247
+ if (options.verbose) {
248
+ io.stdout.push(`capturadoEm: ${BANCOS_DATA_VERSION2.capturadoEm}`);
249
+ }
250
+ return EXIT.OK;
251
+ }
252
+ function runBancosList(options, io = { stdout: [], stderr: [] }) {
253
+ return runBancosListCommand(options, io);
254
+ }
255
+
256
+ // src/commands/reference-lookup/lookup.ts
257
+ function runReferenceLookupCommand(command, input, options, io = { stdout: [], stderr: [] }) {
258
+ const module = REFERENCE_LOOKUP_MODULES[command];
259
+ const trimmed = input.trim();
260
+ if (trimmed.length === 0) {
261
+ io.stderr.push(`Missing code. Pass a lookup value for ${command}.`);
262
+ return EXIT.USAGE;
263
+ }
264
+ const result = module.lookup(trimmed);
265
+ if (!result) {
266
+ io.stderr.push(`Not found: ${trimmed}`);
267
+ return EXIT.INVALID;
268
+ }
269
+ if (options.json) {
270
+ const payload = {
271
+ ok: true,
272
+ [module.resultKey]: result,
273
+ ...options.verbose ? { capturadoEm: module.capturadoEm } : {}
274
+ };
275
+ io.stdout.push(JSON.stringify(payload, null, 2));
276
+ return EXIT.OK;
277
+ }
278
+ io.stdout.push(module.formatHuman(result));
279
+ if (options.verbose) {
280
+ io.stdout.push(`capturadoEm: ${module.capturadoEm}`);
281
+ }
282
+ return EXIT.OK;
283
+ }
284
+ function runReferenceLookup(command, value, options, io = { stdout: [], stderr: [] }) {
285
+ if (!isReferenceLookupCommand(command)) {
286
+ io.stderr.push(`Unknown reference lookup command: ${command}`);
287
+ return EXIT.USAGE;
288
+ }
289
+ if (!value?.trim()) {
290
+ io.stderr.push(`Missing code. Usage: br-validators ${command} lookup <codigo>`);
291
+ return EXIT.USAGE;
292
+ }
293
+ return runReferenceLookupCommand(command, value.trim(), options, io);
294
+ }
295
+
16
296
  // src/commands/brcode.ts
17
297
  import {
18
298
  BRCODE_OFFICIAL_SOURCE_URL,
@@ -1664,6 +1944,37 @@ function handleGenerateCli(type, opts, io = { stdout: [], stderr: [] }) {
1664
1944
  io
1665
1945
  );
1666
1946
  }
1947
+ function handleBancosLookupCli(value, opts, io = { stdout: [], stderr: [] }) {
1948
+ return runBancosLookup(
1949
+ value,
1950
+ {
1951
+ json: Boolean(opts.json),
1952
+ verbose: Boolean(opts.verbose)
1953
+ },
1954
+ io
1955
+ );
1956
+ }
1957
+ function handleBancosListCli(opts, io = { stdout: [], stderr: [] }) {
1958
+ return runBancosList(
1959
+ {
1960
+ json: Boolean(opts.json),
1961
+ verbose: Boolean(opts.verbose),
1962
+ limit: opts.limit
1963
+ },
1964
+ io
1965
+ );
1966
+ }
1967
+ function handleReferenceLookupCli(command, value, opts, io = { stdout: [], stderr: [] }) {
1968
+ return runReferenceLookup(
1969
+ command,
1970
+ value,
1971
+ {
1972
+ json: Boolean(opts.json),
1973
+ verbose: Boolean(opts.verbose)
1974
+ },
1975
+ io
1976
+ );
1977
+ }
1667
1978
 
1668
1979
  // src/argv-dispatch.ts
1669
1980
  var STANDARD_ACTIONS = ["validate", "format", "strip"];
@@ -1727,6 +2038,15 @@ function parseArgv(tokens) {
1727
2038
  index += 1;
1728
2039
  continue;
1729
2040
  }
2041
+ if (token === "--verbose") {
2042
+ opts.verbose = true;
2043
+ continue;
2044
+ }
2045
+ if (token === "--limit") {
2046
+ opts.limit = Number(tokens[index + 1]);
2047
+ index += 1;
2048
+ continue;
2049
+ }
1730
2050
  if (token.startsWith("-")) {
1731
2051
  continue;
1732
2052
  }
@@ -1749,7 +2069,7 @@ function dispatchArgv(tokens, io) {
1749
2069
  if (tokens.length === 0 || tokens.includes("--help") || tokens.includes("-h")) {
1750
2070
  io.stdout.push("br-validators \u2014 100% open-source Brazilian document validators");
1751
2071
  io.stdout.push("Usage: br-validators <command> ...");
1752
- io.stdout.push("Commands: list \xB7 cpf \xB7 cnpj \xB7 cep \xB7 telefone \xB7 cnh \xB7 renavam \xB7 titulo-eleitor \xB7 nfe-chave \xB7 brcode \xB7 placa \xB7 pis-pasep \xB7 pix \xB7 boleto \xB7 cartao \xB7 cartao-credito \xB7 ie \xB7 detect \xB7 sanitize \xB7 generate");
2072
+ io.stdout.push("Commands: list \xB7 cpf \xB7 cnpj \xB7 cep \xB7 telefone \xB7 cnh \xB7 renavam \xB7 titulo-eleitor \xB7 nfe-chave \xB7 brcode \xB7 placa \xB7 pis-pasep \xB7 pix \xB7 boleto \xB7 cartao \xB7 cartao-credito \xB7 ie \xB7 bancos \xB7 natureza-juridica \xB7 nbs \xB7 cest \xB7 moedas \xB7 paises-bacen \xB7 incoterms \xB7 portos \xB7 aeroportos \xB7 detect \xB7 sanitize \xB7 generate");
1753
2073
  return EXIT.OK;
1754
2074
  }
1755
2075
  if (tokens.includes("--version") || tokens.includes("-V")) {
@@ -1851,14 +2171,34 @@ function dispatchArgv(tokens, io) {
1851
2171
  io,
1852
2172
  (action, value, ieOpts, ioArg) => handleIeCli(action, value, ieOpts, ioArg)
1853
2173
  );
2174
+ case "bancos": {
2175
+ const action = rest[0];
2176
+ if (action === "lookup") {
2177
+ const value = rest.slice(1).join(" ") || void 0;
2178
+ return handleBancosLookupCli(value, opts, io);
2179
+ }
2180
+ if (action === "list") {
2181
+ return handleBancosListCli(opts, io);
2182
+ }
2183
+ return usage(io, "Expected: bancos lookup <codigo|ispb> | bancos list [--limit n]");
2184
+ }
1854
2185
  case "detect":
1855
2186
  return handleDetectCli(rest.join(" ") || void 0, opts, io);
1856
2187
  case "sanitize":
1857
2188
  return handleSanitizeCli(rest[0] ?? "", rest.slice(1).join(" ") || void 0, opts, io);
1858
2189
  case "generate":
1859
2190
  return handleGenerateCli(rest[0] ?? "", opts, io);
1860
- default:
2191
+ default: {
2192
+ if (isReferenceLookupCommand(root)) {
2193
+ const action = rest[0];
2194
+ if (action === "lookup") {
2195
+ const value = rest.slice(1).join(" ") || void 0;
2196
+ return handleReferenceLookupCli(root, value, opts, io);
2197
+ }
2198
+ return usage(io, `Expected: ${root} lookup <codigo>`);
2199
+ }
1861
2200
  return usage(io, `Unknown command: ${root}`);
2201
+ }
1862
2202
  }
1863
2203
  }
1864
2204
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@br-validators/cli",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "CLI for @br-validators/core — CPF, CNPJ, NF-e, IE, PIX, boleto + detect/sanitize/generate",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "commander": "^13.1.0",
48
- "@br-validators/core": "1.3.0"
48
+ "@br-validators/core": "1.5.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^22.15.21",