@br-validators/cli 1.8.3 → 1.9.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/dist/index.js CHANGED
@@ -1,16 +1,13 @@
1
1
  #!/usr/bin/env node
2
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
- }) : x)(function(x) {
5
- if (typeof require !== "undefined") return require.apply(this, arguments);
6
- throw Error('Dynamic require of "' + x + '" is not supported');
7
- });
8
2
 
9
3
  // src/program.ts
10
4
  import { Command } from "commander";
11
5
 
6
+ // src/handlers.ts
7
+ import { createRequire } from "module";
8
+
12
9
  // src/commands/bancos/list.ts
13
- import { BANCOS_DATA_VERSION as BANCOS_DATA_VERSION2, getBancos } from "@br-validators/core/bancos";
10
+ import { BANCOS_DATA_VERSION as BANCOS_DATA_VERSION2, getAllBancos } from "@br-validators/core/bancos";
14
11
 
15
12
  // src/constants.ts
16
13
  var SUPPORTED_TYPES = ["cnpj", "cpf", "cep", "telefone", "cnh", "renavam", "titulo-eleitor", "processo-judicial", "rg", "nfe-chave", "brcode", "placa", "pis-pasep", "pix", "boleto", "cartao", "ie"];
@@ -23,9 +20,10 @@ var EXIT = {
23
20
  // src/commands/bancos/lookup.ts
24
21
  import {
25
22
  BANCOS_DATA_VERSION,
26
- getBancoPorCodigo,
27
- getBancoPorIspb
23
+ lookupBancoPorCodigo,
24
+ lookupBancoPorIspb
28
25
  } from "@br-validators/core/bancos";
26
+ import { lookupInvalidFormat } from "@br-validators/core/lookup";
29
27
  function normalizeBancosLookupInput(raw) {
30
28
  const digits = raw.replace(/\D/g, "");
31
29
  if (digits.length === 8) {
@@ -39,33 +37,36 @@ function normalizeBancosLookupInput(raw) {
39
37
  function lookupBanco(raw) {
40
38
  const normalized = normalizeBancosLookupInput(raw);
41
39
  if (!normalized) {
42
- return void 0;
40
+ return lookupInvalidFormat(
41
+ "Invalid bank code or ISPB. Use 3-digit COMPE (e.g. 001) or 8-digit ISPB."
42
+ );
43
43
  }
44
- return normalized.kind === "ispb" ? getBancoPorIspb(normalized.value) : getBancoPorCodigo(normalized.value);
44
+ return normalized.kind === "ispb" ? lookupBancoPorIspb(normalized.value) : lookupBancoPorCodigo(normalized.value);
45
45
  }
46
46
  function formatBancoHuman(banco) {
47
47
  return `${banco.codigo} \u2014 ${banco.nome} (ISPB ${banco.ispb})`;
48
48
  }
49
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;
50
+ const result = lookupBanco(input);
51
+ if (!result.ok) {
52
+ if (options.json) {
53
+ io.stdout.push(
54
+ JSON.stringify({ ok: false, code: result.code, message: result.message }, null, 2)
55
+ );
56
+ } else {
57
+ io.stderr.push(result.message);
58
+ }
59
+ return normalizeBancosLookupInput(input) === null ? EXIT.USAGE : EXIT.INVALID;
59
60
  }
60
61
  if (options.json) {
61
- const payload = { ok: true, banco };
62
+ const payload = { ok: true, banco: result.value };
62
63
  if (options.verbose) {
63
64
  payload.capturadoEm = BANCOS_DATA_VERSION.capturadoEm;
64
65
  }
65
66
  io.stdout.push(JSON.stringify(payload, null, 2));
66
67
  return EXIT.OK;
67
68
  }
68
- io.stdout.push(formatBancoHuman(banco));
69
+ io.stdout.push(formatBancoHuman(result.value));
69
70
  if (options.verbose) {
70
71
  io.stdout.push(`capturadoEm: ${BANCOS_DATA_VERSION.capturadoEm}`);
71
72
  }
@@ -81,7 +82,7 @@ function runBancosLookup(value, options, io = { stdout: [], stderr: [] }) {
81
82
 
82
83
  // src/commands/bancos/list.ts
83
84
  function sliceBancos(limit) {
84
- const all = getBancos();
85
+ const all = getAllBancos();
85
86
  if (limit === void 0 || !Number.isFinite(limit) || limit <= 0) {
86
87
  return all;
87
88
  }
@@ -116,41 +117,45 @@ function runBancosList(options, io = { stdout: [], stderr: [] }) {
116
117
  // src/commands/reference-lookup/registry.ts
117
118
  import {
118
119
  CEST_DATA_VERSION,
119
- getCestPorCodigo
120
+ lookupCestPorCodigo
120
121
  } from "@br-validators/core/cest";
121
122
  import {
122
123
  INCOTERMS_DATA_VERSION,
123
- getIncotermPorCodigo
124
+ lookupIncotermPorCodigo
124
125
  } from "@br-validators/core/incoterms";
125
126
  import {
126
127
  MOEDAS_DATA_VERSION,
127
- getMoedaPorCodigo
128
+ lookupMoedaPorCodigo
128
129
  } from "@br-validators/core/moedas";
129
130
  import {
130
131
  NATUREZA_JURIDICA_DATA_VERSION,
131
- getNaturezaJuridicaPorCodigo
132
+ lookupNaturezaJuridicaPorCodigo
132
133
  } from "@br-validators/core/natureza-juridica";
133
- import { NBS_DATA_VERSION, getNbsPorCodigo } from "@br-validators/core/nbs";
134
+ import { NBS_DATA_VERSION, lookupNbsPorCodigo } from "@br-validators/core/nbs";
134
135
  import {
135
136
  PAISES_BACEN_DATA_VERSION,
136
- getPaisPorCodigoBacen
137
+ lookupPaisPorCodigoBacen
137
138
  } from "@br-validators/core/paises-bacen";
138
139
  import {
139
140
  AEROPORTOS_DATA_VERSION,
140
- getAeroportoPorIata,
141
- getAeroportoPorIcao
141
+ lookupAeroportoPorIata,
142
+ lookupAeroportoPorIcao
142
143
  } from "@br-validators/core/aeroportos";
143
- import { PORTOS_DATA_VERSION, getPortoPorCodigo } from "@br-validators/core/portos";
144
+ import { PORTOS_DATA_VERSION, lookupPortoPorCodigo } from "@br-validators/core/portos";
144
145
  import {
145
146
  CNAES_DATA_VERSION,
146
- getCnaePorCodigo
147
+ lookupCnaePorCodigo
147
148
  } from "@br-validators/core/cnaes";
148
149
  import {
149
150
  CFOP_DATA_VERSION,
150
- getCfopPorCodigo
151
+ lookupCfopPorCodigo
151
152
  } from "@br-validators/core/cfop";
152
- import { NCM_DATA_VERSION, getNcmPorCodigo } from "@br-validators/core/ncm";
153
- import { CBO_DATA_VERSION, getCboPorCodigo } from "@br-validators/core/cbo";
153
+ import { NCM_DATA_VERSION, lookupNcmPorCodigo } from "@br-validators/core/ncm";
154
+ import { CBO_DATA_VERSION, lookupCboPorCodigo } from "@br-validators/core/cbo";
155
+ import {
156
+ lookupInvalidFormat as lookupInvalidFormat2,
157
+ lookupInvalidInput
158
+ } from "@br-validators/core/lookup";
154
159
  var REFERENCE_LOOKUP_COMMANDS = [
155
160
  "natureza-juridica",
156
161
  "nbs",
@@ -168,13 +173,16 @@ var REFERENCE_LOOKUP_COMMANDS = [
168
173
  var REFERENCE_SEARCH_COMMANDS = ["cnae", "cfop", "ncm", "cbo"];
169
174
  function lookupAeroporto(input) {
170
175
  const normalized = input.trim().toUpperCase();
176
+ if (normalized.length === 0) {
177
+ return lookupInvalidInput("Airport code is required");
178
+ }
171
179
  if (/^[A-Z0-9]{3}$/.test(normalized)) {
172
- return getAeroportoPorIata(normalized);
180
+ return lookupAeroportoPorIata(normalized);
173
181
  }
174
182
  if (/^[A-Z]{4}$/.test(normalized)) {
175
- return getAeroportoPorIcao(normalized);
183
+ return lookupAeroportoPorIcao(normalized);
176
184
  }
177
- return void 0;
185
+ return lookupInvalidFormat2("Airport code must be 3-character IATA or 4-character ICAO");
178
186
  }
179
187
  var REFERENCE_LOOKUP_MODULES = {
180
188
  "natureza-juridica": {
@@ -182,7 +190,7 @@ var REFERENCE_LOOKUP_MODULES = {
182
190
  description: "RFB legal nature codes \u2014 offline lookup",
183
191
  resultKey: "naturezaJuridica",
184
192
  capturadoEm: NATUREZA_JURIDICA_DATA_VERSION.capturadoEm,
185
- lookup: (input) => getNaturezaJuridicaPorCodigo(input),
193
+ lookup: (input) => lookupNaturezaJuridicaPorCodigo(input),
186
194
  formatHuman: (result) => {
187
195
  const row = result;
188
196
  return `${row.codigo} \u2014 ${row.descricao}`;
@@ -193,7 +201,7 @@ var REFERENCE_LOOKUP_MODULES = {
193
201
  description: "NFSe NBS service codes \u2014 offline lookup",
194
202
  resultKey: "nbs",
195
203
  capturadoEm: NBS_DATA_VERSION.capturadoEm,
196
- lookup: (input) => getNbsPorCodigo(input),
204
+ lookup: (input) => lookupNbsPorCodigo(input),
197
205
  formatHuman: (result) => {
198
206
  const row = result;
199
207
  return `${row.codigo} \u2014 ${row.descricao}`;
@@ -204,7 +212,7 @@ var REFERENCE_LOOKUP_MODULES = {
204
212
  description: "CONFAZ CEST ST codes \u2014 offline lookup",
205
213
  resultKey: "cest",
206
214
  capturadoEm: CEST_DATA_VERSION.capturadoEm,
207
- lookup: (input) => getCestPorCodigo(input),
215
+ lookup: (input) => lookupCestPorCodigo(input),
208
216
  formatHuman: (result) => {
209
217
  const row = result;
210
218
  return `${row.codigo} \u2014 ${row.descricao}`;
@@ -215,7 +223,7 @@ var REFERENCE_LOOKUP_MODULES = {
215
223
  description: "IBGE CNAE 2.3 subclasses \u2014 offline lookup",
216
224
  resultKey: "cnae",
217
225
  capturadoEm: CNAES_DATA_VERSION.capturadoEm,
218
- lookup: (input) => getCnaePorCodigo(input),
226
+ lookup: (input) => lookupCnaePorCodigo(input),
219
227
  formatHuman: (result) => {
220
228
  const row = result;
221
229
  return `${row.codigo} \u2014 ${row.descricao}`;
@@ -226,7 +234,7 @@ var REFERENCE_LOOKUP_MODULES = {
226
234
  description: "CONFAZ CFOP fiscal operations \u2014 offline lookup",
227
235
  resultKey: "cfop",
228
236
  capturadoEm: CFOP_DATA_VERSION.capturadoEm,
229
- lookup: (input) => getCfopPorCodigo(input),
237
+ lookup: (input) => lookupCfopPorCodigo(input),
230
238
  formatHuman: (result) => {
231
239
  const row = result;
232
240
  return `${row.codigo} \u2014 ${row.descricao}`;
@@ -237,7 +245,7 @@ var REFERENCE_LOOKUP_MODULES = {
237
245
  description: "Siscomex NCM Mercosur codes \u2014 offline lookup",
238
246
  resultKey: "ncm",
239
247
  capturadoEm: NCM_DATA_VERSION.capturadoEm,
240
- lookup: (input) => getNcmPorCodigo(input),
248
+ lookup: (input) => lookupNcmPorCodigo(input),
241
249
  formatHuman: (result) => {
242
250
  const row = result;
243
251
  return `${row.codigo} \u2014 ${row.descricao}`;
@@ -248,7 +256,7 @@ var REFERENCE_LOOKUP_MODULES = {
248
256
  description: "MTE CBO 2002 occupations \u2014 offline lookup",
249
257
  resultKey: "cbo",
250
258
  capturadoEm: CBO_DATA_VERSION.capturadoEm,
251
- lookup: (input) => getCboPorCodigo(input),
259
+ lookup: (input) => lookupCboPorCodigo(input),
252
260
  formatHuman: (result) => {
253
261
  const row = result;
254
262
  return `${row.codigo} \u2014 ${row.descricao}`;
@@ -259,7 +267,7 @@ var REFERENCE_LOOKUP_MODULES = {
259
267
  description: "ISO 4217 + Bacen PTAX currencies \u2014 offline lookup",
260
268
  resultKey: "moeda",
261
269
  capturadoEm: MOEDAS_DATA_VERSION.capturadoEm,
262
- lookup: (input) => getMoedaPorCodigo(input),
270
+ lookup: (input) => lookupMoedaPorCodigo(input),
263
271
  formatHuman: (result) => {
264
272
  const row = result;
265
273
  const symbol = row.simbolo ?? "\u2014";
@@ -271,7 +279,7 @@ var REFERENCE_LOOKUP_MODULES = {
271
279
  description: "NF-e Bacen country codes \u2014 offline lookup",
272
280
  resultKey: "pais",
273
281
  capturadoEm: PAISES_BACEN_DATA_VERSION.capturadoEm,
274
- lookup: (input) => getPaisPorCodigoBacen(input),
282
+ lookup: (input) => lookupPaisPorCodigoBacen(input),
275
283
  formatHuman: (result) => {
276
284
  const row = result;
277
285
  return `${row.codigo} \u2014 ${row.nome}`;
@@ -282,7 +290,7 @@ var REFERENCE_LOOKUP_MODULES = {
282
290
  description: "ICC Incoterms 2020 \u2014 offline lookup",
283
291
  resultKey: "incoterm",
284
292
  capturadoEm: INCOTERMS_DATA_VERSION.capturadoEm,
285
- lookup: (input) => getIncotermPorCodigo(input),
293
+ lookup: (input) => lookupIncotermPorCodigo(input),
286
294
  formatHuman: (result) => {
287
295
  const row = result;
288
296
  return `${row.codigo} \u2014 ${row.nome} (${row.edicao})`;
@@ -293,7 +301,7 @@ var REFERENCE_LOOKUP_MODULES = {
293
301
  description: "ANTAQ port installations \u2014 offline lookup",
294
302
  resultKey: "porto",
295
303
  capturadoEm: PORTOS_DATA_VERSION.capturadoEm,
296
- lookup: (input) => getPortoPorCodigo(input),
304
+ lookup: (input) => lookupPortoPorCodigo(input),
297
305
  formatHuman: (result) => {
298
306
  const row = result;
299
307
  return `${row.codigo} \u2014 ${row.nome} (${row.uf})`;
@@ -320,6 +328,15 @@ function isReferenceSearchCommand(value) {
320
328
  }
321
329
 
322
330
  // src/commands/reference-lookup/lookup.ts
331
+ function emitLookupFailure(result, options, io) {
332
+ if (options.json) {
333
+ io.stdout.push(
334
+ JSON.stringify({ ok: false, code: result.code, message: result.message }, null, 2)
335
+ );
336
+ return;
337
+ }
338
+ io.stderr.push(result.message);
339
+ }
323
340
  function runReferenceLookupCommand(command, input, options, io = { stdout: [], stderr: [] }) {
324
341
  const module = REFERENCE_LOOKUP_MODULES[command];
325
342
  const trimmed = input.trim();
@@ -328,20 +345,20 @@ function runReferenceLookupCommand(command, input, options, io = { stdout: [], s
328
345
  return EXIT.USAGE;
329
346
  }
330
347
  const result = module.lookup(trimmed);
331
- if (!result) {
332
- io.stderr.push(`Not found: ${trimmed}`);
348
+ if (!result.ok) {
349
+ emitLookupFailure(result, options, io);
333
350
  return EXIT.INVALID;
334
351
  }
335
352
  if (options.json) {
336
353
  const payload = {
337
354
  ok: true,
338
- [module.resultKey]: result,
355
+ [module.resultKey]: result.value,
339
356
  ...options.verbose ? { capturadoEm: module.capturadoEm } : {}
340
357
  };
341
358
  io.stdout.push(JSON.stringify(payload, null, 2));
342
359
  return EXIT.OK;
343
360
  }
344
- io.stdout.push(module.formatHuman(result));
361
+ io.stdout.push(module.formatHuman(result.value));
345
362
  if (options.verbose) {
346
363
  io.stdout.push(`capturadoEm: ${module.capturadoEm}`);
347
364
  }
@@ -425,11 +442,239 @@ function runReferenceSearch(command, query, options, io = { stdout: [], stderr:
425
442
  return runReferenceSearchCommand(command, query.trim(), options, io);
426
443
  }
427
444
 
445
+ // src/commands/reference-lookup/validate.ts
446
+ import { validateCfop } from "@br-validators/core/cfop";
447
+ import { validateNcm } from "@br-validators/core/ncm";
448
+ var REFERENCE_VALIDATE_COMMANDS = ["ncm", "cfop"];
449
+ var VALIDATORS = {
450
+ ncm: validateNcm,
451
+ cfop: validateCfop
452
+ };
453
+ var CAPTURED_AT = {
454
+ ncm: "ncm",
455
+ cfop: "cfop"
456
+ };
457
+ function emitFailure(result, options, io) {
458
+ if (options.json) {
459
+ io.stdout.push(JSON.stringify({ ok: false, code: result.code, message: result.message }, null, 2));
460
+ return;
461
+ }
462
+ io.stderr.push(result.message);
463
+ }
464
+ function isReferenceValidateCommand(command) {
465
+ return REFERENCE_VALIDATE_COMMANDS.includes(command);
466
+ }
467
+ function runReferenceValidateCommand(command, input, options, io = { stdout: [], stderr: [] }) {
468
+ const trimmed = input.trim();
469
+ if (trimmed.length === 0) {
470
+ io.stderr.push(`Missing code. Pass a value to validate for ${command}.`);
471
+ return EXIT.USAGE;
472
+ }
473
+ const result = VALIDATORS[command](trimmed);
474
+ if (!result.ok) {
475
+ emitFailure(result, options, io);
476
+ return EXIT.INVALID;
477
+ }
478
+ if (options.json) {
479
+ const payload = {
480
+ ok: true,
481
+ value: result.value,
482
+ description: result.description,
483
+ ...result.format !== void 0 ? { format: result.format } : {},
484
+ ...options.verbose ? { module: CAPTURED_AT[command] } : {}
485
+ };
486
+ io.stdout.push(JSON.stringify(payload, null, 2));
487
+ return EXIT.OK;
488
+ }
489
+ const lines = [`${result.value} \u2014 ${result.description}`];
490
+ if (result.format !== void 0) {
491
+ lines.push(`format: ${result.format}`);
492
+ }
493
+ io.stdout.push(lines.join("\n"));
494
+ return EXIT.OK;
495
+ }
496
+ function runReferenceValidate(command, value, options, io = { stdout: [], stderr: [] }) {
497
+ if (!isReferenceValidateCommand(command)) {
498
+ io.stderr.push(`Unknown reference validate command: ${command}`);
499
+ return EXIT.USAGE;
500
+ }
501
+ if (!value?.trim()) {
502
+ io.stderr.push(`Missing code. Usage: br-validators ${command} validate <codigo>`);
503
+ return EXIT.USAGE;
504
+ }
505
+ return runReferenceValidateCommand(command, value.trim(), options, io);
506
+ }
507
+
508
+ // src/commands/cst/index.ts
509
+ import {
510
+ CST_DATA_VERSION,
511
+ lookupCstCofinsPorCodigo,
512
+ lookupCstIcmsPorCodigo,
513
+ lookupCstIpiPorCodigo,
514
+ lookupCstPisPorCodigo,
515
+ searchCstCofins,
516
+ searchCstIcms,
517
+ searchCstIpi,
518
+ searchCstPis,
519
+ validateCst
520
+ } from "@br-validators/core/cst";
521
+ var CST_TAXES = ["icms", "ipi", "pis", "cofins"];
522
+ function parseCstTax(raw) {
523
+ if (raw === void 0) {
524
+ return null;
525
+ }
526
+ const normalized = raw.trim().toLowerCase();
527
+ if (CST_TAXES.includes(normalized)) {
528
+ return normalized;
529
+ }
530
+ return null;
531
+ }
532
+ function lookupByTax(tax, codigo) {
533
+ switch (tax) {
534
+ case "icms":
535
+ return lookupCstIcmsPorCodigo(codigo);
536
+ case "ipi":
537
+ return lookupCstIpiPorCodigo(codigo);
538
+ case "pis":
539
+ return lookupCstPisPorCodigo(codigo);
540
+ case "cofins":
541
+ return lookupCstCofinsPorCodigo(codigo);
542
+ }
543
+ }
544
+ function searchByTax(tax, query, limit) {
545
+ const options = limit !== void 0 ? { limit } : void 0;
546
+ switch (tax) {
547
+ case "icms":
548
+ return searchCstIcms(query, options);
549
+ case "ipi":
550
+ return searchCstIpi(query, options);
551
+ case "pis":
552
+ return searchCstPis(query, options);
553
+ case "cofins":
554
+ return searchCstCofins(query, options);
555
+ }
556
+ }
557
+ function emitLookupFailure2(result, options, io) {
558
+ if (options.json) {
559
+ io.stdout.push(JSON.stringify({ ok: false, code: result.code, message: result.message }, null, 2));
560
+ return;
561
+ }
562
+ io.stderr.push(result.message);
563
+ }
564
+ function emitValidateFailure(result, options, io) {
565
+ if (options.json) {
566
+ io.stdout.push(JSON.stringify({ ok: false, code: result.code, message: result.message }, null, 2));
567
+ return;
568
+ }
569
+ io.stderr.push(result.message);
570
+ }
571
+ function runCstLookup(codigo, options, io = { stdout: [], stderr: [] }) {
572
+ const tax = parseCstTax(options.tax);
573
+ if (tax === null) {
574
+ io.stderr.push("Missing or invalid --tax. Expected: icms | ipi | pis | cofins");
575
+ return EXIT.USAGE;
576
+ }
577
+ const trimmed = codigo?.trim() ?? "";
578
+ if (trimmed.length === 0) {
579
+ io.stderr.push("Missing code. Usage: br-validators cst lookup <codigo> --tax icms");
580
+ return EXIT.USAGE;
581
+ }
582
+ const result = lookupByTax(tax, trimmed);
583
+ if (!result.ok) {
584
+ emitLookupFailure2(result, options, io);
585
+ return EXIT.INVALID;
586
+ }
587
+ if (options.json) {
588
+ io.stdout.push(
589
+ JSON.stringify(
590
+ {
591
+ ok: true,
592
+ cst: result.value,
593
+ tax,
594
+ ...options.verbose ? { capturadoEm: CST_DATA_VERSION.capturadoEm } : {}
595
+ },
596
+ null,
597
+ 2
598
+ )
599
+ );
600
+ return EXIT.OK;
601
+ }
602
+ io.stdout.push(`${result.value.codigo} \u2014 ${result.value.descricao}`);
603
+ if (options.verbose) {
604
+ io.stdout.push(`tax: ${tax}`);
605
+ io.stdout.push(`capturadoEm: ${CST_DATA_VERSION.capturadoEm}`);
606
+ }
607
+ return EXIT.OK;
608
+ }
609
+ function runCstSearch(query, options, io = { stdout: [], stderr: [] }) {
610
+ const tax = parseCstTax(options.tax);
611
+ if (tax === null) {
612
+ io.stderr.push("Missing or invalid --tax. Expected: icms | ipi | pis | cofins");
613
+ return EXIT.USAGE;
614
+ }
615
+ const trimmed = query?.trim() ?? "";
616
+ if (trimmed.length === 0) {
617
+ io.stderr.push("Missing query. Usage: br-validators cst search <query> --tax icms");
618
+ return EXIT.USAGE;
619
+ }
620
+ const rows = searchByTax(tax, trimmed, options.limit);
621
+ if (options.json) {
622
+ io.stdout.push(JSON.stringify({ ok: true, tax, results: rows }, null, 2));
623
+ return EXIT.OK;
624
+ }
625
+ if (rows.length === 0) {
626
+ io.stdout.push("No matches.");
627
+ return EXIT.OK;
628
+ }
629
+ for (const row of rows) {
630
+ io.stdout.push(`${row.codigo} \u2014 ${row.descricao}`);
631
+ }
632
+ return EXIT.OK;
633
+ }
634
+ function runCstValidate(codigo, options, io = { stdout: [], stderr: [] }) {
635
+ const tax = parseCstTax(options.tax);
636
+ if (tax === null) {
637
+ io.stderr.push("Missing or invalid --tax. Expected: icms | ipi | pis | cofins");
638
+ return EXIT.USAGE;
639
+ }
640
+ const trimmed = codigo?.trim() ?? "";
641
+ if (trimmed.length === 0) {
642
+ io.stderr.push("Missing code. Usage: br-validators cst validate <codigo> --tax icms");
643
+ return EXIT.USAGE;
644
+ }
645
+ const result = validateCst(trimmed, { tax });
646
+ if (!result.ok) {
647
+ emitValidateFailure(result, options, io);
648
+ return EXIT.INVALID;
649
+ }
650
+ if (options.json) {
651
+ io.stdout.push(
652
+ JSON.stringify(
653
+ {
654
+ ok: true,
655
+ tax,
656
+ value: result.value,
657
+ description: result.description
658
+ },
659
+ null,
660
+ 2
661
+ )
662
+ );
663
+ return EXIT.OK;
664
+ }
665
+ io.stdout.push(`${result.value} \u2014 ${result.description}`);
666
+ if (options.verbose) {
667
+ io.stdout.push(`tax: ${tax}`);
668
+ }
669
+ return EXIT.OK;
670
+ }
671
+
428
672
  // src/commands/ibge/lookup.ts
429
673
  import {
430
- getMunicipioPorCodigo,
674
+ lookupMunicipioPorCodigo,
431
675
  IBGE_DATA_VERSION
432
676
  } from "@br-validators/core/ibge";
677
+ import { lookupInvalidFormat as lookupInvalidFormat3 } from "@br-validators/core/lookup";
433
678
  function normalizeIbgeMunicipioCode(raw) {
434
679
  const digits = raw.replace(/\D/g, "");
435
680
  if (digits.length !== 7) {
@@ -437,24 +682,35 @@ function normalizeIbgeMunicipioCode(raw) {
437
682
  }
438
683
  return Number(digits);
439
684
  }
685
+ function lookupMunicipio(raw) {
686
+ const codigo = normalizeIbgeMunicipioCode(raw);
687
+ if (codigo === null) {
688
+ return lookupInvalidFormat3("Invalid IBGE municipality code. Use 7 digits (e.g. 3550308).");
689
+ }
690
+ return lookupMunicipioPorCodigo(codigo);
691
+ }
440
692
  function formatMunicipioHuman(municipio) {
441
693
  return `${municipio.codigo} \u2014 ${municipio.nome} (${municipio.uf})`;
442
694
  }
443
- function runIbgeLookupCommand(input, options, io = { stdout: [], stderr: [] }) {
444
- const codigo = normalizeIbgeMunicipioCode(input);
445
- if (codigo === null) {
446
- io.stderr.push("Invalid IBGE municipality code. Use 7 digits (e.g. 3550308).");
447
- return EXIT.USAGE;
695
+ function emitLookupFailure3(result, options, io) {
696
+ if (options.json) {
697
+ io.stdout.push(
698
+ JSON.stringify({ ok: false, code: result.code, message: result.message }, null, 2)
699
+ );
700
+ return result.code === "INVALID_FORMAT" ? EXIT.USAGE : EXIT.INVALID;
448
701
  }
449
- const municipio = getMunicipioPorCodigo(codigo);
450
- if (!municipio) {
451
- io.stderr.push(`Municipality not found: ${input}`);
452
- return EXIT.INVALID;
702
+ io.stderr.push(result.message);
703
+ return result.code === "INVALID_FORMAT" ? EXIT.USAGE : EXIT.INVALID;
704
+ }
705
+ function runIbgeLookupCommand(input, options, io = { stdout: [], stderr: [] }) {
706
+ const result = lookupMunicipio(input);
707
+ if (!result.ok) {
708
+ return emitLookupFailure3(result, options, io);
453
709
  }
454
710
  if (options.json) {
455
711
  const payload = {
456
712
  ok: true,
457
- municipio
713
+ municipio: result.value
458
714
  };
459
715
  if (options.verbose) {
460
716
  payload.capturadoEm = IBGE_DATA_VERSION.capturadoEm;
@@ -462,7 +718,7 @@ function runIbgeLookupCommand(input, options, io = { stdout: [], stderr: [] }) {
462
718
  io.stdout.push(JSON.stringify(payload, null, 2));
463
719
  return EXIT.OK;
464
720
  }
465
- io.stdout.push(formatMunicipioHuman(municipio));
721
+ io.stdout.push(formatMunicipioHuman(result.value));
466
722
  if (options.verbose) {
467
723
  io.stdout.push(`capturadoEm: ${IBGE_DATA_VERSION.capturadoEm}`);
468
724
  }
@@ -478,8 +734,8 @@ function runIbgeLookup(value, options, io = { stdout: [], stderr: [] }) {
478
734
 
479
735
  // src/commands/ibge/list.ts
480
736
  import {
481
- getEstados,
482
- getMunicipios,
737
+ getAllEstados,
738
+ getAllMunicipios,
483
739
  IBGE_DATA_VERSION as IBGE_DATA_VERSION2
484
740
  } from "@br-validators/core/ibge";
485
741
  function sliceRows(rows, limit) {
@@ -492,7 +748,7 @@ function formatEstadoHuman(estado) {
492
748
  return `${estado.codigo} \u2014 ${estado.sigla} \u2014 ${estado.nome}`;
493
749
  }
494
750
  function runIbgeListEstadosCommand(options, io = { stdout: [], stderr: [] }) {
495
- const estados = sliceRows(getEstados(), options.limit);
751
+ const estados = sliceRows(getAllEstados(), options.limit);
496
752
  if (options.json) {
497
753
  const payload = {
498
754
  ok: true,
@@ -515,7 +771,7 @@ function runIbgeListEstadosCommand(options, io = { stdout: [], stderr: [] }) {
515
771
  }
516
772
  function runIbgeListMunicipiosCommand(options, io = { stdout: [], stderr: [] }) {
517
773
  const uf = options.uf?.trim().toUpperCase();
518
- const municipios = sliceRows(getMunicipios(uf ? { uf } : void 0), options.limit);
774
+ const municipios = sliceRows(getAllMunicipios(uf ? { uf } : void 0), options.limit);
519
775
  if (options.json) {
520
776
  const payload = {
521
777
  ok: true,
@@ -547,7 +803,7 @@ function runIbgeList(target, options, io = { stdout: [], stderr: [] }) {
547
803
  // src/commands/feriados/list.ts
548
804
  import {
549
805
  FERIADOS_DATA_VERSION,
550
- getFeriadosNacionais
806
+ getAllFeriados
551
807
  } from "@br-validators/core/feriados";
552
808
  function resolveYear(year) {
553
809
  if (year !== void 0 && Number.isInteger(year) && year >= 1900 && year <= 2100) {
@@ -560,7 +816,7 @@ function formatFeriadoHuman(feriado) {
560
816
  }
561
817
  function runFeriadosListCommand(options, io = { stdout: [], stderr: [] }) {
562
818
  const year = resolveYear(options.year);
563
- const feriados = getFeriadosNacionais(year);
819
+ const feriados = getAllFeriados(year);
564
820
  if (options.json) {
565
821
  const payload = {
566
822
  ok: true,
@@ -586,13 +842,193 @@ function runFeriadosList(options, io = { stdout: [], stderr: [] }) {
586
842
  return runFeriadosListCommand(options, io);
587
843
  }
588
844
 
845
+ // src/commands/inss/index.ts
846
+ import {
847
+ calcularInssMensal,
848
+ getInssTabelaContribuicao,
849
+ INSS_DATA_VERSION,
850
+ INSS_DEFAULT_ANO
851
+ } from "@br-validators/core/inss";
852
+ function resolveAno(ano) {
853
+ if (ano !== void 0 && Number.isInteger(ano) && ano >= 1900 && ano <= 2100) {
854
+ return ano;
855
+ }
856
+ return INSS_DEFAULT_ANO;
857
+ }
858
+ function formatInssFaixaHuman(faixa) {
859
+ const aliquotaPct = `${String(faixa.aliquota * 100).replace(".", ",")}%`;
860
+ return `Faixa ${String(faixa.faixa)} \u2014 ${faixa.descricao} \u2014 ${aliquotaPct}`;
861
+ }
862
+ function runInssTabelaCommand(options, io = { stdout: [], stderr: [] }) {
863
+ const ano = resolveAno(options.ano);
864
+ const tabela = getInssTabelaContribuicao(ano);
865
+ if (tabela === void 0) {
866
+ io.stderr.push(`INSS contribution table not found for year ${String(ano)}`);
867
+ return EXIT.INVALID;
868
+ }
869
+ if (options.json) {
870
+ const payload = {
871
+ ok: true,
872
+ ano,
873
+ teto: tabela.teto,
874
+ faixas: tabela.faixas
875
+ };
876
+ if (options.verbose) {
877
+ payload.capturadoEm = INSS_DATA_VERSION.capturadoEm;
878
+ }
879
+ io.stdout.push(JSON.stringify(payload, null, 2));
880
+ return EXIT.OK;
881
+ }
882
+ io.stdout.push(`Teto: R$ ${tabela.teto.toFixed(2)}`);
883
+ for (const faixa of tabela.faixas) {
884
+ io.stdout.push(formatInssFaixaHuman(faixa));
885
+ }
886
+ if (options.verbose) {
887
+ io.stdout.push(`capturadoEm: ${INSS_DATA_VERSION.capturadoEm}`);
888
+ }
889
+ return EXIT.OK;
890
+ }
891
+ function runInssCalcCommand(salarioRaw, options, io = { stdout: [], stderr: [] }) {
892
+ const trimmed = salarioRaw.trim().replace(",", ".");
893
+ const salarioContribuicao = Number(trimmed);
894
+ if (!Number.isFinite(salarioContribuicao)) {
895
+ io.stderr.push("Invalid sal\xE1rio de contribui\xE7\xE3o. Pass a numeric value (e.g. 3000).");
896
+ return EXIT.USAGE;
897
+ }
898
+ const ano = resolveAno(options.ano);
899
+ const result = calcularInssMensal(salarioContribuicao, ano);
900
+ if (result === void 0) {
901
+ if (getInssTabelaContribuicao(ano) === void 0) {
902
+ io.stderr.push(`INSS contribution table not found for year ${String(ano)}`);
903
+ return EXIT.INVALID;
904
+ }
905
+ io.stderr.push("Sal\xE1rio de contribui\xE7\xE3o must be a non-negative number");
906
+ return EXIT.INVALID;
907
+ }
908
+ if (options.json) {
909
+ const payload = {
910
+ ok: true,
911
+ ...result,
912
+ ...options.verbose ? { capturadoEm: INSS_DATA_VERSION.capturadoEm } : {}
913
+ };
914
+ io.stdout.push(JSON.stringify(payload, null, 2));
915
+ return EXIT.OK;
916
+ }
917
+ io.stdout.push(
918
+ `INSS ${String(result.ano)} \u2014 sal\xE1rio R$ ${result.salarioContribuicao.toFixed(2)} \u2014 faixa ${String(result.faixa)} \u2014 contribui\xE7\xE3o R$ ${result.contribuicao.toFixed(2)}`
919
+ );
920
+ if (options.verbose) {
921
+ io.stdout.push(`capturadoEm: ${INSS_DATA_VERSION.capturadoEm}`);
922
+ }
923
+ return EXIT.OK;
924
+ }
925
+ function runInssTabela(options, io = { stdout: [], stderr: [] }) {
926
+ return runInssTabelaCommand(options, io);
927
+ }
928
+ function runInssCalc(salario, options, io = { stdout: [], stderr: [] }) {
929
+ if (!salario?.trim()) {
930
+ io.stderr.push("Missing sal\xE1rio de contribui\xE7\xE3o. Usage: br-validators inss calc <salario>");
931
+ return EXIT.USAGE;
932
+ }
933
+ return runInssCalcCommand(salario.trim(), options, io);
934
+ }
935
+
936
+ // src/commands/irpf/index.ts
937
+ import {
938
+ calcularIrpfMensal,
939
+ getIrpfTabelaProgressiva,
940
+ IRPF_DATA_VERSION,
941
+ IRPF_DEFAULT_ANO
942
+ } from "@br-validators/core/irpf";
943
+ function resolveAno2(ano) {
944
+ if (ano !== void 0 && Number.isInteger(ano) && ano >= 1900 && ano <= 2100) {
945
+ return ano;
946
+ }
947
+ return IRPF_DEFAULT_ANO;
948
+ }
949
+ function formatIrpfFaixaHuman(faixa) {
950
+ const aliquotaPct = `${String(faixa.aliquota * 100).replace(".", ",")}%`;
951
+ return `Faixa ${String(faixa.faixa)} \u2014 ${faixa.descricao} \u2014 ${aliquotaPct} \u2014 deduzir R$ ${faixa.parcelaDeduzir.toFixed(2)}`;
952
+ }
953
+ function runIrpfTabelaCommand(options, io = { stdout: [], stderr: [] }) {
954
+ const ano = resolveAno2(options.ano);
955
+ const faixas = getIrpfTabelaProgressiva(ano);
956
+ if (faixas === void 0) {
957
+ io.stderr.push(`IRPF progressive table not found for year ${String(ano)}`);
958
+ return EXIT.INVALID;
959
+ }
960
+ if (options.json) {
961
+ const payload = {
962
+ ok: true,
963
+ ano,
964
+ faixas
965
+ };
966
+ if (options.verbose) {
967
+ payload.capturadoEm = IRPF_DATA_VERSION.capturadoEm;
968
+ }
969
+ io.stdout.push(JSON.stringify(payload, null, 2));
970
+ return EXIT.OK;
971
+ }
972
+ for (const faixa of faixas) {
973
+ io.stdout.push(formatIrpfFaixaHuman(faixa));
974
+ }
975
+ if (options.verbose) {
976
+ io.stdout.push(`capturadoEm: ${IRPF_DATA_VERSION.capturadoEm}`);
977
+ }
978
+ return EXIT.OK;
979
+ }
980
+ function runIrpfCalcCommand(baseRaw, options, io = { stdout: [], stderr: [] }) {
981
+ const trimmed = baseRaw.trim().replace(",", ".");
982
+ const baseCalculo = Number(trimmed);
983
+ if (!Number.isFinite(baseCalculo)) {
984
+ io.stderr.push("Invalid base de c\xE1lculo. Pass a numeric value (e.g. 3000).");
985
+ return EXIT.USAGE;
986
+ }
987
+ const ano = resolveAno2(options.ano);
988
+ const result = calcularIrpfMensal(baseCalculo, ano);
989
+ if (result === void 0) {
990
+ if (getIrpfTabelaProgressiva(ano) === void 0) {
991
+ io.stderr.push(`IRPF progressive table not found for year ${String(ano)}`);
992
+ return EXIT.INVALID;
993
+ }
994
+ io.stderr.push("Base de c\xE1lculo must be a non-negative number");
995
+ return EXIT.INVALID;
996
+ }
997
+ if (options.json) {
998
+ const payload = {
999
+ ok: true,
1000
+ ...result,
1001
+ ...options.verbose ? { capturadoEm: IRPF_DATA_VERSION.capturadoEm } : {}
1002
+ };
1003
+ io.stdout.push(JSON.stringify(payload, null, 2));
1004
+ return EXIT.OK;
1005
+ }
1006
+ io.stdout.push(
1007
+ `IRPF ${String(result.ano)} \u2014 base R$ ${result.baseCalculo.toFixed(2)} \u2014 faixa ${String(result.faixa)} \u2014 imposto R$ ${result.imposto.toFixed(2)}`
1008
+ );
1009
+ if (options.verbose) {
1010
+ io.stdout.push(`capturadoEm: ${IRPF_DATA_VERSION.capturadoEm}`);
1011
+ }
1012
+ return EXIT.OK;
1013
+ }
1014
+ function runIrpfTabela(options, io = { stdout: [], stderr: [] }) {
1015
+ return runIrpfTabelaCommand(options, io);
1016
+ }
1017
+ function runIrpfCalc(base, options, io = { stdout: [], stderr: [] }) {
1018
+ if (!base?.trim()) {
1019
+ io.stderr.push("Missing base de c\xE1lculo. Usage: br-validators irpf calc <base>");
1020
+ return EXIT.USAGE;
1021
+ }
1022
+ return runIrpfCalcCommand(base.trim(), options, io);
1023
+ }
1024
+
589
1025
  // src/commands/tse-municipios/lookup.ts
590
1026
  import {
591
1027
  getCodigosTsePorMunicipio,
592
1028
  getMunicipioIbgePorCodigoTse,
593
1029
  TSE_MUNICIPIOS_DATA_VERSION
594
1030
  } from "@br-validators/core/tse-municipios";
595
- import { getMunicipioPorCodigo as getMunicipioPorCodigo2 } from "@br-validators/core/ibge";
1031
+ import { getMunicipioPorCodigo } from "@br-validators/core/ibge";
596
1032
  function normalizeTseInput(raw) {
597
1033
  const digits = raw.replace(/\D/g, "");
598
1034
  if (digits.length === 5) {
@@ -623,11 +1059,11 @@ function lookupTseMunicipio(raw) {
623
1059
  }
624
1060
  function formatTseLookupHuman(result) {
625
1061
  if (result.kind === "tse-to-ibge") {
626
- const municipio2 = getMunicipioPorCodigo2(result.ibgeCodigo);
1062
+ const municipio2 = getMunicipioPorCodigo(result.ibgeCodigo);
627
1063
  const name2 = municipio2 ? `${municipio2.nome} (${municipio2.uf})` : String(result.ibgeCodigo);
628
1064
  return `TSE ${result.codigoTse} \u2192 IBGE ${result.ibgeCodigo} \u2014 ${name2}`;
629
1065
  }
630
- const municipio = getMunicipioPorCodigo2(result.ibgeCodigo);
1066
+ const municipio = getMunicipioPorCodigo(result.ibgeCodigo);
631
1067
  const name = municipio ? `${municipio.nome} (${municipio.uf})` : String(result.ibgeCodigo);
632
1068
  return `IBGE ${result.ibgeCodigo} \u2014 ${name} \u2192 TSE ${result.codigosTse.join(", ")}`;
633
1069
  }
@@ -771,6 +1207,228 @@ function runDddLookup(value, options, io = { stdout: [], stderr: [] }) {
771
1207
  return runDddLookupCommand(value.trim(), options, io);
772
1208
  }
773
1209
 
1210
+ // src/commands/nfe-cuf/lookup.ts
1211
+ import {
1212
+ lookupCufPorCodigo,
1213
+ NFE_CUF_DATA_VERSION
1214
+ } from "@br-validators/core/nfe-cuf";
1215
+ function formatNfeCufHuman(row) {
1216
+ return `cUF ${row.codigo} \u2014 ${row.uf} \u2014 ${row.nome} (IBGE UF ${row.codigoIbge})`;
1217
+ }
1218
+ function runNfeCufLookupCommand(input, options, io = { stdout: [], stderr: [] }) {
1219
+ const trimmed = input.trim();
1220
+ if (trimmed.length === 0) {
1221
+ io.stderr.push("Invalid cUF code. Use 2 digits (e.g. 35).");
1222
+ return EXIT.USAGE;
1223
+ }
1224
+ const result = lookupCufPorCodigo(trimmed);
1225
+ if (!result.ok) {
1226
+ if (options.json) {
1227
+ io.stdout.push(JSON.stringify({ ok: false, code: result.code, message: result.message }, null, 2));
1228
+ return EXIT.INVALID;
1229
+ }
1230
+ io.stderr.push(result.message);
1231
+ return EXIT.INVALID;
1232
+ }
1233
+ if (options.json) {
1234
+ const payload = {
1235
+ ok: true,
1236
+ cuf: result.value
1237
+ };
1238
+ if (options.verbose) {
1239
+ payload.capturadoEm = NFE_CUF_DATA_VERSION.capturadoEm;
1240
+ }
1241
+ io.stdout.push(JSON.stringify(payload, null, 2));
1242
+ return EXIT.OK;
1243
+ }
1244
+ io.stdout.push(formatNfeCufHuman(result.value));
1245
+ if (options.verbose) {
1246
+ io.stdout.push(`capturadoEm: ${NFE_CUF_DATA_VERSION.capturadoEm}`);
1247
+ }
1248
+ return EXIT.OK;
1249
+ }
1250
+ function runNfeCufLookup(value, options, io = { stdout: [], stderr: [] }) {
1251
+ if (!value?.trim()) {
1252
+ io.stderr.push("Missing cUF code. Usage: br-validators nfe-cuf lookup <code>");
1253
+ return EXIT.USAGE;
1254
+ }
1255
+ return runNfeCufLookupCommand(value.trim(), options, io);
1256
+ }
1257
+
1258
+ // src/commands/selic/index.ts
1259
+ import {
1260
+ getSelicMeta,
1261
+ getSelicMetaPorData,
1262
+ SELIC_DATA_VERSION
1263
+ } from "@br-validators/core/selic";
1264
+ function formatSelicMetaHuman(meta) {
1265
+ return `SELIC meta ${meta.dataReferencia} \u2014 ${String(meta.valor)}% a.a.`;
1266
+ }
1267
+ function runSelicCommand(options, io = { stdout: [], stderr: [] }) {
1268
+ const meta = options.date === void 0 || options.date.trim().length === 0 ? getSelicMeta() : getSelicMetaPorData(options.date.trim());
1269
+ if (meta === void 0) {
1270
+ io.stderr.push(
1271
+ options.date ? `SELIC meta not found for date ${options.date}` : "SELIC meta series is empty"
1272
+ );
1273
+ return EXIT.INVALID;
1274
+ }
1275
+ if (options.json) {
1276
+ const payload = {
1277
+ ok: true,
1278
+ meta
1279
+ };
1280
+ if (options.verbose) {
1281
+ payload.capturadoEm = SELIC_DATA_VERSION.capturadoEm;
1282
+ }
1283
+ io.stdout.push(JSON.stringify(payload, null, 2));
1284
+ return EXIT.OK;
1285
+ }
1286
+ io.stdout.push(formatSelicMetaHuman(meta));
1287
+ if (options.verbose) {
1288
+ io.stdout.push(`dataReferencia: ${meta.dataReferencia}`);
1289
+ io.stdout.push(`isStale: ${String(meta.isStale)}`);
1290
+ if (meta.warning !== void 0) {
1291
+ io.stdout.push(`warning: ${meta.warning}`);
1292
+ }
1293
+ io.stdout.push(`capturadoEm: ${SELIC_DATA_VERSION.capturadoEm}`);
1294
+ }
1295
+ return EXIT.OK;
1296
+ }
1297
+
1298
+ // src/commands/iss-municipal/index.ts
1299
+ import {
1300
+ getIssMunicipalPorIbge,
1301
+ getIssMunicipalPorUfMunicipio,
1302
+ ISS_MUNICIPAL_DATA_VERSION,
1303
+ ISS_MUNICIPAL_ESTIMATION_WARNING,
1304
+ searchIssMunicipal
1305
+ } from "@br-validators/core/iss-municipal";
1306
+ function formatIssMunicipalHuman(row) {
1307
+ return `${row.nome}/${row.uf} \u2014 ISS ${String(row.aliquotaMin)}%\u2013${String(row.aliquotaMax)}%`;
1308
+ }
1309
+ function emitDisclaimer(options, io) {
1310
+ if (options.json) {
1311
+ return;
1312
+ }
1313
+ io.stderr.push(ISS_MUNICIPAL_ESTIMATION_WARNING);
1314
+ }
1315
+ function runIssMunicipalLookup(codigo, options, io = { stdout: [], stderr: [] }) {
1316
+ const trimmed = codigo?.trim() ?? "";
1317
+ if (trimmed.length === 0) {
1318
+ io.stderr.push("Missing IBGE code. Usage: br-validators iss-municipal lookup <codigoIbge>");
1319
+ return EXIT.USAGE;
1320
+ }
1321
+ const result = getIssMunicipalPorIbge(trimmed);
1322
+ if (result === void 0) {
1323
+ io.stderr.push(`ISS municipal row not found for IBGE code ${trimmed}`);
1324
+ return EXIT.INVALID;
1325
+ }
1326
+ emitDisclaimer(options, io);
1327
+ if (options.json) {
1328
+ const payload = {
1329
+ ok: true,
1330
+ iss: result
1331
+ };
1332
+ if (options.verbose) {
1333
+ payload.capturadoEm = ISS_MUNICIPAL_DATA_VERSION.capturadoEm;
1334
+ }
1335
+ io.stdout.push(JSON.stringify(payload, null, 2));
1336
+ return EXIT.OK;
1337
+ }
1338
+ io.stdout.push(formatIssMunicipalHuman(result));
1339
+ io.stdout.push(`warning: ${result.warning}`);
1340
+ if (options.verbose) {
1341
+ io.stdout.push(`leiUrl: ${result.leiUrl}`);
1342
+ io.stdout.push(`estimativa: ${String(result.estimativa)}`);
1343
+ io.stdout.push(`capturadoEm: ${ISS_MUNICIPAL_DATA_VERSION.capturadoEm}`);
1344
+ }
1345
+ return EXIT.OK;
1346
+ }
1347
+ function runIssMunicipalSearch(query, options, io = { stdout: [], stderr: [] }) {
1348
+ const trimmed = query?.trim() ?? "";
1349
+ if (trimmed.length === 0) {
1350
+ io.stderr.push("Missing query. Usage: br-validators iss-municipal search <query>");
1351
+ return EXIT.USAGE;
1352
+ }
1353
+ const rows = searchIssMunicipal(trimmed, { limit: options.limit });
1354
+ emitDisclaimer(options, io);
1355
+ if (options.json) {
1356
+ io.stdout.push(JSON.stringify({ ok: true, results: rows }, null, 2));
1357
+ return EXIT.OK;
1358
+ }
1359
+ if (rows.length === 0) {
1360
+ io.stdout.push("No ISS municipal rows matched.");
1361
+ return EXIT.OK;
1362
+ }
1363
+ for (const row of rows) {
1364
+ io.stdout.push(formatIssMunicipalHuman(row));
1365
+ }
1366
+ return EXIT.OK;
1367
+ }
1368
+ function runIssMunicipalResolve(uf, nome, options, io = { stdout: [], stderr: [] }) {
1369
+ const normalizedUf = uf?.trim() ?? "";
1370
+ const normalizedNome = nome?.trim() ?? "";
1371
+ if (normalizedUf.length === 0 || normalizedNome.length === 0) {
1372
+ io.stderr.push("Missing UF or municipality name. Usage: br-validators iss-municipal resolve <uf> <nome>");
1373
+ return EXIT.USAGE;
1374
+ }
1375
+ const result = getIssMunicipalPorUfMunicipio(normalizedUf, normalizedNome);
1376
+ if (result === void 0) {
1377
+ io.stderr.push(`ISS municipal row not found for ${normalizedNome}/${normalizedUf}`);
1378
+ return EXIT.INVALID;
1379
+ }
1380
+ return runIssMunicipalLookup(String(result.codigoIbge), options, io);
1381
+ }
1382
+
1383
+ // src/commands/ptax/lookup.ts
1384
+ import {
1385
+ getPtaxCotacao,
1386
+ PTAX_DATA_VERSION
1387
+ } from "@br-validators/core/ptax";
1388
+ function formatPtaxCotacaoHuman(cotacao) {
1389
+ return `${cotacao.moeda} Fechamento PTAX \u2014 compra ${String(cotacao.cotacaoCompra)} / venda ${String(cotacao.cotacaoVenda)} (${cotacao.dataReferencia})`;
1390
+ }
1391
+ function runPtaxLookupCommand(moeda, data, options, io = { stdout: [], stderr: [] }) {
1392
+ const trimmedMoeda = moeda.trim();
1393
+ if (trimmedMoeda.length === 0) {
1394
+ io.stderr.push("Missing currency code. Pass a 3-letter ISO code (e.g. USD).");
1395
+ return EXIT.USAGE;
1396
+ }
1397
+ const cotacao = data === void 0 || data.trim().length === 0 ? getPtaxCotacao(trimmedMoeda) : getPtaxCotacao(trimmedMoeda, data.trim());
1398
+ if (cotacao === void 0) {
1399
+ io.stderr.push(`PTAX quote not found: ${trimmedMoeda}${data ? ` ${data}` : ""}`);
1400
+ return EXIT.INVALID;
1401
+ }
1402
+ if (options.json) {
1403
+ const payload = {
1404
+ ok: true,
1405
+ cotacao
1406
+ };
1407
+ if (options.verbose) {
1408
+ payload.capturadoEm = PTAX_DATA_VERSION.capturadoEm;
1409
+ }
1410
+ io.stdout.push(JSON.stringify(payload, null, 2));
1411
+ return EXIT.OK;
1412
+ }
1413
+ io.stdout.push(formatPtaxCotacaoHuman(cotacao));
1414
+ if (options.verbose) {
1415
+ io.stdout.push(`dataReferencia: ${cotacao.dataReferencia}`);
1416
+ io.stdout.push(`isStale: ${String(cotacao.isStale)}`);
1417
+ if (cotacao.warning !== void 0) {
1418
+ io.stdout.push(`warning: ${cotacao.warning}`);
1419
+ }
1420
+ io.stdout.push(`capturadoEm: ${PTAX_DATA_VERSION.capturadoEm}`);
1421
+ }
1422
+ return EXIT.OK;
1423
+ }
1424
+ function runPtaxLookup(moeda, data, options, io = { stdout: [], stderr: [] }) {
1425
+ if (!moeda?.trim()) {
1426
+ io.stderr.push("Missing currency code. Usage: ptax lookup <moeda> [data]");
1427
+ return EXIT.USAGE;
1428
+ }
1429
+ return runPtaxLookupCommand(moeda.trim(), data?.trim(), options, io);
1430
+ }
1431
+
774
1432
  // src/commands/brcode.ts
775
1433
  import {
776
1434
  BRCODE_OFFICIAL_SOURCE_URL,
@@ -2288,6 +2946,184 @@ function runSanitize(type, value, options, io = { stdout: [], stderr: [] }) {
2288
2946
  return printSanitize(result, options, io);
2289
2947
  }
2290
2948
 
2949
+ // src/commands/mask.ts
2950
+ import { isMaskableDocumentType, maskRuntime } from "@br-validators/core";
2951
+ function resolveInput22(value, fileContent) {
2952
+ const input = value ?? fileContent?.trim();
2953
+ if (!input) {
2954
+ return null;
2955
+ }
2956
+ return input;
2957
+ }
2958
+ function runMask(type, value, options, io = { stdout: [], stderr: [] }) {
2959
+ if (!isMaskableDocumentType(type)) {
2960
+ io.stderr.push(`Unsupported mask type: ${type}`);
2961
+ return EXIT.USAGE;
2962
+ }
2963
+ const input = resolveInput22(value, options.file);
2964
+ if (input === null) {
2965
+ io.stderr.push("Missing value. Pass an argument or use --file.");
2966
+ return EXIT.USAGE;
2967
+ }
2968
+ const uf = options.uf?.toUpperCase();
2969
+ const result = maskRuntime(type, input, uf ? { uf } : {});
2970
+ return printFormat(result, options, io);
2971
+ }
2972
+
2973
+ // src/commands/compare.ts
2974
+ import { compareRuntime } from "@br-validators/core";
2975
+
2976
+ // src/commands/platform-document-types.ts
2977
+ var PLATFORM_DOCUMENT_TYPES = [
2978
+ "cpf",
2979
+ "cnpj",
2980
+ "cep",
2981
+ "placa",
2982
+ "pis-pasep",
2983
+ "telefone",
2984
+ "cnh",
2985
+ "renavam",
2986
+ "titulo-eleitor",
2987
+ "processo-judicial",
2988
+ "rg",
2989
+ "nfe-chave",
2990
+ "boleto",
2991
+ "cartao-credito",
2992
+ "ean",
2993
+ "inscricao-estadual",
2994
+ "inscricao-estadual-produtor-rural",
2995
+ "pix",
2996
+ "brcode"
2997
+ ];
2998
+ function isPlatformDocumentType(type) {
2999
+ return PLATFORM_DOCUMENT_TYPES.includes(type);
3000
+ }
3001
+
3002
+ // src/commands/compare.ts
3003
+ function printCompare(result, options, io = { stdout: [], stderr: [] }) {
3004
+ if (options.json) {
3005
+ io.stdout.push(JSON.stringify(result, null, 2));
3006
+ if ("code" in result) {
3007
+ return EXIT.USAGE;
3008
+ }
3009
+ return result.equal ? EXIT.OK : EXIT.INVALID;
3010
+ }
3011
+ if (options.quiet) {
3012
+ if ("code" in result) {
3013
+ return EXIT.USAGE;
3014
+ }
3015
+ return result.equal ? EXIT.OK : EXIT.INVALID;
3016
+ }
3017
+ if ("code" in result) {
3018
+ io.stderr.push(`code: ${result.code}`);
3019
+ io.stderr.push(`message: ${result.message}`);
3020
+ return EXIT.USAGE;
3021
+ }
3022
+ io.stdout.push(`equal: ${result.equal ? "yes" : "no"}`);
3023
+ return result.equal ? EXIT.OK : EXIT.INVALID;
3024
+ }
3025
+ function runCompare(type, valueA, valueB, options, io = { stdout: [], stderr: [] }) {
3026
+ if (!isPlatformDocumentType(type)) {
3027
+ io.stderr.push(`Unsupported compare type: ${type}`);
3028
+ return EXIT.USAGE;
3029
+ }
3030
+ if (!valueA || !valueB) {
3031
+ io.stderr.push("Missing values. Usage: compare <type> <valueA> <valueB>");
3032
+ return EXIT.USAGE;
3033
+ }
3034
+ const uf = options.uf?.toUpperCase();
3035
+ const platformOptions = uf ? { uf } : {};
3036
+ const result = compareRuntime(valueA, valueB, type, platformOptions);
3037
+ return printCompare(result, options, io);
3038
+ }
3039
+
3040
+ // src/commands/batch.ts
3041
+ import { batch } from "@br-validators/core";
3042
+ function parseBatchLines(raw) {
3043
+ return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
3044
+ }
3045
+ function resolveBatchInputs(options) {
3046
+ if (options.lines === void 0) {
3047
+ return null;
3048
+ }
3049
+ const parsed = parseBatchLines(options.lines);
3050
+ if (options.limit !== void 0 && options.limit > 0) {
3051
+ return parsed.slice(0, options.limit);
3052
+ }
3053
+ return parsed;
3054
+ }
3055
+ function printBatch(result, options, io = { stdout: [], stderr: [] }) {
3056
+ if (options.json) {
3057
+ io.stdout.push(JSON.stringify(result, null, 2));
3058
+ return result.summary.invalid === 0 ? EXIT.OK : EXIT.INVALID;
3059
+ }
3060
+ if (options.quiet) {
3061
+ return result.summary.invalid === 0 ? EXIT.OK : EXIT.INVALID;
3062
+ }
3063
+ io.stdout.push(`total: ${String(result.summary.total)}`);
3064
+ io.stdout.push(`valid: ${String(result.summary.valid)}`);
3065
+ io.stdout.push(`invalid: ${String(result.summary.invalid)}`);
3066
+ for (const entry of result.valid) {
3067
+ io.stdout.push(`ok[${String(entry.index)}]: ${entry.value}`);
3068
+ }
3069
+ for (const entry of result.invalid) {
3070
+ io.stderr.push(`fail[${String(entry.index)}]: ${entry.code} \u2014 ${entry.message}`);
3071
+ }
3072
+ return result.summary.invalid === 0 ? EXIT.OK : EXIT.INVALID;
3073
+ }
3074
+ function runBatch(type, options, io = { stdout: [], stderr: [] }) {
3075
+ if (!isPlatformDocumentType(type)) {
3076
+ io.stderr.push(`Unsupported batch type: ${type}`);
3077
+ return EXIT.USAGE;
3078
+ }
3079
+ const inputs = resolveBatchInputs(options);
3080
+ if (inputs === null) {
3081
+ io.stderr.push("Missing input. Pass --file <path> or pipe one value per line on stdin.");
3082
+ return EXIT.USAGE;
3083
+ }
3084
+ if (inputs.length === 0) {
3085
+ io.stderr.push("No values to validate.");
3086
+ return EXIT.USAGE;
3087
+ }
3088
+ const uf = options.uf?.toUpperCase();
3089
+ const platformOptions = uf ? { uf } : {};
3090
+ const result = batch(inputs, type, platformOptions);
3091
+ return printBatch(result, options, io);
3092
+ }
3093
+
3094
+ // src/commands/diff.ts
3095
+ import { diff } from "@br-validators/core";
3096
+ function printDiff(result, options, io = { stdout: [], stderr: [] }) {
3097
+ if (options.json) {
3098
+ io.stdout.push(JSON.stringify(result, null, 2));
3099
+ return result.changed ? EXIT.INVALID : EXIT.OK;
3100
+ }
3101
+ if (options.quiet) {
3102
+ return result.changed ? EXIT.INVALID : EXIT.OK;
3103
+ }
3104
+ io.stdout.push(`changed: ${result.changed ? "yes" : "no"}`);
3105
+ for (const field of result.fields) {
3106
+ io.stdout.push(`field: ${field.field}`);
3107
+ io.stdout.push(` a: ${field.a}`);
3108
+ io.stdout.push(` b: ${field.b}`);
3109
+ }
3110
+ return result.changed ? EXIT.INVALID : EXIT.OK;
3111
+ }
3112
+ function runDiff(type, valueA, valueB, options, io = { stdout: [], stderr: [] }) {
3113
+ if (!isPlatformDocumentType(type)) {
3114
+ io.stderr.push(`Unsupported diff type: ${type}`);
3115
+ return EXIT.USAGE;
3116
+ }
3117
+ if (!valueA || !valueB) {
3118
+ io.stderr.push("Missing values. Usage: diff <type> <valueA> <valueB>");
3119
+ return EXIT.USAGE;
3120
+ }
3121
+ const uf = options.uf?.toUpperCase();
3122
+ const platformOptions = uf ? { uf } : {};
3123
+ const result = diff(valueA, valueB, type, platformOptions);
3124
+ return printDiff(result, options, io);
3125
+ }
3126
+
2291
3127
  // src/commands/generate.ts
2292
3128
  import { generate, isGeneratableCardBrand } from "@br-validators/core";
2293
3129
  var GENERATABLE_TYPES = [
@@ -2317,6 +3153,9 @@ function buildGenerateOptions(options) {
2317
3153
  if (options.masked) {
2318
3154
  core.masked = true;
2319
3155
  }
3156
+ if (options.stripped) {
3157
+ core.stripped = true;
3158
+ }
2320
3159
  if (options.seed !== void 0) {
2321
3160
  core.seed = options.seed;
2322
3161
  }
@@ -2360,10 +3199,14 @@ function listSupportedTypes(io = { stdout: [] }) {
2360
3199
  }
2361
3200
 
2362
3201
  // src/handlers.ts
3202
+ var nodeRequire = createRequire(import.meta.url);
3203
+ function readNodeFileSync(path, encoding) {
3204
+ const fs = nodeRequire("node:fs");
3205
+ return fs.readFileSync(path, encoding);
3206
+ }
2363
3207
  function readInputFile(path, io) {
2364
3208
  try {
2365
- const fsModule = __require("fs");
2366
- return fsModule.readFileSync(path, "utf8");
3209
+ return readNodeFileSync(path, "utf8");
2367
3210
  } catch {
2368
3211
  io.stderr.push(`Cannot read file: ${path}`);
2369
3212
  return null;
@@ -2840,6 +3683,90 @@ function handleSanitizeCli(type, value, opts, io = { stdout: [], stderr: [] }) {
2840
3683
  io
2841
3684
  );
2842
3685
  }
3686
+ function handleMaskCli(type, value, opts, io = { stdout: [], stderr: [] }) {
3687
+ let fileContent;
3688
+ if (opts.file) {
3689
+ const content = readInputFile(opts.file, io);
3690
+ if (content === null) {
3691
+ return EXIT.USAGE;
3692
+ }
3693
+ fileContent = content;
3694
+ }
3695
+ return runMask(
3696
+ type,
3697
+ value,
3698
+ {
3699
+ json: Boolean(opts.json),
3700
+ quiet: Boolean(opts.quiet),
3701
+ uf: opts.uf,
3702
+ file: fileContent
3703
+ },
3704
+ io
3705
+ );
3706
+ }
3707
+ function readStdinSync(io) {
3708
+ try {
3709
+ if (process.stdin.isTTY) {
3710
+ return null;
3711
+ }
3712
+ return readNodeFileSync(0, "utf8");
3713
+ } catch {
3714
+ io.stderr.push("Cannot read stdin.");
3715
+ return null;
3716
+ }
3717
+ }
3718
+ function handleCompareCli(type, valueA, valueB, opts, io = { stdout: [], stderr: [] }) {
3719
+ return runCompare(
3720
+ type,
3721
+ valueA,
3722
+ valueB,
3723
+ {
3724
+ json: Boolean(opts.json),
3725
+ quiet: Boolean(opts.quiet),
3726
+ uf: opts.uf
3727
+ },
3728
+ io
3729
+ );
3730
+ }
3731
+ function handleDiffCli(type, valueA, valueB, opts, io = { stdout: [], stderr: [] }) {
3732
+ return runDiff(
3733
+ type,
3734
+ valueA,
3735
+ valueB,
3736
+ {
3737
+ json: Boolean(opts.json),
3738
+ quiet: Boolean(opts.quiet),
3739
+ uf: opts.uf
3740
+ },
3741
+ io
3742
+ );
3743
+ }
3744
+ function handleBatchCli(type, opts, io = { stdout: [], stderr: [] }) {
3745
+ let lines;
3746
+ if (opts.file) {
3747
+ const content = readInputFile(opts.file, io);
3748
+ if (content === null) {
3749
+ return EXIT.USAGE;
3750
+ }
3751
+ lines = content;
3752
+ } else {
3753
+ const stdin = readStdinSync(io);
3754
+ if (stdin !== null) {
3755
+ lines = stdin;
3756
+ }
3757
+ }
3758
+ return runBatch(
3759
+ type,
3760
+ {
3761
+ json: Boolean(opts.json),
3762
+ quiet: Boolean(opts.quiet),
3763
+ uf: opts.uf,
3764
+ lines,
3765
+ limit: opts.limit
3766
+ },
3767
+ io
3768
+ );
3769
+ }
2843
3770
  function handleGenerateCli(type, opts, io = { stdout: [], stderr: [] }) {
2844
3771
  return runGenerate(
2845
3772
  type,
@@ -2847,6 +3774,7 @@ function handleGenerateCli(type, opts, io = { stdout: [], stderr: [] }) {
2847
3774
  json: Boolean(opts.json),
2848
3775
  quiet: Boolean(opts.quiet),
2849
3776
  masked: Boolean(opts.masked),
3777
+ stripped: Boolean(opts.stripped),
2850
3778
  format: opts.format,
2851
3779
  seed: opts.seed,
2852
3780
  uf: opts.uf,
@@ -2898,6 +3826,39 @@ function handleReferenceSearchCli(command, query, opts, io = { stdout: [], stder
2898
3826
  io
2899
3827
  );
2900
3828
  }
3829
+ function handleReferenceValidateCli(command, value, opts, io = { stdout: [], stderr: [] }) {
3830
+ return runReferenceValidate(
3831
+ command,
3832
+ value,
3833
+ {
3834
+ json: Boolean(opts.json),
3835
+ verbose: Boolean(opts.verbose)
3836
+ },
3837
+ io
3838
+ );
3839
+ }
3840
+ function handleCstLookupCli(value, opts, io = { stdout: [], stderr: [] }) {
3841
+ return runCstLookup(value, {
3842
+ json: Boolean(opts.json),
3843
+ verbose: Boolean(opts.verbose),
3844
+ tax: opts.tax
3845
+ }, io);
3846
+ }
3847
+ function handleCstSearchCli(query, opts, io = { stdout: [], stderr: [] }) {
3848
+ return runCstSearch(query, {
3849
+ json: Boolean(opts.json),
3850
+ verbose: Boolean(opts.verbose),
3851
+ tax: opts.tax,
3852
+ limit: opts.limit
3853
+ }, io);
3854
+ }
3855
+ function handleCstValidateCli(value, opts, io = { stdout: [], stderr: [] }) {
3856
+ return runCstValidate(value, {
3857
+ json: Boolean(opts.json),
3858
+ verbose: Boolean(opts.verbose),
3859
+ tax: opts.tax
3860
+ }, io);
3861
+ }
2901
3862
  function handleIbgeLookupCli(value, opts, io = { stdout: [], stderr: [] }) {
2902
3863
  return runIbgeLookup(value, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
2903
3864
  }
@@ -2916,6 +3877,34 @@ function handleFeriadosListCli(opts, io = { stdout: [], stderr: [] }) {
2916
3877
  year: opts.year
2917
3878
  }, io);
2918
3879
  }
3880
+ function handleInssTabelaCli(opts, io = { stdout: [], stderr: [] }) {
3881
+ return runInssTabela({
3882
+ json: Boolean(opts.json),
3883
+ verbose: Boolean(opts.verbose),
3884
+ ano: opts.year
3885
+ }, io);
3886
+ }
3887
+ function handleInssCalcCli(value, opts, io = { stdout: [], stderr: [] }) {
3888
+ return runInssCalc(value, {
3889
+ json: Boolean(opts.json),
3890
+ verbose: Boolean(opts.verbose),
3891
+ ano: opts.year
3892
+ }, io);
3893
+ }
3894
+ function handleIrpfTabelaCli(opts, io = { stdout: [], stderr: [] }) {
3895
+ return runIrpfTabela({
3896
+ json: Boolean(opts.json),
3897
+ verbose: Boolean(opts.verbose),
3898
+ ano: opts.year
3899
+ }, io);
3900
+ }
3901
+ function handleIrpfCalcCli(value, opts, io = { stdout: [], stderr: [] }) {
3902
+ return runIrpfCalc(value, {
3903
+ json: Boolean(opts.json),
3904
+ verbose: Boolean(opts.verbose),
3905
+ ano: opts.year
3906
+ }, io);
3907
+ }
2919
3908
  function handleTseMunicipiosLookupCli(value, opts, io = { stdout: [], stderr: [] }) {
2920
3909
  return runTseMunicipiosLookup(value, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
2921
3910
  }
@@ -2925,6 +3914,32 @@ function handleCepFaixaCli(value, opts, io = { stdout: [], stderr: [] }) {
2925
3914
  function handleDddLookupCli(value, opts, io = { stdout: [], stderr: [] }) {
2926
3915
  return runDddLookup(value, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
2927
3916
  }
3917
+ function handleNfeCufLookupCli(value, opts, io = { stdout: [], stderr: [] }) {
3918
+ return runNfeCufLookup(value, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
3919
+ }
3920
+ function handleSelicCli(opts, io = { stdout: [], stderr: [] }) {
3921
+ return runSelicCommand({
3922
+ json: Boolean(opts.json),
3923
+ verbose: Boolean(opts.verbose),
3924
+ date: opts.date
3925
+ }, io);
3926
+ }
3927
+ function handleIssMunicipalLookupCli(codigo, opts, io = { stdout: [], stderr: [] }) {
3928
+ return runIssMunicipalLookup(codigo, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
3929
+ }
3930
+ function handleIssMunicipalSearchCli(query, opts, io = { stdout: [], stderr: [] }) {
3931
+ return runIssMunicipalSearch(query, {
3932
+ json: Boolean(opts.json),
3933
+ verbose: Boolean(opts.verbose),
3934
+ limit: opts.limit
3935
+ }, io);
3936
+ }
3937
+ function handleIssMunicipalResolveCli(uf, nome, opts, io = { stdout: [], stderr: [] }) {
3938
+ return runIssMunicipalResolve(uf, nome, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
3939
+ }
3940
+ function handlePtaxLookupCli(moeda, data, opts, io = { stdout: [], stderr: [] }) {
3941
+ return runPtaxLookup(moeda, data, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
3942
+ }
2928
3943
  function writeCliIo(io) {
2929
3944
  for (const line of io.stdout) console.log(line);
2930
3945
  for (const line of io.stderr) console.error(line);
@@ -2940,6 +3955,13 @@ function registerReferenceLookupCommands(program) {
2940
3955
  process.exitCode = handleReferenceLookupCli(command, codigo, opts, io);
2941
3956
  writeCliIo(io);
2942
3957
  });
3958
+ if (REFERENCE_VALIDATE_COMMANDS.includes(command)) {
3959
+ root.command("validate").description(`Validate ${command} format and embedded table`).argument("<codigo>", "Code to validate").option("--json", "JSON output").option("--verbose", "Include module metadata").action((codigo, opts) => {
3960
+ const io = { stdout: [], stderr: [] };
3961
+ process.exitCode = handleReferenceValidateCli(command, codigo, opts, io);
3962
+ writeCliIo(io);
3963
+ });
3964
+ }
2943
3965
  if (REFERENCE_SEARCH_COMMANDS.includes(command)) {
2944
3966
  root.command("search").description(`Search ${command} by description fragment`).argument("<query>", "Search query").option("--json", "JSON output").option("--verbose", "Include dataset capture date").option("--limit <n>", "Maximum rows", (v) => Number(v)).action((query, opts) => {
2945
3967
  const io = { stdout: [], stderr: [] };
@@ -3194,6 +4216,28 @@ function createProgram() {
3194
4216
  process.exitCode = handleFeriadosListCli(opts, io);
3195
4217
  writeCliIo(io);
3196
4218
  });
4219
+ const inss = program.command("inss").description("INSS employee contribution progressive table \u2014 offline");
4220
+ inss.command("tabela").description("Show embedded progressive contribution brackets").option("--ano <yyyy>", "Compet\xEAncia year", (v) => Number(v)).option("--json", "JSON output").option("--verbose", "Include dataset capture date").action((opts) => {
4221
+ const io = { stdout: [], stderr: [] };
4222
+ process.exitCode = handleInssTabelaCli({ ...opts, year: opts.ano ?? opts.year }, io);
4223
+ writeCliIo(io);
4224
+ });
4225
+ inss.command("calc").description("Estimate monthly INSS contribution from salary").argument("<salario>", "Monthly contribution salary (BRL)").option("--ano <yyyy>", "Compet\xEAncia year", (v) => Number(v)).option("--json", "JSON output").option("--verbose", "Include dataset capture date").action((salario, opts) => {
4226
+ const io = { stdout: [], stderr: [] };
4227
+ process.exitCode = handleInssCalcCli(salario, { ...opts, year: opts.ano ?? opts.year }, io);
4228
+ writeCliIo(io);
4229
+ });
4230
+ const irpf = program.command("irpf").description("RFB IRPF progressive monthly table \u2014 offline");
4231
+ irpf.command("tabela").description("Show embedded monthly progressive brackets").option("--ano <yyyy>", "Tax year", (v) => Number(v)).option("--json", "JSON output").option("--verbose", "Include dataset capture date").action((opts) => {
4232
+ const io = { stdout: [], stderr: [] };
4233
+ process.exitCode = handleIrpfTabelaCli({ ...opts, year: opts.ano ?? opts.year }, io);
4234
+ writeCliIo(io);
4235
+ });
4236
+ irpf.command("calc").description("Estimate monthly IRPF from taxable base").argument("<base>", "Monthly taxable base (BRL)").option("--ano <yyyy>", "Tax year", (v) => Number(v)).option("--json", "JSON output").option("--verbose", "Include dataset capture date").action((base, opts) => {
4237
+ const io = { stdout: [], stderr: [] };
4238
+ process.exitCode = handleIrpfCalcCli(base, { ...opts, year: opts.ano ?? opts.year }, io);
4239
+ writeCliIo(io);
4240
+ });
3197
4241
  const tseMunicipios = program.command("tse-municipios").description("TSE \u2194 IBGE municipality cross-walk \u2014 offline lookup");
3198
4242
  tseMunicipios.command("lookup").description("Resolve TSE (5 digits) or IBGE (7 digits) municipality code").argument("<codigo>", "TSE or IBGE code").option("--json", "JSON output").option("--verbose", "Include dataset capture date").action((codigo, opts) => {
3199
4243
  const io = { stdout: [], stderr: [] };
@@ -3206,6 +4250,56 @@ function createProgram() {
3206
4250
  process.exitCode = handleDddLookupCli(code, opts, io);
3207
4251
  writeCliIo(io);
3208
4252
  });
4253
+ const nfeCuf = program.command("nfe-cuf").description("NF-e cUF \u2014 SEFAZ federative unit codes (offline)");
4254
+ nfeCuf.command("lookup").description("Resolve NF-e cUF code to UF sigla and IBGE cross-ref").argument("<code>", "2-digit cUF code (e.g. 35 for SP)").option("--json", "JSON output").option("--verbose", "Include dataset capture date").action((code, opts) => {
4255
+ const io = { stdout: [], stderr: [] };
4256
+ process.exitCode = handleNfeCufLookupCli(code, opts, io);
4257
+ writeCliIo(io);
4258
+ });
4259
+ const selic = program.command("selic").description("Bacen SELIC meta (SGS 432) \u2014 offline embedded series");
4260
+ selic.description("Resolve Copom SELIC meta rate (optional historical date)").option("--date <yyyy-mm-dd>", "Observation date (ISO or MM-DD-YYYY)").option("--json", "JSON output").option("--verbose", "Include dataReferencia, staleness, and dataset capture date").action((opts) => {
4261
+ const io = { stdout: [], stderr: [] };
4262
+ process.exitCode = handleSelicCli(opts, io);
4263
+ writeCliIo(io);
4264
+ });
4265
+ const issMunicipal = program.command("iss-municipal").description("Municipal ISS al\xEDquotas \u2014 partial embed (estimation only, not NFSe)");
4266
+ issMunicipal.command("lookup").description("Resolve ISS band by IBGE municipality code").argument("<codigoIbge>", "IBGE municipality code (7 digits)").option("--json", "JSON output").option("--verbose", "Include leiUrl, estimativa flag, and dataset capture date").action((codigoIbge, opts) => {
4267
+ const io = { stdout: [], stderr: [] };
4268
+ process.exitCode = handleIssMunicipalLookupCli(codigoIbge, opts, io);
4269
+ writeCliIo(io);
4270
+ });
4271
+ issMunicipal.command("resolve").description("Resolve ISS band by UF and municipality name").argument("<uf>", "UF sigla (2 letters)").argument("<nome>", "Municipality name").option("--json", "JSON output").option("--verbose", "Include leiUrl, estimativa flag, and dataset capture date").action((uf, nome, opts) => {
4272
+ const io = { stdout: [], stderr: [] };
4273
+ process.exitCode = handleIssMunicipalResolveCli(uf, nome, opts, io);
4274
+ writeCliIo(io);
4275
+ });
4276
+ issMunicipal.command("search").description("Search embedded municipalities by name, UF, or IBGE code fragment").argument("<query>", "Search query").option("--json", "JSON output").option("--verbose", "Include dataset capture date in JSON responses").option("--limit <n>", "Maximum rows", (value) => Number(value)).action((query, opts) => {
4277
+ const io = { stdout: [], stderr: [] };
4278
+ process.exitCode = handleIssMunicipalSearchCli(query, opts, io);
4279
+ writeCliIo(io);
4280
+ });
4281
+ const ptax = program.command("ptax").description("Bacen PTAX Fechamento \u2014 offline embedded rates");
4282
+ ptax.command("lookup").description("Resolve Fechamento PTAX for currency (optional ISO or Bacen date)").argument("<moeda>", "ISO 4217 currency code (e.g. USD)").argument("[data]", "Quote date \u2014 YYYY-MM-DD or MM-DD-YYYY").option("--json", "JSON output").option("--verbose", "Include dataReferencia, staleness, and dataset capture date").action((moeda, data, opts) => {
4283
+ const io = { stdout: [], stderr: [] };
4284
+ process.exitCode = handlePtaxLookupCli(moeda, data, opts, io);
4285
+ writeCliIo(io);
4286
+ });
4287
+ const cst = program.command("cst").description("RFB SPED CST \u2014 offline ICMS, IPI, PIS, COFINS tables");
4288
+ cst.command("lookup").description("Resolve CST by code and tax").argument("<codigo>", "CST code").requiredOption("--tax <tax>", "Tax table: icms | ipi | pis | cofins").option("--json", "JSON output").option("--verbose", "Include dataset capture date").action((codigo, opts) => {
4289
+ const io = { stdout: [], stderr: [] };
4290
+ process.exitCode = handleCstLookupCli(codigo, opts, io);
4291
+ writeCliIo(io);
4292
+ });
4293
+ cst.command("search").description("Search CST descriptions by tax").argument("<query>", "Search query").requiredOption("--tax <tax>", "Tax table: icms | ipi | pis | cofins").option("--json", "JSON output").option("--limit <n>", "Maximum rows", (v) => Number(v)).action((query, opts) => {
4294
+ const io = { stdout: [], stderr: [] };
4295
+ process.exitCode = handleCstSearchCli(query, opts, io);
4296
+ writeCliIo(io);
4297
+ });
4298
+ cst.command("validate").description("Validate CST format and embedded table").argument("<codigo>", "CST code").requiredOption("--tax <tax>", "Tax table: icms | ipi | pis | cofins").option("--json", "JSON output").option("--verbose", "Include tax metadata").action((codigo, opts) => {
4299
+ const io = { stdout: [], stderr: [] };
4300
+ process.exitCode = handleCstValidateCli(codigo, opts, io);
4301
+ writeCliIo(io);
4302
+ });
3209
4303
  registerReferenceLookupCommands(program);
3210
4304
  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) => {
3211
4305
  const io = { stdout: [], stderr: [] };
@@ -3217,11 +4311,31 @@ function createProgram() {
3217
4311
  process.exitCode = handleSanitizeCli(type, value, opts, io);
3218
4312
  writeCliIo(io);
3219
4313
  });
3220
- program.command("generate <type>").description("Generate synthetic valid test document").option("--json", "JSON output").option("-q, --quiet", "Exit code only").option("--masked", "Return masked/formatted output").option("--format <format>", "Format variant (numeric, alphanumeric, legacy, mercosul, celular, fixo)").option("--seed <number>", "Deterministic PRNG seed", (v) => Number(v)).option("--uf <uf>", "State code (required for inscricao-estadual and titulo-eleitor)").option("--brand <brand>", "Card brand (visa, mastercard, amex, elo, hipercard)").action((type, opts) => {
4314
+ program.command("mask <type> [value]").description("Apply unified display mask (validate first)").option("--uf <uf>", "State code (required for inscricao-estadual and rg)").option("--json", "JSON output").option("-q, --quiet", "Exit code only").option("-f, --file <path>", "Read value from file").action((type, value, opts) => {
4315
+ const io = { stdout: [], stderr: [] };
4316
+ process.exitCode = handleMaskCli(type, value, opts, io);
4317
+ writeCliIo(io);
4318
+ });
4319
+ program.command("generate <type>").description("Generate synthetic valid test document").option("--json", "JSON output").option("-q, --quiet", "Exit code only").option("--masked", "Return masked/formatted output").option("--stripped", "Return canonical stripped digits (default; explicit flag)").option("--format <format>", "Format variant (numeric, alphanumeric, legacy, mercosul, celular, fixo)").option("--seed <number>", "Deterministic PRNG seed", (v) => Number(v)).option("--uf <uf>", "State code (required for inscricao-estadual and titulo-eleitor)").option("--brand <brand>", "Card brand (visa, mastercard, amex, elo, hipercard)").action((type, opts) => {
3221
4320
  const io = { stdout: [], stderr: [] };
3222
4321
  process.exitCode = handleGenerateCli(type, opts, io);
3223
4322
  writeCliIo(io);
3224
4323
  });
4324
+ program.command("compare <type> <valueA> [valueB...]").description("Compare two values for normalized equality").option("--uf <uf>", "State code (required for inscricao-estadual, rg, titulo-eleitor)").option("--json", "JSON output").option("-q, --quiet", "Exit code only").action((type, valueA, valueB, opts) => {
4325
+ const io = { stdout: [], stderr: [] };
4326
+ process.exitCode = handleCompareCli(type, valueA, valueB.join(" ") || void 0, opts, io);
4327
+ writeCliIo(io);
4328
+ });
4329
+ program.command("batch <type>").description("Bulk validate values from stdin or --file").option("--uf <uf>", "State code (required for inscricao-estadual, rg, titulo-eleitor)").option("--json", "JSON output").option("-q, --quiet", "Exit code only").option("-f, --file <path>", "Read values from file (one per line)").option("--limit <n>", "Max number of values to process", (v) => Number(v)).action((type, opts) => {
4330
+ const io = { stdout: [], stderr: [] };
4331
+ process.exitCode = handleBatchCli(type, opts, io);
4332
+ writeCliIo(io);
4333
+ });
4334
+ program.command("diff <type> <valueA> [valueB...]").description("Field-level structural diff between two values").option("--uf <uf>", "State code (required for inscricao-estadual, rg, titulo-eleitor)").option("--json", "JSON output").option("-q, --quiet", "Exit code only").action((type, valueA, valueB, opts) => {
4335
+ const io = { stdout: [], stderr: [] };
4336
+ process.exitCode = handleDiffCli(type, valueA, valueB.join(" ") || void 0, opts, io);
4337
+ writeCliIo(io);
4338
+ });
3225
4339
  return program;
3226
4340
  }
3227
4341
  function run(argv) {