@br-validators/cli 1.8.3 → 1.10.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,47 +117,56 @@ 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 {
154
+ CSOSN_DATA_VERSION,
155
+ lookupCsosnPorCodigo
156
+ } from "@br-validators/core/csosn";
157
+ import { NCM_DATA_VERSION, lookupNcmPorCodigo } from "@br-validators/core/ncm";
158
+ import { CBO_DATA_VERSION, lookupCboPorCodigo } from "@br-validators/core/cbo";
159
+ import {
160
+ lookupInvalidFormat as lookupInvalidFormat2,
161
+ lookupInvalidInput
162
+ } from "@br-validators/core/lookup";
154
163
  var REFERENCE_LOOKUP_COMMANDS = [
155
164
  "natureza-juridica",
156
165
  "nbs",
157
166
  "cest",
158
167
  "cnae",
159
168
  "cfop",
169
+ "csosn",
160
170
  "ncm",
161
171
  "cbo",
162
172
  "moedas",
@@ -165,16 +175,19 @@ var REFERENCE_LOOKUP_COMMANDS = [
165
175
  "portos",
166
176
  "aeroportos"
167
177
  ];
168
- var REFERENCE_SEARCH_COMMANDS = ["cnae", "cfop", "ncm", "cbo"];
178
+ var REFERENCE_SEARCH_COMMANDS = ["cnae", "cfop", "csosn", "ncm", "cbo"];
169
179
  function lookupAeroporto(input) {
170
180
  const normalized = input.trim().toUpperCase();
181
+ if (normalized.length === 0) {
182
+ return lookupInvalidInput("Airport code is required");
183
+ }
171
184
  if (/^[A-Z0-9]{3}$/.test(normalized)) {
172
- return getAeroportoPorIata(normalized);
185
+ return lookupAeroportoPorIata(normalized);
173
186
  }
174
187
  if (/^[A-Z]{4}$/.test(normalized)) {
175
- return getAeroportoPorIcao(normalized);
188
+ return lookupAeroportoPorIcao(normalized);
176
189
  }
177
- return void 0;
190
+ return lookupInvalidFormat2("Airport code must be 3-character IATA or 4-character ICAO");
178
191
  }
179
192
  var REFERENCE_LOOKUP_MODULES = {
180
193
  "natureza-juridica": {
@@ -182,7 +195,7 @@ var REFERENCE_LOOKUP_MODULES = {
182
195
  description: "RFB legal nature codes \u2014 offline lookup",
183
196
  resultKey: "naturezaJuridica",
184
197
  capturadoEm: NATUREZA_JURIDICA_DATA_VERSION.capturadoEm,
185
- lookup: (input) => getNaturezaJuridicaPorCodigo(input),
198
+ lookup: (input) => lookupNaturezaJuridicaPorCodigo(input),
186
199
  formatHuman: (result) => {
187
200
  const row = result;
188
201
  return `${row.codigo} \u2014 ${row.descricao}`;
@@ -193,7 +206,7 @@ var REFERENCE_LOOKUP_MODULES = {
193
206
  description: "NFSe NBS service codes \u2014 offline lookup",
194
207
  resultKey: "nbs",
195
208
  capturadoEm: NBS_DATA_VERSION.capturadoEm,
196
- lookup: (input) => getNbsPorCodigo(input),
209
+ lookup: (input) => lookupNbsPorCodigo(input),
197
210
  formatHuman: (result) => {
198
211
  const row = result;
199
212
  return `${row.codigo} \u2014 ${row.descricao}`;
@@ -204,7 +217,7 @@ var REFERENCE_LOOKUP_MODULES = {
204
217
  description: "CONFAZ CEST ST codes \u2014 offline lookup",
205
218
  resultKey: "cest",
206
219
  capturadoEm: CEST_DATA_VERSION.capturadoEm,
207
- lookup: (input) => getCestPorCodigo(input),
220
+ lookup: (input) => lookupCestPorCodigo(input),
208
221
  formatHuman: (result) => {
209
222
  const row = result;
210
223
  return `${row.codigo} \u2014 ${row.descricao}`;
@@ -215,7 +228,7 @@ var REFERENCE_LOOKUP_MODULES = {
215
228
  description: "IBGE CNAE 2.3 subclasses \u2014 offline lookup",
216
229
  resultKey: "cnae",
217
230
  capturadoEm: CNAES_DATA_VERSION.capturadoEm,
218
- lookup: (input) => getCnaePorCodigo(input),
231
+ lookup: (input) => lookupCnaePorCodigo(input),
219
232
  formatHuman: (result) => {
220
233
  const row = result;
221
234
  return `${row.codigo} \u2014 ${row.descricao}`;
@@ -226,7 +239,18 @@ var REFERENCE_LOOKUP_MODULES = {
226
239
  description: "CONFAZ CFOP fiscal operations \u2014 offline lookup",
227
240
  resultKey: "cfop",
228
241
  capturadoEm: CFOP_DATA_VERSION.capturadoEm,
229
- lookup: (input) => getCfopPorCodigo(input),
242
+ lookup: (input) => lookupCfopPorCodigo(input),
243
+ formatHuman: (result) => {
244
+ const row = result;
245
+ return `${row.codigo} \u2014 ${row.descricao}`;
246
+ }
247
+ },
248
+ csosn: {
249
+ command: "csosn",
250
+ description: "CONFAZ CSOSN Simples Nacional \u2014 offline lookup",
251
+ resultKey: "csosn",
252
+ capturadoEm: CSOSN_DATA_VERSION.capturadoEm,
253
+ lookup: (input) => lookupCsosnPorCodigo(input),
230
254
  formatHuman: (result) => {
231
255
  const row = result;
232
256
  return `${row.codigo} \u2014 ${row.descricao}`;
@@ -237,7 +261,7 @@ var REFERENCE_LOOKUP_MODULES = {
237
261
  description: "Siscomex NCM Mercosur codes \u2014 offline lookup",
238
262
  resultKey: "ncm",
239
263
  capturadoEm: NCM_DATA_VERSION.capturadoEm,
240
- lookup: (input) => getNcmPorCodigo(input),
264
+ lookup: (input) => lookupNcmPorCodigo(input),
241
265
  formatHuman: (result) => {
242
266
  const row = result;
243
267
  return `${row.codigo} \u2014 ${row.descricao}`;
@@ -248,7 +272,7 @@ var REFERENCE_LOOKUP_MODULES = {
248
272
  description: "MTE CBO 2002 occupations \u2014 offline lookup",
249
273
  resultKey: "cbo",
250
274
  capturadoEm: CBO_DATA_VERSION.capturadoEm,
251
- lookup: (input) => getCboPorCodigo(input),
275
+ lookup: (input) => lookupCboPorCodigo(input),
252
276
  formatHuman: (result) => {
253
277
  const row = result;
254
278
  return `${row.codigo} \u2014 ${row.descricao}`;
@@ -259,7 +283,7 @@ var REFERENCE_LOOKUP_MODULES = {
259
283
  description: "ISO 4217 + Bacen PTAX currencies \u2014 offline lookup",
260
284
  resultKey: "moeda",
261
285
  capturadoEm: MOEDAS_DATA_VERSION.capturadoEm,
262
- lookup: (input) => getMoedaPorCodigo(input),
286
+ lookup: (input) => lookupMoedaPorCodigo(input),
263
287
  formatHuman: (result) => {
264
288
  const row = result;
265
289
  const symbol = row.simbolo ?? "\u2014";
@@ -271,7 +295,7 @@ var REFERENCE_LOOKUP_MODULES = {
271
295
  description: "NF-e Bacen country codes \u2014 offline lookup",
272
296
  resultKey: "pais",
273
297
  capturadoEm: PAISES_BACEN_DATA_VERSION.capturadoEm,
274
- lookup: (input) => getPaisPorCodigoBacen(input),
298
+ lookup: (input) => lookupPaisPorCodigoBacen(input),
275
299
  formatHuman: (result) => {
276
300
  const row = result;
277
301
  return `${row.codigo} \u2014 ${row.nome}`;
@@ -282,7 +306,7 @@ var REFERENCE_LOOKUP_MODULES = {
282
306
  description: "ICC Incoterms 2020 \u2014 offline lookup",
283
307
  resultKey: "incoterm",
284
308
  capturadoEm: INCOTERMS_DATA_VERSION.capturadoEm,
285
- lookup: (input) => getIncotermPorCodigo(input),
309
+ lookup: (input) => lookupIncotermPorCodigo(input),
286
310
  formatHuman: (result) => {
287
311
  const row = result;
288
312
  return `${row.codigo} \u2014 ${row.nome} (${row.edicao})`;
@@ -293,7 +317,7 @@ var REFERENCE_LOOKUP_MODULES = {
293
317
  description: "ANTAQ port installations \u2014 offline lookup",
294
318
  resultKey: "porto",
295
319
  capturadoEm: PORTOS_DATA_VERSION.capturadoEm,
296
- lookup: (input) => getPortoPorCodigo(input),
320
+ lookup: (input) => lookupPortoPorCodigo(input),
297
321
  formatHuman: (result) => {
298
322
  const row = result;
299
323
  return `${row.codigo} \u2014 ${row.nome} (${row.uf})`;
@@ -320,6 +344,15 @@ function isReferenceSearchCommand(value) {
320
344
  }
321
345
 
322
346
  // src/commands/reference-lookup/lookup.ts
347
+ function emitLookupFailure(result, options, io) {
348
+ if (options.json) {
349
+ io.stdout.push(
350
+ JSON.stringify({ ok: false, code: result.code, message: result.message }, null, 2)
351
+ );
352
+ return;
353
+ }
354
+ io.stderr.push(result.message);
355
+ }
323
356
  function runReferenceLookupCommand(command, input, options, io = { stdout: [], stderr: [] }) {
324
357
  const module = REFERENCE_LOOKUP_MODULES[command];
325
358
  const trimmed = input.trim();
@@ -328,20 +361,20 @@ function runReferenceLookupCommand(command, input, options, io = { stdout: [], s
328
361
  return EXIT.USAGE;
329
362
  }
330
363
  const result = module.lookup(trimmed);
331
- if (!result) {
332
- io.stderr.push(`Not found: ${trimmed}`);
364
+ if (!result.ok) {
365
+ emitLookupFailure(result, options, io);
333
366
  return EXIT.INVALID;
334
367
  }
335
368
  if (options.json) {
336
369
  const payload = {
337
370
  ok: true,
338
- [module.resultKey]: result,
371
+ [module.resultKey]: result.value,
339
372
  ...options.verbose ? { capturadoEm: module.capturadoEm } : {}
340
373
  };
341
374
  io.stdout.push(JSON.stringify(payload, null, 2));
342
375
  return EXIT.OK;
343
376
  }
344
- io.stdout.push(module.formatHuman(result));
377
+ io.stdout.push(module.formatHuman(result.value));
345
378
  if (options.verbose) {
346
379
  io.stdout.push(`capturadoEm: ${module.capturadoEm}`);
347
380
  }
@@ -363,6 +396,7 @@ function runReferenceLookup(command, value, options, io = { stdout: [], stderr:
363
396
  import { searchCbo } from "@br-validators/core/cbo";
364
397
  import { searchCnaes } from "@br-validators/core/cnaes";
365
398
  import { searchCfop } from "@br-validators/core/cfop";
399
+ import { searchCsosn } from "@br-validators/core/csosn";
366
400
  import { searchNcm } from "@br-validators/core/ncm";
367
401
  function runSearch(command, query, limit) {
368
402
  switch (command) {
@@ -370,6 +404,8 @@ function runSearch(command, query, limit) {
370
404
  return searchCnaes(query, { limit });
371
405
  case "cfop":
372
406
  return searchCfop(query, { limit });
407
+ case "csosn":
408
+ return searchCsosn(query, { limit });
373
409
  case "ncm":
374
410
  return searchNcm(query, { limit });
375
411
  case "cbo":
@@ -425,11 +461,242 @@ function runReferenceSearch(command, query, options, io = { stdout: [], stderr:
425
461
  return runReferenceSearchCommand(command, query.trim(), options, io);
426
462
  }
427
463
 
464
+ // src/commands/reference-lookup/validate.ts
465
+ import { validateCfop } from "@br-validators/core/cfop";
466
+ import { validateCsosn } from "@br-validators/core/csosn";
467
+ import { validateNcm } from "@br-validators/core/ncm";
468
+ var REFERENCE_VALIDATE_COMMANDS = ["ncm", "cfop", "csosn"];
469
+ var VALIDATORS = {
470
+ ncm: validateNcm,
471
+ cfop: validateCfop,
472
+ csosn: validateCsosn
473
+ };
474
+ var CAPTURED_AT = {
475
+ ncm: "ncm",
476
+ cfop: "cfop",
477
+ csosn: "csosn"
478
+ };
479
+ function emitFailure(result, options, io) {
480
+ if (options.json) {
481
+ io.stdout.push(JSON.stringify({ ok: false, code: result.code, message: result.message }, null, 2));
482
+ return;
483
+ }
484
+ io.stderr.push(result.message);
485
+ }
486
+ function isReferenceValidateCommand(command) {
487
+ return REFERENCE_VALIDATE_COMMANDS.includes(command);
488
+ }
489
+ function runReferenceValidateCommand(command, input, options, io = { stdout: [], stderr: [] }) {
490
+ const trimmed = input.trim();
491
+ if (trimmed.length === 0) {
492
+ io.stderr.push(`Missing code. Pass a value to validate for ${command}.`);
493
+ return EXIT.USAGE;
494
+ }
495
+ const result = VALIDATORS[command](trimmed);
496
+ if (!result.ok) {
497
+ emitFailure(result, options, io);
498
+ return EXIT.INVALID;
499
+ }
500
+ if (options.json) {
501
+ const payload = {
502
+ ok: true,
503
+ value: result.value,
504
+ description: result.description,
505
+ ...result.format !== void 0 ? { format: result.format } : {},
506
+ ...options.verbose ? { module: CAPTURED_AT[command] } : {}
507
+ };
508
+ io.stdout.push(JSON.stringify(payload, null, 2));
509
+ return EXIT.OK;
510
+ }
511
+ const lines = [`${result.value} \u2014 ${result.description}`];
512
+ if (result.format !== void 0) {
513
+ lines.push(`format: ${result.format}`);
514
+ }
515
+ io.stdout.push(lines.join("\n"));
516
+ return EXIT.OK;
517
+ }
518
+ function runReferenceValidate(command, value, options, io = { stdout: [], stderr: [] }) {
519
+ if (!isReferenceValidateCommand(command)) {
520
+ io.stderr.push(`Unknown reference validate command: ${command}`);
521
+ return EXIT.USAGE;
522
+ }
523
+ if (!value?.trim()) {
524
+ io.stderr.push(`Missing code. Usage: br-validators ${command} validate <codigo>`);
525
+ return EXIT.USAGE;
526
+ }
527
+ return runReferenceValidateCommand(command, value.trim(), options, io);
528
+ }
529
+
530
+ // src/commands/cst/index.ts
531
+ import {
532
+ CST_DATA_VERSION,
533
+ lookupCstCofinsPorCodigo,
534
+ lookupCstIcmsPorCodigo,
535
+ lookupCstIpiPorCodigo,
536
+ lookupCstPisPorCodigo,
537
+ searchCstCofins,
538
+ searchCstIcms,
539
+ searchCstIpi,
540
+ searchCstPis,
541
+ validateCst
542
+ } from "@br-validators/core/cst";
543
+ var CST_TAXES = ["icms", "ipi", "pis", "cofins"];
544
+ function parseCstTax(raw) {
545
+ if (raw === void 0) {
546
+ return null;
547
+ }
548
+ const normalized = raw.trim().toLowerCase();
549
+ if (CST_TAXES.includes(normalized)) {
550
+ return normalized;
551
+ }
552
+ return null;
553
+ }
554
+ function lookupByTax(tax, codigo) {
555
+ switch (tax) {
556
+ case "icms":
557
+ return lookupCstIcmsPorCodigo(codigo);
558
+ case "ipi":
559
+ return lookupCstIpiPorCodigo(codigo);
560
+ case "pis":
561
+ return lookupCstPisPorCodigo(codigo);
562
+ case "cofins":
563
+ return lookupCstCofinsPorCodigo(codigo);
564
+ }
565
+ }
566
+ function searchByTax(tax, query, limit) {
567
+ const options = limit !== void 0 ? { limit } : void 0;
568
+ switch (tax) {
569
+ case "icms":
570
+ return searchCstIcms(query, options);
571
+ case "ipi":
572
+ return searchCstIpi(query, options);
573
+ case "pis":
574
+ return searchCstPis(query, options);
575
+ case "cofins":
576
+ return searchCstCofins(query, options);
577
+ }
578
+ }
579
+ function emitLookupFailure2(result, options, io) {
580
+ if (options.json) {
581
+ io.stdout.push(JSON.stringify({ ok: false, code: result.code, message: result.message }, null, 2));
582
+ return;
583
+ }
584
+ io.stderr.push(result.message);
585
+ }
586
+ function emitValidateFailure(result, options, io) {
587
+ if (options.json) {
588
+ io.stdout.push(JSON.stringify({ ok: false, code: result.code, message: result.message }, null, 2));
589
+ return;
590
+ }
591
+ io.stderr.push(result.message);
592
+ }
593
+ function runCstLookup(codigo, options, io = { stdout: [], stderr: [] }) {
594
+ const tax = parseCstTax(options.tax);
595
+ if (tax === null) {
596
+ io.stderr.push("Missing or invalid --tax. Expected: icms | ipi | pis | cofins");
597
+ return EXIT.USAGE;
598
+ }
599
+ const trimmed = codigo?.trim() ?? "";
600
+ if (trimmed.length === 0) {
601
+ io.stderr.push("Missing code. Usage: br-validators cst lookup <codigo> --tax icms");
602
+ return EXIT.USAGE;
603
+ }
604
+ const result = lookupByTax(tax, trimmed);
605
+ if (!result.ok) {
606
+ emitLookupFailure2(result, options, io);
607
+ return EXIT.INVALID;
608
+ }
609
+ if (options.json) {
610
+ io.stdout.push(
611
+ JSON.stringify(
612
+ {
613
+ ok: true,
614
+ cst: result.value,
615
+ tax,
616
+ ...options.verbose ? { capturadoEm: CST_DATA_VERSION.capturadoEm } : {}
617
+ },
618
+ null,
619
+ 2
620
+ )
621
+ );
622
+ return EXIT.OK;
623
+ }
624
+ io.stdout.push(`${result.value.codigo} \u2014 ${result.value.descricao}`);
625
+ if (options.verbose) {
626
+ io.stdout.push(`tax: ${tax}`);
627
+ io.stdout.push(`capturadoEm: ${CST_DATA_VERSION.capturadoEm}`);
628
+ }
629
+ return EXIT.OK;
630
+ }
631
+ function runCstSearch(query, options, io = { stdout: [], stderr: [] }) {
632
+ const tax = parseCstTax(options.tax);
633
+ if (tax === null) {
634
+ io.stderr.push("Missing or invalid --tax. Expected: icms | ipi | pis | cofins");
635
+ return EXIT.USAGE;
636
+ }
637
+ const trimmed = query?.trim() ?? "";
638
+ if (trimmed.length === 0) {
639
+ io.stderr.push("Missing query. Usage: br-validators cst search <query> --tax icms");
640
+ return EXIT.USAGE;
641
+ }
642
+ const rows = searchByTax(tax, trimmed, options.limit);
643
+ if (options.json) {
644
+ io.stdout.push(JSON.stringify({ ok: true, tax, results: rows }, null, 2));
645
+ return EXIT.OK;
646
+ }
647
+ if (rows.length === 0) {
648
+ io.stdout.push("No matches.");
649
+ return EXIT.OK;
650
+ }
651
+ for (const row of rows) {
652
+ io.stdout.push(`${row.codigo} \u2014 ${row.descricao}`);
653
+ }
654
+ return EXIT.OK;
655
+ }
656
+ function runCstValidate(codigo, options, io = { stdout: [], stderr: [] }) {
657
+ const tax = parseCstTax(options.tax);
658
+ if (tax === null) {
659
+ io.stderr.push("Missing or invalid --tax. Expected: icms | ipi | pis | cofins");
660
+ return EXIT.USAGE;
661
+ }
662
+ const trimmed = codigo?.trim() ?? "";
663
+ if (trimmed.length === 0) {
664
+ io.stderr.push("Missing code. Usage: br-validators cst validate <codigo> --tax icms");
665
+ return EXIT.USAGE;
666
+ }
667
+ const result = validateCst(trimmed, { tax });
668
+ if (!result.ok) {
669
+ emitValidateFailure(result, options, io);
670
+ return EXIT.INVALID;
671
+ }
672
+ if (options.json) {
673
+ io.stdout.push(
674
+ JSON.stringify(
675
+ {
676
+ ok: true,
677
+ tax,
678
+ value: result.value,
679
+ description: result.description
680
+ },
681
+ null,
682
+ 2
683
+ )
684
+ );
685
+ return EXIT.OK;
686
+ }
687
+ io.stdout.push(`${result.value} \u2014 ${result.description}`);
688
+ if (options.verbose) {
689
+ io.stdout.push(`tax: ${tax}`);
690
+ }
691
+ return EXIT.OK;
692
+ }
693
+
428
694
  // src/commands/ibge/lookup.ts
429
695
  import {
430
- getMunicipioPorCodigo,
696
+ lookupMunicipioPorCodigo,
431
697
  IBGE_DATA_VERSION
432
698
  } from "@br-validators/core/ibge";
699
+ import { lookupInvalidFormat as lookupInvalidFormat3 } from "@br-validators/core/lookup";
433
700
  function normalizeIbgeMunicipioCode(raw) {
434
701
  const digits = raw.replace(/\D/g, "");
435
702
  if (digits.length !== 7) {
@@ -437,24 +704,35 @@ function normalizeIbgeMunicipioCode(raw) {
437
704
  }
438
705
  return Number(digits);
439
706
  }
707
+ function lookupMunicipio(raw) {
708
+ const codigo = normalizeIbgeMunicipioCode(raw);
709
+ if (codigo === null) {
710
+ return lookupInvalidFormat3("Invalid IBGE municipality code. Use 7 digits (e.g. 3550308).");
711
+ }
712
+ return lookupMunicipioPorCodigo(codigo);
713
+ }
440
714
  function formatMunicipioHuman(municipio) {
441
715
  return `${municipio.codigo} \u2014 ${municipio.nome} (${municipio.uf})`;
442
716
  }
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;
717
+ function emitLookupFailure3(result, options, io) {
718
+ if (options.json) {
719
+ io.stdout.push(
720
+ JSON.stringify({ ok: false, code: result.code, message: result.message }, null, 2)
721
+ );
722
+ return result.code === "INVALID_FORMAT" ? EXIT.USAGE : EXIT.INVALID;
448
723
  }
449
- const municipio = getMunicipioPorCodigo(codigo);
450
- if (!municipio) {
451
- io.stderr.push(`Municipality not found: ${input}`);
452
- return EXIT.INVALID;
724
+ io.stderr.push(result.message);
725
+ return result.code === "INVALID_FORMAT" ? EXIT.USAGE : EXIT.INVALID;
726
+ }
727
+ function runIbgeLookupCommand(input, options, io = { stdout: [], stderr: [] }) {
728
+ const result = lookupMunicipio(input);
729
+ if (!result.ok) {
730
+ return emitLookupFailure3(result, options, io);
453
731
  }
454
732
  if (options.json) {
455
733
  const payload = {
456
734
  ok: true,
457
- municipio
735
+ municipio: result.value
458
736
  };
459
737
  if (options.verbose) {
460
738
  payload.capturadoEm = IBGE_DATA_VERSION.capturadoEm;
@@ -462,7 +740,7 @@ function runIbgeLookupCommand(input, options, io = { stdout: [], stderr: [] }) {
462
740
  io.stdout.push(JSON.stringify(payload, null, 2));
463
741
  return EXIT.OK;
464
742
  }
465
- io.stdout.push(formatMunicipioHuman(municipio));
743
+ io.stdout.push(formatMunicipioHuman(result.value));
466
744
  if (options.verbose) {
467
745
  io.stdout.push(`capturadoEm: ${IBGE_DATA_VERSION.capturadoEm}`);
468
746
  }
@@ -478,8 +756,8 @@ function runIbgeLookup(value, options, io = { stdout: [], stderr: [] }) {
478
756
 
479
757
  // src/commands/ibge/list.ts
480
758
  import {
481
- getEstados,
482
- getMunicipios,
759
+ getAllEstados,
760
+ getAllMunicipios,
483
761
  IBGE_DATA_VERSION as IBGE_DATA_VERSION2
484
762
  } from "@br-validators/core/ibge";
485
763
  function sliceRows(rows, limit) {
@@ -492,7 +770,7 @@ function formatEstadoHuman(estado) {
492
770
  return `${estado.codigo} \u2014 ${estado.sigla} \u2014 ${estado.nome}`;
493
771
  }
494
772
  function runIbgeListEstadosCommand(options, io = { stdout: [], stderr: [] }) {
495
- const estados = sliceRows(getEstados(), options.limit);
773
+ const estados = sliceRows(getAllEstados(), options.limit);
496
774
  if (options.json) {
497
775
  const payload = {
498
776
  ok: true,
@@ -515,7 +793,7 @@ function runIbgeListEstadosCommand(options, io = { stdout: [], stderr: [] }) {
515
793
  }
516
794
  function runIbgeListMunicipiosCommand(options, io = { stdout: [], stderr: [] }) {
517
795
  const uf = options.uf?.trim().toUpperCase();
518
- const municipios = sliceRows(getMunicipios(uf ? { uf } : void 0), options.limit);
796
+ const municipios = sliceRows(getAllMunicipios(uf ? { uf } : void 0), options.limit);
519
797
  if (options.json) {
520
798
  const payload = {
521
799
  ok: true,
@@ -547,7 +825,7 @@ function runIbgeList(target, options, io = { stdout: [], stderr: [] }) {
547
825
  // src/commands/feriados/list.ts
548
826
  import {
549
827
  FERIADOS_DATA_VERSION,
550
- getFeriadosNacionais
828
+ getAllFeriados
551
829
  } from "@br-validators/core/feriados";
552
830
  function resolveYear(year) {
553
831
  if (year !== void 0 && Number.isInteger(year) && year >= 1900 && year <= 2100) {
@@ -560,7 +838,7 @@ function formatFeriadoHuman(feriado) {
560
838
  }
561
839
  function runFeriadosListCommand(options, io = { stdout: [], stderr: [] }) {
562
840
  const year = resolveYear(options.year);
563
- const feriados = getFeriadosNacionais(year);
841
+ const feriados = getAllFeriados(year);
564
842
  if (options.json) {
565
843
  const payload = {
566
844
  ok: true,
@@ -586,13 +864,193 @@ function runFeriadosList(options, io = { stdout: [], stderr: [] }) {
586
864
  return runFeriadosListCommand(options, io);
587
865
  }
588
866
 
867
+ // src/commands/inss/index.ts
868
+ import {
869
+ calcularInssMensal,
870
+ getInssTabelaContribuicao,
871
+ INSS_DATA_VERSION,
872
+ INSS_DEFAULT_ANO
873
+ } from "@br-validators/core/inss";
874
+ function resolveAno(ano) {
875
+ if (ano !== void 0 && Number.isInteger(ano) && ano >= 1900 && ano <= 2100) {
876
+ return ano;
877
+ }
878
+ return INSS_DEFAULT_ANO;
879
+ }
880
+ function formatInssFaixaHuman(faixa) {
881
+ const aliquotaPct = `${String(faixa.aliquota * 100).replace(".", ",")}%`;
882
+ return `Faixa ${String(faixa.faixa)} \u2014 ${faixa.descricao} \u2014 ${aliquotaPct}`;
883
+ }
884
+ function runInssTabelaCommand(options, io = { stdout: [], stderr: [] }) {
885
+ const ano = resolveAno(options.ano);
886
+ const tabela = getInssTabelaContribuicao(ano);
887
+ if (tabela === void 0) {
888
+ io.stderr.push(`INSS contribution table not found for year ${String(ano)}`);
889
+ return EXIT.INVALID;
890
+ }
891
+ if (options.json) {
892
+ const payload = {
893
+ ok: true,
894
+ ano,
895
+ teto: tabela.teto,
896
+ faixas: tabela.faixas
897
+ };
898
+ if (options.verbose) {
899
+ payload.capturadoEm = INSS_DATA_VERSION.capturadoEm;
900
+ }
901
+ io.stdout.push(JSON.stringify(payload, null, 2));
902
+ return EXIT.OK;
903
+ }
904
+ io.stdout.push(`Teto: R$ ${tabela.teto.toFixed(2)}`);
905
+ for (const faixa of tabela.faixas) {
906
+ io.stdout.push(formatInssFaixaHuman(faixa));
907
+ }
908
+ if (options.verbose) {
909
+ io.stdout.push(`capturadoEm: ${INSS_DATA_VERSION.capturadoEm}`);
910
+ }
911
+ return EXIT.OK;
912
+ }
913
+ function runInssCalcCommand(salarioRaw, options, io = { stdout: [], stderr: [] }) {
914
+ const trimmed = salarioRaw.trim().replace(",", ".");
915
+ const salarioContribuicao = Number(trimmed);
916
+ if (!Number.isFinite(salarioContribuicao)) {
917
+ io.stderr.push("Invalid sal\xE1rio de contribui\xE7\xE3o. Pass a numeric value (e.g. 3000).");
918
+ return EXIT.USAGE;
919
+ }
920
+ const ano = resolveAno(options.ano);
921
+ const result = calcularInssMensal(salarioContribuicao, ano);
922
+ if (result === void 0) {
923
+ if (getInssTabelaContribuicao(ano) === void 0) {
924
+ io.stderr.push(`INSS contribution table not found for year ${String(ano)}`);
925
+ return EXIT.INVALID;
926
+ }
927
+ io.stderr.push("Sal\xE1rio de contribui\xE7\xE3o must be a non-negative number");
928
+ return EXIT.INVALID;
929
+ }
930
+ if (options.json) {
931
+ const payload = {
932
+ ok: true,
933
+ ...result,
934
+ ...options.verbose ? { capturadoEm: INSS_DATA_VERSION.capturadoEm } : {}
935
+ };
936
+ io.stdout.push(JSON.stringify(payload, null, 2));
937
+ return EXIT.OK;
938
+ }
939
+ io.stdout.push(
940
+ `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)}`
941
+ );
942
+ if (options.verbose) {
943
+ io.stdout.push(`capturadoEm: ${INSS_DATA_VERSION.capturadoEm}`);
944
+ }
945
+ return EXIT.OK;
946
+ }
947
+ function runInssTabela(options, io = { stdout: [], stderr: [] }) {
948
+ return runInssTabelaCommand(options, io);
949
+ }
950
+ function runInssCalc(salario, options, io = { stdout: [], stderr: [] }) {
951
+ if (!salario?.trim()) {
952
+ io.stderr.push("Missing sal\xE1rio de contribui\xE7\xE3o. Usage: br-validators inss calc <salario>");
953
+ return EXIT.USAGE;
954
+ }
955
+ return runInssCalcCommand(salario.trim(), options, io);
956
+ }
957
+
958
+ // src/commands/irpf/index.ts
959
+ import {
960
+ calcularIrpfMensal,
961
+ getIrpfTabelaProgressiva,
962
+ IRPF_DATA_VERSION,
963
+ IRPF_DEFAULT_ANO
964
+ } from "@br-validators/core/irpf";
965
+ function resolveAno2(ano) {
966
+ if (ano !== void 0 && Number.isInteger(ano) && ano >= 1900 && ano <= 2100) {
967
+ return ano;
968
+ }
969
+ return IRPF_DEFAULT_ANO;
970
+ }
971
+ function formatIrpfFaixaHuman(faixa) {
972
+ const aliquotaPct = `${String(faixa.aliquota * 100).replace(".", ",")}%`;
973
+ return `Faixa ${String(faixa.faixa)} \u2014 ${faixa.descricao} \u2014 ${aliquotaPct} \u2014 deduzir R$ ${faixa.parcelaDeduzir.toFixed(2)}`;
974
+ }
975
+ function runIrpfTabelaCommand(options, io = { stdout: [], stderr: [] }) {
976
+ const ano = resolveAno2(options.ano);
977
+ const faixas = getIrpfTabelaProgressiva(ano);
978
+ if (faixas === void 0) {
979
+ io.stderr.push(`IRPF progressive table not found for year ${String(ano)}`);
980
+ return EXIT.INVALID;
981
+ }
982
+ if (options.json) {
983
+ const payload = {
984
+ ok: true,
985
+ ano,
986
+ faixas
987
+ };
988
+ if (options.verbose) {
989
+ payload.capturadoEm = IRPF_DATA_VERSION.capturadoEm;
990
+ }
991
+ io.stdout.push(JSON.stringify(payload, null, 2));
992
+ return EXIT.OK;
993
+ }
994
+ for (const faixa of faixas) {
995
+ io.stdout.push(formatIrpfFaixaHuman(faixa));
996
+ }
997
+ if (options.verbose) {
998
+ io.stdout.push(`capturadoEm: ${IRPF_DATA_VERSION.capturadoEm}`);
999
+ }
1000
+ return EXIT.OK;
1001
+ }
1002
+ function runIrpfCalcCommand(baseRaw, options, io = { stdout: [], stderr: [] }) {
1003
+ const trimmed = baseRaw.trim().replace(",", ".");
1004
+ const baseCalculo = Number(trimmed);
1005
+ if (!Number.isFinite(baseCalculo)) {
1006
+ io.stderr.push("Invalid base de c\xE1lculo. Pass a numeric value (e.g. 3000).");
1007
+ return EXIT.USAGE;
1008
+ }
1009
+ const ano = resolveAno2(options.ano);
1010
+ const result = calcularIrpfMensal(baseCalculo, ano);
1011
+ if (result === void 0) {
1012
+ if (getIrpfTabelaProgressiva(ano) === void 0) {
1013
+ io.stderr.push(`IRPF progressive table not found for year ${String(ano)}`);
1014
+ return EXIT.INVALID;
1015
+ }
1016
+ io.stderr.push("Base de c\xE1lculo must be a non-negative number");
1017
+ return EXIT.INVALID;
1018
+ }
1019
+ if (options.json) {
1020
+ const payload = {
1021
+ ok: true,
1022
+ ...result,
1023
+ ...options.verbose ? { capturadoEm: IRPF_DATA_VERSION.capturadoEm } : {}
1024
+ };
1025
+ io.stdout.push(JSON.stringify(payload, null, 2));
1026
+ return EXIT.OK;
1027
+ }
1028
+ io.stdout.push(
1029
+ `IRPF ${String(result.ano)} \u2014 base R$ ${result.baseCalculo.toFixed(2)} \u2014 faixa ${String(result.faixa)} \u2014 imposto R$ ${result.imposto.toFixed(2)}`
1030
+ );
1031
+ if (options.verbose) {
1032
+ io.stdout.push(`capturadoEm: ${IRPF_DATA_VERSION.capturadoEm}`);
1033
+ }
1034
+ return EXIT.OK;
1035
+ }
1036
+ function runIrpfTabela(options, io = { stdout: [], stderr: [] }) {
1037
+ return runIrpfTabelaCommand(options, io);
1038
+ }
1039
+ function runIrpfCalc(base, options, io = { stdout: [], stderr: [] }) {
1040
+ if (!base?.trim()) {
1041
+ io.stderr.push("Missing base de c\xE1lculo. Usage: br-validators irpf calc <base>");
1042
+ return EXIT.USAGE;
1043
+ }
1044
+ return runIrpfCalcCommand(base.trim(), options, io);
1045
+ }
1046
+
589
1047
  // src/commands/tse-municipios/lookup.ts
590
1048
  import {
591
1049
  getCodigosTsePorMunicipio,
592
1050
  getMunicipioIbgePorCodigoTse,
593
1051
  TSE_MUNICIPIOS_DATA_VERSION
594
1052
  } from "@br-validators/core/tse-municipios";
595
- import { getMunicipioPorCodigo as getMunicipioPorCodigo2 } from "@br-validators/core/ibge";
1053
+ import { getMunicipioPorCodigo } from "@br-validators/core/ibge";
596
1054
  function normalizeTseInput(raw) {
597
1055
  const digits = raw.replace(/\D/g, "");
598
1056
  if (digits.length === 5) {
@@ -623,11 +1081,11 @@ function lookupTseMunicipio(raw) {
623
1081
  }
624
1082
  function formatTseLookupHuman(result) {
625
1083
  if (result.kind === "tse-to-ibge") {
626
- const municipio2 = getMunicipioPorCodigo2(result.ibgeCodigo);
1084
+ const municipio2 = getMunicipioPorCodigo(result.ibgeCodigo);
627
1085
  const name2 = municipio2 ? `${municipio2.nome} (${municipio2.uf})` : String(result.ibgeCodigo);
628
1086
  return `TSE ${result.codigoTse} \u2192 IBGE ${result.ibgeCodigo} \u2014 ${name2}`;
629
1087
  }
630
- const municipio = getMunicipioPorCodigo2(result.ibgeCodigo);
1088
+ const municipio = getMunicipioPorCodigo(result.ibgeCodigo);
631
1089
  const name = municipio ? `${municipio.nome} (${municipio.uf})` : String(result.ibgeCodigo);
632
1090
  return `IBGE ${result.ibgeCodigo} \u2014 ${name} \u2192 TSE ${result.codigosTse.join(", ")}`;
633
1091
  }
@@ -771,6 +1229,339 @@ function runDddLookup(value, options, io = { stdout: [], stderr: [] }) {
771
1229
  return runDddLookupCommand(value.trim(), options, io);
772
1230
  }
773
1231
 
1232
+ // src/commands/nfe-cuf/lookup.ts
1233
+ import {
1234
+ lookupCufPorCodigo,
1235
+ NFE_CUF_DATA_VERSION
1236
+ } from "@br-validators/core/nfe-cuf";
1237
+ function formatNfeCufHuman(row) {
1238
+ return `cUF ${row.codigo} \u2014 ${row.uf} \u2014 ${row.nome} (IBGE UF ${row.codigoIbge})`;
1239
+ }
1240
+ function runNfeCufLookupCommand(input, options, io = { stdout: [], stderr: [] }) {
1241
+ const trimmed = input.trim();
1242
+ if (trimmed.length === 0) {
1243
+ io.stderr.push("Invalid cUF code. Use 2 digits (e.g. 35).");
1244
+ return EXIT.USAGE;
1245
+ }
1246
+ const result = lookupCufPorCodigo(trimmed);
1247
+ if (!result.ok) {
1248
+ if (options.json) {
1249
+ io.stdout.push(JSON.stringify({ ok: false, code: result.code, message: result.message }, null, 2));
1250
+ return EXIT.INVALID;
1251
+ }
1252
+ io.stderr.push(result.message);
1253
+ return EXIT.INVALID;
1254
+ }
1255
+ if (options.json) {
1256
+ const payload = {
1257
+ ok: true,
1258
+ cuf: result.value
1259
+ };
1260
+ if (options.verbose) {
1261
+ payload.capturadoEm = NFE_CUF_DATA_VERSION.capturadoEm;
1262
+ }
1263
+ io.stdout.push(JSON.stringify(payload, null, 2));
1264
+ return EXIT.OK;
1265
+ }
1266
+ io.stdout.push(formatNfeCufHuman(result.value));
1267
+ if (options.verbose) {
1268
+ io.stdout.push(`capturadoEm: ${NFE_CUF_DATA_VERSION.capturadoEm}`);
1269
+ }
1270
+ return EXIT.OK;
1271
+ }
1272
+ function runNfeCufLookup(value, options, io = { stdout: [], stderr: [] }) {
1273
+ if (!value?.trim()) {
1274
+ io.stderr.push("Missing cUF code. Usage: br-validators nfe-cuf lookup <code>");
1275
+ return EXIT.USAGE;
1276
+ }
1277
+ return runNfeCufLookupCommand(value.trim(), options, io);
1278
+ }
1279
+
1280
+ // src/commands/selic/index.ts
1281
+ import {
1282
+ getSelicMeta,
1283
+ getSelicMetaPorData,
1284
+ SELIC_DATA_VERSION
1285
+ } from "@br-validators/core/selic";
1286
+ function formatSelicMetaHuman(meta) {
1287
+ return `SELIC meta ${meta.dataReferencia} \u2014 ${String(meta.valor)}% a.a.`;
1288
+ }
1289
+ function runSelicCommand(options, io = { stdout: [], stderr: [] }) {
1290
+ const meta = options.date === void 0 || options.date.trim().length === 0 ? getSelicMeta() : getSelicMetaPorData(options.date.trim());
1291
+ if (meta === void 0) {
1292
+ io.stderr.push(
1293
+ options.date ? `SELIC meta not found for date ${options.date}` : "SELIC meta series is empty"
1294
+ );
1295
+ return EXIT.INVALID;
1296
+ }
1297
+ if (options.json) {
1298
+ const payload = {
1299
+ ok: true,
1300
+ meta
1301
+ };
1302
+ if (options.verbose) {
1303
+ payload.capturadoEm = SELIC_DATA_VERSION.capturadoEm;
1304
+ }
1305
+ io.stdout.push(JSON.stringify(payload, null, 2));
1306
+ return EXIT.OK;
1307
+ }
1308
+ io.stdout.push(formatSelicMetaHuman(meta));
1309
+ if (options.verbose) {
1310
+ io.stdout.push(`dataReferencia: ${meta.dataReferencia}`);
1311
+ io.stdout.push(`isStale: ${String(meta.isStale)}`);
1312
+ if (meta.warning !== void 0) {
1313
+ io.stdout.push(`warning: ${meta.warning}`);
1314
+ }
1315
+ io.stdout.push(`capturadoEm: ${SELIC_DATA_VERSION.capturadoEm}`);
1316
+ }
1317
+ return EXIT.OK;
1318
+ }
1319
+
1320
+ // src/commands/iss-municipal/index.ts
1321
+ import {
1322
+ getIssMunicipalPorUf,
1323
+ getIssMunicipalPorUfMunicipio,
1324
+ ISS_MUNICIPAL_DATA_VERSION,
1325
+ ISS_MUNICIPAL_ESTIMATION_WARNING,
1326
+ lookupIssMunicipalPorIbge,
1327
+ searchIssMunicipal
1328
+ } from "@br-validators/core/iss-municipal";
1329
+ function sliceRows2(rows, limit) {
1330
+ if (limit === void 0 || !Number.isFinite(limit) || limit <= 0) {
1331
+ return rows;
1332
+ }
1333
+ return rows.slice(0, limit);
1334
+ }
1335
+ function formatIssMunicipalHuman(row) {
1336
+ return `${row.nome}/${row.uf} \u2014 ISS ${String(row.aliquotaMin)}%\u2013${String(row.aliquotaMax)}%`;
1337
+ }
1338
+ function emitDisclaimer(options, io) {
1339
+ if (options.json) {
1340
+ return;
1341
+ }
1342
+ io.stderr.push(ISS_MUNICIPAL_ESTIMATION_WARNING);
1343
+ }
1344
+ function runIssMunicipalLookup(codigo, options, io = { stdout: [], stderr: [] }) {
1345
+ const trimmed = codigo?.trim() ?? "";
1346
+ if (trimmed.length === 0) {
1347
+ io.stderr.push("Missing IBGE code. Usage: br-validators iss-municipal lookup <codigoIbge>");
1348
+ return EXIT.USAGE;
1349
+ }
1350
+ const result = lookupIssMunicipalPorIbge(trimmed);
1351
+ if (result === void 0) {
1352
+ io.stderr.push(`ISS municipal row not found for IBGE code ${trimmed}`);
1353
+ return EXIT.INVALID;
1354
+ }
1355
+ emitDisclaimer(options, io);
1356
+ if (options.json) {
1357
+ const payload = {
1358
+ ok: true,
1359
+ iss: result
1360
+ };
1361
+ if (options.verbose) {
1362
+ payload.capturadoEm = ISS_MUNICIPAL_DATA_VERSION.capturadoEm;
1363
+ }
1364
+ io.stdout.push(JSON.stringify(payload, null, 2));
1365
+ return EXIT.OK;
1366
+ }
1367
+ io.stdout.push(formatIssMunicipalHuman(result));
1368
+ io.stdout.push(`fonte: ${result.fonte}`);
1369
+ io.stdout.push(`warning: ${result.warning}`);
1370
+ if (options.verbose) {
1371
+ io.stdout.push(`leiUrl: ${result.leiUrl}`);
1372
+ io.stdout.push(`estimativa: ${String(result.estimativa)}`);
1373
+ io.stdout.push(`capturadoEm: ${ISS_MUNICIPAL_DATA_VERSION.capturadoEm}`);
1374
+ }
1375
+ return EXIT.OK;
1376
+ }
1377
+ function runIssMunicipalList(options, io = { stdout: [], stderr: [] }) {
1378
+ const uf = options.uf?.trim() ?? "";
1379
+ if (uf.length === 0) {
1380
+ io.stderr.push("Missing UF. Usage: br-validators iss-municipal list --uf <UF>");
1381
+ return EXIT.USAGE;
1382
+ }
1383
+ const rows = sliceRows2(getIssMunicipalPorUf(uf), options.limit);
1384
+ emitDisclaimer(options, io);
1385
+ if (options.json) {
1386
+ const payload = {
1387
+ ok: true,
1388
+ uf: uf.toUpperCase(),
1389
+ total: rows.length,
1390
+ results: rows
1391
+ };
1392
+ if (options.verbose) {
1393
+ payload.capturadoEm = ISS_MUNICIPAL_DATA_VERSION.capturadoEm;
1394
+ }
1395
+ io.stdout.push(JSON.stringify(payload, null, 2));
1396
+ return EXIT.OK;
1397
+ }
1398
+ if (rows.length === 0) {
1399
+ io.stdout.push(`No ISS municipal rows embedded for UF ${uf.toUpperCase()}.`);
1400
+ return EXIT.OK;
1401
+ }
1402
+ for (const row of rows) {
1403
+ io.stdout.push(formatIssMunicipalHuman(row));
1404
+ }
1405
+ if (options.verbose) {
1406
+ io.stdout.push(`capturadoEm: ${ISS_MUNICIPAL_DATA_VERSION.capturadoEm}`);
1407
+ }
1408
+ return EXIT.OK;
1409
+ }
1410
+ function runIssMunicipalSearch(query, options, io = { stdout: [], stderr: [] }) {
1411
+ const trimmed = query?.trim() ?? "";
1412
+ if (trimmed.length === 0) {
1413
+ io.stderr.push("Missing query. Usage: br-validators iss-municipal search <query>");
1414
+ return EXIT.USAGE;
1415
+ }
1416
+ const rows = searchIssMunicipal(trimmed, {
1417
+ limit: options.limit,
1418
+ ...options.uf !== void 0 && options.uf.trim().length > 0 ? { uf: options.uf } : {}
1419
+ });
1420
+ emitDisclaimer(options, io);
1421
+ if (options.json) {
1422
+ io.stdout.push(JSON.stringify({ ok: true, results: rows }, null, 2));
1423
+ return EXIT.OK;
1424
+ }
1425
+ if (rows.length === 0) {
1426
+ io.stdout.push("No ISS municipal rows matched.");
1427
+ return EXIT.OK;
1428
+ }
1429
+ for (const row of rows) {
1430
+ io.stdout.push(formatIssMunicipalHuman(row));
1431
+ }
1432
+ return EXIT.OK;
1433
+ }
1434
+ function runIssMunicipalResolve(uf, nome, options, io = { stdout: [], stderr: [] }) {
1435
+ const normalizedUf = uf?.trim() ?? "";
1436
+ const normalizedNome = nome?.trim() ?? "";
1437
+ if (normalizedUf.length === 0 || normalizedNome.length === 0) {
1438
+ io.stderr.push("Missing UF or municipality name. Usage: br-validators iss-municipal resolve <uf> <nome>");
1439
+ return EXIT.USAGE;
1440
+ }
1441
+ const result = getIssMunicipalPorUfMunicipio(normalizedUf, normalizedNome);
1442
+ if (result === void 0) {
1443
+ io.stderr.push(`ISS municipal row not found for ${normalizedNome}/${normalizedUf}`);
1444
+ return EXIT.INVALID;
1445
+ }
1446
+ return runIssMunicipalLookup(String(result.codigoIbge), options, io);
1447
+ }
1448
+
1449
+ // src/commands/ptax/lookup.ts
1450
+ import {
1451
+ getPtaxCotacao,
1452
+ PTAX_DATA_VERSION
1453
+ } from "@br-validators/core/ptax";
1454
+ function formatPtaxCotacaoHuman(cotacao) {
1455
+ return `${cotacao.moeda} Fechamento PTAX \u2014 compra ${String(cotacao.cotacaoCompra)} / venda ${String(cotacao.cotacaoVenda)} (${cotacao.dataReferencia})`;
1456
+ }
1457
+ function runPtaxLookupCommand(moeda, data, options, io = { stdout: [], stderr: [] }) {
1458
+ const trimmedMoeda = moeda.trim();
1459
+ if (trimmedMoeda.length === 0) {
1460
+ io.stderr.push("Missing currency code. Pass a 3-letter ISO code (e.g. USD).");
1461
+ return EXIT.USAGE;
1462
+ }
1463
+ const cotacao = data === void 0 || data.trim().length === 0 ? getPtaxCotacao(trimmedMoeda) : getPtaxCotacao(trimmedMoeda, data.trim());
1464
+ if (cotacao === void 0) {
1465
+ io.stderr.push(`PTAX quote not found: ${trimmedMoeda}${data ? ` ${data}` : ""}`);
1466
+ return EXIT.INVALID;
1467
+ }
1468
+ if (options.json) {
1469
+ const payload = {
1470
+ ok: true,
1471
+ cotacao
1472
+ };
1473
+ if (options.verbose) {
1474
+ payload.capturadoEm = PTAX_DATA_VERSION.capturadoEm;
1475
+ }
1476
+ io.stdout.push(JSON.stringify(payload, null, 2));
1477
+ return EXIT.OK;
1478
+ }
1479
+ io.stdout.push(formatPtaxCotacaoHuman(cotacao));
1480
+ if (options.verbose) {
1481
+ io.stdout.push(`dataReferencia: ${cotacao.dataReferencia}`);
1482
+ io.stdout.push(`isStale: ${String(cotacao.isStale)}`);
1483
+ if (cotacao.warning !== void 0) {
1484
+ io.stdout.push(`warning: ${cotacao.warning}`);
1485
+ }
1486
+ io.stdout.push(`capturadoEm: ${PTAX_DATA_VERSION.capturadoEm}`);
1487
+ }
1488
+ return EXIT.OK;
1489
+ }
1490
+ function runPtaxLookup(moeda, data, options, io = { stdout: [], stderr: [] }) {
1491
+ if (!moeda?.trim()) {
1492
+ io.stderr.push("Missing currency code. Usage: ptax lookup <moeda> [data]");
1493
+ return EXIT.USAGE;
1494
+ }
1495
+ return runPtaxLookupCommand(moeda.trim(), data?.trim(), options, io);
1496
+ }
1497
+
1498
+ // src/commands/ptax/historico.ts
1499
+ import {
1500
+ getPtaxHistorico,
1501
+ PTAX_DATA_VERSION as PTAX_DATA_VERSION2
1502
+ } from "@br-validators/core/ptax";
1503
+ function formatPtaxHistoricoHuman(results) {
1504
+ return results.map(
1505
+ (cotacao) => `${cotacao.dataReferencia} \u2014 compra ${String(cotacao.cotacaoCompra)} / venda ${String(cotacao.cotacaoVenda)}`
1506
+ );
1507
+ }
1508
+ function runPtaxHistoricoCommand(moeda, desde, ate, options, io = { stdout: [], stderr: [] }) {
1509
+ const trimmedMoeda = moeda.trim();
1510
+ const trimmedDesde = desde.trim();
1511
+ const trimmedAte = ate.trim();
1512
+ if (trimmedMoeda.length === 0) {
1513
+ io.stderr.push("Missing currency code. Pass a 3-letter ISO code (e.g. USD).");
1514
+ return EXIT.USAGE;
1515
+ }
1516
+ if (trimmedDesde.length === 0 || trimmedAte.length === 0) {
1517
+ io.stderr.push("Missing date range. Pass desde and ate as YYYY-MM-DD.");
1518
+ return EXIT.USAGE;
1519
+ }
1520
+ const results = getPtaxHistorico(trimmedMoeda, { desde: trimmedDesde, ate: trimmedAte });
1521
+ if (results.length === 0) {
1522
+ io.stderr.push(
1523
+ `No PTAX quotes in embed for ${trimmedMoeda} between ${trimmedDesde} and ${trimmedAte}`
1524
+ );
1525
+ return EXIT.INVALID;
1526
+ }
1527
+ if (options.json) {
1528
+ const payload = {
1529
+ ok: true,
1530
+ moeda: trimmedMoeda.toUpperCase(),
1531
+ desde: trimmedDesde,
1532
+ ate: trimmedAte,
1533
+ total: results.length,
1534
+ cotacoes: results
1535
+ };
1536
+ if (options.verbose) {
1537
+ payload.capturadoEm = PTAX_DATA_VERSION2.capturadoEm;
1538
+ payload.janelaDiasUteis = PTAX_DATA_VERSION2.janelaDiasUteis;
1539
+ }
1540
+ io.stdout.push(JSON.stringify(payload, null, 2));
1541
+ return EXIT.OK;
1542
+ }
1543
+ io.stdout.push(`${trimmedMoeda.toUpperCase()} PTAX historico (${String(results.length)} rows)`);
1544
+ for (const line of formatPtaxHistoricoHuman(results)) {
1545
+ io.stdout.push(line);
1546
+ }
1547
+ if (options.verbose) {
1548
+ io.stdout.push(`capturadoEm: ${PTAX_DATA_VERSION2.capturadoEm}`);
1549
+ io.stdout.push(`janelaDiasUteis: ${String(PTAX_DATA_VERSION2.janelaDiasUteis)}`);
1550
+ }
1551
+ return EXIT.OK;
1552
+ }
1553
+ function runPtaxHistorico(moeda, desde, ate, options, io = { stdout: [], stderr: [] }) {
1554
+ if (!moeda?.trim()) {
1555
+ io.stderr.push("Missing currency code. Usage: ptax historico <moeda> <desde> <ate>");
1556
+ return EXIT.USAGE;
1557
+ }
1558
+ if (!desde?.trim() || !ate?.trim()) {
1559
+ io.stderr.push("Missing date range. Usage: ptax historico <moeda> <desde> <ate>");
1560
+ return EXIT.USAGE;
1561
+ }
1562
+ return runPtaxHistoricoCommand(moeda.trim(), desde.trim(), ate.trim(), options, io);
1563
+ }
1564
+
774
1565
  // src/commands/brcode.ts
775
1566
  import {
776
1567
  BRCODE_OFFICIAL_SOURCE_URL,
@@ -2225,9 +3016,99 @@ function runDetect(value, options, io = { stdout: [], stderr: [] }) {
2225
3016
  return printDetect(result, options, io);
2226
3017
  }
2227
3018
 
2228
- // src/commands/sanitize.ts
2229
- import { sanitize } from "@br-validators/core";
2230
- var SANITIZABLE_TYPES = [
3019
+ // src/commands/sanitize.ts
3020
+ import { sanitize } from "@br-validators/core";
3021
+ var SANITIZABLE_TYPES = [
3022
+ "cpf",
3023
+ "cnpj",
3024
+ "cep",
3025
+ "placa",
3026
+ "pis-pasep",
3027
+ "telefone",
3028
+ "cnh",
3029
+ "renavam",
3030
+ "titulo-eleitor",
3031
+ "nfe-chave",
3032
+ "boleto",
3033
+ "cartao-credito",
3034
+ "ean",
3035
+ "inscricao-estadual",
3036
+ "inscricao-estadual-produtor-rural",
3037
+ "pix"
3038
+ ];
3039
+ function isSanitizableType(type) {
3040
+ return SANITIZABLE_TYPES.includes(type);
3041
+ }
3042
+ function resolveInput21(value, fileContent) {
3043
+ const input = value ?? fileContent?.trim();
3044
+ if (!input) {
3045
+ return null;
3046
+ }
3047
+ return input;
3048
+ }
3049
+ function printSanitize(result, options, io = { stdout: [], stderr: [] }) {
3050
+ if (options.json) {
3051
+ io.stdout.push(JSON.stringify(result, null, 2));
3052
+ return result.ok ? EXIT.OK : EXIT.INVALID;
3053
+ }
3054
+ if (options.quiet) {
3055
+ return result.ok ? EXIT.OK : EXIT.INVALID;
3056
+ }
3057
+ if (result.ok) {
3058
+ io.stdout.push("valid: yes");
3059
+ io.stdout.push(`value: ${result.value}`);
3060
+ io.stdout.push(`fixes: ${result.fixes.join(", ")}`);
3061
+ return EXIT.OK;
3062
+ }
3063
+ io.stderr.push("valid: no");
3064
+ io.stderr.push(`code: ${result.code}`);
3065
+ io.stderr.push(`message: ${result.message}`);
3066
+ return EXIT.INVALID;
3067
+ }
3068
+ function runSanitize(type, value, options, io = { stdout: [], stderr: [] }) {
3069
+ if (!isSanitizableType(type)) {
3070
+ io.stderr.push(`Unsupported sanitize type: ${type}`);
3071
+ return EXIT.USAGE;
3072
+ }
3073
+ const input = resolveInput21(value, options.file);
3074
+ if (input === null) {
3075
+ io.stderr.push("Missing value. Pass an argument or use --file.");
3076
+ return EXIT.USAGE;
3077
+ }
3078
+ const uf = options.uf?.toUpperCase();
3079
+ const result = sanitize(input, type, uf ? { uf } : {});
3080
+ return printSanitize(result, options, io);
3081
+ }
3082
+
3083
+ // src/commands/mask.ts
3084
+ import { isMaskableDocumentType, maskRuntime } from "@br-validators/core";
3085
+ function resolveInput22(value, fileContent) {
3086
+ const input = value ?? fileContent?.trim();
3087
+ if (!input) {
3088
+ return null;
3089
+ }
3090
+ return input;
3091
+ }
3092
+ function runMask(type, value, options, io = { stdout: [], stderr: [] }) {
3093
+ if (!isMaskableDocumentType(type)) {
3094
+ io.stderr.push(`Unsupported mask type: ${type}`);
3095
+ return EXIT.USAGE;
3096
+ }
3097
+ const input = resolveInput22(value, options.file);
3098
+ if (input === null) {
3099
+ io.stderr.push("Missing value. Pass an argument or use --file.");
3100
+ return EXIT.USAGE;
3101
+ }
3102
+ const uf = options.uf?.toUpperCase();
3103
+ const result = maskRuntime(type, input, uf ? { uf } : {});
3104
+ return printFormat(result, options, io);
3105
+ }
3106
+
3107
+ // src/commands/compare.ts
3108
+ import { compareRuntime } from "@br-validators/core";
3109
+
3110
+ // src/commands/platform-document-types.ts
3111
+ var PLATFORM_DOCUMENT_TYPES = [
2231
3112
  "cpf",
2232
3113
  "cnpj",
2233
3114
  "cep",
@@ -2237,55 +3118,162 @@ var SANITIZABLE_TYPES = [
2237
3118
  "cnh",
2238
3119
  "renavam",
2239
3120
  "titulo-eleitor",
3121
+ "processo-judicial",
3122
+ "rg",
2240
3123
  "nfe-chave",
2241
3124
  "boleto",
2242
3125
  "cartao-credito",
2243
3126
  "ean",
2244
3127
  "inscricao-estadual",
2245
- "inscricao-estadual-produtor-rural"
3128
+ "inscricao-estadual-produtor-rural",
3129
+ "pix",
3130
+ "brcode"
2246
3131
  ];
2247
- function isSanitizableType(type) {
2248
- return SANITIZABLE_TYPES.includes(type);
3132
+ function isPlatformDocumentType(type) {
3133
+ return PLATFORM_DOCUMENT_TYPES.includes(type);
2249
3134
  }
2250
- function resolveInput21(value, fileContent) {
2251
- const input = value ?? fileContent?.trim();
2252
- if (!input) {
2253
- return null;
3135
+
3136
+ // src/commands/compare.ts
3137
+ function printCompare(result, options, io = { stdout: [], stderr: [] }) {
3138
+ if (options.json) {
3139
+ io.stdout.push(JSON.stringify(result, null, 2));
3140
+ if ("code" in result) {
3141
+ return EXIT.USAGE;
3142
+ }
3143
+ return result.equal ? EXIT.OK : EXIT.INVALID;
2254
3144
  }
2255
- return input;
3145
+ if (options.quiet) {
3146
+ if ("code" in result) {
3147
+ return EXIT.USAGE;
3148
+ }
3149
+ return result.equal ? EXIT.OK : EXIT.INVALID;
3150
+ }
3151
+ if ("code" in result) {
3152
+ io.stderr.push(`code: ${result.code}`);
3153
+ io.stderr.push(`message: ${result.message}`);
3154
+ return EXIT.USAGE;
3155
+ }
3156
+ io.stdout.push(`equal: ${result.equal ? "yes" : "no"}`);
3157
+ return result.equal ? EXIT.OK : EXIT.INVALID;
2256
3158
  }
2257
- function printSanitize(result, options, io = { stdout: [], stderr: [] }) {
3159
+ function runCompare(type, valueA, valueB, options, io = { stdout: [], stderr: [] }) {
3160
+ if (!isPlatformDocumentType(type)) {
3161
+ io.stderr.push(`Unsupported compare type: ${type}`);
3162
+ return EXIT.USAGE;
3163
+ }
3164
+ if (!valueA || !valueB) {
3165
+ io.stderr.push("Missing values. Usage: compare <type> <valueA> <valueB>");
3166
+ return EXIT.USAGE;
3167
+ }
3168
+ const uf = options.uf?.toUpperCase();
3169
+ const platformOptions = uf ? { uf } : {};
3170
+ const result = compareRuntime(valueA, valueB, type, platformOptions);
3171
+ return printCompare(result, options, io);
3172
+ }
3173
+
3174
+ // src/commands/batch.ts
3175
+ import { batch, parseBatchCsv } from "@br-validators/core";
3176
+ function parseBatchLines(raw) {
3177
+ return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
3178
+ }
3179
+ function resolveBatchInputs(options) {
3180
+ if (options.lines === void 0) {
3181
+ return { ok: false, reason: "missing_input" };
3182
+ }
3183
+ let parsed;
3184
+ if (options.col !== void 0) {
3185
+ const csv = parseBatchCsv(options.lines, {
3186
+ col: options.col,
3187
+ delimiter: options.delimiter,
3188
+ skipHeader: options.skipHeader
3189
+ });
3190
+ if (!csv.ok) {
3191
+ return { ok: false, reason: "parse_error", message: csv.message };
3192
+ }
3193
+ parsed = csv.values;
3194
+ } else {
3195
+ parsed = parseBatchLines(options.lines);
3196
+ }
3197
+ if (parsed.length === 0) {
3198
+ return { ok: false, reason: "empty" };
3199
+ }
3200
+ if (options.limit !== void 0 && options.limit > 0) {
3201
+ return { ok: true, values: parsed.slice(0, options.limit) };
3202
+ }
3203
+ return { ok: true, values: parsed };
3204
+ }
3205
+ function printBatch(result, options, io = { stdout: [], stderr: [] }) {
2258
3206
  if (options.json) {
2259
3207
  io.stdout.push(JSON.stringify(result, null, 2));
2260
- return result.ok ? EXIT.OK : EXIT.INVALID;
3208
+ return result.summary.invalid === 0 ? EXIT.OK : EXIT.INVALID;
2261
3209
  }
2262
3210
  if (options.quiet) {
2263
- return result.ok ? EXIT.OK : EXIT.INVALID;
3211
+ return result.summary.invalid === 0 ? EXIT.OK : EXIT.INVALID;
2264
3212
  }
2265
- if (result.ok) {
2266
- io.stdout.push("valid: yes");
2267
- io.stdout.push(`value: ${result.value}`);
2268
- io.stdout.push(`fixes: ${result.fixes.join(", ")}`);
2269
- return EXIT.OK;
3213
+ io.stdout.push(`total: ${String(result.summary.total)}`);
3214
+ io.stdout.push(`valid: ${String(result.summary.valid)}`);
3215
+ io.stdout.push(`invalid: ${String(result.summary.invalid)}`);
3216
+ for (const entry of result.valid) {
3217
+ io.stdout.push(`ok[${String(entry.index)}]: ${entry.value}`);
2270
3218
  }
2271
- io.stderr.push("valid: no");
2272
- io.stderr.push(`code: ${result.code}`);
2273
- io.stderr.push(`message: ${result.message}`);
2274
- return EXIT.INVALID;
3219
+ for (const entry of result.invalid) {
3220
+ io.stderr.push(`fail[${String(entry.index)}]: ${entry.code} \u2014 ${entry.message}`);
3221
+ }
3222
+ return result.summary.invalid === 0 ? EXIT.OK : EXIT.INVALID;
2275
3223
  }
2276
- function runSanitize(type, value, options, io = { stdout: [], stderr: [] }) {
2277
- if (!isSanitizableType(type)) {
2278
- io.stderr.push(`Unsupported sanitize type: ${type}`);
3224
+ function runBatch(type, options, io = { stdout: [], stderr: [] }) {
3225
+ if (!isPlatformDocumentType(type)) {
3226
+ io.stderr.push(`Unsupported batch type: ${type}`);
2279
3227
  return EXIT.USAGE;
2280
3228
  }
2281
- const input = resolveInput21(value, options.file);
2282
- if (input === null) {
2283
- io.stderr.push("Missing value. Pass an argument or use --file.");
3229
+ const resolved = resolveBatchInputs(options);
3230
+ if (!resolved.ok) {
3231
+ if (resolved.reason === "missing_input") {
3232
+ io.stderr.push("Missing input. Pass --file <path> or pipe one value per line on stdin.");
3233
+ } else if (resolved.reason === "empty") {
3234
+ io.stderr.push("No values to validate.");
3235
+ } else {
3236
+ io.stderr.push(resolved.message);
3237
+ }
2284
3238
  return EXIT.USAGE;
2285
3239
  }
2286
3240
  const uf = options.uf?.toUpperCase();
2287
- const result = sanitize(input, type, uf ? { uf } : {});
2288
- return printSanitize(result, options, io);
3241
+ const platformOptions = uf ? { uf } : {};
3242
+ const result = batch(resolved.values, type, platformOptions);
3243
+ return printBatch(result, options, io);
3244
+ }
3245
+
3246
+ // src/commands/diff.ts
3247
+ import { diff } from "@br-validators/core";
3248
+ function printDiff(result, options, io = { stdout: [], stderr: [] }) {
3249
+ if (options.json) {
3250
+ io.stdout.push(JSON.stringify(result, null, 2));
3251
+ return result.changed ? EXIT.INVALID : EXIT.OK;
3252
+ }
3253
+ if (options.quiet) {
3254
+ return result.changed ? EXIT.INVALID : EXIT.OK;
3255
+ }
3256
+ io.stdout.push(`changed: ${result.changed ? "yes" : "no"}`);
3257
+ for (const field of result.fields) {
3258
+ io.stdout.push(`field: ${field.field}`);
3259
+ io.stdout.push(` a: ${field.a}`);
3260
+ io.stdout.push(` b: ${field.b}`);
3261
+ }
3262
+ return result.changed ? EXIT.INVALID : EXIT.OK;
3263
+ }
3264
+ function runDiff(type, valueA, valueB, options, io = { stdout: [], stderr: [] }) {
3265
+ if (!isPlatformDocumentType(type)) {
3266
+ io.stderr.push(`Unsupported diff type: ${type}`);
3267
+ return EXIT.USAGE;
3268
+ }
3269
+ if (!valueA || !valueB) {
3270
+ io.stderr.push("Missing values. Usage: diff <type> <valueA> <valueB>");
3271
+ return EXIT.USAGE;
3272
+ }
3273
+ const uf = options.uf?.toUpperCase();
3274
+ const platformOptions = uf ? { uf } : {};
3275
+ const result = diff(valueA, valueB, type, platformOptions);
3276
+ return printDiff(result, options, io);
2289
3277
  }
2290
3278
 
2291
3279
  // src/commands/generate.ts
@@ -2317,6 +3305,9 @@ function buildGenerateOptions(options) {
2317
3305
  if (options.masked) {
2318
3306
  core.masked = true;
2319
3307
  }
3308
+ if (options.stripped) {
3309
+ core.stripped = true;
3310
+ }
2320
3311
  if (options.seed !== void 0) {
2321
3312
  core.seed = options.seed;
2322
3313
  }
@@ -2360,10 +3351,14 @@ function listSupportedTypes(io = { stdout: [] }) {
2360
3351
  }
2361
3352
 
2362
3353
  // src/handlers.ts
3354
+ var nodeRequire = createRequire(import.meta.url);
3355
+ function readNodeFileSync(path, encoding) {
3356
+ const fs = nodeRequire("node:fs");
3357
+ return fs.readFileSync(path, encoding);
3358
+ }
2363
3359
  function readInputFile(path, io) {
2364
3360
  try {
2365
- const fsModule = __require("fs");
2366
- return fsModule.readFileSync(path, "utf8");
3361
+ return readNodeFileSync(path, "utf8");
2367
3362
  } catch {
2368
3363
  io.stderr.push(`Cannot read file: ${path}`);
2369
3364
  return null;
@@ -2840,6 +3835,93 @@ function handleSanitizeCli(type, value, opts, io = { stdout: [], stderr: [] }) {
2840
3835
  io
2841
3836
  );
2842
3837
  }
3838
+ function handleMaskCli(type, value, opts, io = { stdout: [], stderr: [] }) {
3839
+ let fileContent;
3840
+ if (opts.file) {
3841
+ const content = readInputFile(opts.file, io);
3842
+ if (content === null) {
3843
+ return EXIT.USAGE;
3844
+ }
3845
+ fileContent = content;
3846
+ }
3847
+ return runMask(
3848
+ type,
3849
+ value,
3850
+ {
3851
+ json: Boolean(opts.json),
3852
+ quiet: Boolean(opts.quiet),
3853
+ uf: opts.uf,
3854
+ file: fileContent
3855
+ },
3856
+ io
3857
+ );
3858
+ }
3859
+ function readStdinSync(io) {
3860
+ try {
3861
+ if (process.stdin.isTTY) {
3862
+ return null;
3863
+ }
3864
+ return readNodeFileSync(0, "utf8");
3865
+ } catch {
3866
+ io.stderr.push("Cannot read stdin.");
3867
+ return null;
3868
+ }
3869
+ }
3870
+ function handleCompareCli(type, valueA, valueB, opts, io = { stdout: [], stderr: [] }) {
3871
+ return runCompare(
3872
+ type,
3873
+ valueA,
3874
+ valueB,
3875
+ {
3876
+ json: Boolean(opts.json),
3877
+ quiet: Boolean(opts.quiet),
3878
+ uf: opts.uf
3879
+ },
3880
+ io
3881
+ );
3882
+ }
3883
+ function handleDiffCli(type, valueA, valueB, opts, io = { stdout: [], stderr: [] }) {
3884
+ return runDiff(
3885
+ type,
3886
+ valueA,
3887
+ valueB,
3888
+ {
3889
+ json: Boolean(opts.json),
3890
+ quiet: Boolean(opts.quiet),
3891
+ uf: opts.uf
3892
+ },
3893
+ io
3894
+ );
3895
+ }
3896
+ function handleBatchCli(type, opts, io = { stdout: [], stderr: [] }) {
3897
+ let lines;
3898
+ if (opts.file) {
3899
+ const content = readInputFile(opts.file, io);
3900
+ if (content === null) {
3901
+ return EXIT.USAGE;
3902
+ }
3903
+ lines = content;
3904
+ } else {
3905
+ const stdin = readStdinSync(io);
3906
+ if (stdin !== null) {
3907
+ lines = stdin;
3908
+ }
3909
+ }
3910
+ return runBatch(
3911
+ type,
3912
+ {
3913
+ json: Boolean(opts.json),
3914
+ quiet: Boolean(opts.quiet),
3915
+ uf: opts.uf,
3916
+ lines,
3917
+ limit: opts.limit,
3918
+ col: opts.col,
3919
+ delimiter: opts.delimiter,
3920
+ skipHeader: opts.skipHeader
3921
+ },
3922
+ io
3923
+ );
3924
+ }
2843
3925
  function handleGenerateCli(type, opts, io = { stdout: [], stderr: [] }) {
2844
3926
  return runGenerate(
2845
3927
  type,
@@ -2847,6 +3929,7 @@ function handleGenerateCli(type, opts, io = { stdout: [], stderr: [] }) {
2847
3929
  json: Boolean(opts.json),
2848
3930
  quiet: Boolean(opts.quiet),
2849
3931
  masked: Boolean(opts.masked),
3932
+ stripped: Boolean(opts.stripped),
2850
3933
  format: opts.format,
2851
3934
  seed: opts.seed,
2852
3935
  uf: opts.uf,
@@ -2898,6 +3981,39 @@ function handleReferenceSearchCli(command, query, opts, io = { stdout: [], stder
2898
3981
  io
2899
3982
  );
2900
3983
  }
3984
+ function handleReferenceValidateCli(command, value, opts, io = { stdout: [], stderr: [] }) {
3985
+ return runReferenceValidate(
3986
+ command,
3987
+ value,
3988
+ {
3989
+ json: Boolean(opts.json),
3990
+ verbose: Boolean(opts.verbose)
3991
+ },
3992
+ io
3993
+ );
3994
+ }
3995
+ function handleCstLookupCli(value, opts, io = { stdout: [], stderr: [] }) {
3996
+ return runCstLookup(value, {
3997
+ json: Boolean(opts.json),
3998
+ verbose: Boolean(opts.verbose),
3999
+ tax: opts.tax
4000
+ }, io);
4001
+ }
4002
+ function handleCstSearchCli(query, opts, io = { stdout: [], stderr: [] }) {
4003
+ return runCstSearch(query, {
4004
+ json: Boolean(opts.json),
4005
+ verbose: Boolean(opts.verbose),
4006
+ tax: opts.tax,
4007
+ limit: opts.limit
4008
+ }, io);
4009
+ }
4010
+ function handleCstValidateCli(value, opts, io = { stdout: [], stderr: [] }) {
4011
+ return runCstValidate(value, {
4012
+ json: Boolean(opts.json),
4013
+ verbose: Boolean(opts.verbose),
4014
+ tax: opts.tax
4015
+ }, io);
4016
+ }
2901
4017
  function handleIbgeLookupCli(value, opts, io = { stdout: [], stderr: [] }) {
2902
4018
  return runIbgeLookup(value, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
2903
4019
  }
@@ -2916,6 +4032,34 @@ function handleFeriadosListCli(opts, io = { stdout: [], stderr: [] }) {
2916
4032
  year: opts.year
2917
4033
  }, io);
2918
4034
  }
4035
+ function handleInssTabelaCli(opts, io = { stdout: [], stderr: [] }) {
4036
+ return runInssTabela({
4037
+ json: Boolean(opts.json),
4038
+ verbose: Boolean(opts.verbose),
4039
+ ano: opts.year
4040
+ }, io);
4041
+ }
4042
+ function handleInssCalcCli(value, opts, io = { stdout: [], stderr: [] }) {
4043
+ return runInssCalc(value, {
4044
+ json: Boolean(opts.json),
4045
+ verbose: Boolean(opts.verbose),
4046
+ ano: opts.year
4047
+ }, io);
4048
+ }
4049
+ function handleIrpfTabelaCli(opts, io = { stdout: [], stderr: [] }) {
4050
+ return runIrpfTabela({
4051
+ json: Boolean(opts.json),
4052
+ verbose: Boolean(opts.verbose),
4053
+ ano: opts.year
4054
+ }, io);
4055
+ }
4056
+ function handleIrpfCalcCli(value, opts, io = { stdout: [], stderr: [] }) {
4057
+ return runIrpfCalc(value, {
4058
+ json: Boolean(opts.json),
4059
+ verbose: Boolean(opts.verbose),
4060
+ ano: opts.year
4061
+ }, io);
4062
+ }
2919
4063
  function handleTseMunicipiosLookupCli(value, opts, io = { stdout: [], stderr: [] }) {
2920
4064
  return runTseMunicipiosLookup(value, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
2921
4065
  }
@@ -2925,6 +4069,50 @@ function handleCepFaixaCli(value, opts, io = { stdout: [], stderr: [] }) {
2925
4069
  function handleDddLookupCli(value, opts, io = { stdout: [], stderr: [] }) {
2926
4070
  return runDddLookup(value, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
2927
4071
  }
4072
+ function handleNfeCufLookupCli(value, opts, io = { stdout: [], stderr: [] }) {
4073
+ return runNfeCufLookup(value, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
4074
+ }
4075
+ function handleSelicCli(opts, io = { stdout: [], stderr: [] }) {
4076
+ return runSelicCommand({
4077
+ json: Boolean(opts.json),
4078
+ verbose: Boolean(opts.verbose),
4079
+ date: opts.date
4080
+ }, io);
4081
+ }
4082
+ function handleIssMunicipalListCli(opts, io = { stdout: [], stderr: [] }) {
4083
+ return runIssMunicipalList({
4084
+ json: Boolean(opts.json),
4085
+ verbose: Boolean(opts.verbose),
4086
+ uf: opts.uf,
4087
+ limit: opts.limit
4088
+ }, io);
4089
+ }
4090
+ function handleIssMunicipalLookupCli(codigo, opts, io = { stdout: [], stderr: [] }) {
4091
+ return runIssMunicipalLookup(codigo, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
4092
+ }
4093
+ function handleIssMunicipalSearchCli(query, opts, io = { stdout: [], stderr: [] }) {
4094
+ return runIssMunicipalSearch(query, {
4095
+ json: Boolean(opts.json),
4096
+ verbose: Boolean(opts.verbose),
4097
+ limit: opts.limit,
4098
+ uf: opts.uf
4099
+ }, io);
4100
+ }
4101
+ function handleIssMunicipalResolveCli(uf, nome, opts, io = { stdout: [], stderr: [] }) {
4102
+ return runIssMunicipalResolve(uf, nome, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
4103
+ }
4104
+ function handlePtaxLookupCli(moeda, data, opts, io = { stdout: [], stderr: [] }) {
4105
+ return runPtaxLookup(moeda, data, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
4106
+ }
4107
+ function handlePtaxHistoricoCli(moeda, desde, ate, opts, io = { stdout: [], stderr: [] }) {
4108
+ return runPtaxHistorico(
4109
+ moeda,
4110
+ desde,
4111
+ ate,
4112
+ { json: Boolean(opts.json), verbose: Boolean(opts.verbose) },
4113
+ io
4114
+ );
4115
+ }
2928
4116
  function writeCliIo(io) {
2929
4117
  for (const line of io.stdout) console.log(line);
2930
4118
  for (const line of io.stderr) console.error(line);
@@ -2940,6 +4128,13 @@ function registerReferenceLookupCommands(program) {
2940
4128
  process.exitCode = handleReferenceLookupCli(command, codigo, opts, io);
2941
4129
  writeCliIo(io);
2942
4130
  });
4131
+ if (REFERENCE_VALIDATE_COMMANDS.includes(command)) {
4132
+ 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) => {
4133
+ const io = { stdout: [], stderr: [] };
4134
+ process.exitCode = handleReferenceValidateCli(command, codigo, opts, io);
4135
+ writeCliIo(io);
4136
+ });
4137
+ }
2943
4138
  if (REFERENCE_SEARCH_COMMANDS.includes(command)) {
2944
4139
  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
4140
  const io = { stdout: [], stderr: [] };
@@ -3194,6 +4389,28 @@ function createProgram() {
3194
4389
  process.exitCode = handleFeriadosListCli(opts, io);
3195
4390
  writeCliIo(io);
3196
4391
  });
4392
+ const inss = program.command("inss").description("INSS employee contribution progressive table \u2014 offline");
4393
+ 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) => {
4394
+ const io = { stdout: [], stderr: [] };
4395
+ process.exitCode = handleInssTabelaCli({ ...opts, year: opts.ano ?? opts.year }, io);
4396
+ writeCliIo(io);
4397
+ });
4398
+ 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) => {
4399
+ const io = { stdout: [], stderr: [] };
4400
+ process.exitCode = handleInssCalcCli(salario, { ...opts, year: opts.ano ?? opts.year }, io);
4401
+ writeCliIo(io);
4402
+ });
4403
+ const irpf = program.command("irpf").description("RFB IRPF progressive monthly table \u2014 offline");
4404
+ 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) => {
4405
+ const io = { stdout: [], stderr: [] };
4406
+ process.exitCode = handleIrpfTabelaCli({ ...opts, year: opts.ano ?? opts.year }, io);
4407
+ writeCliIo(io);
4408
+ });
4409
+ 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) => {
4410
+ const io = { stdout: [], stderr: [] };
4411
+ process.exitCode = handleIrpfCalcCli(base, { ...opts, year: opts.ano ?? opts.year }, io);
4412
+ writeCliIo(io);
4413
+ });
3197
4414
  const tseMunicipios = program.command("tse-municipios").description("TSE \u2194 IBGE municipality cross-walk \u2014 offline lookup");
3198
4415
  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
4416
  const io = { stdout: [], stderr: [] };
@@ -3206,6 +4423,68 @@ function createProgram() {
3206
4423
  process.exitCode = handleDddLookupCli(code, opts, io);
3207
4424
  writeCliIo(io);
3208
4425
  });
4426
+ const nfeCuf = program.command("nfe-cuf").description("NF-e cUF \u2014 SEFAZ federative unit codes (offline)");
4427
+ 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) => {
4428
+ const io = { stdout: [], stderr: [] };
4429
+ process.exitCode = handleNfeCufLookupCli(code, opts, io);
4430
+ writeCliIo(io);
4431
+ });
4432
+ const selic = program.command("selic").description("Bacen SELIC meta (SGS 432) \u2014 offline embedded series");
4433
+ 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) => {
4434
+ const io = { stdout: [], stderr: [] };
4435
+ process.exitCode = handleSelicCli(opts, io);
4436
+ writeCliIo(io);
4437
+ });
4438
+ const issMunicipal = program.command("iss-municipal").description("Municipal ISS al\xEDquotas \u2014 partial embed (estimation only, not NFSe)");
4439
+ issMunicipal.command("list").description("List embedded municipalities for a UF").requiredOption("--uf <uf>", "UF sigla (2 letters)").option("--json", "JSON output").option("--verbose", "Include dataset capture date in JSON responses").option("--limit <n>", "Maximum rows", (value) => Number(value)).action((opts) => {
4440
+ const io = { stdout: [], stderr: [] };
4441
+ process.exitCode = handleIssMunicipalListCli(opts, io);
4442
+ writeCliIo(io);
4443
+ });
4444
+ 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) => {
4445
+ const io = { stdout: [], stderr: [] };
4446
+ process.exitCode = handleIssMunicipalLookupCli(codigoIbge, opts, io);
4447
+ writeCliIo(io);
4448
+ });
4449
+ 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) => {
4450
+ const io = { stdout: [], stderr: [] };
4451
+ process.exitCode = handleIssMunicipalResolveCli(uf, nome, opts, io);
4452
+ writeCliIo(io);
4453
+ });
4454
+ issMunicipal.command("search").description("Search embedded municipalities by name, UF, or IBGE code fragment").argument("<query>", "Search query").option("--uf <uf>", "Scope search to a UF").option("--json", "JSON output").option("--verbose", "Include dataset capture date in JSON responses").option("--limit <n>", "Maximum rows", (value) => Number(value)).action((query, opts) => {
4455
+ const io = { stdout: [], stderr: [] };
4456
+ process.exitCode = handleIssMunicipalSearchCli(query, opts, io);
4457
+ writeCliIo(io);
4458
+ });
4459
+ const ptax = program.command("ptax").description("Bacen PTAX Fechamento \u2014 offline embedded rates");
4460
+ 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) => {
4461
+ const io = { stdout: [], stderr: [] };
4462
+ process.exitCode = handlePtaxLookupCli(moeda, data, opts, io);
4463
+ writeCliIo(io);
4464
+ });
4465
+ ptax.command("historico").description("List embedded Fechamento PTAX rows for a currency and date range").argument("<moeda>", "ISO 4217 currency code (e.g. USD)").argument("<desde>", "Range start \u2014 YYYY-MM-DD or MM-DD-YYYY").argument("<ate>", "Range end \u2014 YYYY-MM-DD or MM-DD-YYYY").option("--json", "JSON output").option("--verbose", "Include capturadoEm and janelaDiasUteis in JSON responses").action(
4466
+ (moeda, desde, ate, opts) => {
4467
+ const io = { stdout: [], stderr: [] };
4468
+ process.exitCode = handlePtaxHistoricoCli(moeda, desde, ate, opts, io);
4469
+ writeCliIo(io);
4470
+ }
4471
+ );
4472
+ const cst = program.command("cst").description("RFB SPED CST \u2014 offline ICMS, IPI, PIS, COFINS tables");
4473
+ 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) => {
4474
+ const io = { stdout: [], stderr: [] };
4475
+ process.exitCode = handleCstLookupCli(codigo, opts, io);
4476
+ writeCliIo(io);
4477
+ });
4478
+ 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) => {
4479
+ const io = { stdout: [], stderr: [] };
4480
+ process.exitCode = handleCstSearchCli(query, opts, io);
4481
+ writeCliIo(io);
4482
+ });
4483
+ 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) => {
4484
+ const io = { stdout: [], stderr: [] };
4485
+ process.exitCode = handleCstValidateCli(codigo, opts, io);
4486
+ writeCliIo(io);
4487
+ });
3209
4488
  registerReferenceLookupCommands(program);
3210
4489
  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
4490
  const io = { stdout: [], stderr: [] };
@@ -3217,11 +4496,31 @@ function createProgram() {
3217
4496
  process.exitCode = handleSanitizeCli(type, value, opts, io);
3218
4497
  writeCliIo(io);
3219
4498
  });
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) => {
4499
+ 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) => {
4500
+ const io = { stdout: [], stderr: [] };
4501
+ process.exitCode = handleMaskCli(type, value, opts, io);
4502
+ writeCliIo(io);
4503
+ });
4504
+ 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
4505
  const io = { stdout: [], stderr: [] };
3222
4506
  process.exitCode = handleGenerateCli(type, opts, io);
3223
4507
  writeCliIo(io);
3224
4508
  });
4509
+ 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) => {
4510
+ const io = { stdout: [], stderr: [] };
4511
+ process.exitCode = handleCompareCli(type, valueA, valueB.join(" ") || void 0, opts, io);
4512
+ writeCliIo(io);
4513
+ });
4514
+ 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, or CSV with --col)").option("--col <name>", "CSV column name or zero-based index (requires --file)").option("--delimiter <char>", "CSV delimiter (default: comma)").option("--skip-header", "Treat first CSV row as header (default: true)", true).option("--no-skip-header", "Parse CSV without header row").option("--limit <n>", "Max number of values to process", (v) => Number(v)).action((type, opts) => {
4515
+ const io = { stdout: [], stderr: [] };
4516
+ process.exitCode = handleBatchCli(type, opts, io);
4517
+ writeCliIo(io);
4518
+ });
4519
+ 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) => {
4520
+ const io = { stdout: [], stderr: [] };
4521
+ process.exitCode = handleDiffCli(type, valueA, valueB.join(" ") || void 0, opts, io);
4522
+ writeCliIo(io);
4523
+ });
3225
4524
  return program;
3226
4525
  }
3227
4526
  function run(argv) {