cosmemilton-acbr-node 0.1.2 → 0.2.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/LICENSE +502 -502
- package/NOTICE +8 -8
- package/README.md +40 -11
- package/acbr.config.json +16 -16
- package/acbr.lock.json +0 -0
- package/dist/cli.js +39 -13
- package/dist/cli.js.map +2 -2
- package/dist/contract.json +0 -0
- package/dist/index.cjs +36 -11
- package/dist/index.cjs.map +2 -2
- package/dist/index.js +36 -11
- package/dist/index.js.map +2 -2
- package/dist/runtime.cjs +36 -11
- package/dist/runtime.cjs.map +2 -2
- package/dist/runtime.js +36 -11
- package/dist/runtime.js.map +2 -2
- package/dist/types/generated/acbr/index.d.ts +1 -0
- package/dist/types/generated/acbr/models.d.ts +0 -0
- package/dist/types/src/cli.d.ts +0 -0
- package/dist/types/src/config.d.ts +0 -0
- package/dist/types/src/contract-help.d.ts +0 -0
- package/dist/types/src/diagnostics.d.ts +2 -0
- package/dist/types/src/generator.d.ts +0 -0
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/native-build.d.ts +1 -0
- package/dist/types/src/offline-check.d.ts +0 -0
- package/dist/types/src/offline-fixture.d.ts +0 -0
- package/dist/types/src/runtime.d.ts +1 -0
- package/dist/types/src/source.d.ts +0 -0
- package/dist/types/src/types.d.ts +4 -2
- package/docs/API.md +116 -90
- package/docs/ARQUITETURA.md +19 -15
- package/docs/CI.md +60 -60
- package/docs/CLI.md +1 -1
- package/docs/DEPENDENCIAS-NATIVAS.md +93 -93
- package/docs/VALIDACAO.md +67 -67
- package/docs/examples/recuperar-xml.mjs +54 -0
- package/docs/licenses/fpc/COPYING.FPC +25 -25
- package/docs/licenses/fpc/COPYING.GPL.txt +340 -340
- package/docs/licenses/fpc/COPYING.LGPL.txt +504 -504
- package/docs/licenses/fpc/ORIGIN.json +21 -21
- package/docs/licenses/lazarus/COPYING.LGPL.txt +481 -481
- package/docs/licenses/lazarus/COPYING.modifiedLGPL.txt +26 -26
- package/docs/licenses/lazarus/COPYING.txt +46 -46
- package/docs/licenses/lazarus/ORIGIN.json +1 -1
- package/native/NativeMotor.pas +55 -3
- package/native/NativeProtocol.pas +3 -2
- package/native/NativeRecovery.pas +274 -0
- package/native/generated/AcbrModels.pas +0 -0
- package/package.json +76 -76
- package/scripts/build-native-deps.mjs +237 -237
- package/tools/extract-model.lpr +119 -119
package/NOTICE
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
ACBr Node — integração Node.js/Free Pascal para os componentes ACBr.
|
|
2
|
-
Copyright 2026 Cosmemilton e colaboradores.
|
|
3
|
-
|
|
4
|
-
Este projeto usa os fontes públicos do Projeto ACBr, sob LGPL 2.1 ou posterior.
|
|
5
|
-
Os avisos originais das unidades e as licenças das dependências são preservados.
|
|
6
|
-
Fontes: https://svn.code.sf.net/p/acbr/code/trunk2
|
|
7
|
-
A revisão exata, hashes e materiais de reconstrução acompanham cada runtime.
|
|
8
|
-
Não é um pacote oficial do Projeto ACBr. Não depende de Delphi nem dos binários ACBr Pro.
|
|
1
|
+
ACBr Node — integração Node.js/Free Pascal para os componentes ACBr.
|
|
2
|
+
Copyright 2026 Cosmemilton e colaboradores.
|
|
3
|
+
|
|
4
|
+
Este projeto usa os fontes públicos do Projeto ACBr, sob LGPL 2.1 ou posterior.
|
|
5
|
+
Os avisos originais das unidades e as licenças das dependências são preservados.
|
|
6
|
+
Fontes: https://svn.code.sf.net/p/acbr/code/trunk2
|
|
7
|
+
A revisão exata, hashes e materiais de reconstrução acompanham cada runtime.
|
|
8
|
+
Não é um pacote oficial do Projeto ACBr. Não depende de Delphi nem dos binários ACBr Pro.
|
package/README.md
CHANGED
|
@@ -14,32 +14,33 @@ npm install cosmemilton-acbr-node
|
|
|
14
14
|
|
|
15
15
|
O npm instala o runtime opcional da sua plataforma. Não use `--omit=optional` na instalação final. Também é possível informar `runtimeDirectory` para um motor próprio.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Esta documentação descreve a versão 0.2.0, que adiciona `recuperarXml` e mantém o help TAG introduzido na 0.1.2. A disponibilidade da 0.2.0 no npm só é anunciada após a confirmação dessa versão no registry.
|
|
18
18
|
|
|
19
19
|
Para instalar tarballs revisados em um projeto separado, informe o SDK e o runtime da plataforma:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
npm install --offline --ignore-scripts --no-audit --no-fund \
|
|
23
|
-
/caminho/cosmemilton-acbr-node-0.
|
|
24
|
-
/caminho/cosmemilton-acbr-node-linux-x64-0.
|
|
23
|
+
/caminho/cosmemilton-acbr-node-0.2.0.tgz \
|
|
24
|
+
/caminho/cosmemilton-acbr-node-linux-x64-0.2.0.tgz
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
No Windows, selecione `cosmemilton-acbr-node-win32-x64-0.
|
|
27
|
+
No Windows, selecione `cosmemilton-acbr-node-win32-x64-0.2.0.tgz`.
|
|
28
28
|
|
|
29
29
|
```ts
|
|
30
|
-
import {
|
|
30
|
+
import { writeFile } from 'node:fs/promises';
|
|
31
31
|
import { criarEmissor, TNFe, type TNFeInput } from 'cosmemilton-acbr-node';
|
|
32
32
|
import { nota } from './nota.js'; // nota: TNFeInput, com dados fiscais completos
|
|
33
|
+
import { lerA1Privado } from './credencial-local.js'; // função da aplicação
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
// A aplicação lê o PFX e pede a senha de forma oculta, sem argv, env ou logs.
|
|
36
|
+
const certificado = await lerA1Privado(); // { pfx: Uint8Array, senha: string }
|
|
36
37
|
|
|
37
38
|
const emissor = criarEmissor({
|
|
38
39
|
cnpj: '11222333000181',
|
|
39
40
|
uf: 'CE',
|
|
40
41
|
modelo: 55,
|
|
41
42
|
ambiente: 2,
|
|
42
|
-
certificado
|
|
43
|
+
certificado,
|
|
43
44
|
// Para NFC-e: modelo: 65, csc: { id: '000001', valor: 'seu CSC' }
|
|
44
45
|
});
|
|
45
46
|
|
|
@@ -55,17 +56,45 @@ try {
|
|
|
55
56
|
await writeFile('./nota-assinada.xml', Buffer.from(assinado.xmlBase64, 'base64'));
|
|
56
57
|
// Transmissão explícita: await emissor.transmitir(xmlAssinado).
|
|
57
58
|
} finally {
|
|
58
|
-
await emissor.fechar();
|
|
59
|
+
try { await emissor.fechar(); }
|
|
60
|
+
finally { certificado.pfx.fill(0); certificado.senha = ''; }
|
|
59
61
|
}
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
A biblioteca também aceita XML pronto em `assinar`, `validar` e `
|
|
64
|
+
A biblioteca também aceita XML pronto em `assinar`, `validar`, `transmitir` e `recuperarXml`. As operações fiscais incluem `consultar`, `recuperarXml`, `cancelar`, `cartaCorrecao` (somente modelo 55) e `inutilizar`. `lerA1Privado` acima pertence à aplicação, não ao SDK. [API e protocolo](docs/API.md).
|
|
65
|
+
|
|
66
|
+
## Recuperar o XML autorizado
|
|
67
|
+
|
|
68
|
+
`consultar({ chave })` continua devolvendo o XML de resposta da consulta em `xmlBase64`, normalmente `retConsSitNFe`. Para obter o processo completo a partir de uma NF-e já assinada e preservada, use `recuperarXml(xmlAssinado, opcoes?)`. O argumento `opcoes` pode ser omitido e aceita somente `signal?: AbortSignal`:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const controlador = new AbortController();
|
|
72
|
+
const opcoes = { signal: controlador.signal };
|
|
73
|
+
// A interface pode chamar controlador.abort() para solicitar cancelamento.
|
|
74
|
+
const resultado = await emissor.recuperarXml(xmlAssinado, opcoes);
|
|
75
|
+
if (resultado.sucesso === true && resultado.estado === 'XML_RECUPERADO'
|
|
76
|
+
&& resultado.cStat === 100 && resultado.xmlBase64) {
|
|
77
|
+
const xmlProcessado = Buffer.from(resultado.xmlBase64, 'base64');
|
|
78
|
+
// A aplicação decide onde guardar um arquivo NOVO; o original é preservado.
|
|
79
|
+
await writeFile(arquivoProcessadoPrivado, xmlProcessado, { flag: 'wx', mode: 0o600 });
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
O sinal solicita o cancelamento da operação do SDK, sem cancelar a autorização fiscal. A consulta pode já ter sido enviada quando o sinal é acionado. Sem essa opção, chame `emissor.recuperarXml(xmlAssinado)`.
|
|
84
|
+
|
|
85
|
+
Essa operação faz uma consulta externa pela chave extraída do XML, com A1. O motor confere a assinatura da NFe original, seu schema, sua identidade e seu ambiente; só devolve `nfeProc` depois de confirmar `cStat=100`, protocolo correlacionado e `digVal` presente e exatamente igual ao `DigestValue` assinado. Não assina novamente, não emite, não transmite a nota nem grava arquivos fiscais automaticamente. A NFe e sua assinatura original são preservadas, e essa assinatura é verificada antes e depois da composição do processo; o arquivo original continua sob controle da aplicação.
|
|
86
|
+
|
|
87
|
+
Limitação da versão 0.2.0: se o `protNFe` recebido contiver uma assinatura XMLDSig (`ds:Signature`), a recuperação é recusada com `RECUPERACAO_NAO_COMPROVADA`, sem `xmlBase64` ou `protocolo`, mesmo com `cStat=100`. Esta versão não oferece verificação independente dessa assinatura adicional da SEFAZ. Nos protocolos aceitos, os dados vêm da resposta real da consulta e a prova por `digVal` continua obrigatória.
|
|
88
|
+
|
|
89
|
+
Sem digest, com divergência ou com outro status, inclusive `150` e `217`, a recuperação não retorna XML processado nem protocolo. Confira o diagnóstico; não repita nem emita uma nova nota automaticamente para obter o arquivo. `envioIniciado=false` nessa operação não significa ausência de rede: a consulta externa ocorreu sem transmissão de nota ou evento.
|
|
90
|
+
|
|
91
|
+
Atualize o pacote principal e o runtime da sua plataforma juntos. O manifesto do motor precisa anunciar `capabilities: ['recuperarXml']`; um runtime antigo é recusado com `RUNTIME_SEM_CAPACIDADE` antes de iniciar o processo. Veja o [contrato completo](docs/API.md#consulta-e-recuperação-do-xml) e o [exemplo de integração por arquivo](docs/examples/recuperar-xml.mjs), que não executa nenhuma operação ao ser importado. Os testes usam XML e A1 sintéticos com serviço SOAP local, sem SEFAZ real.
|
|
63
92
|
|
|
64
93
|
## Modelos e atualizações explícitas
|
|
65
94
|
|
|
66
95
|
Nomes, grupos e enums espelham o ACBr. Construtores recebem objetos; coleções são arrays tipados e oferecem `New()`. Valores decimais são strings, preservando escala e evitando conversão binária pelo JavaScript. Datas são strings ISO civis, por exemplo `2026-10-08T12:30:00`; o ACBr aplica o fuso da UF na escrita do XML. Campos omitidos preservam os padrões do componente. Assinatura, protocolo e informações suplementares produzidas pelo motor não são entradas editáveis.
|
|
67
96
|
|
|
68
|
-
A versão 0.
|
|
97
|
+
A versão 0.2.0 mantém a revisão SVN **48590**, com **204 tipos gerados** e contrato `e5a497cefe8e0e350a30da5cc04e88b35c38a98535893d52f878890260d1346d`. Revisão, inventário e hashes estão em `acbr.lock.json`; cliente e motor recusam contratos diferentes. Não há atualização automática dos fontes ao instalar.
|
|
69
98
|
|
|
70
99
|
```bash
|
|
71
100
|
npx acbr-node init --revision 48590
|
package/acbr.config.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
{
|
|
2
|
-
"formatVersion": 1,
|
|
3
|
-
"source": {
|
|
4
|
-
"url": "https://svn.code.sf.net/p/acbr/code/trunk2",
|
|
5
|
-
"revision": 48590,
|
|
6
|
-
"directory": ".acbr/source"
|
|
7
|
-
},
|
|
8
|
-
"generator": {
|
|
9
|
-
"outputDir": "."
|
|
10
|
-
},
|
|
11
|
-
"native": {
|
|
12
|
-
"project": "native/acbr-worker.lpr",
|
|
13
|
-
"outputDir": ".acbr/runtime",
|
|
14
|
-
"fpc": "fpc"
|
|
15
|
-
}
|
|
16
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"formatVersion": 1,
|
|
3
|
+
"source": {
|
|
4
|
+
"url": "https://svn.code.sf.net/p/acbr/code/trunk2",
|
|
5
|
+
"revision": 48590,
|
|
6
|
+
"directory": ".acbr/source"
|
|
7
|
+
},
|
|
8
|
+
"generator": {
|
|
9
|
+
"outputDir": "."
|
|
10
|
+
},
|
|
11
|
+
"native": {
|
|
12
|
+
"project": "native/acbr-worker.lpr",
|
|
13
|
+
"outputDir": ".acbr/runtime",
|
|
14
|
+
"fpc": "fpc"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/acbr.lock.json
CHANGED
|
File without changes
|
package/dist/cli.js
CHANGED
|
@@ -647,6 +647,7 @@ Log completo: ${logPath}`, { cause: compilation });
|
|
|
647
647
|
executable,
|
|
648
648
|
files,
|
|
649
649
|
generatedFileHashes: contract.fileHashes,
|
|
650
|
+
capabilities: ["recuperarXml"],
|
|
650
651
|
systemDependencies,
|
|
651
652
|
...options.testOnly ? { testOnly: true } : {}
|
|
652
653
|
};
|
|
@@ -690,7 +691,7 @@ async function checkBuild(config) {
|
|
|
690
691
|
const generated = await generatedManifest(config);
|
|
691
692
|
const directory = runtimeDirectory(config);
|
|
692
693
|
const manifest = JSON.parse(await readFile4(join2(directory, "manifest.json"), "utf8"));
|
|
693
|
-
if (manifest.formatVersion !== 1 || manifest.platform !== process.platform || manifest.arch !== process.arch || manifest.sourceRevision !== lock.source.revision || generated.sourceHash !== lock.source.treeHash || manifest.contractHash !== generated.contractHash || !manifest.generatedFileHashes || Object.entries(generated.fileHashes).some(([name, hash2]) => manifest.generatedFileHashes[name] !== hash2) || typeof manifest.executable !== "string" || !manifest.files || !Object.hasOwn(manifest.files, manifest.executable)) throw new Error("Runtime diverge das fontes ou contrato. Execute build.");
|
|
694
|
+
if (manifest.formatVersion !== 1 || manifest.platform !== process.platform || manifest.arch !== process.arch || manifest.sourceRevision !== lock.source.revision || generated.sourceHash !== lock.source.treeHash || manifest.contractHash !== generated.contractHash || !manifest.generatedFileHashes || Object.entries(generated.fileHashes).some(([name, hash2]) => manifest.generatedFileHashes[name] !== hash2) || !Array.isArray(manifest.capabilities) || !manifest.capabilities.includes("recuperarXml") || typeof manifest.executable !== "string" || !manifest.files || !Object.hasOwn(manifest.files, manifest.executable)) throw new Error("Runtime diverge das fontes ou contrato. Execute build.");
|
|
694
695
|
for (const [path4, expected] of Object.entries(manifest.files)) {
|
|
695
696
|
if (path4.startsWith("/") || path4.includes("\\") || path4.split("/").includes("..") || !/^[a-f0-9]{64}$/.test(expected)) throw new Error(`Caminho/hash inv\xE1lido no runtime: ${path4}.`);
|
|
696
697
|
if (await fileHash(join2(directory, path4)) !== expected) throw new Error(`Hash inv\xE1lido no runtime: ${path4}.`);
|
|
@@ -1174,8 +1175,13 @@ var init_generator = __esm({
|
|
|
1174
1175
|
});
|
|
1175
1176
|
|
|
1176
1177
|
// src/diagnostics.ts
|
|
1178
|
+
function base64XmlRecuperadoValido(value) {
|
|
1179
|
+
if (typeof value !== "string" || value.length === 0 || value.length > Math.ceil(MAX_RECOVERED_XML_BYTES / 3) * 4) return false;
|
|
1180
|
+
const bytes = Buffer.from(value, "base64");
|
|
1181
|
+
return bytes.length > 0 && bytes.length <= MAX_RECOVERED_XML_BYTES && bytes.toString("base64") === value;
|
|
1182
|
+
}
|
|
1177
1183
|
function etapaDaOperacao(comando) {
|
|
1178
|
-
return { gerarXml: "geracao", validar: "validacao", assinar: "assinatura", transmitir: "transmissao", consultar: "consulta", cancelar: "cancelamento", cartaCorrecao: "carta_correcao", inutilizar: "inutilizacao" }[comando] ?? "operacao";
|
|
1184
|
+
return { gerarXml: "geracao", validar: "validacao", assinar: "assinatura", transmitir: "transmissao", consultar: "consulta", recuperarXml: "consulta", cancelar: "cancelamento", cartaCorrecao: "carta_correcao", inutilizar: "inutilizacao" }[comando] ?? "operacao";
|
|
1179
1185
|
}
|
|
1180
1186
|
function criarDiagnosticoErro(codigo, desconhecido = false, detalhes = {}) {
|
|
1181
1187
|
const safeCode = Object.hasOwn(CATALOGO, codigo) ? codigo : "FALHA_LOCAL";
|
|
@@ -1212,18 +1218,30 @@ function diagnosticarResultado(resultado, comando) {
|
|
|
1212
1218
|
const protocolo = typeof resultado.protocolo === "string" && /^\d{15}$/.test(resultado.protocolo);
|
|
1213
1219
|
const chave = typeof resultado.chave === "string" && /^\d{44}$/.test(resultado.chave);
|
|
1214
1220
|
const autorizacao = (comando === "transmitir" || comando === "consultar") && resultado.estado === "AUTORIZADO" && [100, 150].includes(resultado.cStat) && protocolo && chave && (comando === "consultar" || resultado.envioIniciado !== false);
|
|
1221
|
+
const xmlRecuperado = comando === "recuperarXml" && base64XmlRecuperadoValido(resultado.xmlBase64);
|
|
1222
|
+
const recuperacao = comando === "recuperarXml" && resultado.estado === "XML_RECUPERADO" && resultado.cStat === 100 && protocolo && chave && semEvento && xmlRecuperado;
|
|
1215
1223
|
const evento = comando === "cancelar" && resultado.estado === "CANCELADO" && [135, 136].includes(resultado.cStat) && chave || comando === "cartaCorrecao" && resultado.estado === "EVENTO_REGISTRADO" && resultado.cStat === 135 && chave || comando === "inutilizar" && resultado.estado === "INUTILIZADO" && resultado.cStat === 102;
|
|
1216
1224
|
if (local && resultado.sucesso === true && semEvento) {
|
|
1217
1225
|
statusEnvio = "nao_enviado";
|
|
1218
1226
|
codigo = "ETAPA_LOCAL_CONCLUIDA";
|
|
1219
1227
|
mensagem = "A etapa local foi conclu\xEDda; esta opera\xE7\xE3o n\xE3o transmitiu a nota.";
|
|
1220
1228
|
acao = "Confira o resultado antes de seguir para a pr\xF3xima etapa.";
|
|
1229
|
+
} else if (resultado.sucesso === true && recuperacao) {
|
|
1230
|
+
statusEnvio = "autorizado";
|
|
1231
|
+
codigo = "XML_RECUPERADO";
|
|
1232
|
+
mensagem = "O XML autorizado foi recuperado por consulta; esta opera\xE7\xE3o n\xE3o transmitiu a nota.";
|
|
1233
|
+
acao = "Guarde o XML recuperado com o protocolo de autoriza\xE7\xE3o.";
|
|
1234
|
+
} else if (comando === "recuperarXml" && resultado.sucesso === false && resultado.codigo === "RECUPERACAO_NAO_COMPROVADA" && ["CONSULTADO", "ERRO"].includes(resultado.estado) && semEvento) {
|
|
1235
|
+
statusEnvio = "nao_enviado";
|
|
1236
|
+
codigo = "RECUPERACAO_NAO_COMPROVADA";
|
|
1237
|
+
mensagem = CATALOGO.RECUPERACAO_NAO_COMPROVADA[1];
|
|
1238
|
+
acao = CATALOGO.RECUPERACAO_NAO_COMPROVADA[2];
|
|
1221
1239
|
} else if (resultado.sucesso === true && (autorizacao || evento && protocolo && resultado.envioIniciado !== false)) {
|
|
1222
1240
|
statusEnvio = "autorizado";
|
|
1223
1241
|
codigo = "OPERACAO_CONFIRMADA";
|
|
1224
1242
|
mensagem = { AUTORIZADO: "A nota foi autorizada.", CANCELADO: "O cancelamento foi confirmado.", EVENTO_REGISTRADO: "O evento foi registrado.", INUTILIZADO: "A faixa foi inutilizada." }[resultado.estado];
|
|
1225
1243
|
acao = "Guarde o resultado fiscal. N\xE3o repita esta opera\xE7\xE3o.";
|
|
1226
|
-
} else if (resultado.sucesso === false && (comando === "consultar" || resultado.envioIniciado !== false) && ["transmitir", "consultar", "cancelar", "cartaCorrecao", "inutilizar"].includes(comando) && Number.isInteger(resultado.cStat) && Number(resultado.cStat) > 0 && Number(resultado.cStat) <= 9999 && (resultado.estado === "DENEGADO" && [110, 301, 302].includes(resultado.cStat) || resultado.estado === "REJEITADO" && ![100, 150, 110, 301, 302, 101, 151, 155, 103, 104, 105, 108, 109, 204, 539, 573, 135, 136, 102, 217].includes(resultado.cStat))) {
|
|
1244
|
+
} else if (resultado.sucesso === false && (comando === "consultar" || comando === "recuperarXml" || resultado.envioIniciado !== false) && ["transmitir", "consultar", "recuperarXml", "cancelar", "cartaCorrecao", "inutilizar"].includes(comando) && Number.isInteger(resultado.cStat) && Number(resultado.cStat) > 0 && Number(resultado.cStat) <= 9999 && (resultado.estado === "DENEGADO" && [110, 301, 302].includes(resultado.cStat) || resultado.estado === "REJEITADO" && ![100, 150, 110, 301, 302, 101, 151, 155, 103, 104, 105, 108, 109, 204, 539, 573, 135, 136, 102, 217].includes(resultado.cStat))) {
|
|
1227
1245
|
statusEnvio = "rejeitado";
|
|
1228
1246
|
codigo = resultado.estado === "DENEGADO" ? "NOTA_DENEGADA" : "OPERACAO_REJEITADA";
|
|
1229
1247
|
mensagem = resultado.estado === "DENEGADO" ? "A nota foi denegada." : "A opera\xE7\xE3o foi rejeitada.";
|
|
@@ -1238,13 +1256,14 @@ function diagnosticarResultado(resultado, comando) {
|
|
|
1238
1256
|
const cStat = Number.isInteger(resultado.cStat) && Number(resultado.cStat) > 0 && Number(resultado.cStat) <= 9999 ? resultado.cStat : void 0;
|
|
1239
1257
|
return Object.freeze({ codigo, etapa, mensagem, acao, statusEnvio, ...cStat !== void 0 ? { cStat } : {} });
|
|
1240
1258
|
}
|
|
1241
|
-
var ETAPAS, CAMPOS, CATALOGO;
|
|
1259
|
+
var ETAPAS, CAMPOS, MAX_RECOVERED_XML_BYTES, CATALOGO;
|
|
1242
1260
|
var init_diagnostics = __esm({
|
|
1243
1261
|
"src/diagnostics.ts"() {
|
|
1244
1262
|
"use strict";
|
|
1245
1263
|
init_types();
|
|
1246
1264
|
ETAPAS = /* @__PURE__ */ new Set(["configuracao", "documento", "runtime", "geracao", "validacao", "assinatura", "transmissao", "consulta", "cancelamento", "carta_correcao", "inutilizacao", "operacao", "limpeza"]);
|
|
1247
1265
|
CAMPOS = /* @__PURE__ */ new Set(["cnpj", "uf", "modelo", "ambiente", "contrato", "certificado", "certificado.senha", "certificado.pfx", "certificado.arquivo", "csc", "csc.id", "csc.valor", "timeoutMs", "limiteFila", "limiteBytes", "runtimeDirectory", "documento", "xml", "consulta", "chave", "recibo", "cancelamento", "protocolo", "justificativa", "cartaCorrecao", "correcao", "sequenciaEvento", "inutilizacao", "ano", "serie", "numeroInicial", "numeroFinal", "operacao", "signal"]);
|
|
1266
|
+
MAX_RECOVERED_XML_BYTES = 4 * 1024 * 1024;
|
|
1248
1267
|
CATALOGO = {
|
|
1249
1268
|
CONFIGURACAO_INVALIDA: ["configuracao", "A configura\xE7\xE3o cont\xE9m um campo inv\xE1lido.", "Confira o campo indicado e seu tipo antes de continuar."],
|
|
1250
1269
|
CONTRATO_INVALIDO: ["configuracao", "O contrato do cliente \xE9 inv\xE1lido.", "Use o contrato gerado junto com o cliente."],
|
|
@@ -1260,6 +1279,7 @@ var init_diagnostics = __esm({
|
|
|
1260
1279
|
XML_NAO_ASSINADO: ["assinatura", "A opera\xE7\xE3o exige XML previamente assinado.", "Conclua a assinatura e a valida\xE7\xE3o antes de transmitir."],
|
|
1261
1280
|
XML_ALTERADO: ["assinatura", "O XML assinado foi alterado.", "Use exatamente o XML assinado e persistido; n\xE3o edite seu conte\xFAdo."],
|
|
1262
1281
|
RETORNO_INVALIDO: ["transmissao", "A identidade ou o protocolo do retorno fiscal \xE9 inv\xE1lido.", "Consulte a situa\xE7\xE3o fiscal antes de decidir repetir a opera\xE7\xE3o."],
|
|
1282
|
+
RECUPERACAO_NAO_COMPROVADA: ["consulta", "A consulta n\xE3o comprovou a recupera\xE7\xE3o do XML autorizado.", "Preserve o XML assinado original e confira a situa\xE7\xE3o fiscal. N\xE3o transmita a nota para tentar recuperar o protocolo."],
|
|
1263
1283
|
OPERACAO_FISCAL_FALHOU: ["operacao", "O motor n\xE3o conseguiu concluir a etapa fiscal.", "Confira a configura\xE7\xE3o da etapa, os dados do XML e as depend\xEAncias do runtime."],
|
|
1264
1284
|
TLS_SERVIDOR_INVALIDO: ["transmissao", "A verifica\xE7\xE3o TLS do servi\xE7o fiscal falhou.", "Confira o servi\xE7o e a cadeia de confian\xE7a; mantenha a verifica\xE7\xE3o TLS habilitada."],
|
|
1265
1285
|
CSC_INVALIDO: ["configuracao", "A configura\xE7\xE3o do CSC \xE9 inv\xE1lida.", "Confira id e valor do CSC sem registrar seu conte\xFAdo."],
|
|
@@ -1268,6 +1288,7 @@ var init_diagnostics = __esm({
|
|
|
1268
1288
|
RUNTIME_INVALIDO: ["runtime", "O runtime fiscal \xE9 inv\xE1lido.", "Use um runtime distribu\xEDdo e compat\xEDvel com a plataforma."],
|
|
1269
1289
|
RUNTIME_INCOMPLETO: ["runtime", "Falta um arquivo do runtime fiscal.", "Reinstale o runtime da plataforma."],
|
|
1270
1290
|
RUNTIME_ALTERADO: ["runtime", "A integridade do runtime fiscal n\xE3o foi confirmada.", "Reinstale o runtime; n\xE3o desative a verifica\xE7\xE3o dos hashes."],
|
|
1291
|
+
RUNTIME_SEM_CAPACIDADE: ["runtime", "O runtime instalado n\xE3o oferece esta opera\xE7\xE3o.", "Atualize o pacote principal e o runtime da plataforma para vers\xF5es compat\xEDveis com a opera\xE7\xE3o solicitada."],
|
|
1271
1292
|
CONTRATO_DIVERGENTE: ["runtime", "Cliente e motor usam contratos diferentes.", "Use vers\xF5es compat\xEDveis ou regenere e recompile ambos."],
|
|
1272
1293
|
DOCUMENTO_INVALIDO: ["documento", "O documento cont\xE9m dados incompat\xEDveis com o modelo.", "Valide os campos e tipos do documento antes de gerar o XML."],
|
|
1273
1294
|
OPERACAO_INVALIDA: ["operacao", "Os par\xE2metros da opera\xE7\xE3o s\xE3o inv\xE1lidos.", "Informe um objeto com os campos exigidos pela opera\xE7\xE3o."],
|
|
@@ -1360,6 +1381,7 @@ async function resolveRuntime(directory, contract) {
|
|
|
1360
1381
|
throw fail2("RUNTIME_AUSENTE", "Manifesto do runtime n\xE3o encontrado.");
|
|
1361
1382
|
}
|
|
1362
1383
|
if (!validContract(m) || m.formatVersion !== 1 || !plain(m.files) || typeof m.executable !== "string" || m.platform !== process.platform || m.arch !== process.arch) throw fail2("RUNTIME_INVALIDO", "Manifesto ou plataforma do runtime inv\xE1lidos.");
|
|
1384
|
+
if (m.capabilities !== void 0 && (!Array.isArray(m.capabilities) || !m.capabilities.every((value) => typeof value === "string" && /^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(value)))) throw fail2("RUNTIME_INVALIDO", "Capacidades do runtime inv\xE1lidas.");
|
|
1363
1385
|
if (m.sourceRevision !== contract.sourceRevision || m.contractHash !== contract.contractHash) throw fail2("CONTRATO_DIVERGENTE", "Cliente e motor foram gerados de contratos diferentes; regenere e recompile.");
|
|
1364
1386
|
for (const [name, hash2] of Object.entries(m.files)) {
|
|
1365
1387
|
if (typeof hash2 !== "string" || !/^[a-f0-9]{64}$/.test(hash2)) throw fail2("RUNTIME_INVALIDO", "Hash inv\xE1lido no runtime.");
|
|
@@ -1374,7 +1396,7 @@ async function resolveRuntime(directory, contract) {
|
|
|
1374
1396
|
if (createHash4("sha256").update(data).digest("hex") !== hash2) throw fail2("RUNTIME_ALTERADO", "O runtime n\xE3o corresponde ao manifesto distribu\xEDdo.");
|
|
1375
1397
|
}
|
|
1376
1398
|
if (!m.files[m.executable]) throw fail2("RUNTIME_INVALIDO", "O execut\xE1vel n\xE3o consta no invent\xE1rio.");
|
|
1377
|
-
return { directory: root, executable: confined(root, m.executable) };
|
|
1399
|
+
return { directory: root, executable: confined(root, m.executable), capabilities: new Set(m.capabilities ?? []) };
|
|
1378
1400
|
}
|
|
1379
1401
|
function criarEmissor(options) {
|
|
1380
1402
|
const configFail = (campo) => fail2("CONFIGURACAO_INVALIDA", "Informe campos e tipos v\xE1lidos na configura\xE7\xE3o.", void 0, false, { etapa: "configuracao", campo });
|
|
@@ -1391,7 +1413,7 @@ function criarEmissor(options) {
|
|
|
1391
1413
|
if (options.validaDocumento !== void 0 && typeof options.validaDocumento !== "function" || options.criaProcesso !== void 0 && typeof options.criaProcesso !== "function") throw configFail();
|
|
1392
1414
|
const cfg = { cnpj: options.cnpj, uf: options.uf, modelo: options.modelo, ambiente: options.ambiente, csc: options.csc ? { ...options.csc } : void 0 };
|
|
1393
1415
|
const certificate = cert ? { ...cert, pfx: cert.pfx ? Buffer.from(cert.pfx) : void 0 } : void 0;
|
|
1394
|
-
const contract = {
|
|
1416
|
+
const contract = { sourceRevision: options.contrato.sourceRevision, contractHash: options.contrato.contractHash };
|
|
1395
1417
|
const timeout = options.timeoutMs ?? 12e4, capacity = options.limiteFila ?? 32, bytesLimit = options.limiteBytes ?? MAX_BYTES;
|
|
1396
1418
|
if (!Number.isInteger(timeout) || timeout < 1 || timeout > 12e4) throw configFail("timeoutMs");
|
|
1397
1419
|
if (!Number.isInteger(capacity) || capacity < 0 || capacity > 1e3) throw configFail("limiteFila");
|
|
@@ -1414,6 +1436,7 @@ function criarEmissor(options) {
|
|
|
1414
1436
|
async function execute2(item) {
|
|
1415
1437
|
if (closed || item.signal?.aborted) throw fail2("OPERACAO_CANCELADA", "Opera\xE7\xE3o cancelada antes da execu\xE7\xE3o.");
|
|
1416
1438
|
const runtime = await (runtimePromise ??= resolveRuntime(options.runtimeDirectory, contract));
|
|
1439
|
+
if (item.req.comando === "recuperarXml" && !runtime.capabilities.has("recuperarXml")) throw fail2("RUNTIME_SEM_CAPACIDADE", "O runtime n\xE3o oferece recupera\xE7\xE3o de XML; atualize o pacote e o runtime compat\xEDvel.", item.req, false, { etapa: "runtime" });
|
|
1417
1440
|
if (closed || item.signal?.aborted) throw fail2("OPERACAO_CANCELADA", "Opera\xE7\xE3o cancelada antes da execu\xE7\xE3o.");
|
|
1418
1441
|
return await new Promise((done, rejectExecution) => {
|
|
1419
1442
|
let stopError;
|
|
@@ -1536,9 +1559,11 @@ function criarEmissor(options) {
|
|
|
1536
1559
|
continue;
|
|
1537
1560
|
}
|
|
1538
1561
|
if (result || frame.versao !== 1 || frame.sourceRevision !== contract.sourceRevision || frame.contractHash !== contract.contractHash || typeof frame.sucesso !== "boolean" || !STATES.has(String(frame.estado))) throw Error();
|
|
1539
|
-
if (frame.xmlBase64 !== void 0 && (typeof frame.xmlBase64 !== "string" || !frame.xmlBase64.length || Buffer.from(frame.xmlBase64, "base64").toString("base64") !== frame.xmlBase64)) throw Error();
|
|
1562
|
+
if (frame.xmlBase64 !== void 0 && (item.req.comando === "recuperarXml" ? !base64XmlRecuperadoValido(frame.xmlBase64) : typeof frame.xmlBase64 !== "string" || !frame.xmlBase64.length || Buffer.from(frame.xmlBase64, "base64").toString("base64") !== frame.xmlBase64)) throw Error();
|
|
1540
1563
|
if (frame.cStat !== void 0 && (!Number.isInteger(frame.cStat) || Number(frame.cStat) < 1 || Number(frame.cStat) > 9999)) throw Error();
|
|
1541
1564
|
if (frame.envioIniciado !== void 0 && typeof frame.envioIniciado !== "boolean" || sending && frame.envioIniciado === false) throw Error();
|
|
1565
|
+
if (frame.estado === "XML_RECUPERADO" && item.req.comando !== "recuperarXml") throw Error();
|
|
1566
|
+
if (item.req.comando === "recuperarXml" && (frame.sucesso === true || frame.estado === "XML_RECUPERADO") && (frame.sucesso !== true || frame.estado !== "XML_RECUPERADO" || frame.cStat !== 100 || typeof frame.chave !== "string" || !/^\d{44}$/.test(frame.chave) || typeof frame.protocolo !== "string" || !/^\d{15}$/.test(frame.protocolo) || typeof frame.xmlBase64 !== "string" || frame.envioIniciado === true || sending)) throw Error();
|
|
1542
1567
|
const fiscal = frame;
|
|
1543
1568
|
result = { ...fiscal, diagnostico: diagnosticarResultado(sending ? { ...fiscal, envioIniciado: true } : fiscal, item.req.comando) };
|
|
1544
1569
|
} catch {
|
|
@@ -1588,7 +1613,7 @@ function criarEmissor(options) {
|
|
|
1588
1613
|
if (signal !== void 0 && (!plain(signal) || typeof signal.aborted !== "boolean" || typeof signal.addEventListener !== "function" || typeof signal.removeEventListener !== "function")) throw fail2("OPERACAO_INVALIDA", "Informe op\xE7\xF5es v\xE1lidas para a opera\xE7\xE3o.", void 0, false, { etapa: etapaDaOperacao(command), campo: "signal" });
|
|
1589
1614
|
if (closed) throw closedError();
|
|
1590
1615
|
if (operation.signal?.aborted) throw fail2("OPERACAO_CANCELADA", "Opera\xE7\xE3o cancelada antes da execu\xE7\xE3o.");
|
|
1591
|
-
if (["assinar", "transmitir", "consultar", "cancelar", "cartaCorrecao", "inutilizar"].includes(command) && !certificate) throw fail2("CERTIFICADO_AUSENTE", "A opera\xE7\xE3o exige um certificado A1.");
|
|
1616
|
+
if (["assinar", "transmitir", "consultar", "recuperarXml", "cancelar", "cartaCorrecao", "inutilizar"].includes(command) && !certificate) throw fail2("CERTIFICADO_AUSENTE", "A opera\xE7\xE3o exige um certificado A1.", void 0, false, { etapa: etapaDaOperacao(command) });
|
|
1592
1617
|
if (command === "gerarXml") options.validaDocumento?.(data.documento);
|
|
1593
1618
|
const config = { ...cfg };
|
|
1594
1619
|
if (certificate) {
|
|
@@ -1603,7 +1628,7 @@ function criarEmissor(options) {
|
|
|
1603
1628
|
}
|
|
1604
1629
|
let bytes, req;
|
|
1605
1630
|
try {
|
|
1606
|
-
bytes = Buffer.from(JSON.stringify({ versao: 1, id: randomUUID3(),
|
|
1631
|
+
bytes = Buffer.from(JSON.stringify({ ...data, ...contract, versao: 1, id: randomUUID3(), comando: command, config }) + "\n", "utf8");
|
|
1607
1632
|
if (bytes.length > bytesLimit) throw Error();
|
|
1608
1633
|
req = JSON.parse(bytes.toString("utf8"));
|
|
1609
1634
|
} catch {
|
|
@@ -1654,25 +1679,26 @@ function criarEmissor(options) {
|
|
|
1654
1679
|
validar: (xml, op) => request("validar", xmlData(xml), op),
|
|
1655
1680
|
assinar: (xml, op) => request("assinar", xmlData(xml), op),
|
|
1656
1681
|
transmitir: (xml, op) => request("transmitir", xmlData(xml), op),
|
|
1682
|
+
recuperarXml: (xmlAssinado, opcoes) => request("recuperarXml", xmlData(xmlAssinado), opcoes),
|
|
1657
1683
|
consultar: (consulta, op) => {
|
|
1658
1684
|
checkObject(consulta, "consulta");
|
|
1659
1685
|
checkKey(consulta.chave);
|
|
1660
1686
|
if (consulta.recibo !== void 0 && (typeof consulta.recibo !== "string" || !/^\d{15}$/.test(consulta.recibo))) throw fail2("RECIBO_INVALIDO", "Recibo inv\xE1lido.", void 0, false, { campo: "recibo" });
|
|
1661
|
-
return request("consultar", consulta, op);
|
|
1687
|
+
return request("consultar", { chave: consulta.chave, ...consulta.recibo !== void 0 ? { recibo: consulta.recibo } : {} }, op);
|
|
1662
1688
|
},
|
|
1663
1689
|
cancelar: (data, op) => {
|
|
1664
1690
|
checkObject(data, "cancelamento");
|
|
1665
1691
|
checkKey(data.chave);
|
|
1666
1692
|
reason(data.justificativa);
|
|
1667
1693
|
if (typeof data.protocolo !== "string" || !/^\d{15}$/.test(data.protocolo)) throw fail2("PROTOCOLO_INVALIDO", "Protocolo inv\xE1lido.", void 0, false, { campo: "protocolo" });
|
|
1668
|
-
return request("cancelar", data, op);
|
|
1694
|
+
return request("cancelar", { chave: data.chave, protocolo: data.protocolo, justificativa: data.justificativa }, op);
|
|
1669
1695
|
},
|
|
1670
1696
|
cartaCorrecao: (data, op) => {
|
|
1671
1697
|
checkObject(data, "cartaCorrecao");
|
|
1672
1698
|
if (cfg.modelo !== 55) throw fail2("OPERACAO_NAO_SUPORTADA", "Carta de corre\xE7\xE3o exige NF-e modelo 55.");
|
|
1673
1699
|
checkKey(data.chave);
|
|
1674
1700
|
if (typeof data.correcao !== "string" || data.correcao.length < 15 || data.correcao.length > 1e3 || !Number.isInteger(data.sequenciaEvento ?? 1) || (data.sequenciaEvento ?? 1) < 1 || (data.sequenciaEvento ?? 1) > 20) throw fail2("CORRECAO_INVALIDA", "Dados da carta de corre\xE7\xE3o inv\xE1lidos.", void 0, false, { campo: "cartaCorrecao" });
|
|
1675
|
-
return request("cartaCorrecao", {
|
|
1701
|
+
return request("cartaCorrecao", { chave: data.chave, correcao: data.correcao, sequenciaEvento: data.sequenciaEvento ?? 1 }, op);
|
|
1676
1702
|
},
|
|
1677
1703
|
inutilizar: (data, op) => {
|
|
1678
1704
|
checkObject(data, "inutilizacao");
|
|
@@ -1703,7 +1729,7 @@ var init_runtime = __esm({
|
|
|
1703
1729
|
init_diagnostics();
|
|
1704
1730
|
init_types();
|
|
1705
1731
|
init_diagnostics();
|
|
1706
|
-
STATES = /* @__PURE__ */ new Set(["DENEGADO", "XML_GERADO", "VALIDADO", "ASSINADO", "AUTORIZADO", "REJEITADO", "PENDENTE", "INDETERMINADO", "CONSULTADO", "CANCELADO", "EVENTO_REGISTRADO", "INUTILIZADO", "ERRO"]);
|
|
1732
|
+
STATES = /* @__PURE__ */ new Set(["DENEGADO", "XML_GERADO", "VALIDADO", "ASSINADO", "XML_RECUPERADO", "AUTORIZADO", "REJEITADO", "PENDENTE", "INDETERMINADO", "CONSULTADO", "CANCELADO", "EVENTO_REGISTRADO", "INUTILIZADO", "ERRO"]);
|
|
1707
1733
|
UFS = new Set("AC AL AP AM BA CE DF ES GO MA MT MS MG PA PB PR PE PI RJ RN RS RO RR SC SP SE TO".split(" "));
|
|
1708
1734
|
MAX_BYTES = 8 * 1024 * 1024;
|
|
1709
1735
|
MAX_TEARDOWN_MS = 1e3;
|