@br-validators/cli 1.9.0 → 1.10.0-data.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/open-data-brazil/br-validators/blob/main/LICENSE)
5
5
  [![GitHub release](https://img.shields.io/github/v/release/open-data-brazil/br-validators)](https://github.com/open-data-brazil/br-validators/releases)
6
6
 
7
- Terminal CLI for all Brazilian document validators in [@br-validators/core](https://www.npmjs.com/package/@br-validators/core) **v1.9.0**.
7
+ Terminal CLI for all Brazilian document validators in [@br-validators/core](https://www.npmjs.com/package/@br-validators/core) **v1.10.0**.
8
8
 
9
9
  **Repo:** [github.com/open-data-brazil/br-validators](https://github.com/open-data-brazil/br-validators)
10
10
 
@@ -78,7 +78,7 @@ br-validators ie validate P-01100424.3/002 --uf SP # SP produtor rural (auto-d
78
78
  | `sanitize <type> [value]` | ETL fixes + validate; `--uf` for `inscricao-estadual` |
79
79
  | `mask <type> [value]` | Unified display mask; `--uf` for IE / RG |
80
80
  | `compare <type> <valueA> <valueB>` | Normalized equality; `--uf` for IE / RG / título |
81
- | `batch <type>` | Bulk validate (stdin or `--file`); `--uf`, `--limit` |
81
+ | `batch <type>` | Bulk validate (stdin or `--file`, or CSV with `--col`); `--delimiter`, `--skip-header`; `--uf`, `--limit` |
82
82
  | `diff <type> <valueA> <valueB>` | Field-level diff; `--uf` for IE / RG / título |
83
83
  | `generate <type>` | Synthetic test document; `--seed`, `--masked`, `--format` |
84
84
 
@@ -91,6 +91,9 @@ br-validators mask cpf 12345678909 --json
91
91
  br-validators mask inscricao-estadual 110042490114 --uf SP --json
92
92
  br-validators compare cpf '123.456.789-09' 12345678909 --json
93
93
  br-validators batch cpf --file values.txt --json
94
+ br-validators batch cpf --file payroll.csv --col cpf --json
95
+ br-validators sanitize pix ' PIX@BCB.GOV.BR ' --json
96
+ br-validators csosn lookup 102 --json
94
97
  br-validators diff cpf 12345678909 12345678901 --json
95
98
  br-validators generate cpf --seed 42 --masked --json
96
99
  br-validators generate cnpj --format alphanumeric --seed 7 --json
@@ -127,6 +130,17 @@ br-validators cbo lookup 212405 --json
127
130
  br-validators cbo search analista --limit 5
128
131
  ```
129
132
 
133
+ ### Fiscal reference (33o–34)
134
+
135
+ ```bash
136
+ br-validators iss-municipal lookup 3550308 --json
137
+ br-validators iss-municipal list --uf SP --limit 10 --json
138
+ br-validators iss-municipal search campinas --uf SP --limit 5 --json
139
+ br-validators iss-municipal resolve SP "São Paulo" --verbose
140
+ ```
141
+
142
+ Partial embed (~500 municipalities). **Estimation / quoting only — not NFSe emission.**
143
+
130
144
  ### Geography & calendar (27d)
131
145
 
132
146
  ```bash
@@ -145,8 +159,22 @@ br-validators ddd lookup 11 --verbose
145
159
  br-validators moedas lookup BRL --json
146
160
  br-validators paises-bacen lookup 1058 --verbose
147
161
  br-validators incoterms lookup FOB --json
162
+ br-validators ptax lookup USD --verbose
163
+ br-validators ptax lookup USD 2026-06-20 --json --verbose
164
+ br-validators ptax historico USD 2026-06-01 2026-06-26 --json --verbose
148
165
  ```
149
166
 
167
+ **PTAX:** offline embed — last **90 business days** (`janelaDiasUteis: 90`; not live Bacen). `--verbose` prints `dataReferencia`, `isStale`, and `warning` when stale. `ptax historico` lists all embedded rows in a date range.
168
+
169
+ ### Finance (SELIC)
170
+
171
+ ```bash
172
+ br-validators selic --verbose
173
+ br-validators selic --date 2026-06-18 --json --verbose
174
+ ```
175
+
176
+ `--verbose` includes `dataReferencia`, `isStale`, and dataset `capturadoEm` (same pattern as PTAX).
177
+
150
178
  ### Logistics (26e)
151
179
 
152
180
  ```bash
@@ -169,9 +197,10 @@ br-validators aeroportos lookup SBGR --json
169
197
  |------|-------------|
170
198
  | `--json` | JSON output |
171
199
  | `--quiet` / `-q` | Exit code only (CI) |
200
+ | `--verbose` | PTAX / SELIC: `dataReferencia`, `isStale`, `warning`; other lookups: dataset metadata |
172
201
  | `--file` / `-f` | Read value from file |
173
202
  | `--source` | Print official source URL (per-type) |
174
- | `--uf` | Required for IE / detect / sanitize IE; optional filter for `ibge list municipios` |
203
+ | `--uf` | Required for IE / detect / sanitize IE; filter for `ibge list municipios`, `iss-municipal list`, and `iss-municipal search` |
175
204
 
176
205
  ### CI
177
206
 
package/dist/index.js CHANGED
@@ -150,6 +150,10 @@ import {
150
150
  CFOP_DATA_VERSION,
151
151
  lookupCfopPorCodigo
152
152
  } from "@br-validators/core/cfop";
153
+ import {
154
+ CSOSN_DATA_VERSION,
155
+ lookupCsosnPorCodigo
156
+ } from "@br-validators/core/csosn";
153
157
  import { NCM_DATA_VERSION, lookupNcmPorCodigo } from "@br-validators/core/ncm";
154
158
  import { CBO_DATA_VERSION, lookupCboPorCodigo } from "@br-validators/core/cbo";
155
159
  import {
@@ -162,6 +166,7 @@ var REFERENCE_LOOKUP_COMMANDS = [
162
166
  "cest",
163
167
  "cnae",
164
168
  "cfop",
169
+ "csosn",
165
170
  "ncm",
166
171
  "cbo",
167
172
  "moedas",
@@ -170,7 +175,7 @@ var REFERENCE_LOOKUP_COMMANDS = [
170
175
  "portos",
171
176
  "aeroportos"
172
177
  ];
173
- var REFERENCE_SEARCH_COMMANDS = ["cnae", "cfop", "ncm", "cbo"];
178
+ var REFERENCE_SEARCH_COMMANDS = ["cnae", "cfop", "csosn", "ncm", "cbo"];
174
179
  function lookupAeroporto(input) {
175
180
  const normalized = input.trim().toUpperCase();
176
181
  if (normalized.length === 0) {
@@ -240,6 +245,17 @@ var REFERENCE_LOOKUP_MODULES = {
240
245
  return `${row.codigo} \u2014 ${row.descricao}`;
241
246
  }
242
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),
254
+ formatHuman: (result) => {
255
+ const row = result;
256
+ return `${row.codigo} \u2014 ${row.descricao}`;
257
+ }
258
+ },
243
259
  ncm: {
244
260
  command: "ncm",
245
261
  description: "Siscomex NCM Mercosur codes \u2014 offline lookup",
@@ -380,6 +396,7 @@ function runReferenceLookup(command, value, options, io = { stdout: [], stderr:
380
396
  import { searchCbo } from "@br-validators/core/cbo";
381
397
  import { searchCnaes } from "@br-validators/core/cnaes";
382
398
  import { searchCfop } from "@br-validators/core/cfop";
399
+ import { searchCsosn } from "@br-validators/core/csosn";
383
400
  import { searchNcm } from "@br-validators/core/ncm";
384
401
  function runSearch(command, query, limit) {
385
402
  switch (command) {
@@ -387,6 +404,8 @@ function runSearch(command, query, limit) {
387
404
  return searchCnaes(query, { limit });
388
405
  case "cfop":
389
406
  return searchCfop(query, { limit });
407
+ case "csosn":
408
+ return searchCsosn(query, { limit });
390
409
  case "ncm":
391
410
  return searchNcm(query, { limit });
392
411
  case "cbo":
@@ -444,15 +463,18 @@ function runReferenceSearch(command, query, options, io = { stdout: [], stderr:
444
463
 
445
464
  // src/commands/reference-lookup/validate.ts
446
465
  import { validateCfop } from "@br-validators/core/cfop";
466
+ import { validateCsosn } from "@br-validators/core/csosn";
447
467
  import { validateNcm } from "@br-validators/core/ncm";
448
- var REFERENCE_VALIDATE_COMMANDS = ["ncm", "cfop"];
468
+ var REFERENCE_VALIDATE_COMMANDS = ["ncm", "cfop", "csosn"];
449
469
  var VALIDATORS = {
450
470
  ncm: validateNcm,
451
- cfop: validateCfop
471
+ cfop: validateCfop,
472
+ csosn: validateCsosn
452
473
  };
453
474
  var CAPTURED_AT = {
454
475
  ncm: "ncm",
455
- cfop: "cfop"
476
+ cfop: "cfop",
477
+ csosn: "csosn"
456
478
  };
457
479
  function emitFailure(result, options, io) {
458
480
  if (options.json) {
@@ -1297,12 +1319,19 @@ function runSelicCommand(options, io = { stdout: [], stderr: [] }) {
1297
1319
 
1298
1320
  // src/commands/iss-municipal/index.ts
1299
1321
  import {
1300
- getIssMunicipalPorIbge,
1322
+ getIssMunicipalPorUf,
1301
1323
  getIssMunicipalPorUfMunicipio,
1302
1324
  ISS_MUNICIPAL_DATA_VERSION,
1303
1325
  ISS_MUNICIPAL_ESTIMATION_WARNING,
1326
+ lookupIssMunicipalPorIbge,
1304
1327
  searchIssMunicipal
1305
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
+ }
1306
1335
  function formatIssMunicipalHuman(row) {
1307
1336
  return `${row.nome}/${row.uf} \u2014 ISS ${String(row.aliquotaMin)}%\u2013${String(row.aliquotaMax)}%`;
1308
1337
  }
@@ -1318,7 +1347,7 @@ function runIssMunicipalLookup(codigo, options, io = { stdout: [], stderr: [] })
1318
1347
  io.stderr.push("Missing IBGE code. Usage: br-validators iss-municipal lookup <codigoIbge>");
1319
1348
  return EXIT.USAGE;
1320
1349
  }
1321
- const result = getIssMunicipalPorIbge(trimmed);
1350
+ const result = lookupIssMunicipalPorIbge(trimmed);
1322
1351
  if (result === void 0) {
1323
1352
  io.stderr.push(`ISS municipal row not found for IBGE code ${trimmed}`);
1324
1353
  return EXIT.INVALID;
@@ -1336,6 +1365,7 @@ function runIssMunicipalLookup(codigo, options, io = { stdout: [], stderr: [] })
1336
1365
  return EXIT.OK;
1337
1366
  }
1338
1367
  io.stdout.push(formatIssMunicipalHuman(result));
1368
+ io.stdout.push(`fonte: ${result.fonte}`);
1339
1369
  io.stdout.push(`warning: ${result.warning}`);
1340
1370
  if (options.verbose) {
1341
1371
  io.stdout.push(`leiUrl: ${result.leiUrl}`);
@@ -1344,13 +1374,49 @@ function runIssMunicipalLookup(codigo, options, io = { stdout: [], stderr: [] })
1344
1374
  }
1345
1375
  return EXIT.OK;
1346
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
+ }
1347
1410
  function runIssMunicipalSearch(query, options, io = { stdout: [], stderr: [] }) {
1348
1411
  const trimmed = query?.trim() ?? "";
1349
1412
  if (trimmed.length === 0) {
1350
1413
  io.stderr.push("Missing query. Usage: br-validators iss-municipal search <query>");
1351
1414
  return EXIT.USAGE;
1352
1415
  }
1353
- const rows = searchIssMunicipal(trimmed, { limit: options.limit });
1416
+ const rows = searchIssMunicipal(trimmed, {
1417
+ limit: options.limit,
1418
+ ...options.uf !== void 0 && options.uf.trim().length > 0 ? { uf: options.uf } : {}
1419
+ });
1354
1420
  emitDisclaimer(options, io);
1355
1421
  if (options.json) {
1356
1422
  io.stdout.push(JSON.stringify({ ok: true, results: rows }, null, 2));
@@ -1429,6 +1495,73 @@ function runPtaxLookup(moeda, data, options, io = { stdout: [], stderr: [] }) {
1429
1495
  return runPtaxLookupCommand(moeda.trim(), data?.trim(), options, io);
1430
1496
  }
1431
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
+
1432
1565
  // src/commands/brcode.ts
1433
1566
  import {
1434
1567
  BRCODE_OFFICIAL_SOURCE_URL,
@@ -2900,7 +3033,8 @@ var SANITIZABLE_TYPES = [
2900
3033
  "cartao-credito",
2901
3034
  "ean",
2902
3035
  "inscricao-estadual",
2903
- "inscricao-estadual-produtor-rural"
3036
+ "inscricao-estadual-produtor-rural",
3037
+ "pix"
2904
3038
  ];
2905
3039
  function isSanitizableType(type) {
2906
3040
  return SANITIZABLE_TYPES.includes(type);
@@ -3038,19 +3172,35 @@ function runCompare(type, valueA, valueB, options, io = { stdout: [], stderr: []
3038
3172
  }
3039
3173
 
3040
3174
  // src/commands/batch.ts
3041
- import { batch } from "@br-validators/core";
3175
+ import { batch, parseBatchCsv } from "@br-validators/core";
3042
3176
  function parseBatchLines(raw) {
3043
3177
  return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
3044
3178
  }
3045
3179
  function resolveBatchInputs(options) {
3046
3180
  if (options.lines === void 0) {
3047
- return null;
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" };
3048
3199
  }
3049
- const parsed = parseBatchLines(options.lines);
3050
3200
  if (options.limit !== void 0 && options.limit > 0) {
3051
- return parsed.slice(0, options.limit);
3201
+ return { ok: true, values: parsed.slice(0, options.limit) };
3052
3202
  }
3053
- return parsed;
3203
+ return { ok: true, values: parsed };
3054
3204
  }
3055
3205
  function printBatch(result, options, io = { stdout: [], stderr: [] }) {
3056
3206
  if (options.json) {
@@ -3076,18 +3226,20 @@ function runBatch(type, options, io = { stdout: [], stderr: [] }) {
3076
3226
  io.stderr.push(`Unsupported batch type: ${type}`);
3077
3227
  return EXIT.USAGE;
3078
3228
  }
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.");
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
+ }
3086
3238
  return EXIT.USAGE;
3087
3239
  }
3088
3240
  const uf = options.uf?.toUpperCase();
3089
3241
  const platformOptions = uf ? { uf } : {};
3090
- const result = batch(inputs, type, platformOptions);
3242
+ const result = batch(resolved.values, type, platformOptions);
3091
3243
  return printBatch(result, options, io);
3092
3244
  }
3093
3245
 
@@ -3762,7 +3914,10 @@ function handleBatchCli(type, opts, io = { stdout: [], stderr: [] }) {
3762
3914
  quiet: Boolean(opts.quiet),
3763
3915
  uf: opts.uf,
3764
3916
  lines,
3765
- limit: opts.limit
3917
+ limit: opts.limit,
3918
+ col: opts.col,
3919
+ delimiter: opts.delimiter,
3920
+ skipHeader: opts.skipHeader
3766
3921
  },
3767
3922
  io
3768
3923
  );
@@ -3924,6 +4079,14 @@ function handleSelicCli(opts, io = { stdout: [], stderr: [] }) {
3924
4079
  date: opts.date
3925
4080
  }, io);
3926
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
+ }
3927
4090
  function handleIssMunicipalLookupCli(codigo, opts, io = { stdout: [], stderr: [] }) {
3928
4091
  return runIssMunicipalLookup(codigo, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
3929
4092
  }
@@ -3931,7 +4094,8 @@ function handleIssMunicipalSearchCli(query, opts, io = { stdout: [], stderr: []
3931
4094
  return runIssMunicipalSearch(query, {
3932
4095
  json: Boolean(opts.json),
3933
4096
  verbose: Boolean(opts.verbose),
3934
- limit: opts.limit
4097
+ limit: opts.limit,
4098
+ uf: opts.uf
3935
4099
  }, io);
3936
4100
  }
3937
4101
  function handleIssMunicipalResolveCli(uf, nome, opts, io = { stdout: [], stderr: [] }) {
@@ -3940,6 +4104,15 @@ function handleIssMunicipalResolveCli(uf, nome, opts, io = { stdout: [], stderr:
3940
4104
  function handlePtaxLookupCli(moeda, data, opts, io = { stdout: [], stderr: [] }) {
3941
4105
  return runPtaxLookup(moeda, data, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
3942
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
+ }
3943
4116
  function writeCliIo(io) {
3944
4117
  for (const line of io.stdout) console.log(line);
3945
4118
  for (const line of io.stderr) console.error(line);
@@ -4263,6 +4436,11 @@ function createProgram() {
4263
4436
  writeCliIo(io);
4264
4437
  });
4265
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
+ });
4266
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) => {
4267
4445
  const io = { stdout: [], stderr: [] };
4268
4446
  process.exitCode = handleIssMunicipalLookupCli(codigoIbge, opts, io);
@@ -4273,7 +4451,7 @@ function createProgram() {
4273
4451
  process.exitCode = handleIssMunicipalResolveCli(uf, nome, opts, io);
4274
4452
  writeCliIo(io);
4275
4453
  });
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) => {
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) => {
4277
4455
  const io = { stdout: [], stderr: [] };
4278
4456
  process.exitCode = handleIssMunicipalSearchCli(query, opts, io);
4279
4457
  writeCliIo(io);
@@ -4284,6 +4462,13 @@ function createProgram() {
4284
4462
  process.exitCode = handlePtaxLookupCli(moeda, data, opts, io);
4285
4463
  writeCliIo(io);
4286
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
+ );
4287
4472
  const cst = program.command("cst").description("RFB SPED CST \u2014 offline ICMS, IPI, PIS, COFINS tables");
4288
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) => {
4289
4474
  const io = { stdout: [], stderr: [] };
@@ -4326,7 +4511,7 @@ function createProgram() {
4326
4511
  process.exitCode = handleCompareCli(type, valueA, valueB.join(" ") || void 0, opts, io);
4327
4512
  writeCliIo(io);
4328
4513
  });
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) => {
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) => {
4330
4515
  const io = { stdout: [], stderr: [] };
4331
4516
  process.exitCode = handleBatchCli(type, opts, io);
4332
4517
  writeCliIo(io);
@@ -42,6 +42,10 @@ import {
42
42
  CFOP_DATA_VERSION,
43
43
  lookupCfopPorCodigo
44
44
  } from "@br-validators/core/cfop";
45
+ import {
46
+ CSOSN_DATA_VERSION,
47
+ lookupCsosnPorCodigo
48
+ } from "@br-validators/core/csosn";
45
49
  import { NCM_DATA_VERSION, lookupNcmPorCodigo } from "@br-validators/core/ncm";
46
50
  import { CBO_DATA_VERSION, lookupCboPorCodigo } from "@br-validators/core/cbo";
47
51
  import {
@@ -54,6 +58,7 @@ var REFERENCE_LOOKUP_COMMANDS = [
54
58
  "cest",
55
59
  "cnae",
56
60
  "cfop",
61
+ "csosn",
57
62
  "ncm",
58
63
  "cbo",
59
64
  "moedas",
@@ -62,7 +67,7 @@ var REFERENCE_LOOKUP_COMMANDS = [
62
67
  "portos",
63
68
  "aeroportos"
64
69
  ];
65
- var REFERENCE_SEARCH_COMMANDS = ["cnae", "cfop", "ncm", "cbo"];
70
+ var REFERENCE_SEARCH_COMMANDS = ["cnae", "cfop", "csosn", "ncm", "cbo"];
66
71
  function lookupAeroporto(input) {
67
72
  const normalized = input.trim().toUpperCase();
68
73
  if (normalized.length === 0) {
@@ -132,6 +137,17 @@ var REFERENCE_LOOKUP_MODULES = {
132
137
  return `${row.codigo} \u2014 ${row.descricao}`;
133
138
  }
134
139
  },
140
+ csosn: {
141
+ command: "csosn",
142
+ description: "CONFAZ CSOSN Simples Nacional \u2014 offline lookup",
143
+ resultKey: "csosn",
144
+ capturadoEm: CSOSN_DATA_VERSION.capturadoEm,
145
+ lookup: (input) => lookupCsosnPorCodigo(input),
146
+ formatHuman: (result) => {
147
+ const row = result;
148
+ return `${row.codigo} \u2014 ${row.descricao}`;
149
+ }
150
+ },
135
151
  ncm: {
136
152
  command: "ncm",
137
153
  description: "Siscomex NCM Mercosur codes \u2014 offline lookup",
@@ -221,15 +237,18 @@ function isReferenceSearchCommand(value) {
221
237
 
222
238
  // src/commands/reference-lookup/validate.ts
223
239
  import { validateCfop } from "@br-validators/core/cfop";
240
+ import { validateCsosn } from "@br-validators/core/csosn";
224
241
  import { validateNcm } from "@br-validators/core/ncm";
225
- var REFERENCE_VALIDATE_COMMANDS = ["ncm", "cfop"];
242
+ var REFERENCE_VALIDATE_COMMANDS = ["ncm", "cfop", "csosn"];
226
243
  var VALIDATORS = {
227
244
  ncm: validateNcm,
228
- cfop: validateCfop
245
+ cfop: validateCfop,
246
+ csosn: validateCsosn
229
247
  };
230
248
  var CAPTURED_AT = {
231
249
  ncm: "ncm",
232
- cfop: "cfop"
250
+ cfop: "cfop",
251
+ csosn: "csosn"
233
252
  };
234
253
  function emitFailure(result, options, io) {
235
254
  if (options.json) {
@@ -438,6 +457,7 @@ function runReferenceLookup(command, value, options, io = { stdout: [], stderr:
438
457
  import { searchCbo } from "@br-validators/core/cbo";
439
458
  import { searchCnaes } from "@br-validators/core/cnaes";
440
459
  import { searchCfop } from "@br-validators/core/cfop";
460
+ import { searchCsosn } from "@br-validators/core/csosn";
441
461
  import { searchNcm } from "@br-validators/core/ncm";
442
462
  function runSearch(command, query, limit) {
443
463
  switch (command) {
@@ -445,6 +465,8 @@ function runSearch(command, query, limit) {
445
465
  return searchCnaes(query, { limit });
446
466
  case "cfop":
447
467
  return searchCfop(query, { limit });
468
+ case "csosn":
469
+ return searchCsosn(query, { limit });
448
470
  case "ncm":
449
471
  return searchNcm(query, { limit });
450
472
  case "cbo":
@@ -1292,12 +1314,19 @@ function runSelicCommand(options, io = { stdout: [], stderr: [] }) {
1292
1314
 
1293
1315
  // src/commands/iss-municipal/index.ts
1294
1316
  import {
1295
- getIssMunicipalPorIbge,
1317
+ getIssMunicipalPorUf,
1296
1318
  getIssMunicipalPorUfMunicipio,
1297
1319
  ISS_MUNICIPAL_DATA_VERSION,
1298
1320
  ISS_MUNICIPAL_ESTIMATION_WARNING,
1321
+ lookupIssMunicipalPorIbge,
1299
1322
  searchIssMunicipal
1300
1323
  } from "@br-validators/core/iss-municipal";
1324
+ function sliceRows2(rows, limit) {
1325
+ if (limit === void 0 || !Number.isFinite(limit) || limit <= 0) {
1326
+ return rows;
1327
+ }
1328
+ return rows.slice(0, limit);
1329
+ }
1301
1330
  function formatIssMunicipalHuman(row) {
1302
1331
  return `${row.nome}/${row.uf} \u2014 ISS ${String(row.aliquotaMin)}%\u2013${String(row.aliquotaMax)}%`;
1303
1332
  }
@@ -1313,7 +1342,7 @@ function runIssMunicipalLookup(codigo, options, io = { stdout: [], stderr: [] })
1313
1342
  io.stderr.push("Missing IBGE code. Usage: br-validators iss-municipal lookup <codigoIbge>");
1314
1343
  return EXIT.USAGE;
1315
1344
  }
1316
- const result = getIssMunicipalPorIbge(trimmed);
1345
+ const result = lookupIssMunicipalPorIbge(trimmed);
1317
1346
  if (result === void 0) {
1318
1347
  io.stderr.push(`ISS municipal row not found for IBGE code ${trimmed}`);
1319
1348
  return EXIT.INVALID;
@@ -1331,6 +1360,7 @@ function runIssMunicipalLookup(codigo, options, io = { stdout: [], stderr: [] })
1331
1360
  return EXIT.OK;
1332
1361
  }
1333
1362
  io.stdout.push(formatIssMunicipalHuman(result));
1363
+ io.stdout.push(`fonte: ${result.fonte}`);
1334
1364
  io.stdout.push(`warning: ${result.warning}`);
1335
1365
  if (options.verbose) {
1336
1366
  io.stdout.push(`leiUrl: ${result.leiUrl}`);
@@ -1339,13 +1369,49 @@ function runIssMunicipalLookup(codigo, options, io = { stdout: [], stderr: [] })
1339
1369
  }
1340
1370
  return EXIT.OK;
1341
1371
  }
1372
+ function runIssMunicipalList(options, io = { stdout: [], stderr: [] }) {
1373
+ const uf = options.uf?.trim() ?? "";
1374
+ if (uf.length === 0) {
1375
+ io.stderr.push("Missing UF. Usage: br-validators iss-municipal list --uf <UF>");
1376
+ return EXIT.USAGE;
1377
+ }
1378
+ const rows = sliceRows2(getIssMunicipalPorUf(uf), options.limit);
1379
+ emitDisclaimer(options, io);
1380
+ if (options.json) {
1381
+ const payload = {
1382
+ ok: true,
1383
+ uf: uf.toUpperCase(),
1384
+ total: rows.length,
1385
+ results: rows
1386
+ };
1387
+ if (options.verbose) {
1388
+ payload.capturadoEm = ISS_MUNICIPAL_DATA_VERSION.capturadoEm;
1389
+ }
1390
+ io.stdout.push(JSON.stringify(payload, null, 2));
1391
+ return EXIT.OK;
1392
+ }
1393
+ if (rows.length === 0) {
1394
+ io.stdout.push(`No ISS municipal rows embedded for UF ${uf.toUpperCase()}.`);
1395
+ return EXIT.OK;
1396
+ }
1397
+ for (const row of rows) {
1398
+ io.stdout.push(formatIssMunicipalHuman(row));
1399
+ }
1400
+ if (options.verbose) {
1401
+ io.stdout.push(`capturadoEm: ${ISS_MUNICIPAL_DATA_VERSION.capturadoEm}`);
1402
+ }
1403
+ return EXIT.OK;
1404
+ }
1342
1405
  function runIssMunicipalSearch(query, options, io = { stdout: [], stderr: [] }) {
1343
1406
  const trimmed = query?.trim() ?? "";
1344
1407
  if (trimmed.length === 0) {
1345
1408
  io.stderr.push("Missing query. Usage: br-validators iss-municipal search <query>");
1346
1409
  return EXIT.USAGE;
1347
1410
  }
1348
- const rows = searchIssMunicipal(trimmed, { limit: options.limit });
1411
+ const rows = searchIssMunicipal(trimmed, {
1412
+ limit: options.limit,
1413
+ ...options.uf !== void 0 && options.uf.trim().length > 0 ? { uf: options.uf } : {}
1414
+ });
1349
1415
  emitDisclaimer(options, io);
1350
1416
  if (options.json) {
1351
1417
  io.stdout.push(JSON.stringify({ ok: true, results: rows }, null, 2));
@@ -1424,6 +1490,73 @@ function runPtaxLookup(moeda, data, options, io = { stdout: [], stderr: [] }) {
1424
1490
  return runPtaxLookupCommand(moeda.trim(), data?.trim(), options, io);
1425
1491
  }
1426
1492
 
1493
+ // src/commands/ptax/historico.ts
1494
+ import {
1495
+ getPtaxHistorico,
1496
+ PTAX_DATA_VERSION as PTAX_DATA_VERSION2
1497
+ } from "@br-validators/core/ptax";
1498
+ function formatPtaxHistoricoHuman(results) {
1499
+ return results.map(
1500
+ (cotacao) => `${cotacao.dataReferencia} \u2014 compra ${String(cotacao.cotacaoCompra)} / venda ${String(cotacao.cotacaoVenda)}`
1501
+ );
1502
+ }
1503
+ function runPtaxHistoricoCommand(moeda, desde, ate, options, io = { stdout: [], stderr: [] }) {
1504
+ const trimmedMoeda = moeda.trim();
1505
+ const trimmedDesde = desde.trim();
1506
+ const trimmedAte = ate.trim();
1507
+ if (trimmedMoeda.length === 0) {
1508
+ io.stderr.push("Missing currency code. Pass a 3-letter ISO code (e.g. USD).");
1509
+ return EXIT.USAGE;
1510
+ }
1511
+ if (trimmedDesde.length === 0 || trimmedAte.length === 0) {
1512
+ io.stderr.push("Missing date range. Pass desde and ate as YYYY-MM-DD.");
1513
+ return EXIT.USAGE;
1514
+ }
1515
+ const results = getPtaxHistorico(trimmedMoeda, { desde: trimmedDesde, ate: trimmedAte });
1516
+ if (results.length === 0) {
1517
+ io.stderr.push(
1518
+ `No PTAX quotes in embed for ${trimmedMoeda} between ${trimmedDesde} and ${trimmedAte}`
1519
+ );
1520
+ return EXIT.INVALID;
1521
+ }
1522
+ if (options.json) {
1523
+ const payload = {
1524
+ ok: true,
1525
+ moeda: trimmedMoeda.toUpperCase(),
1526
+ desde: trimmedDesde,
1527
+ ate: trimmedAte,
1528
+ total: results.length,
1529
+ cotacoes: results
1530
+ };
1531
+ if (options.verbose) {
1532
+ payload.capturadoEm = PTAX_DATA_VERSION2.capturadoEm;
1533
+ payload.janelaDiasUteis = PTAX_DATA_VERSION2.janelaDiasUteis;
1534
+ }
1535
+ io.stdout.push(JSON.stringify(payload, null, 2));
1536
+ return EXIT.OK;
1537
+ }
1538
+ io.stdout.push(`${trimmedMoeda.toUpperCase()} PTAX historico (${String(results.length)} rows)`);
1539
+ for (const line of formatPtaxHistoricoHuman(results)) {
1540
+ io.stdout.push(line);
1541
+ }
1542
+ if (options.verbose) {
1543
+ io.stdout.push(`capturadoEm: ${PTAX_DATA_VERSION2.capturadoEm}`);
1544
+ io.stdout.push(`janelaDiasUteis: ${String(PTAX_DATA_VERSION2.janelaDiasUteis)}`);
1545
+ }
1546
+ return EXIT.OK;
1547
+ }
1548
+ function runPtaxHistorico(moeda, desde, ate, options, io = { stdout: [], stderr: [] }) {
1549
+ if (!moeda?.trim()) {
1550
+ io.stderr.push("Missing currency code. Usage: ptax historico <moeda> <desde> <ate>");
1551
+ return EXIT.USAGE;
1552
+ }
1553
+ if (!desde?.trim() || !ate?.trim()) {
1554
+ io.stderr.push("Missing date range. Usage: ptax historico <moeda> <desde> <ate>");
1555
+ return EXIT.USAGE;
1556
+ }
1557
+ return runPtaxHistoricoCommand(moeda.trim(), desde.trim(), ate.trim(), options, io);
1558
+ }
1559
+
1427
1560
  // src/commands/brcode.ts
1428
1561
  import {
1429
1562
  BRCODE_OFFICIAL_SOURCE_URL,
@@ -2895,7 +3028,8 @@ var SANITIZABLE_TYPES = [
2895
3028
  "cartao-credito",
2896
3029
  "ean",
2897
3030
  "inscricao-estadual",
2898
- "inscricao-estadual-produtor-rural"
3031
+ "inscricao-estadual-produtor-rural",
3032
+ "pix"
2899
3033
  ];
2900
3034
  function isSanitizableType(type) {
2901
3035
  return SANITIZABLE_TYPES.includes(type);
@@ -3033,19 +3167,35 @@ function runCompare(type, valueA, valueB, options, io = { stdout: [], stderr: []
3033
3167
  }
3034
3168
 
3035
3169
  // src/commands/batch.ts
3036
- import { batch } from "@br-validators/core";
3170
+ import { batch, parseBatchCsv } from "@br-validators/core";
3037
3171
  function parseBatchLines(raw) {
3038
3172
  return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
3039
3173
  }
3040
3174
  function resolveBatchInputs(options) {
3041
3175
  if (options.lines === void 0) {
3042
- return null;
3176
+ return { ok: false, reason: "missing_input" };
3177
+ }
3178
+ let parsed;
3179
+ if (options.col !== void 0) {
3180
+ const csv = parseBatchCsv(options.lines, {
3181
+ col: options.col,
3182
+ delimiter: options.delimiter,
3183
+ skipHeader: options.skipHeader
3184
+ });
3185
+ if (!csv.ok) {
3186
+ return { ok: false, reason: "parse_error", message: csv.message };
3187
+ }
3188
+ parsed = csv.values;
3189
+ } else {
3190
+ parsed = parseBatchLines(options.lines);
3191
+ }
3192
+ if (parsed.length === 0) {
3193
+ return { ok: false, reason: "empty" };
3043
3194
  }
3044
- const parsed = parseBatchLines(options.lines);
3045
3195
  if (options.limit !== void 0 && options.limit > 0) {
3046
- return parsed.slice(0, options.limit);
3196
+ return { ok: true, values: parsed.slice(0, options.limit) };
3047
3197
  }
3048
- return parsed;
3198
+ return { ok: true, values: parsed };
3049
3199
  }
3050
3200
  function printBatch(result, options, io = { stdout: [], stderr: [] }) {
3051
3201
  if (options.json) {
@@ -3071,18 +3221,20 @@ function runBatch(type, options, io = { stdout: [], stderr: [] }) {
3071
3221
  io.stderr.push(`Unsupported batch type: ${type}`);
3072
3222
  return EXIT.USAGE;
3073
3223
  }
3074
- const inputs = resolveBatchInputs(options);
3075
- if (inputs === null) {
3076
- io.stderr.push("Missing input. Pass --file <path> or pipe one value per line on stdin.");
3077
- return EXIT.USAGE;
3078
- }
3079
- if (inputs.length === 0) {
3080
- io.stderr.push("No values to validate.");
3224
+ const resolved = resolveBatchInputs(options);
3225
+ if (!resolved.ok) {
3226
+ if (resolved.reason === "missing_input") {
3227
+ io.stderr.push("Missing input. Pass --file <path> or pipe one value per line on stdin.");
3228
+ } else if (resolved.reason === "empty") {
3229
+ io.stderr.push("No values to validate.");
3230
+ } else {
3231
+ io.stderr.push(resolved.message);
3232
+ }
3081
3233
  return EXIT.USAGE;
3082
3234
  }
3083
3235
  const uf = options.uf?.toUpperCase();
3084
3236
  const platformOptions = uf ? { uf } : {};
3085
- const result = batch(inputs, type, platformOptions);
3237
+ const result = batch(resolved.values, type, platformOptions);
3086
3238
  return printBatch(result, options, io);
3087
3239
  }
3088
3240
 
@@ -3757,7 +3909,10 @@ function handleBatchCli(type, opts, io = { stdout: [], stderr: [] }) {
3757
3909
  quiet: Boolean(opts.quiet),
3758
3910
  uf: opts.uf,
3759
3911
  lines,
3760
- limit: opts.limit
3912
+ limit: opts.limit,
3913
+ col: opts.col,
3914
+ delimiter: opts.delimiter,
3915
+ skipHeader: opts.skipHeader
3761
3916
  },
3762
3917
  io
3763
3918
  );
@@ -3919,6 +4074,14 @@ function handleSelicCli(opts, io = { stdout: [], stderr: [] }) {
3919
4074
  date: opts.date
3920
4075
  }, io);
3921
4076
  }
4077
+ function handleIssMunicipalListCli(opts, io = { stdout: [], stderr: [] }) {
4078
+ return runIssMunicipalList({
4079
+ json: Boolean(opts.json),
4080
+ verbose: Boolean(opts.verbose),
4081
+ uf: opts.uf,
4082
+ limit: opts.limit
4083
+ }, io);
4084
+ }
3922
4085
  function handleIssMunicipalLookupCli(codigo, opts, io = { stdout: [], stderr: [] }) {
3923
4086
  return runIssMunicipalLookup(codigo, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
3924
4087
  }
@@ -3926,7 +4089,8 @@ function handleIssMunicipalSearchCli(query, opts, io = { stdout: [], stderr: []
3926
4089
  return runIssMunicipalSearch(query, {
3927
4090
  json: Boolean(opts.json),
3928
4091
  verbose: Boolean(opts.verbose),
3929
- limit: opts.limit
4092
+ limit: opts.limit,
4093
+ uf: opts.uf
3930
4094
  }, io);
3931
4095
  }
3932
4096
  function handleIssMunicipalResolveCli(uf, nome, opts, io = { stdout: [], stderr: [] }) {
@@ -3935,6 +4099,15 @@ function handleIssMunicipalResolveCli(uf, nome, opts, io = { stdout: [], stderr:
3935
4099
  function handlePtaxLookupCli(moeda, data, opts, io = { stdout: [], stderr: [] }) {
3936
4100
  return runPtaxLookup(moeda, data, { json: Boolean(opts.json), verbose: Boolean(opts.verbose) }, io);
3937
4101
  }
4102
+ function handlePtaxHistoricoCli(moeda, desde, ate, opts, io = { stdout: [], stderr: [] }) {
4103
+ return runPtaxHistorico(
4104
+ moeda,
4105
+ desde,
4106
+ ate,
4107
+ { json: Boolean(opts.json), verbose: Boolean(opts.verbose) },
4108
+ io
4109
+ );
4110
+ }
3938
4111
 
3939
4112
  // src/argv-dispatch.ts
3940
4113
  var STANDARD_ACTIONS = ["validate", "format", "strip"];
@@ -4054,7 +4227,7 @@ function dispatchArgv(tokens, io) {
4054
4227
  if (tokens.length === 0 || tokens.includes("--help") || tokens.includes("-h")) {
4055
4228
  io.stdout.push("br-validators \u2014 100% open-source Brazilian document validators");
4056
4229
  io.stdout.push("Usage: br-validators <command> ...");
4057
- io.stdout.push("Commands: list \xB7 cpf \xB7 cnpj \xB7 cep \xB7 telefone \xB7 cnh \xB7 renavam \xB7 titulo-eleitor \xB7 processo-judicial \xB7 rg \xB7 nfe-chave \xB7 brcode \xB7 placa \xB7 pis-pasep \xB7 cnis \xB7 pix \xB7 boleto \xB7 cartao \xB7 cartao-credito \xB7 ean \xB7 ie \xB7 bancos \xB7 ibge \xB7 feriados \xB7 inss \xB7 irpf \xB7 tse-municipios \xB7 ddd \xB7 nfe-cuf \xB7 selic \xB7 iss-municipal \xB7 ptax \xB7 cst \xB7 natureza-juridica \xB7 nbs \xB7 cest \xB7 cnae \xB7 cfop \xB7 ncm \xB7 cbo \xB7 moedas \xB7 paises-bacen \xB7 incoterms \xB7 portos \xB7 aeroportos \xB7 detect \xB7 sanitize \xB7 mask \xB7 compare \xB7 batch \xB7 diff \xB7 generate");
4230
+ io.stdout.push("Commands: list \xB7 cpf \xB7 cnpj \xB7 cep \xB7 telefone \xB7 cnh \xB7 renavam \xB7 titulo-eleitor \xB7 processo-judicial \xB7 rg \xB7 nfe-chave \xB7 brcode \xB7 placa \xB7 pis-pasep \xB7 cnis \xB7 pix \xB7 boleto \xB7 cartao \xB7 cartao-credito \xB7 ean \xB7 ie \xB7 bancos \xB7 ibge \xB7 feriados \xB7 inss \xB7 irpf \xB7 tse-municipios \xB7 ddd \xB7 nfe-cuf \xB7 selic \xB7 iss-municipal \xB7 ptax \xB7 cst \xB7 csosn \xB7 natureza-juridica \xB7 nbs \xB7 cest \xB7 cnae \xB7 cfop \xB7 ncm \xB7 cbo \xB7 moedas \xB7 paises-bacen \xB7 incoterms \xB7 portos \xB7 aeroportos \xB7 detect \xB7 sanitize \xB7 mask \xB7 compare \xB7 batch \xB7 diff \xB7 generate");
4058
4231
  return EXIT.OK;
4059
4232
  }
4060
4233
  if (tokens.includes("--version") || tokens.includes("-V")) {
@@ -4287,6 +4460,9 @@ function dispatchArgv(tokens, io) {
4287
4460
  const value = rest[1];
4288
4461
  return handleIssMunicipalLookupCli(value, opts, io);
4289
4462
  }
4463
+ if (action === "list") {
4464
+ return handleIssMunicipalListCli(opts, io);
4465
+ }
4290
4466
  if (action === "resolve") {
4291
4467
  const uf = rest[1];
4292
4468
  const nome = rest.slice(2).join(" ") || void 0;
@@ -4296,7 +4472,7 @@ function dispatchArgv(tokens, io) {
4296
4472
  const value = rest.slice(1).join(" ") || void 0;
4297
4473
  return handleIssMunicipalSearchCli(value, opts, io);
4298
4474
  }
4299
- return usage(io, "Expected: iss-municipal lookup|resolve|search <args>");
4475
+ return usage(io, "Expected: iss-municipal lookup|list|resolve|search <args>");
4300
4476
  }
4301
4477
  case "ptax": {
4302
4478
  const action = rest[0];
@@ -4305,7 +4481,13 @@ function dispatchArgv(tokens, io) {
4305
4481
  const data = rest.slice(2).join(" ") || void 0;
4306
4482
  return handlePtaxLookupCli(moeda, data, opts, io);
4307
4483
  }
4308
- return usage(io, "Expected: ptax lookup <moeda> [data]");
4484
+ if (action === "historico") {
4485
+ const moeda = rest[1];
4486
+ const desde = rest[2];
4487
+ const ate = rest[3];
4488
+ return handlePtaxHistoricoCli(moeda, desde, ate, opts, io);
4489
+ }
4490
+ return usage(io, "Expected: ptax lookup <moeda> [data] | ptax historico <moeda> <desde> <ate>");
4309
4491
  }
4310
4492
  case "cst": {
4311
4493
  const action = rest[0];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@br-validators/cli",
3
- "version": "1.9.0",
3
+ "version": "1.10.0-data.2",
4
4
  "description": "CLI for @br-validators/core — CPF, CNPJ, NF-e, IE, PIX, boleto + detect/sanitize/generate",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "commander": "^13.1.0",
48
- "@br-validators/core": "1.9.0"
48
+ "@br-validators/core": "1.10.0-data.2"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^22.15.21",