@sinete/nfe 0.1.0 → 0.3.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 (90) hide show
  1. package/README.md +55 -50
  2. package/dist/build/build.d.ts +23 -23
  3. package/dist/build/build.d.ts.map +1 -1
  4. package/dist/build/destinatario.d.ts +2 -2
  5. package/dist/build/ibscbs.d.ts.map +1 -1
  6. package/dist/build/nfce.d.ts +5 -5
  7. package/dist/build/nfce.d.ts.map +1 -1
  8. package/dist/build/pl.d.ts +4 -4
  9. package/dist/build/pl.d.ts.map +1 -1
  10. package/dist/build/rejeicoes.d.ts +7 -7
  11. package/dist/build/rejeicoes.d.ts.map +1 -1
  12. package/dist/build/textos.d.ts +26 -0
  13. package/dist/build/textos.d.ts.map +1 -0
  14. package/dist/build/values.d.ts +8 -8
  15. package/dist/build/values.d.ts.map +1 -1
  16. package/dist/decimal.d.ts +2 -2
  17. package/dist/decimal.d.ts.map +1 -1
  18. package/dist/format.d.ts +19 -19
  19. package/dist/format.d.ts.map +1 -1
  20. package/dist/ibs-cbs.d.ts +10 -5
  21. package/dist/ibs-cbs.d.ts.map +1 -1
  22. package/dist/ibs-cbs.js +35 -35
  23. package/dist/ibs-cbs.js.map +3 -3
  24. package/dist/index.d.ts +18 -18
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +796 -588
  27. package/dist/index.js.map +27 -26
  28. package/dist/issues.d.ts +10 -10
  29. package/dist/issues.d.ts.map +1 -1
  30. package/dist/model.d.ts +4 -4
  31. package/dist/ports.d.ts +15 -15
  32. package/dist/ports.d.ts.map +1 -1
  33. package/dist/rotulo.d.ts +2 -2
  34. package/dist/rotulo.d.ts.map +1 -1
  35. package/dist/rtc.d.ts +23 -23
  36. package/dist/rtc.d.ts.map +1 -1
  37. package/dist/services/client.d.ts +44 -40
  38. package/dist/services/client.d.ts.map +1 -1
  39. package/dist/services/gzip.d.ts +1 -1
  40. package/dist/services/gzip.d.ts.map +1 -1
  41. package/dist/services/index.d.ts +4 -4
  42. package/dist/services/index.d.ts.map +1 -1
  43. package/dist/services/outcome.d.ts +3 -3
  44. package/dist/services/outcome.d.ts.map +1 -1
  45. package/dist/services/proc.d.ts +6 -6
  46. package/dist/services/proc.d.ts.map +1 -1
  47. package/dist/services/recuperar.d.ts +8 -7
  48. package/dist/services/recuperar.d.ts.map +1 -1
  49. package/dist/services/resolver.d.ts +12 -11
  50. package/dist/services/resolver.d.ts.map +1 -1
  51. package/dist/services/soap.d.ts +8 -8
  52. package/dist/services/soap.d.ts.map +1 -1
  53. package/dist/time.d.ts +4 -4
  54. package/dist/time.d.ts.map +1 -1
  55. package/dist/versao-gerada.d.ts +1 -1
  56. package/package.json +9 -9
  57. package/src/build/build.ts +213 -203
  58. package/src/build/destinatario.ts +8 -8
  59. package/src/build/ibscbs.ts +10 -6
  60. package/src/build/icms.ts +6 -6
  61. package/src/build/nfce.ts +19 -19
  62. package/src/build/pl.ts +12 -12
  63. package/src/build/rejeicoes.ts +13 -13
  64. package/src/build/textos.ts +228 -0
  65. package/src/build/values.ts +14 -14
  66. package/src/data/arredondamento.json +1 -1
  67. package/src/data/cstat.json +6 -6
  68. package/src/data/fusos.json +1 -1
  69. package/src/data/nfce-urls.json +2 -2
  70. package/src/data/produtor-rural.json +1 -1
  71. package/src/data/resp-tec.json +1 -1
  72. package/src/decimal.ts +10 -9
  73. package/src/format.ts +29 -28
  74. package/src/ibs-cbs.ts +79 -74
  75. package/src/index.ts +29 -27
  76. package/src/issues.ts +11 -11
  77. package/src/model.ts +4 -4
  78. package/src/ports.ts +15 -15
  79. package/src/rotulo.ts +4 -4
  80. package/src/rtc.ts +91 -91
  81. package/src/services/client.ts +248 -217
  82. package/src/services/gzip.ts +7 -7
  83. package/src/services/index.ts +12 -10
  84. package/src/services/outcome.ts +9 -9
  85. package/src/services/proc.ts +50 -49
  86. package/src/services/recuperar.ts +29 -25
  87. package/src/services/resolver.ts +25 -23
  88. package/src/services/soap.ts +35 -35
  89. package/src/time.ts +6 -6
  90. package/src/versao-gerada.ts +1 -1
@@ -3,24 +3,24 @@
3
3
  * plataforma (Node 18+, Bun, Deno e browsers), sem dependência.
4
4
  */
5
5
 
6
- import { ProtocolError, UnsupportedError } from '@sinete/core';
7
- import { base64Decode } from '@sinete/core/xml';
6
+ import { ErroNaoSuportado, ErroRespostaInvalida } from '@sinete/core';
7
+ import { decodificarBase64 } from '@sinete/core/xml';
8
8
 
9
9
  /** Base64 de um gzip para o texto UTF-8 de dentro. */
10
- export async function gunzipBase64(b64: string): Promise<string> {
10
+ export async function descomprimirGzipBase64(b64: string): Promise<string> {
11
11
  const Ds = (globalThis as { DecompressionStream?: typeof DecompressionStream }).DecompressionStream;
12
12
  if (Ds === undefined)
13
- throw new UnsupportedError('DecompressionStream indisponível nesta runtime; docZip não pode ser lido');
13
+ throw new ErroNaoSuportado('DecompressionStream indisponível nesta runtime; docZip não pode ser lido');
14
14
  let bytes: Uint8Array<ArrayBuffer>;
15
15
  try {
16
- bytes = base64Decode(b64.trim());
16
+ bytes = decodificarBase64(b64.trim());
17
17
  } catch (cause) {
18
- throw new ProtocolError('docZip com base64 inválido', { cause });
18
+ throw new ErroRespostaInvalida('docZip com base64 inválido', { cause });
19
19
  }
20
20
  try {
21
21
  const stream = new Blob([bytes]).stream().pipeThrough(new Ds('gzip'));
22
22
  return await new Response(stream).text();
23
23
  } catch (cause) {
24
- throw new ProtocolError('docZip não é um gzip válido', { cause });
24
+ throw new ErroRespostaInvalida('docZip não é um gzip válido', { cause });
25
25
  }
26
26
  }
@@ -5,38 +5,40 @@
5
5
 
6
6
  export type {
7
7
  AutorDocumento,
8
- AutorizacaoOutcome,
9
8
  AutorizarOpcoes,
10
9
  Cadastro,
11
10
  CadastroPedido,
12
11
  CancelamentoPedido,
13
12
  CancelamentoSubstituicaoPedido,
14
13
  CartaCorrecaoPedido,
14
+ ClienteNfe,
15
+ ClienteNfeOpcoes,
15
16
  ConsultaNfe,
16
- ConsultaOutcome,
17
17
  ConsultaReciboOpcoes,
18
18
  Distribuicao,
19
19
  DistribuicaoConsulta,
20
20
  DistribuicaoOpcoes,
21
21
  DocumentoDistribuido,
22
- EventoOutcome,
22
+ EnvioOpcoes,
23
+ Espera,
23
24
  EventoRegistrado,
24
25
  Inutilizacao,
25
- InutilizacaoOutcome,
26
26
  InutilizacaoPedido,
27
27
  ManifestacaoPedido,
28
28
  ManifestacaoTipo,
29
- NfeClient,
30
- NfeClientOptions,
31
29
  PoliticaRecibo,
32
30
  ProtocoloNfe,
33
- Sleep,
31
+ ResultadoAutorizacao,
32
+ ResultadoConsulta,
33
+ ResultadoEvento,
34
+ ResultadoInutilizacao,
34
35
  StatusServico,
36
+ StatusServicoOpcoes,
35
37
  } from './client.ts';
36
- export { autorizadorContingencia, createNfeClient } from './client.ts';
37
- export { gunzipBase64 } from './gzip.ts';
38
+ export { autorizadorContingencia, criarClienteNfe } from './client.ts';
39
+ export { descomprimirGzipBase64 } from './gzip.ts';
38
40
  export type { DocumentoAssinado } from './proc.ts';
39
- export { documentoAssinado, nfeAssinadaDoProc, sliceElement } from './proc.ts';
41
+ export { documentoAssinado, nfeAssinadaDoProc, recortarElemento } from './proc.ts';
40
42
  export type { RecuperacaoEvento } from './recuperar.ts';
41
43
  export { recuperarEventoRegistrado } from './recuperar.ts';
42
44
  export type { ConteudoDoProtocolo, ResolucaoEnvio } from './resolver.ts';
@@ -3,9 +3,9 @@
3
3
  * Nenhuma lista de códigos fica no código: quem decide se um `cStat` é autorização, denegação ou pendência é o dado.
4
4
  */
5
5
 
6
- import type { Rejected, RejectionHint, SefazStatus } from '@sinete/core';
7
- import { rejected } from '@sinete/core';
8
- import { enrichRejected } from '@sinete/rejeicoes';
6
+ import type { DicaRejeicao, Recusado, StatusSefaz } from '@sinete/core';
7
+ import { criarRecusado } from '@sinete/core';
8
+ import { completarRecusado } from '@sinete/rejeicoes';
9
9
  import table from '../data/cstat.json' with { type: 'json' };
10
10
 
11
11
  /** Classes de `cStat` que a tabela descreve. */
@@ -31,7 +31,7 @@ export type CStatClasse =
31
31
  | 'consumoIndevido';
32
32
 
33
33
  const CLASSES: Readonly<Record<CStatClasse, readonly string[]>> = table;
34
- const DICAS: Readonly<Record<string, RejectionHint>> = table.dicas;
34
+ const DICAS: Readonly<Record<string, DicaRejeicao>> = table.dicas;
35
35
 
36
36
  /** O `cStat` pertence à classe da tabela. */
37
37
  export function cstatEm(cStat: string, classe: CStatClasse): boolean {
@@ -39,12 +39,12 @@ export function cstatEm(cStat: string, classe: CStatClasse): boolean {
39
39
  }
40
40
 
41
41
  /**
42
- * Desfecho `rejected` enriquecido pelo `@sinete/rejeicoes`; sem curadoria lá, usa a dica própria do `@sinete/nfe`
42
+ * Desfecho `recusado` enriquecido pelo `@sinete/rejeicoes`; sem curadoria lá, usa a dica própria do `@sinete/nfe`
43
43
  * (`dicas` em `data/cstat.json`) quando houver.
44
44
  */
45
- export function rejeitado(status: SefazStatus): Rejected {
46
- const r = enrichRejected(rejected(status));
47
- if (r.hint !== undefined) return r;
45
+ export function rejeitado(status: StatusSefaz): Recusado {
46
+ const r = completarRecusado(criarRecusado(status));
47
+ if (r.dica !== undefined) return r;
48
48
  const dica = Object.hasOwn(DICAS, status.cStat) ? DICAS[status.cStat] : undefined;
49
- return dica === undefined ? r : rejected(status, dica);
49
+ return dica === undefined ? r : criarRecusado(status, dica);
50
50
  }
@@ -4,15 +4,15 @@
4
4
  * para gerar saída nem reserializado (invariante do repositório, ADR 0003).
5
5
  */
6
6
 
7
- import { ConfigError, ProtocolError } from '@sinete/core';
8
- import type { XmlDocument, XmlElement } from '@sinete/core/xml';
7
+ import { ErroDeConfiguracao, ErroRespostaInvalida } from '@sinete/core';
8
+ import type { DocumentoXml, ElementoXml } from '@sinete/core/xml';
9
9
  import {
10
- attributeOf,
11
- descendants,
12
- firstChild,
13
- inScopeNamespaces,
14
- parseXml,
15
- textOf,
10
+ atributoDe,
11
+ descendentes,
12
+ lerXml,
13
+ namespacesEmEscopo,
14
+ primeiroFilho,
15
+ textoDe,
16
16
  XMLDSIG_NS,
17
17
  } from '@sinete/core/xml';
18
18
 
@@ -23,11 +23,11 @@ const XML_DECL = /^?<\?xml[^?]*\?>\s*/;
23
23
 
24
24
  /**
25
25
  * Recorta o elemento da fonte, acrescentando na tag de abertura só as declarações de namespace que ele usa e que
26
- * estão em ancestrais fora do recorte. O default entra apenas quando difere de `parentDefaultNs` (o default do
26
+ * estão em ancestrais fora do recorte. O default entra apenas quando difere de `nsPadraoDoPai` (o default do
27
27
  * envelope onde a fatia vai morar).
28
28
  */
29
- export function sliceElement(doc: XmlDocument, el: XmlElement, parentDefaultNs: string = NFE_NS): string {
30
- const slice = doc.source.slice(el.start, el.end);
29
+ export function recortarElemento(documento: DocumentoXml, el: ElementoXml, nsPadraoDoPai: string = NFE_NS): string {
30
+ const slice = documento.texto.slice(el.inicio, el.fim);
31
31
  // Só os prefixos usados por algum elemento ou atributo cuja declaração está fora do recorte: um prefixo redeclarado
32
32
  // dentro dele não pode ganhar outra declaração na raiz, que mudaria o C14N inclusivo de um irmão assinado.
33
33
  //
@@ -36,39 +36,39 @@ export function sliceElement(doc: XmlDocument, el: XmlElement, parentDefaultNs:
36
36
  // protocolo vai para o proc como o leiaute pede, e é assim que o proc confere depois. Copiar para a fatia todo
37
37
  // namespace em escopo mudaria o C14N inclusivo do documento assinado pelo emitente, que nunca viu o envelope.
38
38
  const used = new Set<string>();
39
- const cobertoDentro = (d: XmlElement, p: string): boolean => {
40
- for (let e: XmlElement | null = d; e; e = e === el ? null : e.parent) if (e.namespaces.has(p)) return true;
39
+ const cobertoDentro = (d: ElementoXml, p: string): boolean => {
40
+ for (let e: ElementoXml | null = d; e; e = e === el ? null : e.pai) if (e.namespaces.has(p)) return true;
41
41
  return false;
42
42
  };
43
- for (const d of descendants(el)) {
44
- const prefixos = [d.prefix, ...d.attributes.map((a) => a.prefix)].filter((p) => p !== '' && p !== 'xml');
43
+ for (const d of descendentes(el)) {
44
+ const prefixos = [d.prefixo, ...d.atributos.map((a) => a.prefixo)].filter((p) => p !== '' && p !== 'xml');
45
45
  for (const p of prefixos) if (!cobertoDentro(d, p)) used.add(p);
46
46
  }
47
47
  const extra: string[] = [];
48
48
  const inScope = new Map<string, string>();
49
- for (let e: XmlElement | null = el.parent; e; e = e.parent) {
49
+ for (let e: ElementoXml | null = el.pai; e; e = e.pai) {
50
50
  for (const [p, u] of e.namespaces) if (!inScope.has(p)) inScope.set(p, u);
51
51
  }
52
52
  // O default herdado de fora entra quando algum elemento sem prefixo do recorte (a raiz ou um descendente, inclusive
53
53
  // sob uma raiz prefixada) depende dele e ele difere do default do envelope de destino.
54
54
  const herdado = inScope.get('') ?? '';
55
- if (herdado !== parentDefaultNs && usaDefaultHerdado(el)) extra.push(` xmlns="${herdado}"`);
55
+ if (herdado !== nsPadraoDoPai && usaDefaultHerdado(el)) extra.push(` xmlns="${herdado}"`);
56
56
  for (const p of [...used].sort()) {
57
57
  const uri = inScope.get(p);
58
- if (uri === undefined) throw new ProtocolError(`prefixo ${p} sem declaração no recorte de ${el.name}`);
58
+ if (uri === undefined) throw new ErroRespostaInvalida(`prefixo ${p} sem declaração no recorte de ${el.nome}`);
59
59
  extra.push(` xmlns:${p}="${uri}"`);
60
60
  }
61
61
  if (extra.length === 0) return slice;
62
- const at = 1 + el.name.length;
62
+ const at = 1 + el.nome.length;
63
63
  return slice.slice(0, at) + extra.join('') + slice.slice(at);
64
64
  }
65
65
 
66
66
  /** Algum elemento sem prefixo do recorte resolve o default por uma declaração de fora do recorte. */
67
- function usaDefaultHerdado(raiz: XmlElement): boolean {
68
- for (const d of descendants(raiz)) {
69
- if (d.prefix !== '') continue;
67
+ function usaDefaultHerdado(raiz: ElementoXml): boolean {
68
+ for (const d of descendentes(raiz)) {
69
+ if (d.prefixo !== '') continue;
70
70
  let coberto = false;
71
- for (let e: XmlElement | null = d; e; e = e === raiz ? null : e.parent) {
71
+ for (let e: ElementoXml | null = d; e; e = e === raiz ? null : e.pai) {
72
72
  if (e.namespaces.has('')) {
73
73
  coberto = true;
74
74
  break;
@@ -87,41 +87,42 @@ export interface DocumentoAssinado {
87
87
  readonly id: string;
88
88
  /** DigestValue da assinatura (base64). */
89
89
  readonly digestValue: string;
90
- readonly doc: XmlDocument;
90
+ readonly documento: DocumentoXml;
91
91
  }
92
92
 
93
93
  /**
94
94
  * Confere que `xml` é um documento `raiz` assinado (no namespace da NF-e, com `Signature` referenciando o filho
95
- * `elemento`) e devolve a string sem a declaração XML. Lança `ConfigError` para qualquer outra coisa: o serviço nunca
95
+ * `elemento`) e devolve a string sem a declaração XML. Lança `ErroDeConfiguracao` para qualquer outra coisa: o serviço nunca
96
96
  * "conserta" o documento de quem chama.
97
97
  */
98
98
  export function documentoAssinado(xml: string, raiz: string, elemento: string): DocumentoAssinado {
99
99
  const text = xml.replace(XML_DECL, '');
100
- let doc: XmlDocument;
100
+ let doc: DocumentoXml;
101
101
  try {
102
- doc = parseXml(text);
102
+ doc = lerXml(text);
103
103
  } catch (cause) {
104
- throw new ConfigError(`${raiz} assinado malformado`, { cause });
104
+ throw new ErroDeConfiguracao(`${raiz} assinado malformado`, { cause });
105
105
  }
106
- if (doc.root.local !== raiz || doc.root.ns !== NFE_NS) {
107
- throw new ConfigError(`esperado <${raiz}> no namespace da NF-e, veio <${doc.root.name}>`);
106
+ if (doc.raiz.local !== raiz || doc.raiz.ns !== NFE_NS) {
107
+ throw new ErroDeConfiguracao(`esperado <${raiz}> no namespace da NF-e, veio <${doc.raiz.nome}>`);
108
108
  }
109
109
  // O envelope (nfeProc, procEventoNFe) declara o default da NF-e; o C14N inclusivo herda os namespaces dos
110
110
  // ancestrais, então a raiz assinada precisa declarar ela mesma esse default, ou a assinatura deixa de conferir
111
111
  // dentro do envelope. Não se conserta o documento de quem chama: recusa.
112
- if (doc.root.namespaces.get('') !== NFE_NS) {
113
- throw new ConfigError(`<${raiz}> assinado precisa declarar xmlns="${NFE_NS}" na própria raiz`);
112
+ if (doc.raiz.namespaces.get('') !== NFE_NS) {
113
+ throw new ErroDeConfiguracao(`<${raiz}> assinado precisa declarar xmlns="${NFE_NS}" na própria raiz`);
114
114
  }
115
- const alvo = firstChild(doc.root, elemento, NFE_NS);
116
- const id = alvo ? attributeOf(alvo, 'Id') : undefined;
117
- const sig = firstChild(doc.root, 'Signature', XMLDSIG_NS);
115
+ const alvo = primeiroFilho(doc.raiz, elemento, NFE_NS);
116
+ const id = alvo ? atributoDe(alvo, 'Id') : undefined;
117
+ const sig = primeiroFilho(doc.raiz, 'Signature', XMLDSIG_NS);
118
118
  const digest = sig && descendantText(sig, 'DigestValue');
119
- if (!id || digest === undefined) throw new ConfigError(`${raiz} sem ${elemento} identificado ou sem assinatura`);
120
- return { xml: text, id, digestValue: digest, doc };
119
+ if (!id || digest === undefined)
120
+ throw new ErroDeConfiguracao(`${raiz} sem ${elemento} identificado ou sem assinatura`);
121
+ return { xml: text, id, digestValue: digest, documento: doc };
121
122
  }
122
123
 
123
- function descendantText(el: XmlElement, local: string): string | undefined {
124
- for (const d of descendants(el)) if (d.local === local && d.ns === XMLDSIG_NS) return textOf(d).trim();
124
+ function descendantText(el: ElementoXml, local: string): string | undefined {
125
+ for (const d of descendentes(el)) if (d.local === local && d.ns === XMLDSIG_NS) return textoDe(d).trim();
125
126
  return undefined;
126
127
  }
127
128
 
@@ -130,29 +131,29 @@ function descendantText(el: XmlElement, local: string): string | undefined {
130
131
  * pronta para `consultar`, `resolverEnvioSemResposta` e a retomada, que recusam raiz sem `xmlns` próprio. O `NFe` dentro
131
132
  * do proc herda os namespaces do envelope; eles são declarados na raiz da fatia. O C14N inclusivo do `infNFe` já os
132
133
  * enxergava em escopo, então o digest e a assinatura são os mesmos dentro e fora do proc. Nada mais muda nos bytes.
133
- * Lança `ConfigError` quando não há NF-e assinada (proc de outro documento, NFe sem assinatura, XML malformado).
134
+ * Lança `ErroDeConfiguracao` quando não há NF-e assinada (proc de outro documento, NFe sem assinatura, XML malformado).
134
135
  */
135
136
  export function nfeAssinadaDoProc(xml: string): string {
136
137
  const text = xml.replace(XML_DECL, '');
137
- let doc: XmlDocument;
138
+ let doc: DocumentoXml;
138
139
  try {
139
- doc = parseXml(text);
140
+ doc = lerXml(text);
140
141
  } catch (cause) {
141
- throw new ConfigError('nfeProc malformado', { cause });
142
+ throw new ErroDeConfiguracao('nfeProc malformado', { cause });
142
143
  }
143
144
  const noProc =
144
- doc.root.local === 'nfeProc' && doc.root.ns === NFE_NS ? firstChild(doc.root, 'NFe', NFE_NS) : undefined;
145
- const el = noProc ?? (doc.root.local === 'NFe' && doc.root.ns === NFE_NS ? doc.root : undefined);
146
- if (el === undefined) throw new ConfigError(`esperado <nfeProc> ou <NFe> assinada, veio <${doc.root.name}>`);
147
- const fatia = doc.source.slice(el.start, el.end);
148
- const herdados = [...inScopeNamespaces(el)].filter(([p]) => p !== 'xml' && !el.namespaces.has(p));
145
+ doc.raiz.local === 'nfeProc' && doc.raiz.ns === NFE_NS ? primeiroFilho(doc.raiz, 'NFe', NFE_NS) : undefined;
146
+ const el = noProc ?? (doc.raiz.local === 'NFe' && doc.raiz.ns === NFE_NS ? doc.raiz : undefined);
147
+ if (el === undefined) throw new ErroDeConfiguracao(`esperado <nfeProc> ou <NFe> assinada, veio <${doc.raiz.nome}>`);
148
+ const fatia = doc.texto.slice(el.inicio, el.fim);
149
+ const herdados = [...namespacesEmEscopo(el)].filter(([p]) => p !== 'xml' && !el.namespaces.has(p));
149
150
  const decl = herdados
150
151
  .map(
151
152
  ([p, uri]) =>
152
153
  ` ${p === '' ? 'xmlns' : `xmlns:${p}`}="${uri.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;')}"`,
153
154
  )
154
155
  .join('');
155
- const at = 1 + el.name.length;
156
+ const at = 1 + el.nome.length;
156
157
  const assinado = decl === '' ? fatia : fatia.slice(0, at) + decl + fatia.slice(at);
157
158
  // Confere raiz, Id e assinatura como a retomada vai conferir: o que sai daqui serve direto nela.
158
159
  return documentoAssinado(assinado, 'NFe', 'infNFe').xml;
@@ -9,36 +9,36 @@
9
9
  * mesmo tipo.
10
10
  */
11
11
 
12
- import type { XmlDocument, XmlElement } from '@sinete/core/xml';
13
- import { firstChild, parseXml, textOf } from '@sinete/core/xml';
14
- import type { ConsultaOutcome, EventoRegistrado, NfeClient } from './client.ts';
12
+ import type { DocumentoXml, ElementoXml } from '@sinete/core/xml';
13
+ import { lerXml, primeiroFilho, textoDe } from '@sinete/core/xml';
14
+ import type { ClienteNfe, EnvioOpcoes, EventoRegistrado, ResultadoConsulta } from './client.ts';
15
15
  import { cstatEm } from './outcome.ts';
16
- import { NFE_NS, sliceElement } from './proc.ts';
16
+ import { NFE_NS, recortarElemento } from './proc.ts';
17
17
 
18
18
  /** Resultado da recuperação: o evento registrado, com a consulta que o prova, ou só a consulta. */
19
19
  export type RecuperacaoEvento =
20
- | { readonly registrado: true; readonly evento: EventoRegistrado; readonly consulta: ConsultaOutcome }
21
- | { readonly registrado: false; readonly consulta: ConsultaOutcome };
20
+ | { readonly registrado: true; readonly evento: EventoRegistrado; readonly consulta: ResultadoConsulta }
21
+ | { readonly registrado: false; readonly consulta: ResultadoConsulta };
22
22
 
23
23
  /** Texto do filho `local` no namespace da NF-e, se houver. */
24
- function campo(el: XmlElement, local: string): string | undefined {
25
- const c = firstChild(el, local, NFE_NS);
26
- return c === undefined ? undefined : textOf(c).trim();
24
+ function campo(el: ElementoXml, local: string): string | undefined {
25
+ const c = primeiroFilho(el, local, NFE_NS);
26
+ return c === undefined ? undefined : textoDe(c).trim();
27
27
  }
28
28
 
29
29
  /** Lê um `procEventoNFe` da consulta; `undefined` para o que não é um evento com retorno legível. */
30
30
  function lerProcEvento(xml: string): EventoRegistrado | undefined {
31
- let doc: XmlDocument;
31
+ let doc: DocumentoXml;
32
32
  try {
33
- doc = parseXml(xml);
33
+ doc = lerXml(xml);
34
34
  } catch {
35
35
  return undefined;
36
36
  }
37
- const evento = firstChild(doc.root, 'evento', NFE_NS);
38
- const pedido = evento === undefined ? undefined : firstChild(evento, 'infEvento', NFE_NS);
39
- const retEl = firstChild(doc.root, 'retEvento', NFE_NS);
40
- const ret = retEl === undefined ? undefined : firstChild(retEl, 'infEvento', NFE_NS);
41
- if (doc.root.local !== 'procEventoNFe' || pedido === undefined || retEl === undefined || ret === undefined) {
37
+ const evento = primeiroFilho(doc.raiz, 'evento', NFE_NS);
38
+ const pedido = evento === undefined ? undefined : primeiroFilho(evento, 'infEvento', NFE_NS);
39
+ const retEl = primeiroFilho(doc.raiz, 'retEvento', NFE_NS);
40
+ const ret = retEl === undefined ? undefined : primeiroFilho(retEl, 'infEvento', NFE_NS);
41
+ if (doc.raiz.local !== 'procEventoNFe' || pedido === undefined || retEl === undefined || ret === undefined) {
42
42
  return undefined;
43
43
  }
44
44
  const cStat = campo(ret, 'cStat');
@@ -62,7 +62,7 @@ function lerProcEvento(xml: string): EventoRegistrado | undefined {
62
62
  tpEvento,
63
63
  nSeqEvento: String(Number(nSeq)),
64
64
  dhRegEvento,
65
- retEvento: sliceElement(doc, retEl, ''),
65
+ retEvento: recortarElemento(doc, retEl, ''),
66
66
  procEventoNFe: xml,
67
67
  ...(nProt === undefined ? {} : { nProt }),
68
68
  };
@@ -70,21 +70,25 @@ function lerProcEvento(xml: string): EventoRegistrado | undefined {
70
70
 
71
71
  /**
72
72
  * Consulta a chave e devolve o evento `tpEvento` que a SEFAZ registrou para ela (o de maior `nSeqEvento`, quando há
73
- * vários, como na CC-e). Serve depois de um pedido de evento sem resposta ou respondido com 573 ou 580: nunca conclua
74
- * que o evento existe só pelo `cStat` do pedido. `registrado: false` quer dizer que a consulta não mostrou o evento
75
- * (ou não decidiu: veja `consulta`); não quer dizer que o evento não existe.
73
+ * vários, como na CC-e; com `nSeqEvento`, só o dessa sequência). Serve depois de um pedido de evento sem resposta ou
74
+ * respondido com 573 ou 580: nunca conclua que o evento existe só pelo `cStat` do pedido. `registrado: false` quer
75
+ * dizer que a consulta não mostrou o evento (ou não decidiu: veja `consulta`); não quer dizer que o evento não existe.
76
+ * `opcoes.signal` cancela a consulta.
76
77
  */
77
78
  export async function recuperarEventoRegistrado(
78
- client: NfeClient,
79
+ cliente: ClienteNfe,
79
80
  chave: string,
80
81
  tpEvento: string,
82
+ nSeqEvento?: number,
83
+ opcoes?: EnvioOpcoes,
81
84
  ): Promise<RecuperacaoEvento> {
82
- const consulta = await client.consultar(chave);
83
- if (consulta.status !== 'authorized' && consulta.status !== 'denied') return { registrado: false, consulta };
85
+ const consulta = await cliente.consultar(chave, undefined, opcoes);
86
+ if (consulta.tipo !== 'autorizado' && consulta.tipo !== 'denegado') return { registrado: false, consulta };
84
87
  let achado: EventoRegistrado | undefined;
85
- for (const xml of consulta.value.eventos) {
88
+ for (const xml of consulta.valor.eventos) {
86
89
  const e = lerProcEvento(xml);
87
- if (e === undefined || e.chNFe !== consulta.value.chNFe || e.tpEvento !== tpEvento) continue;
90
+ if (e === undefined || e.chNFe !== consulta.valor.chNFe || e.tpEvento !== tpEvento) continue;
91
+ if (nSeqEvento !== undefined && Number(e.nSeqEvento) !== nSeqEvento) continue;
88
92
  if (achado === undefined || Number(e.nSeqEvento) > Number(achado.nSeqEvento)) achado = e;
89
93
  }
90
94
  return achado === undefined ? { registrado: false, consulta } : { registrado: true, evento: achado, consulta };
@@ -26,9 +26,9 @@
26
26
  * Acesso [chNFe: ...]") e serviço de consulta protocolo (cStat 217; 561, 562 e 613 quando a numeração tem outra NF-e).
27
27
  */
28
28
 
29
- import type { Rejected } from '@sinete/core';
30
- import { authorized, denied } from '@sinete/core';
31
- import type { AutorizacaoOutcome, ConsultaOutcome, NfeClient, ProtocoloNfe } from './client.ts';
29
+ import type { Recusado } from '@sinete/core';
30
+ import { criarAutorizado, criarDenegado } from '@sinete/core';
31
+ import type { ClienteNfe, EnvioOpcoes, ProtocoloNfe, ResultadoAutorizacao, ResultadoConsulta } from './client.ts';
32
32
  import { cstatEm } from './outcome.ts';
33
33
  import { documentoAssinado } from './proc.ts';
34
34
 
@@ -41,15 +41,15 @@ export type ConteudoDoProtocolo = 'confere' | 'sem-digval' | 'difere';
41
41
  /** O que fazer com uma NF-e cujo envio ficou sem resposta ou voltou como duplicidade. */
42
42
  export type ResolucaoEnvio =
43
43
  /**
44
- * A chave está decidida: autorizada (ou cancelada) com o mesmo conteúdo, e `outcome` traz o protocolo e o `nfeProc`;
45
- * ou denegada, com qualquer conteúdo (a denegação é da chave), e `outcome` traz o protocolo, com o `nfeProc` só
44
+ * A chave está decidida: autorizada (ou cancelada) com o mesmo conteúdo, e `resultado` traz o protocolo e o `nfeProc`;
45
+ * ou denegada, com qualquer conteúdo (a denegação é da chave), e `resultado` traz o protocolo, com o `nfeProc` só
46
46
  * quando `conteudo` é `confere`.
47
47
  */
48
48
  | {
49
49
  readonly acao: 'concluida';
50
50
  readonly situacao: 'autorizada' | 'cancelada' | 'denegada';
51
51
  readonly conteudo: ConteudoDoProtocolo;
52
- readonly outcome: AutorizacaoOutcome;
52
+ readonly resultado: ResultadoAutorizacao;
53
53
  }
54
54
  /** A NF-e não consta na SEFAZ (217): reenvie `nfeAssinada`, os mesmos bytes. */
55
55
  | { readonly acao: 'reenviar'; readonly nfeAssinada: string }
@@ -60,20 +60,20 @@ export type ResolucaoEnvio =
60
60
  | {
61
61
  readonly acao: 'divergente';
62
62
  readonly chNFe?: string;
63
- readonly consulta?: ConsultaOutcome;
64
- readonly motivo: Rejected | ConsultaOutcome;
63
+ readonly consulta?: ResultadoConsulta;
64
+ readonly motivo: Recusado | ResultadoConsulta;
65
65
  }
66
66
  /**
67
67
  * A chave está autorizada (ou cancelada), mas o protocolo não traz `digVal`: nada prova que o conteúdo autorizado é o
68
68
  * destes bytes. Não reenvie nem descarte a nota local, e não a guarde como autorizada sem conferir o XML registrado
69
69
  * na SEFAZ (a Distribuição DF-e ou o portal devolvem a NF-e autorizada).
70
70
  */
71
- | { readonly acao: 'sem-prova'; readonly situacao: 'autorizada' | 'cancelada'; readonly consulta: ConsultaOutcome }
71
+ | { readonly acao: 'sem-prova'; readonly situacao: 'autorizada' | 'cancelada'; readonly consulta: ResultadoConsulta }
72
72
  /**
73
73
  * A consulta não decidiu (serviço paralisado, consumo indevido, rejeição de schema, situação sem protocolo): tente de
74
74
  * novo mais tarde, sem descartar a nota local.
75
75
  */
76
- | { readonly acao: 'indefinida'; readonly outcome: ConsultaOutcome };
76
+ | { readonly acao: 'indefinida'; readonly resultado: ResultadoConsulta };
77
77
 
78
78
  /** Chave com CNPJ alfanumérico (NT 2025.001): 6 dígitos, 12 alfanuméricos, 26 dígitos. */
79
79
  const CHAVE_NO_MOTIVO = /\[\s*chNFe\s*:\s*([0-9]{6}[0-9A-Z]{12}[0-9]{26})\s*\]/i;
@@ -85,21 +85,23 @@ export function chaveDaDuplicidade(xMotivo: string): string | undefined {
85
85
 
86
86
  /**
87
87
  * Resolve um envio de autorização sem resposta, ou cuja resposta foi 204 ou 539, consultando a chave da NF-e
88
- * assinada. `anterior` é o desfecho do envio, quando houve um.
88
+ * assinada. `anterior` é o desfecho do envio, quando houve um. `opcoes.signal` cancela a consulta (lança o
89
+ * `ErroTransporte` com `code: 'cancelado'`).
89
90
  */
90
91
  export async function resolverEnvioSemResposta(
91
- client: NfeClient,
92
+ cliente: ClienteNfe,
92
93
  nfeAssinada: string,
93
- anterior?: AutorizacaoOutcome,
94
+ anterior?: ResultadoAutorizacao,
95
+ opcoes?: EnvioOpcoes,
94
96
  ): Promise<ResolucaoEnvio> {
95
97
  const a = documentoAssinado(nfeAssinada, 'NFe', 'infNFe');
96
98
  const chave = a.id.slice(3);
97
- if (anterior && anterior.status === 'rejected' && cstatEm(anterior.cStat, 'duplicidadeChaveDiferente')) {
99
+ if (anterior && anterior.tipo === 'recusado' && cstatEm(anterior.cStat, 'duplicidadeChaveDiferente')) {
98
100
  const outra = chaveDaDuplicidade(anterior.xMotivo);
99
101
  return { acao: 'divergente', motivo: anterior, ...(outra === undefined ? {} : { chNFe: outra }) };
100
102
  }
101
- const consulta = await client.consultar(chave, nfeAssinada);
102
- if (consulta.status === 'rejected') {
103
+ const consulta = await cliente.consultar(chave, nfeAssinada, opcoes);
104
+ if (consulta.tipo === 'recusado') {
103
105
  if (cstatEm(consulta.cStat, 'naoConsta')) return { acao: 'reenviar', nfeAssinada: a.xml };
104
106
  // 561, 562 e 613 na consulta: a chave local não consta, mas a numeração dela tem outra NF-e (outro mês, outro cNF
105
107
  // ou outra chave). É a mesma situação da 539 no envio; o 562 traz a chave registrada no xMotivo.
@@ -107,21 +109,21 @@ export async function resolverEnvioSemResposta(
107
109
  const outra = chaveDaDuplicidade(consulta.xMotivo);
108
110
  return { acao: 'divergente', motivo: consulta, ...(outra === undefined ? {} : { chNFe: outra }) };
109
111
  }
110
- return { acao: 'indefinida', outcome: consulta };
112
+ return { acao: 'indefinida', resultado: consulta };
111
113
  }
112
- if (consulta.status === 'pending') return { acao: 'indefinida', outcome: consulta };
113
- const v = consulta.value;
114
+ if (consulta.tipo === 'pendente') return { acao: 'indefinida', resultado: consulta };
115
+ const v = consulta.valor;
114
116
  const p: ProtocoloNfe | undefined = v.protocolo;
115
- if (!p) return { acao: 'indefinida', outcome: consulta };
117
+ if (!p) return { acao: 'indefinida', resultado: consulta };
116
118
  const conteudo: ConteudoDoProtocolo =
117
119
  p.digVal === undefined ? 'sem-digval' : v.digValConfere === true ? 'confere' : 'difere';
118
120
  const protStatus = { cStat: p.cStat, xMotivo: p.xMotivo };
119
121
  // A denegação é da chave: o número está denegado com qualquer conteúdo, e o `conteudo` diz se é o destes bytes.
120
122
  if (v.situacao === 'denegada')
121
- return { acao: 'concluida', situacao: 'denegada', conteudo, outcome: denied(protStatus, p) };
123
+ return { acao: 'concluida', situacao: 'denegada', conteudo, resultado: criarDenegado(protStatus, p) };
122
124
  // Só um digVal presente e diferente prova outro conteúdo para a chave.
123
125
  if (conteudo === 'difere') return { acao: 'divergente', chNFe: chave, consulta, motivo: consulta };
124
126
  if (conteudo === 'sem-digval') return { acao: 'sem-prova', situacao: v.situacao, consulta };
125
- if (p.nfeProc === undefined) return { acao: 'indefinida', outcome: consulta };
126
- return { acao: 'concluida', situacao: v.situacao, conteudo, outcome: authorized(protStatus, p) };
127
+ if (p.nfeProc === undefined) return { acao: 'indefinida', resultado: consulta };
128
+ return { acao: 'concluida', situacao: v.situacao, conteudo, resultado: criarAutorizado(protStatus, p) };
127
129
  }