@nfewizard/shared 1.0.2 → 1.1.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.
Files changed (30) hide show
  1. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/adapters/SchemaLoader.js +1 -0
  2. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/adapters/SchemaLoader.js.map +1 -1
  3. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/base/BaseNFe.d.ts +1 -1
  4. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/base/GerarConsulta.js +1 -0
  5. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/base/GerarConsulta.js.map +1 -1
  6. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/environment/LoadCertificate.d.ts +5 -0
  7. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/environment/LoadCertificate.js +30 -9
  8. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/environment/LoadCertificate.js.map +1 -1
  9. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/XmlParser.d.ts +65 -0
  10. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/XmlParser.js +189 -0
  11. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/XmlParser.js.map +1 -1
  12. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/index.d.ts +2 -0
  13. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/index.js +1 -0
  14. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/index.js.map +1 -1
  15. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/validateSchema.d.ts +96 -0
  16. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/validateSchema.js +252 -0
  17. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/src/utils/validateSchema.js.map +1 -0
  18. package/.rollup.cache/usr/projetos/nfewizard/nfewizard-io-idle-old/packages/shared/dist/tsconfig.tsbuildinfo +1 -1
  19. package/README.md +47 -1
  20. package/dist/index.cjs +470 -9
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.mjs +470 -10
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/src/base/BaseNFe.d.ts +1 -1
  25. package/dist/src/environment/LoadCertificate.d.ts +5 -0
  26. package/dist/src/utils/XmlParser.d.ts +65 -0
  27. package/dist/src/utils/index.d.ts +2 -0
  28. package/dist/src/utils/validateSchema.d.ts +96 -0
  29. package/dist/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -434,6 +434,195 @@ class XmlParser {
434
434
  }
435
435
  return jsonBody;
436
436
  }
437
+ /**
438
+ * Converte um XML de envio (`enviNFe`) ou uma `NFe` solo em uma
439
+ * estrutura JSON compatível com o tipo `NFe` esperado pelos serviços
440
+ * de Autorização de NFe/NFCe (`{ idLote, indSinc, NFe }`).
441
+ *
442
+ * Formatos aceitos:
443
+ * - Envelope `enviNFe` contendo uma ou mais `NFe`.
444
+ * - `nfeProc` (XML autorizado) — extrai a `NFe` interna.
445
+ * - `NFe` solo — empacota com defaults (`idLote=1`, `indSinc=1`).
446
+ *
447
+ * Defaults são aplicáveis somente para os campos do envelope; o
448
+ * conteúdo de `infNFe` não é alterado e segue o fluxo normal de
449
+ * geração/assinatura no service.
450
+ *
451
+ * @param xml String XML de entrada.
452
+ * @param overrides Permite sobrescrever `idLote`/`indSinc` quando o XML
453
+ * não contém envelope.
454
+ */
455
+ convertXmlEnvioNFeToJson(xml, overrides) {
456
+ logger.info('Convertendo XML de envio para JSON', {
457
+ context: 'XmlParser',
458
+ method: 'convertXmlEnvioNFeToJson',
459
+ });
460
+ const jsonData = this.parseNfeLikeXml(xml);
461
+ let envelope = this.findInObj(jsonData, 'enviNFe');
462
+ if (!envelope) {
463
+ const nfeProc = this.findInObj(jsonData, 'nfeProc');
464
+ const nfeNode = nfeProc ? this.findInObj(nfeProc, 'NFe') : this.findInObj(jsonData, 'NFe');
465
+ if (!nfeNode) {
466
+ throw new Error('XML inválido: não foi possível localizar `enviNFe` ou `NFe`.');
467
+ }
468
+ envelope = {
469
+ idLote: overrides?.idLote ?? '1',
470
+ indSinc: overrides?.indSinc ?? 1,
471
+ NFe: nfeNode,
472
+ };
473
+ }
474
+ // Normaliza CNPJ/CPF -> CNPJCPF nas NFe (formato esperado pela lib)
475
+ const nfeList = Array.isArray(envelope.NFe) ? envelope.NFe : [envelope.NFe];
476
+ nfeList.forEach((n) => this.normalizeNfeForLibFormat(n));
477
+ return {
478
+ idLote: envelope.idLote ?? overrides?.idLote ?? '1',
479
+ indSinc: Number(envelope.indSinc ?? overrides?.indSinc ?? 1),
480
+ NFe: envelope.NFe,
481
+ };
482
+ }
483
+ /**
484
+ * Converte um XML autorizado (`nfeProc`) ou uma `NFe` solo em uma
485
+ * estrutura JSON compatível com o gerador de DANFE
486
+ * (`{ NFe, protNFe? }`), além de retornar a chave de acesso quando
487
+ * disponível.
488
+ *
489
+ * @param xml String XML de entrada.
490
+ */
491
+ convertXmlNfeProcToJson(xml) {
492
+ logger.info('Convertendo nfeProc para JSON', {
493
+ context: 'XmlParser',
494
+ method: 'convertXmlNfeProcToJson',
495
+ });
496
+ const jsonData = this.parseNfeLikeXml(xml);
497
+ const nfeProc = this.findInObj(jsonData, 'nfeProc');
498
+ const NFe = nfeProc ? this.findInObj(nfeProc, 'NFe') : this.findInObj(jsonData, 'NFe');
499
+ if (!NFe) {
500
+ throw new Error('XML inválido: não foi possível localizar `NFe` ou `nfeProc`.');
501
+ }
502
+ const protNFe = nfeProc ? this.findInObj(nfeProc, 'protNFe') : this.findInObj(jsonData, 'protNFe');
503
+ const chFromProt = protNFe?.infProt?.chNFe;
504
+ const idAttr = NFe?.infNFe?.Id ?? '';
505
+ const chFromId = typeof idAttr === 'string' ? idAttr.replace(/^NFe/, '') : '';
506
+ const chave = chFromProt || chFromId || '';
507
+ const data = { NFe };
508
+ if (protNFe)
509
+ data.protNFe = protNFe;
510
+ return { data, chave };
511
+ }
512
+ /**
513
+ * Faz o parse de um XML de NFe/NFCe (envio, retorno ou solo) preservando
514
+ * atributos e elevando-os para o nível do elemento (ex.: `Id` em `infNFe`,
515
+ * `versao` em `NFe`, `nItem` em `det`). Necessário para alimentar o
516
+ * fluxo interno da lib que espera atributos como propriedades comuns.
517
+ */
518
+ parseNfeLikeXml(xml) {
519
+ const jsonAsString = convert.xml2json(xml, {
520
+ compact: true,
521
+ spaces: 2,
522
+ ignoreAttributes: false,
523
+ ignoreDeclaration: true,
524
+ trim: true,
525
+ ignoreInstruction: true,
526
+ ignoreComment: true,
527
+ ignoreCdata: true,
528
+ ignoreDoctype: true,
529
+ textFn: this.removeJsonTextAttribute,
530
+ });
531
+ const parsed = JSON.parse(jsonAsString);
532
+ this.liftAttributes(parsed);
533
+ return parsed;
534
+ }
535
+ /**
536
+ * Eleva, recursivamente, as chaves de `_attributes` para o próprio nível
537
+ * do elemento e remove o nó `_attributes`. Compatível com a saída de
538
+ * `xml-js` em modo compacto.
539
+ */
540
+ liftAttributes(node) {
541
+ if (!node || typeof node !== 'object')
542
+ return;
543
+ if (Array.isArray(node)) {
544
+ node.forEach(item => this.liftAttributes(item));
545
+ return;
546
+ }
547
+ if (node._attributes && typeof node._attributes === 'object') {
548
+ for (const attrKey of Object.keys(node._attributes)) {
549
+ if (!(attrKey in node)) {
550
+ node[attrKey] = node._attributes[attrKey];
551
+ }
552
+ }
553
+ delete node._attributes;
554
+ }
555
+ for (const key of Object.keys(node)) {
556
+ const value = node[key];
557
+ if (value && typeof value === 'object') {
558
+ this.liftAttributes(value);
559
+ }
560
+ }
561
+ }
562
+ /**
563
+ * Normaliza uma `NFe` parseada de XML para o formato interno da lib:
564
+ * - converte `CNPJ`/`CPF`/`idEstrangeiro` em `CNPJCPF` em `emit`,
565
+ * `dest`, `transp.transporta` e `NFref.refNFP`.
566
+ * - remove atributos elevados que a lib re-aplica via `$` ao montar
567
+ * o XML final (`infNFe.versao`, `det.nItem`), evitando duplicidade
568
+ * no XML gerado.
569
+ *
570
+ * Observação: `infRespTec` mantém o campo `CNPJ` original — a lib não
571
+ * unifica documento para esse nó.
572
+ */
573
+ normalizeNfeForLibFormat(nfeNode) {
574
+ const infNFe = nfeNode?.infNFe;
575
+ if (!infNFe || typeof infNFe !== 'object')
576
+ return;
577
+ // versao é re-aplicado pela lib via $ ao montar `infNFe`
578
+ delete infNFe.versao;
579
+ // O atributo `Id` no XML vem com prefixo "NFe" (ex.: "NFe4126..."),
580
+ // mas a lib espera apenas a chave de 44 dígitos em `infNFe.Id` —
581
+ // ela própria re-prefixa com "NFe" ao calcular `chaveAcesso`.
582
+ if (typeof infNFe.Id === 'string' && /^NFe\d{44}$/.test(infNFe.Id)) {
583
+ infNFe.Id = infNFe.Id.slice(3);
584
+ }
585
+ this.unifyDocFields(infNFe.emit);
586
+ this.unifyDocFields(infNFe.dest);
587
+ this.unifyDocFields(infNFe.transp?.transporta);
588
+ // nItem é re-aplicado pela lib via $ em cada `det` quando é array.
589
+ // Por isso normalizamos sempre para array (no XML, com 1 item, viraria
590
+ // objeto único e a lib não re-aplicaria nItem, quebrando a validação XSD).
591
+ const det = infNFe.det;
592
+ if (Array.isArray(det)) {
593
+ det.forEach((d) => { if (d && typeof d === 'object')
594
+ delete d.nItem; });
595
+ }
596
+ else if (det && typeof det === 'object') {
597
+ delete det.nItem;
598
+ infNFe.det = [det];
599
+ }
600
+ const NFref = infNFe.NFref;
601
+ if (Array.isArray(NFref)) {
602
+ NFref.forEach((ref) => this.unifyDocFields(ref?.refNFP));
603
+ }
604
+ else if (NFref && typeof NFref === 'object') {
605
+ this.unifyDocFields(NFref.refNFP);
606
+ }
607
+ }
608
+ /**
609
+ * Em um nó (ex.: `emit`, `dest`), unifica `CNPJ`/`CPF`/`idEstrangeiro`
610
+ * em um único campo `CNPJCPF`, removendo as chaves originais. Mantém a
611
+ * primeira não-vazia encontrada na ordem `CNPJ → CPF → idEstrangeiro`.
612
+ */
613
+ unifyDocFields(target) {
614
+ if (!target || typeof target !== 'object')
615
+ return;
616
+ if (target.CNPJCPF)
617
+ return;
618
+ const candidate = target.CNPJ || target.CPF || target.idEstrangeiro;
619
+ if (candidate) {
620
+ target.CNPJCPF = candidate;
621
+ }
622
+ delete target.CNPJ;
623
+ delete target.CPF;
624
+ delete target.idEstrangeiro;
625
+ }
437
626
  }
438
627
 
439
628
  class BaseNFE {
@@ -743,6 +932,7 @@ class GerarConsulta {
743
932
  const config = this.environment.getConfig();
744
933
  // Capturando o schema path
745
934
  const { schemaPath } = this.utility.getSchema(metodo);
935
+ logger.info(xmlConsulta);
746
936
  // Valida Schema apenas se o schema path estiver definido
747
937
  if (schemaPath) {
748
938
  if (config.lib?.useForSchemaValidation !== 'validateSchemaJsBased') {
@@ -993,6 +1183,7 @@ const getSchema = (metodo) => {
993
1183
  RecepcaoEvento: `${pathSchemas}/envEvento_v1.00.xsd`,
994
1184
  NFeDistribuicaoDFe: `${pathSchemas}/distDFeInt_v1.01.xsd`,
995
1185
  NFEAutorizacao: `${pathSchemas}/enviNFe_v4.00.xsd`,
1186
+ NFeAutorizacao: `${pathSchemas}/enviNFe_v4.00.xsd`,
996
1187
  NFEInutilizacao: `${pathSchemas}/inutNFe_v4.00.xsd`,
997
1188
  NFERetAutorizacao: `${pathSchemas}/consReciNFe_v4.00.xsd`,
998
1189
  CTeDistribuicaoDFe: `${pathSchemas}/cte/distDFeInt_v1.00.xsd`,
@@ -3656,6 +3847,254 @@ const mountICMS = (icms) => {
3656
3847
  return {};
3657
3848
  };
3658
3849
 
3850
+ /*
3851
+ * This file is part of NFeWizard-io.
3852
+ *
3853
+ * NFeWizard-io is free software: you can redistribute it and/or modify
3854
+ * it under the terms of the GNU General Public License as published by
3855
+ * the Free Software Foundation, either version 3 of the License, or
3856
+ * (at your option) any later version.
3857
+ *
3858
+ * NFeWizard-io is distributed in the hope that it will be useful,
3859
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3860
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3861
+ * GNU General Public License for more details.
3862
+ *
3863
+ * You should have received a copy of the GNU General Public License
3864
+ * along with NFeWizard-io. If not, see <https://www.gnu.org/licenses/>.
3865
+ */
3866
+ /** Remove o prefixo `{namespace}` deixando apenas o nome local do elemento. */
3867
+ function stripNamespace(name) {
3868
+ return name.replace(/^\{[^}]*\}/, '');
3869
+ }
3870
+ /**
3871
+ * Interpreta uma mensagem de erro do libxmljs2 / xsd-schema-validator e retorna
3872
+ * um `SchemaValidationIssue` com versão humanizada + metadados estruturados.
3873
+ */
3874
+ function humanizeIssue(raw, line, column) {
3875
+ const issue = { raw, humanized: raw, line, column };
3876
+ // 1) Elemento com filhos ausentes
3877
+ // Element '{ns}Nome': Missing child element(s). Expected is one of ( {ns}a, {ns}b ).
3878
+ let m = raw.match(/Element\s+'([^']+)':\s*Missing child element\(s\)\.\s*Expected is one of\s*\(\s*([^)]+)\s*\)\.?/i);
3879
+ if (m) {
3880
+ const element = stripNamespace(m[1]);
3881
+ const expected = m[2].split(',').map((s) => stripNamespace(s.trim())).filter(Boolean);
3882
+ issue.element = element;
3883
+ issue.expected = expected;
3884
+ issue.humanized = `Elemento <${element}> está incompleto. Filho ausente, esperado um de: ${expected.join(', ')}.`;
3885
+ return issue;
3886
+ }
3887
+ // 2) Elemento não esperado
3888
+ // Element '{ns}Nome': This element is not expected. Expected is one of ( {ns}a, {ns}b ).
3889
+ m = raw.match(/Element\s+'([^']+)':\s*This element is not expected\.\s*(?:Expected is one of\s*\(\s*([^)]+)\s*\)\.?)?/i);
3890
+ if (m) {
3891
+ const element = stripNamespace(m[1]);
3892
+ issue.element = element;
3893
+ if (m[2]) {
3894
+ issue.expected = m[2].split(',').map((s) => stripNamespace(s.trim())).filter(Boolean);
3895
+ issue.humanized = `Elemento <${element}> inesperado nesta posição. Esperado um de: ${issue.expected.join(', ')}.`;
3896
+ }
3897
+ else {
3898
+ issue.humanized = `Elemento <${element}> inesperado nesta posição.`;
3899
+ }
3900
+ return issue;
3901
+ }
3902
+ // 3) Atributo inválido / faltando
3903
+ // Element '{ns}Nome', attribute 'attr': ...
3904
+ m = raw.match(/Element\s+'([^']+)',\s*attribute\s+'([^']+)':\s*(.+?)\.?$/i);
3905
+ if (m) {
3906
+ const element = stripNamespace(m[1]);
3907
+ const attribute = m[2];
3908
+ const desc = m[3];
3909
+ issue.element = element;
3910
+ issue.attribute = attribute;
3911
+ issue.humanized = `Elemento <${element}> — atributo "${attribute}": ${desc}.`;
3912
+ return issue;
3913
+ }
3914
+ // 4) Valor inválido para tipo
3915
+ // Element '{ns}Nome': [facet 'pattern'] The value '...' is not accepted by the pattern '...'.
3916
+ // Element '{ns}Nome': '...' is not a valid value of the atomic type 'TString'.
3917
+ m = raw.match(/Element\s+'([^']+)':\s*(?:\[facet[^\]]*\]\s*)?(.+?)\.?$/i);
3918
+ if (m) {
3919
+ const element = stripNamespace(m[1]);
3920
+ issue.element = element;
3921
+ issue.humanized = `Elemento <${element}>: ${m[2]}.`;
3922
+ return issue;
3923
+ }
3924
+ // 5) Sem padrão reconhecido — devolve raw como humanizado
3925
+ return issue;
3926
+ }
3927
+ /** Cria um issue "interno" quando o erro não veio do schema (ex.: XML mal formado). */
3928
+ function internalIssue(raw) {
3929
+ return { raw, humanized: raw };
3930
+ }
3931
+ /**
3932
+ * Monta o relatório textual estilo SEFAZ-RS pronto para `console.log`.
3933
+ */
3934
+ function buildReport(metodo, schemaFile, errors) {
3935
+ const linhas = [];
3936
+ linhas.push('=== Resultado da Validação do Schema ===');
3937
+ linhas.push(`Tipo de Mensagem: ${metodo}`);
3938
+ linhas.push(`Schema XML: ${schemaFile}`);
3939
+ if (errors.length === 0) {
3940
+ linhas.push('Parser XML: Nenhum erro encontrado');
3941
+ linhas.push('Schema XML: Nenhum erro encontrado');
3942
+ linhas.push('Status: XML válido.');
3943
+ }
3944
+ else {
3945
+ linhas.push(`Status: ${errors.length} erro(s) de validação`);
3946
+ linhas.push('');
3947
+ errors.forEach((err, idx) => {
3948
+ const pos = err.line ? ` (linha ${err.line}${err.column ? `, col ${err.column}` : ''})` : '';
3949
+ linhas.push(` ${idx + 1}. ${err.humanized}${pos}`);
3950
+ });
3951
+ }
3952
+ linhas.push('=========================================');
3953
+ return linhas.join('\n');
3954
+ }
3955
+ /** Converte a lista de erros em linhas para `console.table`. */
3956
+ function buildTableRows(errors) {
3957
+ if (errors.length === 0) {
3958
+ return [{ '#': 1, Linha: '', Elemento: '-', Mensagem: 'XML válido.' }];
3959
+ }
3960
+ return errors.map((err, idx) => ({
3961
+ '#': idx + 1,
3962
+ Linha: err.line ?? '',
3963
+ Elemento: err.element ?? '-',
3964
+ Mensagem: err.humanized,
3965
+ }));
3966
+ }
3967
+ function schemaFileName(schemaPath) {
3968
+ if (!schemaPath)
3969
+ return '-';
3970
+ const parts = schemaPath.split(/[\\/]/);
3971
+ return parts[parts.length - 1] || schemaPath;
3972
+ }
3973
+ function makeResult(success, metodo, schemaPath, errors) {
3974
+ const schema = schemaFileName(schemaPath);
3975
+ const report = buildReport(metodo, schema, errors);
3976
+ const message = success
3977
+ ? 'XML válido.'
3978
+ : errors[0]?.humanized ?? 'Erro de validação não identificado.';
3979
+ return {
3980
+ success,
3981
+ message,
3982
+ errors,
3983
+ report,
3984
+ tableRows: buildTableRows(errors),
3985
+ metodo,
3986
+ schema,
3987
+ };
3988
+ }
3989
+ function runJsBased(xml, metodo) {
3990
+ return new Promise(async (resolve, reject) => {
3991
+ const { basePath, schemaPath } = getSchema(metodo);
3992
+ if (!schemaPath) {
3993
+ reject(makeResult(false, metodo, '', [internalIssue(`Schema XSD não encontrado para o método '${metodo}'. Verifique se o nome do método está correto.`)]));
3994
+ return;
3995
+ }
3996
+ try {
3997
+ const completeXSD = await xsdAssembler.assemble(schemaPath);
3998
+ const xmlDoc = libxmljs.parseXml(xml);
3999
+ const xsdDoc = libxmljs.parseXml(completeXSD, { baseUrl: `${basePath}/` });
4000
+ const isValid = xmlDoc.validate(xsdDoc);
4001
+ if (isValid) {
4002
+ resolve(makeResult(true, metodo, schemaPath, []));
4003
+ return;
4004
+ }
4005
+ const errors = (xmlDoc.validationErrors || []).map((e) => humanizeIssue(String(e.message ?? e).trim(), e.line, e.column));
4006
+ reject(makeResult(false, metodo, schemaPath, errors.length ? errors : [internalIssue('XML inválido (sem detalhes do validador).')]));
4007
+ }
4008
+ catch (error) {
4009
+ reject(makeResult(false, metodo, schemaPath, [internalIssue(String(error?.message ?? error))]));
4010
+ }
4011
+ });
4012
+ }
4013
+ function runJavaBased(xml, metodo) {
4014
+ return new Promise((resolve, reject) => {
4015
+ const { schemaPath } = getSchema(metodo);
4016
+ if (!schemaPath) {
4017
+ reject(makeResult(false, metodo, '', [internalIssue(`Schema XSD não encontrado para o método '${metodo}'. Verifique se o nome do método está correto.`)]));
4018
+ return;
4019
+ }
4020
+ try {
4021
+ xsdValidator.validateXML(xml, schemaPath, (err, validationResult) => {
4022
+ if (err) {
4023
+ reject(makeResult(false, metodo, schemaPath, [humanizeIssue(String(err.message ?? err))]));
4024
+ return;
4025
+ }
4026
+ if (!validationResult?.valid) {
4027
+ const msgs = validationResult?.messages ?? [];
4028
+ const errors = msgs.length
4029
+ ? msgs.map((m) => humanizeIssue(String(m)))
4030
+ : [internalIssue('XML inválido (sem detalhes do validador).')];
4031
+ reject(makeResult(false, metodo, schemaPath, errors));
4032
+ return;
4033
+ }
4034
+ resolve(makeResult(true, metodo, schemaPath, []));
4035
+ });
4036
+ }
4037
+ catch (error) {
4038
+ reject(makeResult(false, metodo, schemaPath, [internalIssue(String(error?.message ?? error))]));
4039
+ }
4040
+ });
4041
+ }
4042
+ /**
4043
+ * Valida um XML contra o schema XSD correspondente ao método fiscal informado.
4044
+ *
4045
+ * ### Seleção do validador (ordem de prioridade)
4046
+ * 1. `options.validator` — escolha explícita do caller.
4047
+ * 2. `options.environment.getConfig().lib?.useForSchemaValidation` — valor definido
4048
+ * na inicialização da lib.
4049
+ * 3. `'validateSchemaJsBased'` — padrão (sem JDK).
4050
+ *
4051
+ * @param xml - String XML a ser validada.
4052
+ * @param metodo - Nome do método/operação fiscal (ex.: `'NFeAutorizacao'`).
4053
+ * @param options - Opções opcionais: `validator` e/ou `environment`.
4054
+ *
4055
+ * @example
4056
+ * // Sem opções: usa validateSchemaJsBased
4057
+ * await NFE_SchemaValidate(xml, 'NFeAutorizacao');
4058
+ *
4059
+ * @example
4060
+ * // Passando o environment inicializado pela lib (lê useForSchemaValidation do config)
4061
+ * await NFE_SchemaValidate(xml, 'NFeAutorizacao', { environment });
4062
+ *
4063
+ * @example
4064
+ * // Forçando um validador específico
4065
+ * await NFE_SchemaValidate(xml, 'NFeAutorizacao', { validator: 'validateSchemaJavaBased' });
4066
+ */
4067
+ function NFE_SchemaValidate(xml, metodo, options) {
4068
+ let chosen = 'validateSchemaJsBased';
4069
+ if (options?.validator) {
4070
+ chosen = options.validator;
4071
+ }
4072
+ else if (options?.environment) {
4073
+ const fromConfig = options.environment.getConfig()?.lib?.useForSchemaValidation;
4074
+ if (fromConfig)
4075
+ chosen = fromConfig;
4076
+ }
4077
+ const xmlNormalizado = wrapEnviNFeIfNeeded(xml, metodo);
4078
+ return chosen === 'validateSchemaJavaBased'
4079
+ ? runJavaBased(xmlNormalizado, metodo)
4080
+ : runJsBased(xmlNormalizado, metodo);
4081
+ }
4082
+ /**
4083
+ * Para os métodos de autorização de NF-e, o schema XSD (`enviNFe_v4.00.xsd`)
4084
+ * exige que o elemento raiz seja `<enviNFe>`. Quando o caller passa apenas a
4085
+ * árvore `<NFe>`, envelopamos automaticamente em `<enviNFe>` com `idLote=1` e
4086
+ * `indSinc=1` para que a validação funcione.
4087
+ */
4088
+ function wrapEnviNFeIfNeeded(xml, metodo) {
4089
+ if (metodo !== 'NFEAutorizacao' && metodo !== 'NFeAutorizacao')
4090
+ return xml;
4091
+ const semDecl = xml.replace(/^\s*<\?xml[^?]*\?>\s*/, '').trimStart();
4092
+ if (/^<NFe[\s>]/.test(semDecl)) {
4093
+ return `<enviNFe versao="4.00" xmlns="http://www.portalfiscal.inf.br/nfe"><idLote>1</idLote><indSinc>1</indSinc>${semDecl}</enviNFe>`;
4094
+ }
4095
+ return xml;
4096
+ }
4097
+
3659
4098
  /*
3660
4099
  * This file is part of NFeWizard-io.
3661
4100
  *
@@ -3841,12 +4280,35 @@ class LoadCertificate {
3841
4280
  this.certificate = '';
3842
4281
  this.cert_key = '';
3843
4282
  }
3844
- loadCertificateWithPEM() {
4283
+ /**
4284
+ * Resolve o conteúdo binário do PFX a partir do `pathCertificado` informado em
4285
+ * config. Aceita `string` (caminho), `Buffer` ou `NodeJS.ReadableStream`.
4286
+ */
4287
+ async resolvePfxBuffer() {
4288
+ const input = this.config.dfe.pathCertificado;
4289
+ if (typeof input === 'string') {
4290
+ return fs.readFileSync(input);
4291
+ }
4292
+ if (Buffer.isBuffer(input)) {
4293
+ return input;
4294
+ }
4295
+ if (input && typeof input.pipe === 'function') {
4296
+ const stream = input;
4297
+ return await new Promise((resolve, reject) => {
4298
+ const chunks = [];
4299
+ stream.on('data', (chunk) => {
4300
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
4301
+ });
4302
+ stream.on('end', () => resolve(Buffer.concat(chunks)));
4303
+ stream.on('error', (err) => reject(err));
4304
+ });
4305
+ }
4306
+ throw new Error('pathCertificado deve ser uma string (path), Buffer ou Stream legível.');
4307
+ }
4308
+ loadCertificateWithPEM(pfxFile) {
3845
4309
  return new Promise((resolve, reject) => {
3846
4310
  try {
3847
- const pfxPath = this.config.dfe.pathCertificado;
3848
4311
  const pfxPassword = this.config.dfe.senhaCertificado;
3849
- const pfxFile = fs.readFileSync(pfxPath);
3850
4312
  const certsDir = path.resolve(baseDir, dir);
3851
4313
  // Read CA certificates if directory exists and has files
3852
4314
  let caCerts = [];
@@ -3905,13 +4367,10 @@ class LoadCertificate {
3905
4367
  }
3906
4368
  });
3907
4369
  }
3908
- loadCertificateWithNodeForge() {
4370
+ loadCertificateWithNodeForge(pfxFile) {
3909
4371
  return new Promise((resolve, reject) => {
3910
4372
  try {
3911
- const pfxPath = this.config.dfe.pathCertificado;
3912
4373
  const pfxPassword = this.config.dfe.senhaCertificado;
3913
- // Lê o arquivo PFX
3914
- const pfxFile = fs.readFileSync(pfxPath);
3915
4374
  // Decodifica o arquivo PFX (PKCS#12)
3916
4375
  const p12Asn1 = forge.asn1.fromDer(pfxFile.toString('binary'));
3917
4376
  const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, pfxPassword);
@@ -3983,15 +4442,16 @@ class LoadCertificate {
3983
4442
  logger.info('Validando certificado', {
3984
4443
  context: 'LoadCertificate',
3985
4444
  });
4445
+ const pfxFile = await this.resolvePfxBuffer();
3986
4446
  if (this.config.lib?.useOpenSSL || this.config.lib?.useOpenSSL === undefined) {
3987
- const { agent } = await this.loadCertificateWithPEM();
4447
+ const { agent } = await this.loadCertificateWithPEM(pfxFile);
3988
4448
  return {
3989
4449
  certificate: this.certificate,
3990
4450
  cert_key: this.cert_key,
3991
4451
  agent
3992
4452
  };
3993
4453
  }
3994
- const { agent } = await this.loadCertificateWithNodeForge();
4454
+ const { agent } = await this.loadCertificateWithNodeForge(pfxFile);
3995
4455
  return {
3996
4456
  certificate: this.certificate,
3997
4457
  cert_key: this.cert_key,
@@ -4075,5 +4535,5 @@ class Environment {
4075
4535
  }
4076
4536
  }
4077
4537
 
4078
- export { AxiosHttpClient, BaseNFE, BaseNFSe, Environment, GerarConsulta, HttpClientBuilder, JsonArrayTransport, LoadCertificate, SaveFiles, Utility, ValidaCPFCNPJ, ValidateEnvironment, XmlBuilder, XmlParser, getCodIBGE, getDesTipoPag, getSchema, logger, mountCOFINS, mountICMS, mountPIS };
4538
+ export { AxiosHttpClient, BaseNFE, BaseNFSe, Environment, GerarConsulta, HttpClientBuilder, JsonArrayTransport, LoadCertificate, NFE_SchemaValidate, SaveFiles, Utility, ValidaCPFCNPJ, ValidateEnvironment, XmlBuilder, XmlParser, getCodIBGE, getDesTipoPag, getSchema, logger, mountCOFINS, mountICMS, mountPIS };
4079
4539
  //# sourceMappingURL=index.mjs.map